<?php

/**
 * @file
 * Test integration for the entity_view_mode module.
 */

class EntityViewModeTestHelper extends DrupalWebTestCase {

  /**
   * Overrides DrupalWebTestCase::setUp().
   */
  public function setUp(array $modules = array()) {
    $modules[] = 'entity_view_mode';
    $modules[] = 'entity_view_mode_test';
    parent::setUp($modules);

    // Create an administrative user.
    $this->admin_user = $this->drupalCreateUser(array(
      'administer entity view modes',
      'administer content types',
    ));
  }

  /**
   * Overrides DrupalWebTestCase::refreshVariables().
   *
   * Ensures that the entity and field view mode caches are cleared so they
   * can be reliably checked in the test.
   */
  protected function refreshVariables() {
    parent::refreshVariables();

    // Clear the entity and display caches.
    drupal_static_reset('field_view_mode_settings');
    entity_info_cache_clear();
  }

  public function assertViewModeExists($entity_type, $view_mode) {
    $info = entity_get_info($entity_type);
    return $this->assertTrue(!empty($info['view modes'][$view_mode]), "View mode $view_mode found for entity type $entity_type.");
  }

  public function assertNoViewModeExists($entity_type, $view_mode) {
    $info = entity_get_info($entity_type);
    return $this->assertTrue(!isset($info['view modes'][$view_mode]), "View mode $view_mode not found for entity type $entity_type.");
  }

  public function getActualViewMode($entity_type, $bundle, $view_mode) {
    $view_mode_settings = field_view_mode_settings($entity_type, $bundle);
    return (!empty($view_mode_settings[$view_mode]['custom_settings']) ? $view_mode : 'default');
  }

  public function assertActualViewMode($entity_type, $bundle, $view_mode, $expected_view_mode) {
    $actual_view_mode = $this->getActualViewMode($entity_type, $bundle, $view_mode);
    return $this->assertIdentical($expected_view_mode, $actual_view_mode, "View mode $view_mode for entity type $entity_type and bundle $bundle actually uses view mode $actual_view_mode, expected $expected_view_mode.");
  }

  public function assertViewModeDefaultDisplay($entity_type, $bundle, $view_mode) {
    return $this->assertActualViewMode($entity_type, $bundle, $view_mode, 'default');
  }

  public function assertViewModeCustomDisplay($entity_type, $bundle, $view_mode) {
    return $this->assertActualViewMode($entity_type, $bundle, $view_mode, $view_mode);
  }
}

class EntityViewModeFunctionalTest extends EntityViewModeTestHelper {

  public static function getInfo() {
    return array(
      'name' => 'Entity view mode functionality',
      'description' => 'Tests entity view mode module functionality.',
      'group' => 'Entity view mode',
    );
  }

