CtrlK
BlogDocsLog inGet started
Tessl Logo

testland/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.

72

Quality

90%

Does it follow best practices?

Run evals on this skill

Adds up to 20 points to the overall score

View guide

SecuritybySnyk

Passed

No findings from the security scan

Overview
Quality
Evals
Security
Files
name:
cloud-grid-e2e
description:
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.

cloud-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:

ConcernBrowserStackSauce LabsLambdaTest
Auth env varsBROWSERSTACK_USERNAME + BROWSERSTACK_ACCESS_KEYSAUCE_USERNAME + SAUCE_ACCESS_KEYLT_USERNAME + LT_ACCESS_KEY
Hub URLhttps://hub-cloud.browserstack.com/wd/hubregional, e.g. https://ondemand.us-west-1.saucelabs.com:443/wd/hubhttps://hub.lambdatest.com/wd/hub
Vendor options dictbstack:optionssauce:optionsLT:Options
Local tunnelBrowserStackLocalSauce Connect ProxyLambdaTest Tunnel

Everything else is standard W3C capabilities (browserName, browserVersion, platformName) per w3.org/TR/webdriver2/.

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

  • Cross-browser regression across more breadth than Playwright's bundled engines support (real Safari iOS, legacy versions, niche Android devices).
  • Tier-1 browser matrix coverage backed by SLA.
  • Internal / localhost applications testable from the cloud via the vendor's tunnel binary.

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)

  1. Export the vendor's username + access-key env vars from account settings.
  2. Point the WebDriver client at the vendor's hub URL.
  3. Build W3C capabilities and set the vendor options dict (bstack:options / sauce:options / LT:Options) on the Options object before creating the driver - set at minimum a project/build/session name triple so the dashboard groups sessions.
  4. Run the suite; report each session's pass / fail back to the vendor before driver.quit(), so dashboard metrics stay accurate.
  5. For localhost / internal targets, start the vendor's tunnel binary and flag the session as tunneled.
  6. In CI, run the browser matrix as jobs throttled to the plan's parallel-session limit.

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:

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). 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 stop

Sauce 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/ --bstack

All 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-patternWhy it failsFix
Credentials in codeToken leakEnv vars / secret store
All tests on every browser comboSlow + expensive (plan-limited)Tier the matrix - see browser-matrix-strategy-reference
Missing build/project name capsSessions un-grouped in dashboardAlways set build to the CI run / PR identifier
No session-status updateDashboard pass/fail rate inaccurateAlways report status before quit
Tunnel binary not stoppedStale tunnels accumulateAlways stop the daemon after the run
Parallel exceeds plan limitSessions queue + timeoutMatch worker pool to plan
Polling for tunnel-ready without timeoutSuite hangs if the tunnel never connectsBounded wait + fail
Hardcoded vendor URL in testsSwitching grids requires code changesEnv-var-driven hub URL + a small caps abstraction
Treating vendors as drop-in interchangeableOptions dicts differ (bstack:options vs sauce:options vs LT:Options)Isolate vendor caps in one harness module

Limitations

  • Cost. Plan tiers limit parallel sessions; full-matrix runs on every PR are expensive - tier the matrix.
  • Setup latency. Real-device sessions take 5-30s to start; not optimal for short feedback loops.
  • Network shape. Cloud-grid latency is higher than local; some timing-sensitive tests behave differently.
  • Device matrices churn. Vendors add + retire devices; tests pinned to specific versions need periodic updates.
  • Internal networks require the tunnel. Adds setup + tunnel-stability concerns.
  • Cypress does not speak WebDriver. Cloud grids run Cypress via vendor-specific runners (or Cypress Cloud), not the hub URL pattern here.

References

Workspace
testland
Visibility
Public
Created
Last updated
Publish Source
GitHub
Badge
testland/cloud-grid-e2e badge