Version 1
[yaffs-website] / vendor / behat / mink / src / Selector / NamedSelector.php
diff --git a/vendor/behat/mink/src/Selector/NamedSelector.php b/vendor/behat/mink/src/Selector/NamedSelector.php
new file mode 100644 (file)
index 0000000..d1936cc
--- /dev/null
@@ -0,0 +1,263 @@
+<?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);
+    }
+}