  public function testEntityViewModes() {
    $this->drupalLogin($this->admin_user);
    $this->drupalGet('admin/config/system/entity-view-modes');
    $this->assertLinkByHref('admin/config/system/entity-view-modes/add/node');
    $this->drupalGet('admin/config/system/entity-view-modes/add/node');

    // @todo Set some 'default' settings under a field instance display and
    // view mode settings to test that the default settings are applied to
    // custom view modes.
    //$settings = field_bundle_settings('node', 'article');
    //$settings['extra_fields']['display']['test']['default']['testing'] = TRUE;
    //field_bundle_settings('node', 'article', $settings);
    //$settings = field_bundle_settings('node', 'page');
    //$settings['extra_fields']['display']['test']['default']['testing'] = TRUE;
    //field_bundle_settings('node', 'page', $settings);

    // Attempt to create a view mode that already is provided by core.
    $edit = array(
      'label' => 'Custom teaser',
      'machine_name' => 'teaser',
    );
    $this->drupalPost(NULL, $edit, 'Save');
    $this->assertText('The machine-readable name is already in use. It must be unique.');

    // Save a new view mode for real.
    $edit['label'] = 'Custom 1';
    $edit['machine_name'] = 'custom_1';
    $edit['enabled_bundles[article]'] = TRUE;
    $edit['enabled_bundles[page]'] = FALSE;
    $this->drupalPost(NULL, $edit, 'Save');
    $this->assertViewModeExists('node', 'custom_1');
    $this->assertViewModeCustomDisplay('node', 'article', 'custom_1');
    $this->assertViewModeDefaultDisplay('node', 'page', 'custom_1');

    // Switch the view mode from articles to pages.
    $edit = array(
      'enabled_bundles[article]' => FALSE,
      'enabled_bundles[page]' => TRUE,
    );
    $this->drupalPost('admin/config/system/entity-view-modes/edit/node/custom_1', $edit, 'Save');
    $this->assertText('Saved the Custom 1 node view mode.');
    $this->assertViewModeExists('node', 'custom_1');
    $this->assertViewModeDefaultDisplay('node', 'article', 'custom_1');
    $this->assertViewModeCustomDisplay('node', 'page', 'custom_1');

    // Save a custom value into the view mode settings to check that when a
    // view mode's machine name is changed, that the display settings are
    // changed as well.
    //$settings = field_bundle_settings('node', 'page');
    //$settings['view_modes']['custom_1']['testing'] = TRUE;
    //$settings['extra_fields']['display']['testing']['custom_1']['custom_1']['testing'] = TRUE;
    //field_bundle_settings('node', 'page', $settings);

    // Rename the view mode's label and machine name.
    $edit = array(
      'label' => 'Custom 2',
      'machine_name' => 'custom_2',
    );
    $this->drupalPost('admin/config/system/entity-view-modes/edit/node/custom_1', $edit, 'Save');
    $this->assertText('Saved the Custom 2 node view mode.');
    $this->assertNoViewModeExists('node', 'custom_1');
    $this->assertViewModeDefaultDisplay('node', 'article', 'custom_1');
    $this->assertViewModeDefaultDisplay('node', 'page', 'custom_1');
    $this->assertViewModeExists('node', 'custom_2');
    $this->assertViewModeDefaultDisplay('node', 'article', 'custom_2');
    $this->assertViewModeCustomDisplay('node', 'page', 'custom_2');

    // Check that our custom value was transferred to the new view mode
    // settings.
    //$settings = field_bundle_settings('node', 'page');
    //$this->assertTrue(!isset($settings['view_modes']['custom_1']) && !isset($settings['extra_fields']['display']['custom_1']));
    //$this->assertTrue(!empty($settings['view_modes']['custom_2']['testing']) && !empty($settings['extra_fields']['display']['custom_2']['testing']));

    // Delete the view mode.
    $this->drupalPost('admin/config/system/entity-view-modes/delete/node/custom_2', array(), 'Delete');
    $this->assertText('Deleted the Custom 2 node view mode.');
    $this->assertNoViewModeExists('node', 'custom_2');
    $this->assertViewModeDefaultDisplay('node', 'article', 'custom_2');
    $this->assertViewModeDefaultDisplay('node', 'page', 'custom_2');
  }

  /**
   * Test the new entity view mode hooks.
   */
  public function testInfoHooks() {
    variable_set('entity_view_modes', array(
      'node' => array(
        'info_3' => array(
          'label' => t('Variable-altered view mode'),
          'custom settings' => TRUE,
        ),
      ),
      'taxonomy_term' => array(),
    ));
    $this->refreshVariables();

    $info = entity_get_info();

    // An invalid entity type in hook_entity_view_mode_info() does not pass
    // into the entity info array.
    $this->assertTrue(!isset($info['invalid-type']));

    // Test hook-provided view modes.
    $this->assertIdentical($info['node']['view modes']['info_1'], array(
      'label' => t('Hook-defined view mode #1'),
      'custom settings' => FALSE,
    ));
    $this->assertIdentical($info['node']['view modes']['info_2'], array(
      'label' => t('Hook-altered view mode'),
      'custom settings' => TRUE,
    ));
    $this->assertIdentical($info['node']['view modes']['info_3'], array(
      'label' => t('Variable-altered view mode'),
      'custom settings' => TRUE,
    ));
    $this->assertIdentical($info['taxonomy_term']['view modes']['info_1'], array(
      'label' => t('Hook-defined view mode #1'),
      'custom settings' => TRUE,
    ));

    // Test that entity view modes defined in hook_entity_info() are never
    // overridden by custom view modes.
    $this->assertIdentical($info['node']['view modes']['full'], array(
      'label' => 'Full content',
      'custom settings' => FALSE,
    ));
  }
}

