og-8.x-1.x-dev/tests/src/Functional/OgAudienceHiddenGroupWidgetTest.php

tests/src/Functional/OgAudienceHiddenGroupWidgetTest.php
<?php

declare(strict_types=1);

namespace Drupal\Tests\og\Functional;

use Drupal\Component\Utility\UrlHelper;
use Drupal\node\Entity\Node;
use Drupal\node\NodeInterface;
use Drupal\og\Entity\OgRole;
use Drupal\og\Og;
use Drupal\og\OgGroupAudienceHelperInterface;
use Drupal\og\OgRoleInterface;
use Drupal\Tests\BrowserTestBase;
use Drupal\user\Entity\Role;
use Drupal\user\UserInterface;

/**
 * Adds coverage to protect hidden audience references across widgets.
 *
 * @group og
 */
class OgAudienceHiddenGroupWidgetTest extends BrowserTestBase {

  /**
   * {@inheritdoc}
   */
  protected static $modules = [
    'node',
    'og',
    'og_ui',
  ];

  /**
   * {@inheritdoc}
   */
  protected $defaultTheme = 'stark';

  /**
   * Visible group one.
   */
  protected NodeInterface $groupOne;

  /**
   * Visible group two.
   */
  protected NodeInterface $groupTwo;

  /**
   * Hidden group (no membership for editor).
   */
  protected NodeInterface $hiddenGroup;

  /**
   * Group content that references all three groups.
   */
  protected NodeInterface $groupContent;

  /**
   * Additional audience field machine name.
   */
  protected string $secondaryAudienceField = 'og_audience_secondary';

  /**
   * The two-group editor.
   */
  protected UserInterface $editor;

  /**
   * Group owner.
   */
  protected UserInterface $groupOwner;

  /**
   * OG role granting editor permissions within a group.
   */
  protected OgRoleInterface $groupEditorRole;

  /**
   * {@inheritdoc}
   */
  protected function setUp(): void {
    parent::setUp();

    $this->createContentType([
      'name' => 'Group',
      'type' => 'group',
    ]);
    $this->createContentType([
      'name' => 'Group content',
      'type' => 'group_content',
    ]);

    Og::addGroup('node', 'group');

    $primary_settings = [
      'form_display' => [
        'type' => 'entity_reference_autocomplete',
      ],
    ];
    Og::createField(OgGroupAudienceHelperInterface::DEFAULT_FIELD, 'node', 'group_content', $primary_settings);

    $secondary_settings = [
      'field_name' => $this->secondaryAudienceField,
      'field_config' => [
        'label' => 'Secondary groups audience',
      ],
      'form_display' => [
        'type' => 'options_select',
      ],
    ];
    Og::createField(OgGroupAudienceHelperInterface::DEFAULT_FIELD, 'node', 'group_content', $secondary_settings);

    $form_display = \Drupal::entityTypeManager()->getStorage('entity_form_display')->load('node.group_content.default');
    $form_display->setComponent(OgGroupAudienceHelperInterface::DEFAULT_FIELD, [
      'type' => 'entity_reference_autocomplete',
    ]);
    $form_display->setComponent($this->secondaryAudienceField, [
      'type' => 'options_select',
    ]);
    $form_display->save();

    $authenticated = Role::load('authenticated');
    $authenticated
      ->grantPermission('create group_content content')
      ->grantPermission('edit any group_content content')
      ->save();

    $this->groupOwner = $this->drupalCreateUser([], 'group owner');
    $this->editor = $this->drupalCreateUser([], 'two group editor');

    $this->groupOne = Node::create([
      'type' => 'group',
      'title' => 'Visible group one',
      'uid' => $this->groupOwner->id(),
      'status' => NodeInterface::PUBLISHED,
    ]);
    $this->groupOne->save();

    $this->groupTwo = Node::create([
      'type' => 'group',
      'title' => 'Visible group two',
      'uid' => $this->groupOwner->id(),
      'status' => NodeInterface::PUBLISHED,
    ]);
    $this->groupTwo->save();

    $this->hiddenGroup = Node::create([
      'type' => 'group',
      'title' => 'Hidden group three',
      'uid' => $this->groupOwner->id(),
      'status' => NodeInterface::PUBLISHED,
    ]);
    $this->hiddenGroup->save();

    $this->groupEditorRole = OgRole::create();
    $this->groupEditorRole
      ->setName('group_content_editor')
      ->setLabel('Group content editor')
      ->setGroupType('node')
      ->setGroupBundle('group')
      ->grantPermission('create group_content content')
      ->grantPermission('edit any group_content content')
      ->save();

    Og::createMembership($this->groupOne, $this->editor)
      ->addRole($this->groupEditorRole)
      ->save();
    Og::createMembership($this->groupTwo, $this->editor)
      ->addRole($this->groupEditorRole)
      ->save();

    $this->groupContent = Node::create([
      'type' => 'group_content',
      'title' => 'Content referencing hidden group',
      'uid' => $this->groupOwner->id(),
      'status' => NodeInterface::PUBLISHED,
      OgGroupAudienceHelperInterface::DEFAULT_FIELD => [
        ['target_id' => $this->groupOne->id()],
        ['target_id' => $this->groupTwo->id()],
        ['target_id' => $this->hiddenGroup->id()],
      ],
      $this->secondaryAudienceField => [
        ['target_id' => $this->groupOne->id()],
        ['target_id' => $this->groupTwo->id()],
        ['target_id' => $this->hiddenGroup->id()],
      ],
    ]);
    $this->groupContent->save();
  }

