Initial commit: CloudOps infrastructure platform
This commit is contained in:
@@ -0,0 +1,87 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Mautic\EmailBundle\Tests\Functional;
|
||||
|
||||
use Mautic\CoreBundle\Test\MauticMysqlTestCase;
|
||||
use Mautic\EmailBundle\Entity\Stat;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
|
||||
final class BotRatioHelperFunctionalTest extends MauticMysqlTestCase
|
||||
{
|
||||
private const DO_NOT_TRACK_IP = '218.30.65.10';
|
||||
|
||||
private const BOT_BLOCKED_IP = '218.30.65.11';
|
||||
|
||||
private const IP_NOT_IN_ANY_BLOCK_LIST = '218.30.65.12';
|
||||
|
||||
private const IP_NOT_IN_ANY_BLOCK_LIST2 = '218.30.65.111';
|
||||
|
||||
private const BOT_BLOCKED_USER_AGENTS = [
|
||||
'AHC/2.1',
|
||||
'Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.0.6) Gecko/2009011913 Firefox/3.0.6',
|
||||
'Mozilla/5.0 (compatible; Codewisebot/2.0; +http://www.nosite.com/somebot.htm)',
|
||||
'Mozilla/5.0 (iPhone; CPU iPhone OS 8_1 like Mac OS X) AppleWebKit/600.1.4 (KHTML, like Gecko) Version/8.0 Mobile/12B411 Safari/600.1.4 (compatible; YandexMobileBot/3.0; +http://yandex.com/bots)',
|
||||
'serpstatbot/2.0 beta (advanced backlink tracking bot; http://serpstatbot.com/; abuse@serpstatbot.com)',
|
||||
'Mozilla/5.0 (Linux; Android 7.0;) AppleWebKit/537.36 (KHTML, like Gecko) Mobile Safari/537.36 (compatible; AspiegelBot)',
|
||||
'serpstatbot/2.1 (advanced backlink tracking bot; https://serpstatbot.com/; abuse@serpstatbot.com)',
|
||||
];
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
$this->configParams['do_not_track_ips'] = [self::DO_NOT_TRACK_IP];
|
||||
$this->configParams['bot_helper_blocked_ip_addresses'] = [self::BOT_BLOCKED_IP];
|
||||
$this->configParams['bot_helper_blocked_user_agents'] = self::BOT_BLOCKED_USER_AGENTS;
|
||||
parent::setUp();
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws \Doctrine\ORM\ORMException
|
||||
*/
|
||||
#[\PHPUnit\Framework\Attributes\DataProvider('hitBotScenariosProvider')]
|
||||
public function testIsHitByBotFunctional(string $trackingHash, string $sentBefore, string $userAgent, string $ipAddress, bool $isRead): void
|
||||
{
|
||||
$stat = new Stat();
|
||||
$emailSendTime = new \DateTime();
|
||||
$stat->setDateSent($emailSendTime->modify($sentBefore));
|
||||
$stat->setTrackingHash($trackingHash);
|
||||
$stat->setEmailAddress('lukas@mautic.test');
|
||||
$this->em->persist($stat);
|
||||
$this->em->flush();
|
||||
$statId = $stat->getId();
|
||||
|
||||
$server = [
|
||||
'HTTP_USER_AGENT' => $userAgent,
|
||||
'REMOTE_ADDR' => $ipAddress,
|
||||
];
|
||||
$this->client->request(Request::METHOD_GET, '/email/'.$stat->getTrackingHash().'.gif', [], [], $server);
|
||||
$this->assertEquals(200, $this->client->getResponse()->getStatusCode());
|
||||
|
||||
$updatedStat = $this->em->getRepository(Stat::class)->findOneBy(['id'=>$statId]);
|
||||
$this->assertSame($isRead, $updatedStat->getIsRead());
|
||||
if ($isRead) {
|
||||
$this->assertNotNull($updatedStat->getLastOpened());
|
||||
} else {
|
||||
$this->assertNull($updatedStat->getLastOpened());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return iterable<string, array<mixed>>
|
||||
*/
|
||||
public static function hitBotScenariosProvider(): iterable
|
||||
{
|
||||
// $trackingHash, $sentBefore, $userAgent, $ipAddress, $isRead
|
||||
yield 'All good' => ['test_hash_bot_ratio_1', '-80 second', 'Mozilla/5.0', self::IP_NOT_IN_ANY_BLOCK_LIST, true];
|
||||
yield 'Time and User' => ['test_hash_bot_ratio_2', '+80 second', 'AHC/2.1', self::IP_NOT_IN_ANY_BLOCK_LIST, false];
|
||||
yield 'Time and IP' => ['test_hash_bot_ratio_3', '+80 second', 'Mozilla/5.0', self::BOT_BLOCKED_IP, false];
|
||||
yield 'Permanently blocked IP' => ['test_hash_bot_ratio_4', '-80 second', 'Mozilla/5.0', self::DO_NOT_TRACK_IP, false];
|
||||
yield 'Bot Blocked IP address only' => ['test_hash_bot_ratio_5', '-80 second', 'Mozilla/5.0', self::BOT_BLOCKED_IP, true];
|
||||
yield 'Bot Blocked User Agent only' => ['test_hash_bot_ratio_6', '-80 second', 'AHC/2.1', self::IP_NOT_IN_ANY_BLOCK_LIST, true];
|
||||
yield 'Time Only' => ['test_hash_bot_ratio_7', '+80 second', 'Mozilla/5.0', self::IP_NOT_IN_ANY_BLOCK_LIST, true];
|
||||
yield 'Time and Bot User Agent and Bot IP' => ['test_hash_bot_ratio_8', '+80 second', 'AHC/2.1', self::BOT_BLOCKED_IP, false];
|
||||
yield 'Bot User Agent and Bot IP' => ['test_hash_bot_ratio_9', '-80 second', 'AHC/2.1', self::BOT_BLOCKED_IP, false];
|
||||
yield 'Permanently blocked User Agent' => ['test_hash_bot_ratio_10', '-80 second', 'MSNBOT', self::IP_NOT_IN_ANY_BLOCK_LIST2, false];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Mautic\EmailBundle\Tests\Functional;
|
||||
|
||||
use Mautic\CoreBundle\Test\MauticMysqlTestCase;
|
||||
use Mautic\EmailBundle\Entity\Email;
|
||||
use Mautic\EmailBundle\Entity\Stat;
|
||||
use Mautic\LeadBundle\Entity\Lead;
|
||||
use Mautic\PageBundle\Entity\Hit;
|
||||
use Mautic\PageBundle\Entity\HitRepository;
|
||||
use Mautic\PageBundle\Entity\Page;
|
||||
use PHPUnit\Framework\Assert;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
|
||||
final class EmailClickTrackingTest extends MauticMysqlTestCase
|
||||
{
|
||||
public function testEmailClick(): void
|
||||
{
|
||||
$contact = new Lead();
|
||||
$contact->setEmail('john@doe.cz');
|
||||
|
||||
$email = new Email();
|
||||
$email->setName('Test email');
|
||||
$email->setSubject('Test email');
|
||||
$email->setCustomHtml('<html><head></head><body>Test email</body></html>');
|
||||
|
||||
$stat = new Stat();
|
||||
$stat->setLead($contact);
|
||||
$stat->setEmail($email);
|
||||
$stat->setEmailAddress('john@doe.cz');
|
||||
$stat->setTrackingHash('67167f57a4c05265936091');
|
||||
$stat->setDateSent(new \DateTime());
|
||||
|
||||
$page = new Page();
|
||||
$page->setTitle('Test page');
|
||||
$page->setAlias('test-page');
|
||||
$page->setCustomHtml('<html><head></head><body>Test page</body></html>');
|
||||
|
||||
$this->em->persist($contact);
|
||||
$this->em->persist($email);
|
||||
$this->em->persist($stat);
|
||||
$this->em->persist($page);
|
||||
$this->em->flush();
|
||||
|
||||
$this->logoutUser();
|
||||
|
||||
$this->client->request(Request::METHOD_GET, '/test-page?&ct=YToxOntzOjQ6InN0YXQiO3M6MjI6IjY3MTY3ZjU3YTRjMDUyNjU5MzYwOTEiO30%3D');
|
||||
Assert::assertTrue($this->client->getResponse()->isSuccessful());
|
||||
|
||||
$pageHitRepository = $this->em->getRepository(Hit::class);
|
||||
\assert($pageHitRepository instanceof HitRepository);
|
||||
|
||||
$hit = $pageHitRepository->findOneBy(['page' => $page]);
|
||||
Assert::assertSame($contact->getId(), $hit->getLead()->getId());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,164 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Mautic\EmailBundle\Tests\Functional;
|
||||
|
||||
use Doctrine\ORM\Exception\ORMException;
|
||||
use Doctrine\ORM\OptimisticLockException;
|
||||
use Doctrine\Persistence\Mapping\MappingException;
|
||||
use Mautic\CoreBundle\Test\MauticMysqlTestCase;
|
||||
use Mautic\CoreBundle\Tests\Functional\CreateTestEntitiesTrait;
|
||||
use Mautic\CoreBundle\Tests\Functional\UserEntityTrait;
|
||||
use Mautic\EmailBundle\Entity\Email;
|
||||
use Mautic\EmailBundle\Entity\Stat;
|
||||
use Mautic\LeadBundle\Entity\Lead;
|
||||
use Mautic\UserBundle\Entity\User;
|
||||
use Mautic\UserBundle\Entity\UserRepository;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
class EmailContactGridTest extends MauticMysqlTestCase
|
||||
{
|
||||
use CreateTestEntitiesTrait;
|
||||
use UserEntityTrait;
|
||||
|
||||
/**
|
||||
* @throws OptimisticLockException
|
||||
* @throws ORMException
|
||||
* @throws MappingException
|
||||
*/
|
||||
public function testEmailContactsGridWithValidPermissions(): void
|
||||
{
|
||||
list($email, $contactOne, $contactTwo) = $this->setupData();
|
||||
|
||||
// create users
|
||||
$nonAdminUser = $this->createUserWithPermission([
|
||||
'user-name' => 'non-admin',
|
||||
'email' => 'non-admin@mautic-test.com',
|
||||
'first-name' => 'non-admin',
|
||||
'last-name' => 'non-admin',
|
||||
'role' => [
|
||||
'name' => 'perm_non_admin',
|
||||
'permissions' => [
|
||||
'lead:leads' => 6,
|
||||
'email:emails' => 6,
|
||||
],
|
||||
],
|
||||
]);
|
||||
|
||||
$this->em->flush();
|
||||
$this->em->clear();
|
||||
|
||||
$this->loginOtherUser($nonAdminUser);
|
||||
|
||||
$this->client->request(Request::METHOD_GET, '/s/emails/view/'.$email->getId());
|
||||
$this->assertEquals(Response::HTTP_OK, $this->client->getResponse()->getStatusCode());
|
||||
|
||||
$content = $this->client->getResponse()->getContent();
|
||||
|
||||
$this->assertStringContainsString($contactOne->getName(), $content);
|
||||
$this->assertStringContainsString($contactTwo->getName(), $content);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws OptimisticLockException
|
||||
* @throws MappingException
|
||||
* @throws ORMException
|
||||
*/
|
||||
public function testEmailContactsGridWithIncompletePermissions(): void
|
||||
{
|
||||
/** @var Email $email */
|
||||
list($email, $contactOne, $contactTwo) = $this->setupData();
|
||||
|
||||
// create users
|
||||
$nonAdminUser = $this->createUserWithPermission([
|
||||
'user-name' => 'non-admin',
|
||||
'email' => 'non-admin@mautic-test.com',
|
||||
'first-name' => 'non-admin',
|
||||
'last-name' => 'non-admin',
|
||||
'role' => [
|
||||
'name' => 'perm_non_admin',
|
||||
'permissions' => [
|
||||
'lead:leads' => 2,
|
||||
'email:emails' => 6,
|
||||
],
|
||||
],
|
||||
]);
|
||||
|
||||
$email->setCreatedBy($nonAdminUser);
|
||||
$this->em->persist($email);
|
||||
|
||||
$this->em->flush();
|
||||
$this->em->clear();
|
||||
|
||||
$this->loginOtherUser($nonAdminUser);
|
||||
|
||||
$this->client->request(Request::METHOD_GET, '/s/emails/view/'.$email->getId());
|
||||
$this->assertEquals(Response::HTTP_OK, $this->client->getResponse()->getStatusCode());
|
||||
|
||||
$content = $this->client->getResponse()->getContent();
|
||||
|
||||
$this->assertStringContainsString('No Contacts Found', $content, $content);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws ORMException
|
||||
*/
|
||||
private function createStats(Email $email, Lead $contactOne): void
|
||||
{
|
||||
$emailStat = new Stat();
|
||||
$emailStat->setEmail($email);
|
||||
$emailStat->setLead($contactOne);
|
||||
$emailStat->setDateSent(new \DateTime());
|
||||
$emailStat->setEmailAddress($contactOne->getEmail());
|
||||
|
||||
$this->em->persist($emailStat);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, mixed> $userDetails
|
||||
*/
|
||||
private function createUserWithPermission(array $userDetails): User
|
||||
{
|
||||
$role = $this->createRole($userDetails['role']['name']);
|
||||
|
||||
foreach ($userDetails['role']['permissions'] as $permission => $bitwise) {
|
||||
$this->createPermission($role, $permission, $bitwise);
|
||||
}
|
||||
|
||||
return $this->createUser($userDetails['email'], $userDetails['user-name'], $userDetails['first-name'], $userDetails['last-name'], $role);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<mixed>
|
||||
*
|
||||
* @throws ORMException
|
||||
*/
|
||||
private function setupData(): array
|
||||
{
|
||||
/** @var UserRepository $userRepository */
|
||||
$userRepository = $this->em->getRepository(User::class);
|
||||
$adminUser = $userRepository->findOneBy(['username' => 'admin']);
|
||||
|
||||
$segment = $this->createSegment('SegmentOne', []);
|
||||
|
||||
$email = $this->createEmail('Hello');
|
||||
$email->setEmailType('list');
|
||||
$email->addList($segment);
|
||||
$email->setCustomHtml('<h1>Email content created by an API test</h1>{custom-token}<br>{signature}');
|
||||
$email->setIsPublished(true);
|
||||
|
||||
$this->em->persist($email);
|
||||
|
||||
// Create Contact
|
||||
$contactOne = $this->createLead('John', '', 'john@contact.email', $adminUser);
|
||||
$contactTwo = $this->createLead('Alex', '', 'alex@contact.email', $adminUser);
|
||||
|
||||
// Create stats
|
||||
$this->createStats($email, $contactOne);
|
||||
$this->createStats($email, $contactTwo);
|
||||
|
||||
return [$email, $contactOne, $contactTwo];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,313 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Mautic\EmailBundle\Tests\Functional;
|
||||
|
||||
use Mautic\CampaignBundle\Tests\Functional\Fixtures\FixtureHelper;
|
||||
use Mautic\CoreBundle\Test\MauticMysqlTestCase;
|
||||
use Mautic\EmailBundle\Tests\Functional\Fixtures\EmailFixturesHelper;
|
||||
use Mautic\FormBundle\Entity\Action;
|
||||
use Mautic\FormBundle\Entity\Form;
|
||||
use Mautic\LeadBundle\Entity\LeadList;
|
||||
use Mautic\PointBundle\Entity\Point;
|
||||
use Mautic\PointBundle\Entity\Trigger;
|
||||
use Mautic\PointBundle\Entity\TriggerEvent;
|
||||
use Mautic\ReportBundle\Entity\Report;
|
||||
|
||||
final class EmailDependenciesFunctionalTest extends MauticMysqlTestCase
|
||||
{
|
||||
private FixtureHelper $campaignFixturesHelper;
|
||||
private EmailFixturesHelper $emailFixturesHelper;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
$this->campaignFixturesHelper = new FixtureHelper($this->em);
|
||||
$this->emailFixturesHelper = new EmailFixturesHelper($this->em);
|
||||
}
|
||||
|
||||
public function testEmailUsageInSegments(): void
|
||||
{
|
||||
$email = $this->emailFixturesHelper->createEmail();
|
||||
$this->em->flush();
|
||||
|
||||
$segmentRead = $this->createSegment('read-email', [
|
||||
[
|
||||
'glue' => 'and',
|
||||
'field' => 'lead_email_received',
|
||||
'object' => 'behaviors',
|
||||
'type' => 'lead_email_received',
|
||||
'operator' => 'in',
|
||||
'properties' => [
|
||||
'filter' => [
|
||||
$email->getId(),
|
||||
],
|
||||
],
|
||||
],
|
||||
]);
|
||||
|
||||
$segmentSent = $this->createSegment('sent-email', [
|
||||
[
|
||||
'glue' => 'and',
|
||||
'field' => 'lead_email_sent',
|
||||
'object' => 'behaviors',
|
||||
'type' => 'lead_email_received', // it is saved like this
|
||||
'operator' => 'in',
|
||||
'properties' => [
|
||||
'filter' => [
|
||||
$email->getId(),
|
||||
],
|
||||
],
|
||||
],
|
||||
]);
|
||||
|
||||
$this->createSegment('other');
|
||||
|
||||
$this->em->persist($email);
|
||||
$this->em->flush();
|
||||
|
||||
$this->client->request('GET', "/s/ajax?action=email:getEmailUsages&id={$email->getId()}");
|
||||
$clientResponse = $this->client->getResponse();
|
||||
$jsonResponse = json_decode($clientResponse->getContent(), true);
|
||||
|
||||
$searchIds = join(',', [$segmentRead->getId(), $segmentSent->getId()]);
|
||||
$this->assertStringContainsString("/s/segments?search=ids:{$searchIds}", $jsonResponse['usagesHtml']);
|
||||
}
|
||||
|
||||
public function testEmailUsageInCampaigns(): void
|
||||
{
|
||||
$email = $this->emailFixturesHelper->createEmail();
|
||||
$this->em->flush();
|
||||
|
||||
$campaign = $this->campaignFixturesHelper->createCampaignWithEmailSent($email->getId());
|
||||
|
||||
$this->client->request('GET', "/s/ajax?action=email:getEmailUsages&id={$email->getId()}");
|
||||
$clientResponse = $this->client->getResponse();
|
||||
$jsonResponse = json_decode($clientResponse->getContent(), true);
|
||||
|
||||
$searchIds = join(',', [$campaign->getId()]);
|
||||
$this->assertStringContainsString("/s/campaigns?search=ids:{$searchIds}", $jsonResponse['usagesHtml']);
|
||||
}
|
||||
|
||||
public function testEmailUsageWithoutDuplicates(): void
|
||||
{
|
||||
$email = $this->emailFixturesHelper->createEmail();
|
||||
$this->em->flush();
|
||||
|
||||
$formWithEmailSend = $this->createForm('form-with-email-send');
|
||||
$this->createFormActionEmailSend($formWithEmailSend, $email->getId());
|
||||
$this->createFormActionEmailSendToUser($formWithEmailSend, $email->getId());
|
||||
|
||||
$this->client->request('GET', "/s/ajax?action=email:getEmailUsages&id={$email->getId()}");
|
||||
$clientResponse = $this->client->getResponse();
|
||||
$jsonResponse = json_decode($clientResponse->getContent(), true);
|
||||
|
||||
$formId = $formWithEmailSend->getId();
|
||||
$this->assertStringNotContainsString("/s/forms?search=ids:{$formId},{$formId}", $jsonResponse['usagesHtml']);
|
||||
}
|
||||
|
||||
public function testEmailUsageInForms(): void
|
||||
{
|
||||
$email = $this->emailFixturesHelper->createEmail();
|
||||
$this->em->flush();
|
||||
|
||||
$formWithEmailSend = $this->createForm('form-with-email-send');
|
||||
$this->createFormActionEmailSend($formWithEmailSend, $email->getId());
|
||||
|
||||
$formWithEmailSendToUser = $this->createForm('form-with-email-send-to-user');
|
||||
$this->createFormActionEmailSendToUser($formWithEmailSendToUser, $email->getId());
|
||||
|
||||
$this->client->request('GET', "/s/ajax?action=email:getEmailUsages&id={$email->getId()}");
|
||||
$clientResponse = $this->client->getResponse();
|
||||
$jsonResponse = json_decode($clientResponse->getContent(), true);
|
||||
|
||||
$searchIds = join(',', [$formWithEmailSend->getId(), $formWithEmailSendToUser->getId()]);
|
||||
$this->assertStringContainsString("/s/forms?search=ids:{$searchIds}", $jsonResponse['usagesHtml']);
|
||||
}
|
||||
|
||||
public function testEmailUsageInPointActions(): void
|
||||
{
|
||||
$email = $this->emailFixturesHelper->createEmail();
|
||||
$this->em->flush();
|
||||
|
||||
$pointActionIsSent = $this->createEmailPointAction($email->getId(), 'email.send');
|
||||
$pointActionIsOpen = $this->createEmailPointAction($email->getId(), 'email.open');
|
||||
|
||||
$this->client->request('GET', "/s/ajax?action=email:getEmailUsages&id={$email->getId()}");
|
||||
$clientResponse = $this->client->getResponse();
|
||||
$jsonResponse = json_decode($clientResponse->getContent(), true);
|
||||
|
||||
$searchIds = join(',', [$pointActionIsSent->getId(), $pointActionIsOpen->getId()]);
|
||||
$this->assertStringContainsString("/s/points?search=ids:{$searchIds}", $jsonResponse['usagesHtml']);
|
||||
}
|
||||
|
||||
public function testEmailUsageInPointTriggers(): void
|
||||
{
|
||||
$email = $this->emailFixturesHelper->createEmail();
|
||||
$this->em->flush();
|
||||
|
||||
$pointActionIsSent = $this->createPointTriggerWithEmailSendEvent($email->getId(), 'email.send');
|
||||
|
||||
$this->client->request('GET', "/s/ajax?action=email:getEmailUsages&id={$email->getId()}");
|
||||
$clientResponse = $this->client->getResponse();
|
||||
$jsonResponse = json_decode($clientResponse->getContent(), true);
|
||||
|
||||
$searchIds = join(',', [$pointActionIsSent->getId()]);
|
||||
$this->assertStringContainsString("/s/points/triggers?search=ids:{$searchIds}", $jsonResponse['usagesHtml']);
|
||||
}
|
||||
|
||||
public function testEmailUsageInReports(): void
|
||||
{
|
||||
$email = $this->emailFixturesHelper->createEmail();
|
||||
$this->em->flush();
|
||||
|
||||
$emailReport = $this->createEmailReport($email->getId());
|
||||
$emailStatsReport = $this->createEmailStatsReport($email->getId());
|
||||
|
||||
$this->client->request('GET', "/s/ajax?action=email:getEmailUsages&id={$email->getId()}");
|
||||
$clientResponse = $this->client->getResponse();
|
||||
$jsonResponse = json_decode($clientResponse->getContent(), true);
|
||||
|
||||
$searchIds = join(',', [$emailReport->getId(), $emailStatsReport->getId()]);
|
||||
$this->assertStringContainsString("/s/reports?search=ids:{$searchIds}", $jsonResponse['usagesHtml']);
|
||||
}
|
||||
|
||||
private function createEmailReport(int $emailId): Report
|
||||
{
|
||||
$report = new Report();
|
||||
$report->setName('Contact report');
|
||||
$report->setSource('emails');
|
||||
$report->setColumns([
|
||||
'e.id',
|
||||
'e.name',
|
||||
]);
|
||||
$report->setFilters([
|
||||
[
|
||||
'column' => 'e.id',
|
||||
'glue' => 'and',
|
||||
'dynamic' => null,
|
||||
'condition' => 'eq',
|
||||
'value' => $emailId,
|
||||
],
|
||||
]);
|
||||
$this->em->persist($report);
|
||||
$this->em->flush();
|
||||
|
||||
return $report;
|
||||
}
|
||||
|
||||
private function createEmailStatsReport(int $emailId): Report
|
||||
{
|
||||
$report = new Report();
|
||||
$report->setName('Contact report');
|
||||
$report->setSource('email.stats');
|
||||
$report->setColumns([
|
||||
'l.id',
|
||||
'es.date_read',
|
||||
'es.date_sent',
|
||||
'e.id',
|
||||
'e.name',
|
||||
]);
|
||||
$report->setFilters([
|
||||
[
|
||||
'column' => 'e.id',
|
||||
'glue' => 'and',
|
||||
'dynamic' => null,
|
||||
'condition' => 'eq',
|
||||
'value' => $emailId,
|
||||
],
|
||||
]);
|
||||
$this->em->persist($report);
|
||||
$this->em->flush();
|
||||
|
||||
return $report;
|
||||
}
|
||||
|
||||
private function createEmailPointAction(int $emailId, string $type): Point
|
||||
{
|
||||
$pointAction = new Point();
|
||||
$pointAction->setName('Is sent email');
|
||||
$pointAction->setDelta(1);
|
||||
$pointAction->setType($type);
|
||||
$pointAction->setProperties(['emails' => [$emailId]]);
|
||||
$this->em->persist($pointAction);
|
||||
$this->em->flush();
|
||||
|
||||
return $pointAction;
|
||||
}
|
||||
|
||||
private function createPointTriggerWithEmailSendEvent(int $emailId, string $type): Trigger
|
||||
{
|
||||
$pointTrigger = new Trigger();
|
||||
$pointTrigger->setName('trigger');
|
||||
$this->em->persist($pointTrigger);
|
||||
$this->em->flush();
|
||||
|
||||
$triggerEvent = new TriggerEvent();
|
||||
$triggerEvent->setTrigger($pointTrigger);
|
||||
$triggerEvent->setName('event');
|
||||
$triggerEvent->setType($type);
|
||||
$triggerEvent->setProperties(['email'=>$emailId]);
|
||||
$this->em->persist($triggerEvent);
|
||||
$this->em->flush();
|
||||
|
||||
return $pointTrigger;
|
||||
}
|
||||
|
||||
private function createForm(string $alias): Form
|
||||
{
|
||||
$form = new Form();
|
||||
$form->setName($alias);
|
||||
$form->setAlias($alias);
|
||||
$this->em->persist($form);
|
||||
$this->em->flush();
|
||||
|
||||
return $form;
|
||||
}
|
||||
|
||||
private function createFormActionEmailSend(Form $form, int $emailId): Action
|
||||
{
|
||||
$action = new Action();
|
||||
$action->setName('send email');
|
||||
$action->setForm($form);
|
||||
$action->setType('email.send.lead');
|
||||
$action->setProperties(['email'=> $emailId]);
|
||||
$this->em->persist($action);
|
||||
$this->em->flush();
|
||||
|
||||
return $action;
|
||||
}
|
||||
|
||||
private function createFormActionEmailSendToUser(Form $form, int $emailId): Action
|
||||
{
|
||||
$action = new Action();
|
||||
$action->setName('send email');
|
||||
$action->setForm($form);
|
||||
$action->setType('email.send.lead');
|
||||
$action->setProperties([
|
||||
'useremail' => ['email' => $emailId],
|
||||
'user_id' => [1],
|
||||
]);
|
||||
$this->em->persist($action);
|
||||
$this->em->flush();
|
||||
|
||||
return $action;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<int, array<string, mixed>> $filters
|
||||
*/
|
||||
private function createSegment(string $alias, array $filters = []): LeadList
|
||||
{
|
||||
$segment = new LeadList();
|
||||
$segment->setName($alias);
|
||||
$segment->setPublicName($alias);
|
||||
$segment->setAlias($alias);
|
||||
$segment->setFilters($filters);
|
||||
$this->em->persist($segment);
|
||||
$this->em->flush();
|
||||
|
||||
return $segment;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,212 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Mautic\EmailBundle\Tests\Functional;
|
||||
|
||||
use Mautic\CoreBundle\Test\MauticMysqlTestCase;
|
||||
use Mautic\EmailBundle\Entity\Email;
|
||||
use Mautic\EmailBundle\Entity\Stat;
|
||||
use Mautic\EmailBundle\Entity\StatRepository;
|
||||
use Mautic\EmailBundle\Model\EmailModel;
|
||||
use Mautic\LeadBundle\Entity\Lead;
|
||||
use Mautic\LeadBundle\Entity\LeadField;
|
||||
use PHPUnit\Framework\Assert;
|
||||
use Symfony\Component\DomCrawler\Crawler;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
|
||||
class EmailTokenTest extends MauticMysqlTestCase
|
||||
{
|
||||
protected $useCleanupRollback = false;
|
||||
|
||||
public function testEmailTokens(): void
|
||||
{
|
||||
$lead = $this->createLeadWithAllFields();
|
||||
|
||||
$email = new Email();
|
||||
$email->setEmailType('list');
|
||||
$email->setName('CO token test email');
|
||||
$email->setSubject('CO token test email');
|
||||
$email->setCustomHtml('
|
||||
|
||||
Dear %7Bcontactfield=firstname%7D {contactfield=lastname},
|
||||
|
||||
Check these fields:
|
||||
Mobile: %7Bcontactfield%3Dmobile%7D
|
||||
Address: {contactfield=address1}, {contactfield=address2}, {contactfield=city}, {contactfield=country}
|
||||
Email: {contactfield=email}
|
||||
|
||||
Custom Values:
|
||||
|
||||
Contact:
|
||||
Bool: {contactfield=boollead},
|
||||
Date: {contactfield=datelead},
|
||||
Date/Time: {contactfield=datetimelead}
|
||||
Email: {contactfield=emaillead}
|
||||
HTML: {contactfield=htmllead}
|
||||
Country: {contactfield=countrylead}
|
||||
Locale: {contactfield=localelead}
|
||||
Number: {contactfield=numberlead}
|
||||
Phone: {contactfield=phonelead}
|
||||
Region: {contactfield=regionlead}
|
||||
Text: {contactfield=textlead}
|
||||
Textarea: {contactfield=textarealead}
|
||||
Time: {contactfield=timelead}
|
||||
Timezone: {contactfield=timezonelead}
|
||||
URL: {contactfield=urllead}
|
||||
');
|
||||
|
||||
$this->em->persist($email);
|
||||
$this->em->flush();
|
||||
|
||||
/** @var EmailModel $emailModel */
|
||||
$emailModel = self::getContainer()->get('mautic.email.model.email');
|
||||
$emailModel->sendEmail(
|
||||
$email,
|
||||
[
|
||||
[
|
||||
'id' => $lead->getId(),
|
||||
'email' => $lead->getEmail(),
|
||||
'firstname' => $lead->getFirstname(),
|
||||
'lastname' => $lead->getLastname(),
|
||||
'mobile' => $lead->getMobile(),
|
||||
'address1' => $lead->getAddress1(),
|
||||
'address2' => $lead->getAddress2(),
|
||||
'city' => $lead->getCity(),
|
||||
'country' => $lead->getCountry(),
|
||||
'boollead' => $lead->getUpdatedFields()['boollead'],
|
||||
'datelead' => $lead->getUpdatedFields()['datelead'],
|
||||
'datetimelead' => $lead->getUpdatedFields()['datetimelead'],
|
||||
'emaillead' => $lead->getUpdatedFields()['emaillead'],
|
||||
'htmllead' => $lead->getUpdatedFields()['htmllead'],
|
||||
'countrylead' => $lead->getUpdatedFields()['countrylead'],
|
||||
'localelead' => $lead->getUpdatedFields()['localelead'],
|
||||
'numberlead' => $lead->getUpdatedFields()['numberlead'],
|
||||
'phonelead' => $lead->getUpdatedFields()['phonelead'],
|
||||
'regionlead' => $lead->getUpdatedFields()['regionlead'],
|
||||
'textlead' => $lead->getUpdatedFields()['textlead'],
|
||||
'textarealead' => $lead->getUpdatedFields()['textarealead'],
|
||||
'timelead' => $lead->getUpdatedFields()['timelead'],
|
||||
'timezonelead' => $lead->getUpdatedFields()['timezonelead'],
|
||||
'urllead' => $lead->getUpdatedFields()['urllead'],
|
||||
],
|
||||
]
|
||||
);
|
||||
|
||||
/** @var StatRepository $emailStatRepository */
|
||||
$emailStatRepository = $this->em->getRepository(Stat::class);
|
||||
|
||||
/** @var Stat|null $emailStat */
|
||||
$emailStat = $emailStatRepository->findOneBy(
|
||||
[
|
||||
'email' => $email->getId(),
|
||||
'lead' => $lead->getId(),
|
||||
]
|
||||
);
|
||||
|
||||
Assert::assertNotNull($emailStat);
|
||||
|
||||
$crawler = $this->client->request(Request::METHOD_GET, "/email/view/{$emailStat->getTrackingHash()}");
|
||||
|
||||
$body = $crawler->filter('body');
|
||||
|
||||
// Remove the tracking tags that are causing troubles with different Mautic configurations.
|
||||
$body->filter('a,img,div')->each(function (Crawler $crawler) {
|
||||
foreach ($crawler as $node) {
|
||||
$node->parentNode->removeChild($node);
|
||||
}
|
||||
});
|
||||
|
||||
Assert::assertSame(
|
||||
$this->stripWhiteSpaces('Dear Test Lead,
|
||||
|
||||
Check these fields:
|
||||
Mobile: 012
|
||||
Address: Lane 11, Near Post Office, Pune, India
|
||||
Email: test@domain.tld
|
||||
|
||||
Custom Values:
|
||||
|
||||
Contact:
|
||||
Bool: 1,
|
||||
Date: 2022-07-01,
|
||||
Date/Time: 2022-07-01 20:22
|
||||
Email: test@test.com
|
||||
HTML: <p>This is some normal text.</p>
|
||||
Country: India
|
||||
Locale: Hindi
|
||||
Number: 400
|
||||
Phone: 1234567
|
||||
Region: Maharashtra
|
||||
Text: this is text
|
||||
Textarea: This is a paragraph
|
||||
Time: 20:00
|
||||
Timezone: Kolkata
|
||||
URL: www.example.com'),
|
||||
$this->stripWhiteSpaces($body->html())
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array <mixed>
|
||||
*/
|
||||
private function customFieldTypes(): array
|
||||
{
|
||||
return [
|
||||
'bool' => ['boolean', true],
|
||||
'date' => ['date', '2022-07-01'],
|
||||
'datetime' => ['datetime', '2022-07-01 20:22'],
|
||||
'email' => ['email', 'test@test.com'],
|
||||
'html' => ['html', '<p>This is some normal text.</p>'],
|
||||
'country' => ['country', 'India'],
|
||||
'locale' => ['locale', 'Hindi'],
|
||||
'number' => ['number', 400],
|
||||
'phone' => ['tel', 1234567],
|
||||
'region' => ['region', 'Maharashtra'],
|
||||
'text' => ['text', 'this is text'],
|
||||
'textarea' => ['textarea', 'This is a paragraph'],
|
||||
'time' => ['time', '20:00'],
|
||||
'timezone' => ['timezone', 'Kolkata'],
|
||||
'url' => ['url', 'www.example.com'],
|
||||
];
|
||||
}
|
||||
|
||||
private function createLeadWithAllFields(): Lead
|
||||
{
|
||||
$leadModel = self::getContainer()->get('mautic.lead.model.lead');
|
||||
$fieldModel = self::getContainer()->get('mautic.lead.model.field');
|
||||
|
||||
$lead = new Lead();
|
||||
$lead->setFirstname('Test');
|
||||
$lead->setLastname('Lead');
|
||||
$lead->setMobile('012');
|
||||
$lead->setAddress1('Lane 11');
|
||||
$lead->setAddress2('Near Post Office');
|
||||
$lead->setCity('Pune');
|
||||
$lead->setCountry('India');
|
||||
$lead->setEmail('test@domain.tld');
|
||||
$lead->setCompany('Acquia');
|
||||
|
||||
foreach ($this->customFieldTypes() as $alias => [$type, $value]) {
|
||||
$customFieldLead = new LeadField();
|
||||
$customFieldLead->setLabel($alias.'lead');
|
||||
$customFieldLead->setAlias($alias.'lead');
|
||||
$customFieldLead->setType($type);
|
||||
$customFieldLead->setObject('lead');
|
||||
$customFieldLead->setIsPublished(true);
|
||||
$fieldModel->saveEntity($customFieldLead);
|
||||
|
||||
$lead->addUpdatedField($customFieldLead->getAlias(), $value);
|
||||
}
|
||||
|
||||
$leadModel->saveEntity($lead);
|
||||
$this->em->clear();
|
||||
|
||||
return $lead;
|
||||
}
|
||||
|
||||
private function stripWhiteSpaces(string $string): string
|
||||
{
|
||||
return preg_replace('/\s+/', '', $string);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,248 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Mautic\EmailBundle\Tests\Functional;
|
||||
|
||||
use Doctrine\ORM\Exception\ORMException;
|
||||
use Doctrine\ORM\OptimisticLockException;
|
||||
use Mautic\CampaignBundle\Entity\Campaign;
|
||||
use Mautic\CampaignBundle\Entity\Event;
|
||||
use Mautic\CampaignBundle\Entity\Lead as CampaignLead;
|
||||
use Mautic\CoreBundle\Test\MauticMysqlTestCase;
|
||||
use Mautic\EmailBundle\Entity\Email;
|
||||
use Mautic\EmailBundle\Entity\Stat;
|
||||
use Mautic\EmailBundle\Entity\StatRepository;
|
||||
use Mautic\LeadBundle\Entity\Lead;
|
||||
use PHPUnit\Framework\Assert;
|
||||
|
||||
final class EmailVariantInCampaignFunctionalTest extends MauticMysqlTestCase
|
||||
{
|
||||
public function testMarketingEmailWithVariantShouldBeSentOnce(): void
|
||||
{
|
||||
$contact = new Lead();
|
||||
$contact->setEmail('test@example.com');
|
||||
$this->em->persist($contact);
|
||||
$this->em->flush();
|
||||
|
||||
$email = $this->createEmailWithVariant();
|
||||
$campaign = $this->createCampaignWithDoubleEmailSent($email->getId());
|
||||
|
||||
$campaignLead = new CampaignLead();
|
||||
$campaignLead->setCampaign($campaign);
|
||||
$campaignLead->setLead($contact);
|
||||
$campaignLead->setDateAdded(new \DateTime());
|
||||
$this->em->persist($campaignLead);
|
||||
$campaign->addLead(0, $campaignLead);
|
||||
|
||||
$this->em->flush();
|
||||
|
||||
$commandResult = $this->testSymfonyCommand('mautic:campaigns:trigger', ['--campaign-id' => $campaign->getId()]);
|
||||
Assert::assertStringContainsString('2 total events(s) to be processed in batches', $commandResult->getDisplay());
|
||||
|
||||
$variant = $email->getVariantChildren()->first();
|
||||
|
||||
/** @var StatRepository $emailStatRepository */
|
||||
$emailStatRepository = $this->em->getRepository(Stat::class);
|
||||
|
||||
$countVariantSent = $emailStatRepository->count([
|
||||
'email' => $variant->getId(),
|
||||
'lead' => $contact->getId(),
|
||||
]);
|
||||
|
||||
// the email should be sent only one to the contact
|
||||
$this->assertEquals(1, $countVariantSent);
|
||||
}
|
||||
|
||||
private function createEmailWithVariant(): Email
|
||||
{
|
||||
$email = new Email();
|
||||
$email->setName('Email Parent');
|
||||
$email->setSubject('Email Parent subject');
|
||||
$email->setEmailType('template');
|
||||
$email->setIsPublished(true);
|
||||
$this->em->persist($email);
|
||||
$this->em->flush();
|
||||
|
||||
$variant = new Email();
|
||||
$variant->setName('Email Variant');
|
||||
$variant->setSubject('Email Variant subject');
|
||||
$variant->setEmailType('template');
|
||||
$variant->setIsPublished(true);
|
||||
$variant->setVariantParent($email);
|
||||
$variant->setVariantSettings(['weight' => 100, 'winnerCriteria' => 'email.openrate']);
|
||||
$email->addVariantChild($variant);
|
||||
|
||||
$this->em->persist($email);
|
||||
$this->em->persist($variant);
|
||||
$this->em->flush();
|
||||
|
||||
return $email;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates campaign that will try to send the same marketing email twice.
|
||||
*
|
||||
* Campaign diagram:
|
||||
* -------------------
|
||||
* - Start segment -
|
||||
* -------------------
|
||||
* | |
|
||||
* -------------------- ------------------
|
||||
* - Send email - - Send email -
|
||||
* -------------------- ------------------
|
||||
*
|
||||
* @throws ORMException
|
||||
* @throws OptimisticLockException
|
||||
*/
|
||||
private function createCampaignWithDoubleEmailSent(int $emailId): Campaign
|
||||
{
|
||||
$campaign = new Campaign();
|
||||
$campaign->setName('Test Update contact');
|
||||
|
||||
$this->em->persist($campaign);
|
||||
$this->em->flush();
|
||||
|
||||
$emailSend1 = new Event();
|
||||
$emailSend1->setCampaign($campaign);
|
||||
$emailSend1->setName('Send email');
|
||||
$emailSend1->setType('email.send');
|
||||
$emailSend1->setChannel('email');
|
||||
$emailSend1->setChannelId($emailId);
|
||||
$emailSend1->setEventType('action');
|
||||
$emailSend1->setTriggerMode('immediate');
|
||||
$emailSend1->setOrder(1);
|
||||
$emailSend1->setProperties(
|
||||
[
|
||||
'canvasSettings' => [
|
||||
'droppedX' => '549',
|
||||
'droppedY' => '155',
|
||||
],
|
||||
'name' => '',
|
||||
'triggerMode' => 'immediate',
|
||||
'triggerDate' => null,
|
||||
'triggerInterval' => '1',
|
||||
'triggerIntervalUnit' => 'd',
|
||||
'triggerHour' => '',
|
||||
'triggerRestrictedStartHour' => '',
|
||||
'triggerRestrictedStopHour' => '',
|
||||
'anchor' => 'leadsource',
|
||||
'properties' => [
|
||||
'email' => $emailId,
|
||||
'email_type' => 'marketing',
|
||||
'priority' => '2',
|
||||
'attempts' => '3',
|
||||
],
|
||||
'type' => 'email.send',
|
||||
'eventType' => 'action',
|
||||
'anchorEventType' => 'source',
|
||||
'campaignId' => 'mautic_ce6c7dddf8444e579d741c0125f18b33a5d49b45',
|
||||
'_token' => 'HgysZwvH_n0uAp47CcAcsGddRnRk65t-3crOnuLx28Y',
|
||||
'buttons' => [
|
||||
'save' => '',
|
||||
],
|
||||
'email' => $emailId,
|
||||
'email_type' => 'marketing',
|
||||
'priority' => 2,
|
||||
'attempts' => 3.0,
|
||||
]
|
||||
);
|
||||
|
||||
$this->em->persist($emailSend1);
|
||||
$this->em->flush();
|
||||
|
||||
$emailSend2 = new Event();
|
||||
$emailSend2->setCampaign($campaign);
|
||||
$emailSend2->setName('Send email 2');
|
||||
$emailSend2->setType('email.send');
|
||||
$emailSend2->setChannel('email');
|
||||
$emailSend2->setChannelId($emailId);
|
||||
$emailSend2->setEventType('action');
|
||||
$emailSend2->setTriggerMode('immediate');
|
||||
$emailSend2->setOrder(1);
|
||||
$emailSend2->setProperties(
|
||||
[
|
||||
'canvasSettings' => [
|
||||
'droppedX' => '849',
|
||||
'droppedY' => '155',
|
||||
],
|
||||
'name' => '',
|
||||
'triggerMode' => 'immediate',
|
||||
'triggerDate' => null,
|
||||
'triggerInterval' => '1',
|
||||
'triggerIntervalUnit' => 'd',
|
||||
'triggerHour' => '',
|
||||
'triggerRestrictedStartHour' => '',
|
||||
'triggerRestrictedStopHour' => '',
|
||||
'anchor' => 'leadsource',
|
||||
'properties' => [
|
||||
'email' => $emailId,
|
||||
'email_type' => 'marketing',
|
||||
'priority' => '2',
|
||||
'attempts' => '3',
|
||||
],
|
||||
'type' => 'email.send',
|
||||
'eventType' => 'action',
|
||||
'anchorEventType' => 'source',
|
||||
'campaignId' => 'mautic_ce6c7dddf8444e579d741c0125f18b33a5d49b45',
|
||||
'_token' => 'HgysZwvH_n0uAp47CcAcsGddRnRk65t-3crOnuLx28Y',
|
||||
'buttons' => [
|
||||
'save' => '',
|
||||
],
|
||||
'email' => $emailId,
|
||||
'email_type' => 'marketing',
|
||||
'priority' => 2,
|
||||
'attempts' => 3.0,
|
||||
]
|
||||
);
|
||||
$this->em->persist($emailSend2);
|
||||
$this->em->flush();
|
||||
|
||||
$campaign->setCanvasSettings(
|
||||
[
|
||||
'nodes' => [
|
||||
[
|
||||
'id' => $emailSend2->getId(),
|
||||
'positionX' => '849',
|
||||
'positionY' => '155',
|
||||
],
|
||||
[
|
||||
'id' => $emailSend1->getId(),
|
||||
'positionX' => '549',
|
||||
'positionY' => '155',
|
||||
],
|
||||
[
|
||||
'id' => 'lists',
|
||||
'positionX' => '796',
|
||||
'positionY' => '50',
|
||||
],
|
||||
],
|
||||
'connections' => [
|
||||
[
|
||||
'sourceId' => 'lists',
|
||||
'targetId' => $emailSend1->getId(),
|
||||
'anchors' => [
|
||||
'source' => 'leadsource',
|
||||
'target' => 'top',
|
||||
],
|
||||
],
|
||||
[
|
||||
'sourceId' => 'lists',
|
||||
'targetId' => $emailSend2->getId(),
|
||||
'anchors' => [
|
||||
'source' => 'leadsource',
|
||||
'target' => 'top',
|
||||
],
|
||||
],
|
||||
],
|
||||
]
|
||||
);
|
||||
|
||||
$campaign->addEvent(0, $emailSend1);
|
||||
$campaign->addEvent(1, $emailSend2);
|
||||
$this->em->persist($campaign);
|
||||
$this->em->flush();
|
||||
|
||||
return $campaign;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,128 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Mautic\EmailBundle\Tests\Functional\Fixtures;
|
||||
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Mautic\CoreBundle\Entity\IpAddress;
|
||||
use Mautic\EmailBundle\Entity\Email;
|
||||
use Mautic\EmailBundle\Entity\Stat;
|
||||
use Mautic\LeadBundle\Entity\Lead;
|
||||
use Mautic\PageBundle\Entity\Hit;
|
||||
use Mautic\PageBundle\Entity\Redirect;
|
||||
use Mautic\PageBundle\Entity\Trackable;
|
||||
|
||||
final class EmailFixturesHelper
|
||||
{
|
||||
public function __construct(private EntityManagerInterface $em)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<int, mixed> $segments
|
||||
*/
|
||||
public function createEmail(
|
||||
string $name = 'Test email',
|
||||
string $subject = 'Test subject',
|
||||
string $emailType = 'template',
|
||||
bool $isPublished = true,
|
||||
string $template = 'blank',
|
||||
string $customHtml = 'Test Html',
|
||||
array $segments = [],
|
||||
): Email {
|
||||
$email = (new Email())
|
||||
->setName($name)
|
||||
->setSubject($subject)
|
||||
->setEmailType($emailType)
|
||||
->setIsPublished($isPublished)
|
||||
->setTemplate($template)
|
||||
->setCustomHtml($customHtml);
|
||||
|
||||
if (!empty($segments)) {
|
||||
$email->setLists($segments);
|
||||
}
|
||||
|
||||
$this->em->persist($email);
|
||||
|
||||
return $email;
|
||||
}
|
||||
|
||||
public function emulateEmailSend(Lead $contact, Email $email, string $date = 'now', ?string $source = null, ?int $sourceId = null): Stat
|
||||
{
|
||||
$emailStat = new Stat();
|
||||
$emailStat->setEmail($email);
|
||||
$emailStat->setLead($contact);
|
||||
$emailStat->setEmailAddress($contact->getEmail());
|
||||
$emailStat->setDateSent(new \DateTime($date));
|
||||
if ($source && $sourceId) {
|
||||
$emailStat->setSource($source);
|
||||
$emailStat->setSourceId($sourceId);
|
||||
}
|
||||
$email->setSentCount($email->getSentCount() + 1);
|
||||
|
||||
$this->em->persist($emailStat);
|
||||
$this->em->persist($email);
|
||||
|
||||
return $emailStat;
|
||||
}
|
||||
|
||||
public function emulateEmailRead(Stat $emailStat, Email $email, string $date = 'now'): Stat
|
||||
{
|
||||
$emailStat->setIsRead(true);
|
||||
$emailStat->setDateRead(new \DateTime($date));
|
||||
$email->setReadCount($email->getReadCount() + 1);
|
||||
$this->em->persist($emailStat);
|
||||
$this->em->persist($email);
|
||||
|
||||
return $emailStat;
|
||||
}
|
||||
|
||||
public function createEmailLink(string $url, int $channelId, int $hits = 0, int $uniqueHits = 0): Trackable
|
||||
{
|
||||
$redirect = new Redirect();
|
||||
$redirect->setRedirectId(uniqid());
|
||||
$redirect->setUrl($url);
|
||||
$redirect->setHits($hits);
|
||||
$redirect->setUniqueHits($uniqueHits);
|
||||
$this->em->persist($redirect);
|
||||
|
||||
$trackable = new Trackable();
|
||||
$trackable->setChannelId($channelId);
|
||||
$trackable->setChannel('email');
|
||||
$trackable->setHits($hits);
|
||||
$trackable->setUniqueHits($uniqueHits);
|
||||
$trackable->setRedirect($redirect);
|
||||
$this->em->persist($trackable);
|
||||
|
||||
return $trackable;
|
||||
}
|
||||
|
||||
public function emulateLinkClick(Email $email, Trackable $trackable, Lead $lead, string $date = 'now', int $count = 1): void
|
||||
{
|
||||
$trackable->setHits($trackable->getHits() + $count);
|
||||
$trackable->setUniqueHits($trackable->getUniqueHits() + 1);
|
||||
$this->em->persist($trackable);
|
||||
|
||||
$redirect = $trackable->getRedirect();
|
||||
|
||||
$ip = new IpAddress();
|
||||
$ip->setIpAddress('127.0.0.1');
|
||||
$this->em->persist($ip);
|
||||
|
||||
for ($i = 0; $i < $count; ++$i) {
|
||||
$pageHit = new Hit();
|
||||
$pageHit->setRedirect($redirect);
|
||||
$pageHit->setEmail($email);
|
||||
$pageHit->setLead($lead);
|
||||
$pageHit->setIpAddress($ip);
|
||||
$pageHit->setDateHit(new \DateTime($date));
|
||||
$pageHit->setCode(200);
|
||||
$pageHit->setUrl($redirect->getUrl());
|
||||
$pageHit->setTrackingId($redirect->getRedirectId());
|
||||
$pageHit->setSource('email');
|
||||
$pageHit->setSourceId($email->getId());
|
||||
$this->em->persist($pageHit);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Mautic\EmailBundle\Tests\Functional;
|
||||
|
||||
use Mautic\CoreBundle\Test\MauticMysqlTestCase;
|
||||
use Mautic\EmailBundle\Entity\Email;
|
||||
use Mautic\EmailBundle\Entity\Stat;
|
||||
use Mautic\LeadBundle\Entity\Lead;
|
||||
use Mautic\LeadBundle\Entity\LeadList;
|
||||
use Mautic\LeadBundle\Entity\ListLead;
|
||||
use PHPUnit\Framework\Assert;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
|
||||
final class PendingCountTest extends MauticMysqlTestCase
|
||||
{
|
||||
/**
|
||||
* There was an issue that if there is a lead_id = null in the email_stats associated with an email
|
||||
* then the pending count was always 0 even if there are contacts waiting for sent.
|
||||
*/
|
||||
public function testPendingCountWithDeletedContactsInEmailStats(): void
|
||||
{
|
||||
$contact = new Lead();
|
||||
$contact->setEmail('john@doe.email');
|
||||
|
||||
$segment = new LeadList();
|
||||
$segment->setName('Segment A');
|
||||
$segment->setPublicName('Segment A');
|
||||
$segment->setAlias('segment-a');
|
||||
|
||||
$segmentRef = new ListLead();
|
||||
$segmentRef->setLead($contact);
|
||||
$segmentRef->setList($segment);
|
||||
$segmentRef->setDateAdded(new \DateTime());
|
||||
|
||||
$email = new Email();
|
||||
$email->setName('Email A');
|
||||
$email->setSubject('Email A Subject');
|
||||
$email->setEmailType('list');
|
||||
$email->addList($segment);
|
||||
|
||||
$emailStat = new Stat();
|
||||
$emailStat->setEmail($email);
|
||||
$emailStat->setLead(null);
|
||||
$emailStat->setEmailAddress('deleted@contact.email');
|
||||
$emailStat->setDateSent(new \DateTime());
|
||||
|
||||
$this->em->persist($segment);
|
||||
$this->em->persist($contact);
|
||||
$this->em->persist($segmentRef);
|
||||
$this->em->persist($email);
|
||||
$this->em->persist($emailStat);
|
||||
$this->em->flush();
|
||||
|
||||
// The counts are loaded via ajax call after the email list page loads, so checking the ajax request instead of the HTML.
|
||||
$this->client->request(Request::METHOD_GET, '/s/ajax?action=email:getEmailCountStats', ['id' => $email->getId()]);
|
||||
|
||||
Assert::assertSame(
|
||||
'{"id":'.$email->getId().',"pending":"1 Pending","queued":0,"sentCount":"0 Sent","readCount":"0 Read","readPercent":"0% Read"}',
|
||||
$this->client->getResponse()->getContent()
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user