views_fieldapi.test 18 KB

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