class EntityViewModeSuggestionsTest extends EntityViewModeTestHelper {

  public static function getInfo() {
    return array(
      'name' => 'View mode suggestions',
      'description' => 'Tests entity view mode altering theme suggestions.',
      'group' => 'Entity view mode',
    );
  }

  /**
   * Overrides EntityViewModeTestHelper::setUp().
   */
  public function setUp(array $modules = array()) {
    parent::setUp($modules);

    // Core RDF module causes PHP notices with entity rendering.
    module_disable(array('rdf'));

    variable_set('entity_view_mode_theme_output_suggestions', array('comment', 'node', 'taxonomy_term', 'user_profile'));
    drupal_static_reset('theme_get_registry');
  }

  public function testEntityTemplateSuggestions() {
    $node = $this->drupalCreateNode();
    $build = node_view($node, 'node_test');
    $actual_suggestions = theme($build['#theme'], $build);
    $expected_suggestions = array(
      'node__' . $node->nid . '__node_test',
      'node__' . $node->nid,
      'node__' . $node->type . '__node_test',
      'node__' . $node->type,
    );
    $this->assertIdentical($actual_suggestions, $expected_suggestions);

    // Rendering comments uses '#theme' => 'comment__node_type'.
    $comment = (object) array(
      'cid' => NULL,
      'nid' => $node->nid,
      'pid' => 0,
      'uid' => 0,
      'status' => COMMENT_PUBLISHED,
      'subject' => $this->randomName(),
      'language' => LANGUAGE_NONE,
      'comment_body' => array(LANGUAGE_NONE => array($this->randomName())),
    );
    comment_save($comment);
    $build = comment_view($comment, $node, 'comment_test');
    $actual_suggestions = theme($build['#theme'], $build);
    $expected_suggestions = array(
      'comment__' . $comment->cid . '__comment_test',
      'comment__' . $comment->cid,
      'comment__' . 'comment_node_' . $node->type . '__comment_test',
      'comment__' . 'comment_node_' . $node->type,
    );
    $this->assertIdentical($actual_suggestions, $expected_suggestions);

    // Users have no bundles.
    $account = $this->admin_user;
    $build = user_view($this->admin_user, 'user_test');
    $actual_suggestions = theme($build['#theme'], $build);
    $expected_suggestions = array(
      'user__' . $account->uid . '__user_test',
      'user__' . $account->uid,
    );
    $this->assertIdentical($actual_suggestions, $expected_suggestions);

    $term = (object) array(
      'name' => $this->randomName(),
      'vocabulary_machine_name' => 'tags',
    );
    taxonomy_term_save($term);
    $build = taxonomy_term_view($term, 'term_test');
    $actual_suggestions = theme($build['#theme'], $build);
    $expected_suggestions = array(
      'taxonomy_term__' . $term->tid . '__term_test',
      'taxonomy_term__' . $term->tid,
      'taxonomy_term__' . $term->vocabulary_machine_name . '__term_test',
      'taxonomy_term__' . $term->vocabulary_machine_name,
    );
    $this->assertIdentical($actual_suggestions, $expected_suggestions);

    // Test the proper ordering of suggestions using the custom testing
    // theme function.
    $build = array(
      '#theme' => 'entity_view_mode_test',
      '#entity_view_mode' => array(
        'entity_type' => 'test',
        'id' => 'id',
        'bundle' => 'bundle',
        'view_mode' => 'view_mode',
        'langcode' => 'xx-test',
        'has_bundles' => TRUE,
      ),
    );
    $actual_suggestions = theme($build['#theme'], $build);
    $expected_suggestions = array(
      'test__first',
      'test__id__view_mode',
      'test__id',
      'test__bundle__view_mode',
      'test__bundle',
      'test__last',
    );
    $this->assertIdentical($actual_suggestions, $expected_suggestions);
  }
}
