Practical 9: Identifying and Counting the Objects on a Page
Chapter Twelve
Syllabus topic Module 1, "Object Identification and Counting on Web Page", "Tool: Selenium WebDriver", "Write a program to identify and count the total number of objects present on a web page including links, images, buttons, and input elements. Display categorized counts."
Pages 82 to 85 of 162
Aim
To identify and count the objects on a web page, links, images, buttons and input elements, and to display the counts by category.
What you need to know before you start
The exercise sounds like counting and is really about definitions. "How many buttons are on this page?" has more than one right answer, depending on what you mean by a button, and a program only counts what its locator describes. A good answer to this practical states each definition and shows how the count changes with it.
The questions that change the counts:
- Is an
atag with nohrefa link? It looks like a tag for a link, but it goes nowhere, and a browser does not treat it as a link. - Is a hidden element "present"? It is in the page's HTML, but no user can see it or click it.
- Is
<input type="submit">a button or an input? It is aninputelement that behaves as a button. - Is
<input type="hidden">an input? It is an input element that holds data and shows nothing. - Is an image inside a link a link, an image, or both? Both: it is two elements.
A CSS attribute selector writes such definitions exactly. a[href] means "a elements that have an href attribute". input[type=submit] means "inputs whose type is submit". :not(...) excludes a match. Every count in this chapter is one selector.
The page under test
practice/objects.php is a complete page on its own, with no shared header, so that everything counted is visible in one listing:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Objects - Practice Portal</title>
<link rel="stylesheet" href="assets/site.css">
</head>
<body>
<main>
<h1>Objects on a Page</h1>
<p>
<a href="index.php">Home</a>
<a href="about.php">About</a>
<a href="login.php">Login</a>
<a href="mailto:office@example.com">Email the office</a>
<a href="about.php"><img src="assets/logo.png" alt="Portal logo"></a>
<a href="results.php" class="hidden">Results</a>
<a id="notes">Notes (an anchor with no address)</a>
</p>
<h2>Gallery</h2>
<img src="assets/campus.png" alt="Campus">
<img src="assets/library.png" alt="Library">
<img src="assets/lab.png" alt="Laboratory">
<h2>Search</h2>
<form>
<input type="text" name="q" placeholder="Search">
<input type="email" name="email" placeholder="Email">
<input type="password" name="pin" placeholder="PIN">
<input type="hidden" name="source" value="objects">
<label><input type="checkbox" name="exact"> Exact match</label>
<label><input type="radio" name="scope" value="all" checked> All</label>
<label><input type="radio" name="scope" value="titles"> Titles only</label>
<select name="dept">
<option>All departments</option>
<option>Computer Science</option>
</select>
<textarea name="notes" rows="2"></textarea>
<button type="submit">Search</button>
<button type="button">Print</button>
<input type="submit" value="Go">
<input type="button" value="Clear">
<input type="reset" value="Reset">
</form>
</main>
</body>
</html>The four pictures, logo.png, campus.png, library.png and lab.png in practice/assets, can be any small images: rename four pictures you already have. None of the counts depends on what the pictures show, and even a missing picture is still an img element in the page.
The expected counts, from the source
Counted by hand from the listing above, before running anything:
| Category | Definition | Selector | Count |
|---|---|---|---|
| Anchor tags | every a element | a | 7 |
| Links | a elements with an address | a[href] | 6 |
| Visible links | links a user can see | a[href], displayed | 5 |
| Images | every img element | img | 4 |
| Button elements | button tags | button | 2 |
| Input buttons | inputs of type submit, button or reset | input[type=submit], input[type=button], input[type=reset] | 3 |
| All buttons | both of the above | 5 | |
| Input tags | every input element | input | 10 |
| Entry inputs | inputs a user types into or chooses | inputs, not hidden and not buttons | 6 |
| Other form fields | select and textarea | select, textarea | 2 |
Practical 9: Identifying and Counting the Objects on a Page
Where each number comes from:
- 7 anchors, 6 links, 5 visible. The
awithid="notes"has nohref, so it is not a link. The Results link has the classhidden, which the style sheet sets todisplay: none, so it is a link nobody can see. - 4 images. The logo inside the link is an image as well as being inside a link.
- 5 buttons. Two
buttontags, and three inputs of type submit, button and reset. - 10 inputs, 6 for entry. The ten
inputtags include the hidden one and the three button types; the six left are text, email, password, the checkbox and the two radios.
The program
Create practicals/CountObjects.java:
package practicals;
import java.util.List;
import org.openqa.selenium.By;
import org.openqa.selenium.JavascriptExecutor;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.chrome.ChromeDriver;
public class CountObjects {
static WebDriver driver;
static int count(String css) {
return driver.findElements(By.cssSelector(css)).size();
}
static int countVisible(String css) {
int n = 0;
for (WebElement e : driver.findElements(By.cssSelector(css))) {
if (e.isDisplayed()) n++;
}
return n;
}
public static void main(String[] args) {
driver = new ChromeDriver();
try {
driver.get("http://localhost:8080/objects.php");
String inputButtons = "input[type=submit], input[type=button], input[type=reset]";
String entryInputs = "input:not([type=hidden]):not([type=submit]):not([type=button]):not([type=reset])";
System.out.println("LINKS");
System.out.println(" anchor tags (a) " + count("a"));
System.out.println(" links (a with href) " + count("a[href]"));
System.out.println(" visible links " + countVisible("a[href]"));
System.out.println("IMAGES");
System.out.println(" img elements " + count("img"));
int loaded = 0;
for (WebElement img : driver.findElements(By.tagName("img"))) {
Object width = ((JavascriptExecutor) driver).executeScript("return arguments[0].naturalWidth;", img);
if (((Number) width).intValue() > 0) loaded++;
}
System.out.println(" images that loaded " + loaded);
System.out.println("BUTTONS");
System.out.println(" button elements " + count("button"));
System.out.println(" input buttons " + count(inputButtons));
System.out.println(" all buttons " + (count("button") + count(inputButtons)));
System.out.println("INPUT ELEMENTS");
System.out.println(" input tags " + count("input"));
System.out.println(" entry inputs " + count(entryInputs));
System.out.println(" select and textarea " + count("select, textarea"));
System.out.println();
System.out.println("The links, with what each points to:");
List<WebElement> links = driver.findElements(By.cssSelector("a[href]"));
for (WebElement a : links) {
String label = a.isDisplayed() ? a.getText() : "(hidden)";
if (label.isEmpty()) label = "(image: " + a.findElement(By.tagName("img")).getDomAttribute("alt") + ")";
System.out.println(" " + label + " -> " + a.getDomAttribute("href"));
}
} finally {
driver.quit();
}
}
}LINKS
anchor tags (a) 7
links (a with href) 6
visible links 5
IMAGES
img elements 4
images that loaded 4
BUTTONS
button elements 2
input buttons 3
all buttons 5
INPUT ELEMENTS
input tags 10
entry inputs 6
select and textarea 2
The links, with what each points to:
Home -> index.php
About -> about.php
Login -> login.php
Email the office -> mailto:office@example.com
(image: Portal logo) -> about.php
(hidden) -> results.phpPractical 9: Identifying and Counting the Objects on a Page
Every count is the one worked out from the source. Three details of the program:
findElementsis used for every count, neverfindElement: a category with nothing in it should count 0, not throw an exception.isDisplayed()separates the hidden Results link from the five a user can see.getText()of a hidden element is empty, so the list prints "(hidden)" for it instead.- Images that loaded asks the browser for each picture's
naturalWidth, its width in the image file itself, throughJavascriptExecutor. A picture that failed to load has a natural width of 0. This is how a test finds broken images, which a count ofimgtags cannot: a broken image is still animg.
Why the selectors, and not the tag names alone
By.tagName("input") returns ten elements, and a program that reports "10 input elements" is right about the tags and wrong about the form, which has six fields a user fills in. By.tagName("button") returns two, and misses the three input buttons a user would also call buttons. The categorised counts MU asks for are only meaningful when the definition is stated, which is why the output labels each count with its definition and the journal should too.
Observations
| Category | Expected | Program |
|---|---|---|
| Anchor tags | 7 | 7 |
| Links | 6 | 6 |
| Visible links | 5 | 5 |
| Images | 4 | 4 |
| All buttons | 5 | 5 |
| Input tags | 10 | 10 |
| Entry inputs | 6 | 6 |
Result
The objects on the page were identified and counted by category: 6 links (5 visible, plus 1 anchor with no address), 4 images, all loaded, 5 buttons (2 button elements and 3 input buttons), and 10 input tags of which 6 are entry fields, with 2 more form fields. Every count agreed with the count made by hand from the page's source.
Where marks are lost
One number per category with no definition. "Buttons: 2" is wrong for a user and right for a tag count; say which.
By.tagName("a") for links. It includes the anchor with no address.
Counting hidden elements as visible. Use isDisplayed() when the question is what a user sees.
findElement to count. It throws when there are none.
Forgetting input buttons, or counting them twice, once as buttons and once as inputs, without saying so.
For the journal
Aim; the page under test; the table of definitions and selectors; the expected counts; the program; its output; the observations table; the result.
Practical 9: Identifying and Counting the Objects on a Page
Quick revision
- Counting objects is really stating definitions.
acounts anchors;a[href]counts links; addisDisplayed()for visible links.- Buttons:
buttontags plusinputtags whosetypeissubmit,buttonorreset. - Inputs:
inputcounts every tag; exclude hidden and button types for entry fields. findElements(...).size()counts, and returns 0 when there are none.- A broken image is still an
img; checknaturalWidthto know it loaded.
Questions you must be able to answer
1. Why do a and a[href] give different counts here? One a element has no href, so it is an anchor but not a link.
2. How does the program know the Results link is hidden? isDisplayed() returns false for it, because the page's style sheet hides its class.
3. How many buttons are on the page? Five by what a user sees: two button elements and three inputs of type submit, button and reset. Two if only button tags are counted.
4. Why is By.tagName("input") not the number of form fields? It also counts the hidden input and the three input buttons, which a user does not fill in.
5. How can a test tell that an image failed to load? Its naturalWidth, read through JavascriptExecutor, is 0.
6. Why use findElements for counting? It returns an empty list, size 0, when nothing matches; findElement would throw.
The rest of this subject
These notes are cut from the University's printed syllabus. Open the syllabus itself for the same subject.