You are here

public function ViewsTest::testViewsAdmin in Search API 8

Tests the Views admin UI and field handlers.

File

tests/src/Functional/ViewsTest.php, line 677

Class

ViewsTest
Tests the Views integration of the Search API.

Namespace

Drupal\Tests\search_api\Functional

Code

public function testViewsAdmin() {

  // Add a field from a related entity to the index to test whether it gets
  // displayed correctly.

  /** @var \Drupal\search_api\IndexInterface $index */
  $index = Index::load($this->indexId);
  $datasource_id = 'entity:entity_test_mulrev_changed';
  $field = \Drupal::getContainer()
    ->get('search_api.fields_helper')
    ->createField($index, 'author', [
    'label' => 'Author name',
    'type' => 'string',
    'datasource_id' => $datasource_id,
    'property_path' => 'user_id:entity:name',
  ]);
  $index
    ->addField($field);
  $field = \Drupal::getContainer()
    ->get('search_api.fields_helper')
    ->createField($index, 'rendered_item', [
    'label' => 'Rendered HTML output',
    'type' => 'text',
    'property_path' => 'rendered_item',
    'configuration' => [
      'roles' => [
        AccountInterface::ANONYMOUS_ROLE,
      ],
      'view_mode' => [
        $datasource_id => [
          'article' => 'full',
          'item' => 'full',
        ],
      ],
    ],
  ]);
  $index
    ->addField($field);
  $index
    ->save();

  // Add some Dutch nodes.
  foreach ([
    1,
    2,
    3,
    4,
    5,
  ] as $id) {
    $entity = EntityTestMulRevChanged::load($id);
    $entity = $entity
      ->addTranslation('nl', [
      'body' => "dutch node {$id}",
      'category' => "dutch category {$id}",
      'keywords' => [
        "dutch {$id} A",
        "dutch {$id} B",
      ],
    ]);
    $entity
      ->save();
  }
  $this->entities = EntityTestMulRevChanged::loadMultiple();
  $this
    ->indexItems($this->indexId);

  // For viewing the user name and roles of the user associated with test
  // entities, the logged-in user needs to have the permission to administer
  // both users and permissions.
  $permissions = [
    'administer search_api',
    'access administration pages',
    'administer views',
    'administer users',
    'administer permissions',
  ];
  $admin_user = $this
    ->drupalCreateUser($permissions);
  $this
    ->drupalLogin($admin_user);
  $this
    ->drupalGet('admin/structure/views/view/search_api_test_view/edit/page_1');
  $this
    ->assertSession()
    ->statusCodeEquals(200);

  // Set the user IDs associated with our test entities.
  $users[] = $this
    ->createUser();
  $users[] = $this
    ->createUser();
  $users[] = $this
    ->createUser();
  $this->entities[1]
    ->setOwnerId($users[0]
    ->id())
    ->save();
  $this->entities[2]
    ->setOwnerId($users[0]
    ->id())
    ->save();
  $this->entities[3]
    ->setOwnerId($users[1]
    ->id())
    ->save();
  $this->entities[4]
    ->setOwnerId($users[1]
    ->id())
    ->save();
  $this->entities[5]
    ->setOwnerId($users[2]
    ->id())
    ->save();

  // Switch to "Table" format.
  $this
    ->clickLink('Unformatted list');
  $this
    ->assertSession()
    ->statusCodeEquals(200);
  $edit = [
    'style[type]' => 'table',
  ];
  $this
    ->submitForm($edit, 'Apply');
  $this
    ->assertSession()
    ->statusCodeEquals(200);
  $this
    ->submitForm([], 'Apply');
  $this
    ->assertSession()
    ->statusCodeEquals(200);

  // Add the "User ID" relationship.
  $this
    ->clickLink('Add relationships');
  $edit = [
    'name[search_api_datasource_database_search_index_entity_entity_test_mulrev_changed.user_id]' => 'search_api_datasource_database_search_index_entity_entity_test_mulrev_changed.user_id',
  ];
  $this
    ->submitForm($edit, 'Add and configure relationships');
  $this
    ->submitForm([], 'Apply');

  // Add new fields. First check that the listing seems correct.
  $this
    ->clickLink('Add fields');
  $this
    ->assertSession()
    ->statusCodeEquals(200);
  $this
    ->assertSession()
    ->pageTextContains('Test entity - revisions and data table datasource');
  $this
    ->assertSession()
    ->pageTextContains('Authored on');
  $this
    ->assertSession()
    ->pageTextContains('Body (indexed field)');
  $this
    ->assertSession()
    ->pageTextContains('Index Test index');
  $this
    ->assertSession()
    ->pageTextContains('Item ID');
  $this
    ->assertSession()
    ->pageTextContains('Excerpt');
  $this
    ->assertSession()
    ->pageTextContains('The search result excerpted to show found search terms');
  $this
    ->assertSession()
    ->pageTextContains('Relevance');
  $this
    ->assertSession()
    ->pageTextContains('The relevance of this search result with respect to the query');
  $this
    ->assertSession()
    ->pageTextContains('Language code');
  $this
    ->assertSession()
    ->pageTextContains('The user language code.');
  $this
    ->assertSession()
    ->pageTextContains('(No description available)');
  $this
    ->assertSession()
    ->pageTextNotContains('Error: missing help');

  // Then add some fields.
  $fields = [
    'views.counter',
    'search_api_datasource_database_search_index_entity_entity_test_mulrev_changed.id',
    'search_api_index_database_search_index.search_api_datasource',
    'search_api_datasource_database_search_index_entity_entity_test_mulrev_changed.body',
    'search_api_index_database_search_index.category',
    'search_api_index_database_search_index.keywords',
    'search_api_datasource_database_search_index_entity_entity_test_mulrev_changed.user_id',
    'search_api_entity_user.name',
    'search_api_index_database_search_index.author',
    'search_api_entity_user.roles',
    'search_api_index_database_search_index.rendered_item',
    'search_api_index_database_search_index.search_api_rendered_item',
  ];
  $edit = [];
  foreach ($fields as $field) {
    $edit["name[{$field}]"] = $field;
  }
  $this
    ->submitForm($edit, 'Add and configure fields');
  $this
    ->assertSession()
    ->statusCodeEquals(200);

  // @todo For some strange reason, the "roles" field form is not included
  //   automatically in the series of field forms shown to us by Views. Deal
  //   with this graciously (since it's not really our fault, I hope), but it
  //   would be great to have this working normally.
  $get_field_id = function ($key) {
    return Utility::splitPropertyPath($key, TRUE, '.')[1];
  };
  $fields = array_map($get_field_id, $fields);
  $fields = array_combine($fields, $fields);
  for ($i = 0; $i < count($fields); ++$i) {
    $field = $this
      ->submitFieldsForm();
    if (!$field) {
      break;
    }
    unset($fields[$field]);
  }
  foreach ($fields as $field) {
    $this
      ->drupalGet('admin/structure/views/nojs/handler/search_api_test_view/page_1/field/' . $field);
    $this
      ->submitFieldsForm();
  }

  // Add click sorting for all fields where this is possible.
  $this
    ->clickLink('Settings', 0);
  $edit = [
    'style_options[info][search_api_datasource][sortable]' => 1,
    'style_options[info][category][sortable]' => 1,
    'style_options[info][keywords][sortable]' => 1,
  ];
  $this
    ->submitForm($edit, 'Apply');

  // Add a filter for the "Name" field.
  $this
    ->clickLink('Add filter criteria');
  $edit = [
    'name[search_api_index_database_search_index.name]' => 'search_api_index_database_search_index.name',
  ];
  $this
    ->submitForm($edit, 'Add and configure filter criteria');
  $edit = [
    'options[expose_button][checkbox][checkbox]' => 1,
  ];
  $this
    ->submitForm($edit, 'Expose filter');
  $this
    ->submitPluginForm([]);

  // Add a "Search: Fulltext search" filter.
  $this
    ->clickLink('Add filter criteria');
  $edit = [
    'name[search_api_index_database_search_index.search_api_fulltext]' => 'search_api_index_database_search_index.search_api_fulltext',
  ];
  $this
    ->submitForm($edit, 'Add and configure filter criteria');
  $this
    ->assertSession()
    ->pageTextNotContains('No UI parse mode');
  $edit = [
    'options[expose_button][checkbox][checkbox]' => 1,
  ];
  $this
    ->submitForm($edit, 'Expose filter');
  $this
    ->submitPluginForm([]);

  // Save the view.
  $this
    ->submitForm([], 'Save');
  $this
    ->assertSession()
    ->statusCodeEquals(200);

  // Check the results.
  $this
    ->drupalGet('search-api-test');
  $this
    ->assertSession()
    ->statusCodeEquals(200);
  $fields = [
    'search_api_datasource',
    'id',
    'body',
    'category',
    'keywords',
    'user_id',
    'user_id:name',
    'user_id:roles',
    'rendered_item',
    'search_api_rendered_item',
  ];
  $rendered_item_fields = [
    'rendered_item',
    'search_api_rendered_item',
  ];
  foreach ($this->entities as $id => $entity) {
    foreach ($fields as $field) {
      $field_entity = $entity;
      while (strpos($field, ':')) {
        list($direct_property, $field) = Utility::splitPropertyPath($field, FALSE);
        if (empty($field_entity->{$direct_property}[0]->entity)) {
          continue 2;
        }
        $field_entity = $field_entity->{$direct_property}[0]->entity;
      }

      // Check that both the English and the Dutch entity are present in the
      // results, with their correct field values.
      $entities = [
        $field_entity,
      ];
      if ($field_entity
        ->hasTranslation('nl')) {
        $entities[] = $field_entity
          ->getTranslation('nl');
      }
      foreach ($entities as $i => $field_entity) {
        if ($field === 'search_api_datasource') {
          $data = [
            $datasource_id,
          ];
        }
        elseif (in_array($field, $rendered_item_fields)) {
          $view_mode = $field === 'rendered_item' ? 'full' : 'teaser';
          $data = [
            $view_mode,
          ];
        }
        else {
          $data = \Drupal::getContainer()
            ->get('search_api.fields_helper')
            ->extractFieldValues($field_entity
            ->get($field));
          if (!$data) {
            $data = [
              '[EMPTY]',
            ];
          }
        }
        $row_num = 2 * $id + $i - 1;
        $prefix = "#{$row_num} [{$field}] ";
        $text = $prefix . implode("|{$prefix}", $data);
        $this
          ->assertSession()
          ->pageTextContains($text);

        // Special case for field "author", which duplicates content of
        // "name".
        if ($field === 'name') {
          $text = str_replace('[name]', '[author]', $text);
          $this
            ->assertSession()
            ->pageTextContains($text);
        }
      }
    }
  }

  // Check whether the expected retrieved fields were listed on the page.
  // These are only "keywords" and "rendered_item", since only fields that
  // correspond to an indexed field are included (not when a field is added
  // via the datasource table), and only if "Use entity field rendering" is
  // disabled.
  // @see search_api_test_views_search_api_query_alter()
  $retrieved_fields = [
    'keywords',
    'rendered_item',
  ];
  foreach ($retrieved_fields as $field_id) {
    $this
      ->assertSession()
      ->pageTextContains("'{$field_id}'");
  }

  // Check that click-sorting works correctly.
  $options = [
    'query' => [
      'order' => 'category',
      'sort' => 'asc',
    ],
  ];
  $this
    ->drupalGet('search-api-test', $options);
  $this
    ->assertSession()
    ->statusCodeEquals(200);
  $ordered_categories = [
    '[EMPTY]',
    'article_category',
    'article_category',
    'dutch category 1',
    'dutch category 2',
    'dutch category 3',
    'dutch category 4',
    'dutch category 5',
    'item_category',
    'item_category',
  ];
  foreach ($ordered_categories as $i => $category) {
    ++$i;
    $this
      ->assertSession()
      ->pageTextContains("#{$i} [category] {$category}");
  }
  $options['query']['sort'] = 'desc';
  $this
    ->drupalGet('search-api-test', $options);
  $this
    ->assertSession()
    ->statusCodeEquals(200);
  foreach (array_reverse($ordered_categories) as $i => $category) {
    ++$i;
    $this
      ->assertSession()
      ->pageTextContains("#{$i} [category] {$category}");
  }

  // Check the results with an anonymous visitor. All "name" fields should be
  // empty.
  $this
    ->drupalLogout();
  $this
    ->drupalGet('search-api-test');
  $this
    ->assertSession()
    ->statusCodeEquals(200);
  $html = $this
    ->getSession()
    ->getPage()
    ->getContent();
  $this
    ->assertEquals(10, substr_count($html, '[name] [EMPTY]'));

  // Set "Skip access checks" on the "user_id" relationship and check again.
  // The "name" field should now be listed regardless.
  $this
    ->drupalLogin($admin_user);
  $this
    ->drupalGet('admin/structure/views/nojs/handler/search_api_test_view/page_1/relationship/user_id');
  $this
    ->submitForm([
    'options[skip_access]' => 1,
  ], 'Apply');
  $this
    ->submitForm([], 'Save');
  $this
    ->assertSession()
    ->statusCodeEquals(200);
  $this
    ->drupalLogout();
  $this
    ->drupalGet('search-api-test');
  $this
    ->assertSession()
    ->statusCodeEquals(200);
  $this
    ->assertSession()
    ->pageTextNotContains('[name] [EMPTY]');

  // Run regression tests.
  $this
    ->drupalLogin($admin_user);
  $this
    ->adminUiRegressionTests();
}