E-commerce Test Automation with Vibium
E-commerce test automation with Vibium: script cart, checkout, and payment flows in JS or Python with auto-waiting, AI checks, and CI-ready smoke tests.
E-commerce test automation with Vibium means scripting each shopping journey — search, product, add-to-cart, checkout, and confirmation — as a find → act → assert loop that Vibium keeps reliable with built-in auto-waiting. Vibium is AI-native browser automation built on WebDriver BiDi and shipped as a single Go binary that auto-downloads Chrome for Testing; npm install vibium or pip install vibium is the entire setup. Online stores are the hardest thing to test reliably: carts update over AJAX, prices recalculate async, and payment steps redirect through iframes and third-party gateways. Naive scripts flake because they race those updates. Vibium polls every element until it is visible and enabled before acting, so it never clicks a disabled "Place order" button or fills an input that has not mounted. Created by Jason Huggins, co-creator of Selenium and Appium, Vibium lets you cover the whole revenue path deterministically and run it headless in CI on every deploy.
What does an e-commerce test pipeline with Vibium look like?
An e-commerce test pipeline is the ordered set of journeys a shopper takes, each scripted as an independent, isolated test. The diagram below maps the flow this article automates, from opening the store to asserting the order confirmation.
Each stage is a checkpoint you can assert on. If "Add to cart" passes but "Checkout + pay" fails, you know exactly where the revenue path broke — which is the whole point of splitting a store's coverage into small, named journeys instead of one giant script.
The rest of this guide builds each stage in JavaScript first (Python equivalents follow), then wires them into a CI-ready smoke suite.
How do I set up Vibium for store testing?
Install Vibium into your project and you are done — there is no separate driver to download or version-match against your Chrome. Vibium fetches Chrome for Testing itself on first run.
# JavaScript / TypeScript
npm install vibium
# Python
pip install vibiumThe install guide covers platform specifics, but for most stores the one line above is the whole setup. If you are new to the tool, the what is Vibium? overview explains the single-binary, BiDi-native design in two minutes.
How do I script the product search and add-to-cart flow?
The first journey opens the store, searches for a product, opens it, and adds it to the cart. In JavaScript with the sync API it reads top to bottom like the steps a shopper takes.
const { browser } = require('vibium/sync')
const bro = browser.launch()
const page = bro.page()
// 1. Open the store and search.
page.go('https://store.example.com')
page.find('input[name="q"]').type('wireless headphones')
page.find('button[type="submit"]').click() // auto-waits for the button
// 2. Open the first result.
page.find('.product-card a').click()
// 3. Add to cart and confirm the badge updates.
page.find('button[data-testid="add-to-cart"]').click()
const badge = page.find('[data-testid="cart-count"]')
console.log('Cart count:', badge.text()) // "1"
bro.close()Every find() here auto-waits on actionability, so you never insert a sleep() between the click and reading the cart badge. When the store bumps the badge over AJAX, Vibium waits until the element is present and returns the settled value.
The same flow in Python uses the sync client, which is the default since Vibium's v1:
from vibium import browser_sync as browser
vibe = browser.launch()
vibe.go("https://store.example.com")
vibe.find('input[name="q"]').type("wireless headphones")
vibe.find('button[type="submit"]').click()
vibe.find('.product-card a').click()
vibe.find('button[data-testid="add-to-cart"]').click()
print("Cart count:", vibe.find('[data-testid="cart-count"]').text())
vibe.quit()For a deeper walk-through of the search step on its own, see how to automate a Google-style search — the same type + submit pattern applies to any store search box.
How do I automate the checkout and payment step?
Checkout is the highest-stakes journey, and the one where scripts flake most, because it spans cart → address → payment → confirmation with redirects in between. You automate it as one continuous find → act → assert sequence.
const { browser } = require('vibium/sync')
const bro = browser.launch()
const page = bro.page()
page.go('https://store.example.com/products/widget')
// Add to cart and go to checkout.
page.find('button[data-testid="add-to-cart"]').click()
page.find('a[href="/checkout"]').click()
// Shipping details.
page.find('input[name="name"]').type('Jane Doe')
page.find('input[name="address"]').type('123 Main St')
page.find('input[name="zip"]').type('60601')
// Payment — a sandbox test card, never a real one.
page.find('input[name="card"]').type('4242424242424242')
page.find('input[name="exp"]').type('12/30')
page.find('input[name="cvc"]').type('123')
// Place the order and verify the confirmation the success page renders.
page.find('button[data-testid="place-order"]').click()
const confirmation = page.find('[data-testid="order-confirmed"]')
console.log(confirmation.text()) // "Thank you for your order"
bro.close()The key move is the last two lines: you assert on an element that only the confirmation page renders. Reading the rendered confirmation, not just the URL, proves the order actually went through. Because find() waits out the payment redirect, a slow gateway hop does not cause a false failure.
If your login sits in front of checkout (accounts, saved carts), reuse the pattern in automate a login flow with Vibium before the cart step.
How do I test payments without charging a real card?
Never automate a real payment. Use your payment provider's test mode and its published sandbox card numbers, and point Vibium at a staging environment wired to that sandbox.
// Stripe's universal test card — works only in test/sandbox mode.
page.find('input[name="card"]').type('4242424242424242')
page.find('input[name="exp"]').type('12/30')
page.find('input[name="cvc"]').type('123')In sandbox mode the order completes end to end and the confirmation page renders, but no money moves. That makes the full checkout safe to run as a CI smoke test on every deploy, which is exactly what you want protecting your revenue path.
How do I handle payment fields inside an iframe?
Many gateways (Stripe Elements, Braintree, Adyen) render card inputs inside a secure iframe so your page never touches raw card data. Vibium treats frames as first-class browsing contexts, so you address the frame and call the same find() and type() API on it — there is no driver-style "switch to frame" ceremony, because in WebDriver BiDi a frame is just another addressable context.
The documented approach is to locate the payment frame, then interact with its inputs directly. For the exact frame accessor in your Vibium version, check the official docs at vibium.com; the shape mirrors the page API you already use, so the card-entry lines above stay virtually identical whether the inputs are on the page or inside the gateway's iframe.
How do I verify carts, totals, and dynamic prices?
Cart math is where subtle bugs hide: a coupon that does not apply, tax that miscalculates, a total that lags behind a quantity change. Assert on the settled rendered value, not a delay.
// Change quantity, then assert the recalculated total.
page.find('input[name="qty"]').fill('3')
page.find('button[data-testid="update-cart"]').click()
const total = page.find('[data-testid="cart-total"]').text()
console.log('Total after qty change:', total) // e.g. "$149.97"Because Vibium waits for the total element to be present and stable before reading it, the async recalculation is handled for you. This is Vibium's core reliability win over hand-rolled waits — you describe the value you expect, and the tool waits out the AJAX churn to get it.
For assertions where the exact selector is fragile or the state is visual (a badge, a strikethrough sale price, a "sold out" overlay), Vibium's AI-native check() lets you assert in plain English — verify what the shopper sees, without pinning a brittle selector. Pair it with the deterministic reads above: check() for intent, text() for exact values.
How do I test coupon codes and discounts?
Discount logic is a frequent source of production incidents — an expired code that still applies, a percentage that rounds wrong, free shipping that does not trigger at the threshold. Test it by applying the code and asserting on the discounted total the cart renders.
const { browser } = require('vibium/sync')
const bro = browser.launch()
const page = bro.page()
page.go('https://store.example.com/cart')
// Apply a coupon and confirm the discount line appears.
page.find('input[name="coupon"]').type('SAVE10')
page.find('button[data-testid="apply-coupon"]').click()
const discount = page.find('[data-testid="discount-line"]').text()
console.log('Discount applied:', discount) // "-$15.00"
// Assert the new total reflects the discount.
const total = page.find('[data-testid="cart-total"]').text()
console.log('Total after coupon:', total)
bro.close()Also test the failure path: an invalid or expired code should surface an error and leave the total unchanged. Find the code input, submit a bad value, and assert the error element renders while the total holds steady. Testing both the happy and the rejected path is what catches the "expired code still works" class of bug that costs real margin.
// Negative case: a bad code must be rejected, total unchanged.
page.find('input[name="coupon"]').fill('EXPIRED2020')
page.find('button[data-testid="apply-coupon"]').click()
console.log(page.find('[data-testid="coupon-error"]').text()) // "Code is not valid"How do I test out-of-stock and error paths?
The unhappy paths — sold-out items, declined cards, empty carts — matter as much as the happy path, because that is where a broken store loses trust or double-charges. Assert that the UI blocks the action correctly instead of silently proceeding.
// Sold-out product: the add-to-cart button should be disabled.
page.go('https://store.example.com/products/sold-out-item')
const addBtn = page.find('button[data-testid="add-to-cart"]')
console.log('Enabled?', addBtn.isEnabled()) // false
console.log(page.find('[data-testid="stock-status"]').text()) // "Out of stock"Vibium's isEnabled() reads the real DOM state, so you can assert the button is genuinely blocked rather than just hoping the click "did nothing." For a declined-payment path, use your gateway's dedicated decline test card (for Stripe, 4000 0000 0000 0002) and assert the store shows a decline message and does not advance to the confirmation page:
// Declined-card test: expect an error, not a confirmation.
page.find('input[name="card"]').type('4000000000000002')
page.find('input[name="exp"]').type('12/30')
page.find('input[name="cvc"]').type('123')
page.find('button[data-testid="place-order"]').click()
const payError = page.find('[data-testid="payment-error"]')
console.log(payError.text()) // "Your card was declined"Asserting the store correctly refuses a bad card is the difference between a smoke test and a real safety net.
How do I run one checkout test across many products?
Stores rarely sell one product, so data-drive the same journey across a list of SKUs. Keep the flow in a function and loop your test data through it — one script, full catalog coverage.
const { browser } = require('vibium/sync')
const products = [
{ slug: 'wireless-headphones', name: 'Wireless Headphones' },
{ slug: 'usb-c-cable', name: 'USB-C Cable' },
{ slug: 'laptop-stand', name: 'Laptop Stand' },
]
const bro = browser.launch()
const page = bro.page()
for (const product of products) {
page.go(`https://store.example.com/products/${product.slug}`)
// The product page must render the expected name.
const heading = page.find('h1[data-testid="product-title"]').text()
console.log(`${product.slug}: expected "${product.name}", got "${heading}"`)
// Add to cart and confirm the badge increments.
page.find('button[data-testid="add-to-cart"]').click()
console.log('Cart count:', page.find('[data-testid="cart-count"]').text())
}
bro.close()Data-driven loops keep the suite DRY and make catalog coverage a matter of extending an array. When your test data grows, move the products into a JSON or CSV fixture and read it at the top of the file — the loop body never changes. This is the same shape a Page Object Model formalizes, where the reusable steps live in one class and the tests just supply data.
Vibium vs Playwright vs Selenium for e-commerce testing
All three can automate a store. They differ in setup cost, waiting behavior, and how much AI is built in. Here is an honest side-by-side for store-testing specifically.
| Capability | Vibium | Playwright | Selenium |
|---|---|---|---|
| Install / setup | npm install vibium — single Go binary, auto-downloads Chrome | npm i + npx playwright install browsers | Client lib + matching driver (Selenium Manager helps) |
| Auto-waiting on actionability | Built in | Built in | Manual (explicit/WebDriverWait) |
| Frames for payment iframes | First-class BiDi contexts, no switching | Frame locators | switchTo().frame() |
| Protocol | WebDriver BiDi (WebSocket-native) | CDP + own protocol | WebDriver + BiDi (layered) |
AI-native assertions (check) | Yes, built in | No | No |
| Built-in MCP server for AI agents | Yes | Via separate Playwright MCP | No |
| Bundled test runner | No (bring your own) | Yes (@playwright/test) | No |
| Language clients | Python, JS/TS | JS/TS, Python, Java, .NET | Java, C#, Python, Ruby, JS, more |
| Ecosystem maturity | New (v1, 2025) | Mature | 20-year, largest |
When to choose Vibium: you want the lightest possible setup for a store's smoke suite, you value built-in auto-waiting and AI checks for flaky cart/price states, or you are building agent-driven testing on top of the MCP server. When to choose Playwright: you need a batteries-included runner, fixtures, and trace viewer today, or cross-browser coverage beyond Chromium is a hard requirement. When to choose Selenium: you have an existing Java or C# enterprise suite, need Grid, or must support browsers and bindings Vibium does not ship yet.
Verdict: for a fast, reliable revenue-path smoke test, Vibium's zero-driver setup and auto-waiting remove the two biggest sources of e-commerce flakiness with the least code. For a large, established cross-browser regression suite, Playwright or Selenium's maturity still wins today. The deeper trade-offs live in Vibium vs Playwright and Vibium vs Selenium.
Migration note: porting store tests from Playwright is mostly renaming — goto → go, locator → find, page.close/browser.close map directly, and Playwright's getByRole/getByText collapse into Vibium's one find({ role, text }). From Selenium, you delete your WebDriverWait boilerplate outright, since auto-waiting is built in. The main gotcha both directions: Vibium ships no test runner, so keep your existing runner (Jest, pytest, Mocha) and call Vibium inside it.
How do I run the whole suite in CI on every deploy?
Run headless and treat each journey as a smoke test. Launch with headless mode, install Vibium in your pipeline, and fail the build if any confirmation assertion does not pass.
// Same scripts, headless for CI.
const bro = browser.launch({ headless: true })# Python equivalent.
vibe = browser.launch(headless=True)Because your assertions target confirmation elements rather than timers, slow payment redirects on a loaded staging box never cause a flaky failure — Vibium simply waits out the redirect. Wire the search, cart, and checkout journeys as separate steps so a failing stage names exactly where the revenue path broke. To structure a growing suite, keep selectors in one place using the Page Object Model, and let AI agents run the same flows through the Vibium MCP server in Claude Code.
A practical CI layout is one Vibium script per journey, each exiting non-zero on a failed assertion so the pipeline stops the deploy. Run them in whatever runner you already use — Jest, Mocha, or pytest — since Vibium is a library, not a runner, and slots inside your existing test command. Capture a screenshot in the failure branch so a red build ships an image of exactly what the shopper's screen looked like when the assertion broke; that single artifact usually turns a ten-minute repro into a ten-second glance.
How do I test the mobile storefront?
Most e-commerce traffic is mobile, so the phone checkout deserves its own coverage. Set the viewport to a phone size before running the journey and the store renders its responsive layout — hamburger menu, stacked cart, mobile pay button — which is what you actually want to test.
const { browser } = require('vibium/sync')
const bro = browser.launch()
const page = bro.page()
// Emulate a phone viewport, then run the same cart flow.
page.setViewport({ width: 390, height: 844 })
page.go('https://store.example.com')
page.find('button[data-testid="menu-toggle"]').click() // mobile nav
page.find('a[href="/products/widget"]').click()
page.find('button[data-testid="add-to-cart"]').click()
console.log('Mobile cart count:', page.find('[data-testid="cart-count"]').text())
bro.close()Running the identical assertions at both desktop and phone widths catches responsive-only regressions — a checkout button pushed off-screen on mobile, a cart drawer that will not open — that a desktop-only suite silently misses. Consult the official docs for the exact viewport option names in your Vibium version, but the pattern is: size the viewport, then reuse the same journey.
Why does Vibium stay reliable where scripts usually flake?
E-commerce flakiness almost always traces to one root cause: the script acted before the page was ready. A cart badge updates a few hundred milliseconds after the click; a payment gateway redirects through two hops; a price recalculates when React re-renders. Scripts that use fixed sleep(2000) calls either waste time on fast runs or fail on slow ones — and CI boxes are always slower than a laptop.
Vibium removes that failure mode by design. Every find() polls for the element and waits until it is present, visible, and enabled (its actionability contract) before the interaction runs, and reads like text() return the settled value. You never sleep between steps, and you never race the DOM. Concretely, that means the add-to-cart click waits for the button, the confirmation read waits out the gateway redirect, and the total read waits for the recalculated number — all without a single explicit timer in your code.
// No sleeps anywhere: Vibium waits at each step for you.
page.find('button[data-testid="add-to-cart"]').click() // waits until clickable
const count = page.find('[data-testid="cart-count"]').text() // waits until renderedThe payoff compounds in CI, where the same suite runs on shared, variable-speed runners. A timer-based script that passes locally will flake on a loaded pipeline; an auto-waiting script simply takes a little longer and still passes. That is the single biggest reason a Vibium store suite stays green day to day.
Common gotchas in e-commerce automation
- Always use a sandbox and published test cards; never automate a real charge against production.
- Assert on the confirmation element, not a fixed sleep, so slow gateway redirects do not flake the test.
- Keep credentials and card data out of code — read them from environment variables, never commit them.
- Isolate each journey so one failing test does not leave a dirty cart for the next; a fresh browser (or context) per test keeps state clean.
- Screenshot on failure with
screenshot()to debug what the page looked like when an assertion broke. - Do not over-assert on visuals — use
check()for "does this look right" and exacttext()reads for prices and totals.
Next steps
Frequently asked questions
How do I automate e-commerce testing with Vibium?
Model each user journey — search, product, add-to-cart, checkout, confirmation — as a script that navigates the page, finds elements with find(), interacts, and asserts on what the next page renders. Vibium auto-waits on every element, so multi-step flows stay reliable without manual sleeps between steps.
Can Vibium test a full checkout without charging a real card?
Yes. Point Vibium at a staging environment wired to your payment provider's sandbox and use published test cards like Stripe's 4242 4242 4242 4242. The order completes end to end and the confirmation renders, but no money ever moves, so it is safe to run on every deploy.
Does Vibium handle payment fields inside iframes?
Yes. Vibium treats frames as first-class browsing contexts, so you grab the payment iframe and call the same find() and type() API on it. There is no driver-style context switching because in WebDriver BiDi a frame is just another addressable context.
How is Vibium different from Selenium or Playwright for e-commerce?
Vibium ships as a single Go binary that auto-downloads Chrome, adds built-in auto-waiting, and includes AI-native check() and do() methods plus an MCP server. It has no driver to version-match and no bundled runner, so setup for a store's test suite is pip install vibium or npm install vibium.
How do I run e-commerce tests in CI on every deploy?
Launch headless with browser.launch({ headless: true }), install Vibium in your pipeline with npm install vibium or pip install vibium, and run your journey scripts as smoke tests. Assert on confirmation elements rather than timers so slow payment redirects never cause flaky failures.
Can Vibium test dynamic carts and AJAX price updates?
Yes. Because Vibium polls each element until it is visible and enabled before acting, it naturally waits out AJAX cart badge updates, async price recalculations, and shipping-estimate redraws. You assert on the settled value the page renders, not on a fixed delay, so races disappear.
Vibium is created by Jason Huggins. This is an independent tutorial — see the official Vibium site and GitHub repo for canonical docs.
Related guides
Accessibility Testing with Vibium
Accessibility testing with Vibium — read the a11y tree, assert on roles, names, and states, and catch WCAG issues in CI with no driver setup.
14 min read→How-To RecipesMixing API + Web Testing with Vibium
Mix API and web testing with Vibium — assert on backend JSON with waitForResponse and route while driving the real UI, in one script.
14 min read→How-To RecipesBulk Data Extraction with Vibium
Bulk data extraction with Vibium: build a repeatable scrape pipeline over a URL list, extract with findAll(), and write clean JSON, CSV, or a database.
13 min read→How-To RecipesUsing Vibium for RPA (Robotic Process Automation)
Use Vibium for RPA to automate repetitive browser tasks — logins, data entry, report downloads, and scraping — code-first, headless, and free.
13 min read→