newsletterTask = new NewsletterTask(); $this->subscriber = (new SubscriberFactory())->create(); $this->newsletter = Newsletter::create(); $this->newsletter->type = NewsletterEntity::TYPE_STANDARD; $this->newsletter->status = NewsletterEntity::STATUS_ACTIVE; $this->newsletter->subject = Fixtures::get('newsletter_subject_template'); $this->newsletter->body = Fixtures::get('newsletter_body_template'); $this->newsletter->preheader = ''; $this->newsletter->save(); $this->parentNewsletter = Newsletter::create(); $this->parentNewsletter->type = NewsletterEntity::TYPE_STANDARD; $this->parentNewsletter->status = NewsletterEntity::STATUS_ACTIVE; $this->parentNewsletter->subject = 'parent newsletter'; $this->parentNewsletter->preheader = ''; $this->parentNewsletter->save(); $this->sendingTask = SendingTask::create(); $this->sendingTask->newsletter_id = $this->newsletter->id; $this->sendingTask->save(); $this->loggerFactory = LoggerFactory::getInstance(); $this->newslettersRepository = $this->diContainer->get(NewslettersRepository::class); $this->newsletterLinkRepository = $this->diContainer->get(NewsletterLinkRepository::class); $this->sendingQueuesRepository = $this->diContainer->get(SendingQueuesRepository::class); } public function testItConstructs() { expect($this->newsletterTask->trackingEnabled)->true(); } public function testItDoesNotGetNewsletterWhenStatusIsNotActiveOrSending() { // draft or any other status return false $newsletterEntity = $this->newslettersRepository->findOneById($this->newsletter->id); $this->assertInstanceOf(NewsletterEntity::class, $newsletterEntity); $newsletterEntity->setStatus(NewsletterEntity::STATUS_DRAFT); $this->newslettersRepository->persist($newsletterEntity); $this->newslettersRepository->flush(); expect($this->newsletterTask->getNewsletterFromQueue($this->sendingTask))->null(); // active or sending statuses return newsletter $newsletterEntity->setStatus(NewsletterEntity::STATUS_ACTIVE); $this->newslettersRepository->persist($newsletterEntity); $this->newslettersRepository->flush(); expect($this->newsletterTask->getNewsletterFromQueue($this->sendingTask))->isInstanceOf(NewsletterEntity::class); $newsletterEntity->setStatus(NewsletterEntity::STATUS_SENDING); $this->newslettersRepository->persist($newsletterEntity); $this->newslettersRepository->flush(); expect($this->newsletterTask->getNewsletterFromQueue($this->sendingTask))->isInstanceOf(NewsletterEntity::class); } public function testItDoesNotGetDeletedNewsletter() { $newsletter = $this->newsletter; $newsletter->set_expr('deleted_at', 'NOW()'); $newsletter->save(); expect($this->newsletterTask->getNewsletterFromQueue($this->sendingTask))->null(); } public function testItDoesNotGetNewsletterWhenParentNewsletterStatusIsNotActiveOrSending() { // draft or any other status return false $parentNewsletterEntity = $this->newslettersRepository->findOneById($this->parentNewsletter->id); $this->assertInstanceOf(NewsletterEntity::class, $parentNewsletterEntity); $parentNewsletterEntity->setStatus( NewsletterEntity::STATUS_DRAFT); $this->newslettersRepository->persist($parentNewsletterEntity); $this->newslettersRepository->flush(); $newsletterEntity = $this->newslettersRepository->findOneById($this->newsletter->id); $this->assertInstanceOf(NewsletterEntity::class, $newsletterEntity); $newsletterEntity->setType(NewsletterEntity::TYPE_NOTIFICATION_HISTORY); $newsletterEntity->setParent($parentNewsletterEntity); $this->newslettersRepository->persist($newsletterEntity); $this->newslettersRepository->flush(); expect($this->newsletterTask->getNewsletterFromQueue($this->sendingTask))->null(); // active or sending statuses return newsletter $parentNewsletterEntity->setStatus(NewsletterEntity::STATUS_ACTIVE); $this->newslettersRepository->persist($parentNewsletterEntity); $this->newslettersRepository->flush(); expect($this->newsletterTask->getNewsletterFromQueue($this->sendingTask))->isInstanceOf(NewsletterEntity::class); $parentNewsletterEntity->setStatus(NewsletterEntity::STATUS_SENDING); $this->newslettersRepository->persist($parentNewsletterEntity); $this->newslettersRepository->flush(); expect($this->newsletterTask->getNewsletterFromQueue($this->sendingTask))->isInstanceOf(NewsletterEntity::class); } public function testItDoesNotGetDeletedNewsletterWhenParentNewsletterIsDeleted() { $parentNewsletter = $this->parentNewsletter; $parentNewsletter->set_expr('deleted_at', 'NOW()'); $parentNewsletter->save(); $newsletter = $this->newsletter; $newsletter->type = NewsletterEntity::TYPE_NOTIFICATION_HISTORY; $newsletter->parentId = $parentNewsletter->id; $newsletter->save(); expect($this->newsletterTask->getNewsletterFromQueue($this->sendingTask))->null(); } public function testItReturnsNewsletterObjectWhenRenderedNewsletterBodyExistsInTheQueue() { $this->sendingTask->newsletterRenderedBody = ['html' => 'test', 'text' => 'test']; $result = $this->newsletterTask->preProcessNewsletter($this->newsletter, $this->sendingTask); expect($result instanceof \MailPoet\Models\Newsletter)->true(); } public function testItHashesLinksAndInsertsTrackingImageWhenTrackingIsEnabled() { $wp = Stub::make(new WPFunctions, [ 'applyFilters' => asCallable([WPHooksHelper::class, 'applyFilters']), ]); $newsletterTask = new NewsletterTask($wp); $newsletterTask->trackingEnabled = true; $newsletterTask->preProcessNewsletter($this->newsletter, $this->sendingTask); $link = $this->newsletterLinkRepository->findOneBy(['newsletter' => $this->newsletter->id]); $this->assertInstanceOf(NewsletterLinkEntity::class, $link); $updatedQueue = SendingTask::getByNewsletterId($this->newsletter->id); $renderedNewsletter = $updatedQueue->getNewsletterRenderedBody(); expect($renderedNewsletter['html']) ->stringContainsString('[mailpoet_click_data]-' . $link->getHash()); expect($renderedNewsletter['html']) ->stringContainsString('[mailpoet_open_data]'); $hookName = 'mailpoet_sending_newsletter_render_after'; expect(WPHooksHelper::isFilterApplied($hookName))->true(); expect(WPHooksHelper::getFilterApplied($hookName)[0])->array(); expect(WPHooksHelper::getFilterApplied($hookName)[1] instanceof Newsletter)->true(); } public function testItDoesNotHashLinksAndInsertTrackingCodeWhenTrackingIsDisabled() { $wp = Stub::make(new WPFunctions, [ 'applyFilters' => asCallable([WPHooksHelper::class, 'applyFilters']), ]); $newsletterTask = new NewsletterTask($wp); $newsletterTask->trackingEnabled = false; $newsletterTask->preProcessNewsletter($this->newsletter, $this->sendingTask); $link = $this->newsletterLinkRepository->findOneBy(['newsletter' => $this->newsletter->id]); expect($link)->null(); $updatedQueue = SendingTask::getByNewsletterId($this->newsletter->id); $renderedNewsletter = $updatedQueue->getNewsletterRenderedBody(); expect($renderedNewsletter['html']) ->stringNotContainsString('[mailpoet_click_data]'); expect($renderedNewsletter['html']) ->stringNotContainsString('[mailpoet_open_data]'); $hookName = 'mailpoet_sending_newsletter_render_after'; expect(WPHooksHelper::isFilterApplied($hookName))->true(); expect(WPHooksHelper::getFilterApplied($hookName)[0])->array(); expect(WPHooksHelper::getFilterApplied($hookName)[1] instanceof Newsletter)->true(); } public function testItReturnsFalseAndDeletesNewsletterWhenPostNotificationContainsNoPosts() { $newsletter = $this->newsletter; $newsletter->type = NewsletterEntity::TYPE_NOTIFICATION_HISTORY; $newsletter->parentId = $newsletter->id; // replace post id data tag with something else $newsletter->body = str_replace('data-post-id', 'id', $newsletter->getBodyString()); $newsletter->save(); // returned result is false $result = $this->newsletterTask->preProcessNewsletter($this->newsletter, $this->sendingTask); expect($result)->false(); // newsletter is deleted. $this->entityManager->clear(); // needed while part of the code uses Paris models and part uses Doctrine $newsletter = $this->newslettersRepository->findOneById($newsletter->id); expect($newsletter)->null(); } public function testItSavesNewsletterPosts() { $newsletterPostRepository = ContainerWrapper::getInstance()->get(NewsletterPostsRepository::class); $this->newsletter->type = NewsletterEntity::TYPE_NOTIFICATION_HISTORY; $this->newsletter->parentId = $this->newsletter->id; $postsTask = $this->make(PostsTask::class, [ 'getAlcPostsCount' => 1, 'loggerFactory' => $this->loggerFactory, 'newsletterPostRepository' => $newsletterPostRepository, ]); $this->newsletter->save(); $newsletterTask = new NewsletterTask(new WPFunctions, $postsTask); $result = $newsletterTask->preProcessNewsletter($this->newsletter, $this->sendingTask); $newsletterPost = $newsletterPostRepository->findOneBy(['newsletter' => $this->newsletter->id]); expect($newsletterPost)->isInstanceOf(NewsletterPostEntity::class); expect($result)->notEquals(false); expect($newsletterPost->getPostId())->equals('10'); } public function testItUpdatesStatusAndSetsSentAtDateOnlyForStandardAndPostNotificationNewsletters() { $newsletter = $this->newslettersRepository->findOneById($this->newsletter->id); $this->assertInstanceOf(NewsletterEntity::class, $newsletter); $sendingQueue = $this->sendingTask->queue(); $sendingQueue->processedAt = date('Y-m-d H:i:s'); // newsletter type is 'standard' $newsletter->setType(NewsletterEntity::TYPE_STANDARD); $newsletter->setStatus('not_sent'); $this->newslettersRepository->persist($newsletter); $this->newslettersRepository->flush(); $this->newsletterTask->markNewsletterAsSent($newsletter, $this->sendingTask); $updatedNewsletter = $this->newslettersRepository->findOneById($newsletter->getId()); $this->assertInstanceOf(NewsletterEntity::class, $updatedNewsletter); expect($updatedNewsletter->getStatus())->equals(NewsletterEntity::STATUS_SENT); $sentAt = $updatedNewsletter->getSentAt(); $this->assertInstanceOf(\DateTime::class, $sentAt); expect($sentAt->format('Y-m-d H:i:s'))->equals($sendingQueue->processedAt); // newsletter type is 'notification history' $newsletter->setType(NewsletterEntity::TYPE_NOTIFICATION_HISTORY); $newsletter->setStatus('not_sent'); $this->newslettersRepository->persist($newsletter); $this->newslettersRepository->flush(); $this->newsletterTask->markNewsletterAsSent($newsletter, $this->sendingTask); $updatedNewsletter = $this->newslettersRepository->findOneById($newsletter->getId()); $this->assertInstanceOf(NewsletterEntity::class, $updatedNewsletter); expect($updatedNewsletter->getStatus())->equals(NewsletterEntity::STATUS_SENT); $sentAt = $updatedNewsletter->getSentAt(); $this->assertInstanceOf(\DateTime::class, $sentAt); expect($sentAt->format('Y-m-d H:i:s'))->equals($sendingQueue->processedAt); // all other newsletter types $newsletter->setType(NewsletterEntity::TYPE_WELCOME); $newsletter->setStatus('not_sent'); $this->newslettersRepository->persist($newsletter); $this->newslettersRepository->flush(); $this->newsletterTask->markNewsletterAsSent($newsletter, $this->sendingTask); $updatedNewsletter = $this->newslettersRepository->findOneById($newsletter->getId()); $this->assertInstanceOf(NewsletterEntity::class, $updatedNewsletter); expect($updatedNewsletter->getStatus())->notEquals(NewsletterEntity::STATUS_SENT); } public function testItDoesNotRenderSubscriberShortcodeInSubjectWhenPreprocessingNewsletter() { $newsletter = $this->newsletter; $newsletter->subject = 'Newsletter for [subscriber:firstname] [date:dordinal]'; $newsletter = $this->newsletterTask->preProcessNewsletter($newsletter, $this->sendingTask); $this->sendingTask = SendingTask::getByNewsletterId($newsletter->id); $wp = new WPFunctions(); expect($this->sendingTask->newsletterRenderedSubject) ->stringContainsString(date_i18n('jS', $wp->currentTime('timestamp'))); } public function testItUsesADefaultSubjectIfRenderedSubjectIsEmptyWhenPreprocessingNewsletter() { $newsletter = $this->newsletter; $newsletter->subject = ' [custom_shortcode:should_render_empty] '; $newsletter = $this->newsletterTask->preProcessNewsletter($newsletter, $this->sendingTask); $this->sendingTask = SendingTask::getByNewsletterId($newsletter->id); expect($this->sendingTask->newsletterRenderedSubject) ->equals('No subject'); } public function testItUsesRenderedNewsletterBodyAndSubjectFromQueueObjectWhenPreparingNewsletterForSending() { $newsletterEntity = $this->newslettersRepository->findOneById($this->newsletter->id); $this->assertInstanceOf(NewsletterEntity::class, $newsletterEntity); $this->sendingTask->newsletterRenderedBody = [ 'html' => 'queue HTML body', 'text' => 'queue TEXT body', ]; $this->sendingTask->newsletterRenderedSubject = 'queue subject'; $emoji = $this->make( Emoji::class, ['decodeEmojisInBody' => Expected::once(function ($params) { return $params; })] ); $newsletterTask = new NewsletterTask(null, null, null, $emoji); $result = $newsletterTask->prepareNewsletterForSending( $newsletterEntity, $this->subscriber, $this->sendingTask ); expect($result['subject'])->equals('queue subject'); expect($result['body']['html'])->equals('queue HTML body'); expect($result['body']['text'])->equals('queue TEXT body'); } public function testItRendersShortcodesAndReplacesSubscriberDataInLinks() { $newsletter = $this->newsletterTask->preProcessNewsletter($this->newsletter, $this->sendingTask); $newsletterEntity = $this->newslettersRepository->findOneById($newsletter->id); $this->assertInstanceOf(NewsletterEntity::class, $newsletterEntity); $result = $this->newsletterTask->prepareNewsletterForSending( $newsletterEntity, $this->subscriber, $this->sendingTask ); expect($result['subject'])->stringContainsString($this->subscriber->getFirstName()); expect($result['body']['html']) ->stringContainsString(Router::NAME . '&endpoint=track&action=click&data='); expect($result['body']['text']) ->stringContainsString(Router::NAME . '&endpoint=track&action=click&data='); } public function testItDoesNotReplaceSubscriberDataInLinksWhenTrackingIsNotEnabled() { $newsletterTask = $this->newsletterTask; $newsletterTask->trackingEnabled = false; $newsletter = $newsletterTask->preProcessNewsletter($this->newsletter, $this->sendingTask); $newsletterEntity = $this->newslettersRepository->findOneById($newsletter->id); $this->assertInstanceOf(NewsletterEntity::class, $newsletterEntity); $result = $newsletterTask->prepareNewsletterForSending( $newsletterEntity, $this->subscriber, $this->sendingTask ); expect($result['body']['html']) ->stringNotContainsString(Router::NAME . '&endpoint=track&action=click&data='); expect($result['body']['text']) ->stringNotContainsString(Router::NAME . '&endpoint=track&action=click&data='); } public function testItGetsSegments() { $newsletterEntity = $this->newslettersRepository->findOneById($this->newsletter->id); $this->assertInstanceOf(NewsletterEntity::class, $newsletterEntity); $segmentIds = []; for ($i = 1; $i <= 3; $i++) { $segment = (new SegmentFactory())->create(); $segmentIds[] = $segment->getId(); $newsletterSegment = new NewsletterSegmentEntity($newsletterEntity, $segment); $this->entityManager->persist($newsletterSegment); } $this->entityManager->flush(); expect($this->newsletterTask->getNewsletterSegments($newsletterEntity))->equals( $segmentIds ); } public function testItLogsErrorWhenQueueWithCannotBeSaved() { $this->sendingTask->nonExistentColumn = true; // this will trigger save error try { $this->newsletterTask->preProcessNewsletter($this->newsletter, $this->sendingTask); self::fail('Sending error exception was not thrown.'); } catch (\Exception $e) { $mailerLog = MailerLog::getMailerLog(); expect(is_array($mailerLog['error'])); if (is_array($mailerLog['error'])) { expect($mailerLog['error']['operation'])->equals('queue_save'); expect($mailerLog['error']['error_message'])->equals('There was an error processing your newsletter during sending. If possible, please contact us and report this issue.'); } } } public function testItLogsErrorWhenExistingRenderedNewsletterBodyIsInvalid() { $sendingTaskMock = $this->createMock(SendingTask::class); $sendingTaskMock ->expects($this->any()) ->method('__call') ->with('getNewsletterRenderedBody') ->willReturn('a:2:{s:4:"html"'); $sendingTaskMock ->expects($this->once()) ->method('validate') ->willReturn(false); try { $this->newsletterTask->preProcessNewsletter($this->newsletter, $sendingTaskMock); self::fail('Sending error exception was not thrown.'); } catch (\Exception $e) { $mailerLog = MailerLog::getMailerLog(); expect(is_array($mailerLog['error'])); if (is_array($mailerLog['error'])) { expect($mailerLog['error']['operation'])->equals('queue_save'); expect($mailerLog['error']['error_message'])->equals('There was an error processing your newsletter during sending. If possible, please contact us and report this issue.'); } } } public function testItLogsErrorWhenNewlyRenderedNewsletterBodyIsInvalid() { $sendingTaskMock = $this->createMock(SendingTask::class); $sendingTaskMock ->expects($this->any()) ->method('__call') ->with('getNewsletterRenderedBody') ->willReturn(null); $sendingTaskMock ->expects($this->once()) ->method('save'); $sendingTaskMock ->expects($this->once()) ->method('getErrors') ->willReturn([]); $sendingTaskMock ->expects($this->any()) ->method('__get') ->will($this->onConsecutiveCalls($this->sendingTask->id, $this->sendingTask->taskId, $this->sendingTask->id)); $sendingQueuesTable = $this->entityManager->getClassMetadata(SendingQueueEntity::class)->getTableName(); $conn = $this->entityManager->getConnection(); $stmt = $conn->prepare("UPDATE $sendingQueuesTable SET newsletter_rendered_body = :invalid_body WHERE id = :id"); $stmt->executeQuery([ 'invalid_body' => 'a:2:{s:4:"html"', 'id' => $this->sendingTask->id, ]); try { $this->newsletterTask->preProcessNewsletter($this->newsletter, $sendingTaskMock); self::fail('Sending error exception was not thrown.'); } catch (\Exception $e) { $mailerLog = MailerLog::getMailerLog(); expect(is_array($mailerLog['error'])); if (is_array($mailerLog['error'])) { expect($mailerLog['error']['operation'])->equals('queue_save'); expect($mailerLog['error']['error_message'])->equals('There was an error processing your newsletter during sending. If possible, please contact us and report this issue.'); } } } public function testItPreProcessesNewsletterWhenNewlyRenderedNewsletterBodyIsValid() { $sendingTaskMock = $this->createMock(SendingTask::class); $sendingTaskMock ->expects($this->any()) ->method('__call') ->with('getNewsletterRenderedBody') ->willReturn(null); $sendingTaskMock ->expects($this->once()) ->method('save'); $sendingTaskMock ->expects($this->once()) ->method('getErrors') ->willReturn([]); $sendingTaskMock ->expects($this->any()) ->method('__get') ->will($this->onConsecutiveCalls($this->sendingTask->id, $this->sendingTask->taskId, $this->sendingTask->id, $this->sendingTask->newsletterRenderedBody)); // properly serialized object $sendingQueue = $this->sendingQueuesRepository->findOneById($this->sendingTask->id); $this->assertInstanceOf(SendingQueueEntity::class, $sendingQueue); $sendingQueue->setNewsletterRenderedBody(['html' => 'test', 'text' => 'test']); $this->sendingQueuesRepository->persist($sendingQueue); $this->sendingQueuesRepository->flush(); $emoji = $this->make( Emoji::class, ['encodeEmojisInBody' => Expected::once(function ($params) { return $params; })] ); $newsletterTask = new NewsletterTask(null, null, null, $emoji); expect($newsletterTask->preProcessNewsletter($this->newsletter, $sendingTaskMock))->equals($this->newsletter); } public function _after() { $this->diContainer->get(SettingsRepository::class)->truncate(); $this->truncateEntity(SubscriberEntity::class); $this->truncateEntity(NewsletterEntity::class); $this->truncateEntity(ScheduledTaskEntity::class); $this->truncateEntity(SendingQueueEntity::class); $this->truncateEntity(NewsletterLinkEntity::class); $this->truncateEntity(NewsletterPostEntity::class); } }