Add basic support for `ang/*.test.php` test suite.
authorTim Otten <totten@civicrm.org>
Wed, 10 Feb 2021 22:22:36 +0000 (14:22 -0800)
committerTim Otten <totten@civicrm.org>
Wed, 17 Feb 2021 09:24:25 +0000 (01:24 -0800)
The previous pattern was to put all tests in `tests/phpunit/`. They would use
fixtures in a few ways, eg

* Work with example forms from the `ang/` folder. This is useful for testing specific configurations/features
  (e.g. if a form is configured with `permission` of `X`, then the form should behave accordingly).
* Dynamically create/destroy forms. This is useful for testing the API mechanism (eg ensuring the `Afform.create`
  and `Afform.revert` actually do create and revert things).

The two styles are both necessary. However:

1. The first style is easer to work with -- if a form fails its test, then
   you can run that form interactively.  If you want to add a new test-case,
   you can simply copy in a working example -- you don't need to code-up
   some PHPUnit logic to build the example.

2. If you have something written in the first style, then it's easier keep
   the `*.aff.html` and the PHPUnit `*.php` next to each other.

ext/afform/mock/phpunit.xml.dist
ext/afform/mock/tests/phpunit/Civi/AfformMock/FormTestCase.php [new file with mode: 0644]

index fc8f870b723b86a3cdf77609656b6ce38d0288ce..973ea8bec893fc19fc557698276163a370240158 100644 (file)
@@ -4,6 +4,9 @@
     <testsuite name="My Test Suite">
       <directory>./tests/phpunit</directory>
     </testsuite>
+    <testsuite name="form-tests">
+      <directory suffix=".test.php">./ang</directory>
+    </testsuite>
   </testsuites>
   <filter>
     <whitelist>
diff --git a/ext/afform/mock/tests/phpunit/Civi/AfformMock/FormTestCase.php b/ext/afform/mock/tests/phpunit/Civi/AfformMock/FormTestCase.php
new file mode 100644 (file)
index 0000000..113529f
--- /dev/null
@@ -0,0 +1,131 @@
+<?php
+
+namespace Civi\AfformMock;
+
+use Civi\Test\HttpTestTrait;
+
+/**
+ * Class FormTestCase
+ *
+ * This is a particular style of end-to-end test in which:
+ *
+ * - You make a concrete file with the target form (ang/FOO.aff.html)
+ * - You make HTTP requests for that form (FOO).
+ *
+ * This style of test is useful if:
+ *
+ * - You want to test a specific/concrete form, and
+ * - You want to test runtime behavior, and
+ * - You want to be able to run the form manually (for closer inspection).
+ *
+ * Note that this implies some ownership over the life of the form -- e.g. to
+ * ensure consistent execution, it will revert any local overrides on the target form.
+ *
+ * @package Civi\AfformMock
+ */
+abstract class FormTestCase extends \PHPUnit\Framework\TestCase implements \Civi\Test\EndToEndInterface {
+
+  use HttpTestTrait;
+
+  protected $formName = NULL;
+
+  protected function setUp() {
+    parent::setUp();
+
+    if ($this->formName === NULL && preg_match(';^(.*)\.test\.php$;', basename(static::FILE), $m)) {
+      $this->formName = $m[1];
+    }
+
+    $meta = $this->getFormMeta();
+    if (!$meta['has_base']) {
+      $this->fail('Cannot run test because form lacks a baseline definition.');
+    }
+    if ($meta['has_local']) {
+      $this->revertForm();
+    }
+  }
+
+  protected function tearDown() {
+    parent::tearDown();
+  }
+
+  protected function revertForm() {
+    \Civi\Api4\Afform::revert()
+      ->addWhere('name', '=', $this->getFormName())
+      ->execute();
+    return $this;
+  }
+
+  /**
+   * Get the name of the target form.
+   *
+   * @return string
+   */
+  protected function getFormName() {
+    if ($this->formName === NULL) {
+      throw new \RuntimeException("Failed to determine form name for " . self::FILE . ". Please override \$formName or getFormName().");
+    }
+    return $this->formName;
+  }
+
+  /**
+   * Get the metadata for the target form.
+   *
+   * @return array
+   */
+  protected function getFormMeta(): array {
+    $scanner = new \CRM_Afform_AfformScanner();
+    $meta = $scanner->getMeta($this->getFormName());
+    $scanner->addComputedFields($meta);
+    return $meta;
+  }
+
+  /**
+   * Call the 'Afform.prefill' for this form.
+   *
+   * @param array $params
+   *
+   * @return mixed
+   */
+  protected function prefill($params) {
+    $params['name'] = $params['name'] ?? $this->getFormName();
+    return $this->callApi4AjaxSuccess('Afform', 'prefill', $params);
+  }
+
+  /**
+   * Call the 'Afform.prefill' for this form.
+   *
+   * @param array $params
+   *
+   * @return mixed
+   */
+  protected function prefillError($params) {
+    $params['name'] = $params['name'] ?? $this->getFormName();
+    return $this->callApi4AjaxError('Afform', 'prefill', $params);
+  }
+
+  /**
+   * Call the 'Afform.submit' for this form.
+   *
+   * @param array $params
+   *
+   * @return mixed
+   */
+  protected function submit($params) {
+    $params['name'] = $params['name'] ?? $this->getFormName();
+    return $this->callApi4AjaxSuccess('Afform', 'submit', $params);
+  }
+
+  /**
+   * Call the 'Afform.submit' for this form.
+   *
+   * @param array $params
+   *
+   * @return mixed
+   */
+  protected function submitError($params) {
+    $params['name'] = $params['name'] ?? $this->getFormName();
+    return $this->callApi4AjaxError('Afform', 'submit', $params);
+  }
+
+}