@prismatic-io/spectral
Version:
Utility library for building Prismatic connectors and code-native integrations
298 lines (297 loc) • 18.5 kB
TypeScript
/**
* This module provides functions to help developers unit
* test custom components prior to publishing them. For
* information on unit testing, check out our docs:
* https://prismatic.io/docs/custom-connectors/unit-testing/
*/
import type { ActionLogger, Component, ConnectionValue, DataSourceContext, DataSourceResult, ActionPerformReturn as ServerActionPerformReturn, TriggerPayload, TriggerResult } from "./serverTypes";
import type { ActionContext, ActionDefinition, ActionInputParameters, ComponentManifest, ConfigVarResultCollection, ConnectionDefinition, DataSourceDefinition, DataSourceType, Flow, Inputs, ActionPerformReturn as InvokeActionPerformReturn, DataSourceResult as InvokeDataSourceResult, TriggerResult as InvokeTriggerResult, TriggerDefinition, TriggerEventFunctionReturn } from "./types";
/**
* Create a test connection to use when testing your custom component locally.
* This builds a `ConnectionValue` from your connection definition and
* test credential values.
*
* @param connectionDef The connection definition (only `key` is used).
* @param values A record of field values for the connection (e.g. `apiKey`, `baseUrl`).
* @param tokenValues Optional OAuth 2.0 token values (e.g. `access_token`, `refresh_token`).
* @param displayName Optional display name for the connection config variable.
* @returns A `ConnectionValue` object suitable for use in test invocations.
* @see {@link https://prismatic.io/docs/custom-connectors/unit-testing/#providing-test-connection-inputs-to-an-action-test | Test Connection Inputs}
* @example
* import { testing } from "@prismatic-io/spectral";
* import { apiKeyConnection } from "./connections";
*
* const testConnection = testing.createConnection(apiKeyConnection, {
* apiKey: "test-api-key-123",
* baseUrl: "https://api.acme.com/v2",
* });
*
* // Use with testing.invoke()
* const { result } = await testing.invoke(myAction, {
* connection: testConnection,
* });
*/
export declare const createConnection: <T extends ConnectionDefinition>({ key }: T, values: Record<string, unknown>, tokenValues?: Record<string, unknown>, displayName?: string) => ConnectionValue;
export declare const defaultConnectionValueEnvironmentVariable = "PRISMATIC_CONNECTION_VALUE";
/**
* Source a test connection from an environment variable for local testing.
* The environment variable should contain a JSON-serialized connection value.
* Defaults to reading from `PRISMATIC_CONNECTION_VALUE`.
*
* @param envVarKey The name of the environment variable to read. Defaults to `"PRISMATIC_CONNECTION_VALUE"`.
* @returns A `ConnectionValue` parsed from the environment variable.
* @see {@link https://prismatic.io/docs/custom-connectors/unit-testing/#access-connections-for-local-testing | Access Connections for Local Testing}
* @example
* import { testing } from "@prismatic-io/spectral";
*
* // Reads from PRISMATIC_CONNECTION_VALUE env var (default)
* const conn = testing.connectionValue();
*
* // Or specify a custom env var
* const conn = testing.connectionValue("MY_ACME_CONNECTION");
*/
export declare const connectionValue: (envVarKey?: string) => ConnectionValue;
/**
* Pre-built mock of ActionLogger. All log methods (`info`, `warn`, `error`, etc.)
* are Jest spies, so you can assert that your actions log the expected messages.
*
* @returns A mock `ActionLogger` with Jest spy methods.
* @see {@link https://prismatic.io/docs/custom-connectors/unit-testing/#verifying-correct-logging-in-action-tests | Verifying Logging}
* @example
* import { testing } from "@prismatic-io/spectral";
*
* const logger = testing.loggerMock();
* // Pass logger in context, then assert:
* expect(logger.info).toHaveBeenCalledWith("Processing started");
*/
export declare const loggerMock: () => ActionLogger;
/**
* Creates basic component action mocks based on a code-native integration's
* component registry. Each action mock returns the action's `examplePayload`
* by default. Pass overrides in the second argument to customize specific mocks.
*
* @param registry The component registry (or subset) to mock.
* @param mocks Optional overrides for specific component actions.
* @returns An object of mocked component actions, suitable for use in test context.
* @see {@link https://prismatic.io/docs/custom-connectors/unit-testing/ | Unit Testing}
* @example
* import { createMockContextComponents } from "@prismatic-io/spectral/dist/testing";
* import { componentRegistry } from "./componentRegistry";
*
* // Default mocks (uses examplePayload from the manifest)
* const components = createMockContextComponents(componentRegistry);
*
* // With custom overrides
* const components = createMockContextComponents(componentRegistry, {
* actions: {
* slack: {
* postMessage: () => Promise.resolve({ data: { ok: true } }),
* },
* },
* });
*/
export declare const createMockContextComponents: <TMockAction extends () => Promise<any>>(registry: Record<string, {
actions: ComponentManifest["actions"];
}>, mocks?: {
actions: Record<string, Record<string, TMockAction>>;
}) => Record<string, Record<string, TMockAction>>;
/**
* The type of data returned by an `invoke()` function used for unit testing component actions and triggers.
*/
interface InvokeReturn<ReturnData> {
result: ReturnData;
loggerMock: ActionLogger;
}
/**
* Invokes a custom component action's `perform` function within a test harness.
* Returns both the action result and a mock logger for asserting logging behavior.
*
* @param actionDef The action definition to test (only `perform` is used).
* @param params Input parameter values to pass to the action's `perform` function.
* @param context Optional partial context overrides (e.g. custom `configVars` or `instanceState`).
* @returns An object with `result` (the action's return value) and `loggerMock` (for asserting logs).
* @see {@link https://prismatic.io/docs/custom-connectors/unit-testing/ | Unit Testing}
* @example
* import { testing } from "@prismatic-io/spectral";
* import { myAction } from "./actions";
*
* it("should return items", async () => {
* const { result, loggerMock } = await testing.invoke(myAction, {
* connection: testConnection,
* limit: "10",
* });
*
* expect(result.data).toHaveProperty("items");
* expect(loggerMock.info).toHaveBeenCalled();
* });
*/
export declare const invoke: <TInputs extends Inputs, TConfigVars extends ConfigVarResultCollection, TAllowsBranching extends boolean, TReturn extends InvokeActionPerformReturn<TAllowsBranching, unknown>>({ perform }: ActionDefinition<TInputs, TConfigVars, TAllowsBranching, TReturn>, params: ActionInputParameters<TInputs>, context?: Partial<ActionContext<TConfigVars>>) => Promise<InvokeReturn<TReturn>>;
export declare const defaultTriggerPayload: () => TriggerPayload;
/**
* Invokes a custom component trigger's `perform` function within a test harness.
* Provides a default trigger payload that can be overridden. Returns both the
* trigger result and a mock logger for asserting logging behavior.
*
* @param triggerDef The trigger definition to test (only `perform` is used).
* @param context Optional partial context overrides.
* @param payload Optional partial trigger payload overrides (merged with defaults).
* @param params Optional input parameter values for the trigger.
* @returns An object with `result` (the trigger's return value) and `loggerMock`.
* @see {@link https://prismatic.io/docs/custom-connectors/unit-testing/ | Unit Testing}
* @example
* import { testing } from "@prismatic-io/spectral";
* import { webhookTrigger } from "./triggers";
*
* it("should process the webhook payload", async () => {
* const { result } = await testing.invokeTrigger(
* webhookTrigger,
* undefined, // use default context
* {
* body: { data: JSON.stringify({ event: "created" }) },
* headers: { "x-webhook-secret": "test-secret" },
* },
* { secret: "test-secret" },
* );
*
* expect(result.payload).toBeDefined();
* });
*/
export declare const invokeTrigger: <TInputs extends Inputs, TConfigVars extends ConfigVarResultCollection, TAllowsBranching extends boolean, TResult extends InvokeTriggerResult<TAllowsBranching, TriggerPayload>>({ perform }: TriggerDefinition<TInputs, TConfigVars, TAllowsBranching, TResult>, context?: Partial<ActionContext<TConfigVars>>, payload?: TriggerPayload, params?: ActionInputParameters<TInputs>) => Promise<InvokeReturn<TResult>>;
/**
* Invokes a custom component data source's `perform` function within a test harness.
* Returns the data source result directly.
*
* @param dataSourceDef The data source definition to test (only `perform` is used).
* @param params Input parameter values to pass to the data source's `perform` function.
* @param context Optional partial context overrides.
* @returns The data source result (e.g. a picklist, JSON form, or other data source type).
* @see {@link https://prismatic.io/docs/custom-connectors/unit-testing/ | Unit Testing}
* @example
* import { testing } from "@prismatic-io/spectral";
* import { selectChannel } from "./dataSources";
*
* it("should return a list of channels", async () => {
* const result = await testing.invokeDataSource(selectChannel, {
* connection: testConnection,
* });
*
* expect(result.result).toContainEqual(
* expect.objectContaining({ label: "General" }),
* );
* });
*/
export declare const invokeDataSource: <TInputs extends Inputs, TConfigVars extends ConfigVarResultCollection, TDataSourceType extends DataSourceType>({ perform }: DataSourceDefinition<TInputs, TConfigVars, TDataSourceType>, params: ActionInputParameters<TInputs>, context?: Partial<DataSourceContext<TConfigVars>>) => Promise<InvokeDataSourceResult<TDataSourceType>>;
type TestConnectionValue = Pick<ConnectionValue, "fields" | "context" | "token" | "key">;
type TestConfigVarValues = Record<string, string | TestConnectionValue>;
type ToTestValues<TConfigVars extends ConfigVarResultCollection> = {
[K in keyof TConfigVars]: TConfigVars[K] extends ConnectionDefinition ? TestConnectionValue : string;
};
/**
* Invokes a code-native integration flow within a test harness. Runs the
* flow's `onTrigger` (if defined) followed by `onExecution`, and returns
* the execution result. Accepts optional config variables, context overrides,
* and a custom trigger payload.
*
* @param flow The flow definition to test.
* @param options Optional config variables, context overrides, and trigger payload.
* @returns An object with `result` (the flow execution return value) and `loggerMock`.
* @see {@link https://prismatic.io/docs/custom-connectors/unit-testing/ | Unit Testing}
* @example
* import { invokeFlow } from "@prismatic-io/spectral/dist/testing";
* import { myFlow } from "./flows";
*
* it("should execute the flow end-to-end", async () => {
* const { result } = await invokeFlow(myFlow, {
* configVars: {
* "Acme API Endpoint": "https://api.acme.com",
* "Acme Connection": {
* fields: { apiKey: "test-key" },
* key: "apiKey",
* },
* },
* payload: {
* body: { data: JSON.stringify({ event: "created" }) },
* },
* });
*
* expect(result.data).toBeDefined();
* });
*/
export declare const invokeFlow: <TInputs extends Inputs, TActionInputs extends Inputs, TConfigVars extends ConfigVarResultCollection = ConfigVarResultCollection, TConfigVarValues extends TestConfigVarValues = ToTestValues<TConfigVars>, TPayload extends TriggerPayload = TriggerPayload, TAllowsBranching extends boolean = boolean, TResult extends InvokeTriggerResult<TAllowsBranching, TPayload> = InvokeTriggerResult<TAllowsBranching, TPayload>>(flow: Flow<TInputs, TActionInputs, TPayload, TAllowsBranching, TResult>, { configVars, context, payload, }?: {
configVars?: TConfigVarValues;
context?: Partial<ActionContext<TConfigVars>>;
payload?: Partial<TriggerPayload>;
}) => Promise<InvokeReturn<InvokeActionPerformReturn<false, unknown>>>;
export declare class ComponentTestHarness<TInputs extends Inputs, TActionInputs extends Inputs, TConfigVars extends ConfigVarResultCollection = ConfigVarResultCollection, TPayload extends TriggerPayload = TriggerPayload, TAllowsBranching extends boolean = boolean, TResult extends InvokeTriggerResult<TAllowsBranching, TPayload> = InvokeTriggerResult<TAllowsBranching, TPayload>, TComponent extends Component<TInputs, TActionInputs, TConfigVars, TPayload, TAllowsBranching, TResult> = Component<TInputs, TActionInputs, TConfigVars, TPayload, TAllowsBranching, TResult>> {
component: TComponent;
constructor(component: TComponent);
private buildParams;
/**
* Source a test connection from an environment variable for local testing. See
* https://prismatic.io/docs/custom-connectors/unit-testing/#access-connections-for-local-testing
*/
connectionValue({ key }: ConnectionDefinition): ConnectionValue;
/**
* Invoke a trigger by its key within a unit test. See
* https://prismatic.io/docs/custom-connectors/unit-testing/
*/
trigger(key: string, payload?: Partial<TPayload>, params?: Record<string, unknown>, context?: Partial<ActionContext<TConfigVars>>): Promise<TriggerResult>;
/**
* Invoke a trigger's onInstanceDeploy function by its key within a unit test. See
* https://prismatic.io/docs/custom-connectors/unit-testing/
*/
triggerOnInstanceDeploy<TConfigVars extends ConfigVarResultCollection>(key: string, params?: Record<string, unknown>, context?: Partial<ActionContext<TConfigVars>>): Promise<TriggerEventFunctionReturn | void>;
/**
* Invoke a trigger's onInstanceDelete function by its key within a unit test. See
* https://prismatic.io/docs/custom-connectors/unit-testing/
*/
triggerOnInstanceDelete<TConfigVars extends ConfigVarResultCollection>(key: string, params?: Record<string, unknown>, context?: Partial<ActionContext<TConfigVars>>): Promise<TriggerEventFunctionReturn | void>;
/**
* Invoke an action by its key within a unit test. See
* https://prismatic.io/docs/custom-connectors/unit-testing/
*/
action<TConfigVars extends ConfigVarResultCollection>(key: string, params?: Record<string, unknown>, context?: Partial<ActionContext<TConfigVars>>): Promise<ServerActionPerformReturn>;
/**
* Invoke a data source by its key within a unit test. See
* https://prismatic.io/docs/custom-connectors/unit-testing/
*/
dataSource<TConfigVars extends ConfigVarResultCollection>(key: string, params?: Record<string, unknown>, context?: Partial<DataSourceContext<TConfigVars>>): Promise<DataSourceResult>;
}
/**
* Create a testing harness to test a custom component's actions, triggers
* and data sources by key. The harness automatically provides default input
* values and mock context.
*
* @param component The compiled component object (the result of calling `component()`).
* @returns A `ComponentTestHarness` instance with `.action()`, `.trigger()`, and `.dataSource()` methods.
* @see {@link https://prismatic.io/docs/custom-connectors/unit-testing/ | Unit Testing}
* @example
* import { testing } from "@prismatic-io/spectral";
* import myComponent from ".";
*
* const harness = testing.createHarness(myComponent);
*
* it("should list items", async () => {
* const result = await harness.action("listItems", {
* connection: testConnection,
* limit: "10",
* });
* expect(result.data).toHaveProperty("items");
* });
*
* it("should handle a webhook trigger", async () => {
* const result = await harness.trigger("webhook", {
* body: { data: '{"event":"created"}' },
* });
* expect(result.payload).toBeDefined();
* });
*/
export declare const createHarness: <TInputs extends Inputs, TActionInputs extends Inputs, TConfigVars extends ConfigVarResultCollection = ConfigVarResultCollection, TPayload extends TriggerPayload = TriggerPayload, TAllowsBranching extends boolean = boolean, TResult extends InvokeTriggerResult<TAllowsBranching, TPayload> = InvokeTriggerResult<TAllowsBranching, TPayload>, TComponent extends Component<TInputs, TActionInputs, TConfigVars, TPayload, TAllowsBranching, TResult> = Component<TInputs, TActionInputs, TConfigVars, TPayload, TAllowsBranching, TResult>>(component: TComponent) => ComponentTestHarness<TInputs, TActionInputs, TConfigVars, TPayload, TAllowsBranching, TResult, TComponent>;
declare const _default: {
loggerMock: () => ActionLogger;
invoke: <TInputs extends Inputs, TConfigVars extends ConfigVarResultCollection, TAllowsBranching extends boolean, TReturn extends InvokeActionPerformReturn<TAllowsBranching, unknown>>({ perform }: ActionDefinition<TInputs, TConfigVars, TAllowsBranching, TReturn>, params: ActionInputParameters<TInputs>, context?: Partial<ActionContext<TConfigVars>>) => Promise<InvokeReturn<TReturn>>;
invokeTrigger: <TInputs extends Inputs, TConfigVars extends ConfigVarResultCollection, TAllowsBranching extends boolean, TResult extends InvokeTriggerResult<TAllowsBranching, TriggerPayload>>({ perform }: TriggerDefinition<TInputs, TConfigVars, TAllowsBranching, TResult>, context?: Partial<ActionContext<TConfigVars>>, payload?: TriggerPayload, params?: ActionInputParameters<TInputs>) => Promise<InvokeReturn<TResult>>;
createHarness: <TInputs extends Inputs, TActionInputs extends Inputs, TConfigVars extends ConfigVarResultCollection = ConfigVarResultCollection, TPayload extends TriggerPayload = TriggerPayload, TAllowsBranching extends boolean = boolean, TResult extends InvokeTriggerResult<TAllowsBranching, TPayload> = InvokeTriggerResult<TAllowsBranching, TPayload>, TComponent extends Component<TInputs, TActionInputs, TConfigVars, TPayload, TAllowsBranching, TResult> = Component<TInputs, TActionInputs, TConfigVars, TPayload, TAllowsBranching, TResult>>(component: TComponent) => ComponentTestHarness<TInputs, TActionInputs, TConfigVars, TPayload, TAllowsBranching, TResult, TComponent>;
invokeDataSource: <TInputs extends Inputs, TConfigVars extends ConfigVarResultCollection, TDataSourceType extends DataSourceType>({ perform }: DataSourceDefinition<TInputs, TConfigVars, TDataSourceType>, params: ActionInputParameters<TInputs>, context?: Partial<DataSourceContext<TConfigVars>>) => Promise<InvokeDataSourceResult<TDataSourceType>>;
};
export default _default;