views_fieldapi.test

Tests the fieldapi integration of viewsdata.

File

tests/field/views_fieldapi.test

View source
<?php


/**
 * @file
 * Tests the fieldapi integration of viewsdata.
 */

/**
 * @todo Test on a generic entity not on a node.
 *
 * What has to be tested:
 * - Take sure that every wanted field is added to the according entity type.
 * - Take sure the joins are done correct.
 * - Use basic fields and take sure that the full wanted object is build.
 * - Use relationships between different entity types, for example node and
 *   the node author(user).
 */

/**
 * Provides some helper methods for testing fieldapi integration into views.
 */
class ViewsFieldApiTestHelper extends ViewsSqlTest {
    
    /**
     * Stores the field definitions used by the test.
     *
     * @var array
     */
    public $fields;
    
    /**
     * Stores the instances of the fields. They have
     * the same keys as the fields.
     *
     * @var array
     */
    public $instances;
    
    /**
     *
     */
    protected function createUser($extra_edit = array()) {
        $permissions = array(
            'access comments',
            'access content',
            'post comments',
            'skip comment approval',
        );
        // Create a role with the given permission set.
        if (!($rid = $this->drupalCreateRole($permissions))) {
            return FALSE;
        }
        // Create a user assigned to that role.
        $edit = array();
        $edit['name'] = $this->randomName();
        $edit['mail'] = $edit['name'] . '@example.com';
        $edit['roles'] = array(
            $rid => $rid,
        );
        $edit['pass'] = user_password();
        $edit['status'] = 1;
        $edit += $extra_edit;
        $account = user_save(drupal_anonymous_user(), $edit);
        $this->assertTrue(!empty($account->uid), t('User created with name %name and pass %pass', array(
            '%name' => $edit['name'],
            '%pass' => $edit['pass'],
        )), t('User login'));
        if (empty($account->uid)) {
            return FALSE;
        }
        // Add the raw password so that we can log in as this user.
        $account->pass_raw = $edit['pass'];
        return $account;
    }
    
    /**
     *
     */
    public function setUpFields($amount = 3) {
        // Create three fields.
        $field_names = array();
        for ($i = 0; $i < $amount; $i++) {
            $field_names[$i] = 'field_name_' . $i;
            $field = array(
                'field_name' => $field_names[$i],
                'type' => 'text',
            );
            $this->fields[$i] = $field = field_create_field($field);
        }
        return $field_names;
    }
    
    /**
     *
     */
    public function setUpInstances($bundle = 'page') {
        foreach ($this->fields as $key => $field) {
            $instance = array(
                'field_name' => $field['field_name'],
                'entity_type' => 'node',
                'bundle' => 'page',
            );
            $this->instances[$key] = field_create_instance($instance);
        }
    }
    
    /**
     * Clear all views caches and static caches which are required for the patch.
     */
    public function clearViewsCaches() {
        // Reset views data cache.
        drupal_static_reset('_views_fetch_data_cache');
        drupal_static_reset('_views_fetch_data_recursion_protected');
        drupal_static_reset('_views_fetch_data_fully_loaded');
    }

}

/**
 * Test the produced views_data.
 */
class viewsFieldApiDataTest extends ViewsFieldApiTestHelper {
    
    /**
     * Stores the fields for this test case.
     */
    public $fields;
    
    /**
     *
     */
    public static function getInfo() {
        return array(
            'name' => 'Fieldapi: Views Data',
            'description' => 'Tests the fieldapi views data.',
            'group' => 'Views Modules',
        );
    }
    