  /**
   * Select widget should not drop hidden groups on submit.
   */
  public function testOptionsSelectPreservesHiddenGroup(): void {
    $this->configureAudienceWidget('options_select');

    $this->drupalLogin($this->editor);
    $this->drupalGet($this->getEditPath());

    $this->assertSession()->optionExists('Groups audience', $this->groupOne->label());
    $this->assertSession()->optionExists('Groups audience', $this->groupTwo->label());
    $this->assertSession()->optionNotExists('Groups audience', $this->hiddenGroup->label());
    $this->submitForm([], 'Save');
    $this->assertSession()->pageTextContains('has been updated.');

    $this->assertHiddenGroupReferencePreserved();
  }

  /**
   * Checkbox/radio widget should not drop hidden groups on submit.
   */
  public function testOptionsButtonsPreservesHiddenGroup(): void {
    $this->configureAudienceWidget('options_buttons');

    $this->drupalLogin($this->editor);
    $this->drupalGet($this->getEditPath());

    $this->assertSession()->responseContains($this->groupOne->label());
    $this->assertSession()->responseContains($this->groupTwo->label());
    $this->assertSession()->elementNotExists('css', '#edit-og-audience input[value="' . $this->hiddenGroup->id() . '"]');

    $this->submitForm([], 'Save');
    $this->assertSession()->pageTextContains('has been updated.');

    $this->assertHiddenGroupReferencePreserved();
  }

  /**
   * Autocomplete widget should hide labels and preserve hidden groups.
   */
  public function testAutocompleteWidgetPreservesHiddenGroup(): void {
    $this->configureAudienceWidget('entity_reference_autocomplete');

    $this->drupalLogin($this->editor);
    $edit_path = $this->getEditPath();

    $this->drupalGet($edit_path);
    $this->assertSession()->responseContains($this->groupOne->label());
    $this->assertSession()->responseNotContains($this->hiddenGroup->label());

    $input = $this->assertSession()->elementExists('css', '[data-drupal-selector^="edit-og-audience"] input[data-autocomplete-path]');
    $autocomplete_path = $input->getAttribute('data-autocomplete-path');
    $this->assertNotEmpty($autocomplete_path, 'Autocomplete path is present for og_audience.');

    $this->assertHiddenGroupAbsentFromAutocomplete($autocomplete_path);

    $this->drupalGet($edit_path);
    $this->submitForm([], 'Save');
    $this->assertSession()->pageTextContains('has been updated.');

    $this->assertHiddenGroupReferencePreserved();
  }

  /**
   * Tags autocomplete widget should hide labels and preserve hidden groups.
   */
  public function testAutocompleteTagsWidgetPreservesHiddenGroup(): void {
    $this->configureAudienceWidget('entity_reference_autocomplete_tags');

    $this->drupalLogin($this->editor);
    $edit_path = $this->getEditPath();

    $this->drupalGet($edit_path);
    $this->assertSession()->responseContains($this->groupOne->label());
    $this->assertSession()->responseNotContains($this->hiddenGroup->label());

    $input = $this->assertSession()->elementExists('css', '[data-drupal-selector^="edit-og-audience"] input[data-autocomplete-path]');
    $autocomplete_path = $input->getAttribute('data-autocomplete-path');
    $this->assertNotEmpty($autocomplete_path, 'Autocomplete path is present for og_audience tags widget.');

    $this->assertHiddenGroupAbsentFromAutocomplete($autocomplete_path);

    $this->drupalGet($edit_path);
    $this->submitForm([], 'Save');
    $this->assertSession()->pageTextContains('has been updated.');

    $this->assertHiddenGroupReferencePreserved();
  }

