You are here

SchedulerRulesEventsTest.php in Scheduler 2.x

Same filename and directory in other branches
  1. 8 tests/src/Functional/SchedulerRulesEventsTest.php

File

tests/src/Functional/SchedulerRulesEventsTest.php
View source
<?php

namespace Drupal\Tests\scheduler\Functional;

use Drupal\rules\Context\ContextConfig;

/**
 * Tests the six events that Scheduler provides for use in Rules module.
 *
 * phpcs:set Drupal.Arrays.Array lineLimit 140
 *
 * @group scheduler
 */
class SchedulerRulesEventsTest extends SchedulerBrowserTestBase {

  /**
   * Additional modules required.
   *
   * @var array
   */
  protected static $modules = [
    'scheduler_rules_integration',
  ];

  /**
   * {@inheritdoc}
   */
  protected function setUp() : void {
    parent::setUp();
    $this->rulesStorage = $this->container
      ->get('entity_type.manager')
      ->getStorage('rules_reaction_rule');
    $this->expressionManager = $this->container
      ->get('plugin.manager.rules_expression');

    // Create a reaction rule to display a system message for each of the six
    // events that Scheduler triggers, for each entity type. The array of data
    // contains the event name and the text to display.
    // These rules are all active throughout all of the tests, which makes the
    // tests stronger, because it will show not only that the correct events are
    // triggered in the right places, but also that they are not triggered in
    // the wrong places.
    $rule_data = [
      // The first six events are the originals, only dispatched for Nodes.
      1 => [
        'scheduler_new_node_is_scheduled_for_publishing_event',
        'A new node is created and is scheduled for publishing.',
      ],
      2 => [
        'scheduler_existing_node_is_scheduled_for_publishing_event',
        'An existing node is saved and is scheduled for publishing.',
      ],
      3 => [
        'scheduler_has_published_this_node_event',
        'Scheduler has published this node during cron.',
      ],
      4 => [
        'scheduler_new_node_is_scheduled_for_unpublishing_event',
        'A new node is created and is scheduled for unpublishing.',
      ],
      5 => [
        'scheduler_existing_node_is_scheduled_for_unpublishing_event',
        'An existing node is saved and is scheduled for unpublishing.',
      ],
      6 => [
        'scheduler_has_unpublished_this_node_event',
        'Scheduler has unpublished this node during cron.',
      ],
      // These six events are dispatched only for Media entities.
      7 => [
        'scheduler:new_media_is_scheduled_for_publishing',
        'A new media item is created and scheduled for publishing.',
      ],
      8 => [
        'scheduler:existing_media_is_scheduled_for_publishing',
        'An existing media item is saved and scheduled for publishing.',
      ],
      9 => [
        'scheduler:media_has_been_published_via_cron',
        'Scheduler has published this media item during cron.',
      ],
      10 => [
        'scheduler:new_media_is_scheduled_for_unpublishing',
        'A new media item is created and scheduled for unpublishing.',
      ],
      11 => [
        'scheduler:existing_media_is_scheduled_for_unpublishing',
        'An existing media item is saved and scheduled for unpublishing.',
      ],
      12 => [
        'scheduler:media_has_been_unpublished_via_cron',
        'Scheduler has unpublished this media item during cron.',
      ],
      // These six events are dispatched only for Commerce Product entities.
      13 => [
        'scheduler:new_commerce_product_is_scheduled_for_publishing',
        'A new product is created and scheduled for publishing.',
      ],
      14 => [
        'scheduler:existing_commerce_product_is_scheduled_for_publishing',
        'An existing product is scheduled for publishing.',
      ],
      15 => [
        'scheduler:commerce_product_has_been_published_via_cron',
        'Scheduler has published this product during cron.',
      ],
      16 => [
        'scheduler:new_commerce_product_is_scheduled_for_unpublishing',
        'A new product is created and scheduled for unpublishing.',
      ],
      17 => [
        'scheduler:existing_commerce_product_is_scheduled_for_unpublishing',
        'An existing product is scheduled for unpublishing.',
      ],
      18 => [
        'scheduler:commerce_product_has_been_unpublished_via_cron',
        'Scheduler has unpublished this product during cron.',
      ],
    ];

    // PHPCS throws a false-positive 'variable $var is undefined' message when
    // the variable is defined by list( ) syntax. To avoid the unwanted warnings
    // we can put phpcs:ignore DrupalPractice.CodeAnalysis.VariableAnalysis
    // before each line that produces a warning of this type.
    // This has been fixed in Coder 8.3.10 which is used in Core 9.1.
    // @see https://www.drupal.org/project/coder/issues/2876245
    // phpcs:ignore DrupalPractice.CodeAnalysis.VariableAnalysis
    foreach ($rule_data as $i => list($event_name, $description)) {

      // phpcs:ignore DrupalPractice.CodeAnalysis.VariableAnalysis
      $rule[$i] = $this->expressionManager
        ->createRule();

      // phpcs:ignore DrupalPractice.CodeAnalysis.VariableAnalysis
      $this->message[$i] = 'RULES message ' . $i . '. ' . $description;

      // phpcs:ignore DrupalPractice.CodeAnalysis.VariableAnalysis
      $rule[$i]
        ->addAction('rules_system_message', ContextConfig::create()
        ->setValue('message', $this->message[$i])
        ->setValue('type', 'status'));
      $config_entity = $this->rulesStorage
        ->create([
        'id' => 'rule' . $i,
        // phpcs:ignore DrupalPractice.CodeAnalysis.VariableAnalysis
        'events' => [
          [
            'event_name' => $event_name,
          ],
        ],
        // phpcs:ignore DrupalPractice.CodeAnalysis.VariableAnalysis
        'expression' => $rule[$i]
          ->getConfiguration(),
      ]);
      $config_entity
        ->save();
    }
    $this
      ->drupalLogin($this->schedulerUser);
  }