    /**
     * {@inheritdoc}
     */
    public function setUp(array $modules = array()) {
        parent::setUp($modules);
        $langcode = LANGUAGE_NONE;
        $field_names = $this->setUpFields();
        // The first one will be attached to nodes only.
        $instance = array(
            'field_name' => $field_names[0],
            'entity_type' => 'node',
            'bundle' => 'page',
        );
        field_create_instance($instance);
        // The second one will be attached to users only.
        $instance = array(
            'field_name' => $field_names[1],
            'entity_type' => 'user',
            'bundle' => 'user',
        );
        field_create_instance($instance);
        // The third will be attached to both nodes and users.
        $instance = array(
            'field_name' => $field_names[2],
            'entity_type' => 'node',
            'bundle' => 'page',
        );
        field_create_instance($instance);
        $instance = array(
            'field_name' => $field_names[2],
            'entity_type' => 'user',
            'bundle' => 'user',
        );
        field_create_instance($instance);
        // Now create some example nodes/users for the view result.
        for ($i = 0; $i < 5; $i++) {
            $edit = array(
                // @todo Write a helper method to create such values.
'field_name_0' => array(
                    $langcode => array(
                        array(
                            'value' => $this->randomName(),
                        ),
                    ),
                ),
                'field_name_2' => array(
                    $langcode => array(
                        array(
                            'value' => $this->randomName(),
                        ),
                    ),
                ),
            );
            $this->nodes[] = $this->drupalCreateNode($edit);
        }
        for ($i = 0; $i < 5; $i++) {
            $edit = array(
                'field_name_1' => array(
                    $langcode => array(
                        array(
                            'value' => $this->randomName(),
                        ),
                    ),
                ),
                'field_name_2' => array(
                    $langcode => array(
                        array(
                            'value' => $this->randomName(),
                        ),
                    ),
                ),
            );
            $this->users[] = $this->createUser($edit);
        }
        // Reset views data cache.
        $this->clearViewsCaches();
    }
    
    /**
     * Unit testing the views data structure.
     *
     * We check data structure for both node and node revision tables.
     */
    public function testViewsData() {
        $data = views_fetch_data();
        // Check the table and the joins of the first field.
        // Attached to node only.
        $field = $this->fields[0];
        $current_table = _field_sql_storage_tablename($field);
        $revision_table = _field_sql_storage_revision_tablename($field);
        $this->assertTrue(isset($data[$current_table]));
        $this->assertTrue(isset($data[$revision_table]));
        // The node field should join against node.
        $this->assertTrue(isset($data[$current_table]['table']['join']['node']));
        $this->assertTrue(isset($data[$revision_table]['table']['join']['node_revision']));
        $expected_join = array(
            'left_field' => 'nid',
            'field' => 'entity_id',
            'extra' => array(
                array(
                    'field' => 'entity_type',
                    'value' => 'node',
                ),
                array(
                    'field' => 'deleted',
                    'value' => 0,
                    'numeric' => TRUE,
                ),
            ),
        );
        $this->assertEqual($expected_join, $data[$current_table]['table']['join']['node']);
        $expected_join = array(
            'left_field' => 'vid',
            'field' => 'revision_id',
            'extra' => array(
                array(
                    'field' => 'entity_type',
                    'value' => 'node',
                ),
                array(
                    'field' => 'deleted',
                    'value' => 0,
                    'numeric' => TRUE,
                ),
            ),
        );
        $this->assertEqual($expected_join, $data[$revision_table]['table']['join']['node_revision']);
        // Check the table and the joins of the second field.
        // Attached to both node and user.
        $field_2 = $this->fields[2];
        $current_table_2 = _field_sql_storage_tablename($field_2);
        $revision_table_2 = _field_sql_storage_revision_tablename($field_2);
        $this->assertTrue(isset($data[$current_table_2]));
        $this->assertTrue(isset($data[$revision_table_2]));
        // The second field should join against both node and users.
        $this->assertTrue(isset($data[$current_table_2]['table']['join']['node']));
        $this->assertTrue(isset($data[$revision_table_2]['table']['join']['node_revision']));
        $this->assertTrue(isset($data[$current_table_2]['table']['join']['users']));
        $expected_join = array(
            'left_field' => 'nid',
            'field' => 'entity_id',
            'extra' => array(
                array(
                    'field' => 'entity_type',
                    'value' => 'node',
                ),
                array(
                    'field' => 'deleted',
                    'value' => 0,
                    'numeric' => TRUE,
                ),
            ),
        );
        $this->assertEqual($expected_join, $data[$current_table_2]['table']['join']['node']);
        $expected_join = array(
            'left_field' => 'vid',
            'field' => 'revision_id',
            'extra' => array(
                array(
                    'field' => 'entity_type',
                    'value' => 'node',
                ),
                array(
                    'field' => 'deleted',
                    'value' => 0,
                    'numeric' => TRUE,
                ),
            ),
        );
        $this->assertEqual($expected_join, $data[$revision_table_2]['table']['join']['node_revision']);
        $expected_join = array(
            'left_field' => 'uid',
            'field' => 'entity_id',
            'extra' => array(
                array(
                    'field' => 'entity_type',
                    'value' => 'user',
                ),
                array(
                    'field' => 'deleted',
                    'value' => 0,
                    'numeric' => TRUE,
                ),
            ),
        );
        $this->assertEqual($expected_join, $data[$current_table_2]['table']['join']['users']);
        // @todo Check the fields.
        // @todo Check the arguments.
        // @todo Check the sort criterias.
        // @todo Check the relationships.
    }

}

