Version 1
[yaffs-website] / vendor / symfony / css-selector / Tests / XPath / TranslatorTest.php
1 <?php
2
3 /*
4  * This file is part of the Symfony package.
5  *
6  * (c) Fabien Potencier <fabien@symfony.com>
7  *
8  * For the full copyright and license information, please view the LICENSE
9  * file that was distributed with this source code.
10  */
11
12 namespace Symfony\Component\CssSelector\Tests\XPath;
13
14 use PHPUnit\Framework\TestCase;
15 use Symfony\Component\CssSelector\XPath\Extension\HtmlExtension;
16 use Symfony\Component\CssSelector\XPath\Translator;
17
18 class TranslatorTest extends TestCase
19 {
20     /** @dataProvider getXpathLiteralTestData */
21     public function testXpathLiteral($value, $literal)
22     {
23         $this->assertEquals($literal, Translator::getXpathLiteral($value));
24     }
25
26     /** @dataProvider getCssToXPathTestData */
27     public function testCssToXPath($css, $xpath)
28     {
29         $translator = new Translator();
30         $translator->registerExtension(new HtmlExtension($translator));
31         $this->assertEquals($xpath, $translator->cssToXPath($css, ''));
32     }
33
34     /** @dataProvider getXmlLangTestData */
35     public function testXmlLang($css, array $elementsId)
36     {
37         $translator = new Translator();
38         $document = new \SimpleXMLElement(file_get_contents(__DIR__.'/Fixtures/lang.xml'));
39         $elements = $document->xpath($translator->cssToXPath($css));
40         $this->assertEquals(count($elementsId), count($elements));
41         foreach ($elements as $element) {
42             $this->assertTrue(in_array($element->attributes()->id, $elementsId));
43         }
44     }
45
46     /** @dataProvider getHtmlIdsTestData */
47     public function testHtmlIds($css, array $elementsId)
48     {
49         $translator = new Translator();
50         $translator->registerExtension(new HtmlExtension($translator));
51         $document = new \DOMDocument();
52         $document->strictErrorChecking = false;
53         $internalErrors = libxml_use_internal_errors(true);
54         $document->loadHTMLFile(__DIR__.'/Fixtures/ids.html');
55         $document = simplexml_import_dom($document);
56         $elements = $document->xpath($translator->cssToXPath($css));
57         $this->assertCount(count($elementsId), $elementsId);
58         foreach ($elements as $element) {
59             if (null !== $element->attributes()->id) {
60                 $this->assertTrue(in_array($element->attributes()->id, $elementsId));
61             }
62         }
63         libxml_clear_errors();
64         libxml_use_internal_errors($internalErrors);
65     }
66
67     /** @dataProvider getHtmlShakespearTestData */
68     public function testHtmlShakespear($css, $count)
69     {
70         $translator = new Translator();
71         $translator->registerExtension(new HtmlExtension($translator));
72         $document = new \DOMDocument();
73         $document->strictErrorChecking = false;
74         $document->loadHTMLFile(__DIR__.'/Fixtures/shakespear.html');
75         $document = simplexml_import_dom($document);
76         $bodies = $document->xpath('//body');
77         $elements = $bodies[0]->xpath($translator->cssToXPath($css));
78         $this->assertCount($count, $elements);
79     }
80
81     public function getXpathLiteralTestData()
82     {
83         return array(
84             array('foo', "'foo'"),
85             array("foo's bar", '"foo\'s bar"'),
86             array("foo's \"middle\" bar", 'concat(\'foo\', "\'", \'s "middle" bar\')'),
87             array("foo's 'middle' \"bar\"", 'concat(\'foo\', "\'", \'s \', "\'", \'middle\', "\'", \' "bar"\')'),
88         );
89     }
90
91     public function getCssToXPathTestData()
92     {
93         return array(
94             array('*', '*'),
95             array('e', 'e'),
96             array('*|e', 'e'),
97             array('e|f', 'e:f'),
98             array('e[foo]', 'e[@foo]'),
99             array('e[foo|bar]', 'e[@foo:bar]'),
100             array('e[foo="bar"]', "e[@foo = 'bar']"),
101             array('e[foo~="bar"]', "e[@foo and contains(concat(' ', normalize-space(@foo), ' '), ' bar ')]"),
102             array('e[foo^="bar"]', "e[@foo and starts-with(@foo, 'bar')]"),
103             array('e[foo$="bar"]', "e[@foo and substring(@foo, string-length(@foo)-2) = 'bar']"),
104             array('e[foo*="bar"]', "e[@foo and contains(@foo, 'bar')]"),
105             array('e[hreflang|="en"]', "e[@hreflang and (@hreflang = 'en' or starts-with(@hreflang, 'en-'))]"),
106             array('e:nth-child(1)', "*/*[name() = 'e' and (position() = 1)]"),
107             array('e:nth-last-child(1)', "*/*[name() = 'e' and (position() = last() - 0)]"),
108             array('e:nth-last-child(2n+2)', "*/*[name() = 'e' and (last() - position() - 1 >= 0 and (last() - position() - 1) mod 2 = 0)]"),
109             array('e:nth-of-type(1)', '*/e[position() = 1]'),
110             array('e:nth-last-of-type(1)', '*/e[position() = last() - 0]'),
111             array('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 ')]"),
112             array('e:first-child', "*/*[name() = 'e' and (position() = 1)]"),
113             array('e:last-child', "*/*[name() = 'e' and (position() = last())]"),
114             array('e:first-of-type', '*/e[position() = 1]'),
115             array('e:last-of-type', '*/e[position() = last()]'),
116             array('e:only-child', "*/*[name() = 'e' and (last() = 1)]"),
117             array('e:only-of-type', 'e[last() = 1]'),
118             array('e:empty', 'e[not(*) and not(string-length())]'),
119             array('e:EmPTY', 'e[not(*) and not(string-length())]'),
120             array('e:root', 'e[not(parent::*)]'),
121             array('e:hover', 'e[0]'),
122             array('e:contains("foo")', "e[contains(string(.), 'foo')]"),
123             array('e:ConTains(foo)', "e[contains(string(.), 'foo')]"),
124             array('e.warning', "e[@class and contains(concat(' ', normalize-space(@class), ' '), ' warning ')]"),
125             array('e#myid', "e[@id = 'myid']"),
126             array('e:not(:nth-child(odd))', 'e[not(position() - 1 >= 0 and (position() - 1) mod 2 = 0)]'),
127             array('e:nOT(*)', 'e[0]'),
128             array('e f', 'e/descendant-or-self::*/f'),
129             array('e > f', 'e/f'),
130             array('e + f', "e/following-sibling::*[name() = 'f' and (position() = 1)]"),
131             array('e ~ f', 'e/following-sibling::f'),
132             array('div#container p', "div[@id = 'container']/descendant-or-self::*/p"),
133         );
134     }
135
136     public function getXmlLangTestData()
137     {
138         return array(
139             array(':lang("EN")', array('first', 'second', 'third', 'fourth')),
140             array(':lang("en-us")', array('second', 'fourth')),
141             array(':lang(en-nz)', array('third')),
142             array(':lang(fr)', array('fifth')),
143             array(':lang(ru)', array('sixth')),
144             array(":lang('ZH')", array('eighth')),
145             array(':lang(de) :lang(zh)', array('eighth')),
146             array(':lang(en), :lang(zh)', array('first', 'second', 'third', 'fourth', 'eighth')),
147             array(':lang(es)', array()),
148         );
149     }
150
151     public function getHtmlIdsTestData()
152     {
153         return array(
154             array('div', array('outer-div', 'li-div', 'foobar-div')),
155             array('DIV', array('outer-div', 'li-div', 'foobar-div')),  // case-insensitive in HTML
156             array('div div', array('li-div')),
157             array('div, div div', array('outer-div', 'li-div', 'foobar-div')),
158             array('a[name]', array('name-anchor')),
159             array('a[NAme]', array('name-anchor')), // case-insensitive in HTML:
160             array('a[rel]', array('tag-anchor', 'nofollow-anchor')),
161             array('a[rel="tag"]', array('tag-anchor')),
162             array('a[href*="localhost"]', array('tag-anchor')),
163             array('a[href*=""]', array()),
164             array('a[href^="http"]', array('tag-anchor', 'nofollow-anchor')),
165             array('a[href^="http:"]', array('tag-anchor')),
166             array('a[href^=""]', array()),
167             array('a[href$="org"]', array('nofollow-anchor')),
168             array('a[href$=""]', array()),
169             array('div[foobar~="bc"]', array('foobar-div')),
170             array('div[foobar~="cde"]', array('foobar-div')),
171             array('[foobar~="ab bc"]', array('foobar-div')),
172             array('[foobar~=""]', array()),
173             array('[foobar~=" \t"]', array()),
174             array('div[foobar~="cd"]', array()),
175             array('*[lang|="En"]', array('second-li')),
176             array('[lang|="En-us"]', array('second-li')),
177             // Attribute values are case sensitive
178             array('*[lang|="en"]', array()),
179             array('[lang|="en-US"]', array()),
180             array('*[lang|="e"]', array()),
181             // ... :lang() is not.
182             array(':lang("EN")', array('second-li', 'li-div')),
183             array('*:lang(en-US)', array('second-li', 'li-div')),
184             array(':lang("e")', array()),
185             array('li:nth-child(3)', array('third-li')),
186             array('li:nth-child(10)', array()),
187             array('li:nth-child(2n)', array('second-li', 'fourth-li', 'sixth-li')),
188             array('li:nth-child(even)', array('second-li', 'fourth-li', 'sixth-li')),
189             array('li:nth-child(2n+0)', array('second-li', 'fourth-li', 'sixth-li')),
190             array('li:nth-child(+2n+1)', array('first-li', 'third-li', 'fifth-li', 'seventh-li')),
191             array('li:nth-child(odd)', array('first-li', 'third-li', 'fifth-li', 'seventh-li')),
192             array('li:nth-child(2n+4)', array('fourth-li', 'sixth-li')),
193             array('li:nth-child(3n+1)', array('first-li', 'fourth-li', 'seventh-li')),
194             array('li:nth-child(n)', array('first-li', 'second-li', 'third-li', 'fourth-li', 'fifth-li', 'sixth-li', 'seventh-li')),
195             array('li:nth-child(n-1)', array('first-li', 'second-li', 'third-li', 'fourth-li', 'fifth-li', 'sixth-li', 'seventh-li')),
196             array('li:nth-child(n+1)', array('first-li', 'second-li', 'third-li', 'fourth-li', 'fifth-li', 'sixth-li', 'seventh-li')),
197             array('li:nth-child(n+3)', array('third-li', 'fourth-li', 'fifth-li', 'sixth-li', 'seventh-li')),
198             array('li:nth-child(-n)', array()),
199             array('li:nth-child(-n-1)', array()),
200             array('li:nth-child(-n+1)', array('first-li')),
201             array('li:nth-child(-n+3)', array('first-li', 'second-li', 'third-li')),
202             array('li:nth-last-child(0)', array()),
203             array('li:nth-last-child(2n)', array('second-li', 'fourth-li', 'sixth-li')),
204             array('li:nth-last-child(even)', array('second-li', 'fourth-li', 'sixth-li')),
205             array('li:nth-last-child(2n+2)', array('second-li', 'fourth-li', 'sixth-li')),
206             array('li:nth-last-child(n)', array('first-li', 'second-li', 'third-li', 'fourth-li', 'fifth-li', 'sixth-li', 'seventh-li')),
207             array('li:nth-last-child(n-1)', array('first-li', 'second-li', 'third-li', 'fourth-li', 'fifth-li', 'sixth-li', 'seventh-li')),
208             array('li:nth-last-child(n-3)', array('first-li', 'second-li', 'third-li', 'fourth-li', 'fifth-li', 'sixth-li', 'seventh-li')),
209             array('li:nth-last-child(n+1)', array('first-li', 'second-li', 'third-li', 'fourth-li', 'fifth-li', 'sixth-li', 'seventh-li')),
210             array('li:nth-last-child(n+3)', array('first-li', 'second-li', 'third-li', 'fourth-li', 'fifth-li')),
211             array('li:nth-last-child(-n)', array()),
212             array('li:nth-last-child(-n-1)', array()),
213             array('li:nth-last-child(-n+1)', array('seventh-li')),
214             array('li:nth-last-child(-n+3)', array('fifth-li', 'sixth-li', 'seventh-li')),
215             array('ol:first-of-type', array('first-ol')),
216             array('ol:nth-child(1)', array('first-ol')),
217             array('ol:nth-of-type(2)', array('second-ol')),
218             array('ol:nth-last-of-type(1)', array('second-ol')),
219             array('span:only-child', array('foobar-span')),
220             array('li div:only-child', array('li-div')),
221             array('div *:only-child', array('li-div', 'foobar-span')),
222             array('p:only-of-type', array('paragraph')),
223             array('a:empty', array('name-anchor')),
224             array('a:EMpty', array('name-anchor')),
225             array('li:empty', array('third-li', 'fourth-li', 'fifth-li', 'sixth-li')),
226             array(':root', array('html')),
227             array('html:root', array('html')),
228             array('li:root', array()),
229             array('* :root', array()),
230             array('*:contains("link")', array('html', 'outer-div', 'tag-anchor', 'nofollow-anchor')),
231             array(':CONtains("link")', array('html', 'outer-div', 'tag-anchor', 'nofollow-anchor')),
232             array('*:contains("LInk")', array()),  // case sensitive
233             array('*:contains("e")', array('html', 'nil', 'outer-div', 'first-ol', 'first-li', 'paragraph', 'p-em')),
234             array('*:contains("E")', array()),  // case-sensitive
235             array('.a', array('first-ol')),
236             array('.b', array('first-ol')),
237             array('*.a', array('first-ol')),
238             array('ol.a', array('first-ol')),
239             array('.c', array('first-ol', 'third-li', 'fourth-li')),
240             array('*.c', array('first-ol', 'third-li', 'fourth-li')),
241             array('ol *.c', array('third-li', 'fourth-li')),
242             array('ol li.c', array('third-li', 'fourth-li')),
243             array('li ~ li.c', array('third-li', 'fourth-li')),
244             array('ol > li.c', array('third-li', 'fourth-li')),
245             array('#first-li', array('first-li')),
246             array('li#first-li', array('first-li')),
247             array('*#first-li', array('first-li')),
248             array('li div', array('li-div')),
249             array('li > div', array('li-div')),
250             array('div div', array('li-div')),
251             array('div > div', array()),
252             array('div>.c', array('first-ol')),
253             array('div > .c', array('first-ol')),
254             array('div + div', array('foobar-div')),
255             array('a ~ a', array('tag-anchor', 'nofollow-anchor')),
256             array('a[rel="tag"] ~ a', array('nofollow-anchor')),
257             array('ol#first-ol li:last-child', array('seventh-li')),
258             array('ol#first-ol *:last-child', array('li-div', 'seventh-li')),
259             array('#outer-div:first-child', array('outer-div')),
260             array('#outer-div :first-child', array('name-anchor', 'first-li', 'li-div', 'p-b', 'checkbox-fieldset-disabled', 'area-href')),
261             array('a[href]', array('tag-anchor', 'nofollow-anchor')),
262             array(':not(*)', array()),
263             array('a:not([href])', array('name-anchor')),
264             array('ol :Not(li[class])', array('first-li', 'second-li', 'li-div', 'fifth-li', 'sixth-li', 'seventh-li')),
265             // HTML-specific
266             array(':link', array('link-href', 'tag-anchor', 'nofollow-anchor', 'area-href')),
267             array(':visited', array()),
268             array(':enabled', array('link-href', 'tag-anchor', 'nofollow-anchor', 'checkbox-unchecked', 'text-checked', 'checkbox-checked', 'area-href')),
269             array(':disabled', array('checkbox-disabled', 'checkbox-disabled-checked', 'fieldset', 'checkbox-fieldset-disabled')),
270             array(':checked', array('checkbox-checked', 'checkbox-disabled-checked')),
271         );
272     }
273
274     public function getHtmlShakespearTestData()
275     {
276         return array(
277             array('*', 246),
278             array('div:contains(CELIA)', 26),
279             array('div:only-child', 22), // ?
280             array('div:nth-child(even)', 106),
281             array('div:nth-child(2n)', 106),
282             array('div:nth-child(odd)', 137),
283             array('div:nth-child(2n+1)', 137),
284             array('div:nth-child(n)', 243),
285             array('div:last-child', 53),
286             array('div:first-child', 51),
287             array('div > div', 242),
288             array('div + div', 190),
289             array('div ~ div', 190),
290             array('body', 1),
291             array('body div', 243),
292             array('div', 243),
293             array('div div', 242),
294             array('div div div', 241),
295             array('div, div, div', 243),
296             array('div, a, span', 243),
297             array('.dialog', 51),
298             array('div.dialog', 51),
299             array('div .dialog', 51),
300             array('div.character, div.dialog', 99),
301             array('div.direction.dialog', 0),
302             array('div.dialog.direction', 0),
303             array('div.dialog.scene', 1),
304             array('div.scene.scene', 1),
305             array('div.scene .scene', 0),
306             array('div.direction .dialog ', 0),
307             array('div .dialog .direction', 4),
308             array('div.dialog .dialog .direction', 4),
309             array('#speech5', 1),
310             array('div#speech5', 1),
311             array('div #speech5', 1),
312             array('div.scene div.dialog', 49),
313             array('div#scene1 div.dialog div', 142),
314             array('#scene1 #speech1', 1),
315             array('div[class]', 103),
316             array('div[class=dialog]', 50),
317             array('div[class^=dia]', 51),
318             array('div[class$=log]', 50),
319             array('div[class*=sce]', 1),
320             array('div[class|=dialog]', 50), // ? Seems right
321             array('div[class!=madeup]', 243), // ? Seems right
322             array('div[class~=dialog]', 51), // ? Seems right
323         );
324     }
325 }