You are here

NamedSelector.php in Zircon Profile 8

Same filename and directory in other branches
  1. 8.0 vendor/behat/mink/src/Selector/NamedSelector.php

File

vendor/behat/mink/src/Selector/NamedSelector.php
View source
<?php

/*
 * This file is part of the Mink package.
 * (c) Konstantin Kudryashov <ever.zet@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */
namespace Behat\Mink\Selector;

use Behat\Mink\Selector\Xpath\Escaper;

/**
 * Named selectors engine. Uses registered XPath selectors to create new expressions.
 *
 * @author Konstantin Kudryashov <ever.zet@gmail.com>
 */
class NamedSelector implements SelectorInterface {
  private $replacements = array(
    // simple replacements
    '%lowercaseType%' => "translate(./@type, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz')",
    '%lowercaseRole%' => "translate(./@role, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz')",
    '%tagTextMatch%' => 'contains(normalize-space(string(.)), %locator%)',
    '%labelTextMatch%' => './@id = //label[%tagTextMatch%]/@for',
    '%idMatch%' => './@id = %locator%',
    '%valueMatch%' => 'contains(./@value, %locator%)',
    '%idOrValueMatch%' => '(%idMatch% or %valueMatch%)',
    '%idOrNameMatch%' => '(%idMatch% or ./@name = %locator%)',
    '%placeholderMatch%' => './@placeholder = %locator%',
    '%titleMatch%' => 'contains(./@title, %locator%)',
    '%altMatch%' => 'contains(./@alt, %locator%)',
    '%relMatch%' => 'contains(./@rel, %locator%)',
    '%labelAttributeMatch%' => 'contains(./@label, %locator%)',
    // complex replacements
    '%inputTypeWithoutPlaceholderFilter%' => "%lowercaseType% = 'radio' or %lowercaseType% = 'checkbox' or %lowercaseType% = 'file'",
    '%fieldFilterWithPlaceholder%' => 'self::input[not(%inputTypeWithoutPlaceholderFilter%)] | self::textarea',
    '%fieldMatchWithPlaceholder%' => '(%idOrNameMatch% or %labelTextMatch% or %placeholderMatch%)',
    '%fieldMatchWithoutPlaceholder%' => '(%idOrNameMatch% or %labelTextMatch%)',
    '%fieldFilterWithoutPlaceholder%' => 'self::input[%inputTypeWithoutPlaceholderFilter%] | self::select',
    '%buttonTypeFilter%' => "%lowercaseType% = 'submit' or %lowercaseType% = 'image' or %lowercaseType% = 'button' or %lowercaseType% = 'reset'",
    '%notFieldTypeFilter%' => "not(%buttonTypeFilter% or %lowercaseType% = 'hidden')",
    '%buttonMatch%' => '%idOrNameMatch% or %valueMatch% or %titleMatch%',
    '%linkMatch%' => '(%idMatch% or %tagTextMatch% or %titleMatch% or %relMatch%)',
    '%imgAltMatch%' => './/img[%altMatch%]',
  );
  private $selectors = array(
    'fieldset' => <<<XPATH
.//fieldset
[(%idMatch% or .//legend[%tagTextMatch%])]
XPATH
,
    'field' => <<<XPATH
.//*
[%fieldFilterWithPlaceholder%][%notFieldTypeFilter%][%fieldMatchWithPlaceholder%]
|
.//label[%tagTextMatch%]//.//*[%fieldFilterWithPlaceholder%][%notFieldTypeFilter%]
|
.//*
[%fieldFilterWithoutPlaceholder%][%notFieldTypeFilter%][%fieldMatchWithoutPlaceholder%]
|
.//label[%tagTextMatch%]//.//*[%fieldFilterWithoutPlaceholder%][%notFieldTypeFilter%]
XPATH
,
    'link' => <<<XPATH
.//a
[./@href][(%linkMatch% or %imgAltMatch%)]
|
.//*
[%lowercaseRole% = 'link'][(%idOrValueMatch% or %titleMatch% or %tagTextMatch%)]
XPATH
,
    'button' => <<<XPATH
.//input
[%buttonTypeFilter%][(%buttonMatch%)]
|
.//input
[%lowercaseType% = 'image'][%altMatch%]
|
.//button
[(%buttonMatch% or %tagTextMatch%)]
|
.//*
[%lowercaseRole% = 'button'][(%buttonMatch% or %tagTextMatch%)]
XPATH
,
    'link_or_button' => <<<XPATH
.//a
[./@href][(%linkMatch% or %imgAltMatch%)]
|
.//input
[%buttonTypeFilter%][(%idOrValueMatch% or %titleMatch%)]
|
.//input
[%lowercaseType% = 'image'][%altMatch%]
|
.//button
[(%idOrValueMatch% or %titleMatch% or %tagTextMatch%)]
|
.//*
[(%lowercaseRole% = 'button' or %lowercaseRole% = 'link')][(%idOrValueMatch% or %titleMatch% or %tagTextMatch%)]
XPATH
,
    'content' => <<<XPATH
./descendant-or-self::*
[%tagTextMatch%]
XPATH
,
    'select' => <<<XPATH
.//select
[%fieldMatchWithoutPlaceholder%]
|
.//label[%tagTextMatch%]//.//select
XPATH
,
    'checkbox' => <<<XPATH
.//input
[%lowercaseType% = 'checkbox'][%fieldMatchWithoutPlaceholder%]
|
.//label[%tagTextMatch%]//.//input[%lowercaseType% = 'checkbox']
XPATH
,
    'radio' => <<<XPATH
.//input
[%lowercaseType% = 'radio'][%fieldMatchWithoutPlaceholder%]
|
.//label[%tagTextMatch%]//.//input[%lowercaseType% = 'radio']
XPATH
,
    'file' => <<<XPATH
.//input
[%lowercaseType% = 'file'][%fieldMatchWithoutPlaceholder%]
|
.//label[%tagTextMatch%]//.//input[%lowercaseType% = 'file']
XPATH
,
    'optgroup' => <<<XPATH
.//optgroup
[%labelAttributeMatch%]
XPATH
,
    'option' => <<<XPATH
.//option
[(./@value = %locator% or %tagTextMatch%)]
XPATH
,
    'table' => <<<XPATH
.//table
[(%idMatch% or .//caption[%tagTextMatch%])]
XPATH
,
    'id' => <<<XPATH
.//*[%idMatch%]
XPATH
,
    'id_or_name' => <<<XPATH
.//*[%idOrNameMatch%]
XPATH
,
  );
  private $xpathEscaper;

  /**
   * Creates selector instance.
   */
  public function __construct() {
    $this->xpathEscaper = new Escaper();
    foreach ($this->replacements as $from => $to) {
      $this->replacements[$from] = strtr($to, $this->replacements);
    }
    foreach ($this->selectors as $alias => $selector) {
      $this->selectors[$alias] = strtr($selector, $this->replacements);
    }
  }

  /**
   * Registers new XPath selector with specified name.
   *
   * @param string $name  name for selector
   * @param string $xpath xpath expression
   */
  public function registerNamedXpath($name, $xpath) {
    $this->selectors[$name] = $xpath;
  }

  /**
   * Translates provided locator into XPath.
   *
   * @param string|array $locator selector name or array of (selector_name, locator)
   *
   * @return string
   *
   * @throws \InvalidArgumentException
   */
  public function translateToXPath($locator) {
    if (2 < count($locator)) {
      throw new \InvalidArgumentException('NamedSelector expects array(name, locator) as argument');
    }
    if (2 == count($locator)) {
      $selector = $locator[0];
      $locator = $locator[1];
    }
    else {
      $selector = (string) $locator;
      $locator = null;
    }
    if (!isset($this->selectors[$selector])) {
      throw new \InvalidArgumentException(sprintf('Unknown named selector provided: "%s". Expected one of (%s)', $selector, implode(', ', array_keys($this->selectors))));
    }
    $xpath = $this->selectors[$selector];
    if (null !== $locator) {
      $xpath = strtr($xpath, array(
        '%locator%' => $this
          ->escapeLocator($locator),
      ));
    }
    return $xpath;
  }

  /**
   * Registers a replacement in the list of replacements.
   *
   * This method must be called in the constructor before calling the parent constructor.
   *
   * @param string $from
   * @param string $to
   */
  protected function registerReplacement($from, $to) {
    $this->replacements[$from] = $to;
  }
  private function escapeLocator($locator) {

    // If the locator looks like an escaped one, don't escape it again for BC reasons.
    if (preg_match('/^\'[^\']*+\'$/', $locator) || false !== strpos($locator, '\'') && preg_match('/^"[^"]*+"$/', $locator) || 8 < ($length = strlen($locator)) && 'concat(' === substr($locator, 0, 7) && ')' === $locator[$length - 1]) {
      @trigger_error('Passing an escaped locator to the named selector is deprecated as of 1.7 and will be removed in 2.0.' . ' Pass the raw value instead.', E_USER_DEPRECATED);
      return $locator;
    }
    return $this->xpathEscaper
      ->escapeLiteral($locator);
  }

}

Classes

Namesort descending Description
NamedSelector Named selectors engine. Uses registered XPath selectors to create new expressions.