As some will already know, I don’t use XPath selectors. Why? The web doesn’t need XPath and rarely (if ever) uses it. The web, in all its richness, use HTML, JavaScript and CSS.
What is CSS? → Cascading Style Sheet
Briefly, CSS is a way to bring style into an HTML document (i.e. a web page). If you want your site banner to be blue instead of white, you do that with CSS. For example, if your site banner is an HTML <div>
element with a class “my-banner”…
<div class="my-banner"> My amazing website! </div>
HTML div element using class attribute
then your CSS style sheet may contain this:
.my-banner {
background-color:blue;
}
CSS rule defining the class my-banner
Notice in the CSS code, the classname .my-banner
starts with a dot:
CSS class rules start with a dot
that is how CSS identifies a class. HTML, on the other hand, identifies classes with the class=
attribute (you therefore don’t need a dot).
Let’s now look at IDs. Let’s say you want to give your banner an id=
…
<div id="my-website-banner" class="my-banner"> My amazing website! </div>
HTML div element using class and id attributes
Where CSS uses a dot to identify a class, it uses a #
to represent id
…
#my-website-banner {
color: white;
font-size: 30px;
}
CSS rule defining a rule for the id my-website-banner
Now your banner will have a blue background because of its class, and white text and a 30px sized font because of its id.
But Russ, this is about writing HTML and CSS - what about testing?
You’re right. This was meant to be a quick intro but you need this knowledge to understand what CSS selectors do for testing. One last thing before we move on…
If you wanted to give every div
in your web page a red border 2 pixels wide, here is the CSS:
div {
border: 2px solid red;
}
CSS rule that will target every div on the page
The point is, it doesn’t matter if you have 5 divs or 500, that code will give ALL divs a red border 2px wide - unless there are more CSS rules that override that rule. For example, if we specify that your #my-website-banner
is to have a 5px green border instead of a 2px red one, it will override the more general div
rule:
#my-website-banner {
border: 5px solid green;
color: white;
font-size: 30px;
}
CSS rule defining a rule for the id my-website-banner
How does the #my-website-banner
rule override the div
rule? That’s down to something called specificity - the id rule is more specific than the general div
rule. To learn more about that topic, I suggest you search the web for CSS specificity.
Okay. Enough with the theory. Let’s see how we can use CSS selectors for testing.
For this part, you’ll need your browser open on the Katalon forum “latest” page:
Which browser? Any browser is fine, but I’m using Firefox - I say this because we’re going to use DevTools and I prefer the tools offered by Firefox.
Finding the Search button
Katalon forum search button
If you use DevTools to inspect the search button, you should see something like this:
Firefox DevTools showing the details around the search buttonKatalon forum search button
Notice the DevTools inspector landed on the svg image inside the <a>
element with an id="search-button"
. The <a>
element is what we want because <a>
tags handle clicks. And, luckily for us, IDs are always unique because it’s a universal law of HTML
As we learned earlier, IDs are more specific than pretty much everything else. This makes our CSS selector super easy and strong (no flaky and lengthy path required)!
#search-button
Now you can create a Test Object (in the Object Repository) and add that CSS selector - you don’t need anything else! - and use it in a Test Case.
Want to prove the CSS selector works before you create the Test Object?
For that we need to show the console in Devtools. In Firefox, you toggle the console to appear below the inspector window by hitting the Esc key.
To test a selector in the DevTools console we need a web DOM interface (that’s a function, to you and me) called document.querySelector()
If we type this into the console…
document.querySelector("#search-button")
then DevTools should report back with the found element’s details (if we spell the id wrong, we get null
).
Testing a CSS selector in the DevTools inspector and console
So, at this point, you know your selector is completely valid. When you move it into a Test Object in Katalon, the only thing that can stop it working is “you” - you forgot to wait for the page to load, you forgot to check it’s visible, that kind of thing… the selector works fine.
Now let’s take a look at something a little more awkward - something without an id. Note, IDs may still be involved but the element we want to target will NOT have an id.
Let’s say you are working for Katalon. Let’s say @ThanhTo or @duyluong ask you to check the forum home page has a link “FORUM RULES” that should go to a url (href) for post number 9691
. And (yes, there’s more) they want you to verify it’s the second link in the same line of text.
Okay. That’s the job. Let’s get to it.
We’re going to inspect “FORUM RULES” and see what we get:
The FORUM RULES link on the Katalon forum page
The FORUM RULES link in the DevTools inspector
So a first stab might be to create a selector for the highlighted <a>
element and ensure the href
attribute contains `9691. That’s okay, but a little flaky. There might be another reference to the same post on the same page. Disaster. We may find the wrong link or nothing (because there’s more than one).
What if we could find the <a>
that is a child element of something unique above? If you look back at the inspector window, you’ll notice there is a <div id="ember9" ...>
which is an ancestor of the <a>
element we want. That’s perfect for us.
Here is a selector that finds <a>
elements that are children of `id=“ember9”…
#ember9 a
CSS selector to find <a>
elements beneath the element with id ember9
The problem now is, that selector finds ALL <a>
elements that are children of ember9
. We want the second one (because the guys said they want you to verify it’s the second link). So, let’s fix the selector using the CSS nth-of-type
operator.
#ember9 a:nth-of-type(2)
CSS selector to find the second <a>
element beneath the element with id ember9
I agree, nth-of-type
is a bit of a mouthful. Just remember when supplied with 1, it means the first, with 2 it means the second, and so on. So our selector is saying…
“find the second
<a>
element that is a descendant of#ember9
”.
So far, so good. Now to ensure the <a>
element is pointing to post 9691. To do that we can build a selector that examines the href
attribute and tell it to verify any portion of its text. In our case, ensure it mentions 9691…
#ember9 a:nth-of-type(2):[href*=9691]
CSS selector using attribute selector to find post 9691
DevTools console showing CSS attribute selector
You will notice this selector is using the attribute selector - [attribute-name=value]
. But if you look more closely, you’ll see we’re actually using [attribute-name*=value]
, in our case [href*=9691]
.
This is saying “select the <a>
element with href that contains 9691”.
Aside: In addition to contains, we could have said ends with using
$=
or we could have used the whole URL using just=
but I was concerned if someone changed the title of the post, the test would break
For more on attribute selectors:
Attribute selectors - Learn web development | MDN
Lastly, we’ll look at something other than <a>
elements, namely, <button>
elements. And this time we’ll use this page:
On that page you’ll notice there are a number of replies to my original topic. We’re going to focus on the reply from @ThanhTo. Specifically, we’re going to target his user-id, his user-card and also (using JavaScript) click on the Reply button. (Don’t worry, if any spurious responses are posted, I’ll see them and delete them
Okay, a quick look at the page in the DevTools inspector shows a bunch of HTML for @ThanhTo’s post:
ThanhTo’s post in DevTools inspector
You’ll notice I circled …
- the post id
- his user-id
- his user-card
- a single
<p>
element which happens to be his first paragraph inside his post - and lastly, the Reply button
Here are the CSS selectors to target each of those items in the order I specified above:
The CSS selector for the article post id:
#post_22
JavaScript:
document.querySelector("#post_22")
The CSS selector for his user-id:
article[data-user-id='7647']
JavaScript:
document.querySelector("article[data-user-id='7647']")
The CSS selector for his user-card:
#post_22 .trigger-user-card a[data-user-card='ThanhTo']
JavaScript:
document.querySelector("#post_22 .trigger-user-card a[data-user-card='ThanhTo']")
The CSS selector for his first paragraph:
#post_22 div.regular.contents p
JavaScript:
document.querySelector("#post_22 div.regular.contents p")
Of course, we can use the innerText
or textContent
properties to retrieve the content of his first paragraph like so:
document.querySelector("#post_22 div.regular.contents p").innerText
DevTools console showing ThanhTo’s first paragraph
Lastly, let’s get that Reply button so we can bombard @ThanhTo with a million useless replies
The CSS selector for the Reply button
#post_22 .actions button.reply
JavaScript:
document.querySelector("#post_22 .actions button.reply")
And here is a minimal Test Case that will click on that button. It is left to you to create a Test Object for the Reply button (replyBtn
) using the CSS selector above AND to perform the usual navigation to reach the forum page:
// imports...
// navigate to Katalon forum page...
// wait until page is loaded...
WebUI.click(replyBtn)
Of course, you don’t need to create a Test Object - you could just do it directly in JavaScript:
// imports...
// navigate to Katalon forum page...
// wait until page is loaded...
String js = 'document.querySelector("#post_22 .actions button.reply").click();'
WebUI.executeJavaScript(js, null)
To complete the exercise and fill out a reply to @ThanhTo, you’ll need to do a little more work.
I hope you guys find this useful. For a little more reading, try this topic: