You are here

state_flow.test in State Machine 7.3

File

modules/state_flow/tests/state_flow.test
View source
<?php

/**
 * @file
 * state_flow.test
 */

/**
 * Unit tests for the StateFlow revision state machine.
 */
class StateFlowWebTestCase extends DrupalWebTestCase {
  public static function getInfo() {
    return array(
      'name' => 'StateFlow basic tests',
      'description' => 'Perform basic unit tests on the StateFlow revision state machine.',
      'group' => 'State Machine',
    );
  }

  /**
   * {@inheritdoc}
   */
  public function setup() {
    parent::setup('state_machine', 'state_flow', 'state_flow_entity', 'views', 'drafty');

    // Create a revision for nodes by default.
    foreach (array(
      'page',
      'article',
    ) as $node_type) {
      $node_options = variable_get('node_options_' . $node_type, array(
        'status',
        'promote',
      ));
      $node_options[] = 'revision';
      variable_set('node_options_' . $node_type, $node_options);
    }
    $this
      ->stateFlowLoginAdmin();
  }
  public function stateFlowLoginAdmin() {
    $web_user = $this
      ->drupalCreateUser(array(
      'administer nodes',
      'bypass node access',
      'manage content workflow',
      'administer content revisions',
    ));
    $this
      ->drupalLogin($web_user);
  }

  /**
   * Test basic state changes and forward revisions.
   *
   * @todo, can this test be broken up to test forward revisions separate from
   * the basic state changes.
   */
  public function testStateFlowStateMachine() {
    $node = $this
      ->drupalCreateNode(array(
      'status' => 0,
    ));
    $states = db_select('state_flow_states')
      ->fields('state_flow_states', array())
      ->execute()
      ->fetchAll();
    $history = db_select('state_flow_history')
      ->fields('state_flow_history', array())
      ->execute()
      ->fetchAll();

    // Make sure that a record actually saved to the db.
    $this
      ->assert(!empty($history), t('There is a record in state_flow_history after a programmatic save'));
    $this
      ->assert(!empty($states), t('There is a record in state_flow_states after a programmatic save'));

    // Make sure first (default) state is draft.
    $machine = state_flow_entity_load_state_machine($node, 'node');
    $this
      ->assertEqual($machine
      ->get_current_state(), 'draft', t('New nodes should default to the "draft" state.'));

    // Keep in draft should be the default event when editing a draft.
    $this
      ->drupalGet("node/{$node->nid}/edit");
    $this
      ->assertRaw('<option value="keep in draft" selected="selected">', t('Keep in Draft is the default when editing a draft.'));

    // Publish node by firing event.
    $machine
      ->fire_event('publish');
    $this
      ->assertEqual($machine
      ->get_current_state(), 'published', t('State should be "published" after firing the "publish" event.'));

    // Check for Duplicate State records.
    $this
      ->_testOfDuplicateStates();

    // Choose edit link
    // Make sure revision checkbox is checked.
    $this
      ->drupalGet("node/{$node->nid}/edit");
    $this
      ->assertFieldChecked('edit-revision', t('Revision checkbox should be checked after editing a "published" node.'));

    // Edit node (new title)
    // Submit node, confirm creation
    // Ensure new title is updated, but not currently visible.
    $old_title = $node->title;
    $edit = array();
    $new_title = $this
      ->randomName(8);
    $edit['title'] = $new_title;
    $edit['event'] = 'to draft';
    $edit['event_comment'] = $this
      ->randomName(8);
    $this
      ->drupalPost("node/{$node->nid}/edit", $edit, t('Save'));
    $this
      ->drupalGet("node/{$node->nid}");
    $this
      ->assertText($old_title, t('Node title should stay the original despite the update.'));

    // Check for Duplicate State records.
    $this
      ->_testOfDuplicateStates();

    // Get the second-highest vid for this node. We need the second highest
    // because drafty creates a forward revision for the published revision.
    $versions = db_select('node_revision', 'nr')
      ->fields('nr', array(
      'vid',
    ))
      ->condition('nid', $node->nid)
      ->orderBy('nr.vid', 'DESC')
      ->range(1, 1)
      ->execute()
      ->fetchCol();
    $forward_revision_vid = $versions[0];

    // Verify that the new body text is present on the forward revision.
    $this
      ->drupalGet("node/{$node->nid}/revisions/" . $forward_revision_vid . "/view");
    $this
      ->assertText($new_title, t('New title text is present on forward version of node.'));

    // Load the forward node.
    $forward_revision_node = node_load($node->nid, $forward_revision_vid, TRUE);
    $forward_machine = state_flow_entity_load_state_machine($forward_revision_node, 'node');

    // Check state of second revision.
    // Should be unpublished.
    $this
      ->assertEqual($forward_machine
      ->get_current_state(), 'draft', t('Updated published nodes should default to the "draft" state for their latest revision.'));

    // @TODO This test has to be removed since drafty handles this differenty
    // Verify that the forward revision vid is greater than the live revision
    // vid.
    //    $live_revision = db_select('node', 'n')
    //      ->fields('n', array('vid'))
    //      ->condition('nid', $node->nid)
    //      ->execute()
    //      ->fetchCol();
    //    $live_revision_vid = $live_revision[0];
    //    $this->assertTrue($forward_revision_vid > $live_revision_vid, 'Forward revision vid is greater than live revision vid.');
    // Publish second revision.
    $forward_machine
      ->fire_event('publish');

    // This would also check that $machine / $forward_machine are in sync.
    // @TODO Figure out if this is necessary / desirable.
    // $this->assertEqual($machine->get_current_state(), 'published', t('State should be "published" after firing the "publish" event.'));
    $this
      ->assertEqual($forward_machine
      ->get_current_state(), 'published', t('State should be "published" after firing the "publish" event.'));

    // Check the live page.
    $this
      ->drupalGet("node/{$node->nid}");

    // Check for the new title.
    $this
      ->assertText($new_title, t('Node title should be updated to second revision, now published.'));

    // Check for Duplicate State records.
    $this
      ->_testOfDuplicateStates();
  }

