From 197882e6dcde81d54efa73a0342f8f4b41cebcc2 Mon Sep 17 00:00:00 2001 From: Eileen McNaughton Date: Fri, 1 Sep 2023 19:01:59 +1200 Subject: [PATCH] Add form helper for tests --- Civi/Test/FormTrait.php | 35 +++ Civi/Test/FormWrapper.php | 277 ++++++++++++++++++ .../FormWrappers/EventFormParticipant.php | 33 +++ .../CRM/Event/Form/ParticipantTest.php | 20 +- 4 files changed, 354 insertions(+), 11 deletions(-) create mode 100644 Civi/Test/FormTrait.php create mode 100644 Civi/Test/FormWrapper.php create mode 100644 Civi/Test/FormWrappers/EventFormParticipant.php diff --git a/Civi/Test/FormTrait.php b/Civi/Test/FormTrait.php new file mode 100644 index 000000000000..4a49fcd255cd --- /dev/null +++ b/Civi/Test/FormTrait.php @@ -0,0 +1,35 @@ +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']; + } + } + +} diff --git a/Civi/Test/FormWrappers/EventFormParticipant.php b/Civi/Test/FormWrappers/EventFormParticipant.php new file mode 100644 index 000000000000..f79eb10e0cb5 --- /dev/null +++ b/Civi/Test/FormWrappers/EventFormParticipant.php @@ -0,0 +1,33 @@ +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(); + } + +} diff --git a/tests/phpunit/CRM/Event/Form/ParticipantTest.php b/tests/phpunit/CRM/Event/Form/ParticipantTest.php index 4d8dc680b211..9a16722644f4 100644 --- a/tests/phpunit/CRM/Event/Form/ParticipantTest.php +++ b/tests/phpunit/CRM/Event/Form/ParticipantTest.php @@ -5,6 +5,9 @@ use Civi\Api4\LocBlock; use Civi\Api4\Participant; use Civi\Api4\Phone; +use Civi\Test\FormTrait; +use Civi\Test\FormWrapper; +use Civi\Test\FormWrappers\EventFormParticipant; /** * Test CRM_Event_Form_Registration functions. @@ -14,6 +17,7 @@ */ class CRM_Event_Form_ParticipantTest extends CiviUnitTestCase { + use FormTrait; use CRMTraits_Financial_OrderTrait; use CRMTraits_Financial_PriceSetTrait; @@ -33,10 +37,9 @@ public function testSubmit(): void { 'register_date' => date('Ymd'), 'status_id' => 1, 'role_id' => 1, - ]); - $form->postProcess(); + ])->postProcess(); $this->assertEquals($this->getEventID(), $form->getEventID()); - $this->callAPISuccessGetSingle('Participant', []); + $this->callAPISuccessGetSingle('Participant', ['id' => $form->getParticipantID()]); } /** @@ -326,11 +329,11 @@ public function testParticipantOfflineReceipt(string $thousandSeparator): void { * @param array $submittedValues * @param bool $isQuickConfig * - * @return CRM_Event_Form_Participant + * @return \Civi\Test\FormWrappers\EventFormParticipant * * @throws \CRM_Core_Exception */ - protected function getForm(array $eventParams = [], array $submittedValues = [], bool $isQuickConfig = FALSE): CRM_Event_Form_Participant { + protected function getForm(array $eventParams = [], array $submittedValues = [], bool $isQuickConfig = FALSE): EventFormParticipant { $submittedValues['contact_id'] = $this->ids['Contact']['event'] = $this->individualCreate(); if (!empty($eventParams['is_monetary'])) { @@ -348,12 +351,7 @@ protected function getForm(array $eventParams = [], array $submittedValues = [], $event = $this->eventCreateUnpaid($eventParams); } $submittedValues['event_id'] = $event['id']; - $_REQUEST['cid'] = $submittedValues['contact_id']; - /** @var CRM_Event_Form_Participant $form */ - $form = $this->getFormObject('CRM_Event_Form_Participant', $submittedValues); - $form->preProcess(); - $form->buildForm(); - return $form; + return $this->getTestForm('CRM_Event_Form_Participant', $submittedValues, ['cid' => $submittedValues['contact_id']])->processForm(FormWrapper::BUILT); } /**