This recipe is the smallest end-to-end WebdriverIO test that walks the QA Shop guest cart flow.
It opens a product detail page, clicks add-to-cart, asserts the header cart badge updates to "1", and confirms the cart page renders — using WebdriverIO 9's chainable async $() pattern and the built-in waitForDisplayed synchronization primitive.
It is the canonical first WebdriverIO test to write against any e-commerce site, and on QA Shop it is a copy-paste away thanks to the testid contract on every interactive element.
§ 01 · PREREQUISITES
Prerequisites
You need a project with @wdio/cli and the @wdio/globals runtime installed plus a wdio config that points at QA Shop. The repo's tests/recipes/webdriverio/wdio.conf.ts already wires Chrome headless against RECIPE_BASE_URL — copy that file as a starting point if you are bootstrapping fresh.
Bootstrap with npm init wdio@latest . if you don't already have a project — it scaffolds the runner, the config, and an example spec. This recipe is verified against WebdriverIO 9.27.
Set to http://localhost:3002 for a local checkout, or https://automationtestingplatform.com for the hosted demo. The wdio.conf.ts reads it at config-load time with the hosted demo as the default.
The recipe assumes the server is already up. The runner does not start it for you.
§ 02 · THE SPEC
The full spec
Save this file as tests/recipes/webdriverio/specs/e-commerce-flow.e2e.ts and run it with npx wdio run tests/recipes/webdriverio/wdio.conf.ts --spec specs/e-commerce-flow.e2e.ts.
// tests/recipes/webdriverio/specs/e-commerce-flow.e2e.ts
import { browser, $, expect } from "@wdio/globals";
describe("WebdriverIO recipe: e-commerce-flow", () => {
it("guest adds a product and views the cart", async () => {
await browser.url("/products/wooden-train-set-100-piece");
await $("[data-testid='page-pdp']").waitForDisplayed({ timeout: 15_000 });
await $("[data-testid='pdp-add-to-cart']").click();
await expect($("[data-testid='cart-count']")).toHaveText("1");
await browser.url("/cart");
await $("[data-testid='page-cart']").waitForDisplayed({ timeout: 15_000 });
});
});Three details deserve a callout.
The imports come from @wdio/globals, not from the older "globals are magically defined" runtime. WebdriverIO 9 supports both, but the explicit-import form gives you working TypeScript types, IDE autocompletion, and the ability to lint the file out of the wdio runner — tsc --noEmit happily checks the spec on its own. This is the recommended pattern for new code.
Every selector is re-resolved on each command. $("[data-testid='page-pdp']") is called inside the await, then $("[data-testid='pdp-add-to-cart']") is called fresh on the next line. WebdriverIO's chainable element promise resolves the selector against the live DOM each time, so React re-renders between commands cannot produce a stale-element error.
The cart-count assertion uses the expect-webdriverio matcher toHaveText("1") rather than getText() + a manual compare. The matcher polls until the badge text matches the expected value or the assertion timeout elapses, absorbing the React optimistic-update latency without a browser.pause().
§ 03 · FAQ
FAQ
Common questions when running this recipe or adapting it.
RECIPE_BASE_URL=http://localhost:3002 npx wdio run tests/recipes/webdriverio/wdio.conf.ts. The wdio.conf.ts reads the env var at config load and falls back to https://automationtestingplatform.com if it is unset.
Replace { timeout: 15_000 } with { timeout: 30_000 } on either waitForDisplayed call. Fifteen seconds is plenty for a local dev server; thirty is a safer ceiling on a constrained CI runner or against the hosted demo over a slow link.
The cart contract — page-pdp, pdp-add-to-cart, cart-count, page-cart — is the same for every product on QA Shop. Swap the slug in the first browser.url(...) call to any seeded product and the rest of the spec keeps working.
Related
For the same flow in Playwright (TypeScript), see the add-to-cart Playwright recipe. For the broader Playwright vs Selenium vs Cypress vs WebdriverIO picture, see the QA Shop testing guide.
The Cypress, Selenium-Java, and Selenium-TypeScript e-commerce-flow counterparts are coming soon — placeholder slugs are /recipes/cypress/e-commerce-flow, /recipes/selenium/java-e-commerce-flow, and /recipes/selenium/typescript-e-commerce-flow.
Frequently asked questions
Last verified: