Complete Documentation
This documentation is the primary user guide for OrbitTest, the OrbitTest mobile testing layer, and Orbittest Studio. It combines the project README, usage guide, v3.4.0 release notes, and Studio README into a single organized handbook for QA engineers, developers, and teams adopting OrbitTest in local and CI workflows.
Contents
Section titled “Contents”- Product Overview
- Feature Status
- System Requirements
- Installation
- Quick Start
- Project Structure
- Configuration
- Writing Browser Tests
- Lifecycle Hooks
- Locators and Element Targeting
- Working With Elements
- Browser Storage and Sessions
- Alerts, Notifications, Frames, Shadow DOM, and Windows
- Visual Automation
- Reports, Traces, and Diagnostics
- CI/CD Integration
- OrbitTest UI
- Forge Test Recorder
- Mobile Testing
- Mobile Test API
- Orbittest Studio
- Studio Installation
- Studio Quick Start
- Studio Run Configurations and Artifacts
- Release Notes: OrbitTest v3.4.0
- Installer Integrity
- Troubleshooting
- Best Practices
- Contributing
- License and Attribution
Product Overview
Section titled “Product Overview”OrbitTest is an intent-first end-to-end testing framework for browser and Android workflows. Its core idea is simple: tests should describe what users see and do, not the internal markup developers happen to write.
Instead of starting every browser test with CSS selectors or XPath, OrbitTest lets you interact with visible labels, roles, text, accessible names, and stable attributes. That makes test files easier to review, easier to debug, and less likely to fail after harmless implementation refactors.
OrbitTest includes:
- Browser automation through Chrome DevTools Protocol.
- A readable JavaScript testing API.
- Intent-based locators for visible text, labels, roles, CSS, XPath, and attributes.
- Test lifecycle hooks.
- Parallel execution, retries, timeouts, and sharding.
- HTML, JSON, summary JSON, and JUnit reports.
- Screenshot and trace artifacts.
- Smart Report diagnostics for console errors, page errors, failed requests, slow requests, and recent navigation.
- CI mode for GitHub Actions, Jenkins, GitLab CI, Azure DevOps, and similar systems.
- A local OrbitTest UI dashboard.
- Forge, a browser test recorder.
- Android mobile automation through ADB and UIAutomator.
- Orbittest Studio, a Windows desktop IDE for Android QA.
Orbittest Studio is a companion desktop application for Android QA engineers and mobile developers. It brings live device mirroring, UI hierarchy inspection, logcat filtering, test editing, execution, and evidence reports into one Windows application.
Feature Status
Section titled “Feature Status”| Area | Status | Notes |
|---|---|---|
| UI automation | Stable | Intent-first browser actions and assertions. |
| Lifecycle hooks | Stable | beforeAll, beforeEach, afterEach, and afterAll. |
| Locator engine | Stable | Text, role, CSS, XPath, attribute, and locator objects. |
| Text readers | Stable | text, visibleText, and domText. |
| Browser storage and sessions | Stable | Cookies, localStorage, sessionStorage, saved sessions, health checks. |
| Alerts, notifications, tabs, and windows | Stable | Dialog and multi-window handling. |
| Frames and Shadow DOM | Stable | Scoped automation for iframe and web component content. |
| Reports | Stable | HTML, JSON, summary JSON, JUnit, screenshots, and traces. |
| CI/CD mode | Stable | Retries, sharding, fail-fast, max failures, annotations, artifacts. |
| Step debugging | Stable | Live inspector-style debugging with visible browser. |
| Smart Report | Stable | Browser evidence collection and failure diagnosis. |
| OrbitTest UI | Stable | Local dashboard for running and inspecting tests. |
| Forge recorder | Stable | Records browser flows and generates OrbitTest scripts. |
| Android mobile provider | Phase 1 | ADB and UIAutomator-based mobile automation. |
| Visual automation | Experimental | Canvas, WebGL, pixel checks, visual snapshots, coordinate actions. |
| API testing | Planned | Future capability. |
OrbitTest is currently in an architecture cleanup phase. Large new feature work should be kept limited while existing functionality is organized into clearer modules and stronger documentation.
System Requirements
Section titled “System Requirements”OrbitTest CLI
Section titled “OrbitTest CLI”| Requirement | Details |
|---|---|
| Node.js | Version 18 or later. |
| npm | Required for installation and project scripts. |
| Browser | A Chromium-based browser. OrbitTest can use Puppeteer’s managed Chrome, system Chrome, or ORBITTEST_CHROME_PATH. |
| Operating system | Windows, macOS, or Linux for browser testing. |
Android Mobile Testing
Section titled “Android Mobile Testing”| Requirement | Details |
|---|---|
| Android SDK platform-tools | Required for adb. |
| Android device | Android 8.0/API 26 or later recommended. |
| USB debugging | Must be enabled and authorized on the device. |
| USB cable | Must support data transfer. Charge-only cables will not work. |
| Optional ffmpeg | Enables WebM preview generation in some report workflows. |
Orbittest Studio
Section titled “Orbittest Studio”| Requirement | Details |
|---|---|
| Operating system | Windows 10 or later, 64-bit. |
| Android device | Android 8.0/API 26 or later recommended. |
| ADB | Bundled with Android Studio or standalone platform-tools. |
| USB | Data cable with USB debugging enabled. |
| Companion app | Installed from Studio onto the connected Android device. |
Installation
Section titled “Installation”Install OrbitTest as a project dependency
Section titled “Install OrbitTest as a project dependency”Project-local installation is recommended for real test suites because it keeps the OrbitTest version locked with the repository.
npm install --save-dev orbittestRun through npx:
npx orbittest --versionnpx orbittest runInstall OrbitTest globally
Section titled “Install OrbitTest globally”Global installation is useful for quick experiments or local CLI access:
npm install -g orbittestorbittest --versionFor team projects, keep a project dependency even if some developers also install the CLI globally.
Browser download behavior
Section titled “Browser download behavior”OrbitTest uses Chrome at runtime. Depending on installation and environment, Puppeteer may download a compatible browser. To skip browser download:
PUPPETEER_SKIP_DOWNLOAD=1 npm install -g orbittestPowerShell:
$env:PUPPETEER_SKIP_DOWNLOAD = "1"npm install -g orbittestIf browser download is skipped, install Chrome locally or set ORBITTEST_CHROME_PATH.
Custom Chrome path
Section titled “Custom Chrome path”PowerShell:
$env:ORBITTEST_CHROME_PATH = "C:\Path\To\chrome.exe"orbittest runmacOS/Linux:
ORBITTEST_CHROME_PATH=/path/to/chrome orbittest runOrbitTest looks for Chrome in this order:
- Puppeteer’s managed Chrome.
ORBITTEST_CHROME_PATH.- A local system Chrome or Chromium installation.
Quick Start
Section titled “Quick Start”Initialize a project
Section titled “Initialize a project”Run:
orbittest initThis creates a starter structure:
orbittest.config.jstests/ example.test.jsreports/If possible, OrbitTest also adds a package script:
{ "scripts": { "test:e2e": "orbittest run" }}Write a first browser test
Section titled “Write a first browser test”Create tests/home.test.js:
const { test, expect } = require("orbittest");
test("home page loads", async (orbit) => { await orbit.open("https://example.com/");
expect(await orbit.hasText("Example Domain")).toBe(true);});Run it:
orbittest run tests/home.test.jsExpected console output is intentionally quiet:
Passed: 1Failed: 0Report: reports/runs/<run-id>/report.htmlOpen the local dashboard
Section titled “Open the local dashboard”orbittest uiThe dashboard opens at a local URL similar to:
http://127.0.0.1:9323/Use it to run tests, inspect reports, filter files, and review artifacts without manually opening report directories.
Project Structure
Section titled “Project Structure”A typical OrbitTest project looks like this:
project-root/ orbittest.config.js package.json tests/ home.test.js login.test.js reports/ latest.html latest.json latest-summary.json latest-junit.xml runs/ <run-id>/ report.html report.json summary.json junit.xml artifacts/Recommended conventions:
- Put test files under
tests/. - Use
.test.jsor.spec.jssuffixes. - Keep reusable helpers small and named by behavior.
- Keep reports out of source control unless a specific artifact is intentionally committed for documentation.
- Store secrets in environment variables, not test files.
Configuration
Section titled “Configuration”OrbitTest reads orbittest.config.js from the project root. All fields are optional.
module.exports = { testDir: "tests", testMatch: ["**/*.test.js", "**/*.spec.js"], reportsDir: "reports", globalSetup: [], workers: 1, maxWorkers: 4, retries: 0, testTimeout: 30000, actionTimeout: 0, browser: { display: "auto" }, use: { web: { browser: "chrome", headless: null }, mobile: null }, experimental: { ui: true, visualAutomation: true, apiTesting: false }, openReportOnFailure: { enabled: !process.env.CI, port: 0 }, ci: { enabled: Boolean(process.env.CI), retries: 1, trace: "on-failure", screenshot: "on-failure", failFast: false, maxFailures: 0, shard: process.env.ORBITTEST_SHARD || null, summary: true, junit: true, githubAnnotations: Boolean(process.env.GITHUB_ACTIONS) }, smartReport: false, smartReportSlowRequestMs: 2000, environments: { staging: { reportsDir: "reports/staging" } }};Browser display
Section titled “Browser display”browser.display supports:
| Value | Behavior |
|---|---|
"auto" | Show browser locally, hide browser in CI. |
"show" | Always show browser unless a CLI flag overrides it. |
"hide" | Hide browser unless a CLI flag overrides it. |
CLI overrides:
orbittest run --show-browserorbittest run --hide-browser--step always uses a visible browser because live debugging requires a browser that can be watched and controlled.
Named environments
Section titled “Named environments”Use environments for configuration overlays:
module.exports = { reportsDir: "reports/local", environments: { staging: { reportsDir: "reports/staging" } }};Run with:
orbittest run --env stagingCLI flags override config values.
Writing Browser Tests
Section titled “Writing Browser Tests”Each browser test receives an orbit object.
const { test, expect } = require("orbittest");
test("login flow", async (orbit) => { await orbit.open("https://example.com/login");
await orbit.type("Email", "user@example.com"); await orbit.type("Password", "secret"); await orbit.click("Login");
await orbit.waitForText("Dashboard", { timeout: 10000 });
expect(await orbit.hasText("Dashboard")).toBe(true); expect(await orbit.exists(orbit.css(".account-menu"))).toBe(true);});Test options
Section titled “Test options”test("slow checkout flow", { retries: 1, timeout: 60000 }, async (orbit) => { await orbit.open("https://example.com/checkout");});Use retries carefully. Retries can protect CI against temporary infrastructure problems, but they should not hide deterministic product or test defects.
Test naming
Section titled “Test naming”Good test names describe behavior:
test("registered user can open dashboard", async (orbit) => {});test("guest sees pricing page", async (orbit) => {});Avoid names that describe implementation details:
test("clicks blue button", async (orbit) => {});test("checks div class", async (orbit) => {});Lifecycle Hooks
Section titled “Lifecycle Hooks”OrbitTest supports run-level and test-level hooks:
const { beforeAll, afterAll, beforeEach, afterEach, test } = require("orbittest");
beforeAll(async (runInfo) => { console.log(`Run started: ${runInfo.runId}`);});
beforeEach(async (orbit, testInfo) => { console.log(`Starting ${testInfo.name}, attempt ${testInfo.attempt}`);});
afterEach(async (orbit, testInfo) => { if (testInfo.status === "failed") { await orbit.screenshot(`reports/${testInfo.name}.png`); }});
afterAll(async (runInfo) => { console.log(`Run finished: ${runInfo.status}`);});testInfo includes:
namefileindexattemptretryretriestimeoutstatusstartedAtendedAtdurationMserrorartifacts
Global setup file
Section titled “Global setup file”Use globalSetup when multiple test files need shared hooks:
module.exports = { globalSetup: "tests/setup.js"};tests/setup.js:
const { beforeAll, afterAll, beforeEach, afterEach } = require("orbittest");
beforeAll(async (runInfo) => { console.log(`Starting run ${runInfo.runId}`);});
beforeEach(async (orbit, testInfo) => { console.log(`Starting ${testInfo.name}`);});
afterEach(async (orbit, testInfo) => { if (testInfo.status === "failed") { await orbit.screenshot(`reports/${testInfo.name}.png`); }});
afterAll(async (runInfo) => { console.log(`Completed ${runInfo.results.length} results`);});Keep hooks transparent. If a hook logs in, creates data, or changes application state, make that behavior obvious to future readers.
Locators and Element Targeting
Section titled “Locators and Element Targeting”OrbitTest is intent-first. Start with what users can see.
await orbit.click("Login");await orbit.type("Email", "user@example.com");await orbit.waitForText("Dashboard");Use explicit locators when text is ambiguous or when the application exposes a stable automation handle.
CSS selector
Section titled “CSS selector”await orbit.click(orbit.css("#login"));await orbit.type(orbit.css("input[name='email']"), "user@example.com");await orbit.click(orbit.xpath("//button[text()='Login']"));await orbit.waitFor(orbit.xpath("//h1[contains(text(), 'Dashboard')]"));await orbit.click(orbit.getByRole("button", "Login"));await orbit.click(orbit.getByRole("link", "Docs"));expect(await orbit.text(orbit.getByRole("heading", "Welcome"))).toContain("Welcome");Attribute
Section titled “Attribute”await orbit.click(orbit.getByAttribute("data-testid", "submit"));await orbit.type(orbit.getByAttribute("name", "email"), "user@example.com");Locator objects
Section titled “Locator objects”await orbit.click({ css: "#login" });await orbit.click({ xpath: "//button" });await orbit.click({ role: "button", name: "Login" });await orbit.click({ attribute: "data-testid", value: "submit" });Locator guidance
Section titled “Locator guidance”Prefer this order:
- Visible text or label when it is stable and unique.
- Accessible role and name when it communicates user intent.
- Stable attributes such as
data-testid. - CSS selectors for structure that is expected to remain stable.
- XPath only when other options cannot express the target clearly.
Avoid generated class names, deep DOM paths, and positional selectors unless the UI genuinely has no better target.
Working With Elements
Section titled “Working With Elements”Navigation
Section titled “Navigation”await orbit.open("https://example.com/");Clicks and mouse actions
Section titled “Clicks and mouse actions”await orbit.click("Login");await orbit.hover("Menu");await orbit.doubleClick("Open");await orbit.rightClick("File");Click actions show a short red dot at the browser coordinate before the click is sent. This helps during --step debugging and can appear in trace screenshots when the page remains on the same screen long enough.
Disable the marker for a specific action:
await orbit.click("Login", { visualize: false });Typing
Section titled “Typing”OrbitTest resolves fields by label, placeholder, name, or accessible text:
await orbit.type("Email", "user@example.com");Text and state checks
Section titled “Text and state checks”expect(await orbit.hasText("Welcome")).toBe(true);expect(await orbit.exists(orbit.css(".success"))).toBe(true);Page title and URL
Section titled “Page title and URL”const title = await orbit.title();const url = await orbit.url();
expect(title).toContain("Dashboard");expect(url).toContain("/dashboard");Use pageState() when both values are needed:
const page = await orbit.pageState();
expect(page.title).toContain("Dashboard");expect(page.url).toContain("/dashboard");Screenshots
Section titled “Screenshots”await orbit.screenshot("reports/home.png");Collections
Section titled “Collections”Use orbit.all() when a locator matches multiple elements:
const buttons = await orbit.all(orbit.css("button"));
for (const button of buttons) { console.log(button.text);}orbit.elements() is an alias:
const products = await orbit.elements(orbit.css(".product-title"));Each returned item can be reused with actions and checks:
const products = await orbit.all(orbit.css(".product-title"));
for (const product of products) { const name = await orbit.text(product);
if (name.includes("iPhone")) { await orbit.click(product); break; }}Returned items include snapshot details:
const items = await orbit.all(orbit.css(".product-title"));const first = items[0];
console.log(first.tag);console.log(first.text);console.log(first.visible);console.log(first.attributes);If clicking an item removes or reorders elements, fetch the collection again before the next click:
while (await orbit.exists(orbit.css(".delete"), { timeout: 0 })) { const deleteButtons = await orbit.all(orbit.css(".delete"));
await orbit.click(deleteButtons[0]);}Prefer event-based or state-based waits:
await orbit.waitForText("Dashboard");await orbit.waitForText("Dashboard", { timeout: 10000, interval: 200 });await orbit.waitFor(orbit.css(".toast"));await orbit.waitFor(orbit.getByRole("button", "Save"));Fixed waits are available but should be rare:
await orbit.wait(500);Use fixed waits only when there is no reliable state to wait for.
Browser Storage and Sessions
Section titled “Browser Storage and Sessions”OrbitTest starts each test with a clean browser profile. Use orbit.storage when a test needs explicit cookies, localStorage, sessionStorage, or saved login state.
Cookies
Section titled “Cookies”await orbit.open("https://example.com");
await orbit.storage.setCookie({ name: "session", value: "abc123", httpOnly: true, secure: true});
const cookies = await orbit.storage.cookies();expect(cookies.some(cookie => cookie.name === "session")).toBe(true);Common methods:
await orbit.storage.cookies();await orbit.storage.setCookie("theme", "dark");await orbit.storage.setCookie({ name: "session", value: "abc123" });await orbit.storage.deleteCookie("session");await orbit.storage.clearCookies();Local storage and session storage
Section titled “Local storage and session storage”await orbit.storage.setLocal("token", "local-token");await orbit.storage.setSession("view", "compact");
expect(await orbit.storage.getLocal("token")).toBe("local-token");expect(await orbit.storage.getSession("view")).toBe("compact");Useful methods:
await orbit.storage.local();await orbit.storage.setLocal("token", "abc123");await orbit.storage.getLocal("token");await orbit.storage.clearLocal();
await orbit.storage.session();await orbit.storage.setSession("view", "compact");await orbit.storage.getSession("view");await orbit.storage.clearSession();Save and load sessions
Section titled “Save and load sessions”Save login state after a real login:
await orbit.open("https://example.com/login");await orbit.type("Email", "user@example.com");await orbit.type("Password", "secret");await orbit.click("Login");await orbit.waitForText("Dashboard");
await orbit.storage.saveSession("auth/session.json");Load it later:
await orbit.open("https://example.com");await orbit.storage.loadSession("auth/session.json");await orbit.open("https://example.com/dashboard");
expect(await orbit.hasText("Dashboard")).toBe(true);Inspect session health
Section titled “Inspect session health”inspect() does not print cookie or token values by default. It reports counts, key names, auth-like signals, JWT expiry, and recommendations.
const state = await orbit.storage.inspect();
console.log(state.auth.present);console.log(state.auth.signalCount);console.log(state.recommendations);Fail early when a saved login is not healthy:
await orbit.storage.loadSession("auth/admin-session.json");
await orbit.storage.expectHealthySession({ minMinutes: 15, requireCookie: true});
await orbit.open("https://example.com/admin");Alerts, Notifications, Frames, Shadow DOM, and Windows
Section titled “Alerts, Notifications, Frames, Shadow DOM, and Windows”JavaScript alerts, prompts, and confirms
Section titled “JavaScript alerts, prompts, and confirms”await orbit.open("data:text/html,<button onclick='alert(\"hello\")'>Alert</button>");await orbit.click("Alert");
expect(await orbit.alertText()).toBe("hello");await orbit.acceptAlert();Prompt:
await orbit.click("Ask name");await orbit.acceptAlert({ promptText: "Abhay" });Confirm:
await orbit.click("Delete");await orbit.dismissAlert();Notification permissions
Section titled “Notification permissions”await orbit.open("https://example.com");await orbit.grantNotifications();expect(await orbit.getNotificationPermission()).toBe("granted");
await orbit.denyNotifications("https://example.com");expect(await orbit.getNotificationPermission()).toBe("denied");
await orbit.resetNotificationPermission();Windows and tabs
Section titled “Windows and tabs”const main = (await orbit.listWindows()).find(window => window.active);
await orbit.click("Open details");const popup = await orbit.waitForWindow({ switchTo: true });
expect(await orbit.hasText("Details")).toBe(true);
await orbit.switchToWindow(0);await orbit.switchToWindow(main.id);await orbit.closeWindow(popup.id);switchToWindow() and closeWindow() accept:
- Index.
- Target ID.
- URL text.
- Title text.
- Regular expression.
- Predicate function.
newWindow(url) opens a new tab and switches to it unless { switchTo: false } is passed.
Frames
Section titled “Frames”Use orbit.frame() when a form, button, or widget lives inside an iframe:
const billing = await orbit.frame(orbit.getByAttribute("title", "Billing"));
await billing.type("Email", "team@example.test");await billing.click("Save billing");expect(await billing.exists("Saved")).toBe(true);Nested frames:
const checkout = await orbit.frame(orbit.getByAttribute("title", "Checkout"));const vault = await checkout.frame(orbit.getByAttribute("title", "Vault"));
await vault.click("Approve");Or pass the frame path:
const vault = await orbit.frame([ orbit.getByAttribute("title", "Checkout"), orbit.getByAttribute("title", "Vault")]);Shadow DOM
Section titled “Shadow DOM”Use orbit.shadow() for web components:
const profile = await orbit.shadow(orbit.css("user-profile"));
await profile.type("Email", "team@example.test");await profile.click("Save");expect(await profile.text(orbit.css("#status"))).toBe("Saved");Nested shadow roots:
const shell = await orbit.shadow(orbit.css("app-shell"));const panel = await shell.shadow(orbit.css("settings-panel"));
await panel.click("Enable");Or pass a path:
const panel = await orbit.shadow([ orbit.css("app-shell"), orbit.css("settings-panel")]);Visual Automation
Section titled “Visual Automation”Visual automation is useful when the application renders pixels instead of standard DOM elements. Common examples include canvas games, WebGL experiences, maps, charts, remote desktops, and custom UI surfaces.
await orbit.open("https://www.pinthing.com/");
expect(await orbit.exists(orbit.css("canvas"))).toBe(true);
await orbit.evaluate(() => window.stopClock && window.stopClock());
const changed = await orbit.visual.changed(async () => { await orbit.evaluate(() => window.pinthing.down());});
expect(changed).toBe(true);Coordinate actions:
await orbit.mouse.click(680, 450);await orbit.mouse.drag({ x: 680, y: 450 }, { x: 760, y: 360 });await orbit.mouse.wheel(0, -500);Visual checks:
await orbit.visual.waitForStable();await orbit.visual.snapshot("reports/canvas-state.png");await orbit.visual.expectPixel({ x: 30, y: 30 }, "#ff0000", { tolerance: 5 });
const red = await orbit.visual.findColor("#df1f1f", { tolerance: 70 });await orbit.visual.clickColor("#df1f1f", { tolerance: 70 });Visual automation is experimental. Use it for UI surfaces where semantic locators are not available, and keep tests focused on clear visual states.
Reports, Traces, and Diagnostics
Section titled “Reports, Traces, and Diagnostics”After each run, OrbitTest writes:
reports/latest.htmlreports/latest.jsonreports/latest-summary.jsonreports/latest-junit.xmlreports/runs/<run-id>/report.htmlreports/runs/<run-id>/report.jsonreports/runs/<run-id>/summary.jsonreports/runs/<run-id>/junit.xmlreports/runs/<run-id>/artifacts/Use each file this way:
| File | Purpose |
|---|---|
report.html | Human-readable dashboard. |
report.json | Full run data, results, errors, artifacts, traces, and smart evidence. |
summary.json | Compact CI-friendly summary for scripts and dashboards. |
junit.xml | CI test report format. |
latest.* | Shortcut to the newest run. |
The HTML report includes:
- Total tests.
- Passed tests.
- Failed tests.
- Flaky tests.
- Skipped tests.
- Duration.
- Failure diagnostics with likely cause and next actions.
- Inline failure screenshot.
- Error details and stack trace.
- Source code frame for the failing line.
- Embedded trace timeline when
--traceis used. - Smart browser evidence when
--smart-reportis used. - Mobile evidence when Android tests are used.
orbittest run tests/login.test.js --traceTrace captures orbit.* steps, assertion failures, status, duration, URL/title, failed step details, and links to step screenshots.
Raw trace files are stored under:
reports/runs/<run-id>/artifacts/traces/Smart Report
Section titled “Smart Report”orbittest run tests/login.test.js --smart-reportSmart Report captures:
- Console errors.
- Page JavaScript errors.
- Failed requests.
- Slow requests.
- Recent navigation.
- Current page URL.
When Smart Report detects a clear application failure such as invalid credentials, unauthorized access, failed API calls, or serious browser errors, OrbitTest can mark the test failed even if the script itself did not assert after the action.
Local failure report server
Section titled “Local failure report server”For local failures, OrbitTest can start a report server and open the failed report automatically:
http://127.0.0.1:<port>/Disable it for one run:
orbittest run tests/login.test.js --no-open-report-on-failureUse a fixed port:
orbittest run tests/login.test.js --report-port 9323Read summary from a script
Section titled “Read summary from a script”const fs = require("fs");
const summary = JSON.parse(fs.readFileSync("reports/latest-summary.json", "utf8"));
console.log(`OrbitTest status: ${summary.status}`);console.log(`Passed: ${summary.summary.passed}`);console.log(`Failed: ${summary.summary.failed}`);console.log(`Flaky: ${summary.summary.flaky}`);console.log(`Report: ${summary.reportPaths.html}`);
if (summary.status !== "passed") { process.exit(1);}Clean old reports
Section titled “Clean old reports”Preview cleanup:
orbittest clean-reports --dry-runRemove old reports:
orbittest clean-reportsCustomize retention:
orbittest clean-reports --passed 20 --failed 50 --max-age-days 60Default cleanup keeps:
- The latest report.
- The last 10 passed runs.
- The last 30 failed runs.
- No runs older than 30 days.
CI/CD Integration
Section titled “CI/CD Integration”Use --ci in automation:
orbittest run --ciCI mode:
- Does not open the local failure report server.
- Hides the browser by default.
- Prints a CI summary.
- Writes
summary.json. - Writes
junit.xml. - Captures trace files only when a test fails by default.
- Marks a retried test as
flakywhen it fails first and later passes.
Example CI output:
OrbitTest CI Summary--------------------Status: PASSEDTotal: 5Passed: 5Flaky: 0Failed: 0Skipped: 0Duration: 28.42sReport: reports/runs/<run-id>/report.htmlSummary: reports/runs/<run-id>/summary.jsonJUnit: reports/runs/<run-id>/junit.xmlRetries
Section titled “Retries”Config:
module.exports = { retries: 0, ci: { enabled: Boolean(process.env.CI), retries: 1 }};CLI:
orbittest run --ci --retries 2If a test passes only after retry, the run can still pass, but the result is marked flaky in the HTML report, report.json, and summary.json.
Stop early on failures
Section titled “Stop early on failures”orbittest run --ci --fail-fastStop after three failures:
orbittest run --ci --max-failures 3Tests that were not scheduled because of the stop condition are reported as skipped.
Sharding
Section titled “Sharding”orbittest run --ci --shard 1/4orbittest run --ci --shard 2/4orbittest run --ci --shard 3/4orbittest run --ci --shard 4/4--shard current/total splits registered tests by index. Every shard writes its own HTML, JSON, summary, and JUnit files.
PowerShell environment variable:
$env:ORBITTEST_SHARD = "1/2"orbittest run --ciGitHub Actions annotations
Section titled “GitHub Actions annotations”orbittest run --ci --github-annotationsFailed tests are printed as GitHub error annotations. Flaky tests are printed as warning annotations.
GitHub Actions example
Section titled “GitHub Actions example”name: e2e
on: pull_request: push: branches: [main]
jobs: orbittest: runs-on: ubuntu-latest strategy: fail-fast: false matrix: include: - shard: 1/2 artifact: shard-1 - shard: 2/2 artifact: shard-2 steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: node-version: 20 - run: npm ci - run: npx orbittest run --ci --workers 2 --shard ${{ matrix.shard }} --github-annotations - uses: actions/upload-artifact@v4 if: always() with: name: orbittest-report-${{ matrix.artifact }} path: reports/OrbitTest UI
Section titled “OrbitTest UI”Start the local dashboard:
orbittest uiCustom port:
orbittest ui --port 9400Start without opening the browser:
orbittest ui --no-openUse another reports folder:
orbittest ui --reports-dir reports/stagingOrbitTest UI supports:
- Viewing test files and registered test names.
- Running all tests or one file.
- Local, Evidence, and CI presets.
- Previewing the exact command before launch.
- Turning on Trace or Smart Report.
- Running with CI mode or hidden browser mode.
- Live command output.
- Filtering tests and reports.
- Browsing previous HTML, JSON, and JUnit reports.
- Opening failed run artifacts quickly.
Forge Test Recorder
Section titled “Forge Test Recorder”Forge opens a recorder panel alongside a fresh browser window. It records interactions and generates an OrbitTest script.
orbittest forgeorbittest forge https://example.comorbittest forge https://example.com --output tests/login.test.jsForge captures:
- Clicks.
- Double-clicks.
- Right-clicks.
- Typing, excluding passwords and token-like values.
- Select changes.
- Visible checks.
- Navigation.
- Stable locators such as role, text, attribute, and fallback CSS.
Generated scripts are plain OrbitTest files:
const { test, expect } = require("orbittest");
test("Login flow", async (orbit) => { await orbit.open("https://example.com"); await orbit.click("Login"); await orbit.type("Email", "user@example.com"); await orbit.type("Password", process.env.ORBITTEST_PASSWORD || ""); expect(await orbit.exists(orbit.getByRole("heading", "Dashboard"))).toBe(true);});Mobile Testing
Section titled “Mobile Testing”OrbitTest v3.4.0 adds the first publish-ready Android mobile testing layer while keeping the existing browser runner flow intact.
Mobile support uses ADB and UIAutomator directly. It does not require Appium, WebdriverIO, Detox, Maestro, or Playwright mobile.
Install mobile support
Section titled “Install mobile support”npm install orbittest @orbittest/mobileAndroid requirements
Section titled “Android requirements”- Android SDK platform-tools installed.
- USB debugging enabled.
adb devicesshows the device asdevice.- OrbitTest Desktop or Studio may inject
DEVICE_SERIAL,ADB_PATH, andPROJECT_ROOT.
Mobile configuration
Section titled “Mobile configuration”module.exports = { use: { mobile: { provider: "@orbittest/mobile", platform: "android", adbPath: process.env.ADB_PATH || "adb", deviceSerial: process.env.DEVICE_SERIAL, apk: "./app.apk", appPackage: "com.myapp", appActivity: ".MainActivity", artifactsDir: "orbittest-results", screenshotOnFailure: true, logcatOnFailure: true, uiDumpOnFailure: true } }};Mobile test example
Section titled “Mobile test example”const { test, expect } = require("orbittest/mobile");
test("login smoke test", async (orbit) => { await orbit.wakeUp(); await orbit.launchApp("com.example.myapp", "com.example.myapp.MainActivity");
await orbit.waitForId("username_field", 10000); await orbit.tapById("username_field"); await orbit.typeText("testuser@example.com");
await orbit.tapById("password_field"); await orbit.typeText("Test@123");
await orbit.tapByText("Sign In");
await orbit.waitForText("Welcome", 10000); expect(await orbit.hasText("Welcome")).toBe(true);});Hybrid web and mobile test
Section titled “Hybrid web and mobile test”const { test } = require("orbittest");
test("same user works on web and mobile", async ({ page, orbit }) => { await page.goto("https://app.example.com"); await page.clickText("Create account");
await orbit.installApp(); await orbit.launchApp(); await orbit.waitForText("Welcome");});Useful mobile commands
Section titled “Useful mobile commands”orbittest devicesorbittest doctororbittest run tests/mobile-smoke.test.jsMobile failure reports can include:
- Screenshots.
- UI XML.
- UI JSON.
- Logcat output.
- Device metadata.
- App metadata.
- HTML report evidence.
Mobile Test API
Section titled “Mobile Test API”App lifecycle
Section titled “App lifecycle”| Method | Purpose |
|---|---|
launchApp | Launch an installed app. |
stopApp | Stop the target app. |
installApp | Install an APK. |
uninstallApp | Remove an app from the device. |
clearAppData | Clear app data. |
isAppInstalled | Check whether a package is installed. |
Gestures
Section titled “Gestures”| Method | Purpose |
|---|---|
tap | Tap screen coordinates. |
longPress | Long-press screen coordinates or target. |
swipe | Swipe between points. |
scrollDown | Scroll down. |
scrollUp | Scroll up. |
Text and keys
Section titled “Text and keys”| Method | Purpose |
|---|---|
typeText | Type text into focused input. |
clearText | Clear focused text. |
pressKey | Send Android key input. |
UI lookup
Section titled “UI lookup”| Method | Purpose |
|---|---|
tapById | Tap by Android resource ID. |
tapByText | Tap by visible text. |
tapByDescription | Tap by content description. |
hasText | Check whether text exists. |
hasId | Check whether an ID exists. |
| Method | Purpose |
|---|---|
waitForId | Wait for resource ID. |
waitForText | Wait for visible text. |
waitForDescription | Wait for content description. |
waitForGoneId | Wait until ID disappears. |
waitForGoneText | Wait until text disappears. |
Device state
Section titled “Device state”| Method | Purpose |
|---|---|
wakeUp | Wake device. |
sleepScreen | Turn screen off. |
isScreenOn | Check screen state. |
getScreenSize | Read screen size. |
getCurrentActivity | Read current Android activity. |
Evidence
Section titled “Evidence”| Method | Purpose |
|---|---|
screenshot | Capture screenshot. |
saveScreenshot | Save screenshot to file. |
getLogcat | Read logcat output. |
saveLogcat | Save logcat to file. |
Mobile assertions
Section titled “Mobile assertions”v3.4.0 adds mobile-aware assertions:
await expect(orbit).toHaveText("Login");await expect(orbit).toHaveId("username_field");await expect(orbit).toMatchScreenshot("login-screen");Orbittest Studio
Section titled “Orbittest Studio”Orbittest Studio is a Windows desktop IDE for Android QA. It connects to Android devices over USB and combines device mirroring, UI inspection, logcat, test editing, test execution, and evidence reports in a focused workspace.
The source code is private. The public repository is used for distribution and release downloads.
Studio capabilities
Section titled “Studio capabilities”| Capability | Description |
|---|---|
| Live Device Mirror | Streams the connected Android device screen into Studio. |
| UI Inspector | Dumps and browses the UIAutomator hierarchy. |
| Device Logs | Captures and filters logcat output. |
| Mobile Test Runner | Runs JavaScript tests through the orbittest/mobile API. |
| Reports and Evidence | Produces HTML reports with screenshots, logs, and recordings. |
| Built-in IDE | File explorer, Monaco editor, tabs, terminal, Git, and npm manager. |
| Companion App | Android APK that enables bridge, screen capture, and diagnostics. |
Live Device Mirror
Section titled “Live Device Mirror”Studio streams the Android screen using the OrbiStream engine. It supports:
- Auto mode.
- Low Latency mode.
- High Quality mode.
- ADB screenshot fallback.
- Snapshot capture.
Android may show a MediaProjection permission dialog the first time screen capture is used. Approve the dialog on the device.
UI Inspector
Section titled “UI Inspector”The Inspector window lets users:
- Dump the UIAutomator hierarchy.
- Browse nodes.
- Highlight selected nodes on the live preview.
- Copy locators.
- Generate tap code for test scripts.
Device Logs
Section titled “Device Logs”The Logs panel supports filtering by:
- Package name.
- PID.
- Tag.
- Severity level.
- Text match.
- Regex.
- Time range.
Filtered logs can be copied or downloaded as a file.
Built-in IDE
Section titled “Built-in IDE”Studio includes a VS Code-style workspace:
- File explorer.
- Monaco editor.
- Tabs.
- Dirty-state tracking.
- Integrated terminal.
- Git source control.
- npm package manager.
- Active test project scope.
Studio Installation
Section titled “Studio Installation”Download the installer
Section titled “Download the installer”Download the latest Windows installer from the official releases page:
https://github.com/abhay-1994/Orbittest_Studio_download/releases/latestRun the installer
Section titled “Run the installer”Double-click:
Orbittest-Studio-Setup-x.x.x.exeFollow the prompts.
Windows SmartScreen may show a warning because the installer is not yet Authenticode-signed. Use More info and then Run anyway only after verifying that the file came from the official release page. Use the SHA256 integrity process described later in this document.
Enable USB debugging
Section titled “Enable USB debugging”- Open Android Settings.
- Go to About Phone.
- Tap Build Number 7 times to unlock Developer Options.
- Open Developer Options.
- Enable USB Debugging.
- Connect the phone to the PC with a USB data cable.
- Approve the “Allow USB debugging?” prompt on the phone.
Install Orbittest Companion
Section titled “Install Orbittest Companion”- Open Orbittest Studio.
- Wait for the device to appear in the Devices panel.
- Select the device.
- Click Install on the device card.
- Approve installation prompts on the phone.
If Android blocks installation, enable Install via USB in Developer Options and retry.
Studio Quick Start
Section titled “Studio Quick Start”- Open Studio.
- Click New Project or open an existing folder.
- Create a test file under
tests/, for exampletests/smoke.test.js. - Select a connected device in the Devices panel.
- Open the test file in the editor.
- Choose a run configuration.
- Click Run.
- Watch live output in the Console panel.
- Open the Reports panel to inspect results.
Example Studio mobile test:
const { test, expect } = require("orbittest/mobile");
test("app opens to welcome screen", async (orbit) => { await orbit.wakeUp(); await orbit.launchApp("com.example.myapp", "com.example.myapp.MainActivity");
await orbit.waitForText("Welcome", 10000);
expect(await orbit.hasText("Welcome")).toBe(true);});Studio Run Configurations and Artifacts
Section titled “Studio Run Configurations and Artifacts”| Configuration | Trace | Smart Report | Video Recording |
|---|---|---|---|
| Quick Run | Off | On | Always |
| Trace Run | On | On | Always |
| Evidence Run | On | On | Always |
| Minimal Run | Off | Off | Off |
Each run writes artifacts to:
<project>/.orbitest/runs/<timestamp>-<run-id>/Typical artifacts:
run.jsonstdout.logstudio-report.htmlfinal-screenshot.pngdevice-recording.mp4Reports are viewable in-app and include:
- Pass/fail status.
- Action logs.
- Final screenshot.
- Device recording.
- Frame evidence player.
- Fullscreen mode.
Release Notes: OrbitTest v3.4.0
Section titled “Release Notes: OrbitTest v3.4.0”OrbitTest v3.4.0 adds the first publish-ready mobile testing layer for Android automation while preserving the existing browser runner flow.
Highlights
Section titled “Highlights”- Added mobile test configuration through
use.mobile. - Added optional
@orbittest/mobileprovider loading. - Added
.envsupport for ADB and device settings. - Added Android automation APIs for install, uninstall, launch, stop, gestures, keyboard input, UIAutomator selectors, screenshots, logcat, device state, and raw ADB access.
- Added mobile-aware test contexts with
page/webfor browser automation andorbit/mobilefor Android automation. - Added mobile assertions:
expect(orbit).toHaveText(),toHaveId(), andtoMatchScreenshot(). - Added
orbittest devicesfor connected Android device discovery. - Added
orbittest doctorfor environment diagnostics. - Added mobile failure and report artifacts including screenshots, UI XML/JSON, logcat output, device/app metadata, and HTML report evidence.
- Added the
orbittest/mobileexport and TypeScript declarations for mobile-first tests. - Added the publishable
packages/mobileprovider package with ADB, UIAutomator parsing, selector matching, artifacts, visual comparison, and doctor utilities.
Packaging
Section titled “Packaging”- The root
orbittestpackage now includes the mobile provider files needed byorbittest/mobile. - Mobile provider runtime dependencies are declared at the root package level so the bundled mobile export resolves correctly after npm install.
- Packaging tests were added to prevent the mobile export from regressing in future releases.
Validation
Section titled “Validation”- Unit coverage includes mobile parser and provider foundations.
- Unit coverage includes the root mobile export packaging guard.
npm pack --dry-runverifies that these files are included in the root package:mobile.jsmobile.d.tscore/providers/mobile.jspackages/mobile/**
Installer Integrity
Section titled “Installer Integrity”Verify downloaded installers before running them.
Published SHA256 for v1.0.0:
183CF4BD94647F58CF64F0A482EA4734120DA559E131C9D1C7E5056E2A00ECD6Verify with PowerShell:
Get-FileHash .\Orbittest-Studio-Setup-1.0.0.exe -Algorithm SHA256The hash in the output must match exactly. If it does not match:
- Delete the installer.
- Download again from the official GitHub releases page.
- Verify the hash again before running the file.
v1.0.0 release validation
Section titled “v1.0.0 release validation”| Check | Result |
|---|---|
| Prettier format check | Passed |
| ESLint | Passed |
| TypeScript typecheck | Passed |
| Vitest unit tests | 7 files, 32 tests passed |
| npm high-severity security audit | 0 vulnerabilities |
| Third-party license audit | Passed |
| Windows NSIS installer build | Passed |
| Bundled Companion APK verification | Passed |
Troubleshooting
Section titled “Troubleshooting”No tests found
Section titled “No tests found”Run:
orbittest initorbittest runConfirm that test files match:
tests/**/*.test.jstests/**/*.spec.jsIf needed, update testMatch in orbittest.config.js.
Chrome does not launch
Section titled “Chrome does not launch”Try:
npm installThen confirm Chrome availability or set ORBITTEST_CHROME_PATH.
Element not found
Section titled “Element not found”The element may not be visible yet, the text may not match, or the target may be inside a frame/shadow root.
Use a wait:
await orbit.waitFor(orbit.getByRole("button", "Continue"));await orbit.click(orbit.getByRole("button", "Continue"));Use a more specific locator:
await orbit.click(orbit.css("[data-testid='submit']"));Test passes locally but fails in CI
Section titled “Test passes locally but fails in CI”Check:
- Node.js version.
- Chrome availability.
- Environment variables.
- Network access.
- Browser display/headless differences.
- Report screenshots.
- Trace artifacts.
- Smart Report evidence.
Device not appearing in Studio
Section titled “Device not appearing in Studio”Run:
adb devicesConfirm the device appears as device, not unauthorized.
If ADB is not on PATH, set the ADB binary path in Studio settings. Common path:
C:\Users\<you>\AppData\Local\Android\Sdk\platform-tools\adb.exeTry a different USB cable or port. Many cables are charge-only.
Device shows unauthorized
Section titled “Device shows unauthorized”Unlock the phone and approve the “Allow USB debugging?” prompt. If the prompt does not appear:
- Unplug the device.
- Replug it.
- Confirm USB debugging is still enabled.
- Revoke USB debugging authorizations and try again if needed.
Companion install blocked
Section titled “Companion install blocked”Enable:
Developer Options > Install via USBIf a previous version is installed, uninstall it from the phone’s app settings, then retry.
Live mirror is black or stuck
Section titled “Live mirror is black or stuck”OrbiStream uses Android MediaProjection. Approve the screen-capture permission prompt on the phone.
If the stream remains black:
- Tap Ping in the Devices panel.
- Confirm the companion bridge is alive.
- Reopen the Live panel.
- Restart ADB if the device session is stale.
No logs in the Logs panel
Section titled “No logs in the Logs panel”Widen filters:
- Severity: Info+ or All levels.
- Time range: All buffered.
- Clear package, tag, and text filters.
If package PID lookup failed, make sure the app is running and regenerate logs.
Useful ADB reset commands
Section titled “Useful ADB reset commands”adb kill-serveradb start-serveradb devicesadb logcat -cBest Practices
Section titled “Best Practices”Write tests in user language
Section titled “Write tests in user language”Use visible names and roles when possible:
await orbit.click("Login");await orbit.type("Email", "qa@example.com");Use implementation-specific selectors only when they are stable and intentional.
Wait for state, not time
Section titled “Wait for state, not time”Prefer:
await orbit.waitForText("Dashboard");await orbit.waitFor(orbit.css(".ready"));Avoid:
await orbit.wait(2000);Keep assertions close to actions
Section titled “Keep assertions close to actions”Assert after meaningful steps. This makes failures easier to understand and improves report quality.
Use Smart Report in CI
Section titled “Use Smart Report in CI”Smart Report helps explain whether a failure came from:
- A browser-side JavaScript error.
- A failed network request.
- A slow backend response.
- An unauthorized response.
- A page that navigated unexpectedly.
Keep artifacts useful
Section titled “Keep artifacts useful”Reports should help a teammate understand failure without rerunning the test immediately. Use screenshots, traces, logcat, and summaries deliberately.
Keep mobile setup explicit
Section titled “Keep mobile setup explicit”For Android tests, store app package, activity, ADB path, and device serial in config or environment variables. Do not hardcode machine-specific paths in test files.
Review stale automation regularly
Section titled “Review stale automation regularly”Remove obsolete tests, outdated workarounds, and helpers that no longer communicate product behavior.
Contributing
Section titled “Contributing”Contributions should be focused and easy to review.
Recommended workflow:
- Fork the repository.
- Create a branch from
master. - Use branch names such as
feat/<short-description>,fix/<short-description>, orchore/<short-description>. - Make focused changes.
- Avoid new dependencies unless there is a clear reason.
- Run tests.
- Open a pull request against
master.
Run tests:
node --test tests/Code standards:
- Plain JavaScript.
- CommonJS modules.
- Node.js 18 compatible.
- No transpilation required.
- No build step required for core package changes.
- Modules should have a single, clearly named responsibility.
- Prefer readable code over clever abstractions.
- Add tests for behavior changes unless the change is purely cosmetic.
Security issues should not be reported in public issues. Use a private security advisory or contact the maintainer directly.
License and Attribution
Section titled “License and Attribution”OrbitTest is distributed under the Apache License 2.0.
Orbittest Studio is distributed under the MIT License.
Forks and modified versions are permitted under the applicable license, but they must preserve copyright, license, and notice attribution. They must not imply affiliation with the official project.
The OrbitTest name, logo, and branding are governed by the project trademark guidelines.
Copyright 2026 Abhay.