Commit 272c5249 authored by catch's avatar catch

Issue #1959660 by larowlan, pfrenssen, xjm, clemens.tolboom, dawehner,...

Issue #1959660 by larowlan, pfrenssen, xjm, clemens.tolboom, dawehner, RobLoach: Replace xpath() with WebTestBase::cssSelect() by leveraging Symfony CssSelector.
parent f584be98
......@@ -3,7 +3,7 @@
"This file locks the dependencies of your project to a known state",
"Read more about it at http://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file"
],
"hash": "764a98eeaf89d9f52b9c32d343036de0",
"hash": "cc429f39777a4435a2d1415648978f97",
"packages": [
{
"name": "doctrine/annotations",
......@@ -1188,6 +1188,59 @@
"homepage": "http://symfony.com",
"time": "2013-11-26 16:40:27"
},
{
"name": "symfony/css-selector",
"version": "v2.4.4",
"target-dir": "Symfony/Component/CssSelector",
"source": {
"type": "git",
"url": "https://github.com/symfony/CssSelector.git",
"reference": "479a5b409723f596ffc3b5178034e4d76ce615b3"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/CssSelector/zipball/479a5b409723f596ffc3b5178034e4d76ce615b3",
"reference": "479a5b409723f596ffc3b5178034e4d76ce615b3",
"shasum": ""
},
"require": {
"php": ">=5.3.3"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "2.4-dev"
}
},
"autoload": {
"psr-0": {
"Symfony\\Component\\CssSelector\\": ""
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com",
"homepage": "http://fabien.potencier.org",
"role": "Lead Developer"
},
{
"name": "Symfony Community",
"homepage": "http://symfony.com/contributors"
},
{
"name": "Jean-François Simon",
"email": "jeanfrancois.simon@sensiolabs.com"
}
],
"description": "Symfony CssSelector Component",
"homepage": "http://symfony.com",
"time": "2014-04-18 20:37:09"
},
{
"name": "symfony/debug",
"version": "v2.3.4",
......
......@@ -24,6 +24,7 @@
use Drupal\Core\Datetime\DrupalDateTime;
use Drupal\block\Entity\Block;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\CssSelector\CssSelector;
/**
* Test case for typical Drupal tests.
......@@ -2338,6 +2339,22 @@ protected function xpath($xpath, array $arguments = array()) {
}
}
/**
* Performs a CSS selection based search on the contents of the internal
* browser. The search is relative to the root element (HTML tag normally) of
* the page.
*
* @param $selector string
* CSS selector to use in the search.
*
* @return \SimpleXMLElement
* The return value of the xpath search performed after converting the css
* selector to an XPath selector.
*/
protected function cssSelect($selector) {
return $this->xpath(CssSelector::toXPath($selector));
}
/**
* Get all option elements, including nested options, in a select.
*
......
......@@ -53,12 +53,6 @@ public static function getInfo() {
public function testTourFunctionality() {
// Navigate to tour-test-1 and verify the tour_test_1 tip is found with appropriate classes.
$this->drupalGet('tour-test-1');
$elements = $this->xpath('//li[@data-id=:data_id and @class=:classes and ./h2[contains(., :text)]]', array(
':classes' => 'tip-module-tour-test tip-type-text tip-tour-test-1',
':data_id' => 'tour-test-1',
':text' => 'The first tip',
));
$this->assertEqual(count($elements), 1, 'Found English variant of tip 1.');
// Test the TourTestBase class assertTourTips() method.
$tips = array();
......@@ -75,16 +69,13 @@ public function testTourFunctionality() {
));
$this->assertEqual(count($elements), 1, 'Found Token replacement.');
$elements = $this->xpath('//li[@data-id=:data_id and ./h2[contains(., :text)]]', array(
':data_id' => 'tour-test-2',
':text' => 'The quick brown fox',
));
$elements = $this->cssSelect("li[data-id=tour-test-1] h2:contains('The first tip')");
$this->assertEqual(count($elements), 1, 'Found English variant of tip 1.');
$elements = $this->cssSelect("li[data-id=tour-test-2] h2:contains('The quick brown fox')");
$this->assertNotEqual(count($elements), 1, 'Did not find English variant of tip 2.');
$elements = $this->xpath('//li[@data-id=:data_id and ./h2[contains(., :text)]]', array(
':data_id' => 'tour-test-1',
':text' => 'La pioggia cade in spagna',
));
$elements = $this->cssSelect("li[data-id=tour-test-1] h2:contains('La pioggia cade in spagna')");
$this->assertNotEqual(count($elements), 1, 'Did not find Italian variant of tip 1.');
// Ensure that plugins work.
......@@ -93,16 +84,10 @@ public function testTourFunctionality() {
// Navigate to tour-test-2/subpath and verify the tour_test_2 tip is found.
$this->drupalGet('tour-test-2/subpath');
$elements = $this->xpath('//li[@data-id=:data_id and ./h2[contains(., :text)]]', array(
':data_id' => 'tour-test-2',
':text' => 'The quick brown fox',
));
$elements = $this->cssSelect("li[data-id=tour-test-2] h2:contains('The quick brown fox')");
$this->assertEqual(count($elements), 1, 'Found English variant of tip 2.');
$elements = $this->xpath('//li[@data-id=:data_id and ./h2[contains(., :text)]]', array(
':data_id' => 'tour-test-1',
':text' => 'The first tip',
));
$elements = $this->cssSelect("li[data-id=tour-test-1] h2:contains('The first tip')");
$this->assertNotEqual(count($elements), 1, 'Did not find English variant of tip 1.');
// Enable Italian language and navigate to it/tour-test1 and verify italian
......@@ -110,16 +95,10 @@ public function testTourFunctionality() {
language_save(new Language(array('id' => 'it')));
$this->drupalGet('it/tour-test-1');
$elements = $this->xpath('//li[@data-id=:data_id and ./h2[contains(., :text)]]', array(
':data_id' => 'tour-test-1',
':text' => 'La pioggia cade in spagna',
));
$elements = $this->cssSelect("li[data-id=tour-test-1] h2:contains('La pioggia cade in spagna')");
$this->assertEqual(count($elements), 1, 'Found Italian variant of tip 1.');
$elements = $this->xpath('//li[@data-id=:data_id and ./h2[contains(., :text)]]', array(
':data_id' => 'tour-test-1',
':text' => 'The first tip',
));
$elements = $this->cssSelect("li[data-id=tour-test-2] h2:contains('The quick brown fox')");
$this->assertNotEqual(count($elements), 1, 'Did not find English variant of tip 1.');
language_save(new Language(array('id' => 'en')));
......@@ -174,18 +153,12 @@ public function testTourFunctionality() {
// Navigate to tour-test-1 and verify the new tip is found.
$this->drupalGet('tour-test-1');
$elements = $this->xpath('//li[@data-id=:data_id and ./h2[contains(., :text)]]', array(
':data_id' => 'tour-code-test-1',
':text' => 'The rain in spain',
));
$elements = $this->cssSelect("li[data-id=tour-code-test-1] h2:contains('The rain in spain')");
$this->assertEqual(count($elements), 1, 'Found the required tip markup for tip 4');
// Verify that the weight sorting works by ensuring the lower weight item
// (tip 4) has the 'End tour' button.
$elements = $this->xpath('//li[@data-id=:data_id and @data-text=:text]', array(
':data_id' => 'tour-code-test-1',
':text' => 'End tour',
));
$elements = $this->cssSelect("li[data-id=tour-code-test-1][data-text='End tour']");
$this->assertEqual(count($elements), 1, 'Found code tip was weighted last and had "End tour".');
// Test hook_tour_alter().
......
......@@ -266,7 +266,7 @@ public function unregister()
public function loadClass($class)
{
if ($file = $this->findFile($class)) {
includeFile($file);
include $file;
return true;
}
......@@ -291,25 +291,8 @@ public function findFile($class)
return $this->classMap[$class];
}
$file = $this->findFileWithExtension($class, '.php');
// Search for Hack files if we are running on HHVM
if ($file === null && defined('HHVM_VERSION')) {
$file = $this->findFileWithExtension($class, '.hh');
}
if ($file === null) {
// Remember that this class does not exist.
return $this->classMap[$class] = false;
}
return $file;
}
private function findFileWithExtension($class, $ext)
{
// PSR-4 lookup
$logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext;
$logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . '.php';
$first = $class[0];
if (isset($this->prefixLengthsPsr4[$first])) {
......@@ -338,7 +321,7 @@ private function findFileWithExtension($class, $ext)
. strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR);
} else {
// PEAR-like class name
$logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext;
$logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . '.php';
}
if (isset($this->prefixesPsr0[$first])) {
......@@ -364,15 +347,8 @@ private function findFileWithExtension($class, $ext)
if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) {
return $file;
}
}
}
/**
* Scope isolated include.
*
* Prevents access to $this/self from included files.
*/
function includeFile($file)
{
include $file;
// Remember that this class does not exist.
return $this->classMap[$class] = false;
}
}
......@@ -6,8 +6,8 @@
$baseDir = dirname(dirname($vendorDir));
return array(
$vendorDir . '/guzzlehttp/streams/src/functions.php',
$vendorDir . '/kriswallsmith/assetic/src/functions.php',
$vendorDir . '/guzzlehttp/streams/src/functions.php',
$vendorDir . '/guzzlehttp/guzzle/src/functions.php',
$baseDir . '/core/lib/Drupal.php',
);
......@@ -22,6 +22,7 @@
'Symfony\\Component\\EventDispatcher\\' => array($vendorDir . '/symfony/event-dispatcher'),
'Symfony\\Component\\DependencyInjection\\' => array($vendorDir . '/symfony/dependency-injection'),
'Symfony\\Component\\Debug\\' => array($vendorDir . '/symfony/debug'),
'Symfony\\Component\\CssSelector\\' => array($vendorDir . '/symfony/css-selector'),
'Symfony\\Component\\ClassLoader\\' => array($vendorDir . '/symfony/class-loader'),
'Symfony\\Cmf\\Component\\Routing' => array($vendorDir . '/symfony-cmf/routing'),
'Psr\\Log\\' => array($vendorDir . '/psr/log'),
......
......@@ -49,14 +49,9 @@ public static function getLoader()
$includeFiles = require __DIR__ . '/autoload_files.php';
foreach ($includeFiles as $file) {
composerRequireDrupal8($file);
require $file;
}
return $loader;
}
}
function composerRequireDrupal8($file)
{
require $file;
}
......@@ -825,212 +825,6 @@
"string"
]
},
{
"name": "guzzle/common",
"version": "v3.7.1",
"version_normalized": "3.7.1.0",
"target-dir": "Guzzle/Common",
"source": {
"type": "git",
"url": "https://github.com/guzzle/common.git",
"reference": "v3.7.1"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/guzzle/common/zipball/v3.7.1",
"reference": "v3.7.1",
"shasum": ""
},
"require": {
"php": ">=5.3.2",
"symfony/event-dispatcher": ">=2.1"
},
"time": "2013-07-05 20:17:54",
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "3.7-dev"
}
},
"installation-source": "dist",
"autoload": {
"psr-0": {
"Guzzle\\Common": ""
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"description": "Common libraries used by Guzzle",
"homepage": "http://guzzlephp.org/",
"keywords": [
"collection",
"common",
"event",
"exception"
]
},
{
"name": "guzzle/stream",
"version": "v3.7.1",
"version_normalized": "3.7.1.0",
"target-dir": "Guzzle/Stream",
"source": {
"type": "git",
"url": "https://github.com/guzzle/stream.git",
"reference": "v3.7.1"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/guzzle/stream/zipball/v3.7.1",
"reference": "v3.7.1",
"shasum": ""
},
"require": {
"guzzle/common": "self.version",
"php": ">=5.3.2"
},
"suggest": {
"guzzle/http": "To convert Guzzle request objects to PHP streams"
},
"time": "2013-06-27 00:50:43",
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "3.7-dev"
}
},
"installation-source": "dist",
"autoload": {
"psr-0": {
"Guzzle\\Stream": ""
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Michael Dowling",
"email": "mtdowling@gmail.com",
"homepage": "https://github.com/mtdowling"
}
],
"description": "Guzzle stream wrapper component",
"homepage": "http://guzzlephp.org/",
"keywords": [
"Guzzle",
"component",
"stream"
]
},
{
"name": "guzzle/parser",
"version": "v3.7.1",
"version_normalized": "3.7.1.0",
"target-dir": "Guzzle/Parser",
"source": {
"type": "git",
"url": "https://github.com/guzzle/parser.git",
"reference": "v3.7.1"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/guzzle/parser/zipball/v3.7.1",
"reference": "v3.7.1",
"shasum": ""
},
"require": {
"php": ">=5.3.2"
},
"time": "2013-06-11 00:24:07",
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "3.7-dev"
}
},
"installation-source": "dist",
"autoload": {
"psr-0": {
"Guzzle\\Parser": ""
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"description": "Interchangeable parsers used by Guzzle",
"homepage": "http://guzzlephp.org/",
"keywords": [
"URI Template",
"cookie",
"http",
"message",
"url"
]
},
{
"name": "guzzle/http",
"version": "v3.7.1",
"version_normalized": "3.7.1.0",
"target-dir": "Guzzle/Http",
"source": {
"type": "git",
"url": "https://github.com/guzzle/http.git",
"reference": "v3.7.1"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/guzzle/http/zipball/v3.7.1",
"reference": "v3.7.1",
"shasum": ""
},
"require": {
"guzzle/common": "self.version",
"guzzle/parser": "self.version",
"guzzle/stream": "self.version",
"php": ">=5.3.2"
},
"suggest": {
"ext-curl": "*"
},
"time": "2013-07-02 19:53:26",
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "3.7-dev"
}
},
"installation-source": "dist",
"autoload": {
"psr-0": {
"Guzzle\\Http": ""
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Michael Dowling",
"email": "mtdowling@gmail.com",
"homepage": "https://github.com/mtdowling"
}
],
"description": "HTTP libraries used by Guzzle",
"homepage": "http://guzzlephp.org/",
"keywords": [
"Guzzle",
"client",
"curl",
"http",
"http client"
]
},
{
"name": "symfony/debug",
"version": "v2.3.4",
......@@ -2317,5 +2111,60 @@
"rest",
"web service"
]
},
{
"name": "symfony/css-selector",
"version": "v2.4.4",
"version_normalized": "2.4.4.0",
"target-dir": "Symfony/Component/CssSelector",
"source": {
"type": "git",
"url": "https://github.com/symfony/CssSelector.git",
"reference": "479a5b409723f596ffc3b5178034e4d76ce615b3"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/CssSelector/zipball/479a5b409723f596ffc3b5178034e4d76ce615b3",
"reference": "479a5b409723f596ffc3b5178034e4d76ce615b3",
"shasum": ""
},
"require": {
"php": ">=5.3.3"
},
"time": "2014-04-18 20:37:09",
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "2.4-dev"
}
},
"installation-source": "dist",
"autoload": {
"psr-0": {
"Symfony\\Component\\CssSelector\\": ""
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com",
"homepage": "http://fabien.potencier.org",
"role": "Lead Developer"
},
{
"name": "Symfony Community",
"homepage": "http://symfony.com/contributors"
},
{
"name": "Jean-François Simon",
"email": "jeanfrancois.simon@sensiolabs.com"
}
],
"description": "Symfony CssSelector Component",
"homepage": "http://symfony.com"
}
]
<?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;
use Symfony\Component\CssSelector\Parser\Shortcut\ClassParser;
use Symfony\Component\CssSelector\Parser\Shortcut\ElementParser;
use Symfony\Component\CssSelector\Parser\Shortcut\EmptyStringParser;
use Symfony\Component\CssSelector\Parser\Shortcut\HashParser;
use Symfony\Component\CssSelector\XPath\Extension\HtmlExtension;
use Symfony\Component\CssSelector\XPath\Translator;
/**
* CssSelector is the main entry point of the component and can convert CSS
* selectors to XPath expressions.
*
* $xpath = CssSelector::toXpath('h1.foo');
*
* This component is a port of the Python cssselector library,
* which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect.
*
* @author Fabien Potencier <fabien@symfony.com>
*
* @api
*/
class CssSelector
{
private static $html = true;
/**
* Translates a CSS expression to its XPath equivalent.
* Optionally, a prefix can be added to the resulting XPath
* expression with the $prefix parameter.
*
* @param mixed $cssExpr The CSS expression.
* @param string $prefix An optional prefix for the XPath expression.
*
* @return string
*
* @api
*/
public static function toXPath($cssExpr, $prefix = 'descendant-or-self::')
{
$translator = new Translator();
if (self::$html) {
$translator->registerExtension(new HtmlExtension($translator));
}
$translator
->registerParserShortcut(new EmptyStringParser())
->registerParserShortcut(new ElementParser())
->registerParserShortcut(new ClassParser())
->registerParserShortcut(new HashParser())
;
return $translator->cssToXPath($cssExpr, $prefix);
}
/**
* Enables the HTML extension.
*/
public static function enableHtmlExtension()
{
self::$html = true;
}
/**
* Disables the HTML extension.
*/
public static function disableHtmlExtension()
{
self::$html = false;
}
}
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
<