testera-sdk
v0.2.0
Published
Official TypeScript SDK for the Testera test-management API
Downloads
48
Maintainers
Readme
Testera TypeScript SDK
Official TypeScript/JavaScript client for the Testera test-management API. Fully typed, zero runtime dependencies, works in Node.js 18+ (and any runtime with fetch).
Installation
npm install testera-sdkQuick start
import { TesteraClient } from "testera-sdk";
const testera = new TesteraClient({
apiKey: process.env.TESTERA_API_KEY, // "tk_..." — create one under Settings > API Keys
});
const projects = await testera.projects.list();
console.log(projects.map((p) => p.name));Or build the client from environment variables (TESTERA_API_BASE_URL, TESTERA_API_KEY, TESTERA_EMAIL, TESTERA_PASSWORD):
import { createClientFromEnv } from "testera-sdk";
const testera = createClientFromEnv();Authentication
The SDK supports three credential styles, in order of recommendation:
| Method | Config | Notes |
|--------|--------|-------|
| API key | { apiKey: "tk_..." } | Best for CI and machine-to-machine integrations. |
| Pre-issued JWT | { apiKey: "<jwt>" } | Any non-tk_ token is sent as a Bearer JWT. |
| Email + password | { email, password } | The SDK logs in automatically and caches the JWT (7-day TTL). Accounts with MFA enabled must use an API key. |
All requests send Authorization: Bearer <token>. Tenant context is derived from the authenticated user — no extra headers needed.
Configuration
const testera = new TesteraClient({
baseUrl: "http://localhost:3001/api", // default: https://app.testera.io/api
apiKey: "tk_...",
timeoutMs: 15_000, // default 30000; 0 disables
defaultHeaders: { "X-My-Header": "1" },
fetch: customFetch, // e.g. for proxies or testing
});Resources
| Property | Endpoints covered |
|----------|-------------------|
| testera.projects | list, create, update, delete |
| testera.testCases | list, create, update, assign, delete |
| testera.testPlans | list, get, create, update, delete |
| testera.testRuns | list, get, create, update, recordResults, updateAssignments, compare, compareEnvironments, archive, unarchive, delete |
| testera.environments | list, get, create, update, delete |
| testera.testLabels | list, create, update, delete |
| testera.tasks | list, create, update, delete |
| testera.documents | list, get, create, update, delete |
| testera.users | me, updateMe, login, signup, changePassword, list, listAssignees, create, update, delete |
| testera.apiKeys | list, create, rename, revoke |
Plus testera.health() and a raw testera.request(path, options) escape hatch for anything else (uploads, AI endpoints, audit logs, integrations, admin routes).
Examples
Create a test case
const testCase = await testera.testCases.create({
title: "User can log in with valid credentials",
project: "Web App", // project NAME, not ID
priority: "High",
category: "Functional",
steps: [
{ step: "Navigate to /login", expectedResult: "Login form is shown" },
{ step: "Submit valid credentials", expectedResult: "Redirected to dashboard" },
],
});Run a test plan and report results (e.g. from CI)
// 1. Start a run
const run = await testera.testRuns.create({
name: `CI run ${new Date().toISOString()}`,
projectId: project._id,
testPlanId: plan._id,
environmentId: staging._id, // optional
});
// 2. Report results (up to 100 per call) and finalize
await testera.testRuns.recordResults(
run._id,
[
{ testCaseId: "665f...", status: "passed" },
{ testCaseId: "6660...", status: "failed", notes: "Timeout on step 3" },
],
{ finalize: true },
);Compare two runs
const diff = await testera.testRuns.compare(runA._id, runB._id);
for (const c of diff.cases) {
if (c.change === "regressed") console.log(`REGRESSION: ${c.title}`);
}Create a document (rich text or imported Markdown/HTML)
// Plain rich-text document
const doc = await testera.documents.create({
title: "Release notes 1.2",
content: "<h1>Release 1.2</h1><p>Highlights…</p>",
});
// Import an HTML file, preserving the original source
import { readFile } from "node:fs/promises";
const html = await readFile("User_Journey_Book.html", "utf8");
await testera.documents.create({
title: "User Journey Book",
sourceFormat: "html",
rawSource: html,
originalFileName: "User_Journey_Book.html",
});
// List omits bodies; fetch by ID for content/rawSource
const docs = await testera.documents.list({ q: "journey" });
const full = await testera.documents.get(docs[0]._id);Filter and search
const myOpenTasks = await testera.tasks.list({ assignedTo: "me", done: false });
const stagingRuns = await testera.testRuns.list({
environmentId: staging._id,
archived: false,
q: "smoke",
limit: 50,
});Error handling
Every non-2xx response throws a TesteraApiError with the HTTP status and parsed body:
import { TesteraApiError } from "testera-sdk";
try {
await testera.projects.create({ name: "New Project" });
} catch (err) {
if (err instanceof TesteraApiError) {
if (err.isAuthError) {
// 401 — invalid or missing token
} else if (err.isSubscriptionRequired) {
// 402 — active subscription needed for writes
} else if (err.isForbidden) {
// 403 — insufficient role (Viewer vs Editor/Admin)
}
console.error(err.status, err.message, err.body?.code);
}
}Common error codes in err.body.code: email_not_verified, seat_limit_reached, google_auth_required, mfa_session_invalid.
Roles and permissions
Testera roles gate write access:
- Viewer — read-only.
- Editor — create/update/delete projects, test cases, plans, and runs.
- Admin — everything, plus environments, labels, users, integrations, and audit logs. Only Admins see secret environment variable values (
environments.list({ revealSecrets: true })).
Most write endpoints also require an active subscription (402 otherwise).
Development
npm install
npm run typecheck # tsc --noEmit
npm test # vitest
npm run build # tsup → dist/ (ESM + CJS + .d.ts)License
MIT
