mautic / app /bundles /PageBundle /Tests /Model /TrackableModelTest.php
chrisbryan17's picture
Upload folder using huggingface_hub
d2897cd verified
<?php
namespace Mautic\PageBundle\Tests\Model;
use Doctrine\ORM\EntityManagerInterface;
use Mautic\CoreBundle\Helper\CoreParametersHelper;
use Mautic\CoreBundle\Helper\UserHelper;
use Mautic\CoreBundle\Security\Permissions\CorePermissions;
use Mautic\CoreBundle\Translation\Translator;
use Mautic\LeadBundle\Entity\LeadFieldRepository;
use Mautic\PageBundle\Entity\Redirect;
use Mautic\PageBundle\Entity\Trackable;
use Mautic\PageBundle\Model\RedirectModel;
use Mautic\PageBundle\Model\TrackableModel;
use PHPUnit\Framework\Assert;
use PHPUnit\Framework\TestCase;
use Psr\Log\LoggerInterface;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
class TrackableModelTest extends TestCase
{
/**
* @testdox Test that content is detected as HTML
*
* @covers \Mautic\PageBundle\Model\TrackableModel::extractTrackablesFromHtml
* @covers \Mautic\PageBundle\Model\TrackableModel::parseContentForTrackables
* @covers \Mautic\PageBundle\Model\TrackableModel::createTrackingTokens
* @covers \Mautic\PageBundle\Model\TrackableModel::prepareUrlForTracking
* @covers \Mautic\PageBundle\Model\TrackableModel::prepareContentWithTrackableTokens
*/
public function testHtmlIsDetectedInContent(): void
{
$mockRedirectModel = $this->createMock(RedirectModel::class);
$mockLeadFieldRepository = $this->createMock(LeadFieldRepository::class);
$mockModel = $this->getMockBuilder(TrackableModel::class)
->setConstructorArgs([
$mockRedirectModel,
$mockLeadFieldRepository,
$this->createMock(EntityManagerInterface::class),
$this->createMock(CorePermissions::class),
$this->createMock(EventDispatcherInterface::class),
$this->createMock(UrlGeneratorInterface::class),
$this->createMock(Translator::class),
$this->createMock(UserHelper::class),
$this->createMock(LoggerInterface::class),
$this->createMock(CoreParametersHelper::class),
])
->onlyMethods(['getDoNotTrackList', 'getEntitiesFromUrls', 'createTrackingTokens', 'extractTrackablesFromHtml'])
->getMock();
$mockModel->expects($this->once())
->method('getEntitiesFromUrls')
->willReturn([]);
$mockModel->expects($this->once())
->method('getDoNotTrackList')
->willReturn([]);
$mockModel->expects($this->once())
->method('extractTrackablesFromHtml')
->willReturn(
[
'',
[],
]
);
$mockModel->expects($this->once())
->method('createTrackingTokens')
->willReturn([]);
[$content, $trackables] = $mockModel->parseContentForTrackables(
$this->generateContent('https://foo-bar.com', 'html'),
[],
'email',
1
);
}
/**
* @testdox Test that content is detected as plain text
*
* @covers \Mautic\PageBundle\Model\TrackableModel::extractTrackablesFromText
* @covers \Mautic\PageBundle\Model\TrackableModel::parseContentForTrackables
* @covers \Mautic\PageBundle\Model\TrackableModel::createTrackingTokens
* @covers \Mautic\PageBundle\Model\TrackableModel::prepareUrlForTracking
* @covers \Mautic\PageBundle\Model\TrackableModel::prepareContentWithTrackableTokens
*/
public function testPlainTextIsDetectedInContent(): void
{
$mockRedirectModel = $this->createMock(RedirectModel::class);
$mockLeadFieldRepository = $this->createMock(LeadFieldRepository::class);
$mockModel = $this->getMockBuilder(TrackableModel::class)
->setConstructorArgs([
$mockRedirectModel,
$mockLeadFieldRepository,
$this->createMock(EntityManagerInterface::class),
$this->createMock(CorePermissions::class),
$this->createMock(EventDispatcherInterface::class),
$this->createMock(UrlGeneratorInterface::class),
$this->createMock(Translator::class),
$this->createMock(UserHelper::class),
$this->createMock(LoggerInterface::class),
$this->createMock(CoreParametersHelper::class),
])
->onlyMethods(['getDoNotTrackList', 'getEntitiesFromUrls', 'createTrackingTokens', 'extractTrackablesFromText'])
->getMock();
$mockModel->expects($this->once())
->method('getDoNotTrackList')
->willReturn([]);
$mockModel->expects($this->once())
->method('getEntitiesFromUrls')
->willReturn([]);
$mockModel->expects($this->once())
->method('extractTrackablesFromText')
->willReturn(
[
'',
[],
]
);
$mockModel->expects($this->once())
->method('createTrackingTokens')
->willReturn([]);
[$content, $trackables] = $mockModel->parseContentForTrackables(
$this->generateContent('https://foo-bar.com', 'text'),
[],
'email',
1
);
}
/**
* @testdox Test that a standard link with a standard query is parsed correctly
*
* @covers \Mautic\PageBundle\Model\TrackableModel::parseContentForTrackables
* @covers \Mautic\PageBundle\Model\TrackableModel::extractTrackablesFromHtml
* @covers \Mautic\PageBundle\Model\TrackableModel::extractTrackables
* @covers \Mautic\PageBundle\Model\TrackableModel::createTrackingTokens
* @covers \Mautic\PageBundle\Model\TrackableModel::prepareUrlForTracking
* @covers \Mautic\PageBundle\Model\TrackableModel::prepareContentWithTrackableTokens
*
* @dataProvider trackMapProvider
*/
public function testStandardLinkWithStandardQuery(?bool $useMap): void
{
$url = 'https://foo-bar.com?foo=bar&amp;one=two&three=four&amp;five=six';
$model = $this->getModel();
if (null !== $useMap) {
$emailContent = $this->generateContent($url, 'html', false, $useMap);
} else {
$emailContent = $this->generateContent($url, 'html', false, true)
.$this->generateContent($url, 'html', false, false);
}
[$content, $trackables] = $model->parseContentForTrackables(
$emailContent,
[],
'email',
1
);
$tokenFound = preg_match('/\{trackable=(.*?)\}/', $content, $match);
// Assert that a trackable token exists
Assert::assertTrue((bool) $tokenFound, $content);
// Assert the Trackable exists
Assert::assertArrayHasKey($match[0], $trackables);
// Assert that exactly one trackable found
Assert::assertCount(1, $trackables);
// Assert that the URL redirect equals $url
$redirect = $trackables[$match[0]]->getRedirect();
Assert::assertEquals(str_replace('&amp;', '&', $url), $redirect->getUrl());
}
/**
* @testdox Test that a standard link without a query parses correctly
*
* @covers \Mautic\PageBundle\Model\TrackableModel::parseContentForTrackables
* @covers \Mautic\PageBundle\Model\TrackableModel::extractTrackablesFromHtml
* @covers \Mautic\PageBundle\Model\TrackableModel::extractTrackables
* @covers \Mautic\PageBundle\Model\TrackableModel::createTrackingTokens
* @covers \Mautic\PageBundle\Model\TrackableModel::prepareUrlForTracking
* @covers \Mautic\PageBundle\Model\TrackableModel::prepareContentWithTrackableTokens
*
* @dataProvider trackMapProvider
*/
public function testStandardLinkWithoutQuery(?bool $useMap): void
{
$url = 'https://foo-bar.com';
$model = $this->getModel();
if (null !== $useMap) {
$emailContent = $this->generateContent($url, 'html', false, $useMap);
} else {
$emailContent = $this->generateContent($url, 'html', false, true)
.$this->generateContent($url, 'html', false, false);
}
[$content, $trackables] = $model->parseContentForTrackables(
$emailContent,
[],
'email',
1
);
$tokenFound = preg_match('/\{trackable=(.*?)\}/', $content, $match);
// Assert that a trackable token exists
Assert::assertTrue((bool) $tokenFound, $content);
// Assert the Trackable exists
Assert::assertArrayHasKey($match[0], $trackables);
// Assert that exactly one trackable found
Assert::assertCount(1, $trackables);
// Assert that the URL redirect equals $url
$redirect = $trackables[$match[0]]->getRedirect();
Assert::assertEquals($url, $redirect->getUrl());
}
/**
* @testdox Test that a standard link with a tokenized query parses correctly
*
* @covers \Mautic\PageBundle\Model\TrackableModel::parseContentForTrackables
* @covers \Mautic\PageBundle\Model\TrackableModel::extractTrackablesFromHtml
* @covers \Mautic\PageBundle\Model\TrackableModel::extractTrackables
* @covers \Mautic\PageBundle\Model\TrackableModel::createTrackingTokens
* @covers \Mautic\PageBundle\Model\TrackableModel::prepareUrlForTracking
* @covers \Mautic\PageBundle\Model\TrackableModel::prepareContentWithTrackableTokens
*
* @dataProvider trackMapProvider
*/
public function testStandardLinkWithTokenizedQuery(?bool $useMap): void
{
$url = 'https://foo-bar.com?foo={contactfield=bar}&bar=foo';
$model = $this->getModel();
if (null !== $useMap) {
$emailContent = $this->generateContent($url, 'html', false, $useMap);
} else {
$emailContent = $this->generateContent($url, 'html', false, true)
.$this->generateContent($url, 'html', false, false);
}
[$content, $trackables] = $model->parseContentForTrackables(
$emailContent,
[
'{contactfield=bar}' => '',
],
'email',
1
);
$tokenFound = preg_match('/\{trackable=(.*?)\}/', $content, $match);
// Assert that a trackable token exists
Assert::assertTrue((bool) $tokenFound, $content);
// Assert that exactly one trackable found
Assert::assertCount(1, $trackables);
// Assert the Trackable exists
Assert::assertArrayHasKey('{trackable='.$match[1].'}', $trackables);
}
/**
* @testdox Test that a token used in place of a URL is parsed properly
*
* @covers \Mautic\PageBundle\Model\TrackableModel::validateTokenIsTrackable
* @covers \Mautic\PageBundle\Model\TrackableModel::parseContentForTrackables
* @covers \Mautic\PageBundle\Model\TrackableModel::prepareUrlForTracking
*/
public function testTokenizedDomain(): void
{
$url = 'http://{contactfield=foo}.org';
$model = $this->getModel();
[$content, $trackables] = $model->parseContentForTrackables(
$this->generateContent($url, 'html'),
[
'{contactfield=foo}' => 'mautic',
],
'email',
1
);
$tokenFound = preg_match('/\{trackable=(.*?)\}/', $content, $match);
// Assert that a trackable token exists
$this->assertTrue((bool) $tokenFound, $content);
// Assert the Trackable exists
$this->assertArrayHasKey('{trackable='.$match[1].'}', $trackables);
}
/**
* @covers \Mautic\PageBundle\Model\TrackableModel::validateTokenIsTrackable
* @covers \Mautic\PageBundle\Model\TrackableModel::parseContentForTrackables
* @covers \Mautic\PageBundle\Model\TrackableModel::prepareUrlForTracking
*/
public function testTokenizedHostWithScheme(): void
{
$url = '{contactfield=foo}';
$model = $this->getModel();
[$content, $trackables] = $model->parseContentForTrackables(
$this->generateContent($url, 'html'),
[
'{contactfield=foo}' => 'https://mautic.org',
],
'email',
1
);
$tokenFound = preg_match('/\{trackable=(.*?)\}/', $content, $match);
// Assert that a trackable token exists
$this->assertTrue((bool) $tokenFound, $content);
// Assert the Trackable exists
$this->assertArrayHasKey('{trackable='.$match[1].'}', $trackables);
}
/**
* @testdox Test that a token used in place of a URL is parsed
*
* @covers \Mautic\PageBundle\Model\TrackableModel::validateTokenIsTrackable
* @covers \Mautic\PageBundle\Model\TrackableModel::parseContentForTrackables
* @covers \Mautic\PageBundle\Model\TrackableModel::prepareUrlForTracking
*/
public function testTokenizedHostWithQuery(): void
{
$url = 'http://{contactfield=foo}.com?foo=bar';
$model = $this->getModel();
[$content, $trackables] = $model->parseContentForTrackables(
$this->generateContent($url, 'html'),
[
'{contactfield=foo}' => '',
],
'email',
1
);
$tokenFound = preg_match('/\{trackable=(.*?)\}/', $content, $match);
// Assert that a trackable token exists
$this->assertTrue((bool) $tokenFound, $content);
// Assert the Trackable exists
$this->assertArrayHasKey('{trackable='.$match[1].'}', $trackables);
}
/**
* @covers \Mautic\PageBundle\Model\TrackableModel::validateTokenIsTrackable
* @covers \Mautic\PageBundle\Model\TrackableModel::parseContentForTrackables
* @covers \Mautic\PageBundle\Model\TrackableModel::prepareUrlForTracking
*/
public function testTokenizedHostWithTokenizedQuery(): void
{
$url = 'http://{contactfield=foo}.com?foo={contactfield=bar}';
$model = $this->getModel();
[$content, $trackables] = $model->parseContentForTrackables(
$this->generateContent($url, 'html'),
[
'{contactfield=foo}' => '',
'{contactfield=bar}' => '',
],
'email',
1
);
$tokenFound = preg_match('/\{trackable=(.*?)\}/', $content, $match);
// Assert that a trackable token exists
$this->assertTrue((bool) $tokenFound, $content);
// Assert the Trackable exists
$this->assertArrayHasKey('{trackable='.$match[1].'}', $trackables);
}
/**
* @testdox Test that tokens that are supposed to be ignored are
*
* @covers \Mautic\PageBundle\Model\TrackableModel::validateTokenIsTrackable
* @covers \Mautic\PageBundle\Model\TrackableModel::parseContentForTrackables
* @covers \Mautic\PageBundle\Model\TrackableModel::prepareUrlForTracking
*/
public function testIgnoredTokensAreNotConverted(): void
{
$url = 'https://{unsubscribe_url}';
$model = $this->getModel(['{unsubscribe_url}']);
[$content, $trackables] = $model->parseContentForTrackables(
$this->generateContent($url, 'html'),
[
'{unsubscribe_url}' => 'https://domain.com/email/unsubscribe/xxxxxxx',
],
'email',
1
);
$this->assertEmpty($trackables, $content);
$this->assertFalse(strpos($content, $url), 'https:// should have been stripped from the token URL');
}
/**
* @testdox Test that tokens that are supposed to be ignored are
*
* @covers \Mautic\PageBundle\Model\TrackableModel::validateTokenIsTrackable
* @covers \Mautic\PageBundle\Model\TrackableModel::parseContentForTrackables
* @covers \Mautic\PageBundle\Model\TrackableModel::prepareUrlForTracking
*/
public function testUnsupportedTokensAreNotConverted(): void
{
$url = '{random_token}';
$model = $this->getModel();
[$content, $trackables] = $model->parseContentForTrackables(
$this->generateContent($url, 'text'),
[
'{unsubscribe_url}' => 'https://domain.com/email/unsubscribe/xxxxxxx',
],
'email',
1
);
$this->assertEmpty($trackables, $content);
}
/**
* @covers \Mautic\PageBundle\Model\TrackableModel::validateTokenIsTrackable
* @covers \Mautic\PageBundle\Model\TrackableModel::parseContentForTrackables
* @covers \Mautic\PageBundle\Model\TrackableModel::prepareUrlForTracking
*/
public function testTokenWithDefaultValueInPlaintextWillCountAsOne(): void
{
$url = '{contactfield=website|https://mautic.org}';
$model = $this->getModel();
$inputContent = $this->generateContent($url, 'text');
[$content, $trackables] = $model->parseContentForTrackables(
$inputContent,
[
'{contactfield=website}' => 'https://mautic.org/about-us',
],
'email',
1
);
$tokenFound = preg_match('/\{trackable=(.*?)\}/', $content, $match);
// Assert that a trackable token exists
$this->assertTrue((bool) $tokenFound, $content);
// Assert the Trackable exists
$trackableKey = '{trackable='.$match[1].'}';
$this->assertArrayHasKey('{trackable='.$match[1].'}', $trackables);
$this->assertEquals(1, count($trackables));
$this->assertEquals('{contactfield=website|https://mautic.org}', $trackables[$trackableKey]->getRedirect()->getUrl());
}
/**
* @testdox Test that a URL injected into the do not track list is not converted
*
* @covers \Mautic\PageBundle\Model\TrackableModel::validateTokenIsTrackable
* @covers \Mautic\PageBundle\Model\TrackableModel::parseContentForTrackables
* @covers \Mautic\PageBundle\Model\TrackableModel::prepareUrlForTracking
*/
public function testIgnoredUrlDoesNotCrash(): void
{
$url = 'https://domain.com';
$model = $this->getModel([$url]);
[$content, $trackables] = $model->parseContentForTrackables(
$this->generateContent($url, 'html'),
[],
'email',
1
);
$this->assertTrue(str_contains($content, $url), $content);
}
/**
* @testdox Test that a token used in place of a URL is not parsed
*
* @covers \Mautic\PageBundle\Model\TrackableModel::validateTokenIsTrackable
* @covers \Mautic\PageBundle\Model\TrackableModel::parseContentForTrackables
* @covers \Mautic\PageBundle\Model\TrackableModel::extractTrackablesFromHtml
* @covers \Mautic\PageBundle\Model\TrackableModel::extractTrackables
* @covers \Mautic\PageBundle\Model\TrackableModel::createTrackingTokens
* @covers \Mautic\PageBundle\Model\TrackableModel::prepareUrlForTracking
* @covers \Mautic\PageBundle\Model\TrackableModel::prepareContentWithTrackableTokens
*
* @dataProvider trackMapProvider
*/
public function testTokenAsHostIsConvertedToTrackableToken(?bool $useMap): void
{
$url = 'http://{pagelink=1}';
$model = $this->getModel();
if (null !== $useMap) {
$emailContent = $this->generateContent($url, 'html', false, $useMap);
} else {
$emailContent = $this->generateContent($url, 'html', false, true)
.$this->generateContent($url, 'html', false, false);
}
[$content, $trackables] = $model->parseContentForTrackables(
$emailContent,
[
'{pagelink=1}' => 'http://foo-bar.com',
],
'email',
1
);
$token = array_key_first($trackables);
Assert::assertNotEmpty($trackables, $content);
Assert::assertStringContainsString($token, $content);
// Assert that exactly one trackable found
Assert::assertCount(1, $trackables);
}
/**
* @testdox Test that a URLs with same base or correctly replaced
*
* @covers \Mautic\PageBundle\Model\TrackableModel::prepareContentWithTrackableTokens
* @covers \Mautic\PageBundle\Model\TrackableModel::parseContentForTrackables
* @covers \Mautic\PageBundle\Model\TrackableModel::extractTrackablesFromHtml
* @covers \Mautic\PageBundle\Model\TrackableModel::extractTrackables
* @covers \Mautic\PageBundle\Model\TrackableModel::createTrackingTokens
* @covers \Mautic\PageBundle\Model\TrackableModel::prepareUrlForTracking
*
* @dataProvider trackMapProvider
*/
public function testUrlsWithSameBaseAreReplacedCorrectly(?bool $useMap): void
{
$urls = [
'https://foo-bar.com',
'https://foo-bar.com?foo=bar',
'https://FOO-bar.com/bar',
];
$model = $this->getModel();
if (null !== $useMap) {
$emailContent = $this->generateContent($urls, 'html', false, $useMap);
} else {
$emailContent = $this->generateContent($urls, 'html', false, true)
.$this->generateContent($urls, 'html', false, false);
}
[$content, $trackables] = $model->parseContentForTrackables(
$emailContent,
[],
'email',
1
);
// Assert that both trackables found
Assert::assertCount(3, $trackables);
foreach ($trackables as $redirectId => $trackable) {
// If the shared base was correctly parsed, all generated tokens will be in the content
Assert::assertNotFalse(strpos($content, (string) $redirectId), $content);
}
}
/**
* @testdox Test that css images are not converted if there are no links
*/
public function testCssUrlsAreNotConvertedIfThereAreNoLinks(): void
{
$model = $this->getModel();
[$content, $trackables] = $model->parseContentForTrackables(
'<style> .mf-modal { background-image: url(\'https://www.mautic.org/wp-content/uploads/2014/08/iTunesArtwork.png\'); } </style>',
[],
'email',
1
);
$this->assertEmpty($trackables);
}
/**
* @testdox Tests that URLs in the plaintext does not contaminate HTML
*/
public function testPlainTextDoesNotContaminateHtml(): void
{
$model = $this->getModel();
$html = <<<TEXT
Hi {contactfield=firstname},
<br />
Come to our office in {contactfield=city}!
<br />
John Smith<br />
VP of Sales<br />
https://plaintexttest.io
TEXT;
$plainText = strip_tags($html);
$combined = [$html, $plainText];
[$content, $trackables] = $model->parseContentForTrackables(
$combined,
[],
'email',
1
);
$this->assertCount(1, $trackables);
// No links so no trackables
$this->assertEquals($html, $content[0]);
$token = array_key_first($trackables);
self::assertNotNull($token);
$this->assertEquals(str_replace('https://plaintexttest.io', $token, $plainText), $content[1]);
}
/**
* @testdox Tests that URL based contact fields are found in plain text
*/
public function testPlainTextFindsUrlContactFields(): void
{
$model = $this->getModel([], ['website']);
$html = <<<TEXT
Hi {contactfield=firstname},
<br />
Come to our office in {contactfield=city}!
<br />
John Smith<br />
VP of Sales<br />
{contactfield=website}
TEXT;
$plainText = strip_tags($html);
$combined = [$html, $plainText];
[$content, $trackables] = $model->parseContentForTrackables(
$combined,
[],
'email',
1
);
$this->assertCount(1, $trackables);
// No links so no trackables
$this->assertEquals($html, $content[0]);
$token = array_key_first($trackables);
self::assertNotNull($token);
$this->assertEquals(str_replace('{contactfield=website}', $token, $plainText), $content[1]);
}
/**
* @param array $doNotTrack
* @param array $urlFieldsForPlaintext
*
* @return TrackableModel|\PHPUnit\Framework\MockObject\MockObject
*/
protected function getModel($doNotTrack = [], $urlFieldsForPlaintext = [])
{
// Add default DoNotTrack
$doNotTrack = array_merge(
$doNotTrack,
[
'{webview_url}',
'{unsubscribe_url}',
'{trackable=(.*?)}',
]
);
$mockRedirectModel = $this->createMock(RedirectModel::class);
$mockLeadFieldRepository = $this->createMock(LeadFieldRepository::class);
$mockModel = $this->getMockBuilder(TrackableModel::class)
->setConstructorArgs([
$mockRedirectModel,
$mockLeadFieldRepository,
$this->createMock(EntityManagerInterface::class),
$this->createMock(CorePermissions::class),
$this->createMock(EventDispatcherInterface::class),
$this->createMock(UrlGeneratorInterface::class),
$this->createMock(Translator::class),
$this->createMock(UserHelper::class),
$this->createMock(LoggerInterface::class),
$this->createMock(CoreParametersHelper::class),
])
->onlyMethods(['getDoNotTrackList', 'getEntitiesFromUrls', 'getContactFieldUrlTokens'])
->getMock();
$mockModel->expects($this->once())
->method('getDoNotTrackList')
->willReturn($doNotTrack);
$mockModel->expects($this->any())
->method('getEntitiesFromUrls')
->willReturnCallback(
function ($trackableUrls, $channel, $channelId) {
$entities = [];
foreach ($trackableUrls as $url) {
$entities[$url] = $this->getTrackableEntity($url);
}
return $entities;
}
);
$mockModel->expects($this->any())
->method('getContactFieldUrlTokens')
->willReturn($urlFieldsForPlaintext);
return $mockModel;
}
/**
* @return Trackable
*/
protected function getTrackableEntity($url)
{
$redirect = new Redirect();
$redirect->setUrl($url);
$redirect->setRedirectId();
$trackable = new Trackable();
$trackable->setChannel('email')
->setChannelId(1)
->setRedirect($redirect)
->setHits(random_int(1, 10))
->setUniqueHits(random_int(1, 10));
return $trackable;
}
/**
* @param array<int, string>|string $urls
*/
protected function generateContent($urls, string $type, bool $doNotTrack = false, bool $useMap = false): string
{
$content = '';
if (!is_array($urls)) {
$urls = [$urls];
}
foreach ($urls as $url) {
if ('html' === $type) {
$dnc = ($doNotTrack) ? ' mautic:disable-tracking' : '';
if ($useMap) {
$content .= <<<CONTENT
ABC123 321ABC
ABC123 <map><area href="$url"$dnc alt="alt" /></map> 321ABC
CONTENT;
} else {
$content .= <<<CONTENT
ABC123 321ABC
ABC123 <a href="$url"$dnc>$url</a> 321ABC
CONTENT;
}
} else {
$content .= <<<CONTENT
ABC123 321ABC
ABC123 $url 321ABC
CONTENT;
}
}
return $content;
}
/**
* @return array<array<bool|null>> Use null to include both <a> and <map> tags
*/
public static function trackMapProvider(): array
{
return [
[true],
[false],
[null],
];
}
}