  /**
   * Test that adding a node in the UI doesn't create NULL records.
   *
   * Test that when an node is added in the UI, there are no NULL records in
   * the state_flow_states table.
   */
  public function testStateFlowStatesTableNullRecords() {
    $edit = array();
    $new_title = $this
      ->randomName(8);
    $edit['title'] = $new_title;
    $edit['event'] = 'keep in draft';
    $edit['event_comment'] = $this
      ->randomName(8);
    $this
      ->drupalPost("node/add/article", $edit, t('Save'));

    // Get the highest vid for this node.
    $state_flow_states = db_select('state_flow_states', 'sfs')
      ->fields('sfs')
      ->execute()
      ->fetchAll();
    $this
      ->assertEqual(count($state_flow_states), 1, t('There is only one record in state_flow_states after adding a node through the ui.'));
    foreach ($state_flow_states as $state_record) {
      $this
        ->assertNotEqual($state_record->entity_id, NULL, t('The entity_id column for a state_flow_states record is not NULL.'));
      $this
        ->assertNotEqual($state_record->revision_id, NULL, t('The entity_id column for a state_flow_states record is not NULL.'));
    }
  }

  /**
   * Verify that two node objects are not saved.
   */
  public function testOfStraightToPublished() {
    $edit = array();
    $title = $this
      ->randomName(8);
    $edit['title'] = $title;
    $edit['event'] = 'publish';
    $edit['event_comment'] = $event_comment = $this
      ->randomName(8);
    $this
      ->drupalPost("node/add/article", $edit, t('Save'));
    $this
      ->drupalGet("node/1");

    // Get the highest vid for this node.
    $node_records = db_select('node', 'n')
      ->fields('n')
      ->execute()
      ->fetchAll();
    $this
      ->assertEqual(count($node_records), 1, t('There is one record in the node table'));
    $state_flow_history = db_select('state_flow_history', 'sfh')
      ->fields('sfh')
      ->condition('entity_id', 1)
      ->condition('entity_type', 'node')
      ->execute()
      ->fetchAll();
    $this
      ->assertEqual(count($state_flow_history), 1, t('There is one record in the node table'));
    $this
      ->assertEqual($state_flow_history[0]->state, 'published', t('The state flow history record for node 1 has "published" in the state column.'));
    $this
      ->assertEqual($state_flow_history[0]->log, $event_comment, t('The state flow history record for node 1 has the correct log message.'));
  }

