From 2377fd9ddd9344b22a7720423b962d1053877c90 Mon Sep 17 00:00:00 2001 From: Mike Decker Date: Tue, 7 Jan 2025 11:37:29 -0800 Subject: [PATCH] Add acceptance tests --- ...nfig_split.patch.user.role.contributor.yml | 2 + ...fig_split.patch.user.role.site_builder.yml | 2 + ...g_split.patch.user.role.site_developer.yml | 2 + ...nfig_split.patch.user.role.site_editor.yml | 2 + ...fig_split.patch.user.role.site_manager.yml | 2 + ...t.patch.views.view.su_block_edit_links.yml | 14 ++ ...UniqueGlobalMessageConstraintValidator.php | 185 +++++++++-------- ...ueGlobalMessageConstraintValidatorTest.php | 2 + .../GlobalMessage/SummerGlobalMessageCest.php | 195 ++++++++++++++++++ tests/phpunit/example.phpunit.xml | 1 + 10 files changed, 314 insertions(+), 93 deletions(-) create mode 100644 docroot/profiles/lagunita/summer_profile/config/sync/split/summer/config_split.patch.views.view.su_block_edit_links.yml create mode 100644 docroot/profiles/lagunita/summer_profile/tests/codeception/acceptance/GlobalMessage/SummerGlobalMessageCest.php diff --git a/docroot/profiles/lagunita/summer_profile/config/sync/split/summer/config_split.patch.user.role.contributor.yml b/docroot/profiles/lagunita/summer_profile/config/sync/split/summer/config_split.patch.user.role.contributor.yml index 5cbffc46a..bb4ec9630 100644 --- a/docroot/profiles/lagunita/summer_profile/config/sync/split/summer/config_split.patch.user.role.contributor.yml +++ b/docroot/profiles/lagunita/summer_profile/config/sync/split/summer/config_split.patch.user.role.contributor.yml @@ -16,8 +16,10 @@ adding: - 'edit own sum_summer_courses content' - 'revert stanford_page revisions' - 'revert sum_summer_courses revisions' + - 'schedule publishing of summer_entity' - 'use text format sum_markdown' - 'view any unpublished sum_summer_courses content' + - 'view scheduled summer_entity' - 'view sum_summer_courses revisions' removing: permissions: diff --git a/docroot/profiles/lagunita/summer_profile/config/sync/split/summer/config_split.patch.user.role.site_builder.yml b/docroot/profiles/lagunita/summer_profile/config/sync/split/summer/config_split.patch.user.role.site_builder.yml index 41d1f4b43..b7a40eedf 100644 --- a/docroot/profiles/lagunita/summer_profile/config/sync/split/summer/config_split.patch.user.role.site_builder.yml +++ b/docroot/profiles/lagunita/summer_profile/config/sync/split/summer/config_split.patch.user.role.site_builder.yml @@ -9,7 +9,9 @@ adding: - 'administer summer entities' - 'configure all stanford_page node layout overrides' - 'configure editable stanford_page node layout overrides' + - 'schedule publishing of summer_entity' - 'use text format sum_markdown' + - 'view scheduled summer_entity' removing: permissions: - 'edit stanford_global_message config page entity' diff --git a/docroot/profiles/lagunita/summer_profile/config/sync/split/summer/config_split.patch.user.role.site_developer.yml b/docroot/profiles/lagunita/summer_profile/config/sync/split/summer/config_split.patch.user.role.site_developer.yml index 41d1f4b43..b7a40eedf 100644 --- a/docroot/profiles/lagunita/summer_profile/config/sync/split/summer/config_split.patch.user.role.site_developer.yml +++ b/docroot/profiles/lagunita/summer_profile/config/sync/split/summer/config_split.patch.user.role.site_developer.yml @@ -9,7 +9,9 @@ adding: - 'administer summer entities' - 'configure all stanford_page node layout overrides' - 'configure editable stanford_page node layout overrides' + - 'schedule publishing of summer_entity' - 'use text format sum_markdown' + - 'view scheduled summer_entity' removing: permissions: - 'edit stanford_global_message config page entity' diff --git a/docroot/profiles/lagunita/summer_profile/config/sync/split/summer/config_split.patch.user.role.site_editor.yml b/docroot/profiles/lagunita/summer_profile/config/sync/split/summer/config_split.patch.user.role.site_editor.yml index b43a5cb6e..aff07585e 100644 --- a/docroot/profiles/lagunita/summer_profile/config/sync/split/summer/config_split.patch.user.role.site_editor.yml +++ b/docroot/profiles/lagunita/summer_profile/config/sync/split/summer/config_split.patch.user.role.site_editor.yml @@ -30,8 +30,10 @@ adding: - 'edit terms in sum_summer_courses' - 'import sum_csv_summer_courses migration' - 'revert sum_summer_courses revisions' + - 'schedule publishing of summer_entity' - 'use text format sum_markdown' - 'view any unpublished sum_summer_courses content' + - 'view scheduled summer_entity' - 'view sum_summer_courses revisions' removing: dependencies: diff --git a/docroot/profiles/lagunita/summer_profile/config/sync/split/summer/config_split.patch.user.role.site_manager.yml b/docroot/profiles/lagunita/summer_profile/config/sync/split/summer/config_split.patch.user.role.site_manager.yml index 4c25bf550..6fc985e2b 100644 --- a/docroot/profiles/lagunita/summer_profile/config/sync/split/summer/config_split.patch.user.role.site_manager.yml +++ b/docroot/profiles/lagunita/summer_profile/config/sync/split/summer/config_split.patch.user.role.site_manager.yml @@ -31,8 +31,10 @@ adding: - 'edit terms in sum_summer_courses' - 'import sum_csv_summer_courses migration' - 'revert sum_summer_courses revisions' + - 'schedule publishing of summer_entity' - 'use text format sum_markdown' - 'view any unpublished sum_summer_courses content' + - 'view scheduled summer_entity' - 'view sum_summer_courses revisions' removing: dependencies: diff --git a/docroot/profiles/lagunita/summer_profile/config/sync/split/summer/config_split.patch.views.view.su_block_edit_links.yml b/docroot/profiles/lagunita/summer_profile/config/sync/split/summer/config_split.patch.views.view.su_block_edit_links.yml new file mode 100644 index 000000000..009298077 --- /dev/null +++ b/docroot/profiles/lagunita/summer_profile/config/sync/split/summer/config_split.patch.views.view.su_block_edit_links.yml @@ -0,0 +1,14 @@ +adding: + display: + default: + display_options: + access: + options: + perm: 'update any stanford_component_block block content' +removing: + display: + default: + display_options: + access: + options: + perm: 'edit any stanford_component_block block content' diff --git a/docroot/profiles/lagunita/summer_profile/modules/summer_helper/src/Plugin/Validation/Constraint/UniqueGlobalMessageConstraintValidator.php b/docroot/profiles/lagunita/summer_profile/modules/summer_helper/src/Plugin/Validation/Constraint/UniqueGlobalMessageConstraintValidator.php index d0e6fc6b6..4511ef2c8 100644 --- a/docroot/profiles/lagunita/summer_profile/modules/summer_helper/src/Plugin/Validation/Constraint/UniqueGlobalMessageConstraintValidator.php +++ b/docroot/profiles/lagunita/summer_profile/modules/summer_helper/src/Plugin/Validation/Constraint/UniqueGlobalMessageConstraintValidator.php @@ -4,6 +4,7 @@ use Drupal\Core\DependencyInjection\ContainerInjectionInterface; use Drupal\Core\Entity\EntityTypeManagerInterface; +use Drupal\Core\Entity\Query\QueryInterface; use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\Validator\Constraint; use Symfony\Component\Validator\ConstraintValidator; @@ -32,119 +33,117 @@ public function __construct(protected EntityTypeManagerInterface $entityTypeMana */ public function validate(mixed $value, Constraint $constraint) { /** @var \Drupal\summer_helper\SummerInterface $value */ - $summer_storage = $this->entityTypeManager->getStorage('summer_entity'); - $query = $summer_storage->getQuery() - ->accessCheck(FALSE) - ->condition('bundle', 'global_msg'); - - $query->condition($query->orConditionGroup() - ->condition('status', TRUE) - ->condition('publish_on', time(), '>')); - $published_message = $query->execute(); - $publish_date = (int) $value->get('publish_on')->getString(); $unpublish_date = (int) $value->get('unpublish_on')->getString(); - // When saving the current published message, no need to check the published - // dates. - if (!$publish_date && $published_message && $value->id() != reset($published_message)) { + // If no publish or unpublish date configured. + if (!$publish_date && !$unpublish_date && $this->isAnyPublished($value->id())) { $this->context->addViolation($constraint->invalidatePublishedDate); + return; } - // If both "published on" and "unpublished on" dates are configured, validate no other messages occur during that time. - if ($publish_date && $unpublish_date) { - $query = $summer_storage->getQuery() - ->accessCheck(FALSE) - ->condition('bundle', 'global_msg'); - $query->condition($query->orConditionGroup() - ->condition('publish_on', [$publish_date, $unpublish_date], 'between') - ->condition('unpublish_on', [ - $publish_date, - $unpublish_date, - ], 'between')); - - $ids = $query->execute(); - if ($ids && (count($ids) > 1 || $value->id() != reset($ids))) { - $this->context->addViolation($constraint->invalidatePublishedDate); - } + // If published date is configured but not unpublished date. + if ($publish_date && !$unpublish_date && $this->isPublishDateValid($publish_date, $value->id())) { + $this->context->addViolation($constraint->invalidatePublishedDate); return; } - // If only the "published on" date is configured, validate no other messages will occur after. - if ($publish_date && !$unpublish_date) { - // Another message will be published in the future. - $query = $summer_storage->getQuery() - ->accessCheck(FALSE) - ->condition('bundle', 'global_msg'); - $query->condition($query->orConditionGroup() - ->condition('publish_on', $publish_date, '>') - ->condition('unpublish_on', $publish_date, '>')); - - $ids = $query->execute(); - if ($ids && (count($ids) > 1 || $value->id() != reset($ids))) { - $this->context->addViolation($constraint->invalidatePublishedDate); - return; - } - - // Another message is scheduled to publish before this one, but is not - // schedule to unpublish. - $query = $summer_storage->getQuery() - ->accessCheck(FALSE) - ->condition('bundle', 'global_msg') - ->condition('publish_on', $publish_date, '<') - ->condition('unpublish_on', NULL, 'IS NULL'); - $ids = $query->execute(); - if ($ids && (count($ids) > 1 || $value->id() != reset($ids))) { - $this->context->addViolation($constraint->invalidatePublishedDate); - return; - } - - // Another message is currently published, but isn't schedule to be - // unpublished. - $query = $summer_storage->getQuery() - ->accessCheck(FALSE) - ->condition('bundle', 'global_msg') - ->condition('status', TRUE) - ->condition('unpublish_on', NULL, 'IS NULL'); - $ids = $query->execute(); - if ($ids && (count($ids) > 1 || $value->id() != reset($ids))) { - $this->context->addViolation($constraint->invalidatePublishedDate); - return; - } + // If unpublished date is configured but not published date. + if (!$publish_date && $unpublish_date && $this->isUnpublishDateValid($unpublish_date, $value->id())) { + $this->context->addViolation($constraint->invalidatePublishedDate); return; } - // If only the "unpublished on" date is configured, validate no other messages will occur until that date. - if (!$publish_date && $unpublish_date) { - $query = $summer_storage->getQuery() - ->accessCheck(FALSE) - ->condition('bundle', 'global_msg'); + // If unpublished date is configured but not published date. + if ($publish_date && $unpublish_date && $this->isBothDatesValid($publish_date, $unpublish_date, $value->id())) { + $this->context->addViolation($constraint->invalidatePublishedDate); + } + } - $query->condition($query->orConditionGroup() - ->condition('publish_on', $unpublish_date, '<') - ->condition('status', TRUE)); - $ids = $query->execute(); + protected function isAnyPublished($exclude_id = NULL): bool { + $query = $this->getEntityQuery($exclude_id); + $condition_group = $query->orConditionGroup(); + $condition_group->condition('status', TRUE) + ->condition('publish_on', NULL, 'IS NOT NULL'); - if ($ids && (count($ids) > 1 || $value->id() != reset($ids))) { - $this->context->addViolation($constraint->invalidatePublishedDate); - } - return; - } + $query->condition($condition_group); + return !empty($query->execute()); + } - $query = $summer_storage->getQuery() - ->accessCheck(FALSE) - ->condition('bundle', 'global_msg'); + protected function isPublishDateValid($publish_date, $exclude_id = NULL): bool { + $query = $this->getEntityQuery($exclude_id); + + // A message is currently published and won't be unpublished. + $first_condition_group = $query->andConditionGroup() + ->condition('status', TRUE) + ->condition('unpublish_on', NULL, 'IS NULL'); + + // A message is unpublished and will be published after the published date. + $second_condition_group = $query->orConditionGroup() + ->condition('publish_on', $publish_date, '>') + ->condition('unpublish_on', $publish_date, '>'); $query->condition($query->orConditionGroup() + ->condition($first_condition_group) + ->condition($second_condition_group)); + return !empty($query->execute()); + } + + protected function isUnpublishDateValid($unpublish_date, $exclude_id = NULL): bool { + $query = $this->getEntityQuery($exclude_id); + + // A message is currently published. + $first_condition_group = $query->andConditionGroup() + ->condition('status', TRUE); + + // A message will be published before the unpublish date. + $second_condition_group = $query->andConditionGroup() + ->condition('publish_on', $unpublish_date, '<'); + + $query->condition($query->orConditionGroup() + ->condition($first_condition_group) + ->condition($second_condition_group)); + + return !empty($query->execute()); + } + + protected function isBothDatesValid($publish_date, $unpublish_date, $exclude_id = NULL): bool { + $query = $this->getEntityQuery($exclude_id); + // A message is currently published and won't be unpublished. + $first_condition_group = $query->andConditionGroup() ->condition('status', TRUE) - ->condition('publish_on', time(), '>')); - $ids = $query->execute(); + ->condition('unpublish_on', NULL, 'IS NULL'); - // When saving the current published message, no need to check the published - // dates. - if ($ids && (count($ids) > 1 || $value->id() != reset($ids))) { - $this->context->addViolation($constraint->invalidatePublishedDate); + // A message will be published after the published date. + $second_condition_group = $query->andConditionGroup() + ->condition('publish_on', $publish_date, '>') + ->condition($query->orConditionGroup() + ->condition('unpublish_on', $unpublish_date, '<') + ->condition('unpublish_on', NULL, 'IS NULL')); + + // A message will be published before the unpublish date. + $third_condition_group = $query->andConditionGroup() + ->condition('publish_on', $publish_date, '>') + ->condition('publish_on', $unpublish_date, '<'); + + $query->condition($query->orConditionGroup() + ->condition($first_condition_group) + ->condition($second_condition_group) + ->condition($third_condition_group)); + + return !empty($query->execute()); + } + + protected function getEntityQuery($exclude_id = NULL): QueryInterface { + $summer_storage = $this->entityTypeManager->getStorage('summer_entity'); + $query = $summer_storage->getQuery() + ->accessCheck(FALSE) + ->condition('bundle', 'global_msg'); + + if ($exclude_id) { + $query->condition('id', $exclude_id, '!='); } + return $query; } } diff --git a/docroot/profiles/lagunita/summer_profile/modules/summer_helper/tests/src/Unit/Plugin/Validation/Constraint/UniqueGlobalMessageConstraintValidatorTest.php b/docroot/profiles/lagunita/summer_profile/modules/summer_helper/tests/src/Unit/Plugin/Validation/Constraint/UniqueGlobalMessageConstraintValidatorTest.php index 18aed46b3..f54bdb354 100644 --- a/docroot/profiles/lagunita/summer_profile/modules/summer_helper/tests/src/Unit/Plugin/Validation/Constraint/UniqueGlobalMessageConstraintValidatorTest.php +++ b/docroot/profiles/lagunita/summer_profile/modules/summer_helper/tests/src/Unit/Plugin/Validation/Constraint/UniqueGlobalMessageConstraintValidatorTest.php @@ -56,6 +56,7 @@ protected function setUp(): void { $query->method('accessCheck')->willReturnSelf(); $query->method('condition')->willReturnSelf(); $query->method('orConditionGroup')->willReturn($condition_group); + $query->method('andConditionGroup')->willReturn($condition_group); $query->method('execute')->willReturnReference($this->entityQueryResults); $entity_storage = $this->createMock(EntityStorageInterface::class); @@ -70,6 +71,7 @@ protected function setUp(): void { $this->validator->initialize($this->getContext()); $this->messageEntity = $this->createMock(SummerInterface::class); + $this->messageEntity->method('id')->willReturn(999); $this->messageEntity->method('get') ->will($this->returnCallback([$this, 'getMessageField'])); } diff --git a/docroot/profiles/lagunita/summer_profile/tests/codeception/acceptance/GlobalMessage/SummerGlobalMessageCest.php b/docroot/profiles/lagunita/summer_profile/tests/codeception/acceptance/GlobalMessage/SummerGlobalMessageCest.php new file mode 100644 index 000000000..61a24035f --- /dev/null +++ b/docroot/profiles/lagunita/summer_profile/tests/codeception/acceptance/GlobalMessage/SummerGlobalMessageCest.php @@ -0,0 +1,195 @@ +faker = Factory::create(); + } + + public function _after(AcceptanceTester $I) { + $entities = \Drupal::entityTypeManager() + ->getStorage('summer_entity') + ->loadMultiple(); + foreach ($entities as $entity) { + $entity->delete(); + } + } + + public function testAccess(AcceptanceTester $I) { + $I->logInWithRole('contributor'); + $I->amOnPage('/admin/content/summer/add/global_msg'); + $I->canSeeResponseCodeIs(403); + + $I->amOnPage('/user/logout'); + $I->click('Log out', 'form'); + + $I->logInWithRole('site_manager'); + $I->amOnPage('/admin/content/summer/add/global_msg'); + $I->canSeeInField('Label', ''); + $I->canSeeInField('Message', ''); + $I->canSeeInField('URL', ''); + $I->canSeeInField('Link text', ''); + $I->canSeeInField('Hide on Pages', ''); + $I->canSee('Scheduling options'); + } + + public function testExistingPublishedValidation(AcceptanceTester $I) { + $I->logInWithRole('site_manager'); + $I->amOnPage('/admin/content/summer/add/global_msg'); + + $I->fillField('Label', $this->faker->words(4, TRUE)); + $I->checkOption('Published'); + $I->click('Save'); + $I->canSee('has been created.'); + + $I->amOnPage('/admin/content/summer/add/global_msg'); + $I->fillField('Label', $this->faker->words(4, TRUE)); + $I->checkOption('Published'); + $I->click('Save'); + $I->canSee('The published dates are invalid.'); + } + + public function testUnpublishScheduledMessage(AcceptanceTester $I) { + $I->logInWithRole('site_manager'); + $I->amOnPage('/admin/content/summer/add/global_msg'); + + $publish_time = time() + 60 * 60 * 24; + + $I->fillField('Label', $this->faker->words(4, TRUE)); + $I->fillField('publish_on[0][value][date]', date('Y-m-d', $publish_time)); + $I->fillField('publish_on[0][value][time]', date('H:i:s', $publish_time)); + + $I->click('Save'); + $I->canSee('has been created.'); + + $I->amOnPage('/admin/content/summer/add/global_msg'); + + $I->fillField('Label', $this->faker->words(4, TRUE)); + $I->fillField('unpublish_on[0][value][date]', date('Y-m-d', $publish_time)); + $I->fillField('unpublish_on[0][value][time]', date('H:i:s', $publish_time + 60)); + + $I->click('Save'); + $I->cantSee('has been created.'); + $I->canSee('The published dates are invalid.'); + + $I->fillField('unpublish_on[0][value][time]', date('H:i:s', $publish_time - 60)); + + $I->click('Save'); + $I->canSee('has been created.'); + } + + public function testPublishScheduledMessage(AcceptanceTester $I) { + $I->logInWithRole('site_manager'); + $I->amOnPage('/admin/content/summer/add/global_msg'); + + $publish_time = time() + 60 * 60 * 24; + + $I->fillField('Label', $this->faker->words(4, TRUE)); + $I->fillField('unpublish_on[0][value][date]', date('Y-m-d', $publish_time)); + $I->fillField('unpublish_on[0][value][time]', date('H:i:s', $publish_time)); + + $I->click('Save'); + $I->canSee('has been created.'); + + $I->amOnPage('/admin/content/summer/add/global_msg'); + + $I->fillField('Label', $this->faker->words(4, TRUE)); + $I->uncheckOption('Published'); + $I->fillField('publish_on[0][value][date]', date('Y-m-d', $publish_time)); + $I->fillField('publish_on[0][value][time]', date('H:i:s', $publish_time - 60)); + + $I->click('Save'); + $I->cantSee('has been created.'); + $I->canSee('The published dates are invalid.'); + + $I->fillField('publish_on[0][value][time]', date('H:i:s', $publish_time + 60)); + + $I->click('Save'); + $I->canSee('has been created.'); + } + + public function testBothScheduledMessage(AcceptanceTester $I) { + $I->logInWithRole('site_manager'); + $I->amOnPage('/admin/content/summer/add/global_msg'); + + $publish_time = time() + 60 * 60 * 24; + $unpublish_time = time() + 10 * 60 * 60 * 24; + + $I->fillField('Label', $this->faker->words(4, TRUE)); + $I->uncheckOption('Published'); + $I->fillField('publish_on[0][value][date]', date('Y-m-d', $publish_time)); + $I->fillField('publish_on[0][value][time]', date('H:i:s', $publish_time)); + $I->fillField('unpublish_on[0][value][date]', date('Y-m-d', $unpublish_time)); + $I->fillField('unpublish_on[0][value][time]', date('H:i:s', $unpublish_time)); + + $I->click('Save'); + $I->canSee('has been created.'); + + // Before the original message. + $I->amOnPage('/admin/content/summer/add/global_msg'); + $I->fillField('Label', $this->faker->words(4, TRUE)); + + $I->click('Save'); + $I->cantSee('has been created.'); + $I->canSee('The published dates are invalid.'); + + $I->fillField('publish_on[0][value][date]', date('Y-m-d', $publish_time - 60)); + $I->fillField('publish_on[0][value][time]', date('H:i:s', $publish_time)); + $I->click('Save'); + $I->cantSee('has been created.'); + $I->canSee('The published dates are invalid.'); + + $I->checkOption('Published'); + $I->fillField('publish_on[0][value][date]', ''); + $I->fillField('publish_on[0][value][time]', ''); + $I->fillField('unpublish_on[0][value][date]', date('Y-m-d', $publish_time)); + $I->fillField('unpublish_on[0][value][time]', date('H:i:s', $publish_time)); + $I->click('Save'); + $I->canSee('has been created.'); + + // After the original message. + $I->amOnPage('/admin/content/summer/add/global_msg'); + $I->fillField('Label', $this->faker->words(4, TRUE)); + + $I->click('Save'); + $I->cantSee('has been created.'); + $I->canSee('The published dates are invalid.'); + + $I->fillField('publish_on[0][value][date]', date('Y-m-d', $publish_time - 60)); + $I->fillField('publish_on[0][value][time]', date('H:i:s', $publish_time)); + $I->click('Save'); + $I->cantSee('has been created.'); + $I->canSee('The published dates are invalid.'); + + $I->checkOption('Published'); + $I->fillField('publish_on[0][value][date]', date('Y-m-d', $unpublish_time)); + $I->fillField('publish_on[0][value][time]', date('H:i:s', $unpublish_time)); + $I->fillField('unpublish_on[0][value][date]', date('Y-m-d', $unpublish_time + 120)); + $I->fillField('unpublish_on[0][value][time]', date('H:i:s', $unpublish_time + 120)); + $I->click('Save'); + $I->canSee('has been created.'); + + // After the previous + $I->amOnPage('/admin/content/summer/add/global_msg'); + $I->fillField('Label', $this->faker->words(4, TRUE)); + $I->checkOption('Published'); + + $I->fillField('publish_on[0][value][date]', date('Y-m-d', $unpublish_time + 120)); + $I->fillField('publish_on[0][value][time]', date('H:i:s', $unpublish_time + 120)); + $I->click('Save'); + $I->canSee('has been created.'); + } + +} diff --git a/tests/phpunit/example.phpunit.xml b/tests/phpunit/example.phpunit.xml index d069d60c5..387e2134b 100644 --- a/tests/phpunit/example.phpunit.xml +++ b/tests/phpunit/example.phpunit.xml @@ -13,6 +13,7 @@ ../modules/custom/* ../profiles/* + ../profiles/* ../modules/custom/*/tests ../modules/custom/*/*/tests ../modules/custom/*/*/*/tests