Commit ef1fd488 authored by Dries's avatar Dries
Browse files

- Patch #721086 by rfay, aspilicious, andypost: create tests for system...

- Patch #721086 by rfay, aspilicious, andypost: create tests for system actions, clean up token replacement, clean up triggers.
parent 7640e036
......@@ -1101,7 +1101,8 @@ protected function drupalLogout() {
// Make a request to the logout page, and redirect to the user page, the
// idea being if you were properly logged out you should be seeing a login
// screen.
$this->drupalGet('user/logout', array('query' => array('destination' => 'user')));
$this->drupalGet('user/logout');
$this->drupalGet('user');
$pass = $this->assertField('name', t('Username field found.'), t('Logout'));
$pass = $pass && $this->assertField('pass', t('Password field found.'), t('Logout'));
......@@ -2964,7 +2965,9 @@ protected function assertResponse($code, $message = '') {
}
/**
* Assert that the most recently sent e-mail message has a field with the given value.
* Asserts that the most recently sent e-mail message has the given value.
*
* The field in $name must have the content described in $value.
*
* @param $name
* Name of field or message property to assert. Examples: subject, body, id, ...
......@@ -2972,6 +2975,7 @@ protected function assertResponse($code, $message = '') {
* Value of the field to assert.
* @param $message
* Message to display.
*
* @return
* TRUE on pass, FALSE on fail.
*/
......@@ -2982,13 +2986,76 @@ protected function assertMail($name, $value = '', $message = '') {
}
/**
* Log verbose message in a text file.
* Asserts that the most recently sent e-mail message has the string in it.
*
* @param $field_name
* Name of field or message property to assert: subject, body, id, ...
* @param $string
* String to search for.
* @param $email_depth
* Number of emails to search for string, starting with most recent.
*
* @return
* TRUE on pass, FALSE on fail.
*/
protected function assertMailString($field_name, $string, $email_depth) {
$mails = $this->drupalGetMails();
$string_found = FALSE;
for ($i = sizeof($mails) -1; $i >= sizeof($mails) - $email_depth && $i >= 0; $i--) {
$mail = $mails[$i];
// Normalize whitespace, as we don't know what the mail system might have
// done. Any run of whitespace becomes a single space.
$normalized_mail = preg_replace('/\s+/', ' ', $mail[$field_name]);
$normalized_string = preg_replace('/\s+/', ' ', $string);
$string_found = (FALSE !== strpos($normalized_mail, $normalized_string));
if ($string_found) {
break;
}
}
return $this->assertTrue($string_found, t('Expected text found in @field of email message: "@expected".', array('@field' => $field_name, '@expected' => $string)));
}
/**
* Asserts that the most recently sent e-mail message has the pattern in it.
*
* @param $field_name
* Name of field or message property to assert: subject, body, id, ...
* @param $regex
* Pattern to search for.
*
* @return
* TRUE on pass, FALSE on fail.
*/
protected function assertMailPattern($field_name, $regex, $message) {
$mails = $this->drupalGetMails();
$mail = end($mails);
$regex_found = preg_match("/$regex/", $mail[$field_name]);
return $this->assertTrue($regex_found, t('Expected text found in @field of email message: "@expected".', array('@field' => $field_name, '@expected' => $regex)));
}
/**
* Outputs to verbose the most recent $count emails sent.
*
* @param $count
* Optional number of emails to output.
*/
protected function verboseEmail($count = 1) {
$mails = $this->drupalGetMails();
for ($i = sizeof($mails) -1; $i >= sizeof($mails) - $count && $i >= 0; $i--) {
$mail = $mails[$i];
$this->verbose(t('Email:') . '<pre>' . print_r($mail, TRUE) . '</pre>');
}
}
/**
* Logs verbose message in a text file.
*
* The a link to the vebose message will be placed in the test results via
* as a passing assertion with the text '[verbose message]'.
*
* @param $message
* The verbose message to be stored.
*
* @see simpletest_verbose()
*/
protected function verbose($message) {
......@@ -3001,7 +3068,7 @@ protected function verbose($message) {
}
/**
* Log verbose message in a text file.
* Logs verbose message in a text file.
*
* If verbose mode is enabled then page requests will be dumped to a file and
* presented on the test result screen. The messages will be placed in a file
......@@ -3013,8 +3080,10 @@ protected function verbose($message) {
* The original file directory, before it was changed for testing purposes.
* @param $test_class
* The active test case class.
*
* @return
* The ID of the message to be placed in related assertion messages.
*
* @see DrupalTestCase->originalFileDirectory
* @see DrupalWebTestCase->verbose()
*/
......
......@@ -2861,7 +2861,7 @@ function system_action_info() {
'type' => 'user',
'label' => t('Ban IP address of current user'),
'configurable' => FALSE,
'triggers' => array(),
'triggers' => array('any'),
),
'system_goto_action' => array(
'type' => 'system',
......
......@@ -35,7 +35,6 @@ function trigger_test_action_info() {
'comment_insert',
'comment_update',
'comment_delete',
'user_presave',
'user_insert',
'user_update',
'user_delete',
......
......@@ -145,9 +145,6 @@ function trigger_trigger_info() {
),
),
'user' => array(
'user_presave' => array(
'label' => t('When either creating a new user account or updating an existing'),
),
'user_insert' => array(
'label' => t('After creating a new user account'),
),
......@@ -488,17 +485,10 @@ function trigger_user_login(&$edit, $account, $category) {
* Implements hook_user_logout().
*/
function trigger_user_logout($account) {
$edit = NULL;
$edit = array();
_trigger_user('user_logout', $edit, $account);
}
/**
* Implements hook_user_presave().
*/
function trigger_user_presave(&$edit, $account, $category) {
_trigger_user('user_presave', $edit, $account, $category);
}
/**
* Implements hook_user_insert().
*/
......@@ -528,7 +518,8 @@ function trigger_user_cancel($edit, $account, $method) {
* Implements hook_user_delete().
*/
function trigger_user_delete($account) {
_trigger_user('user_delete', $edit, $account, $method);
$edit = array();
_trigger_user('user_delete', $edit, $account, NULL);
}
/**
......@@ -557,7 +548,7 @@ function _trigger_user($hook, &$edit, $account, $category = NULL) {
if (!isset($objects[$type])) {
$objects[$type] = _trigger_normalize_user_context($type, $account);
}
$context['account'] = $account;
$context['user'] = $account;
actions_do($aid, $objects[$type], $context);
}
else {
......
......@@ -2,7 +2,7 @@
// $Id$
/**
* Class with common helper methods.
* Provides common helper methods.
*/
class TriggerWebTestCase extends DrupalWebTestCase {
......@@ -14,6 +14,7 @@ class TriggerWebTestCase extends DrupalWebTestCase {
* @param $edit
* The $edit array for the form to be used to configure.
* Example members would be 'actions_label' (always), 'message', etc.
*
* @return
* the aid (action id) of the configured action, or FALSE if none.
*/
......@@ -30,7 +31,7 @@ class TriggerWebTestCase extends DrupalWebTestCase {
}
/**
* Test node triggers.
* Provides tests for node triggers.
*/
class TriggerContentTestCase extends TriggerWebTestCase {
var $_cleanup_roles = array();
......@@ -49,7 +50,9 @@ class TriggerContentTestCase extends TriggerWebTestCase {
}
/**
* Various tests, all in one function to assure they happen in the right order.
* Tests several content-oriented trigger issues.
*
* These are in one function to assure they happen in the right order.
*/
function testActionsContent() {
global $user;
......@@ -98,8 +101,10 @@ class TriggerContentTestCase extends TriggerWebTestCase {
}
/**
* Test that node actions are fired for each node individually if acting on
* multiple nodes.
* Tests multiple node actions.
*
* Verifies that node actions are fired for each node individually, if acting
* on multiple nodes.
*/
function testActionContentMultiple() {
// Assign an action to the node save/update trigger.
......@@ -127,10 +132,13 @@ class TriggerContentTestCase extends TriggerWebTestCase {
}
/**
* Helper function for testActionsContent(): returns some info about each of the content actions.
* Returns some info about each of the content actions.
*
* This is helper function for testActionsContent().
*
* @param $action
* The name of the action to return info about.
*
* @return
* An associative array of info about the action.
*/
......@@ -172,7 +180,7 @@ class TriggerContentTestCase extends TriggerWebTestCase {
}
/**
* Test cron trigger.
* Tests cron trigger.
*/
class TriggerCronTestCase extends TriggerWebTestCase {
public static function getInfo() {
......@@ -188,7 +196,7 @@ class TriggerCronTestCase extends TriggerWebTestCase {
}
/**
* Test assigning multiple actions to the cron trigger.
* Tests assigning multiple actions to the cron trigger.
*
* This test ensures that both simple and multiple complex actions
* succeed properly. This is done in the cron trigger test because
......@@ -239,7 +247,296 @@ class TriggerCronTestCase extends TriggerWebTestCase {
}
/**
* Test other triggers.
* Provides a base class with trigger assignments and test comparisons.
*/
class TriggerActionTestCase extends TriggerWebTestCase {
function setUp() {
parent::setUp('trigger');
}
/**
* Creates a message with tokens.
*
* @param $trigger
*
* @return
* A message with embedded tokens.
*/
function generateMessageWithTokens($trigger) {
// Note that subject is limited to 254 characters in action configuration.
$message = t('Action was triggered by trigger @trigger user:name=[user:name] user:uid=[user:uid] user:mail=[user:mail] user:url=[user:url] user:edit-url=[user:edit-url] user:created=[user:created]',
array('@trigger' => $trigger));
return trim($message);
}
/**
* Generates a comparison message to match the pre-token-replaced message.
*
* @param $trigger
* Trigger, like 'user_login'.
* @param $account
* Associated user account.
*
* @return
* The token-replaced equivalent message. This does not use token
* functionality.
*
* @see generateMessageWithTokens()
*/
function generateTokenExpandedComparison($trigger, $account) {
// Note that user:last-login was omitted because it changes and can't
// be properly verified.
$message = t('Action was triggered by trigger @trigger user:name=@username user:uid=@uid user:mail=@mail user:url=@user_url user:edit-url=@user_edit_url user:created=@user_created',
array(
'@trigger' => $trigger,
'@username' => $account->name,
'@uid' => !empty($account->uid) ? $account->uid : t('not yet assigned'),
'@mail' => $account->mail,
'@user_url' => !empty($account->uid) ? url("user/$account->uid", array('absolute' => TRUE)) : t('not yet assigned'),
'@user_edit_url' => !empty($account->uid) ? url("user/$account->uid/edit", array('absolute' => TRUE)) : t('not yet assigned'),
'@user_created' => isset($account->created) ? format_date($account->created, 'medium') : t('not yet created'),
)
);
return trim($message);
}
/**
* Assigns a simple (non-configurable) action to a trigger.
*
* @param $trigger
* The trigger to assign to, like 'user_login'.
* @param $action
* The simple action to be assigned, like 'comment_insert'.
*/
function assignSimpleAction($trigger, $action) {
$form_name = "trigger_{$trigger}_assign_form";
$form_html_id = strtr($form_name, '_', '-');
$edit = array('aid' => drupal_hash_base64($action));
$trigger_type = preg_replace('/_.*/', '', $trigger);
$this->drupalPost("admin/structure/trigger/$trigger_type", $edit, t('Assign'), array(), array(), $form_html_id);
$actions = trigger_get_assigned_actions($trigger);
$this->assertTrue(!empty($actions[$action]), t('Simple action @action assigned to trigger @trigger', array('@action' => $action, '@trigger' => $trigger)));
}
/**
* Assigns a system message action to the passed-in trigger.
*
* @param $trigger
* For example, 'user_login'
*/
function assignSystemMessageAction($trigger) {
$form_name = "trigger_{$trigger}_assign_form";
$form_html_id = strtr($form_name, '_', '-');
// Assign a configurable action 'System message' to the passed trigger.
$action_edit = array(
'actions_label' => $trigger . "_system_message_action_" . $this->randomName(16),
'message' => $this->generateMessageWithTokens($trigger),
);
// Configure an advanced action that we can assign.
$aid = $this->configureAdvancedAction('system_message_action', $action_edit);
$edit = array('aid' => drupal_hash_base64($aid));
$this->drupalPost('admin/structure/trigger/user', $edit, t('Assign'), array(), array(), $form_html_id);
}
/**
* Assigns a system_send_email_action to the passed-in trigger.
*
* @param $trigger
* For example, 'user_login'
*/
function assignSystemEmailAction($trigger) {
$form_name = "trigger_{$trigger}_assign_form";
$form_html_id = strtr($form_name, '_', '-');
$message = $this->generateMessageWithTokens($trigger);
// Assign a configurable action 'System message' to the passed trigger.
$action_edit = array(
// 'actions_label' => $trigger . "_system_send_message_action_" . $this->randomName(16),
'actions_label' => $trigger . "_system_send_email_action",
'recipient' => '[user:mail]',
'subject' => $message,
'message' => $message,
);
// Configure an advanced action that we can assign.
$aid = $this->configureAdvancedAction('system_send_email_action', $action_edit);
$edit = array('aid' => drupal_hash_base64($aid));
$this->drupalPost('admin/structure/trigger/user', $edit, t('Assign'), array(), array(), $form_html_id);
}
/**
* Asserts correct token replacement in both system message and email.
*
* @param $trigger
* A trigger like 'user_login'.
* @param $account
* The user account which triggered the action.
* @param $email_depth
* Number of emails to scan, starting with most recent.
*/
function assertSystemMessageAndEmailTokenReplacement($trigger, $account, $email_depth = 1) {
$this->assertSystemMessageTokenReplacement($trigger, $account);
$this->assertSystemEmailTokenReplacement($trigger, $account, $email_depth);
}
/**
* Asserts correct token replacement for the given trigger and account.
*
* @param $trigger
* A trigger like 'user_login'.
* @param $account
* The user account which triggered the action.
*/
function assertSystemMessageTokenReplacement($trigger, $account) {
$expected = $this->generateTokenExpandedComparison($trigger, $account);
$this->assertText($expected,
t('Expected system message to contain token-replaced text "@expected" found in configured system message action', array('@expected' => $expected )) );
}
/**
* Asserts correct token replacement for the given trigger and account.
*
* @param $trigger
* A trigger like 'user_login'.
* @param $account
* The user account which triggered the action.
* @param $email_depth
* Number of emails to scan, starting with most recent.
*/
function assertSystemEmailTokenReplacement($trigger, $account, $email_depth = 1) {
$this->verboseEmail($email_depth);
$expected = $this->generateTokenExpandedComparison($trigger, $account);
$this->assertMailString('subject', $expected, $email_depth);
$this->assertMailString('body', $expected, $email_depth);
$this->assertMail('to', $account->mail, t('Mail sent to correct destination'));
}
}
/**
* Tests token substitution in trigger actions.
*
* This tests nearly every permutation of user triggers with system actions
* and checks the token replacement.
*/
class TriggerUserTokenTestCase extends TriggerActionTestCase {
public static function getInfo() {
return array(
'name' => 'Test user triggers',
'description' => 'Test user triggers and system actions with token replacement.',
'group' => 'Trigger',
);
}
/**
* Tests a variety of token replacements in actions.
*/
function testUserTriggerTokenReplacement() {
$test_user = $this->drupalCreateUser(array('administer actions', 'administer users', 'change own username', 'access user profiles'));
$this->drupalLogin($test_user);
$triggers = array('user_login', 'user_insert', 'user_update', 'user_delete', 'user_logout', 'user_view');
foreach ($triggers as $trigger) {
$this->assignSystemMessageAction($trigger);
$this->assignSystemEmailAction($trigger);
}
$this->drupalLogout();
$this->assertSystemEmailTokenReplacement('user_logout', $test_user);
$this->drupalLogin($test_user);
$this->assertSystemMessageAndEmailTokenReplacement('user_login', $test_user, 2);
$this->assertSystemMessageAndEmailTokenReplacement('user_view', $test_user, 2);
$this->drupalPost("user/{$test_user->uid}/edit", array('name' => $test_user->name . '_changed'), t('Save'));
$test_user->name .= '_changed'; // Since we just changed it.
$this->assertSystemMessageAndEmailTokenReplacement('user_update', $test_user, 2);
$this->drupalGet('user');
$this->assertSystemMessageAndEmailTokenReplacement('user_view', $test_user);
$new_user = $this->drupalCreateUser(array('administer actions', 'administer users', 'cancel account', 'access administration pages'));
$this->assertSystemEmailTokenReplacement('user_insert', $new_user);
$this->drupalLogin($new_user);
$user_to_delete = $this->drupalCreateUser(array('access content'));
variable_set('user_cancel_method', 'user_cancel_delete');
$this->drupalPost("user/{$user_to_delete->uid}/cancel", array(), t('Cancel account'));
$this->assertSystemMessageAndEmailTokenReplacement('user_delete', $user_to_delete);
}
}
/**
* Tests token substitution in trigger actions.
*
* This tests nearly every permutation of user triggers with system actions
* and checks the token replacement.
*/
class TriggerUserActionTestCase extends TriggerActionTestCase {
public static function getInfo() {
return array(
'name' => 'Test user actions',
'description' => 'Test user actions.',
'group' => 'Trigger',
);
}
/**
* Tests user action assignment and execution.
*/
function testUserActionAssignmentExecution() {
$test_user = $this->drupalCreateUser(array('administer actions', 'create article content', 'access comments', 'administer comments', 'post comments without approval', 'edit own comments'));
$this->drupalLogin($test_user);
$triggers = array('comment_presave', 'comment_insert', 'comment_update');
// system_block_ip_action is difficult to test without ruining the test.
$actions = array('user_block_user_action');
foreach ($triggers as $trigger) {
foreach ($actions as $action) {
$this->assignSimpleAction($trigger, $action);
}
}
$node = $this->drupalCreateNode(array('type' => 'article'));
$this->drupalPost("node/{$node->nid}", array('comment_body[und][0][value]' => t("my comment"), 'subject' => t("my comment subject")), t('Save'));
// Posting a comment should have blocked this user.
$account = user_load($test_user->uid, TRUE);
$this->assertTrue($account->status == 0, t('Account is blocked'));
$comment_author_uid = $account->uid;
// Now rehabilitate the comment author so it can be be blocked again when
// the comment is updated.
user_save($account, array('status' => TRUE));
$test_user = $this->drupalCreateUser(array('administer actions', 'create article content', 'access comments', 'administer comments', 'post comments without approval', 'edit own comments'));
$this->drupalLogin($test_user);
// Our original comment will have been comment 1.
$this->drupalPost("comment/1/edit", array('comment_body[und][0][value]' => t("my comment, updated"), 'subject' => t("my comment subject")), t('Save'));
$comment_author_account = user_load($comment_author_uid, TRUE);
$this->assertTrue($comment_author_account->status == 0, t('Comment author account (uid=@uid) is blocked after update to comment', array('@uid' => $comment_author_uid)));
// Verify that the comment was updated.
$test_user = $this->drupalCreateUser(array('administer actions', 'create article content', 'access comments', 'administer comments', 'post comments without approval', 'edit own comments'));
$this->drupalLogin($test_user);
$this->drupalGet("node/$node->nid");
$this->assertText(t("my comment, updated"));
$this->verboseEmail();
}
}
/**
* Tests other triggers.
*/
class TriggerOtherTestCase extends TriggerWebTestCase {
var $_cleanup_roles = array();
......@@ -258,7 +555,7 @@ class TriggerOtherTestCase extends TriggerWebTestCase {
}
/**
* Test triggering on user create and user login.
* Tests triggering on user create and user login.
*/
function testActionsUser() {
// Assign an action to the create user trigger.
......@@ -315,7 +612,7 @@ class TriggerOtherTestCase extends TriggerWebTestCase {
}
/**
* Test triggering on comment save.
* Tests triggering on comment save.
*/
function testActionsComment() {
// Assign an action to the comment save trigger.
......@@ -344,7 +641,7 @@ class TriggerOtherTestCase extends TriggerWebTestCase {
}
/**
* Test triggering on taxonomy new term.
* Tests triggering on taxonomy new term.
*/
function testActionsTaxonomy() {
// Assign an action to the taxonomy term save trigger.
......@@ -382,7 +679,7 @@ class TriggerOtherTestCase extends TriggerWebTestCase {
}
/**
* Test that orphaned actions are properly handled.
* Tests that orphaned actions are properly handled.
*/
class TriggerOrphanedActionsTestCase extends DrupalWebTestCase {
......@@ -399,7 +696,7 @@ class TriggerOrphanedActionsTestCase extends DrupalWebTestCase {
}
/**
* Test logic around orphaned actions.
* Tests logic around orphaned actions.
*/
function testActionsOrphaned() {
$action = 'trigger_test_generic_any_action';
......
......@@ -3373,7 +3373,7 @@ function user_action_info() {
'label' => t('Block current user'),
'type' => 'user',
'configurable' => FALSE,
'triggers' => array(),
'triggers' => array('any'),
),
);
}
......@@ -3384,22 +3384,25 @@ function user_action_info() {
* @ingroup actions
*/
function user_block_user_action(&$entity, $context = array()) {