test() API
Use test() when you need raw TypeScript without the structure that contracts
and workflows intentionally add.
Contracts are built on the same runner and evidence model as test(), so this
is not a lesser API. It is the imperative surface for setup-heavy checks,
diagnostics, custom polling, unusual flows, or behavior that should not become a
projected contract.
Simple test
For real projects, create shared clients with configure() and import those
clients into checks. That keeps runtime setup out of each individual check.
import { configure, test } from "@glubean/sdk";
const { http } = configure({
http: {
prefixUrl: "{{BASE_URL}}",
},
});
export const login = test("login", async (ctx) => {
const res = await http.post("login", {
json: { username: "demo", password: "demo" },
});
ctx.expect(res).toHaveStatus(200);
});Multi-step flow
Use the builder API when steps depend on shared state or when cleanup matters:
type CheckoutState = {
cartId: string;
};
const { http } = configure({
http: {
prefixUrl: "{{BASE_URL}}",
},
});
export const checkout = test<CheckoutState>("checkout-flow")
.meta({ tags: ["e2e", "critical"] })
.setup(async (ctx) => {
const cart = await http.post("carts").json<{ id: string }>();
return { cartId: cart.id };
})
.step("add item", async (ctx, state) => {
await http.post(`carts/${state.cartId}/items`, {
json: { productId: "product-123" },
});
return state;
})
.teardown(async (ctx, state) => {
await http.delete(`carts/${state.cartId}`);
});The .build() call is optional. The builder auto-finalizes via microtask after all synchronous chaining completes.
Metadata
Use metadata when the test needs tags, a clearer name, or a custom timeout:
export const health = test({
id: "health",
name: "API health",
tags: ["smoke"],
timeout: 10000,
}, async (ctx) => {
ctx.expect(await http.get("health")).toHaveStatus(200);
});Reusable step sequences
Use .use() to apply reusable builder transforms, and .group() to visually group steps in reports:
import { test, type TestBuilder } from "@glubean/sdk";
// TestBuilder<T> generic = accumulated state from previous steps (input constraint)
// unknown = no dependency on prior state; use { token: string } if this transform needs a token
const withAuth = (b: TestBuilder<unknown>) => b
.step("login", async (ctx) => {
const data = await http.post("login", { json: creds }).json<{ token: string }>();
return { token: data.token };
});
export const testA = test("test-a").use(withAuth).step("act", async (ctx, { token }) => { /* ... */ });
export const testB = test("test-b").group("auth", withAuth).step("verify", async (ctx, { token }) => { /* ... */ });Focus and skip
export const onlyThis = test.only("only-this", async (ctx) => {
// Only this test runs (local debugging)
});
export const skipThis = test.skip("skip-this", async (ctx) => {
// This test is excluded
});Builder mode also supports .only() and .skip():
export const focused = test("focused").only().step("do it", async (ctx) => { /* ... */ });