  /**
   * Verify that moving from published to draft and editing the draft
   * a second time still leaves the published revision accessible.
   */
  public function testMoveToDraftAndEditDraft() {

    // Create new draft node
    debug('Make node 1, vid: 1');
    $edit = array();
    $title = $this
      ->randomName(8);
    $edit['title'] = $title;
    $edit['event'] = 'publish';
    $edit['event_comment'] = $this
      ->randomName(8);
    $this
      ->drupalPost("node/add/article", $edit, t('Save'));

    // Then make another revision of the first node.
    debug('Create new revision for node 1, vid: 2 and drafty forward revision vid: 3');
    $edit = array();
    $new_title = $this
      ->randomName(8);
    $edit['title'] = $new_title;
    $edit['event'] = 'to draft';
    $edit['event_comment'] = $this
      ->randomName(8);
    $edit['revision'] = 1;
    $this
      ->drupalPost("node/1/edit", $edit, t('Save'));

    // Edit the draft and save it.
    debug('Save draft revision vid: 2 and drafty forward revision vid: 4');
    $edit = array();
    $new_title = $this
      ->randomName(8);
    $edit['title'] = $new_title;
    $edit['event'] = 'keep in draft';
    $edit['event_comment'] = $this
      ->randomName(8);
    $this
      ->drupalPost("node/1/edit", $edit, t('Save'));

    // Now test that the node is accessible
    $this
      ->drupalLogout();
    $this
      ->drupalGet("node/1");
    $this
      ->assertText($title, t('After editing the draft for a second time, anonymous user can see node title.'));
  }

  /**
   * Verify that two node objects are not saved.
   */
  public function testOfFieldValidation() {

    // Add the text integer field.
    $this
      ->_addintegerfield();

    // Test validate of the integer field within a node form.
    $edit = array();
    $edit['event'] = 'keep in draft';
    $edit['event_comment'] = $this
      ->randomName(8);
    $edit['field_sfhe_integer[und][0][value]'] = rand(11, 9999);
    $edit['title'] = $title = $this
      ->randomName(8);
    $this
      ->drupalPost("node/add/article", $edit, t('Save'));
    $this
      ->assertText('Test integer: the value may be no greater than 10.', t('The integer fields shows the correct error message when set to a value greater than 10'));

    // Post again with a legal integer value.
    $edit['field_sfhe_integer[und][0][value]'] = rand(1, 10);
    $this
      ->drupalPost("node/add/article", $edit, t('Save'));

    // Go to the mini form and test validation there too.
    $this
      ->drupalGet("node/1/revisions/1/workflow/publish");
    $this
      ->assertResponse('200', 'Publish tab responds with a 200');

    // Post with an illegal integer value.
    $edit = array();
    $edit['event_comment'] = $this
      ->randomName(8);
    $edit['field_sfhe_integer[und][0][value]'] = rand(11, 9999);
    $this
      ->drupalPost("node/1/revisions/1/workflow/publish", $edit, t('Update State'));
    $this
      ->assertText('Test integer: the value may be no greater than 10.', t('The integer fields shows the correct error message when set to a value greater than 10'));

    // Post again with a legal integer value.
    $edit['field_sfhe_integer[und][0][value]'] = rand(1, 10);
    $this
      ->drupalPost("node/1/revisions/1/workflow/publish", $edit, t('Update State'));

    // Confirm that the node actually published.
    $this
      ->drupalLogout();
    $this
      ->drupalGet("node/1");
    $this
      ->assertText($title, t('After publishing, anonymous user can see node title.'));
  }

  /**
   * Add a text field to the state flow history entity.
   */
  public function _addtextfield() {
    module_load_include('inc', 'state_flow', 'tests/state_flow.test');
    state_flow_add_test_text_field();
  }

  /**
   * Add a text field to the state flow history entity.
   */
  public function _addintegerfield() {
    module_load_include('inc', 'state_flow', 'tests/state_flow.test');
    state_flow_add_test_integer_field();
  }

  /**
   * Helper function to check that there are no duplicate states.
   */
  public function _testOfDuplicateStates() {
    $states = db_select('state_flow_states')
      ->fields('state_flow_states', array())
      ->execute()
      ->fetchAll();
    $hids = array();
    $duplicates = FALSE;
    foreach ($states as $state) {
      if (!empty($state->hid)) {
        if (empty($hids[$state->hid])) {
          $hids[$state->hid][] = $state;
        }
        else {
          $duplicates = TRUE;
        }
      }
    }
    $this
      ->assertFalse($duplicates, t('There are no duplicate records in State Flow States'));

    // If there are duplicates, set a default message.
    if ($duplicates) {
      debug($states);
    }
  }

