UNPKG

payload

Version:

Node, React, Headless CMS and Application Framework built on Next.js

745 lines (744 loc) • 25.9 kB
import { DuplicateFieldName, InvalidFieldName, InvalidFieldRelationship, MissingFieldType } from '../../errors/index.js'; import { sanitizeFields } from './sanitize.js'; import { describe, it, expect } from 'vitest'; describe('sanitizeFields', ()=>{ const config = {}; const collectionConfig = {}; it('should throw on missing type field', async ()=>{ const fields = [ // @ts-expect-error { name: 'Some Collection', label: 'some-collection' } ]; await expect(async ()=>{ await sanitizeFields({ config, collectionConfig, fields, validRelationships: [] }); }).rejects.toThrow(MissingFieldType); }); it('should throw on invalid field name', async ()=>{ const fields = [ { name: 'some.collection', type: 'text', label: 'some.collection' } ]; await expect(async ()=>{ await sanitizeFields({ config, collectionConfig, fields, validRelationships: [] }); }).rejects.toThrow(InvalidFieldName); }); it('should throw on duplicate field name', async ()=>{ const fields = [ { name: 'someField', type: 'text', label: 'someField' }, { name: 'someField', type: 'text', label: 'someField' } ]; await expect(async ()=>{ await sanitizeFields({ config, collectionConfig, fields, validRelationships: [] }); }).rejects.toThrow(DuplicateFieldName); }); it('should throw on duplicate block slug', async ()=>{ const fields = [ { name: 'blocks', type: 'blocks', blocks: [ { slug: 'block', fields: [ { name: 'blockField', type: 'text' } ] }, { slug: 'block', fields: [ { name: 'blockField', type: 'text' } ] } ] } ]; await expect(async ()=>{ await sanitizeFields({ config, collectionConfig, fields, validRelationships: [] }); }).rejects.toThrow(DuplicateFieldName); }); describe('auto-labeling', ()=>{ it('should populate label if missing', async ()=>{ const fields = [ { name: 'someField', type: 'text' } ]; const sanitizedField = (await sanitizeFields({ config, collectionConfig, fields, validRelationships: [] }))[0]; expect(sanitizedField.name).toStrictEqual('someField'); expect(sanitizedField.label).toStrictEqual('Some Field'); expect(sanitizedField.type).toStrictEqual('text'); }); it('should allow auto-label override', async ()=>{ const fields = [ { name: 'someField', type: 'text', label: 'Do not label' } ]; const sanitizedField = (await sanitizeFields({ config, collectionConfig, fields, validRelationships: [] }))[0]; expect(sanitizedField.name).toStrictEqual('someField'); expect(sanitizedField.label).toStrictEqual('Do not label'); expect(sanitizedField.type).toStrictEqual('text'); }); describe('opt-out', ()=>{ it('should allow label opt-out', async ()=>{ const fields = [ { name: 'someField', type: 'text', label: false } ]; const sanitizedField = (await sanitizeFields({ config, collectionConfig, fields, validRelationships: [] }))[0]; expect(sanitizedField.name).toStrictEqual('someField'); expect(sanitizedField.label).toStrictEqual(false); expect(sanitizedField.type).toStrictEqual('text'); }); it('should allow label opt-out for arrays', async ()=>{ const arrayField = { name: 'items', type: 'array', fields: [ { name: 'itemName', type: 'text' } ], label: false }; const sanitizedField = (await sanitizeFields({ config, collectionConfig, fields: [ arrayField ], validRelationships: [] }))[0]; expect(sanitizedField.name).toStrictEqual('items'); expect(sanitizedField.label).toStrictEqual(false); expect(sanitizedField.type).toStrictEqual('array'); expect(sanitizedField.labels).toBeUndefined(); }); it('should allow label opt-out for blocks', async ()=>{ const fields = [ { name: 'noLabelBlock', type: 'blocks', blocks: [ { slug: 'number', fields: [ { name: 'testNumber', type: 'number' } ] } ], label: false } ]; const sanitizedField = (await sanitizeFields({ config, collectionConfig, fields, validRelationships: [] }))[0]; expect(sanitizedField.name).toStrictEqual('noLabelBlock'); expect(sanitizedField.label).toStrictEqual(false); expect(sanitizedField.type).toStrictEqual('blocks'); expect(sanitizedField.labels).toBeUndefined(); }); }); it('should label arrays with plural and singular', async ()=>{ const fields = [ { name: 'items', type: 'array', fields: [ { name: 'itemName', type: 'text' } ] } ]; const sanitizedField = (await sanitizeFields({ config, collectionConfig, fields, validRelationships: [] }))[0]; expect(sanitizedField.name).toStrictEqual('items'); expect(sanitizedField.label).toStrictEqual('Items'); expect(sanitizedField.type).toStrictEqual('array'); expect(sanitizedField.labels).toMatchObject({ plural: 'Items', singular: 'Item' }); }); it('should label blocks with plural and singular', async ()=>{ const fields = [ { name: 'specialBlock', type: 'blocks', blocks: [ { slug: 'number', fields: [ { name: 'testNumber', type: 'number' } ] } ] } ]; const sanitizedField = (await sanitizeFields({ config, collectionConfig, fields, validRelationships: [] }))[0]; expect(sanitizedField.name).toStrictEqual('specialBlock'); expect(sanitizedField.label).toStrictEqual('Special Block'); expect(sanitizedField.type).toStrictEqual('blocks'); expect(sanitizedField.labels).toMatchObject({ plural: 'Special Blocks', singular: 'Special Block' }); expect(sanitizedField.blocks[0].fields[0].label).toStrictEqual('Test Number'); }); }); describe('relationships', ()=>{ it('should not throw on valid relationship', async ()=>{ const validRelationships = [ 'some-collection' ]; const fields = [ { name: 'My Relationship', type: 'relationship', label: 'my-relationship', relationTo: 'some-collection' } ]; await expect(async ()=>{ await sanitizeFields({ config, collectionConfig, fields, validRelationships }); }).not.toThrow(); }); it('should not throw on valid relationship - multiple', async ()=>{ const validRelationships = [ 'some-collection', 'another-collection' ]; const fields = [ { name: 'My Relationship', type: 'relationship', label: 'my-relationship', relationTo: [ 'some-collection', 'another-collection' ] } ]; await expect(async ()=>{ await sanitizeFields({ config, collectionConfig, fields, validRelationships }); }).not.toThrow(); }); it('should not throw on valid relationship inside blocks', async ()=>{ const validRelationships = [ 'some-collection' ]; const relationshipBlock = { slug: 'relationshipBlock', fields: [ { name: 'My Relationship', type: 'relationship', label: 'my-relationship', relationTo: 'some-collection' } ] }; const fields = [ { name: 'layout', type: 'blocks', blocks: [ relationshipBlock ], label: 'Layout Blocks' } ]; await expect(async ()=>{ await sanitizeFields({ config, collectionConfig, fields, validRelationships }); }).not.toThrow(); }); it('should throw on invalid relationship', async ()=>{ const validRelationships = [ 'some-collection' ]; const fields = [ { name: 'My Relationship', type: 'relationship', label: 'my-relationship', relationTo: 'not-valid' } ]; await expect(async ()=>{ await sanitizeFields({ config, collectionConfig, fields, validRelationships }); }).rejects.toThrow(InvalidFieldRelationship); }); it('should throw on invalid relationship - multiple', async ()=>{ const validRelationships = [ 'some-collection', 'another-collection' ]; const fields = [ { name: 'My Relationship', type: 'relationship', label: 'my-relationship', relationTo: [ 'some-collection', 'not-valid' ] } ]; await expect(async ()=>{ await sanitizeFields({ config, collectionConfig, fields, validRelationships }); }).rejects.toThrow(InvalidFieldRelationship); }); it('should throw on invalid relationship inside blocks', async ()=>{ const validRelationships = [ 'some-collection' ]; const relationshipBlock = { slug: 'relationshipBlock', fields: [ { name: 'My Relationship', type: 'relationship', label: 'my-relationship', relationTo: 'not-valid' } ] }; const fields = [ { name: 'layout', type: 'blocks', blocks: [ relationshipBlock ], label: 'Layout Blocks' } ]; await expect(async ()=>{ await sanitizeFields({ config, collectionConfig, fields, validRelationships }); }).rejects.toThrow(InvalidFieldRelationship); }); it('should throw on empty relationTo array', async ()=>{ const validRelationships = [ 'some-collection' ]; const fields = [ { name: 'My Relationship', type: 'relationship', label: 'my-relationship', relationTo: [] } ]; await expect(async ()=>{ await sanitizeFields({ config, collectionConfig, fields, validRelationships }); }).rejects.toThrow('has an empty relationTo array'); }); it('should throw on empty relationTo array for upload field', async ()=>{ const validRelationships = [ 'some-collection' ]; const fields = [ { name: 'My Upload', type: 'upload', label: 'my-upload', relationTo: [] } ]; await expect(async ()=>{ await sanitizeFields({ config, collectionConfig, fields, validRelationships }); }).rejects.toThrow('has an empty relationTo array'); }); it('should throw on empty relationTo array inside blocks', async ()=>{ const validRelationships = [ 'some-collection' ]; const relationshipBlock = { slug: 'relationshipBlock', fields: [ { name: 'My Relationship', type: 'relationship', label: 'my-relationship', relationTo: [] } ] }; const fields = [ { name: 'layout', type: 'blocks', blocks: [ relationshipBlock ], label: 'Layout Blocks' } ]; await expect(async ()=>{ await sanitizeFields({ config, collectionConfig, fields, validRelationships }); }).rejects.toThrow('has an empty relationTo array'); }); it('should defaultValue of checkbox to false if required and undefined', async ()=>{ const fields = [ { name: 'My Checkbox', type: 'checkbox', required: true } ]; const sanitizedField = (await sanitizeFields({ config, collectionConfig, fields, validRelationships: [] }))[0]; expect(sanitizedField.defaultValue).toStrictEqual(false); }); it('should return empty field array if no fields', async ()=>{ const sanitizedFields = await sanitizeFields({ config, collectionConfig, fields: [], validRelationships: [] }); expect(sanitizedFields).toStrictEqual([]); }); }); describe('blocks', ()=>{ it('should maintain admin.blockName true after sanitization', async ()=>{ const fields = [ { name: 'noLabelBlock', type: 'blocks', blocks: [ { slug: 'number', admin: { disableBlockName: true }, fields: [ { name: 'testNumber', type: 'number' } ] } ], label: false } ]; const sanitizedField = (await sanitizeFields({ config, collectionConfig, fields, validRelationships: [] }))[0]; const sanitizedBlock = sanitizedField.blocks[0]; expect(sanitizedBlock.admin?.disableBlockName).toStrictEqual(true); }); it('should default admin.disableBlockName to true after sanitization', async ()=>{ const fields = [ { name: 'noLabelBlock', type: 'blocks', blocks: [ { slug: 'number', fields: [ { name: 'testNumber', type: 'number' } ] } ], label: false } ]; const sanitizedField = (await sanitizeFields({ config, collectionConfig, fields, validRelationships: [] }))[0]; const sanitizedBlock = sanitizedField.blocks[0]; expect(sanitizedBlock.admin?.disableBlockName).toStrictEqual(undefined); }); }); describe('virtual fields', ()=>{ it('should assign a noop validate for virtual: true fields', async ()=>{ const fields = [ { name: 'virtualText', type: 'text', virtual: true } ]; const sanitizedField = (await sanitizeFields({ config, collectionConfig, fields, validRelationships: [] }))[0]; expect(sanitizedField.validate).toBeDefined(); expect(sanitizedField.validate('', {})).toBe(true); expect(sanitizedField.validate(undefined, {})).toBe(true); }); it('should assign a noop validate for virtual: "string" fields', async ()=>{ const fields = [ { name: 'virtualRef', type: 'text', virtual: 'post.title' } ]; const sanitizedField = (await sanitizeFields({ config, collectionConfig, fields, validRelationships: [] }))[0]; expect(sanitizedField.validate).toBeDefined(); expect(sanitizedField.validate(undefined, {})).toBe(true); }); it('should not override an explicit validate on a virtual field', async ()=>{ const customValidate = ()=>true; const fields = [ { name: 'virtualText', type: 'text', virtual: true, validate: customValidate } ]; const sanitizedField = (await sanitizeFields({ config, collectionConfig, fields, validRelationships: [] }))[0]; expect(sanitizedField.validate).toBe(customValidate); }); it('should assign default type-based validate for non-virtual fields', async ()=>{ const fields = [ { name: 'normalText', type: 'text' } ]; const sanitizedField = (await sanitizeFields({ config, collectionConfig, fields, validRelationships: [] }))[0]; expect(sanitizedField.validate).toBeDefined(); // Non-virtual text field should use the text validator which checks required/minLength/etc. // Passing undefined with required should fail const result = sanitizedField.validate(undefined, { required: true, req: { payload: { config: {} }, t: (v)=>v } }); expect(result).not.toBe(true); }); it('should default readOnly to true for virtual: true fields', async ()=>{ const fields = [ { name: 'virtualText', type: 'text', virtual: true } ]; const sanitizedField = (await sanitizeFields({ config, collectionConfig, fields, validRelationships: [] }))[0]; expect(sanitizedField.admin?.readOnly).toBe(true); }); it('should default readOnly to true for virtual: "string" fields', async ()=>{ const fields = [ { name: 'virtualRef', type: 'text', virtual: 'post.title' } ]; const sanitizedField = (await sanitizeFields({ config, collectionConfig, fields, validRelationships: [] }))[0]; expect(sanitizedField.admin?.readOnly).toBe(true); }); it('should not override readOnly: false on virtual fields', async ()=>{ const fields = [ { name: 'virtualText', type: 'text', virtual: true, admin: { readOnly: false } } ]; const sanitizedField = (await sanitizeFields({ config, collectionConfig, fields, validRelationships: [] }))[0]; expect(sanitizedField.admin?.readOnly).toBe(false); }); it('should not set readOnly on non-virtual fields', async ()=>{ const fields = [ { name: 'normalText', type: 'text' } ]; const sanitizedField = (await sanitizeFields({ config, collectionConfig, fields, validRelationships: [] }))[0]; expect(sanitizedField.admin?.readOnly).toBeUndefined(); }); }); }); //# sourceMappingURL=sanitize.spec.js.map