3 namespace Drupal\Tests\views\Functional\Plugin;
5 use Drupal\language\Entity\ConfigurableLanguage;
6 use Drupal\Tests\views\Functional\ViewTestBase;
7 use Drupal\views\Views;
8 use Drupal\views_test_data\Plugin\views\display\DisplayTest as DisplayTestPlugin;
11 * Tests the basic display plugin.
15 class DisplayTest extends ViewTestBase {
18 * Views used by this test.
22 public static $testViews = ['test_filter_groups', 'test_get_attach_displays', 'test_view', 'test_display_more', 'test_display_invalid', 'test_display_empty', 'test_exposed_relationship_admin_ui'];
29 public static $modules = ['views_ui', 'node', 'block'];
31 protected function setUp($import_test_views = TRUE) {
34 $this->enableViewsTestModule();
36 $this->adminUser = $this->drupalCreateUser(['administer views']);
37 $this->drupalLogin($this->adminUser);
40 for ($i = 0; $i <= 10; $i++) {
41 $this->drupalCreateNode(['promote' => TRUE]);
46 * Tests the display test plugin.
48 * @see \Drupal\views_test_data\Plugin\views\display\DisplayTest
50 public function testDisplayPlugin() {
51 /** @var \Drupal\Core\Render\RendererInterface $renderer */
52 $renderer = $this->container->get('renderer');
53 $view = Views::getView('test_view');
55 // Add a new 'display_test' display and test it's there.
56 $view->storage->addDisplay('display_test');
57 $displays = $view->storage->get('display');
59 $this->assertTrue(isset($displays['display_test_1']), 'Added display has been assigned to "display_test_1"');
61 // Check the display options are like expected.
63 'display_options' => [],
64 'display_plugin' => 'display_test',
65 'id' => 'display_test_1',
66 'display_title' => 'Display test',
69 $this->assertEqual($displays['display_test_1'], $options);
71 // Add another one to ensure that position is counted up.
72 $view->storage->addDisplay('display_test');
73 $displays = $view->storage->get('display');
75 'display_options' => [],
76 'display_plugin' => 'display_test',
77 'id' => 'display_test_2',
78 'display_title' => 'Display test 2',
81 $this->assertEqual($displays['display_test_2'], $options);
83 // Move the second display before the first one in order to test custom
85 $displays['display_test_1']['position'] = 2;
86 $displays['display_test_2']['position'] = 1;
87 $view->storage->set('display', $displays);
90 $view->setDisplay('display_test_1');
92 $this->assertTrue($view->display_handler instanceof DisplayTestPlugin, 'The correct display handler instance is on the view object.');
94 // Check the test option.
95 $this->assertIdentical($view->display_handler->getOption('test_option'), '');
97 $style = $view->display_handler->getOption('style');
98 $style['type'] = 'test_style';
99 $view->display_handler->setOption('style', $style);
100 $view->initDisplay();
102 $view->style_plugin->setUsesRowPlugin(FALSE);
104 $output = $view->preview();
105 $output = $renderer->renderRoot($output);
107 $this->assertTrue(strpos($output, '<h1></h1>') !== FALSE, 'An empty value for test_option found in output.');
109 // Change this option and check the title of out output.
110 $view->display_handler->overrideOption('test_option', 'Test option title');
113 $output = $view->preview();
114 $output = $renderer->renderRoot($output);
116 // Test we have our custom <h1> tag in the output of the view.
117 $this->assertTrue(strpos($output, '<h1>Test option title</h1>') !== FALSE, 'The test_option value found in display output title.');
119 // Test that the display category/summary is in the UI.
120 $this->drupalGet('admin/structure/views/view/test_view/edit/display_test_1');
121 $this->assertText('Display test settings');
122 // Ensure that the order is as expected.
123 $result = $this->xpath('//ul[@id="views-display-menu-tabs"]/li/a/child::text()');
124 $this->assertEqual($result[0]->getText(), 'Display test 2');
125 $this->assertEqual($result[1]->getText(), 'Display test');
127 $this->clickLink('Test option title');
129 $test_option = $this->randomString();
130 $this->drupalPostForm(NULL, ['test_option' => $test_option], t('Apply'));
132 // Check the new value has been saved by checking the UI summary text.
133 $this->drupalGet('admin/structure/views/view/test_view/edit/display_test_1');
134 $this->assertLink($test_option);
136 // Test the enable/disable status of a display.
137 $view->display_handler->setOption('enabled', FALSE);
138 $this->assertFalse($view->display_handler->isEnabled(), 'Make sure that isEnabled returns FALSE on a disabled display.');
139 $view->display_handler->setOption('enabled', TRUE);
140 $this->assertTrue($view->display_handler->isEnabled(), 'Make sure that isEnabled returns TRUE on a disabled display.');
144 * Tests the overriding of filter_groups.
146 public function testFilterGroupsOverriding() {
147 $view = Views::getView('test_filter_groups');
148 $view->initDisplay();
150 // mark is as overridden, yes FALSE, means overridden.
151 $view->displayHandlers->get('page')->setOverride('filter_groups', FALSE);
152 $this->assertFalse($view->displayHandlers->get('page')->isDefaulted('filter_groups'), "Make sure that 'filter_groups' is marked as overridden.");
153 $this->assertFalse($view->displayHandlers->get('page')->isDefaulted('filters'), "Make sure that 'filters'' is marked as overridden.");
157 * Tests the getAttachedDisplays method.
159 public function testGetAttachedDisplays() {
160 $view = Views::getView('test_get_attach_displays');
162 // Both the feed_1 and the feed_2 display are attached to the page display.
163 $view->setDisplay('page_1');
164 $this->assertEqual($view->display_handler->getAttachedDisplays(), ['feed_1', 'feed_2']);
166 $view->setDisplay('feed_1');
167 $this->assertEqual($view->display_handler->getAttachedDisplays(), []);
171 * Tests the readmore validation.
173 public function testReadMoreNoDisplay() {
174 $view = Views::getView('test_display_more');
175 // Confirm that the view validates when there is a page display.
176 $errors = $view->validate();
177 $this->assertTrue(empty($errors), 'More link validation has no errors.');
179 // Confirm that the view does not validate when the page display is disabled.
180 $view->setDisplay('page_1');
181 $view->display_handler->setOption('enabled', FALSE);
182 $view->setDisplay('default');
183 $errors = $view->validate();
184 $this->assertTrue(!empty($errors), 'More link validation has some errors.');
185 $this->assertEqual($errors['default'][0], 'Display "Master" uses a "more" link but there are no displays it can link to. You need to specify a custom URL.', 'More link validation has the right error.');
187 // Confirm that the view does not validate when the page display does not exist.
188 $view = Views::getView('test_view');
189 $view->setDisplay('default');
190 $view->display_handler->setOption('use_more', 1);
191 $errors = $view->validate();
192 $this->assertTrue(!empty($errors), 'More link validation has some errors.');
193 $this->assertEqual($errors['default'][0], 'Display "Master" uses a "more" link but there are no displays it can link to. You need to specify a custom URL.', 'More link validation has the right error.');
197 * Tests invalid display plugins.
199 public function testInvalidDisplayPlugins() {
200 $this->drupalGet('test_display_invalid');
201 $this->assertResponse(200);
203 // Change the page plugin id to an invalid one. Bypass the entity system
204 // so no menu rebuild was executed (so the path is still available).
205 $config = $this->config('views.view.test_display_invalid');
206 $config->set('display.page_1.display_plugin', 'invalid');
209 $this->drupalGet('test_display_invalid');
210 $this->assertResponse(200);
211 $this->assertText('The "invalid" plugin does not exist.');
213 // Rebuild the router, and ensure that the path is not accessible anymore.
214 views_invalidate_cache();
215 \Drupal::service('router.builder')->rebuildIfNeeded();
217 $this->drupalGet('test_display_invalid');
218 $this->assertResponse(404);
220 // Change the display plugin ID back to the correct ID.
221 $config = $this->config('views.view.test_display_invalid');
222 $config->set('display.page_1.display_plugin', 'page');
225 // Place the block display.
226 $block = $this->drupalPlaceBlock('views_block:test_display_invalid-block_1', ['label' => 'Invalid display']);
228 $this->drupalGet('<front>');
229 $this->assertResponse(200);
230 $result = $this->xpath('//div[@id = :id]', [':id' => 'block-' . $block->id()]);
231 $this->assertEquals(1, count($result));
233 // Change the block plugin ID to an invalid one.
234 $config = $this->config('views.view.test_display_invalid');
235 $config->set('display.block_1.display_plugin', 'invalid');
238 // Test the page is still displayed, the block not present, and has the
239 // plugin warning message.
240 $this->drupalGet('<front>');
241 $this->assertResponse(200);
242 $this->assertText('The "invalid" plugin does not exist.');
243 $result = $this->xpath('//div[@id = :id]', [':id' => 'block-' . $block->id()]);
244 $this->assertEquals(0, count($result));
248 * Tests display validation when a required relationship is missing.
250 public function testMissingRelationship() {
251 $view = Views::getView('test_exposed_relationship_admin_ui');
253 // Remove the relationship that is not used by other handlers.
254 $view->removeHandler('default', 'relationship', 'uid_1');
255 $errors = $view->validate();
256 // Check that no error message is shown.
257 $this->assertTrue(empty($errors['default']), 'No errors found when removing unused relationship.');
259 // Unset cached relationships (see DisplayPluginBase::getHandlers())
260 unset($view->display_handler->handlers['relationship']);
262 // Remove the relationship used by other handlers.
263 $view->removeHandler('default', 'relationship', 'uid');
265 $errors = $view->validate();
266 // Check that the error messages are shown.
267 $this->assertTrue(count($errors['default']) == 2, 'Error messages found for required relationship');
268 $this->assertEqual($errors['default'][0], t('The %handler_type %handler uses a relationship that has been removed.', ['%handler_type' => 'field', '%handler' => 'User: Last login']));
269 $this->assertEqual($errors['default'][1], t('The %handler_type %handler uses a relationship that has been removed.', ['%handler_type' => 'field', '%handler' => 'User: Created']));
273 * Tests the outputIsEmpty method on the display.
275 public function testOutputIsEmpty() {
276 $view = Views::getView('test_display_empty');
277 $this->executeView($view);
278 $this->assertTrue(count($view->result) > 0, 'Ensure the result of the view is not empty.');
279 $this->assertFalse($view->display_handler->outputIsEmpty(), 'Ensure the view output is marked as not empty.');
282 // Add a filter, so the view result is empty.
283 $view->setDisplay('default');
285 'table' => 'views_test_data',
288 'value' => ['value' => 7297],
290 $view->setHandler('default', 'filter', 'id', $item);
291 $this->executeView($view);
292 $this->assertFalse(count($view->result), 'Ensure the result of the view is empty.');
293 $this->assertFalse($view->display_handler->outputIsEmpty(), 'Ensure the view output is marked as not empty, because the empty text still appears.');
296 // Remove the empty area, but mark the header area to still appear.
297 $view->removeHandler('default', 'empty', 'area');
298 $item = $view->getHandler('default', 'header', 'area');
299 $item['empty'] = TRUE;
300 $view->setHandler('default', 'header', 'area', $item);
301 $this->executeView($view);
302 $this->assertFalse(count($view->result), 'Ensure the result of the view is empty.');
303 $this->assertFalse($view->display_handler->outputIsEmpty(), 'Ensure the view output is marked as not empty, because the header text still appears.');
306 // Hide the header on empty results.
307 $item = $view->getHandler('default', 'header', 'area');
308 $item['empty'] = FALSE;
309 $view->setHandler('default', 'header', 'area', $item);
310 $this->executeView($view);
311 $this->assertFalse(count($view->result), 'Ensure the result of the view is empty.');
312 $this->assertTrue($view->display_handler->outputIsEmpty(), 'Ensure the view output is marked as empty.');
316 * Test translation rendering settings based on entity translatability.
318 public function testTranslationSetting() {
319 \Drupal::service('module_installer')->install(['file']);
320 \Drupal::service('router.builder')->rebuild();
322 // By default there should be no language settings.
323 $this->checkTranslationSetting();
324 \Drupal::service('module_installer')->install(['language']);
326 // Enabling the language module should not make a difference.
327 $this->checkTranslationSetting();
329 // Making the site multilingual should let translatable entity types support
330 // translation rendering.
331 ConfigurableLanguage::createFromLangcode('it')->save();
332 $this->checkTranslationSetting(TRUE);
336 * Asserts a node and a file based view for the translation setting.
338 * The file based view should never expose that setting. The node based view
339 * should if the site is multilingual.
341 * @param bool $expected_node_translatability
342 * Whether the node based view should be expected to support translation
345 protected function checkTranslationSetting($expected_node_translatability = FALSE) {
346 $not_supported_text = 'The view is not based on a translatable entity type or the site is not multilingual.';
347 $supported_text = 'All content that supports translations will be displayed in the selected language.';
349 $this->drupalGet('admin/structure/views/nojs/display/content/page_1/rendering_language');
350 if ($expected_node_translatability) {
351 $this->assertNoText($not_supported_text);
352 $this->assertText($supported_text);
355 $this->assertText($not_supported_text);
356 $this->assertNoText($supported_text);
359 $this->drupalGet('admin/structure/views/nojs/display/files/page_1/rendering_language');
360 $this->assertText($not_supported_text);
361 $this->assertNoText($supported_text);