Move email editor integration tests

[MAILPOET-6216]
This commit is contained in:
Jan Lysý
2024-09-10 19:45:32 +02:00
committed by Jan Lysý
parent 0860f7c45f
commit d0c1ad4aba
24 changed files with 33 additions and 17 deletions

View File

@@ -0,0 +1,38 @@
<?php declare(strict_types = 1);
namespace EmailEditor\Engine;
use MailPoet\EmailEditor\Engine\EmailEditor;
class EmailEditorTest extends \MailPoetTest {
/** @var EmailEditor */
private $emailEditor;
/** @var callable */
private $postRegisterCallback;
public function _before() {
parent::_before();
$this->emailEditor = $this->diContainer->get(EmailEditor::class);
$this->postRegisterCallback = function ($postTypes) {
$postTypes[] = [
'name' => 'custom_email_type',
'args' => [],
'meta' => [],
];
return $postTypes;
};
add_filter('mailpoet_email_editor_post_types', $this->postRegisterCallback);
$this->emailEditor->initialize();
}
public function testItRegistersCustomPostTypeAddedViaHook() {
$postTypes = get_post_types();
$this->assertArrayHasKey('custom_email_type', $postTypes);
}
public function _after() {
parent::_after();
remove_filter('mailpoet_email_editor_post_types', $this->postRegisterCallback);
}
}

View File

@@ -0,0 +1,38 @@
<?php declare(strict_types = 1);
namespace EmailEditor\Engine\Renderer\ContentRenderer;
use MailPoet\EmailEditor\Engine\Renderer\ContentRenderer\BlocksRegistry;
use MailPoet\EmailEditor\Integrations\Core\Renderer\Blocks\Text;
require_once __DIR__ . '/DummyBlockRenderer.php';
class BlocksRegistryTest extends \MailPoetTest {
/** @var BlocksRegistry */
private $registry;
public function _before() {
parent::_before();
$this->registry = $this->diContainer->get(BlocksRegistry::class);
}
public function testItReturnsNullForUnknownRenderer() {
$storedRenderer = $this->registry->getBlockRenderer('test');
verify($storedRenderer)->null();
}
public function testItStoresAddedRenderer() {
$renderer = new Text();
$this->registry->addBlockRenderer('test', $renderer);
$storedRenderer = $this->registry->getBlockRenderer('test');
verify($storedRenderer)->equals($renderer);
}
public function testItReportsWhichRenderersAreRegistered() {
$renderer = new Text();
$this->registry->addBlockRenderer('test', $renderer);
verify($this->registry->hasBlockRenderer('test'))->true();
verify($this->registry->hasBlockRenderer('unknown'))->false();
}
}

View File

@@ -0,0 +1,54 @@
<?php declare(strict_types = 1);
namespace EmailEditor\Engine\Renderer\ContentRenderer;
use MailPoet\EmailEditor\Engine\EmailEditor;
use MailPoet\EmailEditor\Engine\Renderer\ContentRenderer\ContentRenderer;
use MailPoet\EmailEditor\Integrations\MailPoet\Blocks\BlockTypesController;
require_once __DIR__ . '/DummyBlockRenderer.php';
class ContentRendererTest extends \MailPoetTest {
private ContentRenderer $renderer;
private \WP_Post $emailPost;
public function _before(): void {
parent::_before();
$this->diContainer->get(EmailEditor::class)->initialize();
$this->diContainer->get(BlockTypesController::class)->initialize();
$this->renderer = $this->diContainer->get(ContentRenderer::class);
$this->emailPost = $this->tester->createPost([
'post_content' => '<!-- wp:paragraph --><p>Hello!</p><!-- /wp:paragraph -->',
]);
}
public function testItRendersContent(): void {
$template = new \WP_Block_Template();
$template->id = 'template-id';
$template->content = '<!-- wp:core/post-content /-->';
$content = $this->renderer->render(
$this->emailPost,
$template
);
verify($content)->stringContainsString('Hello!');
}
public function testItInlinesContentStyles(): void {
$template = new \WP_Block_Template();
$template->id = 'template-id';
$template->content = '<!-- wp:core/post-content /-->';
$rendered = $this->renderer->render($this->emailPost, $template);
$paragraphStyles = $this->getStylesValueForTag($rendered, 'p');
verify($paragraphStyles)->stringContainsString('margin: 0');
verify($paragraphStyles)->stringContainsString('display: block');
}
private function getStylesValueForTag($html, $tag): ?string {
$html = new \WP_HTML_Tag_Processor($html);
if ($html->next_tag($tag)) {
return $html->get_attribute('style');
}
return null;
}
}