  /**
   * Check the presence or absence of expected message texts on the page.
   *
   * @param string $entityTypeId
   *   The entity type being tested.
   * @param array $expectedMessages
   *   The ids of the messages that should be showing on the current page. All
   *   other messsages should not be displayed.
   */
  public function checkMessages(string $entityTypeId = NULL, array $expectedMessages = []) {

    // Add the required entity offset to each message id in the expected array.
    $offset = [
      'node' => 0,
      'media' => 6,
      'commerce_product' => 12,
    ];
    array_walk($expectedMessages, function (&$item) use ($offset, $entityTypeId) {
      $item = $item + $offset[$entityTypeId];
    });

    // Check that all the expected messages are shown.
    foreach ($expectedMessages as $i) {
      $this
        ->assertSession()
        ->pageTextContains($this->message[$i]);
    }

    // Check that none of the other messages are shown.
    $notExpecting = array_diff(array_keys($this->message), $expectedMessages);
    foreach ($notExpecting as $i) {
      $this
        ->assertSession()
        ->pageTextNotContains($this->message[$i]);
    }
  }

  /**
   * Tests that no events are triggered when there are no scheduling dates.
   *
   * @dataProvider dataStandardEntityTypes()
   */
  public function testRulesEventsNone($entityTypeId, $bundle) {

    // Add and save an entity without any scheduled dates and check that no
    // events are triggered.
    $titleField = $entityTypeId == 'media' ? 'name' : 'title';
    $title = 'A. Create with no dates';
    $this
      ->drupalGet($this
      ->entityAddUrl($entityTypeId, $bundle));
    $this
      ->submitForm([
      "{$titleField}[0][value]" => $title,
    ], 'Save');
    $this
      ->checkMessages();

    // Edit the entity and check that no events are triggered.
    $entity = $this
      ->getEntityByTitle($entityTypeId, $title);
    $this
      ->drupalGet($entity
      ->toUrl('edit-form'));
    $this
      ->submitForm([
      "{$titleField}[0][value]" => 'B. Edit with no dates',
    ], 'Save');
    $this
      ->checkMessages();
  }

  /**
   * Tests the three events related to publishing an entity.
   *
   * @dataProvider dataStandardEntityTypes()
   */
  public function testRulesEventsPublish($entityTypeId, $bundle) {

    // Allow dates in the past.
    $this
      ->entityTypeObject($entityTypeId, $bundle)
      ->setThirdPartySetting('scheduler', 'publish_past_date', 'schedule')
      ->save();

    // Create an entity with a publish-on date, and check that only event 1 is
    // triggered.
    $titleField = $entityTypeId == 'media' ? 'name' : 'title';
    $title = 'C. Create with publish-on date';
    $edit = [
      "{$titleField}[0][value]" => $title,
      'publish_on[0][value][date]' => date('Y-m-d', time() - 60),
      'publish_on[0][value][time]' => date('H:i:s', time() - 60),
    ];
    $this
      ->drupalGet($this
      ->entityAddUrl($entityTypeId, $bundle));
    $this
      ->submitForm($edit, 'Save');
    $this
      ->checkMessages($entityTypeId, [
      1,
    ]);

    // Edit the entity and check that only event 2 is triggered.
    $entity = $this
      ->getEntityByTitle($entityTypeId, $title);
    $this
      ->drupalGet($entity
      ->toUrl('edit-form'));
    $this
      ->submitForm([
      "{$titleField}[0][value]" => 'D. Edit with publish-on date',
    ], 'Save');
    $this
      ->checkMessages($entityTypeId, [
      2,
    ]);

    // Run cron and check that only event 3 is triggered.
    $this
      ->cronRun();
    $this
      ->drupalGet($entity
      ->toUrl());
    $this
      ->checkMessages($entityTypeId, [
      3,
    ]);
  }

