test-fixture-factory
Version:
A minimal library for creating and managing test fixtures using Vitest, enabling structured, repeatable, and efficient testing processes.
103 lines (98 loc) • 6.26 kB
TypeScript
type OneOrMany<T> = T | readonly T[];
type KeysAssignableTo<C, V> = {
[P in keyof C & string]: C[P] extends V ? P : never;
}[keyof C & string];
declare class FieldBuilder<Context extends object, Fixtures extends keyof Context & string, Value, Flag extends RequiredFlag> {
private readonly state;
constructor(field: Field<Pick<Context, Fixtures>, Value, Flag>);
from<K extends KeysAssignableTo<Context, Value>>(keyOrList: OneOrMany<K>): FieldBuilder<Context, Fixtures | K, Value, 'from'>;
from<K extends keyof Context & string>(keyOrList: OneOrMany<K>, getValueFromContext: (ctx: Pick<Context, K>) => Value): FieldBuilder<Context, Fixtures | K, Value, 'from'>;
maybeFrom<K extends KeysAssignableTo<Context, Value | undefined>>(keyOrList: OneOrMany<K>): FieldBuilder<Context, Fixtures | K, Value, 'maybeFrom'>;
maybeFrom<K extends keyof Context & string>(keyOrList: OneOrMany<K>, getValueFromContext: (ctx: Pick<Context, K>) => Value | undefined): FieldBuilder<Context, Fixtures | K, Value, 'maybeFrom'>;
optional(): FieldBuilder<Context, Fixtures, Value | undefined, "optional">;
default(defaultValue: Value | (() => Value)): FieldBuilder<Context, Fixtures, Value, "default">;
}
type NewFieldBuilder<Context extends object> = {
type: <T>() => FieldBuilder<Context, never, T, 'required'>;
};
type Prettify<T> = {
[K in keyof T]: T[K];
} & {};
type RequiredFlag = 'required' | 'optional' | 'default' | 'from' | 'maybeFrom';
type Field<Fixtures extends object, Value, Flag extends RequiredFlag> = {
fixtureList: (keyof Fixtures & string)[];
getValueFromContext: undefined | ((ctx: Fixtures) => Value | undefined);
isRequired: Flag extends 'required' ? true : Flag extends 'default' ? true : Flag extends 'from' ? true : Flag extends 'maybeFrom' ? true : Flag extends 'optional' ? false : true;
defaultValue: undefined | Value | (() => Value);
};
type AsField<F extends FieldBuilder<any, any, any, any>> = F extends FieldBuilder<infer Context, infer Fixtures, infer Value, infer Flag> ? Field<Prettify<Pick<Context, Fixtures>>, Value, Flag> : never;
type AnySchemaBuilder = Record<string, FieldBuilder<any, any, any, any>>;
type SchemaOf<SchemaBuilder extends AnySchemaBuilder> = {
[K in keyof SchemaBuilder]: AsField<SchemaBuilder[K]>;
};
type AnySchemaBuilderWithContext<Context extends object> = Record<string, FieldBuilder<Context, any, any, any>>;
type AnyFieldWithContext<Context extends object> = Field<Context, any, any>;
type AnySchema = Record<string, AnyFieldWithContext<any>>;
type EmptySchema = Record<string, never>;
type MissingField = {
key: string;
fixtureList: string[];
};
type DestroyFn = () => Promise<void> | void;
type VitestFixtureFn<Context, FixtureValue> = (context: object & Context, use: (value: FixtureValue) => Promise<void>) => Promise<void>;
type FactoryResult<Value> = {
value: Value;
destroy?: DestroyFn;
};
type FactoryFn<Attrs extends object, Value> = (attrs: Attrs) => Promise<FactoryResult<Value>> | FactoryResult<Value>;
type FactoryOptions = {
shouldDestroy?: boolean;
};
type ValueOf<S extends AnySchema, K extends keyof S> = S[K] extends Field<infer _C, infer Value, infer _F> ? Value : never;
type FixturesOf<S extends AnySchema, K extends keyof S> = S[K] extends Field<infer Fixtures, infer _V, infer _F> ? Fixtures : never;
type FlagOf<S extends AnySchema, K extends keyof S> = S[K] extends Field<infer _F, infer _V, infer Flag> ? Flag : never;
type OptionalInputKeysOf<S extends AnySchema> = {
[K in keyof S]: FlagOf<S, K> extends 'optional' | 'default' | 'from' | 'maybeFrom' ? K : never;
}[keyof S];
type RequiredInputKeysOf<S extends AnySchema> = S extends EmptySchema ? never : Exclude<keyof S, OptionalInputKeysOf<S>>;
type InputOf<S extends AnySchema> = S extends EmptySchema ? EmptySchema : Prettify<{
[K in RequiredInputKeysOf<S>]: ValueOf<S, K>;
} & {
[K in OptionalInputKeysOf<S>]?: ValueOf<S, K>;
}>;
type OutputOf<S extends AnySchema> = S extends EmptySchema ? EmptySchema : {
[K in keyof S]: ValueOf<S, K>;
};
type VoidableInputOf<Schema extends AnySchema> = RequiredInputKeysOf<Schema> extends never ? InputOf<Schema> | void : InputOf<Schema>;
type RequiredKeys<T> = {
[K in keyof T]-?: {} extends Pick<T, K> ? never : K;
}[keyof T];
type MaybeVoid<T> = RequiredKeys<T> extends never ? T | void : T;
type InferFixtureValue<T> = T extends () => VitestFixtureFn<infer _Deps, infer Value> ? Value : never;
type SetSchemaFieldsOptional<Schema extends AnySchema, Keys extends keyof Schema> = {
[K in keyof Schema]: K extends Keys ? Field<FixturesOf<Schema, K>, ValueOf<Schema, K>, 'optional'> : Schema[K];
};
type FactoryState<S extends AnySchema, V> = {
name: string;
schema: S;
factoryFn: FactoryFn<Prettify<OutputOf<S>>, V> | undefined;
};
type CreateFn<Schema extends AnySchema, Value> = (attrs: VoidableInputOf<Schema>) => Promise<Value>;
declare class FactoryBuilder<Context extends object, Schema extends AnySchema, Value> {
private readonly state;
constructor(state: FactoryState<Schema, Value>);
withContext<Context extends object>(): FactoryBuilder<Context, Schema, Value>;
withSchema<SchemaBuilder extends AnySchemaBuilderWithContext<Context>>(schemaFn: (f: NewFieldBuilder<Context>) => SchemaBuilder): FactoryBuilder<Context, SchemaOf<SchemaBuilder>, Value>;
withValue<Value>(factoryFn: FactoryFn<Prettify<OutputOf<Schema>>, Value>): FactoryBuilder<Context, Schema, Value>;
build(attrs: VoidableInputOf<Schema>, context: MaybeVoid<Context>): Promise<{
value: Value;
destroy: DestroyFn;
}>;
useCreateValue<PresetAttrs extends void | undefined | Partial<InputOf<Schema>>>(presetAttrs?: PresetAttrs, { shouldDestroy }?: FactoryOptions): VitestFixtureFn<Context, CreateFn<SetSchemaFieldsOptional<Schema, keyof PresetAttrs & keyof Schema>, Value>>;
useValue(attrs: VoidableInputOf<Schema>, options?: FactoryOptions): VitestFixtureFn<Context, Value>;
}
declare const createFactory: (name: string) => FactoryBuilder<object, EmptySchema, unknown>;
declare class UndefinedFieldError extends Error {
constructor(name: string, missingFields: MissingField[]);
}
export { type InferFixtureValue, UndefinedFieldError, createFactory };