This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
An Azure DevOps / Visual Studio Team Services (VSTS) extension that integrates
TestingBot (Selenium/Appium cloud grid + tunnel) into
Azure Pipelines builds and releases. It ships as a .vsix package built from
this repo. vss-extension.json is the extension manifest and the source of truth
for what the package contains and contributes.
Requires Node.js >= 20. The build is npm-scripts + TypeScript + webpack 5 (no gulp).
npm install # root build deps
npm run compile # tsc: compile each task's index.ts -> index.js
npm test # compile + mocha (task-lib mock-run/mock-test)
npm run package # full pipeline -> ./Packages/*.vsixnpm run package chains: clean -> compile (tsc per task, emitting
index.js next to index.ts) -> build (webpack: bundle the tab scripts + copy
static files, including the compiled task index.js, into dist/) -> deps
(scripts/install-task-deps.js runs npm ci --omit=dev into each dist/tb-*
that has a package.json) -> stamp (scripts/stamp-version.js) -> create
(tfx extension create). A precompile hook (scripts/ensure-task-src-deps.js)
installs each task's source node_modules so tsc can resolve library types.
Versioning: package.json's version is the single source of truth. The stamp
step writes it into dist/vss-extension.json (flat semver) and both
dist/tb-*/task.json (version.Major/Minor/Patch). Source manifests keep a
placeholder version — only the dist copies are stamped, so don't rely on the
version in the checked-in vss-extension.json / task.json.
Tests: npm test compiles and runs the mocha suites under tb-*/tests/ (they
use azure-pipelines-task-lib's mock-run/mock-test; MockTestRunner
downloads the Node20 runtime on first run, so the first npm test is slow).
Lint: npm run lint runs ESLint (flat config in eslint.config.mjs,
typescript-eslint for the .ts tasks; the legacy tab scripts are linted
leniently). CI (.github/workflows/build.yml) runs lint + tests + a full
package build on every PR targeting master.
The extension contributes four server-side pieces plus one endpoint type,
declared in vss-extension.json under contributions:
-
tb-main/— Pipeline task "TestingBot Configuration" (TBMain). TypeScript task (index.ts) that reads a TestingBot service-endpoint credential, exportsTB_KEY/TB_SECRET/SELENIUM_HOST/etc. as pipeline variables (the secret ones viasetSecret+ a secret variable) so downstream test steps can reach the grid, and optionally launches TestingBot Tunnel via thetestingbot-tunnel-launchernpm package (which downloads/caches the current tunnel jar at runtime — nothing is bundled). It writestestingbot.jsonand attaches it to the build viatask.addattachmentwith typeTestingBotBuildResult— this attachment is how the results tab (below) later finds the build's credentials and build name. The credentials still travel in that attachment because the tab needs them to sign TestingBot/minishare URLs; removing them from the browser (finding #1) is the Phase 3 tab redesign. -
tb-stop-tunnel/— Pipeline task "Stop TestingBot Tunnel" (TBStopTunnel). TypeScript task (index.ts) that kills the tunnel process using the PID thattb-mainstored in theTB_TUNNEL_PIDvariable (the launcher's in-processkillAsynccan't reach a tunnel started by a different task). Add this task after your test steps to tear the tunnel down. -
tb-upload-app/— Pipeline task "Upload App to TestingBot Storage" (TBUploadApp). TypeScript task (index.ts) that reads the TestingBot service-endpoint credential and uploads a native app build (.apk/.aab/.ipa/.zip) to TestingBot Storage viaPOST https://api.testingbot.com/v1/storage, then exports the returnedtb://<appkey>as a pipeline variable (defaultTB_APP_URL) for a downstream Appium step'sappcapability. With theoverwritetoggle it POSTs to/v1/storage/<appkey>instead, keeping the sametb://URL across builds (the "always upload the latest build" CI pattern). The multipart upload uses Node 20's globalfetch/FormData/Blob, so its only runtime dependency isazure-pipelines-task-lib(installed intodist/tb-upload-app/node_modulesby thedepsstep, liketb-main). -
tb-build-info/— A build-results web tab (infoTab.html+scripts/info.js) shown inside the Azure DevOps build view. It reads theTestingBotBuildResultattachment left bytb-main, calls the TestingBot API through the extension's service-endpoint data source (getBuildFullJobs), and renders a paginated table of test results. Clicking a test opensembedDialog.html(viascripts/dialog.js) which iframes the TestingBot mini test view. -
tb-endpoint-type(manifest-only) — Defines the "TestingBot Credentials" service endpoint (basic auth, key/secret) and thegetBuildFullJobsdata source that proxieshttps://api.testingbot.com/v1/builds/{{build}}.
Data flow across the pieces: tb-main (build agent) → build attachment
TestingBotBuildResult → tb-build-info tab (browser) → TestingBot API via the
service endpoint. The tab never sees raw credentials outside that attachment.
-
Task code runs on the build agent under the
Node20_1handler. All three tasks are TypeScript (index.tscompiled toindex.jsin place).tb-main/index.tsusesazure-pipelines-task-lib/task(tl.getInput,tl.getBoolInput,tl.setVariable,tl.setSecret,tl.command,tl.setResult) andtestingbot-tunnel-launcher. It declares those runtime deps intb-main/package.json; they are NOT committed — thedepsbuild step installs them intodist/tb-main/node_modulesso the task is self-contained.tb-stop-tunnel/index.tsmust stay dependency-free — it readsTB_TUNNEL_PIDdirectly fromprocess.env, callsprocess.kill, and emits the raw##vso[task.complete …]logging command itself. Do NOT addazure-pipelines-task-lib(or any runtime dependency) to the stop task; itspackage.jsonhas nodependencies, so nothing is installed intodist/tb-stop-tunnel.tb-upload-app/index.tsusesazure-pipelines-task-lib/task(declared intb-upload-app/package.json, installed intodist/tb-upload-app/node_modulesby thedepsstep) and Node 20's globalfetch/FormData/Blobfor the multipart upload — no HTTP/multipart library is bundled.
-
Web/tab code (
tb-build-info/scripts/*.ts) runs in the browser inside Azure DevOps. It is TypeScript usingazure-devops-extension-sdk(v4) +azure-devops-extension-api(v5) —SDK.init/SDK.ready/SDK.getService,BuildRestClientfor attachments,ServiceEndpointRestClientfor the data source,IHostPageLayoutService.openCustomDialogfor the embed dialog. webpack (webpack.config.js) bundles it self-contained (the SDK/API andmd5are bundled in, not external) via@babel/preset-env+@babel/preset-typescript; the two entry points (info,dialog) emit todist/tb-build-info/scripts/.infoTab.html/embedDialog.htmlload those bundles directly with a plain<script>— there is noVSS.SDK.jsand no AMD loader anymore. babel only strips types, sonpm run typecheck(tb-build-info/tsconfig.json) type-checks the tab separately.
webpack.config.js does the bundle plus a copy-webpack-plugin pass that copies
the static files into dist/: images, overview.md, vss-extension.json, the
tb-main / tb-stop-tunnel folders, and everything in tb-build-info except
scripts/** (those are bundled, not copied).
bin/upload_all.js is a helper that uploads the tb-* build task definitions
directly to a collection via tfx-cli (separate from packaging the extension).
MODERNIZATION.md holds the full modernization roadmap. This build/task rework
is Phase 1 of it; Phases 2-4 (TypeScript tasks, runtime tunnel download, secret
hygiene, SDK migration, CI/publishing) are still pending.