You are here

SchemaMetatagTagsTestBase.php in Schema.org Metatag 8.2

Same filename and directory in other branches
  1. 8 tests/src/Functional/SchemaMetatagTagsTestBase.php

File

tests/src/Functional/SchemaMetatagTagsTestBase.php
View source
<?php

namespace Drupal\Tests\schema_metatag\Functional;

use Drupal\Tests\BrowserTestBase;

/**
 * Base class to test all of the meta tags that are in a specific module.
 */
abstract class SchemaMetatagTagsTestBase extends BrowserTestBase {

  /**
   * The Property Type Manager.
   *
   * @var \Drupal\schema_metatag\Plugin\schema_metatag\PropertyTypeManage
   */
  protected $propertyTypeManager;

  /**
   * The Metatg Manager.
   *
   * @var \Drupal\metatag\MetatagTagPluginManager
   */
  protected $metatagTagManager;

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

  /**
   * {@inheritdoc}
   */
  protected static $modules = [
    // This is needed for the 'access content' permission.
    'node',
    // Dependencies.
    'token',
    'metatag',
    // This module.
    'schema_metatag',
    'schema_metatag_test',
  ];

  /**
   * The name of the module being tested.
   *
   * @var string
   */
  public $moduleName = '';

  /**
   * The group being tested.
   *
   * @var string
   */
  public $groupName = '';

  /**
   * All of the property types which will be tested.
   *
   * @var array
   *   A key/value array of the id of the tag and the property type used to
   *   create it.
   *
   * @see \Drupal\schema_metatag\Plugin\schema_metatag\PropertyTypeInterface.
   */
  public $propertyTypes = [];

  /**
   * Find all of the property types which will be tested.
   */
  public function getPropertyTypes() {
    $property_types = [];
    $definitions = $this
      ->metatagTagManager()
      ->getDefinitions();
    foreach ($definitions as $tag_name => $definition) {
      if ($definition['group'] == $this->groupName) {
        $property_types[$tag_name] = $definition['property_type'];
      }
    }
    return $property_types;
  }

  /**
   * Specific tree parents for tests.
   *
   * @var array
   *   A key/value array of the id of the tag and the tree parent used to
   *   create it.
   */
  public $treeParent = [];

  /**
   * Find tree parents for tests.
   */
  public function getTreeParent() {
    $property_types = [];
    $definitions = $this
      ->metatagTagManager()
      ->getDefinitions();
    foreach ($definitions as $tag_name => $definition) {
      if ($definition['group'] == $this->groupName) {
        if (!empty($definition['tree_parent'])) {
          $property_types[$tag_name] = array_shift($definition['tree_parent']);
        }
      }
    }
    return $property_types;
  }

  /**
   * The PropertyTypeManager.
   *
   * @var Drupal\schema_metatag\Plugin\schema_metatag\PropertyTypeManager
   *   The Property Type Manager service.
   */
  public function propertyTypeManager() {
    return $this->propertyTypeManager;
  }

  /**
   * The Metatag Tag Manager.
   *
   * @var \Drupal\metatag\MetatagTagPluginManager
   *   The Metatag Tag Manager service.
   */
  public function metatagTagManager() {
    return $this->metatagTagManager;
  }

  /**
   * Convert the tag_name into the camelCase key used in the JSON array.
   *
   * @param string $tag_name
   *   The name of the tag.
   *
   * @return string
   *   The key used in the JSON array for this tag.
   */
  public function getKey($tag_name) {
    $key = str_replace($this->moduleName . '_', '', $tag_name);
    $parts = explode('_', $key);
    foreach ($parts as $i => $part) {
      $parts[$i] = $i > 0 ? ucfirst($part) : $part;
    }
    $key = implode($parts);
    if (in_array($key, [
      'type',
      'id',
    ])) {
      $key = '@' . $key;
    }
    return $key;
  }

  /**
   * {@inheritdoc}
   */
  protected function setUp() {
    parent::setUp();
    $this->propertyTypeManager = \Drupal::service('plugin.manager.schema_property_type');
    $this->metatagTagManager = \Drupal::service('plugin.manager.metatag.tag');
    $this->propertyTypes = $this
      ->getPropertyTypes();
    $this->treeParent = $this
      ->getTreeParent();

    // Initiate session with a user who can manage metatags and access content.
    $permissions = [
      'administer site configuration',
      'administer meta tags',
      'access content',
    ];
    $account = $this
      ->drupalCreateUser($permissions);
    $this
      ->drupalLogin($account);

    // Create a content type to test with.
    $this
      ->createContentType([
      'type' => 'page',
    ]);
    $this
      ->drupalCreateNode([
      'title' => 'Node 1!',
      'type' => 'page',
      'promote' => 1,
    ]);

    // Make sure the home page is a valid route in case we want to test it.
    $this
      ->config('system.site')
      ->set('page.front', '/node')
      ->save();
    $this
      ->clear();
  }

