Test User Journeys, not <HTML>
Test what matters — how users interact with your product,
not how the HTML is structured.
I.goTo("https://github.com")
I.click("Sign in")
I.fill("Username", "brucewayne")
I.fill("Password", "supersecretpassword")
I.click("Sign in")
I.see("My repositories")
Write Tests That Are Easy To
Learn
Read
Maintain
Forget Complex Syntax
// Navigate to Google
await page.goto("https://google.com");
// Search for "Mount Fuji"
await page.fill("textarea[aria-label='Search']", "Mount Fuji");
// Click Search button
await page.click("input[aria-label='Google Search']")
// Validate that "Wikipedia" entry is in the results
await expect(page.locator("text=Wikipedia")).toBeVisible();
Takes hours to learn
Write Plain Simple English
// Navigate to Google
I.goTo("https://google.com")
// Search for "Mount Fuji"
I.fill("Search", "Mount Fuji")
// Click Search button
I.click("Search")
// Validate that "Wikipedia" entry is in the results
I.see("Wikipedia")
Takes minutes to learn
Instead of Brittle Selectors
// Navigate to Github Login Page
await page.goto("https://github.com/login");
// Fill username
await page.fill("#login_field", "brucewayne");
// Fill password
await page.fill("#password", "supersecretpassword");
// Click "Sign in"
await page.click(".js-sign-in-button");
Breaks easily whenever the UI changes
Target Human-Facing Labels
// Navigate to Github Login Page
I.goTo("https://github.com/login")
// Fill username
I.fill("Username", "brucewayne")
// Fill password
I.fill("Password", "supersecretpassword")
// Click "Sign in"
I.click("Sign in")
Cut the Boilerplate
import { test, expect } from '@playwright/test';
import fs from 'fs';
import path from 'path';
// Create screenshots directory
if (!fs.existsSync(screenshotDir)) {
fs.mkdirSync(screenshotDir);
}
// Define test case
test('Login to Github with valid credentials', async ({ page }) => {
// Navigate to Github Login Page
await page.goto("https://github.com/login");
await page.screenshot({ path: path.join(screenshotDir, '01-login-page.png') });
// Fill username
await page.fill("#login_field", "brucewayne");
await page.screenshot({ path: path.join(screenshotDir, '02-filled-username.png') });
// Fill password
await page.fill("#password", "supersecretpassword");
await page.screenshot({ path: path.join(screenshotDir, '03-filled-password.png') });
// Click "Sign in"
await page.click(".js-sign-in-button");
await page.screenshot({ path: path.join(screenshotDir, '04-clicked-sign-in.png') });
// Validate that "Dashboard" is shown
const textLocator = page.locator("text=Dashboard");
await expect(textLocator).toBeVisible();
await page.screenshot({ path: path.join(screenshotDir, '05-dashboard.png') });
});
Verbose and hard to read
Focus on what matters — testing user stories
// Navigate to Github Login Page
I.goTo("https://github.com/login")
// Fill username
I.fill("Username", "brucewayne")
// Fill password
I.fill("Password", "supersecretpassword")
// Click "Sign in"
I.click("Sign in")
// Validate that "Dashboard" is shown
I.see("Dashboard")
UI-licious is a happy middle ground
between codeless and scripted automation.
Variables, loops, conditionals, functions!
UI-licious runs on Javascript. Get creative with it!
const shoppingList = [
{ name: "Wireless Mouse", quantity: 2 },
{ name: "Laptop Stand", quantity: 1 },
{ name: "USB-C Hub", quantity: 3 },
{ name: "Bluetooth Keyboard", quantity: 2 }
]
I.goTo("https://acme-computer-store.com")
// Loop through the shopping list
shoppingList.forEach((product)=>{
// Search for the product
I.fill("Search", product.name)
I.pressEnter()
// If product exists, add desired quantity to the cart
if(I.see$(product.name)){
I.click(product.name)
for (var i = 0; i < product.quantity; i++) {
I.click("Add to cart")
}
}
})
Reuse logic
Reuse common flows with test chaining. Run the same script with different test data using datasets or loading from external sources like JSON / CSV files.
// Run Login flow
TEST.run("auth/login")
// Verify welcome message
I.see("Welcome, " + DATA.username + "!")
Show, not Tell
Share interactive test reports that show exactly how a bug happened — with full context, visuals, and logs.
Understand Failures at a Glance
Reduce back-and-forths between QA and Devs. UI-licious reports give developers everything they need to investigate and fix bugs fast.
Replay Step by Step
See exactly what happened — actions, inputs, and screenshots — so nothing gets lost in transmission from QA to devs.

Actionable Reports
Automatic Screenshots
Access Downloaded Files Anytime

Work as a Team — not in Silos
UI-licious unifies specification and implementation into a single document.
There’s no need to maintain spec files in Gherkin / Cucumber / etc and seperate automation code.
Business, QA, and devs can understand and contribute to the same living document.
One test. One source of truth. One team.
Why Stop at Chrome?
Your users are tired of being told to "switch to Chrome" when something doesn't work.
All Major Browsers Supported
Did you know that Safari is the #2 most popular browser after Chrome, with 18% of global market share in 2025?
Yet CDP-based tools like Playwright can't test it.
That's why UI-licious uses the WebDriver Protocol — an industry standard — to deliver true cross-browser testing.
Run the same test script across browsers instead of maintaining variants to handle browser quirks — UI-licious smooths out browser-specific behavior under the hood.

Trusted by Top Teams






Run your first test in minutes.
Resources
Questions and Answers
What language does UI-licious run on?
Does UI-licious commands execute synchronously or asynchronously?
How does UI-licious smart targeting work?
Can I write custom functions?
Is CI/CD integration supported?