2020-08-14 13:36:36 +02:00

954 lines
43 KiB
Plaintext

<?php
/**
* @file
* Scheduler module test case file.
*/
/**
* Provides common helper methods for Scheduler module tests.
*/
abstract class SchedulerTestBase extends DrupalWebTestCase {
/**
* The profile to install as a basis for testing.
*
* @var string
*/
protected $profile = 'testing';
/**
* A user with administration rights.
*
* @var object
*/
protected $admin_user;
/**
* Common settings and options.
*/
function commonSettings() {
// Create a 'Basic Page' content type.
$this->drupalCreateContentType(array('type' => 'page', 'name' => t('Basic page')));
// Create an administrator user.
$this->admin_user = $this->drupalCreateUser(array(
'access content',
'administer scheduler',
'create page content',
'edit own page content',
'delete own page content',
'view own unpublished content',
'administer nodes',
'schedule publishing of nodes'
));
// Add scheduler functionality to the page node type.
variable_set('scheduler_publish_enable_page', 1);
variable_set('scheduler_unpublish_enable_page', 1);
variable_set('scheduler_field_type', 'textfield');
}
/**
* Helper function for testScheduler(). Schedules content and asserts status.
*
* @param array $edit
* Node data, as if it was sent from the edit form.
* @param bool $scheduler_cron_only
* TRUE to only run Scheduler cron, FALSE to run default full Drupal cron.
*/
function helpTestScheduler($edit, $scheduler_cron_only = FALSE) {
// Add a page.
$langcode = LANGUAGE_NONE;
$title = $this->randomName();
$edit["title"] = $title;
$body = $this->randomName();
$edit["body[$langcode][0][value]"] = $body;
$this->drupalLogin($this->admin_user);
$this->drupalPost('node/add/page', $edit, t('Save'));
$node = $this->drupalGetNodeByTitle($title);
// Show the specific page for an anonymous visitor, then assert that the
// node is correctly published or unpublished.
$this->drupalLogout();
$this->drupalGet("node/{$node->nid}");
if (isset($edit['publish_on'])) {
$key = 'publish_on';
$this->assertResponse(403, t('Node is unpublished'));
}
else {
$key = 'unpublish_on';
$this->assertText($body, t('Node is published'));
}
// Verify that the scheduler table is not empty.
$this->assertTrue(db_query_range('SELECT 1 FROM {scheduler}', 0, 1)->fetchField(), 'Scheduler table is not empty');
// Modify the scheduler row to a time far enough in the past because
// scheduler_cron uses REQUEST_TIME and our timestamp has to be before that.
db_update('scheduler')->fields(array($key => time() - 3600))->execute();
if ($scheduler_cron_only) {
scheduler_cron();
}
else {
$this->cronRun();
}
// Verify that the scheduler table is empty.
$this->assertFalse(db_query_range('SELECT 1 FROM {scheduler}', 0, 1)->fetchField(), 'Scheduler table is empty');
// Show the specific page for an anonymous visitor, then assert that the
// node is correctly published or unpublished.
$this->drupalGet("node/{$node->nid}");
if (isset($edit['publish_on'])) {
$this->assertText($body, t('Node is published'));
}
else {
$this->assertResponse(403, t('Node is unpublished'));
}
}
/**
* Simulates the scheduled (un)publication of a node.
*
* @param object $node
* The node to schedule.
* @param string $action
* The action to perform: either 'publish' or 'unpublish'. Defaults to
* 'publish'.
*
* @return object
* The updated node, after scheduled (un)publication.
*/
function schedule($node, $action = 'publish') {
// Simulate scheduling by setting the (un)publication date in the past and
// running cron.
$node->{$action . '_on'} = strtotime('-1 day');
node_save($node);
scheduler_cron();
return node_load($node->nid, NULL, TRUE);
}
/**
* Check if the latest revision log message of a node matches a given string.
*
* @param int $nid
* The node id of the node to check.
* @param string $value
* The value with which the log message will be compared.
* @param string $message
* The message to display along with the assertion.
* @param string $group
* The type of assertion - examples are "Browser", "PHP".
*
* @return
* TRUE if the assertion succeeded, FALSE otherwise.
*/
function assertRevisionLogMessage($nid, $value, $message = '', $group = 'Other') {
$log_message = db_select('node_revision', 'r')
->fields('r', array('log'))
->condition('nid', $nid)
->orderBy('vid', 'DESC')
->range(0, 1)
->execute()
->fetchColumn();
return $this->assertEqual($log_message, $value, $message, $group);
}
/**
* Check if the number of revisions for a node matches a given value.
*
* @param int $nid
* The node id of the node to check.
* @param string $value
* The value with which the number of revisions will be compared.
* @param string $message
* The message to display along with the assertion.
* @param string $group
* The type of assertion - examples are "Browser", "PHP".
*
* @return
* TRUE if the assertion succeeded, FALSE otherwise.
*/
function assertRevisionCount($nid, $value, $message = '', $group = 'Other') {
$count = db_select('node_revision', 'r')
->fields('r', array('vid'))
->condition('nid', $nid)
->countQuery()
->execute()
->fetchColumn();
return $this->assertEqual($count, $value, $message, $group);
}
}
/**
* Tests the scheduler interface.
*/
class SchedulerFunctionalTest extends SchedulerTestBase {
/**
* {@inheritdoc}
*/
public static function getInfo() {
return array(
'name' => 'Scheduler functionality',
'description' => 'Publish/unpublish on time.',
'group' => 'Scheduler',
);
}
/**
* {@inheritdoc}
*/
function setUp() {
parent::setUp('scheduler');
parent::commonSettings();
}
/**
* Tests basic scheduling of content.
*
* @param bool $scheduler_cron_only
* TRUE to only run Scheduler cron, FALSE to run default full Drupal cron.
*/
function testScheduler($scheduler_cron_only = FALSE) {
// Create node values. Set time to one hour in the future.
$edit = array(
'publish_on' => format_date(time() + 3600, 'custom', 'Y-m-d H:i:s'),
'status' => 1,
'promote' => 1,
);
// Test scheduled publishing.
$this->helpTestScheduler($edit, $scheduler_cron_only);
// Test scheduled unpublishing.
$edit['unpublish_on'] = $edit['publish_on'];
unset($edit['publish_on']);
$this->helpTestScheduler($edit, $scheduler_cron_only);
}
/**
* Tests scheduler when not all cron tasks are run during cron.
*
* Verify that we can set variable 'scheduler_cache_clear_all' so the page
* cache is still cleared.
*
* @uses testScheduler()
*/
function testSchedulerWithOnlySchedulerCronAndAnonymousPageCache() {
// Cache pages for anonymous users.
variable_set('cache', 1);
// Instruct scheduler to clear caches itself, instead of relying on
// system_cron.
variable_set('scheduler_cache_clear_all', 1);
// Instruct the helper method to run only the scheduler cron.
$scheduler_cron_only = TRUE;
$this->testScheduler($scheduler_cron_only);
}
/**
* Test the different options for past publication dates.
*/
public function testSchedulerPastDates() {
// Log in.
$this->drupalLogin($this->admin_user);
// Create an unpublished page node.
$node = $this->drupalCreateNode(array('type' => 'page', 'status' => FALSE));
// Test the default behavior: an error message should be shown when the user
// enters a publication date that is in the past.
$edit = array(
'title' => $this->randomName(),
'publish_on' => format_date(strtotime('-1 day'), 'custom', 'Y-m-d H:i:s'),
);
$this->drupalPost('node/' . $node->nid . '/edit', $edit, t('Save'));
$this->assertRaw(t("The 'publish on' date must be in the future"), 'An error message is shown when the publication date is in the past and the "error" behavior is chosen.');
// Test the 'publish' behavior: the node should be published immediately.
variable_set('scheduler_publish_past_date_page', 'publish');
$this->drupalPost('node/' . $node->nid . '/edit', $edit, t('Save'));
$this->assertNoRaw(t("The 'publish on' date must be in the future"), 'No error message is shown when the publication date is in the past and the "publish" behavior is chosen.');
$this->assertRaw(t('@type %title has been updated.', array('@type' => t('Basic page'), '%title' => check_plain($edit['title']))), 'The node is saved successfully when the publication date is in the past and the "publish" behavior is chosen.');
// Reload the changed node and check that it is published.
$node = node_load($node->nid, NULL, TRUE);
$this->assertTrue($node->status, 'The node has been published immediately when the publication date is in the past and the "publish" behavior is chosen.');
// Test the 'schedule' behavior: the node should be unpublished and become
// published on the next cron run.
variable_set('scheduler_publish_past_date_page', 'schedule');
$this->drupalPost('node/' . $node->nid . '/edit', $edit, t('Save'));
$this->assertNoRaw(t("The 'publish on' date must be in the future"), 'No error message is shown when the publication date is in the past and the "schedule" behavior is chosen.');
$this->assertRaw(t('@type %title has been updated.', array('@type' => t('Basic page'), '%title' => check_plain($edit['title']))), 'The node is saved successfully when the publication date is in the past and the "schedule" behavior is chosen.');
$this->assertRaw(t('This post is unpublished and will be published @publish_time.', array('@publish_time' => $edit['publish_on'])), 'The node is scheduled to be published when the publication date is in the past and the "schedule" behavior is chosen.');
// Reload the node and check that it is unpublished but scheduled correctly.
$node = node_load($node->nid, NULL, TRUE);
$this->assertFalse($node->status, 'The node has been unpublished when the publication date is in the past and the "schedule" behavior is chosen.');
$this->assertEqual(format_date($node->publish_on, 'custom', 'Y-m-d H:i:s'), $edit['publish_on'], 'The node is scheduled for the required date');
// Simulate a cron run and check that the node is published.
scheduler_cron();
$node = node_load($node->nid, NULL, TRUE);
$this->assertTrue($node->status, 'The node with publication date in the past and the "schedule" behavior has now been published by cron.');
}
/**
* Tests the creation of new revisions on scheduling.
*/
public function testRevisioning() {
// Create a scheduled node that is not automatically revisioned.
$created = strtotime('-2 day');
$settings = array(
'revision' => 0,
'created' => $created,
);
$node = $this->drupalCreateNode($settings);
// First test scheduled publication with revisioning disabled.
$node = $this->schedule($node);
$this->assertRevisionCount($node->nid, 1, 'No new revision was created when a node was published with revisioning disabled.');
// Test scheduled unpublication.
$node = $this->schedule($node, 'unpublish');
$this->assertRevisionCount($node->nid, 1, 'No new revision was created when a node was unpublished with revisioning disabled.');
// Enable revisioning.
variable_set('scheduler_publish_revision_page', 1);
variable_set('scheduler_unpublish_revision_page', 1);
// Test scheduled publication with revisioning enabled.
$node = $this->schedule($node);
$this->assertRevisionCount($node->nid, 2, 'A new revision was created when revisioning is enabled.');
$expected_message = t('Node published by Scheduler on @now. Previous creation date was @date.', array(
'@now' => format_date(REQUEST_TIME, 'short'),
'@date' => format_date($created, 'short'),
));
$this->assertRevisionLogMessage($node->nid, $expected_message, 'The correct message was found in the node revision log after scheduled publishing.');
// Test scheduled unpublication with revisioning enabled.
$node = $this->schedule($node, 'unpublish');
$this->assertRevisionCount($node->nid, 3, 'A new revision was created when a node was unpublished with revisioning enabled.');
$expected_message = t('Node unpublished by Scheduler on @now. Previous change date was @date.', array(
'@now' => format_date(REQUEST_TIME, 'short'),
'@date' => format_date(REQUEST_TIME, 'short'),
));
$this->assertRevisionLogMessage($node->nid, $expected_message, 'The correct message was found in the node revision log after scheduled unpublishing.');
}
/**
* Tests if options can both be displayed as extra fields and vertical tabs.
*/
function testExtraFields() {
$this->drupalLogin($this->admin_user);
// Test if the options are shown as vertical tabs by default.
$this->drupalGet('node/add/page');
$this->assertTrue($this->xpath('//div[contains(@class, "vertical-tabs-panes")]/fieldset[@id = "edit-scheduler-settings"]'), 'By default the scheduler options are shown as a vertical tab.');
// Test if the options are shown as extra fields when configured to do so.
variable_set('scheduler_use_vertical_tabs_page', 0);
$this->drupalGet('node/add/page');
$this->assertFalse($this->xpath('//div[contains(@class, "vertical-tabs-panes")]/fieldset[@id = "edit-scheduler-settings"]'), 'The scheduler options are not shown as a vertical tab when they are configured to show as an extra field.');
$this->assertTrue($this->xpath('//fieldset[@id = "edit-scheduler-settings" and contains(@class, "collapsed")]'), 'The scheduler options are shown as a collapsed fieldset when they are configured to show as an extra field.');
// Test the option to expand the fieldset.
variable_set('scheduler_expand_fieldset_page', 1);
$this->drupalGet('node/add/page');
$this->assertFalse($this->xpath('//div[contains(@class, "vertical-tabs-panes")]/fieldset[@id = "edit-scheduler-settings"]'), 'The scheduler options are not shown as a vertical tab when they are configured to show as an expanded fieldset.');
$this->assertTrue($this->xpath('//fieldset[@id = "edit-scheduler-settings" and not(contains(@class, "collapsed"))]'), 'The scheduler options are shown as an expanded fieldset.');
}
/**
* Tests creating and editing nodes with required scheduling enabled.
*/
function testRequiredScheduling() {
$this->drupalLogin($this->admin_user);
// Define test scenarios with expected results.
$test_cases = array(
// The 1-10 numbering used below matches the test cases described in
// http://drupal.org/node/1198788#comment-7816119
// A. Test scenarios that require scheduled publishing.
// When creating a new unpublished node it is required to enter a
// publication date.
array(
'id' => 1,
'required' => 'publish',
'operation' => 'add',
'status' => 0,
'expected' => 'required',
'message' => 'When scheduled publishing is required and a new unpublished node is created, entering a date in the publish on field is required.',
),
// When creating a new published node it is required to enter a
// publication date. The node will be unpublished on form submit.
array(
'id' => 2,
'required' => 'publish',
'operation' => 'add',
'status' => 1,
'expected' => 'required',
'message' => 'When scheduled publishing is required and a new published node is created, entering a date in the publish on field is required.',
),
// When editing a published node it is not needed to enter a publication
// date since the node is already published.
array(
'id' => 3,
'required' => 'publish',
'operation' => 'edit',
'scheduled' => 0,
'status' => 1,
'expected' => 'not required',
'message' => 'When scheduled publishing is required and an existing published, unscheduled node is edited, entering a date in the publish on field is not required.',
),
// When editing an unpublished node that is scheduled for publication it
// is required to enter a publication date.
array(
'id' => 4,
'required' => 'publish',
'operation' => 'edit',
'scheduled' => 1,
'status' => 0,
'expected' => 'required',
'message' => 'When scheduled publishing is required and an existing unpublished, scheduled node is edited, entering a date in the publish on field is required.',
),
// When editing an unpublished node that is not scheduled for publication
// it is not required to enter a publication date since this means that
// the node has already gone through a publication > unpublication cycle.
array(
'id' => 5,
'required' => 'publish',
'operation' => 'edit',
'scheduled' => 0,
'status' => 0,
'expected' => 'not required',
'message' => 'When scheduled publishing is required and an existing unpublished, unscheduled node is edited, entering a date in the publish on field is not required.',
),
// B. Test scenarios that require scheduled unpublishing.
// When creating a new unpublished node it is required to enter an
// unpublication date since it is to be expected that the node will be
// published at some point and should subsequently be unpublished.
array(
'id' => 6,
'required' => 'unpublish',
'operation' => 'add',
'status' => 0,
'expected' => 'required',
'message' => 'When scheduled unpublishing is required and a new unpublished node is created, entering a date in the unpublish on field is required.',
),
// When creating a new published node it is required to enter an
// unpublication date.
array(
'id' => 7,
'required' => 'unpublish',
'operation' => 'add',
'status' => 1,
'expected' => 'required',
'message' => 'When scheduled unpublishing is required and a new published node is created, entering a date in the unpublish on field is required.',
),
// When editing a published node it is required to enter an unpublication
// date.
array(
'id' => 8,
'required' => 'unpublish',
'operation' => 'edit',
'scheduled' => 0,
'status' => 1,
'expected' => 'required',
'message' => 'When scheduled unpublishing is required and an existing published, unscheduled node is edited, entering a date in the unpublish on field is required.',
),
// When editing an unpublished node that is scheduled for publication it
// it is required to enter an unpublication date.
array(
'id' => 9,
'required' => 'unpublish',
'operation' => 'edit',
'scheduled' => 1,
'status' => 0,
'expected' => 'required',
'message' => 'When scheduled unpublishing is required and an existing unpublished, scheduled node is edited, entering a date in the unpublish on field is required.',
),
// When editing an unpublished node that is not scheduled for publication
// it is not required to enter an unpublication date since this means that
// the node has already gone through a publication - unpublication cycle.
array(
'id' => 10,
'required' => 'unpublish',
'operation' => 'edit',
'scheduled' => 0,
'status' => 0,
'expected' => 'not required',
'message' => 'When scheduled unpublishing is required and an existing unpublished, unscheduled node is edited, entering a date in the unpublish on field is not required.',
),
);
foreach ($test_cases as $test_case) {
// Enable required (un)publishing as stipulated by the test case.
variable_set('scheduler_publish_required_page', $test_case['required'] == 'publish');
variable_set('scheduler_unpublish_required_page', $test_case['required'] == 'unpublish');
// Set the default node status, used when creating a new node.
$node_options_page = !empty($test_case['status']) ? array('status') : array();
variable_set('node_options_page', $node_options_page);
// To assist viewing and analysing the generated test result pages create
// a text string showing all the test case parameters.
$title_data = array();
foreach ($test_case as $key => $value) {
if ($key != 'message') {
$title_data[] = $key . ' = ' . $value;
}
}
$title = implode(', ', $title_data);
// If the test case requires editing a node, we need to create one first.
if ($test_case['operation'] == 'edit') {
$options = array(
'title' => $title,
'type' => 'page',
'status' => $test_case['status'],
'publish_on' => !empty($test_case['scheduled']) ? strtotime('+ 1 day') : 0,
);
$node = $this->drupalCreateNode($options);
}
// Make sure the publication date fields are empty so we can check if they
// throw form validation errors when they are required.
$edit = array(
'title' => $title,
'publish_on' => '',
'unpublish_on' => '',
);
$path = $test_case['operation'] == 'add' ? 'node/add/page' : 'node/' . $node->nid . '/edit';
$this->drupalPost($path, $edit, t('Save'));
// Check for the expected result.
switch ($test_case['expected']) {
case 'required':
$string = t('!name field is required.', array('!name' => ucfirst($test_case['required']) . ' on'));
$this->assertRaw($string, $test_case['id'] . '. ' . $test_case['message']);
break;
case 'not required':
$string = '@type %title has been ' . ($test_case['operation'] == 'add' ? 'created' : 'updated') . '.';
$args = array('@type' => 'Basic page', '%title' => $title);
$this->assertRaw(t($string, $args), $test_case['id'] . '. ' . $test_case['message']);
break;
}
}
}
/**
* Tests the validation when editing a node.
*
* The 'required' checks and 'dates in the past' checks are handled in other
* tests. This test checks validation when the two fields interact.
*/
function testValidationDuringEdit() {
$this->drupalLogin($this->admin_user);
// Set unpublishing to be required.
variable_set('scheduler_unpublish_required_page', TRUE);
// Create an unpublished page node, then edit the node and check that if a
// publish-on date is entered then an unpublish-on date is also needed.
$node = $this->drupalCreateNode(array('type' => 'page', 'status' => FALSE));
$edit = array(
'publish_on' => date('Y-m-d H:i:s', strtotime('+1 day', REQUEST_TIME)),
);
$this->drupalPost('node/' . $node->nid . '/edit', $edit, t('Save'));
$this->assertRaw(t("If you set a 'publish-on' date then you must also set an 'unpublish-on' date."), 'Validation prevents entering a publish-on date with no unpublish-on date if unpublishing is required.');
// Create an unpublished page node, then edit the node and check that if the
// status is changed to published, then an unpublish-on date is also needed.
$node = $this->drupalCreateNode(array('type' => 'page', 'status' => FALSE));
$edit = array(
'status' => TRUE,
);
$this->drupalPost('node/' . $node->nid . '/edit', $edit, t('Save'));
$this->assertRaw(t("To publish this node you must also set an 'unpublish-on' date."), 'Validation prevents publishing the node directly without an unpublish-on date if unpublishing is required.');
// Create an unpublished page node, edit the node and check that if both
// dates are entered then the unpublish date is later than the publish date.
$node = $this->drupalCreateNode(array('type' => 'page', 'status' => FALSE));
$edit = array(
'publish_on' => date('Y-m-d H:i:s', strtotime('+2 day', REQUEST_TIME)),
'unpublish_on' => date('Y-m-d H:i:s', strtotime('+1 day', REQUEST_TIME)),
);
$this->drupalPost('node/' . $node->nid . '/edit', $edit, t('Save'));
$this->assertRaw(t("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.');
}
/**
* Tests the deletion of a scheduled node.
*
*/
public function testScheduledNodeDelete() {
// Log in.
$this->drupalLogin($this->admin_user);
// 1. Test if it is possible to delete a node that does not have a
// publication date set, when scheduled publishing is required, and likewise
// for unpublishing.
// @see https://drupal.org/node/1614880
// Create a published and an unpublished node, both without scheduling.
$published_node = $this->drupalCreateNode(array('type' => 'page', 'status' => 1));
$unpublished_node = $this->drupalCreateNode(array('type' => 'page', 'status' => 0));
// Make scheduled publishing and unpublishing required.
variable_set('scheduler_publish_required_page', TRUE);
variable_set('scheduler_unpublish_required_page', TRUE);
// Check that deleting the nodes does not throw form validation errors.
// The text 'error message' is used in a header h2 html tag which is
// normally made hidden from browsers but will be in the page source.
// It is also good when testing for the absense of something to also test
// for the presence of text, hence the second assertion for each check.
$this->drupalPost('node/' . $published_node->nid . '/edit', array(), t('Delete'));
$this->assertNoRaw(t('Error message'), 'No error messages are shown when trying to delete a published node with no scheduling information.');
$this->assertRaw(t('Are you sure you want to delete'), 'The deletion warning message is shown immediately when trying to delete a published node with no scheduling information.');
$this->drupalPost('node/' . $unpublished_node->nid . '/edit', array(), t('Delete'));
$this->assertNoRaw(t('Error message'), 'No error messages are shown when trying to delete an unpublished node with no scheduling information.');
$this->assertRaw(t('Are you sure you want to delete'), 'The deletion warning message is shown immediately when trying to delete an unpublished node with no scheduling information.');
// 2. Test that nodes can be deleted with no validation errors if the
// dates are in the past.
// @see http://drupal.org/node/2627370
// Turn off required publishing and unpublishing.
variable_set('scheduler_publish_required_page', FALSE);
variable_set('scheduler_unpublish_required_page', FALSE);
// Create nodes with publish_on and unpublish_on dates in the past.
$published_node = $this->drupalCreateNode(array('type' => 'page', 'status' => 1, 'unpublish_on' => strtotime('- 2 day')));
$unpublished_node = $this->drupalCreateNode(array('type' => 'page', 'status' => 0, 'publish_on' => strtotime('- 2 day')));
// Attempt to delete the published node and check for no validation error.
$this->drupalPost('node/' . $published_node->nid . '/edit', array(), t('Delete'));
$this->assertNoRaw(t('Error message'), 'No error messages are shown when trying to delete a node with an unpublish date in the past.');
$this->assertRaw(t('Are you sure you want to delete'), 'The deletion warning message is shown immediately when trying to delete a node with an unpublish date in the past.');
// Attempt to delete the unpublished node and check for no validation error.
$this->drupalPost('node/' . $unpublished_node->nid . '/edit', array(), t('Delete'));
$this->assertNoRaw(t('Error message'), 'No error messages are shown when trying to delete a node with a publish date in the past.');
$this->assertRaw(t('Are you sure you want to delete'), 'The deletion warning message is shown immediately when trying to delete a node with a publish date in the past.');
}
/**
* Tests meta-information on scheduled nodes.
*
* When nodes are scheduled for unpublication, an X-Robots-Tag HTTP header is
* sent, alerting crawlers about when an item expires and should be removed
* from search results.
*/
public function testMetaInformation() {
// Log in.
$this->drupalLogin($this->admin_user);
// Create a published node without scheduling.
$published_node = $this->drupalCreateNode(array('type' => 'page', 'status' => 1));
$this->drupalGet('node/' . $published_node->nid);
// Since we did not set an unpublish date, there should be no X-Robots-Tag
// header on the response.
$this->assertFalse($this->drupalGetHeader('X-Robots-Tag'), 'X-Robots-Tag is not present when no unpublish date is set.');
// Set a scheduler unpublish date on the node.
$unpublish_date = strtotime('+1 day');
$edit = array(
'unpublish_on' => format_date($unpublish_date, 'custom', 'Y-m-d H:i:s'),
);
$this->drupalPost('node/' . $published_node->nid . '/edit', $edit, t('Save'));
// The node page should now have an X-Robots-Tag header with an
// unavailable_after-directive and RFC850 date- and time-value.
$this->drupalGet('node/' . $published_node->nid);
$robots_tag = $this->drupalGetHeader('X-Robots-Tag');
$this->assertEqual($robots_tag, 'unavailable_after: ' . date(DATE_RFC850, $unpublish_date), 'X-Robots-Tag is present with correct timestamp derived from unpublish_on date.');
}
/**
* Tests that users without permission do not see the scheduler date fields.
*/
public function testPermissions() {
// Create a user who can add the content type but who does not have the
// permission to use the scheduler functionality.
$this->webUser = $this->drupalCreateUser(array(
'access content',
'create page content',
'edit own page content',
'view own unpublished content',
'administer nodes',
));
$this->drupalLogin($this->webUser);
// Set the defaults for a new node. Nothing in array means all OFF.
// 'status', 'promote', 'sticky'
variable_set('node_options_page', array());
// Check that neither of the fields are displayed when creating a node.
$this->drupalGet('node/add/page');
$this->assertNoFieldByName('publish_on', '', 'The Publish-on field is not shown for users who do not have permission to schedule content');
$this->assertNoFieldByName('unpublish_on', '', 'The Unpublish-on field is not shown for users who do not have permission to schedule content');
// Initially run tests when publishing and unpublishing are not required.
variable_set('scheduler_publish_required_page', FALSE);
variable_set('scheduler_unpublish_required_page', FALSE);
// Check that a new node can be saved and published.
$title = $this->randomString(15);
$this->drupalPost('node/add/page', array('title' => $title, 'status' => TRUE), t('Save'));
$this->assertRaw(t('@type %title has been created.', array('@type' => 'Basic page', '%title' => $title)), 'A node can be created and published when the user does not have scheduler permissions, and scheduling is not required.');
$node = $this->drupalGetNodeByTitle($title);
$this->assertTrue($node->status, 'The new node is published.');
// Check that a new node can be saved as unpublished.
$title = $this->randomString(15);
$this->drupalPost('node/add/page', array('title' => $title, 'status' => FALSE), t('Save'));
$this->assertRaw(t('@type %title has been created.', array('@type' => 'Basic page', '%title' => $title)), 'A node can be created and saved as unpublished when the user does not have scheduler permissions, and scheduling is not required.');
$node = $this->drupalGetNodeByTitle($title);
$this->assertFalse($node->status, 'The new node is unpublished.');
// Set publishing and unpublishing to required, to make it a stronger test.
variable_set('scheduler_publish_required_page', TRUE);
variable_set('scheduler_unpublish_required_page', TRUE);
// @TODO Add tests when scheduled publishing and unpublishing are required.
// Cannot be done until we make a decision on what 'required' means.
// @see https://www.drupal.org/node/2707411
// "Conflict between 'required publishing' and not having scheduler permission"
}
/**
* Tests Scheduler token support.
*/
public function testTokenReplacement() {
// Log in.
$this->drupalLogin($this->admin_user);
// Define timestamps for consistent use when repeated throughout this test.
$publish_on_timestamp = REQUEST_TIME + 3600;
$unpublish_on_timestamp = REQUEST_TIME + 7200;
// Create an unpublished page with scheduled dates.
$settings = array(
'type' => 'page',
'status' => FALSE,
'publish_on' => $publish_on_timestamp,
'unpublish_on' => $unpublish_on_timestamp,
);
$node = $this->drupalCreateNode($settings);
// Show that the node is scheduled.
$this->drupalGet('admin/config/content/scheduler/list');
// Create array of test case data.
$test_cases = array(
array('token_format' => '', 'date_format' => 'medium', 'custom' => ''),
array('token_format' => ':long', 'date_format' => 'long', 'custom' => ''),
array('token_format' => ':raw', 'date_format' => 'custom', 'custom' => 'U'),
array('token_format' => ':custom:jS F g:ia e O', 'date_format' => 'custom', 'custom' => 'jS F g:ia e O'),
);
$langcode = LANGUAGE_NONE;
foreach ($test_cases as $test_data) {
// With each of the test cases, test using both numeric and string input.
foreach (array('numeric', 'string') as $test_data['input_type']) {
if ($test_data['input_type'] == 'numeric') {
// Edit the node and set the body tokens to use the format being
// tested. The tokens are not replaced automatically when the node is
// viewed, but using the body is a convenient way to store the data.
$edit = array(
"body[$langcode][0][value]" => 'Publish on: [node:scheduler-publish' . $test_data['token_format'] . ']. Unpublish on: [node:scheduler-unpublish' . $test_data['token_format'] . '].',
);
$this->drupalPost('node/' . $node->nid . '/edit', $edit, t('Save'));
$this->drupalGet('node/' . $node->nid);
// Refresh the node. Third parameter TRUE to reset cache.
$node = node_load($node->nid, NULL, TRUE);
}
else {
// Replicate the scheduler fields as if just input by a user during
// edit, before hook_node_presave() has been executed.
// @see https://www.drupal.org/node/2750467
$node->publish_on = format_date($publish_on_timestamp, 'custom', 'Y-m-d H:i:s');
$node->unpublish_on = format_date($unpublish_on_timestamp, 'custom', 'Y-m-d H:i:s');
}
// Get the node body value after tokens have been replaced.
$token_output = token_replace($node->body[$langcode][0]['value'], array('node' => $node));
// Create the expected text for the body.
$publish_on_date = format_date($publish_on_timestamp, $test_data['date_format'], $test_data['custom']);
$unpublish_on_date = format_date($unpublish_on_timestamp, $test_data['date_format'], $test_data['custom']);
$expected_output = 'Publish on: ' . $publish_on_date . '. Unpublish on: ' . $unpublish_on_date . '.';
// Check that the actual text matches the expected value.
$tested_format = $test_data['token_format'] ? '"' . $test_data['token_format'] . '"' : 'default';
$this->assertEqual($token_output, $expected_output, 'Scheduler tokens replaced correctly for ' . $tested_format . ' format with ' . $test_data['input_type'] . ' input data.');
}
}
}
}
/**
* Tests the components of the scheduler interface which use the date module
*/
class SchedulerDateCombinedFunctionalTest extends SchedulerTestBase {
/**
* {@inheritdoc}
*/
public static function getInfo() {
return array(
'name' => 'Scheduler date functionalities',
'description' => 'Scheduler functionalities which require the date module.',
'group' => 'Scheduler',
);
}
/**
* {@inheritdoc}
*/
function setUp() {
parent::setUp('date', 'date_popup', 'scheduler');
parent::commonSettings();
}
/**
* Asserts that the default time works as expected.
*/
protected function assertDefaultTime() {
// Define the form fields and date formats we will test according to whether
// date popups have been enabled or not.
$using_popup = variable_get('scheduler_field_type', 'date_popup') == 'date_popup';
$publish_date_field = $using_popup ? 'publish_on[date]' : 'publish_on';
$unpublish_date_field = $using_popup ? 'unpublish_on[date]' : 'unpublish_on';
$publish_time_field = $using_popup ? 'publish_on[time]' : 'publish_on';
$unpublish_time_field = $using_popup ? 'unpublish_on[time]' : 'unpublish_on';
$time_format = $using_popup ? 'H:i:s' : 'Y-m-d H:i:s';
// We cannot easily test the exact validation messages as they contain the
// REQUEST_TIME of the POST request, which can be one or more seconds in the
// past. Best we can do is check the fixed part of the message as it is when
// passed to t(). This will only work in English.
$publish_validation_message = $using_popup ? t('The value input for field %field is invalid:', array('%field' => 'Publish on')) : "The 'publish on' value does not match the expected format of";
$unpublish_validation_message = $using_popup ? t('The value input for field %field is invalid:', array('%field' => 'Unpublish on')) : "The 'unpublish on' value does not match the expected format of";
// First test with the "date only" functionality disabled.
$this->drupalPost('admin/config/content/scheduler', array('scheduler_allow_date_only' => FALSE), t('Save configuration'));
// Test if entering a time is required.
$edit = array(
'title' => $this->randomName(),
$publish_date_field => date('Y-m-d', strtotime('+1 day', REQUEST_TIME)),
$unpublish_date_field => date('Y-m-d', strtotime('+2 day', REQUEST_TIME)),
);
$this->drupalPost('node/add/page', $edit, t('Save'));
$this->assertRaw($publish_validation_message, 'By default it is required to enter a time when scheduling content for publication.');
$this->assertRaw($unpublish_validation_message, 'By default it is required to enter a time when scheduling content for unpublication.');
// Allow the user to enter only the date and repeat the test.
$this->drupalPost('admin/config/content/scheduler', array('scheduler_allow_date_only' => TRUE), t('Save configuration'));
$this->drupalPost('node/add/page', $edit, t('Save'));
$this->assertNoRaw("The 'publish on' value does not match the expected format of", 'If the default time option is enabled the user can skip the time when scheduling content for publication.');
$this->assertNoRaw("The 'unpublish on' value does not match the expected format of", 'If the default time option is enabled the user can skip the time when scheduling content for unpublication.');
$this->assertRaw(t('This post is unpublished and will be published @publish_time.', array('@publish_time' => date('Y-m-d H:i:s', strtotime('tomorrow', REQUEST_TIME) + 23400))), 'The user is informed that the content will be published on the requested date, on the default time.');
// Check that the default time has been added to the scheduler form fields.
$this->clickLink(t('Edit'));
$this->assertFieldByName($publish_time_field, date($time_format, strtotime('tomorrow', REQUEST_TIME) + 23400), 'The default time offset has been added to the date field when scheduling content for publication.');
$this->assertFieldByName($unpublish_time_field, date($time_format, strtotime('tomorrow +1 day', REQUEST_TIME) + 23400), 'The default time offset has been added to the date field when scheduling content for unpublication.');
}
/**
* Test the default time functionality.
*/
public function testDefaultTime() {
$this->drupalLogin($this->admin_user);
foreach (array('textfield', 'date_popup') as $field_type) {
// Check that the correct default time is added to the scheduled date.
// For testing we use an offset of 6 hours 30 minutes (23400 seconds).
$edit = array(
'scheduler_date_format' => 'Y-m-d H:i:s',
'scheduler_allow_date_only' => TRUE,
'scheduler_default_time' => '6:30',
'scheduler_field_type' => $field_type,
);
$this->drupalPost('admin/config/content/scheduler', $edit, t('Save configuration'));
$this->assertDefaultTime();
// Check that it is not possible to enter a date format without a time if
// the 'date only' option is not enabled.
$edit = array(
'scheduler_date_format' => 'Y-m-d',
'scheduler_allow_date_only' => FALSE,
'scheduler_field_type' => $field_type,
);
$this->drupalPost('admin/config/content/scheduler', $edit, t('Save configuration'));
$this->assertRaw(t('You must either include a time within the date format or enable the date-only option.'), format_string('It is not possible to enter a date format without a time if the "date only" option is not enabled and the field type is set to %field_type.', array('%field_type' => $field_type)));
}
}
/**
* Tests configuration of different date formats with the Date Popup field.
*/
public function testDatePopupFormats() {
$this->drupalLogin($this->admin_user);
// Define some date formats to test.
$test_cases = array(
// By default we are not using the 'date only' option, so passing only a
// date should fail.
'Y-m-d' => FALSE,
'd-m-Y' => FALSE,
'm-d-Y' => FALSE,
'n/j/y' => FALSE,
'd F Y' => FALSE,
// Test a number of supported date formats.
'Y-m-d H:i' => TRUE,
'd-m-Y h:ia' => TRUE,
'm-d-Y h:iA' => TRUE,
'n-j-y H:i:s' => TRUE,
'Y/M/d h:i:sA' => TRUE,
'j F y h:i:sa' => TRUE,
// Test a number of date formats with invalid time specifications.
'y-m-d G:i' => FALSE,
'y-j-n G:i:sa' => FALSE,
'Y-m-d g:i:sa' => FALSE,
'y-m-d g:i:s' => FALSE,
'n-j-y h:i' => FALSE,
'd-m-y h:i:s' => FALSE,
'd/M/y H:i:sA' => FALSE,
'Y F d H:ia' => FALSE,
);
foreach ($test_cases as $date_format => $expected_result) {
$edit = array(
'scheduler_date_format' => $date_format,
'scheduler_field_type' => 'date_popup',
);
$this->drupalPost('admin/config/content/scheduler', $edit, t('Save configuration'));
$message = format_string('When using date popups the date format %format is @expected', array('%format' => $date_format, '@expected' => $expected_result ? 'allowed' : 'not allowed.'));
$assert = $expected_result ? 'assertNoRaw' : 'assertRaw';
$this->$assert('Error message', $message);
}
}
}