  /**
   * Secondary audience field should hide and preserve hidden groups.
   */
  public function testSecondaryOptionsFieldPreservesHiddenGroup(): void {
    $this->configureAudienceWidget('options_select');

    $this->drupalLogin($this->editor);
    $edit_path = $this->getEditPath();

    $this->drupalGet($edit_path);
    $this->assertSession()->elementExists('css', $this->getSecondarySelectSelector());
    $this->assertSecondaryOption((int) $this->groupOne->id(), TRUE);
    $this->assertSecondaryOption((int) $this->groupTwo->id(), TRUE);
    $this->assertSecondaryOption((int) $this->hiddenGroup->id(), FALSE);

    $this->submitForm([], 'Save');
    $this->assertSession()->pageTextContains('has been updated.');

    $this->assertHiddenGroupReferencePreserved();
  }

  /**
   * Adjust the widget type for the audience field.
   */
  private function configureAudienceWidget(string $widget_type): void {
    $storage = \Drupal::entityTypeManager()->getStorage('entity_form_display');
    $form_display = $storage->load('node.group_content.default');
    $component = $form_display->getComponent(OgGroupAudienceHelperInterface::DEFAULT_FIELD);
    $component['type'] = $widget_type;
    $component['settings'] = $component['settings'] ?? [];
    $form_display->setComponent(OgGroupAudienceHelperInterface::DEFAULT_FIELD, $component);
    $form_display->save();
  }

  /**
   * Path to the edit form of the prepared group content.
   */
  private function getEditPath(): string {
    return $this->groupContent->toUrl('edit-form')->toString();
  }

  /**
   * Hidden group should remain referenced after a form submission.
   */
  private function assertHiddenGroupReferencePreserved(): void {
    $expected = array_map('intval', [
      $this->groupOne->id(),
      $this->groupTwo->id(),
      $this->hiddenGroup->id(),
    ]);
    sort($expected);

    $storage = \Drupal::entityTypeManager()->getStorage('node');
    $storage->resetCache([$this->groupContent->id()]);

    $reloaded = $storage->load($this->groupContent->id());
    self::assertInstanceOf(NodeInterface::class, $reloaded);

    $actual = array_map(static fn(array $item): int => (int) $item['target_id'], $reloaded->get(OgGroupAudienceHelperInterface::DEFAULT_FIELD)->getValue());
    sort($actual);
    self::assertSame($expected, $actual, 'Hidden group reference persists after save in primary field.');

    $secondary = array_map(static fn(array $item): int => (int) $item['target_id'], $reloaded->get($this->secondaryAudienceField)->getValue());
    sort($secondary);

    self::assertSame($expected, $secondary, 'Hidden group reference persists after save in secondary field.');
  }

  /**
   * Assert the autocomplete callback does not expose the hidden group.
   */
  private function assertHiddenGroupAbsentFromAutocomplete(string $autocomplete_path): void {
    $parsed = UrlHelper::parse($autocomplete_path);
    $query = $parsed['query'] ?? [];
    $query['q'] = $this->hiddenGroup->label();

    $this->drupalGet($parsed['path'], ['query' => $query]);
    $this->assertSession()->statusCodeEquals(200);

    $response = json_decode($this->getSession()->getPage()->getContent(), TRUE);
    self::assertIsArray($response);

    foreach ($response as $suggestion) {
      $label = (string) ($suggestion['label'] ?? '');
      self::assertStringNotContainsString($this->hiddenGroup->label(), $label);
    }
  }

  /**
   * Returns the CSS selector for the secondary audience select element.
   */
  private function getSecondarySelectSelector(): string {
    return sprintf('[data-drupal-selector^="edit-%s"]', str_replace('_', '-', $this->secondaryAudienceField));
  }

  /**
   * Assert that the secondary audience select contains or omits a group.
   */
  private function assertSecondaryOption(int $group_id, bool $should_be_present): void {
    $selector = $this->getSecondarySelectSelector() . sprintf(' option[value="%d"]', $group_id);
    if ($should_be_present) {
      $this->assertSession()->elementExists('css', $selector);
    }
    else {
      $this->assertSession()->elementNotExists('css', $selector);
    }
  }

}

Главная | Обратная связь

drupal hosting | друпал хостинг | it patrol .inc