UNPKG

@nocobase/flow-engine

Version:

A standalone flow engine for NocoBase, managing workflows, models, and actions.

193 lines (173 loc) 7.52 kB
/** * This file is part of the NocoBase (R) project. * Copyright (c) 2020-2024 NocoBase Co., Ltd. * Authors: NocoBase Team. * * This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License. * For more information, please refer to: https://www.nocobase.com/agreement. */ import { describe, expect, it } from 'vitest'; import { Collection } from '../../../data-source'; import { FlowEngine } from '../../../flowEngine'; import { FlowModel } from '../../../models'; import { buildWrapperFieldChildren } from '../utils'; describe('buildWrapperFieldChildren', () => { it('generates a group with children per field and correct defaults', () => { const engine = new FlowEngine(); const ds = engine.context.dataSourceManager.getDataSource('main'); const collection = new Collection({ name: 'posts', fields: [ { name: 'title', interface: 'input', title: 'Title' }, { name: 'status', interface: 'select', title: 'Status' }, { name: 'ignored', interface: undefined }, ], }); collection.setDataSource(ds); ds.addCollection(collection); const model = engine.createModel({ use: 'FlowModel' }); const ctx = model.context; ctx.defineProperty('collection', { value: collection }); const groups = buildWrapperFieldChildren(ctx, { useModel: 'TableColumnModel', fieldUseModel: 'ReadPrettyFieldModel', }); expect(groups).toHaveLength(1); const group = groups[0]; expect(group.key).toBe(`addField_${collection.name}`); expect(group.type).toBe('group'); expect(group.searchable).toBe(true); expect(group.searchPlaceholder).toBe('Search fields'); const children = group.children as any[]; // filters out field without interface expect(children.map((c) => c.key)).toEqual(['title', 'status']); expect(children.map((c) => c.label)).toEqual(['Title', 'Status']); // wrapper model is fixed from options children.forEach((c) => expect(c.useModel).toBe('TableColumnModel')); }); it('resolves child use via fieldUseModel function and builds stepParams', async () => { const engine = new FlowEngine(); const ds = engine.context.dataSourceManager.getDataSource('main'); const collection = new Collection({ name: 'posts', fields: [ { name: 'title', interface: 'input', title: 'Title' }, { name: 'status', interface: 'select', title: 'Status' }, ], }); collection.setDataSource(ds); ds.addCollection(collection); const model = engine.createModel({ use: 'FlowModel' }); const ctx = model.context; ctx.defineProperty('collection', { value: collection }); const groups = buildWrapperFieldChildren(ctx, { useModel: 'DetailsItemModel', fieldUseModel: (f) => (f.interface === 'select' ? 'SelectPrettyFieldModel' : 'ReadPrettyFieldModel'), }); const group = groups[0]; const children = group.children; const titleItem = children.find((c) => c.key === 'title'); const statusItem = children.find((c) => c.key === 'status'); const titleCreate = await (typeof titleItem.createModelOptions === 'function' ? titleItem.createModelOptions(ctx) : titleItem.createModelOptions); const statusCreate = await (typeof statusItem.createModelOptions === 'function' ? statusItem.createModelOptions(ctx) : statusItem.createModelOptions); expect(titleCreate.use).toBe('DetailsItemModel'); expect(titleCreate.stepParams).toEqual({ fieldSettings: { init: { dataSourceKey: 'main', collectionName: 'posts', fieldPath: 'title', }, }, }); expect(titleCreate.subModels.field.use).toBe('ReadPrettyFieldModel'); expect(titleCreate.subModels.field.stepParams).toEqual({ fieldSettings: { init: { dataSourceKey: 'main', collectionName: 'posts', fieldPath: 'title', }, }, }); expect(statusCreate.subModels.field.use).toBe('SelectPrettyFieldModel'); expect(statusCreate.stepParams.fieldSettings.init.fieldPath).toBe('status'); }); it('toggleable checks step params for fieldPath (using real FlowModel)', () => { const engine = new FlowEngine(); const ds = engine.context.dataSourceManager.getDataSource('main'); const collection = new Collection({ name: 'posts', fields: [ { name: 'title', interface: 'input', title: 'Title' }, { name: 'status', interface: 'select', title: 'Status' }, ], }); collection.setDataSource(ds); ds.addCollection(collection); const model = engine.createModel({ use: 'FlowModel' }); const ctx = model.context; ctx.defineProperty('collection', { value: collection }); const groups = buildWrapperFieldChildren(ctx, { useModel: 'TableColumnModel', fieldUseModel: 'ReadPrettyFieldModel', }); const group = groups[0]; const [titleItem, statusItem] = group.children; // create real FlowModel instances and set stepParams const titleModel = engine.createModel<FlowModel>({ use: 'FlowModel' }); titleModel.setStepParams('fieldSettings', 'init', { fieldPath: 'title' }); const statusModel = engine.createModel<FlowModel>({ use: 'FlowModel' }); statusModel.setStepParams('fieldSettings', 'init', { fieldPath: 'status' }); expect((titleItem.toggleable as (model: FlowModel) => boolean)(titleModel)).toBe(true); expect((titleItem.toggleable as (model: FlowModel) => boolean)(statusModel)).toBe(false); expect((statusItem.toggleable as (model: FlowModel) => boolean)(statusModel)).toBe(true); }); it('prefers ctx.model.collection over ctx.collection for sub-table scenario', async () => { const engine = new FlowEngine(); const ds = engine.context.dataSourceManager.getDataSource('main'); // Main collection (block-level) const posts = new Collection({ name: 'posts', fields: [ { name: 'title', interface: 'input', title: 'Title' }, { name: 'category', interface: 'select', title: 'Category' }, ], }); posts.setDataSource(ds); ds.addCollection(posts); // Target collection (association sub-table) const comments = new Collection({ name: 'comments', fields: [ { name: 'content', interface: 'input', title: 'Content' }, { name: 'status', interface: 'select', title: 'Status' }, ], }); comments.setDataSource(ds); ds.addCollection(comments); // we can't import the real sub table model, as they are in client core package const model = engine.createModel({ use: 'FlowModel' }); const ctx = model.context; // Block context collection is posts ctx.defineProperty('collection', { value: posts }); // But model.collection points to the association target collection (model as any).collection = comments; const groups = buildWrapperFieldChildren(ctx, { useModel: 'SubTableColumnModel', fieldUseModel: 'FormFieldModel', }); const group = groups[0]; const children = group.children as any[]; expect(children.map((c) => c.key)).toEqual(['content', 'status']); const contentItem = children.find((c) => c.key === 'content'); const contentCreate = await (typeof contentItem.createModelOptions === 'function' ? contentItem.createModelOptions(ctx) : contentItem.createModelOptions); expect(contentCreate.stepParams.fieldSettings.init.collectionName).toBe('comments'); }); });