Skip to content

Commit

Permalink
Add form helper for tests
Browse files Browse the repository at this point in the history
  • Loading branch information
eileenmcnaughton committed Sep 3, 2023
1 parent 09b8113 commit 197882e
Show file tree
Hide file tree
Showing 4 changed files with 354 additions and 11 deletions.
35 changes: 35 additions & 0 deletions Civi/Test/FormTrait.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<?php
/*
+--------------------------------------------------------------------+
| Copyright CiviCRM LLC. All rights reserved. |
| |
| This work is published under the GNU AGPLv3 license with some |
| permitted exceptions and without any warranty. For full license |
| and copyright information, see https://civicrm.org/licensing |
+--------------------------------------------------------------------+
*/

namespace Civi\Test;

use Civi\Test\FormWrappers\EventFormParticipant;

/**
* Trait for writing tests interacting with QuickForm.
*/
trait FormTrait {

/**
* @param $formName
* @param $submittedValues
* @param array $urlParameters
*
* @return \Civi\Test\FormWrapper
*/
public function getTestForm($formName, $submittedValues, array $urlParameters = []) {
if ($formName === 'CRM_Event_Form_Participant') {
return new EventFormParticipant($formName, $submittedValues, $urlParameters);
}
return new FormWrapper($formName, $submittedValues, $urlParameters);
}

}
277 changes: 277 additions & 0 deletions Civi/Test/FormWrapper.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,277 @@
<?php
/*
+--------------------------------------------------------------------+
| Copyright CiviCRM LLC. All rights reserved. |
| |
| This work is published under the GNU AGPLv3 license with some |
| permitted exceptions and without any warranty. For full license |
| and copyright information, see https://civicrm.org/licensing |
+--------------------------------------------------------------------+
*/

namespace Civi\Test;

use Civi\Api4\Utils\ReflectionUtils;

