views_fieldapi.test 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492
  1. <?php
  2. /**
  3. * @file
  4. * Tests the fieldapi integration of viewsdata.
  5. */
  6. /**
  7. * @todo Test on a generic entity not on a node.
  8. *
  9. * What has to be tested:
  10. * - Take sure that every wanted field is added to the according entity type.
  11. * - Take sure the joins are done correct.
  12. * - Use basic fields and take sure that the full wanted object is build.
  13. * - Use relationships between different entity types, for example node and
  14. * the node author(user).
  15. */
  16. /**
  17. * Provides some helper methods for testing fieldapi integration into views.
  18. */
  19. class ViewsFieldApiTestHelper extends ViewsSqlTest {
  20. /**
  21. * Stores the field definitions used by the test.
  22. *
  23. * @var array
  24. */
  25. public $fields;
  26. /**
  27. * Stores the instances of the fields. They have
  28. * the same keys as the fields.
  29. *
  30. * @var array
  31. */
  32. public $instances;
  33. /**
  34. *
  35. */
  36. protected function createUser($extra_edit = array()) {
  37. $permissions = array('access comments', 'access content', 'post comments', 'skip comment approval');
  38. // Create a role with the given permission set.
  39. if (!($rid = $this->drupalCreateRole($permissions))) {
  40. return FALSE;
  41. }
  42. // Create a user assigned to that role.
  43. $edit = array();
  44. $edit['name'] = $this->randomName();
  45. $edit['mail'] = $edit['name'] . '@example.com';
  46. $edit['roles'] = array($rid => $rid);
  47. $edit['pass'] = user_password();
  48. $edit['status'] = 1;
  49. $edit += $extra_edit;
  50. $account = user_save(drupal_anonymous_user(), $edit);
  51. $this->assertTrue(!empty($account->uid), t('User created with name %name and pass %pass', array('%name' => $edit['name'], '%pass' => $edit['pass'])), t('User login'));
  52. if (empty($account->uid)) {
  53. return FALSE;
  54. }
  55. // Add the raw password so that we can log in as this user.
  56. $account->pass_raw = $edit['pass'];
  57. return $account;
  58. }
  59. function setUpFields($amount = 3) {
  60. // Create three fields.
  61. $field_names = array();
  62. for ($i = 0; $i < $amount; $i++) {
  63. $field_names[$i] = 'field_name_' . $i;
  64. $field = array('field_name' => $field_names[$i], 'type' => 'text');
  65. $this->fields[$i] = $field = field_create_field($field);
  66. }
  67. return $field_names;
  68. }
  69. function setUpInstances($bundle = 'page') {
  70. foreach ($this->fields as $key => $field) {
  71. $instance = array(
  72. 'field_name' => $field['field_name'],
  73. 'entity_type' => 'node',
  74. 'bundle' => 'page',
  75. );
  76. $this->instances[$key] = field_create_instance($instance);
  77. }
  78. }
  79. /**
  80. * Clear all views caches and static caches which are required for the patch.
  81. */
  82. function clearViewsCaches() {
  83. // Reset views data cache.
  84. drupal_static_reset('_views_fetch_data_cache');
  85. drupal_static_reset('_views_fetch_data_recursion_protected');
  86. drupal_static_reset('_views_fetch_data_fully_loaded');
  87. }
  88. }
  89. /**
  90. * Test the produced views_data.
  91. */
  92. class viewsFieldApiDataTest extends ViewsFieldApiTestHelper {
  93. /**
  94. * Stores the fields for this test case.
  95. */
  96. var $fields;
  97. public static function getInfo() {
  98. return array(
  99. 'name' => 'Fieldapi: Views Data',
  100. 'description' => 'Tests the fieldapi views data.',
  101. 'group' => 'Views Modules',
  102. );
  103. }
  104. function setUp() {
  105. parent::setUp();
  106. $langcode = LANGUAGE_NONE;
  107. $field_names = $this->setUpFields();
  108. // The first one will be attached to nodes only.
  109. $instance = array(
  110. 'field_name' => $field_names[0],
  111. 'entity_type' => 'node',
  112. 'bundle' => 'page',
  113. );
  114. field_create_instance($instance);
  115. // The second one will be attached to users only.
  116. $instance = array(
  117. 'field_name' => $field_names[1],
  118. 'entity_type' => 'user',
  119. 'bundle' => 'user',
  120. );
  121. field_create_instance($instance);
  122. // The third will be attached to both nodes and users.
  123. $instance = array(
  124. 'field_name' => $field_names[2],
  125. 'entity_type' => 'node',
  126. 'bundle' => 'page',
  127. );
  128. field_create_instance($instance);
  129. $instance = array(
  130. 'field_name' => $field_names[2],
  131. 'entity_type' => 'user',
  132. 'bundle' => 'user',
  133. );
  134. field_create_instance($instance);
  135. // Now create some example nodes/users for the view result.
  136. for ($i = 0; $i < 5; $i++) {
  137. $edit = array(
  138. // @todo Write a helper method to create such values.
  139. 'field_name_0' => array($langcode => array((array('value' => $this->randomName())))),
  140. 'field_name_2' => array($langcode => array((array('value' => $this->randomName())))),
  141. );
  142. $this->nodes[] = $this->drupalCreateNode($edit);
  143. }
  144. for ($i = 0; $i < 5; $i++) {
  145. $edit = array(
  146. 'field_name_1' => array($langcode => array((array('value' => $this->randomName())))),
  147. 'field_name_2' => array($langcode => array((array('value' => $this->randomName())))),
  148. );
  149. $this->users[] = $this->createUser($edit);
  150. }
  151. // Reset views data cache.
  152. $this->clearViewsCaches();
  153. }
  154. /**
  155. * Unit testing the views data structure.
  156. *
  157. * We check data structure for both node and node revision tables.
  158. */
  159. function testViewsData() {
  160. $data = views_fetch_data();
  161. // Check the table and the joins of the first field.
  162. // Attached to node only.
  163. $field = $this->fields[0];
  164. $current_table = _field_sql_storage_tablename($field);
  165. $revision_table = _field_sql_storage_revision_tablename($field);
  166. $this->assertTrue(isset($data[$current_table]));
  167. $this->assertTrue(isset($data[$revision_table]));
  168. // The node field should join against node.
  169. $this->assertTrue(isset($data[$current_table]['table']['join']['node']));
  170. $this->assertTrue(isset($data[$revision_table]['table']['join']['node_revision']));
  171. $expected_join = array(
  172. 'left_field' => 'nid',
  173. 'field' => 'entity_id',
  174. 'extra' => array(
  175. array('field' => 'entity_type', 'value' => 'node'),
  176. array('field' => 'deleted', 'value' => 0, 'numeric' => TRUE),
  177. ),
  178. );
  179. $this->assertEqual($expected_join, $data[$current_table]['table']['join']['node']);
  180. $expected_join = array(
  181. 'left_field' => 'vid',
  182. 'field' => 'revision_id',
  183. 'extra' => array(
  184. array('field' => 'entity_type', 'value' => 'node'),
  185. array('field' => 'deleted', 'value' => 0, 'numeric' => TRUE),
  186. ),
  187. );
  188. $this->assertEqual($expected_join, $data[$revision_table]['table']['join']['node_revision']);
  189. // Check the table and the joins of the second field.
  190. // Attached to both node and user.
  191. $field_2 = $this->fields[2];
  192. $current_table_2 = _field_sql_storage_tablename($field_2);
  193. $revision_table_2 = _field_sql_storage_revision_tablename($field_2);
  194. $this->assertTrue(isset($data[$current_table_2]));
  195. $this->assertTrue(isset($data[$revision_table_2]));
  196. // The second field should join against both node and users.
  197. $this->assertTrue(isset($data[$current_table_2]['table']['join']['node']));
  198. $this->assertTrue(isset($data[$revision_table_2]['table']['join']['node_revision']));
  199. $this->assertTrue(isset($data[$current_table_2]['table']['join']['users']));
  200. $expected_join = array(
  201. 'left_field' => 'nid',
  202. 'field' => 'entity_id',
  203. 'extra' => array(
  204. array('field' => 'entity_type', 'value' => 'node'),
  205. array('field' => 'deleted', 'value' => 0, 'numeric' => TRUE),
  206. ),
  207. );
  208. $this->assertEqual($expected_join, $data[$current_table_2]['table']['join']['node']);
  209. $expected_join = array(
  210. 'left_field' => 'vid',
  211. 'field' => 'revision_id',
  212. 'extra' => array(
  213. array('field' => 'entity_type', 'value' => 'node'),
  214. array('field' => 'deleted', 'value' => 0, 'numeric' => TRUE),
  215. ),
  216. );
  217. $this->assertEqual($expected_join, $data[$revision_table_2]['table']['join']['node_revision']);
  218. $expected_join = array(
  219. 'left_field' => 'uid',
  220. 'field' => 'entity_id',
  221. 'extra' => array(
  222. array('field' => 'entity_type', 'value' => 'user'),
  223. array('field' => 'deleted', 'value' => 0, 'numeric' => TRUE),
  224. ),
  225. );
  226. $this->assertEqual($expected_join, $data[$current_table_2]['table']['join']['users']);
  227. // @todo Check the fields.
  228. // @todo Check the arguments.
  229. // @todo Check the sort criterias.
  230. // @todo Check the relationships.
  231. }
  232. }
  233. /**
  234. * Tests the field_field handler.
  235. *
  236. * @todo Check a entity-type with bundles.
  237. * @todo Check a entity-type without bundles.
  238. * @todo Check locale:disabled, locale:enabled and locale:enabled with another language.
  239. * @todo Check revisions.
  240. */
  241. class viewsHandlerFieldFieldTest extends ViewsFieldApiTestHelper {
  242. public $nodes;
  243. public static function getInfo() {
  244. return array(
  245. 'name' => 'Fieldapi: Field handler',
  246. 'description' => 'Tests the field itself of the fieldapi integration',
  247. 'group' => 'Views Modules',
  248. );
  249. }
  250. protected function setUp() {
  251. parent::setUp();
  252. // Setup basic fields.
  253. $this->setUpFields(3);
  254. // Setup a field with cardinality > 1.
  255. $this->fields[3] = $field = field_create_field(array('field_name' => 'field_name_3', 'type' => 'text', 'cardinality' => FIELD_CARDINALITY_UNLIMITED));
  256. // Setup a field that will have no value.
  257. $this->fields[4] = $field = field_create_field(array('field_name' => 'field_name_4', 'type' => 'text', 'cardinality' => FIELD_CARDINALITY_UNLIMITED));
  258. $this->setUpInstances();
  259. $this->clearViewsCaches();
  260. // Create some nodes.
  261. $this->nodes = array();
  262. for ($i = 0; $i < 3; $i++) {
  263. $edit = array('type' => 'page');
  264. for ($key = 0; $key < 3; $key++) {
  265. $field = $this->fields[$key];
  266. $edit[$field['field_name']][LANGUAGE_NONE][0]['value'] = $this->randomName(8);
  267. }
  268. for ($j = 0; $j < 5; $j++) {
  269. $edit[$this->fields[3]['field_name']][LANGUAGE_NONE][$j]['value'] = $this->randomName(8);
  270. }
  271. // Set this field to be empty.
  272. $edit[$this->fields[4]['field_name']] = array();
  273. $this->nodes[$i] = $this->drupalCreateNode($edit);
  274. }
  275. }
  276. public function testFieldRender() {
  277. $this->_testSimpleFieldRender();
  278. $this->_testFormatterSimpleFieldRender();
  279. $this->_testMultipleFieldRender();
  280. }
  281. public function _testSimpleFieldRender() {
  282. $view = $this->getFieldView();
  283. $this->executeView($view);
  284. // Tests that the rendered fields match the actual value of the fields.
  285. for ($i = 0; $i < 3; $i++) {
  286. for ($key = 0; $key < 2; $key++) {
  287. $field = $this->fields[$key];
  288. $rendered_field = $view->style_plugin->get_field($i, $field['field_name']);
  289. $expected_field = $this->nodes[$i]->{$field['field_name']}[LANGUAGE_NONE][0]['value'];
  290. $this->assertEqual($rendered_field, $expected_field);
  291. }
  292. }
  293. }
  294. /**
  295. * Tests that fields with formatters runs as expected.
  296. */
  297. public function _testFormatterSimpleFieldRender() {
  298. $view = $this->getFieldView();
  299. $view->display['default']->display_options['fields'][$this->fields[0]['field_name']]['type'] = 'text_trimmed';
  300. $view->display['default']->display_options['fields'][$this->fields[0]['field_name']]['settings'] = array(
  301. 'trim_length' => 3,
  302. );
  303. $this->executeView($view);
  304. // Take sure that the formatter works as expected.
  305. // @todo actually there should be a specific formatter.
  306. for ($i = 0; $i < 2; $i++) {
  307. $rendered_field = $view->style_plugin->get_field($i, $this->fields[0]['field_name']);
  308. $this->assertEqual(strlen($rendered_field), 3);
  309. }
  310. }
  311. public function _testMultipleFieldRender() {
  312. $view = $this->getFieldView();
  313. // Test delta limit.
  314. $view->display['default']->display_options['fields'][$this->fields[3]['field_name']]['group_rows'] = TRUE;
  315. $view->display['default']->display_options['fields'][$this->fields[3]['field_name']]['delta_limit'] = 3;
  316. $this->executeView($view);
  317. for ($i = 0; $i < 3; $i++) {
  318. $rendered_field = $view->style_plugin->get_field($i, $this->fields[3]['field_name']);
  319. $items = array();
  320. $pure_items = $this->nodes[$i]->{$this->fields[3]['field_name']}[LANGUAGE_NONE];
  321. $pure_items = array_splice($pure_items, 0, 3);
  322. foreach ($pure_items as $j => $item) {
  323. $items[] = $pure_items[$j]['value'];
  324. }
  325. $this->assertEqual($rendered_field, implode(', ', $items), 'Take sure that the amount of items are limited.');
  326. }
  327. // Test that an empty field is rendered without error.
  328. $rendered_field = $view->style_plugin->get_field(4, $this->fields[4]['field_name']);
  329. $view->destroy();
  330. // Test delta limit + offset.
  331. $view->display['default']->display_options['fields'][$this->fields[3]['field_name']]['group_rows'] = TRUE;
  332. $view->display['default']->display_options['fields'][$this->fields[3]['field_name']]['delta_limit'] = 3;
  333. $view->display['default']->display_options['fields'][$this->fields[3]['field_name']]['delta_offset'] = 1;
  334. $this->executeView($view);
  335. for ($i = 0; $i < 3; $i++) {
  336. $rendered_field = $view->style_plugin->get_field($i, $this->fields[3]['field_name']);
  337. $items = array();
  338. $pure_items = $this->nodes[$i]->{$this->fields[3]['field_name']}[LANGUAGE_NONE];
  339. $pure_items = array_splice($pure_items, 1, 3);
  340. foreach ($pure_items as $j => $item) {
  341. $items[] = $pure_items[$j]['value'];
  342. }
  343. $this->assertEqual($rendered_field, implode(', ', $items), 'Take sure that the amount of items are limited.');
  344. }
  345. $view->destroy();
  346. // Test delta limit + reverse.
  347. $view->display['default']->display_options['fields'][$this->fields[3]['field_name']]['delta_offset'] = 0;
  348. $view->display['default']->display_options['fields'][$this->fields[3]['field_name']]['group_rows'] = TRUE;
  349. $view->display['default']->display_options['fields'][$this->fields[3]['field_name']]['delta_limit'] = 3;
  350. $view->display['default']->display_options['fields'][$this->fields[3]['field_name']]['delta_reversed'] = TRUE;
  351. $this->executeView($view);
  352. for ($i = 0; $i < 3; $i++) {
  353. $rendered_field = $view->style_plugin->get_field($i, $this->fields[3]['field_name']);
  354. $items = array();
  355. $pure_items = $this->nodes[$i]->{$this->fields[3]['field_name']}[LANGUAGE_NONE];
  356. array_splice($pure_items, 0, -3);
  357. $pure_items = array_reverse($pure_items);
  358. foreach ($pure_items as $j => $item) {
  359. $items[] = $pure_items[$j]['value'];
  360. }
  361. $this->assertEqual($rendered_field, implode(', ', $items), 'Take sure that the amount of items are limited.');
  362. }
  363. $view->destroy();
  364. // Test delta first last.
  365. $view->display['default']->display_options['fields'][$this->fields[3]['field_name']]['group_rows'] = TRUE;
  366. $view->display['default']->display_options['fields'][$this->fields[3]['field_name']]['delta_limit'] = 0;
  367. $view->display['default']->display_options['fields'][$this->fields[3]['field_name']]['delta_first_last'] = TRUE;
  368. $view->display['default']->display_options['fields'][$this->fields[3]['field_name']]['delta_reversed'] = FALSE;
  369. $this->executeView($view);
  370. for ($i = 0; $i < 3; $i++) {
  371. $rendered_field = $view->style_plugin->get_field($i, $this->fields[3]['field_name']);
  372. $items = array();
  373. $pure_items = $this->nodes[$i]->{$this->fields[3]['field_name']}[LANGUAGE_NONE];
  374. $items[] = $pure_items[0]['value'];
  375. $items[] = $pure_items[4]['value'];
  376. $this->assertEqual($rendered_field, implode(', ', $items), 'Take sure that the amount of items are limited.');
  377. }
  378. $view->destroy();
  379. // Test delta limit + custom seperator.
  380. $view->display['default']->display_options['fields'][$this->fields[3]['field_name']]['delta_first_last'] = FALSE;
  381. $view->display['default']->display_options['fields'][$this->fields[3]['field_name']]['delta_limit'] = 3;
  382. $view->display['default']->display_options['fields'][$this->fields[3]['field_name']]['group_rows'] = TRUE;
  383. $view->display['default']->display_options['fields'][$this->fields[3]['field_name']]['separator'] = ':';
  384. $this->executeView($view);
  385. for ($i = 0; $i < 3; $i++) {
  386. $rendered_field = $view->style_plugin->get_field($i, $this->fields[3]['field_name']);
  387. $items = array();
  388. $pure_items = $this->nodes[$i]->{$this->fields[3]['field_name']}[LANGUAGE_NONE];
  389. $pure_items = array_splice($pure_items, 0, 3);
  390. foreach ($pure_items as $j => $item) {
  391. $items[] = $pure_items[$j]['value'];
  392. }
  393. $this->assertEqual($rendered_field, implode(':', $items), 'Take sure that the amount of items are limited.');
  394. }
  395. }
  396. protected function getFieldView() {
  397. $view = new view();
  398. $view->name = 'view_fieldapi';
  399. $view->description = '';
  400. $view->tag = 'default';
  401. $view->base_table = 'node';
  402. $view->human_name = 'view_fieldapi';
  403. $view->core = 7;
  404. $view->api_version = '3.0';
  405. $view->disabled = FALSE; /* Edit this to true to make a default view disabled initially */
  406. /* Display: Master */
  407. $handler = $view->new_display('default', 'Master', 'default');
  408. $handler->display->display_options['access']['type'] = 'perm';
  409. $handler->display->display_options['cache']['type'] = 'none';
  410. $handler->display->display_options['query']['type'] = 'views_query';
  411. $handler->display->display_options['exposed_form']['type'] = 'basic';
  412. $handler->display->display_options['pager']['type'] = 'full';
  413. $handler->display->display_options['style_plugin'] = 'default';
  414. $handler->display->display_options['row_plugin'] = 'fields';
  415. $handler->display->display_options['fields']['nid']['id'] = 'nid';
  416. $handler->display->display_options['fields']['nid']['table'] = 'node';
  417. $handler->display->display_options['fields']['nid']['field'] = 'nid';
  418. foreach ($this->fields as $key => $field) {
  419. $handler->display->display_options['fields'][$field['field_name']]['id'] = $field['field_name'];
  420. $handler->display->display_options['fields'][$field['field_name']]['table'] = 'field_data_' . $field['field_name'];
  421. $handler->display->display_options['fields'][$field['field_name']]['field'] = $field['field_name'];
  422. }
  423. return $view;
  424. }
  425. }