PageRenderTime 42ms CodeModel.GetById 14ms RepoModel.GetById 0ms app.codeStats 1ms

/daisymtw/vendor/codeception/base/src/Codeception/Util/Locator.php

https://gitlab.com/panace/public
PHP | 337 lines | 119 code | 15 blank | 203 comment | 11 complexity | 43b90ce04b121a7f07e13bb2b3efe935 MD5 | raw file
  1. <?php
  2. namespace Codeception\Util;
  3. use Symfony\Component\CssSelector\CssSelectorConverter;
  4. use Symfony\Component\CssSelector\Exception\ParseException;
  5. use Symfony\Component\CssSelector\XPath\Translator;
  6. /**
  7. * Set of useful functions for using CSS and XPath locators.
  8. * Please check them before writing complex functional or acceptance tests.
  9. *
  10. */
  11. class Locator
  12. {
  13. /**
  14. * Applies OR operator to any number of CSS or XPath selectors.
  15. * You can mix up CSS and XPath selectors here.
  16. *
  17. * ```php
  18. * <?php
  19. * use \Codeception\Util\Locator;
  20. *
  21. * $I->see('Title', Locator::combine('h1','h2','h3'));
  22. * ?>
  23. * ```
  24. *
  25. * This will search for `Title` text in either `h1`, `h2`, or `h3` tag.
  26. * You can also combine CSS selector with XPath locator:
  27. *
  28. * ```php
  29. * <?php
  30. * use \Codeception\Util\Locator;
  31. *
  32. * $I->fillField(Locator::combine('form input[type=text]','//form/textarea[2]'), 'qwerty');
  33. * ?>
  34. * ```
  35. *
  36. * As a result the Locator will produce a mixed XPath value that will be used in fillField action.
  37. *
  38. * @static
  39. * @param $selector1
  40. * @param $selector2
  41. * @throws \Exception
  42. * @return string
  43. */
  44. public static function combine($selector1, $selector2)
  45. {
  46. $selectors = func_get_args();
  47. foreach ($selectors as $k => $v) {
  48. $selectors[$k] = self::toXPath($v);
  49. if (!$selectors[$k]) {
  50. throw new \Exception("$v is invalid CSS or XPath");
  51. }
  52. }
  53. return implode(' | ', $selectors);
  54. }
  55. /**
  56. * Matches the *a* element with given URL
  57. *
  58. * ```php
  59. * <?php
  60. * use \Codeception\Util\Locator;
  61. *
  62. * $I->see('Log In', Locator::href('/login.php'));
  63. * ?>
  64. * ```
  65. *
  66. * @static
  67. * @param $url
  68. * @return string
  69. */
  70. public static function href($url)
  71. {
  72. return sprintf('//a[@href=normalize-space(%s)]', Translator::getXpathLiteral($url));
  73. }
  74. /**
  75. * Matches the element with given tab index
  76. *
  77. * Do you often use the `TAB` key to navigate through the web page? How do your site respond to this navigation?
  78. * You could try to match elements by their tab position using `tabIndex` method of `Locator` class.
  79. * ```php
  80. * <?php
  81. * use \Codeception\Util\Locator;
  82. *
  83. * $I->fillField(Locator::tabIndex(1), 'davert');
  84. * $I->fillField(Locator::tabIndex(2) , 'qwerty');
  85. * $I->click('Login');
  86. * ?>
  87. * ```
  88. *
  89. * @static
  90. * @param $index
  91. * @return string
  92. */
  93. public static function tabIndex($index)
  94. {
  95. return sprintf('//*[@tabindex = normalize-space(%d)]', $index);
  96. }
  97. /**
  98. * Matches option by text:
  99. *
  100. * ```php
  101. * <?php
  102. * use Codeception\Util\Locator;
  103. *
  104. * $I->seeElement(Locator::option('Male'), '#select-gender');
  105. * ```
  106. *
  107. * @param $value
  108. *
  109. * @return string
  110. */
  111. public static function option($value)
  112. {
  113. return sprintf('//option[.=normalize-space("%s")]', $value);
  114. }
  115. protected static function toXPath($selector)
  116. {
  117. try {
  118. $xpath = (new CssSelectorConverter())->toXPath($selector);
  119. return $xpath;
  120. } catch (ParseException $e) {
  121. if (self::isXPath($selector)) {
  122. return $selector;
  123. }
  124. }
  125. return null;
  126. }
  127. /**
  128. * Finds element by it's attribute(s)
  129. *
  130. * ```php
  131. * <?php
  132. * use \Codeception\Util\Locator;
  133. *
  134. * $I->seeElement(Locator::find('img', ['title' => 'diagram']));
  135. * ```
  136. *
  137. * @static
  138. *
  139. * @param $element
  140. * @param $attributes
  141. *
  142. * @return string
  143. */
  144. public static function find($element, array $attributes)
  145. {
  146. $operands = [];
  147. foreach ($attributes as $attribute => $value) {
  148. if (is_int($attribute)) {
  149. $operands[] = '@' . $value;
  150. } else {
  151. $operands[] = '@' . $attribute . ' = ' . Translator::getXpathLiteral($value);
  152. }
  153. }
  154. return sprintf('//%s[%s]', $element, implode(' and ', $operands));
  155. }
  156. /**
  157. * Checks that provided string is CSS selector
  158. *
  159. * ```php
  160. * <?php
  161. * Locator::isCSS('#user .hello') => true
  162. * Locator::isCSS('body') => true
  163. * Locator::isCSS('//body/p/user') => false
  164. * ```
  165. *
  166. * @param $selector
  167. * @return bool
  168. */
  169. public static function isCSS($selector)
  170. {
  171. try {
  172. (new CssSelectorConverter())->toXPath($selector);
  173. } catch (ParseException $e) {
  174. return false;
  175. }
  176. return true;
  177. }
  178. /**
  179. * Checks that locator is an XPath
  180. *
  181. * ```php
  182. * <?php
  183. * Locator::isCSS('#user .hello') => false
  184. * Locator::isCSS('body') => false
  185. * Locator::isCSS('//body/p/user') => true
  186. * ```
  187. *
  188. * @param $locator
  189. * @return bool
  190. */
  191. public static function isXPath($locator)
  192. {
  193. $document = new \DOMDocument('1.0', 'UTF-8');
  194. $xpath = new \DOMXPath($document);
  195. return @$xpath->evaluate($locator, $document) !== false;
  196. }
  197. /**
  198. * Checks that string and CSS selector for element by ID
  199. * @param $id
  200. * @return bool
  201. */
  202. public static function isID($id)
  203. {
  204. return (bool)preg_match('~^#[\w\.\-\[\]\=\^\~\:]+$~', $id);
  205. }
  206. /**
  207. * Locates an element containing a text inside.
  208. * Either CSS or XPath locator can be passed, however they will be converted to XPath.
  209. *
  210. * ```php
  211. * <?php
  212. * use Codeception\Util\Locator;
  213. *
  214. * Locator::contains('label', 'Name'); // label containing name
  215. * Locator::contains('div[@contenteditable=true]', 'hello world');
  216. * ```
  217. *
  218. * @param $element
  219. * @param $text
  220. * @return string
  221. */
  222. public static function contains($element, $text)
  223. {
  224. $text = Translator::getXpathLiteral($text);
  225. return sprintf('%s[%s]', self::toXPath($element), "contains(., $text)");
  226. }
  227. /**
  228. * Locates element at position.
  229. * Either CSS or XPath locator can be passed as locator,
  230. * position is an integer. If a negative value is provided, counting starts from the last element.
  231. * First element has index 1
  232. *
  233. * ```php
  234. * <?php
  235. * use Codeception\Util\Locator;
  236. *
  237. * Locator::elementAt('//table/tr', 2); // second row
  238. * Locator::elementAt('//table/tr', -1); // last row
  239. * Locator::elementAt('table#grind>tr', -2); // previous than last row
  240. * ```
  241. *
  242. * @param $element CSS or XPath locator
  243. * @param $position xpath index
  244. * @return mixed
  245. */
  246. public static function elementAt($element, $position)
  247. {
  248. if (is_int($position) && $position < 0) {
  249. $position++; // -1 points to the last element
  250. $position = 'last()-'.abs($position);
  251. }
  252. if ($position === 0) {
  253. throw new \InvalidArgumentException(
  254. '0 is not valid element position. XPath expects first element to have index 1'
  255. );
  256. }
  257. return sprintf('(%s)[position()=%s]', self::toXPath($element), $position);
  258. }
  259. /**
  260. * Locates first element of group elements.
  261. * Either CSS or XPath locator can be passed as locator,
  262. * Equal to `Locator::elementAt($locator, 1)`
  263. *
  264. * ```php
  265. * <?php
  266. * use Codeception\Util\Locator;
  267. *
  268. * Locator::firstElement('//table/tr');
  269. * ```
  270. *
  271. * @param $element
  272. * @return mixed
  273. */
  274. public static function firstElement($element)
  275. {
  276. return self::elementAt($element, 1);
  277. }
  278. /**
  279. * Locates last element of group elements.
  280. * Either CSS or XPath locator can be passed as locator,
  281. * Equal to `Locator::elementAt($locator, -1)`
  282. *
  283. * ```php
  284. * <?php
  285. * use Codeception\Util\Locator;
  286. *
  287. * Locator::lastElement('//table/tr');
  288. * ```
  289. *
  290. * @param $element
  291. * @return mixed
  292. */
  293. public static function lastElement($element)
  294. {
  295. return self::elementAt($element, 'last()');
  296. }
  297. /**
  298. * Transforms strict locator, \Facebook\WebDriver\WebDriverBy into a string represenation
  299. *
  300. * @param $selector
  301. * @return string
  302. */
  303. public static function humanReadableString($selector)
  304. {
  305. if (is_string($selector)) {
  306. return "'$selector'";
  307. }
  308. if (is_array($selector)) {
  309. $type = strtolower(key($selector));
  310. $locator = $selector[$type];
  311. return "$type '$locator'";
  312. }
  313. if (class_exists('\Facebook\WebDriver\WebDriverBy')) {
  314. if ($selector instanceof \Facebook\WebDriver\WebDriverBy) {
  315. $type = $selector->getMechanism();
  316. $locator = $selector->getValue();
  317. return "$type '$locator'";
  318. }
  319. }
  320. throw new \InvalidArgumentException("Unrecognized selector");
  321. }
  322. }