<?php

/**
 * @file
 * Test the Block Class module.
 */

/**
 * Provides common functionality for the Block Class test classes.
 */
class BlockClassTestCase extends DrupalWebTestCase {

  /**
   * User object to perform site browsing.
   *
   * @var object
   */
  protected $privilegedUser;

  /**
   * Machine name of the module providing the block coupled with delta.
   *
   * @var string
   */
  protected $module = 'system';

  /**
   * Block delta as provided by its module.
   *
   * @var string
   */
  protected $delta = 'main';

  /**
   * Permissions required by the user to perform the tests.
   *
   * @var array
   */
  protected $permissions = array(
    'administer blocks',
    'administer block classes',
  );

  /**
   * Enable modules and create user with specific permissions.
   *
   * By default Test Cases are carried on the "Main page content" Block.
   */
  public function setUp() {
    // Merge inherited classes modules, see FieldUITestCase for an example.
    $modules = func_get_args();
    if (isset($modules[0]) && is_array($modules[0])) {
      $modules = $modules[0];
    }
    $modules[] = 'block_class';
    parent::setUp($modules);

    // Authenticate test user.
    $this->privilegedUser = $this->drupalCreateUser($this->permissions);
    $this->drupalLogin($this->privilegedUser);
  }

  /**
   * Update Block CSS class and assert whether it is found when displayed.
   *
   * @param bool $anon
   *   (optional) Set to TRUE to view block with anon user, defaults to TRUE.
   * @param string $module
   *   (optional) Machine name of the module Defaults to
   *   $this->module if set to NULL.
   * @param string $delta
   *   (optional) Block delta as provided by its module. Defaults to
   *   $this->delta if set to NULL.
   */
  public function assertUpdateBlockClass($anon = FALSE, $module = NULL, $delta = NULL) {
    // Initialize $module and $delta by default if no value is provided.
    if (!isset($module)) {
      $module = $this->module;
    }
    if (!isset($delta)) {
      $delta = $this->delta;
    }
    // Test with three random class names.
    $css_classes = implode(' ', array(
      $this->randomName(8),
      $this->randomName(8),
      $this->randomName(8),
    ));
    // Update Block CSS class field.
    $this->drupalPost("admin/structure/block/manage/$module/$delta/configure", array('css_class' => $css_classes), t('Save block'));
    // Check Block configuration was saved successfully.
    $this->assertText(t('The block configuration has been saved.'));
    // Log out if the test is for anonymous user.
    if ($anon) {
      $this->drupalLogout();
    }
    // Browse to the homepage.
    $this->drupalGet('');
    // Check if the Block CSS classes could be found.
    $this->assertPattern('/class=\"(.*?)' . $css_classes . '(.*?)\"/', format_string('The CSS classes were found: @css_classes', array('@css_classes' => $css_classes)));
    // Login again after testing with the anonymous user.
    if ($anon) {
      $this->drupalLogin($this->privilegedUser);
    }
  }

}

/**
 * Test the update and display of the CSS class for a Block.
 */
class BlockClassUpdateDisplayTestCase extends BlockClassTestCase {

  /**
   * Implements DrupalWebTestCase::getInfo().
   */
  public static function getInfo() {
    return array(
      'name' => 'Block CSS class update and display',
      'description' => 'Test the update of a Block CSS class field and the display for the Main Page Content Block.',
      'group' => 'Block Class',
    );
  }

  /**
   * Update and display a Block multiple times to ensure CSS class is found.
   *
   * A Block is updated and displayed several times and with logged in or
   * anonymous user, with Block cache enabled or disabled.
   */
  public function testUpdateDisplayClass() {
    // Edit the block, change the class and check if the CSS classes are found.
    $this->assertUpdateBlockClass();

    // Now, turn on caching programmatically and set it to 15 min expiry.
    variable_set('block_cache', TRUE);
    variable_set('cache_lifetime', 900);
    variable_set('page_cache_maximum_age', 900);

    // Edit the block, change the class and check with the anonymous user.
    $this->assertUpdateBlockClass(TRUE);

    // Edit the block, change the class and check with the anonymous user.
    $this->assertUpdateBlockClass(TRUE);
  }

}

/**
 * Test Block Class permissions.
 */
class BlockClassPermissionTestCase extends BlockClassTestCase {

  /**
   * Implements DrupalWebTestCase::getInfo().
   */
  public static function getInfo() {
    return array(
      'name' => 'Administer block classes permission',
      'description' => 'Test the permission added by the module to administer block classes.',
      'group' => 'Block Class',
    );
  }

  /**
   * Enable modules and create user with specific permissions.
   */
  public function setUp() {
    // Remove the 'administer block classes' permission from the base class.
    $this->permissions = array('administer blocks');
    parent::setUp();
  }

