UNPKG

@jivanf/vest

Version:

Declarative Form Validations Framework

396 lines 15.5 kB
/// <reference types="node" /> import { enforce } from 'n4s'; import { DynamicValue, OneOrMoreOf, Maybe, Nullable, CB, ValueOf } from "vest-utils"; import { InjectionToken } from "@angular/core"; import { TIsolate, IsolateKey } from "vestjs-runtime"; declare enum Severity { WARNINGS = "warnings", ERRORS = "errors" } declare enum TestSeverity { Error = "error", Warning = "warning" } declare const TestStatus: { [x: string]: string; CANCELED: string; FAILED: string; OMITTED: string; PASSING: string; SKIPPED: string; UNTESTED: string; WARNING: string; }; type TestStatus = ValueOf<typeof TestStatus>; type TestFnPayload = { signal: AbortSignal; }; type TestFn = (payload: TestFnPayload) => TestResult; type AsyncTest = Promise<void>; type TestResult = Maybe<AsyncTest | boolean> | void; type WithFieldName<F extends TFieldName = TFieldName> = { fieldName: F; }; type TIsolateTest<F extends TFieldName = TFieldName, G extends TGroupName = TGroupName> = TIsolate<CommonTestFields<F, G> & IsolateTestPayload>; type IsolateTestPayload<F extends TFieldName = TFieldName, G extends TGroupName = TGroupName> = CommonTestFields<F, G> & { severity: TestSeverity; status: TestStatus; asyncTest?: AsyncTest; }; type CommonTestFields<F extends TFieldName = TFieldName, G extends TGroupName = TGroupName> = { message?: Maybe<string>; groupName?: G; fieldName: F; testFn: TestFn; }; /** * Injection token for accessing the current test context in Angular environments. * Used internally by runSyncTest to provide the test object to the test function. */ declare const VEST_TEST_CONTEXT: InjectionToken<TIsolateTest<string, string>>; declare class SummaryFailure<F extends TFieldName, G extends TGroupName> implements WithFieldName<F> { fieldName: F; message: string | undefined; groupName: G | undefined; constructor(fieldName: F, message: string | undefined, groupName: G | undefined); static fromTestObject<F extends TFieldName, G extends TGroupName>(testObject: TIsolateTest<F, G>): SummaryFailure<F & string, G & string>; } interface Done<F extends TFieldName, G extends TGroupName> { (...args: [ cb: (res: SuiteResult<F, G>) => void ]): SuiteRunResult<F, G>; (...args: [ fieldName: F, cb: (res: SuiteResult<F, G>) => void ]): SuiteRunResult<F, G>; } // eslint-disable-next-line max-lines-per-function, max-statements declare function suiteSelectors<F extends TFieldName, G extends TGroupName>(summary: SuiteSummary<F, G>): SuiteSelectors<F, G>; interface SuiteSelectors<F extends TFieldName, G extends TGroupName> { getWarning(): SummaryFailure<F, G> | undefined; getWarning(fieldName: F): string | undefined; getWarning(fieldName?: F): SummaryFailure<F, G> | string | undefined; getError(): SummaryFailure<F, G> | undefined; getError(fieldName: F): string | undefined; getError(fieldName?: F): SummaryFailure<F, G> | string | undefined; getMessage(fieldName: F): string | undefined; getErrors(): FailureMessages; getErrors(fieldName: F): string[]; getErrors(fieldName?: F): string[] | FailureMessages; getWarnings(): FailureMessages; getWarnings(fieldName: F): string[]; getWarnings(fieldName?: F): string[] | FailureMessages; getErrorsByGroup(groupName: G): FailureMessages; getErrorsByGroup(groupName: G, fieldName: F): string[]; getErrorsByGroup(groupName: G, fieldName?: F): string[] | FailureMessages; getWarningsByGroup(groupName: G): FailureMessages; getWarningsByGroup(groupName: G, fieldName: F): string[]; getWarningsByGroup(groupName: G, fieldName?: F): string[] | FailureMessages; hasErrors(fieldName?: F): boolean; hasWarnings(fieldName?: F): boolean; hasErrorsByGroup(groupName: G, fieldName?: F): boolean; hasWarningsByGroup(groupName: G, fieldName?: F): boolean; isTested(fieldName: F): boolean; isPending(fieldName?: F): boolean; isValid(fieldName?: F): boolean; isValidByGroup(groupName: G, fieldName?: F): boolean; } declare class SummaryBase { errorCount: number; warnCount: number; testCount: number; pendingCount: number; } declare class SuiteSummary<F extends TFieldName, G extends TGroupName> extends SummaryBase { [Severity.ERRORS]: SummaryFailure<F, G>[]; [Severity.WARNINGS]: SummaryFailure<F, G>[]; groups: Groups<G, F>; tests: Tests<F>; valid: Nullable<boolean>; } type GroupTestSummary = SingleTestSummary; type Groups<G extends TGroupName, F extends TFieldName> = Record<G, Group<F>>; type Group<F extends TFieldName> = Record<F, GroupTestSummary>; type Tests<F extends TFieldName> = Record<F, SingleTestSummary>; type SingleTestSummary = SummaryBase & { errors: string[]; warnings: string[]; valid: Nullable<boolean>; pendingCount: number; }; type FailureMessages = Record<string, string[]>; type SuiteResult<F extends TFieldName, G extends TGroupName> = SuiteSummary<F, G> & SuiteSelectors<F, G> & { suiteName: SuiteName; }; type SuiteRunResult<F extends TFieldName, G extends TGroupName> = SuiteResult<F, G> & { done: Done<F, G>; }; type SuiteName = Maybe<string>; type TFieldName<T extends string = string> = T; type TGroupName<G extends string = string> = G; type OptionalFields = Record<string, OptionalFieldDeclaration>; type OptionalsInput<F extends TFieldName> = OneOrMoreOf<F> | OptionalsObject<F>; type OptionalsObject<F extends TFieldName> = Record<F, TOptionalRule | any>; type ImmediateOptionalFieldDeclaration = { type: OptionalFieldTypes.CUSTOM_LOGIC; rule: TOptionalRule; applied: boolean; }; type DelayedOptionalFieldDeclaration = { type: OptionalFieldTypes.AUTO; applied: boolean; rule: null; }; type TOptionalRule = DynamicValue<boolean>; type OptionalFieldDeclaration = ImmediateOptionalFieldDeclaration | DelayedOptionalFieldDeclaration; declare enum OptionalFieldTypes { CUSTOM_LOGIC = 0, AUTO = 1 } // @vx-allow use-use declare function optional<F extends TFieldName>(optionals: OptionalsInput<F>): void; declare enum Modes { EAGER = "EAGER", ALL = "ALL", ONE = "ONE" } type TIsolateSuite = TIsolate<{ optional: OptionalFields; }>; type Events = "TEST_RUN_STARTED" | "TEST_COMPLETED" | "ALL_RUNNING_TESTS_FINISHED" | "REMOVE_FIELD" | "RESET_FIELD" | "RESET_SUITE" | "SUITE_RUN_STARTED" | "SUITE_CALLBACK_RUN_FINISHED" | "DONE_TEST_OMISSION_PASS"; type Subscribe = { (event: Events, cb: CB): CB<void>; (cb: CB): CB<void>; }; declare enum FocusModes { ONLY = 0, SKIP = 1 } type FieldExclusion<F extends TFieldName> = Maybe<OneOrMoreOf<F>>; type TIsolateFocused = TIsolate<IsolateFocusedPayload>; type IsolateFocusedPayload = { focusMode: FocusModes; match: FieldExclusion<TFieldName>; matchAll: boolean; }; /** * Adds a field or a list of fields into the inclusion list * * @example * * only('username'); */ // @vx-allow use-use declare function only(match: FieldExclusion<TFieldName> | false): TIsolateFocused; /** * Adds a field or a list of fields into the exclusion list * * @example * * skip('username'); */ // @vx-allow use-use declare function skip(match: FieldExclusion<TFieldName> | boolean): TIsolateFocused; declare function vestTest<F extends TFieldName>(fieldName: F, message: string, cb: TestFn): TIsolateTest; declare function vestTest<F extends TFieldName>(fieldName: F, cb: TestFn): TIsolateTest; declare function vestTest<F extends TFieldName>(fieldName: F, message: string, cb: TestFn, key: IsolateKey): TIsolateTest; declare function vestTest<F extends TFieldName>(fieldName: F, cb: TestFn, key: IsolateKey): TIsolateTest; declare const test: typeof vestTest & { memo: TestMemo<string>; }; type TestMemo<F extends TFieldName> = { (fieldName: F, ...args: ParametersWithoutMessage): TIsolateTest; (fieldName: F, ...args: ParametersWithMessage): TIsolateTest; }; type ParametersWithoutMessage = [ test: TestFn, dependencies: unknown[] ]; type ParametersWithMessage = [ message: string, test: TestFn, dependencies: unknown[] ]; type TTypedMethods<F extends TFieldName, G extends TGroupName> = { include: (fieldName: F) => { when: (condition: F | TDraftCondition<F, G>) => void; }; omitWhen: (conditional: TDraftCondition<F, G>, callback: CB) => void; only: { (item: FieldExclusion<F>): void; }; optional: (optionals: OptionalsInput<F>) => void; skip: { (item: FieldExclusion<F>): void; }; skipWhen: (condition: TDraftCondition<F, G>, callback: CB) => void; test: { (fieldName: F, message: string, cb: TestFn): TIsolateTest; (fieldName: F, cb: TestFn): TIsolateTest; (fieldName: F, message: string, cb: TestFn, key: IsolateKey): TIsolateTest; (fieldName: F, cb: TestFn, key: IsolateKey): TIsolateTest; } & { memo: TestMemo<F>; }; group: { (callback: () => void): TIsolate; (groupName: G, callback: () => void): TIsolate; }; }; type TDraftCondition<F extends TFieldName, G extends TGroupName> = DynamicValue<boolean, [ draft: SuiteResult<F, G> ]>; declare function createSuite<F extends TFieldName = string, G extends TGroupName = string, T extends CB = CB>(suiteName: SuiteName, suiteCallback: T): Suite<F, G, T>; declare function createSuite<F extends TFieldName = string, G extends TGroupName = string, T extends CB = CB>(suiteCallback: T): Suite<F, G, T>; /** * Creates a static suite for server-side validation. * * @param {Function} validationFn - The validation function that defines the suite's tests. * @returns {Function} - A function that runs the validations defined in the suite. * * @example * import { staticSuite, test, enforce } from 'vest'; * * const suite = staticSuite(data => { * test('username', 'username is required', () => { * enforce(data.username).isNotEmpty(); * }); * }); * * suite(data); */ declare function staticSuite<F extends TFieldName = string, G extends TGroupName = string, T extends CB = CB>(suiteName: SuiteName, suiteCallback: T): StaticSuite<F, G, T>; declare function staticSuite<F extends TFieldName = string, G extends TGroupName = string, T extends CB = CB>(suiteCallback: T): StaticSuite<F, G, T>; type StaticSuite<F extends TFieldName = string, G extends TGroupName = string, T extends CB = CB> = (...args: Parameters<T>) => StaticSuiteRunResult<F, G>; type StaticSuiteRunResult<F extends TFieldName = string, G extends TGroupName = string> = Promise<SuiteWithDump<F, G>> & WithDump<SuiteRunResult<F, G> & TTypedMethods<F, G>>; type WithDump<T> = T & { dump: CB<TIsolateSuite>; }; type SuiteWithDump<F extends TFieldName, G extends TGroupName> = WithDump<SuiteResult<F, G>>; type Suite<F extends TFieldName, G extends TGroupName, T extends CB = CB> = ((...args: Parameters<T>) => SuiteRunResult<F, G>) & SuiteMethods<F, G, T>; type SuiteMethods<F extends TFieldName, G extends TGroupName, T extends CB> = { dump: CB<TIsolateSuite>; get: CB<SuiteResult<F, G>>; resume: CB<void, [ TIsolateSuite ]>; reset: CB<void>; remove: CB<void, [ fieldName: F ]>; resetField: CB<void, [ fieldName: F ]>; runStatic: CB<StaticSuiteRunResult<F, G>, Parameters<T>>; subscribe: Subscribe; } & TTypedMethods<F, G> & SuiteSelectors<F, G>; declare function registerReconciler(reconciler: IsolateReconciler): void; type IsolateReconciler = { match(currentNode: TIsolate, historyNode: TIsolate): boolean; reconcile(elecurrentNode: TIsolate, historyNode: TIsolate): TIsolate; }; /** * Iterates over an array of items, allowing to run tests individually per item. * * Requires setting a "key" property on each item tested. * * @example * * each(itemsArray, (item) => { * test(item.name, 'Item value must not be empty', () => { * enforce(item.value).isNotEmpty(); * }, item.id) * }) */ declare function each<T>(list: T[], callback: (arg: T, index: number) => void): void; declare function group<G extends TGroupName>(groupName: G, callback: CB<void>): TIsolate; declare function group(callback: CB<void>): TIsolate; /** * Conditionally includes a field for testing, based on specified criteria. * * @param {string} fieldName - The name of the field to include for testing. * * @example * include('confirm').when('password'); * // Includes the "confirm" field for testing when the "password" field is included * * include('confirm').when(someValue); * // Includes the "confirm" field for testing when the value of `someValue` is true * * include('confirm').when(() => someValue); * // Includes the "confirm" field for testing when the callback function returns true * * include('username').when(result => result.hasErrors('username')); * // Includes the "username" field for testing when there are errors associated with it in the current suite result */ // @vx-allow use-use declare function include<F extends TFieldName, G extends TGroupName>(fieldName: F): { when: (condition: F | TFieldName | TDraftCondition<F, G>) => void; }; /** * Sets the current execution mode for the current suite. * * Supported modes: * - `EAGER` - (default) Runs all tests, but stops on first failure for each given field. * - `ALL` - Runs all tests, regardless of failures. * - `ONE` - Stops suite execution on first failure of any field. * * @example * ```js * import {Modes, create} from 'vest'; * * const suite = create('suite_name', () => { * vest.mode(Modes.ALL); * * // ... * }); * ``` * @param 'ALL' | 'EAGER' | 'ONE' mode - The mode to set. */ // @vx-allow use-use declare function mode(mode: Modes): void; /** * Conditionally omits tests from the suite. * * @example * * omitWhen(res => res.hasErrors('username'), () => { * test('username', 'User already taken', async () => await doesUserExist(username) * }); */ // @vx-allow use-use declare function omitWhen<F extends TFieldName, G extends TGroupName>(conditional: TDraftCondition<F, G>, callback: CB): void; /** * Conditionally skips running tests within the callback. * * @example * * skipWhen(res => res.hasErrors('username'), () => { * test('username', 'User already taken', async () => await doesUserExist(username) * }); */ // @vx-allow use-use declare function skipWhen<F extends TFieldName, G extends TGroupName>(condition: TDraftCondition<F, G>, callback: CB): void; /** * Sets the severity level of a test to `warn`, allowing it to fail without marking the suite as invalid. * Use this function within the body of a test to create warn-only tests. * * @returns {void} * * @example * test('password', 'Your password strength is: WEAK', () => { * warn(); * * enforce(data.password).matches(/0-9/); * }); * * @limitations * - The `warn` function should only be used within the body of a `test` function. * - When using `warn()` in an async test, it should be called in the synchronous portion of the test, not after an `await` call or in the Promise body. * - It is recommended to call `warn()` at the top of the test function. */ // @vx-allow use-use declare function warn(): void; export { createSuite as create, test, group, optional, enforce, skip, skipWhen, omitWhen, only, warn, include, suiteSelectors, each, mode, staticSuite, Modes, registerReconciler, VEST_TEST_CONTEXT }; export type { SuiteResult, SuiteRunResult, SuiteSummary, Suite, StaticSuite }; //# sourceMappingURL=vest.d.ts.map