View File

@@ -0,0 +1,12 @@
<?php declare(strict_types = 1);
namespace EmailEditor\Engine\Renderer\ContentRenderer;
use MailPoet\EmailEditor\Engine\Renderer\ContentRenderer\BlockRenderer;
use MailPoet\EmailEditor\Engine\SettingsController;
class DummyBlockRenderer implements BlockRenderer {
public function render(string $blockContent, array $parsedBlock, SettingsController $settingsController): string {
return $parsedBlock['innerHtml'];
}
}

View File

@@ -0,0 +1,236 @@
<?php declare(strict_types = 1);
namespace EmailEditor\Engine\Renderer\ContentRenderer\Layout;
use EmailEditor\Engine\Renderer\ContentRenderer\DummyBlockRenderer;
use MailPoet\EmailEditor\Engine\Renderer\ContentRenderer\Layout\FlexLayoutRenderer;
use MailPoet\EmailEditor\Engine\SettingsController;
require_once __DIR__ . '/../DummyBlockRenderer.php';
class FlexLayoutRendererTest extends \MailPoetTest {
/** @var FlexLayoutRenderer */
private $renderer;
/** @var SettingsController */
private $settingsController;
public function _before(): void {
parent::_before();
$this->settingsController = $this->diContainer->get(SettingsController::class);
$this->renderer = new FlexLayoutRenderer();
register_block_type('dummy/block', []);
add_filter('render_block', [$this, 'renderDummyBlock'], 10, 2);
}
public function testItRendersInnerBlocks(): void {
$parsedBlock = [
'innerBlocks' => [
[
'blockName' => 'dummy/block',
'innerHtml' => 'Dummy 1',
],
[
'blockName' => 'dummy/block',
'innerHtml' => 'Dummy 2',
],
],
'email_attrs' => [],
];
$output = $this->renderer->renderInnerBlocksInLayout($parsedBlock, $this->settingsController);
verify($output)->stringContainsString('Dummy 1');
verify($output)->stringContainsString('Dummy 2');
}
public function testItHandlesJustification(): void {
$parsedBlock = [
'innerBlocks' => [
[
'blockName' => 'dummy/block',
'innerHtml' => 'Dummy 1',
],
],
'email_attrs' => [],
];
// Default justification is left
$output = $this->renderer->renderInnerBlocksInLayout($parsedBlock, $this->settingsController);
verify($output)->stringContainsString('text-align: left');
verify($output)->stringContainsString('align="left"');
// Right justification
$parsedBlock['attrs']['layout']['justifyContent'] = 'right';
$output = $this->renderer->renderInnerBlocksInLayout($parsedBlock, $this->settingsController);
verify($output)->stringContainsString('text-align: right');
verify($output)->stringContainsString('align="right"');
// Center justification
$parsedBlock['attrs']['layout']['justifyContent'] = 'center';
$output = $this->renderer->renderInnerBlocksInLayout($parsedBlock, $this->settingsController);
verify($output)->stringContainsString('text-align: center');
verify($output)->stringContainsString('align="center"');
}
public function testItEscapesAttributes(): void {
$parsedBlock = [
'innerBlocks' => [
[
'blockName' => 'dummy/block',
'innerHtml' => 'Dummy 1',
],
],
'email_attrs' => [],
];
$parsedBlock['attrs']['layout']['justifyContent'] = '"> <script>alert("XSS")</script><div style="text-align: right';
$output = $this->renderer->renderInnerBlocksInLayout($parsedBlock, $this->settingsController);
verify($output)->stringNotContainsString('<script>alert("XSS")</script>');
}
public function testInComputesProperWidthsForReasonableSettings(): void {
$parsedBlock = [
'innerBlocks' => [],
'email_attrs' => [
'width' => '640px',
],
];
// 50% and 25%
$parsedBlock['innerBlocks'] = [
[
'blockName' => 'dummy/block',
'innerHtml' => 'Dummy 1',
'attrs' => ['width' => '50'],
],
[
'blockName' => 'dummy/block',
'innerHtml' => 'Dummy 2',
'attrs' => ['width' => '25'],
],
];
$output = $this->renderer->renderInnerBlocksInLayout($parsedBlock, $this->settingsController);
$flexItems = $this->getFlexItemsFromOutput($output);
verify($flexItems[0])->stringContainsString('width:312px;');
verify($flexItems[1])->stringContainsString('width:148px;');
// 25% and 25% and auto
$parsedBlock['innerBlocks'] = [
[
'blockName' => 'dummy/block',
'innerHtml' => 'Dummy 1',
'attrs' => ['width' => '25'],
],
[
'blockName' => 'dummy/block',
'innerHtml' => 'Dummy 2',
'attrs' => ['width' => '25'],
],
[
'blockName' => 'dummy/block',
'innerHtml' => 'Dummy 3',
'attrs' => [],
],
];
$output = $this->renderer->renderInnerBlocksInLayout($parsedBlock, $this->settingsController);
$flexItems = $this->getFlexItemsFromOutput($output);
verify($flexItems[0])->stringContainsString('width:148px;');
verify($flexItems[1])->stringContainsString('width:148px;');
verify($flexItems[2])->stringNotContainsString('width:');
// 50% and 50%
$parsedBlock['innerBlocks'] = [
[
'blockName' => 'dummy/block',
'innerHtml' => 'Dummy 1',
'attrs' => ['width' => '50'],
],
[
'blockName' => 'dummy/block',
'innerHtml' => 'Dummy 2',
'attrs' => ['width' => '50'],
],
];
$output = $this->renderer->renderInnerBlocksInLayout($parsedBlock, $this->settingsController);
$flexItems = $this->getFlexItemsFromOutput($output);
verify($flexItems[0])->stringContainsString('width:312px;');
verify($flexItems[1])->stringContainsString('width:312px;');
}
public function testInComputesWidthsForStrangeSettingsValues(): void {
$parsedBlock = [
'innerBlocks' => [],
'email_attrs' => [
'width' => '640px',
],
];
// 100% and 25%
$parsedBlock['innerBlocks'] = [
[
'blockName' => 'dummy/block',
'innerHtml' => 'Dummy 1',
'attrs' => ['width' => '100'],
],
[
'blockName' => 'dummy/block',
'innerHtml' => 'Dummy 2',
'attrs' => ['width' => '25'],
],
];
$output = $this->renderer->renderInnerBlocksInLayout($parsedBlock, $this->settingsController);
$flexItems = $this->getFlexItemsFromOutput($output);
verify($flexItems[0])->stringContainsString('width:508px;');
verify($flexItems[1])->stringContainsString('width:105px;');
// 100% and 100%
$parsedBlock['innerBlocks'] = [
[
'blockName' => 'dummy/block',
'innerHtml' => 'Dummy 1',
'attrs' => ['width' => '100'],
],
[
'blockName' => 'dummy/block',
'innerHtml' => 'Dummy 2',
'attrs' => ['width' => '100'],
],
];
$output = $this->renderer->renderInnerBlocksInLayout($parsedBlock, $this->settingsController);
$flexItems = $this->getFlexItemsFromOutput($output);
verify($flexItems[0])->stringContainsString('width:312px;');
verify($flexItems[1])->stringContainsString('width:312px;');
// 100% and auto
$parsedBlock['innerBlocks'] = [
[
'blockName' => 'dummy/block',
'innerHtml' => 'Dummy 1',
'attrs' => ['width' => '100'],
],
[
'blockName' => 'dummy/block',
'innerHtml' => 'Dummy 2',
'attrs' => [],
],
];
$output = $this->renderer->renderInnerBlocksInLayout($parsedBlock, $this->settingsController);
$flexItems = $this->getFlexItemsFromOutput($output);
verify($flexItems[0])->stringContainsString('width:508px;');
verify($flexItems[1])->stringNotContainsString('width:');
}
private function getFlexItemsFromOutput(string $output): array {
$matches = [];
preg_match_all('/<td class="layout-flex-item" style="(.*)">/', $output, $matches);
return explode('><', $matches[0][0] ?? []);
}
public function renderDummyBlock($blockContent, $parsedBlock): string {
$dummyRenderer = new DummyBlockRenderer();
return $dummyRenderer->render($blockContent, $parsedBlock, $this->settingsController);
}
public function _after(): void {
parent::_after();
unregister_block_type('dummy/block');
remove_filter('render_block', [$this, 'renderDummyBlock'], 10);
}
}