  /**
   * Ensure Block CSS classes field is only visible with the right permissions.
   *
   * Test if a user without 'administer block classes' permission has access to
   * the Block CSS classes field on the block configuration page.
   */
  public function testPermission() {
    // Browse to the "Main page content" block editing form page.
    $this->drupalGet("admin/structure/block/manage/{$this->module}/{$this->delta}/configure");
    // Check that the css_class field couldn't be found.
    // If the field is not found, it can't be submitted through drupalPost.
    $this->assertNoFieldById('css_class', 'The Css classes field was not found on the page.');
  }

}

/**
 * Test Block Class integration with Context.
 */
class BlockClassContextTestCase extends BlockClassUpdateDisplayTestCase {

  /**
   * Implements DrupalWebTestCase::getInfo().
   */
  public static function getInfo() {
    return array(
      'name' => 'Integration with Context',
      'description' => 'Test the integration of Block Class with the Context module and the update/display of a Block CSS class.',
      // Include required contributed modules context and ctools for the test.
      'dependencies' => array('context', 'ctools'),
      'group' => 'Block Class',
    );
  }

  /**
   * Enable modules and create user with specific permissions.
   */
  public function setUp() {
    // Override default module and delta to test with the "Who's online" block.
    $this->module = 'user';
    $this->delta = 'online';
    // Include the Context module and its dependencies to be loaded.
    parent::setUp('context');
    // Initialize a test context with the test block.
    $this->initializeContext();
  }

  /**
   * Helper function to initialize a test Context with a test block.
   */
  public function initializeContext() {
    // Import a basic context exported through the admin interface.
    $context = new stdClass();
    $context->disabled = FALSE;
    $context->api_version = 3;
    $context->name = 'front';
    $context->description = 'Frontpage Context';
    $context->tag = '';
    $context->conditions = array(
      'path' => array(
        'values' => array(
          '<front>' => '<front>',
        ),
      ),
    );
    $context->reactions = array(
      'block' => array(
        'blocks' => array(
          $this->module . '-' . $this->delta => array(
            'module' => $this->module,
            'delta' => $this->delta,
            'region' => 'content',
            'weight' => '-10',
          ),
        ),
      ),
    );
    $context->condition_mode = 0;

    // Translatables
    // Included for use with string extractors like potx.
    t('Frontpage Context');

    // Save the context.
    context_save($context);
  }

}

/**
 * Test Block Class integration with Features through FE Block.
 */
class BlockClassFeaturesTestCase extends BlockClassTestCase {

  /**
   * Implements DrupalWebTestCase::getInfo().
   */
  public static function getInfo() {
    return array(
      'name' => 'Integration with Features',
      'description' => 'Test the integration of Block Class with Features through the FE Block module and the update/display of a Block CSS class.',
      // Include Features related modules required for this Test Case.
      'dependencies' => array('features', 'ctools', 'fe_block'),
      'group' => 'Block Class',
    );
  }

  /**
   * Enable modules and create user with specific permissions.
   */
  public function setUp() {
    // Override default module and delta to test with the "Who's online" block.
    $this->module = 'user';
    $this->delta = 'online';
    // Include all Features related modules and Test Helper feature.
    parent::setUp('block_class_fe_block_test');
  }

  /**
   * Test how Block Class reacts when exported to a Feature with FE Block.
   *
   * Helper Feature's Block configuration settings are imported, updated and
   * the display is tested several times, before reverting the feature.
   */
  public function testFeatureDisplayClass() {
    // Block classes exported to the Test Feature module.
    $test_classes = 'fe_block-class1 fe_block-class2 fe_block-class3';
    // Test helper feature machine name.
    $test_feature = 'block_class_fe_block_test';

    // Browse to the front page and check Block's CSS classes configuration.
    $this->drupalGet('');
    // Check if feature's Block CSS classes could be found.
    $this->assertPattern('/class=\"(.*?)' . $test_classes . '(.*?)\"/', format_string('The CSS classes from exported feature were found: @css_classes', array('@css_classes' => $test_classes)));

    // Check Block's configuration form css_class field value.
    $this->drupalGet("admin/structure/block/manage/{$this->module}/{$this->delta}/configure");
    $this->assertFieldByName('css_class', $test_classes, format_string('The CSS classes from exported feature were found for the field <em>css_class</em> in the Block\'s configuration page: @css_classes', array('@css_classes' => $test_classes)));

    // Run a standard Update/Display Test check with Anon.
    $this->assertUpdateBlockClass(TRUE);

    // Ensure Feature's state is overridden for 'fe_block_settings' component.
    module_load_include('inc', 'features', 'features.export');
    $test_feature_state = features_get_storage($test_feature);
    $this->assertFalse(empty($test_feature_state), 'The state of the <em>Block Class FE Block Integration Test Helper</em> feature is <strong>Overridden</strong>.');
    $test_feature_states = features_get_component_states(array($test_feature));
    $this->assertFalse(empty($test_feature_states[$test_feature]['fe_block_settings']), 'The state of the <em>fe_block_settings</em> component of the <em>Block Class FE Block Integration Test Helper</em> feature is <strong>Overridden</strong>.');

    // Revert feature and check again.
    features_revert_module($test_feature);

    // Browse to the front page and check Block's CSS classes configuration.
    $this->drupalGet('');
    // Check if feature's Block CSS classes could be found.
    $this->assertPattern('/class=\"(.*?)' . $test_classes . '(.*?)\"/', format_string('After reverting the feature, the CSS classes from exported feature were found: @css_classes', array('@css_classes' => $test_classes)));

    // Check Block's configuration form css_class field value.
    $this->drupalGet("admin/structure/block/manage/{$this->module}/{$this->delta}/configure");
    $this->assertFieldByName('css_class', $test_classes, format_string('After reverting the feature, the CSS classes from exported feature were found for the field <em>css_class</em> in the Block\'s configuration page: @css_classes', array('@css_classes' => $test_classes)));
  }

}

