diff --git a/CHANGES.md b/CHANGES.md index b40f35551..1829dd9e3 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,3 +1,11 @@ +[Unreleased] +================== + +* Move selector and replacement declaration to dedicated functions. + Note: If you were previously extending the NamedSelector class and defining overrides to existing values, these will + need to be moved to the relevant `getRawSelectors()` or `getRawReplacements()` function as appropriate. + See #815 for further information on this change. + 1.9.0 / 2021-10-11 ================== diff --git a/src/Selector/ExactNamedSelector.php b/src/Selector/ExactNamedSelector.php index 03315a9cf..8b86a2acc 100644 --- a/src/Selector/ExactNamedSelector.php +++ b/src/Selector/ExactNamedSelector.php @@ -15,15 +15,15 @@ */ class ExactNamedSelector extends NamedSelector { - public function __construct() + protected function getRawReplacements() { - $this->registerReplacement('%tagTextMatch%', 'normalize-space(string(.)) = %locator%'); - $this->registerReplacement('%valueMatch%', './@value = %locator%'); - $this->registerReplacement('%titleMatch%', './@title = %locator%'); - $this->registerReplacement('%altMatch%', './@alt = %locator%'); - $this->registerReplacement('%relMatch%', './@rel = %locator%'); - $this->registerReplacement('%labelAttributeMatch%', './@label = %locator%'); - - parent::__construct(); + return array_merge(parent::getRawReplacements(), [ + '%tagTextMatch%' => 'normalize-space(string(.)) = %locator%', + '%valueMatch%' => './@value = %locator%', + '%titleMatch%' => './@title = %locator%', + '%altMatch%' => './@alt = %locator%', + '%relMatch%' => './@rel = %locator%', + '%labelAttributeMatch%' => './@label = %locator%', + ]); } } diff --git a/src/Selector/NamedSelector.php b/src/Selector/NamedSelector.php index 12560b18a..fb00ae28e 100644 --- a/src/Selector/NamedSelector.php +++ b/src/Selector/NamedSelector.php @@ -19,42 +19,80 @@ */ 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' => <<xpathEscaper = new Escaper(); + + foreach ($this->getRawReplacements() as $from => $to) { + $this->registerReplacement($from, $to); + } + + foreach ($this->getRawSelectors() as $alias => $selector) { + $this->registerNamedXpath($alias, $selector); + } + } + + /** + * Get the list of replacements in their raw state with replacements not applied. + * + * @return array + */ + protected function getRawReplacements() + { + return 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%]', + ); + } + + /** + * Get the list of selectors in their raw state with replacements not applied. + * + * @return array + */ + protected function getRawSelectors() + { + + return array( + 'fieldset' => << << << << << << << << << << << << << << << << << << << << << << << << << << << << <<xpathEscaper = new Escaper(); - - foreach ($this->replacements as $from => $to) { - $this->registerReplacement($from, $to); - } - - foreach ($this->selectors as $alias => $selector) { - $this->registerNamedXpath($alias, $selector); - } + ); } /** diff --git a/src/Selector/PartialNamedSelector.php b/src/Selector/PartialNamedSelector.php index b864a2265..d5faed769 100644 --- a/src/Selector/PartialNamedSelector.php +++ b/src/Selector/PartialNamedSelector.php @@ -17,15 +17,15 @@ */ class PartialNamedSelector extends NamedSelector { - public function __construct() + protected function getRawReplacements() { - $this->registerReplacement('%tagTextMatch%', 'contains(normalize-space(string(.)), %locator%)'); - $this->registerReplacement('%valueMatch%', 'contains(./@value, %locator%)'); - $this->registerReplacement('%titleMatch%', 'contains(./@title, %locator%)'); - $this->registerReplacement('%altMatch%', 'contains(./@alt, %locator%)'); - $this->registerReplacement('%relMatch%', 'contains(./@rel, %locator%)'); - $this->registerReplacement('%labelAttributeMatch%', 'contains(./@label, %locator%)'); - - parent::__construct(); + return array_merge(parent::getRawReplacements(), [ + '%tagTextMatch%' => 'contains(normalize-space(string(.)), %locator%)', + '%valueMatch%' => 'contains(./@value, %locator%)', + '%titleMatch%' => 'contains(./@title, %locator%)', + '%altMatch%' => 'contains(./@alt, %locator%)', + '%relMatch%' => 'contains(./@rel, %locator%)', + '%labelAttributeMatch%' => 'contains(./@label, %locator%)', + ]); } }