For Automated Browser Testing
Justin Klemm
Founder and CEO of Ghost Inspector -- an automated browser testing service.
Try our service for free at: https://ghostinspector.com
Feel free to ask questions, make comments, challenge statements, etc. at any point during this presentation!
Applying style to elements:
<style>
#sample-btn { color: red; }
</style>
Interacting with elements using JavaScript:
<script>
document.querySelector("#sample-btn").style.display = "none";
</script>
<button id="sample-btn">Submit</button>
Interacting with elements during automated testing:
var wd = require("wd");
var browser = wd.remote();
...
browser.elementByCssSelector("#sample-btn").click();
<input id="email" value="">
#email
Sample element in the DOM:
CSS Selector:
<input id="email-123" value="">
<input id="email-456" value="">
Sample page load #1:
Sample page load #2:
<input name="email" value="">
[name="email"]
Sample element in the DOM:
CSS Selector:
<a href="/edit/>Edit Entry</a>
[href="/edit/"]
Finding a link using its URL:
CSS Selector:
<input type="text" name="s2aR3e1Djkns" placeholder="Email Address">
[placeholder="Email Address"]
Use creative/semantic attributes to workaround issues:
CSS Selector:
<input id="email" value="">
#email
The IDs that we already covered are just an attribute (as are classes, which we'll cover later).
Sample element in the DOM:
This selector:
[id="email"]
is simply shorthand for this:
<input type="radio" name="x" value="1">
<input type="radio" name="x" value="2">
<input type="radio" name="y" value="1">
<input type="radio" name="y" value="2">
[name="x"][value="2"]
Attributes can be combined to match more precisely.
Sample DOM:
CSS Selector:
You can combine more than just attributes. IDs, classes, etc (which we'll cover) can also be combined:
input[name="something"].some-class.another-class
<input name="sample-email-address" value="">
[name^="sample"]
Wait, there's more! We can swap out the "=" operator to do other types of matching.
Sample element in the DOM:
Match in the beginning of "name":
[name*="email"]
Match in the middle of "name":
[name$="address"]
Match at the end of "name":
<a href="/user/12345/edit/">Edit User</a>
[href^="/user/"][href$="/edit/"]
When are these other operators useful?
Sample link with dynamic value in the URL:
CSS Selector:
<a href="https://staging.company.com/dashboard/?session=3284792">Dashboard</a>
[href*="/dashboard/"]
Sample link with variable domain & session in the URL:
CSS Selector:
<input id="email-123" value="">
[id^="email"]
Going back to the "dynamic" ID issue... Possible workaround:
Sample elements in the DOM:
CSS Selector:
<input id="email-456" value="">
<input id="email-789" value="">
<input name="email" value="">
input[name="email"]
Sample element in the DOM:
CSS Selector:
select[name="level"]
[name="level"]
Consider this selector:
Are we targeting an <input> field, <select>, <textarea> or something else? That may impact our actions.
It's useful to know:
Recommendation: Include the element tag when it adds clarity. I like to use it for <input>, <textarea>, <select>, <button>, <a>, maybe <li> or others...
<div class="foo bar">...</div>
.foo
Sample element in the DOM:
CSS Selector:
.foo.bar
[class*="foo"]
<textarea class="form-control">Bootstrap</textarea>
<div class="intro-section">...</div>
Be picky when using classes in your selectors.
They're generally designed for groups of elements, not individual elements.
Avoid style-oriented & "framework" classes:
Prefer classes with specific/semantic value:
<button class="btn-msg-submit">Send Message</button
<div class="ng-scope">AngularJS stuff</div>
<div class="name">
<span class="label">Justin</span>
</div>
<div class="email">
<span class="label">test@test.com</span>
</div>
.label
Often necessary... Immediately increases complexity, but sometimes actually improves clarity.
Sample DOM:
Simple selector will match both <span> elements:
.name .label
Use parent to match <span> containing "Justin" only:
<ul class="fruits">
<li>Apple</li>
<li>Orange</li>
<li>Pear</li>
</ul>
.fruits li:nth-of-type(2)
In some case (most often in lists) it can be useful to target elements based on their position in a sequence.
Sample DOM:
CSS Selector:
<ul class="fruits">
<li>Apple</li>
<li>Orange</li>
<li>Pear</li>
</ul>
li:contains("Orange")
Wouldn't it be nice if we could target using text contents? Sadly not possible with standard CSS right now :(
Sample DOM:
Non-standard CSS Selector (jQuery/Sizzle only):
//li[contains(text(), "Orange")]
XPath Alternative:
Combination of experience with CSS and knowledge of your application. If in doubt, my logic looks something like this:
Tricky question... decision should be made as a team.
<button data-testing="btn-submit">Submit</button>
Example:
button[data-testing="btn-submit"]
Get acquainted with your browser's developers tools:
document.querySelector(...);
document.querySelectorAll(...);
First matching element:
All matching elements:
You can find a more in-depth blog post on the topic of "CSS Selector Strategies for Automated Browser Testing" on the Ghost Inspector Blog:
https://ghostinspector.com/blog/css-selector-strategies-automated-browser-testing/
Twitter: @justinklemm