Practical 1: Creating a Test Suite with Selenium IDE
Chapter Three
Syllabus topic Module 1, "Creation of Test Suite Using Selenium IDE", "Tool: Selenium IDE (Chrome/Firefox Extension)", "Install Selenium IDE and create a test suite containing a minimum of four test cases to validate different web page formats (HTML page, XML response, JSON API output, and a dynamic form page). Execute the suite and export the automation script in WebDriver format."
Pages 14 to 25 of 162
Aim
To install Selenium IDE, to create a test suite of four test cases that validate four different web page formats (an HTML page, an XML response, a JSON API output and a dynamic form page), to execute the suite, and to export the automation script in WebDriver format.
What you need to know before you start
Selenium IDE is a record-and-playback tool for testing web pages. It runs as an add-on inside the browser. You either record what you do on a page, or type the steps in yourself, and the IDE plays them back and tells you whether the page behaved as expected. No programming is needed, which is why MU puts it first.
The IDE's words, which you will need in the journal:
- Command. One step:
opena page,clicksomething,typeinto a box,assertthat something is true. - Target. What the command acts on. For
openit is an address; forclickit is a locator, a way of finding one element on the page. - Value. A second argument when the command needs one: the text to type, or the text an assertion expects.
- Test case. A named list of commands that checks one thing.
- Test suite. A named group of test cases that are run together.
- Project. Everything together, saved as one file whose name ends in
.side.
Every command is one row: Command | Target | Value. This book writes each test case the same way, one command a line, so you can type it straight into the IDE.
Locators tell the IDE which element you mean. The ones used in this practical:
| Locator | Finds | Example |
|---|---|---|
id= | the element with that id attribute | id=fee |
css= | the first element a CSS selector matches | css=h1 |
xpath= | the first element an XPath expression matches | one cell of the office-hours table, in the test case below |
An id is the best locator when a page has one, because a page should use each id only once.
Assert against verify. Both check something. When an assert fails the test case stops there. When a verify fails, the failure is recorded and the test carries on. The IDE's own description of verify text says exactly that: the test "will continue even if the verify fails".
The four page formats MU names. A web server does not only send web pages. It says what it is sending in a Content-Type header, and the four formats are four answers:
| Format | What the server sends | Content-Type on the Practice Portal |
|---|---|---|
| HTML page | a page for a person to read | text/html |
| XML response | data marked up with tags of its own | application/xml |
| JSON API output | data as JavaScript Object Notation, for programs | application/json |
| Dynamic form page | an HTML page whose content changes after it loads, by JavaScript | text/html |
Practical 1: Creating a Test Suite with Selenium IDE
The first is what the IDE was built for. The next two are data, not pages, and the IDE needs a different technique for them, which is the most instructive part of this practical. The fourth changes while you look at it, which is what waits are for.
Installing Selenium IDE: the one fact that decides the browser
MU names Selenium IDE as a "Chrome/Firefox Extension". Only one of those two still exists.
Chrome. Selenium IDE's Chrome extension was built on an old extension format, Manifest V2, which Google has removed from Chrome. Google's own timeline records that from 24 July 2025, with Chrome 138, Manifest V2 extensions were disabled for all users, who "can no longer turn them back on", and that on 31 August 2026 all remaining Manifest V2 extensions were removed from the Chrome Web Store. Users reported the Selenium IDE extension switched off in January 2025, and a Selenium project member answered on the project's own issue tracker: "v4 is an Electron app, it is not a browser extension."
Firefox. The Firefox add-on, version 3.17.4, is still published on Firefox's add-ons site, and it is what this chapter uses. Every step below was done in it.
If your laboratory has only Chrome, the Selenium project's own route is Selenium IDE 4, a separate desktop program that drives Chrome, published on the project's GitHub releases page as version 4.0.1-beta.14. It is marked beta and its installers are not code-signed, so Windows warns before running one. This book's steps and screenshots are for the Firefox add-on.
To install it in Firefox:
- Open Firefox and go to
addons.mozilla.org. - Search for Selenium IDE and open its page.
- Click Add to Firefox. Firefox lists the permissions it asks for; click Add.
- A Selenium IDE icon appears among the toolbar icons, or under the jigsaw-piece Extensions button. Pin it to the toolbar so it is always one click away.
- Click the icon. The IDE opens in a window of its own.
Figure 3.1 Selenium IDE 3.17.4 opening in Firefox, as the lab's own Firefox showed it.
The version number, 3.17.4, is printed in the header. Write it in your journal.
The pages under test
Make sure the Practice Portal is running (php -S localhost:8080 router.php inside practice), then add these four files to it.
The HTML page, practice/about.php:
<?php $title = 'About'; include 'inc/top.php'; ?>
<h1>About the Practice Portal</h1>
<p id="intro">The Practice Portal is a small college website built for testing practice.</p>
<h2>Departments</h2>
<ul id="departments">
<li>Computer Science</li>
<li>Information Technology</li>
<li>Commerce</li>
</ul>
<h2>Office hours</h2>
<table id="hours">
<tr><th>Day</th><th>Time</th></tr>
<tr><td>Monday to Friday</td><td>10:00 to 17:00</td></tr>
<tr><td>Saturday</td><td>10:00 to 13:00</td></tr>
</table>
<?php include 'inc/bottom.php'; ?>Practical 1: Creating a Test Suite with Selenium IDE
The XML response, practice/api/notices.php. It sends XML and says so in its Content-Type header:
<?php
// api/notices.php: the college notices, as XML.
header('Content-Type: application/xml; charset=utf-8');
echo '<?xml version="1.0" encoding="UTF-8"?>' . "\n";
?>
<notices>
<notice id="1" date="2026-07-01">
<title>Semester 5 lectures begin</title>
</notice>
<notice id="2" date="2026-07-15">
<title>Journal submission for Practical 1</title>
</notice>
<notice id="3" date="2026-08-05">
<title>Mid-term practical examination timetable</title>
</notice>
</notices>The XML declaration is printed with echo rather than written out, because <? at the start of a line means "PHP starts here" to some PHP installations.
The JSON API, practice/api/students.php, with the records it serves in practice/seed/students.json:
<?php
// api/students.php: the student list as JSON. ?roll=103 returns one student.
require __DIR__ . '/../inc/store.php';
header('Content-Type: application/json; charset=utf-8');
$students = load_records('students');
if (isset($_GET['roll'])) {
foreach ($students as $s) {
if ($s['roll'] === (int) $_GET['roll']) {
echo json_encode($s);
exit;
}
}
http_response_code(404);
echo json_encode(['error' => 'No student with that roll number']);
exit;
}
echo json_encode(['count' => count($students), 'students' => $students]);[
{"roll": 101, "name": "Aarav Shah", "course": "B.Sc. CS", "city": "Mumbai", "marks": 72},
{"roll": 102, "name": "Diya Nair", "course": "B.Sc. CS", "city": "Thane", "marks": 65},
{"roll": 103, "name": "Kabir Khan", "course": "B.Sc. IT", "city": "Pune", "marks": 58},
{"roll": 104, "name": "Meera Iyer", "course": "B.Sc. CS", "city": "Mumbai", "marks": 81},
{"roll": 105, "name": "Rohan Patil", "course": "B.Com.", "city": "Nashik", "marks": 49},
{"roll": 106, "name": "Sara D'Souza", "course": "B.Sc. IT", "city": "Mumbai", "marks": 77},
{"roll": 107, "name": "Vivaan Joshi", "course": "B.Sc. CS", "city": "Thane", "marks": 90},
{"roll": 108, "name": "Ananya Rao", "course": "B.Com.", "city": "Pune", "marks": 63},
{"roll": 109, "name": "Ishaan Gupta", "course": "B.Sc. IT", "city": "Mumbai", "marks": 55},
{"roll": 110, "name": "Zoya Sheikh", "course": "B.Sc. CS", "city": "Nashik", "marks": 84}
]The dynamic form, practice/enquiry.php. The Year box is hidden until a course is chosen, the fee appears only then, and the room choice appears only when the hostel box is ticked. None of that is in the page the server sends; JavaScript does it in the browser:
<?php $title = 'Admission Enquiry'; include 'inc/top.php'; ?>
<h1>Admission Enquiry</h1>
<form id="enquiry">
<label>Full name <input type="text" id="name"></label>
<label>Email <input type="text" id="email"></label>
<label>Course
<select id="course">
<option value="">-- Select --</option>
<option>B.Sc. CS</option>
<option>B.Sc. IT</option>
<option>B.Com.</option>
</select>
</label>
<p id="fee"></p>
<label id="yearLabel" class="hidden">Year
<select id="year">
<option>FY</option>
<option>SY</option>
<option>TY</option>
</select>
</label>
<label><input type="checkbox" id="hostel"> I need a hostel room</label>
<div id="rooms" class="hidden">
<label><input type="radio" name="room" id="single" value="Single"> Single room</label>
<label><input type="radio" name="room" id="shared" value="Shared"> Shared room</label>
</div>
<p id="errors" class="error"></p>
<button type="button" id="submit">Send enquiry</button>
</form>
<p id="confirmation" class="success"></p>
<script>
const fees = { 'B.Sc. CS': 'Rs. 38,000', 'B.Sc. IT': 'Rs. 36,000', 'B.Com.': 'Rs. 24,000' };
const course = document.getElementById('course');
course.addEventListener('change', function () {
const chosen = course.value !== '';
document.getElementById('fee').textContent = chosen ? 'Annual fee: ' + fees[course.value] : '';
document.getElementById('yearLabel').classList.toggle('hidden', !chosen);
});
document.getElementById('hostel').addEventListener('change', function () {
document.getElementById('rooms').classList.toggle('hidden', !this.checked);
});
document.getElementById('submit').addEventListener('click', function () {
const name = document.getElementById('name').value.trim();
const email = document.getElementById('email').value.trim();
const problems = [];
if (name === '') problems.push('Please enter your name.');
if (!/^[^@\s]+@[^@\s]+\.[a-z]{2,}$/i.test(email)) problems.push('Please enter a valid email.');
if (course.value === '') problems.push('Please choose a course.');
document.getElementById('errors').textContent = problems.join(' ');
if (problems.length > 0) return;
const year = document.getElementById('year').value;
let text = 'Thank you, ' + name + '. Your enquiry for ' + course.value + ' (' + year + ') has been recorded.';
const room = document.querySelector('input[name="room"]:checked');
if (document.getElementById('hostel').checked && room) text += ' Hostel: ' + room.value + ' room.';
document.getElementById('enquiry').classList.add('hidden');
document.getElementById('confirmation').textContent = text;
});
</script>
<?php include 'inc/bottom.php'; ?>Practical 1: Creating a Test Suite with Selenium IDE
The IDE window
Figure 3.2 The IDE with one test case open: the test list on the left, the command table in the middle, the command editor below it and the log at the bottom.
Four areas matter:
- The left panel lists the project's test cases. Its heading is a menu: Tests, Test suites or Executing, which shows what is running.
- The toolbar above the table: Run all tests (a triangle with lines), Run current test (a plain triangle), Step over, the speed control, and at the right the red REC button for recording.
- The playback base URL box, which holds the start of every address. With
http://localhost:8080in it, a test can sayopen /about.php. - The command table and, under it, the Command, Target, Value and Description boxes where the selected row is edited.
At the bottom are two tabs: Log, where a run reports every step, and Reference, which explains the command in the selected row.
Step 1: a project and its base URL
- In the welcome window, click Create a new project.
- The IDE asks "Name your new project". Type
Practical 1and click OK. - The project opens with one empty test case called Untitled. In the Playback base URL box, type
http://localhost:8080.
Step 2: test case 1, the HTML page
Rename Untitled: hover over it in the left panel, click the three-dot button beside it, choose Rename, and call it HTML page. Then click the first empty row of the table and type each command into the Command, Target and Value boxes below it. The IDE suggests command names as you type.
Test: HTML page
open | /about.php
assert title | About - Practice Portal
assert text | css=h1 | About the Practice Portal
assert element present | id=departments
verify text | xpath=//table[@id='hours']//tr[2]/td[2] | 10:00 to 17:00Practical 1: Creating a Test Suite with Selenium IDE
Line by line:
open /about.phploads the page. The IDE joins the base URL and the target.assert titlechecks the browser's title bar text. The title comes frominc/top.php, which adds " - Practice Portal" to the page's own title.assert textoncss=h1checks the heading.assert element presentonid=departmentschecks the departments list exists at all.verify textreads one cell of the office-hours table: the second row, second column. It is a verify, so if it fails the test still finishes.
Why XPath for the cell: the table cells have no ids, so the locator describes the path to the cell instead. //table[@id='hours'] finds the table, //tr[2] its second row, and /td[2] that row's second cell.
Step 3: test case 2, the XML response
Add a new test case with the + button at the top of the left panel and call it XML response.
The obvious test is wrong, and it is worth seeing why. The obvious test opens the XML address and asserts the text of a notice's title:
Test: XML opened directly
open | /api/notices.php
assert text | xpath=//notice[@id='2']/title | Journal submission for Practical 1Suite: Direct
XML opened directlyPlayed in the IDE, the open succeeds and the assertion fails with a message about the page itself, not about the text:
Running 'XML opened directly'
1. open on /api/notices.php OK
2. assertText on xpath=//notice[@id='2']/title with value Journal submission for Practical 1 Failed:
can't access property "addEventListener", _win is null
'XML opened directly' ended with 1 error(s)The reason: the IDE works by placing a small program of its own inside each page it tests, and an XML document is not a web page. Firefox shows it as a formatted tree, and there is no page window for the IDE's program to work in. The same is true of JSON, which Firefox shows in a viewer of its own.
The technique that works is to open an ordinary page of the same site and ask the browser to fetch the data, which is exactly what a real web page does. The IDE's execute async script command runs a piece of JavaScript in the page; in the IDE's own words, the script "must return a Promise", and its result "will be saved on the variable if you use the 'return' keyword". The assert command then checks that variable:
Test: XML response
open | /index.php
execute async script | return fetch('/api/notices.php').then(r => r.headers.get('Content-Type')) | type
assert | type | application/xml; charset=utf-8
execute async script | return fetch('/api/notices.php').then(r => r.text()).then(t => new DOMParser().parseFromString(t, 'application/xml').getElementsByTagName('notice').length) | noticeCount
assert | noticeCount | 3
execute async script | return fetch('/api/notices.php').then(r => r.text()).then(t => new DOMParser().parseFromString(t, 'application/xml').querySelector('notice[id="2"] title').textContent) | secondTitle
assert | secondTitle | Journal submission for Practical 1Practical 1: Creating a Test Suite with Selenium IDE
Three checks, each on a different property of the response:
- The format. The Content-Type header must say
application/xml. A server that sent the right text with the wrong header would fail here, and many programs that read XML would refuse it. - The structure.
DOMParserturns the text into an XML document, and there must be exactly threenoticeelements. - The content. The title inside the notice whose
idis 2 must be the expected text.
fetch(...) asks the server for the address and returns a Promise; .then(r => r.text()) waits for the body; the arrow functions (r => ...) are JavaScript's short way of writing a small function. You do not need to write JavaScript anywhere else in this paper, and these lines can be copied as they are.
Step 4: test case 3, the JSON API
Add a test case called JSON API. The same technique, with r.json(), which reads the body as JSON and turns it into JavaScript objects:
Test: JSON API
open | /index.php
execute async script | return fetch('/api/students.php').then(r => r.headers.get('Content-Type')) | type
assert | type | application/json; charset=utf-8
execute async script | return fetch('/api/students.php').then(r => r.json()).then(d => d.count) | count
assert | count | 10
execute async script | return fetch('/api/students.php?roll=103').then(r => r.json()).then(s => s.name) | name
assert | name | Kabir KhanAgain three checks: the header says JSON, the count field of the full list is 10, and asking for roll number 103 alone returns the student named Kabir Khan. The IDE's own description of assert says the variable's value "will be converted to a string for comparison", which is why a number, 10, can be compared with the text 10.
Step 5: test case 4, the dynamic form
Add a test case called Dynamic form. This one fills in the enquiry form the way a person would:
Test: Dynamic form
open | /enquiry.php
type | id=name | Priya Sharma
type | id=email | priya@example.com
select | id=course | label=B.Sc. CS
wait for element visible | id=year | 5000
assert text | id=fee | Annual fee: Rs. 38,000
select | id=year | label=TY
click | id=hostel
wait for element visible | id=shared | 5000
click | id=shared
click | id=submit
assert text | id=confirmation | Thank you, Priya Sharma. Your enquiry for B.Sc. CS (TY) has been recorded. Hostel: Shared room.The two wait for element visible lines are what make this a test of a dynamic page. The Year box does not exist on screen until a course is chosen, and the room choices do not exist until the hostel box is ticked. Each wait holds the test until the element can be seen, for at most the Value, 5000 milliseconds. Without them, a test run at full speed can reach for an element in the moment before the page's JavaScript has shown it.
Practical 1: Creating a Test Suite with Selenium IDE
select chooses from a dropdown. label=B.Sc. CS picks the option whose visible text is B.Sc. CS.
Step 6: the suite
A test suite is what MU asks for, and four separate test cases are not yet one.
- Open the left panel's heading menu and choose Test suites. The IDE already shows one called Default Suite.
- Click + and name the new suite
Page formats. - Hover over the suite, click its three-dot button, and choose Add tests.
- Tick the four test cases and click Select.
Suite: Page formats
HTML page
XML response
JSON API
Dynamic formFigure 3.3 Each suite has its own menu; Add tests is how test cases are put into it.
The suite's Settings hold its timeout, and whether its tests run in parallel or share one browser session. Leave them as they are for this practical.
Step 7: execute the suite
Click the arrow beside the suite to open it, click the first test case inside it, then click Run all tests in suite (the triangle with lines).
If you click Run before selecting a test case inside the suite, the IDE answers with a message instead of running: "To play a suite you must select a test case from within that suite." That message is where a lot of laboratory time goes.
The IDE opens a new Firefox window, plays each test case in it, and writes every step into the Log tab. This is the whole log of the suite, as the IDE printed it (the IDE also shows the time of day beside each line):
Running 'HTML page'
1. open on /about.php OK
2. assertTitle on About - Practice Portal OK
3. assertText on css=h1 with value About the Practice Portal OK
4. assertElementPresent on id=departments OK
5. verifyText on xpath=//table[@id='hours']//tr[2]/td[2] with value 10:00 to 17:00 OK
'HTML page' completed successfully
Running 'XML response'
1. open on /index.php OK
2. executeAsyncScript on return fetch('/api/notices.php').then(r => r.headers.get('Content-Type')) with value type OK
3. assert on type with value application/xml; charset=utf-8 OK
4. executeAsyncScript on return fetch('/api/notices.php').then(r => r.text()).then(t => new DOMParser().parseFromString(t, 'application/xml').getElementsByTagName('notice').length) with value noticeCount OK
5. assert on noticeCount with value 3 OK
6. executeAsyncScript on return fetch('/api/notices.php').then(r => r.text()).then(t => new DOMParser().parseFromString(t, 'application/xml').querySelector('notice[id="2"] title').textContent) with value secondTitle OK
7. assert on secondTitle with value Journal submission for Practical 1 OK
'XML response' completed successfully
Running 'JSON API'
1. open on /index.php OK
2. executeAsyncScript on return fetch('/api/students.php').then(r => r.headers.get('Content-Type')) with value type OK
3. assert on type with value application/json; charset=utf-8 OK
4. executeAsyncScript on return fetch('/api/students.php').then(r => r.json()).then(d => d.count) with value count OK
5. assert on count with value 10 OK
6. executeAsyncScript on return fetch('/api/students.php?roll=103').then(r => r.json()).then(s => s.name) with value name OK
7. assert on name with value Kabir Khan OK
'JSON API' completed successfully
Running 'Dynamic form'
1. open on /enquiry.php OK
2. type on id=name with value Priya Sharma OK
3. type on id=email with value priya@example.com OK
4. select on id=course with value label=B.Sc. CS OK
5. waitForElementVisible on id=year with value 5000 OK
6. assertText on id=fee with value Annual fee: Rs. 38,000 OK
7. select on id=year with value label=TY OK
8. click on id=hostel OK
9. waitForElementVisible on id=shared with value 5000 OK
10. click on id=shared OK
11. click on id=submit OK
12. assertText on id=confirmation with value Thank you, Priya Sharma. Your enquiry for B.Sc. CS (TY) has been recorded. Hostel: Shared room. OK
'Dynamic form' completed successfullyPractical 1: Creating a Test Suite with Selenium IDE
Read it the way an examiner will. Each test case starts with a Running line and ends with completed successfully; each numbered line is one command, with the target and value it was given, and OK means it passed. The command names in the log are the IDE's internal ones, assertTitle for "assert title" and so on.
Figure 3.4 After the run: every test case in the suite ticked green, and the end of the log below it.
Step 8: save the project
Click the Save project button at the top right (or press Ctrl+S). Firefox asks where to save; save it as practical-1.side in your practical folder. The .side file is plain text in JSON, and holds the project's name, its base URL, every test case with every command, and every suite. It is also what the command-line runner in Practical 2 plays.
Step 9: export the automation script in WebDriver format
MU's last instruction is to "export the automation script in WebDriver format". WebDriver is Selenium's programming interface, the one every later practical uses from Java; "WebDriver format" means code that does through WebDriver what the IDE did.
- In the Test suites view, open the suite's three-dot menu and choose Export.
- The IDE asks you to "Select language". It offers C# NUnit, C# xUnit, Java JUnit, JavaScript Mocha, Python pytest and Ruby RSpec. Choose Java JUnit.
- Leave the three boxes below unticked (origin tracing comments, step descriptions as comments, and Selenium Grid).
- Click Export, and save the file.
Figure 3.5 Java JUnit is the one this book uses: every later practical is in Java.
This is the file the IDE wrote, exactly:
Practical 1: Creating a Test Suite with Selenium IDE
// Generated by Selenium IDE
import org.junit.Test;
import org.junit.Before;
import org.junit.After;
import static org.junit.Assert.*;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.core.IsNot.not;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.firefox.FirefoxDriver;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.remote.RemoteWebDriver;
import org.openqa.selenium.remote.DesiredCapabilities;
import org.openqa.selenium.Dimension;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.interactions.Actions;
import org.openqa.selenium.support.ui.ExpectedConditions;
import org.openqa.selenium.support.ui.WebDriverWait;
import org.openqa.selenium.JavascriptExecutor;
import org.openqa.selenium.Alert;
import org.openqa.selenium.Keys;
import java.util.*;
import java.net.MalformedURLException;
import java.net.URL;
public class PageformatsTest {
private WebDriver driver;
private Map<String, Object> vars;
JavascriptExecutor js;
@Before
public void setUp() {
driver = new FirefoxDriver();
js = (JavascriptExecutor) driver;
vars = new HashMap<String, Object>();
}
@After
public void tearDown() {
driver.quit();
}
@Test
public void hTMLpage() {
driver.get("http://localhost:8080/about.php");
assertThat(driver.getTitle(), is("About - Practice Portal"));
assertThat(driver.findElement(By.cssSelector("h1")).getText(), is("About the Practice Portal"));
{
List<WebElement> elements = driver.findElements(By.id("departments"));
assert(elements.size() > 0);
}
assertThat(driver.findElement(By.xpath("//table[@id=\'hours\']//tr[2]/td[2]")).getText(), is("10:00 to 17:00"));
}
@Test
public void xMLresponse() {
driver.get("http://localhost:8080/index.php");
vars.put("type", js.executeAsyncScript("var callback = arguments[arguments.length - 1];return fetch(\'/api/notices.php\').then(r => r.headers.get(\'Content-Type\')).then(callback).catch(callback);"));
assertEquals(vars.get("type").toString(), "application/xml; charset=utf-8");
vars.put("noticeCount", js.executeAsyncScript("var callback = arguments[arguments.length - 1];return fetch(\'/api/notices.php\').then(r => r.text()).then(t => new DOMParser().parseFromString(t, \'application/xml\').getElementsByTagName(\'notice\').length).then(callback).catch(callback);"));
assertEquals(vars.get("noticeCount").toString(), "3");
vars.put("secondTitle", js.executeAsyncScript("var callback = arguments[arguments.length - 1];return fetch(\'/api/notices.php\').then(r => r.text()).then(t => new DOMParser().parseFromString(t, \'application/xml\').querySelector(\'notice[id=\"2\"] title\').textContent).then(callback).catch(callback);"));
assertEquals(vars.get("secondTitle").toString(), "Journal submission for Practical 1");
}
@Test
public void jSONAPI() {
driver.get("http://localhost:8080/index.php");
vars.put("type", js.executeAsyncScript("var callback = arguments[arguments.length - 1];return fetch(\'/api/students.php\').then(r => r.headers.get(\'Content-Type\')).then(callback).catch(callback);"));
assertEquals(vars.get("type").toString(), "application/json; charset=utf-8");
vars.put("count", js.executeAsyncScript("var callback = arguments[arguments.length - 1];return fetch(\'/api/students.php\').then(r => r.json()).then(d => d.count).then(callback).catch(callback);"));
assertEquals(vars.get("count").toString(), "10");
vars.put("name", js.executeAsyncScript("var callback = arguments[arguments.length - 1];return fetch(\'/api/students.php?roll=103\').then(r => r.json()).then(s => s.name).then(callback).catch(callback);"));
assertEquals(vars.get("name").toString(), "Kabir Khan");
}
@Test
public void dynamicform() {
driver.get("http://localhost:8080/enquiry.php");
driver.findElement(By.id("name")).sendKeys("Priya Sharma");
driver.findElement(By.id("email")).sendKeys("priya@example.com");
{
WebElement dropdown = driver.findElement(By.id("course"));
dropdown.findElement(By.xpath("//option[. = 'B.Sc. CS']")).click();
}
{
WebDriverWait wait = new WebDriverWait(driver, 5);
wait.until(ExpectedConditions.visibilityOfElementLocated(By.id("year")));
}
assertThat(driver.findElement(By.id("fee")).getText(), is("Annual fee: Rs. 38,000"));
{
WebElement dropdown = driver.findElement(By.id("year"));
dropdown.findElement(By.xpath("//option[. = 'TY']")).click();
}
driver.findElement(By.id("hostel")).click();
{
WebDriverWait wait = new WebDriverWait(driver, 5);
wait.until(ExpectedConditions.visibilityOfElementLocated(By.id("shared")));
}
driver.findElement(By.id("shared")).click();
driver.findElement(By.id("submit")).click();
assertThat(driver.findElement(By.id("confirmation")).getText(), is("Thank you, Priya Sharma. Your enquiry for B.Sc. CS (TY) has been recorded. Hostel: Shared room."));
}
}How to read it:
- It is a JUnit test class. JUnit is a Java testing framework;
@Beforeruns before each test,@Testmarks a test,@Afterruns after it. setUp()starts Firefox withnew FirefoxDriver(), because the IDE was running in Firefox. An export made from IDE 4 in Chrome saysChromeDriverinstead.- Each test case became one
@Testmethod. The IDE built the Java names from the test names: "HTML page" became the methodhTMLpageand the suite "Page formats" became the classPageformatsTest. Name your tests in plain words and the Java names follow. openbecamedriver.get(...)with the base URL joined on;assert titlebecameassertThat(driver.getTitle(), is(...));typebecamesendKeys;execute async scriptbecamejs.executeAsyncScript(...), with the IDE adding the callback that plays the Promise's role in Java.assert element presentbecame a block ending inassert(elements.size() > 0);. That is Java's ownassertstatement, and Java ignores it unless assertions are switched on when the program is started.
Practical 1: Creating a Test Suite with Selenium IDE
Before you try to run this file, read chapter six. The export was written for Selenium 3, and two of its lines use a form of WebDriverWait that Selenium 4 no longer has; compiled against today's Selenium they are errors. Chapter six sets up Java and Maven, compiles this very file, shows both errors, and fixes them.
Recording instead of typing
Everything above can also be recorded. Click REC, give the base URL when asked, and the IDE opens a browser window and writes a command for every click and every piece of typing you do in it. Click the red button again to stop. Assertions are added afterwards, by typing them in or by right-clicking an element in the recorded page and choosing a Selenium IDE assertion.
Recording is quick, but it records exactly what you did, including every misclick, and it chooses its own locators. For a practical that has to be explained in the journal, typing the commands is clearer, which is why this chapter types them.
Observations
| Test case | Page format | Commands | Checks made | Result |
|---|---|---|---|---|
| HTML page | HTML | 5 | title, heading, element present, table cell | passed |
| XML response | XML | 7 | Content-Type, number of notices, one title | passed |
| JSON API | JSON | 7 | Content-Type, record count, one record | passed |
| Dynamic form | dynamic HTML | 12 | fee appears, fields appear, confirmation text | passed |
Opening the XML address directly and asserting on it failed with the IDE's own error message, which is why the XML and JSON tests fetch their data from an ordinary page.
Result
A Selenium IDE 3.17.4 project with four test cases, one for each page format MU names, was created and grouped into the suite Page formats. The suite was executed and all four test cases passed. The suite was exported in WebDriver format as a Java JUnit class.
Where marks are lost
Trying to install the Chrome extension. It is switched off in Chrome and gone from the store. Use Firefox, or Selenium IDE 4 on a Chrome-only machine.
Asserting on an XML or JSON page directly. It fails with an error about the page, not the data. Fetch the data from an ordinary page and assert on the result.
Running a suite with no test selected. Select a test case inside the suite first.
No base URL. A test that says open /about.php needs the base URL to know which site.
Leaving out the waits on the dynamic form. The test may pass once and fail the next time.
Writing only the commands. The journal needs the log too: it is the proof the suite was executed.
Practical 1: Creating a Test Suite with Selenium IDE
Exporting and stopping. Name the language you exported to, and keep the exported file; chapter six runs it.
For the journal
Aim; tool and version (Selenium IDE 3.17.4 in Firefox); the four pages under test and their formats; the four test cases as command tables; the suite and its members; the log of the run; the observations table; the exported Java file; the result.
Quick revision
- Selenium IDE: record and playback in the browser. Command, Target, Value.
- Test case: named list of commands. Suite: named group of test cases. Project: the
.sidefile. - Chrome disabled Manifest V2 extensions from Chrome 138 (24 July 2025); the IDE's Chrome extension is gone. Firefox add-on 3.17.4 still installs.
- Base URL plus
open /about.phpgives the full address. - Assert stops the test on failure; verify records it and carries on.
- XML and JSON: open an ordinary page,
execute async scriptwithfetch, thenassertthe stored variable. - Dynamic pages:
wait for element visiblebefore touching what appears later. - Run a suite: select a test inside it, then Run all tests in suite.
- Export: suite menu, Export, Java JUnit. The export was written for Selenium 3.
Questions you must be able to answer
1. What are the three parts of a Selenium IDE command? The command itself, the target it acts on, and a value when it needs one, such as the text to type or the text expected.
2. What is the difference between a test case and a test suite? A test case is one named list of commands that checks one thing. A suite is a named group of test cases run together.
3. Why does this chapter use Firefox and not Chrome? Selenium IDE's Chrome extension was built on Manifest V2, which Chrome disabled for all users from Chrome 138 in July 2025. The Firefox add-on still installs.
4. What is the difference between assert text and verify text? If assert text fails, the test stops. If verify text fails, the failure is recorded and the test continues.
5. Why not open the XML address and assert on it directly? Because the IDE cannot work inside an XML document; the attempt fails with an error about the page. Fetching the XML from an ordinary page of the same site and checking the result works.
6. What does execute async script do with return? It runs the JavaScript in the page, waits for the Promise it returns, and saves the result in the variable named in the Value.
7. What would happen without the wait for element visible lines? The test could try to use the Year box or the room choice before the page's JavaScript had shown them, and fail on some runs and not others.
Practical 1: Creating a Test Suite with Selenium IDE
8. What does "export in WebDriver format" produce here? A Java JUnit class in which every test case is a @Test method that does the same steps through Selenium WebDriver.
9. The exported Java does not compile with Selenium 4. Why? It uses new WebDriverWait(driver, 5), the Selenium 3 form with a number of seconds. Selenium 4 takes a Duration instead.
The rest of this subject
These notes are cut from the University's printed syllabus. Open the syllabus itself for the same subject.