/**
 * Tests the field_field handler.
 *
 * @todo Check a entity-type with bundles.
 * @todo Check a entity-type without bundles.
 * @todo Check locale:disabled, locale:enabled and locale:enabled with another language.
 * @todo Check revisions.
 */
class viewsHandlerFieldFieldTest extends ViewsFieldApiTestHelper {
    public $nodes;
    
    /**
     *
     */
    public static function getInfo() {
        return array(
            'name' => 'Fieldapi: Field handler',
            'description' => 'Tests the field itself of the fieldapi integration',
            'group' => 'Views Modules',
        );
    }
    
    /**
     * {@inheritdoc}
     */
    public function setUp(array $modules = array()) {
        parent::setUp($modules);
        // Setup basic fields.
        $this->setUpFields(3);
        // Setup a field with cardinality > 1.
        $this->fields[3] = $field = field_create_field(array(
            'field_name' => 'field_name_3',
            'type' => 'text',
            'cardinality' => FIELD_CARDINALITY_UNLIMITED,
        ));
        // Setup a field that will have no value.
        $this->fields[4] = $field = field_create_field(array(
            'field_name' => 'field_name_4',
            'type' => 'text',
            'cardinality' => FIELD_CARDINALITY_UNLIMITED,
        ));
        $this->setUpInstances();
        $this->clearViewsCaches();
        // Create some nodes.
        $this->nodes = array();
        for ($i = 0; $i < 3; $i++) {
            $edit = array(
                'type' => 'page',
            );
            for ($key = 0; $key < 3; $key++) {
                $field = $this->fields[$key];
                $edit[$field['field_name']][LANGUAGE_NONE][0]['value'] = $this->randomName(8);
            }
            for ($j = 0; $j < 5; $j++) {
                $edit[$this->fields[3]['field_name']][LANGUAGE_NONE][$j]['value'] = $this->randomName(8);
            }
            // Set this field to be empty.
            $edit[$this->fields[4]['field_name']] = array();
            $this->nodes[$i] = $this->drupalCreateNode($edit);
        }
    }
    
    /**
     *
     */
    public function testFieldRender() {
        $this->_testSimpleFieldRender();
        $this->_testFormatterSimpleFieldRender();
        $this->_testMultipleFieldRender();
    }
    
    /**
     *
     */
    public function _testSimpleFieldRender() {
        $view = $this->getFieldView();
        $this->executeView($view);
        // Tests that the rendered fields match the actual value of the fields.
        for ($i = 0; $i < 3; $i++) {
            for ($key = 0; $key < 2; $key++) {
                $field = $this->fields[$key];
                $rendered_field = $view->style_plugin
                    ->get_field($i, $field['field_name']);
                $expected_field = $this->nodes[$i]->{$field['field_name']}[LANGUAGE_NONE][0]['value'];
                $this->assertEqual($rendered_field, $expected_field);
            }
        }
    }
    