/**
 * Test Block Class integration with Menu Block.
 */
class BlockClassMenuBlockTestCase extends BlockClassTestCase {

  /**
   * Implements DrupalWebTestCase::getInfo().
   */
  public static function getInfo() {
    return array(
      'name' => 'Integration with Menu Block',
      'description' => 'Test the integration of Block Class with the Menu Block module and the update/display of a Menu Block CSS class.',
      // We could use Menu Block's API to import a block from code. But part of
      // this test is about the creation process of a Menu Block.
      'dependencies' => array('menu_block'),
      'group' => 'Block Class',
    );
  }

  /**
   * Enable modules and create user with specific permissions.
   */
  public function setUp() {
    // Override default parameters to test with the first Menu Block created.
    $this->module = 'menu_block';
    $this->delta = 1;
    // Add permission required by Menu Block to browse the add menu block page.
    $this->permissions[] = 'administer menu';
    // Include the Menu Block module and its dependencies to be loaded.
    parent::setUp('menu_block');
  }

  /**
   * Create, update and display a Menu Block to ensure CSS class is found.
   *
   * A Menu Block is added through the user interface. It is then updated and
   * displayed several times with logged in or anonymous user.
   */
  public function testMenuBlockDisplayClass() {
    // Test with three random class names.
    $css_classes = implode(' ', array(
      $this->randomName(8),
      $this->randomName(8),
      $this->randomName(8),
    ));
    // Start with the creation of a new Menu Block.
    $this->drupalPost("admin/structure/block/add-menu-block", array(
      'css_class' => $css_classes,
      'menu_name' => 'navigation',
      'regions[bartik]' => 'content',
    ), t('Save block'));
    // Check if the Block was successfully created.
    $this->assertText(t('The block has been created.'));
    // Browse to the homepage.
    $this->drupalGet('');
    // Check if the Block CSS classes could be found.
    $this->assertPattern('/class=\"(.*?)' . $css_classes . '(.*?)\"/', format_string('The CSS classes were found: @css_classes', array('@css_classes' => $css_classes)));
    // Run the standard tests on the existing Menu Block created above.
    // Edit the block, change the class and check with the anonymous user.
    $this->assertUpdateBlockClass(TRUE);
  }

}

/**
 * Test Block Class integration with Views block.
 */
class BlockClassViewsTestCase extends BlockClassUpdateDisplayTestCase {

  /**
   * Implements DrupalWebTestCase::getInfo().
   */
  public static function getInfo() {
    return array(
      'name' => 'Integration with Views block',
      'description' => 'Test the integration of Block Class with Views blocks and the update/display of a CSS class.',
      'dependencies' => array('views'),
      'group' => 'Block Class',
    );
  }

  /**
   * Enable modules and create the data necessary to run the tests.
   */
  public function setUp() {
    // Override default parameters to test with the Views: Archive block.
    $this->module = 'views';
    $this->delta = 'archive-block';
    // Include the Views module and its dependencies to be loaded.
    parent::setUp('views');

    // Programmatically enable the default Archive View, based on #820110-1.
    $status = variable_get('views_defaults', array());
    $status['archive'] = FALSE;
    variable_set('views_defaults', $status);

    // Note that the Views Archive block is not visible on the Block admin page
    // unless the registry is rebuilt with: registry_rebuild().
    // Directly publish the Views Archive Block to the content region.
    $this->drupalPost("admin/structure/block/manage/{$this->module}/{$this->delta}/configure", array('regions[bartik]' => 'content'), t('Save block'));

    // Create a sample node to have some data to display in Views: Archive.
    $this->drupalCreateNode(array(
      'type' => 'page',
      'status' => 1,
      'uid' => $this->privilegedUser->uid,
    ));
  }

}