View File

@@ -0,0 +1,119 @@
<?php declare(strict_types = 1);
namespace EmailEditor\Engine\Renderer;
use MailPoet\EmailEditor\Engine\EmailEditor;
use MailPoet\EmailEditor\Engine\Renderer\Renderer;
use MailPoet\EmailEditor\Engine\SettingsController;
use MailPoet\EmailEditor\Engine\ThemeController;
class RendererTest extends \MailPoetTest {
private Renderer $renderer;
private \WP_Post $emailPost;
public function _before(): void {
parent::_before();
$this->diContainer->get(EmailEditor::class)->initialize();
$this->renderer = $this->diContainer->get(Renderer::class);
$styles = [
'spacing' => [
'padding' => [
'bottom' => '4px',
'top' => '3px',
'left' => '2px',
'right' => '1px',
],
],
'typography' => [
'fontFamily' => 'Test Font Family',
],
'color' => [
'background' => '#123456',
],
];
$themeJsonMock = $this->createMock(\WP_Theme_JSON::class);
$themeJsonMock->method('get_data')->willReturn([
'styles' => $styles,
]);
$settingsControllerMock = $this->createMock(SettingsController::class);
$settingsControllerMock->method('getEmailStyles')->willReturn($styles);
$themeControllerMock = $this->createMock(ThemeController::class);
$themeControllerMock->method('getTheme')->willReturn($themeJsonMock);
$themeControllerMock->method('getStyles')->willReturn($styles);
$this->renderer = $this->getServiceWithOverrides(Renderer::class, [
'settingsController' => $settingsControllerMock,
'themeController' => $themeControllerMock,
]);
$this->emailPost = $this->tester->createPost([
'post_content' => '<!-- wp:paragraph --><p>Hello!</p><!-- /wp:paragraph -->',
]);
}
public function testItRendersTemplateWithContent(): void {
$rendered = $this->renderer->render(
$this->emailPost,
'Subject',
'Preheader content',
'en',
'noindex,nofollow'
);
verify($rendered['html'])->stringContainsString('Subject');
verify($rendered['html'])->stringContainsString('Preheader content');
verify($rendered['html'])->stringContainsString('noindex,nofollow');
verify($rendered['html'])->stringContainsString('Hello!');
verify($rendered['text'])->stringContainsString('Preheader content');
verify($rendered['text'])->stringContainsString('Hello!');
}
public function testItInlinesStyles(): void {
$stylesCallback = function ($styles) {
return $styles . 'body { color: pink; }';
};
add_filter('mailpoet_email_renderer_styles', $stylesCallback);
$rendered = $this->renderer->render($this->emailPost, 'Subject', '', 'en');
$style = $this->getStylesValueForTag($rendered['html'], ['tag_name' => 'body']);
verify($style)->stringContainsString('color: pink');
remove_filter('mailpoet_email_renderer_styles', $stylesCallback);
}
public function testItInlinesBodyStyles(): void {
$rendered = $this->renderer->render($this->emailPost, 'Subject', '', 'en');
$style = $this->getStylesValueForTag($rendered['html'], ['tag_name' => 'body']);
verify($style)->stringContainsString('margin: 0; padding: 0;');
}
public function testItInlinesWrappersStyles(): void {
$rendered = $this->renderer->render($this->emailPost, 'Subject', '', 'en');
// Verify body element styles
$style = $this->getStylesValueForTag($rendered['html'], ['tag_name' => 'body']);
verify($style)->stringContainsString('background-color: #123456');
// Verify layout element styles
$doc = new \DOMDocument();
$doc->loadHTML($rendered['html']);
$xpath = new \DOMXPath($doc);
$wrapper = null;
$nodes = $xpath->query('//div[contains(@class, "email_layout_wrapper")]');
if (($nodes instanceof \DOMNodeList) && $nodes->length > 0) {
$wrapper = $nodes->item(0);
}
$this->assertInstanceOf(\DOMElement::class, $wrapper);
$style = $wrapper->getAttribute('style');
verify($style)->stringContainsString('background-color: #123456');
verify($style)->stringContainsString('font-family: Test Font Family;');
verify($style)->stringContainsString('padding-top: 3px;');
verify($style)->stringContainsString('padding-bottom: 4px;');
}
private function getStylesValueForTag(string $html, array $query): ?string {
$html = new \WP_HTML_Tag_Processor($html);
if ($html->next_tag($query)) {
return $html->get_attribute('style');
}
return null;
}
}

