Spaces:
No application file
No application file
namespace Mautic\FormBundle\Tests\Model; | |
use Doctrine\ORM\EntityManager; | |
use Mautic\CampaignBundle\Membership\MembershipManager; | |
use Mautic\CampaignBundle\Model\CampaignModel; | |
use Mautic\CoreBundle\Entity\IpAddress; | |
use Mautic\CoreBundle\Helper\CoreParametersHelper; | |
use Mautic\CoreBundle\Helper\IpLookupHelper; | |
use Mautic\CoreBundle\Helper\UserHelper; | |
use Mautic\CoreBundle\Security\Permissions\CorePermissions; | |
use Mautic\CoreBundle\Translation\Translator; | |
use Mautic\CoreBundle\Twig\Helper\DateHelper; | |
use Mautic\FormBundle\Entity\Field; | |
use Mautic\FormBundle\Entity\Form; | |
use Mautic\FormBundle\Entity\Submission; | |
use Mautic\FormBundle\Entity\SubmissionRepository; | |
use Mautic\FormBundle\Event\Service\FieldValueTransformer; | |
use Mautic\FormBundle\Event\SubmissionEvent; | |
use Mautic\FormBundle\Helper\FormFieldHelper; | |
use Mautic\FormBundle\Helper\FormUploader; | |
use Mautic\FormBundle\Model\FormModel; | |
use Mautic\FormBundle\Model\SubmissionModel; | |
use Mautic\FormBundle\Validator\UploadFieldValidator; | |
use Mautic\LeadBundle\Deduplicate\ContactMerger; | |
use Mautic\LeadBundle\Entity\Lead; | |
use Mautic\LeadBundle\Entity\LeadRepository; | |
use Mautic\LeadBundle\Model\CompanyModel; | |
use Mautic\LeadBundle\Model\FieldModel as LeadFieldModel; | |
use Mautic\LeadBundle\Model\LeadModel; | |
use Mautic\LeadBundle\Tracker\ContactTracker; | |
use Mautic\LeadBundle\Tracker\Service\DeviceTrackingService\DeviceTrackingServiceInterface; | |
use Mautic\PageBundle\Model\PageModel; | |
use Mautic\UserBundle\Entity\User; | |
use Monolog\Logger; | |
use PHPUnit\Framework\MockObject\MockObject; | |
use Symfony\Component\EventDispatcher\EventDispatcherInterface; | |
use Symfony\Component\HttpFoundation\File\UploadedFile; | |
use Symfony\Component\HttpFoundation\Request; | |
use Symfony\Component\Routing\Generator\UrlGeneratorInterface; | |
use Symfony\Component\Routing\RouterInterface; | |
use Twig\Environment; | |
class SubmissionModelTest extends \PHPUnit\Framework\TestCase | |
{ | |
/** | |
* @var MockObject|IpLookupHelper | |
*/ | |
private MockObject $ipLookupHelper; | |
/** | |
* @var MockObject|Environment | |
*/ | |
private MockObject $twigMock; | |
/** | |
* @var MockObject|FormModel | |
*/ | |
private MockObject $formModel; | |
/** | |
* @var MockObject|PageModel | |
*/ | |
private MockObject $pageModel; | |
/** | |
* @var MockObject|LeadModel | |
*/ | |
private MockObject $leadModel; | |
/** | |
* @var MockObject|CampaignModel | |
*/ | |
private MockObject $campaignModel; | |
/** | |
* @var MockObject|MembershipManager | |
*/ | |
private MockObject $membershipManager; | |
/** | |
* @var MockObject|LeadFieldModel | |
*/ | |
private MockObject $leadFieldModel; | |
/** | |
* @var MockObject|CompanyModel | |
*/ | |
private MockObject $companyModel; | |
/** | |
* @var MockObject|FormFieldHelper | |
*/ | |
private MockObject $fieldHelper; | |
/** | |
* @var MockObject|EventDispatcherInterface | |
*/ | |
private MockObject $dispatcher; | |
/** | |
* @var MockObject|Translator | |
*/ | |
private MockObject $translator; | |
private DateHelper $dateHelper; | |
/** | |
* @var MockObject|UserHelper | |
*/ | |
private MockObject $userHelper; | |
/** | |
* @var MockObject|EntityManager | |
*/ | |
private MockObject $entityManager; | |
/** | |
* @var MockObject|SubmissionRepository | |
*/ | |
private MockObject $submissioRepository; | |
/** | |
* @var MockObject|LeadRepository | |
*/ | |
private MockObject $leadRepository; | |
/** | |
* @var MockObject|Logger | |
*/ | |
private MockObject $mockLogger; | |
/** | |
* @var MockObject|UploadFieldValidator | |
*/ | |
private MockObject $uploadFieldValidatorMock; | |
/** | |
* @var MockObject|FormUploader | |
*/ | |
private MockObject $formUploaderMock; | |
/** | |
* @var MockObject|DeviceTrackingServiceInterface | |
*/ | |
private MockObject $deviceTrackingService; | |
/** | |
* @var MockObject|UploadedFile | |
*/ | |
private MockObject $file1Mock; | |
/** | |
* @var MockObject|RouterInterface | |
*/ | |
private MockObject $router; | |
/** | |
* @var MockObject|ContactTracker | |
*/ | |
private MockObject $contactTracker; | |
/** | |
* @var MockObject|ContactMerger | |
*/ | |
private MockObject $contactMerger; | |
private SubmissionModel $submissionModel; | |
/** | |
* @var \ReflectionClass<SubmissionModel> | |
*/ | |
private \ReflectionClass $submissionModelReflection; | |
protected function setUp(): void | |
{ | |
parent::setUp(); | |
$this->ipLookupHelper = $this->createMock(IpLookupHelper::class); | |
$this->twigMock = $this->createMock(Environment::class); | |
$this->formModel = $this->createMock(FormModel::class); | |
$this->pageModel = $this->createMock(PageModel::class); | |
$this->leadModel = $this->createMock(LeadModel::class); | |
$this->campaignModel = $this->createMock(CampaignModel::class); | |
$this->membershipManager = $this->createMock(MembershipManager::class); | |
$this->leadFieldModel = $this->createMock(LeadFieldModel::class); | |
$this->companyModel = $this->createMock(CompanyModel::class); | |
$this->fieldHelper = $this->createMock(FormFieldHelper::class); | |
$this->dispatcher = $this->createMock(EventDispatcherInterface::class); | |
$this->translator = $this->createMock(Translator::class); | |
$this->dateHelper = new DateHelper( | |
'Y-m-d H:i:s', | |
'Y-m-d H:i', | |
'Y-m-d', | |
'H:i', | |
$this->translator, | |
$this->createMock(CoreParametersHelper::class) | |
); | |
$this->userHelper = $this->createMock(UserHelper::class); | |
$this->entityManager = $this->createMock(EntityManager::class); | |
$this->submissioRepository = $this->createMock(SubmissionRepository::class); | |
$this->leadRepository = $this->createMock(LeadRepository::class); | |
$this->mockLogger = $this->createMock(Logger::class); | |
$this->uploadFieldValidatorMock = $this->createMock(UploadFieldValidator::class); | |
$this->formUploaderMock = $this->createMock(FormUploader::class); | |
$this->deviceTrackingService = $this->createMock(DeviceTrackingServiceInterface::class); | |
$this->file1Mock = $this->createMock(UploadedFile::class); | |
$this->router = $this->createMock(RouterInterface::class); | |
$this->contactTracker = $this->createMock(ContactTracker::class); | |
$this->contactMerger = $this->createMock(ContactMerger::class); | |
$this->fieldHelper->method('getFieldFilter')->willReturn('string'); | |
$this->submissionModel = new SubmissionModel( | |
$this->ipLookupHelper, | |
$this->twigMock, | |
$this->formModel, | |
$this->pageModel, | |
$this->leadModel, | |
$this->campaignModel, | |
$this->membershipManager, | |
$this->leadFieldModel, | |
$this->companyModel, | |
$this->fieldHelper, | |
$this->uploadFieldValidatorMock, | |
$this->formUploaderMock, | |
$this->deviceTrackingService, | |
new FieldValueTransformer($this->router), | |
$this->dateHelper, | |
$this->contactTracker, | |
$this->contactMerger, | |
$this->entityManager, | |
$this->createMock(CorePermissions::class), | |
$this->dispatcher, | |
$this->createMock(UrlGeneratorInterface::class), | |
$this->translator, | |
$this->userHelper, | |
$this->mockLogger, | |
$this->createMock(CoreParametersHelper::class) | |
); | |
$this->submissionModelReflection = new \ReflectionClass($this->submissionModel); | |
} | |
public function testSaveSubmission(): void | |
{ | |
$this->contactTracker->expects($this->any()) | |
->method('getContact') | |
->willReturn(new Lead()); | |
$this->userHelper->expects($this->any()) | |
->method('getUser') | |
->willReturn(new User()); | |
$mockLeadField['email'] = [ | |
'label' => 'Email', | |
'alias' => 'email', | |
'type' => 'email', | |
'group' => 'core', | |
'group_label' => 'Core', | |
'defaultValue' => '', | |
'properties' => [], | |
]; | |
$this->leadFieldModel->expects($this->any()) | |
->method('getUniqueIdentifierFields') | |
->willReturn(['eyJpc1B1Ymxpc2hlZCI6dHJ1ZSwiaXNVbmlxdWVJZGVudGlmZXIiOnRydWUsIm9iamVjdCI6ImxlYWQifQ==' => ['email' => 'Email']]); | |
$this->leadFieldModel->expects($this->any()) | |
->method('getFieldListWithProperties') | |
->willReturn($mockLeadField); | |
$this->companyModel->method('fetchCompanyFields')->willReturn([]); | |
$this->entityManager->expects($this->any()) | |
->method('getRepository') | |
->will( | |
$this->returnValueMap( | |
[ | |
[Lead::class, $this->leadRepository], | |
[Submission::class, $this->submissioRepository], | |
] | |
) | |
); | |
$this->leadRepository->expects($this->any()) | |
->method('getLeadsByUniqueFields') | |
->willReturn(null); | |
$this->file1Mock->expects($this->any()) | |
->method('getClientOriginalName') | |
->willReturn('test.jpg'); | |
$this->router->expects($this->any()) | |
->method('generate') | |
->willReturn('test.jpg'); | |
$this->uploadFieldValidatorMock->expects($this->any()) | |
->method('processFileValidation') | |
->willReturn($this->file1Mock); | |
$this->ipLookupHelper->expects($this->any()) | |
->method('getIpAddress') | |
->willReturn(new IpAddress()); | |
$request = new Request(); | |
$request->setMethod('POST'); | |
$formData = [ | |
'var_name_1' => 'value 1', | |
'var_name_2' => 'value 2', | |
'email' => '[email protected]', | |
'file' => 'test.jpg', | |
'submit' => '', | |
'formId' => 1, | |
'return' => '', | |
'formName' => 'testform', | |
'formid' => 1, | |
]; | |
$post = $formData; | |
$server = $request->server->all(); | |
$form = new Form(); | |
$fields = $this->getTestFormFields(); | |
$formModel = new class() extends FormModel { | |
public function __construct() | |
{ | |
} | |
}; | |
$formModel->setFields($form, $fields); | |
/** @var SubmissionEvent $submissionEvent */ | |
$submissionEvent = $this->submissionModel->saveSubmission($post, $server, $form, $request, true)['submission']; | |
$this->assertInstanceOf(SubmissionEvent::class, $submissionEvent); | |
$tokens = $submissionEvent->getTokens(); | |
$this->assertEquals($formData['email'], $tokens['{formfield=email}']); | |
$this->assertEquals($formData['file'], $tokens['{formfield=file}']); | |
$this->assertSame(['email' => '[email protected]'], $submissionEvent->getContactFieldMatches()); | |
$this->assertFalse($this->submissionModel->saveSubmission($post, $server, $form, $request)); | |
} | |
public function testNormalizeValues(): void | |
{ | |
$reflection = new \ReflectionClass(SubmissionModel::class); | |
$method = $reflection->getMethod('normalizeValue'); | |
$method->setAccessible(true); | |
$fieldSession = 'mautic_'.sha1(uniqid((string) mt_rand(), true)); | |
$fields[$fieldSession] = [ | |
'label' => 'Email', | |
'showLabel' => 1, | |
'saveResult' => 1, | |
'defaultValue' => false, | |
'alias' => 'email', | |
'type' => 'email', | |
'mappedField' => 'email', | |
'mappedObject' => 'contact', | |
'id' => $fieldSession, | |
]; | |
$field = new Field(); | |
$this->assertEquals('', $method->invokeArgs($this->submissionModel, ['', $field])); | |
$this->assertEquals(1, $method->invokeArgs($this->submissionModel, [1, $field])); | |
$this->assertEquals('1, 2', $method->invokeArgs($this->submissionModel, [[1, 2], $field])); | |
// field wiht list | |
$field = new Field(); | |
$field->setProperties( | |
[ | |
'list' => [ | |
'list' => [ | |
[ | |
'label' => 'First', | |
'value' => 1, | |
], | |
[ | |
'label' => 'Second', | |
'value' => 2, | |
], | |
], | |
], | |
] | |
); | |
$this->assertEquals('', $method->invokeArgs($this->submissionModel, ['', $field])); | |
$this->assertEquals('First', $method->invokeArgs($this->submissionModel, [1, $field])); | |
$this->assertEquals('First, Second', $method->invokeArgs($this->submissionModel, [[1, 2], $field])); | |
} | |
/** | |
* @return mixed[] | |
*/ | |
private function getTestFormFields(): array | |
{ | |
$fieldSession = 'mautic_'.sha1(uniqid((string) mt_rand(), true)); | |
$fields[$fieldSession] = [ | |
'label' => 'Email', | |
'showLabel' => 1, | |
'saveResult' => 1, | |
'defaultValue' => false, | |
'alias' => 'email', | |
'type' => 'email', | |
'mappedField' => 'email', | |
'mappedObject' => 'contact', | |
'id' => $fieldSession, | |
]; | |
$fields['file'] = [ | |
'label' => 'File', | |
'showLabel' => 1, | |
'saveResult' => 1, | |
'defaultValue' => false, | |
'alias' => 'file', | |
'type' => 'file', | |
'id' => 'file', | |
'allowed_file_size' => 1, | |
'allowed_file_extensions' => ['jpg', 'gif'], | |
]; | |
return $fields; | |
} | |
private function setUpExport(): void | |
{ | |
$this->formModel->expects($this->any()) | |
->method('getCustomComponents') | |
->willReturn(['viewOnlyFields' => ['button', 'captcha', 'freetext']]); | |
$this->submissioRepository->expects($this->any()) | |
->method('getEntities') | |
->willReturn([]); | |
$this->entityManager->expects($this->any()) | |
->method('getRepository') | |
->willReturn($this->submissioRepository); | |
} | |
public function testExportResultsCsv(): void | |
{ | |
$this->setUpExport(); | |
$response = $this->submissionModel->exportResults('csv', new Form(), []); | |
$this->assertSame($response::class, \Symfony\Component\HttpFoundation\StreamedResponse::class); | |
$this->assertStringContainsString('.csv', $response->headers->get('Content-Disposition')); | |
$this->assertSame('0', $response->headers->get('Expires')); | |
} | |
public function testExportResultsExcel(): void | |
{ | |
$this->setUpExport(); | |
$response = $this->submissionModel->exportResults('xlsx', new Form(), []); | |
$this->assertSame($response::class, \Symfony\Component\HttpFoundation\StreamedResponse::class); | |
$this->assertStringContainsString('.xlsx', $response->headers->get('Content-Disposition')); | |
$this->assertSame('0', $response->headers->get('Expires')); | |
} | |
private function mockTranslation(): void | |
{ | |
$values = ['Submission ID', 'Contact ID', 'Date Submitted', 'IP address', 'Referrer', 'Form ID']; | |
$this->translator->expects($this->any()) | |
->method('trans') | |
->with($this->anything()) | |
->will($this->returnCallback(fn ($text) => match ($text) { | |
'mautic.form.report.submission.id' => $values[0], | |
'mautic.lead.report.contact_id' => $values[1], | |
'mautic.form.result.thead.date' => $values[2], | |
'mautic.core.ipaddress' => $values[3], | |
'mautic.form.result.thead.referrer' => $values[4], | |
'mautic.form.report.form_id' => $values[5], | |
default => null, | |
})); | |
} | |
/** | |
* @throws \ReflectionException | |
*/ | |
public function getAccessibleReflectionMethod(string $name): \ReflectionMethod | |
{ | |
$method = $this->submissionModelReflection->getMethod($name); | |
$method->setAccessible(true); | |
return $method; | |
} | |
public function testGetExportHeader(): void | |
{ | |
$form = new Form(); | |
$field = new Field(); | |
$field2 = new Field(); | |
$field->setLabel('Email'); | |
$field2->setType('text'); | |
$field2->setLabel('Click'); | |
$field2->setType('button'); | |
$form->addField('email', $field); | |
$form->addField('button', $field2); | |
$viewOnlyFields = ['button', 'captcha', 'freetext']; | |
$expectedHeader = ['Submission ID', 'Contact ID', 'Date Submitted', 'IP address', 'Referrer', 'Email']; | |
$this->mockTranslation(); | |
try { | |
$getExportHeaderRef = $this->getAccessibleReflectionMethod('getExportHeader'); | |
$header = $getExportHeaderRef->invokeArgs($this->submissionModel, [$form, $viewOnlyFields]); | |
} catch (\ReflectionException $e) { | |
$this->fail($e->getMessage()); | |
} | |
$this->assertCount(6, $header); | |
$this->assertSame($expectedHeader, $header); | |
$this->assertNotContains('Click', $header); | |
} | |
public function testGetExportHeaderForPage(): void | |
{ | |
$expectedHeader = ['Submission ID', 'Contact ID', 'Form ID', 'Date Submitted', 'IP address', 'Referrer']; | |
$this->mockTranslation(); | |
try { | |
$getExportHeaderForPageRef = $this->getAccessibleReflectionMethod('getExportHeaderForPage'); | |
$header1 = $getExportHeaderForPageRef->invokeArgs($this->submissionModel, []); | |
$header2 = $getExportHeaderForPageRef->invokeArgs($this->submissionModel, ['xlsx']); | |
} catch (\ReflectionException $e) { | |
$this->fail($e->getMessage()); | |
} | |
$this->assertCount(6, $header1); | |
$this->assertCount(5, $header2); | |
$this->assertSame($expectedHeader, $header1); | |
$this->assertNotContains('Form ID', $header2); | |
} | |
public function testPutCsvExportRow(): void | |
{ | |
$tmpFile = tempnam(sys_get_temp_dir(), 'mautic_csv_export_test_'); | |
$handle = fopen($tmpFile, 'r+'); | |
$header = ['Submission ID', 'Contact ID', 'Form ID']; | |
try { | |
$putCsvExportRowRef = $this->getAccessibleReflectionMethod('putCsvExportRow'); | |
$putCsvExportRowRef->invokeArgs($this->submissionModel, [$handle, $header]); | |
} catch (\ReflectionException $e) { | |
$this->fail($e->getMessage()); | |
} | |
fclose($handle); | |
$result = array_map('str_getcsv', file($tmpFile)); | |
$this->assertCount(1, $result); | |
$this->assertSame($header, $result[0]); | |
if (file_exists($tmpFile)) { | |
unlink($tmpFile); | |
} | |
} | |
public function testGetExportRow(): void | |
{ | |
$viewOnlyFields = ['button']; | |
$dateSubmitted = '28-03-2023 12:00'; | |
$fixture = [ | |
'id' => 1, | |
'leadId' => 123, | |
'dateSubmitted' => $dateSubmitted, | |
'ipAddress' => '127.0.0.1', | |
'referer' => 'https://test.com', | |
'results' => [ | |
[ | |
'type' => 'text', | |
'label' => 'Email', | |
'value' => '[email protected]', | |
], | |
[ | |
'type' => 'button', | |
'label' => 'Click', | |
'value' => true, | |
], | |
], | |
]; | |
try { | |
$getExportRowRef = $this->getAccessibleReflectionMethod('getExportRow'); | |
$result = $getExportRowRef->invokeArgs($this->submissionModel, [$fixture, $viewOnlyFields]); | |
} catch (\ReflectionException $e) { | |
$this->fail($e->getMessage()); | |
} | |
$this->assertIsArray($result); | |
$this->assertSame([1, 123, $this->dateHelper->toFull($dateSubmitted, 'UTC'), '127.0.0.1', 'https://test.com', '[email protected]'], $result); | |
} | |
public function testGetExportRowForPage(): void | |
{ | |
$email = '[email protected]'; | |
$formId = 432; | |
$dateSubmitted = '28-03-2023 12:00'; | |
$fixture = [ | |
'id' => 1, | |
'leadId' => 123, | |
'dateSubmitted' => $dateSubmitted, | |
'ipAddress' => '127.0.0.1', | |
'referer' => 'https://test.com', | |
'formId' => $formId, | |
'results' => [ | |
[ | |
'type' => 'text', | |
'label' => 'Email', | |
'value' => $email, | |
], | |
], | |
]; | |
try { | |
$getExportRowForPageRef = $this->getAccessibleReflectionMethod('getExportRowForPage'); | |
$row1 = $getExportRowForPageRef->invokeArgs($this->submissionModel, [$fixture]); | |
$row2 = $getExportRowForPageRef->invokeArgs($this->submissionModel, [$fixture, 'xlsx']); | |
} catch (\ReflectionException $e) { | |
$this->fail($e->getMessage()); | |
} | |
$this->assertIsArray($row1); | |
$this->assertIsArray($row2); | |
$this->assertCount(6, $row1); | |
$this->assertCount(5, $row2); | |
$this->assertSame([1, 123, $formId, $this->dateHelper->toFull($dateSubmitted, 'UTC'), '127.0.0.1', 'https://test.com'], $row1); | |
$this->assertSame([1, 123, $this->dateHelper->toFull($dateSubmitted, 'UTC'), '127.0.0.1', 'https://test.com'], $row2); | |
$this->assertNotContains($formId, $row2); | |
$this->assertNotContains($email, $row1); | |
} | |
} | |