@stackbit/utils
Version:
Stackbit utilities
383 lines (345 loc) • 14 kB
text/typescript
import { describe, test, expect } from '@jest/globals';
import { DocumentWithSource } from '@stackbit/types';
import { fieldPathToString, getDocumentFieldValueAtFieldPath, stringToFieldPath } from '../src';
describe('test fieldPathToString', () => {
test('should convert single item fieldPath', () => {
const result = fieldPathToString(['title']);
expect(result).toEqual('title');
});
test('should separate multi item fieldPath with dot', () => {
const result = fieldPathToString(['section', 'title']);
expect(result).toEqual('section.title');
});
test('should wrap first non-alphanumeric field path item with square brackets and single quotes', () => {
const result = fieldPathToString(['hero-section', 'title']);
expect(result).toEqual("['hero-section'].title");
});
test('should wrap middle non-alphanumeric field path item with square brackets', () => {
const result = fieldPathToString(['sections', 'hero-section', 'title']);
expect(result).toEqual("sections['hero-section'].title");
});
test('should wrap successive non-alphanumeric field names with square brackets', () => {
const result = fieldPathToString(['banner', 'sub-component-1', 'sub.component.2']);
expect(result).toEqual("banner['sub-component-1']['sub.component.2']");
});
test('should wrap list indexes with square brackets', () => {
const result = fieldPathToString(['sections', 2, 'title']);
expect(result).toEqual('sections[2].title');
});
test('should not wrap string-numbers with square brackets', () => {
const result = fieldPathToString(['sections', '2', 'title']);
expect(result).toEqual('sections.2.title');
});
});
describe('test stringToFieldPath', () => {
test('should convert single item fieldPath', () => {
const result = stringToFieldPath('title');
expect(result).toEqual(['title']);
});
test('should separate multi item fieldPath with dot', () => {
const result = stringToFieldPath('section.title');
expect(result).toEqual(['section', 'title']);
});
test('should unwrap first non-alphanumeric field path item from square brackets', () => {
const result = stringToFieldPath("['hero-section'].title");
expect(result).toEqual(['hero-section', 'title']);
});
test('should unwrap middle non-alphanumeric field path item from square brackets', () => {
const result = stringToFieldPath("sections['hero-section'].title");
expect(result).toEqual(['sections', 'hero-section', 'title']);
});
test('should wrap successive non-alphanumeric field names with square brackets', () => {
const result = stringToFieldPath("banner['sub-component-1']['sub.component.2']");
expect(result).toEqual(['banner', 'sub-component-1', 'sub.component.2']);
});
test('should wrap list indexes with square brackets', () => {
const result = stringToFieldPath('sections[2].title');
expect(result).toEqual(['sections', 2, 'title']);
});
test('should not wrap string-numbers with square brackets', () => {
const result = stringToFieldPath('sections.2.title');
expect(result).toEqual(['sections', '2', 'title']);
});
});
describe('test getDocumentFieldValueAtFieldPath', () => {
const documents: DocumentWithSource[] = [
{
type: 'document',
id: 'doc-1',
srcType: 'src-1',
srcProjectId: 'src-project-1',
manageUrl: 'https://www.example.com',
modelName: 'page',
status: 'published',
createdAt: '2025-01-01T10:00:00.000Z',
updatedAt: '2025-01-01T10:00:00.000Z',
context: null,
fields: {
title: { type: 'string', value: 'Title Value' },
localizedString: {
type: 'string',
localized: true,
locales: {
en: {
locale: 'en',
value: 'Hello'
},
es: {
locale: 'es',
value: 'Hola'
}
}
},
modelField: {
type: 'model',
modelName: 'heroSection',
fields: {
label: { type: 'string', value: 'Label Value' }
}
},
localizedModelField: {
type: 'model',
localized: true,
locales: {
en: {
locale: 'en',
modelName: 'heroSection',
fields: {
label: { type: 'string', value: 'Localized Label Value' },
localizedString: {
type: 'string',
localized: true,
locales: {
es: {
locale: 'es',
value: 'Spanish value'
}
}
}
}
}
}
},
listField: {
type: 'list',
items: [
{
type: 'string',
value: 'one'
},
{
type: 'string',
value: 'two'
}
]
},
referenceField: {
type: 'reference',
refType: 'document',
refId: 'doc-2'
}
}
},
{
type: 'document',
id: 'doc-2',
srcType: 'src-1',
srcProjectId: 'src-project-1',
manageUrl: 'https://www.example.com',
modelName: 'page',
status: 'published',
createdAt: '2025-01-01T10:00:00.000Z',
updatedAt: '2025-01-01T10:00:00.000Z',
context: null,
fields: {
title: { type: 'string', value: 'Doc 2 Title Value' }
}
}
];
const document = documents[0]!;
function getDocumentById(options: { id: string; srcType?: string; srcProjectId?: string }) {
return documents.find((doc) => doc.id === options.id);
}
test('should throw an error if fieldPath is empty', () => {
expect(() => {
getDocumentFieldValueAtFieldPath({
document,
fieldPath: '',
getDocumentById
});
}).toThrow("Illegal fieldPath ''. The fieldPath cannot be empty");
expect(() => {
getDocumentFieldValueAtFieldPath({
document,
fieldPath: [],
getDocumentById
});
}).toThrow("Illegal fieldPath ''. The fieldPath cannot be empty");
});
test('should return value of a string field', () => {
const result = getDocumentFieldValueAtFieldPath({
document,
fieldPath: 'title',
getDocumentById
});
expect(result).toEqual('Title Value');
});
test('should throw an error if a primitive field is followed by another field path', () => {
expect(() => {
getDocumentFieldValueAtFieldPath({
document,
fieldPath: 'title.noField',
getDocumentById
});
}).toThrow(
"Illegal fieldPath 'title.noField'. The field at path 'title' is a primitive field of type 'string' and cannot be followed by another field path."
);
});
test('should return undefined for non existing field', () => {
const result = getDocumentFieldValueAtFieldPath({
document,
fieldPath: 'no_such_field',
getDocumentById
});
expect(result).toBeUndefined();
});
test('should return value of a localized string field', () => {
const result = getDocumentFieldValueAtFieldPath({
document,
fieldPath: 'localizedString.es',
getDocumentById
});
expect(result).toEqual('Hola');
});
test('should return undefined for a non existing locale value of a localized field', () => {
const result = getDocumentFieldValueAtFieldPath({
document,
fieldPath: 'localizedString.fr',
getDocumentById
});
expect(result).toBeUndefined();
});
test('should throw if locale for a localized field was not specified', () => {
expect(() => {
getDocumentFieldValueAtFieldPath({
document,
fieldPath: 'localizedString',
getDocumentById
});
}).toThrow("Illegal fieldPath 'localizedString'. The field at path 'localizedString' is a localized field and must be followed by a locale specifier.");
});
test('should return a flattened object of a model field', () => {
const result = getDocumentFieldValueAtFieldPath({
document,
fieldPath: 'modelField',
getDocumentById
});
expect(result).toEqual({
__metadata: { modelName: 'heroSection' },
label: 'Label Value'
});
});
test('should return a value of a field in a nested object', () => {
const result = getDocumentFieldValueAtFieldPath({
document,
fieldPath: 'modelField.label',
getDocumentById
});
expect(result).toEqual('Label Value');
});
test('should return undefined for a non-existing field in a nested object', () => {
const result = getDocumentFieldValueAtFieldPath({
document,
fieldPath: 'modelField.noField',
getDocumentById
});
expect(result).toBeUndefined();
});
test('should return undefined for a non-existing nested fields', () => {
const result = getDocumentFieldValueAtFieldPath({
document,
fieldPath: 'noField1.noField2.noField3',
getDocumentById
});
expect(result).toBeUndefined();
});
test('should return a flattened object of a localized model field', () => {
const result = getDocumentFieldValueAtFieldPath({
document,
fieldPath: 'localizedModelField.en',
getDocumentById
});
expect(result).toEqual({
__metadata: { modelName: 'heroSection' },
label: 'Localized Label Value'
});
});
test('should return a value of a field in a localized model field', () => {
const result = getDocumentFieldValueAtFieldPath({
document,
fieldPath: 'localizedModelField.en.label',
getDocumentById
});
expect(result).toEqual('Localized Label Value');
});
test('should return undefined for a non existing locale of a localized model field', () => {
const result = getDocumentFieldValueAtFieldPath({
document,
fieldPath: 'localizedModelField.es',
getDocumentById
});
expect(result).toBeUndefined();
});
test('should return a value for a localized string within a localized model field', () => {
const result = getDocumentFieldValueAtFieldPath({
document,
fieldPath: 'localizedModelField.en.localizedString.es',
getDocumentById
});
expect(result).toEqual('Spanish value');
});
test('should throw if locale for a localized model field was not specified', () => {
expect(() => {
getDocumentFieldValueAtFieldPath({
document,
fieldPath: 'localizedModelField',
getDocumentById
});
}).toThrow(
"Illegal fieldPath 'localizedModelField'. The field at path 'localizedModelField' is a localized field and must be followed by a locale specifier."
);
});
test('should return an item list value in a list at specified index', () => {
const result = getDocumentFieldValueAtFieldPath({
document,
fieldPath: 'listField[1]',
getDocumentById
});
expect(result).toEqual('two');
});
test('should throw an error if list field is not followed by index', () => {
expect(() => {
getDocumentFieldValueAtFieldPath({
document,
fieldPath: 'listField.fieldName',
getDocumentById
});
}).toThrow("Illegal fieldPath 'listField.fieldName'. The field at path 'listField' is a list field and must be followed by a list item index.");
});
test('should return a document id of a reference field', () => {
const result = getDocumentFieldValueAtFieldPath({
document,
fieldPath: 'referenceField',
getDocumentById
});
expect(result).toEqual('doc-2');
});
test('should return a value of a field in a referenced document', () => {
const result = getDocumentFieldValueAtFieldPath({
document,
fieldPath: 'referenceField.title',
getDocumentById
});
expect(result).toEqual('Doc 2 Title Value');
});
});