    /**
     * Tests that fields with formatters runs as expected.
     */
    public function _testFormatterSimpleFieldRender() {
        $view = $this->getFieldView();
        $view->display['default']->display_options['fields'][$this->fields[0]['field_name']]['type'] = 'text_trimmed';
        $view->display['default']->display_options['fields'][$this->fields[0]['field_name']]['settings'] = array(
            'trim_length' => 3,
        );
        $this->executeView($view);
        // Take sure that the formatter works as expected.
        // @todo actually there should be a specific formatter.
        for ($i = 0; $i < 2; $i++) {
            $rendered_field = $view->style_plugin
                ->get_field($i, $this->fields[0]['field_name']);
            $this->assertEqual(strlen($rendered_field), 3);
        }
    }
    
    /**
     *
     */
    public function _testMultipleFieldRender() {
        $view = $this->getFieldView();
        // Test delta limit.
        $view->display['default']->display_options['fields'][$this->fields[3]['field_name']]['group_rows'] = TRUE;
        $view->display['default']->display_options['fields'][$this->fields[3]['field_name']]['delta_limit'] = 3;
        $this->executeView($view);
        for ($i = 0; $i < 3; $i++) {
            $rendered_field = $view->style_plugin
                ->get_field($i, $this->fields[3]['field_name']);
            $this->assertNotNull($rendered_field);
            $items = array();
            $pure_items = $this->nodes[$i]->{$this->fields[3]['field_name']}[LANGUAGE_NONE];
            $pure_items = array_splice($pure_items, 0, 3);
            foreach ($pure_items as $j => $item) {
                $items[] = $pure_items[$j]['value'];
            }
            $this->assertEqual($rendered_field, implode(', ', $items), 'Take sure that the amount of items are limited.');
        }
        // Test that an empty field is rendered without error.
        $rendered_field = $view->style_plugin
            ->get_field(4, $this->fields[4]['field_name']);
        $view->destroy();
        // Test delta limit + offset.
        $view->display['default']->display_options['fields'][$this->fields[3]['field_name']]['group_rows'] = TRUE;
        $view->display['default']->display_options['fields'][$this->fields[3]['field_name']]['delta_limit'] = 3;
        $view->display['default']->display_options['fields'][$this->fields[3]['field_name']]['delta_offset'] = 1;
        $this->executeView($view);
        for ($i = 0; $i < 3; $i++) {
            $rendered_field = $view->style_plugin
                ->get_field($i, $this->fields[3]['field_name']);
            $this->assertNotNull($rendered_field);
            $items = array();
            $pure_items = $this->nodes[$i]->{$this->fields[3]['field_name']}[LANGUAGE_NONE];
            $pure_items = array_splice($pure_items, 1, 3);
            foreach ($pure_items as $j => $item) {
                $items[] = $pure_items[$j]['value'];
            }
            $this->assertEqual($rendered_field, implode(', ', $items), 'Take sure that the amount of items are limited.');
        }
        $view->destroy();
        // Test delta limit + reverse.
        $view->display['default']->display_options['fields'][$this->fields[3]['field_name']]['delta_offset'] = 0;
        $view->display['default']->display_options['fields'][$this->fields[3]['field_name']]['group_rows'] = TRUE;
        $view->display['default']->display_options['fields'][$this->fields[3]['field_name']]['delta_limit'] = 3;
        $view->display['default']->display_options['fields'][$this->fields[3]['field_name']]['delta_reversed'] = TRUE;
        $this->executeView($view);
        for ($i = 0; $i < 3; $i++) {
            $rendered_field = $view->style_plugin
                ->get_field($i, $this->fields[3]['field_name']);
            $this->assertNotNull($rendered_field);
            $items = array();
            $pure_items = $this->nodes[$i]->{$this->fields[3]['field_name']}[LANGUAGE_NONE];
            array_splice($pure_items, 0, -3);
            $pure_items = array_reverse($pure_items);
            foreach ($pure_items as $j => $item) {
                $items[] = $pure_items[$j]['value'];
            }
            $this->assertEqual($rendered_field, implode(', ', $items), 'Take sure that the amount of items are limited.');
        }
        $view->destroy();
        // Test delta first last.
        $view->display['default']->display_options['fields'][$this->fields[3]['field_name']]['group_rows'] = TRUE;
        $view->display['default']->display_options['fields'][$this->fields[3]['field_name']]['delta_limit'] = 'all';
        $view->display['default']->display_options['fields'][$this->fields[3]['field_name']]['delta_first_last'] = TRUE;
        $view->display['default']->display_options['fields'][$this->fields[3]['field_name']]['delta_reversed'] = FALSE;
        $this->executeView($view);
        for ($i = 0; $i < 3; $i++) {
            $rendered_field = $view->style_plugin
                ->get_field($i, $this->fields[3]['field_name']);
            $this->assertNotNull($rendered_field);
            $items = array();
            $pure_items = $this->nodes[$i]->{$this->fields[3]['field_name']}[LANGUAGE_NONE];
            $items[] = $pure_items[0]['value'];
            $items[] = $pure_items[4]['value'];
            $this->assertEqual($rendered_field, implode(', ', $items), 'Take sure that the amount of items are limited.');
        }
        $view->destroy();
        // Test delta limit + custom seperator.
        $view->display['default']->display_options['fields'][$this->fields[3]['field_name']]['delta_first_last'] = FALSE;
        $view->display['default']->display_options['fields'][$this->fields[3]['field_name']]['delta_limit'] = 3;
        $view->display['default']->display_options['fields'][$this->fields[3]['field_name']]['group_rows'] = TRUE;
        $view->display['default']->display_options['fields'][$this->fields[3]['field_name']]['separator'] = ':';
        $this->executeView($view);
        for ($i = 0; $i < 3; $i++) {
            $rendered_field = $view->style_plugin
                ->get_field($i, $this->fields[3]['field_name']);
            $this->assertNotNull($rendered_field);
            $items = array();
            $pure_items = $this->nodes[$i]->{$this->fields[3]['field_name']}[LANGUAGE_NONE];
            $pure_items = array_splice($pure_items, 0, 3);
            foreach ($pure_items as $j => $item) {
                $items[] = $pure_items[$j]['value'];
            }
            $this->assertEqual($rendered_field, implode(':', $items), 'Take sure that the amount of items are limited.');
        }
    }
    
