cloud-grid-e2e
Author and run E2E tests on a cloud browser grid - BrowserStack Automate, Sauce Labs, or LambdaTest. All three follow one pattern: username + access-key env vars, a W3C WebDriver hub URL, a vendor options dict inside the capabilities (bstack:options / sauce:options / LT:Options), a local tunnel binary for internal apps, session pass/fail reporting, and a CI matrix throttled to the plan's parallel-session limit. Worked example uses BrowserStack; per-vendor deltas live in references/. Use for cross-browser regression on real devices + browsers beyond the engines bundled on the local machine - distinct from a local matrix runner and from self-hosted Selenium Grid.
Install with skills.sh (any agent)
npx skills add testland/qa --skill cloud-grid-e2ecloud-grid-e2e
Overview
A cloud grid is a hosted farm of real devices + browsers exposed through a W3C-compliant WebDriver endpoint. Point any WebDriver client (Selenium, WebdriverIO, Nightwatch) at the vendor's hub URL and the suite runs on combinations the local machine cannot host: real Safari on iOS, legacy browser versions, niche Android devices.
The three major vendors are isomorphic - the same suite moves between them by swapping four things:
| Concern | BrowserStack | Sauce Labs | LambdaTest |
|---|---|---|---|
| Auth env vars | BROWSERSTACK_USERNAME + BROWSERSTACK_ACCESS_KEY | SAUCE_USERNAME + SAUCE_ACCESS_KEY | LT_USERNAME + LT_ACCESS_KEY |
| Hub URL | https://hub-cloud.browserstack.com/wd/hub | regional, e.g. https://ondemand.us-west-1.saucelabs.com:443/wd/hub | https://hub.lambdatest.com/wd/hub |
| Vendor options dict | bstack:options | sauce:options | LT:Options |
| Local tunnel | BrowserStackLocal | Sauce Connect Proxy | LambdaTest Tunnel |
Everything else is standard W3C capabilities (browserName, browserVersion, platformName) per w3.org/TR/webdriver2/ (opens in new window).
Vendor deep detail (full options tables, tunnel setup, REST artifact retrieval, CI matrix examples): references/browserstack.md · references/sauce-labs.md · references/lambdatest.md.
Composes with the sibling browser-matrix-strategy-reference for matrix planning.
When to use
For bundled-engine matrix (Chromium / Firefox / WebKit on the runner machine), use playwright-testing (references/browser-matrix.md). For a self-hosted grid (data residency, cost control), use the sibling selenium-grid-4-runner.
Choosing between the vendors: BrowserStack has the broadest real-device matrix and enterprise procurement; Sauce Labs suits Selenium-grid-centric parallel CI farms; LambdaTest is the cost-sensitive smaller-scale option. Full deltas in the per-vendor references.
How to use (the vendor-generic pattern)
Worked example (BrowserStack)
Run one Selenium suite on the grid end to end - build capabilities, create the remote driver, drive the test, report status, quit. Per browserstack.com/docs/automate/selenium (opens in new window):
import os
from selenium import webdriver
options = webdriver.SafariOptions()
options.browser_version = "17"
bstack_options = {
"os": "OS X",
"osVersion": "Sonoma",
"projectName": "my-app",
"buildName": os.environ.get("BUILD_TAG", "local-run"),
"sessionName": "Checkout flow on Safari macOS",
"local": "false",
}
# Vendor caps must be set on Options BEFORE Remote(); driver.capabilities is a
# read-only result dict, so assigning to it afterwards is a no-op ([Selenium options]).
options.set_capability("bstack:options", bstack_options)
driver = webdriver.Remote(
command_executor=(
f"https://{os.environ['BROWSERSTACK_USERNAME']}:"
f"{os.environ['BROWSERSTACK_ACCESS_KEY']}"
f"@hub-cloud.browserstack.com/wd/hub"
),
options=options,
)
driver.get("https://example.com")
# ... test ...
# mark the session pass / fail so the dashboard metrics are accurate
driver.execute_script(
'browserstack_executor: {"action": "setSessionStatus", '
'"arguments": {"status":"passed","reason":"Login redirected as expected"}}'
)
driver.quit()Every non-W3C capability - os, osVersion, projectName, buildName, sessionName, local - lives inside bstack:options; only the standard fields (browserName, browserVersion, platformName) sit at the top level (Selenium options (opens in new window)). Use "status":"failed","reason":"..." on failure.
The same shape on the other vendors: Sauce Labs reports status via driver.execute_script("sauce:job-result=passed") and LambdaTest via driver.execute_script("lambda-status=passed") - see the references.
Local tunnel (internal / localhost apps)
Each vendor ships a tunnel binary that lets grid sessions reach hosts on your network. BrowserStack example:
# Download the BrowserStackLocal binary from browserstack.com
./BrowserStackLocal --key "$BROWSERSTACK_ACCESS_KEY" --daemon start
# Sessions with bstack:options.local = "true" now tunnel
./BrowserStackLocal --key "$BROWSERSTACK_ACCESS_KEY" --daemon stopSauce Connect (./sc --tunnel-name ... + sauce:options.tunnelName) and LambdaTest Tunnel (./LT --tunnelName ... + LT:Options.tunnel: true) are in the references. For ephemeral CI: spawn → wait-for-ready with a bounded timeout → run tests → terminate.
CI wiring and parallel limits
Run the browser matrix as CI jobs, one combination per job, with the credentials in secrets and buildName/build set to the PR identifier:
on: pull_request
jobs:
grid:
runs-on: ubuntu-latest
strategy:
matrix:
browser:
- { name: Chrome, version: latest, os: Windows, osVersion: "11" }
- { name: Safari, version: "17", os: "OS X", osVersion: Sonoma }
steps:
- uses: actions/checkout@v5
- name: Run cross-browser tests
env:
BROWSERSTACK_USERNAME: ${{ secrets.BROWSERSTACK_USERNAME }}
BROWSERSTACK_ACCESS_KEY: ${{ secrets.BROWSERSTACK_ACCESS_KEY }}
BUILD_TAG: pr-${{ github.event.pull_request.number }}
run: pytest tests/e2e/ --bstackAll three vendors cap concurrent sessions by plan tier; queue overflow blocks subsequent sessions until earlier ones complete. Throttle the worker pool (ThreadPoolExecutor(max_workers=N) or the CI matrix max-parallel key) to the plan limit, and tier the matrix per browser-matrix-strategy-reference so full runs stay inside it.
Anti-patterns
| Anti-pattern | Why it fails | Fix |
|---|---|---|
| Credentials in code | Token leak | Env vars / secret store |
| All tests on every browser combo | Slow + expensive (plan-limited) | Tier the matrix - see browser-matrix-strategy-reference |
| Missing build/project name caps | Sessions un-grouped in dashboard | Always set build to the CI run / PR identifier |
| No session-status update | Dashboard pass/fail rate inaccurate | Always report status before quit |
| Tunnel binary not stopped | Stale tunnels accumulate | Always stop the daemon after the run |
| Parallel exceeds plan limit | Sessions queue + timeout | Match worker pool to plan |
| Polling for tunnel-ready without timeout | Suite hangs if the tunnel never connects | Bounded wait + fail |
| Hardcoded vendor URL in tests | Switching grids requires code changes | Env-var-driven hub URL + a small caps abstraction |
| Treating vendors as drop-in interchangeable | Options dicts differ (bstack:options vs sauce:options vs LT:Options) | Isolate vendor caps in one harness module |
Limitations
References
BrowserStack Automate - vendor detail
View source (opens in new window)BrowserStack Automate - vendor detail
Deep reference for cloud-grid-e2e. Consult when tuning the full bstack:options set, running BrowserStackLocal, scaling parallel sessions, or pulling session artifacts via REST.
BrowserStack Automate is a hosted Selenium / Playwright / Cypress grid exposing 3000+ real device + browser combinations (iOS, Android, Windows, macOS) via a WebDriver-compatible endpoint. Per browserstack.com/docs/automate/selenium (opens in new window). The umbrella skill covers the Selenium-style invocation; Playwright + Cypress integrations follow a different (but similar) pattern documented separately by BrowserStack.
Auth + hub URL
export BROWSERSTACK_USERNAME="your-username"
export BROWSERSTACK_ACCESS_KEY="<access-key-from-account-settings>"Hub: https://hub-cloud.browserstack.com/wd/hub.
Capabilities (W3C)
Standard W3C fields - browserName, browserVersion, platformName (or BrowserStack's non-standard os + osVersion) - plus a bstack:options block:
{
"browserName": "Chrome",
"browserVersion": "latest",
"os": "Windows",
"osVersion": "11",
"bstack:options": {
"projectName": "My App",
"buildName": "PR-1234",
"sessionName": "Login flow on Chrome Windows",
"local": "false"
}
}Full bstack:options table
Per BrowserStack docs (browserstack.com/automate/capabilities, Cloudflare-protected; cite by stable URL):
| Option | Purpose |
|---|---|
projectName | Group sessions by project (dashboard organisation) |
buildName | Group sessions by build / CI run |
sessionName | Human-readable session label |
local | "true" if testing localhost / internal via BrowserStackLocal |
debug | Enable visual debugging (screenshots + DOM) |
networkLogs | Capture HAR file |
consoleLogs | "errors" / "warnings" / "info" / "verbose" |
video | Default "true" - session video recording |
seleniumVersion | Pin a Selenium version (e.g., "4.21.0") |
Session status
driver.execute_script(
'browserstack_executor: {"action": "setSessionStatus", '
'"arguments": {"status":"passed","reason":"..."}}'
)BrowserStackLocal
Per browserstack.com/local-testing/automate - start the tunnel and set bstack:options.local = "true" on the session:
./BrowserStackLocal --key "$BROWSERSTACK_ACCESS_KEY" --daemon start
./BrowserStackLocal --key "$BROWSERSTACK_ACCESS_KEY" --daemon stopOr via Docker:
docker run --name bstacklocal -d --rm \
browserstack/local --key "$BROWSERSTACK_ACCESS_KEY"Parallel session limits
Plans limit parallel sessions (typically 5-50). Queue overflow blocks subsequent sessions until earlier ones complete. Match the worker pool to the plan:
from concurrent.futures import ThreadPoolExecutor
MAX_PARALLEL = 5 # match plan
with ThreadPoolExecutor(max_workers=MAX_PARALLEL) as exe:
for case in cases:
exe.submit(run_case, case)Parsing results
Session reports include: session video, network HAR (if networkLogs: true), browser console logs (per consoleLogs level), Selenium logs, and visual-debugging screenshots at each command. Retrieve via REST:
curl -u "$BROWSERSTACK_USERNAME:$BROWSERSTACK_ACCESS_KEY" \
"https://api.browserstack.com/automate/sessions/<session-id>.json"Feed failure videos + HAR to the from-CI-failure workflow in bug-report-template (qa-bug-repro plugin) for triage.
CI integration
# .github/workflows/cross-browser.yml
on: pull_request
jobs:
bstack:
runs-on: ubuntu-latest
strategy:
matrix:
browser:
- { name: Chrome, version: latest, os: Windows, osVersion: "11" }
- { name: Safari, version: "17", os: "OS X", osVersion: Sonoma }
- { name: Firefox, version: latest, os: Windows, osVersion: "11" }
- { name: Edge, version: latest, os: Windows, osVersion: "11" }
steps:
- uses: actions/checkout@v5
- name: Run cross-browser tests
env:
BROWSERSTACK_USERNAME: ${{ secrets.BROWSERSTACK_USERNAME }}
BROWSERSTACK_ACCESS_KEY: ${{ secrets.BROWSERSTACK_ACCESS_KEY }}
BSTACK_BROWSER: ${{ matrix.browser.name }}
BSTACK_VERSION: ${{ matrix.browser.version }}
BSTACK_OS: ${{ matrix.browser.os }}
BSTACK_OS_VERSION: ${{ matrix.browser.osVersion }}
BUILD_TAG: pr-${{ github.event.pull_request.number }}
run: pytest tests/e2e/ --bstackMatch the matrix breadth to a tiered plan - see browser-matrix-strategy-reference for how to tier the matrix so full runs stay within the plan's parallel-session limit.
Vendor-specific limitations
LambdaTest - vendor detail
View source (opens in new window)LambdaTest - vendor detail
Deep reference for cloud-grid-e2e. LambdaTest is a newer cloud-grid provider with strong real-device coverage and a SmartUI visual-testing add-on; like the others it exposes a W3C-compliant endpoint. Per lambdatest.com/support/docs/getting-started-with-lambdatest-automation (opens in new window) (Cloudflare-protected; cite by stable URL).
Auth + hub URL
export LT_USERNAME="your-username"
export LT_ACCESS_KEY="<access-key>"Hub: https://hub.lambdatest.com/wd/hub. Capabilities can be generated from the dashboard (lambdatest.com/capabilities-generator).
Capabilities (W3C)
{
"browserName": "Chrome",
"browserVersion": "latest",
"platformName": "Windows 11",
"LT:Options": {
"user": "$LT_USERNAME",
"accessKey": "$LT_ACCESS_KEY",
"build": "PR-1234",
"name": "Login flow on Chrome Windows",
"project": "my-app"
}
}Full LT:Options table
| Option | Purpose |
|---|---|
user / accessKey | Credentials (alternative to env vars) |
build | CI build / PR identifier |
name | Session label |
project | Group sessions by project (dashboard) |
selenium_version | Pin Selenium version |
w3c | Enable W3C mode (default true) |
console | Console-log level: "errors" / "warnings" / "info" / "verbose" |
network | Capture HAR file |
video | Session recording |
visual | Per-step screenshots |
tunnel / tunnelName | LambdaTest Tunnel for internal apps |
smartUI.project | Link to SmartUI visual-regression project |
Python example
import os
from selenium import webdriver
options = webdriver.EdgeOptions()
options.browser_version = "latest"
options.platform_name = "Windows 11"
lt_options = {
"user": os.environ["LT_USERNAME"],
"accessKey": os.environ["LT_ACCESS_KEY"],
"build": os.environ.get("BUILD_TAG", "local"),
"name": "Checkout on Edge",
"project": "my-app",
"console": "errors",
"network": True,
"video": True,
}
# Vendor caps must be set on Options BEFORE Remote() ([Selenium options]).
options.set_capability("LT:Options", lt_options)
driver = webdriver.Remote(
command_executor="https://hub.lambdatest.com/wd/hub",
options=options,
)
driver.get("https://example.com")
# test...
# report pass / fail; LambdaTest's JS-executor pattern is "lambda-<command>=..."
failed = False
driver.execute_script("lambda-status=" + ("failed" if failed else "passed"))
driver.quit()LambdaTest Tunnel
Per lambdatest.com/support/docs/lambda-tunnel - start the tunnel, then set LT:Options.tunnel: true and tunnelName: "my-tunnel":
./LT --user $LT_USERNAME --key $LT_ACCESS_KEY --tunnelName "my-tunnel"SmartUI integration
SmartUI handles visual regression alongside the functional test (paid add-on; alternative to Percy / Chromatic in qa-visual-regression):
driver.execute_script("smartui.takeScreenshot=login-page")Screenshots compare against a baseline; differences are flagged in the SmartUI dashboard. Approve the initial baseline or false positives flood reports.
Parsing results
Session reports: video (video: true), network HAR (network: true), console logs (console level), per-step screenshots (visual: true), SmartUI diffs (if configured). REST:
curl -u "$LT_USERNAME:$LT_ACCESS_KEY" \
"https://api.lambdatest.com/automation/api/v1/sessions/<session-id>"CI integration
on: pull_request
jobs:
lambdatest:
runs-on: ubuntu-latest
strategy:
matrix:
browser:
- { name: Chrome, version: latest, platform: "Windows 11" }
- { name: Firefox, version: latest, platform: "Windows 11" }
- { name: Safari, version: "17", platform: "macOS Sonoma" }
steps:
- uses: actions/checkout@v6
- name: Run on LambdaTest
env:
LT_USERNAME: ${{ secrets.LT_USERNAME }}
LT_ACCESS_KEY: ${{ secrets.LT_ACCESS_KEY }}
LT_BROWSER: ${{ matrix.browser.name }}
LT_VERSION: ${{ matrix.browser.version }}
LT_PLATFORM: ${{ matrix.browser.platform }}
BUILD_TAG: pr-${{ github.event.pull_request.number }}
run: pytest tests/e2e/ --lambdatestVendor-specific anti-patterns
| Anti-pattern | Why it fails | Fix |
|---|---|---|
Confusing tunnel (boolean) with tunnelName (string) | Tunnel won't activate | Set both when using tunnel |
Missing project field | Dashboard organisation suffers | Always set project |
Non-W3C mode (w3c: false) | Parity issues; future versions remove it | Keep w3c: true (default) |
| SmartUI baseline never approved | False positives flood reports | Approve initial baseline; audit changes |
Vendor-specific limitations
Sauce Labs - vendor detail
View source (opens in new window)Sauce Labs - vendor detail
Deep reference for cloud-grid-e2e. Sauce Labs is one of the original cloud-grid providers, with a W3C-compliant WebDriver endpoint covering desktop + mobile browser combinations and strong Cypress / Playwright / Appium support. Per docs.saucelabs.com/dev/test-configuration-options (opens in new window).
Also worth knowing: running the same suite on Sauce + a second grid is a practical way to catch grid-specific flakes.
Auth + hub URLs (regional)
export SAUCE_USERNAME="oauth-...-..."
export SAUCE_ACCESS_KEY="<access-key-from-user-settings>"US-West: https://ondemand.us-west-1.saucelabs.com:443/wd/hub
US-East: https://ondemand.us-east-4.saucelabs.com:443/wd/hub
EU-Central: https://ondemand.eu-central-1.saucelabs.com:443/wd/hubPick the region closest to your CI runner for lower latency. Each region has its own device matrix + availability; mixing regions in one run adds flake.
Capabilities (W3C)
{
"browserName": "chrome",
"browserVersion": "latest",
"platformName": "Windows 11",
"sauce:options": {
"build": "PR-1234",
"name": "Login flow on Chrome Windows",
"username": "$SAUCE_USERNAME",
"accessKey": "$SAUCE_ACCESS_KEY",
"screenResolution": "1920x1080",
"tunnelName": "my-internal-tunnel",
"extendedDebugging": true,
"capturePerformance": true,
"recordVideo": true,
"recordScreenshots": true,
"tags": ["smoke", "e2e", "auth"]
}
}sauce:options field | Purpose |
|---|---|
username / accessKey | Credentials (can also be in URL) |
build | Group sessions by CI build |
name | Session label in dashboard |
screenResolution | Default 1024x768; common: 1920x1080 |
tunnelName | Sauce Connect tunnel reference (preferred over deprecated tunnelIdentifier) |
extendedDebugging | Enable HAR + console + Selenium logs |
capturePerformance | Browser performance metrics |
recordVideo / recordScreenshots | Session capture |
tags | Free-form tags for filtering |
browserVersion accepts "latest", "latest-1", etc. - version-relative pinning works across release cycles.
Python example
import os
from selenium import webdriver
options = webdriver.FirefoxOptions()
options.browser_version = "latest"
options.platform_name = "Windows 11"
sauce_options = {
"build": os.environ.get("BUILD_TAG", "local"),
"name": "Checkout flow Firefox",
"username": os.environ["SAUCE_USERNAME"],
"accessKey": os.environ["SAUCE_ACCESS_KEY"],
"screenResolution": "1920x1080",
"extendedDebugging": True,
}
# Vendor caps must be set on Options BEFORE Remote() ([Selenium options]).
options.set_capability("sauce:options", sauce_options)
driver = webdriver.Remote(
command_executor="https://ondemand.us-west-1.saucelabs.com:443/wd/hub",
options=options,
)
driver.get("https://example.com")
# test...
driver.quit()Session status
driver.execute_script("sauce:job-result=" + ("passed" if not failed else "failed"))Or via REST API: PUT /rest/v1/{username}/jobs/{session_id}.
Sauce Connect Proxy
Per docs.saucelabs.com/secure-connections/sauce-connect-5 (opens in new window):
# Download Sauce Connect 5 from saucelabs.com
./sc \
--username $SAUCE_USERNAME \
--access-key $SAUCE_ACCESS_KEY \
--tunnel-name "my-internal-tunnel" \
--region us-west-1Then set sauce:options.tunnelName: "my-internal-tunnel" in capabilities. Tunnel cleans up on Ctrl+C. For ephemeral CI: spawn → wait-for-ready → run tests → terminate. SC tunnel setup adds 10-30s to test start.
Parsing results
Session reports include: video (always - recordVideo: true default), network HAR (if extendedDebugging), browser console logs, Selenium logs, per-command screenshots (if recordScreenshots), performance metrics (if capturePerformance). Retrieve via REST (docs.saucelabs.com/dev/api):
curl -u "$SAUCE_USERNAME:$SAUCE_ACCESS_KEY" \
"https://api.us-west-1.saucelabs.com/rest/v1/$SAUCE_USERNAME/jobs/<session-id>"CI integration
on: pull_request
jobs:
sauce:
runs-on: ubuntu-latest
strategy:
matrix:
browser:
- { name: chrome, version: latest, platform: "Windows 11" }
- { name: safari, version: "17", platform: "macOS 14" }
- { name: edge, version: latest, platform: "Windows 11" }
steps:
- uses: actions/checkout@v6
- name: Run on Sauce Labs
env:
SAUCE_USERNAME: ${{ secrets.SAUCE_USERNAME }}
SAUCE_ACCESS_KEY: ${{ secrets.SAUCE_ACCESS_KEY }}
SAUCE_BROWSER: ${{ matrix.browser.name }}
SAUCE_VERSION: ${{ matrix.browser.version }}
SAUCE_PLATFORM: ${{ matrix.browser.platform }}
BUILD_TAG: pr-${{ github.event.pull_request.number }}
run: pytest tests/e2e/ --sauceVendor-specific anti-patterns
| Anti-pattern | Why it fails | Fix |
|---|---|---|
| Hardcoded region | Cross-region latency adds 100ms+ per command | Match region to CI runner location |
tunnelIdentifier (deprecated) | Newer SC versions emit warnings | Use tunnelName |
| Mixed regions in one test run | Increases flake | Pick one region per run |
recordVideo: false to "save money" | Failed-session debugging hard | Keep video for failed sessions at minimum |
Vendor-specific limitations
Related skills
browser-matrix-strategy-reference
Pure-reference for designing and reviewing a browser / OS / device test matrix from traffic data - the T1/T2/T3 tier-membership heuristics (T1 >=5% traffic, T2 1-5% or statutory, T3 <1% with customer demand), the traffic-share sources (own analytics, StatCounter, MDN browser-compat-data), a worked matrix template with tier-change log, the matrix review checklist (staleness, T1 oversize, below-threshold T1 entries, missing real-device coverage), how to justify dropping a legacy browser (IE11, old iOS Safari), and the compatibility budget (tier caps, CI cost formula, published support statement) in references/compatibility-budget.md. Use when designing an initial matrix, capping or publishing a support policy, running a quarterly re-tier review, or making the case to drop a browser. This is the WHAT-to-test strategy reference - to execute the matrix use playwright-testing browser projects (bundled engines), selenium-grid-4-runner (self-hosted), or cloud-grid-e2e (managed grids).
cypress-testing
Authors and improves Cypress E2E tests - installs Cypress, configures `cypress.config.ts`, authors `cy.*` command chains, refactors existing specs (`cy.wait(ms)` sleeps into assertions, repeated flows into `cy.session` custom commands), and debugs with the time-travel GUI; Cypress Cloud for parallel runs and recording. Use for both greenfield test authoring and improving hand-written specs already in the codebase. For automated refactor of raw Cypress Studio recordings specifically, use a dedicated codegen-review pass.
playwright-testing
Authors and remediates Playwright E2E tests across Chromium, Firefox, WebKit - `npm init playwright@latest` scaffolding, `playwright.config.ts` browser projects, accessibility-first locators (`getByRole`/`getByLabelText`) to replace brittle CSS selectors, web-first assertions to eliminate `waitForTimeout` flakiness, Page Object pattern, trace viewer debugging, sharded parallel execution with merged HTML reporting, mobile-web emulation via the `devices` catalog (viewport / DPR / touch per-device projects), the cross-browser matrix with branded channels (chrome / msedge) in references/browser-matrix.md, and GitHub Actions CI integration. Use for new test authoring, flakiness remediation, mobile-breakpoint regression, cross-browser matrix setup, and CI setup; for reviewing codegen output specifically, use a dedicated codegen-review pass.
selenium-grid-4-runner
Author and operate Selenium Grid 4 - self-hosted distributed WebDriver. Covers the six-component architecture (Router / Distributor / Session Map / Event Bus / New Session Queue / Node), standalone vs hub-and-node modes, the Docker-image stack (selenium/standalone-chrome, selenium/hub, selenium/node-chrome), node registration, session-queue tuning, and observability. Use for self-hosted cross-browser testing when data residency or cost-control require an on-prem grid. This is the self-hosted execution RUNNER - for the zero-infra alternative use playwright-testing browser projects (bundled engines); for managed cloud grids use cloud-grid-e2e (BrowserStack / Sauce Labs / LambdaTest); to decide WHICH browsers and tiers to run use browser-matrix-strategy-reference.
selenium-testing
Authors Selenium WebDriver tests in any of its 6+ supported languages (Java, Python, JavaScript, C#, Ruby, Kotlin, PHP) - picks the appropriate language binding, configures WebDriver per browser, uses `By.*` locators with the team's accessibility-first preference where supported, runs locally + via Selenium Grid for distributed execution, parses results to JUnit XML. Use for legacy Selenium-locked stacks; new projects pick Playwright or Cypress.
web-e2e-overview
Teaches web end-to-end testing from first principles: what browser-driven E2E covers and how it differs from unit and integration tests, a decision table for choosing between Playwright, Cypress, Selenium WebDriver, WebdriverIO, Puppeteer, TestCafe and the BrowserStack / Sauce Labs / LambdaTest cloud grids based on files already present in the repo, install and first-run commands for each, and the flakiness traps (fixed sleeps, CSS and XPath selectors, state shared between tests) that sink new suites. Use when a web application has no E2E coverage yet, when picking or replacing an E2E framework, or when a first browser test needs to go green end to end.
webdriverio-testing
Authors WebdriverIO E2E tests - `npm init wdio@latest` scaffolding, services architecture (sauce, browserstack, appium, devtools), reporters (spec, allure, junit), built-in Mocha/Jasmine/Cucumber framework integrations. WebdriverIO sits between Selenium (W3C protocol) and Playwright (modern API) - Selenium-protocol-compatible with rich plugin ecosystem. Use when the team needs WebDriver protocol + service-based device-farm integration.