Initial commit: CloudOps infrastructure platform
This commit is contained in:
@@ -0,0 +1,64 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Mautic\PageBundle\Tests\Controller;
|
||||
|
||||
use Mautic\CoreBundle\Test\MauticMysqlTestCase;
|
||||
use Mautic\PageBundle\Entity\Page;
|
||||
use PHPUnit\Framework\Assert;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
|
||||
final class DeviceTrackingServiceClearCookiesTest extends MauticMysqlTestCase
|
||||
{
|
||||
/**
|
||||
* @return array<string, array{bool}>
|
||||
*/
|
||||
public static function blockedTrackingCookieDataProvider(): array
|
||||
{
|
||||
return [
|
||||
'with blocked tracking cookie' => [true],
|
||||
'without blocked tracking cookie' => [false],
|
||||
];
|
||||
}
|
||||
|
||||
#[\PHPUnit\Framework\Attributes\DataProvider('blockedTrackingCookieDataProvider')]
|
||||
public function testClearTrackingCookiesBehavior(bool $shouldClearCookies): void
|
||||
{
|
||||
$this->logoutUser();
|
||||
|
||||
$page = new Page();
|
||||
$page->setIsPublished(true);
|
||||
$page->setTitle('Test Page for Clear Tracking Cookies');
|
||||
$page->setAlias('test-clear-cookies');
|
||||
$page->setCustomHtml('<html><body><h1>Test Page</h1></body></html>');
|
||||
$this->em->persist($page);
|
||||
$this->em->flush();
|
||||
|
||||
if ($shouldClearCookies) {
|
||||
$this->client->getCookieJar()->set(new \Symfony\Component\BrowserKit\Cookie('Blocked-Tracking', '1'));
|
||||
}
|
||||
|
||||
$this->client->request(Request::METHOD_GET, '/test-clear-cookies');
|
||||
$this->assertResponseIsSuccessful();
|
||||
|
||||
$deviceIdCookieCleared = false;
|
||||
$mtcIdCookieCleared = false;
|
||||
|
||||
foreach ($this->client->getResponse()->headers->getCookies() as $cookie) {
|
||||
// Check if tracking cookies are being deleted (empty value + past expiration)
|
||||
$cookieIsDeleted = '' === $cookie->getValue() && $cookie->getExpiresTime() < time();
|
||||
|
||||
if ('mautic_device_id' === $cookie->getName() && $cookieIsDeleted) {
|
||||
$deviceIdCookieCleared = true;
|
||||
}
|
||||
|
||||
if ('mtc_id' === $cookie->getName() && $cookieIsDeleted) {
|
||||
$mtcIdCookieCleared = true;
|
||||
}
|
||||
}
|
||||
|
||||
Assert::assertSame($shouldClearCookies, $deviceIdCookieCleared);
|
||||
Assert::assertSame($shouldClearCookies, $mtcIdCookieCleared);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Mautic\PageBundle\Tests\Controller;
|
||||
|
||||
use Mautic\CoreBundle\Test\MauticMysqlTestCase;
|
||||
use Mautic\PageBundle\Entity\Page;
|
||||
use PHPUnit\Framework\Assert;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
final class NotFoundFunctionalTest extends MauticMysqlTestCase
|
||||
{
|
||||
protected $useCleanupRollback = false;
|
||||
|
||||
public function testCustom404Page(): void
|
||||
{
|
||||
// Create a custom 404 page:
|
||||
$notFoundPage = new Page();
|
||||
$notFoundPage->setTitle('404 Not Found');
|
||||
$notFoundPage->setAlias('404-not-found');
|
||||
$notFoundPage->setCustomHtml('<html><body>Custom 404 Not Found Page</body></html>');
|
||||
|
||||
$this->em->persist($notFoundPage);
|
||||
$this->em->flush();
|
||||
|
||||
// Configure the 404 page:
|
||||
$this->configParams['404_page'] = $notFoundPage->getId();
|
||||
parent::setUpSymfony($this->configParams);
|
||||
|
||||
// Test the custom 404 page:
|
||||
$crawler = $this->client->request(Request::METHOD_GET, '/page-that-does-not-exist');
|
||||
Assert::assertSame(Response::HTTP_NOT_FOUND, $this->client->getResponse()->getStatusCode());
|
||||
Assert::assertStringContainsString('Custom 404 Not Found Page', $crawler->text());
|
||||
Assert::assertFalse($this->client->getResponse()->isRedirection(), 'The response should not be a redirect.');
|
||||
Assert::assertSame('/page-that-does-not-exist', $this->client->getRequest()->getRequestUri(), 'The request URI should be the same as the original URI.');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,134 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Mautic\PageBundle\Tests\Controller;
|
||||
|
||||
use Mautic\CoreBundle\Test\MauticMysqlTestCase;
|
||||
use Mautic\DynamicContentBundle\Entity\DynamicContent;
|
||||
use Mautic\LeadBundle\Entity\LeadList;
|
||||
use Mautic\PageBundle\Entity\Page;
|
||||
use Mautic\ProjectBundle\Entity\Project;
|
||||
use PHPUnit\Framework\Assert;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
|
||||
class PageControllerFunctionalTest extends MauticMysqlTestCase
|
||||
{
|
||||
public function testPagePreview(): void
|
||||
{
|
||||
$segment = $this->createSegment();
|
||||
$filter = [
|
||||
[
|
||||
'glue' => 'and',
|
||||
'field' => 'leadlist',
|
||||
'object' => 'lead',
|
||||
'type' => 'leadlist',
|
||||
'filter' => [$segment->getId()],
|
||||
'display' => null,
|
||||
'operator' => 'in',
|
||||
],
|
||||
];
|
||||
$dynamicContent = $this->createDynamicContentWithSegmentFilter($filter);
|
||||
|
||||
$dynamicContentToken = sprintf('{dwc=%s}', $dynamicContent->getSlotName());
|
||||
$page = $this->createPage($dynamicContentToken);
|
||||
|
||||
$this->client->request(Request::METHOD_GET, sprintf('/%s', $page->getAlias()));
|
||||
$response = $this->client->getResponse();
|
||||
$this->assertSame(200, $response->getStatusCode());
|
||||
$this->assertStringContainsString('Test Html', $response->getContent());
|
||||
}
|
||||
|
||||
private function createSegment(): LeadList
|
||||
{
|
||||
$segment = new LeadList();
|
||||
$segment->setName('Segment 1');
|
||||
$segment->setPublicName('Segment 1');
|
||||
$segment->setAlias('segment_1');
|
||||
$this->em->persist($segment);
|
||||
$this->em->flush();
|
||||
|
||||
return $segment;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed[] $filters
|
||||
*/
|
||||
private function createDynamicContentWithSegmentFilter(array $filters = []): DynamicContent
|
||||
{
|
||||
$dynamicContent = new DynamicContent();
|
||||
$dynamicContent->setName('DC 1');
|
||||
$dynamicContent->setDescription('Customised value');
|
||||
$dynamicContent->setFilters($filters);
|
||||
$dynamicContent->setIsCampaignBased(false);
|
||||
$dynamicContent->setSlotName('Segment1_Slot');
|
||||
$this->em->persist($dynamicContent);
|
||||
$this->em->flush();
|
||||
|
||||
return $dynamicContent;
|
||||
}
|
||||
|
||||
private function createPage(string $token = ''): Page
|
||||
{
|
||||
$page = new Page();
|
||||
$page->setIsPublished(true);
|
||||
$page->setTitle('Page Title');
|
||||
$page->setAlias('page-alias');
|
||||
$page->setTemplate('blank');
|
||||
$page->setCustomHtml('Test Html'.$token);
|
||||
$this->em->persist($page);
|
||||
$this->em->flush();
|
||||
|
||||
return $page;
|
||||
}
|
||||
|
||||
public function testPageWithProject(): void
|
||||
{
|
||||
$page = $this->createPage();
|
||||
|
||||
$project = new Project();
|
||||
$project->setName('Test Project');
|
||||
$this->em->persist($project);
|
||||
|
||||
$this->em->flush();
|
||||
$this->em->clear();
|
||||
|
||||
$crawler = $this->client->request('GET', '/s/pages/edit/'.$page->getId());
|
||||
$form = $crawler->selectButton('Save')->form();
|
||||
$form['page[projects]']->setValue((string) $project->getId());
|
||||
|
||||
$this->client->submit($form);
|
||||
|
||||
$this->assertResponseIsSuccessful();
|
||||
|
||||
$savedPage = $this->em->find(Page::class, $page->getId());
|
||||
$this->assertSame($project->getId(), $savedPage->getProjects()->first()->getId());
|
||||
}
|
||||
|
||||
public function testPageWithNullCustomHtmlIsUpdated(): void
|
||||
{
|
||||
$page = new Page();
|
||||
|
||||
$page->setTitle('Page A');
|
||||
$page->setAlias('page-a');
|
||||
$page->setTemplate('mautic_code_mode');
|
||||
|
||||
$this->em->persist($page);
|
||||
$this->em->flush();
|
||||
|
||||
$pageId = $page->getId();
|
||||
$crawler = $this->client->request(Request::METHOD_GET, '/s/pages/edit/'.$pageId);
|
||||
$buttonCrawler = $crawler->selectButton('Save & Close');
|
||||
$form = $buttonCrawler->form();
|
||||
|
||||
$form['page[title]']->setValue('New Page');
|
||||
|
||||
$this->client->submit($form);
|
||||
|
||||
$this->assertResponseIsSuccessful();
|
||||
|
||||
$this->em->clear();
|
||||
|
||||
Assert::assertEquals('New Page', $this->em->find(Page::class, $pageId)->getTitle());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Mautic\PageBundle\Tests\Controller;
|
||||
|
||||
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\Redirect;
|
||||
use PHPUnit\Framework\Assert;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
|
||||
class PageControllerSqlRollbackFunctionalTest extends MauticMysqlTestCase
|
||||
{
|
||||
protected $useCleanupRollback = false;
|
||||
|
||||
public function testRedirectNotPersistClickthrough(): void
|
||||
{
|
||||
$lead = new Lead();
|
||||
$lead->setEmail('test@example.com');
|
||||
$this->em->persist($lead);
|
||||
$this->em->flush();
|
||||
|
||||
$redirectUrl = 'https://mautic.org/';
|
||||
$redirect = new Redirect();
|
||||
$redirectHash = uniqid('', true);
|
||||
$redirect->setRedirectId($redirectHash);
|
||||
$redirect->setUrl($redirectUrl);
|
||||
$this->em->persist($redirect);
|
||||
$this->em->flush();
|
||||
|
||||
$email = new Email();
|
||||
$email->setName('Test email');
|
||||
$this->em->persist($email);
|
||||
$this->em->flush();
|
||||
|
||||
$statHash = uniqid('', true);
|
||||
$stat = new Stat();
|
||||
$stat->setEmail($email);
|
||||
$stat->setEmailAddress($lead->getEmail());
|
||||
$stat->setDateSent(new \DateTime());
|
||||
$stat->setLead($lead);
|
||||
$stat->setTrackingHash($statHash);
|
||||
$this->em->persist($stat);
|
||||
$this->em->flush();
|
||||
|
||||
$ct = [
|
||||
'source' => ['email', $email->getId()],
|
||||
'email' => $email->getId(),
|
||||
'stat' => $statHash,
|
||||
'lead' => '1',
|
||||
'channel' => ['email' => $email->getId()],
|
||||
];
|
||||
$encodedCt = base64_encode(serialize($ct));
|
||||
|
||||
$this->setUpSymfony($this->configParams);
|
||||
$this->client->followRedirects(false);
|
||||
|
||||
$this->client->request(Request::METHOD_GET, "/r/{$redirectHash}?ct={$encodedCt}");
|
||||
$response = $this->client->getResponse();
|
||||
|
||||
Assert::assertTrue($response->isRedirect($redirectUrl), (string) $response);
|
||||
|
||||
// Re-enable redirect following for subsequent tests.
|
||||
$this->client->followRedirects();
|
||||
|
||||
$hitRepository = $this->em->getRepository(Hit::class);
|
||||
/** @var Hit|null $hit */
|
||||
$hit = $hitRepository->findOneBy(['lead' => $lead]);
|
||||
|
||||
Assert::assertNotNull($hit, 'A Hit entity should have been created.');
|
||||
Assert::assertSame('email', $hit->getSource(), 'The hit source should be email.');
|
||||
Assert::assertSame($email->getId(), $hit->getSourceId(), 'The hit source ID should be the email ID.');
|
||||
Assert::assertSame($redirect->getId(), $hit->getRedirect()->getId(), 'The hit should be associated with the correct redirect.');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,307 @@
|
||||
<?php
|
||||
|
||||
namespace Mautic\PageBundle\Tests\Controller;
|
||||
|
||||
use Mautic\CoreBundle\Test\MauticMysqlTestCase;
|
||||
use Mautic\CoreBundle\Tests\Traits\ControllerTrait;
|
||||
use Mautic\LeadBundle\Entity\UtmTag;
|
||||
use Mautic\PageBundle\DataFixtures\ORM\LoadPageCategoryData;
|
||||
use Mautic\PageBundle\DataFixtures\ORM\LoadPageData;
|
||||
use Mautic\PageBundle\Entity\Page;
|
||||
use Mautic\PageBundle\Model\PageModel;
|
||||
use PHPUnit\Framework\Assert;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
class PageControllerTest extends MauticMysqlTestCase
|
||||
{
|
||||
use ControllerTrait;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $prefix;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
private $id;
|
||||
|
||||
/**
|
||||
* @throws \Exception
|
||||
*/
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
$this->prefix = static::getContainer()->getParameter('mautic.db_table_prefix');
|
||||
|
||||
$pageData = [
|
||||
'title' => 'Test Page',
|
||||
'template' => 'blank',
|
||||
];
|
||||
|
||||
$model = static::getContainer()->get('mautic.page.model.page');
|
||||
$page = new Page();
|
||||
$page->setTitle($pageData['title'])
|
||||
->setTemplate($pageData['template']);
|
||||
|
||||
$model->saveEntity($page);
|
||||
|
||||
$this->id = $page->getId();
|
||||
}
|
||||
|
||||
/**
|
||||
* Index should return status code 200.
|
||||
*/
|
||||
public function testIndexAction(): void
|
||||
{
|
||||
$urlAlias = 'pages';
|
||||
$routeAlias = 'page';
|
||||
$column = 'dateModified';
|
||||
$column2 = 'title';
|
||||
$tableAlias = 'p.';
|
||||
|
||||
$this->getControllerColumnTests($urlAlias, $routeAlias, $column, $tableAlias, $column2);
|
||||
}
|
||||
|
||||
public function testLandingPageTracking(): void
|
||||
{
|
||||
$this->logoutUser();
|
||||
$this->connection->insert($this->prefix.'pages', [
|
||||
'is_published' => true,
|
||||
'date_added' => (new \DateTime())->format('Y-m-d H:i:s'),
|
||||
'title' => 'Page:Page:LandingPageTracking',
|
||||
'alias' => 'page-page-landingPageTracking',
|
||||
'template' => 'blank',
|
||||
'custom_html' => 'some content',
|
||||
'hits' => 0,
|
||||
'unique_hits' => 0,
|
||||
'variant_hits' => 0,
|
||||
'revision' => 0,
|
||||
'lang' => 'en',
|
||||
]);
|
||||
$leadsBeforeTest = $this->connection->fetchAllAssociative('SELECT `id` FROM `'.$this->prefix.'leads`;');
|
||||
$leadIdsBeforeTest = array_column($leadsBeforeTest, 'id');
|
||||
$this->client->request('GET', '/page-page-landingPageTracking');
|
||||
$this->assertEquals(200, $this->client->getResponse()->getStatusCode(), $this->client->getResponse()->getContent());
|
||||
|
||||
$sql = 'SELECT `id` FROM `'.$this->prefix.'leads`';
|
||||
if (!empty($leadIdsBeforeTest)) {
|
||||
$sql .= ' WHERE `id` NOT IN ('.implode(',', $leadIdsBeforeTest).');';
|
||||
}
|
||||
$newLeads = $this->connection->fetchAllAssociative($sql);
|
||||
$this->assertCount(1, $newLeads);
|
||||
$leadId = reset($newLeads)['id'];
|
||||
$leadEventLogs = $this->connection->fetchAllAssociative('
|
||||
SELECT `id`, `action`
|
||||
FROM `'.$this->prefix.'lead_event_log`
|
||||
WHERE `lead_id` = :leadId
|
||||
AND `bundle` = "page" AND `object` = "page";', ['leadId' => $leadId]
|
||||
);
|
||||
$this->assertCount(1, $leadEventLogs);
|
||||
$this->assertSame('created_contact', reset($leadEventLogs)['action']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Skipped for now.
|
||||
*/
|
||||
public function LandingPageTrackingSecondVisit(): void
|
||||
{
|
||||
$this->connection->insert($this->prefix.'pages', [
|
||||
'is_published' => true,
|
||||
'date_added' => (new \DateTime())->format('Y-m-d H:i:s'),
|
||||
'title' => 'Page:Page:LandingPageTrackingSecondVisit',
|
||||
'alias' => 'page-page-landingPageTrackingSecondVisit',
|
||||
'template' => 'blank',
|
||||
'hits' => 0,
|
||||
'unique_hits' => 0,
|
||||
'variant_hits' => 0,
|
||||
'revision' => 0,
|
||||
'lang' => 'en',
|
||||
]);
|
||||
$leadsBeforeTest = $this->connection->fetchAllAssociative('SELECT `id` FROM `'.$this->prefix.'leads`;');
|
||||
$leadIdsBeforeTest = array_column($leadsBeforeTest, 'id');
|
||||
$this->client->request('GET', '/page-page-landingPageTrackingSecondVisit');
|
||||
$this->assertEquals(200, $this->client->getResponse()->getStatusCode());
|
||||
$sql = 'SELECT `id` FROM `'.$this->prefix.'leads`';
|
||||
if (!empty($leadIdsBeforeTest)) {
|
||||
$sql .= ' WHERE `id` NOT IN ('.implode(',', $leadIdsBeforeTest).');';
|
||||
}
|
||||
$newLeadsAfterFirstVisit = $this->connection->fetchAllAssociative($sql);
|
||||
$this->assertCount(1, $newLeadsAfterFirstVisit);
|
||||
$leadId = reset($newLeadsAfterFirstVisit)['id'];
|
||||
$eventLogsAfterFirstVisit = $this->connection->fetchAllAssociative('
|
||||
SELECT `id`, `action`
|
||||
FROM `'.$this->prefix.'lead_event_log`
|
||||
WHERE `lead_id` = :leadId
|
||||
AND `bundle` = "page" AND `object` = "page";', ['leadId' => $leadId]
|
||||
);
|
||||
$this->assertCount(1, $eventLogsAfterFirstVisit);
|
||||
$this->assertSame('created_contact', reset($eventLogsAfterFirstVisit)['action']);
|
||||
$this->client->request('GET', '/page-page-landingPageTrackingSecondVisit');
|
||||
$this->assertEquals(200, $this->client->getResponse()->getStatusCode());
|
||||
$eventLogsAfterSecondVisit = $this->connection->fetchAllAssociative('
|
||||
SELECT `id`, `action`
|
||||
FROM `'.$this->prefix.'lead_event_log`
|
||||
WHERE `lead_id` = :leadId
|
||||
AND `bundle` = "page" AND `object` = "page";', ['leadId' => $leadId]
|
||||
);
|
||||
$this->assertCount(1, $eventLogsAfterSecondVisit);
|
||||
$this->assertSame(reset($eventLogsAfterFirstVisit)['id'], reset($eventLogsAfterSecondVisit)['id']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test tracking of a first visit with UTM Tags.
|
||||
*/
|
||||
public function testLandingPageWithUtmTracking(): void
|
||||
{
|
||||
$this->logoutUser();
|
||||
|
||||
$timestamp = \time();
|
||||
$page = $this->createTestPage();
|
||||
|
||||
$this->client->request('GET', "/{$page->getAlias()}?utm_source=linkedin&utm_medium=social&utm_campaign=mautic&utm_content=".$timestamp);
|
||||
$clientResponse = $this->client->getResponse();
|
||||
$this->assertEquals(Response::HTTP_OK, $clientResponse->getStatusCode(), $clientResponse->getContent());
|
||||
|
||||
$allUtmTags = $this->em->getRepository(UtmTag::class)->getEntities();
|
||||
$this->assertNotCount(0, $allUtmTags);
|
||||
|
||||
foreach ($allUtmTags as $utmTag) {
|
||||
$this->assertSame('linkedin', $utmTag->getUtmSource(), 'utm_source does not match');
|
||||
$this->assertSame('social', $utmTag->getUtmMedium(), 'utm_medium does not match');
|
||||
$this->assertSame('mautic', $utmTag->getUtmCampaign(), 'utm_campaign does not match');
|
||||
$this->assertSame(strval($timestamp), $utmTag->getUtmContent(), 'utm_content does not match');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a page for testing.
|
||||
*/
|
||||
protected function createTestPage($pageParams = []): Page
|
||||
{
|
||||
$page = new Page();
|
||||
$title = $pageParams['title'] ?? 'Page:Page:LandingPageTracking';
|
||||
$alias = $pageParams['alias'] ?? 'page-page-landingPageTracking';
|
||||
$isPublished = $pageParams['isPublished'] ?? true;
|
||||
$template = $pageParams['template'] ?? 'blank';
|
||||
|
||||
$page->setTitle($title);
|
||||
$page->setAlias($alias);
|
||||
$page->setIsPublished($isPublished);
|
||||
$page->setTemplate($template);
|
||||
$page->setCustomHtml('some content');
|
||||
|
||||
$this->em->persist($page);
|
||||
$this->em->flush();
|
||||
|
||||
return $page;
|
||||
}
|
||||
|
||||
/*
|
||||
* Get page's view.
|
||||
*/
|
||||
public function testViewActionPage(): void
|
||||
{
|
||||
$this->client->request('GET', '/s/pages/view/'.$this->id);
|
||||
$clientResponse = $this->client->getResponse();
|
||||
$clientResponseContent = $clientResponse->getContent();
|
||||
$model = static::getContainer()->get('mautic.page.model.page');
|
||||
$page = $model->getEntity($this->id);
|
||||
$this->assertEquals(Response::HTTP_OK, $clientResponse->getStatusCode());
|
||||
$this->assertStringContainsString($page->getTitle(), $clientResponseContent, 'The return must contain the title of page');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get landing page's create page.
|
||||
*/
|
||||
public function testNewActionPage(): void
|
||||
{
|
||||
$this->client->request('GET', '/s/pages/new/');
|
||||
$clientResponse = $this->client->getResponse();
|
||||
$clientResponseContent = $clientResponse->getContent();
|
||||
$this->assertEquals(Response::HTTP_OK, $clientResponse->getStatusCode());
|
||||
}
|
||||
|
||||
/* Get landing page's submissions list */
|
||||
public function testListLandingPageSubmissions(): void
|
||||
{
|
||||
$this->client->request('GET', 's/pages/results/'.$this->id);
|
||||
$clientResponse = $this->client->getResponse();
|
||||
$clientResponseContent = $clientResponse->getContent();
|
||||
$model = static::getContainer()->get('mautic.page.model.page');
|
||||
$page = $model->getEntity($this->id);
|
||||
$this->assertEquals(Response::HTTP_OK, $clientResponse->getStatusCode());
|
||||
}
|
||||
|
||||
/**
|
||||
* Only tests if an actual CSV file is returned.
|
||||
*/
|
||||
public function testCsvIsExportedCorrectly(): void
|
||||
{
|
||||
$this->loadFixtures([LoadPageCategoryData::class, LoadPageData::class]);
|
||||
|
||||
ob_start();
|
||||
$this->client->request(Request::METHOD_GET, '/s/pages/results/'.$this->id.'/export');
|
||||
$content = ob_get_contents();
|
||||
ob_end_clean();
|
||||
|
||||
$clientResponse = $this->client->getResponse();
|
||||
|
||||
$this->assertEquals(Response::HTTP_OK, $clientResponse->getStatusCode());
|
||||
$this->assertEquals($this->client->getInternalResponse()->getHeader('content-type'), 'text/csv; charset=UTF-8');
|
||||
}
|
||||
|
||||
/**
|
||||
* Only tests if an actual Excel file is returned.
|
||||
*/
|
||||
public function testExcelIsExportedCorrectly(): void
|
||||
{
|
||||
$this->loadFixtures([LoadPageCategoryData::class, LoadPageData::class]);
|
||||
|
||||
ob_start();
|
||||
$this->client->request(Request::METHOD_GET, '/s/pages/results/'.$this->id.'/export/xlsx');
|
||||
$content = ob_get_contents();
|
||||
ob_end_clean();
|
||||
|
||||
$clientResponse = $this->client->getResponse();
|
||||
|
||||
$this->assertEquals(Response::HTTP_OK, $clientResponse->getStatusCode());
|
||||
$this->assertEquals($this->client->getInternalResponse()->getHeader('content-type'), 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
|
||||
}
|
||||
|
||||
/**
|
||||
* Only tests if an actual HTML file is returned.
|
||||
*/
|
||||
public function testHTMLIsExportedCorrectly(): void
|
||||
{
|
||||
$this->loadFixtures([LoadPageCategoryData::class, LoadPageData::class]);
|
||||
|
||||
ob_start();
|
||||
$this->client->request(Request::METHOD_GET, '/s/pages/results/'.$this->id.'/export/html');
|
||||
$content = ob_get_contents();
|
||||
ob_end_clean();
|
||||
|
||||
$clientResponse = $this->client->getResponse();
|
||||
|
||||
$this->assertEquals(Response::HTTP_OK, $clientResponse->getStatusCode());
|
||||
$this->assertEquals($this->client->getInternalResponse()->getHeader('content-type'), 'text/html; charset=UTF-8');
|
||||
}
|
||||
|
||||
public function testSavePageAliasWithUnderscores(): void
|
||||
{
|
||||
/** @var PageModel $pageModel */
|
||||
$pageModel = static::getContainer()->get('mautic.page.model.page');
|
||||
|
||||
$parentPage = new Page();
|
||||
$parentPage->setTitle('This is My Page');
|
||||
$parentPage->setAlias('This_Is_My_Page');
|
||||
$parentPage->setTemplate('blank');
|
||||
$parentPage->setCustomHtml('This is My Page');
|
||||
$pageModel->saveEntity($parentPage);
|
||||
|
||||
$this->client->request(Request::METHOD_GET, '/this_is_my_page');
|
||||
$response = $this->client->getResponse();
|
||||
Assert::assertTrue($response->isOk());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,131 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Mautic\PageBundle\Tests\Controller;
|
||||
|
||||
use Mautic\CoreBundle\Test\MauticMysqlTestCase;
|
||||
use Mautic\PageBundle\Entity\Page;
|
||||
use Mautic\PageBundle\Entity\PageDraft;
|
||||
use PHPUnit\Framework\Assert;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
|
||||
final class PageDraftFunctionalTest extends MauticMysqlTestCase
|
||||
{
|
||||
protected function setUp(): void
|
||||
{
|
||||
$this->configParams['page_draft_enabled'] = 'testPageDraftNotConfigured' !== $this->name();
|
||||
|
||||
parent::setUp();
|
||||
}
|
||||
|
||||
public function testPageDraftNotConfigured(): void
|
||||
{
|
||||
$page = $this->createNewPage();
|
||||
$crawler = $this->client->request(Request::METHOD_GET, "/s/pages/edit/{$page->getId()}");
|
||||
Assert::assertEquals(0, $crawler->selectButton('Save as Draft')->count());
|
||||
Assert::assertEquals(0, $crawler->selectButton('Apply Draft')->count());
|
||||
Assert::assertEquals(0, $crawler->selectButton('Discard Draft')->count());
|
||||
}
|
||||
|
||||
public function testPageDraftConfigured(): void
|
||||
{
|
||||
$page = $this->createNewPage();
|
||||
$crawler = $this->client->request(Request::METHOD_GET, "/s/pages/edit/{$page->getId()}");
|
||||
|
||||
Assert::assertEquals(1, $crawler->selectButton('Save as Draft')->count());
|
||||
Assert::assertEquals(0, $crawler->selectButton('Apply Draft')->count());
|
||||
Assert::assertEquals(0, $crawler->selectButton('Discard Draft')->count());
|
||||
}
|
||||
|
||||
public function testCheckDraftInList(): void
|
||||
{
|
||||
$page = $this->createNewPage();
|
||||
$crawler = $this->client->request(Request::METHOD_GET, '/s/pages');
|
||||
$this->assertStringNotContainsString('Has Draft', $crawler->filter('#app-content a[href="/s/pages/view/'.$page->getId().'"]')->html());
|
||||
$this->saveDraft($page);
|
||||
$crawler = $this->client->request(Request::METHOD_GET, '/s/pages');
|
||||
$this->assertStringContainsString('Has Draft', $crawler->filter('#app-content a[href="/s/pages/view/'.$page->getId().'"]')->html());
|
||||
}
|
||||
|
||||
public function testPreviewDraft(): void
|
||||
{
|
||||
$page = $this->createNewPage();
|
||||
$this->saveDraft($page);
|
||||
$crawler = $this->client->request(Request::METHOD_GET, "/page/preview/{$page->getId()}");
|
||||
$this->assertEquals('Test html', $crawler->filter('body')->text());
|
||||
|
||||
$crawler = $this->client->request(Request::METHOD_GET, "/page/preview/{$page->getId()}/draft");
|
||||
$this->assertEquals('Test html Draft', $crawler->filter('body')->text());
|
||||
}
|
||||
|
||||
public function testSaveDraftAndApplyDraft(): void
|
||||
{
|
||||
$page = $this->createNewPage();
|
||||
$this->saveDraft($page);
|
||||
$this->applyDraft($page);
|
||||
}
|
||||
|
||||
public function testDiscardDraft(): void
|
||||
{
|
||||
$page = $this->createNewPage();
|
||||
$this->saveDraft($page);
|
||||
$this->discardDraft($page);
|
||||
}
|
||||
|
||||
private function applyDraft(Page $page): void
|
||||
{
|
||||
$crawler = $this->client->request(Request::METHOD_GET, "/s/pages/edit/{$page->getId()}");
|
||||
$form = $crawler->selectButton('Apply Draft')->form();
|
||||
$this->client->submit($form);
|
||||
Assert::assertTrue($this->client->getResponse()->isOk());
|
||||
|
||||
$pageDraft = $this->em->getRepository(PageDraft::class)->findOneBy(['page' => $page]);
|
||||
|
||||
Assert::assertNull($pageDraft);
|
||||
Assert::assertSame('Test html Draft', $page->getCustomHtml());
|
||||
}
|
||||
|
||||
private function discardDraft(Page $page): void
|
||||
{
|
||||
$crawler = $this->client->request(Request::METHOD_GET, "/s/pages/edit/{$page->getId()}");
|
||||
$form = $crawler->selectButton('Discard Draft')->form();
|
||||
$this->client->submit($form);
|
||||
Assert::assertTrue($this->client->getResponse()->isOk());
|
||||
|
||||
$pageDraft = $this->em->getRepository(PageDraft::class)->findOneBy(['page' => $page]);
|
||||
|
||||
Assert::assertNull($pageDraft);
|
||||
Assert::assertSame('Test html', $page->getCustomHtml());
|
||||
}
|
||||
|
||||
private function saveDraft(Page $page): void
|
||||
{
|
||||
$crawler = $this->client->request(Request::METHOD_GET, "/s/pages/edit/{$page->getId()}");
|
||||
|
||||
$form = $crawler->selectButton('Save as Draft')->form();
|
||||
$form['page[customHtml]'] = 'Test html Draft';
|
||||
$this->client->submit($form);
|
||||
Assert::assertTrue($this->client->getResponse()->isOk());
|
||||
|
||||
$pageDraft = $this->em->getRepository(PageDraft::class)->findOneBy(['page' => $page]);
|
||||
Assert::assertEquals('Test html Draft', $pageDraft->getHtml());
|
||||
Assert::assertSame('Test html', $page->getCustomHtml());
|
||||
}
|
||||
|
||||
private function createNewPage(): Page
|
||||
{
|
||||
$pageObject = new Page();
|
||||
$pageObject->setIsPublished(true);
|
||||
$pageObject->setDateAdded(new \DateTime());
|
||||
$pageObject->setTitle('Page Test');
|
||||
$pageObject->setAlias('Page Test');
|
||||
$pageObject->setTemplate('blank');
|
||||
$pageObject->setCustomHtml('Test html');
|
||||
$pageObject->setLanguage('en');
|
||||
$this->em->persist($pageObject);
|
||||
$this->em->flush();
|
||||
|
||||
return $pageObject;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Mautic\PageBundle\Tests\Controller;
|
||||
|
||||
use Mautic\PageBundle\Entity\Page;
|
||||
use Mautic\ProjectBundle\Tests\Functional\AbstractProjectSearchTestCase;
|
||||
|
||||
final class PageProjectSearchFunctionalTest extends AbstractProjectSearchTestCase
|
||||
{
|
||||
#[\PHPUnit\Framework\Attributes\DataProvider('searchDataProvider')]
|
||||
public function testProjectSearch(string $searchTerm, array $expectedEntities, array $unexpectedEntities): void
|
||||
{
|
||||
$projectOne = $this->createProject('Project One');
|
||||
$projectTwo = $this->createProject('Project Two');
|
||||
$projectThree = $this->createProject('Project Three');
|
||||
|
||||
$pageAlpha = $this->createPage('Page Alpha');
|
||||
$pageBeta = $this->createPage('Page Beta');
|
||||
$this->createPage('Page Gamma');
|
||||
$this->createPage('Page Delta');
|
||||
|
||||
$pageAlpha->addProject($projectOne);
|
||||
$pageAlpha->addProject($projectTwo);
|
||||
$pageBeta->addProject($projectTwo);
|
||||
$pageBeta->addProject($projectThree);
|
||||
|
||||
$this->em->flush();
|
||||
$this->em->clear();
|
||||
|
||||
$this->searchAndAssert($searchTerm, $expectedEntities, $unexpectedEntities, ['/api/pages', '/s/pages']);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \Generator<string, array{searchTerm: string, expectedEntities: array<string>, unexpectedEntities: array<string>}>
|
||||
*/
|
||||
public static function searchDataProvider(): \Generator
|
||||
{
|
||||
yield 'search by one project' => [
|
||||
'searchTerm' => 'project:"Project Two"',
|
||||
'expectedEntities' => ['Page Alpha', 'Page Beta'],
|
||||
'unexpectedEntities' => ['Page Gamma', 'Page Delta'],
|
||||
];
|
||||
|
||||
yield 'search by one project AND page name' => [
|
||||
'searchTerm' => 'project:"Project Two" AND Beta',
|
||||
'expectedEntities' => ['Page Beta'],
|
||||
'unexpectedEntities' => ['Page Alpha', 'Page Gamma', 'Page Delta'],
|
||||
];
|
||||
|
||||
yield 'search by one project OR page name' => [
|
||||
'searchTerm' => 'project:"Project Two" OR Gamma',
|
||||
'expectedEntities' => ['Page Alpha', 'Page Beta', 'Page Gamma'],
|
||||
'unexpectedEntities' => ['Page Delta'],
|
||||
];
|
||||
|
||||
yield 'search by NOT one project' => [
|
||||
'searchTerm' => '!project:"Project Two"',
|
||||
'expectedEntities' => ['Page Gamma', 'Page Delta'],
|
||||
'unexpectedEntities' => ['Page Alpha', 'Page Beta'],
|
||||
];
|
||||
|
||||
yield 'search by two projects with AND' => [
|
||||
'searchTerm' => 'project:"Project Two" AND project:"Project Three"',
|
||||
'expectedEntities' => ['Page Beta'],
|
||||
'unexpectedEntities' => ['Page Alpha', 'Page Gamma', 'Page Delta'],
|
||||
];
|
||||
|
||||
yield 'search by two projects with NOT AND' => [
|
||||
'searchTerm' => '!project:"Project Two" AND !project:"Project Three"',
|
||||
'expectedEntities' => ['Page Gamma', 'Page Delta'],
|
||||
'unexpectedEntities' => ['Page Alpha', 'Page Beta'],
|
||||
];
|
||||
|
||||
yield 'search by two projects with OR' => [
|
||||
'searchTerm' => 'project:"Project Two" OR project:"Project Three"',
|
||||
'expectedEntities' => ['Page Alpha', 'Page Beta'],
|
||||
'unexpectedEntities' => ['Page Gamma', 'Page Delta'],
|
||||
];
|
||||
|
||||
yield 'search by two projects with NOT OR' => [
|
||||
'searchTerm' => '!project:"Project Two" OR !project:"Project Three"',
|
||||
'expectedEntities' => ['Page Alpha', 'Page Gamma', 'Page Delta'],
|
||||
'unexpectedEntities' => ['Page Beta'],
|
||||
];
|
||||
}
|
||||
|
||||
private function createPage(string $name): Page
|
||||
{
|
||||
$page = new Page();
|
||||
$page->setTitle($name);
|
||||
$page->setAlias($name);
|
||||
$this->em->persist($page);
|
||||
|
||||
return $page;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,298 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Mautic\PageBundle\Tests\Controller;
|
||||
|
||||
use Mautic\CoreBundle\Security\Permissions\CorePermissions;
|
||||
use Mautic\CoreBundle\Test\MauticMysqlTestCase;
|
||||
use Mautic\DynamicContentBundle\Entity\DynamicContent;
|
||||
use Mautic\LeadBundle\Entity\Lead;
|
||||
use Mautic\PageBundle\Entity\Page;
|
||||
use Mautic\UserBundle\Entity\User;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
class PreviewFunctionalTest extends MauticMysqlTestCase
|
||||
{
|
||||
public function testPreviewPageWithContact(): void
|
||||
{
|
||||
$user = $this->em->getRepository(User::class)->findOneBy(['username' => 'admin']);
|
||||
$lead = $this->createLead();
|
||||
$dynamicContent = $this->createDynamicContent($lead);
|
||||
$defaultContent = 'Default web content';
|
||||
// Create non public landing page.
|
||||
$page = $this->createPage($dynamicContent, $defaultContent, true, false);
|
||||
|
||||
$this->em->flush();
|
||||
$this->em->clear();
|
||||
|
||||
$url = "/page/preview/{$page->getId()}";
|
||||
|
||||
// Anonymous visitor is not allowed to access preview if not public
|
||||
$this->logoutUser();
|
||||
$this->client->request(Request::METHOD_GET, $url);
|
||||
self::assertSame(Response::HTTP_FORBIDDEN, $this->client->getResponse()->getStatusCode());
|
||||
|
||||
$this->loginUser($user);
|
||||
|
||||
// Admin user is allowed to access preview
|
||||
$this->assertPageContent($url, $defaultContent);
|
||||
|
||||
// Check DWC replacement for the given lead
|
||||
$this->assertPageContent("{$url}?contactId={$lead->getId()}", $dynamicContent->getContent());
|
||||
|
||||
// Check there is no DWC replacement for a non-existent lead
|
||||
$this->assertPageContent("{$url}?contactId=987", $defaultContent);
|
||||
|
||||
$this->logoutUser();
|
||||
|
||||
// Anonymous visitor is not allowed to access preview
|
||||
$this->client->request(Request::METHOD_GET, $url);
|
||||
self::assertSame(Response::HTTP_FORBIDDEN, $this->client->getResponse()->getStatusCode());
|
||||
}
|
||||
|
||||
public function testPreviewPageUrlIsValid(): void
|
||||
{
|
||||
$page = $this->createPage();
|
||||
|
||||
$this->em->flush();
|
||||
$this->em->clear();
|
||||
|
||||
$pageId = $page->getId();
|
||||
|
||||
// Check for correct preview URL.
|
||||
$crawler = $this->client->request(Request::METHOD_GET, '/s/pages/view/'.$pageId);
|
||||
self::assertStringContainsString('/page/preview/'.$pageId, $crawler->filter('#content_preview_url')->attr('value'));
|
||||
}
|
||||
|
||||
public function testPreviewPagePublicToggle(): void
|
||||
{
|
||||
$page = $this->createPage();
|
||||
|
||||
$this->em->flush();
|
||||
$this->em->clear();
|
||||
|
||||
$pageId = $page->getId();
|
||||
|
||||
// Check for public preview ON.
|
||||
$crawler = $this->client->request(Request::METHOD_GET, '/s/pages/view/'.$pageId);
|
||||
$toggleElem = $crawler->filter('i.ri-toggle-fill');
|
||||
self::assertEquals(1, $toggleElem->count());
|
||||
|
||||
// Toggle public preview.
|
||||
$parameters = [
|
||||
'action' => 'togglePublishStatus',
|
||||
'model' => 'page',
|
||||
'id' => $pageId,
|
||||
'customToggle' => 'publicPreview',
|
||||
];
|
||||
$this->client->request(Request::METHOD_POST, '/s/ajax', $parameters);
|
||||
|
||||
// Check for public preview OFF.
|
||||
$crawler = $this->client->request(Request::METHOD_GET, '/s/pages/view/'.$pageId);
|
||||
$toggleElem = $crawler->filter('i.ri-toggle-line');
|
||||
self::assertEquals(1, $toggleElem->count());
|
||||
|
||||
// Create landing page with public preview OFF.
|
||||
$page = $this->createPage(null, '', true, false);
|
||||
|
||||
$this->em->flush();
|
||||
$this->em->clear();
|
||||
|
||||
$pageId = $page->getId();
|
||||
|
||||
// Check for public preview OFF.
|
||||
$crawler = $this->client->request(Request::METHOD_GET, '/s/pages/view/'.$pageId);
|
||||
self::assertEquals(1, $crawler->filter('i.ri-toggle-line')->count());
|
||||
|
||||
// Toggle public preview.
|
||||
$parameters['id'] = $pageId;
|
||||
$this->client->request(Request::METHOD_POST, '/s/ajax', $parameters);
|
||||
|
||||
// Check for public preview ON.
|
||||
$crawler = $this->client->request(Request::METHOD_GET, '/s/pages/view/'.$pageId);
|
||||
$toggleElem = $crawler->filter('i.ri-toggle-fill');
|
||||
self::assertEquals(1, $toggleElem->count());
|
||||
}
|
||||
|
||||
public function testPreviewPageWithPublishAndPublicOptions(): void
|
||||
{
|
||||
$page = $this->createPage();
|
||||
|
||||
$this->em->flush();
|
||||
$this->em->clear();
|
||||
|
||||
$pageId = $page->getId();
|
||||
|
||||
// Check for public preview ON.
|
||||
$this->client->request(Request::METHOD_GET, '/s/logout');
|
||||
$crawler = $this->client->request(Request::METHOD_GET, '/page/preview/'.$pageId);
|
||||
self::assertEquals(Response::HTTP_OK, $this->client->getResponse()->getStatusCode());
|
||||
self::assertEquals('Hello', $crawler->filter('body')->text());
|
||||
|
||||
// Create landing page with public preview OFF.
|
||||
$page = $this->createPage(null, '', true, false);
|
||||
|
||||
$this->em->flush();
|
||||
$this->em->clear();
|
||||
|
||||
$pageId = $page->getId();
|
||||
|
||||
// Check public preview without login.
|
||||
|
||||
$crawler = $this->client->request(Request::METHOD_GET, '/page/preview/'.$pageId);
|
||||
self::assertEquals(Response::HTTP_FORBIDDEN, $this->client->getResponse()->getStatusCode());
|
||||
self::assertStringContainsString(
|
||||
'Unauthorized access to requested URL: /page/preview/'.$pageId,
|
||||
$crawler->text()
|
||||
);
|
||||
|
||||
// Create page with publish OFF.
|
||||
$page = $this->createPage(null, '', false);
|
||||
|
||||
$this->em->flush();
|
||||
$this->em->clear();
|
||||
|
||||
$pageId = $page->getId();
|
||||
|
||||
// Check for public preview ON.
|
||||
$crawler = $this->client->request(Request::METHOD_GET, '/page/preview/'.$pageId);
|
||||
self::assertEquals(Response::HTTP_FORBIDDEN, $this->client->getResponse()->getStatusCode());
|
||||
self::assertStringContainsString(
|
||||
'Unauthorized access to requested URL: /page/preview/'.$pageId,
|
||||
$crawler->text()
|
||||
);
|
||||
|
||||
// Create landing page with publish and public preview OFF.
|
||||
$page = $this->createPage(null, '', false, false);
|
||||
|
||||
$this->em->flush();
|
||||
$this->em->clear();
|
||||
|
||||
$pageId = $page->getId();
|
||||
|
||||
// Check for public preview ON.
|
||||
$crawler = $this->client->request(Request::METHOD_GET, '/page/preview/'.$pageId);
|
||||
self::assertEquals(Response::HTTP_FORBIDDEN, $this->client->getResponse()->getStatusCode());
|
||||
self::assertStringContainsString(
|
||||
'Unauthorized access to requested URL: /page/preview/'.$pageId,
|
||||
$crawler->text()
|
||||
);
|
||||
}
|
||||
|
||||
public function testPreviewPageNotFound(): void
|
||||
{
|
||||
// Check for non existing landing page preview.
|
||||
$crawler = $this->client->request(Request::METHOD_GET, '/page/preview/20000');
|
||||
self::assertEquals(Response::HTTP_NOT_FOUND, $this->client->getResponse()->getStatusCode());
|
||||
self::assertStringContainsString('404 Not Found', $crawler->text());
|
||||
}
|
||||
|
||||
public function testPreviewPageAccess(): void
|
||||
{
|
||||
// Create non published, non public landing page.
|
||||
$page = $this->createPage(null, '', false, false);
|
||||
|
||||
$this->em->flush();
|
||||
$this->em->clear();
|
||||
|
||||
$pageId = $page->getId();
|
||||
|
||||
// Check public preview with login.
|
||||
$user = $this->em->getRepository(User::class)->findOneBy(['username' => 'admin']);
|
||||
$this->loginUser($user);
|
||||
$crawler = $this->client->request(Request::METHOD_GET, '/page/preview/'.$pageId);
|
||||
self::assertEquals(Response::HTTP_OK, $this->client->getResponse()->getStatusCode());
|
||||
self::assertEquals('Hello', $crawler->filter('body')->text());
|
||||
|
||||
// Check public preview without login.
|
||||
$this->client->request(Request::METHOD_GET, '/s/logout');
|
||||
$crawler = $this->client->request(Request::METHOD_GET, '/page/preview/'.$pageId);
|
||||
self::assertEquals(Response::HTTP_FORBIDDEN, $this->client->getResponse()->getStatusCode());
|
||||
self::assertStringContainsString(
|
||||
'Unauthorized access to requested URL: /page/preview/'.$pageId,
|
||||
$crawler->text()
|
||||
);
|
||||
|
||||
// Check public preview access without permissions
|
||||
$this->loginUser($user);
|
||||
$security = $this->createMock(CorePermissions::class);
|
||||
$security->method('isAnonymous')->willReturn(false);
|
||||
$security->method('hasEntityAccess')->with(
|
||||
'page:pages:viewown',
|
||||
'page:pages:viewother',
|
||||
$page->getCreatedBy()
|
||||
)->willReturn(false);
|
||||
$this->getContainer()->set('mautic.security', $security);
|
||||
self::assertEquals(Response::HTTP_FORBIDDEN, $this->client->getResponse()->getStatusCode());
|
||||
self::assertStringContainsString(
|
||||
'Unauthorized access to requested URL: /page/preview/'.$pageId,
|
||||
$crawler->text()
|
||||
);
|
||||
}
|
||||
|
||||
private function assertPageContent(string $url, string $expectedContent): void
|
||||
{
|
||||
$crawler = $this->client->request(Request::METHOD_GET, $url);
|
||||
self::assertSame(Response::HTTP_OK, $this->client->getResponse()->getStatusCode(), $this->client->getResponse()->getContent());
|
||||
self::assertSame($expectedContent, $crawler->filter('body')->text());
|
||||
}
|
||||
|
||||
private function createPage(
|
||||
?DynamicContent $dynamicContent = null,
|
||||
string $defaultContent = '',
|
||||
bool $isPublished = true,
|
||||
bool $publicPreview = true,
|
||||
): Page {
|
||||
if (null === $dynamicContent) {
|
||||
$customHtml = '<html lang="en"><body>Hello</body></html>';
|
||||
} else {
|
||||
$customHtml = sprintf('<div data-slot="dwc" data-param-slot-name="%s"><span>%s</span></div>', $dynamicContent->getSlotName(), $defaultContent);
|
||||
}
|
||||
|
||||
$page = new Page();
|
||||
$page->setIsPublished($isPublished);
|
||||
$page->setDateAdded(new \DateTime());
|
||||
$page->setTitle('Preview settings test - main page');
|
||||
$page->setAlias('page-main');
|
||||
$page->setTemplate('Blank');
|
||||
$page->setCustomHtml($customHtml);
|
||||
$page->setPublicPreview($publicPreview);
|
||||
$this->em->persist($page);
|
||||
|
||||
return $page;
|
||||
}
|
||||
|
||||
private function createLead(): Lead
|
||||
{
|
||||
$lead = new Lead();
|
||||
$lead->setEmail('test@domain.tld');
|
||||
$this->em->persist($lead);
|
||||
|
||||
return $lead;
|
||||
}
|
||||
|
||||
private function createDynamicContent(Lead $lead): DynamicContent
|
||||
{
|
||||
$dynamicContent = new DynamicContent();
|
||||
$dynamicContent->setName('Test DWC');
|
||||
$dynamicContent->setIsCampaignBased(false);
|
||||
$dynamicContent->setContent('DWC content');
|
||||
$dynamicContent->setSlotName('test');
|
||||
$dynamicContent->setFilters([
|
||||
[
|
||||
'glue' => 'and',
|
||||
'field' => 'email',
|
||||
'object' => 'lead',
|
||||
'type' => 'email',
|
||||
'filter' => $lead->getEmail(),
|
||||
'display' => null,
|
||||
'operator' => '=',
|
||||
],
|
||||
]);
|
||||
$this->em->persist($dynamicContent);
|
||||
|
||||
return $dynamicContent;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Mautic\PageBundle\Tests\Controller;
|
||||
|
||||
use Mautic\CoreBundle\Test\MauticMysqlTestCase;
|
||||
use Mautic\PageBundle\Entity\Page;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
|
||||
class PreviewSettingsFunctionalTest extends MauticMysqlTestCase
|
||||
{
|
||||
public function testPreviewSettingsAllEnabled(): void
|
||||
{
|
||||
$pageMain = new Page();
|
||||
$pageMain->setIsPublished(true);
|
||||
$pageMain->setDateAdded(new \DateTime());
|
||||
$pageMain->setTitle('Preview settings test - main page');
|
||||
$pageMain->setAlias('page-main');
|
||||
$pageMain->setTemplate('Blank');
|
||||
$pageMain->setCustomHtml('Test Html');
|
||||
$pageMain->setLanguage('en');
|
||||
|
||||
$this->em->persist($pageMain);
|
||||
$this->em->flush();
|
||||
|
||||
$mainPageId = $pageMain->getId();
|
||||
|
||||
$crawler = $this->client->request(Request::METHOD_GET, '/s/pages');
|
||||
self::assertStringContainsString($pageMain->getTitle(), $crawler->text());
|
||||
|
||||
$crawler = $this->client->request(Request::METHOD_GET, "/s/pages/view/{$mainPageId}");
|
||||
|
||||
// Translation choice is not visible
|
||||
self::assertCount(
|
||||
0,
|
||||
$crawler->filterXPath('//*[@id="content_preview_settings_translation"]')
|
||||
);
|
||||
|
||||
// Variant choice is not visible
|
||||
self::assertCount(
|
||||
0,
|
||||
$crawler->filterXPath('//*[@id="content_preview_settings_variant"]')
|
||||
);
|
||||
|
||||
// Contact lookup is not visible
|
||||
self::assertCount(
|
||||
1,
|
||||
$crawler->filterXPath('//*[@id="content_preview_settings_contact"]')
|
||||
);
|
||||
|
||||
$pageTranslated = new Page();
|
||||
$pageTranslated->setIsPublished(true);
|
||||
$pageTranslated->setDateAdded(new \DateTime());
|
||||
$pageTranslated->setTitle('Preview settings test - NL translation');
|
||||
$pageTranslated->setAlias('page-trans-nl');
|
||||
$pageTranslated->setTemplate('Blank');
|
||||
$pageTranslated->setCustomHtml('Test Html');
|
||||
$pageTranslated->setLanguage('nl_CW');
|
||||
|
||||
// Add translation relationship to main page
|
||||
$pageMain->addTranslationChild($pageTranslated);
|
||||
$pageTranslated->setTranslationParent($pageMain);
|
||||
|
||||
$pageVariant = new Page();
|
||||
$pageVariant->setIsPublished(true);
|
||||
$pageVariant->setDateAdded(new \DateTime());
|
||||
$pageVariant->setTitle('Preview settings test - B variant');
|
||||
$pageVariant->setAlias('page-variant-b');
|
||||
$pageVariant->setTemplate('Blank');
|
||||
$pageVariant->setCustomHtml('Test Html');
|
||||
$pageVariant->setLanguage('en');
|
||||
|
||||
// Add variant relationship to main page
|
||||
$pageMain->addVariantChild($pageVariant);
|
||||
|
||||
$this->em->persist($pageMain);
|
||||
$this->em->persist($pageTranslated);
|
||||
$this->em->persist($pageVariant);
|
||||
$this->em->flush();
|
||||
|
||||
$crawler = $this->client->request(Request::METHOD_GET, "/s/pages/view/{$mainPageId}");
|
||||
|
||||
// Translation choice is visible
|
||||
self::assertCount(
|
||||
1,
|
||||
$crawler->filterXPath('//*[@id="content_preview_settings_translation"]')
|
||||
);
|
||||
|
||||
self::assertCount(
|
||||
1,
|
||||
$crawler->filterXPath('//*[@id="content_preview_settings_translation"]/option[@value="'.$pageTranslated->getId().'"]')
|
||||
);
|
||||
|
||||
// Variant choice is visible
|
||||
self::assertCount(
|
||||
1,
|
||||
$crawler->filterXPath('//*[@id="content_preview_settings_variant"]')
|
||||
);
|
||||
|
||||
self::assertCount(
|
||||
1,
|
||||
$crawler->filterXPath('//*[@id="content_preview_settings_variant"]/option[@value="'.$pageVariant->getId().'"]')
|
||||
);
|
||||
|
||||
// Contact lookup is visible
|
||||
self::assertCount(
|
||||
1,
|
||||
$crawler->filterXPath('//*[@id="content_preview_settings_contact"]')
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,141 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Mautic\PageBundle\Tests\Controller;
|
||||
|
||||
use Mautic\CoreBundle\Test\MauticMysqlTestCase;
|
||||
use Mautic\LeadBundle\Entity\Tag;
|
||||
use Mautic\PageBundle\Entity\Page;
|
||||
use Mautic\UserBundle\Entity\User;
|
||||
use PHPUnit\Framework\Assert;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
|
||||
class PublicControllerFunctionalTest extends MauticMysqlTestCase
|
||||
{
|
||||
#[\PHPUnit\Framework\Attributes\DataProvider('xssPayloadsProvider')]
|
||||
public function testContactTrackingTagsXss(string $payload, ?string $expectedSanitized): void
|
||||
{
|
||||
$this->logoutUser();
|
||||
|
||||
$page = new Page();
|
||||
$page->setIsPublished(true);
|
||||
$page->setTitle('XSS Test');
|
||||
$page->setAlias('xss-test');
|
||||
$page->setCustomHtml('xss-test');
|
||||
$this->em->persist($page);
|
||||
$this->em->flush();
|
||||
|
||||
$encodedPayload = urlencode($payload);
|
||||
$this->client->request(Request::METHOD_GET, "/xss-test?tags={$encodedPayload}");
|
||||
Assert::assertTrue($this->client->getResponse()->isOk());
|
||||
|
||||
$tagRepository = $this->em->getRepository(Tag::class);
|
||||
$tags = $tagRepository->findAll();
|
||||
|
||||
if ($expectedSanitized) {
|
||||
// Assert that a tag was created
|
||||
Assert::assertCount(1, $tags);
|
||||
|
||||
// Get the created tag
|
||||
$tag = $tags[0];
|
||||
|
||||
// Assert that the tag name does not contain the malicious script
|
||||
Assert::assertStringNotContainsString('<script>', $tag->getTag());
|
||||
Assert::assertStringNotContainsString('</script>', $tag->getTag());
|
||||
|
||||
// Assert that the tag name has been properly sanitized
|
||||
Assert::assertEquals($expectedSanitized, $tag->getTag());
|
||||
} else {
|
||||
// Assert that a tag was NOT created
|
||||
Assert::assertCount(0, $tags);
|
||||
}
|
||||
|
||||
// Check the response content to ensure no script is present
|
||||
$content = $this->client->getResponse()->getContent();
|
||||
Assert::assertStringNotContainsString($payload, $content);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, array<int, string|null>>
|
||||
*/
|
||||
public static function xssPayloadsProvider(): array
|
||||
{
|
||||
return [
|
||||
'Basic script tag' => [
|
||||
'<script>alert(1)</script>',
|
||||
'alert(1)',
|
||||
],
|
||||
'Script tag with attributes' => [
|
||||
'<script src="http://example.com/evil.js"></script>',
|
||||
null,
|
||||
],
|
||||
'Encoded script tag' => [
|
||||
'<script>alert(1)</script>',
|
||||
'alert(1)',
|
||||
],
|
||||
'On-event handler' => [
|
||||
'<img src="x" onerror="alert(1)">',
|
||||
null,
|
||||
],
|
||||
'JavaScript protocol in URL' => [
|
||||
'<a href="javascript:alert(1)">Click me</a>',
|
||||
'Click me',
|
||||
],
|
||||
'SVG with embedded script' => [
|
||||
'<svg><script>alert(1)</script></svg>',
|
||||
'alert(1)',
|
||||
],
|
||||
'CSS expression' => [
|
||||
'<div style="background:url(javascript:alert(1))">',
|
||||
null,
|
||||
],
|
||||
'Malformed tag' => [
|
||||
'<img """><script>alert("XSS")</script>"<',
|
||||
'alert("XSS")"',
|
||||
],
|
||||
'Malformed tag2' => [
|
||||
'<IMG SRC="jav	ascript:alert(\'XSS\');">',
|
||||
null,
|
||||
],
|
||||
'Unicode escape' => [
|
||||
'<script>\u0061lert(1)</script>',
|
||||
'\u0061lert(1)',
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
public function testMtcEventCompanyXss(): void
|
||||
{
|
||||
$this->logoutUser();
|
||||
|
||||
$this->client->request('POST', '/mtc/event', [
|
||||
'page_url' => 'https://example.com?Company=%3Cimg+src+onerror%3Dalert%28%27Company%27%29%3E',
|
||||
]);
|
||||
$this->assertResponseIsSuccessful();
|
||||
|
||||
$this->loginUser($this->em->getRepository(User::class)->findOneBy(['username' => 'admin']));
|
||||
|
||||
$response = json_decode($this->client->getResponse()->getContent(), true);
|
||||
|
||||
$this->client->request('GET', sprintf('/s/contacts/view/%d', $response['id']));
|
||||
$this->assertResponseIsSuccessful();
|
||||
$content = $this->client->getResponse()->getContent();
|
||||
|
||||
Assert::assertStringNotContainsString('<img src onerror=alert(\'Company\')>', $content);
|
||||
|
||||
$crawler = $this->client->request('GET', sprintf('/s/contacts/edit/%d', $response['id']));
|
||||
$this->assertResponseIsSuccessful();
|
||||
$content = $this->client->getResponse()->getContent();
|
||||
|
||||
Assert::assertStringNotContainsString('<img src onerror=alert(\'Company\')>', $content);
|
||||
|
||||
$buttonCrawlerNode = $crawler->selectButton('Save & Close');
|
||||
Assert::assertCount(1, $buttonCrawlerNode, $crawler->html());
|
||||
$form = $buttonCrawlerNode->form();
|
||||
$this->client->submit($form);
|
||||
$this->assertResponseIsSuccessful();
|
||||
$content = $this->client->getResponse()->getContent();
|
||||
Assert::assertStringNotContainsString('<img src onerror=alert(\'Company\')>', $content);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Mautic\PageBundle\Tests\Controller;
|
||||
|
||||
use Mautic\CoreBundle\Test\MauticMysqlTestCase;
|
||||
use Mautic\PageBundle\Entity\Page;
|
||||
use Mautic\PageBundle\Entity\Redirect;
|
||||
use PHPUnit\Framework\Assert;
|
||||
use Symfony\Component\HttpFoundation\RedirectResponse;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
class PublicControllerRedirectTest extends MauticMysqlTestCase
|
||||
{
|
||||
#[\PHPUnit\Framework\Attributes\DataProvider('redirectTypeOptions')]
|
||||
public function testValidationRedirectWithoutUrl(string $redirectUrl, string $expectedMessage): void
|
||||
{
|
||||
$crawler = $this->client->request(Request::METHOD_GET, '/s/pages/new');
|
||||
$saveButton = $crawler->selectButton('Save');
|
||||
$form = $saveButton->form();
|
||||
$form['page[title]']->setValue('Redirect test');
|
||||
$form['page[redirectType]']->setValue((string) Response::HTTP_MOVED_PERMANENTLY);
|
||||
$form['page[redirectUrl]']->setValue($redirectUrl);
|
||||
$form['page[template]']->setValue('mautic_code_mode');
|
||||
|
||||
$this->client->submit($form);
|
||||
|
||||
Assert::assertStringContainsString($expectedMessage, $this->client->getResponse()->getContent());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return iterable<string, array{string, string}>
|
||||
*/
|
||||
public static function redirectTypeOptions(): iterable
|
||||
{
|
||||
yield 'redirect set, empty redirect URL' => ['', 'A value is required.'];
|
||||
yield 'redirect set, invalid redirect URL' => ['invalid.url', 'This value is not a valid URL.'];
|
||||
yield 'redirect set, valid redirect URL' => ['https://valid.url', 'Edit Page - Redirect test'];
|
||||
}
|
||||
|
||||
public function testCreateRedirectWithNoUrlForExistingPages(): void
|
||||
{
|
||||
$page = new Page();
|
||||
$page->setTitle('Page A');
|
||||
$page->setAlias('page-a');
|
||||
$page->setIsPublished(false);
|
||||
$page->setRedirectType((string) Response::HTTP_MOVED_PERMANENTLY);
|
||||
$this->em->persist($page);
|
||||
$this->em->flush();
|
||||
|
||||
$this->logoutUser();
|
||||
|
||||
$this->client->request(Request::METHOD_GET, '/page-a');
|
||||
|
||||
Assert::assertSame(Response::HTTP_NOT_FOUND, $this->client->getResponse()->getStatusCode());
|
||||
}
|
||||
|
||||
public function testRedirectWithSpacesInQuery(): void
|
||||
{
|
||||
$url = 'https://google.com?q=this%20has%20spaces';
|
||||
$redirect = new Redirect();
|
||||
$redirect->setUrl($url);
|
||||
$redirect->setRedirectId('57cf5a66a9f9414f301082cf0');
|
||||
$this->em->persist($redirect);
|
||||
$this->em->flush();
|
||||
|
||||
$this->client->followRedirects(false);
|
||||
$this->client->request(Request::METHOD_GET, sprintf('/r/%s', $redirect->getRedirectId()));
|
||||
|
||||
$response = $this->client->getResponse();
|
||||
\assert($response instanceof RedirectResponse);
|
||||
Assert::assertSame(Response::HTTP_FOUND, $response->getStatusCode());
|
||||
Assert::assertSame($url, $response->getTargetUrl(), 'The spaces in the query part must not be encoded with plus signs.');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,702 @@
|
||||
<?php
|
||||
|
||||
namespace Mautic\PageBundle\Tests\Controller;
|
||||
|
||||
use Doctrine\Persistence\ManagerRegistry;
|
||||
use Mautic\CoreBundle\Entity\IpAddress;
|
||||
use Mautic\CoreBundle\Exception\InvalidDecodedStringException;
|
||||
use Mautic\CoreBundle\Factory\ModelFactory;
|
||||
use Mautic\CoreBundle\Helper\CookieHelper;
|
||||
use Mautic\CoreBundle\Helper\CoreParametersHelper;
|
||||
use Mautic\CoreBundle\Helper\IpLookupHelper;
|
||||
use Mautic\CoreBundle\Helper\ThemeHelper;
|
||||
use Mautic\CoreBundle\Helper\UserHelper;
|
||||
use Mautic\CoreBundle\Security\Permissions\CorePermissions;
|
||||
use Mautic\CoreBundle\Service\FlashBag;
|
||||
use Mautic\CoreBundle\Test\MauticMysqlTestCase;
|
||||
use Mautic\CoreBundle\Translation\Translator;
|
||||
use Mautic\CoreBundle\Twig\Helper\AnalyticsHelper;
|
||||
use Mautic\CoreBundle\Twig\Helper\AssetsHelper;
|
||||
use Mautic\LeadBundle\Entity\Lead;
|
||||
use Mautic\LeadBundle\Helper\ContactRequestHelper;
|
||||
use Mautic\LeadBundle\Helper\PrimaryCompanyHelper;
|
||||
use Mautic\LeadBundle\Model\LeadModel;
|
||||
use Mautic\LeadBundle\Tracker\ContactTracker;
|
||||
use Mautic\LeadBundle\Tracker\Service\DeviceTrackingService\DeviceTrackingServiceInterface;
|
||||
use Mautic\PageBundle\Controller\PublicController;
|
||||
use Mautic\PageBundle\Entity\Page;
|
||||
use Mautic\PageBundle\Entity\Redirect;
|
||||
use Mautic\PageBundle\Event\TrackingEvent;
|
||||
use Mautic\PageBundle\Helper\TrackingHelper;
|
||||
use Mautic\PageBundle\Model\PageModel;
|
||||
use Mautic\PageBundle\Model\RedirectModel;
|
||||
use Mautic\PageBundle\Model\Tracking404Model;
|
||||
use Mautic\PageBundle\PageEvents;
|
||||
use PHPUnit\Framework\Attributes\DataProvider;
|
||||
use PHPUnit\Framework\MockObject\MockObject;
|
||||
use Symfony\Component\Asset\Packages;
|
||||
use Symfony\Component\DependencyInjection\Container;
|
||||
use Symfony\Component\EventDispatcher\EventDispatcher;
|
||||
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\RequestStack;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\Routing\Router;
|
||||
use Symfony\Component\Routing\RouterInterface;
|
||||
|
||||
#[\PHPUnit\Framework\Attributes\CoversClass(TrackingEvent::class)]
|
||||
class PublicControllerTest extends MauticMysqlTestCase
|
||||
{
|
||||
/**
|
||||
* @var MockObject|Container
|
||||
*/
|
||||
private MockObject $internalContainer;
|
||||
|
||||
/**
|
||||
* @var \Psr\Log\LoggerInterface
|
||||
*/
|
||||
private MockObject $logger;
|
||||
|
||||
/**
|
||||
* @var ModelFactory<object>&MockObject
|
||||
*/
|
||||
private MockObject $modelFactory;
|
||||
|
||||
/**
|
||||
* @var RedirectModel
|
||||
*/
|
||||
private MockObject $redirectModel;
|
||||
|
||||
/**
|
||||
* @var Redirect
|
||||
*/
|
||||
private MockObject $redirect;
|
||||
|
||||
private Request $request;
|
||||
|
||||
/**
|
||||
* @var IpLookupHelper
|
||||
*/
|
||||
private MockObject $ipLookupHelper;
|
||||
|
||||
/**
|
||||
* @var IpAddress
|
||||
*/
|
||||
private MockObject $ipAddress;
|
||||
|
||||
/**
|
||||
* @var LeadModel
|
||||
*/
|
||||
private MockObject $leadModel;
|
||||
|
||||
/**
|
||||
* @var PageModel
|
||||
*/
|
||||
private MockObject $pageModel;
|
||||
|
||||
/**
|
||||
* @var PrimaryCompanyHelper
|
||||
*/
|
||||
private MockObject $primaryCompanyHelper;
|
||||
|
||||
/**
|
||||
* @var ContactRequestHelper&MockObject
|
||||
*/
|
||||
private MockObject $contactRequestHelper;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
$this->request = new Request();
|
||||
$this->internalContainer = $this->createMock(Container::class);
|
||||
$this->logger = $this->createMock(\Psr\Log\LoggerInterface::class);
|
||||
$this->modelFactory = $this->createMock(ModelFactory::class);
|
||||
$this->redirectModel = $this->createMock(RedirectModel::class);
|
||||
$this->redirect = $this->createMock(Redirect::class);
|
||||
$this->ipLookupHelper = $this->createMock(IpLookupHelper::class);
|
||||
$this->ipAddress = $this->createMock(IpAddress::class);
|
||||
$this->leadModel = $this->createMock(LeadModel::class);
|
||||
$this->pageModel = $this->createMock(PageModel::class);
|
||||
$this->primaryCompanyHelper = $this->createMock(PrimaryCompanyHelper::class);
|
||||
$this->contactRequestHelper = $this->createMock(ContactRequestHelper::class);
|
||||
|
||||
parent::setUp();
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that the appropriate variant is displayed based on hit counts and variant weights.
|
||||
*/
|
||||
public function testVariantPageWeightsAreAppropriate(): void
|
||||
{
|
||||
// Each of these should return the one with the greatest weight deficit based on
|
||||
// A = 50%
|
||||
// B = 25%
|
||||
// C = 25%
|
||||
|
||||
// A = 0/50; B = 0/25; C = 0/25
|
||||
$this->assertEquals('pageA', $this->getVariantContent(0, 0, 0));
|
||||
|
||||
// A = 100/50; B = 0/25; C = 0/25
|
||||
$this->assertEquals('pageB', $this->getVariantContent(1, 0, 0));
|
||||
|
||||
// A = 50/50; B = 50/25; C = 0/25;
|
||||
$this->assertEquals('pageC', $this->getVariantContent(1, 1, 0));
|
||||
|
||||
// A = 33/50; B = 33/25; C = 33/25;
|
||||
$this->assertEquals('pageA', $this->getVariantContent(1, 1, 1));
|
||||
|
||||
// A = 66/50; B = 33/25; C = 0/25
|
||||
$this->assertEquals('pageC', $this->getVariantContent(2, 1, 0));
|
||||
|
||||
// A = 50/50; B = 25/25; C = 25/25
|
||||
$this->assertEquals('pageA', $this->getVariantContent(2, 1, 1));
|
||||
|
||||
// A = 33/50; B = 66/50; C = 0/25
|
||||
$this->assertEquals('pageC', $this->getVariantContent(1, 2, 0));
|
||||
|
||||
// A = 25/50; B = 50/50; C = 25/25
|
||||
$this->assertEquals('pageA', $this->getVariantContent(1, 2, 1));
|
||||
|
||||
// A = 55/50; B = 18/25; C = 27/25
|
||||
$this->assertEquals('pageB', $this->getVariantContent(6, 2, 3));
|
||||
|
||||
// A = 50/50; B = 25/25; C = 25/25
|
||||
$this->assertEquals('pageA', $this->getVariantContent(6, 3, 3));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
private function getVariantContent($aCount, $bCount, $cCount)
|
||||
{
|
||||
$pageEntityB = $this->createMock(Page::class);
|
||||
$pageEntityB->method('getId')
|
||||
->willReturn(2);
|
||||
$pageEntityB->method('isPublished')
|
||||
->willReturn(true);
|
||||
$pageEntityB->method('getVariantHits')
|
||||
->willReturn($bCount);
|
||||
$pageEntityB->method('getTranslations')
|
||||
->willReturn([]);
|
||||
$pageEntityB->method('isTranslation')
|
||||
->willReturn(false);
|
||||
$pageEntityB->method('getContent')
|
||||
->willReturn(null);
|
||||
$pageEntityB->method('getCustomHtml')
|
||||
->willReturn('pageB');
|
||||
$pageEntityB->method('getVariantSettings')
|
||||
->willReturn(['weight' => '25']);
|
||||
|
||||
$pageEntityC = $this->createMock(Page::class);
|
||||
$pageEntityC->method('getId')
|
||||
->willReturn(3);
|
||||
$pageEntityC->method('isPublished')
|
||||
->willReturn(true);
|
||||
$pageEntityC->method('getVariantHits')
|
||||
->willReturn($cCount);
|
||||
$pageEntityC->method('getTranslations')
|
||||
->willReturn([]);
|
||||
$pageEntityC->method('isTranslation')
|
||||
->willReturn(false);
|
||||
$pageEntityC->method('getContent')
|
||||
->willReturn(null);
|
||||
$pageEntityC->method('getCustomHtml')
|
||||
->willReturn('pageC');
|
||||
$pageEntityC->method('getVariantSettings')
|
||||
->willReturn(['weight' => '25']);
|
||||
|
||||
$pageEntityA = $this->createMock(Page::class);
|
||||
$pageEntityA->method('getId')
|
||||
->willReturn(1);
|
||||
$pageEntityA->method('isPublished')
|
||||
->willReturn(true);
|
||||
$pageEntityA->method('getVariants')
|
||||
->willReturn([$pageEntityA, [2 => $pageEntityB, 3 => $pageEntityC]]);
|
||||
$pageEntityA->method('getVariantHits')
|
||||
->willReturn($aCount);
|
||||
$pageEntityA->method('getTranslations')
|
||||
->willReturn([]);
|
||||
$pageEntityA->method('isTranslation')
|
||||
->willReturn(false);
|
||||
$pageEntityA->method('getContent')
|
||||
->willReturn(null);
|
||||
$pageEntityA->method('getCustomHtml')
|
||||
->willReturn('pageA');
|
||||
$pageEntityA->method('getVariantSettings')
|
||||
->willReturn(['weight' => '50']);
|
||||
|
||||
$cookieHelper = $this->createMock(CookieHelper::class);
|
||||
|
||||
/** @var Packages&MockObject $packagesMock */
|
||||
$packagesMock = $this->createMock(Packages::class);
|
||||
|
||||
/** @var CoreParametersHelper&MockObject $coreParametersHelper */
|
||||
$coreParametersHelper = $this->createMock(CoreParametersHelper::class);
|
||||
|
||||
$assetHelper = new AssetsHelper($packagesMock);
|
||||
|
||||
$mauticSecurity = $this->createMock(CorePermissions::class);
|
||||
$mauticSecurity->method('hasEntityAccess')
|
||||
->willReturn(false);
|
||||
|
||||
$analyticsHelper = new AnalyticsHelper($coreParametersHelper);
|
||||
|
||||
$pageModel = $this->createMock(PageModel::class);
|
||||
$pageModel->method('getHitQuery')
|
||||
->willReturn([]);
|
||||
$pageModel->method('getEntityBySlugs')
|
||||
->willReturn($pageEntityA);
|
||||
$pageModel->method('hitPage')
|
||||
->willReturn(true);
|
||||
|
||||
$this->contactRequestHelper->method('getContactFromQuery')
|
||||
->willReturn(new Lead());
|
||||
|
||||
$router = $this->createMock(Router::class);
|
||||
|
||||
$dispatcher = new EventDispatcher();
|
||||
|
||||
$modelFactory = $this->createMock(ModelFactory::class);
|
||||
$modelFactory->method('getModel')
|
||||
->willReturnMap(
|
||||
[
|
||||
['page', $pageModel],
|
||||
['lead', $this->leadModel],
|
||||
]
|
||||
);
|
||||
|
||||
$container = $this->createMock(Container::class);
|
||||
$container->expects(self::never())
|
||||
->method('has');
|
||||
$container->expects(self::never())
|
||||
->method('get');
|
||||
|
||||
$this->request->attributes->set('ignore_mismatch', true);
|
||||
|
||||
$router = $this->createMock(RouterInterface::class);
|
||||
$doctrine = $this->createMock(ManagerRegistry::class);
|
||||
$userHelper = $this->createMock(UserHelper::class);
|
||||
$coreParametersHelper = $this->createMock(CoreParametersHelper::class);
|
||||
$translator = $this->createMock(Translator::class);
|
||||
$flashBag = $this->createMock(FlashBag::class);
|
||||
$themeHelper = $this->createMock(ThemeHelper::class);
|
||||
$themeHelper->expects(self::never())
|
||||
->method('checkForTwigTemplate');
|
||||
$requestStack = new RequestStack();
|
||||
|
||||
$controller = new PublicController(
|
||||
$doctrine,
|
||||
$modelFactory,
|
||||
$userHelper,
|
||||
$coreParametersHelper,
|
||||
$dispatcher,
|
||||
$translator,
|
||||
$flashBag,
|
||||
$requestStack,
|
||||
$mauticSecurity
|
||||
);
|
||||
$controller->setContainer($container);
|
||||
|
||||
$response = $controller->indexAction(
|
||||
$this->request,
|
||||
$this->contactRequestHelper,
|
||||
$cookieHelper,
|
||||
$analyticsHelper,
|
||||
$assetHelper,
|
||||
$themeHelper,
|
||||
$this->createMock(Tracking404Model::class),
|
||||
$router,
|
||||
$this->createMock(DeviceTrackingServiceInterface::class),
|
||||
'/page/a',
|
||||
);
|
||||
|
||||
return $response->getContent();
|
||||
}
|
||||
|
||||
public function testThatInvalidClickTroughGetsProcessed(): void
|
||||
{
|
||||
$redirectId = 'someRedirectId';
|
||||
$clickTrough = 'someClickTroughValue';
|
||||
$redirectUrl = 'https://someurl.test/';
|
||||
|
||||
$this->redirectModel->expects(self::once())
|
||||
->method('getRedirectById')
|
||||
->with($redirectId)
|
||||
->willReturn($this->redirect);
|
||||
$matcher = self::exactly(2);
|
||||
|
||||
$this->modelFactory->expects($matcher)
|
||||
->method('getModel')->willReturnCallback(function (...$parameters) use ($matcher) {
|
||||
if (1 === $matcher->numberOfInvocations()) {
|
||||
$this->assertSame(RedirectModel::class, $parameters[0]);
|
||||
|
||||
return $this->redirectModel;
|
||||
}
|
||||
if (2 === $matcher->numberOfInvocations()) {
|
||||
$this->assertSame(PageModel::class, $parameters[0]);
|
||||
|
||||
return $this->pageModel;
|
||||
}
|
||||
|
||||
self::fail('The index '.$matcher->numberOfInvocations().' is not set.');
|
||||
});
|
||||
|
||||
$this->redirect->expects(self::once())
|
||||
->method('isPublished')
|
||||
->with(false)
|
||||
->willReturn(true);
|
||||
|
||||
$this->redirect->expects(self::once())
|
||||
->method('getUrl')
|
||||
->willReturn($redirectUrl);
|
||||
|
||||
$this->ipLookupHelper->expects(self::once())
|
||||
->method('getIpAddress')
|
||||
->willReturn($this->ipAddress);
|
||||
|
||||
$this->ipAddress->expects(self::once())
|
||||
->method('isTrackable')
|
||||
->willReturn(true);
|
||||
|
||||
$getContactFromRequestCallback = function ($queryFields) use ($clickTrough) {
|
||||
if (empty($queryFields)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
throw new InvalidDecodedStringException($clickTrough);
|
||||
};
|
||||
|
||||
$this->contactRequestHelper->expects(self::exactly(2))
|
||||
->method('getContactFromQuery')
|
||||
->willReturnCallback($getContactFromRequestCallback);
|
||||
|
||||
$routerMock = $this->createMock(Router::class);
|
||||
$routerMock->expects(self::once())
|
||||
->method('generate')
|
||||
->willReturn('/asset/');
|
||||
|
||||
$this->internalContainer
|
||||
->expects(self::once())
|
||||
->method('get')
|
||||
->willReturnMap([
|
||||
['router', Container::EXCEPTION_ON_INVALID_REFERENCE, $routerMock],
|
||||
]);
|
||||
|
||||
$this->request->query->set('ct', $clickTrough);
|
||||
|
||||
$doctrine = $this->createMock(ManagerRegistry::class);
|
||||
$userHelper = $this->createMock(UserHelper::class);
|
||||
$coreParametersHelper = $this->createMock(CoreParametersHelper::class);
|
||||
$dispatcher = $this->createMock(EventDispatcherInterface::class);
|
||||
$translator = $this->createMock(Translator::class);
|
||||
$flashBag = $this->createMock(FlashBag::class);
|
||||
$requestStack = new RequestStack();
|
||||
$mauticSecurity = $this->createMock(CorePermissions::class);
|
||||
|
||||
$controller = new PublicController(
|
||||
$doctrine,
|
||||
$this->modelFactory,
|
||||
$userHelper,
|
||||
$coreParametersHelper,
|
||||
$dispatcher,
|
||||
$translator,
|
||||
$flashBag,
|
||||
$requestStack,
|
||||
$mauticSecurity
|
||||
);
|
||||
$controller->setContainer($this->internalContainer);
|
||||
|
||||
$response = $controller->redirectAction(
|
||||
$this->request,
|
||||
$this->contactRequestHelper,
|
||||
$this->primaryCompanyHelper,
|
||||
$this->ipLookupHelper,
|
||||
$this->logger,
|
||||
$redirectId
|
||||
);
|
||||
|
||||
self::assertSame('https://someurl.test/', $response->getTargetUrl());
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws \Exception
|
||||
*/
|
||||
#[DataProvider('provideRedirectUrls')]
|
||||
public function testAssetRedirectUrlWithClickThrough(string $redirectUrl, string $targetUrl): void
|
||||
{
|
||||
$redirectId = 'dummy_redirect_id';
|
||||
$clickThrough = 'dummy_click_through';
|
||||
|
||||
$this->redirectModel->expects(self::once())
|
||||
->method('getRedirectById')
|
||||
->with($redirectId)
|
||||
->willReturn($this->redirect);
|
||||
$matcher = self::exactly(2);
|
||||
|
||||
$this->modelFactory->expects($matcher)
|
||||
->method('getModel')->willReturnCallback(function (...$parameters) use ($matcher) {
|
||||
if (1 === $matcher->numberOfInvocations()) {
|
||||
$this->assertSame(RedirectModel::class, $parameters[0]);
|
||||
|
||||
return $this->redirectModel;
|
||||
}
|
||||
if (2 === $matcher->numberOfInvocations()) {
|
||||
$this->assertSame(PageModel::class, $parameters[0]);
|
||||
|
||||
return $this->pageModel;
|
||||
}
|
||||
|
||||
self::fail('Unknown invocation.');
|
||||
});
|
||||
|
||||
$this->redirect->expects(self::once())
|
||||
->method('isPublished')
|
||||
->with(false)
|
||||
->willReturn(true);
|
||||
|
||||
$this->redirect->expects(self::once())
|
||||
->method('getUrl')
|
||||
->willReturn($redirectUrl);
|
||||
|
||||
$this->ipLookupHelper->expects(self::once())
|
||||
->method('getIpAddress')
|
||||
->willReturn($this->ipAddress);
|
||||
|
||||
$this->ipAddress->expects(self::once())
|
||||
->method('isTrackable')
|
||||
->willReturn(true);
|
||||
|
||||
$getContactFromRequestCallback = function ($queryFields) use ($clickThrough) {
|
||||
if (empty($queryFields)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
throw new InvalidDecodedStringException($clickThrough);
|
||||
};
|
||||
|
||||
$this->contactRequestHelper->expects(self::exactly(2))
|
||||
->method('getContactFromQuery')
|
||||
->willReturnCallback($getContactFromRequestCallback);
|
||||
|
||||
$routerMock = $this->createMock(Router::class);
|
||||
$routerMock->expects(self::once())
|
||||
->method('generate')
|
||||
->with('mautic_asset_download')
|
||||
->willReturn('/asset');
|
||||
|
||||
$this->internalContainer
|
||||
->expects(self::once())
|
||||
->method('get')
|
||||
->willReturnMap([
|
||||
['router', Container::EXCEPTION_ON_INVALID_REFERENCE, $routerMock],
|
||||
]);
|
||||
|
||||
$this->request->query->set('ct', $clickThrough);
|
||||
|
||||
$doctrine = $this->createMock(ManagerRegistry::class);
|
||||
$userHelper = $this->createMock(UserHelper::class);
|
||||
$coreParametersHelper = $this->createMock(CoreParametersHelper::class);
|
||||
$dispatcher = $this->createMock(EventDispatcherInterface::class);
|
||||
$translator = $this->createMock(Translator::class);
|
||||
$flashBag = $this->createMock(FlashBag::class);
|
||||
$requestStack = new RequestStack();
|
||||
$mauticSecurity = $this->createMock(CorePermissions::class);
|
||||
|
||||
$controller = new PublicController(
|
||||
$doctrine,
|
||||
$this->modelFactory,
|
||||
$userHelper,
|
||||
$coreParametersHelper,
|
||||
$dispatcher,
|
||||
$translator,
|
||||
$flashBag,
|
||||
$requestStack,
|
||||
$mauticSecurity
|
||||
);
|
||||
$controller->setContainer($this->internalContainer);
|
||||
|
||||
$response = $controller->redirectAction(
|
||||
$this->request,
|
||||
$this->contactRequestHelper,
|
||||
$this->primaryCompanyHelper,
|
||||
$this->ipLookupHelper,
|
||||
$this->logger,
|
||||
$redirectId
|
||||
);
|
||||
self::assertSame($targetUrl, $response->getTargetUrl());
|
||||
self::assertSame(Response::HTTP_FOUND, $response->getStatusCode());
|
||||
}
|
||||
|
||||
public static function provideRedirectUrls(): \Generator
|
||||
{
|
||||
yield 'No query parameters' => [
|
||||
'https://some.test.url/asset/1:examplefilejpg',
|
||||
'https://some.test.url/asset/1:examplefilejpg?ct=dummy_click_through',
|
||||
];
|
||||
|
||||
yield 'With query parameter' => [
|
||||
'https://some.test.url/asset/1:examplefilejpg?param=value',
|
||||
'https://some.test.url/asset/1:examplefilejpg?param=value&ct=dummy_click_through',
|
||||
];
|
||||
|
||||
yield 'With click-through parameter' => [
|
||||
'https://some.test.url/asset/1:examplefilejpg?ct=parameter',
|
||||
'https://some.test.url/asset/1:examplefilejpg?ct=dummy_click_through',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function testMtcTrackingEvent(): void
|
||||
{
|
||||
$request = new Request(
|
||||
[
|
||||
'foo' => 'bar',
|
||||
]
|
||||
);
|
||||
|
||||
$contact = new Lead();
|
||||
$contact->setEmail('foo@bar.com');
|
||||
|
||||
$mtcSessionEventArray = ['mtc' => 'foobar'];
|
||||
|
||||
$event = new TrackingEvent($contact, $request, $mtcSessionEventArray);
|
||||
$eventDispatcher = $this->createMock(EventDispatcherInterface::class);
|
||||
$eventDispatcher->expects($this->once())
|
||||
->method('dispatch')
|
||||
->with($event, PageEvents::ON_CONTACT_TRACKED)
|
||||
->willReturnCallback(
|
||||
function (TrackingEvent $event) {
|
||||
$contact = $event->getContact()->getEmail();
|
||||
$request = $event->getRequest();
|
||||
$response = $event->getResponse();
|
||||
|
||||
$response->set('tracking', $contact);
|
||||
$response->set('foo', $request->get('foo'));
|
||||
|
||||
return $event;
|
||||
}
|
||||
);
|
||||
|
||||
$security = $this->createMock(CorePermissions::class);
|
||||
$security->expects($this->once())
|
||||
->method('isAnonymous')
|
||||
->willReturn(true);
|
||||
|
||||
$pageModel = $this->createMock(PageModel::class);
|
||||
$modelFactory = $this->createMock(ModelFactory::class);
|
||||
$modelFactory->expects($this->once())
|
||||
->method('getModel')
|
||||
->with('page')
|
||||
->willReturn($pageModel);
|
||||
|
||||
$deviceTrackingService = $this->createMock(DeviceTrackingServiceInterface::class);
|
||||
|
||||
$trackingHelper = $this->createMock(TrackingHelper::class);
|
||||
$trackingHelper->expects($this->once())
|
||||
->method('getCacheItem')
|
||||
->willReturn($mtcSessionEventArray);
|
||||
|
||||
$contactTracker = $this->createMock(ContactTracker::class);
|
||||
$contactTracker->method('getContact')
|
||||
->willReturn($contact);
|
||||
|
||||
$doctrine = $this->createMock(ManagerRegistry::class);
|
||||
$userHelper = $this->createMock(UserHelper::class);
|
||||
$coreParametersHelper = $this->createMock(CoreParametersHelper::class);
|
||||
$translator = $this->createMock(Translator::class);
|
||||
$flashBag = $this->createMock(FlashBag::class);
|
||||
$requestStack = new RequestStack();
|
||||
|
||||
$publicController = new PublicController(
|
||||
$doctrine,
|
||||
$modelFactory,
|
||||
$userHelper,
|
||||
$coreParametersHelper,
|
||||
$eventDispatcher,
|
||||
$translator,
|
||||
$flashBag,
|
||||
$requestStack,
|
||||
$security
|
||||
);
|
||||
|
||||
$response = $publicController->trackingAction(
|
||||
$request,
|
||||
$deviceTrackingService,
|
||||
$trackingHelper,
|
||||
$contactTracker
|
||||
);
|
||||
|
||||
$json = json_decode($response->getContent(), true);
|
||||
|
||||
$this->assertEquals(
|
||||
[
|
||||
'mtc' => 'foobar',
|
||||
'tracking' => 'foo@bar.com',
|
||||
'foo' => 'bar',
|
||||
],
|
||||
$json['events']
|
||||
);
|
||||
}
|
||||
|
||||
public function testTrackingActionWithInvalidCt(): void
|
||||
{
|
||||
$request = new Request();
|
||||
|
||||
$pageModel = $this->createMock(PageModel::class);
|
||||
$pageModel->expects($this->once())->method('hitPage')->willReturnCallback(
|
||||
function (): void {
|
||||
throw new InvalidDecodedStringException();
|
||||
}
|
||||
);
|
||||
|
||||
$modelFactory = $this->createMock(ModelFactory::class);
|
||||
$modelFactory->expects($this->once())
|
||||
->method('getModel')
|
||||
->with('page')
|
||||
->willReturn($pageModel);
|
||||
|
||||
$security = $this->createMock(CorePermissions::class);
|
||||
$security->expects($this->once())
|
||||
->method('isAnonymous')
|
||||
->willReturn(true);
|
||||
|
||||
$doctrine = $this->createMock(ManagerRegistry::class);
|
||||
$userHelper = $this->createMock(UserHelper::class);
|
||||
$coreParametersHelper = $this->createMock(CoreParametersHelper::class);
|
||||
$dispatcher = $this->createMock(EventDispatcherInterface::class);
|
||||
$translator = $this->createMock(Translator::class);
|
||||
$flashBag = $this->createMock(FlashBag::class);
|
||||
$requestStack = new RequestStack();
|
||||
|
||||
$publicController = new PublicController(
|
||||
$doctrine,
|
||||
$modelFactory,
|
||||
$userHelper,
|
||||
$coreParametersHelper,
|
||||
$dispatcher,
|
||||
$translator,
|
||||
$flashBag,
|
||||
$requestStack,
|
||||
$security
|
||||
);
|
||||
|
||||
$response = $publicController->trackingAction(
|
||||
$request,
|
||||
$this->createMock(DeviceTrackingServiceInterface::class),
|
||||
$this->createMock(TrackingHelper::class),
|
||||
$this->createMock(ContactTracker::class)
|
||||
);
|
||||
$this->assertEquals(
|
||||
['success' => 0],
|
||||
json_decode($response->getContent(), true)
|
||||
);
|
||||
}
|
||||
|
||||
public function testTrackingImageAction(): void
|
||||
{
|
||||
$this->client->request('GET', '/mtracking.gif?url=http%3A%2F%2Fmautic.org');
|
||||
|
||||
$this->assertResponseStatusCodeSame(200);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Mautic\PageBundle\Tests\Controller;
|
||||
|
||||
use Mautic\CoreBundle\Test\MauticMysqlTestCase;
|
||||
use Mautic\PageBundle\Entity\Hit;
|
||||
use Mautic\PageBundle\Entity\HitRepository;
|
||||
use Mautic\PageBundle\Entity\Page;
|
||||
use PHPUnit\Framework\Assert;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
|
||||
class VisitPageWitIpAnonymizationOffFunctionalTest extends MauticMysqlTestCase
|
||||
{
|
||||
protected function setUp(): void
|
||||
{
|
||||
$this->configParams['anonymize_ip'] = false;
|
||||
|
||||
parent::setUp();
|
||||
}
|
||||
|
||||
public function testPageWithIpAnonymizationOff(): void
|
||||
{
|
||||
// create landing page
|
||||
$pageObject = new Page();
|
||||
$pageObject->setIsPublished(true);
|
||||
$pageObject->setDateAdded(new \DateTime());
|
||||
$pageObject->setTitle('Page:Page:Anonymization:Off');
|
||||
$pageObject->setAlias('page-page-anonymizaiton-off');
|
||||
$pageObject->setTemplate('Blank');
|
||||
$pageObject->setCustomHtml('Test Html');
|
||||
$pageObject->setLanguage('en');
|
||||
$this->em->persist($pageObject);
|
||||
$this->em->flush();
|
||||
|
||||
$this->logoutUser();
|
||||
$pageContent = $this->client->request(Request::METHOD_GET, '/page-page-anonymizaiton-off');
|
||||
|
||||
Assert::assertTrue($this->client->getResponse()->isOk(), $pageContent->text());
|
||||
Assert::assertStringContainsString('Test Html', $pageContent->text());
|
||||
|
||||
/** @var HitRepository $hitRepository */
|
||||
$hitRepository = $this->em->getRepository(Hit::class);
|
||||
|
||||
/** @var Hit[] $hits */
|
||||
$hits = $hitRepository->findBy(['page' => $pageObject->getId()]);
|
||||
Assert::assertCount(1, $hits);
|
||||
Assert::assertSame('127.0.0.1', $hits[0]->getIpAddress()->getIpAddress());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Mautic\PageBundle\Tests\Controller;
|
||||
|
||||
use Mautic\CoreBundle\Test\MauticMysqlTestCase;
|
||||
use Mautic\PageBundle\Entity\Hit;
|
||||
use Mautic\PageBundle\Entity\HitRepository;
|
||||
use Mautic\PageBundle\Entity\Page;
|
||||
use PHPUnit\Framework\Assert;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
|
||||
class VisitPageWitIpAnonymizationOnFunctionalTest extends MauticMysqlTestCase
|
||||
{
|
||||
protected function setUp(): void
|
||||
{
|
||||
$this->configParams['anonymize_ip'] = true;
|
||||
|
||||
parent::setUp();
|
||||
}
|
||||
|
||||
public function testPageWithIpAnonymizationOn(): void
|
||||
{
|
||||
// create landing page
|
||||
$pageObject = new Page();
|
||||
$pageObject->setIsPublished(true);
|
||||
$pageObject->setDateAdded(new \DateTime());
|
||||
$pageObject->setTitle('Page:Page:Anonymization:On');
|
||||
$pageObject->setAlias('page-page-anonymizaiton-on');
|
||||
$pageObject->setTemplate('Blank');
|
||||
$pageObject->setCustomHtml('Test Html');
|
||||
$pageObject->setLanguage('en');
|
||||
$this->em->persist($pageObject);
|
||||
$this->em->flush();
|
||||
|
||||
$this->logoutUser();
|
||||
$pageContent = $this->client->request(Request::METHOD_GET, '/page-page-anonymizaiton-on');
|
||||
|
||||
Assert::assertTrue($this->client->getResponse()->isOk(), $pageContent->text());
|
||||
Assert::assertStringContainsString('Test Html', $pageContent->text());
|
||||
|
||||
/** @var HitRepository $hitRepository */
|
||||
$hitRepository = $this->em->getRepository(Hit::class);
|
||||
|
||||
/** @var Hit[] $hits */
|
||||
$hits = $hitRepository->findBy(['page' => $pageObject->getId()]);
|
||||
Assert::assertCount(1, $hits);
|
||||
Assert::assertSame('*.*.*.*', $hits[0]->getIpAddress()->getIpAddress());
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user