PageRenderTime 25ms CodeModel.GetById 25ms RepoModel.GetById 0ms app.codeStats 0ms

/library/Zend/Test/PHPUnit/Constraint/DomQuery.php

https://github.com/Exercise/zf2
PHP | 421 lines | 227 code | 39 blank | 155 comment | 34 complexity | 6fe54f0a727704e422c03dba833244e6 MD5 | raw file
  1. <?php
  2. /**
  3. * Zend Framework
  4. *
  5. * LICENSE
  6. *
  7. * This source file is subject to the new BSD license that is bundled
  8. * with this package in the file LICENSE.txt.
  9. * It is also available through the world-wide-web at this URL:
  10. * http://framework.zend.com/license/new-bsd
  11. * If you did not receive a copy of the license and are unable to
  12. * obtain it through the world-wide-web, please send an email
  13. * to license@zend.com so we can send you a copy immediately.
  14. *
  15. * @category Zend
  16. * @package Zend_Test
  17. * @subpackage PHPUnit
  18. * @copyright Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
  19. * @license http://framework.zend.com/license/new-bsd New BSD License
  20. * @version $Id$
  21. */
  22. /**
  23. * @namespace
  24. */
  25. namespace Zend\Test\PHPUnit\Constraint;
  26. /**
  27. * Zend_Dom_Query-based PHPUnit Constraint
  28. *
  29. * @uses PHPUnit_Framework_Constraint
  30. * @uses \Zend\Dom\Query
  31. * @uses \Zend\Test\PHPUnit\Constraint\Exception
  32. * @category Zend
  33. * @package Zend_Test
  34. * @subpackage PHPUnit
  35. * @copyright Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
  36. * @license http://framework.zend.com/license/new-bsd New BSD License
  37. */
  38. class DomQuery extends \PHPUnit_Framework_Constraint
  39. {
  40. /**#@+
  41. * Assertion type constants
  42. */
  43. const ASSERT_QUERY = 'assertQuery';
  44. const ASSERT_CONTENT_CONTAINS = 'assertQueryContentContains';
  45. const ASSERT_CONTENT_REGEX = 'assertQueryContentRegex';
  46. const ASSERT_CONTENT_COUNT = 'assertQueryCount';
  47. const ASSERT_CONTENT_COUNT_MIN= 'assertQueryCountMin';
  48. const ASSERT_CONTENT_COUNT_MAX= 'assertQueryCountMax';
  49. /**#@-*/
  50. /**
  51. * Current assertion type
  52. * @var string
  53. */
  54. protected $_assertType = null;
  55. /**
  56. * Available assertion types
  57. * @var array
  58. */
  59. protected $_assertTypes = array(
  60. self::ASSERT_QUERY,
  61. self::ASSERT_CONTENT_CONTAINS,
  62. self::ASSERT_CONTENT_REGEX,
  63. self::ASSERT_CONTENT_COUNT,
  64. self::ASSERT_CONTENT_COUNT_MIN,
  65. self::ASSERT_CONTENT_COUNT_MAX,
  66. );
  67. /**
  68. * Content being matched
  69. * @var string
  70. */
  71. protected $_content = null;
  72. /**
  73. * Whether or not assertion is negated
  74. * @var bool
  75. */
  76. protected $_negate = false;
  77. /**
  78. * CSS selector or XPath path to select against
  79. * @var string
  80. */
  81. protected $_path = null;
  82. /**
  83. * Whether or not to use XPath when querying
  84. * @var bool
  85. */
  86. protected $_useXpath = false;
  87. /**
  88. * XPath namespaces
  89. * @var array
  90. */
  91. protected $_xpathNamespaces = array();
  92. /**
  93. * Constructor; setup constraint state
  94. *
  95. * @param string $path CSS selector path
  96. * @return void
  97. */
  98. public function __construct($path)
  99. {
  100. $this->_path = $path;
  101. }
  102. /**
  103. * Indicate negative match
  104. *
  105. * @param bool $flag
  106. * @return void
  107. */
  108. public function setNegate($flag = true)
  109. {
  110. $this->_negate = $flag;
  111. }
  112. /**
  113. * Whether or not path is a straight XPath expression
  114. *
  115. * @param bool $flag
  116. * @return \Zend\Test\PHPUnit\Constraint\DomQuery
  117. */
  118. public function setUseXpath($flag = true)
  119. {
  120. $this->_useXpath = (bool) $flag;
  121. return $this;
  122. }
  123. /**
  124. * Evaluate an object to see if it fits the constraints
  125. *
  126. * @param string $other String to examine
  127. * @param null|string Assertion type
  128. * @return bool
  129. */
  130. public function evaluate($other, $assertType = null)
  131. {
  132. if (strstr($assertType, 'Not')) {
  133. $this->setNegate(true);
  134. $assertType = str_replace('Not', '', $assertType);
  135. }
  136. if (strstr($assertType, 'Xpath')) {
  137. $this->setUseXpath(true);
  138. $assertType = str_replace('Xpath', 'Query', $assertType);
  139. }
  140. if (!in_array($assertType, $this->_assertTypes)) {
  141. throw new Exception(sprintf('Invalid assertion type "%s" provided to %s constraint', $assertType, __CLASS__));
  142. }
  143. $this->_assertType = $assertType;
  144. $method = $this->_useXpath ? 'queryXpath' : 'execute';
  145. $domQuery = new \Zend\Dom\Query($other);
  146. $domQuery->registerXpathNamespaces($this->_xpathNamespaces);
  147. $result = $domQuery->$method($this->_path);
  148. $argv = func_get_args();
  149. $argc = func_num_args();
  150. switch ($assertType) {
  151. case self::ASSERT_CONTENT_CONTAINS:
  152. if (3 > $argc) {
  153. throw new Exception('No content provided against which to match');
  154. }
  155. $this->_content = $content = $argv[2];
  156. return ($this->_negate)
  157. ? $this->_notMatchContent($result, $content)
  158. : $this->_matchContent($result, $content);
  159. case self::ASSERT_CONTENT_REGEX:
  160. if (3 > $argc) {
  161. throw new Exception('No pattern provided against which to match');
  162. }
  163. $this->_content = $content = $argv[2];
  164. return ($this->_negate)
  165. ? $this->_notRegexContent($result, $content)
  166. : $this->_regexContent($result, $content);
  167. case self::ASSERT_CONTENT_COUNT:
  168. case self::ASSERT_CONTENT_COUNT_MIN:
  169. case self::ASSERT_CONTENT_COUNT_MAX:
  170. if (3 > $argc) {
  171. throw new Exception('No count provided against which to compare');
  172. }
  173. $this->_content = $content = $argv[2];
  174. return $this->_countContent($result, $content, $assertType);
  175. case self::ASSERT_QUERY:
  176. default:
  177. if ($this->_negate) {
  178. return (0 == count($result));
  179. } else {
  180. return (0 != count($result));
  181. }
  182. }
  183. }
  184. /**
  185. * Report Failure
  186. *
  187. * @see PHPUnit_Framework_Constraint for implementation details
  188. * @param mixed $other CSS selector path
  189. * @param string $description
  190. * @param bool $not
  191. * @return void
  192. * @throws PHPUnit_Framework_ExpectationFailedException
  193. */
  194. public function fail($other, $description, $not = false)
  195. {
  196. switch ($this->_assertType) {
  197. case self::ASSERT_CONTENT_CONTAINS:
  198. $failure = 'Failed asserting node denoted by %s CONTAINS content "%s"';
  199. if ($this->_negate) {
  200. $failure = 'Failed asserting node DENOTED BY %s DOES NOT CONTAIN content "%s"';
  201. }
  202. $failure = sprintf($failure, $other, $this->_content);
  203. break;
  204. case self::ASSERT_CONTENT_REGEX:
  205. $failure = 'Failed asserting node denoted by %s CONTAINS content MATCHING "%s"';
  206. if ($this->_negate) {
  207. $failure = 'Failed asserting node DENOTED BY %s DOES NOT CONTAIN content MATCHING "%s"';
  208. }
  209. $failure = sprintf($failure, $other, $this->_content);
  210. break;
  211. case self::ASSERT_CONTENT_COUNT:
  212. $failure = 'Failed asserting node DENOTED BY %s OCCURS EXACTLY %d times';
  213. if ($this->_negate) {
  214. $failure = 'Failed asserting node DENOTED BY %s DOES NOT OCCUR EXACTLY %d times';
  215. }
  216. $failure = sprintf($failure, $other, $this->_content);
  217. break;
  218. case self::ASSERT_CONTENT_COUNT_MIN:
  219. $failure = 'Failed asserting node DENOTED BY %s OCCURS AT LEAST %d times';
  220. $failure = sprintf($failure, $other, $this->_content);
  221. break;
  222. case self::ASSERT_CONTENT_COUNT_MAX:
  223. $failure = 'Failed asserting node DENOTED BY %s OCCURS AT MOST %d times';
  224. $failure = sprintf($failure, $other, $this->_content);
  225. break;
  226. case self::ASSERT_QUERY:
  227. default:
  228. $failure = 'Failed asserting node DENOTED BY %s EXISTS';
  229. if ($this->_negate) {
  230. $failure = 'Failed asserting node DENOTED BY %s DOES NOT EXIST';
  231. }
  232. $failure = sprintf($failure, $other);
  233. break;
  234. }
  235. if (!empty($description)) {
  236. $failure = $description . "\n" . $failure;
  237. }
  238. throw new Exception($failure);
  239. }
  240. /**
  241. * Complete implementation
  242. *
  243. * @return string
  244. */
  245. public function toString()
  246. {
  247. return '';
  248. }
  249. /**
  250. * Register XPath namespaces
  251. *
  252. * @param array $xpathNamespaces
  253. * @return void
  254. */
  255. public function registerXpathNamespaces($xpathNamespaces)
  256. {
  257. $this->_xpathNamespaces = $xpathNamespaces;
  258. }
  259. /**
  260. * Check to see if content is matched in selected nodes
  261. *
  262. * @param \Zend\Dom\NodeList $result
  263. * @param string $match Content to match
  264. * @return bool
  265. */
  266. protected function _matchContent($result, $match)
  267. {
  268. $match = (string) $match;
  269. if (0 == count($result)) {
  270. return false;
  271. }
  272. foreach ($result as $node) {
  273. $content = $this->_getNodeContent($node);
  274. if (strstr($content, $match)) {
  275. return true;
  276. }
  277. }
  278. return false;
  279. }
  280. /**
  281. * Check to see if content is NOT matched in selected nodes
  282. *
  283. * @param \Zend\Dom\NodeList $result
  284. * @param string $match
  285. * @return bool
  286. */
  287. protected function _notMatchContent($result, $match)
  288. {
  289. if (0 == count($result)) {
  290. return true;
  291. }
  292. foreach ($result as $node) {
  293. $content = $this->_getNodeContent($node);
  294. if (strstr($content, $match)) {
  295. return false;
  296. }
  297. }
  298. return true;
  299. }
  300. /**
  301. * Check to see if content is matched by regex in selected nodes
  302. *
  303. * @param \Zend\Dom\NodeList $result
  304. * @param string $pattern
  305. * @return bool
  306. */
  307. protected function _regexContent($result, $pattern)
  308. {
  309. if (0 == count($result)) {
  310. return false;
  311. }
  312. foreach ($result as $node) {
  313. $content = $this->_getNodeContent($node);
  314. if (preg_match($pattern, $content)) {
  315. return true;
  316. }
  317. }
  318. return false;
  319. }
  320. /**
  321. * Check to see if content is NOT matched by regex in selected nodes
  322. *
  323. * @param \Zend\Dom\NodeList $result
  324. * @param string $pattern
  325. * @return bool
  326. */
  327. protected function _notRegexContent($result, $pattern)
  328. {
  329. if (0 == count($result)) {
  330. return true;
  331. }
  332. foreach ($result as $node) {
  333. $content = $this->_getNodeContent($node);
  334. if (preg_match($pattern, $content)) {
  335. return false;
  336. }
  337. }
  338. return true;
  339. }
  340. /**
  341. * Determine if content count matches criteria
  342. *
  343. * @param \Zend\Dom\NodeList $result
  344. * @param int $test Value against which to test
  345. * @param string $type assertion type
  346. * @return boolean
  347. */
  348. protected function _countContent($result, $test, $type)
  349. {
  350. $count = count($result);
  351. switch ($type) {
  352. case self::ASSERT_CONTENT_COUNT:
  353. return ($this->_negate)
  354. ? ($test != $count)
  355. : ($test == $count);
  356. case self::ASSERT_CONTENT_COUNT_MIN:
  357. return ($count >= $test);
  358. case self::ASSERT_CONTENT_COUNT_MAX:
  359. return ($count <= $test);
  360. default:
  361. return false;
  362. }
  363. }
  364. /**
  365. * Get node content, minus node markup tags
  366. *
  367. * @param DOMNode $node
  368. * @return string
  369. */
  370. protected function _getNodeContent(\DOMNode $node)
  371. {
  372. if ($node instanceof \DOMAttr) {
  373. return $node->value;
  374. } else {
  375. $doc = $node->ownerDocument;
  376. $content = $doc->saveXML($node);
  377. $tag = $node->nodeName;
  378. $regex = '|</?' . $tag . '[^>]*>|';
  379. return preg_replace($regex, '', $content);
  380. }
  381. }
  382. }