diff --git a/docroot/modules/custom/va_gov_form_builder/css/va_gov_form_builder.css b/docroot/modules/custom/va_gov_form_builder/css/va_gov_form_builder.css
index 7e1957833e..9245dfbac3 100644
--- a/docroot/modules/custom/va_gov_form_builder/css/va_gov_form_builder.css
+++ b/docroot/modules/custom/va_gov_form_builder/css/va_gov_form_builder.css
@@ -3,6 +3,7 @@
--vads-color-divider: #a9aeb1;
--color-shadow: #00000040;
+ color: var(--vads-color-base);
font-family: var(--font-family-serif);
}
@@ -85,6 +86,7 @@
a.form-builder-button {
border-radius: var(--units-0p5);
cursor: pointer;
+ font-family: var(--font-source-sans);
font-weight: var(--font-weight-bold);
padding: var(--units-1p5) var(--units-2p5);
text-decoration: none;
@@ -106,3 +108,20 @@ a.form-builder-button--primary:hover {
color: var(--vads-button-color-text-primary-on-light);
text-decoration: none;
}
+
+.form-builder-button--secondary,
+.form-builder-button--secondary:focus,
+a.form-builder-button--secondary,
+a.form-builder-button--secondary:focus {
+ background: rgba(0, 0, 0, 0);
+ border: var(--units-0p25) solid var(--vads-color-primary);
+ color: var(--vads-color-primary);
+ text-decoration: none;
+}
+
+.form-builder-button--secondary:hover,
+a.form-builder-button--secondary:hover {
+ background: #dce4ef; /*secondary-button hover color missing from tokens*/
+ color: var(--vads-color-primary);
+ text-decoration: none;
+}
diff --git a/docroot/modules/custom/va_gov_form_builder/css/va_gov_form_builder__layout.css b/docroot/modules/custom/va_gov_form_builder/css/va_gov_form_builder__layout.css
new file mode 100644
index 0000000000..66ef20f45a
--- /dev/null
+++ b/docroot/modules/custom/va_gov_form_builder/css/va_gov_form_builder__layout.css
@@ -0,0 +1,68 @@
+/* Page content*/
+.form-builder-page-content--layout {
+ width: 620px;
+}
+
+.form-builder-content__page-help-text {
+ font-family: var(--font-source-sans);
+ margin-bottom: var(--units-6);
+}
+
+/* Step groups*/
+.form-builder-layout-step-group {
+ font-family: var(--font-source-sans);
+ margin-top: var(--units-4);
+}
+
+.form-builder-layout-step-group__heading {
+ font-family: var(--font-family-serif);
+ margin-bottom: var(--units-5);
+}
+
+.form-builder-layout-step-group__heading--viewing-form {
+ margin-bottom: var(--units-2);
+}
+
+/* Individual steps */
+.form-builder-layout-step {
+ border-bottom: var(--units-1px) solid var(--vads-color-base-light);
+ margin: var(--units-3) 0 0 0;
+ padding: 0 0 var(--units-3) var(--units-3);
+ position: relative;
+}
+
+.form-builder-layout-step-group > .form-builder-layout-step:last-child {
+ border-bottom: 0;
+}
+
+.form-builder-layout-step::before {
+ border-radius: var(--units-0p25);
+ color: var(--vads-color-white);
+ font-size: var(--font-size-sm);
+ font-weight: var(--font-weight-normal);
+ padding: var(--units-1px) var(--units-1);
+ position: absolute;
+ right: 0;
+ top: 0;
+}
+
+.form-builder-layout-step--complete::before {
+ background-color: var(
+ --vads-button-color-background-primary-alt-active-on-light
+ );
+ content: "COMPLETE";
+}
+
+.form-builder-layout-step--incomplete::before {
+ background-color: var(--vads-color-error-darker);
+ content: "INCOMPLETE";
+}
+
+.form-builder-layout-step__help-text {
+ color: var(--vads-color-gray-medium);
+}
+
+.form-builder-layout-step__view-link {
+ display: inline-block;
+ margin-top: var(--units-3);
+}
diff --git a/docroot/modules/custom/va_gov_form_builder/src/Controller/VaGovFormBuilderController.php b/docroot/modules/custom/va_gov_form_builder/src/Controller/VaGovFormBuilderController.php
index 80c434504c..732edcab76 100644
--- a/docroot/modules/custom/va_gov_form_builder/src/Controller/VaGovFormBuilderController.php
+++ b/docroot/modules/custom/va_gov_form_builder/src/Controller/VaGovFormBuilderController.php
@@ -36,6 +36,13 @@ class VaGovFormBuilderController extends ControllerBase {
*/
const LIBRARY_PREFIX = 'va_gov_form_builder/va_gov_form_builder_styles__';
+ /**
+ * The entity type manager service.
+ *
+ * @var \Drupal\Core\Entity\EntityTypeManagerInterface
+ */
+ protected $entityTypeManager;
+
/**
* The Drupal form builder.
*
@@ -51,16 +58,16 @@ class VaGovFormBuilderController extends ControllerBase {
protected $digitalFormsService;
/**
- * The Digital Form node.
+ * The Digital Form object.
*
* When the page in question edits or references an existing
* digital form node, this property is populated. When the
* page creates a new digital form node or otherwise does
* not reference a node, this is empty.
*
- * @var \Drupal\node\Entity\Node|null
+ * @var \Drupal\va_gov_form_builder\EntityWrapper\DigitalForm|null
*/
- protected $digitalFormNode;
+ protected $digitalForm;
/**
* {@inheritdoc}
@@ -68,6 +75,7 @@ class VaGovFormBuilderController extends ControllerBase {
public static function create(ContainerInterface $container) {
$instance = parent::create($container);
+ $instance->entityTypeManager = $container->get('entity_type.manager');
$instance->drupalFormBuilder = $container->get('form_builder');
$instance->digitalFormsService = $container->get('va_gov_form_builder.digital_forms_service');
@@ -75,7 +83,7 @@ public static function create(ContainerInterface $container) {
}
/**
- * Loads and sets the Digital Form node.
+ * Loads and sets the Digital Form object.
*
* @param string $nid
* The node id to load.
@@ -83,10 +91,9 @@ public static function create(ContainerInterface $container) {
* @return bool
* TRUE if successfully loaded. FALSE otherwise.
*/
- protected function loadDigitalFormNode($nid) {
- $digitalFormNode = $this->digitalFormsService->getDigitalForm($nid);
- if (!empty($digitalFormNode)) {
- $this->digitalFormNode = $digitalFormNode;
+ protected function loadDigitalForm($nid) {
+ $this->digitalForm = $this->digitalFormsService->getDigitalForm($nid);
+ if ($this->digitalForm) {
return TRUE;
}
@@ -108,6 +115,11 @@ protected function getPage($pageContent, $subtitle, $libraries = NULL) {
$page = [
'#type' => 'page',
'content' => $pageContent,
+ '#cache' => [
+ // Do not cache Form Builder pages.
+ // @todo Make caching more granular/contextual.
+ 'max-age' => 0,
+ ],
// Add custom data.
'form_builder_page_data' => [
'subtitle' => $subtitle,
@@ -142,7 +154,7 @@ protected function getPage($pageContent, $subtitle, $libraries = NULL) {
*/
protected function getFormPage($formName, $subtitle, $libraries = NULL) {
// @phpstan-ignore-next-line
- $form = $this->drupalFormBuilder->getForm('Drupal\va_gov_form_builder\Form\\' . $formName, $this->digitalFormNode);
+ $form = $this->drupalFormBuilder->getForm('Drupal\va_gov_form_builder\Form\\' . $formName, $this->digitalForm);
return $this->getPage($form, $subtitle, $libraries);
}
@@ -184,7 +196,7 @@ public function home() {
/**
* Form-info page.
*
- * @param string $nid
+ * @param string|null $nid
* The node id, passed in when the page edits an existing node.
*/
public function formInfo($nid = NULL) {
@@ -194,7 +206,7 @@ public function formInfo($nid = NULL) {
if (!empty($nid)) {
// This is an edit.
- $nodeFound = $this->loadDigitalFormNode($nid);
+ $nodeFound = $this->loadDigitalForm($nid);
if (!$nodeFound) {
throw new NotFoundHttpException();
}
@@ -203,13 +215,82 @@ public function formInfo($nid = NULL) {
return $this->getFormPage($formName, $subtitle, $libraries);
}
+ /**
+ * Layout page.
+ *
+ * @param string $nid
+ * The node id of the Digital Form.
+ */
+ public function layout($nid) {
+ $nodeFound = $this->loadDigitalForm($nid);
+ if (!$nodeFound) {
+ throw new NotFoundHttpException();
+ }
+
+ $pageContent = [
+ '#theme' => self::PAGE_CONTENT_THEME_PREFIX . 'layout',
+ '#form_info' => [
+ 'status' => $this->digitalForm->getStepStatus('form_info'),
+ 'url' => Url::fromRoute('va_gov_form_builder.form_info.edit', ['nid' => $nid])->toString(),
+ ],
+ '#intro' => [
+ 'status' => $this->digitalForm->getStepStatus('intro'),
+ 'url' => '',
+ ],
+ '#your_personal_info' => [
+ 'status' => $this->digitalForm->getStepStatus('your_personal_info'),
+ 'url' => '',
+ ],
+ '#address_info' => [
+ 'status' => $this->digitalForm->getStepStatus('address_info'),
+ 'url' => '',
+ ],
+ '#contact_info' => [
+ 'status' => $this->digitalForm->getStepStatus('contact_info'),
+ 'url' => '',
+ ],
+ '#additional_steps' => [
+ 'steps' => array_map(function ($step) {
+ return [
+ // If an additional step exists, it's complete.
+ 'type' => $step['type'],
+ 'title' => $step['fields']['field_title'][0]['value'],
+ 'status' => 'complete',
+ 'url' => '',
+ ];
+ }, $this->digitalForm->getNonStandarddSteps()),
+ 'add_step' => [
+ 'url' => '',
+ ],
+ ],
+ '#review_and_sign' => [
+ 'status' => $this->digitalForm->getStepStatus('review_and_sign'),
+ 'url' => '',
+ ],
+ '#confirmation' => [
+ 'status' => $this->digitalForm->getStepStatus('confirmation'),
+ 'url' => '',
+ ],
+ '#view_form' => [
+ 'url' => '',
+ ],
+ ];
+ $subtitle = $this->digitalForm->getTitle();
+ $libraries = ['layout'];
+
+ return $this->getPage($pageContent, $subtitle, $libraries);
+ }
+
/**
* Name-and-date-of-birth page.
+ *
+ * @param string $nid
+ * The node id of the Digital Form.
*/
public function nameAndDob($nid) {
$formName = 'NameAndDob';
$subtitle = 'Subtitle Placeholder';
- $nodeFound = $this->loadDigitalFormNode($nid);
+ $nodeFound = $this->loadDigitalForm($nid);
if (!$nodeFound) {
throw new NotFoundHttpException();
}
diff --git a/docroot/modules/custom/va_gov_form_builder/src/EntityWrapper/DigitalForm.php b/docroot/modules/custom/va_gov_form_builder/src/EntityWrapper/DigitalForm.php
new file mode 100644
index 0000000000..6c581665ea
--- /dev/null
+++ b/docroot/modules/custom/va_gov_form_builder/src/EntityWrapper/DigitalForm.php
@@ -0,0 +1,206 @@
+ 'digital_form_your_personal_info',
+ 'address_info' => 'digital_form_address',
+ 'contact_info' => 'digital_form_phone_and_email',
+ ];
+
+ /**
+ * The entity type manager service.
+ *
+ * @var \Drupal\Core\Entity\EntityTypeManagerInterface
+ */
+ protected $entityTypeManager;
+
+ /**
+ * The Digital Form node.
+ *
+ * @var \Drupal\node\NodeInterface
+ */
+ private $node;
+
+ /**
+ * Constructs a DigitalForm object.
+ *
+ * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
+ * The entity type manager.
+ * @param \Drupal\node\NodeInterface $node
+ * The Digital Form node to wrap.
+ */
+ public function __construct(EntityTypeManagerInterface $entity_type_manager, NodeInterface $node) {
+ $this->entityTypeManager = $entity_type_manager;
+
+ if ($node->getType() !== 'digital_form') {
+ throw new \InvalidArgumentException('The node must be of type "digital_form".');
+ }
+
+ $this->node = $node;
+ }
+
+ /**
+ * Magic method to forward other method calls to the node.
+ *
+ * This makes it so we can call, for example, $wrappedNode->getTitle().
+ * These methods are annotated in the class comment above.
+ *
+ * @param string $name
+ * The name of the method being called.
+ * @param array $arguments
+ * The arguments passed to the method.
+ *
+ * @return mixed
+ * The return value of the method called on the node.
+ */
+ public function __call($name, $arguments) {
+ if (method_exists($this->node, $name)) {
+ return call_user_func_array([$this->node, $name], $arguments);
+ }
+
+ throw new \BadMethodCallException("Method $name does not exist on the underlying node class.");
+ }
+
+ /**
+ * Returns an array of all steps added to the form.
+ *
+ * @return array
+ * A collection of all steps.
+ */
+ public function getAllSteps() {
+ $steps = [];
+
+ if ($this->node->hasField('field_chapters')) {
+ $chapters = $this->node->get('field_chapters')->getValue();
+
+ foreach ($chapters as $chapter) {
+ $paragraph = $this->entityTypeManager
+ ->getStorage('paragraph')
+ ->load($chapter['target_id']);
+
+ if ($paragraph) {
+ $steps[] = [
+ 'type' => $paragraph->bundle(),
+ 'fields' => array_map(function ($field) {
+ return $field->getValue();
+ }, $paragraph->getFields()),
+ ];
+ }
+ }
+ }
+
+ return $steps;
+ }
+
+ /**
+ * Returns an array of all steps that are not standard steps.
+ *
+ * @return array
+ * A collection of non-standard steps.
+ */
+ public function getNonStandarddSteps() {
+ $allSteps = $this->getAllSteps();
+ $nonStandardSteps = array_values(array_filter($allSteps, function ($step) {
+ return !in_array($step['type'], array_values(self::STANDARD_STEPS));
+ }));
+
+ return $nonStandardSteps;
+ }
+
+ /**
+ * Determines if the Digital Form node has a chapter of a given type.
+ *
+ * If the node has a chapter (paragraph) of the given type, returns TRUE.
+ * Otherwise, returns FALSE.
+ *
+ * @param string $type
+ * The chapter (paragraph) type.
+ *
+ * @return bool
+ * TRUE if the chapter exists; FALSE if the chapter
+ * does not exist.
+ */
+ public function hasChapterOfType($type) {
+ if ($this->node->hasField('field_chapters') && !$this->node->get('field_chapters')->isEmpty()) {
+ $chapters = $this->node->get('field_chapters')->getValue();
+
+ foreach ($chapters as $chapter) {
+ if (isset($chapter['target_id'])) {
+ $paragraph = $this->entityTypeManager->getStorage('paragraph')->load($chapter['target_id']);
+
+ if ($paragraph) {
+ if ($paragraph->bundle() === $type) {
+ return TRUE;
+ }
+ }
+ }
+ }
+ }
+
+ return FALSE;
+ }
+
+ /**
+ * Returns the status of a step on the Digital Form node.
+ *
+ * Completeness of the step varies by step, and is documented
+ * in the function body.
+ *
+ * @param string $stepName
+ * The step name of the step in question.
+ *
+ * @return 'complete'|'incomplete'
+ * Returns 'complete' if step is complete.
+ * Returns 'incomplete if step is incomplete
+ * or if the step name does not exist.
+ */
+ public function getStepStatus($stepName) {
+ if ($stepName === 'form_info') {
+ // If the node exists, this will necessarily be complete.
+ return 'complete';
+ }
+
+ if ($stepName === 'review_and_sign') {
+ // This is added automatically by the Forms Library.
+ return 'complete';
+ }
+
+ if (in_array($stepName, [
+ 'intro',
+ 'confirmation',
+ ])) {
+ // These haven't been handled yet.
+ // Return 'incomplete' for the time being.
+ return 'incomplete';
+ }
+
+ // Standard steps are complete if a corresponding chapter exists.
+ if (array_key_exists($stepName, self::STANDARD_STEPS)) {
+ $paragraphName = self::STANDARD_STEPS[$stepName];
+ return $this->hasChapterOfType($paragraphName)
+ ? 'complete'
+ : 'incomplete';
+ }
+
+ return 'incomplete';
+ }
+
+}
diff --git a/docroot/modules/custom/va_gov_form_builder/src/Form/Base/FormBuilderBase.php b/docroot/modules/custom/va_gov_form_builder/src/Form/Base/FormBuilderBase.php
index 2e3d163837..8303b2cc0e 100644
--- a/docroot/modules/custom/va_gov_form_builder/src/Form/Base/FormBuilderBase.php
+++ b/docroot/modules/custom/va_gov_form_builder/src/Form/Base/FormBuilderBase.php
@@ -5,6 +5,7 @@
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Form\FormBase;
use Drupal\Core\Form\FormStateInterface;
+use Drupal\va_gov_form_builder\Service\DigitalFormsService;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
@@ -20,41 +21,49 @@ abstract class FormBuilderBase extends FormBase {
protected $entityTypeManager;
/**
- * The Digital Form node created or loaded by this form step.
+ * The Digital Forms service.
*
- * @var \Drupal\node\Entity\Node
+ * @var \Drupal\va_gov_form_builder\Service\DigitalFormsService
*/
- protected $digitalFormNode;
+ protected $digitalFormsService;
/**
- * Flag indicating if the node has been changed.
+ * The DigitalForm object created or loaded by this form step.
*
- * Indicates if the node has been changed
+ * @var \Drupal\va_gov_form_builder\EntityWrapper\DigitalForm
+ */
+ protected $digitalForm;
+
+ /**
+ * Flag indicating if the Digital Form has been changed.
+ *
+ * Indicates if the Digital Form has been changed
* since the form was first instantiated.
*
* @var bool
*/
- protected $digitalFormNodeIsChanged;
+ protected $digitalFormIsChanged;
/**
- * Flag indicating whether this form allows an empty node.
+ * Flag indicating whether this form allows an empty DigitalForm object.
*
- * This defaults to FALSE. The only time an empty node
+ * This defaults to FALSE. The only time an empty object
* should be allowed is on the form that creates
* the node for the first time. Every other form should
- * operate on an existing form and should require a
- * node to be populated.
+ * operate on an existing form and should require an
+ * object to be populated.
*
* @var bool
*/
- protected $allowEmptyDigitalFormNode;
+ protected $allowEmptyDigitalForm;
/**
* {@inheritDoc}
*/
- public function __construct(EntityTypeManagerInterface $entityTypeManager) {
+ public function __construct(EntityTypeManagerInterface $entityTypeManager, DigitalFormsService $digitalFormsService) {
$this->entityTypeManager = $entityTypeManager;
- $this->allowEmptyDigitalFormNode = FALSE;
+ $this->digitalFormsService = $digitalFormsService;
+ $this->allowEmptyDigitalForm = FALSE;
}
/**
@@ -62,7 +71,8 @@ public function __construct(EntityTypeManagerInterface $entityTypeManager) {
*/
public static function create(ContainerInterface $container) {
return new static(
- $container->get('entity_type.manager')
+ $container->get('entity_type.manager'),
+ $container->get('va_gov_form_builder.digital_forms_service')
);
}
@@ -72,32 +82,32 @@ public static function create(ContainerInterface $container) {
abstract protected function getFields();
/**
- * Sets (creates or updates) a Digital Form node from the form-state data.
+ * Sets (creates or updates) a DigitalForm object from the form-state data.
*/
- abstract protected function setDigitalFormNodeFromFormState(array &$form, FormStateInterface $form_state);
+ abstract protected function setDigitalFormFromFormState(array &$form, FormStateInterface $form_state);
/**
- * Returns a field value from the Digital Form node.
+ * Returns a field value from the Digital Form.
*
- * If Digital Form node is not set, or `fieldName`
+ * If Digital Form is not set, or `fieldName`
* does not exist, returns NULL. This is primarily
* used to populate forms with default values when the
- * form edits an existing Digital Form node.
+ * form edits an existing Digital Form.
*
* @param string $fieldName
* The name of the field whose value should be fetched.
*/
- protected function getDigitalFormNodeFieldValue($fieldName) {
- if (empty($this->digitalFormNode)) {
+ protected function getDigitalFormFieldValue($fieldName) {
+ if (empty($this->digitalForm)) {
return NULL;
}
try {
if ($fieldName === 'title') {
- return $this->digitalFormNode->getTitle();
+ return $this->digitalForm->getTitle();
}
- return $this->digitalFormNode->get($fieldName)->value;
+ return $this->digitalForm->get($fieldName)->value;
}
catch (\Exception $e) {
return NULL;
@@ -107,58 +117,27 @@ protected function getDigitalFormNodeFieldValue($fieldName) {
/**
* {@inheritdoc}
*/
- public function buildForm(array $form, FormStateInterface $form_state, $node = NULL) {
+ public function buildForm(array $form, FormStateInterface $form_state, $digitalForm = NULL) {
// When form is first built, initialize flag to false.
- $this->digitalFormNodeIsChanged = FALSE;
+ $this->digitalFormIsChanged = FALSE;
- if (empty($node) && !$this->allowEmptyDigitalFormNode) {
- throw new \InvalidArgumentException('Digital Form node cannot be null.');
+ if (empty($digitalForm) && !$this->allowEmptyDigitalForm) {
+ throw new \InvalidArgumentException('Digital Form cannot be null.');
}
- $this->digitalFormNode = $node;
+ $this->digitalForm = $digitalForm;
return $form;
}
- /**
- * Determines if `digitalFormNode` has a chapter (paragraph) of a given type.
- *
- * @param string $type
- * The chapter (paragraph) type.
- *
- * @return bool
- * TRUE if the chapter exists; FALSE if the chapter
- * does not exist or the node does not exist.
- */
- protected function digitalFormNodeHasChapterOfType($type) {
- if (empty($this->digitalFormNode)) {
- return FALSE;
- }
-
- $chapters = $this->digitalFormNode->get('field_chapters')->getValue();
-
- foreach ($chapters as $chapter) {
- if (isset($chapter['target_id'])) {
- $paragraph = $this->entityTypeManager->getStorage('paragraph')->load($chapter['target_id']);
- if ($paragraph) {
- if ($paragraph->bundle() === $type) {
- return TRUE;
- }
- }
- }
- }
-
- return FALSE;
- }
-
/**
* {@inheritdoc}
*/
public function validateForm(array &$form, FormStateInterface $form_state) {
- $this->setDigitalFormNodeFromFormState($form, $form_state);
+ $this->setDigitalFormFromFormState($form, $form_state);
// Validate the node entity.
/** @var \Symfony\Component\Validator\ConstraintViolationListInterface $violations */
- $violations = $this->digitalFormNode->validate();
+ $violations = $this->digitalForm->validate();
// Loop through each violation and set errors on the form.
if ($violations->count() > 0) {
@@ -179,8 +158,8 @@ public function validateForm(array &$form, FormStateInterface $form_state) {
* {@inheritdoc}
*/
public function submitForm(array &$form, FormStateInterface $form_state) {
- // Save the previously validated node.
- $this->digitalFormNode->save();
+ // Save the previously validated Digital Form.
+ $this->digitalForm->save();
}
}
diff --git a/docroot/modules/custom/va_gov_form_builder/src/Form/FormInfo.php b/docroot/modules/custom/va_gov_form_builder/src/Form/FormInfo.php
index 8b54bf3b56..3bcc577bae 100644
--- a/docroot/modules/custom/va_gov_form_builder/src/Form/FormInfo.php
+++ b/docroot/modules/custom/va_gov_form_builder/src/Form/FormInfo.php
@@ -16,7 +16,7 @@ class FormInfo extends FormBuilderBase {
* Flag indicating if the form mode is "create".
*
* Form mode is "create", and this value is TRUE,
- * if no node id is passed in representing an existing node.
+ * if no Digital Form is passed in representing an existing Digital Form.
*
* Form mode is "edit" otherwise, and this value is FALSE.
*
@@ -47,18 +47,18 @@ protected function getFields() {
/**
* {@inheritdoc}
*/
- public function buildForm(array $form, FormStateInterface $form_state, $node = NULL) {
- // On this form, the Digital Form node should be allowed to be empty,
+ public function buildForm(array $form, FormStateInterface $form_state, $digitalForm = NULL) {
+ // On this form, the Digital Form should be allowed to be empty,
// to accommodate the case where it is in "create" mode.
- $this->allowEmptyDigitalFormNode = TRUE;
- $form = parent::buildForm($form, $form_state, $node);
+ $this->allowEmptyDigitalForm = TRUE;
+ $form = parent::buildForm($form, $form_state, $digitalForm);
- if (empty($node)) {
- // If no node is passed in, this is "create" mode.
+ if (empty($digitalForm)) {
+ // If no Digital Form is passed in, this is "create" mode.
$this->isCreate = TRUE;
}
else {
- // If a node is passed in, this is "edit" mode.
+ // If a Digital Form is passed in, this is "edit" mode.
$this->isCreate = FALSE;
}
@@ -68,14 +68,14 @@ public function buildForm(array $form, FormStateInterface $form_state, $node = N
'#type' => 'textfield',
'#title' => $this->t('Form Name'),
'#required' => TRUE,
- '#default_value' => $this->getDigitalFormNodeFieldValue('title'),
+ '#default_value' => $this->getDigitalFormFieldValue('title'),
];
$form['field_va_form_number'] = [
'#type' => 'textfield',
'#title' => $this->t('Form Number'),
'#required' => TRUE,
- '#default_value' => $this->getDigitalFormNodeFieldValue('field_va_form_number'),
+ '#default_value' => $this->getDigitalFormFieldValue('field_va_form_number'),
];
$form['field_omb_number'] = [
@@ -83,7 +83,7 @@ public function buildForm(array $form, FormStateInterface $form_state, $node = N
'#title' => $this->t('OMB number'),
'#description' => $this->t('Insert the OMB number (format: xxxx-xxxx)'),
'#required' => TRUE,
- '#default_value' => $this->getDigitalFormNodeFieldValue('field_omb_number'),
+ '#default_value' => $this->getDigitalFormFieldValue('field_omb_number'),
];
$form['field_respondent_burden'] = [
@@ -91,7 +91,7 @@ public function buildForm(array $form, FormStateInterface $form_state, $node = N
'#title' => $this->t('Respondent burden'),
'#description' => $this->t('Number of minutes as indicated on the form'),
'#required' => TRUE,
- '#default_value' => $this->getDigitalFormNodeFieldValue('field_respondent_burden'),
+ '#default_value' => $this->getDigitalFormFieldValue('field_respondent_burden'),
];
$form['field_expiration_date'] = [
@@ -99,7 +99,7 @@ public function buildForm(array $form, FormStateInterface $form_state, $node = N
'#title' => $this->t('Expiration date'),
'#description' => $this->t('Form expiration date as indicated on the form'),
'#required' => TRUE,
- '#default_value' => $this->getDigitalFormNodeFieldValue('field_expiration_date'),
+ '#default_value' => $this->getDigitalFormFieldValue('field_expiration_date'),
];
$form['actions']['save_and_continue'] = [
@@ -122,7 +122,7 @@ public function buildForm(array $form, FormStateInterface $form_state, $node = N
/**
* {@inheritdoc}
*/
- protected function setDigitalFormNodeFromFormState(array &$form, FormStateInterface $form_state) {
+ protected function setDigitalFormFromFormState(array &$form, FormStateInterface $form_state) {
$title = $form_state->getValue('title');
$vaFormNumber = $form_state->getValue('field_va_form_number');
$ombNumber = $form_state->getValue('field_omb_number');
@@ -131,11 +131,14 @@ protected function setDigitalFormNodeFromFormState(array &$form, FormStateInterf
if ($this->isCreate) {
/*
- * This form is creating a new node.
+ * This form is creating a new Digital Form.
*
- * We can simply create the new node with the fields from this form.
+ * We need to create a new Digital Form by doing two things:
+ * 1. Create the new Digital Form with the fields from this form.
+ * 2. Add default "Your personal information" step.
*/
- $this->digitalFormNode = $this->entityTypeManager->getStorage('node')->create([
+ // 1. Create the new Digital Form with the fields from this form.
+ $node = $this->entityTypeManager->getStorage('node')->create([
'type' => 'digital_form',
'title' => $title,
'field_va_form_number' => $vaFormNumber,
@@ -143,19 +146,43 @@ protected function setDigitalFormNodeFromFormState(array &$form, FormStateInterf
'field_respondent_burden' => $respondentBurden,
'field_expiration_date' => $expirationDate,
]);
+
+ // 2. Add "Your personal information" step (paragraph).
+ // This step contains two sub-steps:
+ // --> Name and date of birth
+ $nameAndDobParagraph = $this->entityTypeManager->getStorage('paragraph')->create([
+ 'type' => 'digital_form_name_and_date_of_bi',
+ 'field_title' => 'Name and date of birth',
+ 'field_include_date_of_birth' => TRUE,
+ ]);
+ // --> Identification information
+ $identificationInfo = $this->entityTypeManager->getStorage('paragraph')->create([
+ 'type' => 'digital_form_identification_info',
+ 'field_title' => 'Identifying information',
+ 'field_include_veteran_s_service' => TRUE,
+ ]);
+ // Your personal information wraps both.
+ $yourPersonalInformation = $this->entityTypeManager->getStorage('paragraph')->create([
+ 'type' => 'digital_form_your_personal_info',
+ 'field_name_and_date_of_birth' => $nameAndDobParagraph,
+ 'field_identification_information' => $identificationInfo,
+ ]);
+ $node->get('field_chapters')->appendItem($yourPersonalInformation);
+
+ $this->digitalForm = $this->digitalFormsService->wrapDigitalForm($node);
}
else {
/*
- * This form is editing an existing node.
+ * This form is editing an existing Digital Form.
*
* We need to update only the fields from this form,
* ensuring other fields are not changed.
*/
- $this->digitalFormNode->set('title', $title);
- $this->digitalFormNode->set('field_va_form_number', $vaFormNumber);
- $this->digitalFormNode->set('field_omb_number', $ombNumber);
- $this->digitalFormNode->set('field_respondent_burden', $respondentBurden);
- $this->digitalFormNode->set('field_expiration_date', $expirationDate);
+ $this->digitalForm->set('title', $title);
+ $this->digitalForm->set('field_va_form_number', $vaFormNumber);
+ $this->digitalForm->set('field_omb_number', $ombNumber);
+ $this->digitalForm->set('field_respondent_burden', $respondentBurden);
+ $this->digitalForm->set('field_expiration_date', $expirationDate);
}
}
@@ -165,8 +192,8 @@ protected function setDigitalFormNodeFromFormState(array &$form, FormStateInterf
public function submitForm(array &$form, FormStateInterface $form_state) {
parent::submitForm($form, $form_state);
- $form_state->setRedirect('va_gov_form_builder.name_and_dob', [
- 'nid' => $this->digitalFormNode->id(),
+ $form_state->setRedirect('va_gov_form_builder.layout', [
+ 'nid' => $this->digitalForm->id(),
]);
}
diff --git a/docroot/modules/custom/va_gov_form_builder/src/Form/NameAndDob.php b/docroot/modules/custom/va_gov_form_builder/src/Form/NameAndDob.php
index e1c00d792b..169efc6f37 100644
--- a/docroot/modules/custom/va_gov_form_builder/src/Form/NameAndDob.php
+++ b/docroot/modules/custom/va_gov_form_builder/src/Form/NameAndDob.php
@@ -42,8 +42,8 @@ protected function getFields() {
/**
* {@inheritdoc}
*/
- public function buildForm(array $form, FormStateInterface $form_state, $node = NULL) {
- $form = parent::buildForm($form, $form_state, $node);
+ public function buildForm(array $form, FormStateInterface $form_state, $digitalForm = NULL) {
+ $form = parent::buildForm($form, $form_state, $digitalForm);
$form['name_and_dob_header'] = [
'#type' => 'html_tag',
@@ -124,9 +124,9 @@ public function buildForm(array $form, FormStateInterface $form_state, $node = N
/**
* {@inheritdoc}
*/
- protected function setDigitalFormNodeFromFormState(array &$form, FormStateInterface $form_state) {
+ protected function setDigitalFormFromFormState(array &$form, FormStateInterface $form_state) {
// Do not add the name-and-dob chapter if there's one already present.
- if ($this->digitalFormNodeHasChapterOfType($this->chapterType)) {
+ if ($this->digitalForm->hasChapterOfType($this->chapterType)) {
return;
}
@@ -135,10 +135,10 @@ protected function setDigitalFormNodeFromFormState(array &$form, FormStateInterf
'field_title' => $form_state->getValue('step_name'),
'field_include_date_of_birth' => TRUE,
]);
- $this->digitalFormNode->get('field_chapters')->appendItem($nameAndDobParagraph);
+ $this->digitalForm->get('field_chapters')->appendItem($nameAndDobParagraph);
- // Node has been changed.
- $this->digitalFormNodeIsChanged = TRUE;
+ // Digital Form has been changed.
+ $this->digitalFormIsChanged = TRUE;
}
/**
@@ -147,7 +147,7 @@ protected function setDigitalFormNodeFromFormState(array &$form, FormStateInterf
public function backButtonSubmitHandler(array &$form, FormStateInterface $form_state) {
// This will almost certainly change.
$form_state->setRedirect('va_gov_form_builder.form_info.edit', [
- 'nid' => $this->digitalFormNode->id(),
+ 'nid' => $this->digitalForm->id(),
]);
}
@@ -155,15 +155,15 @@ public function backButtonSubmitHandler(array &$form, FormStateInterface $form_s
* {@inheritdoc}
*/
public function submitForm(array &$form, FormStateInterface $form_state) {
- // Only save the form if there have been changes to the node.
- if ($this->digitalFormNodeIsChanged) {
+ // Only save the form if there have been changes to the Digital Form.
+ if ($this->digitalFormIsChanged) {
parent::submitForm($form, $form_state);
}
// For now, redirect to the default node-edit form
// to confirm creation of the node.
$form_state->setRedirect('entity.node.edit_form', [
- 'node' => $this->digitalFormNode->id(),
+ 'node' => $this->digitalForm->id(),
]);
}
diff --git a/docroot/modules/custom/va_gov_form_builder/src/Service/DigitalFormsService.php b/docroot/modules/custom/va_gov_form_builder/src/Service/DigitalFormsService.php
index e14482c5ec..81f346afe7 100644
--- a/docroot/modules/custom/va_gov_form_builder/src/Service/DigitalFormsService.php
+++ b/docroot/modules/custom/va_gov_form_builder/src/Service/DigitalFormsService.php
@@ -3,11 +3,16 @@
namespace Drupal\va_gov_form_builder\Service;
use Drupal\Core\Entity\EntityTypeManagerInterface;
+use Drupal\va_gov_form_builder\EntityWrapper\DigitalForm;
/**
- * Service for handling Digital Form nodes.
+ * Service for fetching and creating Digital Forms.
+ *
+ * Digital Form nodes are wrapped in DigitalForm
+ * entity-wrapper objects before being returned.
*/
class DigitalFormsService {
+
/**
* The entity type manager service.
*
@@ -46,7 +51,13 @@ public function getDigitalForms($publishedOnly = TRUE) {
$nids = $query->execute();
if (!empty($nids)) {
- return $this->entityTypeManager->getStorage('node')->loadMultiple($nids);
+ $nodes = $this->entityTypeManager->getStorage('node')->loadMultiple($nids);
+ $digitalForms = [];
+ foreach ($nodes as $node) {
+ $digitalForms[] = new DigitalForm($this->entityTypeManager, $node);
+ }
+
+ return $digitalForms;
}
return [];
}
@@ -61,7 +72,31 @@ public function getDigitalForms($publishedOnly = TRUE) {
* A node object of type 'digital_form', or NULL if not found.
*/
public function getDigitalForm($nid) {
- return $this->entityTypeManager->getStorage('node')->load($nid);
+ $node = $this->entityTypeManager->getStorage('node')->load($nid);
+
+ return $this->wrapDigitalForm($node);
+ }
+
+ /**
+ * Returns a DigitalForm object from a passed-in Digital Form node.
+ *
+ * @param \Drupal\node\NodeInterface $node
+ * The `digital_form` node to wrap.
+ *
+ * @return \Drupal\va_gov_form_builder\EntityWrapper\DigitalForm
+ * The DigitalForm object wrapping the passed-in $node.
+ */
+ public function wrapDigitalForm($node) {
+ if (!$node) {
+ return NULL;
+ }
+
+ // Only return the node if it is a Digital Form node.
+ if ($node->getType() !== 'digital_form') {
+ return NULL;
+ }
+
+ return new DigitalForm($this->entityTypeManager, $node);
}
}
diff --git a/docroot/modules/custom/va_gov_form_builder/templates/page-content/page-content--va-gov-form-builder--home.html.twig b/docroot/modules/custom/va_gov_form_builder/templates/page-content/page-content--va-gov-form-builder--home.html.twig
index 83702f0936..1608478d32 100644
--- a/docroot/modules/custom/va_gov_form_builder/templates/page-content/page-content--va-gov-form-builder--home.html.twig
+++ b/docroot/modules/custom/va_gov_form_builder/templates/page-content/page-content--va-gov-form-builder--home.html.twig
@@ -24,7 +24,7 @@
{{ form.title }} (VA Form {{ form.formNumber }})
diff --git a/docroot/modules/custom/va_gov_form_builder/templates/page-content/page-content--va-gov-form-builder--layout.html.twig b/docroot/modules/custom/va_gov_form_builder/templates/page-content/page-content--va-gov-form-builder--layout.html.twig
new file mode 100644
index 0000000000..0f9a22c500
--- /dev/null
+++ b/docroot/modules/custom/va_gov_form_builder/templates/page-content/page-content--va-gov-form-builder--layout.html.twig
@@ -0,0 +1,88 @@
+
+
Build this form
+
Use this page to access and edit any content of this form or add additional steps as needed.
+
+
+
+
+
+
+
+
+
diff --git a/docroot/modules/custom/va_gov_form_builder/va_gov_form_builder.libraries.yml b/docroot/modules/custom/va_gov_form_builder/va_gov_form_builder.libraries.yml
index 96d60c3973..25b469e12f 100644
--- a/docroot/modules/custom/va_gov_form_builder/va_gov_form_builder.libraries.yml
+++ b/docroot/modules/custom/va_gov_form_builder/va_gov_form_builder.libraries.yml
@@ -15,3 +15,8 @@ va_gov_form_builder_styles__form_info:
css:
theme:
css/va_gov_form_builder__form_info.css: {}
+va_gov_form_builder_styles__layout:
+ version: 1.x
+ css:
+ theme:
+ css/va_gov_form_builder__layout.css: {}
diff --git a/docroot/modules/custom/va_gov_form_builder/va_gov_form_builder.module b/docroot/modules/custom/va_gov_form_builder/va_gov_form_builder.module
index 680a7955a1..5e5b16bdaa 100644
--- a/docroot/modules/custom/va_gov_form_builder/va_gov_form_builder.module
+++ b/docroot/modules/custom/va_gov_form_builder/va_gov_form_builder.module
@@ -33,7 +33,6 @@ function va_gov_form_builder_theme($_existing, $_type, $_theme, $path) {
// Add page-content themes.
$page_content_theme_prefix = 'page_content__va_gov_form_builder__';
$page_content_theme_path = $path . '/templates/page-content';
-
// 1. Home page
$theme[$page_content_theme_prefix . 'home'] = [
'path' => $page_content_theme_path,
@@ -42,6 +41,49 @@ function va_gov_form_builder_theme($_existing, $_type, $_theme, $path) {
'build_form_url' => '',
],
];
+ // 2. Layout page
+ $theme[$page_content_theme_prefix . 'layout'] = [
+ 'path' => $page_content_theme_path,
+ 'variables' => [
+ 'form_info' => [
+ 'status' => '',
+ 'url' => '',
+ ],
+ 'intro' => [
+ 'status' => '',
+ 'url' => '',
+ ],
+ 'your_personal_info' => [
+ 'status' => '',
+ 'url' => '',
+ ],
+ 'address_info' => [
+ 'status' => '',
+ 'url' => '',
+ ],
+ 'contact_info' => [
+ 'status' => '',
+ 'url' => '',
+ ],
+ 'additional_steps' => [
+ 'steps' => [],
+ 'add_step' => [
+ 'url' => '',
+ ],
+ ],
+ 'review_and_sign' => [
+ 'status' => '',
+ 'url' => '',
+ ],
+ 'confirmation' => [
+ 'status' => '',
+ 'url' => '',
+ ],
+ 'view_form' => [
+ 'url' => '',
+ ],
+ ],
+ ];
// Add form themes.
$forms = ['form_info'];
diff --git a/docroot/modules/custom/va_gov_form_builder/va_gov_form_builder.routing.yml b/docroot/modules/custom/va_gov_form_builder/va_gov_form_builder.routing.yml
index b33cb0e121..9d78b624fc 100644
--- a/docroot/modules/custom/va_gov_form_builder/va_gov_form_builder.routing.yml
+++ b/docroot/modules/custom/va_gov_form_builder/va_gov_form_builder.routing.yml
@@ -16,6 +16,10 @@ va_gov_form_builder.form_info.edit:
_controller: '\Drupal\va_gov_form_builder\Controller\VaGovFormBuilderController::formInfo'
requirements:
nid: \d*
+va_gov_form_builder.layout:
+ path: "/form-builder/{nid}/layout"
+ defaults:
+ _controller: '\Drupal\va_gov_form_builder\Controller\VaGovFormBuilderController::layout'
va_gov_form_builder.name_and_dob:
path: "/form-builder/{nid}/name-and-dob"
defaults:
diff --git a/tests/phpunit/va_gov_form_builder/functional/Controller/VaGovFormBuilderControllerTest.php b/tests/phpunit/va_gov_form_builder/functional/Controller/VaGovFormBuilderControllerTest.php
index 8a1fc58029..2ec803ec1c 100644
--- a/tests/phpunit/va_gov_form_builder/functional/Controller/VaGovFormBuilderControllerTest.php
+++ b/tests/phpunit/va_gov_form_builder/functional/Controller/VaGovFormBuilderControllerTest.php
@@ -38,13 +38,13 @@ class VaGovFormBuilderControllerTest extends VaGovExistingSiteBase {
public function setUp(): void {
parent::setUp();
- $container = new ContainerBuilder();
-
- // Add Drupal's form builder to the service container.
- $container->set('form_builder', \Drupal::formBuilder());
+ $entityTypeManager = \Drupal::service('entity_type.manager');
+ $drupalFormBuilder = \Drupal::service('form_builder');
+ $digitalFormsService = new DigitalFormsService($entityTypeManager);
- // Add our DigitalFormsService to the service container.
- $digitalFormsService = new DigitalFormsService(\Drupal::service('entity_type.manager'));
+ $container = new ContainerBuilder();
+ $container->set('entity_type.manager', $entityTypeManager);
+ $container->set('form_builder', $drupalFormBuilder);
$container->set('va_gov_form_builder.digital_forms_service', $digitalFormsService);
// Create the controller instance.
@@ -148,6 +148,60 @@ public function testFormInfoException() {
$this->controller->formInfo($someNonExistentNodeId);
}
+ /**
+ * Tests the layout method returns a Layout page.
+ */
+ public function testLayout() {
+ $title = 'Test Digital Form ' . uniqid();
+ $formNumber = '99-9999';
+
+ // Create a new Digital Form node.
+ $node = $this->createNode([
+ 'type' => 'digital_form',
+ 'title' => $title,
+ 'field_chapters' => [],
+ 'field_va_form_number' => $formNumber,
+ ]);
+
+ // Add paragraphs.
+ // Contact information.
+ $contactInfoParagraph = \Drupal::entityTypeManager()->getStorage('paragraph')->create([
+ 'type' => 'digital_form_phone_and_email',
+ 'field_title' => 'Contact information',
+ 'field_include_email' => TRUE,
+ ]);
+ $node->get('field_chapters')->appendItem($contactInfoParagraph);
+ // List and loop.
+ $listAndLoopParagraph = \Drupal::entityTypeManager()->getStorage('paragraph')->create([
+ 'type' => 'digital_form_list_loop',
+ 'field_title' => 'Your employers',
+ ]);
+ $node->get('field_chapters')->appendItem($listAndLoopParagraph);
+
+ // Save node.
+ $node->save();
+
+ $page = $this->controller->layout($node->id());
+
+ $this->assertArrayHasKey('content', $page);
+ $this->assertArrayHasKey('#theme', $page['content']);
+ $this->assertEquals('page_content__va_gov_form_builder__layout', $page['content']['#theme']);
+
+ // Ensure step statuses are calculated correctly.
+ // --> Contact info should be "complete" since paragraph exists.
+ $this->assertEquals('complete', $page['content']['#contact_info']['status'], 'Contact info is complete');
+ // --> Address info should be "incomplete" since paragraph does not exist.
+ $this->assertEquals('incomplete', $page['content']['#address_info']['status'], 'Address info is incomplete');
+
+ // Ensure additional steps are included and have "complete" status.
+ $this->assertArrayHasKey('#additional_steps', $page['content']);
+ $this->assertEquals('complete', $page['content']['#additional_steps']['steps'][0]['status']);
+
+ // Ensure css is added.
+ $this->assertArrayHasKey('#attached', $page);
+ $this->assertContains('va_gov_form_builder/va_gov_form_builder_styles__layout', $page['#attached']['library']);
+ }
+
/**
* Tests the nameAndDob method returns a NameAndDob form.
*/
diff --git a/tests/phpunit/va_gov_form_builder/functional/Form/FormInfoTest.php b/tests/phpunit/va_gov_form_builder/functional/Form/FormInfoTest.php
index c62cdd0c3c..152b7f308d 100644
--- a/tests/phpunit/va_gov_form_builder/functional/Form/FormInfoTest.php
+++ b/tests/phpunit/va_gov_form_builder/functional/Form/FormInfoTest.php
@@ -2,6 +2,8 @@
namespace tests\phpunit\va_gov_form_builder\functional\Form;
+use Drupal\node\Entity\Node;
+use Drupal\paragraphs\Entity\Paragraph;
use tests\phpunit\va_gov_form_builder\Traits\SharedConstants;
use tests\phpunit\va_gov_form_builder\Traits\TestPageLoads;
use Tests\Support\Classes\VaGovExistingSiteBase;
@@ -136,7 +138,31 @@ public function testFormSubmissionSucceeds() {
// Successful submission should take user to next page.
$nextPageUrl = $this->getSession()->getCurrentUrl();
- $this->assertStringContainsString('/name-and-dob', $nextPageUrl);
+ $this->assertStringContainsString('/layout', $nextPageUrl);
+
+ // Form should have default "Your personal information" chapter.
+ preg_match('|/form-builder\/(\d+)/layout|', $nextPageUrl, $matches);
+ $createdNodeId = $matches[1];
+ $createdNode = Node::load($createdNodeId);
+
+ $steps = $createdNode->get('field_chapters')->getValue();
+ $this->assertNotEmpty($steps, 'Default chapter should be added on initial node creation.');
+
+ $paragraphId = $steps[0]['target_id'];
+ $paragraph = Paragraph::load($paragraphId);
+ $this->assertEquals($paragraph->bundle(), 'digital_form_your_personal_info');
+
+ // That chapter should have two sub-chapters:
+ // 1. Name and date of birth.
+ $nameAndDob = $paragraph->get('field_name_and_date_of_birth')->getValue();
+ $nameAndDobParagraphId = $nameAndDob[0]['target_id'];
+ $nameAndDobParagraph = Paragraph::load($nameAndDobParagraphId);
+ $this->assertEquals($nameAndDobParagraph->bundle(), 'digital_form_name_and_date_of_bi');
+ // 2. Identification information.
+ $identificationInfo = $paragraph->get('field_identification_information')->getValue();
+ $identificationInfoParagraphId = $identificationInfo[0]['target_id'];
+ $identificationInfoParagraph = Paragraph::load($identificationInfoParagraphId);
+ $this->assertEquals($identificationInfoParagraph->bundle(), 'digital_form_identification_info');
}
/**
diff --git a/tests/phpunit/va_gov_form_builder/functional/Form/NameAndDobTest.php b/tests/phpunit/va_gov_form_builder/functional/Form/NameAndDobTest.php
index 38a615fa77..504fee3ae6 100644
--- a/tests/phpunit/va_gov_form_builder/functional/Form/NameAndDobTest.php
+++ b/tests/phpunit/va_gov_form_builder/functional/Form/NameAndDobTest.php
@@ -2,7 +2,6 @@
namespace tests\phpunit\va_gov_form_builder\functional\Form;
-use Drupal\node\Entity\Node;
use tests\phpunit\va_gov_form_builder\Traits\SharedConstants;
use tests\phpunit\va_gov_form_builder\Traits\TestPageLoads;
use Tests\Support\Classes\VaGovExistingSiteBase;
@@ -26,25 +25,33 @@ class NameAndDobTest extends VaGovExistingSiteBase {
private static $modules = ['va_gov_form_builder'];
/**
- * The Digital Form node.
+ * The DigitalFormsService object.
*
- * @var \Drupal\node\Entity\Node
+ * @var \Drupal\va_gov_form_builder\Service\DigitalFormsService
*/
- private $node;
+ private $digitalFormsService;
/**
- * Returns the url for this form (for the given node)
+ * The Digital Form object.
+ *
+ * @var \Drupal\va_gov_form_builder\EntityWrapper\DigitalForm
+ */
+ private $digitalForm;
+
+ /**
+ * Returns the url for this form (for the given Digital Form)
*/
private function getFormPageUrl() {
- return '/form-builder/' . $this->node->id() . '/name-and-dob';
+ return '/form-builder/' . $this->digitalForm->id() . '/name-and-dob';
}
/**
- * Reloads the node from the database.
+ * Reloads the Digital Form from the database.
*/
- private function reloadNode() {
- \Drupal::entityTypeManager()->getStorage('node')->resetCache([$this->node->id()]);
- $this->node = Node::load($this->node->id());
+ private function reloadDigitalForm() {
+ \Drupal::entityTypeManager()->getStorage('node')->resetCache([$this->digitalForm->id()]);
+
+ $this->digitalForm = $this->digitalFormsService->getDigitalForm($this->digitalForm->id());
}
/**
@@ -55,11 +62,14 @@ public function setUp(): void {
$this->loginFormBuilderUser();
+ $this->digitalFormsService = \Drupal::service('va_gov_form_builder.digital_forms_service');
+
// Create a node that doesn't have any chapters.
- $this->node = $this->createNode([
+ $node = $this->createNode([
'type' => 'digital_form',
'field_chapters' => [],
]);
+ $this->digitalForm = $this->digitalFormsService->wrapDigitalForm($node);
$this->drupalGet($this->getFormPageUrl());
}
@@ -87,9 +97,9 @@ public function testFormSubmissionAddsChapter() {
];
$this->submitForm($formInput, 'Continue');
- // Reload node and assert that chapters has been updated.
- $this->reloadNode();
- $this->assertCount(1, $this->node->get('field_chapters')->getValue());
+ // Reload Digital Form and assert that chapters has been updated.
+ $this->reloadDigitalForm();
+ $this->assertCount(1, $this->digitalForm->get('field_chapters')->getValue());
}
/**
@@ -102,17 +112,17 @@ public function testFormSubmissionDoesNotAddChapter() {
'field_title' => 'Your personal information',
'field_include_date_of_birth' => TRUE,
]);
- $this->node->get('field_chapters')->appendItem($nameAndDobParagraph);
- $this->node->save();
+ $this->digitalForm->get('field_chapters')->appendItem($nameAndDobParagraph);
+ $this->digitalForm->save();
$formInput = [
'step_name' => 'Your Personal Information',
];
$this->submitForm($formInput, 'Continue');
- // Reload node and assert that chapters still has only one item.
- $this->reloadNode();
- $this->assertCount(1, $this->node->get('field_chapters')->getValue());
+ // Reload Digital Form and assert that chapters still has only one item.
+ $this->reloadDigitalForm();
+ $this->assertCount(1, $this->digitalForm->get('field_chapters')->getValue());
}
/**
diff --git a/tests/phpunit/va_gov_form_builder/functional/content-pages/LayoutTest.php b/tests/phpunit/va_gov_form_builder/functional/content-pages/LayoutTest.php
new file mode 100644
index 0000000000..b2ecdd0587
--- /dev/null
+++ b/tests/phpunit/va_gov_form_builder/functional/content-pages/LayoutTest.php
@@ -0,0 +1,207 @@
+digitalFormNode->id()}/layout";
+ }
+
+ /**
+ * Helper method to generate a test node.
+ */
+ private function generateTestNode(
+ $title = NULL,
+ $formNumber = '99-9999',
+ $contactInfoTitle = 'Contact information',
+ $listAndLoopTitle = 'Your employers',
+ ) {
+ if (!$title) {
+ $title = 'Test Digital Form ' . uniqid();
+ }
+
+ // Create a new Digital Form node.
+ $this->digitalFormNode = $this->createNode([
+ 'type' => 'digital_form',
+ 'title' => $title,
+ 'field_chapters' => [],
+ 'field_va_form_number' => $formNumber,
+ ]);
+
+ // Add paragraphs.
+ // Contact information.
+ $contactInfoParagraph = \Drupal::entityTypeManager()->getStorage('paragraph')->create([
+ 'type' => 'digital_form_phone_and_email',
+ 'field_title' => $contactInfoTitle,
+ 'field_include_email' => TRUE,
+ ]);
+ $this->digitalFormNode->get('field_chapters')->appendItem($contactInfoParagraph);
+ // List and loop.
+ $listAndLoopParagraph = \Drupal::entityTypeManager()->getStorage('paragraph')->create([
+ 'type' => 'digital_form_list_loop',
+ 'field_title' => $listAndLoopTitle,
+ ]);
+ $this->digitalFormNode->get('field_chapters')->appendItem($listAndLoopParagraph);
+
+ // Save node.
+ $this->digitalFormNode->save();
+ }
+
+ /**
+ * Set up the environment for each test.
+ */
+ public function setUp(): void {
+ parent::setUp();
+
+ $this->generateTestNode();
+ $this->loginFormBuilderUser();
+ }
+
+ /**
+ * Test that the page is accessible to a user with the correct privilege.
+ */
+ public function testPageLoads() {
+ // Ensure page loads.
+ $this->sharedTestPageLoads($this->getFormPageUrl($this->digitalFormNode->id()), 'Build this form');
+ }
+
+ /**
+ * Test that the page is not accessible to a user without privilege.
+ */
+ public function testPageDoesNotLoad() {
+ $this->sharedTestPageDoesNotLoad($this->getFormPageUrl());
+ }
+
+ /**
+ * Test that the page has the expected subtitle.
+ *
+ * The subtitle should be the form title.
+ */
+ public function testPageSubtitle() {
+ $this->sharedTestPageHasExpectedSubtitle(
+ $this->getFormPageUrl(),
+ $this->digitalFormNode->getTitle(),
+ );
+ }
+
+ /**
+ * Test the "Form info" section.
+ */
+ public function testFormInfo() {
+ $this->drupalGet($this->getFormPageUrl());
+ $this->assertSession()->linkExists('View form info');
+
+ $this->clickLink('View form info');
+ $this->assertSession()->addressEquals("/form-builder/{$this->digitalFormNode->id()}/form-info");
+ }
+
+ /**
+ * Test the "Introduction page" section.
+ */
+ public function testIntroductionPage() {
+ $this->drupalGet($this->getFormPageUrl());
+
+ // There is no destination for this link yet.
+ $this->assertSession()->linkExists('View introduction page');
+ }
+
+ /**
+ * Test the "Your personal information" section.
+ */
+ public function testYourPersonalInfo() {
+ $this->drupalGet($this->getFormPageUrl());
+
+ // There is no destination for this link yet.
+ $this->assertSession()->linkExists('View personal information');
+ }
+
+ /**
+ * Test the "Address information" section.
+ */
+ public function testAddressInfo() {
+ $this->drupalGet($this->getFormPageUrl());
+
+ // There is no destination for this link yet.
+ $this->assertSession()->linkExists('View address information');
+ }
+
+ /**
+ * Test the "Contact information" section.
+ */
+ public function testContactInfo() {
+ $this->drupalGet($this->getFormPageUrl());
+
+ // There is no destination for this link yet.
+ $this->assertSession()->linkExists('View contact information');
+ }
+
+ /**
+ * Test that additional (non-standard) steps are rendered.
+ */
+ public function testAdditionalSteps() {
+ $this->drupalGet($this->getFormPageUrl());
+
+ // There is no destination for this link yet.
+ $this->assertSession()->linkExists('View your employers');
+
+ // There is no destination for this link yet.
+ $this->assertSession()->linkExists('Add a step');
+ }
+
+ /**
+ * Test the "Review and sign" section.
+ */
+ public function testReviewAndSign() {
+ $this->drupalGet($this->getFormPageUrl());
+
+ // There is no destination for this link yet.
+ $this->assertSession()->linkExists('View review and sign page');
+ }
+
+ /**
+ * Test the "Confirmation page" section.
+ */
+ public function testConfirmationPage() {
+ $this->drupalGet($this->getFormPageUrl());
+
+ // There is no destination for this link yet.
+ $this->assertSession()->linkExists('View confirmation page');
+ }
+
+ /**
+ * Test the "Viewing the form" section.
+ */
+ public function testViewingTheForm() {
+ $this->drupalGet($this->getFormPageUrl());
+
+ // There is no destination for this link yet.
+ $this->assertSession()->linkExists('View form');
+ }
+
+}
diff --git a/tests/phpunit/va_gov_form_builder/unit/EntityWrapper/DigitalFormTest.php b/tests/phpunit/va_gov_form_builder/unit/EntityWrapper/DigitalFormTest.php
new file mode 100644
index 0000000000..3eff8aae86
--- /dev/null
+++ b/tests/phpunit/va_gov_form_builder/unit/EntityWrapper/DigitalFormTest.php
@@ -0,0 +1,246 @@
+entityTypeManager = $this->createMock(EntityTypeManagerInterface::class);
+ }
+
+ /**
+ * Test the constructor with wrong node type passed in.
+ *
+ * Ensures that passing in a node that is not a
+ * DigitalForm node thorws an error.
+ */
+ public function testConstructorWrongNodeType() {
+ $node = $this->createMock(NodeInterface::class);
+
+ // Configure the mock to return something other
+ // than 'digital_form' when getType() is called.
+ $node->method('getType')
+ ->willReturn('article');
+
+ $this->expectException(\InvalidArgumentException::class);
+ $this->digitalForm = new DigitalForm($this->entityTypeManager, $node);
+ }
+
+ /**
+ * Test the __call magic method.
+ */
+ public function testCallMagicMethod() {
+ $id = '123';
+ $title = 'Test Digital Form 1';
+ $field = $this->createMock(FieldItemListInterface::class);
+ $field->value = 'Some field value';
+
+ $node = $this->createMock(NodeInterface::class);
+ $node->method('getType')
+ ->willReturn('digital_form');
+ $node->method('getTitle')
+ ->willReturn($title);
+ $node->method('id')
+ ->willReturn('123');
+ $node->method('get')
+ ->with('my_field')
+ ->willReturn($field);
+
+ $this->digitalForm = new DigitalForm($this->entityTypeManager, $node);
+
+ // id()
+ $idResult = $this->digitalForm->id();
+ $this->assertEquals($id, $idResult);
+
+ // getTitle()
+ $getTitleResult = $this->digitalForm->getTitle();
+ $this->assertEquals($title, $getTitleResult);
+
+ // get()->value
+ $getValueResult = $this->digitalForm->get('my_field')->value;
+ $this->assertEquals($field->value, $getValueResult);
+
+ // Unknown method.
+ $this->expectException(\BadMethodCallException::class);
+ $this->digitalForm->someUnknownMethod();
+ }
+
+ /**
+ * Helper function to set up a mock query for paragraphs.
+ */
+ private function setUpMockQueryParagraph() {
+ // Mock the paragraph.
+ $mockParagraph = $this->createMock(Paragraph::class);
+ $mockParagraph->expects($this->once())
+ ->method('bundle')
+ ->willReturn('expected_paragraph');
+
+ // Mock the entity storage.
+ $entityStorage = $this->createMock(EntityStorageInterface::class);
+ $entityStorage->expects($this->once())
+ ->method('load')
+ ->willReturnMap([
+ ['1', $mockParagraph],
+ ]);
+
+ // Mock the entity type manager.
+ $this->entityTypeManager->expects($this->once())
+ ->method('getStorage')
+ ->with('paragraph')
+ ->willReturn($entityStorage);
+ }
+
+ /**
+ * Helper function to create and return a mock a node with a paragraph.
+ */
+ private function createMockNodeWithParagraph() {
+ // Mock the field_chapters field.
+ $mockField = $this->createMock(FieldItemListInterface::class);
+ $mockField->expects($this->once())
+ ->method('isEmpty')
+ ->willReturn(FALSE);
+ $mockField->expects($this->once())
+ ->method('getValue')
+ ->willReturn([
+ ['target_id' => '1'],
+ ]);
+
+ // Mock the node.
+ $mockNode = $this->createMock(NodeInterface::class);
+ $mockNode->expects($this->once())
+ ->method('hasField')
+ ->with('field_chapters')
+ ->willReturn(TRUE);
+ $mockNode->expects($this->exactly(2))
+ ->method('get')
+ ->with('field_chapters')
+ ->willReturn($mockField);
+ $mockNode->expects($this->once())
+ ->method('getType')
+ ->willReturn('digital_form');
+
+ return $mockNode;
+ }
+
+ /**
+ * Helper function to set up a test for hasChapterOfType.
+ */
+ private function setUpHasChapterOfTypeTest() {
+ $this->setUpMockQueryParagraph();
+ $node = $this->createMockNodeWithParagraph();
+
+ // Instantiate an object to test.
+ $this->digitalForm = new DigitalForm($this->entityTypeManager, $node);
+ }
+
+ /**
+ * Tests hasChapterOfType() with expected paragraph.
+ */
+ public function testHasChapterOfTypeWithExpectedParagraph() {
+ $this->setUpHasChapterOfTypeTest();
+
+ // Assert expected paragraph type returns true.
+ $resultExpectedParagraphType = $this->digitalForm->hasChapterOfType('expected_paragraph');
+ $this->assertTrue($resultExpectedParagraphType);
+ }
+
+ /**
+ * Tests hasChapterOfType() with unexpected paragraph.
+ */
+ public function testHasChapterOfTypeWithUnexpectedParagraph() {
+ $this->setUpHasChapterOfTypeTest();
+
+ // Assert unexpected paragraph type returns false.
+ $resultUnexpectedParagraphType = $this->digitalForm->hasChapterOfType('any_other_paragraph_type');
+ $this->assertFalse($resultUnexpectedParagraphType);
+ }
+
+ /**
+ * Helper function to set up a test for getStepStatus.
+ */
+ private function setUpGetStepStatusTest() {
+ $this->digitalForm = $this->getMockBuilder(DigitalForm::class)
+ ->disableOriginalConstructor()
+ ->onlyMethods(['hasChapterOfType'])
+ ->getMock();
+
+ // Mock the behavior of hasChapterOfType method.
+ $this->digitalForm->method('hasChapterOfType')
+ ->willReturnMap([
+ ['digital_form_phone_and_email', TRUE],
+ ['digital_form_address', FALSE],
+ ]);
+ }
+
+ /**
+ * Tests getStepStatus() with unknown step name.
+ */
+ public function testGetDigitalFormStepStatusUnknownStepName() {
+ $this->setUpGetStepStatusTest();
+
+ // Assert step status is incomplete for unknown name.
+ $result = $this->digitalForm->getStepStatus('some_unknown_step_name');
+ $this->assertEquals('incomplete', $result);
+ }
+
+ /**
+ * Tests getStepStatus() with paragraph present.
+ */
+ public function testGetDigitalFormStepStatusParagraphPresent() {
+ $this->setUpGetStepStatusTest();
+
+ // Assert the status is 'complete' when paragraph is present.
+ // Note: `contact_info` step = `digital_form_phone_and_email` paragraph.
+ $result = $this->digitalForm->getStepStatus('contact_info');
+ $this->assertEquals('complete', $result);
+ }
+
+ /**
+ * Tests getStepStatus() with paragraph absent.
+ */
+ public function testGetDigitalFormStepStatusParagraphAbsent() {
+ $this->setUpGetStepStatusTest();
+
+ // Assert the status is 'incomplete' when paragraph is absent.
+ // Note: `address_info` step = `digital_form_address` paragraph.
+ $result = $this->digitalForm->getStepStatus('address_info');
+ $this->assertEquals('incomplete', $result);
+ }
+
+}
diff --git a/tests/phpunit/va_gov_form_builder/unit/Form/Base/FormBuilderBaseTest.php b/tests/phpunit/va_gov_form_builder/unit/Form/Base/FormBuilderBaseTest.php
index 2eb4847ac2..0c985a1827 100644
--- a/tests/phpunit/va_gov_form_builder/unit/Form/Base/FormBuilderBaseTest.php
+++ b/tests/phpunit/va_gov_form_builder/unit/Form/Base/FormBuilderBaseTest.php
@@ -4,8 +4,9 @@
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Form\FormStateInterface;
-use Drupal\node\Entity\Node;
+use Drupal\va_gov_form_builder\EntityWrapper\DigitalForm;
use Drupal\va_gov_form_builder\Form\Base\FormBuilderBase;
+use Drupal\va_gov_form_builder\Service\DigitalFormsService;
use Symfony\Component\Validator\ConstraintViolation;
use Symfony\Component\Validator\ConstraintViolationList;
use tests\phpunit\va_gov_form_builder\Traits\AnonymousFormClass;
@@ -21,6 +22,13 @@
*/
class FormBuilderBaseTest extends VaGovUnitTestBase {
+ /**
+ * The Digital Forms service.
+ *
+ * @var \Drupal\va_gov_form_builder\Service\DigitalFormsService
+ */
+ protected $digitalFormsService;
+
/**
* An instance of an anonymous class that extends the abstract class.
*
@@ -35,9 +43,10 @@ public function setUp(): void {
parent::setUp();
$entityTypeManager = $this->createMock(EntityTypeManagerInterface::class);
+ $this->digitalFormsService = $this->createMock(DigitalFormsService::class);
// Create an anonymous instance of a class that extends our abstract class.
- $this->classInstance = new class($entityTypeManager) extends FormBuilderBase {
+ $this->classInstance = new class($entityTypeManager, $this->digitalFormsService) extends FormBuilderBase {
use AnonymousFormClass;
/**
@@ -51,17 +60,17 @@ protected function getFields() {
}
/**
- * setDigitalFormNodeFromFormState.
+ * setDigitalFormFromFormState.
*/
- protected function setDigitalFormNodeFromFormState(array &$form, FormStateInterface $form_state) {
- // Do nothing. We'll set the digitalFormNode via reflection.
+ protected function setDigitalFormFromFormState(array &$form, FormStateInterface $form_state) {
+ // Do nothing. We'll set the digitalForm via reflection.
}
};
}
/**
- * Test that the buildForm method throws error when node not passed.
+ * Test that the buildForm method throws error when Digital Form not passed.
*/
public function testBuildFormThrowsError() {
$form = [];
@@ -77,32 +86,55 @@ public function testBuildFormThrowsError() {
*/
public function testBuildFormInitializesChangedFlag() {
$reflection = new \ReflectionClass($this->classInstance);
- $isChangedFlag = $reflection->getProperty('digitalFormNodeIsChanged');
+ $isChangedFlag = $reflection->getProperty('digitalFormIsChanged');
$isChangedFlag->setAccessible(TRUE);
$form = [];
$formStateMock = $this->createMock(FormStateInterface::class);
- // Pass a good node id so no error is thrown.
- $nid = 1;
- $form = $this->classInstance->buildForm($form, $formStateMock, $nid);
+ // Pass a good Digital Form so no error is thrown.
+ $digitalForm = $this->createMock(DigitalForm::class);
+ $form = $this->classInstance->buildForm($form, $formStateMock, $digitalForm);
$isChangedFlagValue = $isChangedFlag->getValue($this->classInstance);
$this->assertEquals($isChangedFlagValue, FALSE);
}
/**
- * Test the validateForm method with a Digital Form with no violations.
+ * Helper function to set up tests for violations.
+ *
+ * @param \Symfony\Component\Validator\ConstraintViolationList $violationList
+ * The expected violations.
*/
- public function testValidateFormWithNoViolations() {
- $digitalFormNode = $this->createMock(Node::class);
- $violationList = new ConstraintViolationList([]);
- $digitalFormNode->method('validate')->willReturn($violationList);
+ private function setUpViolationTest($violationList = NULL) {
+ if (!$violationList) {
+ $violationList = new ConstraintViolationList([]);
+ }
+
+ $digitalForm = $this->getMockBuilder(DigitalForm::class)
+ ->disableOriginalConstructor()
+ ->onlyMethods(['__call'])
+ ->getMock();
+
+ $digitalForm->method('__call')
+ ->willReturnCallback(function ($name, $arguments) use ($violationList) {
+ if ($name === 'validate') {
+ return $violationList;
+ }
+ return NULL;
+ });
$reflection = new \ReflectionClass($this->classInstance);
- $digitalFormNodeProperty = $reflection->getProperty('digitalFormNode');
- $digitalFormNodeProperty->setAccessible(TRUE);
- $digitalFormNodeProperty->setValue($this->classInstance, $digitalFormNode);
+ $digitalFormProperty = $reflection->getProperty('digitalForm');
+ $digitalFormProperty->setAccessible(TRUE);
+ $digitalFormProperty->setValue($this->classInstance, $digitalForm);
+ }
+
+ /**
+ * Test the validateForm method with a Digital Form with no violations.
+ */
+ public function testValidateFormWithNoViolations() {
+ $this->setUpViolationTest();
$form = [];
@@ -114,11 +146,9 @@ public function testValidateFormWithNoViolations() {
}
/**
- * Test the validateForm method with a node with applicable violations.
+ * Test validateForm method with a Digital Form with applicable violations.
*/
public function testValidateFormWithApplicableViolations() {
- $digitalFormNode = $this->createMock(Node::class);
-
// Has violations on fields related to this form;
// should raise errors.
$violationList = new ConstraintViolationList([
@@ -126,12 +156,7 @@ public function testValidateFormWithApplicableViolations() {
new ConstraintViolation('Invalid value 2', '', [], '', 'test_field_2', 'Invalid value'),
]);
- $digitalFormNode->method('validate')->willReturn($violationList);
-
- $reflection = new \ReflectionClass($this->classInstance);
- $digitalFormNodeProperty = $reflection->getProperty('digitalFormNode');
- $digitalFormNodeProperty->setAccessible(TRUE);
- $digitalFormNodeProperty->setValue($this->classInstance, $digitalFormNode);
+ $this->setUpViolationTest($violationList);
$form = [];
@@ -147,11 +172,9 @@ public function testValidateFormWithApplicableViolations() {
}
/**
- * Test the validateForm method with a Digital Form with other violations.
+ * Test validateForm method with a Digital Form with other violations.
*/
public function testValidateFormWithOtherViolations() {
- $digitalFormNode = $this->createMock(Node::class);
-
// Has violations, but not on fields related to this form;
// should not raise errors.
$violationList = new ConstraintViolationList([
@@ -159,12 +182,7 @@ public function testValidateFormWithOtherViolations() {
new ConstraintViolation('Invalid value 4', '', [], '', 'test_field_4', 'Invalid value'),
]);
- $digitalFormNode->method('validate')->willReturn($violationList);
-
- $reflection = new \ReflectionClass($this->classInstance);
- $digitalFormNodeProperty = $reflection->getProperty('digitalFormNode');
- $digitalFormNodeProperty->setAccessible(TRUE);
- $digitalFormNodeProperty->setValue($this->classInstance, $digitalFormNode);
+ $this->setUpViolationTest($violationList);
$form = [];
@@ -176,23 +194,16 @@ public function testValidateFormWithOtherViolations() {
}
/**
- * Test the validateForm method with a deeply-nested violation path.
+ * Test validateForm method with a deeply-nested violation path.
*/
public function testValidateFormWithNestedViolationPath() {
- $digitalFormNode = $this->createMock(Node::class);
-
// Has violation with a nested path; should raise an error the same way
// as if the path were not nested (on `test_field_1`).
$violationList = new ConstraintViolationList([
new ConstraintViolation('Invalid value 1', '', [], '', 'test_field_1.0.value', 'Invalid value'),
]);
- $digitalFormNode->method('validate')->willReturn($violationList);
-
- $reflection = new \ReflectionClass($this->classInstance);
- $digitalFormNodeProperty = $reflection->getProperty('digitalFormNode');
- $digitalFormNodeProperty->setAccessible(TRUE);
- $digitalFormNodeProperty->setValue($this->classInstance, $digitalFormNode);
+ $this->setUpViolationTest($violationList);
$form = [];
diff --git a/tests/phpunit/va_gov_form_builder/unit/LibrariesTest.php b/tests/phpunit/va_gov_form_builder/unit/LibrariesTest.php
index b43f9aa89e..997eeef55d 100644
--- a/tests/phpunit/va_gov_form_builder/unit/LibrariesTest.php
+++ b/tests/phpunit/va_gov_form_builder/unit/LibrariesTest.php
@@ -76,12 +76,16 @@ public function testLibraryCss() {
$this->assertArrayHasKey($homeLibrary, $this->libraries);
$homeCssArray = array_keys($this->libraries[$homeLibrary]['css']['theme']);
$this->assertContains($cssPrefix . '__home.css', $homeCssArray, 'Home page css is present.');
-
// 2. Form Info.
$formInfoLibrary = $libraryPrefix . '__form_info';
$this->assertArrayHasKey($formInfoLibrary, $this->libraries);
$formInfoCssArray = array_keys($this->libraries[$formInfoLibrary]['css']['theme']);
$this->assertContains($cssPrefix . '__form_info.css', $formInfoCssArray, 'Form Info page css is present.');
+ // 3. Layout.
+ $layoutLibrary = $libraryPrefix . '__layout';
+ $this->assertArrayHasKey($layoutLibrary, $this->libraries);
+ $layoutCssArray = array_keys($this->libraries[$layoutLibrary]['css']['theme']);
+ $this->assertContains($cssPrefix . '__layout.css', $layoutCssArray, 'Layout page css is present.');
}
}
diff --git a/tests/phpunit/va_gov_form_builder/unit/ModuleTest.php b/tests/phpunit/va_gov_form_builder/unit/ModuleTest.php
index f48ab02caa..0d6c3c904c 100644
--- a/tests/phpunit/va_gov_form_builder/unit/ModuleTest.php
+++ b/tests/phpunit/va_gov_form_builder/unit/ModuleTest.php
@@ -74,11 +74,26 @@ public function testVaGovFormBuilderHookTheme() {
$page_content_theme_prefix = 'page_content__va_gov_form_builder__';
$page_content_theme_path = $this->modulePath . '/templates/page-content';
// 1. Home page.
- $this->assertArrayHasKey($page_content_theme_prefix . 'home', $result);
- $this->assertEquals($page_content_theme_path, $result[$page_content_theme_prefix . 'home']['path']);
- $this->assertArrayHasKey('variables', $result[$page_content_theme_prefix . 'home']);
- $this->assertArrayHasKey('recent_forms', $result[$page_content_theme_prefix . 'home']['variables']);
- $this->assertArrayHasKey('build_form_url', $result[$page_content_theme_prefix . 'home']['variables']);
+ $homeTheme = $page_content_theme_prefix . 'home';
+ $this->assertArrayHasKey($homeTheme, $result);
+ $this->assertEquals($page_content_theme_path, $result[$homeTheme]['path']);
+ $this->assertArrayHasKey('variables', $result[$homeTheme]);
+ $this->assertArrayHasKey('recent_forms', $result[$homeTheme]['variables']);
+ $this->assertArrayHasKey('build_form_url', $result[$homeTheme]['variables']);
+ // 2. Layout page.
+ $layoutTheme = $page_content_theme_prefix . 'layout';
+ $this->assertArrayHasKey($layoutTheme, $result);
+ $this->assertEquals($page_content_theme_path, $result[$layoutTheme]['path']);
+ $this->assertArrayHasKey('variables', $result[$layoutTheme]);
+ $this->assertArrayHasKey('form_info', $result[$layoutTheme]['variables']);
+ $this->assertArrayHasKey('intro', $result[$layoutTheme]['variables']);
+ $this->assertArrayHasKey('your_personal_info', $result[$layoutTheme]['variables']);
+ $this->assertArrayHasKey('address_info', $result[$layoutTheme]['variables']);
+ $this->assertArrayHasKey('contact_info', $result[$layoutTheme]['variables']);
+ $this->assertArrayHasKey('additional_steps', $result[$layoutTheme]['variables']);
+ $this->assertArrayHasKey('review_and_sign', $result[$layoutTheme]['variables']);
+ $this->assertArrayHasKey('confirmation', $result[$layoutTheme]['variables']);
+ $this->assertArrayHasKey('view_form', $result[$layoutTheme]['variables']);
// Form themes.
$form_theme_prefix = 'form__va_gov_form_builder__';
diff --git a/tests/phpunit/va_gov_form_builder/unit/Service/DigitalFormsServiceTest.php b/tests/phpunit/va_gov_form_builder/unit/Service/DigitalFormsServiceTest.php
index 841da5516d..5f1abc55f0 100644
--- a/tests/phpunit/va_gov_form_builder/unit/Service/DigitalFormsServiceTest.php
+++ b/tests/phpunit/va_gov_form_builder/unit/Service/DigitalFormsServiceTest.php
@@ -77,9 +77,16 @@ private function setUpMockQueryGetDigitalForms($publishedOnly, $hasResults = TRU
}
if ($hasResults) {
- $query->expects($this->once())
- ->method('execute')
- ->willReturn([1, 2]);
+ if ($publishedOnly) {
+ $query->expects($this->once())
+ ->method('execute')
+ ->willReturn([1]);
+ }
+ else {
+ $query->expects($this->once())
+ ->method('execute')
+ ->willReturn([1, 2]);
+ }
}
else {
$query->expects($this->once())
@@ -93,13 +100,31 @@ private function setUpMockQueryGetDigitalForms($publishedOnly, $hasResults = TRU
->willReturn($query);
if ($hasResults) {
- $entityStorage->expects($this->once())
- ->method('loadMultiple')
- ->with([1, 2])
- ->willReturn([
- 1 => $this->createMock('Drupal\node\NodeInterface'),
- 2 => $this->createMock('Drupal\node\NodeInterface'),
- ]);
+ $node1 = $this->createMock('Drupal\node\NodeInterface');
+ $node1->method('getType')
+ ->willReturn('digital_form');
+
+ $node2 = $this->createMock('Drupal\node\NodeInterface');
+ $node2->method('getType')
+ ->willReturn('digital_form');
+
+ if ($publishedOnly) {
+ $entityStorage->expects($this->once())
+ ->method('loadMultiple')
+ ->with([1])
+ ->willReturn([
+ 1 => $node1,
+ ]);
+ }
+ else {
+ $entityStorage->expects($this->once())
+ ->method('loadMultiple')
+ ->with([1, 2])
+ ->willReturn([
+ 1 => $node1,
+ 2 => $node2,
+ ]);
+ }
}
// Mock the entity type manager.
@@ -130,7 +155,9 @@ public function testGetDigitalFormsPublishedOnlyTrue() {
$this->setUpMockQueryGetDigitalForms(TRUE);
// Call the method, which asserts expectations set in setup.
- $this->digitalFormsService->getDigitalForms(TRUE);
+ $result = $this->digitalFormsService->getDigitalForms(TRUE);
+ // Assert one result is returned.
+ $this->assertCount(1, $result);
}
/**
@@ -146,7 +173,9 @@ public function testGetDigitalFormsPublishedOnlyFalse() {
$this->setUpMockQueryGetDigitalForms(FALSE);
// Call the method, which asserts expectations set in setup.
- $this->digitalFormsService->getDigitalForms(FALSE);
+ $result = $this->digitalFormsService->getDigitalForms(FALSE);
+ // Assert two results are returned.
+ $this->assertCount(2, $result);
}
/**
@@ -162,7 +191,9 @@ public function testGetDigitalFormsPublishedOnlyDefault() {
$this->setUpMockQueryGetDigitalForms(TRUE);
// Call the method, which asserts expectations set in setup.
- $this->digitalFormsService->getDigitalForms();
+ $result = $this->digitalFormsService->getDigitalForms();
+ // Assert one result is returned.
+ $this->assertcount(1, $result);
}
/**
@@ -187,21 +218,27 @@ public function testGetDigitalFormsNoResults() {
// Call the method, which asserts expectations set in setup.
$result = $this->digitalFormsService->getDigitalForms(TRUE);
-
- // Additionally, assert the function returns no results.
+ // Assert the function returns no results.
$this->assertCount(0, $result);
}
/**
* Helper function to DRY up expectation setup for getDigitalForm.
+ *
+ * @param \Drupal\node\NodeInterface $node
+ * The mock node to return from entity storage.
*/
- private function setUpMockQueryGetDigitalForm() {
+ private function setUpMockQueryGetDigitalForm($node = NULL) {
+ if (!$node) {
+ $node = $this->createMock('Drupal\node\NodeInterface');
+ }
+
// Mock the entity storage.
$entityStorage = $this->createMock(EntityStorageInterface::class);
$entityStorage->expects($this->once())
->method('load')
->willReturnMap([
- ['1', $this->createMock('Drupal\node\NodeInterface')],
+ ['1', $node],
]);
// Mock the entity type manager.
@@ -212,14 +249,33 @@ private function setUpMockQueryGetDigitalForm() {
}
/**
- * Tests getDigitalForm() with $nid of existing node.
+ * Tests getDigitalForm() with $nid of digital_form node.
*/
- public function testGetDigitalFormReturnsNode() {
- $this->setUpMockQueryGetDigitalForm();
+ public function testGetDigitalFormWithCorrectNodeType() {
+ $node = $this->createMock('Drupal\node\NodeInterface');
+ $node->method('getType')
+ ->willReturn('digital_form');
+
+ $this->setUpMockQueryGetDigitalForm($node);
+
+ // Call the method with a nid that exists.
+ $digitalForm = $this->digitalFormsService->getDigitalForm('1');
+ $this->assertNotEmpty($digitalForm);
+ }
+
+ /**
+ * Tests getDigitalForm() with $nid of some other node type.
+ */
+ public function testGetDigitalFormWithIncorrectNodeType() {
+ $node = $this->createMock('Drupal\node\NodeInterface');
+ $node->method('getType')
+ ->willReturn('some_other_node_type');
+
+ $this->setUpMockQueryGetDigitalForm($node);
// Call the method with a nid that exists.
- $node = $this->digitalFormsService->getDigitalForm('1');
- $this->assertNotEmpty($node);
+ $digitalForm = $this->digitalFormsService->getDigitalForm('1');
+ $this->assertEmpty($digitalForm);
}
/**
@@ -229,8 +285,34 @@ public function testGetDigitalFormReturnsNull() {
$this->setUpMockQueryGetDigitalForm();
// Call the method with a nid that does not exist.
- $node = $this->digitalFormsService->getDigitalForm('99999');
- $this->assertEmpty($node);
+ $digitalForm = $this->digitalFormsService->getDigitalForm('99999');
+ $this->assertEmpty($digitalForm);
+ }
+
+ /**
+ * Tests wrapDigitalForm() with `digital_form` $node passed in.
+ */
+ public function testWrapDigitalFormCorrectNodeType() {
+ $node = $this->createMock('Drupal\node\NodeInterface');
+ $node->method('getType')
+ ->willReturn('digital_form');
+
+ // Call the method and assert a DigitalForm object is returned.
+ $digitalForm = $this->digitalFormsService->wrapDigitalForm($node);
+ $this->assertNotEmpty($digitalForm);
+ }
+
+ /**
+ * Tests wrapDigitalForm() with other type of $node passed in.
+ */
+ public function testWrapDigitalFormIncorrectNodeType() {
+ $node = $this->createMock('Drupal\node\NodeInterface');
+ $node->method('getType')
+ ->willReturn('some_other_node_type');
+
+ // Call the method and assert a DigitalForm object is returned.
+ $digitalForm = $this->digitalFormsService->wrapDigitalForm($node);
+ $this->assertEmpty($digitalForm);
}
}