  /**
   * Tests the three events related to unpublishing an entity.
   *
   * @dataProvider dataStandardEntityTypes()
   */
  public function testRulesEventsUnpublish($entityTypeId, $bundle) {

    // Create an entity with an unpublish-on date, and check that only event 4
    // is triggered.
    $titleField = $entityTypeId == 'media' ? 'name' : 'title';
    $title = 'E. Create with unpublish-on date';
    $edit = [
      "{$titleField}[0][value]" => $title,
      'unpublish_on[0][value][date]' => date('Y-m-d', time() + 5),
      'unpublish_on[0][value][time]' => date('H:i:s', time() + 5),
    ];
    $this
      ->drupalGet($this
      ->entityAddUrl($entityTypeId, $bundle));
    $this
      ->submitForm($edit, 'Save');
    $this
      ->checkMessages($entityTypeId, [
      4,
    ]);

    // Edit the entity and check that only event 5 is triggered.
    $entity = $this
      ->getEntityByTitle($entityTypeId, $title);
    $this
      ->drupalGet($entity
      ->toUrl('edit-form'));
    $this
      ->submitForm([
      "{$titleField}[0][value]" => 'F. Edit with unpublish-on date',
    ], 'Save');
    $this
      ->checkMessages($entityTypeId, [
      5,
    ]);

    // Delay to ensure that the dates are in the past so that the entity will be
    // processed during cron, and check that only event 6 is triggered.
    sleep(6);
    $this
      ->cronRun();
    $this
      ->drupalGet($entity
      ->toUrl());
    $this
      ->checkMessages($entityTypeId, [
      6,
    ]);
  }

  /**
   * Tests all six events related to publishing and unpublishing an entity.
   *
   * @dataProvider dataStandardEntityTypes()
   */
  public function testRulesEventsBoth($entityTypeId, $bundle) {

    // Allow dates in the past.
    $this
      ->entityTypeObject($entityTypeId, $bundle)
      ->setThirdPartySetting('scheduler', 'publish_past_date', 'schedule')
      ->save();

    // Create an entity with both publish-on and unpublish-on dates, and check
    // that both event 1 and event 4 are triggered.
    $titleField = $entityTypeId == 'media' ? 'name' : 'title';
    $title = 'G. Create with both dates';
    $edit = [
      "{$titleField}[0][value]" => $title,
      'publish_on[0][value][date]' => date('Y-m-d', time() - 60),
      'publish_on[0][value][time]' => date('H:i:s', time() - 60),
      'unpublish_on[0][value][date]' => date('Y-m-d', time() + 5),
      'unpublish_on[0][value][time]' => date('H:i:s', time() + 5),
    ];
    $this
      ->drupalGet($this
      ->entityAddUrl($entityTypeId, $bundle));
    $this
      ->submitForm($edit, 'Save');
    $this
      ->checkMessages($entityTypeId, [
      1,
      4,
    ]);

    // Edit the entity and check that only events 2 and 5 are triggered.
    $entity = $this
      ->getEntityByTitle($entityTypeId, $title);
    $this
      ->drupalGet($entity
      ->toUrl('edit-form'));
    $this
      ->submitForm([
      "{$titleField}[0][value]" => 'H. Edit with both dates',
    ], 'Save');
    $this
      ->checkMessages($entityTypeId, [
      2,
      5,
    ]);

    // Delay to ensure that the dates are in the past so that the entity will be
    // processed during cron, and assert that events 3, 5 and 6 are triggered.
    sleep(6);
    $this
      ->cronRun();
    $this
      ->drupalGet($entity
      ->toUrl());
    $this
      ->checkMessages($entityTypeId, [
      3,
      5,
      6,
    ]);
  }

}

Classes

Namesort descending Description
SchedulerRulesEventsTest Tests the six events that Scheduler provides for use in Rules module.