Initial commit: CloudOps infrastructure platform
This commit is contained in:
@@ -0,0 +1,77 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Mautic\FormBundle\Tests\Collection;
|
||||
|
||||
use Mautic\FormBundle\Collection\FieldCollection;
|
||||
use Mautic\FormBundle\Crate\FieldCrate;
|
||||
use Mautic\FormBundle\Exception\FieldNotFoundException;
|
||||
|
||||
final class FieldCollectionTest extends \PHPUnit\Framework\TestCase
|
||||
{
|
||||
public function testToChoicesWithObjects(): void
|
||||
{
|
||||
$collection = new FieldCollection(
|
||||
[
|
||||
new FieldCrate('6', 'email', 'email', []),
|
||||
new FieldCrate('7', 'first_name', 'text', []),
|
||||
]
|
||||
);
|
||||
|
||||
$this->assertSame(
|
||||
[
|
||||
'email' => '6',
|
||||
'first_name' => '7',
|
||||
],
|
||||
$collection->toChoices()
|
||||
);
|
||||
}
|
||||
|
||||
public function testToChoicesWithoutObjects(): void
|
||||
{
|
||||
$collection = new FieldCollection();
|
||||
|
||||
$this->assertSame([], $collection->toChoices());
|
||||
}
|
||||
|
||||
public function testGetFieldByKey(): void
|
||||
{
|
||||
$field6 = new FieldCrate('6', 'email', 'email', []);
|
||||
$field7 = new FieldCrate('7', 'first_name', 'text', []);
|
||||
$collection = new FieldCollection([$field6, $field7]);
|
||||
|
||||
$this->assertSame($field6, $collection->getFieldByKey('6'));
|
||||
$this->assertSame($field7, $collection->getFieldByKey('7'));
|
||||
|
||||
$this->expectException(FieldNotFoundException::class);
|
||||
$collection->getFieldByKey('8');
|
||||
}
|
||||
|
||||
public function testRemoveFieldsWithKeysWithNoKeyToKeep(): void
|
||||
{
|
||||
$field6 = new FieldCrate('6', 'email', 'email', []);
|
||||
$field7 = new FieldCrate('7', 'first_name', 'text', []);
|
||||
$field8 = new FieldCrate('8', 'last_name', 'text', []);
|
||||
$originalCollection = new FieldCollection([$field6, $field7, $field8]);
|
||||
$resultCollection = $originalCollection->removeFieldsWithKeys(['6', '8']);
|
||||
|
||||
// It should return a clone of the original collection. Not mutation.
|
||||
$this->assertNotSame($originalCollection, $resultCollection);
|
||||
$this->assertCount(1, $resultCollection);
|
||||
$this->assertSame($field7, $resultCollection->getFieldByKey('7'));
|
||||
}
|
||||
|
||||
public function testRemoveFieldsWithKeysWithKeyToKeep(): void
|
||||
{
|
||||
$field6 = new FieldCrate('6', 'email', 'email', []);
|
||||
$field7 = new FieldCrate('7', 'first_name', 'text', []);
|
||||
$field8 = new FieldCrate('8', 'last_name', 'text', []);
|
||||
$originalCollection = new FieldCollection([$field6, $field7, $field8]);
|
||||
$resultCollection = $originalCollection->removeFieldsWithKeys(['6', '8'], '8');
|
||||
|
||||
$this->assertCount(2, $resultCollection);
|
||||
$this->assertSame($field7, $resultCollection->getFieldByKey('7'));
|
||||
$this->assertSame($field8, $resultCollection->getFieldByKey('8'));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Mautic\FormBundle\Tests\Collection;
|
||||
|
||||
use Mautic\FormBundle\Collection\ObjectCollection;
|
||||
use Mautic\FormBundle\Crate\ObjectCrate;
|
||||
|
||||
final class ObjectCollectionTest extends \PHPUnit\Framework\TestCase
|
||||
{
|
||||
public function testToChoicesWithObjects(): void
|
||||
{
|
||||
$collection = new ObjectCollection(
|
||||
[
|
||||
new ObjectCrate('contact', 'Contact'),
|
||||
new ObjectCrate('company', 'Company'),
|
||||
]
|
||||
);
|
||||
|
||||
$this->assertSame(
|
||||
[
|
||||
'Contact' => 'contact',
|
||||
'Company' => 'company',
|
||||
],
|
||||
$collection->toChoices()
|
||||
);
|
||||
}
|
||||
|
||||
public function testToChoicesWithoutObjects(): void
|
||||
{
|
||||
$collection = new ObjectCollection();
|
||||
|
||||
$this->assertSame([], $collection->toChoices());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Mautic\FormBundle\Tests\Collector;
|
||||
|
||||
use Mautic\CacheBundle\Cache\CacheProviderTagAwareInterface;
|
||||
use Mautic\FormBundle\Collector\AlreadyMappedFieldCollector;
|
||||
use PHPUnit\Framework\MockObject\MockObject;
|
||||
use Symfony\Component\Cache\CacheItem;
|
||||
|
||||
final class AlreadyMappedFieldCollectorTest extends \PHPUnit\Framework\TestCase
|
||||
{
|
||||
private MockObject&CacheProviderTagAwareInterface $cacheProvider;
|
||||
private AlreadyMappedFieldCollector $collector;
|
||||
|
||||
protected function setup(): void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
$this->cacheProvider = $this->createMock(CacheProviderTagAwareInterface::class);
|
||||
$this->collector = new AlreadyMappedFieldCollector($this->cacheProvider);
|
||||
}
|
||||
|
||||
public function testWorkflow(): void
|
||||
{
|
||||
$createCacheItem = \Closure::bind(
|
||||
function () {
|
||||
$item = new CacheItem();
|
||||
$item->isHit = false;
|
||||
$item->isTaggable = true;
|
||||
|
||||
return $item;
|
||||
},
|
||||
$this,
|
||||
CacheItem::class
|
||||
);
|
||||
$cacheItem = $createCacheItem();
|
||||
|
||||
$formId = '3';
|
||||
$object = 'contact';
|
||||
|
||||
$this->cacheProvider->method('getItem')
|
||||
->with('mautic.form.3.object.contact.fields.mapped')
|
||||
->willReturn($cacheItem);
|
||||
|
||||
$this->cacheProvider->expects($this->exactly(4))
|
||||
->method('save')
|
||||
->with($cacheItem);
|
||||
|
||||
// Ensure we get an empty array at the beginning.
|
||||
$this->assertNull($cacheItem->get());
|
||||
$this->assertSame([], $this->collector->getFields($formId, $object));
|
||||
|
||||
// Add a mapped field.
|
||||
$this->collector->addField('3', 'contact', '44');
|
||||
$this->assertSame(['44'], $this->collector->getFields($formId, $object));
|
||||
|
||||
// The field with key 44 should be added to the cache item.
|
||||
$this->assertSame('["44"]', $cacheItem->get());
|
||||
|
||||
// Add another mapped field.
|
||||
$this->collector->addField('3', 'contact', '55');
|
||||
|
||||
// The field with key 55 should be added to the cache item.
|
||||
$this->assertSame('["44","55"]', $cacheItem->get());
|
||||
$this->assertSame(['44', '55'], $this->collector->getFields($formId, $object));
|
||||
|
||||
// Remove an exsting field.
|
||||
$this->collector->removeField('3', 'contact', '44');
|
||||
|
||||
// The field with key 44 should be removed from the cache item.
|
||||
$this->assertSame('["55"]', $cacheItem->get());
|
||||
$this->assertSame(['55'], $this->collector->getFields($formId, $object));
|
||||
|
||||
// Remove a not exsting field.
|
||||
$this->collector->removeField('3', 'contact', '44');
|
||||
|
||||
// Still the same result after removing a field that did not exist.
|
||||
$this->assertSame('["55"]', $cacheItem->get());
|
||||
$this->assertSame(['55'], $this->collector->getFields($formId, $object));
|
||||
|
||||
$this->cacheProvider->expects($this->once())
|
||||
->method('invalidateTags')
|
||||
->with(['mautic.form.3.fields.mapped']);
|
||||
|
||||
$this->collector->removeAllForForm($formId);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Mautic\FormBundle\Tests\Collector;
|
||||
|
||||
use Mautic\FormBundle\Collection\FieldCollection;
|
||||
use Mautic\FormBundle\Collector\FieldCollector;
|
||||
use Mautic\FormBundle\Event\FieldCollectEvent;
|
||||
use PHPUnit\Framework\Assert;
|
||||
use Symfony\Component\EventDispatcher\EventDispatcher;
|
||||
|
||||
final class FieldCollectorTest extends \PHPUnit\Framework\TestCase
|
||||
{
|
||||
public function testBuildCollectionForNoObject(): void
|
||||
{
|
||||
$dispatcher = new class extends EventDispatcher {
|
||||
public int $dispatchMethodCallCounter = 0;
|
||||
|
||||
public function dispatch(object $event, ?string $eventName = null): object
|
||||
{
|
||||
++$this->dispatchMethodCallCounter;
|
||||
|
||||
\assert($event instanceof FieldCollectEvent);
|
||||
Assert::assertSame('contact', $event->getObject());
|
||||
|
||||
return new FieldCollection();
|
||||
}
|
||||
};
|
||||
|
||||
$fieldCollector = new FieldCollector($dispatcher);
|
||||
$fieldCollector->getFields('contact');
|
||||
|
||||
// Calling for the second time to ensure it's cached and the dispatcher is called only once.
|
||||
$fieldCollection = $fieldCollector->getFields('contact');
|
||||
|
||||
Assert::assertEquals(1, $dispatcher->dispatchMethodCallCounter);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Mautic\FormBundle\Tests\Collector;
|
||||
|
||||
use Mautic\FormBundle\Collection\FieldCollection;
|
||||
use Mautic\FormBundle\Collector\FieldCollectorInterface;
|
||||
use Mautic\FormBundle\Collector\MappedObjectCollector;
|
||||
use PHPUnit\Framework\Assert;
|
||||
|
||||
final class MappedObjectCollectorTest extends \PHPUnit\Framework\TestCase
|
||||
{
|
||||
public function testBuildCollectionForNoObject(): void
|
||||
{
|
||||
$fieldCollector = new class implements FieldCollectorInterface {
|
||||
public int $getFieldsMethodCallCounter = 0;
|
||||
|
||||
public function getFields(string $object): FieldCollection
|
||||
{
|
||||
++$this->getFieldsMethodCallCounter;
|
||||
|
||||
return new FieldCollection();
|
||||
}
|
||||
};
|
||||
|
||||
$mappedObjectCollector = new MappedObjectCollector($fieldCollector);
|
||||
$objectCollection = $mappedObjectCollector->buildCollection('');
|
||||
Assert::assertCount(0, $objectCollection);
|
||||
Assert::assertEquals(0, $fieldCollector->getFieldsMethodCallCounter);
|
||||
}
|
||||
|
||||
public function testBuildCollectionForOneObject(): void
|
||||
{
|
||||
$fieldCollector = new class implements FieldCollectorInterface {
|
||||
public int $getFieldsMethodCallCounter = 0;
|
||||
|
||||
public function getFields(string $object): FieldCollection
|
||||
{
|
||||
Assert::assertSame($object, 'contact');
|
||||
++$this->getFieldsMethodCallCounter;
|
||||
|
||||
return new FieldCollection();
|
||||
}
|
||||
};
|
||||
|
||||
$mappedObjectCollector = new MappedObjectCollector($fieldCollector);
|
||||
$objectCollection = $mappedObjectCollector->buildCollection('contact');
|
||||
Assert::assertCount(1, $objectCollection);
|
||||
Assert::assertEquals(1, $fieldCollector->getFieldsMethodCallCounter);
|
||||
}
|
||||
|
||||
public function testBuildCollectionForMultipleObjects(): void
|
||||
{
|
||||
$fieldCollector = new class implements FieldCollectorInterface {
|
||||
public int $getFieldsMethodCallCounter = 0;
|
||||
|
||||
public function getFields(string $object): FieldCollection
|
||||
{
|
||||
Assert::assertContains($object, ['company', 'contact']);
|
||||
++$this->getFieldsMethodCallCounter;
|
||||
|
||||
return new FieldCollection();
|
||||
}
|
||||
};
|
||||
|
||||
$mappedObjectCollector = new MappedObjectCollector($fieldCollector);
|
||||
$objectCollection = $mappedObjectCollector->buildCollection('contact', 'company');
|
||||
Assert::assertCount(2, $objectCollection);
|
||||
Assert::assertEquals(2, $fieldCollector->getFieldsMethodCallCounter);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Mautic\FormBundle\Tests\Collector;
|
||||
|
||||
use Mautic\FormBundle\Collection\ObjectCollection;
|
||||
use Mautic\FormBundle\Collector\ObjectCollector;
|
||||
use Mautic\FormBundle\Event\ObjectCollectEvent;
|
||||
use PHPUnit\Framework\Assert;
|
||||
use Symfony\Component\EventDispatcher\EventDispatcher;
|
||||
|
||||
final class ObjectCollectorTest extends \PHPUnit\Framework\TestCase
|
||||
{
|
||||
public function testBuildCollectionForNoObject(): void
|
||||
{
|
||||
$dispatcher = new class extends EventDispatcher {
|
||||
public int $dispatchMethodCallCounter = 0;
|
||||
|
||||
public function dispatch(object $event, ?string $eventName = null): object
|
||||
{
|
||||
++$this->dispatchMethodCallCounter;
|
||||
|
||||
Assert::assertInstanceOf(ObjectCollectEvent::class, $event);
|
||||
|
||||
return new ObjectCollection();
|
||||
}
|
||||
};
|
||||
|
||||
$objectCollector = new ObjectCollector($dispatcher);
|
||||
$objectCollector->getObjects();
|
||||
|
||||
// Calling for the second time to ensure it's cached and the dispatcher is called only once.
|
||||
$objectCollection = $objectCollector->getObjects();
|
||||
|
||||
Assert::assertEquals(1, $dispatcher->dispatchMethodCallCounter);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Mautic\FormBundle\Tests\Controller;
|
||||
|
||||
use Mautic\CoreBundle\Test\MauticMysqlTestCase;
|
||||
use Mautic\FormBundle\Entity\Form;
|
||||
use PHPUnit\Framework\Assert;
|
||||
use Symfony\Component\DomCrawler\Crawler;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
|
||||
final class ActionControllerFunctionalTest extends MauticMysqlTestCase
|
||||
{
|
||||
/**
|
||||
* @throws \Doctrine\ORM\OptimisticLockException
|
||||
* @throws \Doctrine\ORM\ORMException
|
||||
*/
|
||||
public function testNewActionWithJapanese(): void
|
||||
{
|
||||
// Create new form
|
||||
$form = new Form();
|
||||
$form->setName('Test Form');
|
||||
$form->setAlias('testform');
|
||||
$this->em->persist($form);
|
||||
$this->em->flush();
|
||||
|
||||
// Fetch the form
|
||||
$this->client->xmlHttpRequest(Request::METHOD_GET, '/s/forms/action/new',
|
||||
[
|
||||
'formId' => $form->getId(),
|
||||
'type' => 'form.email',
|
||||
]
|
||||
);
|
||||
$this->assertResponseIsSuccessful();
|
||||
$content = $this->client->getResponse()->getContent();
|
||||
$content = json_decode($content)->newContent;
|
||||
$crawler = new Crawler($content, $this->client->getInternalRequest()->getUri());
|
||||
$formCrawler = $crawler->filter('form');
|
||||
$this->assertCount(1, $formCrawler);
|
||||
$form = $formCrawler->form();
|
||||
|
||||
// Save new Send Form Results action
|
||||
$form->setValues([
|
||||
'formaction[properties][subject]' => 'Test Japanese',
|
||||
'formaction[properties][message]' => '<p style="font-family: メイリオ">Test</p>',
|
||||
]);
|
||||
$this->client->submit($form);
|
||||
$this->assertResponseIsSuccessful();
|
||||
$content = $this->client->getResponse()->getContent();
|
||||
$actionHtml = json_decode($content, true)['actionHtml'] ?? null;
|
||||
Assert::assertNotNull($actionHtml, $content);
|
||||
$crawler = new Crawler($actionHtml);
|
||||
$editPage = $crawler->filter('.btn-edit')->attr('href');
|
||||
|
||||
// Check the content was not changed
|
||||
$this->client->xmlHttpRequest(Request::METHOD_GET, $editPage);
|
||||
$this->assertResponseIsSuccessful();
|
||||
$this->assertStringContainsString('<p style="font-family: メイリオ">Test</p>', json_decode($this->client->getResponse()->getContent())->newContent);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Mautic\FormBundle\Tests\Controller;
|
||||
|
||||
use Mautic\CoreBundle\Test\MauticMysqlTestCase;
|
||||
use PHPUnit\Framework\Assert;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
|
||||
final class AjaxControllerFunctionalTest extends MauticMysqlTestCase
|
||||
{
|
||||
public function testGetFieldsForObjectAction(): void
|
||||
{
|
||||
$this->client->xmlHttpRequest(
|
||||
Request::METHOD_GET,
|
||||
'/s/ajax?action=form:getFieldsForObject&mappedObject=company&mappedField=&formId=10'
|
||||
);
|
||||
$clientResponse = $this->client->getResponse();
|
||||
$payload = json_decode($clientResponse->getContent(), true);
|
||||
self::assertResponseIsSuccessful();
|
||||
|
||||
// Assert some random fields exist.
|
||||
Assert::assertSame(
|
||||
[
|
||||
'label' => 'Company Email',
|
||||
'value' => 'companyemail',
|
||||
'isListType' => false,
|
||||
],
|
||||
$payload['fields'][4]
|
||||
);
|
||||
Assert::assertSame(
|
||||
[
|
||||
'label' => 'Industry',
|
||||
'value' => 'companyindustry',
|
||||
'isListType' => true,
|
||||
],
|
||||
$payload['fields'][9]
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,643 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Mautic\FormBundle\Tests\Controller\Api;
|
||||
|
||||
use Mautic\CoreBundle\Test\MauticMysqlTestCase;
|
||||
use Mautic\FormBundle\Entity\Submission;
|
||||
use Mautic\LeadBundle\Entity\Company;
|
||||
use Mautic\UserBundle\Entity\User;
|
||||
use PHPUnit\Framework\Assert;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
final class FormApiControllerFunctionalTest extends MauticMysqlTestCase
|
||||
{
|
||||
protected $useCleanupRollback = false;
|
||||
|
||||
private const TEST_PAYLOAD = [
|
||||
'name' => 'API form',
|
||||
'description' => 'Form created via API test',
|
||||
'formType' => 'standalone',
|
||||
'isPublished' => true,
|
||||
'fields' => [
|
||||
[
|
||||
'label' => 'Email',
|
||||
'type' => 'text',
|
||||
'alias' => 'email',
|
||||
'mappedObject' => 'contact',
|
||||
'mappedField' => 'email',
|
||||
'showLabel' => true,
|
||||
'isRequired' => true,
|
||||
],
|
||||
[
|
||||
'label' => 'Number',
|
||||
'type' => 'number',
|
||||
'alias' => 'number',
|
||||
'leadField' => 'points', // @deprecated Setting leadField, no mappedField or mappedObject (BC).
|
||||
],
|
||||
[
|
||||
'label' => 'Company',
|
||||
'type' => 'text',
|
||||
'alias' => 'company',
|
||||
'leadField' => 'company', // @deprecated Setting leadField, no mappedField or mappedObject (BC).
|
||||
],
|
||||
[
|
||||
'label' => 'Company Phone',
|
||||
'type' => 'tel',
|
||||
'alias' => 'phone',
|
||||
'leadField' => 'companyphone', // @deprecated Setting leadField, no mappedField or mappedObject (BC).
|
||||
],
|
||||
[
|
||||
'label' => 'Country',
|
||||
'type' => 'country',
|
||||
'alias' => 'country',
|
||||
'mappedObject' => 'contact',
|
||||
'mappedField' => 'country',
|
||||
],
|
||||
[
|
||||
'label' => 'Multiselect',
|
||||
'type' => 'select',
|
||||
'alias' => 'multiselect',
|
||||
'properties' => [
|
||||
'syncList' => 0,
|
||||
'multiple' => 1,
|
||||
'list' => [
|
||||
'list' => [
|
||||
[
|
||||
'label' => 'One',
|
||||
'value' => 'one',
|
||||
],
|
||||
[
|
||||
'label' => 'Two',
|
||||
'value' => 'two',
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
[
|
||||
'label' => 'Submit',
|
||||
'type' => 'button',
|
||||
],
|
||||
],
|
||||
'actions' => [
|
||||
],
|
||||
'postAction' => 'return',
|
||||
];
|
||||
|
||||
/**
|
||||
* @param array<string, mixed> $payload
|
||||
* @param array<string, mixed> $expectedResponse
|
||||
*/
|
||||
#[\PHPUnit\Framework\Attributes\DataProvider('formDataProvider')]
|
||||
public function testAddAndEditForms(array $payload, array $expectedResponse): void
|
||||
{
|
||||
$this->client->request('POST', '/api/forms/new', $payload);
|
||||
$clientResponse = $this->client->getResponse();
|
||||
$response = json_decode($clientResponse->getContent(), true);
|
||||
|
||||
if (!empty($response['errors'][0])) {
|
||||
$this->fail($response['errors'][0]['code'].': '.$response['errors'][0]['message']);
|
||||
}
|
||||
$this->assertResponseStatusCodeSame(Response::HTTP_CREATED, 'Return code must be 201.');
|
||||
|
||||
$formId = $response['form']['id'];
|
||||
$this->assertGreaterThan(0, $formId);
|
||||
$this->assertEquals($payload['name'], $response['form']['name']);
|
||||
$this->assertEquals($payload['formType'], $response['form']['formType']);
|
||||
$this->assertEquals($payload['isPublished'], $response['form']['isPublished']);
|
||||
$this->assertEquals($payload['description'], $response['form']['description']);
|
||||
$this->assertIsArray($response['form']['fields']);
|
||||
$this->assertCount(count($payload['fields']), $response['form']['fields']);
|
||||
for ($i = 0; $i < count($payload['fields']); ++$i) {
|
||||
$this->assertEquals($payload['fields'][$i]['label'], $response['form']['fields'][$i]['label']);
|
||||
$this->assertEquals($payload['fields'][$i]['alias'], $response['form']['fields'][$i]['alias']);
|
||||
$this->assertEquals($payload['fields'][$i]['type'], $response['form']['fields'][$i]['type']);
|
||||
$this->assertEquals($expectedResponse['fields'][$i]['leadField'], $response['form']['fields'][$i]['leadField']);
|
||||
$this->assertEquals($expectedResponse['fields'][$i]['mappedField'], $response['form']['fields'][$i]['mappedField']);
|
||||
$this->assertEquals($expectedResponse['fields'][$i]['mappedObject'], $response['form']['fields'][$i]['mappedObject']);
|
||||
}
|
||||
|
||||
// Edit PATCH:
|
||||
$this->client->request('PATCH', "/api/forms/{$formId}/edit", ['name' => $expectedResponse['newName']]);
|
||||
$clientResponse = $this->client->getResponse();
|
||||
$responsePatch = json_decode($clientResponse->getContent(), true);
|
||||
$this->assertResponseIsSuccessful();
|
||||
$this->assertSame($formId, $responsePatch['form']['id'], 'ID of the created form does not match with the edited one.');
|
||||
$this->assertEquals($expectedResponse['newName'], $responsePatch['form']['name']);
|
||||
$this->assertEquals($payload['formType'], $responsePatch['form']['formType']);
|
||||
$this->assertEquals($payload['isPublished'], $responsePatch['form']['isPublished']);
|
||||
$this->assertEquals($payload['description'], $responsePatch['form']['description']);
|
||||
$this->assertIsArray($responsePatch['form']['fields']);
|
||||
$this->assertCount(count($payload['fields']), $responsePatch['form']['fields']);
|
||||
for ($i = 0; $i < count($payload['fields']); ++$i) {
|
||||
$this->assertEquals($payload['fields'][$i]['label'], $responsePatch['form']['fields'][$i]['label']);
|
||||
$this->assertEquals($payload['fields'][$i]['alias'], $responsePatch['form']['fields'][$i]['alias']);
|
||||
$this->assertEquals($payload['fields'][$i]['type'], $responsePatch['form']['fields'][$i]['type']);
|
||||
$this->assertEquals($expectedResponse['fields'][$i]['leadField'], $responsePatch['form']['fields'][$i]['leadField']);
|
||||
$this->assertEquals($expectedResponse['fields'][$i]['mappedField'], $responsePatch['form']['fields'][$i]['mappedField']);
|
||||
$this->assertEquals($expectedResponse['fields'][$i]['mappedObject'], $responsePatch['form']['fields'][$i]['mappedObject']);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<int, array<int, array<string, mixed>>>
|
||||
*/
|
||||
public static function formDataProvider(): array
|
||||
{
|
||||
return [
|
||||
[
|
||||
[
|
||||
'name' => 'Form API test',
|
||||
'formType' => 'standalone',
|
||||
'isPublished' => true,
|
||||
'description' => 'Functional API test',
|
||||
'fields' => [
|
||||
[
|
||||
'label' => 'Email',
|
||||
'alias' => 'email',
|
||||
'type' => 'text',
|
||||
'leadField' => 'email',
|
||||
],
|
||||
[
|
||||
'label' => 'Company Address',
|
||||
'type' => 'text',
|
||||
'alias' => 'companyaddress1',
|
||||
'leadField' => 'companyaddress1',
|
||||
],
|
||||
[
|
||||
'label' => 'Company Phone',
|
||||
'type' => 'tel',
|
||||
'alias' => 'phone',
|
||||
'leadField' => 'companyphone',
|
||||
],
|
||||
[
|
||||
'label' => 'Country',
|
||||
'type' => 'country',
|
||||
'alias' => 'country',
|
||||
'mappedObject' => 'contact',
|
||||
'mappedField' => 'country',
|
||||
],
|
||||
],
|
||||
'postAction' => 'return',
|
||||
],
|
||||
[
|
||||
'newName' => 'Form API test',
|
||||
'fields' => [
|
||||
[
|
||||
'mappedObject' => 'contact',
|
||||
'leadField' => 'email',
|
||||
'mappedField' => 'email',
|
||||
],
|
||||
[
|
||||
'mappedObject' => 'company',
|
||||
'leadField' => 'companyaddress1',
|
||||
'mappedField' => 'companyaddress1',
|
||||
],
|
||||
[
|
||||
'mappedObject' => 'company',
|
||||
'leadField' => 'companyphone',
|
||||
'mappedField' => 'companyphone',
|
||||
], [
|
||||
'mappedObject' => 'contact',
|
||||
'leadField' => 'country',
|
||||
'mappedField' => 'country',
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
[
|
||||
[
|
||||
'name' => 'Form',
|
||||
'formType' => 'standalone',
|
||||
'isPublished' => true,
|
||||
'description' => 'Functional API test2',
|
||||
'fields' => [
|
||||
[
|
||||
'label' => 'Lastname',
|
||||
'alias' => 'lastname',
|
||||
'type' => 'text',
|
||||
'mappedField' => 'lastname',
|
||||
'mappedObject' => 'contact',
|
||||
],
|
||||
[
|
||||
'label' => 'Company Email',
|
||||
'type' => 'text',
|
||||
'alias' => 'companyemail1',
|
||||
'mappedField' => 'companyemail',
|
||||
'mappedObject' => 'company',
|
||||
'leadField' => 'companyemail',
|
||||
],
|
||||
[
|
||||
'label' => 'Phone',
|
||||
'type' => 'tel',
|
||||
'alias' => 'phone',
|
||||
'leadField' => 'position',
|
||||
],
|
||||
],
|
||||
'postAction' => 'return',
|
||||
],
|
||||
[
|
||||
'newName' => 'Form API test',
|
||||
'fields' => [
|
||||
[
|
||||
'mappedObject' => 'contact',
|
||||
'leadField' => 'lastname',
|
||||
'mappedField' => 'lastname',
|
||||
],
|
||||
[
|
||||
'mappedObject' => 'company',
|
||||
'leadField' => 'companyemail',
|
||||
'mappedField' => 'companyemail',
|
||||
],
|
||||
[
|
||||
'mappedObject' => 'contact',
|
||||
'leadField' => 'position',
|
||||
'mappedField' => 'position',
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
public function testSingleFormWorkflow(): void
|
||||
{
|
||||
$payload = self::TEST_PAYLOAD;
|
||||
$fieldCount = count($payload['fields']);
|
||||
$this->client->request(Request::METHOD_POST, '/api/forms/new', $payload);
|
||||
$clientResponse = $this->client->getResponse();
|
||||
$response = json_decode($clientResponse->getContent(), true);
|
||||
|
||||
Assert::assertTrue(isset($response['form']['id']), $clientResponse->getContent());
|
||||
|
||||
$formId = $response['form']['id'];
|
||||
|
||||
$this->assertResponseStatusCodeSame(Response::HTTP_CREATED, $clientResponse->getContent());
|
||||
$this->assertGreaterThan(0, $formId);
|
||||
$this->assertEquals($payload['name'], $response['form']['name']);
|
||||
$this->assertEquals($payload['description'], $response['form']['description']);
|
||||
$this->assertEquals($payload['formType'], $response['form']['formType']);
|
||||
$this->assertNotEmpty($response['form']['cachedHtml']);
|
||||
$this->assertCount($fieldCount, $response['form']['fields']);
|
||||
$this->assertEquals($payload['fields'][0]['label'], $response['form']['fields'][0]['label']);
|
||||
$this->assertEquals($payload['fields'][0]['type'], $response['form']['fields'][0]['type']);
|
||||
$this->assertEquals($payload['fields'][0]['mappedObject'], $response['form']['fields'][0]['mappedObject']);
|
||||
$this->assertEquals($payload['fields'][0]['mappedField'], $response['form']['fields'][0]['mappedField']);
|
||||
$this->assertEquals(
|
||||
$payload['fields'][0]['mappedField'],
|
||||
$response['form']['fields'][0]['leadField']
|
||||
); // @deprecated leadField was replaced by mappedField. Check for BC.
|
||||
$this->assertEquals($payload['fields'][0]['showLabel'], $response['form']['fields'][0]['showLabel']);
|
||||
$this->assertEquals($payload['fields'][0]['isRequired'], $response['form']['fields'][0]['isRequired']);
|
||||
$this->assertEquals($payload['fields'][1]['label'], $response['form']['fields'][1]['label']);
|
||||
$this->assertEquals($payload['fields'][1]['type'], $response['form']['fields'][1]['type']);
|
||||
$this->assertEquals('contact', $response['form']['fields'][1]['mappedObject']);
|
||||
$this->assertEquals('points', $response['form']['fields'][1]['mappedField']);
|
||||
$this->assertEquals(
|
||||
$payload['fields'][1]['leadField'],
|
||||
$response['form']['fields'][1]['leadField']
|
||||
); // @deprecated leadField was replaced by mappedField. Check for BC.
|
||||
$this->assertTrue($response['form']['fields'][1]['showLabel']);
|
||||
$this->assertFalse($response['form']['fields'][1]['isRequired']);
|
||||
$this->assertEquals($payload['fields'][2]['label'], $response['form']['fields'][2]['label']);
|
||||
$this->assertEquals($payload['fields'][2]['type'], $response['form']['fields'][2]['type']);
|
||||
$this->assertEquals('contact', $response['form']['fields'][2]['mappedObject']);
|
||||
$this->assertEquals('company', $response['form']['fields'][2]['mappedField']);
|
||||
$this->assertEquals(
|
||||
$payload['fields'][2]['leadField'],
|
||||
$response['form']['fields'][2]['leadField']
|
||||
); // @deprecated leadField was replaced by mappedField. Check for BC.
|
||||
$this->assertEquals($payload['fields'][3]['label'], $response['form']['fields'][3]['label']);
|
||||
$this->assertEquals($payload['fields'][3]['type'], $response['form']['fields'][3]['type']);
|
||||
$this->assertEquals('company', $response['form']['fields'][3]['mappedObject']);
|
||||
$this->assertEquals('companyphone', $response['form']['fields'][3]['mappedField']);
|
||||
$this->assertEquals(
|
||||
$payload['fields'][3]['leadField'],
|
||||
$response['form']['fields'][3]['leadField']
|
||||
); // @deprecated leadField was replaced by mappedField. Check for BC.
|
||||
|
||||
// Edit PATCH:
|
||||
$patchPayload = [
|
||||
'name' => 'API form renamed',
|
||||
'fields' => [
|
||||
[
|
||||
'label' => 'State',
|
||||
'type' => 'select',
|
||||
'alias' => 'state',
|
||||
'mappedObject' => 'contact',
|
||||
'mappedField' => 'state',
|
||||
'parent' => $response['form']['fields'][4]['id'],
|
||||
'conditions' => [
|
||||
'expr' => 'in',
|
||||
'any' => 1,
|
||||
'values' => [],
|
||||
],
|
||||
'properties' => [
|
||||
'syncList' => 1,
|
||||
'multiple' => 0,
|
||||
],
|
||||
],
|
||||
],
|
||||
];
|
||||
$this->client->request(Request::METHOD_PATCH, "/api/forms/{$formId}/edit", $patchPayload);
|
||||
$clientResponse = $this->client->getResponse();
|
||||
$response = json_decode($clientResponse->getContent(), true);
|
||||
$fieldCount = $fieldCount + 1;
|
||||
|
||||
$this->assertResponseIsSuccessful($clientResponse->getContent());
|
||||
$this->assertSame($formId, $response['form']['id']);
|
||||
$this->assertEquals('API form renamed', $response['form']['name']);
|
||||
$this->assertEquals($payload['description'], $response['form']['description']);
|
||||
$this->assertCount($fieldCount, $response['form']['fields']);
|
||||
$this->assertEquals($payload['formType'], $response['form']['formType']);
|
||||
$this->assertNotEmpty($response['form']['cachedHtml']);
|
||||
|
||||
// Edit PUT:
|
||||
$payload['description'] .= ' renamed';
|
||||
$payload['fields'] = []; // Set fields to an empty array as it would duplicate all fields.
|
||||
$payload['postAction'] = 'return'; // Must be present for PUT as all empty values are being cleared.
|
||||
$this->client->request(Request::METHOD_PUT, "/api/forms/{$formId}/edit", $payload);
|
||||
$clientResponse = $this->client->getResponse();
|
||||
$response = json_decode($clientResponse->getContent(), true);
|
||||
$this->assertResponseIsSuccessful($clientResponse->getContent());
|
||||
$this->assertSame($formId, $response['form']['id']);
|
||||
$this->assertEquals($payload['name'], $response['form']['name']);
|
||||
$this->assertEquals('Form created via API test renamed', $response['form']['description']);
|
||||
$this->assertCount($fieldCount, $response['form']['fields']);
|
||||
$this->assertEquals($payload['formType'], $response['form']['formType']);
|
||||
$this->assertNotEmpty($response['form']['cachedHtml']);
|
||||
|
||||
// Get:
|
||||
$this->client->request(Request::METHOD_GET, "/api/forms/{$formId}");
|
||||
$clientResponse = $this->client->getResponse();
|
||||
$response = json_decode($clientResponse->getContent(), true);
|
||||
|
||||
$this->assertResponseIsSuccessful($clientResponse->getContent());
|
||||
$this->assertSame($formId, $response['form']['id']);
|
||||
$this->assertEquals($payload['name'], $response['form']['name']);
|
||||
$this->assertEquals($payload['description'], $response['form']['description']);
|
||||
$this->assertCount($fieldCount, $response['form']['fields']);
|
||||
$this->assertEquals($payload['formType'], $response['form']['formType']);
|
||||
$this->assertNotEmpty($response['form']['cachedHtml']);
|
||||
|
||||
// Submit the form:
|
||||
$crawler = $this->client->request(Request::METHOD_GET, "/form/{$formId}");
|
||||
$formCrawler = $crawler->filter('form[id=mauticform_apiform]');
|
||||
$this->assertCount(1, $formCrawler);
|
||||
$form = $formCrawler->form();
|
||||
$form->setValues([
|
||||
'mauticform[email]' => 'john@doe.test',
|
||||
'mauticform[number]' => '123',
|
||||
'mauticform[company]' => 'Doe Corp',
|
||||
'mauticform[phone]' => '+420444555666',
|
||||
'mauticform[country]' => 'Czech Republic',
|
||||
'mauticform[state]' => 'Plzeňský kraj',
|
||||
'mauticform[multiselect]' => ['two'],
|
||||
]);
|
||||
$this->client->submit($form);
|
||||
|
||||
// Ensure the submission was created properly.
|
||||
$submissions = $this->em->getRepository(Submission::class)->findAll();
|
||||
|
||||
Assert::assertCount(1, $submissions);
|
||||
|
||||
/** @var Submission $submission */
|
||||
$submission = $submissions[0];
|
||||
Assert::assertSame([
|
||||
'email' => 'john@doe.test',
|
||||
'number' => 123.0,
|
||||
'company' => 'Doe Corp',
|
||||
'phone' => '+420444555666',
|
||||
'country' => 'Czech Republic',
|
||||
'multiselect' => 'two',
|
||||
'state' => 'Plzeňský kraj',
|
||||
], $submission->getResults());
|
||||
|
||||
// A contact should be created by the submission.
|
||||
$contact = $submission->getLead();
|
||||
|
||||
Assert::assertSame('john@doe.test', $contact->getEmail());
|
||||
Assert::assertSame('Czech Republic', $contact->getCountry());
|
||||
Assert::assertSame('Plzeňský kraj', $contact->getState());
|
||||
Assert::assertSame(123, $contact->getPoints());
|
||||
Assert::assertSame('Doe Corp', $contact->getCompany());
|
||||
|
||||
$companies = $this->em->getRepository(Company::class)->findAll();
|
||||
|
||||
Assert::assertCount(1, $companies);
|
||||
|
||||
// A company should be created by the submission.
|
||||
/** @var Company $company */
|
||||
$company = $companies[0];
|
||||
Assert::assertSame('Doe Corp', $company->getName());
|
||||
Assert::assertSame('+420444555666', $company->getPhone());
|
||||
|
||||
// The previous request changes user to anonymous.
|
||||
$this->loginUser($this->em->getRepository(User::class)->findOneBy(['username' => 'admin']));
|
||||
|
||||
// Delete:
|
||||
$this->client->request(Request::METHOD_DELETE, "/api/forms/{$formId}/delete");
|
||||
$clientResponse = $this->client->getResponse();
|
||||
$response = json_decode($clientResponse->getContent(), true);
|
||||
|
||||
$this->assertResponseIsSuccessful($clientResponse->getContent());
|
||||
$this->assertNull($response['form']['id']);
|
||||
$this->assertEquals($payload['name'], $response['form']['name']);
|
||||
$this->assertEquals($payload['description'], $response['form']['description']);
|
||||
$this->assertCount($fieldCount, $response['form']['fields']);
|
||||
$this->assertEquals($payload['formType'], $response['form']['formType']);
|
||||
$this->assertNotEmpty($response['form']['cachedHtml']);
|
||||
|
||||
// Get (ensure that the form is gone):
|
||||
$this->client->request(Request::METHOD_GET, "/api/forms/{$formId}");
|
||||
$clientResponse = $this->client->getResponse();
|
||||
$response = json_decode($clientResponse->getContent(), true);
|
||||
|
||||
$this->assertResponseStatusCodeSame(Response::HTTP_NOT_FOUND, $clientResponse->getContent());
|
||||
$this->assertSame(Response::HTTP_NOT_FOUND, $response['errors'][0]['code']);
|
||||
}
|
||||
|
||||
public function testFormWithChangeTagsAction(): void
|
||||
{
|
||||
// Create tag:
|
||||
$tag1Payload = ['tag' => 'add this'];
|
||||
$tag2Payload = ['tag' => 'remove this'];
|
||||
|
||||
$this->client->request('POST', '/api/tags/new', $tag1Payload);
|
||||
$clientResponse = $this->client->getResponse();
|
||||
$response = json_decode($clientResponse->getContent(), true);
|
||||
$tag1Id = $response['tag']['id'];
|
||||
|
||||
$this->client->request('POST', '/api/tags/new', $tag2Payload);
|
||||
$clientResponse = $this->client->getResponse();
|
||||
$response = json_decode($clientResponse->getContent(), true);
|
||||
$tag2Id = $response['tag']['id'];
|
||||
|
||||
$payload = [
|
||||
'name' => 'Form API test',
|
||||
'formType' => 'standalone',
|
||||
'isPublished' => true,
|
||||
'description' => 'Functional API test',
|
||||
'fields' => [
|
||||
[
|
||||
'label' => 'lab',
|
||||
'alias' => 'email',
|
||||
'type' => 'text',
|
||||
'leadField' => 'email',
|
||||
],
|
||||
],
|
||||
'actions' => [
|
||||
[
|
||||
'name' => 'Add tags to contact',
|
||||
'description' => 'action description',
|
||||
'type' => 'lead.changetags',
|
||||
'order' => 1,
|
||||
'properties' => [
|
||||
'add_tags' => [$tag1Id],
|
||||
'remove_tags' => [$tag2Id],
|
||||
],
|
||||
],
|
||||
],
|
||||
'postAction' => 'return',
|
||||
];
|
||||
|
||||
// Create form with lead.changetags action:
|
||||
$this->client->request('POST', '/api/forms/new', $payload);
|
||||
$clientResponse = $this->client->getResponse();
|
||||
$response = json_decode($clientResponse->getContent(), true);
|
||||
|
||||
if (!empty($response['errors'][0])) {
|
||||
$this->fail($response['errors'][0]['code'].': '.$response['errors'][0]['message']);
|
||||
}
|
||||
$this->assertResponseStatusCodeSame(Response::HTTP_CREATED, 'Return code must be 201.');
|
||||
|
||||
$formId = $response['form']['id'];
|
||||
$this->assertGreaterThan(0, $formId);
|
||||
$this->assertEquals($payload['name'], $response['form']['name']);
|
||||
$this->assertEquals($payload['formType'], $response['form']['formType']);
|
||||
$this->assertEquals($payload['isPublished'], $response['form']['isPublished']);
|
||||
$this->assertEquals($payload['description'], $response['form']['description']);
|
||||
$this->assertIsArray($response['form']['fields']);
|
||||
$this->assertCount(count($payload['fields']), $response['form']['fields']);
|
||||
for ($i = 0; $i < count($payload['fields']); ++$i) {
|
||||
$this->assertEquals($payload['fields'][$i]['label'], $response['form']['fields'][$i]['label']);
|
||||
$this->assertEquals($payload['fields'][$i]['alias'], $response['form']['fields'][$i]['alias']);
|
||||
$this->assertEquals($payload['fields'][$i]['type'], $response['form']['fields'][$i]['type']);
|
||||
$this->assertEquals($payload['fields'][$i]['leadField'], $response['form']['fields'][$i]['leadField']);
|
||||
}
|
||||
$this->assertIsArray($response['form']['actions']);
|
||||
$this->assertCount(count($payload['actions']), $response['form']['actions']);
|
||||
$this->assertEquals($payload['actions'][0]['name'], $response['form']['actions'][0]['name']);
|
||||
$this->assertEquals($payload['actions'][0]['description'], $response['form']['actions'][0]['description']);
|
||||
$this->assertEquals($payload['actions'][0]['type'], $response['form']['actions'][0]['type']);
|
||||
$this->assertEquals($payload['actions'][0]['order'], $response['form']['actions'][0]['order']);
|
||||
$this->assertIsArray($response['form']['actions'][0]['properties']['add_tags']);
|
||||
$this->assertIsArray($response['form']['actions'][0]['properties']['remove_tags']);
|
||||
$this->assertEquals($tag1Payload['tag'], $response['form']['actions'][0]['properties']['add_tags'][0]);
|
||||
$this->assertEquals($tag2Payload['tag'], $response['form']['actions'][0]['properties']['remove_tags'][0]);
|
||||
}
|
||||
|
||||
public function testFormWithDuplicateFieldAliases(): void
|
||||
{
|
||||
// Create form
|
||||
$payload = [
|
||||
'name' => 'Form API test',
|
||||
'formType' => 'standalone',
|
||||
'isPublished' => true,
|
||||
'description' => 'Functional API test',
|
||||
'fields' => [
|
||||
[
|
||||
'label' => 'Email',
|
||||
'alias' => 'email',
|
||||
'type' => 'text',
|
||||
'leadField' => 'email',
|
||||
],
|
||||
],
|
||||
'postAction' => 'return',
|
||||
];
|
||||
|
||||
$this->client->request(Request::METHOD_POST, '/api/forms/new', $payload);
|
||||
$clientResponse = $this->client->getResponse();
|
||||
$response = json_decode($clientResponse->getContent(), true);
|
||||
|
||||
$lastValidFormId = $response['form']['id'];
|
||||
$this->assertGreaterThan(0, $lastValidFormId);
|
||||
$this->assertResponseStatusCodeSame(Response::HTTP_CREATED, 'Return code must be 201.');
|
||||
|
||||
// Get the last correctly saved form
|
||||
$this->client->request(Request::METHOD_GET, '/api/forms/'.$lastValidFormId);
|
||||
$clientResponse = $this->client->getResponse();
|
||||
$response = json_decode($clientResponse->getContent(), true);
|
||||
|
||||
$this->assertIsArray($response['form']);
|
||||
$this->assertCount(1, $response);
|
||||
$this->assertEquals($lastValidFormId, $response['form']['id']);
|
||||
|
||||
// Try to update invalid, non-existent form
|
||||
$longAlias = 'very_long_field_alias_12345';
|
||||
$invalidPayload = [
|
||||
'name' => 'Form API test',
|
||||
'formType' => 'standalone',
|
||||
'isPublished' => true,
|
||||
'description' => 'Functional API test',
|
||||
'fields' => [
|
||||
[
|
||||
'label' => 'test1',
|
||||
'alias' => 'very_long_field_alias_12345',
|
||||
'type' => 'text',
|
||||
],
|
||||
[
|
||||
'label' => 'test2',
|
||||
'alias' => 'very_long_field_alias_123456',
|
||||
'type' => 'text',
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
$this->client->request(Request::METHOD_PUT, '/api/forms/123/edit', $invalidPayload);
|
||||
$clientResponse = $this->client->getResponse();
|
||||
$response = json_decode($clientResponse->getContent(), true);
|
||||
|
||||
$validationMessage = 'Another field is already using this alias: %alias%. Please choose another or leave it blank to have it autogenerated.';
|
||||
$expectedMessage = str_replace('%alias%', substr($longAlias, 0, 25), $validationMessage);
|
||||
|
||||
$this->assertNotEmpty($response['errors'], 'No errors were returned when trying to save an invalid form');
|
||||
$this->assertSame(Response::HTTP_BAD_REQUEST, $response['errors'][0]['code'], 'Return code must be 400.');
|
||||
$this->assertResponseStatusCodeSame(Response::HTTP_BAD_REQUEST, 'Return code must be 400.');
|
||||
$this->assertSame($expectedMessage, $response['errors'][0]['message'], 'Returned message is different than expected');
|
||||
}
|
||||
|
||||
public function testFormWithInvalidField(): void
|
||||
{
|
||||
$payload = [
|
||||
'name' => 'Form API test',
|
||||
'formType' => 'standalone',
|
||||
'isPublished' => true,
|
||||
'description' => 'Functional API test',
|
||||
'fields' => [
|
||||
[
|
||||
'label' => 'test1',
|
||||
'alias' => 'test1',
|
||||
'type' => 'text',
|
||||
],
|
||||
[
|
||||
'label' => 'test2',
|
||||
'id' => 123,
|
||||
'alias' => 'test2',
|
||||
'type' => 'invalidField',
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
$this->client->request(Request::METHOD_PUT, '/api/forms/123/edit', $payload);
|
||||
$response = $this->client->getResponse();
|
||||
$responseContent = json_decode($response->getContent());
|
||||
|
||||
$this->assertNotEmpty($responseContent->errors, 'No errors were returned when trying to save an invalid form');
|
||||
$this->assertSame('Form Field ID 123 not found', $responseContent->errors[0]->message);
|
||||
$this->assertResponseStatusCodeSame(Response::HTTP_NOT_FOUND, 'Return code must be 404.');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,296 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Mautic\FormBundle\Tests\Controller\Api;
|
||||
|
||||
use Mautic\CoreBundle\Test\MauticMysqlTestCase;
|
||||
use Mautic\FormBundle\Entity\Form;
|
||||
use PHPUnit\Framework\Attributes\DataProvider;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
final class FormApiControllerTest extends MauticMysqlTestCase
|
||||
{
|
||||
protected $useCleanupRollback = false;
|
||||
|
||||
/**
|
||||
* @param array<string, mixed> $formData
|
||||
*/
|
||||
#[DataProvider('formDataProvider')]
|
||||
public function testCreateFormWithFieldsAndActions(array $formData, int $expectedStatusCode): void
|
||||
{
|
||||
$this->client->request(
|
||||
'POST',
|
||||
'/api/forms/new',
|
||||
$formData,
|
||||
);
|
||||
|
||||
$response = $this->client->getResponse();
|
||||
$this->assertSame($expectedStatusCode, $response->getStatusCode());
|
||||
$this->assertResponseIsSuccessful();
|
||||
|
||||
$responseData = json_decode($response->getContent(), true);
|
||||
|
||||
$this->assertArrayHasKey('form', $responseData);
|
||||
$this->assertArrayHasKey('id', $responseData['form']);
|
||||
$this->assertArrayHasKey('name', $responseData['form']);
|
||||
$this->assertSame($formData['name'], $responseData['form']['name']);
|
||||
|
||||
// Verify fields were created if provided
|
||||
if (isset($formData['fields'])) {
|
||||
$this->assertArrayHasKey('fields', $responseData['form']);
|
||||
$this->assertCount(count($formData['fields']), $responseData['form']['fields']);
|
||||
|
||||
foreach ($formData['fields'] as $index => $expectedField) {
|
||||
$actualField = $responseData['form']['fields'][$index];
|
||||
$this->assertSame($expectedField['label'], $actualField['label']);
|
||||
$this->assertSame($expectedField['type'], $actualField['type']);
|
||||
|
||||
if (isset($expectedField['alias'])) {
|
||||
$this->assertSame($expectedField['alias'], $actualField['alias']);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Verify actions were created if provided
|
||||
if (isset($formData['actions'])) {
|
||||
$this->assertArrayHasKey('actions', $responseData['form']);
|
||||
$this->assertCount(count($formData['actions']), $responseData['form']['actions']);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, mixed> $initialFormData
|
||||
* @param array<string, mixed> $updateData
|
||||
*/
|
||||
#[DataProvider('updateFormDataProvider')]
|
||||
public function testUpdateFormWithFieldsAndActions(array $initialFormData, array $updateData, int $expectedStatusCode): void
|
||||
{
|
||||
$form = $this->createForm($initialFormData);
|
||||
|
||||
$this->client->request(
|
||||
'PUT',
|
||||
'/api/forms/'.$form->getId().'/edit',
|
||||
$updateData,
|
||||
);
|
||||
|
||||
$response = $this->client->getResponse();
|
||||
$this->assertSame($expectedStatusCode, $response->getStatusCode());
|
||||
$this->assertResponseIsSuccessful();
|
||||
|
||||
$responseData = json_decode($response->getContent(), true);
|
||||
|
||||
$this->assertArrayHasKey('form', $responseData);
|
||||
$this->assertSame($form->getId(), $responseData['form']['id']);
|
||||
|
||||
// Verify updates were applied
|
||||
if (isset($updateData['name'])) {
|
||||
$this->assertSame($updateData['name'], $responseData['form']['name']);
|
||||
}
|
||||
|
||||
// Verify field updates/deletions for PUT requests
|
||||
if (isset($updateData['fields'])) {
|
||||
$this->assertArrayHasKey('fields', $responseData['form']);
|
||||
$this->assertCount(count($updateData['fields']), $responseData['form']['fields']);
|
||||
}
|
||||
}
|
||||
|
||||
public function testCreateFormWithDuplicateFieldAliasesHandledCorrectly(): void
|
||||
{
|
||||
$formData = [
|
||||
'name' => 'Test Form with Duplicate Aliases',
|
||||
'fields' => [
|
||||
[
|
||||
'label' => 'First Field',
|
||||
'type' => 'text',
|
||||
'alias' => 'duplicate_alias',
|
||||
],
|
||||
[
|
||||
'label' => 'Second Field',
|
||||
'type' => 'text',
|
||||
'alias' => 'duplicate_alias',
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
$this->client->request(
|
||||
'POST',
|
||||
'/api/forms/new',
|
||||
$formData,
|
||||
);
|
||||
|
||||
$response = $this->client->getResponse();
|
||||
|
||||
// The form creation should be rejected due to duplicate aliases
|
||||
$this->assertSame(Response::HTTP_BAD_REQUEST, $response->getStatusCode());
|
||||
$responseData = json_decode($response->getContent(), true);
|
||||
|
||||
// Check for error response - could be either 'error' or 'errors' key depending on API format
|
||||
$this->assertTrue(
|
||||
isset($responseData['error']) || isset($responseData['errors']),
|
||||
'Response should contain error information'
|
||||
);
|
||||
|
||||
// Check that the error mentions the duplicate alias
|
||||
if (isset($responseData['error']['message'])) {
|
||||
$this->assertStringContainsString('duplicate_alias', $responseData['error']['message']);
|
||||
} elseif (isset($responseData['errors'][0]['message'])) {
|
||||
$this->assertStringContainsString('duplicate_alias', $responseData['errors'][0]['message']);
|
||||
}
|
||||
}
|
||||
|
||||
public function testCreateFormWithInvalidFieldData(): void
|
||||
{
|
||||
$formData = [
|
||||
'name' => 'Test Form with Invalid Field',
|
||||
'fields' => [
|
||||
[
|
||||
'label' => '', // Empty label should cause validation error
|
||||
'type' => 'text',
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
$this->client->request(
|
||||
'POST',
|
||||
'/api/forms/new',
|
||||
$formData,
|
||||
);
|
||||
|
||||
$response = $this->client->getResponse();
|
||||
$this->assertSame(Response::HTTP_BAD_REQUEST, $response->getStatusCode());
|
||||
}
|
||||
|
||||
public function testUpdateFormRemovesUnspecifiedFieldsOnPut(): void
|
||||
{
|
||||
// Create form with multiple fields
|
||||
$form = $this->createForm([
|
||||
'name' => 'Test Form',
|
||||
'fields' => [
|
||||
['label' => 'Field 1', 'type' => 'text'],
|
||||
['label' => 'Field 2', 'type' => 'email'],
|
||||
['label' => 'Field 3', 'type' => 'textarea'],
|
||||
],
|
||||
]);
|
||||
|
||||
// Update with PUT - only include one field (should remove others)
|
||||
$firstField = $form->getFields()->first();
|
||||
$this->assertNotFalse($firstField, 'Form should have at least one field');
|
||||
|
||||
$updateData = [
|
||||
'name' => 'Updated Form',
|
||||
'fields' => [
|
||||
[
|
||||
'id' => $firstField->getId(),
|
||||
'label' => 'Updated Field 1',
|
||||
'type' => 'text',
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
$this->client->request(
|
||||
'PUT',
|
||||
'/api/forms/'.$form->getId().'/edit',
|
||||
$updateData,
|
||||
);
|
||||
|
||||
$response = $this->client->getResponse();
|
||||
$this->assertResponseIsSuccessful();
|
||||
|
||||
$responseData = json_decode($response->getContent(), true);
|
||||
$this->assertCount(1, $responseData['form']['fields']);
|
||||
$this->assertSame('Updated Field 1', $responseData['form']['fields'][0]['label']);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, array<mixed>>
|
||||
*/
|
||||
public static function formDataProvider(): array
|
||||
{
|
||||
return [
|
||||
'simple form' => [
|
||||
[
|
||||
'name' => 'Simple Test Form',
|
||||
'description' => 'A simple test form',
|
||||
],
|
||||
Response::HTTP_CREATED,
|
||||
],
|
||||
'form with fields' => [
|
||||
[
|
||||
'name' => 'Form with Fields',
|
||||
'fields' => [
|
||||
[
|
||||
'label' => 'First Name',
|
||||
'type' => 'text',
|
||||
'alias' => 'first_name',
|
||||
],
|
||||
[
|
||||
'label' => 'Email Address',
|
||||
'type' => 'email',
|
||||
'alias' => 'email',
|
||||
],
|
||||
],
|
||||
],
|
||||
Response::HTTP_CREATED,
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, array<mixed>>
|
||||
*/
|
||||
public static function updateFormDataProvider(): array
|
||||
{
|
||||
return [
|
||||
'update name only' => [
|
||||
['name' => 'Original Form'],
|
||||
['name' => 'Updated Form Name'],
|
||||
Response::HTTP_OK,
|
||||
],
|
||||
'add fields to existing form' => [
|
||||
['name' => 'Form without fields'],
|
||||
[
|
||||
'name' => 'Form with fields',
|
||||
'fields' => [
|
||||
[
|
||||
'label' => 'New Field',
|
||||
'type' => 'text',
|
||||
],
|
||||
],
|
||||
],
|
||||
Response::HTTP_OK,
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, mixed> $data
|
||||
*/
|
||||
private function createForm(array $data): Form
|
||||
{
|
||||
$form = new Form();
|
||||
$form->setName($data['name']);
|
||||
$form->setDescription($data['description'] ?? '');
|
||||
$form->setAlias($data['alias'] ?? strtolower(str_replace(' ', '_', $data['name'])));
|
||||
|
||||
$this->em->persist($form);
|
||||
$this->em->flush();
|
||||
|
||||
// If fields are provided, create them through the API to properly test the preSaveEntity method
|
||||
if (isset($data['fields'])) {
|
||||
$this->client->request(
|
||||
'PUT',
|
||||
'/api/forms/'.$form->getId().'/edit',
|
||||
[
|
||||
'name' => $form->getName(),
|
||||
'fields' => $data['fields'],
|
||||
]
|
||||
);
|
||||
|
||||
// Refresh the form entity to get the updated data
|
||||
$this->em->refresh($form);
|
||||
}
|
||||
|
||||
return $form;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,206 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Mautic\FormBundle\Tests\Controller;
|
||||
|
||||
use Mautic\CoreBundle\Test\MauticMysqlTestCase;
|
||||
use Mautic\FormBundle\Entity\Field;
|
||||
use Mautic\FormBundle\Entity\Form;
|
||||
use Symfony\Component\DomCrawler\Crawler;
|
||||
|
||||
final class AutoFillReadOnlyFormSubmissionTest extends MauticMysqlTestCase
|
||||
{
|
||||
protected $useCleanupRollback = false;
|
||||
|
||||
/**
|
||||
* @param array<string, bool|null> $data
|
||||
* @param array<string, string> $expected
|
||||
*/
|
||||
#[\PHPUnit\Framework\Attributes\DataProvider('dataForReadOnlyConfigurationSetting')]
|
||||
public function testFieldConfiguration(array $data, array $expected): void
|
||||
{
|
||||
// Create a form
|
||||
$form = $this->createForm();
|
||||
|
||||
$emailField = $this->createFormField($form, 'Email', 'email', $data['isAutoFill'], $data['isReadOnly'], 'email', 'contact');
|
||||
$form->addField(1, $emailField);
|
||||
|
||||
$this->em->flush();
|
||||
$this->em->clear();
|
||||
|
||||
$crawler = $this->client->request('GET', sprintf('/s/forms/edit/%d', $form->getId()));
|
||||
$this->assertResponseIsSuccessful();
|
||||
|
||||
$formElement = $crawler->filterXPath('//form[@name="mauticform"]')->form();
|
||||
$this->client->submit($formElement);
|
||||
$this->assertResponseIsSuccessful();
|
||||
|
||||
$this->client->xmlHttpRequest('GET', sprintf('/s/forms/field/edit/%d?formId=%d', $emailField->getId(), $form->getId()));
|
||||
$this->assertResponseIsSuccessful();
|
||||
|
||||
$response = $this->client->getResponse();
|
||||
$content = json_decode($response->getContent())->newContent;
|
||||
$crawler = new Crawler($content, $this->client->getInternalRequest()->getUri());
|
||||
|
||||
$formValues = $crawler->selectButton('Update')->form()->getPhpValues();
|
||||
|
||||
$this->assertSame($expected['isAutoFill'], $formValues['formfield']['isAutoFill']);
|
||||
$this->assertSame($expected['isReadOnly'], $formValues['formfield']['isReadOnly']);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return iterable<string, array<int, array<string, bool|string|null>>>
|
||||
*/
|
||||
public static function dataForReadOnlyConfigurationSetting(): iterable
|
||||
{
|
||||
yield 'When no behaviour configured' => [
|
||||
// given
|
||||
[
|
||||
'isAutoFill' => null,
|
||||
'isReadOnly' => null,
|
||||
],
|
||||
// expected
|
||||
[
|
||||
'isAutoFill' => '0',
|
||||
'isReadOnly' => '0',
|
||||
],
|
||||
];
|
||||
|
||||
yield 'When field set to read only' => [
|
||||
// given
|
||||
[
|
||||
'isAutoFill' => true,
|
||||
'isReadOnly' => true,
|
||||
],
|
||||
// expected
|
||||
[
|
||||
'isAutoFill' => '1',
|
||||
'isReadOnly' => '1',
|
||||
],
|
||||
];
|
||||
|
||||
yield 'When field set to read only and not autofill' => [
|
||||
// given
|
||||
[
|
||||
'isAutoFill' => false,
|
||||
'isReadOnly' => true,
|
||||
],
|
||||
// expected
|
||||
[
|
||||
'isAutoFill' => '0',
|
||||
'isReadOnly' => '1',
|
||||
],
|
||||
];
|
||||
|
||||
yield 'When field set to autofill and not read only' => [
|
||||
// given
|
||||
[
|
||||
'isAutoFill' => true,
|
||||
'isReadOnly' => false,
|
||||
],
|
||||
// expected
|
||||
[
|
||||
'isAutoFill' => '1',
|
||||
'isReadOnly' => '0',
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
public function testAutoFilledFormForReadOnlyAttribute(): void
|
||||
{
|
||||
$form = $this->createFormWithFields();
|
||||
$formId = $form->getId();
|
||||
|
||||
// Initial request
|
||||
$crawler = $this->client->request('GET', '/form/'.$formId);
|
||||
$this->assertResponseIsSuccessful();
|
||||
$this->assertInputCounts($crawler, 0);
|
||||
|
||||
$formValues = ['john@doe.com', 'John', 'Doe'];
|
||||
|
||||
// Submit the form
|
||||
$formCrawler = $crawler->filter('form[id=mauticform_test]');
|
||||
$form = $formCrawler->form([
|
||||
'mauticform[email]' => $formValues[0],
|
||||
'mauticform[firstname]' => $formValues[1],
|
||||
'mauticform[lastname]' => $formValues[2],
|
||||
]);
|
||||
$this->client->submit($form);
|
||||
|
||||
// Request the form again
|
||||
$crawler = $this->client->request('GET', '/form/'.$formId);
|
||||
$this->assertResponseIsSuccessful();
|
||||
$this->assertInputCounts($crawler, 2);
|
||||
|
||||
$readOnlyInput = $crawler->filterXPath('//input[@readonly]');
|
||||
$readOnlyInput->each(function (Crawler $node, $i) use ($formValues) {
|
||||
$this->assertStringContainsString('readonly', $node->outerHtml());
|
||||
$this->assertSame($formValues[$i], $node->attr('value'));
|
||||
});
|
||||
}
|
||||
|
||||
private function assertInputCounts(Crawler $crawler, int $readonly): void
|
||||
{
|
||||
$this->assertCount(3, $crawler->filterXPath('//input[not(@type="hidden")]'));
|
||||
$this->assertCount(6, $crawler->filterXPath('//input'));
|
||||
$this->assertCount($readonly, $crawler->filterXPath('//input[@readonly]'));
|
||||
}
|
||||
|
||||
private function createFormWithFields(): Form
|
||||
{
|
||||
$form = $this->createForm();
|
||||
|
||||
$emailField = $this->createFormField($form, 'Email', 'email', true, true, 'email', 'contact');
|
||||
$form->addField(1, $emailField);
|
||||
|
||||
$firstNameField = $this->createFormField($form, 'First name', 'text', true, true, 'firstname', 'contact');
|
||||
$form->addField(2, $firstNameField);
|
||||
|
||||
$lastNameField = $this->createFormField($form, 'Last name', 'text', false, true, 'lastname', 'contact');
|
||||
$form->addField(3, $lastNameField);
|
||||
|
||||
$submitButton = $this->createFormField($form, 'Submit', 'button');
|
||||
$form->addField(4, $submitButton);
|
||||
|
||||
$this->em->flush();
|
||||
$this->em->clear();
|
||||
|
||||
return $form;
|
||||
}
|
||||
|
||||
private function createForm(): Form
|
||||
{
|
||||
$form = new Form();
|
||||
$form->setName('Test');
|
||||
$form->setAlias('test');
|
||||
$form->setPostActionProperty('Success');
|
||||
$this->em->persist($form);
|
||||
|
||||
return $form;
|
||||
}
|
||||
|
||||
private function createFormField(
|
||||
Form $form,
|
||||
string $label,
|
||||
string $type,
|
||||
?bool $isAutoFill = false,
|
||||
?bool $isReadOnly = false,
|
||||
?string $mappedField = null,
|
||||
?string $mappedObject = null,
|
||||
): Field {
|
||||
$field = new Field();
|
||||
$field->setLabel($label);
|
||||
$field->setType($type);
|
||||
$field->setForm($form);
|
||||
$field->setAlias(strtolower(str_replace(' ', '', $label)));
|
||||
$field->setIsAutoFill($isAutoFill);
|
||||
$field->setIsReadOnly($isReadOnly);
|
||||
$field->setMappedObject($mappedObject);
|
||||
$field->setMappedField($mappedField);
|
||||
|
||||
$this->em->persist($field);
|
||||
|
||||
return $field;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,278 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Mautic\FormBundle\Tests\Controller;
|
||||
|
||||
use Mautic\CoreBundle\Test\MauticMysqlTestCase;
|
||||
use Mautic\FormBundle\Entity\Form;
|
||||
use PHPUnit\Framework\Assert;
|
||||
use Symfony\Component\DomCrawler\Crawler;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
final class FieldControllerFunctionalTest extends MauticMysqlTestCase
|
||||
{
|
||||
protected $useCleanupRollback = false;
|
||||
|
||||
public function testNewEmailFieldFormIsPreMapped(): void
|
||||
{
|
||||
$this->client->xmlHttpRequest(
|
||||
Request::METHOD_GET,
|
||||
'/s/forms/field/new?type=email&tmpl=field&formId=temporary_form_hash&inBuilder=1'
|
||||
);
|
||||
$clientResponse = $this->client->getResponse();
|
||||
$payload = json_decode($clientResponse->getContent(), true);
|
||||
self::assertResponseIsSuccessful();
|
||||
Assert::assertStringContainsString('<option value="email" selected="selected">', $payload['newContent']);
|
||||
}
|
||||
|
||||
public function testNewCaptchaFieldFormCanBeSaved(): void
|
||||
{
|
||||
$payload = [
|
||||
'name' => 'Submission test form',
|
||||
'description' => 'Form created via captcha test',
|
||||
'formType' => 'standalone',
|
||||
'isPublished' => true,
|
||||
'fields' => [
|
||||
[
|
||||
'label' => 'Email',
|
||||
'type' => 'email',
|
||||
'alias' => 'email',
|
||||
'leadField' => 'email',
|
||||
],
|
||||
[
|
||||
'label' => 'Submit',
|
||||
'type' => 'button',
|
||||
],
|
||||
],
|
||||
'postAction' => 'return',
|
||||
];
|
||||
|
||||
$this->client->request(Request::METHOD_POST, '/api/forms/new', $payload);
|
||||
$clientResponse = $this->client->getResponse();
|
||||
$response = json_decode($clientResponse->getContent(), true);
|
||||
$formId = $response['form']['id'];
|
||||
|
||||
$this->assertResponseStatusCodeSame(Response::HTTP_CREATED, $clientResponse->getContent());
|
||||
|
||||
$crawler = $this->client->xmlHttpRequest(Request::METHOD_GET, "/s/forms/field/new?type=captcha&tmpl=field&formId={$formId}&inBuilder=1");
|
||||
$this->assertResponseIsSuccessful();
|
||||
$content = $this->client->getResponse()->getContent();
|
||||
$content = json_decode($content)->newContent;
|
||||
$crawler = new Crawler($content, $this->client->getInternalRequest()->getUri());
|
||||
$formCrawler = $crawler->filter('form[name=formfield]');
|
||||
Assert::assertCount(1, $formCrawler, $this->client->getResponse()->getContent());
|
||||
$form = $formCrawler->form();
|
||||
$form->setValues(
|
||||
[
|
||||
'formfield[formId]' => $formId,
|
||||
'formfield[type]' => 'captcha',
|
||||
'formfield[label]' => 'What is the capital of Czech Republic?',
|
||||
'formfield[properties][captcha]' => 'Prague',
|
||||
]
|
||||
);
|
||||
$this->setCsrfHeader();
|
||||
$this->client->xmlHttpRequest($form->getMethod(), $form->getUri(), $form->getPhpValues(), $form->getPhpFiles());
|
||||
$this->assertResponseIsSuccessful();
|
||||
|
||||
$response = json_decode($this->client->getResponse()->getContent(), true);
|
||||
|
||||
Assert::assertSame(1, $response['success'] ?? null, $this->client->getResponse()->getContent());
|
||||
Assert::assertSame(1, $response['closeModal'] ?? null, $this->client->getResponse()->getContent());
|
||||
}
|
||||
|
||||
public function testNewCompanyLookupFieldForm(): void
|
||||
{
|
||||
$form = new Form();
|
||||
$form->setName('Test Form')
|
||||
->setIsPublished(true)
|
||||
->setAlias('testform');
|
||||
$this->em->persist($form);
|
||||
$this->em->flush();
|
||||
|
||||
$this->client->xmlHttpRequest(
|
||||
Request::METHOD_GET,
|
||||
'/s/forms/field/new?type=companyLookup&tmpl=field&formId='.$form->getId().'&inBuilder=1'
|
||||
);
|
||||
|
||||
Assert::assertTrue($this->client->getResponse()->isOk());
|
||||
$content = $this->client->getResponse()->getContent();
|
||||
$content = json_decode($content)->newContent;
|
||||
$crawler = new Crawler($content, $this->client->getInternalRequest()->getUri());
|
||||
|
||||
$this->assertSame('Contact', $crawler->filter('select[id="formfield_mappedObject"]')->filter('option[selected]')->text());
|
||||
$this->assertSame('Primary company', $crawler->filter('select[id="formfield_mappedField"]')->filter('option[selected]')->text());
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, mixed>|null $additionalValues
|
||||
*/
|
||||
#[\PHPUnit\Framework\Attributes\DataProvider('provideFieldTypesData')]
|
||||
public function testFieldWithLinkInLabel(
|
||||
string $fieldType,
|
||||
string $label,
|
||||
string $expectedHtmlFragment,
|
||||
string $helpMessage = '',
|
||||
?array $additionalValues = null,
|
||||
): void {
|
||||
$this->client->xmlHttpRequest(
|
||||
Request::METHOD_GET,
|
||||
sprintf('/s/forms/field/new?type=%s&tmpl=field&formId=temporary_form_hash&inBuilder=1', $fieldType)
|
||||
);
|
||||
$this->assertResponseIsSuccessful();
|
||||
$content = $this->client->getResponse()->getContent();
|
||||
$content = json_decode($content)->newContent;
|
||||
$crawler = new Crawler($content, $this->client->getInternalRequest()->getUri());
|
||||
$formCrawler = $crawler->filter('form[name=formfield]');
|
||||
Assert::assertCount(1, $formCrawler, $this->client->getResponse()->getContent());
|
||||
$form = $formCrawler->form();
|
||||
$form->setValues(
|
||||
[
|
||||
'formfield[formId]' => 'temporary_form_hash',
|
||||
'formfield[label]' => $label,
|
||||
'formfield[helpMessage]' => $helpMessage,
|
||||
]
|
||||
);
|
||||
|
||||
$values = $form->getPhpValues();
|
||||
if ($additionalValues) {
|
||||
$values = array_merge_recursive($values, $additionalValues);
|
||||
}
|
||||
|
||||
$this->setCsrfHeader();
|
||||
$this->client->xmlHttpRequest($form->getMethod(), $form->getUri(), $values, $form->getPhpFiles());
|
||||
$this->assertResponseIsSuccessful();
|
||||
|
||||
$response = json_decode($this->client->getResponse()->getContent(), true);
|
||||
$this->assertStringContainsString($expectedHtmlFragment, $response['fieldHtml']);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, array{
|
||||
* fieldType: string,
|
||||
* label: string,
|
||||
* expectedHtmlFragment: string,
|
||||
* additionalValues: array<string, mixed>|null
|
||||
* }>
|
||||
*/
|
||||
public static function provideFieldTypesData(): array
|
||||
{
|
||||
return [
|
||||
'email field with link in label' => [
|
||||
'fieldType' => 'email',
|
||||
'label' => 'Email <a href="https://example.com" target="_blank">link</a>',
|
||||
'expectedHtmlFragment' => '<a href="https://example.com" target="_blank" rel="noreferrer noopener">link</a>',
|
||||
'helpMessage' => '',
|
||||
'additionalValues' => null,
|
||||
],
|
||||
'email field with link in helpMessage' => [
|
||||
'fieldType' => 'email',
|
||||
'label' => 'Email',
|
||||
'expectedHtmlFragment' => '<a href="https://example.com" target="_blank" rel="noreferrer noopener">link</a>',
|
||||
'helpMessage' => 'Find more info at <a href="https://example.com" target="_blank">link</a>',
|
||||
'additionalValues' => null,
|
||||
],
|
||||
'checkbox group field with link in label' => [
|
||||
'fieldType' => 'checkboxgrp',
|
||||
'label' => 'Checkbox Group <a href="https://example.com" target="_blank">link</a>',
|
||||
'expectedHtmlFragment' => '<a href="https://example.com" target="_blank" rel="noreferrer noopener">link</a>',
|
||||
'helpMessage' => '',
|
||||
'additionalValues' => [
|
||||
'formfield' => [
|
||||
'properties' => [
|
||||
'optionlist' => [
|
||||
'list' => [
|
||||
[
|
||||
'label' => 'option1',
|
||||
'value' => 'option1',
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
'checkbox group field with link in helpMessage' => [
|
||||
'fieldType' => 'checkboxgrp',
|
||||
'label' => 'Checkbox Group',
|
||||
'expectedHtmlFragment' => '<a href="https://example.com" target="_blank" rel="noreferrer noopener">link</a>',
|
||||
'helpMessage' => 'Find <a href="https://example.com" target="_blank">link</a>',
|
||||
'additionalValues' => [
|
||||
'formfield' => [
|
||||
'properties' => [
|
||||
'optionlist' => [
|
||||
'list' => [
|
||||
[
|
||||
'label' => 'option1',
|
||||
'value' => 'option1',
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
'checkbox group field with link in option label' => [
|
||||
'fieldType' => 'checkboxgrp',
|
||||
'label' => 'Checkbox Group',
|
||||
'expectedHtmlFragment' => '<a href="https://example.com" target="_blank" rel="noreferrer noopener">terms and conditions</a>',
|
||||
'helpMessage' => '',
|
||||
'additionalValues' => [
|
||||
'formfield' => [
|
||||
'properties' => [
|
||||
'optionlist' => [
|
||||
'list' => [
|
||||
[
|
||||
'label' => 'I agree with the <a href="https://example.com" target="_blank">terms and conditions</a>.',
|
||||
'value' => '1',
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
'select field with link in label' => [
|
||||
'fieldType' => 'select',
|
||||
'label' => 'Select',
|
||||
'expectedHtmlFragment' => '<a href="https://example.com" target="_blank" rel="noreferrer noopener">link</a>',
|
||||
'helpMessage' => 'Get <a href="https://example.com" target="_blank" rel="noreferrer noopener">link</a>',
|
||||
'additionalValues' => [
|
||||
'formfield' => [
|
||||
'properties' => [
|
||||
'list' => [
|
||||
'list' => [
|
||||
[
|
||||
'label' => 'abc',
|
||||
'value' => 'abc',
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
'select field with link in helpMessage' => [
|
||||
'fieldType' => 'select',
|
||||
'label' => 'Select <a href="https://example.com" target="_blank">link</a>',
|
||||
'expectedHtmlFragment' => '<a href="https://example.com" target="_blank" rel="noreferrer noopener">link</a>',
|
||||
'helpMessage' => '',
|
||||
'additionalValues' => [
|
||||
'formfield' => [
|
||||
'properties' => [
|
||||
'list' => [
|
||||
'list' => [
|
||||
[
|
||||
'label' => 'abc',
|
||||
'value' => 'abc',
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,786 @@
|
||||
<?php
|
||||
|
||||
namespace Mautic\FormBundle\Tests\Controller;
|
||||
|
||||
use Mautic\AssetBundle\Entity\Asset;
|
||||
use Mautic\CategoryBundle\Entity\Category;
|
||||
use Mautic\CoreBundle\Helper\LanguageHelper;
|
||||
use Mautic\CoreBundle\Test\MauticMysqlTestCase;
|
||||
use Mautic\EmailBundle\Entity\Email;
|
||||
use Mautic\FormBundle\Entity\Action;
|
||||
use Mautic\FormBundle\Entity\Field;
|
||||
use Mautic\FormBundle\Entity\Form;
|
||||
use Mautic\LeadBundle\Entity\LeadField;
|
||||
use Mautic\LeadBundle\Entity\LeadList;
|
||||
use Mautic\ProjectBundle\Entity\Project;
|
||||
use PHPUnit\Framework\Assert;
|
||||
use Symfony\Component\DomCrawler\Crawler;
|
||||
use Symfony\Component\Filesystem\Filesystem;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Contracts\Translation\TranslatorInterface;
|
||||
|
||||
class FormControllerFunctionalTest extends MauticMysqlTestCase
|
||||
{
|
||||
protected $useCleanupRollback = false;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
if ('testLabelsForFormAction' === $this->name()) {
|
||||
$this->truncateTables('assets', 'categories', 'emails', 'lead_lists');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Index should return status code 200.
|
||||
*/
|
||||
public function testIndexActionWhenNotFiltered(): void
|
||||
{
|
||||
$this->client->request('GET', '/s/forms');
|
||||
$this->assertTrue($this->client->getResponse()->isOk());
|
||||
}
|
||||
|
||||
/**
|
||||
* Filtering should return status code 200.
|
||||
*/
|
||||
public function testIndexActionWhenFiltering(): void
|
||||
{
|
||||
$this->client->request('GET', '/s/forms?search=has%3Aresults&tmpl=list');
|
||||
$this->assertTrue($this->client->getResponse()->isOk());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get form's create page.
|
||||
*/
|
||||
public function testNewActionForm(): void
|
||||
{
|
||||
$this->client->request('GET', '/s/forms/new/');
|
||||
$this->assertTrue($this->client->getResponse()->isOk());
|
||||
}
|
||||
|
||||
/**
|
||||
* @see https://github.com/mautic/mautic/issues/10453
|
||||
*/
|
||||
public function testSaveActionForm(): void
|
||||
{
|
||||
$crawler = $this->client->request('GET', '/s/forms/new/');
|
||||
$this->assertTrue($this->client->getResponse()->isOk());
|
||||
|
||||
$form = $crawler->filterXPath('//form[@name="mauticform"]')->form();
|
||||
$form->setValues(
|
||||
[
|
||||
'mauticform[name]' => 'Test',
|
||||
'mauticform[renderStyle]' => '0',
|
||||
]
|
||||
);
|
||||
$crawler = $this->client->submit($form);
|
||||
$this->assertTrue($this->client->getResponse()->isOk());
|
||||
|
||||
$form = $crawler->filterXPath('//form[@name="mauticform"]')->form();
|
||||
$form->setValues(
|
||||
[
|
||||
'mauticform[renderStyle]' => '0',
|
||||
]
|
||||
);
|
||||
|
||||
// The form failed to save when saved for the second time with renderStyle=No.
|
||||
$this->client->submit($form);
|
||||
$this->assertTrue($this->client->getResponse()->isOk(), $this->client->getResponse()->getContent());
|
||||
$this->assertStringNotContainsString('Internal Server Error - Expected argument of type "null or string", "boolean" given', $this->client->getResponse()->getContent());
|
||||
}
|
||||
|
||||
public function testNewActionCheckDisplayMessageOptionsForm(): void
|
||||
{
|
||||
$this->client->request('GET', '/s/forms/new');
|
||||
$this->assertTrue($this->client->getResponse()->isOk());
|
||||
$clientResponse = $this->client->getResponse();
|
||||
self::assertResponseStatusCodeSame(Response::HTTP_OK, $clientResponse->getContent());
|
||||
$this->assertStringContainsString('Hide form', $clientResponse->getContent(), $clientResponse->getContent());
|
||||
$this->assertStringContainsString('Redirect URL', $clientResponse->getContent(), $clientResponse->getContent());
|
||||
$this->assertStringContainsString('Remain at form', $clientResponse->getContent(), $clientResponse->getContent());
|
||||
}
|
||||
|
||||
public function testErrorValidationWithHideFormTypeWithoutMessage(): void
|
||||
{
|
||||
$crawler = $this->client->request('GET', '/s/forms/new/');
|
||||
$this->assertTrue($this->client->getResponse()->isOk());
|
||||
|
||||
$selectedValue = $crawler->filter('#mauticform_postAction option:selected')->attr('value');
|
||||
|
||||
$this->assertEquals('message', $selectedValue);
|
||||
|
||||
$form = $crawler->filterXPath('//form[@name="mauticform"]')->form();
|
||||
|
||||
$form->setValues(
|
||||
[
|
||||
'mauticform[name]' => 'Test',
|
||||
'mauticform[postAction]' => 'hideform',
|
||||
]
|
||||
);
|
||||
|
||||
$crawler = $this->client->submit($form);
|
||||
$this->assertTrue($this->client->getResponse()->isOk());
|
||||
$divClass = $crawler->filter('#mauticform_postActionProperty')->ancestors()->first()->attr('class');
|
||||
$this->assertStringContainsString('has-error', $divClass, $crawler->html());
|
||||
}
|
||||
|
||||
public function testSuccessWithHideForm(): void
|
||||
{
|
||||
$crawler = $this->client->request('GET', '/s/forms/new/');
|
||||
$this->assertTrue($this->client->getResponse()->isOk());
|
||||
|
||||
$selectedValue = $crawler->filter('#mauticform_postAction option:selected')->attr('value');
|
||||
|
||||
$this->assertEquals('message', $selectedValue);
|
||||
|
||||
$form = $crawler->filterXPath('//form[@name="mauticform"]')->form();
|
||||
|
||||
$form->setValues(
|
||||
[
|
||||
'mauticform[name]' => 'Test',
|
||||
'mauticform[postAction]' => 'hideform',
|
||||
'mauticform[postActionProperty]' => 'message',
|
||||
]
|
||||
);
|
||||
$crawler = $this->client->submit($form);
|
||||
$this->assertTrue($this->client->getResponse()->isOk(), $this->client->getResponse()->getContent());
|
||||
$divClass = $crawler->filter('#mauticform_postActionProperty')->ancestors()->first()->attr('class');
|
||||
$this->assertStringNotContainsString('has-error', $divClass, $crawler->html());
|
||||
}
|
||||
|
||||
public function testLanguageForm(): void
|
||||
{
|
||||
$translationsPath = __DIR__.'/resource/language/fr';
|
||||
$languagePath = __DIR__.'/../../../../../translations/fr';
|
||||
$filesystem = new Filesystem();
|
||||
|
||||
// copy all from $translationsPath to $languagePath
|
||||
$filesystem->mirror($translationsPath, $languagePath);
|
||||
|
||||
/** @var LanguageHelper $languageHelper */
|
||||
$languageHelper = $this->getContainer()->get('mautic.helper.language');
|
||||
|
||||
$formPayload = [
|
||||
'name' => 'Test Form',
|
||||
'formType' => 'campaign',
|
||||
'language' => 'fr',
|
||||
'postAction' => 'return',
|
||||
'fields' => [
|
||||
[
|
||||
'label' => 'Email',
|
||||
'alias' => 'email',
|
||||
'type' => 'email',
|
||||
'leadField' => 'email',
|
||||
'isRequired' => true,
|
||||
], [
|
||||
'label' => 'Submit',
|
||||
'alias' => 'submit',
|
||||
'type' => 'button',
|
||||
],
|
||||
],
|
||||
];
|
||||
$this->client->request('POST', '/api/forms/new', $formPayload);
|
||||
$clientResponse = $this->client->getResponse();
|
||||
$response = json_decode($clientResponse->getContent(), true);
|
||||
$this->assertSame(Response::HTTP_CREATED, $clientResponse->getStatusCode(), json_encode($languageHelper->getLanguageChoices()));
|
||||
$form = $response['form'];
|
||||
$formId = $form['id'];
|
||||
|
||||
$crawler = $this->client->request('GET', '/form/'.$form['id']);
|
||||
$this->assertStringContainsString('Merci de patienter...', $crawler->html());
|
||||
$this->assertStringContainsString('Ceci est requis.', $crawler->html());
|
||||
|
||||
$filesystem->remove($languagePath);
|
||||
}
|
||||
|
||||
public function testMappedFieldIsNotMarkedAsRemappedUponSavingTheForm(): void
|
||||
{
|
||||
$form = $this->createForm('Test', 'test');
|
||||
$field = $this->createFormField([
|
||||
'label' => 'Email',
|
||||
'type' => 'email',
|
||||
])->setForm($form);
|
||||
|
||||
// @phpstan-ignore-next-line (using the deprecated method on purpose)
|
||||
$field->setLeadField('email');
|
||||
$this->em->persist($field);
|
||||
$this->em->flush();
|
||||
$this->em->clear();
|
||||
|
||||
$crawler = $this->client->request('GET', sprintf('/s/forms/edit/%d', $form->getId()));
|
||||
$this->assertTrue($this->client->getResponse()->isOk());
|
||||
|
||||
$formElement = $crawler->filterXPath('//form[@name="mauticform"]')->form();
|
||||
$this->client->submit($formElement);
|
||||
$response = $this->client->getResponse();
|
||||
$this->assertTrue($response->isOk());
|
||||
$this->assertStringNotContainsString('contact: Email', $response->getContent(), 'Email field should not be marked as mapped.');
|
||||
}
|
||||
|
||||
public function testMappedFieldIsNotAutoFilledWhenUpdatingField(): void
|
||||
{
|
||||
$form = $this->createForm('Test', 'test');
|
||||
$field = $this->createFormField([
|
||||
'label' => 'Email',
|
||||
'type' => 'email',
|
||||
])->setForm($form);
|
||||
$field->setMappedObject(null);
|
||||
$field->setMappedField(null);
|
||||
$this->em->persist($field);
|
||||
$this->em->flush();
|
||||
$this->em->clear();
|
||||
|
||||
$crawler = $this->client->request('GET', sprintf('/s/forms/edit/%d', $form->getId()));
|
||||
$this->assertTrue($this->client->getResponse()->isOk(), $this->client->getResponse()->getContent());
|
||||
|
||||
$formElement = $crawler->filterXPath('//form[@name="mauticform"]')->form();
|
||||
$this->client->submit($formElement);
|
||||
$this->assertTrue($this->client->getResponse()->isOk());
|
||||
|
||||
$this->client->xmlHttpRequest('GET', sprintf('/s/forms/field/edit/%d?formId=%d', $field->getId(), $form->getId()));
|
||||
$response = $this->client->getResponse();
|
||||
$this->assertTrue($response->isOk());
|
||||
$this->assertJson($response->getContent());
|
||||
|
||||
$content = json_decode($response->getContent())->newContent;
|
||||
$crawler = new Crawler($content, $this->client->getInternalRequest()->getUri());
|
||||
$options = $crawler->filterXPath('//select[@name="formfield[mappedField]"]')->html();
|
||||
$this->assertStringContainsString('<option value="email">Email</option>', $options, 'Email option should not be pre-selected.');
|
||||
}
|
||||
|
||||
public function testMappedFieldCheckboxGroup(): void
|
||||
{
|
||||
// Create custom boolean field.
|
||||
$customField = new LeadField();
|
||||
$customField->setObject('lead');
|
||||
$customField->setType('boolean');
|
||||
$customField->setLabel('Custom Bool Field');
|
||||
$customField->setAlias('custom_boolean_field');
|
||||
$customField->setProperties([
|
||||
'yes' => 'Absolutely yes',
|
||||
'no' => 'Obviously No',
|
||||
]);
|
||||
|
||||
// Create & add checkbox group type field to form.
|
||||
$form = $this->createForm('Test form', 'test_form');
|
||||
$field = $this->createFormField([
|
||||
'label' => 'Test Checkbox Group',
|
||||
'type' => 'checkboxgrp',
|
||||
]);
|
||||
$field->setMappedObject('contact');
|
||||
$field->setMappedField('custom_boolean_field');
|
||||
$fieldProperties = [
|
||||
'list' => [
|
||||
'option1' => 'First Option',
|
||||
'option2' => 'Second Option',
|
||||
],
|
||||
];
|
||||
$field->setProperties($fieldProperties);
|
||||
$field->setForm($form);
|
||||
$this->em->persist($field);
|
||||
$this->em->flush();
|
||||
$this->em->clear();
|
||||
|
||||
// Verify form creation
|
||||
$crawler = $this->client->request('GET', sprintf('/s/forms/edit/%d', $form->getId()));
|
||||
$this->assertResponseIsSuccessful();
|
||||
|
||||
// Visit the form preview page
|
||||
$crawler = $this->client->request('GET', sprintf('/s/forms/preview/%d', $form->getId()));
|
||||
$this->assertResponseIsSuccessful();
|
||||
$this->assertStringContainsString('First Option', $this->client->getResponse()->getContent());
|
||||
$this->assertStringContainsString('Second Option', $this->client->getResponse()->getContent());
|
||||
}
|
||||
|
||||
public function testCreateNewActionUsingBaseTemplateToDisplay(): void
|
||||
{
|
||||
// Create new form
|
||||
$form = $this->createForm('Test', 'test');
|
||||
$this->em->persist($form);
|
||||
|
||||
// Fetch the form
|
||||
$this->client->xmlHttpRequest(Request::METHOD_GET, '/s/forms/action/new',
|
||||
[
|
||||
'formId' => $form->getId(),
|
||||
'type' => 'lead.addutmtags',
|
||||
]
|
||||
);
|
||||
$this->assertResponseIsSuccessful();
|
||||
$content = $this->client->getResponse()->getContent();
|
||||
$content = json_decode($content)->newContent;
|
||||
$crawler = new Crawler($content, $this->client->getInternalRequest()->getUri());
|
||||
$formCrawler = $crawler->filter('form');
|
||||
$this->assertCount(1, $formCrawler);
|
||||
$form = $formCrawler->form();
|
||||
|
||||
// Save new Send Form Results action
|
||||
$this->client->submit($form);
|
||||
$this->assertResponseIsSuccessful();
|
||||
$content = $this->client->getResponse()->getContent();
|
||||
$actionHtml = json_decode($content, true)['actionHtml'] ?? null;
|
||||
$this->assertNotNull($actionHtml, $content);
|
||||
$crawler = new Crawler($actionHtml);
|
||||
$editPage = $crawler->filter('.btn-edit')->attr('href');
|
||||
|
||||
// Check the content was not changed
|
||||
$this->client->xmlHttpRequest(Request::METHOD_GET, $editPage);
|
||||
$this->assertResponseIsSuccessful();
|
||||
}
|
||||
|
||||
public function testEditNewActionUsingBaseTemplateToDisplay(): void
|
||||
{
|
||||
// Create new form
|
||||
$form = $this->createForm('Test', 'test');
|
||||
|
||||
// Create action
|
||||
$action = $this->createFormAction($form, 'lead.addutmtags');
|
||||
$form->addAction(0, $action);
|
||||
$this->em->persist($form);
|
||||
|
||||
$this->em->flush();
|
||||
$this->em->clear();
|
||||
|
||||
// Edit and submit the form to be able to push action into session
|
||||
$crawler = $this->client->request('GET', sprintf('/s/forms/edit/%d', $form->getId()));
|
||||
$formElement = $crawler->filterXPath('//form[@name="mauticform"]')->form();
|
||||
$this->client->submit($formElement);
|
||||
$this->assertResponseIsSuccessful();
|
||||
|
||||
// Update the Action
|
||||
$this->setCsrfHeader();
|
||||
$this->client->setServerParameter('HTTP_X-Requested-With', 'XMLHttpRequest');
|
||||
$this->client->xmlHttpRequest(
|
||||
Request::METHOD_POST,
|
||||
sprintf('/s/forms/action/edit/%s?formId=%s', $action->getId(), $form->getId()),
|
||||
['formId' => $form->getId()], // Query parameters (handled in URL)
|
||||
[], // Files
|
||||
['CONTENT_TYPE' => 'application/json'], // server
|
||||
json_encode([
|
||||
'formaction' => [
|
||||
'id' => $action->getId(),
|
||||
'name' => $action->getName(),
|
||||
'type' => 'lead.addutmtags',
|
||||
'order' => $action->getOrder(),
|
||||
'properties' => [],
|
||||
'formId' => $form->getId(),
|
||||
],
|
||||
])
|
||||
);
|
||||
$this->assertResponseIsSuccessful();
|
||||
|
||||
$content = $this->client->getResponse()->getContent();
|
||||
$content = json_decode($content)->newContent;
|
||||
$crawler = new Crawler($content, $this->client->getInternalRequest()->getUri());
|
||||
$formCrawler = $crawler->filter('form');
|
||||
$this->assertCount(1, $formCrawler);
|
||||
$form = $formCrawler->form();
|
||||
$this->client->submit($form);
|
||||
$this->assertResponseIsSuccessful();
|
||||
|
||||
$content = $this->client->getResponse()->getContent();
|
||||
$actionHtml = json_decode($content, true)['actionHtml'] ?? null;
|
||||
$this->assertNotNull($actionHtml, $content);
|
||||
$crawler = new Crawler($actionHtml);
|
||||
$editPage = $crawler->filter('.btn-edit')->attr('href');
|
||||
|
||||
// Check the content was not changed
|
||||
$this->client->xmlHttpRequest(Request::METHOD_GET, $editPage);
|
||||
$this->assertResponseIsSuccessful();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array{
|
||||
* type: string,
|
||||
* properties: array<string, mixed>,
|
||||
* entities?: array<object>
|
||||
* } $inputValues The input configuration for the form action
|
||||
* @param array<int, array{
|
||||
* message: string,
|
||||
* message_arg: array<string, mixed>
|
||||
* }> $expectedMessages The expected messages with translation arguments
|
||||
*/
|
||||
#[\PHPUnit\Framework\Attributes\DataProvider('dataTestLabelsForFormActions')]
|
||||
public function testLabelsForFormAction(array $inputValues, array $expectedMessages): void
|
||||
{
|
||||
$form = $this->createForm('test', 'test');
|
||||
|
||||
// Persist entities if provided
|
||||
if (!empty($inputValues['entities'])) {
|
||||
foreach ($inputValues['entities'] as $entity) {
|
||||
$this->em->persist($entity);
|
||||
}
|
||||
}
|
||||
|
||||
// create form action
|
||||
$action = $this->createFormAction($form, $inputValues['type'], $inputValues['properties']);
|
||||
$form->addAction(0, $action);
|
||||
$this->em->persist($form);
|
||||
|
||||
$this->em->flush();
|
||||
$this->em->clear();
|
||||
|
||||
$crawler = $this->client->request('GET', sprintf('/s/forms/edit/%d', $form->getId()));
|
||||
$this->assertResponseIsSuccessful();
|
||||
|
||||
$translator = $this->getContainer()->get('translator');
|
||||
\assert($translator instanceof TranslatorInterface);
|
||||
|
||||
foreach ($expectedMessages as $expectedMessage) {
|
||||
$translatedMessage = $translator->trans($expectedMessage['message'], $expectedMessage['message_arg']);
|
||||
$this->assertStringContainsString(strip_tags($translatedMessage), $crawler->html());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return iterable<string, array{
|
||||
* 0: array{
|
||||
* type: string,
|
||||
* properties: array<string, mixed>,
|
||||
* entities?: array<object>
|
||||
* },
|
||||
* 1: array<array{
|
||||
* message: string,
|
||||
* message_arg: array<string, mixed>
|
||||
* }>
|
||||
* }>
|
||||
*/
|
||||
public static function dataTestLabelsForFormActions(): iterable
|
||||
{
|
||||
$category = new Category();
|
||||
$category->setTitle('Category');
|
||||
$category->setAlias('category');
|
||||
$category->setBundle('global');
|
||||
|
||||
$asset = new Asset();
|
||||
$asset->setTitle('test');
|
||||
$asset->setAlias('test');
|
||||
$asset->setCategory($category);
|
||||
|
||||
yield 'Action: Download asset using category' => [
|
||||
// input
|
||||
[
|
||||
'type' => 'asset.download',
|
||||
'properties' => [
|
||||
'asset' => null,
|
||||
'category' => 1,
|
||||
],
|
||||
'entities' => [
|
||||
$category,
|
||||
$asset,
|
||||
],
|
||||
],
|
||||
// expected
|
||||
[
|
||||
[
|
||||
'message' => 'mautic.form.field.asset.use_category',
|
||||
'message_arg' => [
|
||||
'%category_name%' => $category->getTitle(),
|
||||
],
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
yield 'Action: Add to company points' => [
|
||||
// input
|
||||
[
|
||||
'type' => 'lead.scorecontactscompanies',
|
||||
'properties' => ['score' => 10],
|
||||
],
|
||||
// expected
|
||||
[
|
||||
[
|
||||
'message' => 'mautic.form.form.change_points_by',
|
||||
'message_arg' => ['%value%' => 10],
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
yield 'Action: Add to contact points' => [
|
||||
// input
|
||||
[
|
||||
'type' => 'lead.pointschange',
|
||||
'properties' => [
|
||||
'operator' => 'plus',
|
||||
'points' => 10,
|
||||
'group' => 0,
|
||||
],
|
||||
],
|
||||
// expected
|
||||
[
|
||||
[
|
||||
'message' => 'mautic.form.field.points.operation',
|
||||
'message_arg' => [
|
||||
'%operator%' => '(+)',
|
||||
'%points%' => 10,
|
||||
'%group%' => '',
|
||||
],
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
yield 'Action: Email to send to user' => [
|
||||
// input
|
||||
[
|
||||
'type' => 'email.send.user',
|
||||
'properties' => [
|
||||
'useremail' => ['email' => 1],
|
||||
'user_id' => [1],
|
||||
],
|
||||
'entities' => [
|
||||
(new Email())->setName('Email')
|
||||
->setSubject('Test Subject')
|
||||
->setIsPublished(true),
|
||||
],
|
||||
],
|
||||
// expected
|
||||
[
|
||||
[
|
||||
'message' => 'Email',
|
||||
'message_arg' => [],
|
||||
],
|
||||
[
|
||||
'message' => 'Email',
|
||||
'message_arg' => [],
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
$segmentOne = new LeadList();
|
||||
$segmentOne->setName('list one');
|
||||
$segmentOne->setAlias('list_one');
|
||||
$segmentOne->setPublicName('list_one');
|
||||
$segmentOne->setFilters([]);
|
||||
|
||||
$segmentTwo = new LeadList();
|
||||
$segmentTwo->setName('list two');
|
||||
$segmentTwo->setAlias('list_two');
|
||||
$segmentTwo->setPublicName('list_two');
|
||||
$segmentTwo->setFilters([]);
|
||||
|
||||
yield 'Action: Change segments' => [
|
||||
// input
|
||||
[
|
||||
'type' => 'lead.changelist',
|
||||
'properties' => [
|
||||
'addToLists' => [1],
|
||||
'removeFromLists' => [2],
|
||||
],
|
||||
'entities' => [
|
||||
$segmentOne,
|
||||
$segmentTwo,
|
||||
],
|
||||
],
|
||||
// expected
|
||||
[
|
||||
[
|
||||
'message' => $segmentOne->getName(),
|
||||
'message_arg' => [],
|
||||
],
|
||||
[
|
||||
'message' => $segmentTwo->getName(),
|
||||
'message_arg' => [],
|
||||
],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, int|string|array<mixed>> $properties
|
||||
*/
|
||||
private function createFormAction(Form $form, string $type, array $properties = []): Action
|
||||
{
|
||||
$action = new Action();
|
||||
|
||||
$action->setName($type);
|
||||
$action->setType($type);
|
||||
$action->setForm($form);
|
||||
$action->setProperties($properties);
|
||||
|
||||
$this->em->persist($action);
|
||||
|
||||
return $action;
|
||||
}
|
||||
|
||||
public function testCloneActionWithCondition(): void
|
||||
{
|
||||
$form = $this->createForm('Conditional Form', 'Conditional Form');
|
||||
$this->em->flush();
|
||||
|
||||
$field1 = $this->createFormField([
|
||||
'label' => 'Country',
|
||||
'type' => 'country',
|
||||
'mappedObject' => 'contact',
|
||||
'mappedField' => 'country',
|
||||
])->setForm($form);
|
||||
$this->em->persist($field1);
|
||||
|
||||
$field2 = $this->createFormField([
|
||||
'label' => 'State',
|
||||
'mappedObject' => 'contact',
|
||||
'mappedField' => 'state',
|
||||
'conditions' => [
|
||||
'any' => 0,
|
||||
'expr' => 'in',
|
||||
'values' => ['United States'],
|
||||
],
|
||||
'parent' => $field1->getId(),
|
||||
])->setForm($form);
|
||||
|
||||
$fieldSubmit = $this->createFormField([
|
||||
'label' => 'Submit',
|
||||
'type' => 'button',
|
||||
])->setForm($form);
|
||||
|
||||
$this->em->persist($field2);
|
||||
$this->em->flush();
|
||||
|
||||
$form->addField($field1->getId(), $field1);
|
||||
$form->addField($field2->getId(), $field2);
|
||||
$form->addField($fieldSubmit->getId(), $fieldSubmit);
|
||||
|
||||
$field2->setParent((string) $field1->getId());
|
||||
|
||||
$this->em->persist($form);
|
||||
$this->em->flush();
|
||||
|
||||
// request for form clone
|
||||
$crawler = $this->client->request(Request::METHOD_GET, "/s/forms/clone/{$form->getId()}");
|
||||
$mauticform = $crawler->filterXPath('//form[@name="mauticform"]')->form();
|
||||
$mauticform['mauticform[name]']->setValue('Clone Conditional Form');
|
||||
$mauticform['mauticform[isPublished]']->setValue('1');
|
||||
|
||||
$this->client->submit($mauticform);
|
||||
|
||||
$this->assertTrue($this->client->getResponse()->isOk());
|
||||
|
||||
$forms = $this->em->getRepository(Form::class)->findBy([], ['id' => 'ASC']);
|
||||
Assert::assertCount(2, $forms);
|
||||
|
||||
$originalForm = $forms[0];
|
||||
$clonedForm = $forms[1];
|
||||
Assert::assertSame($form->getId(), $originalForm->getId());
|
||||
Assert::assertNotSame($form->getId(), $clonedForm->getId());
|
||||
|
||||
$fields = $clonedForm->getFields()->getValues();
|
||||
Assert::assertCount(3, $fields);
|
||||
|
||||
list($clonedField1, $clonedField2, $clonedSubmit) = $fields;
|
||||
Assert::assertSame((int) $clonedField2->getParent(), $clonedField1->getId());
|
||||
}
|
||||
|
||||
public function testFormWithProject(): void
|
||||
{
|
||||
$form = $this->createForm('Name', 'Alias');
|
||||
|
||||
$project = new Project();
|
||||
$project->setName('Test Project');
|
||||
$this->em->persist($project);
|
||||
|
||||
$this->em->flush();
|
||||
$this->em->clear();
|
||||
|
||||
$crawler = $this->client->request('GET', '/s/forms/edit/'.$form->getId());
|
||||
$formCrawler = $crawler->selectButton('Save')->form();
|
||||
$formCrawler['mauticform[projects]']->setValue((string) $project->getId());
|
||||
|
||||
$this->client->submit($formCrawler);
|
||||
|
||||
$this->assertResponseIsSuccessful();
|
||||
|
||||
$savedForm = $this->em->find(Form::class, $form->getId());
|
||||
Assert::assertSame($project->getId(), $savedForm->getProjects()->first()->getId());
|
||||
}
|
||||
|
||||
public function testFormDetailsViewWithPreviewPanel(): void
|
||||
{
|
||||
// Create a form
|
||||
$form = $this->createForm('Test Form Details', 'test_form_details');
|
||||
$this->em->persist($form);
|
||||
$this->em->flush();
|
||||
|
||||
// Request the form details view
|
||||
$crawler = $this->client->request('GET', sprintf('/s/forms/view/%d', $form->getId()));
|
||||
$this->assertResponseIsSuccessful();
|
||||
|
||||
// Check if preview panel exists
|
||||
$previewPanel = $crawler->filter('div.panel.shd-none.bdr-rds-0.bdr-w-0.mt-sm.mb-0');
|
||||
|
||||
if ($previewPanel->count() > 0) {
|
||||
// If preview panel exists, verify its structure
|
||||
$panelHeading = $previewPanel->filter('.panel-heading .panel-title:contains("Preview")');
|
||||
$this->assertCount(1, $panelHeading, 'Preview panel should have correct heading structure');
|
||||
|
||||
$panelBody = $previewPanel->filter('.panel-body.pt-xs');
|
||||
$this->assertCount(1, $panelBody, 'Preview panel should have correct body structure');
|
||||
}
|
||||
}
|
||||
|
||||
public function testSliderFieldRendersWithInputAttributes(): void
|
||||
{
|
||||
// Create a form with a slider field
|
||||
$form = $this->createForm('Test Slider Form', 'test_slider_form');
|
||||
$this->em->persist($form);
|
||||
$this->em->flush();
|
||||
|
||||
// Create a slider field
|
||||
$sliderField = $this->createFormField([
|
||||
'label' => 'Test Slider',
|
||||
'type' => 'slider',
|
||||
]);
|
||||
$sliderField->setProperties([
|
||||
'min' => 0,
|
||||
'max' => 100,
|
||||
'step' => 5,
|
||||
]);
|
||||
$sliderField->setForm($form);
|
||||
$sliderField->setOrder(1);
|
||||
$this->em->persist($sliderField);
|
||||
|
||||
$this->em->flush();
|
||||
$this->em->clear();
|
||||
|
||||
// Request the form preview instead of view
|
||||
$crawler = $this->client->request('GET', sprintf('/s/forms/preview/%d', $form->getId()));
|
||||
$this->assertResponseIsSuccessful();
|
||||
|
||||
// Check that the slider input has the oninput attribute
|
||||
$sliderInput = $crawler->filter('input[type="range"]');
|
||||
$this->assertCount(1, $sliderInput, 'Slider input should be present');
|
||||
|
||||
$oninputAttr = $sliderInput->attr('oninput');
|
||||
$this->assertNotNull($oninputAttr, 'Slider input should have oninput attribute');
|
||||
$this->assertStringContainsString('document.getElementById', $oninputAttr, 'Slider input should use getElementById to target output element');
|
||||
$this->assertStringContainsString('.textContent = this.value', $oninputAttr, 'Slider input should set output value to input value');
|
||||
}
|
||||
|
||||
private function createForm(string $name, string $alias): Form
|
||||
{
|
||||
$form = new Form();
|
||||
$form->setName($name);
|
||||
$form->setAlias($alias);
|
||||
$form->setPostActionProperty('Success');
|
||||
$this->em->persist($form);
|
||||
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string,mixed> $data
|
||||
*/
|
||||
private function createFormField(array $data = []): Field
|
||||
{
|
||||
$field = new Field();
|
||||
$aliasSlug = strtolower(str_replace(' ', '_', $data['label'] ?? 'Field 1'));
|
||||
$field->setLabel($data['label'] ?? 'Field 1');
|
||||
$field->setAlias('field_'.$aliasSlug);
|
||||
$field->setType($data['type'] ?? 'text');
|
||||
$field->setMappedObject($data['mappedObject'] ?? '');
|
||||
$field->setMappedField($data['mappedField'] ?? '');
|
||||
$field->setConditions($data['conditions'] ?? []);
|
||||
$this->em->persist($field);
|
||||
|
||||
return $field;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Mautic\FormBundle\Tests\Controller;
|
||||
|
||||
use Mautic\FormBundle\Entity\Form;
|
||||
use Mautic\ProjectBundle\Tests\Functional\AbstractProjectSearchTestCase;
|
||||
|
||||
final class FormProjectSearchFunctionalTest 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');
|
||||
|
||||
$formAlpha = $this->createForm('Form Alpha');
|
||||
$formBeta = $this->createForm('Form Beta');
|
||||
$this->createForm('Form Gamma');
|
||||
$this->createForm('Form Delta');
|
||||
|
||||
$formAlpha->addProject($projectOne);
|
||||
$formAlpha->addProject($projectTwo);
|
||||
$formBeta->addProject($projectTwo);
|
||||
$formBeta->addProject($projectThree);
|
||||
|
||||
$this->em->flush();
|
||||
$this->em->clear();
|
||||
|
||||
$this->searchAndAssert($searchTerm, $expectedEntities, $unexpectedEntities, ['/api/forms', '/s/forms']);
|
||||
}
|
||||
|
||||
/**
|
||||
* @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' => ['Form Alpha', 'Form Beta'],
|
||||
'unexpectedEntities' => ['Form Gamma', 'Form Delta'],
|
||||
];
|
||||
|
||||
yield 'search by one project AND form name' => [
|
||||
'searchTerm' => 'project:"Project Two" AND Beta',
|
||||
'expectedEntities' => ['Form Beta'],
|
||||
'unexpectedEntities' => ['Form Alpha', 'Form Gamma', 'Form Delta'],
|
||||
];
|
||||
|
||||
yield 'search by one project OR form name' => [
|
||||
'searchTerm' => 'project:"Project Two" OR Gamma',
|
||||
'expectedEntities' => ['Form Alpha', 'Form Beta', 'Form Gamma'],
|
||||
'unexpectedEntities' => ['Form Delta'],
|
||||
];
|
||||
|
||||
yield 'search by NOT one project' => [
|
||||
'searchTerm' => '!project:"Project Two"',
|
||||
'expectedEntities' => ['Form Gamma', 'Form Delta'],
|
||||
'unexpectedEntities' => ['Form Alpha', 'Form Beta'],
|
||||
];
|
||||
|
||||
yield 'search by two projects with AND' => [
|
||||
'searchTerm' => 'project:"Project Two" AND project:"Project Three"',
|
||||
'expectedEntities' => ['Form Beta'],
|
||||
'unexpectedEntities' => ['Form Alpha', 'Form Gamma', 'Form Delta'],
|
||||
];
|
||||
|
||||
yield 'search by two projects with NOT AND' => [
|
||||
'searchTerm' => '!project:"Project Two" AND !project:"Project Three"',
|
||||
'expectedEntities' => ['Form Gamma', 'Form Delta'],
|
||||
'unexpectedEntities' => ['Form Alpha', 'Form Beta'],
|
||||
];
|
||||
|
||||
yield 'search by two projects with OR' => [
|
||||
'searchTerm' => 'project:"Project Two" OR project:"Project Three"',
|
||||
'expectedEntities' => ['Form Alpha', 'Form Beta'],
|
||||
'unexpectedEntities' => ['Form Gamma', 'Form Delta'],
|
||||
];
|
||||
|
||||
yield 'search by two projects with NOT OR' => [
|
||||
'searchTerm' => '!project:"Project Two" OR !project:"Project Three"',
|
||||
'expectedEntities' => ['Form Alpha', 'Form Gamma', 'Form Delta'],
|
||||
'unexpectedEntities' => ['Form Beta'],
|
||||
];
|
||||
}
|
||||
|
||||
private function createForm(string $name): Form
|
||||
{
|
||||
$form = new Form();
|
||||
$form->setName($name);
|
||||
$form->setAlias($name);
|
||||
$this->em->persist($form);
|
||||
|
||||
return $form;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,122 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Mautic\FormBundle\Tests\Controller;
|
||||
|
||||
use Mautic\CoreBundle\Test\MauticMysqlTestCase;
|
||||
use Mautic\FormBundle\Entity\Field;
|
||||
use Mautic\FormBundle\Entity\Form;
|
||||
use Mautic\LeadBundle\Entity\Company;
|
||||
use PHPUnit\Framework\Assert;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
final class PublicControllerFunctionalTest extends MauticMysqlTestCase
|
||||
{
|
||||
public function testLookupActionWithNoLookupFormField(): void
|
||||
{
|
||||
$this->makeRequest(['string' => 'Company']);
|
||||
$clientResponse = $this->client->getResponse();
|
||||
|
||||
self::assertResponseStatusCodeSame(Response::HTTP_BAD_REQUEST, $clientResponse->getContent());
|
||||
Assert::assertSame('{"error":"Invalid request param"}', $clientResponse->getContent(), $clientResponse->getContent());
|
||||
}
|
||||
|
||||
public function testLookupActionWithInvalidLookupFormField(): void
|
||||
{
|
||||
$this->makeRequest(['string' => 'Company', 'formId' => 3]);
|
||||
$clientResponse = $this->client->getResponse();
|
||||
|
||||
self::assertResponseStatusCodeSame(Response::HTTP_BAD_REQUEST, $clientResponse->getContent());
|
||||
Assert::assertSame('{"error":"Invalid request param"}', $clientResponse->getContent(), $clientResponse->getContent());
|
||||
}
|
||||
|
||||
public function testLookupActionWithTooFewLetters(): void
|
||||
{
|
||||
$form = $this->createForm();
|
||||
|
||||
$this->makeRequest(['string' => 'Co', 'formId' => $form->getId()]);
|
||||
$clientResponse = $this->client->getResponse();
|
||||
|
||||
self::assertResponseStatusCodeSame(Response::HTTP_BAD_REQUEST, $clientResponse->getContent());
|
||||
Assert::assertSame('{"error":"Invalid request param"}', $clientResponse->getContent(), $clientResponse->getContent());
|
||||
}
|
||||
|
||||
public function testLookupActionWithCompanyData(): void
|
||||
{
|
||||
$this->createCompany('Unicorn A');
|
||||
$companyA = $this->createCompany('Company A');
|
||||
$companyB = $this->createCompany('Company B', 'Boston', 'Massachusetts');
|
||||
$form = $this->createForm();
|
||||
|
||||
$this->makeRequest(['search' => 'Company', 'formId' => $form->getId()]);
|
||||
$clientResponse = $this->client->getResponse();
|
||||
|
||||
Assert::assertTrue($clientResponse->isOk(), $clientResponse->getContent());
|
||||
Assert::assertSame(
|
||||
[
|
||||
[
|
||||
'id' => (string) $companyA->getId(),
|
||||
'companyname' => 'Company A',
|
||||
'companycity' => null,
|
||||
'companystate' => null,
|
||||
], [
|
||||
'id' => (string) $companyB->getId(),
|
||||
'companyname' => 'Company B',
|
||||
'companycity' => 'Boston',
|
||||
'companystate' => 'Massachusetts',
|
||||
],
|
||||
],
|
||||
json_decode($clientResponse->getContent(), true)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed[] $payload
|
||||
*/
|
||||
private function makeRequest(array $payload): void
|
||||
{
|
||||
$this->client->request(
|
||||
Request::METHOD_POST,
|
||||
'/form/company-lookup/autocomplete',
|
||||
[],
|
||||
[],
|
||||
['Content-Type' => 'application/json'],
|
||||
json_encode($payload)
|
||||
);
|
||||
}
|
||||
|
||||
private function createCompany(string $name, ?string $city = null, ?string $state = null): Company
|
||||
{
|
||||
$company = new Company();
|
||||
$company->setName($name);
|
||||
$company->setCity($city);
|
||||
$company->setState($state);
|
||||
|
||||
$this->em->persist($company);
|
||||
$this->em->flush();
|
||||
|
||||
return $company;
|
||||
}
|
||||
|
||||
private function createForm(): Form
|
||||
{
|
||||
$field = new Field();
|
||||
$field->setAlias('company-lookup');
|
||||
$field->setLabel('Company');
|
||||
$field->setType('companyLookup');
|
||||
|
||||
$form = new Form();
|
||||
$form->setName('Company Lookup Test');
|
||||
$form->setAlias('company-lookup-test');
|
||||
$form->addField(0, $field);
|
||||
$field->setForm($form);
|
||||
|
||||
$this->em->persist($field);
|
||||
$this->em->persist($form);
|
||||
$this->em->flush();
|
||||
|
||||
return $form;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,168 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Mautic\FormBundle\Tests\Controller;
|
||||
|
||||
use Mautic\CoreBundle\Test\MauticMysqlTestCase;
|
||||
use Symfony\Component\HttpFoundation\File\UploadedFile;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
final class ResultControllerFunctionalTest extends MauticMysqlTestCase
|
||||
{
|
||||
protected $useCleanupRollback = false;
|
||||
|
||||
public function testDownloadFileByFileNameAction(): void
|
||||
{
|
||||
$fieldModel = static::getContainer()->get('mautic.form.model.field');
|
||||
$formUploader = static::getContainer()->get('mautic.form.helper.form_uploader');
|
||||
$fileName = 'image.png';
|
||||
|
||||
$this->createFile($fileName);
|
||||
|
||||
$formPayload = [
|
||||
'name' => 'API form',
|
||||
'formType' => 'standalone',
|
||||
'alias' => 'apiform',
|
||||
'description' => 'Test API Form',
|
||||
'isPublished' => true,
|
||||
'fields' => [
|
||||
[
|
||||
'label' => 'File',
|
||||
'alias' => 'file_field',
|
||||
'type' => 'file',
|
||||
'properties' => [
|
||||
'allowed_file_size' => 1,
|
||||
'allowed_file_extensions' => ['txt', 'jpg', 'gif', 'png'],
|
||||
'public' => true,
|
||||
],
|
||||
],
|
||||
],
|
||||
'postAction' => 'return',
|
||||
];
|
||||
|
||||
$this->client->request('POST', '/api/forms/new', $formPayload);
|
||||
$clientResponse = $this->client->getResponse();
|
||||
|
||||
$this->assertSame(Response::HTTP_CREATED, $clientResponse->getStatusCode(), $clientResponse->getContent());
|
||||
$response = json_decode($clientResponse->getContent(), true);
|
||||
$form = $response['form'];
|
||||
$formId = $form['id'];
|
||||
$fieldId = $form['fields'][0]['id'];
|
||||
|
||||
$crawler = $this->client->request(Request::METHOD_GET, "/form/{$formId}");
|
||||
$formCrawler = $crawler->filter('form[id=mauticform_apiform]');
|
||||
$form = $formCrawler->form();
|
||||
$file = new UploadedFile($fileName, $fileName, 'image/png');
|
||||
$form->setValues([
|
||||
'mauticform[file_field]' => $file,
|
||||
]);
|
||||
$this->client->submit($form);
|
||||
$this->assertTrue($this->client->getResponse()->isOk());
|
||||
|
||||
$this->client->request(Request::METHOD_GET, "/forms/results/file/{$fieldId}/filename/{$fileName}");
|
||||
$this->assertTrue($this->client->getResponse()->isOk());
|
||||
|
||||
$field = $fieldModel->getEntity($fieldId);
|
||||
unlink($fileName);
|
||||
unlink($formUploader->getCompleteFilePath($field, $fileName));
|
||||
|
||||
$folderPath = str_replace(DIRECTORY_SEPARATOR.$fileName, '', $formUploader->getCompleteFilePath($field, $fileName));
|
||||
if (is_dir($folderPath)) {
|
||||
rmdir($folderPath);
|
||||
}
|
||||
}
|
||||
|
||||
public function testAddToSegmentActionRendersBatchForm(): void
|
||||
{
|
||||
// Create a form
|
||||
$formPayload = [
|
||||
'name' => 'Segment Test Form',
|
||||
'formType' => 'standalone',
|
||||
'alias' => 'segmenttestform',
|
||||
'description' => 'Form for segment batch test',
|
||||
'isPublished' => true,
|
||||
'fields' => [
|
||||
[
|
||||
'label' => 'Email',
|
||||
'alias' => 'email',
|
||||
'type' => 'email',
|
||||
'properties' => [],
|
||||
],
|
||||
],
|
||||
'postAction' => 'return',
|
||||
];
|
||||
|
||||
$this->client->request('POST', '/api/forms/new', $formPayload);
|
||||
$clientResponse = $this->client->getResponse();
|
||||
|
||||
$this->assertSame(Response::HTTP_CREATED, $clientResponse->getStatusCode(), $clientResponse->getContent());
|
||||
$response = json_decode($clientResponse->getContent(), true);
|
||||
$form = $response['form'];
|
||||
$formId = $form['id'];
|
||||
|
||||
// Submit a form result (simulate a contact submission)
|
||||
$this->client->request('POST', "/form/{$formId}", [
|
||||
'mauticform[email]' => 'test@example.com',
|
||||
'mauticform[formId]' => $formId,
|
||||
'mauticform[return]' => '',
|
||||
]);
|
||||
$this->assertResponseIsSuccessful();
|
||||
|
||||
// Call the addToSegmentAction
|
||||
$this->client->request('GET', "/s/forms/results/{$formId}/add-to-segment");
|
||||
$response = $this->client->getResponse();
|
||||
$this->assertResponseIsSuccessful();
|
||||
$this->assertStringContainsString('form', $response->getContent());
|
||||
$this->assertStringContainsString('batch', $response->getContent());
|
||||
}
|
||||
|
||||
public function testEditButtonIsDisplayedOnFormResultsPage(): void
|
||||
{
|
||||
$formPayload = [
|
||||
'name' => 'Test Form for Results',
|
||||
'formType' => 'standalone',
|
||||
'alias' => 'testformresults',
|
||||
'description' => 'Test Form for Results Page',
|
||||
'isPublished' => true,
|
||||
'fields' => [
|
||||
[
|
||||
'label' => 'Name',
|
||||
'alias' => 'name',
|
||||
'type' => 'text',
|
||||
],
|
||||
],
|
||||
'postAction' => 'return',
|
||||
];
|
||||
|
||||
$this->client->request('POST', '/api/forms/new', $formPayload);
|
||||
$clientResponse = $this->client->getResponse();
|
||||
|
||||
$this->assertSame(Response::HTTP_CREATED, $clientResponse->getStatusCode(), $clientResponse->getContent());
|
||||
$response = json_decode($clientResponse->getContent(), true);
|
||||
$form = $response['form'];
|
||||
$formId = $form['id'];
|
||||
|
||||
$crawler = $this->client->request(Request::METHOD_GET, "/s/forms/results/{$formId}");
|
||||
$response = $this->client->getResponse();
|
||||
|
||||
if (!$response->isOk()) {
|
||||
$this->fail('Response is not OK. Status: '.$response->getStatusCode().', Content: '.$response->getContent());
|
||||
}
|
||||
|
||||
$editButton = $crawler->filter('a[href*="/s/forms/edit/'.$formId.'"]');
|
||||
$this->assertCount(1, $editButton, 'Edit button should be present on form results page');
|
||||
}
|
||||
|
||||
private function createFile(string $filename): void
|
||||
{
|
||||
$data = 'data:image/png;base64,AAAFBfj42Pj4';
|
||||
|
||||
[$type, $data] = explode(';', $data);
|
||||
[, $data] = explode(',', $data);
|
||||
$data = base64_decode($data);
|
||||
|
||||
file_put_contents($filename, $data);
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1 @@
|
||||
mautic.form.submission.pleasewait="Merci de patienter..."
|
||||
@@ -0,0 +1 @@
|
||||
mautic.form.field.generic.required="Ceci est requis."
|
||||
@@ -0,0 +1 @@
|
||||
{"name":"French","locale":"fr","author":"Mautic Translators"}
|
||||
@@ -0,0 +1,39 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Mautic\FormBundle\Tests\Crate;
|
||||
|
||||
use Mautic\FormBundle\Crate\FieldCrate;
|
||||
use PHPUnit\Framework\Assert;
|
||||
|
||||
final class FieldCrateTest extends \PHPUnit\Framework\TestCase
|
||||
{
|
||||
public function testGettersForEmailField(): void
|
||||
{
|
||||
$field = new FieldCrate('6', 'Email', 'email', []);
|
||||
|
||||
Assert::assertSame('6', $field->getKey());
|
||||
Assert::assertSame('Email', $field->getName());
|
||||
Assert::assertSame('email', $field->getType());
|
||||
Assert::assertSame([], $field->getProperties());
|
||||
Assert::assertFalse($field->isListType());
|
||||
}
|
||||
|
||||
public function testGettersForSelectField(): void
|
||||
{
|
||||
$properties = [
|
||||
'list' => [
|
||||
'Red' => 'red',
|
||||
'Green' => 'green',
|
||||
],
|
||||
];
|
||||
$field = new FieldCrate('7', 'Colors', 'select', $properties);
|
||||
|
||||
Assert::assertSame('7', $field->getKey());
|
||||
Assert::assertSame('Colors', $field->getName());
|
||||
Assert::assertSame('select', $field->getType());
|
||||
Assert::assertSame($properties, $field->getProperties());
|
||||
Assert::assertTrue($field->isListType());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Mautic\FormBundle\Tests\Crate;
|
||||
|
||||
use Mautic\FormBundle\Crate\ObjectCrate;
|
||||
use PHPUnit\Framework\Assert;
|
||||
|
||||
final class ObjectCrateTest extends \PHPUnit\Framework\TestCase
|
||||
{
|
||||
public function testGetters(): void
|
||||
{
|
||||
$field = new ObjectCrate('contact', 'Contact');
|
||||
|
||||
Assert::assertSame('contact', $field->getKey());
|
||||
Assert::assertSame('Contact', $field->getName());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,342 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Mautic\FormBundle\Tests\Entity;
|
||||
|
||||
use Mautic\CoreBundle\Helper\InputHelper;
|
||||
use Mautic\FormBundle\Entity\Field;
|
||||
use Mautic\FormBundle\Entity\Form;
|
||||
use Mautic\LeadBundle\Entity\Lead;
|
||||
use PHPUnit\Framework\Assert;
|
||||
|
||||
final class FieldTest extends \PHPUnit\Framework\TestCase
|
||||
{
|
||||
public function testShowForConditionalFieldWithNoParent(): void
|
||||
{
|
||||
$field = new Field();
|
||||
$this->assertTrue($field->showForConditionalField([]));
|
||||
}
|
||||
|
||||
public function testShowForConditionalFieldWithParentButNoAlias(): void
|
||||
{
|
||||
$parentFieldId = '55';
|
||||
$field = new Field();
|
||||
$parentField = $this->createMock(Field::class);
|
||||
$form = new Form();
|
||||
$form->addField(0, $parentField);
|
||||
$field->setForm($form);
|
||||
$field->setParent($parentFieldId);
|
||||
$parentField->method('getId')->willReturn($parentFieldId);
|
||||
|
||||
$this->assertFalse($field->showForConditionalField([]));
|
||||
}
|
||||
|
||||
public function testShowForConditionalFieldWithParentAndAliasAndNotInConditionAndBadValue(): void
|
||||
{
|
||||
$parentFieldId = '55';
|
||||
$parentFieldAlias = 'field_a';
|
||||
$field = new Field();
|
||||
$parentField = $this->createMock(Field::class);
|
||||
$form = new Form();
|
||||
$form->addField(0, $parentField);
|
||||
$field->setForm($form);
|
||||
$field->setParent($parentFieldId);
|
||||
$field->setConditions(['expr' => 'notIn', 'values' => []]);
|
||||
$parentField->method('getId')->willReturn($parentFieldId);
|
||||
$parentField->method('getAlias')->willReturn($parentFieldAlias);
|
||||
$data = [$parentFieldAlias => 'value A'];
|
||||
|
||||
$this->assertTrue($field->showForConditionalField($data));
|
||||
}
|
||||
|
||||
public function testShowForConditionalFieldWithParentAndAliasWith0ValueAndNotInConditionAndBadValue(): void
|
||||
{
|
||||
$parentFieldId = '55';
|
||||
$parentFieldAlias = 'field_a';
|
||||
$field = new Field();
|
||||
$parentField = $this->createMock(Field::class);
|
||||
$form = new Form();
|
||||
$form->addField(0, $parentField);
|
||||
$field->setForm($form);
|
||||
$field->setParent($parentFieldId);
|
||||
$field->setConditions(['expr' => 'notIn', 'values' => [1]]);
|
||||
$parentField->method('getId')->willReturn($parentFieldId);
|
||||
$parentField->method('getAlias')->willReturn($parentFieldAlias);
|
||||
$data = [$parentFieldAlias => 0];
|
||||
|
||||
$this->assertTrue($field->showForConditionalField($data));
|
||||
}
|
||||
|
||||
public function testShowForConditionalFieldWithParentAndAliasAndNotInConditionAndMatchingValue(): void
|
||||
{
|
||||
$parentFieldId = '55';
|
||||
$parentFieldAlias = 'field_a';
|
||||
$field = new Field();
|
||||
$parentField = $this->createMock(Field::class);
|
||||
$form = new Form();
|
||||
$form->addField(0, $parentField);
|
||||
$field->setForm($form);
|
||||
$field->setParent($parentFieldId);
|
||||
$field->setConditions(['expr' => 'notIn', 'values' => ['value A']]);
|
||||
$parentField->method('getId')->willReturn($parentFieldId);
|
||||
$parentField->method('getAlias')->willReturn($parentFieldAlias);
|
||||
$data = [$parentFieldAlias => 'value A'];
|
||||
|
||||
$this->assertFalse($field->showForConditionalField($data));
|
||||
}
|
||||
|
||||
public function testShowForConditionalFieldWithParentAndAliasAndAnyValue(): void
|
||||
{
|
||||
$parentFieldId = '55';
|
||||
$parentFieldAlias = 'field_a';
|
||||
$field = new Field();
|
||||
$parentField = $this->createMock(Field::class);
|
||||
$form = new Form();
|
||||
$form->addField(0, $parentField);
|
||||
$field->setForm($form);
|
||||
$field->setParent($parentFieldId);
|
||||
$field->setConditions(['expr' => '', 'any' => true, 'values' => ['value A']]);
|
||||
$parentField->method('getId')->willReturn($parentFieldId);
|
||||
$parentField->method('getAlias')->willReturn($parentFieldAlias);
|
||||
$data = [$parentFieldAlias => 'value A'];
|
||||
|
||||
$this->assertTrue($field->showForConditionalField($data));
|
||||
}
|
||||
|
||||
public function testShowForConditionalFieldWithParentValue0AndAliasAndAnyValue(): void
|
||||
{
|
||||
$parentFieldId = '55';
|
||||
$parentFieldAlias = 'field_a';
|
||||
$field = new Field();
|
||||
$parentField = $this->createMock(Field::class);
|
||||
$form = new Form();
|
||||
$form->addField(0, $parentField);
|
||||
$field->setForm($form);
|
||||
$field->setParent($parentFieldId);
|
||||
$field->setConditions(['expr' => '', 'any' => true, 'values' => [1]]);
|
||||
$parentField->method('getId')->willReturn($parentFieldId);
|
||||
$parentField->method('getAlias')->willReturn($parentFieldAlias);
|
||||
$data = [$parentFieldAlias => 0];
|
||||
|
||||
$this->assertTrue($field->showForConditionalField($data));
|
||||
}
|
||||
|
||||
public function testShowForConditionalFieldWithParentAndAliasAndInValueMatches(): void
|
||||
{
|
||||
$parentFieldId = '55';
|
||||
$parentFieldAlias = 'field_a';
|
||||
$field = new Field();
|
||||
$parentField = $this->createMock(Field::class);
|
||||
$form = new Form();
|
||||
$form->addField(0, $parentField);
|
||||
$field->setForm($form);
|
||||
$field->setParent($parentFieldId);
|
||||
$field->setConditions(['expr' => 'in', 'values' => ['value A']]);
|
||||
$parentField->method('getId')->willReturn($parentFieldId);
|
||||
$parentField->method('getAlias')->willReturn($parentFieldAlias);
|
||||
$data = [$parentFieldAlias => ['value A']];
|
||||
|
||||
$this->assertTrue($field->showForConditionalField($data));
|
||||
}
|
||||
|
||||
public function testShowForConditionalFieldWithParentAndAliasAndInValueDoesNotMatch(): void
|
||||
{
|
||||
$parentFieldId = '55';
|
||||
$parentFieldAlias = 'field_a';
|
||||
$field = new Field();
|
||||
$parentField = $this->createMock(Field::class);
|
||||
$form = new Form();
|
||||
$form->addField(0, $parentField);
|
||||
$field->setForm($form);
|
||||
$field->setParent($parentFieldId);
|
||||
$field->setConditions(['expr' => 'in', 'values' => ['value B']]);
|
||||
$parentField->method('getId')->willReturn(55);
|
||||
$parentField->method('getAlias')->willReturn($parentFieldAlias);
|
||||
$data = [$parentFieldAlias => ['value A']];
|
||||
|
||||
$this->assertFalse($field->showForConditionalField($data));
|
||||
}
|
||||
|
||||
public function testShowForConditionalFieldWithParentAndAliasAndInValueMatchesWithDifferentTypes(): void
|
||||
{
|
||||
$parentFieldId = '55';
|
||||
$parentFieldAlias = 'field_a';
|
||||
$field = new Field();
|
||||
$parentField = $this->createMock(Field::class);
|
||||
$form = new Form();
|
||||
$form->addField(0, $parentField);
|
||||
$field->setForm($form);
|
||||
$field->setParent($parentFieldId);
|
||||
$field->setConditions(['expr' => 'in', 'values' => ['0']]);
|
||||
$parentField->method('getId')->willReturn($parentFieldId);
|
||||
$parentField->method('getAlias')->willReturn($parentFieldAlias);
|
||||
$data = [$parentFieldAlias => [0]];
|
||||
|
||||
$this->assertTrue($field->showForConditionalField($data));
|
||||
}
|
||||
|
||||
public function testShowForConditionalFieldWithParentAndAliasAndInValueMatchesSpecialCharacters(): void
|
||||
{
|
||||
$parentFieldId = '55';
|
||||
$parentFieldAlias = 'field_a';
|
||||
$field = new Field();
|
||||
$parentField = $this->createMock(Field::class);
|
||||
$form = new Form();
|
||||
$form->addField(0, $parentField);
|
||||
$field->setForm($form);
|
||||
$field->setParent($parentFieldId);
|
||||
$specialValue = 'čé+äà>&"\'è';
|
||||
$field->setConditions(['expr' => 'in', 'values' => [InputHelper::clean($specialValue)]]);
|
||||
$parentField->method('getId')->willReturn($parentFieldId);
|
||||
$parentField->method('getAlias')->willReturn($parentFieldAlias);
|
||||
$data = [$parentFieldAlias => [$specialValue]];
|
||||
|
||||
$this->assertTrue($field->showForConditionalField($data));
|
||||
}
|
||||
|
||||
public function testShowForContactIfFormIsNull(): void
|
||||
{
|
||||
$field = new Field();
|
||||
Assert::assertTrue($field->showForContact());
|
||||
}
|
||||
|
||||
public function testShowForContactIfInKioskMode(): void
|
||||
{
|
||||
$field = new Field();
|
||||
$form = new Form();
|
||||
$form->setInKioskMode(true);
|
||||
Assert::assertTrue($field->showForContact(null, null, $form));
|
||||
}
|
||||
|
||||
public function testShowForContactIfShowWhenValueExistsIsTrue(): void
|
||||
{
|
||||
$field = new Field();
|
||||
$form = new Form();
|
||||
$form->setInKioskMode(false);
|
||||
$field->setShowWhenValueExists(true);
|
||||
Assert::assertTrue($field->showForContact(null, null, $form));
|
||||
}
|
||||
|
||||
public function testShowForContactIfShowWhenValueExistsIsFalseAndSubmissionExists(): void
|
||||
{
|
||||
$field = new Field();
|
||||
$form = new Form();
|
||||
$submissions = [['field_a' => 'Value A']];
|
||||
$form->setInKioskMode(false);
|
||||
$field->setShowWhenValueExists(false);
|
||||
$field->setIsAutoFill(false);
|
||||
$field->setAlias('field_a');
|
||||
Assert::assertFalse($field->showForContact($submissions, null, $form));
|
||||
}
|
||||
|
||||
public function testShowForContactIfShowWhenValueExistsIsFalseAndSubmissionDoesNotExist(): void
|
||||
{
|
||||
$field = new Field();
|
||||
$form = new Form();
|
||||
$submissions = [['field_a' => 'Value A']];
|
||||
$form->setInKioskMode(false);
|
||||
$field->setShowWhenValueExists(false);
|
||||
$field->setIsAutoFill(false);
|
||||
$field->setAlias('unicorn');
|
||||
Assert::assertTrue($field->showForContact($submissions, null, $form));
|
||||
}
|
||||
|
||||
public function testShowForContactIfShowWhenValueExistsIsFalseAndMappedLeadFieldValueExists(): void
|
||||
{
|
||||
$field = new Field();
|
||||
$form = new Form();
|
||||
$contact = new class extends Lead {
|
||||
public function getFieldValue($field, $group = null)
|
||||
{
|
||||
Assert::assertSame('field_a', $field);
|
||||
|
||||
return 'Value A';
|
||||
}
|
||||
};
|
||||
$form->setInKioskMode(false);
|
||||
$field->setShowWhenValueExists(false);
|
||||
$field->setMappedField('field_a');
|
||||
$field->setMappedObject('contact');
|
||||
$field->setIsAutoFill(false);
|
||||
Assert::assertFalse($field->showForContact(null, $contact, $form));
|
||||
}
|
||||
|
||||
public function testShowForContactIfShowWhenValueExistsIsFalseAndMappedLeadFieldValueDoesNotExist(): void
|
||||
{
|
||||
$field = new Field();
|
||||
$form = new Form();
|
||||
$contact = new class extends Lead {
|
||||
public function getFieldValue($field, $group = null)
|
||||
{
|
||||
Assert::assertSame('field_a', $field);
|
||||
|
||||
return null;
|
||||
}
|
||||
};
|
||||
$form->setInKioskMode(false);
|
||||
$field->setShowWhenValueExists(false);
|
||||
$field->setMappedField('field_a');
|
||||
$field->setMappedObject('contact');
|
||||
$field->setIsAutoFill(false);
|
||||
Assert::assertTrue($field->showForContact(null, $contact, $form));
|
||||
}
|
||||
|
||||
public function testShowForContactIfShowWhenValueExistsIsFalseAndMappedNotLeadFieldValueExists(): void
|
||||
{
|
||||
$field = new Field();
|
||||
$form = new Form();
|
||||
$contact = new class extends Lead {
|
||||
public function getFieldValue($field, $group = null)
|
||||
{
|
||||
Assert::assertSame('field_a', $field);
|
||||
|
||||
return 'Value A';
|
||||
}
|
||||
};
|
||||
$form->setInKioskMode(false);
|
||||
$field->setShowWhenValueExists(false);
|
||||
$field->setMappedField('field_a');
|
||||
$field->setMappedObject('unicorn_object');
|
||||
$field->setIsAutoFill(false);
|
||||
Assert::assertTrue($field->showForContact(null, $contact, $form));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, int> $properties
|
||||
*/
|
||||
#[\PHPUnit\Framework\Attributes\DataProvider('dataProvider')]
|
||||
public function testHasChoices(string $type, array $properties, bool $result): void
|
||||
{
|
||||
$field = new Field();
|
||||
$field->setProperties($properties);
|
||||
$field->setType($type);
|
||||
|
||||
$this->assertEquals($result, $field->hasChoices());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<int, mixed>
|
||||
*/
|
||||
public static function dataProvider(): iterable
|
||||
{
|
||||
yield ['string', [], false];
|
||||
yield ['string', ['multiple' => 0], false];
|
||||
yield ['string', ['multiple' => 1], true];
|
||||
yield ['checkboxgrp', [], true];
|
||||
yield ['checkboxgrp', ['multiple' => 0], true];
|
||||
yield ['checkboxgrp', ['multiple' => 1], true];
|
||||
}
|
||||
|
||||
public function testFieldWidth(): void
|
||||
{
|
||||
$field = new Field();
|
||||
$this->assertEquals('100%', $field->getFieldWidth(), 'Default field width should be 100%');
|
||||
|
||||
$field->setFieldWidth('50%');
|
||||
$this->assertEquals('50%', $field->getFieldWidth(), 'Field width should be updated to 50%');
|
||||
|
||||
$field->setFieldWidth('');
|
||||
$this->assertEquals('100%', $field->getFieldWidth(), 'Field width should default to 100% when set to empty string');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Mautic\FormBundle\Tests\Entity;
|
||||
|
||||
use Mautic\FormBundle\Entity\Field;
|
||||
use Mautic\FormBundle\Entity\Form;
|
||||
use PHPUnit\Framework\Assert;
|
||||
|
||||
final class FormTest extends \PHPUnit\Framework\TestCase
|
||||
{
|
||||
#[\PHPUnit\Framework\Attributes\DataProvider('setNoIndexDataProvider')]
|
||||
public function testSetNoIndex($value, $expected, array $changes): void
|
||||
{
|
||||
$form = new Form();
|
||||
$form->setNoIndex($value);
|
||||
|
||||
Assert::assertSame($expected, $form->getNoIndex());
|
||||
Assert::assertSame($changes, $form->getChanges());
|
||||
}
|
||||
|
||||
public static function setNoIndexDataProvider(): iterable
|
||||
{
|
||||
yield [null, null, ['noIndex' => [true, null]]];
|
||||
yield [true, true, []];
|
||||
yield [false, false, ['noIndex' => [true, false]]];
|
||||
yield ['', false, ['noIndex' => [true, false]]];
|
||||
yield [0, false, ['noIndex' => [true, false]]];
|
||||
yield ['string', true, []];
|
||||
}
|
||||
|
||||
public function testGetMappedFieldValues(): void
|
||||
{
|
||||
$form = $this->createForm();
|
||||
$result = [
|
||||
[
|
||||
'formFieldId' => null,
|
||||
'mappedObject' => 'contact',
|
||||
'mappedField' => 'email',
|
||||
],
|
||||
[
|
||||
'formFieldId' => null,
|
||||
'mappedObject' => 'company',
|
||||
'mappedField' => 'companyemail',
|
||||
],
|
||||
[
|
||||
'formFieldId' => null,
|
||||
'mappedObject' => 'company',
|
||||
'mappedField' => 'companyname',
|
||||
],
|
||||
];
|
||||
|
||||
Assert::assertSame($result, $form->getMappedFieldValues());
|
||||
}
|
||||
|
||||
public function testGetMappedFieldObjects(): void
|
||||
{
|
||||
$form = $this->createForm();
|
||||
|
||||
Assert::assertSame(['contact', 'company'], $form->getMappedFieldObjects());
|
||||
}
|
||||
|
||||
private function createForm(): Form
|
||||
{
|
||||
$form = new Form();
|
||||
$contactField = new Field();
|
||||
$companyFieldA = new Field();
|
||||
$companyFieldB = new Field();
|
||||
$notMappedField = new Field();
|
||||
$contactField->setMappedObject('contact');
|
||||
$contactField->setMappedField('email');
|
||||
$companyFieldA->setMappedObject('company');
|
||||
$companyFieldA->setMappedField('companyemail');
|
||||
$companyFieldB->setMappedObject('company');
|
||||
$companyFieldB->setMappedField('companyname');
|
||||
$form->addField('contact_field_a', $contactField);
|
||||
$form->addField('company_field_a', $companyFieldA);
|
||||
$form->addField('company_field_b', $companyFieldB);
|
||||
$form->addField('not_mapped_field_a', $notMappedField);
|
||||
|
||||
return $form;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Mautic\FormBundle\Tests\Event;
|
||||
|
||||
use Mautic\FormBundle\Entity\Field;
|
||||
use Mautic\FormBundle\Event\FormFieldEvent;
|
||||
|
||||
final class FormFieldEventTest extends \PHPUnit\Framework\TestCase
|
||||
{
|
||||
public function testWorkflow(): void
|
||||
{
|
||||
$field = new Field();
|
||||
$field2 = new Field();
|
||||
$event = new FormFieldEvent($field, true);
|
||||
$this->assertTrue($event->isNew());
|
||||
$this->assertSame($field, $event->getField());
|
||||
|
||||
$event->setField($field2);
|
||||
|
||||
$this->assertSame($field2, $event->getField());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Mautic\FormBundle\Tests\Event\Service;
|
||||
|
||||
use Mautic\FormBundle\Entity\Field;
|
||||
use Mautic\FormBundle\Entity\Form;
|
||||
use Mautic\FormBundle\Entity\Submission;
|
||||
use Mautic\FormBundle\Event\Service\FieldValueTransformer;
|
||||
use Mautic\FormBundle\Event\SubmissionEvent;
|
||||
use PHPUnit\Framework\Assert;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\Routing\Router;
|
||||
|
||||
final class FieldValueTransformerTest extends \PHPUnit\Framework\TestCase
|
||||
{
|
||||
public function testTransformValuesAfterSubmitWithNoFieldsNoMatchesAndNoTokens(): void
|
||||
{
|
||||
$router = new class extends Router {
|
||||
public function __construct()
|
||||
{
|
||||
}
|
||||
};
|
||||
$transformer = new FieldValueTransformer($router);
|
||||
$submission = new Submission();
|
||||
$form = new Form();
|
||||
$request = new Request();
|
||||
$submissionEvent = new SubmissionEvent($submission, [], [], $request);
|
||||
$submission->setForm($form);
|
||||
$transformer->transformValuesAfterSubmit($submissionEvent);
|
||||
|
||||
Assert::assertSame([], $submissionEvent->getTokens());
|
||||
Assert::assertSame([], $submissionEvent->getContactFieldMatches());
|
||||
}
|
||||
|
||||
public function testTransformValuesAfterSubmitWithFileFieldMatchesAndTokens(): void
|
||||
{
|
||||
$router = new class extends Router {
|
||||
public int $generateMethodCallCounter = 0;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
}
|
||||
|
||||
public function generate(string $name, mixed $parameters = [], int $referenceType = self::ABSOLUTE_PATH): string
|
||||
{
|
||||
Assert::assertSame('mautic_form_file_download', $name);
|
||||
Assert::assertSame([
|
||||
'submissionId' => 456,
|
||||
'field' => 'file_field_1',
|
||||
], $parameters);
|
||||
Assert::assertSame(self::ABSOLUTE_URL, $referenceType);
|
||||
++$this->generateMethodCallCounter;
|
||||
|
||||
return 'generated/route';
|
||||
}
|
||||
};
|
||||
$transformer = new FieldValueTransformer($router);
|
||||
$submission = new class extends Submission {
|
||||
public function getId(): int
|
||||
{
|
||||
return 456;
|
||||
}
|
||||
};
|
||||
$form = new Form();
|
||||
$field = new Field();
|
||||
$request = new Request();
|
||||
$submissionEvent = new SubmissionEvent($submission, [], [], $request);
|
||||
$field->setType('file');
|
||||
$field->setAlias('file_field_1');
|
||||
$field->setMappedField('contact_field_1');
|
||||
$field->setMappedObject('contact');
|
||||
$form->addField('123', $field);
|
||||
$submission->setForm($form);
|
||||
$submissionEvent->setTokens(['{formfield=file_field_1}' => 'original/route']);
|
||||
$submissionEvent->setContactFieldMatches(['contact_field_1' => 'original/route']);
|
||||
$transformer->transformValuesAfterSubmit($submissionEvent);
|
||||
|
||||
Assert::assertSame(['{formfield=file_field_1}' => 'generated/route'], $submissionEvent->getTokens());
|
||||
Assert::assertSame(['{formfield=file_field_1}' => 'generated/route'], $transformer->getTokensToUpdate());
|
||||
Assert::assertSame(['contact_field_1' => 'generated/route'], $submissionEvent->getContactFieldMatches());
|
||||
Assert::assertSame(['contact_field_1' => 'generated/route'], $transformer->getContactFieldsToUpdate());
|
||||
|
||||
// Calling it for the second time to ensure it's executed only once.
|
||||
$transformer->transformValuesAfterSubmit($submissionEvent);
|
||||
Assert::assertSame(1, $router->generateMethodCallCounter);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,164 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Mautic\FormBundle\Tests\EventListener;
|
||||
|
||||
use Mautic\CampaignBundle\Entity\Campaign;
|
||||
use Mautic\CampaignBundle\Entity\Event;
|
||||
use Mautic\CampaignBundle\Event\CampaignExecutionEvent;
|
||||
use Mautic\CoreBundle\Test\MauticMysqlTestCase;
|
||||
use Mautic\FormBundle\FormEvents;
|
||||
use Mautic\LeadBundle\Entity\Lead;
|
||||
use PHPUnit\Framework\Assert;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Contracts\EventDispatcher\EventDispatcherInterface;
|
||||
|
||||
class CampaignSubscriberFunctionalTest extends MauticMysqlTestCase
|
||||
{
|
||||
protected $useCleanupRollback = false;
|
||||
|
||||
#[\PHPUnit\Framework\Attributes\DataProvider('valueProvider')]
|
||||
public function testComparingFormSubmissionValues(string $valueToCompare, string $submittedValue, bool $result, string $operator = '='): void
|
||||
{
|
||||
$formPayload = [
|
||||
'name' => 'Test Form',
|
||||
'formType' => 'campaign',
|
||||
'fields' => [
|
||||
[
|
||||
'label' => 'Select A',
|
||||
'alias' => 'select_a',
|
||||
'type' => 'select',
|
||||
'properties' => [
|
||||
'list' => [
|
||||
'list' => [
|
||||
[
|
||||
'label' => $valueToCompare,
|
||||
'value' => $valueToCompare,
|
||||
],
|
||||
[
|
||||
'label' => $submittedValue,
|
||||
'value' => $submittedValue,
|
||||
],
|
||||
],
|
||||
],
|
||||
'multiple' => false,
|
||||
],
|
||||
], [
|
||||
'label' => 'Email',
|
||||
'alias' => 'email',
|
||||
'type' => 'email',
|
||||
'leadField' => 'email',
|
||||
], [
|
||||
'label' => 'Submit',
|
||||
'alias' => 'submit',
|
||||
'type' => 'button',
|
||||
],
|
||||
],
|
||||
'postAction' => 'return',
|
||||
];
|
||||
|
||||
// Creating the form via API so it would create the submission table.
|
||||
$this->client->request('POST', '/api/forms/new', $formPayload);
|
||||
$clientResponse = $this->client->getResponse();
|
||||
|
||||
$this->assertSame(Response::HTTP_CREATED, $clientResponse->getStatusCode(), $clientResponse->getContent());
|
||||
$response = json_decode($clientResponse->getContent(), true);
|
||||
$formId = $response['form']['id'];
|
||||
|
||||
// Submitting the form.
|
||||
$crawler = $this->client->request(Request::METHOD_GET, "/form/{$formId}");
|
||||
$saveButton = $crawler->selectButton('mauticform[submit]');
|
||||
$form = $saveButton->form();
|
||||
$form['mauticform[select_a]']->setValue($submittedValue);
|
||||
$form['mauticform[email]']->setValue('testing@ampersand.select');
|
||||
|
||||
$this->client->submit($form);
|
||||
Assert::assertTrue($this->client->getResponse()->isOk(), $this->client->getResponse()->getContent());
|
||||
|
||||
// Create some necessary entities.
|
||||
$campaignEvent = new Event();
|
||||
$campaignEvent->setName('Test Event');
|
||||
$campaignEvent->setType('form.field_value');
|
||||
$campaignEvent->setEventType('condition');
|
||||
$campaignEvent->setProperties(
|
||||
[
|
||||
'form' => $formId,
|
||||
'field' => 'select_a',
|
||||
'value' => $valueToCompare,
|
||||
'operator' => $operator,
|
||||
]
|
||||
);
|
||||
|
||||
$campaign = new Campaign();
|
||||
$campaign->setName('Test Campaign');
|
||||
$campaign->addEvent(1, $campaignEvent);
|
||||
|
||||
$campaignEvent->setCampaign($campaign);
|
||||
|
||||
$this->em->persist($campaignEvent);
|
||||
$this->em->persist($campaign);
|
||||
$this->em->flush();
|
||||
$this->em->detach($campaignEvent);
|
||||
$this->em->detach($campaign);
|
||||
|
||||
$contact = $this->em->getRepository(Lead::class)->findOneBy(['email' => 'testing@ampersand.select']);
|
||||
$event = new CampaignExecutionEvent(
|
||||
[
|
||||
'lead' => $contact,
|
||||
'event' => $campaignEvent,
|
||||
'eventDetails' => null,
|
||||
'systemTriggered' => false,
|
||||
'eventSettings' => [],
|
||||
],
|
||||
null
|
||||
);
|
||||
|
||||
/** @var EventDispatcherInterface $dispatcher */
|
||||
$dispatcher = static::getContainer()->get('event_dispatcher');
|
||||
|
||||
$dispatcher->dispatch($event, FormEvents::ON_CAMPAIGN_TRIGGER_CONDITION);
|
||||
|
||||
Assert::assertSame('form', $event->getChannel());
|
||||
Assert::assertSame($result, $event->getResult());
|
||||
}
|
||||
|
||||
public static function valueProvider(): \Generator
|
||||
{
|
||||
yield [
|
||||
'test & test',
|
||||
'test & test',
|
||||
true,
|
||||
];
|
||||
|
||||
yield [
|
||||
'test & test',
|
||||
'unicorn',
|
||||
false,
|
||||
];
|
||||
|
||||
yield [
|
||||
'test',
|
||||
'tester',
|
||||
false,
|
||||
'!like',
|
||||
];
|
||||
|
||||
yield [
|
||||
'test',
|
||||
'tst',
|
||||
true,
|
||||
'!like',
|
||||
];
|
||||
}
|
||||
|
||||
protected function beforeTearDown(): void
|
||||
{
|
||||
$tablePrefix = static::getContainer()->getParameter('mautic.db_table_prefix');
|
||||
|
||||
if ($this->connection->createSchemaManager()->tablesExist("{$tablePrefix}form_results_1_test_form")) {
|
||||
$this->connection->executeStatement("DROP TABLE {$tablePrefix}form_results_1_test_form");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Mautic\FormBundle\Tests\EventListener;
|
||||
|
||||
use Mautic\CoreBundle\Event\DetermineWinnerEvent;
|
||||
use Mautic\FormBundle\Entity\SubmissionRepository;
|
||||
use Mautic\FormBundle\EventListener\DetermineWinnerSubscriber;
|
||||
use Mautic\PageBundle\Entity\Page;
|
||||
use PHPUnit\Framework\MockObject\MockObject;
|
||||
use Symfony\Contracts\Translation\TranslatorInterface;
|
||||
|
||||
class DetermineWinnerSubscriberTest extends \PHPUnit\Framework\TestCase
|
||||
{
|
||||
/**
|
||||
* @var MockObject|SubmissionRepository
|
||||
*/
|
||||
private MockObject $submissionRepository;
|
||||
|
||||
/**
|
||||
* @var MockObject|TranslatorInterface
|
||||
*/
|
||||
private MockObject $translator;
|
||||
|
||||
private DetermineWinnerSubscriber $subscriber;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
$this->submissionRepository = $this->createMock(SubmissionRepository::class);
|
||||
$this->translator = $this->createMock(TranslatorInterface::class);
|
||||
$this->subscriber = new DetermineWinnerSubscriber($this->submissionRepository, $this->translator);
|
||||
}
|
||||
|
||||
public function testOnDetermineSubmissionWinner(): void
|
||||
{
|
||||
$parentMock = $this->createMock(Page::class);
|
||||
$childMock = $this->createMock(Page::class);
|
||||
$children = [2 => $childMock];
|
||||
$parameters = ['parent' => $parentMock, 'children' => $children];
|
||||
$event = new DetermineWinnerEvent($parameters);
|
||||
$startDate = new \DateTime();
|
||||
$transSubmissions = 'submissions';
|
||||
$transHits = 'hits';
|
||||
$counts = [
|
||||
1 => [
|
||||
'count' => 20,
|
||||
'id' => 1,
|
||||
'name' => 'Test 5',
|
||||
'total' => 100,
|
||||
],
|
||||
2 => [
|
||||
'count' => 25,
|
||||
'id' => 2,
|
||||
'name' => 'Test 6',
|
||||
'total' => 150,
|
||||
],
|
||||
];
|
||||
|
||||
$this->translator->method('trans')
|
||||
->willReturnOnConsecutiveCalls($transSubmissions, $transHits);
|
||||
|
||||
$parentMock->expects($this->any())
|
||||
->method('isPublished')
|
||||
->willReturn(true);
|
||||
|
||||
$childMock->expects($this->any())
|
||||
->method('isPublished')
|
||||
->willReturn(true);
|
||||
|
||||
$parentMock->expects($this->any())
|
||||
->method('getId')
|
||||
->willReturn(1);
|
||||
|
||||
$childMock->expects($this->any())
|
||||
->method('getId')
|
||||
->willReturn(2);
|
||||
|
||||
$parentMock->expects($this->once())
|
||||
->method('getVariantStartDate')
|
||||
->willReturn($startDate);
|
||||
|
||||
$this->submissionRepository->expects($this->once())
|
||||
->method('getSubmissionCountsByPage')
|
||||
->with([1, 2], $startDate)
|
||||
->willReturn($counts);
|
||||
|
||||
$this->subscriber->onDetermineSubmissionWinner($event);
|
||||
|
||||
$expectedData = [
|
||||
$transSubmissions => [$counts[1]['count'], $counts[2]['count']],
|
||||
$transHits => [$counts[1]['total'], $counts[2]['total']],
|
||||
];
|
||||
|
||||
$abTestResults = $event->getAbTestResults();
|
||||
|
||||
$this->assertEquals($abTestResults['winners'], [1]);
|
||||
$this->assertEquals($abTestResults['support']['data'], $expectedData);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Mautic\FormBundle\Tests\EventListener;
|
||||
|
||||
use Mautic\FormBundle\Entity\Field;
|
||||
use Mautic\FormBundle\Entity\Form;
|
||||
use Mautic\FormBundle\Event\FormEvent;
|
||||
use Mautic\FormBundle\EventListener\FormConditionalSubscriber;
|
||||
use Mautic\FormBundle\Model\FieldModel;
|
||||
use Mautic\FormBundle\Model\FormModel;
|
||||
use PHPUnit\Framework\MockObject\MockObject;
|
||||
|
||||
final class FormConditionalSubscriberTest extends \PHPUnit\Framework\TestCase
|
||||
{
|
||||
/**
|
||||
* @var MockObject|FormModel
|
||||
*/
|
||||
private MockObject $formModel;
|
||||
|
||||
/**
|
||||
* @var MockObject|FieldModel
|
||||
*/
|
||||
private MockObject $fieldModel;
|
||||
|
||||
private FormConditionalSubscriber $subscriber;
|
||||
|
||||
public function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
$this->formModel = $this->createMock(FormModel::class);
|
||||
$this->fieldModel = $this->createMock(FieldModel::class);
|
||||
$this->subscriber = new FormConditionalSubscriber(
|
||||
$this->formModel,
|
||||
$this->fieldModel
|
||||
);
|
||||
}
|
||||
|
||||
public function testOnFormPostSaveForNewForm(): void
|
||||
{
|
||||
$parentField = $this->createMock(Field::class);
|
||||
$childField = $this->createMock(Field::class);
|
||||
$parentId = 'new_parent_id';
|
||||
$childId = 'new_child_id';
|
||||
$form = new Form();
|
||||
|
||||
$parentField->method('getId')->willReturn($parentId);
|
||||
$parentField->method('getSessionId')->willReturn($parentId);
|
||||
$childField->method('getId')->willReturn($childId);
|
||||
$childField->method('getSessionId')->willReturn($childId);
|
||||
$childField->method('getParent')->willReturn($parentId);
|
||||
|
||||
$form->addField(0, $parentField);
|
||||
$form->addField(1, $childField);
|
||||
|
||||
$this->fieldModel->expects($this->once())
|
||||
->method('saveEntity')
|
||||
->with($parentField);
|
||||
|
||||
$this->formModel->expects($this->never())
|
||||
->method('deleteFields');
|
||||
|
||||
$this->subscriber->onFormPostSave(new FormEvent($form, true));
|
||||
}
|
||||
|
||||
/**
|
||||
* A child field should be deleted when the parent does not exist anymore.
|
||||
*/
|
||||
public function testOnFormPostSaveForDeletedParent(): void
|
||||
{
|
||||
$childField = $this->createMock(Field::class);
|
||||
$parentId = 123;
|
||||
$childId = 456;
|
||||
$form = new Form();
|
||||
|
||||
$childField->method('getId')->willReturn($childId);
|
||||
$childField->method('getSessionId')->willReturn($childId);
|
||||
$childField->method('getParent')->willReturn($parentId);
|
||||
|
||||
$form->addField(1, $childField);
|
||||
|
||||
$this->fieldModel->expects($this->never())
|
||||
->method('saveEntity');
|
||||
|
||||
$this->formModel->expects($this->once())
|
||||
->method('deleteFields')
|
||||
->with($form, [$childId]);
|
||||
|
||||
$this->subscriber->onFormPostSave(new FormEvent($form, true));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Mautic\FormBundle\Tests\EventListener;
|
||||
|
||||
use Mautic\FormBundle\EventListener\FormFieldSubscriber;
|
||||
use Mautic\FormBundle\FormEvents;
|
||||
use Mautic\FormBundle\Model\FieldModel;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
final class FormFieldSubscriberTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* @var FormFieldSubscriber
|
||||
*/
|
||||
private $subscriber;
|
||||
|
||||
public function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
$fieldModel = $this->createMock(FieldModel::class);
|
||||
|
||||
$this->subscriber = new FormFieldSubscriber($fieldModel);
|
||||
}
|
||||
|
||||
public function testGetSubscribedEvents(): void
|
||||
{
|
||||
$this->assertEquals(
|
||||
[
|
||||
FormEvents::FIELD_POST_DELETE => ['onFieldPostDelete', 0],
|
||||
],
|
||||
$this->subscriber::getSubscribedEvents()
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,541 @@
|
||||
<?php
|
||||
|
||||
namespace FormBundle\Tests\EventListener;
|
||||
|
||||
use Mautic\CoreBundle\Entity\IpAddress;
|
||||
use Mautic\CoreBundle\Helper\IpLookupHelper;
|
||||
use Mautic\CoreBundle\Helper\LanguageHelper;
|
||||
use Mautic\CoreBundle\Model\AuditLogModel;
|
||||
use Mautic\EmailBundle\Helper\MailHelper;
|
||||
use Mautic\FormBundle\Entity\Action;
|
||||
use Mautic\FormBundle\Entity\Form;
|
||||
use Mautic\FormBundle\Entity\Submission;
|
||||
use Mautic\FormBundle\Event\SubmissionEvent;
|
||||
use Mautic\FormBundle\EventListener\FormSubscriber;
|
||||
use Mautic\LeadBundle\Entity\Lead;
|
||||
use Mautic\UserBundle\Entity\User;
|
||||
use PHPUnit\Framework\MockObject\MockObject;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\Routing\RouterInterface;
|
||||
use Symfony\Contracts\Translation\TranslatorInterface;
|
||||
|
||||
class FormSubscriberTest extends TestCase
|
||||
{
|
||||
private FormSubscriber $subscriber;
|
||||
|
||||
/**
|
||||
* @var MailHelper&MockObject
|
||||
*/
|
||||
private MailHelper $mailer;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
$ipLookupHelper = $this->createMock(IpLookupHelper::class);
|
||||
$auditLogModel = $this->createMock(AuditLogModel::class);
|
||||
$this->mailer = $this->createMock(MailHelper::class);
|
||||
$translator = $this->createMock(TranslatorInterface::class);
|
||||
$router = $this->createMock(RouterInterface::class);
|
||||
$languageHelper = $this->createMock(LanguageHelper::class);
|
||||
|
||||
$this->mailer->expects($this->once())
|
||||
->method('getMailer')
|
||||
->willReturnSelf();
|
||||
|
||||
$this->subscriber = new FormSubscriber(
|
||||
$ipLookupHelper,
|
||||
$auditLogModel,
|
||||
$this->mailer,
|
||||
$translator,
|
||||
$router,
|
||||
$languageHelper
|
||||
);
|
||||
}
|
||||
|
||||
public function testOnFormSubmitActionRepost(): void
|
||||
{
|
||||
$postData = [
|
||||
'first_name' => "Test's Name un être> and être",
|
||||
'notes' => 'A & B < dy >
|
||||
New line',
|
||||
'formId' => '1',
|
||||
'return' => '',
|
||||
'formName' => 'form190122',
|
||||
'messenger' => '1',
|
||||
];
|
||||
|
||||
$resultData = [
|
||||
'first_name' => 'Test's Name un être> and être',
|
||||
'notes' => 'A & B < dy > New line',
|
||||
];
|
||||
|
||||
$request = new Request();
|
||||
$submission = $this->getFormSubmission();
|
||||
$submissionEvent = new SubmissionEvent($submission, $postData, $request->server, $request);
|
||||
$submissionEvent->setResults($resultData)
|
||||
->setFields($this->getFormFields())
|
||||
->setAction($this->getFormRepostAction());
|
||||
|
||||
$this->subscriber->onFormSubmitActionRepost($submissionEvent);
|
||||
$postPayload = $submissionEvent->getPostSubmitPayload();
|
||||
|
||||
$this->assertSame([
|
||||
$postData['first_name'],
|
||||
$postData['notes'],
|
||||
], [
|
||||
$postPayload['first_name'],
|
||||
$postPayload['notes'],
|
||||
], 'Form data should be decode before posting to next form');
|
||||
}
|
||||
|
||||
public function testOnFormSubmitSendsNothingIfNoEmailsWereSet(): void
|
||||
{
|
||||
$tokensData = [];
|
||||
|
||||
$request = new Request();
|
||||
$submission = $this->getFormSubmission();
|
||||
$submissionEvent = new SubmissionEvent($submission, [], $request->server, $request);
|
||||
$action = $this->getFormSubmitActionSendEmail();
|
||||
$action->setProperties([
|
||||
'to' => null,
|
||||
'cc' => null,
|
||||
'bcc' => null,
|
||||
'copy_lead' => false,
|
||||
]);
|
||||
$submissionEvent->setTokens($tokensData)
|
||||
->setFields($this->getFormFields())
|
||||
->setAction($action);
|
||||
|
||||
$this->mailer->expects(self::never())
|
||||
->method('send');
|
||||
|
||||
$this->subscriber->onFormSubmitActionSendEmail($submissionEvent);
|
||||
}
|
||||
|
||||
#[\PHPUnit\Framework\Attributes\DataProvider('toCcBccProvider')]
|
||||
public function testOnFormSubmitSendsIfOneOfEmailsEmailsWereSet(?string $to, ?string $cc, ?string $bcc): void
|
||||
{
|
||||
$subject = 'subject';
|
||||
$message = 'message';
|
||||
$tokensData = [
|
||||
'first_name' => 'Test's Name',
|
||||
'notes' => "A & B \n < dy >",
|
||||
];
|
||||
|
||||
$emailTokens = [
|
||||
'first_name' => "Test's Name",
|
||||
'notes' => "A & B <br />\n < dy >",
|
||||
];
|
||||
|
||||
$request = new Request();
|
||||
$submission = $this->getFormSubmission();
|
||||
$submissionEvent = new SubmissionEvent($submission, [], $request->server, $request);
|
||||
$action = $this->getFormSubmitActionSendEmail();
|
||||
$action->setProperties([
|
||||
'to' => $to,
|
||||
'cc' => $cc,
|
||||
'bcc' => $bcc,
|
||||
'copy_lead' => false,
|
||||
'subject' => $subject,
|
||||
'message' => $message,
|
||||
]);
|
||||
$submissionEvent->setTokens($tokensData)
|
||||
->setFields($this->getFormFields())
|
||||
->setAction($action);
|
||||
|
||||
$this->mailer->expects(self::once())
|
||||
->method('reset');
|
||||
$this->mailer->expects(self::once())
|
||||
->method('send');
|
||||
|
||||
if (null !== $to) {
|
||||
$this->mailer->expects(self::once())
|
||||
->method('setTo')
|
||||
->with(array_fill_keys(array_map('trim', explode(',', $to)), null));
|
||||
}
|
||||
|
||||
if (null !== $cc) {
|
||||
$this->mailer->expects(self::once())
|
||||
->method('setCc')
|
||||
->with(array_fill_keys(array_map('trim', explode(',', $cc)), null));
|
||||
}
|
||||
|
||||
if (null !== $bcc) {
|
||||
$this->mailer->expects(self::once())
|
||||
->method('setBcc')
|
||||
->with(array_fill_keys(array_map('trim', explode(',', $bcc)), null));
|
||||
}
|
||||
|
||||
$this->mailer->expects(self::once())
|
||||
->method('setSubject')
|
||||
->with($subject);
|
||||
$this->mailer->expects(self::once())
|
||||
->method('setBody')
|
||||
->with($message);
|
||||
$this->mailer->expects(self::once())
|
||||
->method('parsePlainText')
|
||||
->with($message);
|
||||
$this->mailer->expects(self::once())
|
||||
->method('addTokens')
|
||||
->with($emailTokens);
|
||||
|
||||
$this->mailer->expects(self::once())
|
||||
->method('setLead');
|
||||
|
||||
$this->subscriber->onFormSubmitActionSendEmail($submissionEvent);
|
||||
}
|
||||
|
||||
public static function toCcBccProvider(): \Generator
|
||||
{
|
||||
yield ['to@email.email, to2@email.email', null, null];
|
||||
yield [null, 'cc@email.email, cc2@email.email', null];
|
||||
yield [null, null, 'bcc@email.email, bcc2@email.email'];
|
||||
}
|
||||
|
||||
public function testOnFormSubmitSendsIfCcAndBccWereSet(): void
|
||||
{
|
||||
$cc = 'cc@email.email, cc2@email.email';
|
||||
$bcc = 'bcc@email.email, bcc@email.email'; // same emails will produce single email address
|
||||
$subject = 'subject';
|
||||
$message = 'message';
|
||||
$tokensData = [
|
||||
'first_name' => 'Test's Name',
|
||||
'notes' => "A & B \n < dy >",
|
||||
];
|
||||
|
||||
$emailTokens = [
|
||||
'first_name' => "Test's Name",
|
||||
'notes' => "A & B <br />\n < dy >",
|
||||
];
|
||||
|
||||
$request = new Request();
|
||||
$submission = $this->getFormSubmission();
|
||||
$submissionEvent = new SubmissionEvent($submission, [], $request->server, $request);
|
||||
$action = $this->getFormSubmitActionSendEmail();
|
||||
$action->setProperties([
|
||||
'to' => null,
|
||||
'cc' => $cc,
|
||||
'bcc' => $bcc,
|
||||
'copy_lead' => false,
|
||||
'email_to_owner' => false,
|
||||
'subject' => $subject,
|
||||
'message' => $message,
|
||||
]);
|
||||
$submissionEvent->setTokens($tokensData)
|
||||
->setFields($this->getFormFields())
|
||||
->setAction($action);
|
||||
|
||||
$this->mailer->expects(self::once())
|
||||
->method('reset');
|
||||
$this->mailer->expects(self::once())
|
||||
->method('send');
|
||||
|
||||
$this->mailer->expects(self::never())
|
||||
->method('setTo');
|
||||
$this->mailer->expects(self::once())
|
||||
->method('setCc')
|
||||
->with(array_fill_keys(array_map('trim', explode(',', $cc)), null));
|
||||
$this->mailer->expects(self::once())
|
||||
->method('setBcc')
|
||||
->with(array_fill_keys(array_map('trim', explode(',', $bcc)), null));
|
||||
$this->mailer->expects(self::once())
|
||||
->method('setSubject')
|
||||
->with($subject);
|
||||
$this->mailer->expects(self::once())
|
||||
->method('setBody')
|
||||
->with($message);
|
||||
$this->mailer->expects(self::once())
|
||||
->method('parsePlainText')
|
||||
->with($message);
|
||||
$this->mailer->expects(self::once())
|
||||
->method('addTokens')
|
||||
->with($emailTokens);
|
||||
|
||||
$this->mailer->expects(self::once())
|
||||
->method('setLead');
|
||||
|
||||
$this->subscriber->onFormSubmitActionSendEmail($submissionEvent);
|
||||
}
|
||||
|
||||
public function testOnFormSubmitSendsIfCopyLeadEmailWasSet(): void
|
||||
{
|
||||
$subject = 'subject';
|
||||
$message = 'message';
|
||||
$leadEmail = 'lead@email.email';
|
||||
$tokensData = [
|
||||
'first_name' => 'Test's Name',
|
||||
'notes' => "A & B \n < dy >",
|
||||
];
|
||||
|
||||
$emailTokens = [
|
||||
'first_name' => "Test's Name",
|
||||
'notes' => "A & B <br />\n < dy >",
|
||||
];
|
||||
|
||||
$request = new Request();
|
||||
$submission = $this->getFormSubmission();
|
||||
$submission->getLead()->setEmail($leadEmail);
|
||||
$submissionEvent = new SubmissionEvent($submission, [], $request->server, $request);
|
||||
$action = $this->getFormSubmitActionSendEmail();
|
||||
$action->setProperties([
|
||||
'to' => null,
|
||||
'cc' => null,
|
||||
'bcc' => null,
|
||||
'copy_lead' => true,
|
||||
'subject' => $subject,
|
||||
'message' => $message,
|
||||
]);
|
||||
$submissionEvent->setTokens($tokensData)
|
||||
->setFields($this->getFormFields())
|
||||
->setAction($action);
|
||||
|
||||
$this->mailer->expects(self::once())
|
||||
->method('reset');
|
||||
$this->mailer->expects(self::once())
|
||||
->method('send');
|
||||
|
||||
$this->mailer->expects(self::once())
|
||||
->method('setTo')
|
||||
->with([$leadEmail => null]);
|
||||
$this->mailer->expects(self::once())
|
||||
->method('setSubject')
|
||||
->with($subject);
|
||||
$this->mailer->expects(self::once())
|
||||
->method('setBody')
|
||||
->with($message);
|
||||
$this->mailer->expects(self::once())
|
||||
->method('parsePlainText')
|
||||
->with($message);
|
||||
$this->mailer->expects(self::once())
|
||||
->method('addTokens')
|
||||
->with($emailTokens);
|
||||
|
||||
$this->mailer->expects(self::once())
|
||||
->method('setLead');
|
||||
|
||||
$this->subscriber->onFormSubmitActionSendEmail($submissionEvent);
|
||||
}
|
||||
|
||||
public function testOnFormSubmitSendsIfEmailToOwnerEmailWasSet(): void
|
||||
{
|
||||
$subject = 'subject';
|
||||
$message = 'message';
|
||||
$ownerEmail = 'lead@email.email';
|
||||
$tokensData = [
|
||||
'first_name' => 'Test's Name',
|
||||
'notes' => "A & B \n < dy >",
|
||||
];
|
||||
|
||||
$emailTokens = [
|
||||
'first_name' => "Test's Name",
|
||||
'notes' => "A & B <br />\n < dy >",
|
||||
];
|
||||
|
||||
$request = new Request();
|
||||
$submission = $this->getFormSubmission();
|
||||
$owner = new User();
|
||||
$owner->setEmail($ownerEmail);
|
||||
$submission->getLead()->setOwner($owner);
|
||||
$submissionEvent = new SubmissionEvent($submission, [], $request->server, $request);
|
||||
$action = $this->getFormSubmitActionSendEmail();
|
||||
$action->setProperties([
|
||||
'to' => null,
|
||||
'cc' => null,
|
||||
'bcc' => null,
|
||||
'copy_lead' => false,
|
||||
'email_to_owner' => true,
|
||||
'subject' => $subject,
|
||||
'message' => $message,
|
||||
]);
|
||||
$submissionEvent->setTokens($tokensData)
|
||||
->setFields($this->getFormFields())
|
||||
->setAction($action);
|
||||
|
||||
$this->mailer->expects(self::once())
|
||||
->method('reset');
|
||||
$this->mailer->expects(self::once())
|
||||
->method('send');
|
||||
|
||||
$this->mailer->expects(self::once())
|
||||
->method('setTo')
|
||||
->with([$ownerEmail => null]);
|
||||
$this->mailer->expects(self::once())
|
||||
->method('setSubject')
|
||||
->with($subject);
|
||||
$this->mailer->expects(self::once())
|
||||
->method('setBody')
|
||||
->with($message);
|
||||
$this->mailer->expects(self::once())
|
||||
->method('parsePlainText')
|
||||
->with($message);
|
||||
$this->mailer->expects(self::once())
|
||||
->method('addTokens')
|
||||
->with($emailTokens);
|
||||
|
||||
$this->mailer->expects(self::once())
|
||||
->method('setLead');
|
||||
|
||||
$this->subscriber->onFormSubmitActionSendEmail($submissionEvent);
|
||||
}
|
||||
|
||||
public function testOnFormSubmitSendsIfAllWereSet(): void
|
||||
{
|
||||
$to = 'to@email.email';
|
||||
$cc = 'cc@email.email, cc2@email.email';
|
||||
$bcc = 'bcc@email.email';
|
||||
$leadEmail = 'lead@email.email';
|
||||
$ownerEmail = 'owner@email.email';
|
||||
$subject = 'subject';
|
||||
$message = 'message';
|
||||
$tokensData = [
|
||||
'first_name' => 'Test's Name',
|
||||
'notes' => "A & B \n < dy >",
|
||||
];
|
||||
|
||||
$emailTokens = [
|
||||
'first_name' => "Test's Name",
|
||||
'notes' => "A & B <br />\n < dy >",
|
||||
];
|
||||
|
||||
$request = new Request();
|
||||
$submission = $this->getFormSubmission();
|
||||
$owner = new User();
|
||||
$owner->setEmail($ownerEmail);
|
||||
$submission->getLead()->setOwner($owner);
|
||||
$submission->getLead()->setEmail($leadEmail);
|
||||
$submissionEvent = new SubmissionEvent($submission, [], $request->server, $request);
|
||||
$action = $this->getFormSubmitActionSendEmail();
|
||||
$action->setProperties([
|
||||
'to' => $to,
|
||||
'cc' => $cc,
|
||||
'bcc' => $bcc,
|
||||
'copy_lead' => true,
|
||||
'email_to_owner' => true,
|
||||
'subject' => $subject,
|
||||
'message' => $message,
|
||||
]);
|
||||
$submissionEvent->setTokens($tokensData)
|
||||
->setFields($this->getFormFields())
|
||||
->setAction($action);
|
||||
|
||||
$this->mailer->expects(self::exactly(3))
|
||||
->method('reset');
|
||||
$this->mailer->expects(self::exactly(3))
|
||||
->method('send');
|
||||
$matcher = self::exactly(3);
|
||||
|
||||
$this->mailer->expects($matcher)
|
||||
->method('setTo')->willReturnCallback(function (...$parameters) use ($matcher, $to, $leadEmail, $ownerEmail) {
|
||||
if (1 === $matcher->numberOfInvocations()) {
|
||||
$this->assertSame([$to => null], $parameters[0]);
|
||||
}
|
||||
if (2 === $matcher->numberOfInvocations()) {
|
||||
$this->assertSame([$leadEmail => null], $parameters[0]);
|
||||
}
|
||||
if (3 === $matcher->numberOfInvocations()) {
|
||||
$this->assertSame([$ownerEmail => null], $parameters[0]);
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
$this->mailer->expects(self::once())
|
||||
->method('setCc')
|
||||
->with(array_fill_keys(array_map('trim', explode(',', $cc)), null));
|
||||
$this->mailer->expects(self::once())
|
||||
->method('setBcc')
|
||||
->with([$bcc => null]);
|
||||
$this->mailer->expects(self::exactly(3))
|
||||
->method('setSubject')
|
||||
->with($subject);
|
||||
$this->mailer->expects(self::exactly(3))
|
||||
->method('setBody')
|
||||
->with($message);
|
||||
$this->mailer->expects(self::exactly(3))
|
||||
->method('parsePlainText')
|
||||
->with($message);
|
||||
$this->mailer->expects(self::exactly(3))
|
||||
->method('addTokens')
|
||||
->with($emailTokens);
|
||||
|
||||
$this->mailer->expects(self::exactly(3))
|
||||
->method('setLead');
|
||||
|
||||
$this->subscriber->onFormSubmitActionSendEmail($submissionEvent);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string[][]
|
||||
*/
|
||||
private function getFormFields(): array
|
||||
{
|
||||
return [
|
||||
1 => [
|
||||
'id' => '1',
|
||||
'type' => 'text',
|
||||
'alias' => 'first_name',
|
||||
],
|
||||
2 => [
|
||||
'id' => '2',
|
||||
'type' => 'text',
|
||||
'alias' => 'notes',
|
||||
],
|
||||
3 => [
|
||||
'id' => '3',
|
||||
'type' => 'button',
|
||||
'alias' => 'submit',
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
private function getForm(): Form
|
||||
{
|
||||
$form = new Form();
|
||||
$form->setName('Test Form');
|
||||
$form->setAlias('test_form');
|
||||
$form->setFormType('standalone');
|
||||
|
||||
return $form;
|
||||
}
|
||||
|
||||
private function getFormRepostAction(): Action
|
||||
{
|
||||
$onSubmitActionConfig = [
|
||||
'post_url' => 'https://mautic.org',
|
||||
'failure_email' => '',
|
||||
'authorization_header' => '',
|
||||
];
|
||||
|
||||
$action = new Action();
|
||||
|
||||
return $action->setForm($this->getForm())
|
||||
->setType('form.repost')
|
||||
->setName('Test Action')
|
||||
->setProperties($onSubmitActionConfig);
|
||||
}
|
||||
|
||||
private function getFormSubmitActionSendEmail(): Action
|
||||
{
|
||||
$action = new Action();
|
||||
|
||||
return $action->setForm($this->getForm())
|
||||
->setType('form.email')
|
||||
->setName('Test Action');
|
||||
}
|
||||
|
||||
private function getFormSubmission(): Submission
|
||||
{
|
||||
$lead = new Lead();
|
||||
$lead->setFields($this->getFormFields());
|
||||
|
||||
$ipAddress = new IpAddress();
|
||||
$ipAddress->setIpAddress('127.0.0.1');
|
||||
|
||||
$submission = new Submission();
|
||||
|
||||
return $submission->setForm($this->getForm())
|
||||
->setLead($lead)
|
||||
->setIpAddress($ipAddress);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,215 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Mautic\FormBundle\Tests\EventListener;
|
||||
|
||||
use Mautic\LeadBundle\Entity\DoNotContact;
|
||||
use Mautic\LeadBundle\Entity\Lead;
|
||||
use Mautic\ReportBundle\Tests\Functional\AbstractReportSubscriberTestCase;
|
||||
use Symfony\Component\DomCrawler\Crawler;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
class ReportSubscriberFunctionalTest extends AbstractReportSubscriberTestCase
|
||||
{
|
||||
public function testLeadReportWithDncListColumn(): void
|
||||
{
|
||||
$leads[] = $this->createContact('test1@example.com');
|
||||
$leads[] = $this->createContact('test2@example.com');
|
||||
$leads[] = $this->createContact('test3@example.com');
|
||||
$this->em->flush();
|
||||
|
||||
$this->createDnc('email', $leads[0], DoNotContact::BOUNCED);
|
||||
$this->createDnc('email', $leads[1], DoNotContact::MANUAL);
|
||||
$this->createDnc('email', $leads[2], DoNotContact::UNSUBSCRIBED);
|
||||
$this->createDnc('sms', $leads[2], DoNotContact::MANUAL);
|
||||
$this->em->flush();
|
||||
|
||||
$formId = $this->createFormThroughApi();
|
||||
$this->submitForm($formId, [
|
||||
'email' => 'test1@example.com',
|
||||
'firstname' => 'test1',
|
||||
]);
|
||||
$this->submitForm($formId, [
|
||||
'email' => 'test2@example.com',
|
||||
'firstname' => 'test2',
|
||||
]);
|
||||
|
||||
$report = $this->createReport(
|
||||
source: 'form.submissions',
|
||||
columns: [
|
||||
'l.id',
|
||||
'l.firstname',
|
||||
'dnc_preferences',
|
||||
],
|
||||
filters: [
|
||||
[
|
||||
'column' => 'f.id',
|
||||
'glue' => 'and',
|
||||
'dynamic' => null,
|
||||
'condition' => 'eq',
|
||||
'value' => $formId,
|
||||
],
|
||||
],
|
||||
order: [['column' => 'l.id', 'direction' => 'ASC']]
|
||||
);
|
||||
|
||||
$expectedReport = [
|
||||
// id, firstname, dnc_preferences
|
||||
[(string) $leads[0]->getId(), 'test1', 'DNC Bounced: Email'],
|
||||
[(string) $leads[1]->getId(), 'test2', 'DNC Manually Unsubscribed: Email'],
|
||||
];
|
||||
$this->verifyReport($report->getId(), $expectedReport);
|
||||
$this->verifyApiReport($report->getId(), $expectedReport);
|
||||
}
|
||||
|
||||
public function testLeadReportWithDncListFilterIn(): void
|
||||
{
|
||||
$leads[] = $this->createContact('test1@example.com');
|
||||
$leads[] = $this->createContact('test2@example.com');
|
||||
$leads[] = $this->createContact('test3@example.com');
|
||||
$this->em->flush();
|
||||
|
||||
$this->createDnc('email', $leads[0], DoNotContact::BOUNCED);
|
||||
$this->createDnc('email', $leads[1], DoNotContact::MANUAL);
|
||||
$this->createDnc('email', $leads[2], DoNotContact::UNSUBSCRIBED);
|
||||
$this->createDnc('sms', $leads[2], DoNotContact::MANUAL);
|
||||
$this->em->flush();
|
||||
|
||||
$formId = $this->createFormThroughApi();
|
||||
$this->submitForm($formId, [
|
||||
'email' => 'test1@example.com',
|
||||
'firstname' => 'test1',
|
||||
]);
|
||||
$this->submitForm($formId, [
|
||||
'email' => 'test2@example.com',
|
||||
'firstname' => 'test2',
|
||||
]);
|
||||
$this->submitForm($formId, [
|
||||
'email' => 'test3@example.com',
|
||||
'firstname' => 'test3',
|
||||
]);
|
||||
|
||||
$report = $this->createReport(
|
||||
source: 'form.submissions',
|
||||
columns: [
|
||||
'l.id',
|
||||
'l.firstname',
|
||||
'dnc_preferences',
|
||||
],
|
||||
filters: [
|
||||
[
|
||||
'column' => 'f.id',
|
||||
'glue' => 'and',
|
||||
'dynamic' => null,
|
||||
'condition' => 'eq',
|
||||
'value' => $formId,
|
||||
],
|
||||
[
|
||||
'column' => 'dnc_preferences',
|
||||
'glue' => 'and',
|
||||
'dynamic' => null,
|
||||
'condition' => 'in',
|
||||
'value' => [
|
||||
'email:'.DoNotContact::UNSUBSCRIBED,
|
||||
'email:'.DoNotContact::BOUNCED,
|
||||
],
|
||||
],
|
||||
],
|
||||
order: [['column' => 'l.id', 'direction' => 'ASC']]
|
||||
);
|
||||
|
||||
$expectedReport = [
|
||||
// id, firstname, dnc_preferences
|
||||
[(string) $leads[0]->getId(), 'test1', 'DNC Bounced: Email'],
|
||||
[(string) $leads[2]->getId(), 'test3', 'DNC Manually Unsubscribed: Text Message, DNC Unsubscribed: Email'],
|
||||
];
|
||||
$this->verifyReport($report->getId(), $expectedReport);
|
||||
$this->verifyApiReport($report->getId(), $expectedReport);
|
||||
}
|
||||
|
||||
private function createFormThroughApi(): int
|
||||
{
|
||||
$formPayload = [
|
||||
'name' => 'Submission test form',
|
||||
'description' => 'Form created via submission test',
|
||||
'formType' => 'standalone',
|
||||
'isPublished' => true,
|
||||
'fields' => [
|
||||
[
|
||||
'label' => 'Email',
|
||||
'type' => 'email',
|
||||
'alias' => 'email',
|
||||
'leadField' => 'email',
|
||||
'mappedField' => 'email',
|
||||
'mappedObject' => 'contact',
|
||||
],
|
||||
[
|
||||
'label' => 'Firstname',
|
||||
'type' => 'text',
|
||||
'alias' => 'firstname',
|
||||
'leadField' => 'firstname',
|
||||
'mappedField' => 'firstname',
|
||||
'mappedObject' => 'contact',
|
||||
],
|
||||
[
|
||||
'label' => 'Submit',
|
||||
'type' => 'button',
|
||||
],
|
||||
],
|
||||
'postAction' => 'return',
|
||||
];
|
||||
|
||||
// Create the form
|
||||
$this->client->request(Request::METHOD_POST, '/api/forms/new', $formPayload);
|
||||
$clientResponse = $this->client->getResponse();
|
||||
$response = json_decode($clientResponse->getContent(), true);
|
||||
$formId = (int) $response['form']['id'];
|
||||
|
||||
$this->assertSame(Response::HTTP_CREATED, $clientResponse->getStatusCode(), $clientResponse->getContent());
|
||||
|
||||
return $formId;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, mixed> $submissionData
|
||||
*/
|
||||
private function submitForm(int $formId, array $submissionData): Crawler
|
||||
{
|
||||
// Submit the form
|
||||
$crawler = $this->client->request(Request::METHOD_GET, "/form/{$formId}");
|
||||
$formCrawler = $crawler->filter('form[id=mauticform_submissiontestform]');
|
||||
$this->assertCount(1, $formCrawler);
|
||||
$form = $formCrawler->form();
|
||||
|
||||
$formData = [];
|
||||
foreach ($submissionData as $key => $value) {
|
||||
$formData["mauticform[{$key}]"] = $value;
|
||||
}
|
||||
$form->setValues($formData);
|
||||
|
||||
return $this->client->submit($form);
|
||||
}
|
||||
|
||||
public function createDnc(string $channel, Lead $contact, int $reason): DoNotContact
|
||||
{
|
||||
$dnc = new DoNotContact();
|
||||
$dnc->setChannel($channel);
|
||||
$dnc->setLead($contact);
|
||||
$dnc->setReason($reason);
|
||||
$dnc->setDateAdded(new \DateTime());
|
||||
$this->em->persist($dnc);
|
||||
|
||||
return $dnc;
|
||||
}
|
||||
|
||||
private function createContact(string $email): Lead
|
||||
{
|
||||
$lead = new Lead();
|
||||
$lead->setEmail($email);
|
||||
$this->em->persist($lead);
|
||||
|
||||
return $lead;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,396 @@
|
||||
<?php
|
||||
|
||||
namespace Mautic\FormBundle\Tests\EventListener;
|
||||
|
||||
use Doctrine\DBAL\Query\QueryBuilder;
|
||||
use Mautic\ChannelBundle\Helper\ChannelListHelper;
|
||||
use Mautic\CoreBundle\Helper\Chart\ChartQuery;
|
||||
use Mautic\CoreBundle\Helper\CoreParametersHelper;
|
||||
use Mautic\CoreBundle\Test\AbstractMauticTestCase;
|
||||
use Mautic\CoreBundle\Translation\Translator;
|
||||
use Mautic\FormBundle\Entity\Field;
|
||||
use Mautic\FormBundle\Entity\Form;
|
||||
use Mautic\FormBundle\Entity\FormRepository;
|
||||
use Mautic\FormBundle\Entity\SubmissionRepository;
|
||||
use Mautic\FormBundle\EventListener\ReportSubscriber;
|
||||
use Mautic\FormBundle\Model\FormModel;
|
||||
use Mautic\LeadBundle\Model\CompanyReportData;
|
||||
use Mautic\LeadBundle\Report\DncReportService;
|
||||
use Mautic\ReportBundle\Event\ReportBuilderEvent;
|
||||
use Mautic\ReportBundle\Event\ReportGeneratorEvent;
|
||||
use Mautic\ReportBundle\Event\ReportGraphEvent;
|
||||
use Mautic\ReportBundle\Helper\ReportHelper;
|
||||
use PHPUnit\Framework\Assert;
|
||||
use PHPUnit\Framework\MockObject\MockObject;
|
||||
use Symfony\Component\EventDispatcher\EventDispatcher;
|
||||
use Symfony\Contracts\Translation\TranslatorInterface;
|
||||
|
||||
class ReportSubscriberTest extends AbstractMauticTestCase
|
||||
{
|
||||
/**
|
||||
* @var CompanyReportData|MockObject
|
||||
*/
|
||||
private MockObject $companyReportData;
|
||||
|
||||
/**
|
||||
* @var SubmissionRepository|MockObject
|
||||
*/
|
||||
private MockObject $submissionRepository;
|
||||
|
||||
/**
|
||||
* @var FormModel|MockObject
|
||||
*/
|
||||
private MockObject $formModel;
|
||||
|
||||
/**
|
||||
* @var FormRepository|MockObject
|
||||
*/
|
||||
private MockObject $formRepository;
|
||||
|
||||
private ReportHelper $reportHelper;
|
||||
|
||||
/**
|
||||
* @var CoreParametersHelper|MockObject
|
||||
*/
|
||||
private MockObject $coreParametersHelper;
|
||||
|
||||
/**
|
||||
* @var TranslatorInterface|MockObject
|
||||
*/
|
||||
private MockObject $translator;
|
||||
|
||||
private ReportSubscriber $subscriber;
|
||||
|
||||
private MockObject&DncReportService $dncReportService;
|
||||
|
||||
public function setUp(): void
|
||||
{
|
||||
$this->configParams['form_results_data_sources'] = true;
|
||||
|
||||
parent::setUp();
|
||||
|
||||
$this->companyReportData = $this->createMock(CompanyReportData::class);
|
||||
$this->submissionRepository = $this->createMock(SubmissionRepository::class);
|
||||
$this->formModel = $this->createMock(FormModel::class);
|
||||
$this->formRepository = $this->createMock(FormRepository::class);
|
||||
$this->reportHelper = new ReportHelper($this->createMock(EventDispatcher::class));
|
||||
$this->coreParametersHelper = $this->createMock(CoreParametersHelper::class);
|
||||
$this->translator = $this->createMock(TranslatorInterface::class);
|
||||
$this->dncReportService = $this->createMock(DncReportService::class);
|
||||
$this->subscriber = new ReportSubscriber(
|
||||
$this->companyReportData,
|
||||
$this->submissionRepository,
|
||||
$this->formModel,
|
||||
$this->reportHelper,
|
||||
$this->coreParametersHelper,
|
||||
$this->translator,
|
||||
$this->dncReportService
|
||||
);
|
||||
}
|
||||
|
||||
public function testOnReportBuilderAddsFormAndFormSubmissionReports(): void
|
||||
{
|
||||
$mockEvent = $this->getMockBuilder(ReportBuilderEvent::class)
|
||||
->disableOriginalConstructor()
|
||||
->onlyMethods([
|
||||
'checkContext',
|
||||
'addGraph',
|
||||
'getStandardColumns',
|
||||
'getCategoryColumns',
|
||||
'getCampaignByChannelColumns',
|
||||
'getLeadColumns',
|
||||
'getIpColumn',
|
||||
'addTable',
|
||||
])
|
||||
->getMock();
|
||||
|
||||
$mockEvent->expects($this->once())
|
||||
->method('getStandardColumns')
|
||||
->willReturn([]);
|
||||
|
||||
$mockEvent->expects($this->once())
|
||||
->method('getCategoryColumns')
|
||||
->willReturn([]);
|
||||
|
||||
$mockEvent->expects($this->once())
|
||||
->method('getCampaignByChannelColumns')
|
||||
->willReturn([]);
|
||||
|
||||
$mockEvent->expects($this->once())
|
||||
->method('getLeadColumns')
|
||||
->willReturn([]);
|
||||
|
||||
$mockEvent->expects($this->once())
|
||||
->method('getIpColumn')
|
||||
->willReturn([]);
|
||||
|
||||
$mockEvent->expects($this->exactly(3))
|
||||
->method('checkContext')
|
||||
->willReturnOnConsecutiveCalls(true, true, false);
|
||||
|
||||
$setTables = [];
|
||||
$setGraphs = [];
|
||||
|
||||
$mockEvent->expects($this->exactly(2))
|
||||
->method('addTable')
|
||||
->willReturnCallback(function () use (&$setTables): void {
|
||||
$args = func_get_args();
|
||||
|
||||
$setTables[] = $args;
|
||||
});
|
||||
|
||||
$mockEvent->expects($this->exactly(3))
|
||||
->method('addGraph')
|
||||
->willReturnCallback(function () use (&$setGraphs): void {
|
||||
$args = func_get_args();
|
||||
|
||||
$setGraphs[] = $args;
|
||||
});
|
||||
|
||||
$this->companyReportData->expects($this->once())
|
||||
->method('getCompanyData')
|
||||
->with()
|
||||
->willReturn([]);
|
||||
|
||||
$this->subscriber->onReportBuilder($mockEvent);
|
||||
|
||||
$this->assertCount(2, $setTables);
|
||||
$this->assertCount(3, $setGraphs);
|
||||
}
|
||||
|
||||
public function testOnReportBuilderWithWrongContext(): void
|
||||
{
|
||||
$reportBuilderEvent = new ReportBuilderEvent(
|
||||
$this->translator,
|
||||
$this->createMock(ChannelListHelper::class),
|
||||
'test',
|
||||
[],
|
||||
$this->reportHelper,
|
||||
''
|
||||
);
|
||||
|
||||
$this->subscriber->onReportBuilder($reportBuilderEvent);
|
||||
|
||||
Assert::assertCount(0, $reportBuilderEvent->getTables());
|
||||
}
|
||||
|
||||
public function testOnReportBuilderAddsFormAndFormResultReports(): void
|
||||
{
|
||||
$reportBuilderEvent = new ReportBuilderEvent(
|
||||
$this->translator,
|
||||
$this->createMock(ChannelListHelper::class),
|
||||
ReportSubscriber::CONTEXT_FORM_RESULT,
|
||||
[],
|
||||
$this->reportHelper,
|
||||
''
|
||||
);
|
||||
|
||||
$field = new Field();
|
||||
$field->setAlias('email');
|
||||
$field->setType('string');
|
||||
$field->setLabel('Email');
|
||||
$field->setMappedObject('contact');
|
||||
$field->setMappedField('email');
|
||||
|
||||
$form = new Form();
|
||||
$form->addField('email', $field);
|
||||
$field->setForm($form);
|
||||
|
||||
$this->formModel->expects($this->once())
|
||||
->method('getRepository')
|
||||
->willReturn($this->formRepository);
|
||||
|
||||
$this->formModel->expects($this->once())
|
||||
->method('getCustomComponents')
|
||||
->willReturn(['viewOnlyFields' => ['button', 'captcha', 'freetext', 'freehtml', 'pagebreak', 'plugin.loginSocial']]);
|
||||
|
||||
$this->formRepository->expects($this->once())
|
||||
->method('getEntities')
|
||||
->willReturn([[$form, 1]]);
|
||||
|
||||
$this->formRepository->expects($this->once())
|
||||
->method('getResultsTableName')
|
||||
->willReturn('test');
|
||||
|
||||
$this->subscriber->onReportBuilder($reportBuilderEvent);
|
||||
|
||||
$tables = $reportBuilderEvent->getTables();
|
||||
|
||||
Assert::assertCount(2, $tables);
|
||||
Assert::assertArrayHasKey('form.results.test', $tables);
|
||||
Assert::assertCount(3, $tables['form.results.test']['columns']);
|
||||
}
|
||||
|
||||
public function testOnReportGenerateFormsContext(): void
|
||||
{
|
||||
$mockQueryBuilder = $this->createMock(QueryBuilder::class);
|
||||
$mockEvent = $this->getMockBuilder(ReportGeneratorEvent::class)
|
||||
->disableOriginalConstructor()
|
||||
->onlyMethods([
|
||||
'getContext',
|
||||
'getQueryBuilder',
|
||||
'addCategoryLeftJoin',
|
||||
'setQueryBuilder',
|
||||
])
|
||||
->getMock();
|
||||
|
||||
$mockQueryBuilder->expects($this->once())
|
||||
->method('from')
|
||||
->willReturn($mockQueryBuilder);
|
||||
|
||||
$mockEvent->expects($this->once())
|
||||
->method('getQueryBuilder')
|
||||
->willReturn($mockQueryBuilder);
|
||||
|
||||
$mockEvent->expects($this->once())
|
||||
->method('getContext')
|
||||
->willReturn('forms');
|
||||
|
||||
$this->subscriber->onReportGenerate($mockEvent);
|
||||
}
|
||||
|
||||
public function testOnReportGenerateFormSubmissionContext(): void
|
||||
{
|
||||
$mockQueryBuilder = $this->createMock(QueryBuilder::class);
|
||||
$mockEvent = $this->getMockBuilder(ReportGeneratorEvent::class)
|
||||
->disableOriginalConstructor()
|
||||
->onlyMethods([
|
||||
'getContext',
|
||||
'getQueryBuilder',
|
||||
'addCategoryLeftJoin',
|
||||
'addIpAddressLeftJoin',
|
||||
'addLeadLeftJoin',
|
||||
'addCampaignByChannelJoin',
|
||||
'applyDateFilters',
|
||||
'setQueryBuilder',
|
||||
])
|
||||
->getMock();
|
||||
|
||||
$mockQueryBuilder->expects($this->once())
|
||||
->method('from')
|
||||
->willReturn($mockQueryBuilder);
|
||||
|
||||
$mockQueryBuilder->expects($this->exactly(2))
|
||||
->method('leftJoin')
|
||||
->willReturn($mockQueryBuilder);
|
||||
|
||||
$mockEvent->expects($this->once())
|
||||
->method('getQueryBuilder')
|
||||
->willReturn($mockQueryBuilder);
|
||||
|
||||
$mockEvent->expects($this->once())
|
||||
->method('getContext')
|
||||
->willReturn('form.submissions');
|
||||
|
||||
$this->subscriber->onReportGenerate($mockEvent);
|
||||
}
|
||||
|
||||
public function testOnReportGenerateFormResultsContext(): void
|
||||
{
|
||||
$mockQueryBuilder = $this->createMock(QueryBuilder::class);
|
||||
$mockEvent = $this->getMockBuilder(ReportGeneratorEvent::class)
|
||||
->disableOriginalConstructor()
|
||||
->onlyMethods([
|
||||
'getContext',
|
||||
'getQueryBuilder',
|
||||
'addLeadLeftJoin',
|
||||
'setQueryBuilder',
|
||||
])
|
||||
->getMock();
|
||||
|
||||
$mockQueryBuilder->expects($this->once())
|
||||
->method('from')
|
||||
->willReturn($mockQueryBuilder);
|
||||
|
||||
$mockQueryBuilder->expects($this->once())
|
||||
->method('leftJoin')
|
||||
->willReturn($mockQueryBuilder);
|
||||
|
||||
$mockEvent->expects($this->once())
|
||||
->method('getQueryBuilder')
|
||||
->willReturn($mockQueryBuilder);
|
||||
|
||||
$mockEvent->expects($this->once())
|
||||
->method('getContext')
|
||||
->willReturn('form.results');
|
||||
|
||||
$this->subscriber->onReportGenerate($mockEvent);
|
||||
}
|
||||
|
||||
public function testOnReportGraphGenerateBadContextWillReturn(): void
|
||||
{
|
||||
$mockEvent = $this->createMock(ReportGraphEvent::class);
|
||||
|
||||
$mockEvent->expects($this->once())
|
||||
->method('checkContext')
|
||||
->willReturn(false);
|
||||
|
||||
$mockEvent->expects($this->never())
|
||||
->method('getRequestedGraphs');
|
||||
|
||||
$this->subscriber->onReportGraphGenerate($mockEvent);
|
||||
}
|
||||
|
||||
public function testOnReportGraphGenerate(): void
|
||||
{
|
||||
$mockEvent = $this->createMock(ReportGraphEvent::class);
|
||||
$mockTrans = $this->createMock(Translator::class);
|
||||
$mockQueryBuilder = $this->createMock(QueryBuilder::class);
|
||||
$mockChartQuery = $this->createMock(ChartQuery::class);
|
||||
|
||||
$mockTrans->expects($this->any())
|
||||
->method('trans')
|
||||
->willReturnArgument(0);
|
||||
|
||||
$mockEvent->expects($this->once())
|
||||
->method('getQueryBuilder')
|
||||
->willReturn($mockQueryBuilder);
|
||||
|
||||
$mockChartQuery->expects($this->any())
|
||||
->method('loadAndBuildTimeData')
|
||||
->willReturn(['a', 'b', 'c']);
|
||||
|
||||
$mockChartQuery->expects($this->any())
|
||||
->method('fetchCount')
|
||||
->willReturn(2);
|
||||
|
||||
$mockChartQuery->expects($this->any())
|
||||
->method('fetchCountDateDiff')
|
||||
->willReturn(2);
|
||||
|
||||
$graphOptions = [
|
||||
'chartQuery' => $mockChartQuery,
|
||||
'translator' => $mockTrans,
|
||||
'dateFrom' => new \DateTime(),
|
||||
'dateTo' => new \DateTime(),
|
||||
];
|
||||
|
||||
$mockEvent->expects($this->once())
|
||||
->method('checkContext')
|
||||
->willReturn(true);
|
||||
|
||||
$mockEvent->expects($this->any())
|
||||
->method('getOptions')
|
||||
->willReturn($graphOptions);
|
||||
|
||||
$mockEvent->expects($this->once())
|
||||
->method('getRequestedGraphs')
|
||||
->willReturn(
|
||||
[
|
||||
'mautic.form.graph.line.submissions',
|
||||
'mautic.form.table.top.referrers',
|
||||
'mautic.form.table.most.submitted',
|
||||
]
|
||||
);
|
||||
|
||||
$this->submissionRepository->expects($this->once())
|
||||
->method('getTopReferrers')
|
||||
->willReturn(['a', 'b', 'c']);
|
||||
|
||||
$this->submissionRepository->expects($this->once())
|
||||
->method('getMostSubmitted')
|
||||
->willReturn(['a', 'b', 'c']);
|
||||
|
||||
$this->subscriber->onReportGraphGenerate($mockEvent);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,128 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Mautic\FormBundle\Tests\Form\Type;
|
||||
|
||||
use Mautic\FormBundle\Collection\FieldCollection;
|
||||
use Mautic\FormBundle\Collection\ObjectCollection;
|
||||
use Mautic\FormBundle\Collector\AlreadyMappedFieldCollectorInterface;
|
||||
use Mautic\FormBundle\Collector\FieldCollectorInterface;
|
||||
use Mautic\FormBundle\Collector\ObjectCollectorInterface;
|
||||
use Mautic\FormBundle\Crate\FieldCrate;
|
||||
use Mautic\FormBundle\Crate\ObjectCrate;
|
||||
use Mautic\FormBundle\Form\Type\FieldType;
|
||||
use Symfony\Component\Form\Extension\Validator\ValidatorExtension;
|
||||
use Symfony\Component\Form\FormExtensionInterface;
|
||||
use Symfony\Component\Form\PreloadedExtension;
|
||||
use Symfony\Component\Form\Test\TypeTestCase;
|
||||
use Symfony\Component\Validator\Validation;
|
||||
use Symfony\Contracts\Translation\TranslatorInterface;
|
||||
|
||||
class FieldTypeTest extends TypeTestCase
|
||||
{
|
||||
private TranslatorInterface $translator;
|
||||
private ObjectCollectorInterface $objectCollector;
|
||||
private FieldCollectorInterface $fieldCollector;
|
||||
private AlreadyMappedFieldCollectorInterface $mappedFieldCollector;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
$this->translator = $this->createMock(TranslatorInterface::class);
|
||||
$this->objectCollector = $this->createMock(ObjectCollectorInterface::class);
|
||||
$this->fieldCollector = $this->createMock(FieldCollectorInterface::class);
|
||||
$this->mappedFieldCollector = $this->createMock(AlreadyMappedFieldCollectorInterface::class);
|
||||
|
||||
// Set up expected behavior for objectCollector
|
||||
$objectCollection = new ObjectCollection();
|
||||
$objectCollection->append(new ObjectCrate('contact', 'Contact'));
|
||||
$this->objectCollector->method('getObjects')->willReturn($objectCollection);
|
||||
|
||||
// Set up expected behavior for fieldCollector
|
||||
$fieldCollection = new FieldCollection([
|
||||
new FieldCrate('1', 'email', 'text', []),
|
||||
]);
|
||||
$this->fieldCollector->method('getFields')->willReturn($fieldCollection);
|
||||
|
||||
parent::setUp();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<FormExtensionInterface>
|
||||
*/
|
||||
protected function getExtensions(): array
|
||||
{
|
||||
return [
|
||||
new ValidatorExtension(Validation::createValidator()),
|
||||
new PreloadedExtension([
|
||||
FieldType::class => new FieldType(
|
||||
$this->translator,
|
||||
$this->objectCollector,
|
||||
$this->fieldCollector,
|
||||
$this->mappedFieldCollector
|
||||
),
|
||||
], []),
|
||||
];
|
||||
}
|
||||
|
||||
public function testFieldWidthDefaultValue(): void
|
||||
{
|
||||
$formData = [
|
||||
'type' => 'text',
|
||||
'addFieldWidth' => true,
|
||||
'formId' => 1,
|
||||
];
|
||||
|
||||
$form = $this->factory->create(FieldType::class, $formData);
|
||||
$view = $form->createView();
|
||||
|
||||
$this->assertArrayHasKey('fieldWidth', $view);
|
||||
$fieldWidth = $form->get('fieldWidth');
|
||||
$this->assertEquals('100%', $fieldWidth->getData());
|
||||
}
|
||||
|
||||
public function testFieldWidthChoices(): void
|
||||
{
|
||||
$formData = [
|
||||
'type' => 'text',
|
||||
'addFieldWidth' => true,
|
||||
'formId' => 1,
|
||||
];
|
||||
|
||||
$form = $this->factory->create(FieldType::class, $formData);
|
||||
$view = $form->createView();
|
||||
|
||||
$expectedChoices = [
|
||||
'100%' => 'mautic.form.field.form.field_width.one_hundred',
|
||||
'75%' => 'mautic.form.field.form.field_width.seventy_five',
|
||||
'66.66%' => 'mautic.form.field.form.field_width.sixty_six',
|
||||
'50%' => 'mautic.form.field.form.field_width.fifty',
|
||||
'33.33%' => 'mautic.form.field.form.field_width.thirty_three',
|
||||
'25%' => 'mautic.form.field.form.field_width.twenty_five',
|
||||
];
|
||||
|
||||
$this->assertArrayHasKey('fieldWidth', $view);
|
||||
$fieldWidth = $view->children['fieldWidth'];
|
||||
$choices = $fieldWidth->vars['choices'];
|
||||
|
||||
$this->assertCount(count($expectedChoices), $choices);
|
||||
foreach ($choices as $choice) {
|
||||
$this->assertArrayHasKey($choice->value, $expectedChoices);
|
||||
$this->assertEquals($expectedChoices[$choice->value], $choice->label);
|
||||
}
|
||||
}
|
||||
|
||||
public function testFieldWidthCustomValue(): void
|
||||
{
|
||||
$formData = [
|
||||
'type' => 'text',
|
||||
'addFieldWidth' => true,
|
||||
'fieldWidth' => '75%',
|
||||
'formId' => 1,
|
||||
];
|
||||
|
||||
$form = $this->factory->create(FieldType::class, $formData);
|
||||
$fieldWidth = $form->get('fieldWidth');
|
||||
$this->assertEquals('75%', $fieldWidth->getData());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,153 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Mautic\FormBundle\Tests\Form\Type;
|
||||
|
||||
use Mautic\CoreBundle\Form\Type\YesNoButtonGroupType;
|
||||
use Mautic\FormBundle\Form\Type\FormFieldConditionType;
|
||||
use Mautic\FormBundle\Helper\PropertiesAccessor;
|
||||
use Mautic\FormBundle\Model\FieldModel;
|
||||
use PHPUnit\Framework\MockObject\MockObject;
|
||||
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
|
||||
final class FormFieldConditionTypeTest extends \PHPUnit\Framework\TestCase
|
||||
{
|
||||
/**
|
||||
* @var MockObject|FieldModel
|
||||
*/
|
||||
private MockObject $fieldModel;
|
||||
|
||||
/**
|
||||
* @var MockObject|PropertiesAccessor
|
||||
*/
|
||||
private MockObject $propertiesAccessor;
|
||||
|
||||
/**
|
||||
* @var MockObject&FormBuilderInterface<string|FormBuilderInterface>
|
||||
*/
|
||||
private MockObject $formBuilder;
|
||||
|
||||
private FormFieldConditionType $form;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
$this->fieldModel = $this->createMock(FieldModel::class);
|
||||
$this->propertiesAccessor = $this->createMock(PropertiesAccessor::class);
|
||||
$this->formBuilder = $this->createMock(FormBuilderInterface::class);
|
||||
$this->form = new FormFieldConditionType(
|
||||
$this->fieldModel,
|
||||
$this->propertiesAccessor
|
||||
);
|
||||
}
|
||||
|
||||
public function testBuildFormIfParentIsEmpty(): void
|
||||
{
|
||||
$options = [];
|
||||
|
||||
$this->fieldModel->expects($this->never())
|
||||
->method('getSessionFields');
|
||||
|
||||
$this->propertiesAccessor->expects($this->never())
|
||||
->method('getChoices');
|
||||
$matcher = $this->exactly(3);
|
||||
|
||||
$this->formBuilder->expects($matcher)
|
||||
->method('add')->willReturnCallback(function (...$parameters) use ($matcher) {
|
||||
if (1 === $matcher->numberOfInvocations()) {
|
||||
$this->assertSame('values', $parameters[0]);
|
||||
$this->assertSame(ChoiceType::class, $parameters[1]);
|
||||
$this->assertSame([
|
||||
'choices' => [],
|
||||
'multiple' => true,
|
||||
'label' => false,
|
||||
'attr' => [
|
||||
'class' => 'form-control',
|
||||
'data-show-on' => '{"formfield_conditions_any_0": "checked","formfield_conditions_expr": "notIn"}',
|
||||
],
|
||||
'required' => false,
|
||||
], $parameters[2]);
|
||||
}
|
||||
if (2 === $matcher->numberOfInvocations()) {
|
||||
$this->assertSame('any', $parameters[0]);
|
||||
$this->assertSame(YesNoButtonGroupType::class, $parameters[1]);
|
||||
$this->assertSame([
|
||||
'label' => 'mautic.form.field.form.condition.any_value',
|
||||
'attr' => [
|
||||
'data-show-on' => '{"formfield_conditions_expr": "in"}',
|
||||
],
|
||||
'data' => false,
|
||||
], $parameters[2]);
|
||||
}
|
||||
if (3 === $matcher->numberOfInvocations()) {
|
||||
$this->assertSame('expr', $parameters[0]);
|
||||
$this->assertSame(ChoiceType::class, $parameters[1]);
|
||||
$this->assertSame([
|
||||
'choices' => [
|
||||
'mautic.core.operator.in' => 'in',
|
||||
'mautic.core.operator.notin' => 'notIn',
|
||||
],
|
||||
'label' => false,
|
||||
'placeholder' => false,
|
||||
'attr' => [
|
||||
'class' => 'form-control',
|
||||
],
|
||||
'required' => false,
|
||||
], $parameters[2]);
|
||||
}
|
||||
|
||||
return $this->formBuilder;
|
||||
});
|
||||
|
||||
$this->form->buildForm($this->formBuilder, $options);
|
||||
}
|
||||
|
||||
public function testBuildFormIfParentExists(): void
|
||||
{
|
||||
$options = [
|
||||
'parent' => 'parent_field_id',
|
||||
'formId' => 'form_id',
|
||||
];
|
||||
|
||||
$this->fieldModel->expects($this->once())
|
||||
->method('getSessionFields')
|
||||
->with('form_id')
|
||||
->willReturn(['parent_field_id' => ['some_field_props_here']]);
|
||||
|
||||
$this->propertiesAccessor->expects($this->once())
|
||||
->method('getProperties')
|
||||
->with(['some_field_props_here'])
|
||||
->willReturn(['some_choice_here' => 'Some choice here']);
|
||||
|
||||
$this->propertiesAccessor->expects($this->once())
|
||||
->method('getChoices')
|
||||
->with(['some_choice_here' => 'Some choice here'])
|
||||
->willReturn(['some_choice_here' => 'Some choice here']);
|
||||
$matcher = $this->any();
|
||||
|
||||
$this->formBuilder->expects($matcher)->method('add')
|
||||
->willReturnCallback(
|
||||
function (...$parameters) use ($matcher) {
|
||||
if (1 === $matcher->numberOfInvocations()) {
|
||||
$this->assertSame('values', $parameters[0]);
|
||||
$this->assertSame(ChoiceType::class, $parameters[1]);
|
||||
$this->assertSame([
|
||||
'choices' => ['some_choice_here' => 'Some choice here'],
|
||||
'multiple' => true,
|
||||
'label' => false,
|
||||
'attr' => [
|
||||
'class' => 'form-control',
|
||||
'data-show-on' => '{"formfield_conditions_any_0": "checked","formfield_conditions_expr": "notIn"}',
|
||||
],
|
||||
'required' => false,
|
||||
], $parameters[2]);
|
||||
}
|
||||
|
||||
return $this->formBuilder;
|
||||
}
|
||||
);
|
||||
|
||||
$this->form->buildForm($this->formBuilder, $options);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Mautic\FormBundle\Tests\Form\Type;
|
||||
|
||||
use Mautic\FormBundle\Form\Type\FormFieldNumberType;
|
||||
use PHPUnit\Framework\MockObject\MockObject;
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\IntegerType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\TextType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Symfony\Component\Form\Test\TypeTestCase;
|
||||
|
||||
final class FormFieldNumberTypeTest extends TypeTestCase
|
||||
{
|
||||
/**
|
||||
* @var MockObject|FormBuilderInterface
|
||||
*/
|
||||
private $formBuilder;
|
||||
|
||||
/**
|
||||
* @var AbstractType<FormFieldNumberType>
|
||||
*/
|
||||
private $form;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
$this->formBuilder = $this->createMock(FormBuilderInterface::class);
|
||||
$this->form = new FormFieldNumberType();
|
||||
}
|
||||
|
||||
public function testBuildFormIfParentIsEmpty(): void
|
||||
{
|
||||
$options = [
|
||||
'data' => [
|
||||
'precision' => 0,
|
||||
],
|
||||
];
|
||||
$matcher = $this->exactly(2);
|
||||
|
||||
$this->formBuilder->expects($matcher)
|
||||
->method('add')->willReturnCallback(function (...$parameters) use ($matcher) {
|
||||
if (1 === $matcher->numberOfInvocations()) {
|
||||
$this->assertSame('placeholder', $parameters[0]);
|
||||
$this->assertSame(TextType::class, $parameters[1]);
|
||||
$this->assertSame([
|
||||
'label' => 'mautic.form.field.form.property_placeholder',
|
||||
'label_attr' => ['class' => 'control-label'],
|
||||
'attr' => ['class' => 'form-control'],
|
||||
'required' => false,
|
||||
], $parameters[2]);
|
||||
}
|
||||
if (2 === $matcher->numberOfInvocations()) {
|
||||
$this->assertSame('precision', $parameters[0]);
|
||||
$this->assertSame(IntegerType::class, $parameters[1]);
|
||||
$this->assertSame([
|
||||
'label' => 'mautic.form.field.form.number_precision',
|
||||
'label_attr' => ['class' => 'control-label'],
|
||||
'data' => 0,
|
||||
'attr' => [
|
||||
'class' => 'form-control',
|
||||
'tooltip' => 'mautic.form.field.form.number_precision.tooltip',
|
||||
],
|
||||
'required' => false,
|
||||
], $parameters[2]);
|
||||
}
|
||||
|
||||
return $this->formBuilder;
|
||||
});
|
||||
|
||||
$this->form->buildForm($this->formBuilder, $options);
|
||||
}
|
||||
|
||||
public function testSubmitValidData(): void
|
||||
{
|
||||
$formData = [
|
||||
'placeholder' => 'test',
|
||||
'precision' => 1,
|
||||
];
|
||||
$form = $this->factory->create(FormFieldNumberType::class);
|
||||
|
||||
$form->submit($formData);
|
||||
|
||||
$this->assertTrue($form->isSynchronized());
|
||||
$this->assertNotEmpty($form->getData());
|
||||
|
||||
$view = $form->createView();
|
||||
$children = $view->children;
|
||||
|
||||
foreach (array_keys($formData) as $key) {
|
||||
$this->assertArrayHasKey($key, $children);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Mautic\FormBundle\Tests\Form\Type;
|
||||
|
||||
use Mautic\FormBundle\Form\Type\FormFieldSliderType;
|
||||
use Symfony\Component\Form\Extension\Validator\ValidatorExtension;
|
||||
use Symfony\Component\Form\PreloadedExtension;
|
||||
use Symfony\Component\Form\Test\TypeTestCase;
|
||||
use Symfony\Component\Validator\Validation;
|
||||
|
||||
final class FormFieldSliderTypeTest extends TypeTestCase
|
||||
{
|
||||
protected function getExtensions(): array
|
||||
{
|
||||
return [
|
||||
new ValidatorExtension(Validation::createValidator()),
|
||||
new PreloadedExtension([
|
||||
FormFieldSliderType::class => new FormFieldSliderType(),
|
||||
], []),
|
||||
];
|
||||
}
|
||||
|
||||
public function testSubmitValidData(): void
|
||||
{
|
||||
$formData = [
|
||||
'min' => 0,
|
||||
'max' => 50,
|
||||
'step' => 5,
|
||||
];
|
||||
$form = $this->factory->create(FormFieldSliderType::class);
|
||||
|
||||
$form->submit($formData);
|
||||
|
||||
$this->assertTrue($form->isSynchronized());
|
||||
$this->assertNotEmpty($form->getData());
|
||||
|
||||
$view = $form->createView();
|
||||
$children = $view->children;
|
||||
|
||||
foreach (array_keys($formData) as $key) {
|
||||
$this->assertArrayHasKey($key, $children);
|
||||
}
|
||||
}
|
||||
|
||||
public function testSubmitInvalidData(): void
|
||||
{
|
||||
$form = $this->factory->create(FormFieldSliderType::class);
|
||||
|
||||
$invalidData = [
|
||||
'min' => 10,
|
||||
'max' => 5,
|
||||
'step' => 15,
|
||||
];
|
||||
|
||||
$form->submit($invalidData);
|
||||
|
||||
$this->assertTrue($form->isSynchronized());
|
||||
$this->assertFalse($form->isValid());
|
||||
|
||||
$errors = $form->getErrors(true);
|
||||
$this->assertGreaterThan(0, count($errors));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,132 @@
|
||||
<?php
|
||||
|
||||
namespace Mautic\FormBundle\Tests\Helper;
|
||||
|
||||
use Mautic\CoreBundle\Translation\Translator;
|
||||
use Mautic\FormBundle\Entity\Field;
|
||||
use Mautic\FormBundle\Helper\FormFieldHelper;
|
||||
use Symfony\Component\Validator\Validator\ValidatorInterface;
|
||||
|
||||
class FormFieldHelperTest extends \PHPUnit\Framework\TestCase
|
||||
{
|
||||
/**
|
||||
* @var FormFieldHelper
|
||||
*/
|
||||
protected $fixture;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
$translatorMock = $this->createMock(Translator::class);
|
||||
|
||||
$validatorMock = $this->createMock(ValidatorInterface::class);
|
||||
|
||||
$this->fixture = new FormFieldHelper($translatorMock, $validatorMock);
|
||||
}
|
||||
|
||||
#[\PHPUnit\Framework\Attributes\DataProvider('fieldProvider')]
|
||||
public function testPopulateField($field, $value, $formHtml, $expectedValue, $message): void
|
||||
{
|
||||
$this->fixture->populateField($field, $value, 'mautic', $formHtml);
|
||||
|
||||
$this->assertEquals($expectedValue, $formHtml, $message);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public static function fieldProvider()
|
||||
{
|
||||
return [
|
||||
[
|
||||
self::getField('First Name', 'text'),
|
||||
'%22%2F%3E%3Cscript%3Ealert%280%29%3C%2Fscript%3E',
|
||||
'<input value="" id="mauticform_input_mautic_firstname" />',
|
||||
'<input id="mauticform_input_mautic_firstname" value=""/>alert(0)" />',
|
||||
'Tags should be stripped from textet field values submitted via GET to prevent XSS.',
|
||||
],
|
||||
[
|
||||
self::getField('First Name', 'text'),
|
||||
'%22%20onfocus=%22alert(123)',
|
||||
'<input value="" id="mauticform_input_mautic_firstname" />',
|
||||
'<input id="mauticform_input_mautic_firstname" value="" onfocus="alert(123)" />',
|
||||
'Inline JS values should not be allowed via GET to prevent XSS.',
|
||||
],
|
||||
[
|
||||
self::getField('Phone', 'tel'),
|
||||
'+41 123 456 7890',
|
||||
'<input value="" id="mauticform_input_mautic_phone" />',
|
||||
'<input id="mauticform_input_mautic_phone" value="+41 123 456 7890" />',
|
||||
'Phone number are populated properly',
|
||||
],
|
||||
[
|
||||
self::getField('Description', 'textarea'),
|
||||
'%22%2F%3E%3Cscript%3Ealert%280%29%3C%2Fscript%3E',
|
||||
'<textarea id="mauticform_input_mautic_description"></textarea>',
|
||||
'<textarea id="mauticform_input_mautic_description">"/>alert(0)</textarea>',
|
||||
'Tags should be stripped from textarea field values submitted via GET to prevent XSS.',
|
||||
],
|
||||
[
|
||||
self::getField('Description', 'textarea'),
|
||||
'%22%20onfocus=%22alert(123)',
|
||||
'<textarea id="mauticform_input_mautic_description"></textarea>',
|
||||
'<textarea id="mauticform_input_mautic_description">" onfocus="alert(123)</textarea>',
|
||||
'Tags should be stripped from textarea field values submitted via GET to prevent XSS.',
|
||||
],
|
||||
[
|
||||
self::getField('Checkbox Single', 'checkboxgrp'),
|
||||
'myvalue',
|
||||
'<input id="mauticform_checkboxgrp_checkbox_'.self::getAliasFromName('Checkbox Single').'1" value="myvalue"/><input id="mauticform_checkboxgrp_checkbox_'.self::getAliasFromName('Checkbox Single').'2" value="notmyvalue"/>',
|
||||
'<input id="mauticform_checkboxgrp_checkbox_'.self::getAliasFromName('Checkbox Single').'1" value="myvalue" checked /><input id="mauticform_checkboxgrp_checkbox_'.self::getAliasFromName('Checkbox Single').'2" value="notmyvalue"/>',
|
||||
'Single value checkbox groups should have their values set appropriately via GET.',
|
||||
],
|
||||
[
|
||||
self::getField('Checkbox Multi', 'checkboxgrp'),
|
||||
'myvalue%7Calsomyvalue',
|
||||
'<input id="mauticform_checkboxgrp_checkbox_'.self::getAliasFromName('Checkbox Multi').'1" value="myvalue"/><input id="mauticform_checkboxgrp_checkbox_'.self::getAliasFromName('Checkbox Multi').'2" value="alsomyvalue"/><input id="mauticform_checkboxgrp_checkbox_'.self::getAliasFromName('Checkbox Multi').'3" value="notmyvalue"/>',
|
||||
'<input id="mauticform_checkboxgrp_checkbox_'.self::getAliasFromName('Checkbox Multi').'1" value="myvalue" checked /><input id="mauticform_checkboxgrp_checkbox_'.self::getAliasFromName('Checkbox Multi').'2" value="alsomyvalue" checked /><input id="mauticform_checkboxgrp_checkbox_'.self::getAliasFromName('Checkbox Multi').'3" value="notmyvalue"/>',
|
||||
'Multi-value checkbox groups should have their values set appropriately via GET.',
|
||||
],
|
||||
[
|
||||
self::getField('Radio Single', 'radiogrp'),
|
||||
'myvalue',
|
||||
'<input id="mauticform_radiogrp_radio_'.self::getAliasFromName('Radio Single').'1" value="myvalue"/><input id="mauticform_radiogrp_radio_'.self::getAliasFromName('Radio Single').'1" value="notmyvalue"/>',
|
||||
'<input id="mauticform_radiogrp_radio_'.self::getAliasFromName('Radio Single').'1" value="myvalue" checked /><input id="mauticform_radiogrp_radio_'.self::getAliasFromName('Radio Single').'1" value="notmyvalue"/>',
|
||||
'Single value radio groups should have their values set appropriately via GET.',
|
||||
],
|
||||
[
|
||||
self::getField('Select', 'select'),
|
||||
'myvalue',
|
||||
'<select id="mauticform_input_mautic_select"><option value="myvalue">My Value</option></select>',
|
||||
'<select id="mauticform_input_mautic_select"><option value="myvalue" selected="selected">My Value</option></select>',
|
||||
'Select lists should have their values set appropriately via GET.',
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $name
|
||||
* @param string $type
|
||||
*
|
||||
* @return Field
|
||||
*/
|
||||
protected static function getField($name, $type)
|
||||
{
|
||||
$field = new Field();
|
||||
|
||||
$field->setLabel($name);
|
||||
$field->setAlias(self::getAliasFromName($name));
|
||||
$field->setType($type);
|
||||
|
||||
return $field;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $name
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private static function getAliasFromName($name)
|
||||
{
|
||||
return strtolower(str_replace(' ', '', $name));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,156 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Mautic\FormBundle\Tests\Helper;
|
||||
|
||||
use Mautic\FormBundle\Helper\PropertiesAccessor;
|
||||
use Mautic\FormBundle\Model\FormModel;
|
||||
use PHPUnit\Framework\MockObject\MockObject;
|
||||
|
||||
final class PropertiesAccessorTest extends \PHPUnit\Framework\TestCase
|
||||
{
|
||||
/**
|
||||
* @var MockObject|FormModel
|
||||
*/
|
||||
private MockObject $formModel;
|
||||
|
||||
private PropertiesAccessor $propertiesAccessor;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
$this->formModel = $this->createMock(FormModel::class);
|
||||
$this->propertiesAccessor = new PropertiesAccessor(
|
||||
$this->formModel
|
||||
);
|
||||
}
|
||||
|
||||
public function testGetPropertiesForCountryField(): void
|
||||
{
|
||||
$field = [
|
||||
'type' => 'country',
|
||||
'mappedField' => 'country',
|
||||
];
|
||||
|
||||
$this->formModel->expects($this->once())
|
||||
->method('getContactFieldPropertiesList')
|
||||
->with('country')
|
||||
->willReturn(['some_props_here']);
|
||||
|
||||
$this->assertSame(
|
||||
['some_props_here'],
|
||||
$this->propertiesAccessor->getProperties($field)
|
||||
);
|
||||
}
|
||||
|
||||
public function testGetPropertiesForSyncList(): void
|
||||
{
|
||||
$field = [
|
||||
'type' => 'custom_select_a',
|
||||
'mappedField' => 'contact_field_a',
|
||||
'mappedObject' => 'contact',
|
||||
'properties' => ['syncList' => true],
|
||||
];
|
||||
|
||||
$this->formModel->expects($this->once())
|
||||
->method('getContactFieldPropertiesList')
|
||||
->with('contact_field_a')
|
||||
->willReturn(['some_props_here']);
|
||||
|
||||
$this->assertSame(
|
||||
['some_props_here'],
|
||||
$this->propertiesAccessor->getProperties($field)
|
||||
);
|
||||
}
|
||||
|
||||
public function testGetPropertiesForTextField(): void
|
||||
{
|
||||
$field = [
|
||||
'type' => 'custom_text_a',
|
||||
'mappedField' => 'contact_field_a',
|
||||
'mappedObject' => 'contact',
|
||||
'properties' => ['syncList' => false],
|
||||
];
|
||||
|
||||
$this->formModel->expects($this->never())
|
||||
->method('getContactFieldPropertiesList');
|
||||
|
||||
$this->assertSame(
|
||||
[],
|
||||
$this->propertiesAccessor->getProperties($field)
|
||||
);
|
||||
}
|
||||
|
||||
public function testGetPropertiesForListField(): void
|
||||
{
|
||||
$field = [
|
||||
'type' => 'custom_select_a',
|
||||
'properties' => [
|
||||
'syncList' => false,
|
||||
'list' => ['list' => ['option_a' => 'Option A']],
|
||||
],
|
||||
];
|
||||
|
||||
$this->formModel->expects($this->never())
|
||||
->method('getContactFieldPropertiesList');
|
||||
|
||||
$this->assertSame(
|
||||
['option_a' => 'Option A'],
|
||||
$this->propertiesAccessor->getProperties($field)
|
||||
);
|
||||
}
|
||||
|
||||
public function testGetPropertiesForOptionlistField(): void
|
||||
{
|
||||
$field = [
|
||||
'type' => 'custom_select_a',
|
||||
'properties' => [
|
||||
'syncList' => false,
|
||||
'optionlist' => ['list' => ['option_a' => 'Option A']],
|
||||
],
|
||||
];
|
||||
|
||||
$this->formModel->expects($this->never())
|
||||
->method('getContactFieldPropertiesList');
|
||||
|
||||
$this->assertSame(
|
||||
['option_a' => 'Option A'],
|
||||
$this->propertiesAccessor->getProperties($field)
|
||||
);
|
||||
}
|
||||
|
||||
public function testGetChoicesForWellFormattedChoices(): void
|
||||
{
|
||||
$options = ['choice_a' => 'Choice A'];
|
||||
|
||||
$this->assertSame(
|
||||
array_flip($options),
|
||||
$this->propertiesAccessor->getChoices($options)
|
||||
);
|
||||
}
|
||||
|
||||
public function testGetChoicesForPipeFormattedChoices(): void
|
||||
{
|
||||
$options = 'Choice A|Choice B';
|
||||
|
||||
$this->assertSame(
|
||||
['Choice A' => 'Choice A', 'Choice B' => 'Choice B'],
|
||||
$this->propertiesAccessor->getChoices($options)
|
||||
);
|
||||
}
|
||||
|
||||
public function testGetChoicesForLabelValueArrayChoices(): void
|
||||
{
|
||||
$options = [
|
||||
[
|
||||
'label' => 'Choice A',
|
||||
'value' => 'Value A',
|
||||
],
|
||||
];
|
||||
|
||||
$this->assertSame(
|
||||
['Choice A' => 'Value A'],
|
||||
$this->propertiesAccessor->getChoices($options)
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Mautic\FormBundle\Tests\Model;
|
||||
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Mautic\CoreBundle\Doctrine\Helper\ColumnSchemaHelper;
|
||||
use Mautic\CoreBundle\Doctrine\Helper\TableSchemaHelper;
|
||||
use Mautic\CoreBundle\Helper\CoreParametersHelper;
|
||||
use Mautic\CoreBundle\Helper\ThemeHelperInterface;
|
||||
use Mautic\CoreBundle\Helper\UserHelper;
|
||||
use Mautic\CoreBundle\Security\Permissions\CorePermissions;
|
||||
use Mautic\CoreBundle\Translation\Translator;
|
||||
use Mautic\FormBundle\Collector\MappedObjectCollectorInterface;
|
||||
use Mautic\FormBundle\Entity\Form;
|
||||
use Mautic\FormBundle\Entity\FormRepository;
|
||||
use Mautic\FormBundle\Helper\FormFieldHelper;
|
||||
use Mautic\FormBundle\Helper\FormUploader;
|
||||
use Mautic\FormBundle\Model\ActionModel;
|
||||
use Mautic\FormBundle\Model\FieldModel;
|
||||
use Mautic\FormBundle\Model\FormModel;
|
||||
use Mautic\LeadBundle\Helper\PrimaryCompanyHelper;
|
||||
use Mautic\LeadBundle\Model\FieldModel as LeadFieldModel;
|
||||
use Mautic\LeadBundle\Tracker\ContactTracker;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Symfony\Component\EventDispatcher\EventDispatcher;
|
||||
use Symfony\Component\HttpFoundation\RequestStack;
|
||||
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
|
||||
use Twig\Environment;
|
||||
|
||||
class DeleteFormTest extends \PHPUnit\Framework\TestCase
|
||||
{
|
||||
public function testDelete(): void
|
||||
{
|
||||
$requestStack = $this->createMock(RequestStack::class);
|
||||
$twigMock = $this->createMock(Environment::class);
|
||||
$themeHelper = $this->createMock(ThemeHelperInterface::class);
|
||||
$formActionModel = $this->createMock(ActionModel::class);
|
||||
$formFieldModel = $this->createMock(FieldModel::class);
|
||||
$fieldHelper = $this->createMock(FormFieldHelper::class);
|
||||
$primaryCompanyHelper = $this->createMock(PrimaryCompanyHelper::class);
|
||||
$leadFieldModel = $this->createMock(LeadFieldModel::class);
|
||||
$formUploaderMock = $this->createMock(FormUploader::class);
|
||||
$contactTracker = $this->createMock(ContactTracker::class);
|
||||
$columnSchemaHelper = $this->createMock(ColumnSchemaHelper::class);
|
||||
$tableSchemaHelper = $this->createMock(TableSchemaHelper::class);
|
||||
$entityManager = $this->createMock(EntityManagerInterface::class);
|
||||
$dispatcher = $this->createMock(EventDispatcher::class);
|
||||
$formRepository = $this->createMock(FormRepository::class);
|
||||
$form = $this->createMock(Form::class);
|
||||
$mappedObjectCollector = $this->createMock(MappedObjectCollectorInterface::class);
|
||||
$formModel = new FormModel(
|
||||
$requestStack,
|
||||
$twigMock,
|
||||
$themeHelper,
|
||||
$formActionModel,
|
||||
$formFieldModel,
|
||||
$fieldHelper,
|
||||
$primaryCompanyHelper,
|
||||
$leadFieldModel,
|
||||
$formUploaderMock,
|
||||
$contactTracker,
|
||||
$columnSchemaHelper,
|
||||
$tableSchemaHelper,
|
||||
$mappedObjectCollector,
|
||||
$entityManager,
|
||||
$this->createMock(CorePermissions::class),
|
||||
$dispatcher,
|
||||
$this->createMock(UrlGeneratorInterface::class),
|
||||
$this->createMock(Translator::class),
|
||||
$this->createMock(UserHelper::class),
|
||||
$this->createMock(LoggerInterface::class),
|
||||
$this->createMock(CoreParametersHelper::class)
|
||||
);
|
||||
$matcher = $this->exactly(2);
|
||||
|
||||
$dispatcher->expects($matcher)
|
||||
->method('hasListeners')->willReturnCallback(function (...$parameters) use ($matcher) {
|
||||
if (1 === $matcher->numberOfInvocations()) {
|
||||
$this->assertSame('mautic.form_pre_delete', $parameters[0]);
|
||||
}
|
||||
if (2 === $matcher->numberOfInvocations()) {
|
||||
$this->assertSame('mautic.form_post_delete', $parameters[0]);
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
$entityManager->expects($this->once())
|
||||
->method('getRepository')
|
||||
->willReturn($formRepository);
|
||||
|
||||
$form->expects($this->exactly(2))
|
||||
->method('getId')
|
||||
->with()
|
||||
->willReturn(1);
|
||||
|
||||
$formUploaderMock->expects($this->once())
|
||||
->method('deleteFilesOfForm')
|
||||
->with($form);
|
||||
|
||||
$formRepository->expects($this->once())
|
||||
->method('deleteEntity')
|
||||
->with($form);
|
||||
|
||||
$formModel->deleteEntity($form);
|
||||
|
||||
$this->assertSame(1, $form->deletedId);
|
||||
}
|
||||
}
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 8.7 KiB |
@@ -0,0 +1 @@
|
||||
test file for FormUploaderTest
|
||||
@@ -0,0 +1,115 @@
|
||||
<?php
|
||||
|
||||
namespace Mautic\FormBundle\Tests\Model;
|
||||
|
||||
use Doctrine\DBAL\Schema\Column;
|
||||
use Mautic\CoreBundle\Doctrine\Helper\ColumnSchemaHelper;
|
||||
use Mautic\CoreBundle\Test\MauticMysqlTestCase;
|
||||
use Mautic\FormBundle\Entity\Form;
|
||||
use Mautic\FormBundle\Model\FieldModel;
|
||||
use Mautic\FormBundle\Model\FormModel;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
|
||||
class FieldModelFunctionalTest extends MauticMysqlTestCase
|
||||
{
|
||||
protected $useCleanupRollback = false;
|
||||
|
||||
public function testDeleteFormFieldShouldRemoveTableColumn(): void
|
||||
{
|
||||
$formData = [
|
||||
'name' => 'FormTest',
|
||||
'description' => 'Form created via submission test',
|
||||
'formType' => 'standalone',
|
||||
'isPublished' => true,
|
||||
'fields' => [
|
||||
[
|
||||
'label' => 'Email',
|
||||
'type' => 'email',
|
||||
'alias' => 'email',
|
||||
'mappedObject' => 'contact',
|
||||
'mappedField' => 'email',
|
||||
],
|
||||
[
|
||||
'label' => 'First name',
|
||||
'type' => 'text',
|
||||
'alias' => 'fname',
|
||||
],
|
||||
[
|
||||
'label' => 'Last name',
|
||||
'type' => 'text',
|
||||
'alias' => 'lname',
|
||||
],
|
||||
[
|
||||
'label' => 'Submit',
|
||||
'type' => 'button',
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
// Create form.
|
||||
$form = $this->createForm($formData);
|
||||
|
||||
/** @var ColumnSchemaHelper $helper */
|
||||
$helper = $this->getContainer()->get('mautic.schema.helper.column');
|
||||
|
||||
// Table name to check the fields.
|
||||
$name = 'form_results_'.$form->getId().'_'.$form->getAlias();
|
||||
$schemaHelper = $helper->setName($name);
|
||||
|
||||
// The table will have four column, 'submission_id', 'form_id', 'email', and 'fname'.
|
||||
$this->assertEquals(5, count($schemaHelper->getColumns()));
|
||||
|
||||
/** @var FieldModel $fieldModel */
|
||||
$fieldModel = $this->getContainer()->get('mautic.form.model.field');
|
||||
|
||||
$ids = $this->getDeleteIds($fieldModel);
|
||||
|
||||
// Let's delete the 'First name' field.
|
||||
$fieldModel->deleteEntities($ids);
|
||||
|
||||
$this->assertTrue($schemaHelper->checkColumnExists('email'), 'The table has \'email\' field.');
|
||||
$this->assertFalse($schemaHelper->checkColumnExists('fname'), 'The table does not have the \'fname\' field.');
|
||||
$this->assertFalse($schemaHelper->checkColumnExists('lname'), 'The table does not have the \'lname\' field.');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed[] $payload
|
||||
*/
|
||||
private function createForm(array $payload): Form
|
||||
{
|
||||
$this->client->request(Request::METHOD_POST, '/api/forms/new', $payload);
|
||||
$clientResponse = $this->client->getResponse();
|
||||
$response = json_decode($clientResponse->getContent(), true);
|
||||
|
||||
/** @var FormModel $formModel */
|
||||
$formModel = $this->getContainer()->get('mautic.form.model.form');
|
||||
|
||||
return $formModel->getEntity($response['form']['id']);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed[]
|
||||
*/
|
||||
private function getDeleteIds(FieldModel $fieldModel): array
|
||||
{
|
||||
$fields = $fieldModel->getEntities([
|
||||
'filter' => [
|
||||
'force' => [
|
||||
[
|
||||
'column' => $fieldModel->getRepository()->getTableAlias().'.alias',
|
||||
'expr' => 'in',
|
||||
'value' => ['fname', 'lname'],
|
||||
],
|
||||
],
|
||||
],
|
||||
'ignore_paginator' => true,
|
||||
]);
|
||||
|
||||
$ids = [];
|
||||
foreach ($fields as $field) {
|
||||
$ids[] = $field->getId();
|
||||
}
|
||||
|
||||
return $ids;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
<?php
|
||||
|
||||
namespace Mautic\FormBundle\Tests\Model;
|
||||
|
||||
use Doctrine\DBAL\Connection;
|
||||
use Doctrine\ORM\EntityManager;
|
||||
use Mautic\CoreBundle\Doctrine\Helper\ColumnSchemaHelper;
|
||||
use Mautic\CoreBundle\Helper\CoreParametersHelper;
|
||||
use Mautic\CoreBundle\Helper\UserHelper;
|
||||
use Mautic\CoreBundle\Security\Permissions\CorePermissions;
|
||||
use Mautic\CoreBundle\Translation\Translator;
|
||||
use Mautic\FormBundle\Model\FieldModel;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
|
||||
use Symfony\Component\HttpFoundation\RequestStack;
|
||||
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
|
||||
|
||||
class FieldModelTest extends TestCase
|
||||
{
|
||||
public function testGenerateAlias(): void
|
||||
{
|
||||
$connection = $this->createMock(Connection::class);
|
||||
|
||||
$platform = new class {
|
||||
public function getReservedKeywordsList(): object
|
||||
{
|
||||
return new class {
|
||||
public function isKeyword(): bool
|
||||
{
|
||||
return false;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public function isKeyword(): bool
|
||||
{
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
$connection->method('getDatabasePlatform')
|
||||
->willReturn($platform);
|
||||
|
||||
$leadFieldModel = $this->createMock(\Mautic\LeadBundle\Model\FieldModel::class);
|
||||
$entityManager = $this->createMock(EntityManager::class);
|
||||
$schemaHelper = $this->createMock(ColumnSchemaHelper::class);
|
||||
$fieldModel = new FieldModel(
|
||||
$leadFieldModel,
|
||||
$entityManager,
|
||||
$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),
|
||||
$this->createMock(RequestStack::class),
|
||||
$schemaHelper
|
||||
);
|
||||
|
||||
$entityManager->expects($this->any())
|
||||
->method('getConnection')
|
||||
->willReturn($connection);
|
||||
|
||||
$aliases = [
|
||||
'existed_alias',
|
||||
'existed_alias_with_space',
|
||||
];
|
||||
|
||||
$strings = [
|
||||
'existed_alias1' => 'existed alias',
|
||||
'not_existed' => 'not existed',
|
||||
'existed_alias_with_space1' => 'existed alias with space',
|
||||
'alias_test' => 'alias test',
|
||||
];
|
||||
|
||||
foreach ($strings as $expected => $string) {
|
||||
$alias = $fieldModel->generateAlias($string, $aliases);
|
||||
$this->assertEquals($expected, $alias);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,249 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Mautic\FormBundle\Tests\Model;
|
||||
|
||||
use Mautic\CoreBundle\Test\MauticMysqlTestCase;
|
||||
use Mautic\FormBundle\Entity\Form;
|
||||
use Mautic\FormBundle\Model\FormModel;
|
||||
use Mautic\LeadBundle\Entity\Lead;
|
||||
use Mautic\LeadBundle\Entity\LeadField;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
class FormModelFunctionalTest extends MauticMysqlTestCase
|
||||
{
|
||||
protected $useCleanupRollback = false;
|
||||
|
||||
public function testPopulateValuesWithGetParameters(): void
|
||||
{
|
||||
$formId = $this->createForm();
|
||||
$crawler = $this->client->request(
|
||||
Request::METHOD_GET,
|
||||
"/s/forms/preview/{$formId}?email=testform@test.com&firstname=test&description=test-test&checkbox=val1|val3"
|
||||
);
|
||||
$inputValue = $crawler->filter('input[type=email]')->attr('value');
|
||||
self::assertSame('testform@test.com', $inputValue);
|
||||
$inputValue = $crawler->filter('input[type=text]')->attr('value');
|
||||
self::assertSame('test', $inputValue);
|
||||
$inputValue = $crawler->filter('textarea[name^=mauticform]')->html();
|
||||
self::assertSame('test-test', $inputValue);
|
||||
$inputValue = $crawler->filter('textarea[name^=mauticform]')->html();
|
||||
self::assertSame('test-test', $inputValue);
|
||||
$inputValue = $crawler->filter('input[value^=val1]')->attr('checked');
|
||||
self::assertNotNull($inputValue, $crawler->html());
|
||||
$inputValue = $crawler->filter('input[value^=val2]')->attr('checked');
|
||||
self::assertNull($inputValue);
|
||||
$inputValue = $crawler->filter('input[value^=val3]')->attr('checked');
|
||||
self::assertNotNull($inputValue);
|
||||
|
||||
$this->createPage($formId);
|
||||
$crawler = $this->client->request(Request::METHOD_GET, '/test-page?email=test%2Bpage@test.com&firstname=test');
|
||||
$inputValue = $crawler->filter('input[type=email]')->attr('value');
|
||||
self::assertSame('test+page@test.com', $inputValue);
|
||||
$inputValue = $crawler->filter('input[type=text]')->attr('value');
|
||||
self::assertSame('test', $inputValue);
|
||||
}
|
||||
|
||||
private function createForm(): int
|
||||
{
|
||||
$formPayload = [
|
||||
'name' => 'Test Form',
|
||||
'formType' => 'standalone',
|
||||
'description' => 'API test',
|
||||
'fields' => [
|
||||
[
|
||||
'label' => 'firstname',
|
||||
'alias' => 'firstname',
|
||||
'type' => 'text',
|
||||
],
|
||||
[
|
||||
'label' => 'email',
|
||||
'alias' => 'email',
|
||||
'type' => 'email',
|
||||
'leadField' => 'email',
|
||||
],
|
||||
[
|
||||
'label' => 'description',
|
||||
'alias' => 'description',
|
||||
'type' => 'textarea',
|
||||
],
|
||||
[
|
||||
'label' => 'checkbox',
|
||||
'alias' => 'checkbox',
|
||||
'type' => 'checkboxgrp',
|
||||
'properties' => [
|
||||
'syncList' => 0,
|
||||
'optionlist' => [
|
||||
'list' => [
|
||||
[
|
||||
'label' => 'val1',
|
||||
'value' => 'val1',
|
||||
],
|
||||
[
|
||||
'label' => 'val2',
|
||||
'value' => 'val2',
|
||||
],
|
||||
[
|
||||
'label' => 'val3',
|
||||
'value' => 'val3',
|
||||
],
|
||||
],
|
||||
],
|
||||
'labelAttributes' => null,
|
||||
],
|
||||
],
|
||||
[
|
||||
'label' => 'Submit',
|
||||
'alias' => 'submit',
|
||||
'type' => 'button',
|
||||
],
|
||||
],
|
||||
'postAction' => 'return',
|
||||
];
|
||||
|
||||
$this->client->request('POST', '/api/forms/new', $formPayload);
|
||||
$clientResponse = $this->client->getResponse();
|
||||
$this->assertEquals(Response::HTTP_CREATED, $clientResponse->getStatusCode(), $clientResponse->getContent());
|
||||
$response = json_decode($clientResponse->getContent(), true);
|
||||
|
||||
return $response['form']['id'];
|
||||
}
|
||||
|
||||
private function createPage(int $formId): void
|
||||
{
|
||||
$pagePayload = [
|
||||
'title' => 'Test Page',
|
||||
'alias' => 'test-page',
|
||||
'description' => 'This is my first page created via API.',
|
||||
'isPublished' => true,
|
||||
'customHtml' => '<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Test Page</title>
|
||||
<meta name="description" content="Test Page" />
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<div>{form='.$formId.'}</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>',
|
||||
];
|
||||
|
||||
$this->client->request('POST', '/api/pages/new', $pagePayload);
|
||||
$clientResponse = $this->client->getResponse();
|
||||
$this->assertEquals(Response::HTTP_CREATED, $clientResponse->getStatusCode(), $clientResponse->getContent());
|
||||
}
|
||||
|
||||
public function testLeadPopulateValuesWithLeadFields(): void
|
||||
{
|
||||
$multiselectFieldId = $this->createMultiselectLeadField();
|
||||
|
||||
$fieldModel = $this->getContainer()->get('mautic.lead.model.field');
|
||||
$multiselectField = $fieldModel->getEntity($multiselectFieldId);
|
||||
$fieldAlias = $multiselectField->getAlias();
|
||||
|
||||
$form = $this->createFormWithMultiselect($fieldAlias);
|
||||
$formId = $form->getId();
|
||||
|
||||
$lead = new Lead();
|
||||
$lead->setEmail('test@example.com');
|
||||
$lead->addUpdatedField($fieldAlias, 'a|b');
|
||||
$this->em->persist($lead);
|
||||
$this->em->flush();
|
||||
|
||||
$this->logoutUser();
|
||||
|
||||
$contactTracker = $this->getContainer()->get('mautic.tracker.contact');
|
||||
$contactTracker->setTrackedContact($lead);
|
||||
|
||||
$this->client->request('GET', "/form/{$formId}");
|
||||
$formCrawler = $this->client->getCrawler();
|
||||
$checkboxA = $formCrawler->filter('[id*="mauticform_checkboxgrp_checkbox_"][id$="_a0"]')->attr('checked');
|
||||
$checkboxB = $formCrawler->filter('[id*="mauticform_checkboxgrp_checkbox_"][id$="_b1"]')->attr('checked');
|
||||
$checkboxC = $formCrawler->filter('[id*="mauticform_checkboxgrp_checkbox_"][id$="_c2"]')->attr('checked');
|
||||
|
||||
$this->assertNotNull($checkboxA, 'Checkbox A should be preselected.');
|
||||
$this->assertNotNull($checkboxB, 'Checkbox B should be preselected.');
|
||||
$this->assertNull($checkboxC, 'Checkbox C should NOT be preselected.');
|
||||
}
|
||||
|
||||
private function createFormWithMultiselect(string $leadFieldAlias): Form
|
||||
{
|
||||
$payload = [
|
||||
'name' => 'Test Form',
|
||||
'formType' => 'standalone',
|
||||
'description' => 'API test',
|
||||
'fields' => [
|
||||
[
|
||||
'label' => 'email',
|
||||
'alias' => 'email',
|
||||
'type' => 'email',
|
||||
'mappedField' => 'email',
|
||||
'mappedObject' => 'contact',
|
||||
'isAutoFill' => true,
|
||||
'showWhenValueExists' => false,
|
||||
],
|
||||
[
|
||||
'label' => $leadFieldAlias,
|
||||
'alias' => $leadFieldAlias,
|
||||
'type' => 'checkboxgrp',
|
||||
'mappedField' => $leadFieldAlias,
|
||||
'mappedObject' => 'contact',
|
||||
'isAutoFill' => true,
|
||||
'showWhenValueExists' => true,
|
||||
|
||||
'properties' => [
|
||||
'syncList' => 1,
|
||||
'labelAttributes' => null,
|
||||
],
|
||||
],
|
||||
[
|
||||
'label' => 'Submit',
|
||||
'alias' => 'submit',
|
||||
'type' => 'button',
|
||||
],
|
||||
],
|
||||
'postAction' => 'return',
|
||||
];
|
||||
|
||||
$this->client->request(Request::METHOD_POST, '/api/forms/new', $payload);
|
||||
$clientResponse = $this->client->getResponse();
|
||||
$response = json_decode($clientResponse->getContent(), true);
|
||||
|
||||
/** @var FormModel $formModel */
|
||||
$formModel = $this->getContainer()->get('mautic.form.model.form');
|
||||
|
||||
return $formModel->getEntity($response['form']['id']);
|
||||
}
|
||||
|
||||
private function createMultiselectLeadField(): int
|
||||
{
|
||||
/** @var \Mautic\LeadBundle\Model\FieldModel $fieldModel */
|
||||
$fieldModel = $this->getContainer()->get('mautic.lead.model.field');
|
||||
$alias = 'test_multiselect_'.uniqid();
|
||||
|
||||
$field = new LeadField();
|
||||
$field->setType('multiselect');
|
||||
$field->setObject('lead');
|
||||
$field->setAlias($alias);
|
||||
$field->setName($alias);
|
||||
$field->setLabel($alias);
|
||||
$field->setGroup('core');
|
||||
|
||||
$properties = [
|
||||
'list' => [
|
||||
['label' => 'a', 'value' => 'a'],
|
||||
['label' => 'b', 'value' => 'b'],
|
||||
['label' => 'c', 'value' => 'c'],
|
||||
],
|
||||
];
|
||||
$field->setProperties($properties);
|
||||
|
||||
$fieldModel->saveEntity($field);
|
||||
|
||||
return $field->getId();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,776 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Mautic\FormBundle\Tests\Model;
|
||||
|
||||
use Doctrine\Common\Collections\ArrayCollection;
|
||||
use Doctrine\ORM\EntityManager;
|
||||
use Mautic\CoreBundle\Doctrine\Helper\ColumnSchemaHelper;
|
||||
use Mautic\CoreBundle\Doctrine\Helper\TableSchemaHelper;
|
||||
use Mautic\CoreBundle\Helper\CoreParametersHelper;
|
||||
use Mautic\CoreBundle\Helper\ThemeHelper;
|
||||
use Mautic\CoreBundle\Helper\UserHelper;
|
||||
use Mautic\CoreBundle\Security\Permissions\CorePermissions;
|
||||
use Mautic\CoreBundle\Translation\Translator;
|
||||
use Mautic\FormBundle\Collector\MappedObjectCollectorInterface;
|
||||
use Mautic\FormBundle\Entity\Field;
|
||||
use Mautic\FormBundle\Entity\Form;
|
||||
use Mautic\FormBundle\Entity\FormRepository;
|
||||
use Mautic\FormBundle\Helper\FormFieldHelper;
|
||||
use Mautic\FormBundle\Helper\FormUploader;
|
||||
use Mautic\FormBundle\Model\ActionModel;
|
||||
use Mautic\FormBundle\Model\FieldModel;
|
||||
use Mautic\FormBundle\Model\FormModel;
|
||||
use Mautic\LeadBundle\Entity\Lead;
|
||||
use Mautic\LeadBundle\Entity\LeadField;
|
||||
use Mautic\LeadBundle\Helper\PrimaryCompanyHelper;
|
||||
use Mautic\LeadBundle\Model\FieldModel as LeadFieldModel;
|
||||
use Mautic\LeadBundle\Tracker\ContactTracker;
|
||||
use PHPUnit\Framework\MockObject\MockObject;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Symfony\Component\EventDispatcher\EventDispatcher;
|
||||
use Symfony\Component\HttpFoundation\RequestStack;
|
||||
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
|
||||
use Twig\Environment;
|
||||
|
||||
class FormModelTest extends \PHPUnit\Framework\TestCase
|
||||
{
|
||||
/**
|
||||
* @var MockObject&RequestStack
|
||||
*/
|
||||
private MockObject $requestStack;
|
||||
|
||||
/**
|
||||
* @var MockObject&Environment
|
||||
*/
|
||||
private MockObject $twigMock;
|
||||
|
||||
/**
|
||||
* @var MockObject&ThemeHelper
|
||||
*/
|
||||
private MockObject $themeHelper;
|
||||
|
||||
/**
|
||||
* @var MockObject&ActionModel
|
||||
*/
|
||||
private MockObject $formActionModel;
|
||||
|
||||
/**
|
||||
* @var MockObject&FieldModel
|
||||
*/
|
||||
private MockObject $formFieldModel;
|
||||
|
||||
/**
|
||||
* @var MockObject&EventDispatcher
|
||||
*/
|
||||
private MockObject $dispatcher;
|
||||
|
||||
/**
|
||||
* @var MockObject&Translator
|
||||
*/
|
||||
private MockObject $translator;
|
||||
|
||||
/**
|
||||
* @var MockObject&EntityManager
|
||||
*/
|
||||
private MockObject $entityManager;
|
||||
|
||||
/**
|
||||
* @var MockObject&FormUploader
|
||||
*/
|
||||
private MockObject $formUploaderMock;
|
||||
|
||||
/**
|
||||
* @var MockObject&ColumnSchemaHelper
|
||||
*/
|
||||
private MockObject $columnSchemaHelper;
|
||||
|
||||
/**
|
||||
* @var MockObject&TableSchemaHelper
|
||||
*/
|
||||
private MockObject $tableSchemaHelper;
|
||||
|
||||
/**
|
||||
* @var MockObject&FormRepository
|
||||
*/
|
||||
private MockObject $formRepository;
|
||||
|
||||
/**
|
||||
* @var MockObject&LeadFieldModel
|
||||
*/
|
||||
private MockObject $leadFieldModel;
|
||||
|
||||
/**
|
||||
* @var MockObject&ContactTracker
|
||||
*/
|
||||
private MockObject $contactTracker;
|
||||
|
||||
/**
|
||||
* @var MockObject&FormFieldHelper
|
||||
*/
|
||||
private MockObject $fieldHelper;
|
||||
|
||||
/**
|
||||
* @var MockObject&PrimaryCompanyHelper
|
||||
*/
|
||||
private MockObject $primaryCompanyHelper;
|
||||
|
||||
/**
|
||||
* @var MockObject&MappedObjectCollectorInterface
|
||||
*/
|
||||
private MockObject $mappedObjectCollector;
|
||||
|
||||
private FormModel $formModel;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
$this->requestStack = $this->createMock(RequestStack::class);
|
||||
$this->twigMock = $this->createMock(Environment::class);
|
||||
$this->themeHelper = $this->createMock(ThemeHelper::class);
|
||||
$this->formActionModel = $this->createMock(ActionModel::class);
|
||||
$this->formFieldModel = $this->createMock(FieldModel::class);
|
||||
$this->contactTracker = $this->createMock(ContactTracker::class);
|
||||
$this->fieldHelper = $this->createMock(FormFieldHelper::class);
|
||||
$this->primaryCompanyHelper = $this->createMock(PrimaryCompanyHelper::class);
|
||||
$this->dispatcher = $this->createMock(EventDispatcher::class);
|
||||
$this->translator = $this->createMock(Translator::class);
|
||||
$this->entityManager = $this->createMock(EntityManager::class);
|
||||
$this->formUploaderMock = $this->createMock(FormUploader::class);
|
||||
$this->leadFieldModel = $this->createMock(LeadFieldModel::class);
|
||||
$this->formRepository = $this->createMock(FormRepository::class);
|
||||
$this->columnSchemaHelper = $this->createMock(ColumnSchemaHelper::class);
|
||||
$this->tableSchemaHelper = $this->createMock(TableSchemaHelper::class);
|
||||
$this->mappedObjectCollector = $this->createMock(MappedObjectCollectorInterface::class);
|
||||
|
||||
$this->entityManager->expects($this
|
||||
->any())
|
||||
->method('getRepository')
|
||||
->willReturnMap(
|
||||
[
|
||||
[Form::class, $this->formRepository],
|
||||
]
|
||||
);
|
||||
|
||||
$this->formModel = new FormModel(
|
||||
$this->requestStack,
|
||||
$this->twigMock,
|
||||
$this->themeHelper,
|
||||
$this->formActionModel,
|
||||
$this->formFieldModel,
|
||||
$this->fieldHelper,
|
||||
$this->primaryCompanyHelper,
|
||||
$this->leadFieldModel,
|
||||
$this->formUploaderMock,
|
||||
$this->contactTracker,
|
||||
$this->columnSchemaHelper,
|
||||
$this->tableSchemaHelper,
|
||||
$this->mappedObjectCollector,
|
||||
$this->entityManager,
|
||||
$this->createMock(CorePermissions::class),
|
||||
$this->dispatcher,
|
||||
$this->createMock(UrlGeneratorInterface::class),
|
||||
$this->translator,
|
||||
$this->createMock(UserHelper::class),
|
||||
$this->createMock(LoggerInterface::class),
|
||||
$this->createMock(CoreParametersHelper::class)
|
||||
);
|
||||
}
|
||||
|
||||
public function testSetFields(): void
|
||||
{
|
||||
$form = new Form();
|
||||
$fields = $this->getTestFormFields();
|
||||
$this->formModel->setFields($form, $fields);
|
||||
$entityFields = $form->getFields()->toArray();
|
||||
|
||||
$newField = $entityFields[array_keys($fields)[0]];
|
||||
|
||||
/** @var Field $fileField */
|
||||
$fileField = $entityFields[array_keys($fields)[1]];
|
||||
|
||||
/** @var Field $parentField */
|
||||
$parentField = $entityFields[array_keys($fields)[2]];
|
||||
|
||||
/** @var Field $childField */
|
||||
$childField = $entityFields[array_keys($fields)[3]];
|
||||
|
||||
/** @var Field $childField */
|
||||
$newChildField = $entityFields[array_keys($fields)[4]];
|
||||
|
||||
$this->assertSame('email', $newField->getType());
|
||||
$this->assertSame('email', $newField->getAlias());
|
||||
$this->assertSame(1, $newField->getOrder());
|
||||
$this->assertSame('file', $fileField->getType());
|
||||
$this->assertSame('file', $fileField->getAlias());
|
||||
$this->assertSame(2, $fileField->getOrder());
|
||||
$this->assertSame('select', $parentField->getType());
|
||||
$this->assertSame('parent', $parentField->getAlias());
|
||||
$this->assertSame(3, $parentField->getOrder());
|
||||
$this->assertSame('text', $childField->getType());
|
||||
$this->assertSame('child', $childField->getAlias());
|
||||
$this->assertSame(4, $childField->getOrder());
|
||||
$this->assertSame('text', $newChildField->getType());
|
||||
$this->assertSame('new_child', $newChildField->getAlias());
|
||||
$this->assertSame(4, $newChildField->getOrder());
|
||||
}
|
||||
|
||||
public function testGetComponentsFields(): void
|
||||
{
|
||||
$components = $this->formModel->getCustomComponents();
|
||||
$this->assertArrayHasKey('fields', $components);
|
||||
}
|
||||
|
||||
public function testGetComponentsActions(): void
|
||||
{
|
||||
$components = $this->formModel->getCustomComponents();
|
||||
$this->assertArrayHasKey('actions', $components);
|
||||
}
|
||||
|
||||
public function testGetComponentsValidators(): void
|
||||
{
|
||||
$components = $this->formModel->getCustomComponents();
|
||||
$this->assertArrayHasKey('validators', $components);
|
||||
}
|
||||
|
||||
public function testGetEntityForNotFoundContactField(): void
|
||||
{
|
||||
$formEntity = $this->createMock(Form::class);
|
||||
$fields = new ArrayCollection();
|
||||
$formField = new Field();
|
||||
$formField->setMappedField('contactselect');
|
||||
$formField->setMappedObject('contact');
|
||||
$formField->setProperties(['syncList' => true]);
|
||||
|
||||
$fields->add($formField);
|
||||
|
||||
$formEntity->expects($this->exactly(2))
|
||||
->method('getFields')
|
||||
->willReturn($fields);
|
||||
|
||||
$this->formRepository->expects($this->once())
|
||||
->method('getEntity')
|
||||
->with(5)
|
||||
->willReturn($formEntity);
|
||||
|
||||
$this->leadFieldModel->expects($this->once())
|
||||
->method('getEntityByAlias')
|
||||
->willReturn(null);
|
||||
|
||||
$this->formModel->getEntity(5);
|
||||
|
||||
$this->assertSame(['syncList' => true], $formField->getProperties());
|
||||
}
|
||||
|
||||
public function testGetEntityForNotLinkedSelectField(): void
|
||||
{
|
||||
$formEntity = $this->createMock(Form::class);
|
||||
$fields = new ArrayCollection();
|
||||
$formField = new Field();
|
||||
$formField->setProperties(['syncList' => true]);
|
||||
|
||||
$fields->add($formField);
|
||||
|
||||
$formEntity->expects($this->exactly(2))
|
||||
->method('getFields')
|
||||
->willReturn($fields);
|
||||
|
||||
$this->formRepository->expects($this->once())
|
||||
->method('getEntity')
|
||||
->with(5)
|
||||
->willReturn($formEntity);
|
||||
|
||||
$this->leadFieldModel->expects($this->never())
|
||||
->method('getEntityByAlias');
|
||||
|
||||
$this->formModel->getEntity(5);
|
||||
}
|
||||
|
||||
public function testGetEntityForNotSyncedSelectField(): void
|
||||
{
|
||||
$formEntity = $this->createMock(Form::class);
|
||||
$fields = new ArrayCollection();
|
||||
$formField = new Field();
|
||||
$formField->setMappedField('contactselect');
|
||||
$formField->setMappedObject('contact');
|
||||
$formField->setProperties(['syncList' => false]);
|
||||
|
||||
$fields->add($formField);
|
||||
|
||||
$formEntity->expects($this->exactly(2))
|
||||
->method('getFields')
|
||||
->willReturn($fields);
|
||||
|
||||
$this->formRepository->expects($this->once())
|
||||
->method('getEntity')
|
||||
->with(5)
|
||||
->willReturn($formEntity);
|
||||
|
||||
$this->leadFieldModel->expects($this->never())
|
||||
->method('getEntityByAlias');
|
||||
|
||||
$this->formModel->getEntity(5);
|
||||
}
|
||||
|
||||
public function testGetEntityForSyncedBooleanFieldFromNotLeadObject(): void
|
||||
{
|
||||
$formEntity = $this->createMock(Form::class);
|
||||
$fields = new ArrayCollection();
|
||||
$options = ['no' => 'lunch?', 'yes' => 'dinner?'];
|
||||
$formField = new Field();
|
||||
$formField->setMappedField('contactbool');
|
||||
$formField->setMappedObject('unicorn');
|
||||
$formField->setProperties(['syncList' => true]);
|
||||
|
||||
$fields->add($formField);
|
||||
|
||||
$contactField = new LeadField();
|
||||
$contactField->setType('boolean');
|
||||
$contactField->setProperties($options);
|
||||
|
||||
$formEntity->expects($this->exactly(2))
|
||||
->method('getFields')
|
||||
->willReturn($fields);
|
||||
|
||||
$this->formRepository->expects($this->once())
|
||||
->method('getEntity')
|
||||
->with(5)
|
||||
->willReturn($formEntity);
|
||||
|
||||
$this->leadFieldModel->expects($this->never())
|
||||
->method('getEntityByAlias');
|
||||
|
||||
$this->formModel->getEntity(5);
|
||||
}
|
||||
|
||||
public function testGetEntityForSyncedBooleanField(): void
|
||||
{
|
||||
$formEntity = $this->createMock(Form::class);
|
||||
$fields = new ArrayCollection();
|
||||
$options = ['no' => 'lunch?', 'yes' => 'dinner?'];
|
||||
$formField = new Field();
|
||||
$formField->setMappedField('contactbool');
|
||||
$formField->setMappedObject('contact');
|
||||
$formField->setProperties(['syncList' => true]);
|
||||
|
||||
$fields->add($formField);
|
||||
|
||||
$contactField = new LeadField();
|
||||
$contactField->setType('boolean');
|
||||
$contactField->setProperties($options);
|
||||
|
||||
$formEntity->expects($this->exactly(2))
|
||||
->method('getFields')
|
||||
->willReturn($fields);
|
||||
|
||||
$this->formRepository->expects($this->once())
|
||||
->method('getEntity')
|
||||
->with(5)
|
||||
->willReturn($formEntity);
|
||||
|
||||
$this->leadFieldModel->expects($this->once())
|
||||
->method('getEntityByAlias')
|
||||
->with('contactbool')
|
||||
->willReturn($contactField);
|
||||
|
||||
$this->formModel->getEntity(5);
|
||||
|
||||
$this->assertSame(['lunch?', 'dinner?'], $formField->getProperties()['list']['list']);
|
||||
}
|
||||
|
||||
public function testGetEntityForSyncedCountryField(): void
|
||||
{
|
||||
$formField = $this->standardSyncListStaticFieldTest('country');
|
||||
|
||||
$this->assertArrayHasKey('Czech Republic', $formField->getProperties()['list']['list']);
|
||||
}
|
||||
|
||||
public function testGetEntityForSyncedRegionField(): void
|
||||
{
|
||||
$formField = $this->standardSyncListStaticFieldTest('region');
|
||||
|
||||
$this->assertArrayHasKey('Canada', $formField->getProperties()['list']['list']);
|
||||
}
|
||||
|
||||
public function testGetEntityForSyncedTimezoneField(): void
|
||||
{
|
||||
$formField = $this->standardSyncListStaticFieldTest('timezone');
|
||||
|
||||
$this->assertArrayHasKey('Africa', $formField->getProperties()['list']['list']);
|
||||
}
|
||||
|
||||
public function testGetEntityForSyncedLocaleField(): void
|
||||
{
|
||||
$formField = $this->standardSyncListStaticFieldTest('locale');
|
||||
|
||||
$this->assertArrayHasKey('Czech (Czechia)', $formField->getProperties()['list']['list']);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string[]>
|
||||
*/
|
||||
public static function fieldTypeProvider(): array
|
||||
{
|
||||
return [
|
||||
['select'],
|
||||
['multiselect'],
|
||||
['lookup'],
|
||||
];
|
||||
}
|
||||
|
||||
#[\PHPUnit\Framework\Attributes\DataProvider('fieldTypeProvider')]
|
||||
public function testSyncListField(string $type): void
|
||||
{
|
||||
$formEntity = $this->createMock(Form::class);
|
||||
$fields = new ArrayCollection();
|
||||
$options = [
|
||||
['label' => 'label1', 'value' => 'value1'],
|
||||
['label' => 'label2', 'value' => 'value2'],
|
||||
];
|
||||
|
||||
$formField = new Field();
|
||||
$formField->setMappedField('contactfieldalias');
|
||||
$formField->setMappedObject('contact');
|
||||
$formField->setProperties(['syncList' => true]);
|
||||
|
||||
$contactField = new LeadField();
|
||||
$contactField->setType($type);
|
||||
$contactField->setProperties(['list' => $options]);
|
||||
|
||||
$fields->add($formField);
|
||||
|
||||
$formEntity->expects($this->exactly(2))
|
||||
->method('getFields')
|
||||
->willReturn($fields);
|
||||
|
||||
$this->formRepository->expects($this->once())
|
||||
->method('getEntity')
|
||||
->with(5)
|
||||
->willReturn($formEntity);
|
||||
|
||||
$this->leadFieldModel->expects($this->once())
|
||||
->method('getEntityByAlias')
|
||||
->with('contactfieldalias')
|
||||
->willReturn($contactField);
|
||||
|
||||
$this->formModel->getEntity(5);
|
||||
|
||||
if ('lookup' === $type) {
|
||||
$expectedList = [];
|
||||
foreach ($options as $option) {
|
||||
$expectedList[$option['value']] = $option['label'];
|
||||
}
|
||||
$this->assertSame($expectedList, $formField->getProperties()['list']['list']);
|
||||
} else {
|
||||
$this->assertSame($options, $formField->getProperties()['list']['list']);
|
||||
}
|
||||
}
|
||||
|
||||
private function standardSyncListStaticFieldTest(string $type): Field
|
||||
{
|
||||
$formEntity = $this->createMock(Form::class);
|
||||
$fields = new ArrayCollection();
|
||||
$formField = new Field();
|
||||
$formField->setMappedField('contactfield');
|
||||
$formField->setMappedObject('contact');
|
||||
$formField->setProperties(['syncList' => true]);
|
||||
|
||||
$fields->add($formField);
|
||||
|
||||
$contactField = new LeadField();
|
||||
$contactField->setType($type);
|
||||
|
||||
$formEntity->expects($this->exactly(2))
|
||||
->method('getFields')
|
||||
->willReturn($fields);
|
||||
|
||||
$this->formRepository->expects($this->once())
|
||||
->method('getEntity')
|
||||
->with(5)
|
||||
->willReturn($formEntity);
|
||||
|
||||
$this->leadFieldModel->expects($this->once())
|
||||
->method('getEntityByAlias')
|
||||
->with('contactfield')
|
||||
->willReturn($contactField);
|
||||
|
||||
$this->formModel->getEntity(5);
|
||||
|
||||
return $formField;
|
||||
}
|
||||
|
||||
public function testGetContactFieldPropertiesListWhenFieldNotFound(): void
|
||||
{
|
||||
$this->leadFieldModel->expects($this->once())
|
||||
->method('getEntityByAlias');
|
||||
|
||||
$this->assertNull($this->formModel->getContactFieldPropertiesList('alias_a'));
|
||||
}
|
||||
|
||||
public function testGetContactFieldPropertiesListWhenFieldFoundButNotList(): void
|
||||
{
|
||||
$field = new LeadField();
|
||||
$field->setType('text');
|
||||
|
||||
$this->leadFieldModel->expects($this->once())
|
||||
->method('getEntityByAlias')
|
||||
->willReturn($field);
|
||||
|
||||
$this->assertNull($this->formModel->getContactFieldPropertiesList('alias_a'));
|
||||
}
|
||||
|
||||
public function testGetContactFieldPropertiesListWhenSelectFieldFound(): void
|
||||
{
|
||||
$field = new LeadField();
|
||||
$field->setType('select');
|
||||
$field->setProperties(['list' => ['choice_a' => 'Choice A']]);
|
||||
|
||||
$this->leadFieldModel->expects($this->once())
|
||||
->method('getEntityByAlias')
|
||||
->willReturn($field);
|
||||
|
||||
$this->assertSame(
|
||||
['choice_a' => 'Choice A'],
|
||||
$this->formModel->getContactFieldPropertiesList('alias_a')
|
||||
);
|
||||
}
|
||||
|
||||
public function testPopulateValuesWithLeadWithoutAutofill(): void
|
||||
{
|
||||
$formHtml = '<html>';
|
||||
$form = new Form();
|
||||
$emailField = new Field();
|
||||
$emailField->setMappedField('email');
|
||||
$emailField->setMappedObject('contact');
|
||||
$emailField->setIsAutoFill(false);
|
||||
$form->addField(123, $emailField);
|
||||
|
||||
$this->contactTracker->expects($this->never())
|
||||
->method('getContact');
|
||||
|
||||
$this->formModel->populateValuesWithLead($form, $formHtml);
|
||||
}
|
||||
|
||||
public function testPopulateValuesWithLeadWithoutLeadObject(): void
|
||||
{
|
||||
$formHtml = '<html>';
|
||||
$form = new Form();
|
||||
$emailField = new Field();
|
||||
$emailField->setMappedField('email');
|
||||
$emailField->setMappedObject('unicorn');
|
||||
$emailField->setIsAutoFill(true);
|
||||
$form->addField(123, $emailField);
|
||||
|
||||
$this->contactTracker->expects($this->never())
|
||||
->method('getContact');
|
||||
|
||||
$this->formModel->populateValuesWithLead($form, $formHtml);
|
||||
}
|
||||
|
||||
public function testPopulateValuesWithLeadWithoutLeadEntity(): void
|
||||
{
|
||||
$formHtml = '<html>';
|
||||
$form = new Form();
|
||||
$emailField = new Field();
|
||||
$emailField->setMappedField('email');
|
||||
$emailField->setMappedObject('contact');
|
||||
$emailField->setIsAutoFill(true);
|
||||
$form->addField(123, $emailField);
|
||||
|
||||
$this->contactTracker->expects($this->once())
|
||||
->method('getContact')
|
||||
->willReturn(null);
|
||||
|
||||
$this->fieldHelper->expects($this->never())
|
||||
->method('populateField');
|
||||
|
||||
$this->formModel->populateValuesWithLead($form, $formHtml);
|
||||
}
|
||||
|
||||
public function testPopulateValuesWithLeadWithoutMappedField(): void
|
||||
{
|
||||
$formHtml = '<html>';
|
||||
$form = new Form();
|
||||
$emailField = new Field();
|
||||
$emailField->setIsAutoFill(true);
|
||||
$form->addField(123, $emailField);
|
||||
|
||||
$this->contactTracker->expects($this->never())
|
||||
->method('getContact');
|
||||
|
||||
$this->formModel->populateValuesWithLead($form, $formHtml);
|
||||
}
|
||||
|
||||
public function testPopulateValuesWithLeadWithEmptyLeadFieldValue(): void
|
||||
{
|
||||
$formHtml = '<html>';
|
||||
$form = new Form();
|
||||
$emailField = new Field();
|
||||
$contact = new Lead();
|
||||
$emailField->setMappedField('email');
|
||||
$emailField->setMappedObject('contact');
|
||||
$emailField->setIsAutoFill(true);
|
||||
$form->addField(123, $emailField);
|
||||
|
||||
$this->contactTracker->method('getContact')
|
||||
->willReturn($contact);
|
||||
|
||||
$this->fieldHelper->expects($this->never())
|
||||
->method('populateField');
|
||||
|
||||
$this->formModel->populateValuesWithLead($form, $formHtml);
|
||||
}
|
||||
|
||||
public function testPopulateValuesWithLead(): void
|
||||
{
|
||||
$formHtml = '<html>';
|
||||
$form = new Form();
|
||||
$emailField = new Field();
|
||||
$contact = new Lead();
|
||||
|
||||
$emailField->setMappedField('email');
|
||||
$emailField->setMappedObject('contact');
|
||||
$emailField->setIsAutoFill(true);
|
||||
$form->addField(123, $emailField);
|
||||
|
||||
$contactCompanyData = [
|
||||
'email' => 'john@doe.email',
|
||||
];
|
||||
|
||||
$this->contactTracker->method('getContact')
|
||||
->willReturn($contact);
|
||||
|
||||
$this->primaryCompanyHelper->method('getProfileFieldsWithPrimaryCompany')
|
||||
->willReturn($contactCompanyData);
|
||||
|
||||
$this->fieldHelper->expects($this->once())
|
||||
->method('populateField')
|
||||
->with($emailField, 'john@doe.email', 'form-', $formHtml);
|
||||
|
||||
$this->formModel->populateValuesWithLead($form, $formHtml);
|
||||
}
|
||||
|
||||
public function testPopulateValuesWithLeadWithSuffixEMail(): void
|
||||
{
|
||||
$formHtml = '<html>';
|
||||
$form = new Form();
|
||||
$emailField = new Field();
|
||||
$contact = new Lead();
|
||||
|
||||
$emailField->setMappedField('email');
|
||||
$emailField->setMappedObject('contact');
|
||||
$emailField->setIsAutoFill(true);
|
||||
$form->addField(123, $emailField);
|
||||
|
||||
$contactCompanyData = [
|
||||
'email' => 'john+test@doe.email',
|
||||
];
|
||||
|
||||
$this->contactTracker->method('getContact')
|
||||
->willReturn($contact);
|
||||
|
||||
$this->primaryCompanyHelper->method('getProfileFieldsWithPrimaryCompany')
|
||||
->willReturn($contactCompanyData);
|
||||
|
||||
$this->fieldHelper->expects($this->once())
|
||||
->method('populateField')
|
||||
->with($emailField, 'john+test@doe.email', 'form-', $formHtml);
|
||||
|
||||
$this->formModel->populateValuesWithLead($form, $formHtml);
|
||||
}
|
||||
|
||||
public function testPopulateValuesWithCompany(): void
|
||||
{
|
||||
$formHtml = '<html>';
|
||||
$form = new Form();
|
||||
$companyname = new Field();
|
||||
$contact = new Lead();
|
||||
|
||||
$companyname->setMappedField('companyname');
|
||||
$companyname->setMappedObject('company');
|
||||
$companyname->setIsAutoFill(true);
|
||||
$form->addField(123, $companyname);
|
||||
|
||||
$contactCompanyData = [
|
||||
'companyname' => 'Mautic',
|
||||
];
|
||||
|
||||
$this->contactTracker->method('getContact')
|
||||
->willReturn($contact);
|
||||
|
||||
$this->primaryCompanyHelper->method('getProfileFieldsWithPrimaryCompany')
|
||||
->willReturn($contactCompanyData);
|
||||
|
||||
$this->fieldHelper->expects($this->once())
|
||||
->method('populateField')
|
||||
->with($companyname, 'Mautic', 'form-', $formHtml);
|
||||
|
||||
$this->formModel->populateValuesWithLead($form, $formHtml);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed[]
|
||||
*/
|
||||
private function getTestFormFields(): array
|
||||
{
|
||||
$fieldSession = 'mautic_'.sha1(uniqid((string) mt_rand(), true));
|
||||
$fieldSession2 = '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'],
|
||||
];
|
||||
|
||||
$fields['123'] = [
|
||||
'label' => 'Parent Field',
|
||||
'showLabel' => 1,
|
||||
'saveResult' => 1,
|
||||
'defaultValue' => false,
|
||||
'alias' => 'parent',
|
||||
'type' => 'select',
|
||||
'id' => '123',
|
||||
];
|
||||
|
||||
$fields['456'] = [
|
||||
'label' => 'Child',
|
||||
'showLabel' => 1,
|
||||
'saveResult' => 1,
|
||||
'defaultValue' => false,
|
||||
'alias' => 'child',
|
||||
'type' => 'text',
|
||||
'id' => '456',
|
||||
'parent' => '123',
|
||||
];
|
||||
|
||||
$fields[$fieldSession2] = [
|
||||
'label' => 'New Child',
|
||||
'showLabel' => 1,
|
||||
'saveResult' => 1,
|
||||
'defaultValue' => false,
|
||||
'alias' => 'new_child',
|
||||
'type' => 'text',
|
||||
'id' => $fieldSession2,
|
||||
'parent' => '123',
|
||||
];
|
||||
|
||||
return $fields;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,343 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Mautic\FormBundle\Tests\Model;
|
||||
|
||||
use Mautic\CoreBundle\Exception\FileUploadException;
|
||||
use Mautic\CoreBundle\Helper\CoreParametersHelper;
|
||||
use Mautic\CoreBundle\Helper\FileUploader;
|
||||
use Mautic\FormBundle\Crate\UploadFileCrate;
|
||||
use Mautic\FormBundle\Entity\Field;
|
||||
use Mautic\FormBundle\Entity\Form;
|
||||
use Mautic\FormBundle\Entity\Submission;
|
||||
use Mautic\FormBundle\Helper\FormUploader;
|
||||
use Symfony\Component\HttpFoundation\File\UploadedFile;
|
||||
|
||||
class FormUploaderTest extends \PHPUnit\Framework\TestCase
|
||||
{
|
||||
private $formId1 = 1;
|
||||
|
||||
private $formId2 = 2;
|
||||
|
||||
private $uploadDir = __DIR__.'/DummyFiles';
|
||||
|
||||
#[\PHPUnit\Framework\Attributes\TestDox('Uploader uploads files correctly')]
|
||||
public function testSuccessfulUploadFiles(): void
|
||||
{
|
||||
$fileUploaderMock = $this->createMock(FileUploader::class);
|
||||
$coreParametersHelperMock = $this->createMock(CoreParametersHelper::class);
|
||||
$formUploader = new FormUploader($fileUploaderMock, $coreParametersHelperMock);
|
||||
$file1Mock = $this->createMock(UploadedFile::class);
|
||||
$file2Mock = $this->createMock(UploadedFile::class);
|
||||
$form1Mock = $this->createMock(Form::class);
|
||||
$field1Mock = $this->createMock(Field::class);
|
||||
$form2Mock = $this->createMock(Form::class);
|
||||
$field2Mock = $this->createMock(Field::class);
|
||||
|
||||
$coreParametersHelperMock->expects($this->exactly(2))
|
||||
->method('get')
|
||||
->with('form_upload_dir')
|
||||
->willReturn($this->uploadDir);
|
||||
|
||||
$form1Mock->expects($this->once())
|
||||
->method('getId')
|
||||
->with()
|
||||
->willReturn($this->formId1);
|
||||
|
||||
$field1Mock->expects($this->once())
|
||||
->method('getId')
|
||||
->with()
|
||||
->willReturn('fieldId1');
|
||||
|
||||
$field1Mock->expects($this->once())
|
||||
->method('getForm')
|
||||
->with()
|
||||
->willReturn($form1Mock);
|
||||
|
||||
$field1Mock->expects($this->once())
|
||||
->method('getAlias')
|
||||
->with()
|
||||
->willReturn('file1');
|
||||
|
||||
$form2Mock->expects($this->once())
|
||||
->method('getId')
|
||||
->with()
|
||||
->willReturn($this->formId2);
|
||||
|
||||
$field2Mock->expects($this->once())
|
||||
->method('getId')
|
||||
->with()
|
||||
->willReturn('fieldId2');
|
||||
|
||||
$field2Mock->expects($this->once())
|
||||
->method('getForm')
|
||||
->with()
|
||||
->willReturn($form2Mock);
|
||||
|
||||
$field2Mock->expects($this->once())
|
||||
->method('getAlias')
|
||||
->with()
|
||||
->willReturn('file2');
|
||||
|
||||
$filesToUpload = new UploadFileCrate();
|
||||
$filesToUpload->addFile($file1Mock, $field1Mock);
|
||||
$filesToUpload->addFile($file2Mock, $field2Mock);
|
||||
|
||||
$submission = new Submission();
|
||||
$submission->setResults(['key' => 'value']);
|
||||
|
||||
$path1 = $this->uploadDir.'/1/fieldId1';
|
||||
$path2 = $this->uploadDir.'/2/fieldId2';
|
||||
$matcher = $this->exactly(2);
|
||||
|
||||
$fileUploaderMock->expects($matcher)
|
||||
->method('upload')->willReturnCallback(function (...$parameters) use ($matcher, $path1, $file1Mock, $path2, $file2Mock) {
|
||||
if (1 === $matcher->numberOfInvocations()) {
|
||||
$this->assertSame($path1, $parameters[0]);
|
||||
$this->assertSame($file1Mock, $parameters[1]);
|
||||
|
||||
return 'upload1.jpg';
|
||||
}
|
||||
if (2 === $matcher->numberOfInvocations()) {
|
||||
$this->assertSame($path2, $parameters[0]);
|
||||
$this->assertSame($file2Mock, $parameters[1]);
|
||||
|
||||
return 'upload2.txt';
|
||||
}
|
||||
});
|
||||
|
||||
$formUploader->uploadFiles($filesToUpload, $submission);
|
||||
|
||||
$expected = [
|
||||
'key' => 'value',
|
||||
'file1' => 'upload1.jpg',
|
||||
'file2' => 'upload2.txt',
|
||||
];
|
||||
|
||||
$this->assertSame($expected, $submission->getResults());
|
||||
}
|
||||
|
||||
#[\PHPUnit\Framework\Attributes\TestDox('Uploader delete uploaded file if anz error occures')]
|
||||
public function testUploadFilesWithError(): void
|
||||
{
|
||||
$fileUploaderMock = $this->createMock(FileUploader::class);
|
||||
$coreParametersHelperMock = $this->createMock(CoreParametersHelper::class);
|
||||
$formUploader = new FormUploader($fileUploaderMock, $coreParametersHelperMock);
|
||||
$file1Mock = $this->createMock(UploadedFile::class);
|
||||
$file2Mock = $this->createMock(UploadedFile::class);
|
||||
$form1Mock = $this->createMock(Form::class);
|
||||
$field1Mock = $this->createMock(Field::class);
|
||||
$form2Mock = $this->createMock(Form::class);
|
||||
$field2Mock = $this->createMock(Field::class);
|
||||
|
||||
$coreParametersHelperMock->expects($this->exactly(2))
|
||||
->method('get')
|
||||
->with('form_upload_dir')
|
||||
->willReturn($this->uploadDir);
|
||||
|
||||
$form1Mock->expects($this->once())
|
||||
->method('getId')
|
||||
->willReturn($this->formId1);
|
||||
|
||||
$field1Mock->expects($this->once())
|
||||
->method('getId')
|
||||
->willReturn('fieldId1');
|
||||
|
||||
$field1Mock->expects($this->once())
|
||||
->method('getForm')
|
||||
->willReturn($form1Mock);
|
||||
|
||||
$field1Mock->expects($this->once())
|
||||
->method('getAlias')
|
||||
->willReturn('file1');
|
||||
|
||||
$form2Mock->expects($this->once())
|
||||
->method('getId')
|
||||
->willReturn($this->formId2);
|
||||
|
||||
$field2Mock->expects($this->once())
|
||||
->method('getId')
|
||||
->willReturn('fieldId2');
|
||||
|
||||
$field2Mock->expects($this->once())
|
||||
->method('getForm')
|
||||
->willReturn($form2Mock);
|
||||
|
||||
$field2Mock->expects($this->once())
|
||||
->method('getAlias')
|
||||
->willReturn('file2');
|
||||
|
||||
$filesToUpload = new UploadFileCrate();
|
||||
$filesToUpload->addFile($file1Mock, $field1Mock);
|
||||
$filesToUpload->addFile($file2Mock, $field2Mock);
|
||||
|
||||
$submission = new Submission();
|
||||
$submission->setResults(['key' => 'value']);
|
||||
|
||||
$path1 = $this->uploadDir.'/1/fieldId1';
|
||||
$path2 = $this->uploadDir.'/2/fieldId2';
|
||||
$matcher = $this->exactly(2);
|
||||
|
||||
$fileUploaderMock->expects($matcher)
|
||||
->method('upload')->willReturnCallback(function (...$parameters) use ($matcher, $path1, $file1Mock, $path2, $file2Mock) {
|
||||
if (1 === $matcher->numberOfInvocations()) {
|
||||
$this->assertSame($path1, $parameters[0]);
|
||||
$this->assertSame($file1Mock, $parameters[1]);
|
||||
|
||||
return 'upload1.jpg';
|
||||
}
|
||||
if (2 === $matcher->numberOfInvocations()) {
|
||||
$this->assertSame($path2, $parameters[0]);
|
||||
$this->assertSame($file2Mock, $parameters[1]);
|
||||
throw new FileUploadException();
|
||||
}
|
||||
});
|
||||
|
||||
$fileUploaderMock->expects($this->once())
|
||||
->method('delete')
|
||||
->with($this->uploadDir.'/1/fieldId1/upload1.jpg');
|
||||
|
||||
$this->expectException(FileUploadException::class);
|
||||
$this->expectExceptionMessage('file2');
|
||||
|
||||
$formUploader->uploadFiles($filesToUpload, $submission);
|
||||
|
||||
$expected = [
|
||||
'key' => 'value',
|
||||
'file1' => 'upload1.jpg',
|
||||
'file2' => 'upload2.txt',
|
||||
];
|
||||
|
||||
$this->assertSame($expected, $submission->getResults());
|
||||
}
|
||||
|
||||
#[\PHPUnit\Framework\Attributes\TestDox('Uploader do nothing if no files for upload provided')]
|
||||
public function testNoFilesUploadFiles(): void
|
||||
{
|
||||
$fileUploaderMock = $this->createMock(FileUploader::class);
|
||||
|
||||
$fileUploaderMock->expects($this->never())
|
||||
->method('upload');
|
||||
|
||||
$fileUploaderMock->expects($this->never())
|
||||
->method('delete');
|
||||
|
||||
$coreParametersHelperMock = $this->createMock(CoreParametersHelper::class);
|
||||
|
||||
$formUploader = new FormUploader($fileUploaderMock, $coreParametersHelperMock);
|
||||
|
||||
$filesToUpload = new UploadFileCrate();
|
||||
$submission = new Submission();
|
||||
|
||||
$formUploader->uploadFiles($filesToUpload, $submission);
|
||||
}
|
||||
|
||||
#[\PHPUnit\Framework\Attributes\TestDox('Uploader returs correct path for file')]
|
||||
public function testGetCompleteFilePath(): void
|
||||
{
|
||||
$fileUploaderMock = $this->createMock(FileUploader::class);
|
||||
|
||||
$coreParametersHelperMock = $this->createMock(CoreParametersHelper::class);
|
||||
$coreParametersHelperMock->expects($this->once())
|
||||
->method('get')
|
||||
->with('form_upload_dir')
|
||||
->willReturn($this->uploadDir);
|
||||
|
||||
$formMock = $this->createMock(Form::class);
|
||||
|
||||
$formMock->expects($this->once())
|
||||
->method('getId')
|
||||
->with()
|
||||
->willReturn($this->formId1);
|
||||
|
||||
$fieldMock = $this->createMock(Field::class);
|
||||
|
||||
$fieldMock->expects($this->once())
|
||||
->method('getId')
|
||||
->with()
|
||||
->willReturn('fieldId1');
|
||||
|
||||
$fieldMock->expects($this->once())
|
||||
->method('getForm')
|
||||
->with()
|
||||
->willReturn($formMock);
|
||||
|
||||
$formUploader = new FormUploader($fileUploaderMock, $coreParametersHelperMock);
|
||||
|
||||
$actual = $formUploader->getCompleteFilePath($fieldMock, 'fileName');
|
||||
|
||||
$this->assertSame($this->uploadDir.'/1/fieldId1/fileName', $actual);
|
||||
}
|
||||
|
||||
#[\PHPUnit\Framework\Attributes\TestDox('Uploader delete files correctly')]
|
||||
public function testDeleteAllFilesOfFormField(): void
|
||||
{
|
||||
$fileUploaderMock = $this->createMock(FileUploader::class);
|
||||
|
||||
$fileUploaderMock->expects($this->once())
|
||||
->method('delete')
|
||||
->with($this->uploadDir.'/1/fieldId1');
|
||||
|
||||
$coreParametersHelperMock = $this->createMock(CoreParametersHelper::class);
|
||||
$coreParametersHelperMock->expects($this->once())
|
||||
->method('get')
|
||||
->with('form_upload_dir')
|
||||
->willReturn($this->uploadDir);
|
||||
|
||||
$formUploader = new FormUploader($fileUploaderMock, $coreParametersHelperMock);
|
||||
|
||||
$formMock = $this->createMock(Form::class);
|
||||
|
||||
$formMock->expects($this->once())
|
||||
->method('getId')
|
||||
->with()
|
||||
->willReturn($this->formId1);
|
||||
|
||||
$fieldMock = $this->createMock(Field::class);
|
||||
|
||||
$fieldMock->expects($this->once())
|
||||
->method('getId')
|
||||
->with()
|
||||
->willReturn('fieldId1');
|
||||
|
||||
$fieldMock->expects($this->once())
|
||||
->method('getForm')
|
||||
->with()
|
||||
->willReturn($formMock);
|
||||
|
||||
$fieldMock->expects($this->once())
|
||||
->method('isFileType')
|
||||
->with()
|
||||
->willReturn(true);
|
||||
|
||||
$formUploader->deleteAllFilesOfFormField($fieldMock);
|
||||
}
|
||||
|
||||
public function testDeleteFilesOfForm(): void
|
||||
{
|
||||
$fileUploaderMock = $this->createMock(FileUploader::class);
|
||||
$formMock = $this->createMock(Form::class);
|
||||
$coreParametersHelperMock = $this->createMock(CoreParametersHelper::class);
|
||||
|
||||
$fileUploaderMock
|
||||
->method('delete')
|
||||
->with($this->uploadDir.'/1');
|
||||
|
||||
$coreParametersHelperMock->expects($this->exactly(2))
|
||||
->method('get')
|
||||
->with('form_upload_dir')
|
||||
->willReturn($this->uploadDir);
|
||||
|
||||
$formMock->expects($this->exactly(2))
|
||||
->method('getId')
|
||||
->willReturnOnConsecutiveCalls($this->formId1, null);
|
||||
|
||||
$formUploader = new FormUploader($fileUploaderMock, $coreParametersHelperMock);
|
||||
$formUploader->deleteFilesOfForm($formMock);
|
||||
|
||||
$formMock->deletedId = $this->formId1;
|
||||
|
||||
$formUploader->deleteFilesOfForm($formMock);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,207 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Mautic\FormBundle\Tests\Model;
|
||||
|
||||
use Mautic\CoreBundle\Test\MauticMysqlTestCase;
|
||||
use Mautic\LeadBundle\Entity\Company;
|
||||
use Mautic\LeadBundle\Entity\Lead;
|
||||
use PHPUnit\Framework\Assert;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
final class SubmissionModelFunctionalTest extends MauticMysqlTestCase
|
||||
{
|
||||
protected $useCleanupRollback = false;
|
||||
|
||||
public function testSaveSubmissionChangeCompanyField(): void
|
||||
{
|
||||
[$formId, $formAlias] = $this->createFormWithCompanies();
|
||||
|
||||
$this->submitFormWithCompanies($formId, $formAlias, 'test@acquia.cz', 'Luk', 'Doe', 'Acquia', 'Keplerova');
|
||||
|
||||
// Check the address.
|
||||
$companyRepository = $this->em->getRepository(Company::class);
|
||||
$companiesOriginal = $companyRepository->findBy(['address1' => 'Keplerova']);
|
||||
Assert::assertCount(1, $companiesOriginal);
|
||||
|
||||
// Create contact with the same company but different address.
|
||||
$this->submitFormWithCompanies($formId, $formAlias, 'test2@acquia.cz', 'Luk', 'Syk', 'Acquia', 'Krejpskeho');
|
||||
|
||||
// Check that the address is changed.
|
||||
$companiesOld = $companyRepository->findBy(['address1' => 'Keplerova']);
|
||||
Assert::assertCount(0, $companiesOld);
|
||||
$companiesNew = $companyRepository->findBy(['address1' => 'Krejpskeho']);
|
||||
Assert::assertCount(1, $companiesNew);
|
||||
}
|
||||
|
||||
public function testSaveSubmissionChangeContactField(): void
|
||||
{
|
||||
[$formId, $formAlias] = $this->createFormWithoutCompanies();
|
||||
|
||||
$this->submitFormWithoutCompanies($formId, $formAlias, 'test@acquia.cz', 'Luk', 'Doe Smith');
|
||||
|
||||
// Check the contact.
|
||||
$contactRepository = $this->em->getRepository(Lead::class);
|
||||
$contactsOriginal = $contactRepository->findBy(['lastname' => 'Doe Smith']);
|
||||
Assert::assertCount(1, $contactsOriginal);
|
||||
|
||||
// Create contact with the same email but different lastname.
|
||||
$this->submitFormWithoutCompanies($formId, $formAlias, 'test@acquia.cz', 'Luk', 'Sykora');
|
||||
|
||||
// Check that the address is changed.
|
||||
$contactsOld = $contactRepository->findBy(['lastname' => 'Doe Smith']);
|
||||
Assert::assertCount(0, $contactsOld);
|
||||
$contactsNew = $contactRepository->findBy(['lastname' => 'Sykora']);
|
||||
Assert::assertCount(1, $contactsNew);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed[]
|
||||
*/
|
||||
private function createFormWithCompanies(): array
|
||||
{
|
||||
$payload = [
|
||||
'name' => 'FormTest',
|
||||
'description' => 'Form created via submission test',
|
||||
'formType' => 'standalone',
|
||||
'isPublished' => true,
|
||||
'fields' => [
|
||||
[
|
||||
'label' => 'Email',
|
||||
'type' => 'email',
|
||||
'alias' => 'email',
|
||||
'mappedObject' => 'contact',
|
||||
'mappedField' => 'email',
|
||||
],
|
||||
[
|
||||
'label' => 'First Name',
|
||||
'type' => 'text',
|
||||
'alias' => 'firstname',
|
||||
'mappedObject' => 'contact',
|
||||
'mappedField' => 'firstname',
|
||||
],
|
||||
[
|
||||
'label' => 'Last Name',
|
||||
'type' => 'text',
|
||||
'alias' => 'lastname',
|
||||
'mappedObject' => 'contact',
|
||||
'mappedField' => 'lastname',
|
||||
],
|
||||
[
|
||||
'label' => 'Company',
|
||||
'type' => 'text',
|
||||
'alias' => 'companyname',
|
||||
'mappedObject' => 'company',
|
||||
'mappedField' => 'companyname',
|
||||
],
|
||||
[
|
||||
'label' => 'Company Address',
|
||||
'type' => 'text',
|
||||
'alias' => 'companyaddress1',
|
||||
'mappedObject' => 'company',
|
||||
'mappedField' => 'companyaddress1',
|
||||
],
|
||||
[
|
||||
'label' => 'Submit',
|
||||
'type' => 'button',
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
return $this->createForm($payload);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed[]
|
||||
*/
|
||||
private function createFormWithoutCompanies(): array
|
||||
{
|
||||
$payload = [
|
||||
'name' => 'FormTest',
|
||||
'description' => 'Form created via submission test',
|
||||
'formType' => 'standalone',
|
||||
'isPublished' => true,
|
||||
'fields' => [
|
||||
[
|
||||
'label' => 'Email',
|
||||
'type' => 'email',
|
||||
'alias' => 'email',
|
||||
'mappedObject' => 'contact',
|
||||
'mappedField' => 'email',
|
||||
],
|
||||
[
|
||||
'label' => 'First Name',
|
||||
'type' => 'text',
|
||||
'alias' => 'firstname',
|
||||
'mappedObject' => 'contact',
|
||||
'mappedField' => 'firstname',
|
||||
],
|
||||
[
|
||||
'label' => 'Last Name',
|
||||
'type' => 'text',
|
||||
'alias' => 'lastname',
|
||||
'mappedObject' => 'contact',
|
||||
'mappedField' => 'lastname',
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
return $this->createForm($payload);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed[] $payload
|
||||
*
|
||||
* @return array{int,string}
|
||||
*/
|
||||
private function createForm(array $payload): array
|
||||
{
|
||||
$this->client->request(Request::METHOD_POST, '/api/forms/new', $payload);
|
||||
$clientResponse = $this->client->getResponse();
|
||||
$response = json_decode($clientResponse->getContent(), true);
|
||||
$formId = $response['form']['id'];
|
||||
$formAlias = $response['form']['alias'];
|
||||
Assert::assertSame(Response::HTTP_CREATED, $clientResponse->getStatusCode(), $clientResponse->getContent());
|
||||
|
||||
return [$formId, $formAlias];
|
||||
}
|
||||
|
||||
private function submitFormWithCompanies(int $formId, string $formAlias, string $email, string $firstname, string $lastname, string $company, string $companyAddress): void
|
||||
{
|
||||
$values = [
|
||||
'mauticform[email]' => $email,
|
||||
'mauticform[firstname]' => $firstname,
|
||||
'mauticform[lastname]' => $lastname,
|
||||
'mauticform[companyname]' => $company,
|
||||
'mauticform[companyaddress1]' => $companyAddress,
|
||||
];
|
||||
$this->submitForm($formId, $formAlias, $values);
|
||||
}
|
||||
|
||||
private function submitFormWithoutCompanies(int $formId, string $formAlias, string $email, string $firstname, string $lastname): void
|
||||
{
|
||||
$values = [
|
||||
'mauticform[email]' => $email,
|
||||
'mauticform[firstname]' => $firstname,
|
||||
'mauticform[lastname]' => $lastname,
|
||||
];
|
||||
$this->submitForm($formId, $formAlias, $values);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string,string> $values
|
||||
*/
|
||||
private function submitForm(int $formId, string $formAlias, array $values): void
|
||||
{
|
||||
$crawler = $this->client->request(Request::METHOD_GET, "/form/{$formId}");
|
||||
$this->assertResponseIsSuccessful();
|
||||
$formCrawler = $crawler->filter('form[id=mauticform_'.$formAlias.']');
|
||||
$this::assertCount(1, $formCrawler, $this->client->getResponse()->getContent());
|
||||
$form = $formCrawler->form();
|
||||
$form->setValues($values);
|
||||
$this->client->submit($form);
|
||||
Assert::assertTrue($this->client->getResponse()->isOk(), $this->client->getResponse()->getContent());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,635 @@
|
||||
<?php
|
||||
|
||||
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\CsvHelper;
|
||||
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\Field\FieldsWithUniqueIdentifier;
|
||||
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&FieldsWithUniqueIdentifier
|
||||
*/
|
||||
private MockObject $fieldsWithUniqueIdentifier;
|
||||
|
||||
/**
|
||||
* @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->fieldsWithUniqueIdentifier = $this->createMock(FieldsWithUniqueIdentifier::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->fieldsWithUniqueIdentifier,
|
||||
$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->fieldsWithUniqueIdentifier->expects($this->any())
|
||||
->method('getFieldsWithUniqueIdentifier')
|
||||
->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')
|
||||
->willReturnMap(
|
||||
[
|
||||
[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' => 'test@email.com',
|
||||
'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);
|
||||
|
||||
$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' => 'test@email.com'], $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())
|
||||
->willReturnCallback(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(fn ($line) => CsvHelper::strGetCsv($line), 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' => 'a@b.c',
|
||||
],
|
||||
[
|
||||
'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', 'a@b.c'], $result);
|
||||
}
|
||||
|
||||
public function testGetExportRowForPage(): void
|
||||
{
|
||||
$email = 'a@b.c';
|
||||
$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);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
<?php
|
||||
|
||||
namespace Mautic\FormBundle\Tests\ProgressiveProfiling;
|
||||
|
||||
use Mautic\FormBundle\Entity\Field;
|
||||
use Mautic\FormBundle\Entity\Form;
|
||||
use Mautic\FormBundle\ProgressiveProfiling\DisplayCounter;
|
||||
use Mautic\FormBundle\ProgressiveProfiling\DisplayManager;
|
||||
|
||||
class DisplayManagerTest extends \PHPUnit\Framework\TestCase
|
||||
{
|
||||
private Form $form;
|
||||
|
||||
private array $viewOnlyFields;
|
||||
|
||||
private DisplayCounter $displayCounter;
|
||||
|
||||
public function setUp(): void
|
||||
{
|
||||
$this->viewOnlyFields = [];
|
||||
$this->form = new Form();
|
||||
$this->displayCounter = new DisplayCounter($this->form);
|
||||
}
|
||||
|
||||
public function testShowForField(): void
|
||||
{
|
||||
$form = new Form();
|
||||
$viewOnlyFields = ['button'];
|
||||
$displayManager = new DisplayManager($form, $viewOnlyFields);
|
||||
$displayCounter = $displayManager->getDisplayCounter();
|
||||
|
||||
$field = new Field();
|
||||
$this->assertTrue($displayManager->showForField($field));
|
||||
|
||||
$field->setType('button');
|
||||
$this->assertTrue($displayManager->showForField($field));
|
||||
|
||||
$field->setType('text');
|
||||
|
||||
// display If first field is always display and progressive limit 1
|
||||
$field->setAlwaysDisplay(true);
|
||||
$form->setProgressiveProfilingLimit(1);
|
||||
$this->assertTrue($displayManager->showForField($field));
|
||||
|
||||
// not display If second field is always display and progressive limit 1
|
||||
$displayCounter->increaseDisplayedFields();
|
||||
$this->assertFalse($displayManager->showForField($field));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,122 @@
|
||||
<?php
|
||||
|
||||
namespace Mautic\FormBundle\Tests\Validator;
|
||||
|
||||
use Mautic\CoreBundle\Exception\FileInvalidException;
|
||||
use Mautic\CoreBundle\Validator\FileUploadValidator;
|
||||
use Mautic\FormBundle\Entity\Field;
|
||||
use Mautic\FormBundle\Exception\FileValidationException;
|
||||
use Mautic\FormBundle\Exception\NoFileGivenException;
|
||||
use Mautic\FormBundle\Validator\UploadFieldValidator;
|
||||
use Symfony\Component\HttpFoundation\File\UploadedFile;
|
||||
use Symfony\Component\HttpFoundation\FileBag;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
|
||||
#[\PHPUnit\Framework\Attributes\CoversClass(UploadFieldValidator::class)]
|
||||
class UploadFieldValidatorTest extends \PHPUnit\Framework\TestCase
|
||||
{
|
||||
#[\PHPUnit\Framework\Attributes\TestDox('No Files given')]
|
||||
public function testNoFilesGiven(): void
|
||||
{
|
||||
$fileUploadValidatorMock = $this->createMock(FileUploadValidator::class);
|
||||
|
||||
$fileUploadValidatorMock->expects($this->never())
|
||||
->method('validate');
|
||||
|
||||
$parameterBagMock = $this->createMock(FileBag::class);
|
||||
|
||||
$parameterBagMock->expects($this->once())
|
||||
->method('get')
|
||||
->with('mauticform')
|
||||
->willReturn(false);
|
||||
|
||||
$request = new Request();
|
||||
$request->files = $parameterBagMock;
|
||||
|
||||
$fileUploadValidator = new UploadFieldValidator($fileUploadValidatorMock);
|
||||
|
||||
$field = new Field();
|
||||
|
||||
$this->expectException(NoFileGivenException::class);
|
||||
|
||||
$fileUploadValidator->processFileValidation($field, $request);
|
||||
}
|
||||
|
||||
#[\PHPUnit\Framework\Attributes\TestDox('Exception should be thrown when validation fails')]
|
||||
public function testValidationFailed(): void
|
||||
{
|
||||
$fileUploadValidatorMock = $this->createMock(FileUploadValidator::class);
|
||||
|
||||
$fileUploadValidatorMock->expects($this->once())
|
||||
->method('validate')
|
||||
->willThrowException(new FileInvalidException('Validation failed'));
|
||||
|
||||
$parameterBagMock = $this->createMock(FileBag::class);
|
||||
|
||||
$fileMock = $this->createMock(UploadedFile::class);
|
||||
|
||||
$files = [
|
||||
'file' => $fileMock,
|
||||
];
|
||||
|
||||
$parameterBagMock->expects($this->once())
|
||||
->method('get')
|
||||
->with('mauticform')
|
||||
->willReturn($files);
|
||||
|
||||
$request = new Request();
|
||||
$request->files = $parameterBagMock;
|
||||
|
||||
$fileUploadValidator = new UploadFieldValidator($fileUploadValidatorMock);
|
||||
|
||||
$field = new Field();
|
||||
$field->setAlias('file');
|
||||
$field->setProperties([
|
||||
'allowed_file_size' => 1,
|
||||
'allowed_file_extensions' => ['jpg', 'gif'],
|
||||
]);
|
||||
|
||||
$this->expectException(FileValidationException::class);
|
||||
$this->expectExceptionMessage('Validation failed');
|
||||
|
||||
$fileUploadValidator->processFileValidation($field, $request);
|
||||
}
|
||||
|
||||
#[\PHPUnit\Framework\Attributes\TestDox('No validation error')]
|
||||
public function testFileIsValid(): void
|
||||
{
|
||||
$fileUploadValidatorMock = $this->createMock(FileUploadValidator::class);
|
||||
|
||||
$fileUploadValidatorMock->expects($this->once())
|
||||
->method('validate');
|
||||
|
||||
$parameterBagMock = $this->createMock(FileBag::class);
|
||||
|
||||
$fileMock = $this->createMock(UploadedFile::class);
|
||||
|
||||
$files = [
|
||||
'file' => $fileMock,
|
||||
];
|
||||
|
||||
$parameterBagMock->expects($this->once())
|
||||
->method('get')
|
||||
->with('mauticform')
|
||||
->willReturn($files);
|
||||
|
||||
$request = new Request();
|
||||
$request->files = $parameterBagMock;
|
||||
|
||||
$fileUploadValidator = new UploadFieldValidator($fileUploadValidatorMock);
|
||||
|
||||
$field = new Field();
|
||||
$field->setAlias('file');
|
||||
$field->setProperties([
|
||||
'allowed_file_size' => 1,
|
||||
'allowed_file_extensions' => ['jpg', 'gif'],
|
||||
]);
|
||||
|
||||
$file = $fileUploadValidator->processFileValidation($field, $request);
|
||||
|
||||
$this->assertSame($fileMock, $file);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user