  /**
   * Test state_flow_handler_field_is_node_revision_published.inc.
   *
   * Test state_flow_handler_field_is_node_revision_published.inc  and some of
   * the other columns used at
   * node/1/revisions-state-flow-states and node/1/state-flow-history
   */
  public function testOfPublishedViewsHandler() {

    // Add fields to the state_flow_history_entity Field API integration can be
    // tested.
    $this
      ->_addtextfield();

    // This array holds values added to Field API fields on a
    // state_flow_history_entity.
    // The 0 index in the array is a dummy value so that the next '1' index
    // value syncs up with
    // node revision 1.
    $field_text = array(
      'placeholder_at_zero_key',
    );

    // Make a dummy node to offset revision ids. This accommodates that Views
    // bug in http://drupal.org/node/1862014
    debug('Make node 1, vid: 1');
    $edit = array();
    $new_title = $this
      ->randomName(8);
    $edit['title'] = $new_title;
    $edit['event'] = 'keep in draft';

    // @todo, test presence of comment.
    $edit['event_comment'] = $this
      ->randomName(8);
    $field_text[] = $edit['field_sfhe_text[und][0][value]'] = $this
      ->randomName(8);
    $this
      ->drupalPost("node/add/article", $edit, t('Save'));

    // Then make another revision of the first node.
    debug('Create new revision for node 1, vid: 2');
    $edit = array();
    $new_title = $this
      ->randomName(8);
    $edit['title'] = $new_title;
    $edit['event'] = 'keep in draft';
    $edit['event_comment'] = $this
      ->randomName(8);
    $field_text[] = $edit['field_sfhe_text[und][0][value]'] = $this
      ->randomName(8);
    $edit['revision'] = TRUE;
    $this
      ->drupalPost("node/1/edit", $edit, t('Save'));

    // // Here's the real test.
    // Make a new node.
    // nid: 2, vid: 3
    debug('Create new node 2: vid: 3');
    $edit = array();
    $new_title = $this
      ->randomName(8);
    $edit['title'] = $new_title;
    $edit['event'] = 'keep in draft';
    $edit['event_comment'] = $this
      ->randomName(8);
    $field_text[] = $edit['field_sfhe_text[und][0][value]'] = $this
      ->randomName(8);
    $this
      ->drupalPost("node/add/article", $edit, t('Save'));

    // Then make another revision of the second node.
    // nid: 2, vid: 4
    debug('Create new revision for node 2, vid: 4');
    $edit = array();
    $new_title = $this
      ->randomName(8);
    $edit['title'] = $new_title;
    $edit['event'] = 'keep in draft';
    $edit['event_comment'] = $this
      ->randomName(8);
    $field_text[] = $edit['field_sfhe_text[und][0][value]'] = $this
      ->randomName(8);
    $edit['revision'] = 1;
    $this
      ->drupalPost("node/2/edit", $edit, t('Save'));

    // Publish revision 4.
    // nid: 2, vid: 4
    debug('Pubish revision 4 of node 2');
    $edit = array();
    $edit['event'] = 'publish';
    $edit['event_comment'] = $this
      ->randomName(8);
    $field_text[] = $edit['field_sfhe_text[und][0][value]'] = $this
      ->randomName(8);
    $this
      ->drupalPost("node/2/revisions-state-flow-states", $edit, t('Update State'));

    // Make revision 5.
    // nid: 2, vid: 5 -> forward rev. nid: 2, vid: 6
    debug('Create new revision for node 2, vid: 5');
    debug('Forward revision for published  vid: 4 of node 2 is created, vid: 6');
    $edit = array();
    $new_title = $this
      ->randomName(8);
    $edit['title'] = $new_title;
    $edit['event'] = 'to draft';
    $edit['event_comment'] = $this
      ->randomName(8);
    $field_text[] = $edit['field_sfhe_text[und][0][value]'] = $this
      ->randomName(8);
    $edit['revision'] = 1;
    $this
      ->drupalPost("node/2/edit", $edit, t('Save'));

    // Publish revision 5.
    // nid: 2 vid: 5
    debug('Pubish revision 5 of node 2');
    debug('No forward revisioning necessary. vid 5 is just set as active in the node table.');
    $edit = array();
    $edit['event'] = 'publish';
    $edit['event_comment'] = $this
      ->randomName(8);
    $field_text[] = $edit['field_sfhe_text[und][0][value]'] = $this
      ->randomName(8);
    $this
      ->drupalPost("node/2/revisions-state-flow-states", $edit, t('Update State'));
    $this
      ->drupalGet('node/2/revisions-state-flow-states');

    // Make revision 6.
    // nid: 2 vid: 7  -> forward rev. nid: 2, vid: 8
    debug('Create new revision for node 2, vid: 7');
    debug('Forward revision for published  vid: 5 of node 2 is created, vid: 8');
    $edit = array();
    $new_title = $this
      ->randomName(8);
    $edit['title'] = $new_title;
    $edit['event'] = 'to draft';
    $edit['event_comment'] = $this
      ->randomName(8);
    $field_text[] = $edit['field_sfhe_text[und][0][value]'] = $this
      ->randomName(8);
    $edit['revision'] = 1;
    $this
      ->drupalPost("node/2/edit", $edit, t('Save'));
    debug('field_texts: <pre>' . print_r($field_text, TRUE) . '</pre>');

    // Check the states page.
    $this
      ->drupalGet('node/2/revisions-state-flow-states');

    // Make sure that the list handler is present.
    // (state_flow_entity_handler_field_state_flow_states_history_list.inc)
    // @todo, shouldn't this be xpath?
    $this
      ->assertRaw('views-field-history-list');

    // Verify that CSS was added.
    $this
      ->assertRaw('modules/state_flow_entity/css/state_flow_entity.css', 'CSS has been added');

    // Set the testing xpath string and initial replacement patterns.
    // @todo, add xpath comments.
    $xpath_string = "//tbody/tr[\n\n      ./td/a[contains(@href, :revision_url)]\n      and ./td[@class=:state_class and contains(., :state_value)]\n      and  contains(concat(' ', @class), :row_class)\n      and ./td/div[@class=:hid_class and contains(., :hid_text)]\n      and ./td/div[\n        @class=:hid_class\n        and ./div[\n          @class='state-flow-history-entity-content'\n          and ./div[contains(concat(' ', @class, ' '), :field_class)]\n          and ./div[contains(., :field_text)]\n        ]\n      ]\n      and ./td[contains(., :published_value)]\n    ]";
    $xpath_replacements = array(
      ':row_class' => 'state-flow--is-published--no',
      ':revision_url' => url('node/2/revisions/3/view'),
      ':state_value' => 'Draft',
      ':state_class' => 'views-field views-field-state',
      ':hid_class' => 'state-flow-history-entity-hid-3',
      ':published_value' => '',
      ':hid_text' => 'draft --> draft',
      ':field_class' => 'field-name-field-sfhe-text',
      ':field_text' => $field_text[3],
    );

    // Check revision 3.
    $tr = $this
      ->xpath($xpath_string, $xpath_replacements);
    $this
      ->assertEqual(count($tr), 1, 'There is a table row with for revision 3. It is in a Draft state and not in the node table.');

    // Check revision 4.
    $xpath_replacements[':revision_url'] = url('node/2/revisions/4/view');
    $xpath_replacements[':state_value'] = 'Published';
    $xpath_replacements[':hid_class'] = 'state-flow-history-entity-hid-4';
    $xpath_replacements[':hid_text'] = 'draft --> draft';
    $xpath_replacements[':field_text'] = $field_text[4];
    $tr = $this
      ->xpath($xpath_string, $xpath_replacements);
    $this
      ->assertEqual(count($tr), 1, 'There is a table row with for revision 4. It is in a Published state and not in the node table.');

    // Check the other history entity in revision 4.
    $xpath_replacements[':hid_text'] = 'draft --> published';
    $xpath_replacements[':hid_class'] = 'state-flow-history-entity-hid-5';
    $xpath_replacements[':field_text'] = $field_text[5];
    $tr = $this
      ->xpath($xpath_string, $xpath_replacements);
    $this
      ->assertEqual(count($tr), 1, 'Revision 4 contains the correct draft --> published record.');

    // Check revision 5.
    $xpath_replacements[':revision_url'] = url('node/2/revisions/5/view');
    $xpath_replacements[':state_value'] = 'Published';
    $xpath_replacements[':published_value'] = '';
    $xpath_replacements[':row_class'] = 'state-flow--is-published--no';
    $xpath_replacements[':hid_text'] = 'published --> draft';
    $xpath_replacements[':hid_class'] = 'state-flow-history-entity-hid-6';
    $xpath_replacements[':field_text'] = $field_text[6];
    $tr = $this
      ->xpath($xpath_string, $xpath_replacements);
    $this
      ->verbose($this
      ->buildXPathQuery($xpath_string, $xpath_replacements));
    $this
      ->assertEqual(count($tr), 1, 'There is a table row with for revision 5. It is in a Draft state and not in the node table.');

    // Check other state of revision 5.
    $xpath_replacements[':hid_text'] = 'draft --> published';
    $xpath_replacements[':hid_class'] = 'state-flow-history-entity-hid-9';
    $xpath_replacements[':field_text'] = $field_text[7];
    $tr = $this
      ->xpath($xpath_string, $xpath_replacements);
    $this
      ->verbose($this
      ->buildXPathQuery($xpath_string, $xpath_replacements));
    $this
      ->assertEqual(count($tr), 1, 'Revision 5 contains the correct draft --> published record.');

    // Check the forward revision 8 of revision 5 and the other history entity.
    $xpath_replacements[':revision_url'] = url('node/2/revisions/8/view');
    $xpath_replacements[':state_value'] = 'Published';
    $xpath_replacements[':published_value'] = 'Published';
    $xpath_replacements[':row_class'] = 'state-flow--is-published--yes';
    $xpath_replacements[':hid_text'] = 'draft --> published';
    $xpath_replacements[':hid_class'] = 'state-flow-history-entity-hid-12';
    $xpath_replacements[':field_text'] = $field_text[7];
    $tr = $this
      ->xpath($xpath_string, $xpath_replacements);
    $this
      ->verbose($this
      ->buildXPathQuery($xpath_string, $xpath_replacements));
    $this
      ->assertEqual(count($tr), 1, 'There is a table row with for revision 8. It is in a Published state and in the node table.');
    $this
      ->assertEqual(count($tr), 1, 'Revision 8 contains the correct draft --> published record.');

    // Check the other history entity in revision 8.
    $xpath_replacements[':hid_text'] = 'published --> draft';
    $xpath_replacements[':hid_class'] = 'state-flow-history-entity-hid-11';
    $xpath_replacements[':field_text'] = $field_text[6];
    $tr = $this
      ->xpath($xpath_string, $xpath_replacements);
    $this
      ->verbose($this
      ->buildXPathQuery($xpath_string, $xpath_replacements));
    $this
      ->assertEqual(count($tr), 1, 'Revision 8 contains the correct published --> draft record.');

    // Check revision 6.
    $xpath_replacements[':revision_url'] = url('node/2/revisions/7/view');
    $xpath_replacements[':state_value'] = 'Draft';
    $xpath_replacements[':published_value'] = '';
    $xpath_replacements[':row_class'] = 'state-flow--is-published--no';
    $xpath_replacements[':hid_class'] = 'state-flow-history-entity-hid-10';
    $xpath_replacements[':hid_text'] = 'published --> draft';
    $xpath_replacements[':field_text'] = $field_text[8];
    $tr = $this
      ->xpath($xpath_string, $xpath_replacements);
    $this
      ->assertEqual(count($tr), 1, 'There is a table row with for revision 6. It is in a Draft state and not in the node table.');

    // Test the unpublish step and verify the Views results.
    // Unpublish the whole node.
    $edit = array();
    $edit['event'] = 'unpublish';
    $edit['event_comment'] = $this
      ->randomName(8);
    $field_text[] = $edit['field_sfhe_text[und][0][value]'] = $this
      ->randomName(8);
    $this
      ->drupalPost("node/2/revisions-state-flow-states", $edit, t('Update State'));

    // Check the states page.
    $this
      ->drupalGet('node/2/revisions-state-flow-states');
    $xpath_replacements[':state_value'] = 'Unpublished';
    $xpath_replacements[':hid_class'] = 'state-flow-history-entity-hid-13';
    $xpath_replacements[':hid_text'] = 'draft --> unpublished';
    $xpath_replacements[':field_text'] = $field_text[9];
    $tr = $this
      ->xpath($xpath_string, $xpath_replacements);
    $this
      ->assertEqual(count($tr), 1);

    // Check revision 5.
    $xpath_replacements[':revision_url'] = url('node/2/revisions/5/view');
    $xpath_replacements[':state_value'] = 'Published';
    $xpath_replacements[':published_value'] = '';
    $xpath_replacements[':row_class'] = 'state-flow--is-published--no';
    $xpath_replacements[':hid_text'] = 'published --> draft';
    $xpath_replacements[':hid_class'] = 'state-flow-history-entity-hid-6';
    $xpath_replacements[':field_text'] = $field_text[6];
    $tr = $this
      ->xpath($xpath_string, $xpath_replacements);
    $this
      ->assertEqual(count($tr), 1, 'There is a table row with for revision 5. It is in a Published state and in the node table.');
  }