    /**
     *
     */
    protected function getFieldView() {
        $view = new view();
        $view->name = 'view_fieldapi';
        $view->description = '';
        $view->tag = 'default';
        $view->base_table = 'node';
        $view->human_name = 'view_fieldapi';
        $view->core = 7;
        $view->api_version = '3.0';
        $view->disabled = FALSE;
        
        /* Edit this to true to make a default view disabled initially */
        
        /* Display: Master */
        $handler = $view->new_display('default', 'Master', 'default');
        $handler->display->display_options['access']['type'] = 'perm';
        $handler->display->display_options['cache']['type'] = 'none';
        $handler->display->display_options['query']['type'] = 'views_query';
        $handler->display->display_options['exposed_form']['type'] = 'basic';
        $handler->display->display_options['pager']['type'] = 'full';
        $handler->display->display_options['style_plugin'] = 'default';
        $handler->display->display_options['row_plugin'] = 'fields';
        $handler->display->display_options['fields']['nid']['id'] = 'nid';
        $handler->display->display_options['fields']['nid']['table'] = 'node';
        $handler->display->display_options['fields']['nid']['field'] = 'nid';
        foreach ($this->fields as $field) {
            $handler->display->display_options['fields'][$field['field_name']]['id'] = $field['field_name'];
            $handler->display->display_options['fields'][$field['field_name']]['table'] = 'field_data_' . $field['field_name'];
            $handler->display->display_options['fields'][$field['field_name']]['field'] = $field['field_name'];
        }
        return $view;
    }

}

Classes

Title Deprecated Summary
viewsFieldApiDataTest Test the produced views_data.
ViewsFieldApiTestHelper Provides some helper methods for testing fieldapi integration into views.
viewsHandlerFieldFieldTest Tests the field_field handler.