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 7c73c407f7..4cb8a821c1 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 @@ -6,6 +6,18 @@ font-family: var(--font-family-serif); } +/* links */ +.form-builder-page-container a { + text-decoration: underline; +} + +.form-builder-page-container a:focus, +.form-builder-page-container a:hover { + background-color: rgba(0, 0, 0, 0.05); + color: var(--vads-color-base); + text-decoration: underline; +} + /* headings */ .form-builder-page-container h1 { font-size: var(--vads-font-size-heading-level-1); @@ -56,3 +68,30 @@ line-height: var(--units-3); padding: var(--units-1p5); } + +/* buttons */ +.form-builder-button, +a.form-builder-button { + border-radius: var(--units-0p5); + cursor: pointer; + font-weight: var(--font-weight-bold); + padding: var(--units-1p5) var(--units-2p5); + text-decoration: none; + transition: none; +} + +.form-builder-button--primary, +.form-builder-button--primary:focus, +a.form-builder-button--primary, +a.form-builder-button--primary:focus { + background: var(--vads-color-primary); + color: var(--vads-button-color-text-primary-on-light); + text-decoration: none; +} + +.form-builder-button--primary:hover, +a.form-builder-button--primary:hover { + background: var(--vads-color-primary-dark); + color: var(--vads-button-color-text-primary-on-light); + text-decoration: none; +} diff --git a/docroot/modules/custom/va_gov_form_builder/css/va_gov_form_builder__home.css b/docroot/modules/custom/va_gov_form_builder/css/va_gov_form_builder__home.css new file mode 100644 index 0000000000..903acfd19b --- /dev/null +++ b/docroot/modules/custom/va_gov_form_builder/css/va_gov_form_builder__home.css @@ -0,0 +1,30 @@ +/* Headings */ +.form-builder-page-content--home .form-builder-content-section__heading { + font-size: var(--vads-font-size-heading-level-4); +} + +/* New form */ +.form-builder-content-section--new-form__subheading { + margin-bottom: var(--units-3); +} + +/* Recent Forms */ +.form-builder-content-section--recent-forms { + margin-top: 60px; +} + +.form-builder-content-section--recent-forms__forms-list { + border-top: var(--units-1px) solid var(--vads-color-base-light); + list-style-type: none; + margin: 0; + padding-bottom: var(--units-3); + padding-top: var(--units-1p5); +} + +.form-builder-content-section--recent-forms__form-list-item { + margin-bottom: var(--units-1p5); +} + +.form-builder-content-section--recent-forms__form-link { + font-weight: var(--font-weight-bold); +} 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 58709040ae..2ea5b9ff8a 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 @@ -3,6 +3,7 @@ namespace Drupal\va_gov_form_builder\Controller; use Drupal\Core\Controller\ControllerBase; +use Drupal\Core\Url; use Symfony\Component\DependencyInjection\ContainerInterface; /** @@ -24,6 +25,16 @@ */ class VaGovFormBuilderController extends ControllerBase { + /** + * The prefix for the page-content theme definitions. + */ + const PAGE_CONTENT_THEME_PREFIX = 'page_content__va_gov_form_builder__'; + + /** + * The prefix for the page-specific style libraries. + */ + const LIBRARY_PREFIX = 'va_gov_form_builder/va_gov_form_builder_styles__'; + /** * The Drupal form builder. * @@ -32,11 +43,11 @@ class VaGovFormBuilderController extends ControllerBase { private $drupalFormBuilder; /** - * The page subtitle. + * The Digital Forms service. * - * @var string + * @var \Drupal\va_gov_form_builder\Service\DigitalFormsService */ - private $subtitle; + private $digitalFormsService; /** * {@inheritdoc} @@ -44,28 +55,29 @@ class VaGovFormBuilderController extends ControllerBase { public static function create(ContainerInterface $container) { $instance = parent::create($container); $instance->drupalFormBuilder = $container->get('form_builder'); + $instance->digitalFormsService = $container->get('va_gov_form_builder.digital_forms_service'); return $instance; } /** - * Returns a render array representing the page with the passed-in form. + * Returns a render array representing the page with the passed-in content. * - * @param string $formName - * The filename of the form to be rendered. - * @param string $nid - * The node id, passed in when the form in question edits an existing node. + * @param array $pageContent + * A render array representing the page content. + * @param string $subtitle + * The subtitle for the page. + * @param string $libraries + * Libraries for the page, in addition to the Form Builder general library, + * which is added automatically. */ - private function getFormPage($formName, $nid = NULL) { - // @phpstan-ignore-next-line - $form = $this->drupalFormBuilder->getForm('Drupal\va_gov_form_builder\Form\\' . $formName, $nid); - - return [ + private function getPage($pageContent, $subtitle, $libraries = NULL) { + $page = [ '#type' => 'page', - 'content' => $form, + 'content' => $pageContent, // Add custom data. 'form_builder_page_data' => [ - 'subtitle' => $this->subtitle, + 'subtitle' => $subtitle, ], // Add styles. '#attached' => [ @@ -74,37 +86,86 @@ private function getFormPage($formName, $nid = NULL) { ], ], ]; + + if (!empty($libraries)) { + foreach ($libraries as $library) { + $page['#attached']['library'][] = self::LIBRARY_PREFIX . $library; + } + } + + return $page; } /** - * Entry point for the VA Form Builder. Redirects to the intro page. + * Returns a render array representing the page with the passed-in form. + * + * @param string $formName + * The filename of the form to be rendered. + * @param string $subtitle + * The subtitle for the page. + * @param string $libraries + * Libraries for the page, in addition to the Form Builder general library, + * which is added automatically. + * @param string $nid + * The node id, passed in when the form in question edits an existing node. + */ + private function getFormPage($formName, $subtitle, $libraries = NULL, $nid = NULL) { + // @phpstan-ignore-next-line + $form = $this->drupalFormBuilder->getForm('Drupal\va_gov_form_builder\Form\\' . $formName, $nid); + + return $this->getPage($form, $subtitle, $libraries); + } + + /** + * Entry point for the VA Form Builder. Redirects to the home page. */ public function entry() { - return $this->redirect('va_gov_form_builder.intro'); + return $this->redirect('va_gov_form_builder.home'); } /** - * Intro page. + * Home page. */ - public function intro() { - $this->subtitle = 'Subtitle Placeholder'; - return $this->getFormPage('Intro'); + public function home() { + // Passing "FALSE" to fetch draft nodes rather than only published nodes. + $digitalForms = $this->digitalFormsService->getDigitalForms(FALSE); + + $recentForms = []; + foreach ($digitalForms as $digitalForm) { + $recentForms[] = [ + 'nid' => $digitalForm->id(), + 'title' => $digitalForm->getTitle(), + 'formNumber' => $digitalForm->get('field_va_form_number')->value, + ]; + } + + $pageContent = [ + '#theme' => self::PAGE_CONTENT_THEME_PREFIX . 'home', + '#build_form_url' => Url::fromRoute('va_gov_form_builder.start_conversion')->toString(), + '#recent_forms' => $recentForms, + ]; + $subtitle = 'Select a form'; + $libraries = ['home']; + + return $this->getPage($pageContent, $subtitle, $libraries); } /** * Start-conversion page. */ public function startConversion() { - $this->subtitle = 'Subtitle Placeholder'; - return $this->getFormPage('StartConversion'); + $formName = 'StartConversion'; + $subtitle = 'Subtitle Placeholder'; + return $this->getFormPage($formName, $subtitle); } /** * Name-and-date-of-birth page. */ public function nameAndDob($nid) { - $this->subtitle = 'Subtitle Placeholder'; - return $this->getFormPage('NameAndDob', $nid); + $formName = 'NameAndDob'; + $subtitle = 'Subtitle Placeholder'; + return $this->getFormPage($formName, $subtitle, NULL, $nid); } } diff --git a/docroot/modules/custom/va_gov_form_builder/src/Form/Intro.php b/docroot/modules/custom/va_gov_form_builder/src/Form/Intro.php deleted file mode 100644 index 6ee3bc7f04..0000000000 --- a/docroot/modules/custom/va_gov_form_builder/src/Form/Intro.php +++ /dev/null @@ -1,103 +0,0 @@ - 'html_tag', - '#tag' => 'h2', - '#children' => $this->t('Working with the Form Builder'), - ]; - - $form['paragraph_1'] = [ - '#type' => 'html_tag', - '#tag' => 'p', - '#children' => $this->t('This is where the conversion of an existing form, or the continued editing - of a converted form, takes place.'), - ]; - - $form['before_beginning_header'] = [ - '#type' => 'html_tag', - '#tag' => 'h3', - '#children' => $this->t('Before beginning'), - ]; - - $form['paragraph_2'] = [ - '#type' => 'html_tag', - '#tag' => 'p', - '#children' => $this->t('Make sure you have a copy of the form you are intending to convert. - This will make it easier to reference the information - that will be needed, and the order in which it appears for your users.'), - ]; - - $form['paragraph_3'] = [ - '#type' => 'html_tag', - '#tag' => 'p', - '#children' => $this->t('When returning to update an existing conversion, your past projects - are listed under Projects at right.'), - ]; - - $form['paragraph_4'] = [ - '#type' => 'html_tag', - '#tag' => 'p', - '#children' => $this->t('If you need to access a conversion that you did not create, you can use the - content search to find that project.'), - ]; - - $form['paragraph_5'] = [ - '#type' => 'html_tag', - '#tag' => 'p', - '#children' => $this->t('To begin a new project select Start conversion.'), - ]; - - $form['actions']['start_conversion'] = [ - '#type' => 'submit', - '#value' => $this->t('Start conversion'), - '#attributes' => [ - 'class' => [ - 'button', - 'button--primary', - 'js-form-submit', - 'form-submit', - ], - ], - ]; - - return $form; - } - - /** - * {@inheritdoc} - */ - public function submitForm(array &$form, FormStateInterface $form_state) { - $form_state->setRedirect('va_gov_form_builder.start_conversion'); - } - -} diff --git a/docroot/modules/custom/va_gov_form_builder/src/Form/StartConversion.php b/docroot/modules/custom/va_gov_form_builder/src/Form/StartConversion.php index 51f6376f50..1c4ba0c1d4 100644 --- a/docroot/modules/custom/va_gov_form_builder/src/Form/StartConversion.php +++ b/docroot/modules/custom/va_gov_form_builder/src/Form/StartConversion.php @@ -133,7 +133,7 @@ protected function setDigitalFormNodeFromFormState(array &$form, FormStateInterf * Submit handler for the 'Back' button. */ public function backButtonSubmitHandler(array &$form, FormStateInterface $form_state) { - $form_state->setRedirect('va_gov_form_builder.intro'); + $form_state->setRedirect('va_gov_form_builder.home'); } /** 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 new file mode 100644 index 0000000000..3b9a8ab4a6 --- /dev/null +++ b/docroot/modules/custom/va_gov_form_builder/src/Service/DigitalFormsService.php @@ -0,0 +1,54 @@ +entityTypeManager = $entityTypeManager; + } + + /** + * Retrieves all Digital Form nodes. + * + * @param bool $publishedOnly + * Whether to retrieve only published nodes. + * + * @return \Drupal\node\NodeInterface[] + * An array of node objects of type 'digital_form'. + */ + public function getDigitalForms($publishedOnly = TRUE) { + $query = $this->entityTypeManager->getStorage('node')->getQuery() + ->accessCheck(FALSE) + ->condition('type', 'digital_form'); + + if ($publishedOnly) { + $query->condition('status', 1); + } + + $nids = $query->execute(); + + if (!empty($nids)) { + return $this->entityTypeManager->getStorage('node')->loadMultiple($nids); + } + return []; + } + +} 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 new file mode 100644 index 0000000000..83702f0936 --- /dev/null +++ b/docroot/modules/custom/va_gov_form_builder/templates/page-content/page-content--va-gov-form-builder--home.html.twig @@ -0,0 +1,37 @@ +
+

Start a new form, or select a previous form to work with

+ +
+

Start a new form

+

+ Build a new form in Form Builder by selecting Build a form +

+ + Build a form + +
+ +
+

Recent forms

+

Select a recent form to edit

+ +
+ + +
diff --git a/docroot/modules/custom/va_gov_form_builder/templates/page--va-gov-form-builder.html.twig b/docroot/modules/custom/va_gov_form_builder/templates/page/page--va-gov-form-builder.html.twig similarity index 100% rename from docroot/modules/custom/va_gov_form_builder/templates/page--va-gov-form-builder.html.twig rename to docroot/modules/custom/va_gov_form_builder/templates/page/page--va-gov-form-builder.html.twig 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 4c70ae555e..691f830b8f 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 @@ -5,3 +5,8 @@ va_gov_form_builder_styles: css/va_gov_form_builder.css: {} https://unpkg.com/@department-of-veterans-affairs/css-library@0.16.0/dist/tokens/css/variables.css: {} +va_gov_form_builder_styles__home: + version: 1.x + css: + theme: + css/va_gov_form_builder__home.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 69e564509f..cd4fce3e7f 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 @@ -21,13 +21,29 @@ function va_gov_form_builder_entity_bundle_field_info_alter(&$fields, EntityType /** * Implements hook_theme(). */ -function va_gov_form_builder_theme() { - return [ - 'page__va_gov_form_builder' => [ - 'base hook' => 'page', - 'path' => \Drupal::service('extension.list.module')->getPath('va_gov_form_builder') . '/templates', +function va_gov_form_builder_theme($_existing, $_type, $_theme, $path) { + $theme = []; + + // Add page-wrapper theme. + $theme['page__va_gov_form_builder'] = [ + 'base hook' => 'page', + 'path' => $path . '/templates/page', + ]; + + // 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, + 'variables' => [ + 'recent_forms' => [], + 'build_form_url' => '', ], ]; + + return $theme; } /** 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 52a9145505..1afecc33e9 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 @@ -2,10 +2,10 @@ va_gov_form_builder.entry: path: "/form-builder" defaults: _controller: '\Drupal\va_gov_form_builder\Controller\VaGovFormBuilderController::entry' -va_gov_form_builder.intro: - path: "/form-builder/intro" +va_gov_form_builder.home: + path: "/form-builder/home" defaults: - _controller: '\Drupal\va_gov_form_builder\Controller\VaGovFormBuilderController::intro' + _controller: '\Drupal\va_gov_form_builder\Controller\VaGovFormBuilderController::home' va_gov_form_builder.start_conversion: path: "/form-builder/start-conversion" defaults: diff --git a/docroot/modules/custom/va_gov_form_builder/va_gov_form_builder.services.yml b/docroot/modules/custom/va_gov_form_builder/va_gov_form_builder.services.yml index b6af461fe8..507dbd20a9 100644 --- a/docroot/modules/custom/va_gov_form_builder/va_gov_form_builder.services.yml +++ b/docroot/modules/custom/va_gov_form_builder/va_gov_form_builder.services.yml @@ -3,3 +3,6 @@ services: class: Drupal\va_gov_form_builder\Routing\RouteSubscriber tags: - { name: "event_subscriber" } + va_gov_form_builder.digital_forms_service: + class: Drupal\va_gov_form_builder\Service\DigitalFormsService + arguments: ["@entity_type.manager"] diff --git a/tests/phpunit/va_gov_form_builder/Traits/TestFormLoads.php b/tests/phpunit/va_gov_form_builder/Traits/TestPageLoads.php similarity index 69% rename from tests/phpunit/va_gov_form_builder/Traits/TestFormLoads.php rename to tests/phpunit/va_gov_form_builder/Traits/TestPageLoads.php index 506f523ad3..1cd9a681e5 100644 --- a/tests/phpunit/va_gov_form_builder/Traits/TestFormLoads.php +++ b/tests/phpunit/va_gov_form_builder/Traits/TestPageLoads.php @@ -3,9 +3,9 @@ namespace tests\phpunit\va_gov_form_builder\Traits; /** - * Provides a trait for testing that forms load and do not load appropriately. + * Provides a trait for testing that pages load and do not load appropriately. */ -trait TestFormLoads { +trait TestPageLoads { /** * Logs-in a user with appropriate privileges. @@ -17,14 +17,14 @@ private function loginFormBuilderUser() { } /** - * Test the form is accessible to a user with the correct privilege. + * Test the page is accessible to a user with the correct privilege. * * @param string $url - * The (form) page to load. + * The page to load. * @param string $expectedText * The text expected to show on the loaded page. */ - private function sharedTestFormLoads($url, $expectedText) { + private function sharedTestPageLoads($url, $expectedText) { // Log in a user with permission. $this->loginFormBuilderUser(); @@ -36,12 +36,12 @@ private function sharedTestFormLoads($url, $expectedText) { } /** - * Test the form is not accessible to a user without the correct privilege. + * Test the page is not accessible to a user without the correct privilege. * * @param string $url - * The (form) page to load. + * The page to load. */ - private function sharedTestFormDoesNotLoad($url) { + private function sharedTestPageDoesNotLoad($url) { // Log in a user without permission. $this->drupalLogin($this->createUser([])); 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 7c6bdae099..2eddf1cda5 100644 --- a/tests/phpunit/va_gov_form_builder/functional/Controller/VaGovFormBuilderControllerTest.php +++ b/tests/phpunit/va_gov_form_builder/functional/Controller/VaGovFormBuilderControllerTest.php @@ -4,6 +4,7 @@ use Drupal\Core\Url; use Drupal\va_gov_form_builder\Controller\VaGovFormBuilderController; +use Drupal\va_gov_form_builder\Service\DigitalFormsService; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\HttpFoundation\RedirectResponse; use Tests\Support\Classes\VaGovExistingSiteBase; @@ -37,7 +38,15 @@ public function setUp(): void { parent::setUp(); $container = new ContainerBuilder(); + + // Add Drupal's form builder to the service container. $container->set('form_builder', \Drupal::formBuilder()); + + // Add our DigitalFormsService to the service container. + $digitalFormsService = new DigitalFormsService(\Drupal::service('entity_type.manager')); + $container->set('va_gov_form_builder.digital_forms_service', $digitalFormsService); + + // Create the controller instance. $this->controller = VaGovFormBuilderController::create($container); } @@ -45,7 +54,7 @@ public function setUp(): void { * Tests css is included. */ public function testCssIncluded() { - $page = $this->controller->intro(); + $page = $this->controller->home(); $this->assertContains( 'va_gov_form_builder/va_gov_form_builder_styles', @@ -61,17 +70,21 @@ public function testEntryRedirect() { $response = $this->controller->entry(); $this->assertInstanceOf(RedirectResponse::class, $response); - $this->assertStringContainsString(Url::fromRoute('va_gov_form_builder.intro')->toString(), $response->getTargetUrl()); + $this->assertStringContainsString(Url::fromRoute('va_gov_form_builder.home')->toString(), $response->getTargetUrl()); } /** - * Tests the intro method returns an Intro form. + * Tests the home method returns a Home page. */ - public function testIntro() { - $page = $this->controller->intro(); + public function testHome() { + $page = $this->controller->home(); $this->assertArrayHasKey('content', $page); - $this->assertArrayHasKey('working_with_form_builder_header', $page['content']); + $this->assertArrayHasKey('#theme', $page['content']); + $this->assertEquals('page_content__va_gov_form_builder__home', $page['content']['#theme']); + + $this->assertArrayHasKey('#attached', $page); + $this->assertContains('va_gov_form_builder/va_gov_form_builder_styles__home', $page['#attached']['library']); } /** diff --git a/tests/phpunit/va_gov_form_builder/functional/Form/IntroTest.php b/tests/phpunit/va_gov_form_builder/functional/Form/IntroTest.php deleted file mode 100644 index 7c310d9aff..0000000000 --- a/tests/phpunit/va_gov_form_builder/functional/Form/IntroTest.php +++ /dev/null @@ -1,63 +0,0 @@ -loginFormBuilderUser(); - $this->drupalGet($this->getFormPageUrl()); - } - - /** - * Test that the form is accessible to a user with the correct privilege. - */ - public function testFormLoads() { - $this->sharedTestFormLoads($this->getFormPageUrl(), 'Working with the Form Builder'); - } - - /** - * Test that the form is not accessible to a user without privilege. - */ - public function testFormDoesNotLoad() { - $this->sharedTestFormDoesNotLoad($this->getFormPageUrl()); - } - - /** - * Test the 'Start conversion' button. - */ - public function testButton() { - $this->click('.button#edit-start-conversion'); - $this->assertSession()->addressEquals('/form-builder/start-conversion'); - } - -} 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 06a3d883ab..5134c98326 100644 --- a/tests/phpunit/va_gov_form_builder/functional/Form/NameAndDobTest.php +++ b/tests/phpunit/va_gov_form_builder/functional/Form/NameAndDobTest.php @@ -4,7 +4,7 @@ use Drupal\node\Entity\Node; use tests\phpunit\va_gov_form_builder\Traits\SharedConstants; -use tests\phpunit\va_gov_form_builder\Traits\TestFormLoads; +use tests\phpunit\va_gov_form_builder\Traits\TestPageLoads; use Tests\Support\Classes\VaGovExistingSiteBase; /** @@ -18,7 +18,7 @@ class NameAndDobTest extends VaGovExistingSiteBase { use SharedConstants; - use TestFormLoads; + use TestPageLoads; /** * {@inheritdoc} @@ -65,17 +65,17 @@ public function setUp(): void { } /** - * Test that the form is accessible to a user with the correct privilege. + * Test that the page is accessible to a user with the correct privilege. */ - public function testFormLoads() { - $this->sharedTestFormLoads($this->getFormPageUrl(), 'Collecting Name and Date of birth'); + public function testPageLoads() { + $this->sharedTestPageLoads($this->getFormPageUrl(), 'Collecting Name and Date of birth'); } /** - * Test that the form is not accessible to a user without privilege. + * Test that the page is not accessible to a user without privilege. */ - public function testFormDoesNotLoad() { - $this->sharedTestFormDoesNotLoad($this->getFormPageUrl()); + public function testPageDoesNotLoad() { + $this->sharedTestPageDoesNotLoad($this->getFormPageUrl()); } /** diff --git a/tests/phpunit/va_gov_form_builder/functional/Form/StartConversionTest.php b/tests/phpunit/va_gov_form_builder/functional/Form/StartConversionTest.php index 597fa905fa..314ad6ea09 100644 --- a/tests/phpunit/va_gov_form_builder/functional/Form/StartConversionTest.php +++ b/tests/phpunit/va_gov_form_builder/functional/Form/StartConversionTest.php @@ -3,7 +3,7 @@ namespace tests\phpunit\va_gov_form_builder\functional\Form; use tests\phpunit\va_gov_form_builder\Traits\SharedConstants; -use tests\phpunit\va_gov_form_builder\Traits\TestFormLoads; +use tests\phpunit\va_gov_form_builder\Traits\TestPageLoads; use Tests\Support\Classes\VaGovExistingSiteBase; /** @@ -17,7 +17,7 @@ class StartConversionTest extends VaGovExistingSiteBase { use SharedConstants; - use TestFormLoads; + use TestPageLoads; /** * {@inheritdoc} @@ -42,17 +42,17 @@ public function setUp(): void { } /** - * Test that the form is accessible to a user with the correct privilege. + * Test that the page is accessible to a user with the correct privilege. */ - public function testFormLoads() { - $this->sharedTestFormLoads($this->getFormPageUrl(), 'Start a new conversion'); + public function testPageLoads() { + $this->sharedTestPageLoads($this->getFormPageUrl(), 'Start a new conversion'); } /** - * Test that the form is not accessible to a user without privilege. + * Test that the page is not accessible to a user without privilege. */ - public function testFormDoesNotLoad() { - $this->sharedTestFormDoesNotLoad($this->getFormPageUrl()); + public function testPageDoesNotLoad() { + $this->sharedTestPageDoesNotLoad($this->getFormPageUrl()); } /** @@ -95,11 +95,11 @@ public function testFormSubmissionFailsOnMissingRequiredField() { } /** - * Test the 'Back' button takes the user back to the Intro page. + * Test the 'Back' button takes the user back to the Home page. */ public function testBackButton() { $this->click('.button#edit-back'); - $this->assertSession()->addressEquals('/form-builder/intro'); + $this->assertSession()->addressEquals('/form-builder/home'); } } diff --git a/tests/phpunit/va_gov_form_builder/functional/content-pages/HomeTest.php b/tests/phpunit/va_gov_form_builder/functional/content-pages/HomeTest.php new file mode 100644 index 0000000000..5f00ef132a --- /dev/null +++ b/tests/phpunit/va_gov_form_builder/functional/content-pages/HomeTest.php @@ -0,0 +1,87 @@ +loginFormBuilderUser(); + } + + /** + * Test that the page is accessible to a user with the correct privilege. + */ + public function testPageLoads() { + $this->drupalGet($this->getPageUrl()); + $this->sharedTestPageLoads($this->getPageUrl(), 'Start a new form, or select a previous form to work with'); + } + + /** + * Test that the page is not accessible to a user without privilege. + */ + public function testPageDoesNotLoad() { + $this->drupalGet($this->getPageUrl()); + $this->sharedTestPageDoesNotLoad($this->getPageUrl()); + } + + /** + * Test the 'Build a form' button. + */ + public function testButton() { + $this->drupalGet($this->getPageUrl()); + $this->click('a#form-builder-build-form-button'); + $this->assertSession()->addressEquals('/form-builder/start-conversion'); + } + + /** + * Test the list of recent forms. + */ + public function testRecentFormsList() { + $title = 'Test Digital Form ' . uniqid(); + $formNumber = '99-9999'; + + // Create a new Digital Form node. + $this->createNode([ + 'type' => 'digital_form', + 'title' => $title, + 'field_chapters' => [], + 'field_va_form_number' => $formNumber, + ]); + + // Load page. + $this->drupalGet($this->getPageUrl()); + + // Ensure a link to the form appears on the page + // (in the list of recent forms). + // Ensure the link text is formatted as expected. + $this->assertSession()->linkExists($title . ' (VA Form ' . $formNumber . ')'); + } + +} diff --git a/tests/phpunit/va_gov_form_builder/functional/templates/FormBuilderPageTemplateTest.php b/tests/phpunit/va_gov_form_builder/functional/templates/FormBuilderPageTemplateTest.php index 44c2395318..47c26834b6 100644 --- a/tests/phpunit/va_gov_form_builder/functional/templates/FormBuilderPageTemplateTest.php +++ b/tests/phpunit/va_gov_form_builder/functional/templates/FormBuilderPageTemplateTest.php @@ -11,7 +11,7 @@ * that should result in rendering a page that utilizes the * page template under test. In this way, we can test the expected * behavior of the template file in a consistent manner, assuming - * the route properly utilizes the theme (which is tested elswhere). + * the route properly utilizes the theme (which is tested elsewhere). * * The route that makes the most sense here * is the `entry` route, as that should always be present, regardless diff --git a/tests/phpunit/va_gov_form_builder/unit/ModuleTest.php b/tests/phpunit/va_gov_form_builder/unit/ModuleTest.php index 07ad0b2858..c2c296cdcd 100644 --- a/tests/phpunit/va_gov_form_builder/unit/ModuleTest.php +++ b/tests/phpunit/va_gov_form_builder/unit/ModuleTest.php @@ -3,7 +3,6 @@ namespace tests\phpunit\va_gov_form_builder\unit; use Drupal\Core\DependencyInjection\ContainerBuilder; -use Drupal\Core\Extension\ModuleExtensionList; use Drupal\Core\Routing\RouteMatchInterface; use DrupalFinder\DrupalFinder; use Tests\Support\Classes\VaGovUnitTestBase; @@ -57,21 +56,29 @@ protected function setUp(): void { * @covers ::va_gov_form_builder_theme */ public function testVaGovFormBuilderHookTheme() { - // Mock the extension.list.module service and add to the container. - $extensionListMock = $this->createMock(ModuleExtensionList::class); - $extensionListMock->expects($this->once()) - ->method('getPath') - ->with('va_gov_form_builder') - ->willReturn($this->modulePath); - $this->container->set('extension.list.module', $extensionListMock); - // Call the function to test. - $result = va_gov_form_builder_theme(); + $result = va_gov_form_builder_theme( + NULL, + NULL, + NULL, + $this->modulePath + ); // Assert the expected theme definition exists. + // Page (wrapper) theme. $this->assertArrayHasKey('page__va_gov_form_builder', $result); $this->assertEquals('page', $result['page__va_gov_form_builder']['base hook']); - $this->assertEquals($this->modulePath . '/templates', $result['page__va_gov_form_builder']['path']); + $this->assertEquals($this->modulePath . '/templates/page', $result['page__va_gov_form_builder']['path']); + + // Page-content themes. + $page_content_theme_prefix = 'page_content__va_gov_form_builder__'; + $page_content_theme_path = $this->modulePath . '/templates/page-content'; + // 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']); } /** diff --git a/tests/phpunit/va_gov_form_builder/unit/Service/DigitalFormsServiceTest.php b/tests/phpunit/va_gov_form_builder/unit/Service/DigitalFormsServiceTest.php new file mode 100644 index 0000000000..ed937587c9 --- /dev/null +++ b/tests/phpunit/va_gov_form_builder/unit/Service/DigitalFormsServiceTest.php @@ -0,0 +1,195 @@ +entityTypeManager = $this->createMock(EntityTypeManagerInterface::class); + + // Instantiate the service with the mocked dependencies. + $this->digitalFormsService = new DigitalFormsService($this->entityTypeManager); + } + + /** + * Helper function to DRY up expectation setup. + */ + private function setUpMockQuery($publishedOnly, $hasResults = TRUE) { + // Mock the entity storage. + $entityStorage = $this->createMock(EntityStorageInterface::class); + + // Mock the query and return node IDs. + $query = $this->createMock(QueryInterface::class); + + $query->method('accessCheck') + ->willReturnSelf(); + + if ($publishedOnly) { + $query->expects($this->exactly(2)) + ->method('condition') + ->withConsecutive( + ['type', 'digital_form'], + ['status', 1], + ) + ->willReturnSelf(); + } + else { + $query->expects($this->once()) + ->method('condition') + ->withConsecutive( + ['type', 'digital_form'], + ) + ->willReturnSelf(); + } + + if ($hasResults) { + $query->expects($this->once()) + ->method('execute') + ->willReturn([1, 2]); + } + else { + $query->expects($this->once()) + ->method('execute') + ->willReturn(NULL); + } + + // Mock the entity storage to return the query and nodes. + $entityStorage->expects($this->once()) + ->method('getQuery') + ->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'), + ]); + } + + // Mock the entity type manager. + if ($hasResults) { + $this->entityTypeManager->expects($this->exactly(2)) + ->method('getStorage') + ->with('node') + ->willReturn($entityStorage); + } + else { + $this->entityTypeManager->expects($this->once()) + ->method('getStorage') + ->with('node') + ->willReturn($entityStorage); + } + } + + /** + * Tests getDigitalForms() when $publishedOnly is TRUE. + * + * Query should include `status` condition. + * + * @covers ::getDigitalForms + */ + public function testGetDigitalFormsPublishedOnlyTrue() { + // We will call the method with $publishedOnly = TRUE, + // so we set up expectations accordingly. + $this->setUpMockQuery(TRUE); + + // Call the method, which asserts expectations set in setup. + $this->digitalFormsService->getDigitalForms(TRUE); + } + + /** + * Tests getDigitalForms() when $publishedOnly is FALSE. + * + * Query should not include `status` condition. + * + * @covers ::getDigitalForms + */ + public function testGetDigitalFormsPublishedOnlyFalse() { + // We will call the method with $publishedOnly = FALSE, + // so we set up expectations accordingly. + $this->setUpMockQuery(FALSE); + + // Call the method, which asserts expectations set in setup. + $this->digitalFormsService->getDigitalForms(FALSE); + } + + /** + * Tests getDigitalForms() defaults to $publishedOnly TRUE. + * + * @covers ::getDigitalForms + */ + public function testGetDigitalFormsPublishedOnlyDefault() { + // We will call the method without passing $publishedOnly, + // and we want to ensure that the expectations are set up + // as though the value were set to TRUE, which is the + // expected default. + $this->setUpMockQuery(TRUE); + + // Call the method, which asserts expectations set in setup. + $this->digitalFormsService->getDigitalForms(); + } + + /** + * Tests getDigitalForms() returns empty array if no results. + * + * @covers ::getDigitalForms + */ + public function testGetDigitalFormsNoResults() { + // We set up the query to return no results + // by passing FALSE as second parameter. + // + // In the setup helper, this sets up expectations accordingly: + // 1. That + // `$this->entityTypeManager->getStorage('node')->loadMultiple($nids)` + // will not be called, since no node ids will be returned from + // the initial fetch. + // + // 2. That `$this->entityTypeManager->getStorage('node')` + // will be called only once, rather than twice, + // since there will be no need to call `loadMultiple`. + $this->setUpMockQuery(TRUE, FALSE); + + // Call the method, which asserts expectations set in setup. + $result = $this->digitalFormsService->getDigitalForms(TRUE); + + // Additionally, assert the function returns no results. + $this->assertCount(0, $result); + } + +}