  /**
   * Test just the unpublish event, verify access/non-access of anonymous users.
   */
  public function testUnpublish() {
    $edit = array();
    $edit['title'] = $this
      ->randomName(8);
    $edit['event'] = 'publish';
    $edit['event_comment'] = $this
      ->randomName(8);
    $this
      ->drupalPost("node/add/article", $edit, t('Save'));
    $this
      ->drupalGet('node/1');

    // Log out. Verify the node is publicly accessible.
    $this
      ->drupalLogout();
    $this
      ->drupalGet("node/1");
    $this
      ->assertResponse(200, 'The node is publicly accessible.');

    // Log back in and unpublish.
    $this
      ->stateFlowLoginAdmin();
    $edit = array();
    $edit['event'] = 'unpublish';
    $edit['event_comment'] = $this
      ->randomName(8);
    $edit['title'] = $this
      ->randomName(8);
    $this
      ->drupalPost('node/1/edit', $edit, t('Save'));

    // Log out. Verify the node is not publicly accessible.
    $this
      ->drupalLogout();
    $this
      ->drupalGet('node/1');
    $this
      ->assertResponse(403, 'The node is not publicly accessible.');

    // Log in and publish from the Views tab. Send back to draft first.
    $this
      ->stateFlowLoginAdmin();
    $edit = array();
    $edit['event'] = 'to draft';
    $edit['event_comment'] = $this
      ->randomName(8);
    $this
      ->drupalPost("node/1/revisions-state-flow-states", $edit, t('Update State'));
    $edit = array();
    $edit['event'] = 'publish';
    $edit['event_comment'] = $this
      ->randomName(8);
    $this
      ->drupalPost("node/1/revisions-state-flow-states", $edit, t('Update State'));

    // Make a forward revision draft.
    $edit = array();
    $edit['event'] = 'to draft';
    $edit['event_comment'] = $this
      ->randomName(8);
    $this
      ->drupalPost('node/1/edit', $edit, t('Save'));

    // Unpublish from the Views tab.
    $edit = array();
    $edit['event'] = 'unpublish';
    $edit['event_comment'] = $this
      ->randomName(8);
    $this
      ->drupalPost("node/1/revisions-state-flow-states", $edit, t('Update State'));

    // Log out. Verify the node is not publicly accessible.
    $this
      ->drupalLogout();
    $this
      ->drupalGet('node/1');
    $this
      ->assertResponse(403, 'The node is not publicly accessible.');
  }

