Problem/Motivation
XPath is hard to write and is often a barrier to new contributors writing tests.
Most devs know CSS selectors.
Symfony has a component css-selector that parses CSS selectors into xpaths.
Proposed resolution
Add symfony/css-selector to core
Add a ::cssSelect() method to WebTestBase
Refactor a couple of xpath based tests for example purposes.
Profit.
Remaining tasks
Review the attached patch
User interface changes
None
API changes
New method ::cssSelect() on WebTestBase
Before
$elements = $this->xpath('//div[contains(concat(" ", normalize-space(@class), " "), :field_class)]/li[@data-id=:data_id and ./h2[contains(., :text)]]', array(
':field_class' => 'tours',
':data_id' => 'tour-test-1',
':text' => 'The first tip',
));
After
$elements = $this->cssSelect("div.tours > li[data-id=tour-test-1] h2:contains('The first tip')");
Much easier.
Background on symfony/css-selector
symfony/css-selector is required by Mink. Mink is part of the Behat BDD QA family. Mink in conjunction with Goutte provides a headless-browser emulator in php and is fast becoming the defacto-standard for web-tests in the PHP community. Our goal for D9 is to replace SimpleTest with Goutte + Mink (see #1567500: [meta] Pave the way to replace the testing framework with PHPUnit and possibly rewrite Simpletest).
Behat in turn is part of the PHP QA Toolchain, along with PHPUnit (which we already use) and PHP_CS (Code sniffer - which we use too, particularly in contrib - but not to its full extent in core).
Our longer term goal is to move towards the standard php quality toolkit. Although much of this will have to wait until Drupal 9, this change eases the burden on coders creating new tests. Whilst much of the new test functionality required for Drupal 8 is written, contrib authors will be writing SimpleTest tests for Drupal 8 for at least the next three years, most likely more as Drupal 10 is a long way off. Adding this now lowers the barrier to writing test coverage for code and improves Drupal core and contrib's overall quality.
| Comment | File | Size | Author |
|---|---|---|---|
| #71 | 1959660-psr4-reroll.patch | 261.91 KB | xjm |
| #70 | css-selector-symfont-1959660.70.patch | 262.02 KB | larowlan |
| #60 | css-selector-symfont-1959660.core-only.do-not-test.patch | 6.54 KB | larowlan |
| #60 | css-selector-symfont-1959660.60.patch | 262.02 KB | larowlan |
| #54 | css-selector-symfont-1959660.shebang.patch | 253.26 KB | larowlan |
Comments
Comment #1
larowlanSo three files
composer update symfony/css-selectorComment #2
larowlanComment #3
andypostLooks nice and actually more usable for newbies to use
Comment #4
Crell commentedSeems reasonable to me. My only question would be how much effort we continue to put into Simpletest/WebTest in the first place. :-) I guess we can't get rid of it entirely any time soon, though.
Comment #5
larowlanUpdated issue summary with example of new format
Comment #6
sunWeb tests via PHPUnit/Mink/Goutte are most likely not going to happen for D8 (since that is a completely different can of worms), so keep on rocking there.
Comment #7
xjmComment #8
xjmxpost
Comment #9
larowlan#1: css-selector-1959660.1.patch queued for re-testing.
Comment #11
larowlanRe-roll based on changes to tour tests.
Comment #13
larowlanComment #14
larowlanGreen again
Comment #15
robloachThese changes are just from the composer.json move. Nothing to worry about. PHPUnit tests still run fine.
That's the .gitignore from CSS Selector. No problem.
Comment #16
larowlanxjm pointed out some docblock issues
Comment #17
larowlanFixes doc-block issues
Comment #18
larowlanAdding change notice tag
Comment #19
robloachGood catch on the docblock.
Comment #20
webchickSince this adds a new library, assigning to Dries. TBH, despite the noted improvement on test readability, this kinda strikes me as "feature-ish" and should probably be subject to thresholds. Not touching the status for now.
That's a, um. Serious diffstat. :P
This could use a more fleshed-out issue summary to help understand why this is necessary (some before/after snippets — oops, that's there already, didn't scroll down enough :D), what other libraries were considered, how stable this library is in terms of Symfony's LTS support, etc.
Comment #21
robloachIt includes a lot of new files because it adds the Symfony CSS Selector.
Attached is the above patch without running
composer update symfony/css-selector, or the included vendor code. You can see it replaces the ugly XPath selectors with nicer, much more familiar, CSS selectors. Makes it much easier to write tests which check output of pages.3 files changed, 29 insertions, 37 deletions.Comment #22
webchickWell, whether it's pulled in via composer or committed directly to the vendor branch, it's still an extra 4600 lines of code we're loading for doing this task that we didn't load before.
Comment #23
larowlanFWIW some of those 4600 lines are unit tests but yes we are loading more code, when that method is invoked.
In terms of how stable this library is and what else was considered, symfony/css-selector is required by Mink. Mink is part of the Behat BDD QA family. Mink in conjunction with Goutte provides a headless-browser emulator in php and is fast becoming the defacto-standard for web-tests in the PHP community. Our goal for D9 is to replace SimpleTest with Goutte + Mink (see #1567500: [meta] Pave the way to replace the testing framework with PHPUnit and possibly rewrite Simpletest).
Behat in turn is part of the PHP QA Toolchain, along with PHPUnit (which we already use) and PHP_CS (Code sniffer - which we use too, particularly in contrib - but not to its full extent in core).
Our longer term goal is to remove SimpleTest altogether (see #1567500: [meta] Pave the way to replace the testing framework with PHPUnit and possibly rewrite Simpletest) in favour of a more standard and supported testing toolkit, given we're the only ones left on SimpleTest. The goal of this is to move towards the standard php quality toolkit. Although much of this will have to wait until Drupal 9, this change eases the burden on coders creating new tests. Whilst much of the new test functionality required for Drupal 8 is written, contrib authors will be writing SimpleTest tests for Drupal 8 for at least the next three years, most likely more as Drupal 10 is a long way off. Adding this now lowers the barrier to writing test coverage for code and improves Drupal core and contrib's overall quality.
I will add this to the issue summary too.
Hope that helps
Comment #23.0
larowlanUpdated issue summary.
Comment #23.1
larowlanUpdated issue summary
Comment #24
pfrenssenGreat idea! This would make my life so much easier.
Comment #25
larowlanGoing to change this to try and use PHPUnit_Util_XML::cssSelect which is already in core
Comment #26
larowlanComment #27
larowlanPHPUnit_Util_XML::cssSelect doesn't support the :contains psuedo selector.
Resetting status etc.
Comment #28
larowlanIt's not as nice but got there in the end.
No interdiff, as bears little resemblence to #17
Before
After
Leverages the $content option from PHPUnit_Util_XML::cssSelect() which doesn't work with nested selectors (eg 'li h2') but works by chaining (of sorts) as per the above example.
Comment #29
ParisLiakos commentedthis is awesome!
how about adding it to UnitTestCase as well?
Comment #30
dawehnerIt would be so great to actually move this to TestBase so also drupal unit tests could use it. Just for example when testing the output of a table in views, I don't give a shit about the whole page, as I can render a view properly in the test.
Comment #31
ParisLiakos commentedBoth me and dawehner think that testBase should be a better place. you dont need to be in a webtest to test X/Html output
Comment #32
dawehnerMoved it and tried to write a test, but it feels like the DX is not perfect.
Comment #33
dawehner#32: drupal-1959660-32.patch queued for re-testing.
Comment #34
ParisLiakos commentedshouldnt this be an assertion isntead of debug?
Comment #35
xtfer commentedDreditor doesnt seem to like this patch...
The only major issue I could see was the debug() statement. The DX is fine, IMHO.
Comment #36
xtfer commentedEdit: duplicate comment
Comment #37
clemens.tolboomI just created a duplicate #2030369: Add Symfony CssSelector to core which was needed for #2028535: Provide a TourTestBase class for use by core and contrib modules where the CSS selections come from Tour YAML files which we never can code against.
So I really hope this gets in :)
Comment #38
clemens.tolboomPatch needed a reroll. Had to rewrite one Tour test.
I've fixed #34 and added some more unit tests to core/modules/simpletest/lib/Drupal/simpletest/Tests/SimpleTestUnitTest.php
What I liked about using #2030369: Add Symfony CssSelector to core instead it's selector is a one shot instead of make sure to address the right contexts on complex css selection. But it's a HUGE improvement over XPath selection.
Comment #39
clemens.tolboomThis needs tests for the comment from the function documentation.
We need a tests for ie
Make a failing one.
Comment #40
dawehnerWe wrote other phpunit tests for simpletest before, so why not use it here as well? I guess B => C would be enough
Comment #41
xjmThis isn't ready for a change notification until it goes into HEAD.
Comment #42
pfrenssen"Context" should be "Content".
It would be nice to have some tests that extend WebTestBase so that we can test the usage of
$this->content. Granted, the Tour tests already do this, but developers looking for some example implementations will perhaps not go looking in there.To properly test the anonymous function inside the array_reduce() I would like to add a test that takes a content array with multiple items, in addition to the tests using an array with a single item.
Maybe shorten the name of the unit test from "SimpleTest functionality (Unit test)" to "SimpleTest unit tests" to be consistent with most other unit tests.
I think
$elementswould be a better name for the variable that holds the selected elements (instead of$rootor$child).My new hobby is to do code coverage analysis with XDebug, and I'm happy to announce that the tests cover 100% of the code. To be fully complete though, we should ensure that an empty array is returned when no match was found.
Regarding comments #39 and #40:
I too wonder where this is coming from. @dawehner, how did you figure this out? I couldn't find anything about this limitation in the documentation of PHPUnit. I think this might just be a bug. If so, we should either find an existing bug report, or create one, and refer to that. Also if it is a bug in PHPUnit we should not add a test for that, that should be added in PHPUnit.
@dawehner, I don't catch your drift, what do you mean with B => C?
Attached patch addresses my own remarks, #39 and #40 are not yet addressed.
Comment #43
pfrenssenMarked #597866: Support for QueryPath-based assertions as a duplicate of this issue.
Comment #44
pfrenssenThis was first mentioned by larowlan in comment #27. Indeed, PHPUnit_Util_XML::cssSelect() is very simple in comparison to the one from Symfony, and doesn't support this. It is still better than nothing though, and certainly easier than XPath.
I don't think we need to test for this case if it is simply not supported upstream.
Comment #44.0
pfrenssenAdded the familiar [contains(concat(" ", normalize-space(@class), " ")] XPath drama to the example.
Comment #45
clemens.tolboom42: drupal-1959660-42.patch queued for re-testing.
Comment #47
sunThat is a pretty heavy limitation. It degrades the very DX that we're trying to fix here back to a similarly awkward level as XPath. :-(
Does the Symfony CssSelector component also have that limitation? — If it does not, then I'd actually rather go back to the original proposal to simply include that.
Otherwise, it appears that some of the converted tests are working around that limitation by recursively calling
cssSelect()... — so if pulling in the Symfony CssSelector component is not an option, I wonder whether we could at least automate that to retain a sane DX?CSS selectors are predictable and standardized, so something along the lines of the following might work:
The signature looks really strange to me.
$selectoris fine.But why does
$containsdefault to TRUE? The default should be NULL.It's also not 100% clear to me what exactly
$containsmeans — (1) does it operate on the raw content/markup (including elements), or (2) does it operate on the raw HTML node values, and in any case, (3) does it operate on the raw HTML content or on decoded text? (cf. htmlentitydecode())Likewise,
$contentshould default to NULL. — All of the Boolean flags in this method signature do not make sense.Let's also be more accurate in the phpDoc @params and @return data types; i.e.:
@param string $contains
@param string|\DOMElement[] $content
@return \DOMElement[]
Lastly, DOMElement or DOMNode? Not the same.
Note that "HTML" is all-uppercase in DOMDocument::saveHTML()
The "XML" part in this method concerns me.
Is this compatible with HTML5?
Let's change the test to additionally operate on some HTML5 elements; i.e.:
1. Simply wrap the list into a SECTION, followed by an ASIDE, and lastly a NAV.
2. Add some DETAILS + SUMMARY markup.
3. Add one or more incomplete/auto-closed tags; e.g., a paragraph that isn't terminated.
4. Some more dummy elements, time/audio/video/etc.
Comment #48
larowlanDoes the Symfony component have that limitation: no
Does PHP Unit work with HTML 5: no, not until we bump above 3.7
Does Symfony: yes
I too favour the original patch, so here it is re-rolled.
Same concept, the do-not-test patch is just the core changes, the other is the whole shebang.
<rant style="old-man-grumbles">Also I'd like to go on record again that we are using composer wrong, we shouldn't include core/vendor in version control, just composer.lock.
Yes thats an 11Mb patch just from running
composer update symfony/css-selector.</rant>Comment #49
kim.pepperI second the motion.
Comment #50
larowlanSeems a composer update changed the way dependencies are resolved, as on 2014-02-13 15:23:53 zf2 and guzzle/guzzle weren't brought in, but on 2014-03-09 15:09:15 they are.
So if I pin zend-feed and guzzle/http to their minor versions, and use --no-dev - I just get css-selector like expected.
Smaller patch.
Comment #51
larowlanthis is the right do-not-test.patch
and the --no-dev is not important/relevant
Comment #52
sunThank you!
Seems unrelated? Can we revert these?
Comment #53
larowlanPlease see comment 50
Comment #54
larowlanComment #55
sunThanks!
Next to the major argument of lacking HTML5 support, today I learned that the Behat/Mink framework also uses Symfony CssSelector — the grand master plan is to fully migrate from Simpletest to Mink (give or take Behat) in the long run (rather unlikely for 8.0.0, but definitely beyond).
So that makes two important reasons for going with Symfony CssSelector instead of PHPUnit_Util_XML.
Comment #57
larowlanre-roll
Comment #58
larowlanback to rtbc, straight re-roll
Comment #59
larowlan@kim.pepper pointed out that the zend-feed pin has snuck back in.
re-roll to remove that and adding a 'core only' patch to aid reviews.
Comment #60
larowlanmissed the composer.lock change from removing the pin, thanks again @kim.pepper
Comment #61
kim.pepperBack to RTBC from me if green.
Comment #63
larowlan60: css-selector-symfont-1959660.60.patch queued for re-testing.
Comment #64
larowlanComment #65
kim.pepper...aaaand back to RTBC
Comment #66
sunThanks a lot, @larowlan!
This is desperately needed. XPath continues to cause pain for developers who are willing to write tests, but who get stuck on more complex DOM element assertions.
Let's pretty please end this pain right here and right now for D8. Sans the vendor component, the actual changes of this patch are almost neutral.
Tons of win at almost no cost. The one and only way to make everyone adopt the "tests by design" mantra is a stellar test authoring DX. This is a major contribution towards that goal.
Comment #67
catchI like the css selector stuff using mink/behat, good to introduce that here.
Needs Dries for the new library though.
Comment #69
dries commentedSeems like a handy library to me. Green light to use it!
Comment #70
larowlanre-roll
Comment #71
xjmReroll for #2247991: [May 27] Move all module code from …/lib/Drupal/… to …/src/… for PSR-4.
Comment #72
catchCommitted/pushed to 8.x, thanks!
Comment #73
pfrenssenYay!!
Comment #74
larowlanDraft change notice is here:
https://drupal.org/node/2276689
Comment #75
pfrenssenChange record looks good, does not need more explanation I think. I added a link to the documentation of the Symfony CssSelector component for people that like to read more.