fix: slug in media paths (#793)
This commit is contained in:
parent
f96bb026d9
commit
d28c43e95a
36
packages/core/src/__mocks__/backend.ts
Normal file
36
packages/core/src/__mocks__/backend.ts
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
import type { Entry } from '../interface';
|
||||||
|
|
||||||
|
const {
|
||||||
|
expandSearchEntries: actualExpandSearchEntries,
|
||||||
|
getEntryField: actualGetEntryField,
|
||||||
|
mergeExpandedEntries: actualMergeExpandedEntries,
|
||||||
|
} = jest.requireActual('@staticcms/core/backend');
|
||||||
|
|
||||||
|
const isGitBackend = jest.fn().mockReturnValue(true);
|
||||||
|
|
||||||
|
export const resolveBackend = jest.fn().mockReturnValue({
|
||||||
|
isGitBackend,
|
||||||
|
});
|
||||||
|
|
||||||
|
export const currentBackend = jest.fn();
|
||||||
|
|
||||||
|
export const expandSearchEntries = jest.fn().mockImplementation(
|
||||||
|
(
|
||||||
|
entries: Entry[],
|
||||||
|
searchFields: string[],
|
||||||
|
): (Entry & {
|
||||||
|
field: string;
|
||||||
|
})[] => {
|
||||||
|
return actualExpandSearchEntries(entries, searchFields);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
export const getEntryField = jest.fn().mockImplementation((field: string, entry: Entry): string => {
|
||||||
|
return actualGetEntryField(field, entry);
|
||||||
|
});
|
||||||
|
|
||||||
|
export const mergeExpandedEntries = jest
|
||||||
|
.fn()
|
||||||
|
.mockImplementation((entries: (Entry & { field: string })[]): Entry[] => {
|
||||||
|
return actualMergeExpandedEntries(entries);
|
||||||
|
});
|
@ -1,3 +1,3 @@
|
|||||||
export default function cleanStack(parts: string[]) {
|
export default function urlJoin(...parts: string[]) {
|
||||||
return parts.join('/');
|
return parts.join('/');
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,7 @@ import trim from 'lodash/trim';
|
|||||||
import trimStart from 'lodash/trimStart';
|
import trimStart from 'lodash/trimStart';
|
||||||
import yaml from 'yaml';
|
import yaml from 'yaml';
|
||||||
|
|
||||||
import { resolveBackend } from '../backend';
|
import { resolveBackend } from '@staticcms/core/backend';
|
||||||
import { CONFIG_FAILURE, CONFIG_REQUEST, CONFIG_SUCCESS } from '../constants';
|
import { CONFIG_FAILURE, CONFIG_REQUEST, CONFIG_SUCCESS } from '../constants';
|
||||||
import validateConfig from '../constants/configSchema';
|
import validateConfig from '../constants/configSchema';
|
||||||
import {
|
import {
|
||||||
@ -124,7 +124,9 @@ function throwOnMissingDefaultLocale(i18n?: I18nInfo) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function applyDefaults(originalConfig: Config) {
|
export function applyDefaults<EF extends BaseField = UnknownField>(
|
||||||
|
originalConfig: Config<EF>,
|
||||||
|
): Config<EF> {
|
||||||
return produce(originalConfig, (config: Config) => {
|
return produce(originalConfig, (config: Config) => {
|
||||||
config.slug = config.slug || {};
|
config.slug = config.slug || {};
|
||||||
config.collections = config.collections || [];
|
config.collections = config.collections || [];
|
||||||
|
@ -133,7 +133,12 @@ export function extractSearchFields(searchFields: string[]) {
|
|||||||
}, '');
|
}, '');
|
||||||
}
|
}
|
||||||
|
|
||||||
export function expandSearchEntries(entries: Entry[], searchFields: string[]) {
|
export function expandSearchEntries(
|
||||||
|
entries: Entry[],
|
||||||
|
searchFields: string[],
|
||||||
|
): (Entry & {
|
||||||
|
field: string;
|
||||||
|
})[] {
|
||||||
// expand the entries for the purpose of the search
|
// expand the entries for the purpose of the search
|
||||||
const expandedEntries = entries.reduce((acc, e) => {
|
const expandedEntries = entries.reduce((acc, e) => {
|
||||||
const expandedFields = searchFields.reduce((acc, f) => {
|
const expandedFields = searchFields.reduce((acc, f) => {
|
||||||
@ -152,7 +157,7 @@ export function expandSearchEntries(entries: Entry[], searchFields: string[]) {
|
|||||||
return expandedEntries;
|
return expandedEntries;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function mergeExpandedEntries(entries: (Entry & { field: string })[]) {
|
export function mergeExpandedEntries(entries: (Entry & { field: string })[]): Entry[] {
|
||||||
// merge the search results by slug and only keep data that matched the search
|
// merge the search results by slug and only keep data that matched the search
|
||||||
const fields = entries.map(f => f.field);
|
const fields = entries.map(f => f.field);
|
||||||
const arrayPaths: Record<string, Set<string>> = {};
|
const arrayPaths: Record<string, Set<string>> = {};
|
||||||
|
@ -1,6 +1,4 @@
|
|||||||
import flow from 'lodash/flow';
|
|
||||||
import get from 'lodash/get';
|
import get from 'lodash/get';
|
||||||
import partialRight from 'lodash/partialRight';
|
|
||||||
|
|
||||||
import { COMMIT_AUTHOR, COMMIT_DATE } from '../constants/commitProps';
|
import { COMMIT_AUTHOR, COMMIT_DATE } from '../constants/commitProps';
|
||||||
import { sanitizeSlug } from './urlHelper';
|
import { sanitizeSlug } from './urlHelper';
|
||||||
@ -14,7 +12,15 @@ import {
|
|||||||
parseDateFromEntry,
|
parseDateFromEntry,
|
||||||
} from './widgets/stringTemplate';
|
} from './widgets/stringTemplate';
|
||||||
|
|
||||||
import type { BaseField, Collection, Config, Entry, EntryData, Slug } from '../interface';
|
import type {
|
||||||
|
BaseField,
|
||||||
|
Collection,
|
||||||
|
Config,
|
||||||
|
Entry,
|
||||||
|
EntryData,
|
||||||
|
Slug,
|
||||||
|
UnknownField,
|
||||||
|
} from '../interface';
|
||||||
|
|
||||||
const commitMessageTemplates = {
|
const commitMessageTemplates = {
|
||||||
create: 'Create {{collection}} “{{slug}}”',
|
create: 'Create {{collection}} “{{slug}}”',
|
||||||
@ -81,10 +87,14 @@ export function getProcessSegment(slugConfig?: Slug, ignoreValues?: string[]) {
|
|||||||
return (value: string) =>
|
return (value: string) =>
|
||||||
ignoreValues && ignoreValues.includes(value)
|
ignoreValues && ignoreValues.includes(value)
|
||||||
? value
|
? value
|
||||||
: flow([value => String(value), prepareSlug, partialRight(sanitizeSlug, slugConfig)])(value);
|
: sanitizeSlug(prepareSlug(String(value)), slugConfig);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function slugFormatter(collection: Collection, entryData: EntryData, slugConfig?: Slug) {
|
export function slugFormatter<EF extends BaseField = UnknownField>(
|
||||||
|
collection: Collection<EF>,
|
||||||
|
entryData: EntryData,
|
||||||
|
slugConfig?: Slug,
|
||||||
|
) {
|
||||||
const slugTemplate = collection.slug || '{{slug}}';
|
const slugTemplate = collection.slug || '{{slug}}';
|
||||||
|
|
||||||
const identifier = get(entryData, keyToPathArray(selectIdentifier(collection)));
|
const identifier = get(entryData, keyToPathArray(selectIdentifier(collection)));
|
||||||
@ -151,16 +161,10 @@ export function folderFormatter<EF extends BaseField>(
|
|||||||
);
|
);
|
||||||
|
|
||||||
const date = parseDateFromEntry(entry, selectInferredField(collection, 'date')) || null;
|
const date = parseDateFromEntry(entry, selectInferredField(collection, 'date')) || null;
|
||||||
const identifier = get(fields, keyToPathArray(selectIdentifier(collection)));
|
const slug = slugFormatter(collection, entry.data, slugConfig);
|
||||||
const processSegment = getProcessSegment(slugConfig, [defaultFolder, fields?.dirname as string]);
|
const processSegment = getProcessSegment(slugConfig, [defaultFolder, fields?.dirname as string]);
|
||||||
|
|
||||||
const mediaFolder = compileStringTemplate(
|
const mediaFolder = compileStringTemplate(folderTemplate, date, slug, fields, processSegment);
|
||||||
folderTemplate,
|
|
||||||
date,
|
|
||||||
identifier,
|
|
||||||
fields,
|
|
||||||
processSegment,
|
|
||||||
);
|
|
||||||
|
|
||||||
return mediaFolder;
|
return mediaFolder;
|
||||||
}
|
}
|
||||||
|
245
packages/core/src/lib/util/__tests__/media.util.spec.ts
Normal file
245
packages/core/src/lib/util/__tests__/media.util.spec.ts
Normal file
@ -0,0 +1,245 @@
|
|||||||
|
import { createMockCollection } from '@staticcms/test/data/collections.mock';
|
||||||
|
import { createMockConfig } from '@staticcms/test/data/config.mock';
|
||||||
|
import { createMockEntry } from '@staticcms/test/data/entry.mock';
|
||||||
|
import { mockImageField as mockBaseImageField } from '@staticcms/test/data/fields.mock';
|
||||||
|
import { selectMediaFolder } from '../media.util';
|
||||||
|
|
||||||
|
import type { FileOrImageField, FolderCollection, UnknownField } from '@staticcms/core/interface';
|
||||||
|
|
||||||
|
jest.mock('@staticcms/core/backend');
|
||||||
|
|
||||||
|
describe('media.util', () => {
|
||||||
|
describe('selectMediaFolder', () => {
|
||||||
|
const mockBaseCollection = createMockCollection<UnknownField>({
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
name: 'title',
|
||||||
|
widget: 'string',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
const mockBaseEntry = createMockEntry({
|
||||||
|
path: 'path/to/entry/index.md',
|
||||||
|
data: {
|
||||||
|
title: 'I am a title',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('top level', () => {
|
||||||
|
it('should default to top level config media_folder', () => {
|
||||||
|
const mockConfig = createMockConfig({
|
||||||
|
collections: [mockBaseCollection],
|
||||||
|
media_folder: 'path/to/media/folder',
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(selectMediaFolder(mockConfig, undefined, undefined, undefined)).toBe(
|
||||||
|
'path/to/media/folder',
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('entry', () => {
|
||||||
|
it('should default to top level config media_folder', () => {
|
||||||
|
const mockConfig = createMockConfig({
|
||||||
|
collections: [mockBaseCollection],
|
||||||
|
media_folder: 'path/to/media/folder',
|
||||||
|
});
|
||||||
|
|
||||||
|
const mockCollection = mockConfig.collections[0];
|
||||||
|
const mockImageField = (mockConfig.collections[0] as FolderCollection)
|
||||||
|
.fields[3] as FileOrImageField;
|
||||||
|
|
||||||
|
expect(selectMediaFolder(mockConfig, mockCollection, mockBaseEntry, mockImageField)).toBe(
|
||||||
|
'path/to/media/folder',
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('relative path', () => {
|
||||||
|
it('should use collection media_folder if available', () => {
|
||||||
|
const mockConfig = createMockConfig({
|
||||||
|
collections: [
|
||||||
|
createMockCollection<UnknownField>({
|
||||||
|
folder: 'base/folder',
|
||||||
|
media_folder: 'path/to/some/other/media/folder',
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
name: 'title',
|
||||||
|
widget: 'string',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
media_folder: 'path/to/media/folder',
|
||||||
|
});
|
||||||
|
|
||||||
|
const mockCollection = mockConfig.collections[0];
|
||||||
|
const mockImageField = (mockConfig.collections[0] as FolderCollection)
|
||||||
|
.fields[3] as FileOrImageField;
|
||||||
|
|
||||||
|
expect(selectMediaFolder(mockConfig, mockCollection, mockBaseEntry, mockImageField)).toBe(
|
||||||
|
'path/to/entry/path/to/some/other/media/folder',
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('template variable', () => {
|
||||||
|
it('should substitute field value', () => {
|
||||||
|
const mockConfig = createMockConfig({
|
||||||
|
collections: [
|
||||||
|
createMockCollection<UnknownField>({
|
||||||
|
folder: 'base/folder',
|
||||||
|
media_folder: 'path/to/some/other/media/{{fields.title}}',
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
name: 'title',
|
||||||
|
widget: 'string',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
media_folder: 'path/to/media/folder',
|
||||||
|
});
|
||||||
|
|
||||||
|
const mockCollection = mockConfig.collections[0];
|
||||||
|
const mockImageField = (mockConfig.collections[0] as FolderCollection)
|
||||||
|
.fields[3] as FileOrImageField;
|
||||||
|
|
||||||
|
expect(
|
||||||
|
selectMediaFolder(mockConfig, mockCollection, mockBaseEntry, mockImageField),
|
||||||
|
).toBe('path/to/entry/path/to/some/other/media/i-am-a-title');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should substitute slug', () => {
|
||||||
|
const mockConfig = createMockConfig({
|
||||||
|
collections: [
|
||||||
|
createMockCollection<UnknownField>({
|
||||||
|
folder: 'base/folder',
|
||||||
|
media_folder: 'path/to/some/other/media/{{slug}}',
|
||||||
|
slug: '{{fields.title}}-{{fields.name}}',
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
name: 'title',
|
||||||
|
widget: 'string',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'name',
|
||||||
|
widget: 'string',
|
||||||
|
},
|
||||||
|
mockBaseImageField,
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
media_folder: 'path/to/media/folder',
|
||||||
|
});
|
||||||
|
|
||||||
|
const mockCollection = mockConfig.collections[0];
|
||||||
|
const mockImageField = (mockConfig.collections[0] as FolderCollection)
|
||||||
|
.fields[3] as FileOrImageField;
|
||||||
|
|
||||||
|
const mockEntry = createMockEntry({
|
||||||
|
path: 'path/to/entry/index.md',
|
||||||
|
data: { title: 'i am a title', name: 'fish' },
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(selectMediaFolder(mockConfig, mockCollection, mockEntry, mockImageField)).toBe(
|
||||||
|
'path/to/entry/path/to/some/other/media/i-am-a-title-fish',
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('absolute path', () => {
|
||||||
|
it('should use collection media_folder if available', () => {
|
||||||
|
const mockConfig = createMockConfig({
|
||||||
|
collections: [
|
||||||
|
createMockCollection<UnknownField>({
|
||||||
|
folder: 'base/folder',
|
||||||
|
media_folder: '/path/to/some/other/media/folder',
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
name: 'title',
|
||||||
|
widget: 'string',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
media_folder: 'path/to/media/folder',
|
||||||
|
});
|
||||||
|
|
||||||
|
const mockCollection = mockConfig.collections[0];
|
||||||
|
const mockImageField = (mockConfig.collections[0] as FolderCollection)
|
||||||
|
.fields[3] as FileOrImageField;
|
||||||
|
|
||||||
|
expect(selectMediaFolder(mockConfig, mockCollection, mockBaseEntry, mockImageField)).toBe(
|
||||||
|
'path/to/some/other/media/folder',
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('template variable', () => {
|
||||||
|
it('should substitute field value', () => {
|
||||||
|
const mockConfig = createMockConfig({
|
||||||
|
collections: [
|
||||||
|
createMockCollection<UnknownField>({
|
||||||
|
folder: 'base/folder',
|
||||||
|
media_folder: '/path/to/some/other/media/{{fields.title}}',
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
name: 'title',
|
||||||
|
widget: 'string',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
media_folder: 'path/to/media/folder',
|
||||||
|
});
|
||||||
|
|
||||||
|
const mockCollection = mockConfig.collections[0];
|
||||||
|
const mockImageField = (mockConfig.collections[0] as FolderCollection)
|
||||||
|
.fields[3] as FileOrImageField;
|
||||||
|
|
||||||
|
expect(
|
||||||
|
selectMediaFolder(mockConfig, mockCollection, mockBaseEntry, mockImageField),
|
||||||
|
).toBe('path/to/some/other/media/i-am-a-title');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should substitute slug', () => {
|
||||||
|
const mockConfig = createMockConfig({
|
||||||
|
collections: [
|
||||||
|
createMockCollection<UnknownField>({
|
||||||
|
folder: 'base/folder',
|
||||||
|
media_folder: '/path/to/some/other/media/{{slug}}',
|
||||||
|
slug: '{{fields.title}}-{{fields.name}}',
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
name: 'title',
|
||||||
|
widget: 'string',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'name',
|
||||||
|
widget: 'string',
|
||||||
|
},
|
||||||
|
mockBaseImageField,
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
media_folder: 'path/to/media/folder',
|
||||||
|
});
|
||||||
|
|
||||||
|
const mockCollection = mockConfig.collections[0];
|
||||||
|
const mockImageField = (mockConfig.collections[0] as FolderCollection)
|
||||||
|
.fields[3] as FileOrImageField;
|
||||||
|
|
||||||
|
const mockEntry = createMockEntry({
|
||||||
|
path: 'path/to/entry/index.md',
|
||||||
|
data: { title: 'i am a title', name: 'fish' },
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(selectMediaFolder(mockConfig, mockCollection, mockEntry, mockImageField)).toBe(
|
||||||
|
'path/to/some/other/media/i-am-a-title-fish',
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
@ -1,5 +1,5 @@
|
|||||||
import trim from 'lodash/trim';
|
import trim from 'lodash/trim';
|
||||||
import { dirname, join } from 'path';
|
import { dirname } from 'path';
|
||||||
|
|
||||||
import { basename, isAbsolutePath } from '.';
|
import { basename, isAbsolutePath } from '.';
|
||||||
import { folderFormatter } from '../formatters';
|
import { folderFormatter } from '../formatters';
|
||||||
@ -243,12 +243,15 @@ export function selectMediaFolder<EF extends BaseField>(
|
|||||||
} else if (hasCustomFolder('media_folder', collection, entryMap?.slug, field)) {
|
} else if (hasCustomFolder('media_folder', collection, entryMap?.slug, field)) {
|
||||||
const folder = evaluateFolder('media_folder', config, collection!, entryMap, field);
|
const folder = evaluateFolder('media_folder', config, collection!, entryMap, field);
|
||||||
if (folder.startsWith('/')) {
|
if (folder.startsWith('/')) {
|
||||||
mediaFolder = join(folder);
|
mediaFolder = folder.replace(/^[/]*/g, '');
|
||||||
} else {
|
} else {
|
||||||
const entryPath = entryMap?.path;
|
const entryPath = entryMap?.path;
|
||||||
mediaFolder = entryPath
|
mediaFolder = entryPath
|
||||||
? join(dirname(entryPath), folder)
|
? joinUrlPath(dirname(entryPath), folder)
|
||||||
: join(collection && 'folder' in collection ? collection.folder : '', DRAFT_MEDIA_FILES);
|
: joinUrlPath(
|
||||||
|
collection && 'folder' in collection ? collection.folder : '',
|
||||||
|
DRAFT_MEDIA_FILES,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -289,7 +292,7 @@ export function selectMediaFilePublicPath<EF extends BaseField>(
|
|||||||
return joinUrlPath(selectedPublicFolder, basename(mediaPath));
|
return joinUrlPath(selectedPublicFolder, basename(mediaPath));
|
||||||
}
|
}
|
||||||
|
|
||||||
return join(selectedPublicFolder, basename(mediaPath));
|
return joinUrlPath(selectedPublicFolder, basename(mediaPath));
|
||||||
}
|
}
|
||||||
|
|
||||||
export function selectMediaFilePath(
|
export function selectMediaFilePath(
|
||||||
@ -331,5 +334,5 @@ export function selectMediaFilePath(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return join(mediaFolder, basename(mediaPath));
|
return joinUrlPath(mediaFolder, basename(mediaPath));
|
||||||
}
|
}
|
||||||
|
@ -193,6 +193,10 @@ export function compileStringTemplate(
|
|||||||
data: ObjectValue | undefined | null = {},
|
data: ObjectValue | undefined | null = {},
|
||||||
processor?: (value: string) => string,
|
processor?: (value: string) => string,
|
||||||
) {
|
) {
|
||||||
|
if (template === '') {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
let missingRequiredDate;
|
let missingRequiredDate;
|
||||||
|
|
||||||
// Turn off date processing (support for replacements like `{{year}}`), by passing in
|
// Turn off date processing (support for replacements like `{{year}}`), by passing in
|
||||||
|
@ -18,9 +18,9 @@ import { mockFileField } from '@staticcms/test/data/fields.mock';
|
|||||||
import { createWidgetControlHarness } from '@staticcms/test/harnesses/widget.harness';
|
import { createWidgetControlHarness } from '@staticcms/test/harnesses/widget.harness';
|
||||||
import withFileControl from '../withFileControl';
|
import withFileControl from '../withFileControl';
|
||||||
|
|
||||||
import type { Config, FileOrImageField, MediaFile } from '@staticcms/core/interface';
|
import type { Config, MediaFile } from '@staticcms/core/interface';
|
||||||
|
|
||||||
const FileControl = withFileControl();
|
jest.mock('@staticcms/core/backend');
|
||||||
|
|
||||||
jest.mock('@staticcms/core/lib/hooks/useMediaFiles', () => {
|
jest.mock('@staticcms/core/lib/hooks/useMediaFiles', () => {
|
||||||
const mockMediaFiles: MediaFile[] = [
|
const mockMediaFiles: MediaFile[] = [
|
||||||
@ -54,10 +54,11 @@ jest.mock('@staticcms/core/actions/mediaLibrary', () => ({
|
|||||||
jest.mock('@staticcms/core/lib/hooks/useMediaAsset', () => (url: string) => url);
|
jest.mock('@staticcms/core/lib/hooks/useMediaAsset', () => (url: string) => url);
|
||||||
|
|
||||||
describe('File Control', () => {
|
describe('File Control', () => {
|
||||||
|
const FileControl = withFileControl();
|
||||||
const collection = createMockCollection({}, mockFileField);
|
const collection = createMockCollection({}, mockFileField);
|
||||||
const config = createMockConfig({
|
const config = createMockConfig({
|
||||||
collections: [collection],
|
collections: [collection],
|
||||||
}) as unknown as Config<FileOrImageField>;
|
});
|
||||||
|
|
||||||
const mockInsertMedia = insertMedia as jest.Mock;
|
const mockInsertMedia = insertMedia as jest.Mock;
|
||||||
|
|
||||||
|
@ -32,6 +32,8 @@ import type { MdEditor } from '@staticcms/markdown/plate/plateTypes';
|
|||||||
import type { TRange } from '@udecode/plate';
|
import type { TRange } from '@udecode/plate';
|
||||||
import type { FC } from 'react';
|
import type { FC } from 'react';
|
||||||
|
|
||||||
|
jest.mock('@staticcms/core/backend');
|
||||||
|
|
||||||
interface BalloonToolbarWrapperProps {
|
interface BalloonToolbarWrapperProps {
|
||||||
useMdx?: boolean;
|
useMdx?: boolean;
|
||||||
}
|
}
|
||||||
|
@ -27,6 +27,8 @@ import type {
|
|||||||
StringOrTextField,
|
StringOrTextField,
|
||||||
} from '@staticcms/core/interface';
|
} from '@staticcms/core/interface';
|
||||||
|
|
||||||
|
jest.mock('@staticcms/core/backend');
|
||||||
|
|
||||||
const dateField: DateTimeField = {
|
const dateField: DateTimeField = {
|
||||||
widget: 'datetime',
|
widget: 'datetime',
|
||||||
name: 'date',
|
name: 'date',
|
||||||
@ -72,20 +74,22 @@ const bodyField: StringOrTextField = {
|
|||||||
name: 'body',
|
name: 'body',
|
||||||
};
|
};
|
||||||
|
|
||||||
const searchCollection = createMockCollection(
|
|
||||||
{
|
|
||||||
name: 'posts',
|
|
||||||
},
|
|
||||||
dateField,
|
|
||||||
authorField,
|
|
||||||
coAuthorsField,
|
|
||||||
tagsField,
|
|
||||||
bodyField,
|
|
||||||
) as unknown as Collection;
|
|
||||||
|
|
||||||
const config = createMockConfig({
|
const config = createMockConfig({
|
||||||
collections: [searchCollection] as unknown as Collection[],
|
collections: [
|
||||||
}) as unknown as Config<RelationField>;
|
createMockCollection(
|
||||||
|
{
|
||||||
|
name: 'posts',
|
||||||
|
},
|
||||||
|
dateField,
|
||||||
|
authorField,
|
||||||
|
coAuthorsField,
|
||||||
|
tagsField,
|
||||||
|
bodyField,
|
||||||
|
) as unknown as Collection<RelationField>,
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
const searchCollection = config.collections[0];
|
||||||
|
|
||||||
describe(RelationControl.name, () => {
|
describe(RelationControl.name, () => {
|
||||||
const renderControl = createWidgetControlHarness(RelationControl, {
|
const renderControl = createWidgetControlHarness(RelationControl, {
|
||||||
@ -649,7 +653,7 @@ describe(RelationControl.name, () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('parse options', () => {
|
describe('parse options', () => {
|
||||||
it('should default to valueField if displayFields is not set', async () => {
|
fit('should default to valueField if displayFields is not set', async () => {
|
||||||
const field: RelationField = {
|
const field: RelationField = {
|
||||||
label: 'Relation',
|
label: 'Relation',
|
||||||
name: 'relation',
|
name: 'relation',
|
||||||
|
@ -1,11 +1,14 @@
|
|||||||
/* eslint-disable import/prefer-default-export */
|
/* eslint-disable import/prefer-default-export */
|
||||||
|
import { applyDefaults } from '@staticcms/core/actions/config';
|
||||||
|
|
||||||
import type { BaseField, Config } from '@staticcms/core';
|
import type { BaseField, Config } from '@staticcms/core';
|
||||||
|
|
||||||
export const createMockConfig = <EF extends BaseField>(
|
export const createMockConfig = <EF extends BaseField>(
|
||||||
options: Omit<Partial<Config<EF>>, 'collections'> & Pick<Config<EF>, 'collections'>,
|
options: Omit<Partial<Config<EF>>, 'collections'> & Pick<Config<EF>, 'collections'>,
|
||||||
): Config<EF> => ({
|
): Config<EF> =>
|
||||||
backend: {
|
applyDefaults({
|
||||||
name: 'test-repo',
|
backend: {
|
||||||
},
|
name: 'test-repo',
|
||||||
...options,
|
},
|
||||||
});
|
...options,
|
||||||
|
});
|
||||||
|
@ -10,6 +10,8 @@ import type {
|
|||||||
WidgetControlProps,
|
WidgetControlProps,
|
||||||
} from '@staticcms/core';
|
} from '@staticcms/core';
|
||||||
|
|
||||||
|
jest.mock('@staticcms/core/backend');
|
||||||
|
|
||||||
export const createMockWidgetControlProps = <
|
export const createMockWidgetControlProps = <
|
||||||
T extends ValueOrNestedValue,
|
T extends ValueOrNestedValue,
|
||||||
F extends BaseField = UnknownField,
|
F extends BaseField = UnknownField,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user