Skip to content

Commit

Permalink
test: add unit tests for hapify-core fields factories (#599)
Browse files Browse the repository at this point in the history
  • Loading branch information
maxmousse authored Mar 28, 2023
1 parent 6952d6c commit a8c44c4
Show file tree
Hide file tree
Showing 11 changed files with 558 additions and 32 deletions.
234 changes: 234 additions & 0 deletions libs/hapify/core/src/fields/factories/create-field-factory.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,234 @@
import { createFieldFactory } from './create-field-factory';
import { PrimaryField } from '..';
import { BooleanField } from '../boolean-field';
import { DateField } from '../date-field';
import { EnumField } from '../enum-field';
import { FieldType } from '../field';
import { FileField } from '../file-field';
import { ForeignField } from '../foreign-field';
import { NumberField } from '../number-field';
import { ObjectField } from '../object-field';
import { StringField } from '../string-field';
import { VirtualField } from '../virtual-field';

describe('createFieldFactory', () => {
it('should return a function', () => {
const type = 'string';
const defaultConstraints = {};
const defaultOptions = {};

const result = createFieldFactory(type, defaultConstraints, defaultOptions);

expect(result).toBeInstanceOf(Function);
});

it('should return a function that initialize a field with a strict type', () => {
const defaultConstraints = {};
const defaultOptions = {};
const [
createStringField,
createNumberField,
createBooleanField,
createObjectField,
createDateField,
createEnumField,
createFileField,
createForeignField,
createPrimaryField,
createVirtualField,
] = (
[
'string',
'number',
'boolean',
'object',
'date',
'enum',
'file',
'foreign',
'primary',
'virtual',
] satisfies FieldType[]
).map((type) =>
createFieldFactory(type, defaultConstraints, defaultOptions),
);

const stringField: StringField = createStringField('name');
const numberField: NumberField = createNumberField('name');
const booleanField: BooleanField = createBooleanField('name');
const objectField: ObjectField = createObjectField('name');
const dateField: DateField = createDateField('name');
const enumField: EnumField = createEnumField('name');
const fileField: FileField = createFileField('name');
const foreignField: ForeignField = createForeignField('name');
const primaryField: PrimaryField = createPrimaryField('name');
const virtualField: VirtualField = createVirtualField('name');

expect(stringField.type).toEqual('string');
expect(numberField.type).toEqual('number');
expect(booleanField.type).toEqual('boolean');
expect(objectField.type).toEqual('object');
expect(dateField.type).toEqual('date');
expect(enumField.type).toEqual('enum');
expect(fileField.type).toEqual('file');
expect(foreignField.type).toEqual('foreign');
expect(primaryField.type).toEqual('primary');
expect(virtualField.type).toEqual('virtual');
});

it('should return a function that initialize a field with the correct name', () => {
const type = 'string';
const defaultConstraints = {};
const defaultOptions = {};
const name = 'name';

const createStringField = createFieldFactory(
type,
defaultConstraints,
defaultOptions,
);

const result = createStringField(name);

expect(result.name).toBeDefined();
expect(result.name).toEqual(name);
});

it('should return a function that initialize a field with the correct scalar type', () => {
const typesToScalarMapping = [
['string', 'string'],
['number', 'number'],
['boolean', 'boolean'],
['object', 'object'],
['date', 'date'],
['enum', 'string'],
['file', 'string'],
['foreign', 'string'],
['primary', 'string'],
['virtual', 'object'],
] as const;

const defaultConstraints = {};
const defaultOptions = {};

typesToScalarMapping.forEach(([type, scalar]) => {
const createStringField = createFieldFactory(
type,
defaultConstraints,
defaultOptions,
);
const result = createStringField('name');

expect(result.scalar).toBeDefined();
expect(result.scalar).toEqual(scalar);
});
});

it('should return a function that initialize a field with a default plural name if none is provided', () => {
const type = 'string';
const defaultConstraints = {};
const defaultOptions = {};

const createStringField = createFieldFactory(
type,
defaultConstraints,
defaultOptions,
);

const result = createStringField('name');

expect(result.pluralName).toBeDefined();
expect(result.pluralName).toEqual('names');
});

it('should return a function that initialize a field with the provided plural name', () => {
const type = 'string';
const defaultConstraints = {};
const defaultOptions = {
pluralName: 'test',
};

const createStringField = createFieldFactory(
type,
defaultConstraints,
defaultOptions,
);

const result = createStringField('name');

expect(result.pluralName).toBeDefined();
expect(result.pluralName).toEqual('test');
});

it('should return a function that initialize a field with the default constraints', () => {
const type = 'string';
const defaultConstraints = {
isSearchable: true,
};
const defaultOptions = {};

const createStringField = createFieldFactory(
type,
defaultConstraints,
defaultOptions,
);

const result = createStringField('name');

expect(result.isSearchable).toBeDefined();
expect(result.isSearchable).toEqual(true);
});

it('should return a function that initialize a field with the custom constraints if provided', () => {
const type = 'string';
const defaultConstraints = {
isSearchable: true,
};
const defaultOptions = {};

const createStringField = createFieldFactory(
type,
defaultConstraints,
defaultOptions,
);

const result = createStringField('name', { isSearchable: false });

expect(result.isSearchable).toBeDefined();
expect(result.isSearchable).toEqual(false);
});

it('should return a function that initialize a field with the default options', () => {
const type = 'string';
const defaultConstraints = {};
const defaultOptions = { notes: 'default notes' };

const createStringField = createFieldFactory(
type,
defaultConstraints,
defaultOptions,
);

const result = createStringField('name');

expect(result.notes).toBeDefined();
expect(result.notes).toEqual('default notes');
});

it('should return a function that initialize a field with the custom options if provided', () => {
const type = 'string';
const defaultConstraints = {};
const defaultOptions = { notes: 'default notes' };

const createStringField = createFieldFactory(
type,
defaultConstraints,
defaultOptions,
);

const result = createStringField('name', {}, { notes: 'custom notes' });

expect(result.notes).toBeDefined();
expect(result.notes).toEqual('custom notes');
});
});
28 changes: 16 additions & 12 deletions libs/hapify/core/src/fields/factories/create-field-factory.ts
Original file line number Diff line number Diff line change
@@ -1,23 +1,25 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import pluralize = require('pluralize');
import { plural } from 'pluralize';
import type { Function } from 'ts-toolbelt';

import { BaseFieldPropertiesKeys, ExtractField, GetField } from '../base-types';
import { BaseFieldPropertiesKeys, ExtractField } from '../base-types';
import { Field, FieldType } from '../field';
import { CreateFieldOptions } from '../helpers';
import { getScalarFromType } from '../helpers/get-scalar-from-type';

/**
* Factory to create a field
* @param type
* @param defaultConstraints
* @param defaultOptions
* @returns
* Factory to create a function that initialize a field
*
* @param type - Type of the field to initialize
* @param defaultConstraints - Constraints to apply by default to the field to initialize
* @param defaultOptions - Options to apply by default to the field to initialize
* @returns Function to initialize a field
*/
export const createFieldFactory =
<
T extends FieldType,
DC extends Partial<Omit<ExtractField<T>, BaseFieldPropertiesKeys>>,
DC extends Partial<
Omit<ExtractField<T>, BaseFieldPropertiesKeys | 'scalar'>
>,
DO extends CreateFieldOptions<ExtractField<T>>,
>(
type: T,
Expand All @@ -26,18 +28,20 @@ export const createFieldFactory =
) =>
<
N extends string,
C extends Omit<ExtractField<T>, BaseFieldPropertiesKeys>,
C extends Omit<ExtractField<T>, BaseFieldPropertiesKeys | 'scalar'>,
// TODO: we should exclude pluralName
O extends CreateFieldOptions<ExtractField<T>>,
>(
// TODO: update return function params to be dynamic depending on the type of DC et DP
name: Function.Narrow<N>,
constraints?: Function.Narrow<C>,
options?: Function.Narrow<O>,
) =>
({
type,
name,
scalar: typeof type === 'string' ? getScalarFromType(type) : null,
pluralName: typeof name === 'string' ? pluralize(name) : name,
scalar: getScalarFromType(type),
pluralName: plural(name as string),
...(defaultConstraints as object),
...(constraints as object),
...(defaultOptions as object),
Expand Down
Loading

0 comments on commit a8c44c4

Please sign in to comment.