  /**
   * Test the make new revision checkbox.
   */
  public function testRevisionCheckbox() {

    // Make a new node.
    $edit = array();
    $new_title = $this
      ->randomName(8);
    $edit['title'] = $new_title;
    $edit['event'] = 'keep in draft';
    $edit['revision'] = TRUE;
    $edit['event_comment'] = $this
      ->randomName(8);
    $this
      ->drupalPost("node/add/page", $edit, t('Save'));

    // Make a revision of the first node.
    $edit = array();
    $new_title = $this
      ->randomName(8);
    $edit['title'] = $new_title;
    $edit['event'] = 'publish';
    $edit['revision'] = TRUE;
    $edit['event_comment'] = $this
      ->randomName(8);
    $this
      ->drupalPost("node/1/edit", $edit, t('Save'));

    // Then change the state of the node without making a revision.
    $edit = array();
    $new_title = $this
      ->randomName(8);
    $edit['title'] = $new_title;
    $edit['event'] = 'unpublish';
    $edit['revision'] = FALSE;
    $edit['event_comment'] = $this
      ->randomName(8);
    $this
      ->drupalPost("node/1/edit", $edit, t('Save'));

    // Check to make sure that there are 3 database records for the 3 state
    // changes.
    $state_flow_history = db_select('state_flow_history', 'sfh')
      ->fields('sfh')
      ->condition('entity_id', 1)
      ->condition('entity_type', 'node')
      ->execute()
      ->fetchAll();
    $this
      ->assertEqual(count($state_flow_history), 3, t('There are 3 changes to the revision state.'));

    // Check to make sure there are 2 database records for the 2 revisions of
    // the node.
    $state_flow_history = db_select('state_flow_states', 'sfs')
      ->fields('sfs')
      ->condition('entity_id', 1)
      ->condition('entity_type', 'node')
      ->execute()
      ->fetchAll();
    $this
      ->assertEqual(count($state_flow_history), 2, t('There are 2 revisions.'));
  }

}