View File

@@ -0,0 +1,113 @@
<?php declare(strict_types = 1);
namespace EmailEditor\Engine;
use MailPoet\EmailEditor\Engine\ThemeController;
class ThemeControllerTest extends \MailPoetTest {
private ThemeController $themeController;
public function _before() {
parent::_before();
$this->themeController = $this->diContainer->get(ThemeController::class);
}
public function testItGeneratesCssStylesForRenderer() {
$css = $this->themeController->getStylesheetForRendering();
// Font families
verify($css)->stringContainsString('.has-arial-font-family');
verify($css)->stringContainsString('.has-comic-sans-ms-font-family');
verify($css)->stringContainsString('.has-courier-new-font-family');
verify($css)->stringContainsString('.has-georgia-font-family');
verify($css)->stringContainsString('.has-lucida-font-family');
verify($css)->stringContainsString('.has-tahoma-font-family');
verify($css)->stringContainsString('.has-times-new-roman-font-family');
verify($css)->stringContainsString('.has-trebuchet-ms-font-family');
verify($css)->stringContainsString('.has-verdana-font-family');
verify($css)->stringContainsString('.has-arvo-font-family');
verify($css)->stringContainsString('.has-lato-font-family');
verify($css)->stringContainsString('.has-merriweather-font-family');
verify($css)->stringContainsString('.has-merriweather-sans-font-family');
verify($css)->stringContainsString('.has-noticia-text-font-family');
verify($css)->stringContainsString('.has-open-sans-font-family');
verify($css)->stringContainsString('.has-playfair-display-font-family');
verify($css)->stringContainsString('.has-roboto-font-family');
verify($css)->stringContainsString('.has-source-sans-pro-font-family');
verify($css)->stringContainsString('.has-oswald-font-family');
verify($css)->stringContainsString('.has-raleway-font-family');
verify($css)->stringContainsString('.has-permanent-marker-font-family');
verify($css)->stringContainsString('.has-pacifico-font-family');
verify($css)->stringContainsString('.has-small-font-size');
verify($css)->stringContainsString('.has-medium-font-size');
verify($css)->stringContainsString('.has-large-font-size');
verify($css)->stringContainsString('.has-x-large-font-size');
// Font sizes
verify($css)->stringContainsString('.has-small-font-size');
verify($css)->stringContainsString('.has-medium-font-size');
verify($css)->stringContainsString('.has-large-font-size');
verify($css)->stringContainsString('.has-x-large-font-size');
// Colors
verify($css)->stringContainsString('.has-black-color');
verify($css)->stringContainsString('.has-black-background-color');
verify($css)->stringContainsString('.has-black-border-color');
verify($css)->stringContainsString('.has-black-color');
verify($css)->stringContainsString('.has-black-background-color');
verify($css)->stringContainsString('.has-black-border-color');
$this->checkCorrectThemeConfiguration();
if (wp_get_theme()->get('Name') === 'Twenty Twenty-One') {
verify($css)->stringContainsString('.has-yellow-background-color');
verify($css)->stringContainsString('.has-yellow-color');
verify($css)->stringContainsString('.has-yellow-border-color');
}
}
public function testItCanTranslateFontSizeSlug() {
verify($this->themeController->translateSlugToFontSize('small'))->equals('13px');
verify($this->themeController->translateSlugToFontSize('medium'))->equals('16px');
verify($this->themeController->translateSlugToFontSize('large'))->equals('28px');
verify($this->themeController->translateSlugToFontSize('x-large'))->equals('42px');
verify($this->themeController->translateSlugToFontSize('unknown'))->equals('unknown');
}
public function testItCanTranslateColorSlug() {
verify($this->themeController->translateSlugToColor('black'))->equals('#000000');
verify($this->themeController->translateSlugToColor('white'))->equals('#ffffff');
verify($this->themeController->translateSlugToColor('cyan-bluish-gray'))->equals('#abb8c3');
verify($this->themeController->translateSlugToColor('pale-pink'))->equals('#f78da7');
$this->checkCorrectThemeConfiguration();
if (wp_get_theme()->get('Name') === 'Twenty Twenty-One') {
verify($this->themeController->translateSlugToColor('yellow'))->equals('#eeeadd');
}
}
public function testItLoadsColorPaletteFromSiteTheme() {
$this->checkCorrectThemeConfiguration();
$settings = $this->themeController->getSettings();
if (wp_get_theme()->get('Name') === 'Twenty Twenty-One') {
verify($settings['color']['palette']['theme'])->notEmpty();
}
}
public function testItReturnsCorrectPresetVariablesMap() {
$variableMap = $this->themeController->getVariablesValuesMap();
verify($variableMap['--wp--preset--color--black'])->equals('#000000');
verify($variableMap['--wp--preset--spacing--20'])->equals('20px');
}
/**
* This test depends on using Twenty Twenty-One or Twenty Nineteen theme.
* This method checks if the theme is correctly configured and trigger a failure if not
* to prevent silent failures in case we change theme configuration in the test environment.
*/
private function checkCorrectThemeConfiguration() {
$expectedThemes = ['Twenty Twenty-One'];
if (!in_array(wp_get_theme()->get('Name'), $expectedThemes)) {
$this->fail('Test depends on using Twenty Twenty-One or Twenty Nineteen theme. If you changed the theme, please update the test.');
}
}
}