Drupal 8's functional JavaScript testing just got better with visibility
Drupal level: advanced
Yesterday a patch for Inline Form Errors landed in Drupal, which as a by-product, brought us a new useful testing possibility: we can now more easily write tests that check if elements on a webpage are actually really visible in the viewport. The visibility of page elements in different scenarios is important for the usability and accessibility of your site.
Drupal 8 comes with exiting "new" testing possibilities, the latest addition was Functional JavaScript testing. Which enables us to automatically test if our JavaScript interactions work and keep working throughout the development process. The tests are run in the PhantomJS headless browser. Simulating interaction with this browser is handled by Behat's Mink. For a bit more background and how to set this up locally checkout this Chapter Three blog.
Visibility testing hardship
\Behat\Mink\Element\NodeElement->isVisible()
Now let me first warn you that visibility testing can be quite tricky to grasp if you just start out. For example Mink has a method isVisible() and I was tempted to believe that this method checks that a user can see the targeted element on his screen. But nothing could be further from the truth... it only checks that the element is not styled explicitly invisible. So, for example, if you have some CSS styling like display: none; or visibility: hidden; on an element this isVisible() assertion would fail. However if your element is not explicitly hidden, but is outside your devices viewport or is being obscured by an overlapping element, you are out of luck with this assertion. Your test would pass with flying colors, while the users sees nothing.
Another thing to note for if you'd want to dig in this matter more: as a Drupal user you probably by now interpret the word node as an object or entity with data that is primarily being use to publish content. In our testing context however it has to be understood as a DOM element which can be targeted by using a CSS selector or XPath. In this article I'll continue to use element.
New assertions
\Drupal\FunctionalJavascriptTests\JSWebAssert->assertVisibleInViewport()
To work around the first problem of elements falling outside the viewport of a users device, we added a new assertions to Drupal's JSWebAssert class. It is now possible to check if an element is in (the defined) viewport or not using the assertVisibleInViewport() and assertNotVissibleInViewport() methods respectively.
You can target the to be tested element with CSS selectors or XPath and test if a certain corner or the whole element is visible in the viewport.
'How to use' examples
Let's assume the following for testing purposes prepared "Create page" interface of Drupal with an overlay helper of the viewport size.
To check if, in a certain situation, the header title is visible within the viewport you'll first have to define the viewport size. This can be done with a PhantomJS startup arguments and/or during runtime with the method \Behat\Mink\Session->resizeWindow(). In case of testing with the testbots on Drupal.org a default viewport as shown above will be setup for you. From your own Drupal project root you can start the headless browsers using the following command. The last two parameters indicate the default width and the height of the viewport used during your tests.
/path/to/phantomjs --ssl-protocol=any --ignore-ssl-errors=true vendor/jcalderonzumba/gastonjs/src/Client/main.js 8510 1024 768
Now you can use our new assertion to check if the header title is completely visible (partial code snippet):
$web_assert->assertVisibleInViewport('css', 'h1', FALSE, 'The page header title is not visible.');
In case of our first example the user (robot) scrolled down and the header title is not visible (the element is outside the green region). So the assertion would fail. How to setup a complete functional JavaScript test is outside the scope of the article, but you can use the recently commited CKEditor test code as an example.
For our second example we use a viewport of a mobile device. The user just loaded the page and hasn't scrolled down. For testing purposes we pushed the body field a bit down. And we see that the CKeditor of that field is partly visible (in the red area). By default the method tests the visibility of all four corners of a (rectangular) element (third parameter value: FALSE). With the third parameter we can tell the method to check a certain corner for visibility (topLeft, topRight, bottomRight or bottomLeft).
So the assertion given below will pass because the top left corner of the element is within the viewport (red area). But by default it would have failed, because not all corners are visible.
$web_assert->assertVisibleInViewport('css', '#cke_edit-body-0-value', 'topLeft', 'The top left of the CKEditor enabled body field is not visible.');
Final notes
To actually see the used viewport in a screenshot, a patch is available in an ongoing issue on Drupal.org. Chime in if you would like this possibility in core. It has been used to generate the illustrations in this blog. And remember, visibility testing doesn't take into account that the element could be obscured by another element. So for now, using printscreens, is the only out-of-the-box way of checking if an element is really really visible. Time for another assertion?
Questions or comments? @dmsmidt