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