  /**
   * Confirm that tags can be saved and that the output of each tag is correct.
   */
  public function testTagsInputOutput() {
    if (empty($this->propertyTypes)) {
      $this
        ->markTestSkipped('Not enough information to test.');
      return;
    }
    $paths = $this
      ->getPaths();
    foreach ($paths as $item) {
      list($config_path, $rendered_path, $save_message) = $item;

      // Load the config page.
      $this
        ->drupalGet($config_path);
      $this
        ->assertSession()
        ->statusCodeEquals(200);
      $this
        ->assertSession()
        ->elementExists('xpath', '//input[@type="submit"][@value="Save"]');

      // Configure all the tag values and post the results.
      $expected_output_values = $raw_values = $form_values = [];
      $form_values = [];
      foreach ($this->propertyTypes as $tag_name => $property_type) {

        // Transform the tag_name to the camelCase key used in the form.
        $key = $this
          ->getKey($tag_name);

        // Find the name of the property type and use it to
        // identify a valid test value, and determine what the rendered output
        // should look like. Store the rendered value so we can compare it to
        // the output. Store the raw value so we can check that it exists in the
        // config form.
        $property_plugin = $this
          ->propertyTypeManager()
          ->createInstance($property_type);
        $type = array_key_exists($tag_name, $this->treeParent) ? $this->treeParent[$tag_name] : $property_plugin
          ->getTreeParent();
        $test_type = is_array($type) ? array_shift($type) : $type;
        $test_value = $property_plugin
          ->testValue($test_type);

        // Store the input value.
        $raw_values[$tag_name] = $test_value;

        // Adjust the input value as necessary to transform it to the
        // expected output value, and store that.
        $processed_value = $property_plugin
          ->processedTestValue($test_value);
        $expected_output_values[$key] = $property_plugin
          ->outputValue($processed_value);

        // Rewrite the test values to match the way the form elements are
        // structured.
        // @TODO There is probably some way to write this as a recursive
        // function that will go more than three levels deep, but for now this
        // is enough.
        if (!is_array($test_value)) {
          $form_values[$tag_name] = $test_value;
        }
        else {
          foreach ($test_value as $key => $value) {
            if (is_array($value)) {
              foreach ($value as $key2 => $value2) {
                if (is_array($value2)) {
                  foreach ($value2 as $key3 => $value3) {
                    if (is_array($value3)) {
                      foreach ($value3 as $key4 => $value4) {
                        $keystring = implode('][', [
                          $key,
                          $key2,
                          $key3,
                          $key4,
                        ]);
                        $form_values[$tag_name . '[' . $keystring . ']'] = $value4;
                      }
                    }
                    else {
                      $keystring = implode('][', [
                        $key,
                        $key2,
                        $key3,
                      ]);
                      $form_values[$tag_name . '[' . $keystring . ']'] = $value3;
                    }
                  }
                }
                else {
                  $keystring = implode('][', [
                    $key,
                    $key2,
                  ]);
                  $form_values[$tag_name . '[' . $keystring . ']'] = $value2;
                }
              }
            }
            else {
              $keystring = implode('][', [
                $key,
              ]);
              $form_values[$tag_name . '[' . $keystring . ']'] = $value;
            }
          }
        }
      }
      $this
        ->drupalPostForm(NULL, $form_values, 'Save');
      $this
        ->assertSession()
        ->pageTextContains($save_message, 'Configuration successfully posted.');

      // Load the config page to confirm the settings got saved.
      $this
        ->drupalGet($config_path);
      foreach ($this->propertyTypes as $tag_name => $property_type) {

        // Check that simple string test values exist in the form to see that
        // form values were saved accurately. Don't try to recurse through all
        // arrays, more complicated values will be tested from the JSON output
        // they create.
        if (is_string($raw_values[$tag_name])) {
          $string = strtr('//*[@name=":tag_name"]', [
            ':tag_name' => $tag_name,
          ]);
          $elements = $this
            ->xpath($string);
          $value = count($elements) ? $elements[0]
            ->getValue() : NULL;
          $this
            ->assertEquals($value, $raw_values[$tag_name]);
        }
      }

      // Load the rendered page to see if the JSON-LD is displayed correctly.
      $this
        ->drupalGet($rendered_path);
      $this
        ->assertSession()
        ->statusCodeEquals(200);

      // Make sure JSON-LD is present and can be decoded.
      $this
        ->assertSession()
        ->elementExists('xpath', '//script[@type="application/ld+json"]');
      $elements = $this
        ->xpath('//script[@type="application/ld+json"]');
      $this
        ->assertEquals(count($elements), 1);
      $json = json_decode($elements[0]
        ->getHtml(), TRUE);
      $this
        ->assertNotEmpty($json);
      $output_values = $json['@graph'][0];

      // Compare input and output values.
      foreach ($this->propertyTypes as $tag_name => $property_type) {
        $key = $this
          ->getKey($tag_name);
        $this
          ->assertEquals($output_values[$key], $expected_output_values[$key]);
      }
    }
    $this
      ->drupalLogout();
  }

  /**
   * Paths to test.
   *
   * Tags that need to be tested on other paths can extend this method.
   *
   * [$config_path, $rendered_path, $message]
   *
   * Examples:
   * // Global options.
   * [
   *   'admin/config/search/metatag/global',
   *   'somepath/that/must/exist',
   *   'Saved the Global Metatag defaults.',
   * ],
   * // The front page.
   * [
   *   'admin/config/search/metatag/front',
   *   '<front>',
   *   'Saved the Front page Metatag defaults.',
   * ],
   */
  public function getPaths() {
    return [
      // The node page.
      [
        'admin/config/search/metatag/node',
        'node/1',
        'Saved the Content Metatag defaults',
      ],
    ];
  }

  /**
   * A way to clear caches.
   */
  protected function clear() {
    $this
      ->rebuildContainer();
  }

}

Classes

Namesort descending Description
SchemaMetatagTagsTestBase Base class to test all of the meta tags that are in a specific module.