import { GroupContent, GroupContentType, GroupItemContent, GroupItemContentType, isGroupContent, } from "@prismicio/types-internal/lib/content/fields" import type { NestableContent } from "@prismicio/types-internal/lib/content/fields/nestable/NestableContent" import { type Group, type GroupWidgetDiff, type NestableWidget, DiffOperation, GroupFieldType, } from "@prismicio/types-internal/lib/customtypes" import { v4 as uuid } from "uuid" import { IncompatibleMockConfigError } from "../../errors" import type { NestableWidgetMockConfig } from ".." import type { WidgetMockWithDiff } from "../Mock" import type { MockConfig } from "../MockConfig" import { NestableWidgetMock } from "." export interface GroupMockConfig extends MockConfig< Group["type"], Array> > { nbBlocks?: number fields?: Partial> } export function randomGroupBlock( fieldsDefs: Array<[string, NestableWidget | Group]>, mockConfigs?: Partial< Record >, ): Array<[string, NestableContent | GroupContent]> { return fieldsDefs.map(([key, field]: [string, NestableWidget | Group]) => { const mockConfig = mockConfigs?.[key] switch (field.type) { case GroupFieldType: { if (mockConfig && mockConfig.type !== GroupFieldType) { throw new IncompatibleMockConfigError(mockConfig.type, field.type) } return [key, GroupMock.generate(field, mockConfig)] } default: { if ( !mockConfig || mockConfig.type === field.type || (mockConfig.type == "Repeatable" && field.type === "Link" && field.config?.repeat) ) { return [key, NestableWidgetMock.generate(field, mockConfig)] } throw new IncompatibleMockConfigError(mockConfig.type, field.type) } } }) } function random( def: Group, nbBlocks: number | undefined = 1, fields: Partial< Record > = {}, ): Array> { if (!def.config || !def.config.fields) { return [] } const fieldsDefs = Object.entries(def.config.fields) return Array(nbBlocks) .fill(null) .map(() => randomGroupBlock(fieldsDefs, fields)) } export const GroupMock: WidgetMockWithDiff< Group, GroupContent, GroupMockConfig > = { generate(def: Group, config?: GroupMockConfig): GroupContent { const value = config?.value || random(def, config?.nbBlocks, config?.fields) return { __TYPE__: GroupContentType, value: value.map((item) => ({ __TYPE__: GroupItemContentType, key: uuid(), value: item, })), } as GroupContent }, patch( diff: GroupWidgetDiff, content?: GroupContent, config?: GroupMockConfig, ) { if (diff.op === DiffOperation.Removed) return // Retrieve group item values or create empty new ones after mock config const items: GroupItemContent[] = content?.value || Array.from({ length: config?.nbBlocks ?? 1 }, () => ({ __TYPE__: GroupItemContentType, key: uuid(), value: [], })) // Patch each group item values const patched = items.map((item) => { // Take current group item value const valueObject = Object.fromEntries(item.value) // Patch each group item widget value Object.entries(diff.value.config.fields ?? {}).forEach( ([key, fieldDiff]) => { const itemContent = valueObject[key] const widgetConfig = config?.fields?.[key] let patchedWidget if ( isGroupContent(itemContent) && (!widgetConfig || widgetConfig?.type === GroupFieldType) ) { patchedWidget = GroupMock.patch( fieldDiff, itemContent, widgetConfig, ) } else if ( !isGroupContent(itemContent) && widgetConfig?.type !== GroupFieldType ) { patchedWidget = NestableWidgetMock.patch( fieldDiff, itemContent, widgetConfig, ) } if (patchedWidget) { // If the widget was patched, update the group item widget value valueObject[key] = patchedWidget } else { // If the widget was removed, delete it from the group item value delete valueObject[key] } }, ) return { __TYPE__: GroupItemContentType, ...(item.key ? { key: item.key } : {}), value: Object.entries(valueObject), } }) return { __TYPE__: GroupContentType, value: patched, } }, }