diff --git a/scheduler.module b/scheduler.module index a0b58a0..c67a267 100644 --- a/scheduler.module +++ b/scheduler.module @@ -308,6 +308,7 @@ function scheduler_node_view(array &$build, EntityInterface $node, EntityViewDis function scheduler_node_presave(EntityInterface $node) { $config = \Drupal::config('scheduler.settings'); $entity = $node->type->entity; + $request_time = \Drupal::time()->getRequestTime(); // If there is no entity object or the class is incorrect then stop here. This // should not really happen but it has been observed, so better to be safe. @@ -337,14 +338,14 @@ function scheduler_node_presave(EntityInterface $node) { if (rand(1, 100) <= $publishing_percent) { // Randomly assign a publish_on value in the range starting with the // created date and up to the selected time range in the future. - $node->set('publish_on', rand($node->created->value + 1, \Drupal::time()->getRequestTime() + $time_range)); + $node->set('publish_on', rand($node->created->value + 1, $request_time + $time_range)); } } if ($unpublishing_percent && in_array($node->getType(), $unpublishing_enabled_types)) { if (rand(1, 100) <= $unpublishing_percent) { // Randomly assign an unpublish_on value in the range from the later of // created date/publish_on date up to the time range in the future. - $node->set('unpublish_on', rand(max($node->created->value, $node->publish_on->value), \Drupal::time()->getRequestTime() + $time_range)); + $node->set('unpublish_on', rand(max($node->created->value, $node->publish_on->value), $request_time + $time_range)); } } } @@ -359,7 +360,7 @@ function scheduler_node_presave(EntityInterface $node) { // Publish the node immediately if the publication date is in the past. $publish_immediately = $entity->getThirdPartySetting('scheduler', 'publish_past_date', $config->get('default_publish_past_date')) == 'publish'; - if ($publication_allowed && $publish_immediately && $node->publish_on->value <= \Drupal::time()->getRequestTime()) { + if ($publication_allowed && $publish_immediately && $node->publish_on->value <= $request_time) { // Trigger the PRE_PUBLISH_INMEDIATELY event so that modules can react // before the node has been published. $event = new SchedulerEvent($node); diff --git a/tests/src/Functional/SchedulerApiTest.php b/tests/src/Functional/SchedulerApiTest.php index bf11473..1840122 100644 --- a/tests/src/Functional/SchedulerApiTest.php +++ b/tests/src/Functional/SchedulerApiTest.php @@ -253,10 +253,9 @@ class SchedulerApiTest extends SchedulerBrowserTestBase { $node->set('sticky', FALSE)->set('promote', FALSE)->save(); // Edit the node and set a publish-on date in the past. - $request_time = $this->time->getRequestTime(); $edit = [ - 'publish_on[0][value][date]' => date('Y-m-d', strtotime('-2 day', $request_time)), - 'publish_on[0][value][time]' => date('H:i:s', strtotime('-2 day', $request_time)), + 'publish_on[0][value][date]' => date('Y-m-d', strtotime('-2 day', $this->requestTime)), + 'publish_on[0][value][time]' => date('H:i:s', strtotime('-2 day', $this->requestTime)), ]; $this->drupalPostForm('node/' . $node->id() . '/edit', $edit, t('Save')); // Verify that the values have been altered as expected. diff --git a/tests/src/Functional/SchedulerBasicTest.php b/tests/src/Functional/SchedulerBasicTest.php index 0b6b6d4..ab18b51 100644 --- a/tests/src/Functional/SchedulerBasicTest.php +++ b/tests/src/Functional/SchedulerBasicTest.php @@ -63,7 +63,7 @@ class SchedulerBasicTest extends SchedulerBrowserTestBase { } // Modify the scheduler field data to a time in the past, then run cron. - $node->$key = $this->time->getRequestTime() - 1; + $node->$key = $this->requestTime - 1; $node->save(); $this->cronRun(); diff --git a/tests/src/Functional/SchedulerBrowserTestBase.php b/tests/src/Functional/SchedulerBrowserTestBase.php index e4b361b..91431fd 100644 --- a/tests/src/Functional/SchedulerBrowserTestBase.php +++ b/tests/src/Functional/SchedulerBrowserTestBase.php @@ -75,11 +75,11 @@ abstract class SchedulerBrowserTestBase extends BrowserTestBase { protected $database; /** - * The time service. + * The request time stored as interger for direct re-use in many tests. * - * @var \Drupal\Component\Datetime\TimeInterface + * @var int */ - protected $time; + protected $requestTime; /** * {@inheritdoc} @@ -141,7 +141,8 @@ abstract class SchedulerBrowserTestBase extends BrowserTestBase { // Store the database connection for re-use in the actual tests. $this->database = $this->container->get('database'); - $this->time = $this->container->get('datetime.time'); + // Determine the request time and save for re-use in the actual tests. + $this->requestTime = $this->container->get('datetime.time')->getRequestTime(); } /** diff --git a/tests/src/Functional/SchedulerDefaultTimeTest.php b/tests/src/Functional/SchedulerDefaultTimeTest.php index 5719fbe..22af451 100644 --- a/tests/src/Functional/SchedulerDefaultTimeTest.php +++ b/tests/src/Functional/SchedulerDefaultTimeTest.php @@ -37,12 +37,12 @@ class SchedulerDefaultTimeTest extends SchedulerBrowserTestBase { // First test with the "date only" functionality disabled. $config->set('allow_date_only', FALSE)->save(); - $request_time = $this->time->getRequestTime(); + // Test that entering a time is required. $edit = [ 'title[0][value]' => 'No time ' . $this->randomString(15), - 'publish_on[0][value][date]' => $date_formatter->format(strtotime('+1 day', $request_time), 'custom', 'Y-m-d'), - 'unpublish_on[0][value][date]' => $date_formatter->format(strtotime('+2 day', $request_time), 'custom', 'Y-m-d'), + 'publish_on[0][value][date]' => $date_formatter->format(strtotime('+1 day', $this->requestTime), 'custom', 'Y-m-d'), + 'unpublish_on[0][value][date]' => $date_formatter->format(strtotime('+2 day', $this->requestTime), 'custom', 'Y-m-d'), ]; // Create a node and check that the expected error messages are shown. $this->drupalPostForm('node/add/' . $this->type, $edit, t('Save')); @@ -58,8 +58,8 @@ class SchedulerDefaultTimeTest extends SchedulerBrowserTestBase { $this->assertSession()->pageTextNotContains($unpublish_validation_message, 'If the default time option is enabled the user can skip the time when scheduling content for unpublication.'); // Check that the scheduled information is shown after saving. - $publish_time = strtotime('+1 day midnight', $request_time) + $seconds; - $unpublish_time = strtotime('+2 day midnight', $request_time) + $seconds; + $publish_time = strtotime('+1 day midnight', $this->requestTime) + $seconds; + $unpublish_time = strtotime('+2 day midnight', $this->requestTime) + $seconds; $this->assertText(sprintf('This post is unpublished and will be published %s', $date_formatter->format($publish_time, 'long')), 'The user is informed that the content will be published on the requested date, on the default time.'); // Protect in case the node was not created. diff --git a/tests/src/Functional/SchedulerDevelGenerateTest.php b/tests/src/Functional/SchedulerDevelGenerateTest.php index 04d474c..4af7852 100644 --- a/tests/src/Functional/SchedulerDevelGenerateTest.php +++ b/tests/src/Functional/SchedulerDevelGenerateTest.php @@ -82,8 +82,7 @@ class SchedulerDevelGenerateTest extends SchedulerBrowserTestBase { // slowly creep forward during sucessive calls. Tests can fail incorrectly // for this reason, hence the best approximation is to use time() when // calculating the upper end of the range. - $request_time = $this->time->getRequestTime(); - $min = $request_time - $time_range; + $min = $this->requestTime - $time_range; $max = time() + $time_range; $query = $this->nodeStorage->getAggregateQuery(); diff --git a/tests/src/Functional/SchedulerMultilingualTest.php b/tests/src/Functional/SchedulerMultilingualTest.php index 1b67417..99e3777 100644 --- a/tests/src/Functional/SchedulerMultilingualTest.php +++ b/tests/src/Functional/SchedulerMultilingualTest.php @@ -158,11 +158,10 @@ class SchedulerMultilingualTest extends SchedulerBrowserTestBase { // Create the second translation, to be published in the future. $this->drupalGet('node/' . $node->id() . '/translations/add/' . $this->languages[0]['code'] . '/' . $this->languages[2]['code']); - $request_time = $this->time->getRequestTime(); $edit = [ 'title[0][value]' => $this->languages[2]['name'] . '(2) - Publish in the future', - 'publish_on[0][value][date]' => date('Y-m-d', strtotime('+2 day', $request_time)), - 'publish_on[0][value][time]' => date('H:i:s', strtotime('+2 day', $request_time)), + 'publish_on[0][value][date]' => date('Y-m-d', strtotime('+2 day', $this->requestTime)), + 'publish_on[0][value][time]' => date('H:i:s', strtotime('+2 day', $this->requestTime)), ]; $this->submitForm($edit, $save_button_text); @@ -170,8 +169,8 @@ class SchedulerMultilingualTest extends SchedulerBrowserTestBase { $this->drupalGet('node/' . $node->id() . '/translations/add/' . $this->languages[0]['code'] . '/' . $this->languages[3]['code']); $edit = [ 'title[0][value]' => $this->languages[3]['name'] . '(3) - Publish in the past', - 'publish_on[0][value][date]' => date('Y-m-d', strtotime('-2 day', $request_time)), - 'publish_on[0][value][time]' => date('H:i:s', strtotime('-2 day', $request_time)), + 'publish_on[0][value][date]' => date('Y-m-d', strtotime('-2 day', $this->requestTime)), + 'publish_on[0][value][time]' => date('H:i:s', strtotime('-2 day', $this->requestTime)), ]; $this->submitForm($edit, $save_button_text); diff --git a/tests/src/Functional/SchedulerNodeAccessTest.php b/tests/src/Functional/SchedulerNodeAccessTest.php index 2ac79da..f9a56f5 100644 --- a/tests/src/Functional/SchedulerNodeAccessTest.php +++ b/tests/src/Functional/SchedulerNodeAccessTest.php @@ -57,7 +57,7 @@ class SchedulerNodeAccessTest extends SchedulerBrowserTestBase { 'type' => $this->type, 'status' => $data['status'], 'title' => 'Test node to be ' . $data['after'], - $field => $this->time->getRequestTime() + 1, + $field => $this->requestTime + 1, ]; $node = $this->drupalCreateNode($settings); $this->drupalGet('node/' . $node->id()); diff --git a/tests/src/Functional/SchedulerNonEnabledTypeTest.php b/tests/src/Functional/SchedulerNonEnabledTypeTest.php index 02770a6..d4988fa 100644 --- a/tests/src/Functional/SchedulerNonEnabledTypeTest.php +++ b/tests/src/Functional/SchedulerNonEnabledTypeTest.php @@ -74,12 +74,11 @@ class SchedulerNonEnabledTypeTest extends SchedulerBrowserTestBase { // could be done by a third-party module, or a by-product of the node type // being enabled for publishing then being disabled before it got published. $title = $info . ' (' . $run_number . 'b)'; - $request_time = $this->time->getRequestTime(); $edit = [ 'title' => $title, 'status' => 0, 'type' => $this->contentName, - 'publish_on' => $request_time - 2, + 'publish_on' => $this->requestTime - 2, ]; $node = $this->drupalCreateNode($edit); @@ -106,7 +105,7 @@ class SchedulerNonEnabledTypeTest extends SchedulerBrowserTestBase { 'title' => $title, 'status' => 1, 'type' => $this->contentName, - 'unpublish_on' => $request_time - 1, + 'unpublish_on' => $this->requestTime - 1, ]; $node = $this->drupalCreateNode($edit); diff --git a/tests/src/Functional/SchedulerPastDatesTest.php b/tests/src/Functional/SchedulerPastDatesTest.php index 2a7ceab..239513f 100644 --- a/tests/src/Functional/SchedulerPastDatesTest.php +++ b/tests/src/Functional/SchedulerPastDatesTest.php @@ -24,13 +24,13 @@ class SchedulerPastDatesTest extends SchedulerBrowserTestBase { // Create an unpublished page node. $node = $this->drupalCreateNode(['type' => $this->type, 'status' => FALSE]); - $request_time = $this->time->getRequestTime(); + // Test the default behavior: an error message should be shown when the user // enters a publication date that is in the past. $edit = [ 'title[0][value]' => 'Past ' . $this->randomString(10), - 'publish_on[0][value][date]' => \Drupal::service('date.formatter')->format(strtotime('-1 day', $request_time), 'custom', 'Y-m-d'), - 'publish_on[0][value][time]' => \Drupal::service('date.formatter')->format(strtotime('-1 day', $request_time), 'custom', 'H:i:s'), + 'publish_on[0][value][date]' => \Drupal::service('date.formatter')->format(strtotime('-1 day', $this->requestTime), 'custom', 'Y-m-d'), + 'publish_on[0][value][time]' => \Drupal::service('date.formatter')->format(strtotime('-1 day', $this->requestTime), 'custom', 'H:i:s'), ]; $this->drupalPostForm('node/' . $node->id() . '/edit', $edit, t('Save')); $this->assertText("The 'publish on' date must be in the future", 'An error message is shown by default when the publication date is in the past.'); @@ -66,7 +66,7 @@ class SchedulerPastDatesTest extends SchedulerBrowserTestBase { $this->nodeStorage->resetCache([$node->id()]); $node = $this->nodeStorage->load($node->id()); $this->assertFalse($node->isPublished(), 'The node has been unpublished when the publication date is in the past and the "schedule" behavior is chosen.'); - $this->assertEqual($node->publish_on->value, strtotime('-1 day', $request_time), 'The node has the correct publish_on date stored.'); + $this->assertEqual($node->publish_on->value, strtotime('-1 day', $this->requestTime), 'The node has the correct publish_on date stored.'); // Simulate a cron run and check that the node is published. scheduler_cron(); @@ -77,8 +77,8 @@ class SchedulerPastDatesTest extends SchedulerBrowserTestBase { // Check that an Unpublish date in the past fails validation. $edit = [ 'title[0][value]' => 'Unpublish in the past ' . $this->randomString(10), - 'unpublish_on[0][value][date]' => \Drupal::service('date.formatter')->format($request_time - 3600, 'custom', 'Y-m-d'), - 'unpublish_on[0][value][time]' => \Drupal::service('date.formatter')->format($request_time - 3600, 'custom', 'H:i:s'), + 'unpublish_on[0][value][date]' => \Drupal::service('date.formatter')->format($this->requestTime - 3600, 'custom', 'Y-m-d'), + 'unpublish_on[0][value][time]' => \Drupal::service('date.formatter')->format($this->requestTime - 3600, 'custom', 'H:i:s'), ]; $this->drupalPostForm('node/add/' . $this->type, $edit, t('Save')); $this->assertText("The 'unpublish on' date must be in the future", 'An error message is shown when the unpublish date is in the past.'); diff --git a/tests/src/Functional/SchedulerRevisioningTest.php b/tests/src/Functional/SchedulerRevisioningTest.php index 097e1ba..1c3b000 100644 --- a/tests/src/Functional/SchedulerRevisioningTest.php +++ b/tests/src/Functional/SchedulerRevisioningTest.php @@ -26,7 +26,7 @@ class SchedulerRevisioningTest extends SchedulerBrowserTestBase { protected function schedule(NodeInterface $node, $action = 'publish') { // Simulate scheduling by setting the (un)publication date in the past and // running cron. - $node->{$action . '_on'} = strtotime('-1 day', $this->time->getRequestTime()); + $node->{$action . '_on'} = strtotime('-1 day', $this->requestTime); $node->save(); scheduler_cron(); $this->nodeStorage->resetCache([$node->id()]); @@ -91,7 +91,7 @@ class SchedulerRevisioningTest extends SchedulerBrowserTestBase { */ public function testRevisioning() { // Create a scheduled node that is not automatically revisioned. - $created = strtotime('-2 day', $this->time->getRequestTime()); + $created = strtotime('-2 day', $this->requestTime); $settings = [ 'type' => $this->type, 'revision' => 0, @@ -119,7 +119,7 @@ class SchedulerRevisioningTest extends SchedulerBrowserTestBase { $node = $this->schedule($node); $this->assertRevisionCount($node->id(), 2, 'A new revision was created when revisioning is enabled.'); $expected_message = sprintf('Node published by Scheduler on %s. Previous creation date was %s.', - \Drupal::service('date.formatter')->format($this->time->getRequestTime(), 'short'), + \Drupal::service('date.formatter')->format($this->requestTime, 'short'), \Drupal::service('date.formatter')->format($created, 'short')); $this->assertRevisionLogMessage($node->id(), $expected_message, 'The correct message was found in the node revision log after scheduled publishing.'); @@ -127,8 +127,8 @@ class SchedulerRevisioningTest extends SchedulerBrowserTestBase { $node = $this->schedule($node, 'unpublish'); $this->assertRevisionCount($node->id(), 3, 'A new revision was created when a node was unpublished with revisioning enabled.'); $expected_message = sprintf('Node unpublished by Scheduler on %s. Previous change date was %s.', - \Drupal::service('date.formatter')->format($this->time->getRequestTime(), 'short'), - \Drupal::service('date.formatter')->format($this->time->getRequestTime(), 'short')); + \Drupal::service('date.formatter')->format($this->requestTime, 'short'), + \Drupal::service('date.formatter')->format($this->requestTime, 'short')); $this->assertRevisionLogMessage($node->id(), $expected_message, 'The correct message was found in the node revision log after scheduled unpublishing.'); } @@ -140,7 +140,7 @@ class SchedulerRevisioningTest extends SchedulerBrowserTestBase { $this->nodetype->setThirdPartySetting('scheduler', 'publish_past_date', 'schedule')->save(); // Create a node with a 'created' date two days in the past. - $created = strtotime('-2 day', $this->time->getRequestTime()); + $created = strtotime('-2 day', $this->requestTime); $settings = [ 'type' => $this->type, 'created' => $created, @@ -164,7 +164,7 @@ class SchedulerRevisioningTest extends SchedulerBrowserTestBase { $node = $this->schedule($node, 'publish'); // Check that the created date has changed to match the publish_on date. $created_after_cron = $node->created->value; - $this->assertEqual(strtotime('-1 day', $this->time->getRequestTime()), $created_after_cron, "With 'touch' option set, the node creation date is changed to match the publishing date."); + $this->assertEqual(strtotime('-1 day', $this->requestTime), $created_after_cron, "With 'touch' option set, the node creation date is changed to match the publishing date."); } } diff --git a/tests/src/Functional/SchedulerRulesActionsTest.php b/tests/src/Functional/SchedulerRulesActionsTest.php index 846ea10..9a3beb0 100644 --- a/tests/src/Functional/SchedulerRulesActionsTest.php +++ b/tests/src/Functional/SchedulerRulesActionsTest.php @@ -60,7 +60,7 @@ class SchedulerRulesActionsTest extends SchedulerBrowserTestBase { $rule1->addAction('scheduler_set_publishing_date_action', ContextConfig::create() ->map('node', 'node') - ->setValue('date', $this->time->getRequestTime() + 1800) + ->setValue('date', $this->requestTime + 1800) ) ->addAction('rules_system_message', ContextConfig::create() @@ -182,7 +182,7 @@ class SchedulerRulesActionsTest extends SchedulerBrowserTestBase { $rule3->addAction('scheduler_set_unpublishing_date_action', ContextConfig::create() ->map('node', 'node') - ->setValue('date', $this->time->getRequestTime() + 1800) + ->setValue('date', $this->requestTime + 1800) ) ->addAction('rules_system_message', ContextConfig::create() diff --git a/tests/src/Functional/SchedulerRulesConditionsTest.php b/tests/src/Functional/SchedulerRulesConditionsTest.php index 04bbbc7..23a85b5 100644 --- a/tests/src/Functional/SchedulerRulesConditionsTest.php +++ b/tests/src/Functional/SchedulerRulesConditionsTest.php @@ -236,10 +236,9 @@ class SchedulerRulesConditionsTest extends SchedulerBrowserTestBase { $this->assertNoText($message8, '"' . $message8 . '" is not shown'); // Edit the node and set a publish_on date. - $request_time = $this->time->getRequestTime(); $edit = [ - 'publish_on[0][value][date]' => date('Y-m-d', strtotime('+1 day', $request_time)), - 'publish_on[0][value][time]' => date('H:i:s', strtotime('+1 day', $request_time)), + 'publish_on[0][value][date]' => date('Y-m-d', strtotime('+1 day', $this->requestTime)), + 'publish_on[0][value][time]' => date('H:i:s', strtotime('+1 day', $this->requestTime)), ]; $this->drupalPostForm('node/' . $this->node->id() . '/edit', $edit, t('Save')); @@ -250,8 +249,8 @@ class SchedulerRulesConditionsTest extends SchedulerBrowserTestBase { // Edit the node and set an unpublish_on date. $edit = [ - 'unpublish_on[0][value][date]' => date('Y-m-d', strtotime('+2 day', $request_time)), - 'unpublish_on[0][value][time]' => date('H:i:s', strtotime('+2 day', $request_time)), + 'unpublish_on[0][value][date]' => date('Y-m-d', strtotime('+2 day', $this->requestTime)), + 'unpublish_on[0][value][time]' => date('H:i:s', strtotime('+2 day', $this->requestTime)), ]; $this->drupalPostForm('node/' . $this->node->id() . '/edit', $edit, t('Save')); diff --git a/tests/src/Functional/SchedulerTokenReplaceTest.php b/tests/src/Functional/SchedulerTokenReplaceTest.php index 2a263a0..e1576cc 100644 --- a/tests/src/Functional/SchedulerTokenReplaceTest.php +++ b/tests/src/Functional/SchedulerTokenReplaceTest.php @@ -15,10 +15,9 @@ class SchedulerTokenReplaceTest extends SchedulerBrowserTestBase { public function testSchedulerTokenReplacement() { $this->drupalLogin($this->schedulerUser); $date_formatter = \Drupal::service('date.formatter'); - $request_time = $this->time->getRequestTime(); // Define timestamps for consistent use when repeated throughout this test. - $publish_on_timestamp = $request_time + 3600; - $unpublish_on_timestamp = $request_time + 7200; + $publish_on_timestamp = $this->requestTime + 3600; + $unpublish_on_timestamp = $this->requestTime + 7200; // Create an unpublished page with scheduled dates. $node = $this->drupalCreateNode([ diff --git a/tests/src/Functional/SchedulerValidationTest.php b/tests/src/Functional/SchedulerValidationTest.php index e8eacca..9b9d3c8 100644 --- a/tests/src/Functional/SchedulerValidationTest.php +++ b/tests/src/Functional/SchedulerValidationTest.php @@ -27,10 +27,9 @@ class SchedulerValidationTest extends SchedulerBrowserTestBase { 'type' => $this->type, 'status' => FALSE, ]); - $request_time = $this->time->getRequestTime(); $edit = [ - 'publish_on[0][value][date]' => date('Y-m-d', strtotime('+1 day', $request_time)), - 'publish_on[0][value][time]' => date('H:i:s', strtotime('+1 day', $request_time)), + 'publish_on[0][value][date]' => date('Y-m-d', strtotime('+1 day', $this->requestTime)), + 'publish_on[0][value][time]' => date('H:i:s', strtotime('+1 day', $this->requestTime)), ]; $this->drupalGet('node/' . $node->id() . '/edit'); @@ -67,12 +66,11 @@ class SchedulerValidationTest extends SchedulerBrowserTestBase { 'type' => $this->type, 'status' => FALSE, ]); - $request_time = $this->time->getRequestTime(); $edit = [ - 'publish_on[0][value][date]' => \Drupal::service('date.formatter')->format($request_time + 8100, 'custom', 'Y-m-d'), - 'publish_on[0][value][time]' => \Drupal::service('date.formatter')->format($request_time + 8100, 'custom', 'H:i:s'), - 'unpublish_on[0][value][date]' => \Drupal::service('date.formatter')->format($request_time + 1800, 'custom', 'Y-m-d'), - 'unpublish_on[0][value][time]' => \Drupal::service('date.formatter')->format($request_time + 1800, 'custom', 'H:i:s'), + 'publish_on[0][value][date]' => \Drupal::service('date.formatter')->format($this->requestTime + 8100, 'custom', 'Y-m-d'), + 'publish_on[0][value][time]' => \Drupal::service('date.formatter')->format($this->requestTime + 8100, 'custom', 'H:i:s'), + 'unpublish_on[0][value][date]' => \Drupal::service('date.formatter')->format($this->requestTime + 1800, 'custom', 'Y-m-d'), + 'unpublish_on[0][value][time]' => \Drupal::service('date.formatter')->format($this->requestTime + 1800, 'custom', 'H:i:s'), ]; $this->drupalPostForm('node/' . $node->id() . '/edit', $edit, $checkbox ? 'Save' : 'Save and keep unpublished'); $this->assertText("The 'unpublish on' date must be later than the 'publish on' date.", 'Validation prevents entering an unpublish-on date which is earlier than the publish-on date.');