Component Basics
All external library components follow a common structure and pattern.
Component Structure
Every component has these properties:
interface Component {
id: string; // Unique identifier (kebab-case)
name: string; // Display name in UI
description?: string; // What this component does
enabled: boolean; // Available in test engine
deprecated: boolean; // Hidden from UI but still works
tested: boolean; // Self-declared quality flag
function: Function; // The implementation
}Component Flags
| Flag | Default | Purpose |
|---|---|---|
enabled | false | When true, component is available in test engine and UI |
deprecated | false | When true, hidden from UI but existing tests still work |
tested | false | Self-declared flag indicating component has been tested |
Flag Combinations
| enabled | deprecated | Result |
|---|---|---|
false | false | Not available anywhere |
true | false | Available in UI and engine |
true | true | Works in engine, hidden from UI |
false | true | Not available anywhere |
Export Patterns
Each component type uses a specific export name:
// src/index.ts
export { $$AssertionFunctions } from "./assertions";
export { $$ValueFunctions } from "./generators";
export { $$ApiHooks } from "./hooks";
export { $$Utilities } from "./utils";
export { $$TestCases } from "./test-cases";ID Management
Component IDs must be unique within your library.
// ✅ Good: Descriptive, kebab-case
id: "is-valid-email"
id: "generate-test-user"
id: "auth-before-request"
// ❌ Bad: Generic, could conflict
id: "validate"
id: "generate"
id: "hook1"Tip: Prefix IDs with your domain:
id: "payments-is-valid-card"
id: "auth-generate-jwt"Naming Conventions
| Property | Convention | Example |
|---|---|---|
id | kebab-case | is-valid-user |
name | Title Case | Is Valid User |
| Function name | camelCase | isValidUser |
Type Definitions
The template includes type definitions for components:
// src/types.ts
export interface AssertionFunction {
id: string;
name: string;
description?: string;
enabled: boolean;
deprecated: boolean;
tested: boolean;
function: (value: unknown, expected?: unknown, options?: unknown) => void | Promise<void>;
}
export interface ValueFunction {
id: string;
name: string;
description?: string;
enabled: boolean;
deprecated: boolean;
tested: boolean;
function: (options?: unknown) => unknown | Promise<unknown>;
}
export interface ApiHook {
id: string;
name: string;
description?: string;
enabled: boolean;
deprecated: boolean;
tested: boolean;
type: "beforeRequest" | "afterResponse";
function: () => void | Promise<void>;
}Error Handling
Components should handle errors gracefully:
// ✅ Good: Catches errors, reports them
function: async (value) => {
try {
// Your logic
$addAssertionResult({ passed: true, ... });
} catch (error) {
$addAssertionResult({
passed: false,
message: `Error: ${error.message}`,
...
});
}
}
// ❌ Bad: Throws, crashes the test
function: async (value) => {
const result = riskyOperation(value); // Might throw
$addAssertionResult({ passed: result, ... });
}Accessing Built-in Libraries
All components can use built-in libraries:
function: async (options) => {
// Lodash
const filtered = _.filter(data, predicate);
// Faker
const email = faker.internet.email();
// Zod
const schema = z.object({ id: z.number() });
// Luxon
const now = luxon.DateTime.now();
// HTTP requests
const data = await ky.get(url).json();
}