/**
 * Tests for State Flow when nodes are created before installing state machine.
 */
class StateFlowWebTestCaseInstallOnExistingSite extends DrupalWebTestCase {
  public static function getInfo() {
    return array(
      'name' => 'StateFlow tests for sites with pre-existing nodes.',
      'description' => 'Test how State Flow behaves with nodes created before installing State Flow.',
      'group' => 'State Machine',
    );
  }

  /**
   * {@inheritdoc}
   */
  public function setup() {
    parent::setup();

    // Create a revision for nodes by default.
    foreach (array(
      'page',
      'article',
    ) as $node_type) {
      $node_options = variable_get('node_options_' . $node_type, array(
        'status',
        'promote',
      ));
      $node_options[] = 'revision';
      variable_set('node_options_' . $node_type, $node_options);
    }
    $web_user = $this
      ->drupalCreateUser(array(
      'administer nodes',
      'bypass node access',
      'administer modules',
    ));
    $this
      ->drupalLogin($web_user);
  }

  /**
   * Test adding node by the UI doesn't create NULL records.
   *
   * Test that when an node is added in the UI, there are no NULL records in
   * the state_flow_states table.
   */
  public function testStateFlowPreexistingNode() {
    $node = $this
      ->drupalCreateNode();
    $unpublished_node = $this
      ->drupalCreateNode(array(
      'status' => 0,
    ));

    // @todo, Some time between Ctools 1.0 and 1.2, this module_enable() call
    // stopped working for the tests below. The UI enabling of the modules
    // works.
    // module_enable(array('state_machine', 'state_flow', 'state_flow_entity'));
    $edit = array(
      'modules[Chaos tool suite][ctools][enable]' => TRUE,
      'modules[Other][drafty][enable]' => TRUE,
      'modules[Other][entity][enable]' => TRUE,
      'modules[State Machine][state_machine][enable]' => TRUE,
      'modules[State Machine][state_flow_entity][enable]' => TRUE,
      'modules[State Machine][state_flow][enable]' => TRUE,
    );
    $this
      ->drupalPost("admin/modules", $edit, t('Save configuration'));
    $this
      ->assertText('The configuration options have been saved.', t('Required modules enabled'));
    module_list(TRUE);
    cache_clear_all();
    $this
      ->drupalGet("node/add/article");

    // For some reason clearing the static cache after the Get request works
    // better.
    drupal_static_reset();

    // /end cache clearing gymnastics.
    $machine = state_flow_entity_load_state_machine($node, 'node');
    $unpublished_machine = state_flow_entity_load_state_machine($unpublished_node, 'node');
    $this
      ->assert('published' === $machine
      ->get_current_state(), 'Nodes published before installing are considered in the "published" state.');
    $this
      ->assert('draft' === $unpublished_machine
      ->get_current_state(), 'Nodes unpublished before installing are considered in the "draft" state.');
  }

}

