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="">#emailSample 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="">#emailThe 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>.fooSample 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>.labelOften necessary... Immediately increases complexity, but sometimes actually improves clarity.
Sample DOM:
Simple selector will match both <span> elements:
.name .labelUse 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