composer update
This commit is contained in:
3
vendor/symfony/css-selector/.gitattributes
vendored
Normal file
3
vendor/symfony/css-selector/.gitattributes
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
/Tests export-ignore
|
||||
/phpunit.xml.dist export-ignore
|
||||
/.gitignore export-ignore
|
||||
3
vendor/symfony/css-selector/.gitignore
vendored
3
vendor/symfony/css-selector/.gitignore
vendored
@@ -1,3 +0,0 @@
|
||||
vendor/
|
||||
composer.lock
|
||||
phpunit.xml
|
||||
5
vendor/symfony/css-selector/CHANGELOG.md
vendored
5
vendor/symfony/css-selector/CHANGELOG.md
vendored
@@ -1,6 +1,11 @@
|
||||
CHANGELOG
|
||||
=========
|
||||
|
||||
4.4.0
|
||||
-----
|
||||
|
||||
* Added support for `*:only-of-type`
|
||||
|
||||
2.8.0
|
||||
-----
|
||||
|
||||
|
||||
@@ -53,12 +53,9 @@ class CssSelectorConverter
|
||||
* Optionally, a prefix can be added to the resulting XPath
|
||||
* expression with the $prefix parameter.
|
||||
*
|
||||
* @param string $cssExpr The CSS expression
|
||||
* @param string $prefix An optional prefix for the XPath expression
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function toXPath($cssExpr, $prefix = 'descendant-or-self::')
|
||||
public function toXPath(string $cssExpr, string $prefix = 'descendant-or-self::')
|
||||
{
|
||||
return $this->translator->cssToXPath($cssExpr, $prefix);
|
||||
}
|
||||
|
||||
@@ -24,33 +24,25 @@ use Symfony\Component\CssSelector\Parser\Token;
|
||||
class SyntaxErrorException extends ParseException
|
||||
{
|
||||
/**
|
||||
* @param string $expectedValue
|
||||
* @param Token $foundToken
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public static function unexpectedToken($expectedValue, Token $foundToken)
|
||||
public static function unexpectedToken(string $expectedValue, Token $foundToken)
|
||||
{
|
||||
return new self(sprintf('Expected %s, but %s found.', $expectedValue, $foundToken));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $pseudoElement
|
||||
* @param string $unexpectedLocation
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public static function pseudoElementFound($pseudoElement, $unexpectedLocation)
|
||||
public static function pseudoElementFound(string $pseudoElement, string $unexpectedLocation)
|
||||
{
|
||||
return new self(sprintf('Unexpected pseudo-element "::%s" found %s.', $pseudoElement, $unexpectedLocation));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $position
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public static function unclosedString($position)
|
||||
public static function unclosedString(int $position)
|
||||
{
|
||||
return new self(sprintf('Unclosed/invalid string at %s.', $position));
|
||||
}
|
||||
|
||||
@@ -28,9 +28,6 @@ abstract class AbstractNode implements NodeInterface
|
||||
*/
|
||||
private $nodeName;
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getNodeName(): string
|
||||
{
|
||||
if (null === $this->nodeName) {
|
||||
|
||||
14
vendor/symfony/css-selector/Node/ElementNode.php
vendored
14
vendor/symfony/css-selector/Node/ElementNode.php
vendored
@@ -26,28 +26,18 @@ class ElementNode extends AbstractNode
|
||||
private $namespace;
|
||||
private $element;
|
||||
|
||||
/**
|
||||
* @param string|null $namespace
|
||||
* @param string|null $element
|
||||
*/
|
||||
public function __construct(string $namespace = null, string $element = null)
|
||||
{
|
||||
$this->namespace = $namespace;
|
||||
$this->element = $element;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string|null
|
||||
*/
|
||||
public function getNamespace()
|
||||
public function getNamespace(): ?string
|
||||
{
|
||||
return $this->namespace;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string|null
|
||||
*/
|
||||
public function getElement()
|
||||
public function getElement(): ?string
|
||||
{
|
||||
return $this->element;
|
||||
}
|
||||
|
||||
@@ -30,9 +30,7 @@ class FunctionNode extends AbstractNode
|
||||
private $arguments;
|
||||
|
||||
/**
|
||||
* @param NodeInterface $selector
|
||||
* @param string $name
|
||||
* @param Token[] $arguments
|
||||
* @param Token[] $arguments
|
||||
*/
|
||||
public function __construct(NodeInterface $selector, string $name, array $arguments = [])
|
||||
{
|
||||
@@ -54,7 +52,7 @@ class FunctionNode extends AbstractNode
|
||||
/**
|
||||
* @return Token[]
|
||||
*/
|
||||
public function getArguments()
|
||||
public function getArguments(): array
|
||||
{
|
||||
return $this->arguments;
|
||||
}
|
||||
|
||||
@@ -32,18 +32,12 @@ class NegationNode extends AbstractNode
|
||||
$this->subSelector = $subSelector;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return NodeInterface
|
||||
*/
|
||||
public function getSelector()
|
||||
public function getSelector(): NodeInterface
|
||||
{
|
||||
return $this->selector;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return NodeInterface
|
||||
*/
|
||||
public function getSubSelector()
|
||||
public function getSubSelector(): NodeInterface
|
||||
{
|
||||
return $this->subSelector;
|
||||
}
|
||||
|
||||
@@ -53,10 +53,8 @@ class Specificity
|
||||
/**
|
||||
* Returns -1 if the object specificity is lower than the argument,
|
||||
* 0 if they are equal, and 1 if the argument is lower.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function compareTo(self $specificity)
|
||||
public function compareTo(self $specificity): int
|
||||
{
|
||||
if ($this->a !== $specificity->a) {
|
||||
return $this->a > $specificity->a ? 1 : -1;
|
||||
|
||||
@@ -26,8 +26,5 @@ use Symfony\Component\CssSelector\Parser\TokenStream;
|
||||
*/
|
||||
interface HandlerInterface
|
||||
{
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function handle(Reader $reader, TokenStream $stream): bool;
|
||||
}
|
||||
|
||||
@@ -56,7 +56,7 @@ class TokenStream
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function push(Token $token)
|
||||
public function push(Token $token): self
|
||||
{
|
||||
$this->tokens[] = $token;
|
||||
|
||||
@@ -68,7 +68,7 @@ class TokenStream
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function freeze()
|
||||
public function freeze(): self
|
||||
{
|
||||
return $this;
|
||||
}
|
||||
@@ -76,11 +76,9 @@ class TokenStream
|
||||
/**
|
||||
* Returns next token.
|
||||
*
|
||||
* @return Token
|
||||
*
|
||||
* @throws InternalErrorException If there is no more token
|
||||
*/
|
||||
public function getNext()
|
||||
public function getNext(): Token
|
||||
{
|
||||
if ($this->peeking) {
|
||||
$this->peeking = false;
|
||||
@@ -98,10 +96,8 @@ class TokenStream
|
||||
|
||||
/**
|
||||
* Returns peeked token.
|
||||
*
|
||||
* @return Token
|
||||
*/
|
||||
public function getPeek()
|
||||
public function getPeek(): Token
|
||||
{
|
||||
if (!$this->peeking) {
|
||||
$this->peeked = $this->getNext();
|
||||
@@ -116,7 +112,7 @@ class TokenStream
|
||||
*
|
||||
* @return Token[]
|
||||
*/
|
||||
public function getUsed()
|
||||
public function getUsed(): array
|
||||
{
|
||||
return $this->used;
|
||||
}
|
||||
@@ -128,7 +124,7 @@ class TokenStream
|
||||
*
|
||||
* @throws SyntaxErrorException If next token is not an identifier
|
||||
*/
|
||||
public function getNextIdentifier()
|
||||
public function getNextIdentifier(): string
|
||||
{
|
||||
$next = $this->getNext();
|
||||
|
||||
@@ -146,7 +142,7 @@ class TokenStream
|
||||
*
|
||||
* @throws SyntaxErrorException If next token is not an identifier or a star delimiter
|
||||
*/
|
||||
public function getNextIdentifierOrStar()
|
||||
public function getNextIdentifierOrStar(): ?string
|
||||
{
|
||||
$next = $this->getNext();
|
||||
|
||||
@@ -155,7 +151,7 @@ class TokenStream
|
||||
}
|
||||
|
||||
if ($next->isDelimiter(['*'])) {
|
||||
return;
|
||||
return null;
|
||||
}
|
||||
|
||||
throw SyntaxErrorException::unexpectedToken('identifier or "*"', $next);
|
||||
|
||||
@@ -50,10 +50,8 @@ class Tokenizer
|
||||
|
||||
/**
|
||||
* Tokenize selector source code.
|
||||
*
|
||||
* @return TokenStream
|
||||
*/
|
||||
public function tokenize(Reader $reader)
|
||||
public function tokenize(Reader $reader): TokenStream
|
||||
{
|
||||
$stream = new TokenStream();
|
||||
|
||||
|
||||
@@ -58,6 +58,8 @@ class TokenizerEscaping
|
||||
if (0x10000 > $c) {
|
||||
return \chr(0xE0 | $c >> 12).\chr(0x80 | $c >> 6 & 0x3F).\chr(0x80 | $c & 0x3F);
|
||||
}
|
||||
|
||||
return '';
|
||||
}, $value);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,76 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\CssSelector\Tests;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Symfony\Component\CssSelector\CssSelectorConverter;
|
||||
|
||||
class CssSelectorConverterTest extends TestCase
|
||||
{
|
||||
public function testCssToXPath()
|
||||
{
|
||||
$converter = new CssSelectorConverter();
|
||||
|
||||
$this->assertEquals('descendant-or-self::*', $converter->toXPath(''));
|
||||
$this->assertEquals('descendant-or-self::h1', $converter->toXPath('h1'));
|
||||
$this->assertEquals("descendant-or-self::h1[@id = 'foo']", $converter->toXPath('h1#foo'));
|
||||
$this->assertEquals("descendant-or-self::h1[@class and contains(concat(' ', normalize-space(@class), ' '), ' foo ')]", $converter->toXPath('h1.foo'));
|
||||
$this->assertEquals('descendant-or-self::foo:h1', $converter->toXPath('foo|h1'));
|
||||
$this->assertEquals('descendant-or-self::h1', $converter->toXPath('H1'));
|
||||
}
|
||||
|
||||
public function testCssToXPathXml()
|
||||
{
|
||||
$converter = new CssSelectorConverter(false);
|
||||
|
||||
$this->assertEquals('descendant-or-self::H1', $converter->toXPath('H1'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \Symfony\Component\CssSelector\Exception\ParseException
|
||||
* @expectedExceptionMessage Expected identifier, but <eof at 3> found.
|
||||
*/
|
||||
public function testParseExceptions()
|
||||
{
|
||||
$converter = new CssSelectorConverter();
|
||||
$converter->toXPath('h1:');
|
||||
}
|
||||
|
||||
/** @dataProvider getCssToXPathWithoutPrefixTestData */
|
||||
public function testCssToXPathWithoutPrefix($css, $xpath)
|
||||
{
|
||||
$converter = new CssSelectorConverter();
|
||||
|
||||
$this->assertEquals($xpath, $converter->toXPath($css, ''), '->parse() parses an input string and returns a node');
|
||||
}
|
||||
|
||||
public function getCssToXPathWithoutPrefixTestData()
|
||||
{
|
||||
return [
|
||||
['h1', 'h1'],
|
||||
['foo|h1', 'foo:h1'],
|
||||
['h1, h2, h3', 'h1 | h2 | h3'],
|
||||
['h1:nth-child(3n+1)', "*/*[(name() = 'h1') and (position() - 1 >= 0 and (position() - 1) mod 3 = 0)]"],
|
||||
['h1 > p', 'h1/p'],
|
||||
['h1#foo', "h1[@id = 'foo']"],
|
||||
['h1.foo', "h1[@class and contains(concat(' ', normalize-space(@class), ' '), ' foo ')]"],
|
||||
['h1[class*="foo bar"]', "h1[@class and contains(@class, 'foo bar')]"],
|
||||
['h1[foo|class*="foo bar"]', "h1[@foo:class and contains(@foo:class, 'foo bar')]"],
|
||||
['h1[class]', 'h1[@class]'],
|
||||
['h1 .foo', "h1/descendant-or-self::*/*[@class and contains(concat(' ', normalize-space(@class), ' '), ' foo ')]"],
|
||||
['h1 #foo', "h1/descendant-or-self::*/*[@id = 'foo']"],
|
||||
['h1 [class*=foo]', "h1/descendant-or-self::*/*[@class and contains(@class, 'foo')]"],
|
||||
['div>.foo', "div/*[@class and contains(concat(' ', normalize-space(@class), ' '), ' foo ')]"],
|
||||
['div > .foo', "div/*[@class and contains(concat(' ', normalize-space(@class), ' '), ' foo ')]"],
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\CssSelector\Tests\Node;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Symfony\Component\CssSelector\Node\NodeInterface;
|
||||
|
||||
abstract class AbstractNodeTest extends TestCase
|
||||
{
|
||||
/** @dataProvider getToStringConversionTestData */
|
||||
public function testToStringConversion(NodeInterface $node, $representation)
|
||||
{
|
||||
$this->assertEquals($representation, (string) $node);
|
||||
}
|
||||
|
||||
/** @dataProvider getSpecificityValueTestData */
|
||||
public function testSpecificityValue(NodeInterface $node, $value)
|
||||
{
|
||||
$this->assertEquals($value, $node->getSpecificity()->getValue());
|
||||
}
|
||||
|
||||
abstract public function getToStringConversionTestData();
|
||||
|
||||
abstract public function getSpecificityValueTestData();
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\CssSelector\Tests\Node;
|
||||
|
||||
use Symfony\Component\CssSelector\Node\AttributeNode;
|
||||
use Symfony\Component\CssSelector\Node\ElementNode;
|
||||
|
||||
class AttributeNodeTest extends AbstractNodeTest
|
||||
{
|
||||
public function getToStringConversionTestData()
|
||||
{
|
||||
return [
|
||||
[new AttributeNode(new ElementNode(), null, 'attribute', 'exists', null), 'Attribute[Element[*][attribute]]'],
|
||||
[new AttributeNode(new ElementNode(), null, 'attribute', '$=', 'value'), "Attribute[Element[*][attribute $= 'value']]"],
|
||||
[new AttributeNode(new ElementNode(), 'namespace', 'attribute', '$=', 'value'), "Attribute[Element[*][namespace|attribute $= 'value']]"],
|
||||
];
|
||||
}
|
||||
|
||||
public function getSpecificityValueTestData()
|
||||
{
|
||||
return [
|
||||
[new AttributeNode(new ElementNode(), null, 'attribute', 'exists', null), 10],
|
||||
[new AttributeNode(new ElementNode(null, 'element'), null, 'attribute', 'exists', null), 11],
|
||||
[new AttributeNode(new ElementNode(), null, 'attribute', '$=', 'value'), 10],
|
||||
[new AttributeNode(new ElementNode(), 'namespace', 'attribute', '$=', 'value'), 10],
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\CssSelector\Tests\Node;
|
||||
|
||||
use Symfony\Component\CssSelector\Node\ClassNode;
|
||||
use Symfony\Component\CssSelector\Node\ElementNode;
|
||||
|
||||
class ClassNodeTest extends AbstractNodeTest
|
||||
{
|
||||
public function getToStringConversionTestData()
|
||||
{
|
||||
return [
|
||||
[new ClassNode(new ElementNode(), 'class'), 'Class[Element[*].class]'],
|
||||
];
|
||||
}
|
||||
|
||||
public function getSpecificityValueTestData()
|
||||
{
|
||||
return [
|
||||
[new ClassNode(new ElementNode(), 'class'), 10],
|
||||
[new ClassNode(new ElementNode(null, 'element'), 'class'), 11],
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\CssSelector\Tests\Node;
|
||||
|
||||
use Symfony\Component\CssSelector\Node\CombinedSelectorNode;
|
||||
use Symfony\Component\CssSelector\Node\ElementNode;
|
||||
|
||||
class CombinedSelectorNodeTest extends AbstractNodeTest
|
||||
{
|
||||
public function getToStringConversionTestData()
|
||||
{
|
||||
return [
|
||||
[new CombinedSelectorNode(new ElementNode(), '>', new ElementNode()), 'CombinedSelector[Element[*] > Element[*]]'],
|
||||
[new CombinedSelectorNode(new ElementNode(), ' ', new ElementNode()), 'CombinedSelector[Element[*] <followed> Element[*]]'],
|
||||
];
|
||||
}
|
||||
|
||||
public function getSpecificityValueTestData()
|
||||
{
|
||||
return [
|
||||
[new CombinedSelectorNode(new ElementNode(), '>', new ElementNode()), 0],
|
||||
[new CombinedSelectorNode(new ElementNode(null, 'element'), '>', new ElementNode()), 1],
|
||||
[new CombinedSelectorNode(new ElementNode(null, 'element'), '>', new ElementNode(null, 'element')), 2],
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\CssSelector\Tests\Node;
|
||||
|
||||
use Symfony\Component\CssSelector\Node\ElementNode;
|
||||
|
||||
class ElementNodeTest extends AbstractNodeTest
|
||||
{
|
||||
public function getToStringConversionTestData()
|
||||
{
|
||||
return [
|
||||
[new ElementNode(), 'Element[*]'],
|
||||
[new ElementNode(null, 'element'), 'Element[element]'],
|
||||
[new ElementNode('namespace', 'element'), 'Element[namespace|element]'],
|
||||
];
|
||||
}
|
||||
|
||||
public function getSpecificityValueTestData()
|
||||
{
|
||||
return [
|
||||
[new ElementNode(), 0],
|
||||
[new ElementNode(null, 'element'), 1],
|
||||
[new ElementNode('namespace', 'element'), 1],
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -1,47 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\CssSelector\Tests\Node;
|
||||
|
||||
use Symfony\Component\CssSelector\Node\ElementNode;
|
||||
use Symfony\Component\CssSelector\Node\FunctionNode;
|
||||
use Symfony\Component\CssSelector\Parser\Token;
|
||||
|
||||
class FunctionNodeTest extends AbstractNodeTest
|
||||
{
|
||||
public function getToStringConversionTestData()
|
||||
{
|
||||
return [
|
||||
[new FunctionNode(new ElementNode(), 'function'), 'Function[Element[*]:function()]'],
|
||||
[new FunctionNode(new ElementNode(), 'function', [
|
||||
new Token(Token::TYPE_IDENTIFIER, 'value', 0),
|
||||
]), "Function[Element[*]:function(['value'])]"],
|
||||
[new FunctionNode(new ElementNode(), 'function', [
|
||||
new Token(Token::TYPE_STRING, 'value1', 0),
|
||||
new Token(Token::TYPE_NUMBER, 'value2', 0),
|
||||
]), "Function[Element[*]:function(['value1', 'value2'])]"],
|
||||
];
|
||||
}
|
||||
|
||||
public function getSpecificityValueTestData()
|
||||
{
|
||||
return [
|
||||
[new FunctionNode(new ElementNode(), 'function'), 10],
|
||||
[new FunctionNode(new ElementNode(), 'function', [
|
||||
new Token(Token::TYPE_IDENTIFIER, 'value', 0),
|
||||
]), 10],
|
||||
[new FunctionNode(new ElementNode(), 'function', [
|
||||
new Token(Token::TYPE_STRING, 'value1', 0),
|
||||
new Token(Token::TYPE_NUMBER, 'value2', 0),
|
||||
]), 10],
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\CssSelector\Tests\Node;
|
||||
|
||||
use Symfony\Component\CssSelector\Node\ElementNode;
|
||||
use Symfony\Component\CssSelector\Node\HashNode;
|
||||
|
||||
class HashNodeTest extends AbstractNodeTest
|
||||
{
|
||||
public function getToStringConversionTestData()
|
||||
{
|
||||
return [
|
||||
[new HashNode(new ElementNode(), 'id'), 'Hash[Element[*]#id]'],
|
||||
];
|
||||
}
|
||||
|
||||
public function getSpecificityValueTestData()
|
||||
{
|
||||
return [
|
||||
[new HashNode(new ElementNode(), 'id'), 100],
|
||||
[new HashNode(new ElementNode(null, 'id'), 'class'), 101],
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\CssSelector\Tests\Node;
|
||||
|
||||
use Symfony\Component\CssSelector\Node\ClassNode;
|
||||
use Symfony\Component\CssSelector\Node\ElementNode;
|
||||
use Symfony\Component\CssSelector\Node\NegationNode;
|
||||
|
||||
class NegationNodeTest extends AbstractNodeTest
|
||||
{
|
||||
public function getToStringConversionTestData()
|
||||
{
|
||||
return [
|
||||
[new NegationNode(new ElementNode(), new ClassNode(new ElementNode(), 'class')), 'Negation[Element[*]:not(Class[Element[*].class])]'],
|
||||
];
|
||||
}
|
||||
|
||||
public function getSpecificityValueTestData()
|
||||
{
|
||||
return [
|
||||
[new NegationNode(new ElementNode(), new ClassNode(new ElementNode(), 'class')), 10],
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\CssSelector\Tests\Node;
|
||||
|
||||
use Symfony\Component\CssSelector\Node\ElementNode;
|
||||
use Symfony\Component\CssSelector\Node\PseudoNode;
|
||||
|
||||
class PseudoNodeTest extends AbstractNodeTest
|
||||
{
|
||||
public function getToStringConversionTestData()
|
||||
{
|
||||
return [
|
||||
[new PseudoNode(new ElementNode(), 'pseudo'), 'Pseudo[Element[*]:pseudo]'],
|
||||
];
|
||||
}
|
||||
|
||||
public function getSpecificityValueTestData()
|
||||
{
|
||||
return [
|
||||
[new PseudoNode(new ElementNode(), 'pseudo'), 10],
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\CssSelector\Tests\Node;
|
||||
|
||||
use Symfony\Component\CssSelector\Node\ElementNode;
|
||||
use Symfony\Component\CssSelector\Node\SelectorNode;
|
||||
|
||||
class SelectorNodeTest extends AbstractNodeTest
|
||||
{
|
||||
public function getToStringConversionTestData()
|
||||
{
|
||||
return [
|
||||
[new SelectorNode(new ElementNode()), 'Selector[Element[*]]'],
|
||||
[new SelectorNode(new ElementNode(), 'pseudo'), 'Selector[Element[*]::pseudo]'],
|
||||
];
|
||||
}
|
||||
|
||||
public function getSpecificityValueTestData()
|
||||
{
|
||||
return [
|
||||
[new SelectorNode(new ElementNode()), 0],
|
||||
[new SelectorNode(new ElementNode(), 'pseudo'), 1],
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -1,63 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\CssSelector\Tests\Node;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Symfony\Component\CssSelector\Node\Specificity;
|
||||
|
||||
class SpecificityTest extends TestCase
|
||||
{
|
||||
/** @dataProvider getValueTestData */
|
||||
public function testValue(Specificity $specificity, $value)
|
||||
{
|
||||
$this->assertEquals($value, $specificity->getValue());
|
||||
}
|
||||
|
||||
/** @dataProvider getValueTestData */
|
||||
public function testPlusValue(Specificity $specificity, $value)
|
||||
{
|
||||
$this->assertEquals($value + 123, $specificity->plus(new Specificity(1, 2, 3))->getValue());
|
||||
}
|
||||
|
||||
public function getValueTestData()
|
||||
{
|
||||
return [
|
||||
[new Specificity(0, 0, 0), 0],
|
||||
[new Specificity(0, 0, 2), 2],
|
||||
[new Specificity(0, 3, 0), 30],
|
||||
[new Specificity(4, 0, 0), 400],
|
||||
[new Specificity(4, 3, 2), 432],
|
||||
];
|
||||
}
|
||||
|
||||
/** @dataProvider getCompareTestData */
|
||||
public function testCompareTo(Specificity $a, Specificity $b, $result)
|
||||
{
|
||||
$this->assertEquals($result, $a->compareTo($b));
|
||||
}
|
||||
|
||||
public function getCompareTestData()
|
||||
{
|
||||
return [
|
||||
[new Specificity(0, 0, 0), new Specificity(0, 0, 0), 0],
|
||||
[new Specificity(0, 0, 1), new Specificity(0, 0, 1), 0],
|
||||
[new Specificity(0, 0, 2), new Specificity(0, 0, 1), 1],
|
||||
[new Specificity(0, 0, 2), new Specificity(0, 0, 3), -1],
|
||||
[new Specificity(0, 4, 0), new Specificity(0, 4, 0), 0],
|
||||
[new Specificity(0, 6, 0), new Specificity(0, 5, 11), 1],
|
||||
[new Specificity(0, 7, 0), new Specificity(0, 8, 0), -1],
|
||||
[new Specificity(9, 0, 0), new Specificity(9, 0, 0), 0],
|
||||
[new Specificity(11, 0, 0), new Specificity(10, 11, 0), 1],
|
||||
[new Specificity(12, 11, 0), new Specificity(13, 0, 0), -1],
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -1,70 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\CssSelector\Tests\Parser\Handler;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Symfony\Component\CssSelector\Parser\Reader;
|
||||
use Symfony\Component\CssSelector\Parser\Token;
|
||||
use Symfony\Component\CssSelector\Parser\TokenStream;
|
||||
|
||||
/**
|
||||
* @author Jean-François Simon <contact@jfsimon.fr>
|
||||
*/
|
||||
abstract class AbstractHandlerTest extends TestCase
|
||||
{
|
||||
/** @dataProvider getHandleValueTestData */
|
||||
public function testHandleValue($value, Token $expectedToken, $remainingContent)
|
||||
{
|
||||
$reader = new Reader($value);
|
||||
$stream = new TokenStream();
|
||||
|
||||
$this->assertTrue($this->generateHandler()->handle($reader, $stream));
|
||||
$this->assertEquals($expectedToken, $stream->getNext());
|
||||
$this->assertRemainingContent($reader, $remainingContent);
|
||||
}
|
||||
|
||||
/** @dataProvider getDontHandleValueTestData */
|
||||
public function testDontHandleValue($value)
|
||||
{
|
||||
$reader = new Reader($value);
|
||||
$stream = new TokenStream();
|
||||
|
||||
$this->assertFalse($this->generateHandler()->handle($reader, $stream));
|
||||
$this->assertStreamEmpty($stream);
|
||||
$this->assertRemainingContent($reader, $value);
|
||||
}
|
||||
|
||||
abstract public function getHandleValueTestData();
|
||||
|
||||
abstract public function getDontHandleValueTestData();
|
||||
|
||||
abstract protected function generateHandler();
|
||||
|
||||
protected function assertStreamEmpty(TokenStream $stream)
|
||||
{
|
||||
$property = new \ReflectionProperty($stream, 'tokens');
|
||||
$property->setAccessible(true);
|
||||
|
||||
$this->assertEquals([], $property->getValue($stream));
|
||||
}
|
||||
|
||||
protected function assertRemainingContent(Reader $reader, $remainingContent)
|
||||
{
|
||||
if ('' === $remainingContent) {
|
||||
$this->assertEquals(0, $reader->getRemainingLength());
|
||||
$this->assertTrue($reader->isEOF());
|
||||
} else {
|
||||
$this->assertEquals(\strlen($remainingContent), $reader->getRemainingLength());
|
||||
$this->assertEquals(0, $reader->getOffset($remainingContent));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,55 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\CssSelector\Tests\Parser\Handler;
|
||||
|
||||
use Symfony\Component\CssSelector\Parser\Handler\CommentHandler;
|
||||
use Symfony\Component\CssSelector\Parser\Reader;
|
||||
use Symfony\Component\CssSelector\Parser\Token;
|
||||
use Symfony\Component\CssSelector\Parser\TokenStream;
|
||||
|
||||
class CommentHandlerTest extends AbstractHandlerTest
|
||||
{
|
||||
/** @dataProvider getHandleValueTestData */
|
||||
public function testHandleValue($value, Token $unusedArgument, $remainingContent)
|
||||
{
|
||||
$reader = new Reader($value);
|
||||
$stream = new TokenStream();
|
||||
|
||||
$this->assertTrue($this->generateHandler()->handle($reader, $stream));
|
||||
// comments are ignored (not pushed as token in stream)
|
||||
$this->assertStreamEmpty($stream);
|
||||
$this->assertRemainingContent($reader, $remainingContent);
|
||||
}
|
||||
|
||||
public function getHandleValueTestData()
|
||||
{
|
||||
return [
|
||||
// 2nd argument only exists for inherited method compatibility
|
||||
['/* comment */', new Token(null, null, null), ''],
|
||||
['/* comment */foo', new Token(null, null, null), 'foo'],
|
||||
];
|
||||
}
|
||||
|
||||
public function getDontHandleValueTestData()
|
||||
{
|
||||
return [
|
||||
['>'],
|
||||
['+'],
|
||||
[' '],
|
||||
];
|
||||
}
|
||||
|
||||
protected function generateHandler()
|
||||
{
|
||||
return new CommentHandler();
|
||||
}
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\CssSelector\Tests\Parser\Handler;
|
||||
|
||||
use Symfony\Component\CssSelector\Parser\Handler\HashHandler;
|
||||
use Symfony\Component\CssSelector\Parser\Token;
|
||||
use Symfony\Component\CssSelector\Parser\Tokenizer\TokenizerEscaping;
|
||||
use Symfony\Component\CssSelector\Parser\Tokenizer\TokenizerPatterns;
|
||||
|
||||
class HashHandlerTest extends AbstractHandlerTest
|
||||
{
|
||||
public function getHandleValueTestData()
|
||||
{
|
||||
return [
|
||||
['#id', new Token(Token::TYPE_HASH, 'id', 0), ''],
|
||||
['#123', new Token(Token::TYPE_HASH, '123', 0), ''],
|
||||
|
||||
['#id.class', new Token(Token::TYPE_HASH, 'id', 0), '.class'],
|
||||
['#id element', new Token(Token::TYPE_HASH, 'id', 0), ' element'],
|
||||
];
|
||||
}
|
||||
|
||||
public function getDontHandleValueTestData()
|
||||
{
|
||||
return [
|
||||
['id'],
|
||||
['123'],
|
||||
['<'],
|
||||
['<'],
|
||||
['#'],
|
||||
];
|
||||
}
|
||||
|
||||
protected function generateHandler()
|
||||
{
|
||||
$patterns = new TokenizerPatterns();
|
||||
|
||||
return new HashHandler($patterns, new TokenizerEscaping($patterns));
|
||||
}
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\CssSelector\Tests\Parser\Handler;
|
||||
|
||||
use Symfony\Component\CssSelector\Parser\Handler\IdentifierHandler;
|
||||
use Symfony\Component\CssSelector\Parser\Token;
|
||||
use Symfony\Component\CssSelector\Parser\Tokenizer\TokenizerEscaping;
|
||||
use Symfony\Component\CssSelector\Parser\Tokenizer\TokenizerPatterns;
|
||||
|
||||
class IdentifierHandlerTest extends AbstractHandlerTest
|
||||
{
|
||||
public function getHandleValueTestData()
|
||||
{
|
||||
return [
|
||||
['foo', new Token(Token::TYPE_IDENTIFIER, 'foo', 0), ''],
|
||||
['foo|bar', new Token(Token::TYPE_IDENTIFIER, 'foo', 0), '|bar'],
|
||||
['foo.class', new Token(Token::TYPE_IDENTIFIER, 'foo', 0), '.class'],
|
||||
['foo[attr]', new Token(Token::TYPE_IDENTIFIER, 'foo', 0), '[attr]'],
|
||||
['foo bar', new Token(Token::TYPE_IDENTIFIER, 'foo', 0), ' bar'],
|
||||
];
|
||||
}
|
||||
|
||||
public function getDontHandleValueTestData()
|
||||
{
|
||||
return [
|
||||
['>'],
|
||||
['+'],
|
||||
[' '],
|
||||
['*|foo'],
|
||||
['/* comment */'],
|
||||
];
|
||||
}
|
||||
|
||||
protected function generateHandler()
|
||||
{
|
||||
$patterns = new TokenizerPatterns();
|
||||
|
||||
return new IdentifierHandler($patterns, new TokenizerEscaping($patterns));
|
||||
}
|
||||
}
|
||||
@@ -1,50 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\CssSelector\Tests\Parser\Handler;
|
||||
|
||||
use Symfony\Component\CssSelector\Parser\Handler\NumberHandler;
|
||||
use Symfony\Component\CssSelector\Parser\Token;
|
||||
use Symfony\Component\CssSelector\Parser\Tokenizer\TokenizerPatterns;
|
||||
|
||||
class NumberHandlerTest extends AbstractHandlerTest
|
||||
{
|
||||
public function getHandleValueTestData()
|
||||
{
|
||||
return [
|
||||
['12', new Token(Token::TYPE_NUMBER, '12', 0), ''],
|
||||
['12.34', new Token(Token::TYPE_NUMBER, '12.34', 0), ''],
|
||||
['+12.34', new Token(Token::TYPE_NUMBER, '+12.34', 0), ''],
|
||||
['-12.34', new Token(Token::TYPE_NUMBER, '-12.34', 0), ''],
|
||||
|
||||
['12 arg', new Token(Token::TYPE_NUMBER, '12', 0), ' arg'],
|
||||
['12]', new Token(Token::TYPE_NUMBER, '12', 0), ']'],
|
||||
];
|
||||
}
|
||||
|
||||
public function getDontHandleValueTestData()
|
||||
{
|
||||
return [
|
||||
['hello'],
|
||||
['>'],
|
||||
['+'],
|
||||
[' '],
|
||||
['/* comment */'],
|
||||
];
|
||||
}
|
||||
|
||||
protected function generateHandler()
|
||||
{
|
||||
$patterns = new TokenizerPatterns();
|
||||
|
||||
return new NumberHandler($patterns);
|
||||
}
|
||||
}
|
||||
@@ -1,50 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\CssSelector\Tests\Parser\Handler;
|
||||
|
||||
use Symfony\Component\CssSelector\Parser\Handler\StringHandler;
|
||||
use Symfony\Component\CssSelector\Parser\Token;
|
||||
use Symfony\Component\CssSelector\Parser\Tokenizer\TokenizerEscaping;
|
||||
use Symfony\Component\CssSelector\Parser\Tokenizer\TokenizerPatterns;
|
||||
|
||||
class StringHandlerTest extends AbstractHandlerTest
|
||||
{
|
||||
public function getHandleValueTestData()
|
||||
{
|
||||
return [
|
||||
['"hello"', new Token(Token::TYPE_STRING, 'hello', 1), ''],
|
||||
['"1"', new Token(Token::TYPE_STRING, '1', 1), ''],
|
||||
['" "', new Token(Token::TYPE_STRING, ' ', 1), ''],
|
||||
['""', new Token(Token::TYPE_STRING, '', 1), ''],
|
||||
["'hello'", new Token(Token::TYPE_STRING, 'hello', 1), ''],
|
||||
|
||||
["'foo'bar", new Token(Token::TYPE_STRING, 'foo', 1), 'bar'],
|
||||
];
|
||||
}
|
||||
|
||||
public function getDontHandleValueTestData()
|
||||
{
|
||||
return [
|
||||
['hello'],
|
||||
['>'],
|
||||
['1'],
|
||||
[' '],
|
||||
];
|
||||
}
|
||||
|
||||
protected function generateHandler()
|
||||
{
|
||||
$patterns = new TokenizerPatterns();
|
||||
|
||||
return new StringHandler($patterns, new TokenizerEscaping($patterns));
|
||||
}
|
||||
}
|
||||
@@ -1,44 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\CssSelector\Tests\Parser\Handler;
|
||||
|
||||
use Symfony\Component\CssSelector\Parser\Handler\WhitespaceHandler;
|
||||
use Symfony\Component\CssSelector\Parser\Token;
|
||||
|
||||
class WhitespaceHandlerTest extends AbstractHandlerTest
|
||||
{
|
||||
public function getHandleValueTestData()
|
||||
{
|
||||
return [
|
||||
[' ', new Token(Token::TYPE_WHITESPACE, ' ', 0), ''],
|
||||
["\n", new Token(Token::TYPE_WHITESPACE, "\n", 0), ''],
|
||||
["\t", new Token(Token::TYPE_WHITESPACE, "\t", 0), ''],
|
||||
|
||||
[' foo', new Token(Token::TYPE_WHITESPACE, ' ', 0), 'foo'],
|
||||
[' .foo', new Token(Token::TYPE_WHITESPACE, ' ', 0), '.foo'],
|
||||
];
|
||||
}
|
||||
|
||||
public function getDontHandleValueTestData()
|
||||
{
|
||||
return [
|
||||
['>'],
|
||||
['1'],
|
||||
['a'],
|
||||
];
|
||||
}
|
||||
|
||||
protected function generateHandler()
|
||||
{
|
||||
return new WhitespaceHandler();
|
||||
}
|
||||
}
|
||||
@@ -1,253 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\CssSelector\Tests\Parser;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Symfony\Component\CssSelector\Exception\SyntaxErrorException;
|
||||
use Symfony\Component\CssSelector\Node\FunctionNode;
|
||||
use Symfony\Component\CssSelector\Node\SelectorNode;
|
||||
use Symfony\Component\CssSelector\Parser\Parser;
|
||||
use Symfony\Component\CssSelector\Parser\Token;
|
||||
|
||||
class ParserTest extends TestCase
|
||||
{
|
||||
/** @dataProvider getParserTestData */
|
||||
public function testParser($source, $representation)
|
||||
{
|
||||
$parser = new Parser();
|
||||
|
||||
$this->assertEquals($representation, array_map(function (SelectorNode $node) {
|
||||
return (string) $node->getTree();
|
||||
}, $parser->parse($source)));
|
||||
}
|
||||
|
||||
/** @dataProvider getParserExceptionTestData */
|
||||
public function testParserException($source, $message)
|
||||
{
|
||||
$parser = new Parser();
|
||||
|
||||
try {
|
||||
$parser->parse($source);
|
||||
$this->fail('Parser should throw a SyntaxErrorException.');
|
||||
} catch (SyntaxErrorException $e) {
|
||||
$this->assertEquals($message, $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/** @dataProvider getPseudoElementsTestData */
|
||||
public function testPseudoElements($source, $element, $pseudo)
|
||||
{
|
||||
$parser = new Parser();
|
||||
$selectors = $parser->parse($source);
|
||||
$this->assertCount(1, $selectors);
|
||||
|
||||
/** @var SelectorNode $selector */
|
||||
$selector = $selectors[0];
|
||||
$this->assertEquals($element, (string) $selector->getTree());
|
||||
$this->assertEquals($pseudo, (string) $selector->getPseudoElement());
|
||||
}
|
||||
|
||||
/** @dataProvider getSpecificityTestData */
|
||||
public function testSpecificity($source, $value)
|
||||
{
|
||||
$parser = new Parser();
|
||||
$selectors = $parser->parse($source);
|
||||
$this->assertCount(1, $selectors);
|
||||
|
||||
/** @var SelectorNode $selector */
|
||||
$selector = $selectors[0];
|
||||
$this->assertEquals($value, $selector->getSpecificity()->getValue());
|
||||
}
|
||||
|
||||
/** @dataProvider getParseSeriesTestData */
|
||||
public function testParseSeries($series, $a, $b)
|
||||
{
|
||||
$parser = new Parser();
|
||||
$selectors = $parser->parse(sprintf(':nth-child(%s)', $series));
|
||||
$this->assertCount(1, $selectors);
|
||||
|
||||
/** @var FunctionNode $function */
|
||||
$function = $selectors[0]->getTree();
|
||||
$this->assertEquals([$a, $b], Parser::parseSeries($function->getArguments()));
|
||||
}
|
||||
|
||||
/** @dataProvider getParseSeriesExceptionTestData */
|
||||
public function testParseSeriesException($series)
|
||||
{
|
||||
$parser = new Parser();
|
||||
$selectors = $parser->parse(sprintf(':nth-child(%s)', $series));
|
||||
$this->assertCount(1, $selectors);
|
||||
|
||||
/** @var FunctionNode $function */
|
||||
$function = $selectors[0]->getTree();
|
||||
$this->expectException('Symfony\Component\CssSelector\Exception\SyntaxErrorException');
|
||||
Parser::parseSeries($function->getArguments());
|
||||
}
|
||||
|
||||
public function getParserTestData()
|
||||
{
|
||||
return [
|
||||
['*', ['Element[*]']],
|
||||
['*|*', ['Element[*]']],
|
||||
['*|foo', ['Element[foo]']],
|
||||
['foo|*', ['Element[foo|*]']],
|
||||
['foo|bar', ['Element[foo|bar]']],
|
||||
['#foo#bar', ['Hash[Hash[Element[*]#foo]#bar]']],
|
||||
['div>.foo', ['CombinedSelector[Element[div] > Class[Element[*].foo]]']],
|
||||
['div> .foo', ['CombinedSelector[Element[div] > Class[Element[*].foo]]']],
|
||||
['div >.foo', ['CombinedSelector[Element[div] > Class[Element[*].foo]]']],
|
||||
['div > .foo', ['CombinedSelector[Element[div] > Class[Element[*].foo]]']],
|
||||
["div \n> \t \t .foo", ['CombinedSelector[Element[div] > Class[Element[*].foo]]']],
|
||||
['td.foo,.bar', ['Class[Element[td].foo]', 'Class[Element[*].bar]']],
|
||||
['td.foo, .bar', ['Class[Element[td].foo]', 'Class[Element[*].bar]']],
|
||||
["td.foo\t\r\n\f ,\t\r\n\f .bar", ['Class[Element[td].foo]', 'Class[Element[*].bar]']],
|
||||
['td.foo,.bar', ['Class[Element[td].foo]', 'Class[Element[*].bar]']],
|
||||
['td.foo, .bar', ['Class[Element[td].foo]', 'Class[Element[*].bar]']],
|
||||
["td.foo\t\r\n\f ,\t\r\n\f .bar", ['Class[Element[td].foo]', 'Class[Element[*].bar]']],
|
||||
['div, td.foo, div.bar span', ['Element[div]', 'Class[Element[td].foo]', 'CombinedSelector[Class[Element[div].bar] <followed> Element[span]]']],
|
||||
['div > p', ['CombinedSelector[Element[div] > Element[p]]']],
|
||||
['td:first', ['Pseudo[Element[td]:first]']],
|
||||
['td :first', ['CombinedSelector[Element[td] <followed> Pseudo[Element[*]:first]]']],
|
||||
['a[name]', ['Attribute[Element[a][name]]']],
|
||||
["a[ name\t]", ['Attribute[Element[a][name]]']],
|
||||
['a [name]', ['CombinedSelector[Element[a] <followed> Attribute[Element[*][name]]]']],
|
||||
['[name="foo"]', ["Attribute[Element[*][name = 'foo']]"]],
|
||||
["[name='foo[1]']", ["Attribute[Element[*][name = 'foo[1]']]"]],
|
||||
["[name='foo[0][bar]']", ["Attribute[Element[*][name = 'foo[0][bar]']]"]],
|
||||
['a[rel="include"]', ["Attribute[Element[a][rel = 'include']]"]],
|
||||
['a[rel = include]', ["Attribute[Element[a][rel = 'include']]"]],
|
||||
["a[hreflang |= 'en']", ["Attribute[Element[a][hreflang |= 'en']]"]],
|
||||
['a[hreflang|=en]', ["Attribute[Element[a][hreflang |= 'en']]"]],
|
||||
['div:nth-child(10)', ["Function[Element[div]:nth-child(['10'])]"]],
|
||||
[':nth-child(2n+2)', ["Function[Element[*]:nth-child(['2', 'n', '+2'])]"]],
|
||||
['div:nth-of-type(10)', ["Function[Element[div]:nth-of-type(['10'])]"]],
|
||||
['div div:nth-of-type(10) .aclass', ["CombinedSelector[CombinedSelector[Element[div] <followed> Function[Element[div]:nth-of-type(['10'])]] <followed> Class[Element[*].aclass]]"]],
|
||||
['label:only', ['Pseudo[Element[label]:only]']],
|
||||
['a:lang(fr)', ["Function[Element[a]:lang(['fr'])]"]],
|
||||
['div:contains("foo")', ["Function[Element[div]:contains(['foo'])]"]],
|
||||
['div#foobar', ['Hash[Element[div]#foobar]']],
|
||||
['div:not(div.foo)', ['Negation[Element[div]:not(Class[Element[div].foo])]']],
|
||||
['td ~ th', ['CombinedSelector[Element[td] ~ Element[th]]']],
|
||||
['.foo[data-bar][data-baz=0]', ["Attribute[Attribute[Class[Element[*].foo][data-bar]][data-baz = '0']]"]],
|
||||
];
|
||||
}
|
||||
|
||||
public function getParserExceptionTestData()
|
||||
{
|
||||
return [
|
||||
['attributes(href)/html/body/a', SyntaxErrorException::unexpectedToken('selector', new Token(Token::TYPE_DELIMITER, '(', 10))->getMessage()],
|
||||
['attributes(href)', SyntaxErrorException::unexpectedToken('selector', new Token(Token::TYPE_DELIMITER, '(', 10))->getMessage()],
|
||||
['html/body/a', SyntaxErrorException::unexpectedToken('selector', new Token(Token::TYPE_DELIMITER, '/', 4))->getMessage()],
|
||||
[' ', SyntaxErrorException::unexpectedToken('selector', new Token(Token::TYPE_FILE_END, '', 1))->getMessage()],
|
||||
['div, ', SyntaxErrorException::unexpectedToken('selector', new Token(Token::TYPE_FILE_END, '', 5))->getMessage()],
|
||||
[' , div', SyntaxErrorException::unexpectedToken('selector', new Token(Token::TYPE_DELIMITER, ',', 1))->getMessage()],
|
||||
['p, , div', SyntaxErrorException::unexpectedToken('selector', new Token(Token::TYPE_DELIMITER, ',', 3))->getMessage()],
|
||||
['div > ', SyntaxErrorException::unexpectedToken('selector', new Token(Token::TYPE_FILE_END, '', 6))->getMessage()],
|
||||
[' > div', SyntaxErrorException::unexpectedToken('selector', new Token(Token::TYPE_DELIMITER, '>', 2))->getMessage()],
|
||||
['foo|#bar', SyntaxErrorException::unexpectedToken('identifier or "*"', new Token(Token::TYPE_HASH, 'bar', 4))->getMessage()],
|
||||
['#.foo', SyntaxErrorException::unexpectedToken('selector', new Token(Token::TYPE_DELIMITER, '#', 0))->getMessage()],
|
||||
['.#foo', SyntaxErrorException::unexpectedToken('identifier', new Token(Token::TYPE_HASH, 'foo', 1))->getMessage()],
|
||||
[':#foo', SyntaxErrorException::unexpectedToken('identifier', new Token(Token::TYPE_HASH, 'foo', 1))->getMessage()],
|
||||
['[*]', SyntaxErrorException::unexpectedToken('"|"', new Token(Token::TYPE_DELIMITER, ']', 2))->getMessage()],
|
||||
['[foo|]', SyntaxErrorException::unexpectedToken('identifier', new Token(Token::TYPE_DELIMITER, ']', 5))->getMessage()],
|
||||
['[#]', SyntaxErrorException::unexpectedToken('identifier or "*"', new Token(Token::TYPE_DELIMITER, '#', 1))->getMessage()],
|
||||
['[foo=#]', SyntaxErrorException::unexpectedToken('string or identifier', new Token(Token::TYPE_DELIMITER, '#', 5))->getMessage()],
|
||||
[':nth-child()', SyntaxErrorException::unexpectedToken('at least one argument', new Token(Token::TYPE_DELIMITER, ')', 11))->getMessage()],
|
||||
['[href]a', SyntaxErrorException::unexpectedToken('selector', new Token(Token::TYPE_IDENTIFIER, 'a', 6))->getMessage()],
|
||||
['[rel:stylesheet]', SyntaxErrorException::unexpectedToken('operator', new Token(Token::TYPE_DELIMITER, ':', 4))->getMessage()],
|
||||
['[rel=stylesheet', SyntaxErrorException::unexpectedToken('"]"', new Token(Token::TYPE_FILE_END, '', 15))->getMessage()],
|
||||
[':lang(fr', SyntaxErrorException::unexpectedToken('an argument', new Token(Token::TYPE_FILE_END, '', 8))->getMessage()],
|
||||
[':contains("foo', SyntaxErrorException::unclosedString(10)->getMessage()],
|
||||
['foo!', SyntaxErrorException::unexpectedToken('selector', new Token(Token::TYPE_DELIMITER, '!', 3))->getMessage()],
|
||||
];
|
||||
}
|
||||
|
||||
public function getPseudoElementsTestData()
|
||||
{
|
||||
return [
|
||||
['foo', 'Element[foo]', ''],
|
||||
['*', 'Element[*]', ''],
|
||||
[':empty', 'Pseudo[Element[*]:empty]', ''],
|
||||
[':BEfore', 'Element[*]', 'before'],
|
||||
[':aftER', 'Element[*]', 'after'],
|
||||
[':First-Line', 'Element[*]', 'first-line'],
|
||||
[':First-Letter', 'Element[*]', 'first-letter'],
|
||||
['::befoRE', 'Element[*]', 'before'],
|
||||
['::AFter', 'Element[*]', 'after'],
|
||||
['::firsT-linE', 'Element[*]', 'first-line'],
|
||||
['::firsT-letteR', 'Element[*]', 'first-letter'],
|
||||
['::Selection', 'Element[*]', 'selection'],
|
||||
['foo:after', 'Element[foo]', 'after'],
|
||||
['foo::selection', 'Element[foo]', 'selection'],
|
||||
['lorem#ipsum ~ a#b.c[href]:empty::selection', 'CombinedSelector[Hash[Element[lorem]#ipsum] ~ Pseudo[Attribute[Class[Hash[Element[a]#b].c][href]]:empty]]', 'selection'],
|
||||
['video::-webkit-media-controls', 'Element[video]', '-webkit-media-controls'],
|
||||
];
|
||||
}
|
||||
|
||||
public function getSpecificityTestData()
|
||||
{
|
||||
return [
|
||||
['*', 0],
|
||||
[' foo', 1],
|
||||
[':empty ', 10],
|
||||
[':before', 1],
|
||||
['*:before', 1],
|
||||
[':nth-child(2)', 10],
|
||||
['.bar', 10],
|
||||
['[baz]', 10],
|
||||
['[baz="4"]', 10],
|
||||
['[baz^="4"]', 10],
|
||||
['#lipsum', 100],
|
||||
[':not(*)', 0],
|
||||
[':not(foo)', 1],
|
||||
[':not(.foo)', 10],
|
||||
[':not([foo])', 10],
|
||||
[':not(:empty)', 10],
|
||||
[':not(#foo)', 100],
|
||||
['foo:empty', 11],
|
||||
['foo:before', 2],
|
||||
['foo::before', 2],
|
||||
['foo:empty::before', 12],
|
||||
['#lorem + foo#ipsum:first-child > bar:first-line', 213],
|
||||
];
|
||||
}
|
||||
|
||||
public function getParseSeriesTestData()
|
||||
{
|
||||
return [
|
||||
['1n+3', 1, 3],
|
||||
['1n +3', 1, 3],
|
||||
['1n + 3', 1, 3],
|
||||
['1n+ 3', 1, 3],
|
||||
['1n-3', 1, -3],
|
||||
['1n -3', 1, -3],
|
||||
['1n - 3', 1, -3],
|
||||
['1n- 3', 1, -3],
|
||||
['n-5', 1, -5],
|
||||
['odd', 2, 1],
|
||||
['even', 2, 0],
|
||||
['3n', 3, 0],
|
||||
['n', 1, 0],
|
||||
['+n', 1, 0],
|
||||
['-n', -1, 0],
|
||||
['5', 0, 5],
|
||||
];
|
||||
}
|
||||
|
||||
public function getParseSeriesExceptionTestData()
|
||||
{
|
||||
return [
|
||||
['foo'],
|
||||
['n+'],
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -1,102 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\CssSelector\Tests\Parser;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Symfony\Component\CssSelector\Parser\Reader;
|
||||
|
||||
class ReaderTest extends TestCase
|
||||
{
|
||||
public function testIsEOF()
|
||||
{
|
||||
$reader = new Reader('');
|
||||
$this->assertTrue($reader->isEOF());
|
||||
|
||||
$reader = new Reader('hello');
|
||||
$this->assertFalse($reader->isEOF());
|
||||
|
||||
$this->assignPosition($reader, 2);
|
||||
$this->assertFalse($reader->isEOF());
|
||||
|
||||
$this->assignPosition($reader, 5);
|
||||
$this->assertTrue($reader->isEOF());
|
||||
}
|
||||
|
||||
public function testGetRemainingLength()
|
||||
{
|
||||
$reader = new Reader('hello');
|
||||
$this->assertEquals(5, $reader->getRemainingLength());
|
||||
|
||||
$this->assignPosition($reader, 2);
|
||||
$this->assertEquals(3, $reader->getRemainingLength());
|
||||
|
||||
$this->assignPosition($reader, 5);
|
||||
$this->assertEquals(0, $reader->getRemainingLength());
|
||||
}
|
||||
|
||||
public function testGetSubstring()
|
||||
{
|
||||
$reader = new Reader('hello');
|
||||
$this->assertEquals('he', $reader->getSubstring(2));
|
||||
$this->assertEquals('el', $reader->getSubstring(2, 1));
|
||||
|
||||
$this->assignPosition($reader, 2);
|
||||
$this->assertEquals('ll', $reader->getSubstring(2));
|
||||
$this->assertEquals('lo', $reader->getSubstring(2, 1));
|
||||
}
|
||||
|
||||
public function testGetOffset()
|
||||
{
|
||||
$reader = new Reader('hello');
|
||||
$this->assertEquals(2, $reader->getOffset('ll'));
|
||||
$this->assertFalse($reader->getOffset('w'));
|
||||
|
||||
$this->assignPosition($reader, 2);
|
||||
$this->assertEquals(0, $reader->getOffset('ll'));
|
||||
$this->assertFalse($reader->getOffset('he'));
|
||||
}
|
||||
|
||||
public function testFindPattern()
|
||||
{
|
||||
$reader = new Reader('hello');
|
||||
|
||||
$this->assertFalse($reader->findPattern('/world/'));
|
||||
$this->assertEquals(['hello', 'h'], $reader->findPattern('/^([a-z]).*/'));
|
||||
|
||||
$this->assignPosition($reader, 2);
|
||||
$this->assertFalse($reader->findPattern('/^h.*/'));
|
||||
$this->assertEquals(['llo'], $reader->findPattern('/^llo$/'));
|
||||
}
|
||||
|
||||
public function testMoveForward()
|
||||
{
|
||||
$reader = new Reader('hello');
|
||||
$this->assertEquals(0, $reader->getPosition());
|
||||
|
||||
$reader->moveForward(2);
|
||||
$this->assertEquals(2, $reader->getPosition());
|
||||
}
|
||||
|
||||
public function testToEnd()
|
||||
{
|
||||
$reader = new Reader('hello');
|
||||
$reader->moveToEnd();
|
||||
$this->assertTrue($reader->isEOF());
|
||||
}
|
||||
|
||||
private function assignPosition(Reader $reader, $value)
|
||||
{
|
||||
$position = new \ReflectionProperty($reader, 'position');
|
||||
$position->setAccessible(true);
|
||||
$position->setValue($reader, $value);
|
||||
}
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\CssSelector\Tests\Parser\Shortcut;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Symfony\Component\CssSelector\Node\SelectorNode;
|
||||
use Symfony\Component\CssSelector\Parser\Shortcut\ClassParser;
|
||||
|
||||
/**
|
||||
* @author Jean-François Simon <jeanfrancois.simon@sensiolabs.com>
|
||||
*/
|
||||
class ClassParserTest extends TestCase
|
||||
{
|
||||
/** @dataProvider getParseTestData */
|
||||
public function testParse($source, $representation)
|
||||
{
|
||||
$parser = new ClassParser();
|
||||
$selectors = $parser->parse($source);
|
||||
$this->assertCount(1, $selectors);
|
||||
|
||||
/** @var SelectorNode $selector */
|
||||
$selector = $selectors[0];
|
||||
$this->assertEquals($representation, (string) $selector->getTree());
|
||||
}
|
||||
|
||||
public function getParseTestData()
|
||||
{
|
||||
return [
|
||||
['.testclass', 'Class[Element[*].testclass]'],
|
||||
['testel.testclass', 'Class[Element[testel].testclass]'],
|
||||
['testns|.testclass', 'Class[Element[testns|*].testclass]'],
|
||||
['testns|*.testclass', 'Class[Element[testns|*].testclass]'],
|
||||
['testns|testel.testclass', 'Class[Element[testns|testel].testclass]'],
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -1,44 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\CssSelector\Tests\Parser\Shortcut;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Symfony\Component\CssSelector\Node\SelectorNode;
|
||||
use Symfony\Component\CssSelector\Parser\Shortcut\ElementParser;
|
||||
|
||||
/**
|
||||
* @author Jean-François Simon <jeanfrancois.simon@sensiolabs.com>
|
||||
*/
|
||||
class ElementParserTest extends TestCase
|
||||
{
|
||||
/** @dataProvider getParseTestData */
|
||||
public function testParse($source, $representation)
|
||||
{
|
||||
$parser = new ElementParser();
|
||||
$selectors = $parser->parse($source);
|
||||
$this->assertCount(1, $selectors);
|
||||
|
||||
/** @var SelectorNode $selector */
|
||||
$selector = $selectors[0];
|
||||
$this->assertEquals($representation, (string) $selector->getTree());
|
||||
}
|
||||
|
||||
public function getParseTestData()
|
||||
{
|
||||
return [
|
||||
['*', 'Element[*]'],
|
||||
['testel', 'Element[testel]'],
|
||||
['testns|*', 'Element[testns|*]'],
|
||||
['testns|testel', 'Element[testns|testel]'],
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\CssSelector\Tests\Parser\Shortcut;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Symfony\Component\CssSelector\Node\SelectorNode;
|
||||
use Symfony\Component\CssSelector\Parser\Shortcut\EmptyStringParser;
|
||||
|
||||
/**
|
||||
* @author Jean-François Simon <jeanfrancois.simon@sensiolabs.com>
|
||||
*/
|
||||
class EmptyStringParserTest extends TestCase
|
||||
{
|
||||
public function testParse()
|
||||
{
|
||||
$parser = new EmptyStringParser();
|
||||
$selectors = $parser->parse('');
|
||||
$this->assertCount(1, $selectors);
|
||||
|
||||
/** @var SelectorNode $selector */
|
||||
$selector = $selectors[0];
|
||||
$this->assertEquals('Element[*]', (string) $selector->getTree());
|
||||
|
||||
$selectors = $parser->parse('this will produce an empty array');
|
||||
$this->assertCount(0, $selectors);
|
||||
}
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\CssSelector\Tests\Parser\Shortcut;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Symfony\Component\CssSelector\Node\SelectorNode;
|
||||
use Symfony\Component\CssSelector\Parser\Shortcut\HashParser;
|
||||
|
||||
/**
|
||||
* @author Jean-François Simon <jeanfrancois.simon@sensiolabs.com>
|
||||
*/
|
||||
class HashParserTest extends TestCase
|
||||
{
|
||||
/** @dataProvider getParseTestData */
|
||||
public function testParse($source, $representation)
|
||||
{
|
||||
$parser = new HashParser();
|
||||
$selectors = $parser->parse($source);
|
||||
$this->assertCount(1, $selectors);
|
||||
|
||||
/** @var SelectorNode $selector */
|
||||
$selector = $selectors[0];
|
||||
$this->assertEquals($representation, (string) $selector->getTree());
|
||||
}
|
||||
|
||||
public function getParseTestData()
|
||||
{
|
||||
return [
|
||||
['#testid', 'Hash[Element[*]#testid]'],
|
||||
['testel#testid', 'Hash[Element[testel]#testid]'],
|
||||
['testns|#testid', 'Hash[Element[testns|*]#testid]'],
|
||||
['testns|*#testid', 'Hash[Element[testns|*]#testid]'],
|
||||
['testns|testel#testid', 'Hash[Element[testns|testel]#testid]'],
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -1,96 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\CssSelector\Tests\Parser;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Symfony\Component\CssSelector\Parser\Token;
|
||||
use Symfony\Component\CssSelector\Parser\TokenStream;
|
||||
|
||||
class TokenStreamTest extends TestCase
|
||||
{
|
||||
public function testGetNext()
|
||||
{
|
||||
$stream = new TokenStream();
|
||||
$stream->push($t1 = new Token(Token::TYPE_IDENTIFIER, 'h1', 0));
|
||||
$stream->push($t2 = new Token(Token::TYPE_DELIMITER, '.', 2));
|
||||
$stream->push($t3 = new Token(Token::TYPE_IDENTIFIER, 'title', 3));
|
||||
|
||||
$this->assertSame($t1, $stream->getNext());
|
||||
$this->assertSame($t2, $stream->getNext());
|
||||
$this->assertSame($t3, $stream->getNext());
|
||||
}
|
||||
|
||||
public function testGetPeek()
|
||||
{
|
||||
$stream = new TokenStream();
|
||||
$stream->push($t1 = new Token(Token::TYPE_IDENTIFIER, 'h1', 0));
|
||||
$stream->push($t2 = new Token(Token::TYPE_DELIMITER, '.', 2));
|
||||
$stream->push($t3 = new Token(Token::TYPE_IDENTIFIER, 'title', 3));
|
||||
|
||||
$this->assertSame($t1, $stream->getPeek());
|
||||
$this->assertSame($t1, $stream->getNext());
|
||||
$this->assertSame($t2, $stream->getPeek());
|
||||
$this->assertSame($t2, $stream->getPeek());
|
||||
$this->assertSame($t2, $stream->getNext());
|
||||
}
|
||||
|
||||
public function testGetNextIdentifier()
|
||||
{
|
||||
$stream = new TokenStream();
|
||||
$stream->push(new Token(Token::TYPE_IDENTIFIER, 'h1', 0));
|
||||
|
||||
$this->assertEquals('h1', $stream->getNextIdentifier());
|
||||
}
|
||||
|
||||
public function testFailToGetNextIdentifier()
|
||||
{
|
||||
$this->expectException('Symfony\Component\CssSelector\Exception\SyntaxErrorException');
|
||||
|
||||
$stream = new TokenStream();
|
||||
$stream->push(new Token(Token::TYPE_DELIMITER, '.', 2));
|
||||
$stream->getNextIdentifier();
|
||||
}
|
||||
|
||||
public function testGetNextIdentifierOrStar()
|
||||
{
|
||||
$stream = new TokenStream();
|
||||
|
||||
$stream->push(new Token(Token::TYPE_IDENTIFIER, 'h1', 0));
|
||||
$this->assertEquals('h1', $stream->getNextIdentifierOrStar());
|
||||
|
||||
$stream->push(new Token(Token::TYPE_DELIMITER, '*', 0));
|
||||
$this->assertNull($stream->getNextIdentifierOrStar());
|
||||
}
|
||||
|
||||
public function testFailToGetNextIdentifierOrStar()
|
||||
{
|
||||
$this->expectException('Symfony\Component\CssSelector\Exception\SyntaxErrorException');
|
||||
|
||||
$stream = new TokenStream();
|
||||
$stream->push(new Token(Token::TYPE_DELIMITER, '.', 2));
|
||||
$stream->getNextIdentifierOrStar();
|
||||
}
|
||||
|
||||
public function testSkipWhitespace()
|
||||
{
|
||||
$stream = new TokenStream();
|
||||
$stream->push($t1 = new Token(Token::TYPE_IDENTIFIER, 'h1', 0));
|
||||
$stream->push($t2 = new Token(Token::TYPE_WHITESPACE, ' ', 2));
|
||||
$stream->push($t3 = new Token(Token::TYPE_IDENTIFIER, 'h1', 3));
|
||||
|
||||
$stream->skipWhitespace();
|
||||
$this->assertSame($t1, $stream->getNext());
|
||||
|
||||
$stream->skipWhitespace();
|
||||
$this->assertSame($t3, $stream->getNext());
|
||||
}
|
||||
}
|
||||
@@ -1,48 +0,0 @@
|
||||
<html id="html"><head>
|
||||
<link id="link-href" href="foo" />
|
||||
<link id="link-nohref" />
|
||||
</head><body>
|
||||
<div id="outer-div">
|
||||
<a id="name-anchor" name="foo"></a>
|
||||
<a id="tag-anchor" rel="tag" href="http://localhost/foo">link</a>
|
||||
<a id="nofollow-anchor" rel="nofollow" href="https://example.org">
|
||||
link</a>
|
||||
<ol id="first-ol" class="a b c">
|
||||
<li id="first-li">content</li>
|
||||
<li id="second-li" lang="En-us">
|
||||
<div id="li-div">
|
||||
</div>
|
||||
</li>
|
||||
<li id="third-li" class="ab c"></li>
|
||||
<li id="fourth-li" class="ab
|
||||
c"></li>
|
||||
<li id="fifth-li"></li>
|
||||
<li id="sixth-li"></li>
|
||||
<li id="seventh-li"> </li>
|
||||
</ol>
|
||||
<p id="paragraph">
|
||||
<b id="p-b">hi</b> <em id="p-em">there</em>
|
||||
<b id="p-b2">guy</b>
|
||||
<input type="checkbox" id="checkbox-unchecked" />
|
||||
<input type="checkbox" id="checkbox-disabled" disabled="" />
|
||||
<input type="text" id="text-checked" checked="checked" />
|
||||
<input type="hidden" />
|
||||
<input type="hidden" disabled="disabled" />
|
||||
<input type="checkbox" id="checkbox-checked" checked="checked" />
|
||||
<input type="checkbox" id="checkbox-disabled-checked"
|
||||
disabled="disabled" checked="checked" />
|
||||
<fieldset id="fieldset" disabled="disabled">
|
||||
<input type="checkbox" id="checkbox-fieldset-disabled" />
|
||||
<input type="hidden" />
|
||||
</fieldset>
|
||||
</p>
|
||||
<ol id="second-ol">
|
||||
</ol>
|
||||
<map name="dummymap">
|
||||
<area shape="circle" coords="200,250,25" href="foo.html" id="area-href" />
|
||||
<area shape="default" id="area-nohref" />
|
||||
</map>
|
||||
</div>
|
||||
<div id="foobar-div" foobar="ab bc
|
||||
cde"><span id="foobar-span"></span></div>
|
||||
</body></html>
|
||||
@@ -1,11 +0,0 @@
|
||||
<test>
|
||||
<a id="first" xml:lang="en">a</a>
|
||||
<b id="second" xml:lang="en-US">b</b>
|
||||
<c id="third" xml:lang="en-Nz">c</c>
|
||||
<d id="fourth" xml:lang="En-us">d</d>
|
||||
<e id="fifth" xml:lang="fr">e</e>
|
||||
<f id="sixth" xml:lang="ru">f</f>
|
||||
<g id="seventh" xml:lang="de">
|
||||
<h id="eighth" xml:lang="zh"/>
|
||||
</g>
|
||||
</test>
|
||||
@@ -1,308 +0,0 @@
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
|
||||
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en" debug="true">
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
|
||||
</head>
|
||||
<body>
|
||||
<div id="test">
|
||||
<div class="dialog">
|
||||
<h2>As You Like It</h2>
|
||||
<div id="playwright">
|
||||
by William Shakespeare
|
||||
</div>
|
||||
<div class="dialog scene thirdClass" id="scene1">
|
||||
<h3>ACT I, SCENE III. A room in the palace.</h3>
|
||||
<div class="dialog">
|
||||
<div class="direction">Enter CELIA and ROSALIND</div>
|
||||
</div>
|
||||
<div id="speech1" class="character">CELIA</div>
|
||||
<div class="dialog">
|
||||
<div id="scene1.3.1">Why, cousin! why, Rosalind! Cupid have mercy! not a word?</div>
|
||||
</div>
|
||||
<div id="speech2" class="character">ROSALIND</div>
|
||||
<div class="dialog">
|
||||
<div id="scene1.3.2">Not one to throw at a dog.</div>
|
||||
</div>
|
||||
<div id="speech3" class="character">CELIA</div>
|
||||
<div class="dialog">
|
||||
<div id="scene1.3.3">No, thy words are too precious to be cast away upon</div>
|
||||
<div id="scene1.3.4">curs; throw some of them at me; come, lame me with reasons.</div>
|
||||
</div>
|
||||
<div id="speech4" class="character">ROSALIND</div>
|
||||
<div id="speech5" class="character">CELIA</div>
|
||||
<div class="dialog">
|
||||
<div id="scene1.3.8">But is all this for your father?</div>
|
||||
</div>
|
||||
<div class="dialog">
|
||||
<div id="scene1.3.5">Then there were two cousins laid up; when the one</div>
|
||||
<div id="scene1.3.6">should be lamed with reasons and the other mad</div>
|
||||
<div id="scene1.3.7">without any.</div>
|
||||
</div>
|
||||
<div id="speech6" class="character">ROSALIND</div>
|
||||
<div class="dialog">
|
||||
<div id="scene1.3.9">No, some of it is for my child's father. O, how</div>
|
||||
<div id="scene1.3.10">full of briers is this working-day world!</div>
|
||||
</div>
|
||||
<div id="speech7" class="character">CELIA</div>
|
||||
<div class="dialog">
|
||||
<div id="scene1.3.11">They are but burs, cousin, thrown upon thee in</div>
|
||||
<div id="scene1.3.12">holiday foolery: if we walk not in the trodden</div>
|
||||
<div id="scene1.3.13">paths our very petticoats will catch them.</div>
|
||||
</div>
|
||||
<div id="speech8" class="character">ROSALIND</div>
|
||||
<div class="dialog">
|
||||
<div id="scene1.3.14">I could shake them off my coat: these burs are in my heart.</div>
|
||||
</div>
|
||||
<div id="speech9" class="character">CELIA</div>
|
||||
<div class="dialog">
|
||||
<div id="scene1.3.15">Hem them away.</div>
|
||||
</div>
|
||||
<div id="speech10" class="character">ROSALIND</div>
|
||||
<div class="dialog">
|
||||
<div id="scene1.3.16">I would try, if I could cry 'hem' and have him.</div>
|
||||
</div>
|
||||
<div id="speech11" class="character">CELIA</div>
|
||||
<div class="dialog">
|
||||
<div id="scene1.3.17">Come, come, wrestle with thy affections.</div>
|
||||
</div>
|
||||
<div id="speech12" class="character">ROSALIND</div>
|
||||
<div class="dialog">
|
||||
<div id="scene1.3.18">O, they take the part of a better wrestler than myself!</div>
|
||||
</div>
|
||||
<div id="speech13" class="character">CELIA</div>
|
||||
<div class="dialog">
|
||||
<div id="scene1.3.19">O, a good wish upon you! you will try in time, in</div>
|
||||
<div id="scene1.3.20">despite of a fall. But, turning these jests out of</div>
|
||||
<div id="scene1.3.21">service, let us talk in good earnest: is it</div>
|
||||
<div id="scene1.3.22">possible, on such a sudden, you should fall into so</div>
|
||||
<div id="scene1.3.23">strong a liking with old Sir Rowland's youngest son?</div>
|
||||
</div>
|
||||
<div id="speech14" class="character">ROSALIND</div>
|
||||
<div class="dialog">
|
||||
<div id="scene1.3.24">The duke my father loved his father dearly.</div>
|
||||
</div>
|
||||
<div id="speech15" class="character">CELIA</div>
|
||||
<div class="dialog">
|
||||
<div id="scene1.3.25">Doth it therefore ensue that you should love his son</div>
|
||||
<div id="scene1.3.26">dearly? By this kind of chase, I should hate him,</div>
|
||||
<div id="scene1.3.27">for my father hated his father dearly; yet I hate</div>
|
||||
<div id="scene1.3.28">not Orlando.</div>
|
||||
</div>
|
||||
<div id="speech16" class="character">ROSALIND</div>
|
||||
<div title="wtf" class="dialog">
|
||||
<div id="scene1.3.29">No, faith, hate him not, for my sake.</div>
|
||||
</div>
|
||||
<div id="speech17" class="character">CELIA</div>
|
||||
<div class="dialog">
|
||||
<div id="scene1.3.30">Why should I not? doth he not deserve well?</div>
|
||||
</div>
|
||||
<div id="speech18" class="character">ROSALIND</div>
|
||||
<div class="dialog">
|
||||
<div id="scene1.3.31">Let me love him for that, and do you love him</div>
|
||||
<div id="scene1.3.32">because I do. Look, here comes the duke.</div>
|
||||
</div>
|
||||
<div id="speech19" class="character">CELIA</div>
|
||||
<div class="dialog">
|
||||
<div id="scene1.3.33">With his eyes full of anger.</div>
|
||||
<div class="direction">Enter DUKE FREDERICK, with Lords</div>
|
||||
</div>
|
||||
<div id="speech20" class="character">DUKE FREDERICK</div>
|
||||
<div class="dialog">
|
||||
<div id="scene1.3.34">Mistress, dispatch you with your safest haste</div>
|
||||
<div id="scene1.3.35">And get you from our court.</div>
|
||||
</div>
|
||||
<div id="speech21" class="character">ROSALIND</div>
|
||||
<div class="dialog">
|
||||
<div id="scene1.3.36">Me, uncle?</div>
|
||||
</div>
|
||||
<div id="speech22" class="character">DUKE FREDERICK</div>
|
||||
<div class="dialog">
|
||||
<div id="scene1.3.37">You, cousin</div>
|
||||
<div id="scene1.3.38">Within these ten days if that thou be'st found</div>
|
||||
<div id="scene1.3.39">So near our public court as twenty miles,</div>
|
||||
<div id="scene1.3.40">Thou diest for it.</div>
|
||||
</div>
|
||||
<div id="speech23" class="character">ROSALIND</div>
|
||||
<div class="dialog">
|
||||
<div id="scene1.3.41"> I do beseech your grace,</div>
|
||||
<div id="scene1.3.42">Let me the knowledge of my fault bear with me:</div>
|
||||
<div id="scene1.3.43">If with myself I hold intelligence</div>
|
||||
<div id="scene1.3.44">Or have acquaintance with mine own desires,</div>
|
||||
<div id="scene1.3.45">If that I do not dream or be not frantic,--</div>
|
||||
<div id="scene1.3.46">As I do trust I am not--then, dear uncle,</div>
|
||||
<div id="scene1.3.47">Never so much as in a thought unborn</div>
|
||||
<div id="scene1.3.48">Did I offend your highness.</div>
|
||||
</div>
|
||||
<div id="speech24" class="character">DUKE FREDERICK</div>
|
||||
<div class="dialog">
|
||||
<div id="scene1.3.49">Thus do all traitors:</div>
|
||||
<div id="scene1.3.50">If their purgation did consist in words,</div>
|
||||
<div id="scene1.3.51">They are as innocent as grace itself:</div>
|
||||
<div id="scene1.3.52">Let it suffice thee that I trust thee not.</div>
|
||||
</div>
|
||||
<div id="speech25" class="character">ROSALIND</div>
|
||||
<div class="dialog">
|
||||
<div id="scene1.3.53">Yet your mistrust cannot make me a traitor:</div>
|
||||
<div id="scene1.3.54">Tell me whereon the likelihood depends.</div>
|
||||
</div>
|
||||
<div id="speech26" class="character">DUKE FREDERICK</div>
|
||||
<div class="dialog">
|
||||
<div id="scene1.3.55">Thou art thy father's daughter; there's enough.</div>
|
||||
</div>
|
||||
<div id="speech27" class="character">ROSALIND</div>
|
||||
<div class="dialog">
|
||||
<div id="scene1.3.56">So was I when your highness took his dukedom;</div>
|
||||
<div id="scene1.3.57">So was I when your highness banish'd him:</div>
|
||||
<div id="scene1.3.58">Treason is not inherited, my lord;</div>
|
||||
<div id="scene1.3.59">Or, if we did derive it from our friends,</div>
|
||||
<div id="scene1.3.60">What's that to me? my father was no traitor:</div>
|
||||
<div id="scene1.3.61">Then, good my liege, mistake me not so much</div>
|
||||
<div id="scene1.3.62">To think my poverty is treacherous.</div>
|
||||
</div>
|
||||
<div id="speech28" class="character">CELIA</div>
|
||||
<div class="dialog">
|
||||
<div id="scene1.3.63">Dear sovereign, hear me speak.</div>
|
||||
</div>
|
||||
<div id="speech29" class="character">DUKE FREDERICK</div>
|
||||
<div class="dialog">
|
||||
<div id="scene1.3.64">Ay, Celia; we stay'd her for your sake,</div>
|
||||
<div id="scene1.3.65">Else had she with her father ranged along.</div>
|
||||
</div>
|
||||
<div id="speech30" class="character">CELIA</div>
|
||||
<div class="dialog">
|
||||
<div id="scene1.3.66">I did not then entreat to have her stay;</div>
|
||||
<div id="scene1.3.67">It was your pleasure and your own remorse:</div>
|
||||
<div id="scene1.3.68">I was too young that time to value her;</div>
|
||||
<div id="scene1.3.69">But now I know her: if she be a traitor,</div>
|
||||
<div id="scene1.3.70">Why so am I; we still have slept together,</div>
|
||||
<div id="scene1.3.71">Rose at an instant, learn'd, play'd, eat together,</div>
|
||||
<div id="scene1.3.72">And wheresoever we went, like Juno's swans,</div>
|
||||
<div id="scene1.3.73">Still we went coupled and inseparable.</div>
|
||||
</div>
|
||||
<div id="speech31" class="character">DUKE FREDERICK</div>
|
||||
<div class="dialog">
|
||||
<div id="scene1.3.74">She is too subtle for thee; and her smoothness,</div>
|
||||
<div id="scene1.3.75">Her very silence and her patience</div>
|
||||
<div id="scene1.3.76">Speak to the people, and they pity her.</div>
|
||||
<div id="scene1.3.77">Thou art a fool: she robs thee of thy name;</div>
|
||||
<div id="scene1.3.78">And thou wilt show more bright and seem more virtuous</div>
|
||||
<div id="scene1.3.79">When she is gone. Then open not thy lips:</div>
|
||||
<div id="scene1.3.80">Firm and irrevocable is my doom</div>
|
||||
<div id="scene1.3.81">Which I have pass'd upon her; she is banish'd.</div>
|
||||
</div>
|
||||
<div id="speech32" class="character">CELIA</div>
|
||||
<div class="dialog">
|
||||
<div id="scene1.3.82">Pronounce that sentence then on me, my liege:</div>
|
||||
<div id="scene1.3.83">I cannot live out of her company.</div>
|
||||
</div>
|
||||
<div id="speech33" class="character">DUKE FREDERICK</div>
|
||||
<div class="dialog">
|
||||
<div id="scene1.3.84">You are a fool. You, niece, provide yourself:</div>
|
||||
<div id="scene1.3.85">If you outstay the time, upon mine honour,</div>
|
||||
<div id="scene1.3.86">And in the greatness of my word, you die.</div>
|
||||
<div class="direction">Exeunt DUKE FREDERICK and Lords</div>
|
||||
</div>
|
||||
<div id="speech34" class="character">CELIA</div>
|
||||
<div class="dialog">
|
||||
<div id="scene1.3.87">O my poor Rosalind, whither wilt thou go?</div>
|
||||
<div id="scene1.3.88">Wilt thou change fathers? I will give thee mine.</div>
|
||||
<div id="scene1.3.89">I charge thee, be not thou more grieved than I am.</div>
|
||||
</div>
|
||||
<div id="speech35" class="character">ROSALIND</div>
|
||||
<div class="dialog">
|
||||
<div id="scene1.3.90">I have more cause.</div>
|
||||
</div>
|
||||
<div id="speech36" class="character">CELIA</div>
|
||||
<div class="dialog">
|
||||
<div id="scene1.3.91"> Thou hast not, cousin;</div>
|
||||
<div id="scene1.3.92">Prithee be cheerful: know'st thou not, the duke</div>
|
||||
<div id="scene1.3.93">Hath banish'd me, his daughter?</div>
|
||||
</div>
|
||||
<div id="speech37" class="character">ROSALIND</div>
|
||||
<div class="dialog">
|
||||
<div id="scene1.3.94">That he hath not.</div>
|
||||
</div>
|
||||
<div id="speech38" class="character">CELIA</div>
|
||||
<div class="dialog">
|
||||
<div id="scene1.3.95">No, hath not? Rosalind lacks then the love</div>
|
||||
<div id="scene1.3.96">Which teacheth thee that thou and I am one:</div>
|
||||
<div id="scene1.3.97">Shall we be sunder'd? shall we part, sweet girl?</div>
|
||||
<div id="scene1.3.98">No: let my father seek another heir.</div>
|
||||
<div id="scene1.3.99">Therefore devise with me how we may fly,</div>
|
||||
<div id="scene1.3.100">Whither to go and what to bear with us;</div>
|
||||
<div id="scene1.3.101">And do not seek to take your change upon you,</div>
|
||||
<div id="scene1.3.102">To bear your griefs yourself and leave me out;</div>
|
||||
<div id="scene1.3.103">For, by this heaven, now at our sorrows pale,</div>
|
||||
<div id="scene1.3.104">Say what thou canst, I'll go along with thee.</div>
|
||||
</div>
|
||||
<div id="speech39" class="character">ROSALIND</div>
|
||||
<div class="dialog">
|
||||
<div id="scene1.3.105">Why, whither shall we go?</div>
|
||||
</div>
|
||||
<div id="speech40" class="character">CELIA</div>
|
||||
<div class="dialog">
|
||||
<div id="scene1.3.106">To seek my uncle in the forest of Arden.</div>
|
||||
</div>
|
||||
<div id="speech41" class="character">ROSALIND</div>
|
||||
<div class="dialog">
|
||||
<div id="scene1.3.107">Alas, what danger will it be to us,</div>
|
||||
<div id="scene1.3.108">Maids as we are, to travel forth so far!</div>
|
||||
<div id="scene1.3.109">Beauty provoketh thieves sooner than gold.</div>
|
||||
</div>
|
||||
<div id="speech42" class="character">CELIA</div>
|
||||
<div class="dialog">
|
||||
<div id="scene1.3.110">I'll put myself in poor and mean attire</div>
|
||||
<div id="scene1.3.111">And with a kind of umber smirch my face;</div>
|
||||
<div id="scene1.3.112">The like do you: so shall we pass along</div>
|
||||
<div id="scene1.3.113">And never stir assailants.</div>
|
||||
</div>
|
||||
<div id="speech43" class="character">ROSALIND</div>
|
||||
<div class="dialog">
|
||||
<div id="scene1.3.114">Were it not better,</div>
|
||||
<div id="scene1.3.115">Because that I am more than common tall,</div>
|
||||
<div id="scene1.3.116">That I did suit me all points like a man?</div>
|
||||
<div id="scene1.3.117">A gallant curtle-axe upon my thigh,</div>
|
||||
<div id="scene1.3.118">A boar-spear in my hand; and--in my heart</div>
|
||||
<div id="scene1.3.119">Lie there what hidden woman's fear there will--</div>
|
||||
<div id="scene1.3.120">We'll have a swashing and a martial outside,</div>
|
||||
<div id="scene1.3.121">As many other mannish cowards have</div>
|
||||
<div id="scene1.3.122">That do outface it with their semblances.</div>
|
||||
</div>
|
||||
<div id="speech44" class="character">CELIA</div>
|
||||
<div class="dialog">
|
||||
<div id="scene1.3.123">What shall I call thee when thou art a man?</div>
|
||||
</div>
|
||||
<div id="speech45" class="character">ROSALIND</div>
|
||||
<div class="dialog">
|
||||
<div id="scene1.3.124">I'll have no worse a name than Jove's own page;</div>
|
||||
<div id="scene1.3.125">And therefore look you call me Ganymede.</div>
|
||||
<div id="scene1.3.126">But what will you be call'd?</div>
|
||||
</div>
|
||||
<div id="speech46" class="character">CELIA</div>
|
||||
<div class="dialog">
|
||||
<div id="scene1.3.127">Something that hath a reference to my state</div>
|
||||
<div id="scene1.3.128">No longer Celia, but Aliena.</div>
|
||||
</div>
|
||||
<div id="speech47" class="character">ROSALIND</div>
|
||||
<div class="dialog">
|
||||
<div id="scene1.3.129">But, cousin, what if we assay'd to steal</div>
|
||||
<div id="scene1.3.130">The clownish fool out of your father's court?</div>
|
||||
<div id="scene1.3.131">Would he not be a comfort to our travel?</div>
|
||||
</div>
|
||||
<div id="speech48" class="character">CELIA</div>
|
||||
<div class="dialog">
|
||||
<div id="scene1.3.132">He'll go along o'er the wide world with me;</div>
|
||||
<div id="scene1.3.133">Leave me alone to woo him. Let's away,</div>
|
||||
<div id="scene1.3.134">And get our jewels and our wealth together,</div>
|
||||
<div id="scene1.3.135">Devise the fittest time and safest way</div>
|
||||
<div id="scene1.3.136">To hide us from pursuit that will be made</div>
|
||||
<div id="scene1.3.137">After my flight. Now go we in content</div>
|
||||
<div id="scene1.3.138">To liberty and not to banishment.</div>
|
||||
<div class="direction">Exeunt</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,398 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\CssSelector\Tests\XPath;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Symfony\Component\CssSelector\Node\ElementNode;
|
||||
use Symfony\Component\CssSelector\Node\FunctionNode;
|
||||
use Symfony\Component\CssSelector\Parser\Parser;
|
||||
use Symfony\Component\CssSelector\XPath\Extension\HtmlExtension;
|
||||
use Symfony\Component\CssSelector\XPath\Translator;
|
||||
use Symfony\Component\CssSelector\XPath\XPathExpr;
|
||||
|
||||
class TranslatorTest extends TestCase
|
||||
{
|
||||
/** @dataProvider getXpathLiteralTestData */
|
||||
public function testXpathLiteral($value, $literal)
|
||||
{
|
||||
$this->assertEquals($literal, Translator::getXpathLiteral($value));
|
||||
}
|
||||
|
||||
/** @dataProvider getCssToXPathTestData */
|
||||
public function testCssToXPath($css, $xpath)
|
||||
{
|
||||
$translator = new Translator();
|
||||
$translator->registerExtension(new HtmlExtension($translator));
|
||||
$this->assertEquals($xpath, $translator->cssToXPath($css, ''));
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \Symfony\Component\CssSelector\Exception\ExpressionErrorException
|
||||
*/
|
||||
public function testCssToXPathPseudoElement()
|
||||
{
|
||||
$translator = new Translator();
|
||||
$translator->registerExtension(new HtmlExtension($translator));
|
||||
$translator->cssToXPath('e::first-line');
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \Symfony\Component\CssSelector\Exception\ExpressionErrorException
|
||||
*/
|
||||
public function testGetExtensionNotExistsExtension()
|
||||
{
|
||||
$translator = new Translator();
|
||||
$translator->registerExtension(new HtmlExtension($translator));
|
||||
$translator->getExtension('fake');
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \Symfony\Component\CssSelector\Exception\ExpressionErrorException
|
||||
*/
|
||||
public function testAddCombinationNotExistsExtension()
|
||||
{
|
||||
$translator = new Translator();
|
||||
$translator->registerExtension(new HtmlExtension($translator));
|
||||
$parser = new Parser();
|
||||
$xpath = $parser->parse('*')[0];
|
||||
$combinedXpath = $parser->parse('*')[0];
|
||||
$translator->addCombination('fake', $xpath, $combinedXpath);
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \Symfony\Component\CssSelector\Exception\ExpressionErrorException
|
||||
*/
|
||||
public function testAddFunctionNotExistsFunction()
|
||||
{
|
||||
$translator = new Translator();
|
||||
$translator->registerExtension(new HtmlExtension($translator));
|
||||
$xpath = new XPathExpr();
|
||||
$function = new FunctionNode(new ElementNode(), 'fake');
|
||||
$translator->addFunction($xpath, $function);
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \Symfony\Component\CssSelector\Exception\ExpressionErrorException
|
||||
*/
|
||||
public function testAddPseudoClassNotExistsClass()
|
||||
{
|
||||
$translator = new Translator();
|
||||
$translator->registerExtension(new HtmlExtension($translator));
|
||||
$xpath = new XPathExpr();
|
||||
$translator->addPseudoClass($xpath, 'fake');
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \Symfony\Component\CssSelector\Exception\ExpressionErrorException
|
||||
*/
|
||||
public function testAddAttributeMatchingClassNotExistsClass()
|
||||
{
|
||||
$translator = new Translator();
|
||||
$translator->registerExtension(new HtmlExtension($translator));
|
||||
$xpath = new XPathExpr();
|
||||
$translator->addAttributeMatching($xpath, '', '', '');
|
||||
}
|
||||
|
||||
/** @dataProvider getXmlLangTestData */
|
||||
public function testXmlLang($css, array $elementsId)
|
||||
{
|
||||
$translator = new Translator();
|
||||
$document = new \SimpleXMLElement(file_get_contents(__DIR__.'/Fixtures/lang.xml'));
|
||||
$elements = $document->xpath($translator->cssToXPath($css));
|
||||
$this->assertCount(\count($elementsId), $elements);
|
||||
foreach ($elements as $element) {
|
||||
$this->assertTrue(\in_array($element->attributes()->id, $elementsId));
|
||||
}
|
||||
}
|
||||
|
||||
/** @dataProvider getHtmlIdsTestData */
|
||||
public function testHtmlIds($css, array $elementsId)
|
||||
{
|
||||
$translator = new Translator();
|
||||
$translator->registerExtension(new HtmlExtension($translator));
|
||||
$document = new \DOMDocument();
|
||||
$document->strictErrorChecking = false;
|
||||
$internalErrors = libxml_use_internal_errors(true);
|
||||
$document->loadHTMLFile(__DIR__.'/Fixtures/ids.html');
|
||||
$document = simplexml_import_dom($document);
|
||||
$elements = $document->xpath($translator->cssToXPath($css));
|
||||
$this->assertCount(\count($elementsId), $elementsId);
|
||||
foreach ($elements as $element) {
|
||||
if (null !== $element->attributes()->id) {
|
||||
$this->assertTrue(\in_array($element->attributes()->id, $elementsId));
|
||||
}
|
||||
}
|
||||
libxml_clear_errors();
|
||||
libxml_use_internal_errors($internalErrors);
|
||||
}
|
||||
|
||||
/** @dataProvider getHtmlShakespearTestData */
|
||||
public function testHtmlShakespear($css, $count)
|
||||
{
|
||||
$translator = new Translator();
|
||||
$translator->registerExtension(new HtmlExtension($translator));
|
||||
$document = new \DOMDocument();
|
||||
$document->strictErrorChecking = false;
|
||||
$document->loadHTMLFile(__DIR__.'/Fixtures/shakespear.html');
|
||||
$document = simplexml_import_dom($document);
|
||||
$bodies = $document->xpath('//body');
|
||||
$elements = $bodies[0]->xpath($translator->cssToXPath($css));
|
||||
$this->assertCount($count, $elements);
|
||||
}
|
||||
|
||||
public function getXpathLiteralTestData()
|
||||
{
|
||||
return [
|
||||
['foo', "'foo'"],
|
||||
["foo's bar", '"foo\'s bar"'],
|
||||
["foo's \"middle\" bar", 'concat(\'foo\', "\'", \'s "middle" bar\')'],
|
||||
["foo's 'middle' \"bar\"", 'concat(\'foo\', "\'", \'s \', "\'", \'middle\', "\'", \' "bar"\')'],
|
||||
];
|
||||
}
|
||||
|
||||
public function getCssToXPathTestData()
|
||||
{
|
||||
return [
|
||||
['*', '*'],
|
||||
['e', 'e'],
|
||||
['*|e', 'e'],
|
||||
['e|f', 'e:f'],
|
||||
['e[foo]', 'e[@foo]'],
|
||||
['e[foo|bar]', 'e[@foo:bar]'],
|
||||
['e[foo="bar"]', "e[@foo = 'bar']"],
|
||||
['e[foo~="bar"]', "e[@foo and contains(concat(' ', normalize-space(@foo), ' '), ' bar ')]"],
|
||||
['e[foo^="bar"]', "e[@foo and starts-with(@foo, 'bar')]"],
|
||||
['e[foo$="bar"]', "e[@foo and substring(@foo, string-length(@foo)-2) = 'bar']"],
|
||||
['e[foo*="bar"]', "e[@foo and contains(@foo, 'bar')]"],
|
||||
['e[foo!="bar"]', "e[not(@foo) or @foo != 'bar']"],
|
||||
['e[foo!="bar"][foo!="baz"]', "e[(not(@foo) or @foo != 'bar') and (not(@foo) or @foo != 'baz')]"],
|
||||
['e[hreflang|="en"]', "e[@hreflang and (@hreflang = 'en' or starts-with(@hreflang, 'en-'))]"],
|
||||
['e:nth-child(1)', "*/*[(name() = 'e') and (position() = 1)]"],
|
||||
['e:nth-last-child(1)', "*/*[(name() = 'e') and (position() = last() - 0)]"],
|
||||
['e:nth-last-child(2n+2)', "*/*[(name() = 'e') and (last() - position() - 1 >= 0 and (last() - position() - 1) mod 2 = 0)]"],
|
||||
['e:nth-of-type(1)', '*/e[position() = 1]'],
|
||||
['e:nth-last-of-type(1)', '*/e[position() = last() - 0]'],
|
||||
['div e:nth-last-of-type(1) .aclass', "div/descendant-or-self::*/e[position() = last() - 0]/descendant-or-self::*/*[@class and contains(concat(' ', normalize-space(@class), ' '), ' aclass ')]"],
|
||||
['e:first-child', "*/*[(name() = 'e') and (position() = 1)]"],
|
||||
['e:last-child', "*/*[(name() = 'e') and (position() = last())]"],
|
||||
['e:first-of-type', '*/e[position() = 1]'],
|
||||
['e:last-of-type', '*/e[position() = last()]'],
|
||||
['e:only-child', "*/*[(name() = 'e') and (last() = 1)]"],
|
||||
['e:only-of-type', 'e[last() = 1]'],
|
||||
['e:empty', 'e[not(*) and not(string-length())]'],
|
||||
['e:EmPTY', 'e[not(*) and not(string-length())]'],
|
||||
['e:root', 'e[not(parent::*)]'],
|
||||
['e:hover', 'e[0]'],
|
||||
['e:contains("foo")', "e[contains(string(.), 'foo')]"],
|
||||
['e:ConTains(foo)', "e[contains(string(.), 'foo')]"],
|
||||
['e.warning', "e[@class and contains(concat(' ', normalize-space(@class), ' '), ' warning ')]"],
|
||||
['e#myid', "e[@id = 'myid']"],
|
||||
['e:not(:nth-child(odd))', 'e[not(position() - 1 >= 0 and (position() - 1) mod 2 = 0)]'],
|
||||
['e:nOT(*)', 'e[0]'],
|
||||
['e f', 'e/descendant-or-self::*/f'],
|
||||
['e > f', 'e/f'],
|
||||
['e + f', "e/following-sibling::*[(name() = 'f') and (position() = 1)]"],
|
||||
['e ~ f', 'e/following-sibling::f'],
|
||||
['div#container p', "div[@id = 'container']/descendant-or-self::*/p"],
|
||||
];
|
||||
}
|
||||
|
||||
public function getXmlLangTestData()
|
||||
{
|
||||
return [
|
||||
[':lang("EN")', ['first', 'second', 'third', 'fourth']],
|
||||
[':lang("en-us")', ['second', 'fourth']],
|
||||
[':lang(en-nz)', ['third']],
|
||||
[':lang(fr)', ['fifth']],
|
||||
[':lang(ru)', ['sixth']],
|
||||
[":lang('ZH')", ['eighth']],
|
||||
[':lang(de) :lang(zh)', ['eighth']],
|
||||
[':lang(en), :lang(zh)', ['first', 'second', 'third', 'fourth', 'eighth']],
|
||||
[':lang(es)', []],
|
||||
];
|
||||
}
|
||||
|
||||
public function getHtmlIdsTestData()
|
||||
{
|
||||
return [
|
||||
['div', ['outer-div', 'li-div', 'foobar-div']],
|
||||
['DIV', ['outer-div', 'li-div', 'foobar-div']], // case-insensitive in HTML
|
||||
['div div', ['li-div']],
|
||||
['div, div div', ['outer-div', 'li-div', 'foobar-div']],
|
||||
['a[name]', ['name-anchor']],
|
||||
['a[NAme]', ['name-anchor']], // case-insensitive in HTML:
|
||||
['a[rel]', ['tag-anchor', 'nofollow-anchor']],
|
||||
['a[rel="tag"]', ['tag-anchor']],
|
||||
['a[href*="localhost"]', ['tag-anchor']],
|
||||
['a[href*=""]', []],
|
||||
['a[href^="http"]', ['tag-anchor', 'nofollow-anchor']],
|
||||
['a[href^="http:"]', ['tag-anchor']],
|
||||
['a[href^=""]', []],
|
||||
['a[href$="org"]', ['nofollow-anchor']],
|
||||
['a[href$=""]', []],
|
||||
['div[foobar~="bc"]', ['foobar-div']],
|
||||
['div[foobar~="cde"]', ['foobar-div']],
|
||||
['[foobar~="ab bc"]', ['foobar-div']],
|
||||
['[foobar~=""]', []],
|
||||
['[foobar~=" \t"]', []],
|
||||
['div[foobar~="cd"]', []],
|
||||
['*[lang|="En"]', ['second-li']],
|
||||
['[lang|="En-us"]', ['second-li']],
|
||||
// Attribute values are case sensitive
|
||||
['*[lang|="en"]', []],
|
||||
['[lang|="en-US"]', []],
|
||||
['*[lang|="e"]', []],
|
||||
// ... :lang() is not.
|
||||
[':lang("EN")', ['second-li', 'li-div']],
|
||||
['*:lang(en-US)', ['second-li', 'li-div']],
|
||||
[':lang("e")', []],
|
||||
['li:nth-child(3)', ['third-li']],
|
||||
['li:nth-child(10)', []],
|
||||
['li:nth-child(2n)', ['second-li', 'fourth-li', 'sixth-li']],
|
||||
['li:nth-child(even)', ['second-li', 'fourth-li', 'sixth-li']],
|
||||
['li:nth-child(2n+0)', ['second-li', 'fourth-li', 'sixth-li']],
|
||||
['li:nth-child(+2n+1)', ['first-li', 'third-li', 'fifth-li', 'seventh-li']],
|
||||
['li:nth-child(odd)', ['first-li', 'third-li', 'fifth-li', 'seventh-li']],
|
||||
['li:nth-child(2n+4)', ['fourth-li', 'sixth-li']],
|
||||
['li:nth-child(3n+1)', ['first-li', 'fourth-li', 'seventh-li']],
|
||||
['li:nth-child(n)', ['first-li', 'second-li', 'third-li', 'fourth-li', 'fifth-li', 'sixth-li', 'seventh-li']],
|
||||
['li:nth-child(n-1)', ['first-li', 'second-li', 'third-li', 'fourth-li', 'fifth-li', 'sixth-li', 'seventh-li']],
|
||||
['li:nth-child(n+1)', ['first-li', 'second-li', 'third-li', 'fourth-li', 'fifth-li', 'sixth-li', 'seventh-li']],
|
||||
['li:nth-child(n+3)', ['third-li', 'fourth-li', 'fifth-li', 'sixth-li', 'seventh-li']],
|
||||
['li:nth-child(-n)', []],
|
||||
['li:nth-child(-n-1)', []],
|
||||
['li:nth-child(-n+1)', ['first-li']],
|
||||
['li:nth-child(-n+3)', ['first-li', 'second-li', 'third-li']],
|
||||
['li:nth-last-child(0)', []],
|
||||
['li:nth-last-child(2n)', ['second-li', 'fourth-li', 'sixth-li']],
|
||||
['li:nth-last-child(even)', ['second-li', 'fourth-li', 'sixth-li']],
|
||||
['li:nth-last-child(2n+2)', ['second-li', 'fourth-li', 'sixth-li']],
|
||||
['li:nth-last-child(n)', ['first-li', 'second-li', 'third-li', 'fourth-li', 'fifth-li', 'sixth-li', 'seventh-li']],
|
||||
['li:nth-last-child(n-1)', ['first-li', 'second-li', 'third-li', 'fourth-li', 'fifth-li', 'sixth-li', 'seventh-li']],
|
||||
['li:nth-last-child(n-3)', ['first-li', 'second-li', 'third-li', 'fourth-li', 'fifth-li', 'sixth-li', 'seventh-li']],
|
||||
['li:nth-last-child(n+1)', ['first-li', 'second-li', 'third-li', 'fourth-li', 'fifth-li', 'sixth-li', 'seventh-li']],
|
||||
['li:nth-last-child(n+3)', ['first-li', 'second-li', 'third-li', 'fourth-li', 'fifth-li']],
|
||||
['li:nth-last-child(-n)', []],
|
||||
['li:nth-last-child(-n-1)', []],
|
||||
['li:nth-last-child(-n+1)', ['seventh-li']],
|
||||
['li:nth-last-child(-n+3)', ['fifth-li', 'sixth-li', 'seventh-li']],
|
||||
['ol:first-of-type', ['first-ol']],
|
||||
['ol:nth-child(1)', ['first-ol']],
|
||||
['ol:nth-of-type(2)', ['second-ol']],
|
||||
['ol:nth-last-of-type(1)', ['second-ol']],
|
||||
['span:only-child', ['foobar-span']],
|
||||
['li div:only-child', ['li-div']],
|
||||
['div *:only-child', ['li-div', 'foobar-span']],
|
||||
['p:only-of-type', ['paragraph']],
|
||||
['a:empty', ['name-anchor']],
|
||||
['a:EMpty', ['name-anchor']],
|
||||
['li:empty', ['third-li', 'fourth-li', 'fifth-li', 'sixth-li']],
|
||||
[':root', ['html']],
|
||||
['html:root', ['html']],
|
||||
['li:root', []],
|
||||
['* :root', []],
|
||||
['*:contains("link")', ['html', 'outer-div', 'tag-anchor', 'nofollow-anchor']],
|
||||
[':CONtains("link")', ['html', 'outer-div', 'tag-anchor', 'nofollow-anchor']],
|
||||
['*:contains("LInk")', []], // case sensitive
|
||||
['*:contains("e")', ['html', 'nil', 'outer-div', 'first-ol', 'first-li', 'paragraph', 'p-em']],
|
||||
['*:contains("E")', []], // case-sensitive
|
||||
['.a', ['first-ol']],
|
||||
['.b', ['first-ol']],
|
||||
['*.a', ['first-ol']],
|
||||
['ol.a', ['first-ol']],
|
||||
['.c', ['first-ol', 'third-li', 'fourth-li']],
|
||||
['*.c', ['first-ol', 'third-li', 'fourth-li']],
|
||||
['ol *.c', ['third-li', 'fourth-li']],
|
||||
['ol li.c', ['third-li', 'fourth-li']],
|
||||
['li ~ li.c', ['third-li', 'fourth-li']],
|
||||
['ol > li.c', ['third-li', 'fourth-li']],
|
||||
['#first-li', ['first-li']],
|
||||
['li#first-li', ['first-li']],
|
||||
['*#first-li', ['first-li']],
|
||||
['li div', ['li-div']],
|
||||
['li > div', ['li-div']],
|
||||
['div div', ['li-div']],
|
||||
['div > div', []],
|
||||
['div>.c', ['first-ol']],
|
||||
['div > .c', ['first-ol']],
|
||||
['div + div', ['foobar-div']],
|
||||
['a ~ a', ['tag-anchor', 'nofollow-anchor']],
|
||||
['a[rel="tag"] ~ a', ['nofollow-anchor']],
|
||||
['ol#first-ol li:last-child', ['seventh-li']],
|
||||
['ol#first-ol *:last-child', ['li-div', 'seventh-li']],
|
||||
['#outer-div:first-child', ['outer-div']],
|
||||
['#outer-div :first-child', ['name-anchor', 'first-li', 'li-div', 'p-b', 'checkbox-fieldset-disabled', 'area-href']],
|
||||
['a[href]', ['tag-anchor', 'nofollow-anchor']],
|
||||
[':not(*)', []],
|
||||
['a:not([href])', ['name-anchor']],
|
||||
['ol :Not(li[class])', ['first-li', 'second-li', 'li-div', 'fifth-li', 'sixth-li', 'seventh-li']],
|
||||
// HTML-specific
|
||||
[':link', ['link-href', 'tag-anchor', 'nofollow-anchor', 'area-href']],
|
||||
[':visited', []],
|
||||
[':enabled', ['link-href', 'tag-anchor', 'nofollow-anchor', 'checkbox-unchecked', 'text-checked', 'checkbox-checked', 'area-href']],
|
||||
[':disabled', ['checkbox-disabled', 'checkbox-disabled-checked', 'fieldset', 'checkbox-fieldset-disabled']],
|
||||
[':checked', ['checkbox-checked', 'checkbox-disabled-checked']],
|
||||
];
|
||||
}
|
||||
|
||||
public function getHtmlShakespearTestData()
|
||||
{
|
||||
return [
|
||||
['*', 246],
|
||||
['div:contains(CELIA)', 26],
|
||||
['div:only-child', 22], // ?
|
||||
['div:nth-child(even)', 106],
|
||||
['div:nth-child(2n)', 106],
|
||||
['div:nth-child(odd)', 137],
|
||||
['div:nth-child(2n+1)', 137],
|
||||
['div:nth-child(n)', 243],
|
||||
['div:last-child', 53],
|
||||
['div:first-child', 51],
|
||||
['div > div', 242],
|
||||
['div + div', 190],
|
||||
['div ~ div', 190],
|
||||
['body', 1],
|
||||
['body div', 243],
|
||||
['div', 243],
|
||||
['div div', 242],
|
||||
['div div div', 241],
|
||||
['div, div, div', 243],
|
||||
['div, a, span', 243],
|
||||
['.dialog', 51],
|
||||
['div.dialog', 51],
|
||||
['div .dialog', 51],
|
||||
['div.character, div.dialog', 99],
|
||||
['div.direction.dialog', 0],
|
||||
['div.dialog.direction', 0],
|
||||
['div.dialog.scene', 1],
|
||||
['div.scene.scene', 1],
|
||||
['div.scene .scene', 0],
|
||||
['div.direction .dialog ', 0],
|
||||
['div .dialog .direction', 4],
|
||||
['div.dialog .dialog .direction', 4],
|
||||
['#speech5', 1],
|
||||
['div#speech5', 1],
|
||||
['div #speech5', 1],
|
||||
['div.scene div.dialog', 49],
|
||||
['div#scene1 div.dialog div', 142],
|
||||
['#scene1 #speech1', 1],
|
||||
['div[class]', 103],
|
||||
['div[class=dialog]', 50],
|
||||
['div[class^=dia]', 51],
|
||||
['div[class$=log]', 50],
|
||||
['div[class*=sce]', 1],
|
||||
['div[class|=dialog]', 50], // ? Seems right
|
||||
['div[class!=madeup]', 243], // ? Seems right
|
||||
['div[class~=dialog]', 51], // ? Seems right
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -26,7 +26,7 @@ abstract class AbstractExtension implements ExtensionInterface
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getNodeTranslators()
|
||||
public function getNodeTranslators(): array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
@@ -34,7 +34,7 @@ abstract class AbstractExtension implements ExtensionInterface
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getCombinationTranslators()
|
||||
public function getCombinationTranslators(): array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
@@ -42,7 +42,7 @@ abstract class AbstractExtension implements ExtensionInterface
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getFunctionTranslators()
|
||||
public function getFunctionTranslators(): array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
@@ -50,7 +50,7 @@ abstract class AbstractExtension implements ExtensionInterface
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getPseudoClassTranslators()
|
||||
public function getPseudoClassTranslators(): array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
@@ -58,7 +58,7 @@ abstract class AbstractExtension implements ExtensionInterface
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getAttributeMatchingTranslators()
|
||||
public function getAttributeMatchingTranslators(): array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@ class AttributeMatchingExtension extends AbstractExtension
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getAttributeMatchingTranslators()
|
||||
public function getAttributeMatchingTranslators(): array
|
||||
{
|
||||
return [
|
||||
'exists' => [$this, 'translateExists'],
|
||||
@@ -112,7 +112,7 @@ class AttributeMatchingExtension extends AbstractExtension
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getName()
|
||||
public function getName(): string
|
||||
{
|
||||
return 'attribute-matching';
|
||||
}
|
||||
|
||||
@@ -38,26 +38,17 @@ class CombinationExtension extends AbstractExtension
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return XPathExpr
|
||||
*/
|
||||
public function translateDescendant(XPathExpr $xpath, XPathExpr $combinedXpath): XPathExpr
|
||||
{
|
||||
return $xpath->join('/descendant-or-self::*/', $combinedXpath);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return XPathExpr
|
||||
*/
|
||||
public function translateChild(XPathExpr $xpath, XPathExpr $combinedXpath)
|
||||
public function translateChild(XPathExpr $xpath, XPathExpr $combinedXpath): XPathExpr
|
||||
{
|
||||
return $xpath->join('/', $combinedXpath);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return XPathExpr
|
||||
*/
|
||||
public function translateDirectAdjacent(XPathExpr $xpath, XPathExpr $combinedXpath)
|
||||
public function translateDirectAdjacent(XPathExpr $xpath, XPathExpr $combinedXpath): XPathExpr
|
||||
{
|
||||
return $xpath
|
||||
->join('/following-sibling::', $combinedXpath)
|
||||
@@ -65,10 +56,7 @@ class CombinationExtension extends AbstractExtension
|
||||
->addCondition('position() = 1');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return XPathExpr
|
||||
*/
|
||||
public function translateIndirectAdjacent(XPathExpr $xpath, XPathExpr $combinedXpath)
|
||||
public function translateIndirectAdjacent(XPathExpr $xpath, XPathExpr $combinedXpath): XPathExpr
|
||||
{
|
||||
return $xpath->join('/following-sibling::', $combinedXpath);
|
||||
}
|
||||
@@ -76,7 +64,7 @@ class CombinationExtension extends AbstractExtension
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getName()
|
||||
public function getName(): string
|
||||
{
|
||||
return 'combination';
|
||||
}
|
||||
|
||||
@@ -30,40 +30,38 @@ interface ExtensionInterface
|
||||
*
|
||||
* @return callable[]
|
||||
*/
|
||||
public function getNodeTranslators();
|
||||
public function getNodeTranslators(): array;
|
||||
|
||||
/**
|
||||
* Returns combination translators.
|
||||
*
|
||||
* @return callable[]
|
||||
*/
|
||||
public function getCombinationTranslators();
|
||||
public function getCombinationTranslators(): array;
|
||||
|
||||
/**
|
||||
* Returns function translators.
|
||||
*
|
||||
* @return callable[]
|
||||
*/
|
||||
public function getFunctionTranslators();
|
||||
public function getFunctionTranslators(): array;
|
||||
|
||||
/**
|
||||
* Returns pseudo-class translators.
|
||||
*
|
||||
* @return callable[]
|
||||
*/
|
||||
public function getPseudoClassTranslators();
|
||||
public function getPseudoClassTranslators(): array;
|
||||
|
||||
/**
|
||||
* Returns attribute operation translators.
|
||||
*
|
||||
* @return callable[]
|
||||
*/
|
||||
public function getAttributeMatchingTranslators();
|
||||
public function getAttributeMatchingTranslators(): array;
|
||||
|
||||
/**
|
||||
* Returns extension name.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getName();
|
||||
public function getName(): string;
|
||||
}
|
||||
|
||||
@@ -33,7 +33,7 @@ class FunctionExtension extends AbstractExtension
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getFunctionTranslators()
|
||||
public function getFunctionTranslators(): array
|
||||
{
|
||||
return [
|
||||
'nth-child' => [$this, 'translateNthChild'],
|
||||
@@ -164,7 +164,7 @@ class FunctionExtension extends AbstractExtension
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getName()
|
||||
public function getName(): string
|
||||
{
|
||||
return 'function';
|
||||
}
|
||||
|
||||
@@ -39,7 +39,7 @@ class HtmlExtension extends AbstractExtension
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getPseudoClassTranslators()
|
||||
public function getPseudoClassTranslators(): array
|
||||
{
|
||||
return [
|
||||
'checked' => [$this, 'translateChecked'],
|
||||
@@ -56,17 +56,14 @@ class HtmlExtension extends AbstractExtension
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getFunctionTranslators()
|
||||
public function getFunctionTranslators(): array
|
||||
{
|
||||
return [
|
||||
'lang' => [$this, 'translateLang'],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return XPathExpr
|
||||
*/
|
||||
public function translateChecked(XPathExpr $xpath)
|
||||
public function translateChecked(XPathExpr $xpath): XPathExpr
|
||||
{
|
||||
return $xpath->addCondition(
|
||||
'(@checked '
|
||||
@@ -75,18 +72,12 @@ class HtmlExtension extends AbstractExtension
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return XPathExpr
|
||||
*/
|
||||
public function translateLink(XPathExpr $xpath)
|
||||
public function translateLink(XPathExpr $xpath): XPathExpr
|
||||
{
|
||||
return $xpath->addCondition("@href and (name(.) = 'a' or name(.) = 'link' or name(.) = 'area')");
|
||||
}
|
||||
|
||||
/**
|
||||
* @return XPathExpr
|
||||
*/
|
||||
public function translateDisabled(XPathExpr $xpath)
|
||||
public function translateDisabled(XPathExpr $xpath): XPathExpr
|
||||
{
|
||||
return $xpath->addCondition(
|
||||
'('
|
||||
@@ -112,10 +103,7 @@ class HtmlExtension extends AbstractExtension
|
||||
// todo: in the second half, add "and is not a descendant of that fieldset element's first legend element child, if any."
|
||||
}
|
||||
|
||||
/**
|
||||
* @return XPathExpr
|
||||
*/
|
||||
public function translateEnabled(XPathExpr $xpath)
|
||||
public function translateEnabled(XPathExpr $xpath): XPathExpr
|
||||
{
|
||||
return $xpath->addCondition(
|
||||
'('
|
||||
@@ -149,11 +137,9 @@ class HtmlExtension extends AbstractExtension
|
||||
}
|
||||
|
||||
/**
|
||||
* @return XPathExpr
|
||||
*
|
||||
* @throws ExpressionErrorException
|
||||
*/
|
||||
public function translateLang(XPathExpr $xpath, FunctionNode $function)
|
||||
public function translateLang(XPathExpr $xpath, FunctionNode $function): XPathExpr
|
||||
{
|
||||
$arguments = $function->getArguments();
|
||||
foreach ($arguments as $token) {
|
||||
@@ -171,34 +157,22 @@ class HtmlExtension extends AbstractExtension
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return XPathExpr
|
||||
*/
|
||||
public function translateSelected(XPathExpr $xpath)
|
||||
public function translateSelected(XPathExpr $xpath): XPathExpr
|
||||
{
|
||||
return $xpath->addCondition("(@selected and name(.) = 'option')");
|
||||
}
|
||||
|
||||
/**
|
||||
* @return XPathExpr
|
||||
*/
|
||||
public function translateInvalid(XPathExpr $xpath)
|
||||
public function translateInvalid(XPathExpr $xpath): XPathExpr
|
||||
{
|
||||
return $xpath->addCondition('0');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return XPathExpr
|
||||
*/
|
||||
public function translateHover(XPathExpr $xpath)
|
||||
public function translateHover(XPathExpr $xpath): XPathExpr
|
||||
{
|
||||
return $xpath->addCondition('0');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return XPathExpr
|
||||
*/
|
||||
public function translateVisited(XPathExpr $xpath)
|
||||
public function translateVisited(XPathExpr $xpath): XPathExpr
|
||||
{
|
||||
return $xpath->addCondition('0');
|
||||
}
|
||||
@@ -206,7 +180,7 @@ class HtmlExtension extends AbstractExtension
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getName()
|
||||
public function getName(): string
|
||||
{
|
||||
return 'html';
|
||||
}
|
||||
|
||||
@@ -41,7 +41,7 @@ class NodeExtension extends AbstractExtension
|
||||
/**
|
||||
* @return $this
|
||||
*/
|
||||
public function setFlag(int $flag, bool $on)
|
||||
public function setFlag(int $flag, bool $on): self
|
||||
{
|
||||
if ($on && !$this->hasFlag($flag)) {
|
||||
$this->flags += $flag;
|
||||
@@ -62,7 +62,7 @@ class NodeExtension extends AbstractExtension
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getNodeTranslators()
|
||||
public function getNodeTranslators(): array
|
||||
{
|
||||
return [
|
||||
'Selector' => [$this, 'translateSelector'],
|
||||
@@ -185,7 +185,7 @@ class NodeExtension extends AbstractExtension
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getName()
|
||||
public function getName(): string
|
||||
{
|
||||
return 'node';
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@ class PseudoClassExtension extends AbstractExtension
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getPseudoClassTranslators()
|
||||
public function getPseudoClassTranslators(): array
|
||||
{
|
||||
return [
|
||||
'root' => [$this, 'translateRoot'],
|
||||
@@ -43,18 +43,12 @@ class PseudoClassExtension extends AbstractExtension
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return XPathExpr
|
||||
*/
|
||||
public function translateRoot(XPathExpr $xpath)
|
||||
public function translateRoot(XPathExpr $xpath): XPathExpr
|
||||
{
|
||||
return $xpath->addCondition('not(parent::*)');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return XPathExpr
|
||||
*/
|
||||
public function translateFirstChild(XPathExpr $xpath)
|
||||
public function translateFirstChild(XPathExpr $xpath): XPathExpr
|
||||
{
|
||||
return $xpath
|
||||
->addStarPrefix()
|
||||
@@ -62,10 +56,7 @@ class PseudoClassExtension extends AbstractExtension
|
||||
->addCondition('position() = 1');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return XPathExpr
|
||||
*/
|
||||
public function translateLastChild(XPathExpr $xpath)
|
||||
public function translateLastChild(XPathExpr $xpath): XPathExpr
|
||||
{
|
||||
return $xpath
|
||||
->addStarPrefix()
|
||||
@@ -74,11 +65,9 @@ class PseudoClassExtension extends AbstractExtension
|
||||
}
|
||||
|
||||
/**
|
||||
* @return XPathExpr
|
||||
*
|
||||
* @throws ExpressionErrorException
|
||||
*/
|
||||
public function translateFirstOfType(XPathExpr $xpath)
|
||||
public function translateFirstOfType(XPathExpr $xpath): XPathExpr
|
||||
{
|
||||
if ('*' === $xpath->getElement()) {
|
||||
throw new ExpressionErrorException('"*:first-of-type" is not implemented.');
|
||||
@@ -90,11 +79,9 @@ class PseudoClassExtension extends AbstractExtension
|
||||
}
|
||||
|
||||
/**
|
||||
* @return XPathExpr
|
||||
*
|
||||
* @throws ExpressionErrorException
|
||||
*/
|
||||
public function translateLastOfType(XPathExpr $xpath)
|
||||
public function translateLastOfType(XPathExpr $xpath): XPathExpr
|
||||
{
|
||||
if ('*' === $xpath->getElement()) {
|
||||
throw new ExpressionErrorException('"*:last-of-type" is not implemented.');
|
||||
@@ -105,10 +92,7 @@ class PseudoClassExtension extends AbstractExtension
|
||||
->addCondition('position() = last()');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return XPathExpr
|
||||
*/
|
||||
public function translateOnlyChild(XPathExpr $xpath)
|
||||
public function translateOnlyChild(XPathExpr $xpath): XPathExpr
|
||||
{
|
||||
return $xpath
|
||||
->addStarPrefix()
|
||||
@@ -116,24 +100,14 @@ class PseudoClassExtension extends AbstractExtension
|
||||
->addCondition('last() = 1');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return XPathExpr
|
||||
*
|
||||
* @throws ExpressionErrorException
|
||||
*/
|
||||
public function translateOnlyOfType(XPathExpr $xpath)
|
||||
public function translateOnlyOfType(XPathExpr $xpath): XPathExpr
|
||||
{
|
||||
if ('*' === $xpath->getElement()) {
|
||||
throw new ExpressionErrorException('"*:only-of-type" is not implemented.');
|
||||
}
|
||||
$element = $xpath->getElement();
|
||||
|
||||
return $xpath->addCondition('last() = 1');
|
||||
return $xpath->addCondition(sprintf('count(preceding-sibling::%s)=0 and count(following-sibling::%s)=0', $element, $element));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return XPathExpr
|
||||
*/
|
||||
public function translateEmpty(XPathExpr $xpath)
|
||||
public function translateEmpty(XPathExpr $xpath): XPathExpr
|
||||
{
|
||||
return $xpath->addCondition('not(*) and not(string-length())');
|
||||
}
|
||||
@@ -141,7 +115,7 @@ class PseudoClassExtension extends AbstractExtension
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getName()
|
||||
public function getName(): string
|
||||
{
|
||||
return 'pseudo-class';
|
||||
}
|
||||
|
||||
@@ -114,6 +114,9 @@ class Translator implements TranslatorInterface
|
||||
return ($prefix ?: '').$this->nodeToXPath($selector);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return $this
|
||||
*/
|
||||
public function registerExtension(Extension\ExtensionInterface $extension): self
|
||||
{
|
||||
$this->extensions[$extension->getName()] = $extension;
|
||||
@@ -139,6 +142,9 @@ class Translator implements TranslatorInterface
|
||||
return $this->extensions[$name];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return $this
|
||||
*/
|
||||
public function registerParserShortcut(ParserInterface $shortcut): self
|
||||
{
|
||||
$this->shortcutParsers[] = $shortcut;
|
||||
@@ -209,7 +215,7 @@ class Translator implements TranslatorInterface
|
||||
/**
|
||||
* @return SelectorNode[]
|
||||
*/
|
||||
private function parseSelectors(string $css)
|
||||
private function parseSelectors(string $css): array
|
||||
{
|
||||
foreach ($this->shortcutParsers as $shortcut) {
|
||||
$tokens = $shortcut->parse($css);
|
||||
|
||||
4
vendor/symfony/css-selector/composer.json
vendored
4
vendor/symfony/css-selector/composer.json
vendored
@@ -20,7 +20,7 @@
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"php": "^7.1.3"
|
||||
"php": "^7.2.5"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": { "Symfony\\Component\\CssSelector\\": "" },
|
||||
@@ -31,7 +31,7 @@
|
||||
"minimum-stability": "dev",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "4.3-dev"
|
||||
"dev-master": "5.0-dev"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
31
vendor/symfony/css-selector/phpunit.xml.dist
vendored
31
vendor/symfony/css-selector/phpunit.xml.dist
vendored
@@ -1,31 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:noNamespaceSchemaLocation="http://schema.phpunit.de/5.2/phpunit.xsd"
|
||||
backupGlobals="false"
|
||||
colors="true"
|
||||
bootstrap="vendor/autoload.php"
|
||||
failOnRisky="true"
|
||||
failOnWarning="true"
|
||||
>
|
||||
<php>
|
||||
<ini name="error_reporting" value="-1" />
|
||||
</php>
|
||||
|
||||
<testsuites>
|
||||
<testsuite name="Symfony CssSelector Component Test Suite">
|
||||
<directory>./Tests/</directory>
|
||||
</testsuite>
|
||||
</testsuites>
|
||||
|
||||
<filter>
|
||||
<whitelist>
|
||||
<directory>./</directory>
|
||||
<exclude>
|
||||
<directory>./Resources</directory>
|
||||
<directory>./Tests</directory>
|
||||
<directory>./vendor</directory>
|
||||
</exclude>
|
||||
</whitelist>
|
||||
</filter>
|
||||
</phpunit>
|
||||
Reference in New Issue
Block a user