class FormWrapper {

/**
* @var string
*/
private $formName;

/**
* @var array
*/
private $urlParameters;

/**
* @var array
*/
private $formValues;

/**
* @var \CRM_Core_Form
*/
protected $form;

private $output;

private $templateVariables;

private $mail;

private $redirects;

private $validation;

public const CONSTRUCTED = 0;
public const PREPROCESSED = 1;
public const BUILT = 3;
public const VALIDATED = 4;
public const SUBMITTED = 5;

/**
* @var \CRM_Contribute_Import_Controller|\CRM_Core_Controller|\CRM_Event_Controller_Registration
*/
private $formController;

/**
* @param string $formName
* @param array $formValues
* @param array $urlParameters
*/
public function __construct(string $formName, array $formValues = [], array $urlParameters = []) {
$this->formName = $formName;
$this->urlParameters = $urlParameters;
$this->formValues = $formValues;
}

/**
* Process a CiviCRM form.
*
* @param int $state
*
* @return \Civi\Test\FormWrapper
*/
public function processForm(int $state = self::SUBMITTED): self {
$this->setFormObject($this->formName, $this->formValues, $this->urlParameters);
if ($state > self::CONSTRUCTED) {
$this->form->preProcess();
}
if ($state > self::PREPROCESSED) {
$this->form->buildForm();
}
if ($state > self::BUILT) {
$this->validation = $this->form->validate();
}
if ($state > self::VALIDATED) {
$this->form->postProcess();
}
return $this;
}

/**
* Call a function declared as externally accessible on the form.
*
* This will only call a limited number of form functions that are either
* a) supported from use from outside of core or
* b) direct form flow functions
*
* As this class is expected to be used in extension tests we don't want to
* perpetuate the current issue with internal / unsupported properties being
* accessed willy nilly - so this provides testing support using supported
* or intrinsic functions only.
*
* @param string $name
* @param array $arguments
*
* @return mixed
* @throws \CRM_Core_Exception
* @throws \ReflectionException
*/
public function __call(string $name, array $arguments) {
if (!empty(ReflectionUtils::getCodeDocs((new \ReflectionMethod($this->form, $name)), 'Method')['api'])) {
return call_user_func([$this->form, $name], $arguments);
}
throw new \CRM_Core_Exception($name . ' method not supported for external use');
}

/**
* Call form post process function.
*
* @return $this
*/
public function postProcess(): self {
$this->form->postProcess();
return $this;
}

/**
* Instantiate form object.
*
* We need to instantiate the form to run preprocess, which means we have to trick it about the request method.
*
* @param string $class
* Name of form class.
*
* @param array $formValues
*
* @param array $urlParameters
*/
private function setFormObject(string $class, array $formValues = [], array $urlParameters = []) {
$_POST = $formValues;
$this->form = new $class();
$_SERVER['REQUEST_METHOD'] = 'GET';
$_REQUEST += $urlParameters;
switch ($class) {
case 'CRM_Event_Cart_Form_Checkout_Payment':
case 'CRM_Event_Cart_Form_Checkout_ParticipantsAndPrices':
$this->form->controller = new \CRM_Event_Cart_Controller_Checkout();
break;

case 'CRM_Event_Form_Registration_Register':
$this->form->controller = $this->formController = new \CRM_Event_Controller_Registration();
break;

case 'CRM_Event_Form_Registration_Confirm':
case 'CRM_Event_Form_Registration_AdditionalParticipant':
if ($this->formController) {
// Add to the existing form controller.
$this->form->controller = $this->formController;
}
else {
$this->form->controller = $this->formController = new \CRM_Event_Controller_Registration();
}
break;

case 'CRM_Contribute_Form_Contribution_Main':
$this->form->controller = new \CRM_Contribute_Controller_Contribution();
break;

case 'CRM_Contribute_Form_Contribution_Confirm':
$this->form->controller = new \CRM_Contribute_Controller_Contribution();
$this->form->controller->setStateMachine(new \CRM_Contribute_StateMachine_Contribution($this->form->controller));
// The submitted values are on the Main form.
$_SESSION['_' . $this->form->controller->_name . '_container']['values']['Main'] = $formValues;
return;

case 'CRM_Contact_Import_Form_DataSource':
case 'CRM_Contact_Import_Form_MapField':
case 'CRM_Contact_Import_Form_Preview':
$this->form->controller = new \CRM_Contact_Import_Controller();
$this->form->controller->setStateMachine(new \CRM_Core_StateMachine($this->form->controller));
// The submitted values should be set on one or the other of the forms in the flow.
// For test simplicity we set on all rather than figuring out which ones go where....
$_SESSION['_' . $this->form->controller->_name . '_container']['values']['DataSource'] = $formValues;
$_SESSION['_' . $this->form->controller->_name . '_container']['values']['MapField'] = $formValues;
$_SESSION['_' . $this->form->controller->_name . '_container']['values']['Preview'] = $formValues;
return;

case 'CRM_Contribute_Import_Form_DataSource':
case 'CRM_Contribute_Import_Form_MapField':
case 'CRM_Contribute_Import_Form_Preview':
if ($this->formController) {
// Add to the existing form controller.
$this->form->controller = $this->formController;
}
else {
$this->form->controller = new \CRM_Contribute_Import_Controller();
$this->form->controller->setStateMachine(new \CRM_Core_StateMachine($this->form->controller));
$this->formController = $this->form->controller;
}
// The submitted values should be set on one or the other of the forms in the flow.
// For test simplicity we set on all rather than figuring out which ones go where....
$_SESSION['_' . $this->form->controller->_name . '_container']['values']['DataSource'] = $formValues;
$_SESSION['_' . $this->form->controller->_name . '_container']['values']['MapField'] = $formValues;
$_SESSION['_' . $this->form->controller->_name . '_container']['values']['Preview'] = $formValues;
return;

case 'CRM_Member_Import_Form_DataSource':
case 'CRM_Member_Import_Form_MapField':
case 'CRM_Member_Import_Form_Preview':
$this->form->controller = new \CRM_Member_Import_Controller();
$this->form->controller->setStateMachine(new \CRM_Core_StateMachine($this->form->controller));
// The submitted values should be set on one or the other of the forms in the flow.
// For test simplicity we set on all rather than figuring out which ones go where....
$_SESSION['_' . $this->form->controller->_name . '_container']['values']['DataSource'] = $this->formValues;
$_SESSION['_' . $this->form->controller->_name . '_container']['values']['MapField'] = $formValues;
$_SESSION['_' . $this->form->controller->_name . '_container']['values']['Preview'] = $formValues;
return;

case 'CRM_Event_Import_Form_DataSource':
case 'CRM_Event_Import_Form_MapField':
case 'CRM_Event_Import_Form_Preview':
$this->form->controller = new \CRM_Event_Import_Controller();
$this->form->controller->setStateMachine(new \CRM_Core_StateMachine($this->form->controller));
// The submitted values should be set on one or the other of the forms in the flow.
// For test simplicity we set on all rather than figuring out which ones go where....
$_SESSION['_' . $this->form->controller->_name . '_container']['values']['DataSource'] = $formValues;
$_SESSION['_' . $this->form->controller->_name . '_container']['values']['MapField'] = $formValues;
$_SESSION['_' . $this->form->controller->_name . '_container']['values']['Preview'] = $formValues;
return;

case 'CRM_Activity_Import_Form_DataSource':
case 'CRM_Activity_Import_Form_MapField':
case 'CRM_Activity_Import_Form_Preview':
$this->form->controller = new \CRM_Activity_Import_Controller();
$this->form->controller->setStateMachine(new \CRM_Core_StateMachine($this->form->controller));
// The submitted values should be set on one or the other of the forms in the flow.
// For test simplicity we set on all rather than figuring out which ones go where....
$_SESSION['_' . $this->form->controller->_name . '_container']['values']['DataSource'] = $formValues;
$_SESSION['_' . $this->form->controller->_name . '_container']['values']['MapField'] = $formValues;
$_SESSION['_' . $this->form->controller->_name . '_container']['values']['Preview'] = $formValues;
return;

case 'CRM_Custom_Import_Form_DataSource':
case 'CRM_Custom_Import_Form_MapField':
case 'CRM_Custom_Import_Form_Preview':
$this->form->controller = new \CRM_Custom_Import_Controller();
$this->form->controller->setStateMachine(new \CRM_Core_StateMachine($this->form->controller));
// The submitted values should be set on one or the other of the forms in the flow.
// For test simplicity we set on all rather than figuring out which ones go where....
$_SESSION['_' . $this->form->controller->_name . '_container']['values']['DataSource'] = $formValues;
$_SESSION['_' . $this->form->controller->_name . '_container']['values']['MapField'] = $formValues;
$_SESSION['_' . $this->form->controller->_name . '_container']['values']['Preview'] = $formValues;
return;

case strpos($class, 'Search') !== FALSE:
$this->form->controller = new \CRM_Contact_Controller_Search();
break;

case strpos($class, '_Form_') !== FALSE:
$this->form->controller = new \CRM_Core_Controller_Simple($class, $this->form->getName());
break;

default:
$this->form->controller = new \CRM_Core_Controller();
}

$this->form->controller->setStateMachine(new \CRM_Core_StateMachine($this->form->controller));
$_SESSION['_' . $this->form->controller->_name . '_container']['values'][$this->form->getName()] = $formValues;
if (isset($formValues['_qf_button_name'])) {
$_SESSION['_' . $this->form->controller->_name . '_container']['_qf_button_name'] = $this->formValues['_qf_button_name'];
}
}

}
33 changes: 33 additions & 0 deletions Civi/Test/FormWrappers/EventFormParticipant.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<?php

namespace Civi\Test\FormWrappers;

use Civi\Test\FormWrapper;

class EventFormParticipant extends FormWrapper {

/**
* @var \CRM_Event_Form_Participant
*/
protected $form;

/**
* @return int
*/
public function getEventID(): int {
return $this->form->getEventID();
}

public function getParticipantID(): int {
return $this->form->getEventID();
}

public function getDiscountID(): int {
return $this->form->getDiscountID();
}

public function getPriceSetID(): int {
return $this->form->getPriceSetID();
}

}
Loading

0 comments on commit 197882e

Please sign in to comment.