Newsletter validation updates
- Rename validator to newsletterValidator for clarity - Add validation for ALC content - Refactor tests to use data factory for consistency and to avoid validation issues - Add separate tests for NewsletterValidator service - Add test helper for retrieving service with private properties overridden by name [MAILPOET-4236]
This commit is contained in:
committed by
Veljko V
parent
a5103f9596
commit
9bfe2b2ca1
@ -17,12 +17,12 @@ use MailPoet\Listing;
|
|||||||
use MailPoet\Newsletter\Listing\NewsletterListingRepository;
|
use MailPoet\Newsletter\Listing\NewsletterListingRepository;
|
||||||
use MailPoet\Newsletter\NewsletterSaveController;
|
use MailPoet\Newsletter\NewsletterSaveController;
|
||||||
use MailPoet\Newsletter\NewslettersRepository;
|
use MailPoet\Newsletter\NewslettersRepository;
|
||||||
|
use MailPoet\Newsletter\NewsletterValidator;
|
||||||
use MailPoet\Newsletter\Preview\SendPreviewController;
|
use MailPoet\Newsletter\Preview\SendPreviewController;
|
||||||
use MailPoet\Newsletter\Preview\SendPreviewException;
|
use MailPoet\Newsletter\Preview\SendPreviewException;
|
||||||
use MailPoet\Newsletter\Scheduler\PostNotificationScheduler;
|
use MailPoet\Newsletter\Scheduler\PostNotificationScheduler;
|
||||||
use MailPoet\Newsletter\Scheduler\Scheduler;
|
use MailPoet\Newsletter\Scheduler\Scheduler;
|
||||||
use MailPoet\Newsletter\Url as NewsletterUrl;
|
use MailPoet\Newsletter\Url as NewsletterUrl;
|
||||||
use MailPoet\Newsletter\Validator;
|
|
||||||
use MailPoet\Settings\SettingsController;
|
use MailPoet\Settings\SettingsController;
|
||||||
use MailPoet\UnexpectedValueException;
|
use MailPoet\UnexpectedValueException;
|
||||||
use MailPoet\Util\License\Features\Subscribers as SubscribersFeature;
|
use MailPoet\Util\License\Features\Subscribers as SubscribersFeature;
|
||||||
@ -76,11 +76,8 @@ class Newsletters extends APIEndpoint {
|
|||||||
/** @var NewsletterUrl */
|
/** @var NewsletterUrl */
|
||||||
private $newsletterUrl;
|
private $newsletterUrl;
|
||||||
|
|
||||||
/** @var TrackingConfig */
|
/** @var NewsletterValidator */
|
||||||
private $trackingConfig;
|
private $newsletterValidator;
|
||||||
|
|
||||||
/** @var Validator */
|
|
||||||
private $validator;
|
|
||||||
|
|
||||||
/** @var Scheduler */
|
/** @var Scheduler */
|
||||||
private $scheduler;
|
private $scheduler;
|
||||||
@ -100,7 +97,7 @@ class Newsletters extends APIEndpoint {
|
|||||||
NewsletterSaveController $newsletterSaveController,
|
NewsletterSaveController $newsletterSaveController,
|
||||||
NewsletterUrl $newsletterUrl,
|
NewsletterUrl $newsletterUrl,
|
||||||
Scheduler $scheduler,
|
Scheduler $scheduler,
|
||||||
Validator $validator
|
NewsletterValidator $newsletterValidator
|
||||||
) {
|
) {
|
||||||
$this->listingHandler = $listingHandler;
|
$this->listingHandler = $listingHandler;
|
||||||
$this->wp = $wp;
|
$this->wp = $wp;
|
||||||
@ -116,7 +113,7 @@ class Newsletters extends APIEndpoint {
|
|||||||
$this->newsletterSaveController = $newsletterSaveController;
|
$this->newsletterSaveController = $newsletterSaveController;
|
||||||
$this->newsletterUrl = $newsletterUrl;
|
$this->newsletterUrl = $newsletterUrl;
|
||||||
$this->scheduler = $scheduler;
|
$this->scheduler = $scheduler;
|
||||||
$this->validator = $validator;
|
$this->newsletterValidator = $newsletterValidator;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function get($data = []) {
|
public function get($data = []) {
|
||||||
@ -188,7 +185,7 @@ class Newsletters extends APIEndpoint {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ($status === NewsletterEntity::STATUS_ACTIVE) {
|
if ($status === NewsletterEntity::STATUS_ACTIVE) {
|
||||||
$validationError = $this->validator->validate($newsletter);
|
$validationError = $this->newsletterValidator->validate($newsletter);
|
||||||
if ($validationError !== null) {
|
if ($validationError !== null) {
|
||||||
return $this->errorResponse([APIError::FORBIDDEN => $validationError], [], Response::STATUS_FORBIDDEN);
|
return $this->errorResponse([APIError::FORBIDDEN => $validationError], [], Response::STATUS_FORBIDDEN);
|
||||||
}
|
}
|
||||||
|
@ -14,10 +14,10 @@ use MailPoet\Mailer\MailerFactory;
|
|||||||
use MailPoet\Models\Newsletter;
|
use MailPoet\Models\Newsletter;
|
||||||
use MailPoet\Models\SendingQueue as SendingQueueModel;
|
use MailPoet\Models\SendingQueue as SendingQueueModel;
|
||||||
use MailPoet\Newsletter\NewslettersRepository;
|
use MailPoet\Newsletter\NewslettersRepository;
|
||||||
|
use MailPoet\Newsletter\NewsletterValidator;
|
||||||
use MailPoet\Newsletter\Scheduler\Scheduler;
|
use MailPoet\Newsletter\Scheduler\Scheduler;
|
||||||
use MailPoet\Newsletter\Sending\ScheduledTasksRepository;
|
use MailPoet\Newsletter\Sending\ScheduledTasksRepository;
|
||||||
use MailPoet\Newsletter\Sending\SendingQueuesRepository;
|
use MailPoet\Newsletter\Sending\SendingQueuesRepository;
|
||||||
use MailPoet\Newsletter\Validator;
|
|
||||||
use MailPoet\Segments\SubscribersFinder;
|
use MailPoet\Segments\SubscribersFinder;
|
||||||
use MailPoet\Tasks\Sending as SendingTask;
|
use MailPoet\Tasks\Sending as SendingTask;
|
||||||
use MailPoet\Util\License\Features\Subscribers as SubscribersFeature;
|
use MailPoet\Util\License\Features\Subscribers as SubscribersFeature;
|
||||||
@ -45,12 +45,12 @@ class SendingQueue extends APIEndpoint {
|
|||||||
/** @var MailerFactory */
|
/** @var MailerFactory */
|
||||||
private $mailerFactory;
|
private $mailerFactory;
|
||||||
|
|
||||||
|
/** @var NewsletterValidator */
|
||||||
|
private $newsletterValidator;
|
||||||
|
|
||||||
/** @var Scheduler */
|
/** @var Scheduler */
|
||||||
private $scheduler;
|
private $scheduler;
|
||||||
|
|
||||||
/** @var Validator */
|
|
||||||
private $validator;
|
|
||||||
|
|
||||||
public function __construct(
|
public function __construct(
|
||||||
SubscribersFeature $subscribersFeature,
|
SubscribersFeature $subscribersFeature,
|
||||||
NewslettersRepository $newsletterRepository,
|
NewslettersRepository $newsletterRepository,
|
||||||
@ -59,7 +59,7 @@ class SendingQueue extends APIEndpoint {
|
|||||||
ScheduledTasksRepository $scheduledTasksRepository,
|
ScheduledTasksRepository $scheduledTasksRepository,
|
||||||
MailerFactory $mailerFactory,
|
MailerFactory $mailerFactory,
|
||||||
Scheduler $scheduler,
|
Scheduler $scheduler,
|
||||||
Validator $validator
|
NewsletterValidator $newsletterValidator
|
||||||
) {
|
) {
|
||||||
$this->subscribersFeature = $subscribersFeature;
|
$this->subscribersFeature = $subscribersFeature;
|
||||||
$this->subscribersFinder = $subscribersFinder;
|
$this->subscribersFinder = $subscribersFinder;
|
||||||
@ -68,7 +68,7 @@ class SendingQueue extends APIEndpoint {
|
|||||||
$this->scheduledTasksRepository = $scheduledTasksRepository;
|
$this->scheduledTasksRepository = $scheduledTasksRepository;
|
||||||
$this->mailerFactory = $mailerFactory;
|
$this->mailerFactory = $mailerFactory;
|
||||||
$this->scheduler = $scheduler;
|
$this->scheduler = $scheduler;
|
||||||
$this->validator = $validator;
|
$this->newsletterValidator = $newsletterValidator;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function add($data = []) {
|
public function add($data = []) {
|
||||||
@ -97,7 +97,7 @@ class SendingQueue extends APIEndpoint {
|
|||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
$validationError = $this->validator->validate($newsletterEntity);
|
$validationError = $this->newsletterValidator->validate($newsletterEntity);
|
||||||
if ($validationError) {
|
if ($validationError) {
|
||||||
return $this->errorResponse([
|
return $this->errorResponse([
|
||||||
APIError::BAD_REQUEST => $validationError,
|
APIError::BAD_REQUEST => $validationError,
|
||||||
|
@ -391,7 +391,7 @@ class ContainerConfigurator implements IContainerConfigurator {
|
|||||||
$container->autowire(\MailPoet\Newsletter\AutomaticEmailsRepository::class)->setPublic(true);
|
$container->autowire(\MailPoet\Newsletter\AutomaticEmailsRepository::class)->setPublic(true);
|
||||||
$container->autowire(\MailPoet\Newsletter\NewsletterHtmlSanitizer::class)->setPublic(true);
|
$container->autowire(\MailPoet\Newsletter\NewsletterHtmlSanitizer::class)->setPublic(true);
|
||||||
$container->autowire(\MailPoet\Newsletter\Url::class)->setPublic(true);
|
$container->autowire(\MailPoet\Newsletter\Url::class)->setPublic(true);
|
||||||
$container->autowire(\MailPoet\Newsletter\Validator::class)->setPublic(true);
|
$container->autowire(\MailPoet\Newsletter\NewsletterValidator::class)->setPublic(true);
|
||||||
$container->autowire(\MailPoet\Newsletter\Links\Links::class)->setPublic(true);
|
$container->autowire(\MailPoet\Newsletter\Links\Links::class)->setPublic(true);
|
||||||
$container->autowire(\MailPoet\Newsletter\Listing\NewsletterListingRepository::class)->setPublic(true);
|
$container->autowire(\MailPoet\Newsletter\Listing\NewsletterListingRepository::class)->setPublic(true);
|
||||||
$container->autowire(\MailPoet\Newsletter\Options\NewsletterOptionsRepository::class)->setPublic(true);
|
$container->autowire(\MailPoet\Newsletter\Options\NewsletterOptionsRepository::class)->setPublic(true);
|
||||||
|
@ -7,7 +7,7 @@ use MailPoet\Services\Bridge;
|
|||||||
use MailPoet\Settings\TrackingConfig;
|
use MailPoet\Settings\TrackingConfig;
|
||||||
use MailPoet\Validator\ValidationException;
|
use MailPoet\Validator\ValidationException;
|
||||||
|
|
||||||
class Validator {
|
class NewsletterValidator {
|
||||||
|
|
||||||
/** @var Bridge */
|
/** @var Bridge */
|
||||||
private $bridge;
|
private $bridge;
|
||||||
@ -27,7 +27,8 @@ class Validator {
|
|||||||
try {
|
try {
|
||||||
$this->validateBody($newsletterEntity);
|
$this->validateBody($newsletterEntity);
|
||||||
$this->validateUnsubscribeRequirements($newsletterEntity);
|
$this->validateUnsubscribeRequirements($newsletterEntity);
|
||||||
$this->validateReengagementRequirements($newsletterEntity);
|
$this->validateReEngagementRequirements($newsletterEntity);
|
||||||
|
$this->validateAutomaticLatestContentRequirements($newsletterEntity);
|
||||||
} catch (ValidationException $exception) {
|
} catch (ValidationException $exception) {
|
||||||
return __($exception->getMessage(), 'mailpoet');
|
return __($exception->getMessage(), 'mailpoet');
|
||||||
}
|
}
|
||||||
@ -61,7 +62,7 @@ class Validator {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private function validateReengagementRequirements(NewsletterEntity $newsletterEntity): void {
|
private function validateReEngagementRequirements(NewsletterEntity $newsletterEntity): void {
|
||||||
if ($newsletterEntity->getType() !== NewsletterEntity::TYPE_RE_ENGAGEMENT) {
|
if ($newsletterEntity->getType() !== NewsletterEntity::TYPE_RE_ENGAGEMENT) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -74,4 +75,17 @@ class Validator {
|
|||||||
throw new ValidationException('Re-engagement emails are disabled because open and click tracking is disabled in MailPoet → Settings → Advanced.');
|
throw new ValidationException('Re-engagement emails are disabled because open and click tracking is disabled in MailPoet → Settings → Advanced.');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function validateAutomaticLatestContentRequirements(NewsletterEntity $newsletterEntity) {
|
||||||
|
if ($newsletterEntity->getType() !== NewsletterEntity::TYPE_NOTIFICATION) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$content = $newsletterEntity->getContent();
|
||||||
|
if (
|
||||||
|
strpos($content, '"type":"automatedLatestContent"') === false &&
|
||||||
|
strpos($content, '"type":"automatedLatestContentLayout"') === false
|
||||||
|
) {
|
||||||
|
throw new ValidationException('Please add an “Automatic Latest Content” widget to the email from the right sidebar.');
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
namespace MailPoet\Test\DataFactories;
|
namespace MailPoet\Test\DataFactories;
|
||||||
|
|
||||||
|
use Codeception\Util\Fixtures;
|
||||||
use MailPoet\DI\ContainerWrapper;
|
use MailPoet\DI\ContainerWrapper;
|
||||||
use MailPoet\Entities\NewsletterEntity;
|
use MailPoet\Entities\NewsletterEntity;
|
||||||
use MailPoet\Entities\NewsletterOptionEntity;
|
use MailPoet\Entities\NewsletterOptionEntity;
|
||||||
@ -12,6 +13,7 @@ use MailPoet\Entities\ScheduledTaskSubscriberEntity;
|
|||||||
use MailPoet\Entities\SegmentEntity;
|
use MailPoet\Entities\SegmentEntity;
|
||||||
use MailPoet\Entities\SendingQueueEntity;
|
use MailPoet\Entities\SendingQueueEntity;
|
||||||
use MailPoet\Entities\SubscriberEntity;
|
use MailPoet\Entities\SubscriberEntity;
|
||||||
|
use MailPoet\Util\Security;
|
||||||
use MailPoetVendor\Carbon\Carbon;
|
use MailPoetVendor\Carbon\Carbon;
|
||||||
use MailPoetVendor\Doctrine\ORM\EntityManager;
|
use MailPoetVendor\Doctrine\ORM\EntityManager;
|
||||||
|
|
||||||
@ -57,6 +59,10 @@ class Newsletter {
|
|||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function withDefaultBody() {
|
||||||
|
return $this->withBody(json_decode(Fixtures::get('newsletter_body_template'), true));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return Newsletter
|
* @return Newsletter
|
||||||
*/
|
*/
|
||||||
@ -140,6 +146,11 @@ class Newsletter {
|
|||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function withReengagementType() {
|
||||||
|
$this->data['type'] = NewsletterEntity::TYPE_RE_ENGAGEMENT;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return Newsletter
|
* @return Newsletter
|
||||||
*/
|
*/
|
||||||
@ -332,6 +343,7 @@ class Newsletter {
|
|||||||
$newsletter->setSenderAddress('john.doe@example.com');
|
$newsletter->setSenderAddress('john.doe@example.com');
|
||||||
$newsletter->setSenderName('John Doe');
|
$newsletter->setSenderName('John Doe');
|
||||||
}
|
}
|
||||||
|
$newsletter->setHash(Security::generateHash());
|
||||||
if (isset($this->data['parent'])) $newsletter->setParent($this->data['parent']);
|
if (isset($this->data['parent'])) $newsletter->setParent($this->data['parent']);
|
||||||
if (isset($this->data['deleted_at'])) $newsletter->setDeletedAt($this->data['deleted_at']);
|
if (isset($this->data['deleted_at'])) $newsletter->setDeletedAt($this->data['deleted_at']);
|
||||||
return $newsletter;
|
return $newsletter;
|
||||||
|
@ -3,7 +3,6 @@
|
|||||||
namespace MailPoet\Test\API\JSON\v1;
|
namespace MailPoet\Test\API\JSON\v1;
|
||||||
|
|
||||||
use Codeception\Stub\Expected;
|
use Codeception\Stub\Expected;
|
||||||
use Codeception\Util\Fixtures;
|
|
||||||
use Codeception\Util\Stub;
|
use Codeception\Util\Stub;
|
||||||
use Helper\WordPressHooks as WPHooksHelper;
|
use Helper\WordPressHooks as WPHooksHelper;
|
||||||
use MailPoet\API\JSON\Response as APIResponse;
|
use MailPoet\API\JSON\Response as APIResponse;
|
||||||
@ -26,6 +25,7 @@ use MailPoet\Models\SendingQueue;
|
|||||||
use MailPoet\Newsletter\Listing\NewsletterListingRepository;
|
use MailPoet\Newsletter\Listing\NewsletterListingRepository;
|
||||||
use MailPoet\Newsletter\NewsletterSaveController;
|
use MailPoet\Newsletter\NewsletterSaveController;
|
||||||
use MailPoet\Newsletter\NewslettersRepository;
|
use MailPoet\Newsletter\NewslettersRepository;
|
||||||
|
use MailPoet\Newsletter\NewsletterValidator;
|
||||||
use MailPoet\Newsletter\Options\NewsletterOptionFieldsRepository;
|
use MailPoet\Newsletter\Options\NewsletterOptionFieldsRepository;
|
||||||
use MailPoet\Newsletter\Options\NewsletterOptionsRepository;
|
use MailPoet\Newsletter\Options\NewsletterOptionsRepository;
|
||||||
use MailPoet\Newsletter\Preview\SendPreviewController;
|
use MailPoet\Newsletter\Preview\SendPreviewController;
|
||||||
@ -36,13 +36,12 @@ use MailPoet\Newsletter\Segment\NewsletterSegmentRepository;
|
|||||||
use MailPoet\Newsletter\Sending\SendingQueuesRepository;
|
use MailPoet\Newsletter\Sending\SendingQueuesRepository;
|
||||||
use MailPoet\Newsletter\Statistics\NewsletterStatisticsRepository;
|
use MailPoet\Newsletter\Statistics\NewsletterStatisticsRepository;
|
||||||
use MailPoet\Newsletter\Url;
|
use MailPoet\Newsletter\Url;
|
||||||
use MailPoet\Newsletter\Validator;
|
|
||||||
use MailPoet\Router\Router;
|
use MailPoet\Router\Router;
|
||||||
use MailPoet\Segments\SegmentsRepository;
|
use MailPoet\Segments\SegmentsRepository;
|
||||||
use MailPoet\Settings\SettingsController;
|
use MailPoet\Settings\SettingsController;
|
||||||
use MailPoet\Tasks\Sending as SendingTask;
|
use MailPoet\Tasks\Sending as SendingTask;
|
||||||
|
use MailPoet\Test\DataFactories\Newsletter;
|
||||||
use MailPoet\Util\License\Features\Subscribers as SubscribersFeature;
|
use MailPoet\Util\License\Features\Subscribers as SubscribersFeature;
|
||||||
use MailPoet\Util\Security;
|
|
||||||
use MailPoet\WooCommerce\Helper as WCHelper;
|
use MailPoet\WooCommerce\Helper as WCHelper;
|
||||||
use MailPoet\WP\Emoji;
|
use MailPoet\WP\Emoji;
|
||||||
use MailPoet\WP\Functions as WPFunctions;
|
use MailPoet\WP\Functions as WPFunctions;
|
||||||
@ -113,8 +112,8 @@ class NewslettersTest extends \MailPoetTest {
|
|||||||
),
|
),
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
$this->newsletter = $this->createNewsletter('My Standard Newsletter', NewsletterEntity::TYPE_STANDARD);
|
$this->newsletter = (new Newsletter())->withDefaultBody()->withSubject('My Standard Newsletter')->create();
|
||||||
$this->postNotification = $this->createNewsletter('My Post Notification', NewsletterEntity::TYPE_NOTIFICATION);
|
$this->postNotification = (new Newsletter())->withPostNotificationsType()->withSubject('My Post Notification')->loadBodyFrom('newsletterWithALC.json')->create();
|
||||||
|
|
||||||
$this->createNewsletterOptionField(NewsletterOptionFieldEntity::NAME_IS_SCHEDULED, NewsletterEntity::TYPE_STANDARD);
|
$this->createNewsletterOptionField(NewsletterOptionFieldEntity::NAME_IS_SCHEDULED, NewsletterEntity::TYPE_STANDARD);
|
||||||
$this->createNewsletterOptionField(NewsletterOptionFieldEntity::NAME_SCHEDULED_AT, NewsletterEntity::TYPE_STANDARD);
|
$this->createNewsletterOptionField(NewsletterOptionFieldEntity::NAME_SCHEDULED_AT, NewsletterEntity::TYPE_STANDARD);
|
||||||
@ -129,7 +128,7 @@ class NewslettersTest extends \MailPoetTest {
|
|||||||
if (!$sentAt) {
|
if (!$sentAt) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
$sentNewsletters[$i] = $this->createNewsletter("Sent newsletter {$i}", NewsletterEntity::TYPE_STANDARD);
|
$sentNewsletters[$i] = (new Newsletter())->withSubject("Sent newsletter {$i}")->create();
|
||||||
$sentNewsletters[$i]->setSentAt($sentAt);
|
$sentNewsletters[$i]->setSentAt($sentAt);
|
||||||
}
|
}
|
||||||
$this->newsletterRepository->flush();
|
$this->newsletterRepository->flush();
|
||||||
@ -695,21 +694,10 @@ class NewslettersTest extends \MailPoetTest {
|
|||||||
$this->diContainer->get(NewsletterSaveController::class),
|
$this->diContainer->get(NewsletterSaveController::class),
|
||||||
$this->diContainer->get(Url::class),
|
$this->diContainer->get(Url::class),
|
||||||
$this->scheduler,
|
$this->scheduler,
|
||||||
$this->diContainer->get(Validator::class)
|
$this->diContainer->get(NewsletterValidator::class)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private function createNewsletter(string $subject, string $type): NewsletterEntity {
|
|
||||||
$newsletter = new NewsletterEntity();
|
|
||||||
$newsletter->setSubject($subject);
|
|
||||||
$newsletter->setBody((array)json_decode(Fixtures::get('newsletter_body_template'), true));
|
|
||||||
$newsletter->setType($type);
|
|
||||||
$newsletter->setHash(Security::generateHash());
|
|
||||||
$this->newsletterRepository->persist($newsletter);
|
|
||||||
$this->newsletterRepository->flush();
|
|
||||||
return $newsletter;
|
|
||||||
}
|
|
||||||
|
|
||||||
private function createNewsletterOptionField(string $name, string $type): NewsletterOptionFieldEntity {
|
private function createNewsletterOptionField(string $name, string $type): NewsletterOptionFieldEntity {
|
||||||
$optionField = new NewsletterOptionFieldEntity();
|
$optionField = new NewsletterOptionFieldEntity();
|
||||||
$optionField->setName($name);
|
$optionField->setName($name);
|
||||||
|
@ -13,12 +13,12 @@ use MailPoet\Entities\ScheduledTaskEntity;
|
|||||||
use MailPoet\Entities\SendingQueueEntity;
|
use MailPoet\Entities\SendingQueueEntity;
|
||||||
use MailPoet\Mailer\MailerFactory;
|
use MailPoet\Mailer\MailerFactory;
|
||||||
use MailPoet\Newsletter\NewslettersRepository;
|
use MailPoet\Newsletter\NewslettersRepository;
|
||||||
|
use MailPoet\Newsletter\NewsletterValidator;
|
||||||
use MailPoet\Newsletter\Options\NewsletterOptionFieldsRepository;
|
use MailPoet\Newsletter\Options\NewsletterOptionFieldsRepository;
|
||||||
use MailPoet\Newsletter\Options\NewsletterOptionsRepository;
|
use MailPoet\Newsletter\Options\NewsletterOptionsRepository;
|
||||||
use MailPoet\Newsletter\Scheduler\Scheduler;
|
use MailPoet\Newsletter\Scheduler\Scheduler;
|
||||||
use MailPoet\Newsletter\Sending\ScheduledTasksRepository;
|
use MailPoet\Newsletter\Sending\ScheduledTasksRepository;
|
||||||
use MailPoet\Newsletter\Sending\SendingQueuesRepository;
|
use MailPoet\Newsletter\Sending\SendingQueuesRepository;
|
||||||
use MailPoet\Newsletter\Validator;
|
|
||||||
use MailPoet\Segments\SubscribersFinder;
|
use MailPoet\Segments\SubscribersFinder;
|
||||||
use MailPoet\Services\Bridge;
|
use MailPoet\Services\Bridge;
|
||||||
use MailPoet\Settings\SettingsController;
|
use MailPoet\Settings\SettingsController;
|
||||||
@ -86,7 +86,7 @@ class SendingQueueTest extends \MailPoetTest {
|
|||||||
$this->diContainer->get(ScheduledTasksRepository::class),
|
$this->diContainer->get(ScheduledTasksRepository::class),
|
||||||
$this->diContainer->get(MailerFactory::class),
|
$this->diContainer->get(MailerFactory::class),
|
||||||
$this->diContainer->get(Scheduler::class),
|
$this->diContainer->get(Scheduler::class),
|
||||||
$this->diContainer->get(Validator::class)
|
$this->diContainer->get(NewsletterValidator::class)
|
||||||
);
|
);
|
||||||
$res = $sendingQueue->add(['newsletter_id' => $this->newsletter->getId()]);
|
$res = $sendingQueue->add(['newsletter_id' => $this->newsletter->getId()]);
|
||||||
expect($res->status)->equals(APIResponse::STATUS_FORBIDDEN);
|
expect($res->status)->equals(APIResponse::STATUS_FORBIDDEN);
|
||||||
@ -169,7 +169,7 @@ class SendingQueueTest extends \MailPoetTest {
|
|||||||
$this->diContainer->get(ScheduledTasksRepository::class),
|
$this->diContainer->get(ScheduledTasksRepository::class),
|
||||||
$this->diContainer->get(MailerFactory::class),
|
$this->diContainer->get(MailerFactory::class),
|
||||||
$this->diContainer->get(Scheduler::class),
|
$this->diContainer->get(Scheduler::class),
|
||||||
new Validator(Stub::make(Bridge::class, [
|
new NewsletterValidator(Stub::make(Bridge::class, [
|
||||||
'isMailpoetSendingServiceEnabled' => true,
|
'isMailpoetSendingServiceEnabled' => true,
|
||||||
]), $this->diContainer->get(TrackingConfig::class))
|
]), $this->diContainer->get(TrackingConfig::class))
|
||||||
);
|
);
|
||||||
|
@ -0,0 +1,109 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace MailPoet\Test\Newsletter;
|
||||||
|
|
||||||
|
use Codeception\Util\Stub;
|
||||||
|
use MailPoet\Newsletter\NewsletterValidator;
|
||||||
|
use MailPoet\Services\Bridge;
|
||||||
|
use MailPoet\Settings\TrackingConfig;
|
||||||
|
use MailPoet\Test\DataFactories\Newsletter;
|
||||||
|
|
||||||
|
class NewsletterValidatorTest extends \MailPoetTest {
|
||||||
|
/** @var NewsletterValidator */
|
||||||
|
private $newsletterValidator;
|
||||||
|
|
||||||
|
public function _before() {
|
||||||
|
parent::_before();
|
||||||
|
$this->newsletterValidator = $this->diContainer->get(NewsletterValidator::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testUnsubscribeFooterIsNotRequiredIfNotUsingMSS() {
|
||||||
|
$newsletter = (new Newsletter())->loadBodyFrom('newsletterWithTextNoFooter.json')->create();
|
||||||
|
$validationError = $this->newsletterValidator->validate($newsletter);
|
||||||
|
expect($validationError)->null();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testUnsubscribeFooterRequiredIfUsingMSS() {
|
||||||
|
$newsletter = (new Newsletter())->loadBodyFrom('newsletterWithTextNoFooter.json')->create();
|
||||||
|
$bridge = Stub::make(Bridge::class, ['isMailpoetSendingServiceEnabled' => true]);
|
||||||
|
$validator = $this->getServiceWithOverrides(NewsletterValidator::class, ['bridge' => $bridge]);
|
||||||
|
$validationError = $validator->validate($newsletter);
|
||||||
|
expect($validationError)->equals('All emails must include an "Unsubscribe" link. Add a footer widget to your email to continue.');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testItRequiresBodyContent() {
|
||||||
|
$newsletter = (new Newsletter())->withBody('')->create();
|
||||||
|
$validationError = $this->newsletterValidator->validate($newsletter);
|
||||||
|
expect($validationError)->equals('Poet, please add prose to your masterpiece before you send it to your followers.');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testItRequiresContentBlocks() {
|
||||||
|
$newsletter = (new Newsletter())->withBody(['content' => ['type' => 'container', 'columnLayout' => false, 'orientation' => 'vertical', 'blocks' => []]])->create();
|
||||||
|
$validationError = $this->newsletterValidator->validate($newsletter);
|
||||||
|
expect($validationError)->equals('Poet, please add prose to your masterpiece before you send it to your followers.');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testItIsValidWithAContentBlock() {
|
||||||
|
$newsletter = (new Newsletter())->withBody(['content' => ['type' => 'container', 'columnLayout' => false, 'orientation' => 'vertical', 'blocks' => [
|
||||||
|
[
|
||||||
|
'type' => 'text',
|
||||||
|
'text' => 'Some text'
|
||||||
|
]
|
||||||
|
]]])->create();
|
||||||
|
$validationError = $this->newsletterValidator->validate($newsletter);
|
||||||
|
expect($validationError)->null();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public function testItRequiresReengagementShortcodes() {
|
||||||
|
$newsletter = (new Newsletter())->withReengagementType()->withDefaultBody()->create();
|
||||||
|
$validationError = $this->newsletterValidator->validate($newsletter);
|
||||||
|
expect($validationError)->equals('A re-engagement email must include a link with [link:subscription_re_engage_url] shortcode.');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testReengagementNewsletterIsValidWithRequiredShortcode() {
|
||||||
|
$newsletter = (new Newsletter())->withReengagementType()->withBody([
|
||||||
|
'content' => [
|
||||||
|
'blocks' => [
|
||||||
|
[
|
||||||
|
'type' => 'text',
|
||||||
|
'text' => '[link:subscription_re_engage_url]',
|
||||||
|
]
|
||||||
|
]
|
||||||
|
]
|
||||||
|
])->create();
|
||||||
|
$validationError = $this->newsletterValidator->validate($newsletter);
|
||||||
|
expect($validationError)->null();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testItRequiresTrackingForReengagementEmails() {
|
||||||
|
$newsletter = (new Newsletter())->withReengagementType()->withBody([
|
||||||
|
'content' => [
|
||||||
|
'blocks' => [
|
||||||
|
[
|
||||||
|
'type' => 'text',
|
||||||
|
'text' => '[link:subscription_re_engage_url]',
|
||||||
|
]
|
||||||
|
]
|
||||||
|
]
|
||||||
|
])->create();
|
||||||
|
$validator = $this->getServiceWithOverrides(NewsletterValidator::class, [
|
||||||
|
'trackingConfig' => Stub::make(TrackingConfig::class, ['isEmailTrackingEnabled' => false])
|
||||||
|
]);
|
||||||
|
$validationError = $validator->validate($newsletter);
|
||||||
|
expect($validationError)->equals('Re-engagement emails are disabled because open and click tracking is disabled in MailPoet → Settings → Advanced.');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testAlcEmailFailsValidationWithoutAlcBlock() {
|
||||||
|
$newsletter = (new Newsletter())->withDefaultBody()->withPostNotificationsType()->create();
|
||||||
|
$validationError = $this->newsletterValidator->validate($newsletter);
|
||||||
|
expect($validationError)->equals('Please add an “Automatic Latest Content” widget to the email from the right sidebar.');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testAlcEmailPassesWithAlcBlock() {
|
||||||
|
$newsletter = (new Newsletter())->loadBodyFrom('newsletterWithALC.json')->withPostNotificationsType()->create();
|
||||||
|
$validationError = $this->newsletterValidator->validate($newsletter);
|
||||||
|
expect($validationError)->null();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -183,6 +183,29 @@ abstract class MailPoetTest extends \Codeception\TestCase\Test { // phpcs:ignore
|
|||||||
return $method->invokeArgs($object, $parameters);
|
return $method->invokeArgs($object, $parameters);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve a clone of a DI service with specific private/protected properties replaced
|
||||||
|
*
|
||||||
|
* @template T of object
|
||||||
|
* @param class-string<T> $id
|
||||||
|
* @param array<string, Object> $overrides
|
||||||
|
* string = protected/private property name
|
||||||
|
* Object = replacement for that property
|
||||||
|
* @return T
|
||||||
|
*/
|
||||||
|
public function getServiceWithOverrides(string $id, array $overrides) {
|
||||||
|
$instance = $this->diContainer->get($id);
|
||||||
|
$clone = clone $instance;
|
||||||
|
$reflection = new \ReflectionClass($clone);
|
||||||
|
foreach ($overrides as $propertyName => $override) {
|
||||||
|
$property = $reflection->getProperty($propertyName);
|
||||||
|
$property->setAccessible(true);
|
||||||
|
$property->setValue($clone, $override);
|
||||||
|
$property->setAccessible(false);
|
||||||
|
}
|
||||||
|
return $clone;
|
||||||
|
}
|
||||||
|
|
||||||
public static function markTestSkipped(string $message = ''): void {
|
public static function markTestSkipped(string $message = ''): void {
|
||||||
$branchName = getenv('CIRCLE_BRANCH');
|
$branchName = getenv('CIRCLE_BRANCH');
|
||||||
if ($branchName === 'master' || $branchName === 'release') {
|
if ($branchName === 'master' || $branchName === 'release') {
|
||||||
|
Reference in New Issue
Block a user