/**
 * Tests for the State Flow when there are nodes created before installing state machine.
 */
class StateFlowWebTestFiles extends FileFieldTestCase {
  public static function getInfo() {
    return array(
      'name' => 'StateFlow tests for files',
      'description' => 'Test how State Flow behaves with files attached to nodes.',
      'group' => 'State Machine',
    );
  }
  function setup() {
    parent::setup('state_machine', 'state_flow', 'state_flow_entity', 'drafty');

    // Create a revision for nodes by default.
    foreach (array(
      'page',
      'article',
    ) as $node_type) {
      $node_options = variable_get('node_options_' . $node_type, array(
        'status',
        'promote',
      ));
      $node_options[] = 'revision';
      variable_set('node_options_' . $node_type, $node_options);
    }
    $web_user = $this
      ->drupalCreateUser(array(
      'administer nodes',
      'bypass node access',
      'manage content workflow',
      'administer content revisions',
    ));
    $this
      ->drupalLogin($web_user);
  }

  /**
   * Test that when an node is added in the UI, there are no NULL records in
   * the state_flow_states table.
   */
  function testStateFlowImageOnForwardRevision() {

    // Create node, straight to published.
    $edit = array();
    $edit['event'] = 'publish';
    $edit['event_comment'] = $this
      ->randomName(8);
    $edit['title'] = $title = $this
      ->randomName(8);
    $this
      ->drupalPost("node/add/article", $edit, t('Save'));

    // Edit node, add image, save as DRAFT
    $file = $this
      ->getTestFile('image');
    $edit = array();
    $edit['event'] = 'to draft';
    $edit['event_comment'] = $this
      ->randomName(8);
    $edit['files[field_image_' . LANGUAGE_NONE . '_0]'] = drupal_realpath($file->uri);
    $this
      ->drupalPost("node/1/edit", $edit, t('Save'));

    // Verify there is an image on the revision page.
    $this
      ->drupalGet("node/1/revisions/2/view");
    $this
      ->assertRaw('image-test.png');
  }

}

Classes

Namesort descending Description
StateFlowWebTestCase Unit tests for the StateFlow revision state machine.
StateFlowWebTestCaseInstallOnExistingSite Tests for State Flow when nodes are created before installing state machine.
StateFlowWebTestFiles Tests for the State Flow when there are nodes created before installing state machine.