UNPKG

itdoc

Version:

Test-driven documentation for RESTful services

299 lines (286 loc) 10.4 kB
declare enum HttpMethod { GET = "GET", POST = "POST", PUT = "PUT", DELETE = "DELETE", PATCH = "PATCH", HEAD = "HEAD", OPTIONS = "OPTIONS", TRACE = "TRACE", CONNECT = "CONNECT" } declare enum HttpStatus { CONTINUE = 100, SWITCHING_PROTOCOLS = 101, PROCESSING = 102, EARLY_HINTS = 103, OK = 200, CREATED = 201, ACCEPTED = 202, NON_AUTHORITATIVE_INFORMATION = 203, NO_CONTENT = 204, RESET_CONTENT = 205, PARTIAL_CONTENT = 206, MULTI_STATUS = 207, ALREADY_REPORTED = 208, IM_USED = 226, MULTIPLE_CHOICES = 300, MOVED_PERMANENTLY = 301, FOUND = 302, SEE_OTHER = 303, NOT_MODIFIED = 304, USE_PROXY = 305, TEMPORARY_REDIRECT = 307, PERMANENT_REDIRECT = 308, BAD_REQUEST = 400, UNAUTHORIZED = 401, PAYMENT_REQUIRED = 402, FORBIDDEN = 403, NOT_FOUND = 404, METHOD_NOT_ALLOWED = 405, NOT_ACCEPTABLE = 406, PROXY_AUTHENTICATION_REQUIRED = 407, REQUEST_TIMEOUT = 408, CONFLICT = 409, GONE = 410, LENGTH_REQUIRED = 411, PRECONDITION_FAILED = 412, PAYLOAD_TOO_LARGE = 413, URI_TOO_LONG = 414, UNSUPPORTED_MEDIA_TYPE = 415, RANGE_NOT_SATISFIABLE = 416, EXPECTATION_FAILED = 417, IM_A_TEAPOT = 418, MISDIRECTED_REQUEST = 421, UNPROCESSABLE_ENTITY = 422, LOCKED = 423, FAILED_DEPENDENCY = 424, TOO_EARLY = 425, UPGRADE_REQUIRED = 426, PRECONDITION_REQUIRED = 428, TOO_MANY_REQUESTS = 429, REQUEST_HEADER_FIELDS_TOO_LARGE = 431, UNAVAILABLE_FOR_LEGAL_REASONS = 451, INTERNAL_SERVER_ERROR = 500, NOT_IMPLEMENTED = 501, BAD_GATEWAY = 502, SERVICE_UNAVAILABLE = 503, GATEWAY_TIMEOUT = 504, HTTP_VERSION_NOT_SUPPORTED = 505, VARIANT_ALSO_NEGOTIATES = 506, INSUFFICIENT_STORAGE = 507, LOOP_DETECTED = 508, NOT_EXTENDED = 510, NETWORK_AUTHENTICATION_REQUIRED = 511 } type FIELD_TYPES = string | number | boolean | object | null | Record<string, string | number | boolean | object | null | DSLField> | FIELD_TYPES[]; /** * DSL Field interface * - example can be a value or value validation function. */ interface DSLField<T extends FIELD_TYPES = FIELD_TYPES> { readonly description: string; readonly example: T | ((value: T) => void); readonly required: boolean; } interface DSLRequestFile { readonly description: string; readonly file: { path?: string; buffer?: Buffer; stream?: NodeJS.ReadableStream; }; readonly opts: { contentType: string; filename?: string; }; } /** * DSL Helper Functions * - DSL Field creation function * @param {string} description Field description to be displayed in documentation * @param {T | (value: T) => void} example Example value, or a validator that receives the value * @param {boolean} required Whether the field is required * @returns {DSLField<FIELD_TYPES>} DSL Field interface */ declare function field<T extends FIELD_TYPES>(description: string, example: T | ((value: T) => void), required?: boolean): DSLField<FIELD_TYPES>; type PATH_PARAM_TYPES = string | number; type QUERY_PARAM_TYPES = string | number | boolean; /** * Defines configuration values set for each test case. */ interface TestCaseConfig { /** * Options for API documentation */ apiOptions?: ApiDocOptions; pathParams?: Record<string, DSLField<PATH_PARAM_TYPES> | PATH_PARAM_TYPES>; queryParams?: Record<string, DSLField<QUERY_PARAM_TYPES> | QUERY_PARAM_TYPES>; requestBody?: Record<string, DSLField | FIELD_TYPES>; requestHeaders?: Record<string, DSLField<string> | string>; requestFile?: DSLRequestFile; expectedStatus?: HttpStatus | number; expectedResponseBody?: Record<string, DSLField>; expectedResponseHeaders?: Record<string, DSLField<string> | string>; prettyPrint?: boolean; } /** * Abstract class containing common settings for builder classes under test-builders. * @see https://github.com/do-pa/itdoc/issues/10 */ declare abstract class AbstractTestBuilder { protected readonly config: TestCaseConfig; protected readonly method: HttpMethod; protected readonly url: string; protected readonly app: any; constructor(defaults: TestCaseConfig | undefined, method: HttpMethod, url: string, app: any); } /** * Test result interface * * This interface contains information for capturing API test results. * @property {HttpMethod} method - HTTP method (e.g., GET, POST, etc.). * @property {string} url - Request URL. * @property {ApiDocOptions} options - API documentation generation options. * @property {object} request - Request-related information. * @property {unknown} [request.body] - Request body (optional). * @property {Record<string, string | unknown>} [request.headers] - Request headers (optional). * @property {Record<string, string | unknown>} [request.queryParams] - URL query parameters (optional). * @property {Record<string, string | unknown>} [request.pathParams] - URL path parameters (optional). * @property {object} response - Response-related information. * @property {number} response.status - HTTP response status code. * @property {unknown} [response.body] - Response body (optional). * @property {Record<string, string | unknown>} [response.headers] - Response headers (optional). * @property {string} [testSuiteDescription] - Test context description. For example, * the "test context" part in itDoc("test context", () => { ... }). */ interface TestResult { method: HttpMethod; url: string; options: ApiDocOptions; request: { file?: DSLRequestFile; body?: unknown; headers?: Record<string, string | unknown>; queryParams?: Record<string, string | unknown>; pathParams?: Record<string, string | unknown>; }; response: { status: number; body?: unknown; headers?: Record<string, string | unknown>; }; testSuiteDescription?: string; } /** * Builder class for setting result values to validate API responses. */ declare class ResponseBuilder extends AbstractTestBuilder { status(status: HttpStatus | number): this; header(headers: Record<string, string | DSLField<string>>): this; body(body: Record<string, DSLField>): this; private runTest; private prepareHeadersForCollector; then<TResult1 = TestResult, TResult2 = never>(resolve?: ((value: TestResult) => TResult1 | PromiseLike<TResult1>) | null, reject?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | null): Promise<TResult1 | TResult2>; } interface FileDescriptor { readonly path?: string; readonly buffer?: Buffer; readonly stream?: NodeJS.ReadableStream; readonly filename?: string; readonly contentType?: string; } /** * Builder class for setting API request information. */ declare class RequestBuilder extends AbstractTestBuilder { /** * Sets headers to be used in requests. Header names are normalized to lowercase. * @param {Record<string, DSLField<string>>} headers Headers to be used in requests * @returns {this} Request builder instance */ header(headers: Record<string, DSLField<string>>): this; /** * Sets the request body as a raw file (NOT multipart/form-data). * * Two invocation styles are supported: * 1. Shorthand – `req().file("description", { path | buffer | stream, filename?, contentType? })` * 2. Advanced – pass a custom {@link DSLRequestFile} object (legacy support). * * The request is mutually exclusive with {@link body()}. */ file(description: string, descriptor: FileDescriptor): this; file(requestFile: DSLRequestFile): this; private normalizeFileArguments; private isReadableStream; private applyFile; /** * Sets the request body. * @param {Record<string, DSLField<FIELD_TYPES> | FIELD_TYPES>} body Request body * @returns {this} Request builder instance */ body(body: Record<string, DSLField<FIELD_TYPES> | FIELD_TYPES>): this; /** * Sets query parameters to be used in requests. * @param {Record<string, any>} params Query parameters to be used in requests * @returns {this} Request builder instance */ queryParam(params: Record<string, any>): this; /** * Sets path parameters to be used in requests. * @param {Record<string, any>} params Path parameters to be used in requests * @returns {this} Request builder instance */ pathParam(params: Record<string, any>): this; /** * Creates a ResponseBuilder instance. * @returns {ResponseBuilder} Response builder instance */ res(): ResponseBuilder; } /** * The RootBuilder class is the starting point for API testing. */ declare class RootBuilder extends AbstractTestBuilder { /** * Sets the prettyPrint configuration value to true. */ prettyPrint(): this; req(): RequestBuilder; } /** * Option interface to pass to Describe API */ declare class ItdocBuilderEntry { readonly method: HttpMethod; readonly url: string; readonly options: ApiDocOptions; readonly app: unknown; constructor(method: HttpMethod, url: string, options: ApiDocOptions, app: unknown); test(): RootBuilder; } /** * Option interface to pass to Describe API * @param summary One-line API summary * @param tag API tag * @param description Detailed API description */ interface ApiDocOptions { summary?: string; tag?: string; description?: string; defaults?: TestCaseConfig; } /** * Describe function for API specification * @param {HttpMethod} method HTTP method * @param {string} url API URL * @param {ApiDocOptions} options API documentation options * @param {unknown} app Express app instance (used for supertest creation) * @param {Function} callback API test function * @returns {void} * @throws {Error} When required parameters are missing */ declare const describeAPI: (method: HttpMethod, url: string, options: ApiDocOptions, app: unknown, callback: (apiDoc: ItdocBuilderEntry) => void) => void; declare const itDoc: (description: string, testFn: () => Promise<void> | Promise<TestResult> | void | TestResult) => void; export { type ApiDocOptions, HttpMethod, HttpStatus, describeAPI, field, itDoc };