diff --git a/schemas/tests/helpers.ts b/schemas/tests/helpers.ts new file mode 100644 index 00000000..bf512333 --- /dev/null +++ b/schemas/tests/helpers.ts @@ -0,0 +1,63 @@ +import Account from '../app/Account.json'; +import Customer from '../app/Customer.json'; +import JournalEntry from '../app/JournalEntry.json'; +import JournalEntryAccount from '../app/JournalEntryAccount.json'; +import Party from '../app/Party.json'; +import PartyRegional from '../regional/in/Party.json'; +import { Schema, SchemaStub, SchemaStubMap } from '../types'; + +interface AppSchemaMap extends SchemaStubMap { + Account: SchemaStub; + JournalEntry: SchemaStub; + JournalEntryAccount: SchemaStub; + Party: SchemaStub; + Customer: SchemaStub; +} + +interface RegionalSchemaMap extends SchemaStubMap { + Party: SchemaStub; +} + +export function getTestSchemaMap(): { + appSchemaMap: AppSchemaMap; + regionalSchemaMap: RegionalSchemaMap; +} { + const appSchemaMap = { + Account, + JournalEntry, + JournalEntryAccount, + Party, + Customer, + } as AppSchemaMap; + const regionalSchemaMap = { Party: PartyRegional } as RegionalSchemaMap; + + return { + appSchemaMap, + regionalSchemaMap, + }; +} + +export function everyFieldExists(fieldList: string[], schema: Schema): boolean { + return fieldsExist(fieldList, schema, 'every'); +} + +export function someFieldExists(fieldList: string[], schema: Schema): boolean { + return fieldsExist(fieldList, schema, 'some'); +} + +function fieldsExist( + fieldList: string[], + schema: Schema, + type: 'every' | 'some' +): boolean { + const schemaFieldNames = schema.fields.map((f) => f.fieldname); + return fieldList.map((f) => schemaFieldNames.includes(f))[type](Boolean); +} + +export function subtract( + targetList: string[], + ...removalLists: string[][] +): string[] { + const removalList = removalLists.flat(); + return targetList.filter((f) => !removalList.includes(f)); +} diff --git a/schemas/tests/testSchemaBuilder.spec.ts b/schemas/tests/testSchemaBuilder.spec.ts new file mode 100644 index 00000000..eeabafb9 --- /dev/null +++ b/schemas/tests/testSchemaBuilder.spec.ts @@ -0,0 +1,213 @@ +import * as assert from 'assert'; +import { cloneDeep, isEqual } from 'lodash'; +import { describe } from 'mocha'; +import { getMapFromList } from '../helpers'; +import { + addMetaFields, + cleanSchemas, + getAbstractCombinedSchemas, + getRegionalCombinedSchemas, +} from '../index'; +import { metaSchemas } from '../schemas'; +import { + everyFieldExists, + getTestSchemaMap, + someFieldExists, + subtract, +} from './helpers'; + +describe('Schema Builder', function () { + const { appSchemaMap, regionalSchemaMap } = getTestSchemaMap(); + describe('Raw Schemas', function () { + specify('Meta Properties', function () { + assert.strictEqual(appSchemaMap.Party.isAbstract, true); + assert.strictEqual(appSchemaMap.Customer.extends, 'Party'); + assert.strictEqual(appSchemaMap.Account.isTree, true); + assert.strictEqual(appSchemaMap.JournalEntryAccount.isChild, true); + }); + + specify('Field Counts', function () { + assert.strictEqual(appSchemaMap.Account.fields?.length, 6); + assert.strictEqual(appSchemaMap.JournalEntry.fields?.length, 8); + assert.strictEqual(appSchemaMap.JournalEntryAccount.fields?.length, 3); + assert.strictEqual(appSchemaMap.Party.fields?.length, 8); + assert.strictEqual(appSchemaMap.Customer.fields?.length, undefined); + assert.strictEqual(regionalSchemaMap.Party.fields?.length, 2); + }); + + specify('Quick Edit Field Counts', function () { + assert.strictEqual(appSchemaMap.Party.quickEditFields?.length, 5); + assert.strictEqual(regionalSchemaMap.Party.quickEditFields?.length, 7); + }); + }); + + const regionalCombined = getRegionalCombinedSchemas( + appSchemaMap, + regionalSchemaMap + ); + describe('Regional Combined Schemas', function () { + specify('Field Counts', function () { + assert.strictEqual(regionalCombined.Party.fields?.length, 10); + }); + + specify('Quick Edit Field Counts', function () { + assert.strictEqual(regionalSchemaMap.Party.quickEditFields?.length, 7); + }); + + specify('Schema Equality with App Schemas', function () { + assert.strictEqual( + isEqual(regionalCombined.Account, appSchemaMap.Account), + true + ); + assert.strictEqual( + isEqual(regionalCombined.JournalEntry, appSchemaMap.JournalEntry), + true + ); + assert.strictEqual( + isEqual( + regionalCombined.JournalEntryAccount, + appSchemaMap.JournalEntryAccount + ), + true + ); + assert.strictEqual( + isEqual(regionalCombined.Customer, appSchemaMap.Customer), + true + ); + assert.strictEqual( + isEqual(regionalCombined.Party, appSchemaMap.Party), + false + ); + }); + }); + + const abstractCombined = cleanSchemas( + getAbstractCombinedSchemas(regionalCombined) + ); + describe('Abstract Combined Schemas', function () { + specify('Meta Properties', function () { + assert.strictEqual(abstractCombined.Customer.extends, undefined); + }); + + specify('Abstract Schema Existance', function () { + assert.strictEqual(abstractCombined.Party, undefined); + }); + + specify('Field Counts', function () { + assert.strictEqual(abstractCombined.Customer.fields?.length, 10); + }); + + specify('Quick Edit Field Counts', function () { + assert.strictEqual(abstractCombined.Customer.quickEditFields?.length, 7); + }); + + specify('Schema Equality with App Schemas', function () { + assert.strictEqual( + isEqual(abstractCombined.Account, appSchemaMap.Account), + true + ); + assert.strictEqual( + isEqual(abstractCombined.JournalEntry, appSchemaMap.JournalEntry), + true + ); + assert.strictEqual( + isEqual( + abstractCombined.JournalEntryAccount, + appSchemaMap.JournalEntryAccount + ), + true + ); + assert.strictEqual( + isEqual(abstractCombined.Customer, appSchemaMap.Customer), + false + ); + }); + + specify('Schema Field Existance', function () { + assert.strictEqual( + everyFieldExists( + regionalSchemaMap.Party.quickEditFields ?? [], + abstractCombined.Customer + ), + true + ); + }); + }); + + const finalSchemas = addMetaFields(cloneDeep(abstractCombined)); + const metaSchemaMap = getMapFromList(metaSchemas, 'name'); + const baseFieldNames = metaSchemaMap.base.fields!.map((f) => f.fieldname); + const childFieldNames = metaSchemaMap.child.fields!.map((f) => f.fieldname); + const treeFieldNames = metaSchemaMap.tree.fields!.map((f) => f.fieldname); + const submittableFieldNames = metaSchemaMap.submittable.fields!.map( + (f) => f.fieldname + ); + const allFieldNames = [ + ...baseFieldNames, + ...childFieldNames, + ...treeFieldNames, + ...submittableFieldNames, + ]; + + describe('Final Schemas', function () { + specify('Schema Field Existance', function () { + assert.strictEqual( + everyFieldExists(baseFieldNames, finalSchemas.Customer), + true + ); + + assert.strictEqual( + someFieldExists( + subtract(allFieldNames, baseFieldNames), + finalSchemas.Customer + ), + false + ); + + assert.strictEqual( + everyFieldExists( + [...baseFieldNames, ...submittableFieldNames], + finalSchemas.JournalEntry + ), + true + ); + + assert.strictEqual( + someFieldExists( + subtract(allFieldNames, baseFieldNames, submittableFieldNames), + finalSchemas.JournalEntry + ), + false + ); + + assert.strictEqual( + everyFieldExists(childFieldNames, finalSchemas.JournalEntryAccount), + true + ); + + assert.strictEqual( + someFieldExists( + subtract(allFieldNames, childFieldNames), + finalSchemas.JournalEntryAccount + ), + false + ); + + assert.strictEqual( + everyFieldExists( + [...treeFieldNames, ...baseFieldNames], + finalSchemas.Account + ), + true + ); + + assert.strictEqual( + someFieldExists( + subtract(allFieldNames, treeFieldNames, baseFieldNames), + finalSchemas.Account + ), + false + ); + }); + }); +}); diff --git a/schemas/types.ts b/schemas/types.ts index 1e531ac7..269e3f44 100644 --- a/schemas/types.ts +++ b/schemas/types.ts @@ -107,7 +107,7 @@ export type Field = export type TreeSettings = { parentField: string }; -// prettier-ignore +// @formattoer:off export interface Schema { name: string; // Table PK label: string; // Translateable UI facing name @@ -118,7 +118,8 @@ export interface Schema { isSingle?: boolean; // Fields will be values in SingleValue, i.e. an Entity Attr. Value isAbstract?: boolean; // Not entered into db, used to extend a Subclass schema isSubmittable?: boolean; // For transactional types, values considered only after submit - keywordFields?: string[]; // Used for fields that are to be used for search. + keywordFields?: string[]; // Used to get fields that are to be used for search. + quickEditFields?: string[]; // Used to get fields for the quickEditForm treeSettings?: TreeSettings; // Used to determine root nodes }