SeleniumLibrary and Browser Automation Integration: Guided Hands-On Workflow
Build a disposable local login-like application and automate the same domain flow with SeleniumLibrary and Browser using explicit lifecycle, locator, waiting, screenshot, and trace-aware evidence practices.
Learning objectives
- Create and inspect a loopback-only browser AUT before opening any browser.
- Install a version-compatible SeleniumLibrary/Selenium pair and an optional Browser/BrowserBatteries stack.
- Implement the same domain keyword contract over two separate resources.
- Use explicit Selenium waits and Browser assertion/auto-wait behavior without sleeps.
- Capture screenshots/optional trace evidence and prove teardown ownership.
Current compatibility baseline — verified 2026-08-31.
Robot Framework 7.4.2 is the stable course
baseline. SeleniumLibrary 6.9.0 is paired in these
labs with Selenium 4.44.0 because 6.9.0 documents
support through that Selenium version; use Python 3.10+ for this
chapter. Browser 20.4.0 requires Python 3.10+ and
Robot Framework 7.1.1+, and its release is tested with Playwright
1.62.1. The easiest Browser path uses
robotframework-browser[bb] plus
rfbrowser install; the Node-managed path supports Node
22/24 LTS and Node 26. Re-check current compatibility before
upgrading any layer. No paid browser cloud, production site, real
credential, Pabot, container, or CI account is required.
1. Scenario and state boundary
You will build a tiny static login-like application served only on
127.0.0.1:8765. It uses fake credentials
demo/mode and intentionally delays the UI result by 350
ms. That delay is long enough to demonstrate real synchronization
without adding a network dependency or production account.
The browser is the only external mutable state. The app files are disposable. Robot outputs are evidence. No database, API credential, cloud browser, or production service is involved.
2. Create the disposable project
rf-browser-lab/
├── app/
│ └── index.html
├── resources/
│ ├── login_selenium.resource
│ └── login_browser.resource
├── tests/
│ ├── selenium_login.robot
│ └── browser_login.robot
└── results/
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>RF Browser Lab</title>
<style>
body { font-family: system-ui, sans-serif; max-width: 34rem; margin: 4rem auto; }
label { display:block; margin:.8rem 0; }
input, button { padding:.55rem; }
#status { margin-top:1rem; font-weight:700; }
</style>
</head>
<body>
<h1>Local Login Lab</h1>
<form data-testid="login-form">
<label>Username <input data-testid="username" autocomplete="off"></label>
<label>Password <input data-testid="password" type="password" autocomplete="off"></label>
<button data-testid="submit" type="submit">Sign in</button>
</form>
<p data-testid="status" aria-live="polite">Signed out</p>
<script>
const form = document.querySelector('[data-testid="login-form"]');
const status = document.querySelector('[data-testid="status"]');
form.addEventListener('submit', (event) => {
event.preventDefault();
const user = document.querySelector('[data-testid="username"]').value;
const pass = document.querySelector('[data-testid="password"]').value;
status.textContent = 'Checking…';
setTimeout(() => {
if (user === 'demo' && pass === 'mode') {
status.textContent = `Welcome, ${user}`;
history.replaceState({}, '', '#dashboard');
} else {
status.textContent = 'Invalid credentials';
}
}, 350);
});
</script>
</body>
</html>
3. Start and inspect the local AUT
cd rf-browser-lab
python -m http.server 8765 --bind 127.0.0.1 --directory app
Keep that terminal open. In a second terminal, verify before browser automation:
python -c "import urllib.request; r=urllib.request.urlopen('http://127.0.0.1:8765/', timeout=2); print(r.status, r.geturl())"
Expected observation: HTTP 200 and a loopback URL. If another
process already owns port 8765, choose a different private port and
update ${LOGIN_URL} consistently. Do not “solve” the
collision by killing unrelated processes.
4. Create an isolated Python environment
python -m venv .venv
# PowerShell
.\.venv\Scripts\Activate.ps1
# Bash
# source .venv/bin/activate
python -m pip install --upgrade pip
python -m pip install robotframework==7.4.2 robotframework-seleniumlibrary==6.9.0 selenium==4.44.0
python -m robot --version
python -m pip show robotframework-seleniumlibrary selenium
This deliberately pins Selenium 4.44.0 even if a newer Selenium exists, because SeleniumLibrary 6.9.0 documents its supported Selenium range through 4.44.0. The browser itself (for example Chrome) must already be installed; Selenium Manager normally resolves the corresponding driver. On restricted networks, pre-provision the browser/driver according to Selenium policy rather than disabling verification.
5. Build the SeleniumLibrary domain resource
*** Settings ***
Library SeleniumLibrary
*** Variables ***
${LOGIN_URL} http://127.0.0.1:8765/
*** Keywords ***
Open Login App
Open Browser ${LOGIN_URL} chrome
Sign In As
[Arguments] ${username} ${password}
Wait Until Element Is Visible css:[data-testid="username"] 5s
Input Text css:[data-testid="username"] ${username}
Input Text css:[data-testid="password"] ${password}
Click Button css:[data-testid="submit"]
Welcome Should Be Visible
[Arguments] ${username}
Wait Until Element Contains css:[data-testid="status"] Welcome, ${username} 5s
Location Should Contain #dashboard
Capture Browser Evidence
Capture Page Screenshot selenium-login-{index}.png
Close Login App
Close All Browsers
Wait Until Element Is Visible reads browser DOM state
until the username element becomes visible.
Input Text mutates form fields.
Click Button triggers the local JavaScript.
Wait Until Element Contains is the meaningful
synchronization point: it waits for the delayed business result.
Location Should Contain then verifies the URL hash. The
screenshot is derivative evidence, and teardown closes only
WebDriver sessions opened through SeleniumLibrary.
6. Run the SeleniumLibrary path
*** Settings ***
Resource ../resources/login_selenium.resource
Test Setup Open Login App
Test Teardown Close Login App
*** Test Cases ***
Local Login Works Through SeleniumLibrary
Sign In As demo mode
Welcome Should Be Visible demo
Capture Browser Evidence
python -m robot --outputdir results/selenium tests/selenium_login.robot
Expected state transition: no WebDriver session → one Chrome
WebDriver session → local page loaded → two inputs changed → status
becomes Welcome, demo → URL ends in
#dashboard → screenshot written → browser session
closed. Verify results/selenium/output.xml,
log.html, report.html, and the screenshot
before cleanup.
7. Add Browser 20.4.0 as a second, optional stack
The easiest current route avoids a separately managed Node installation:
python -m pip install "robotframework-browser[bb]==20.4.0"
rfbrowser --version
rfbrowser install chromium
rfbrowser install chromium downloads the
Playwright-managed Chromium binary. If your machine cannot download
binaries but already has Chrome/Edge, the Browser documentation also
supports using a Chromium channel instead; document that deviation.
If BrowserBatteries has no wheel for your platform, use the official
Node-managed route (pip install robotframework-browser==20.4.0
then rfbrowser init chromium) with a supported Node
runtime.
8. Build the equivalent Browser resource
*** Settings ***
Library Browser timeout=5s
*** Variables ***
${LOGIN_URL} http://127.0.0.1:8765/
*** Keywords ***
Open Login App
New Browser chromium headless=True
New Context
New Page ${LOGIN_URL}
Sign In As
[Arguments] ${username} ${password}
Fill Text [data-testid="username"] ${username}
Fill Text [data-testid="password"] ${password}
Click [data-testid="submit"]
Welcome Should Be Visible
[Arguments] ${username}
Get Text [data-testid="status"] == Welcome, ${username}
${url}= Get Url
Should Contain ${url} #dashboard
Capture Browser Evidence
Take Screenshot filename=browser-login-{index}.png
Close Login App
Close Browser
The domain contract is intentionally the same as the Selenium
resource. Underneath, however, state differs:
New Browser creates the Playwright browser,
New Context creates isolated context state, and
New Page creates the page. Click benefits
from Playwright actionability waiting.
Get Text … == … is a Browser assertion and is retried
for the assertion timeout; that is why no sleep is needed for the
350 ms application delay.
9. Run the Browser path and optional trace
*** Settings ***
Resource ../resources/login_browser.resource
Test Setup Open Login App
Test Teardown Close Login App
*** Test Cases ***
Local Login Works Through Browser
Sign In As demo mode
Welcome Should Be Visible demo
Capture Browser Evidence
python -m robot --outputdir results/browser tests/browser_login.robot
Expected state transition: no Browser-owned browser → one Chromium browser → one isolated context → one page → DOM mutation → assertion success → screenshot → browser close. Browser screenshots are normally under its output subdirectory.
For a diagnostic run, enable trace explicitly because traces are richer and more sensitive than ordinary logs:
# PowerShell
$env:ROBOT_FRAMEWORK_BROWSER_TRACING = 'True'
python -m robot --outputdir results/browser-trace tests/browser_login.robot
Remove-Item Env:ROBOT_FRAMEWORK_BROWSER_TRACING
# Bash alternative
# ROBOT_FRAMEWORK_BROWSER_TRACING=True python -m robot --outputdir results/browser-trace tests/browser_login.robot
Inspect the generated Browser trace directory only with synthetic data. A trace should be treated as a protected diagnostic artifact in a real system.
10. Compare the evidence rather than the syntax
| Observation | SeleniumLibrary run | Browser run |
|---|---|---|
| High-level Robot call | Sign In As |
Sign In As |
| Underlying browser unit | WebDriver session | browser → context → page |
| Readiness mechanism | explicit Wait Until Element Contains |
retrying Get Text assertion after auto-waited
actions
|
| Screenshot | Capture Page Screenshot |
Take Screenshot |
| Trace | not a SeleniumLibrary core artifact | optional Playwright trace through Browser |
| Teardown | Close All Browsers |
Close Browser |
| Result source of truth | Robot output.xml/log/report | Robot output.xml/log/report |
11. Challenge: choose the correct layer
The app is changed so the submit button is visible immediately, but it becomes enabled only after client-side validation. Where should the fix go?
Do not add a sleep to the test. Keep the business test unchanged. In the Selenium resource, wait for the button’s enabled state before clicking. In Browser, rely on Click actionability if enabledness is the relevant Playwright actionability condition, and add a condition-specific assertion only if application readiness is distinct. Explain which state each stack observes.
12. Cleanup and rollback
-
Stop only the
python -m http.serverprocess you started (Ctrl+C in its terminal). - Allow each Robot teardown to close its own browser state; never kill browsers by process name.
-
Retain
results/until you have inspected the evidence packet. -
Then remove only the disposable
rf-browser-labdirectory if desired.
Knowledge check
Why does the Browser example use Get Text with an assertion operator instead of Get Text followed immediately by a string comparison?
Browser getter assertions are retried for the configured assertion window. A getter used only to return a value reads once; separating the comparison could lose the retry behavior needed for delayed UI state.
Why pin Selenium 4.44.0 instead of blindly installing the newest Selenium?
SeleniumLibrary 6.9.0 documents its supported/tested range through Selenium 4.44.0. A lab should pin a compatible pair and re-check compatibility before upgrading independently.
What evidence proves cleanup without killing processes?
The Robot log shows the library teardown keyword executing successfully, and subsequent suite state has no owned browser/session/context. Process-name killing is not ownership evidence.
If neither browser stack can currently run, what is the safe fallback?
Use robot --dryrun to validate imports/syntax where possible, inspect the loopback AUT, and document the missing browser prerequisite. Do not claim browser behavior was tested. Complete the browser checkpoint once at least one supported local stack is installed.
13. Summary and bridge
You now have two implementations of one domain contract, with explicit browser ownership and evidence. Lesson 3 turns that experience into architecture decisions for a production Robot estate.
References and version anchors
- Robot Framework 7.4.2 User Guide — suite/resource/library lifecycle, variable/result behavior, and external-library boundary.
- SeleniumLibrary documentation and SeleniumLibrary 6.9.0 on PyPI — Selenium 4 integration, WebDriver lifecycle, waits, screenshots, and current compatibility.
- Robot Framework Browser documentation, installation, waiting concepts, and logging/tracing — Browser 20.4.0 / Playwright integration.
- Browser 20.4.0 on PyPI — Python and package release anchor.
- DevOps Academy Selenium course — prerequisite browser-testing principles; this chapter focuses on the Robot Framework integration layer.
Keep the academy open
Support free, practical DevOps education.
Every lesson is designed to remain readable in a browser, downloadable from GitHub, and usable without a paid learning platform. Contributions help expand and maintain the curriculum.
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0
Send only Ethereum/ERC-20 compatible assets to this
address.