Added more componet store unit test converage. Added more fixtures. Refactored some component caching logic causing issus with testing in to the component store
This commit is contained in:
parent
92d2ad69cc
commit
b73156e0c7
|
@ -4,7 +4,6 @@ import {
|
|||
findAllMatchingComponents,
|
||||
findComponent,
|
||||
findComponentPath,
|
||||
getComponentSettings,
|
||||
} from "stores/builder/components/utils"
|
||||
import {
|
||||
currentAsset,
|
||||
|
@ -281,7 +280,7 @@ export const getActionProviders = (
|
|||
* Gets a datasource object for a certain data provider component
|
||||
*/
|
||||
export const getDatasourceForProvider = (asset, component) => {
|
||||
const settings = getComponentSettings(component?._component)
|
||||
const settings = componentStore.getComponentSettings(component?._component)
|
||||
|
||||
// If this component has a dataProvider setting, go up the stack and use it
|
||||
const dataProviderSetting = settings.find(setting => {
|
||||
|
@ -706,7 +705,7 @@ export const getEventContextBindings = ({
|
|||
const definition =
|
||||
componentDefinition ?? componentStore.getDefinition(component?._component)
|
||||
|
||||
const settings = getComponentSettings(component?._component)
|
||||
const settings = componentStore.getComponentSettings(component?._component)
|
||||
const eventSetting = settings.find(setting => setting.key === settingKey)
|
||||
|
||||
if (eventSetting?.context?.length) {
|
||||
|
@ -1011,7 +1010,7 @@ export const buildFormSchema = (component, asset) => {
|
|||
}
|
||||
|
||||
// Otherwise find all field component children
|
||||
const settings = getComponentSettings(component._component)
|
||||
const settings = componentStore.getComponentSettings(component._component)
|
||||
const fieldSetting = settings.find(
|
||||
setting => setting.key === "field" && setting.type.startsWith("field/")
|
||||
)
|
||||
|
@ -1037,7 +1036,7 @@ export const getAllStateVariables = () => {
|
|||
let eventSettings = []
|
||||
getAllAssets().forEach(asset => {
|
||||
findAllMatchingComponents(asset.props, component => {
|
||||
const settings = getComponentSettings(component._component)
|
||||
const settings = componentStore.getComponentSettings(component._component)
|
||||
settings
|
||||
.filter(setting => setting.type === "event")
|
||||
.forEach(setting => {
|
||||
|
|
|
@ -13,10 +13,9 @@
|
|||
import { generate } from "shortid"
|
||||
import DrawerBindableInput from "components/common/bindings/DrawerBindableInput.svelte"
|
||||
import { LuceneUtils, Constants } from "@budibase/frontend-core"
|
||||
import { selectedComponent } from "stores/builder"
|
||||
import { selectedComponent, componentStore } from "stores/builder"
|
||||
import { getComponentForSetting } from "components/design/settings/componentSettings"
|
||||
import PropertyControl from "components/design/settings/controls/PropertyControl.svelte"
|
||||
import { getComponentSettings } from "stores/builder/components/utils"
|
||||
|
||||
export let conditions = []
|
||||
export let bindings = []
|
||||
|
@ -56,11 +55,13 @@
|
|||
]
|
||||
|
||||
let dragDisabled = true
|
||||
$: settings = getComponentSettings($selectedComponent?._component)?.concat({
|
||||
label: "Custom CSS",
|
||||
key: "_css",
|
||||
type: "text",
|
||||
})
|
||||
$: settings = componentStore
|
||||
.getComponentSettings($selectedComponent?._component)
|
||||
?.concat({
|
||||
label: "Custom CSS",
|
||||
key: "_css",
|
||||
type: "text",
|
||||
})
|
||||
$: settingOptions = settings
|
||||
.filter(setting => setting.supportsConditions !== false)
|
||||
.map(setting => ({
|
||||
|
|
|
@ -5,20 +5,20 @@ import { Helpers } from "@budibase/bbui"
|
|||
import analytics, { Events } from "analytics"
|
||||
import { makePropSafe as safe } from "@budibase/string-templates"
|
||||
import {
|
||||
getComponentSettings,
|
||||
findComponentPath,
|
||||
findClosestMatchingComponent,
|
||||
findComponent,
|
||||
findComponentParent,
|
||||
findAllMatchingComponents,
|
||||
makeComponentUnique,
|
||||
} from "../components/utils"
|
||||
} from "stores/builder/components/utils"
|
||||
import { getComponentFieldOptions } from "helpers/formFields"
|
||||
import { selectedScreen } from "../screens"
|
||||
import { screenStore, appStore, previewStore, tables } from "stores/builder"
|
||||
import { buildFormSchema, getSchemaForDatasource } from "builder/dataBinding"
|
||||
import {
|
||||
BUDIBASE_INTERNAL_DB_ID,
|
||||
DEFAULT_BB_DATASOURCE_ID,
|
||||
DB_TYPE_INTERNAL,
|
||||
DB_TYPE_EXTERNAL,
|
||||
} from "constants/backend"
|
||||
|
@ -26,10 +26,11 @@ import BudiStore from "../BudiStore"
|
|||
import { Utils } from "@budibase/frontend-core"
|
||||
|
||||
export const INITIAL_COMPONENTS_STATE = {
|
||||
components: [],
|
||||
components: {},
|
||||
customComponents: [],
|
||||
selectedComponentId: null,
|
||||
componentToPaste: null,
|
||||
settingsCache: {},
|
||||
}
|
||||
|
||||
export class ComponentStore extends BudiStore {
|
||||
|
@ -63,6 +64,9 @@ export class ComponentStore extends BudiStore {
|
|||
this.updateSetting = this.updateSetting.bind(this)
|
||||
this.updateComponentSetting = this.updateComponentSetting.bind(this)
|
||||
this.addParent = this.addParent.bind(this)
|
||||
this.isCached = this.isCached.bind(this)
|
||||
this.cacheSettings = this.cacheSettings.bind(this)
|
||||
this.getComponentSettings = this.getComponentSettings.bind(this)
|
||||
|
||||
this.selected = derived(
|
||||
[this.store, selectedScreen],
|
||||
|
@ -112,14 +116,14 @@ export class ComponentStore extends BudiStore {
|
|||
*/
|
||||
async refreshDefinitions(appId) {
|
||||
if (!appId) {
|
||||
appId = get(this.store).appId
|
||||
return
|
||||
}
|
||||
|
||||
// Fetch definitions and filter out custom component definitions so we
|
||||
// can flag them
|
||||
const components = await API.fetchComponentLibDefinitions(appId)
|
||||
const customComponents = Object.keys(components).filter(name =>
|
||||
name.startsWith("plugin/")
|
||||
const customComponents = Object.keys(components).filter(key =>
|
||||
key.startsWith("plugin/")
|
||||
)
|
||||
|
||||
// Update store
|
||||
|
@ -136,17 +140,17 @@ export class ComponentStore extends BudiStore {
|
|||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {string} componentName
|
||||
* Retrieve the component definition object
|
||||
* @param {string} componentType
|
||||
* @example
|
||||
* '@budibase/standard-components/container'
|
||||
* @returns {object}
|
||||
*/
|
||||
getDefinition(componentName) {
|
||||
if (!componentName) {
|
||||
getDefinition(componentType) {
|
||||
if (!componentType) {
|
||||
return null
|
||||
}
|
||||
return get(this.store).components[componentName]
|
||||
return get(this.store).components[componentType]
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -160,7 +164,7 @@ export class ComponentStore extends BudiStore {
|
|||
// Try to use their own internal table first
|
||||
let table = validTables.find(table => {
|
||||
return (
|
||||
table.sourceId !== BUDIBASE_INTERNAL_DB_ID &&
|
||||
table.sourceId === BUDIBASE_INTERNAL_DB_ID &&
|
||||
table.sourceType === DB_TYPE_INTERNAL
|
||||
)
|
||||
})
|
||||
|
@ -171,7 +175,7 @@ export class ComponentStore extends BudiStore {
|
|||
// Then try sample data
|
||||
table = validTables.find(table => {
|
||||
return (
|
||||
table.sourceId === BUDIBASE_INTERNAL_DB_ID &&
|
||||
table.sourceId === DEFAULT_BB_DATASOURCE_ID &&
|
||||
table.sourceType === DB_TYPE_INTERNAL
|
||||
)
|
||||
})
|
||||
|
@ -231,7 +235,7 @@ export class ComponentStore extends BudiStore {
|
|||
return
|
||||
}
|
||||
const defaultDS = this.getDefaultDatasource()
|
||||
const settings = getComponentSettings(component._component)
|
||||
const settings = this.getComponentSettings(component._component)
|
||||
const { parent, screen, useDefaultValues } = opts || {}
|
||||
const treeId = parent?._id || component._id
|
||||
if (!screen) {
|
||||
|
@ -933,7 +937,7 @@ export class ComponentStore extends BudiStore {
|
|||
return false
|
||||
}
|
||||
|
||||
const settings = getComponentSettings(component._component)
|
||||
const settings = this.getComponentSettings(component._component)
|
||||
const updatedSetting = settings.find(setting => setting.key === name)
|
||||
|
||||
// Reset dependent fields
|
||||
|
@ -1057,6 +1061,82 @@ export class ComponentStore extends BudiStore {
|
|||
return state
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the components settings have been cached
|
||||
* @param {string} componentType
|
||||
* @example
|
||||
* '@budibase/standard-components/container'
|
||||
* @returns {boolean}
|
||||
*/
|
||||
isCached(componentType) {
|
||||
const settings = get(this.store).settingsCache
|
||||
return componentType in settings
|
||||
}
|
||||
|
||||
/**
|
||||
* Cache component settings
|
||||
* @param {string} componentType
|
||||
* @param {object} definition
|
||||
* @example
|
||||
* '@budibase/standard-components/container'
|
||||
* @returns {boolean}
|
||||
*/
|
||||
cacheSettings(componentType, definition) {
|
||||
let settings = []
|
||||
if (definition && componentType) {
|
||||
settings = definition.settings?.filter(setting => !setting.section) ?? []
|
||||
definition.settings
|
||||
?.filter(setting => setting.section)
|
||||
.forEach(section => {
|
||||
settings = settings.concat(
|
||||
(section.settings || []).map(setting => ({
|
||||
...setting,
|
||||
section: section.name,
|
||||
}))
|
||||
)
|
||||
})
|
||||
this.update(state => ({
|
||||
...state,
|
||||
settingsCache: {
|
||||
...state.settingsCache,
|
||||
[componentType]: settings,
|
||||
},
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve an array of the component settings.
|
||||
* These settings are cached because they cannot change at run time.
|
||||
*
|
||||
* Searches a component's definition for a setting matching a certain predicate.
|
||||
* @param {string} componentType
|
||||
* @example
|
||||
* '@budibase/standard-components/container'
|
||||
* @returns {Array<object>}
|
||||
*/
|
||||
getComponentSettings(componentType) {
|
||||
if (!componentType) {
|
||||
return []
|
||||
}
|
||||
|
||||
// Ensure whole component name is used
|
||||
if (
|
||||
!componentType.startsWith("plugin/") &&
|
||||
!componentType.startsWith("@budibase")
|
||||
) {
|
||||
componentType = `@budibase/standard-components/${componentType}`
|
||||
}
|
||||
|
||||
if (this.isCached(componentType)) {
|
||||
return get(this.store).settingsCache[componentType]
|
||||
} else {
|
||||
const def = this.getDefinition(componentType)
|
||||
this.cacheSettings(componentType, def)
|
||||
return get(this.store).settingsCache[componentType]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const componentStore = new ComponentStore()
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { componentStore } from "."
|
||||
import { componentStore } from "stores/builder"
|
||||
import { get } from "svelte/store"
|
||||
import { Helpers } from "@budibase/bbui"
|
||||
import {
|
||||
|
@ -134,50 +134,6 @@ const searchComponentTree = (rootComponent, matchComponent) => {
|
|||
return null
|
||||
}
|
||||
|
||||
/**
|
||||
* Searches a component's definition for a setting matching a certain predicate.
|
||||
* These settings are cached because they cannot change at run time.
|
||||
*/
|
||||
let componentSettingCache = {}
|
||||
export const getComponentSettings = componentType => {
|
||||
if (!componentType) {
|
||||
return []
|
||||
}
|
||||
|
||||
// Ensure whole component name is used
|
||||
if (
|
||||
!componentType.startsWith("plugin/") &&
|
||||
!componentType.startsWith("@budibase")
|
||||
) {
|
||||
componentType = `@budibase/standard-components/${componentType}`
|
||||
}
|
||||
|
||||
// Check if we have cached this type already
|
||||
if (componentSettingCache[componentType]) {
|
||||
return componentSettingCache[componentType]
|
||||
}
|
||||
|
||||
// Otherwise get the settings and cache them
|
||||
const def = componentStore.getDefinition(componentType)
|
||||
let settings = []
|
||||
if (def) {
|
||||
settings = def.settings?.filter(setting => !setting.section) ?? []
|
||||
def.settings
|
||||
?.filter(setting => setting.section)
|
||||
.forEach(section => {
|
||||
settings = settings.concat(
|
||||
(section.settings || []).map(setting => ({
|
||||
...setting,
|
||||
section: section.name,
|
||||
}))
|
||||
)
|
||||
})
|
||||
}
|
||||
componentSettingCache[componentType] = settings
|
||||
|
||||
return settings
|
||||
}
|
||||
|
||||
/**
|
||||
* Randomises a components ID's, including all child component IDs, and also
|
||||
* updates all data bindings to still be valid.
|
||||
|
|
|
@ -1,13 +1,82 @@
|
|||
import { it, expect, describe, beforeEach, vi } from "vitest"
|
||||
import { get } from "svelte/store"
|
||||
import { get, writable } from "svelte/store"
|
||||
import {
|
||||
INITIAL_COMPONENTS_STATE,
|
||||
ComponentStore,
|
||||
} from "stores/builder/components"
|
||||
import { API } from "api"
|
||||
import { appStore, tables } from "stores/builder"
|
||||
import {
|
||||
componentDefinitionMap,
|
||||
getComponentFixture,
|
||||
getScreenFixture,
|
||||
pluginDefinitionMap,
|
||||
clientFeaturesResp,
|
||||
sampleTableDoc,
|
||||
internalTableDoc,
|
||||
userTableDoc,
|
||||
externalTableDoc,
|
||||
componentsToNested,
|
||||
} from "./fixtures"
|
||||
import {
|
||||
DB_TYPE_INTERNAL,
|
||||
DB_TYPE_EXTERNAL,
|
||||
DEFAULT_BB_DATASOURCE_ID,
|
||||
} from "constants/backend"
|
||||
|
||||
// Could move to fixtures
|
||||
const COMP_PREFIX = "@budibase/standard-components"
|
||||
|
||||
vi.mock("api", () => {
|
||||
return {
|
||||
API: {
|
||||
fetchComponentLibDefinitions: vi.fn(),
|
||||
},
|
||||
}
|
||||
})
|
||||
|
||||
vi.mock("stores/builder", async () => {
|
||||
const mockAppStore = writable()
|
||||
const appStore = {
|
||||
subscribe: mockAppStore.subscribe,
|
||||
update: mockAppStore.update,
|
||||
set: mockAppStore.set,
|
||||
syncClientFeatures: vi.fn(),
|
||||
}
|
||||
const mockTableStore = writable()
|
||||
const tables = {
|
||||
subscribe: mockTableStore.subscribe,
|
||||
update: mockTableStore.update,
|
||||
set: mockTableStore.set,
|
||||
}
|
||||
return {
|
||||
appStore,
|
||||
tables,
|
||||
}
|
||||
})
|
||||
|
||||
// Simple base config for components and data sources
|
||||
const baseInitialisation = ctx => {
|
||||
// Init components
|
||||
ctx.test.componentStore.update(state => ({
|
||||
...state,
|
||||
components: {
|
||||
...componentDefinitionMap(),
|
||||
},
|
||||
}))
|
||||
|
||||
// Add datasources
|
||||
tables.update(state => ({
|
||||
...state,
|
||||
list: [sampleTableDoc, internalTableDoc, userTableDoc, externalTableDoc],
|
||||
}))
|
||||
}
|
||||
|
||||
describe("Component store", () => {
|
||||
beforeEach(ctx => {
|
||||
vi.clearAllMocks()
|
||||
vi.resetAllMocks()
|
||||
|
||||
const componentStore = new ComponentStore()
|
||||
ctx.test = {}
|
||||
ctx.test = {
|
||||
|
@ -24,4 +93,379 @@ describe("Component store", () => {
|
|||
it("Create base component store with defaults", ctx => {
|
||||
expect(ctx.test.store).toStrictEqual(INITIAL_COMPONENTS_STATE)
|
||||
})
|
||||
|
||||
it("Reset the component store to default", ctx => {
|
||||
const container = getComponentFixture(`${COMP_PREFIX}/container`)
|
||||
const pluginDefs = pluginDefinitionMap()
|
||||
|
||||
ctx.test.componentStore.update(state => ({
|
||||
...state,
|
||||
components: {
|
||||
...componentDefinitionMap(),
|
||||
...pluginDefs,
|
||||
},
|
||||
customComponents: Object.keys(pluginDefs),
|
||||
componentToPaste: container.json(),
|
||||
selectedComponentId: container._id,
|
||||
}))
|
||||
|
||||
expect(ctx.test.store).not.toStrictEqual(INITIAL_COMPONENTS_STATE)
|
||||
|
||||
ctx.test.componentStore.reset()
|
||||
|
||||
expect(ctx.test.store).toStrictEqual(INITIAL_COMPONENTS_STATE)
|
||||
})
|
||||
|
||||
it("Refresh the component definitions", async ctx => {
|
||||
const componentDefs = componentDefinitionMap()
|
||||
let mockAPIResponse = {
|
||||
features: clientFeaturesResp,
|
||||
...componentDefs,
|
||||
}
|
||||
const apiDefRequest = vi
|
||||
.spyOn(API, "fetchComponentLibDefinitions")
|
||||
.mockResolvedValue(mockAPIResponse)
|
||||
|
||||
const fakeAppId = "abc123"
|
||||
const components = await ctx.test.componentStore.refreshDefinitions(
|
||||
fakeAppId
|
||||
)
|
||||
|
||||
expect(components).toStrictEqual(mockAPIResponse)
|
||||
expect(ctx.test.store.components).toStrictEqual(mockAPIResponse)
|
||||
|
||||
expect(apiDefRequest).toBeCalled()
|
||||
expect(appStore.syncClientFeatures).toBeCalledWith(clientFeaturesResp)
|
||||
})
|
||||
|
||||
it("Refresh and sync component and plugin definitions", async ctx => {
|
||||
const componentDefs = componentDefinitionMap()
|
||||
const pluginDefs = pluginDefinitionMap()
|
||||
|
||||
let mockAPIResponse = {
|
||||
features: clientFeaturesResp,
|
||||
...componentDefs,
|
||||
...pluginDefs,
|
||||
}
|
||||
const apiDefRequest = vi
|
||||
.spyOn(API, "fetchComponentLibDefinitions")
|
||||
.mockResolvedValue(mockAPIResponse)
|
||||
|
||||
const fakeAppId = "abc123"
|
||||
const components = await ctx.test.componentStore.refreshDefinitions(
|
||||
fakeAppId
|
||||
)
|
||||
|
||||
expect(components).toStrictEqual(mockAPIResponse)
|
||||
expect(ctx.test.store.components).toStrictEqual(mockAPIResponse)
|
||||
|
||||
expect(apiDefRequest).toBeCalled()
|
||||
expect(appStore.syncClientFeatures).toBeCalledWith(clientFeaturesResp)
|
||||
|
||||
expect(ctx.test.store.customComponents).toStrictEqual(
|
||||
Object.keys(pluginDefs)
|
||||
)
|
||||
})
|
||||
|
||||
it("Ignores definition sync if no appId is specified.", async ctx => {
|
||||
const apiDefRequest = vi.spyOn(API, "fetchComponentLibDefinitions")
|
||||
|
||||
const components = await ctx.test.componentStore.refreshDefinitions()
|
||||
|
||||
expect(components).toBeUndefined()
|
||||
|
||||
expect(apiDefRequest).not.toBeCalled()
|
||||
})
|
||||
|
||||
it("Retrieves component definitions by component type", ctx => {
|
||||
const pluginDefs = pluginDefinitionMap()
|
||||
const componentDefs = componentDefinitionMap()
|
||||
|
||||
ctx.test.componentStore.update(state => ({
|
||||
...state,
|
||||
components: {
|
||||
...componentDefs,
|
||||
...pluginDefs,
|
||||
},
|
||||
customComponents: Object.keys(pluginDefs),
|
||||
}))
|
||||
|
||||
const def = ctx.test.componentStore.getDefinition(
|
||||
"@budibase/standard-components/container"
|
||||
)
|
||||
|
||||
expect(def).toStrictEqual(
|
||||
componentDefs["@budibase/standard-components/container"]
|
||||
)
|
||||
|
||||
const pluginDef = ctx.test.componentStore.getDefinition("plugin/budi-video")
|
||||
|
||||
expect(pluginDef).toStrictEqual(pluginDefs["plugin/budi-video"])
|
||||
})
|
||||
|
||||
it("Handle missing or invalid component definition keys", ctx => {
|
||||
const pluginDefs = pluginDefinitionMap()
|
||||
const componentDefs = componentDefinitionMap()
|
||||
|
||||
ctx.test.componentStore.update(state => ({
|
||||
...state,
|
||||
components: {
|
||||
...componentDefs,
|
||||
...pluginDefs,
|
||||
},
|
||||
customComponents: Object.keys(pluginDefs),
|
||||
}))
|
||||
|
||||
const def = ctx.test.componentStore.getDefinition(
|
||||
"@budibase/standard-components/mystery"
|
||||
)
|
||||
expect(def).toBeUndefined()
|
||||
|
||||
const defEmpty = ctx.test.componentStore.getDefinition()
|
||||
expect(defEmpty).toBeNull()
|
||||
})
|
||||
|
||||
it("Select an appropriate default datasource - Internal Table ", ctx => {
|
||||
tables.update(state => ({
|
||||
...state,
|
||||
list: [sampleTableDoc, internalTableDoc, userTableDoc, externalTableDoc],
|
||||
}))
|
||||
|
||||
const table = ctx.test.componentStore.getDefaultDatasource()
|
||||
expect(table.sourceType).toBe(DB_TYPE_INTERNAL)
|
||||
expect(table.sourceId).not.toBe(DEFAULT_BB_DATASOURCE_ID)
|
||||
})
|
||||
|
||||
it("Select an appropriate default datasource - Sample Table ", ctx => {
|
||||
tables.update(state => ({
|
||||
...state,
|
||||
list: [sampleTableDoc, userTableDoc, externalTableDoc],
|
||||
}))
|
||||
|
||||
const table = ctx.test.componentStore.getDefaultDatasource()
|
||||
expect(table.sourceType).toBe(DB_TYPE_INTERNAL)
|
||||
expect(table.sourceId).toBe(DEFAULT_BB_DATASOURCE_ID)
|
||||
})
|
||||
|
||||
it("Select an appropriate default datasource - External Table ", ctx => {
|
||||
tables.update(state => ({
|
||||
...state,
|
||||
list: [userTableDoc, externalTableDoc],
|
||||
}))
|
||||
|
||||
const table = ctx.test.componentStore.getDefaultDatasource()
|
||||
expect(table.sourceType).toBe(DB_TYPE_EXTERNAL)
|
||||
})
|
||||
|
||||
it("Select an appropriate default datasource - No Table and ignore user table", ctx => {
|
||||
tables.update(state => ({
|
||||
...state,
|
||||
list: [userTableDoc],
|
||||
}))
|
||||
|
||||
const table = ctx.test.componentStore.getDefaultDatasource()
|
||||
expect(table).toBeUndefined()
|
||||
})
|
||||
|
||||
it("Apply no migrations if component is not a formblock", ctx => {
|
||||
const comp = getComponentFixture(`${COMP_PREFIX}/container`).json()
|
||||
const orig = { ...comp }
|
||||
const migrated = ctx.test.componentStore.migrateSettings(orig)
|
||||
|
||||
expect(migrated).toBe(false)
|
||||
expect(comp).toStrictEqual(orig)
|
||||
})
|
||||
|
||||
it("Should initialise the buttons prop if it didnt exist", ctx => {
|
||||
const formBlock = getComponentFixture(`${COMP_PREFIX}/formblock`).json()
|
||||
const migrated = ctx.test.componentStore.migrateSettings(formBlock)
|
||||
|
||||
expect(migrated).toBe(true)
|
||||
expect(formBlock.buttons).toEqual([])
|
||||
})
|
||||
|
||||
it("Should initialise the buttons prop if it was nullified/reset", ctx => {
|
||||
const formBlock = getComponentFixture(`${COMP_PREFIX}/formblock`).json()
|
||||
formBlock.buttons = null
|
||||
const migrated = ctx.test.componentStore.migrateSettings(formBlock)
|
||||
|
||||
expect(migrated).toBe(true)
|
||||
expect(formBlock.buttons).toEqual([])
|
||||
})
|
||||
|
||||
it("Should initialise formblock button position when not set", ctx => {
|
||||
const formBlock = getComponentFixture(`${COMP_PREFIX}/formblock`).json()
|
||||
const migrated = ctx.test.componentStore.migrateSettings(formBlock)
|
||||
|
||||
expect(migrated).toBe(true)
|
||||
expect(formBlock.buttonPosition).toEqual("top")
|
||||
})
|
||||
|
||||
it("Should ignore formblock migration if already initialised", ctx => {
|
||||
const formBlock = getComponentFixture(`${COMP_PREFIX}/formblock`).json()
|
||||
formBlock.buttonPosition = "bottom"
|
||||
formBlock.buttons = []
|
||||
const migrated = ctx.test.componentStore.migrateSettings(formBlock)
|
||||
|
||||
expect(migrated).toBe(false)
|
||||
expect(formBlock.buttonPosition).toEqual("bottom")
|
||||
})
|
||||
|
||||
it("enrichEmptySettings - initialise multifield type with schema keys", async ctx => {
|
||||
const coreScreen = getScreenFixture()
|
||||
|
||||
baseInitialisation(ctx)
|
||||
|
||||
const componentDefs = componentDefinitionMap()
|
||||
const targetCompDef =
|
||||
componentDefs["@budibase/standard-components/rowexplorer"]
|
||||
|
||||
const comp = getComponentFixture(`${COMP_PREFIX}/rowexplorer`).json()
|
||||
ctx.test.componentStore.enrichEmptySettings(comp, {
|
||||
parent: null,
|
||||
screen: coreScreen.json(),
|
||||
useDefaultValues: true,
|
||||
})
|
||||
|
||||
const multifieldKey = targetCompDef.settings[0].key
|
||||
const multifieldOptions = comp[multifieldKey]
|
||||
|
||||
expect(multifieldOptions).toStrictEqual(
|
||||
Object.keys(internalTableDoc.schema)
|
||||
)
|
||||
})
|
||||
|
||||
const enrichSettingsDS = (type, ctx) => {
|
||||
const coreScreen = getScreenFixture()
|
||||
|
||||
baseInitialisation(ctx)
|
||||
|
||||
const componentDefs = componentDefinitionMap()
|
||||
const targetCompDef = componentDefs[`${COMP_PREFIX}/${type}`]
|
||||
|
||||
const comp = getComponentFixture(`${COMP_PREFIX}/${type}`).json()
|
||||
ctx.test.componentStore.enrichEmptySettings(comp, {
|
||||
parent: null,
|
||||
screen: coreScreen.json(),
|
||||
useDefaultValues: true,
|
||||
})
|
||||
|
||||
const settingKey = targetCompDef.settings[0].key
|
||||
const settingValue = comp[settingKey]
|
||||
|
||||
expect(settingValue).toStrictEqual({
|
||||
label: internalTableDoc.name,
|
||||
tableId: internalTableDoc._id,
|
||||
resourceId: internalTableDoc._id,
|
||||
type: "table",
|
||||
})
|
||||
}
|
||||
|
||||
it("enrichEmptySettings - set default datasource for 'table' setting type", async ctx => {
|
||||
enrichSettingsDS("formblock", ctx)
|
||||
})
|
||||
|
||||
it("enrichEmptySettings - set default datasource for 'dataSource' setting type", async ctx => {
|
||||
enrichSettingsDS("dataprovider", ctx)
|
||||
})
|
||||
|
||||
it("enrichEmptySettings - set default datasource for type dataprovider", ctx => {
|
||||
const coreScreen = getScreenFixture()
|
||||
|
||||
baseInitialisation(ctx)
|
||||
|
||||
const componentDefs = componentDefinitionMap()
|
||||
const targetCompDef = componentDefs[`${COMP_PREFIX}/table`]
|
||||
|
||||
const providerOne = getComponentFixture(`${COMP_PREFIX}/dataprovider`)
|
||||
const tableOne = getComponentFixture(`${COMP_PREFIX}/table`)
|
||||
|
||||
const components = Array(10)
|
||||
.fill()
|
||||
.map(() => getComponentFixture(`${COMP_PREFIX}/container`))
|
||||
|
||||
components.splice(5, 0, providerOne)
|
||||
components.push(tableOne)
|
||||
|
||||
let nested = componentsToNested(components)
|
||||
coreScreen.addChild(nested)
|
||||
|
||||
const comp = tableOne.json()
|
||||
ctx.test.componentStore.enrichEmptySettings(comp, {
|
||||
parent: null,
|
||||
screen: coreScreen.json(),
|
||||
useDefaultValues: true,
|
||||
})
|
||||
const settingKey = targetCompDef.settings[0].key
|
||||
const settingValue = comp[settingKey]
|
||||
|
||||
expect(settingValue).toBe(`{{ literal [${providerOne.json()._id}] }}`)
|
||||
})
|
||||
|
||||
it("enrichEmptySettings - set default datasource for type dataprovider - get closest provider", ctx => {
|
||||
const coreScreen = getScreenFixture()
|
||||
|
||||
baseInitialisation(ctx)
|
||||
|
||||
const componentDefs = componentDefinitionMap()
|
||||
const targetCompDef = componentDefs[`${COMP_PREFIX}/table`]
|
||||
|
||||
const providerOne = getComponentFixture(`${COMP_PREFIX}/dataprovider`)
|
||||
const providerTwo = getComponentFixture(`${COMP_PREFIX}/dataprovider`)
|
||||
const tableOne = getComponentFixture(`${COMP_PREFIX}/table`)
|
||||
|
||||
const components = Array(10)
|
||||
.fill()
|
||||
.map(() => getComponentFixture(`${COMP_PREFIX}/container`))
|
||||
|
||||
components.unshift(providerOne)
|
||||
components.splice(5, 0, providerTwo)
|
||||
components.push(tableOne)
|
||||
|
||||
let nested = componentsToNested(components)
|
||||
coreScreen.addChild(nested)
|
||||
|
||||
const comp = tableOne.json()
|
||||
ctx.test.componentStore.enrichEmptySettings(comp, {
|
||||
parent: null,
|
||||
screen: coreScreen.json(),
|
||||
useDefaultValues: true,
|
||||
})
|
||||
const settingKey = targetCompDef.settings[0].key
|
||||
const settingValue = comp[settingKey]
|
||||
|
||||
// Get the closest data provider in the tree.
|
||||
expect(settingValue).toBe(`{{ literal [${providerTwo.json()._id}] }}`)
|
||||
})
|
||||
|
||||
it("enrichEmptySettings - set default datasource for type dataprovider - no providers in tree", ctx => {
|
||||
const coreScreen = getScreenFixture()
|
||||
|
||||
baseInitialisation(ctx)
|
||||
|
||||
const componentDefs = componentDefinitionMap()
|
||||
const targetCompDef = componentDefs[`${COMP_PREFIX}/table`]
|
||||
|
||||
const tableOne = getComponentFixture(`${COMP_PREFIX}/table`)
|
||||
const components = Array(10)
|
||||
.fill()
|
||||
.map(() => getComponentFixture(`${COMP_PREFIX}/container`))
|
||||
|
||||
components.push(tableOne)
|
||||
|
||||
let nested = componentsToNested(components)
|
||||
coreScreen.addChild(nested)
|
||||
|
||||
const comp = tableOne.json()
|
||||
ctx.test.componentStore.enrichEmptySettings(comp, {
|
||||
parent: null,
|
||||
screen: coreScreen.json(),
|
||||
useDefaultValues: true,
|
||||
})
|
||||
const settingKey = targetCompDef.settings[0].key
|
||||
const settingValue = comp[settingKey]
|
||||
|
||||
// The value should remain unset
|
||||
expect(settingValue).toBeUndefined()
|
||||
})
|
||||
})
|
||||
|
|
|
@ -2,6 +2,12 @@ import { v4 } from "uuid"
|
|||
import { Component } from "builder/store/screenTemplates/utils/Component"
|
||||
import { Screen } from "builder/store/screenTemplates/utils/Screen"
|
||||
import { get } from "svelte/store"
|
||||
import {
|
||||
BUDIBASE_INTERNAL_DB_ID,
|
||||
DB_TYPE_INTERNAL,
|
||||
DB_TYPE_EXTERNAL,
|
||||
DEFAULT_BB_DATASOURCE_ID,
|
||||
} from "constants/backend"
|
||||
|
||||
const getDocId = () => {
|
||||
return v4().replace(/-/g, "")
|
||||
|
@ -22,20 +28,90 @@ export const getComponentFixture = type => {
|
|||
return new Component(type)
|
||||
}
|
||||
|
||||
// Sample Definitions
|
||||
export const COMPONENT_DEFINITIONS = {
|
||||
form: {
|
||||
name: "Form",
|
||||
icon: "Form",
|
||||
hasChildren: true,
|
||||
illegalChildren: ["section", "form", "formblock"],
|
||||
},
|
||||
formblock: {
|
||||
name: "Form Block",
|
||||
block: true,
|
||||
settings: [
|
||||
{
|
||||
type: "table",
|
||||
key: "dataSource",
|
||||
},
|
||||
],
|
||||
},
|
||||
container: {
|
||||
name: "Container",
|
||||
},
|
||||
rowexplorer: {
|
||||
name: "Row Explorer",
|
||||
settings: [
|
||||
{
|
||||
// combo unique to the row explorer
|
||||
type: "multifield",
|
||||
selectAllFields: true,
|
||||
key: "detailFields",
|
||||
},
|
||||
],
|
||||
},
|
||||
dataprovider: {
|
||||
name: "Data Provider",
|
||||
settings: [
|
||||
{
|
||||
type: "dataSource",
|
||||
},
|
||||
],
|
||||
},
|
||||
table: {
|
||||
name: "Table",
|
||||
settings: [
|
||||
{
|
||||
type: "dataProvider",
|
||||
key: "dataProvider",
|
||||
},
|
||||
],
|
||||
},
|
||||
stringfield: {
|
||||
name: "Text Field",
|
||||
settings: [
|
||||
{
|
||||
type: "field/string",
|
||||
key: "field",
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
|
||||
// Sample plugin definitions
|
||||
export const PLUGIN_DEFINITIONS = {
|
||||
"budi-video": {
|
||||
component: "plugin/budi-video",
|
||||
description: "Embedded video component. ",
|
||||
friendlyName: "Budi Video",
|
||||
icon: "VideoOutline",
|
||||
name: "budi-video",
|
||||
},
|
||||
}
|
||||
|
||||
// Take a component array and turn it into a deeply nested tree
|
||||
export const componentsToNested = components => {
|
||||
let nested
|
||||
do {
|
||||
const current = components.pop()
|
||||
if (!nested) {
|
||||
nested = current
|
||||
continue
|
||||
}
|
||||
//review this for the empty
|
||||
current.addChild(nested)
|
||||
nested = current
|
||||
} while (components.length)
|
||||
return nested
|
||||
}
|
||||
|
||||
export const getFakeScreenPatch = store => {
|
||||
|
@ -48,7 +124,7 @@ export const getFakeScreenPatch = store => {
|
|||
}
|
||||
}
|
||||
|
||||
export const componentMap = () => {
|
||||
export const componentDefinitionMap = () => {
|
||||
return Object.keys(COMPONENT_DEFINITIONS).reduce((acc, key) => {
|
||||
const def = COMPONENT_DEFINITIONS[key]
|
||||
acc[`@budibase/standard-components/${key}`] = def
|
||||
|
@ -56,6 +132,14 @@ export const componentMap = () => {
|
|||
}, {})
|
||||
}
|
||||
|
||||
export const pluginDefinitionMap = () => {
|
||||
return Object.keys(PLUGIN_DEFINITIONS).reduce((acc, key) => {
|
||||
const def = PLUGIN_DEFINITIONS[key]
|
||||
acc[`plugin/${key}`] = def
|
||||
return acc
|
||||
}, {})
|
||||
}
|
||||
|
||||
export const getPluginFixture = pluginName => {
|
||||
const fakeName = pluginName || v4().replace(/-/g, "")
|
||||
return {
|
||||
|
@ -145,15 +229,56 @@ export const clientFeaturesResp = {
|
|||
sidePanel: true,
|
||||
}
|
||||
|
||||
export const fetchDefinitionsResp = {
|
||||
"@budibase/standard-components/text": {
|
||||
component: "@budibase/standard-components/text",
|
||||
},
|
||||
"plugin/budi-video": {
|
||||
component: "plugin/budi-video",
|
||||
},
|
||||
"plugin/budi-audio": {
|
||||
component: "plugin/budi-audio",
|
||||
},
|
||||
features: clientFeaturesResp,
|
||||
export const userTableDoc = {
|
||||
_id: "ta_users",
|
||||
type: "table",
|
||||
name: "Users",
|
||||
schema: {},
|
||||
}
|
||||
|
||||
export const sampleTableDoc = {
|
||||
_id: "ta_bb_employee",
|
||||
type: "table",
|
||||
name: "Employees",
|
||||
sourceId: DEFAULT_BB_DATASOURCE_ID,
|
||||
sourceType: DB_TYPE_INTERNAL,
|
||||
primaryDisplay: "First Name",
|
||||
schema: {
|
||||
"First Name": {
|
||||
name: "First Name",
|
||||
type: "string",
|
||||
},
|
||||
"Last Name": {
|
||||
name: "Last Name",
|
||||
type: "string",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
export const internalTableDoc = {
|
||||
_id: "ta_db5ac9e254da415899adcec21a025b3f",
|
||||
tableId: "ta_db5ac9e254da415899adcec21a025b3f",
|
||||
type: "table",
|
||||
name: "Media",
|
||||
sourceId: BUDIBASE_INTERNAL_DB_ID,
|
||||
sourceType: DB_TYPE_INTERNAL,
|
||||
schema: {
|
||||
MediaTitle: {
|
||||
name: "MediaTitle",
|
||||
type: "string",
|
||||
},
|
||||
MediaVersion: {
|
||||
name: "MediaVersion",
|
||||
type: "string",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
export const externalTableDoc = {
|
||||
type: "table",
|
||||
_id: "datasource_plus_c5e6ae7fbe534da6917c44b284c54b45__Tester",
|
||||
name: "Tester",
|
||||
sourceId: "datasource_plus_c5e6ae7fbe534da6917c44b284c54b45",
|
||||
sourceType: DB_TYPE_EXTERNAL,
|
||||
sql: true,
|
||||
}
|
||||
|
|
|
@ -8,9 +8,10 @@ import {
|
|||
getScreenFixture,
|
||||
getComponentFixture,
|
||||
COMPONENT_DEFINITIONS,
|
||||
componentMap,
|
||||
componentDefinitionMap,
|
||||
getScreenDocId,
|
||||
getPluginFixture,
|
||||
componentsToNested,
|
||||
} from "./fixtures"
|
||||
|
||||
const COMP_PREFIX = "@budibase/standard-components"
|
||||
|
@ -173,7 +174,7 @@ describe("Screens store", () => {
|
|||
const defSpy = vi
|
||||
.spyOn(componentStore, "getDefinition")
|
||||
.mockImplementation(comp => {
|
||||
const defMap = componentMap()
|
||||
const defMap = componentDefinitionMap()
|
||||
return defMap[comp]
|
||||
})
|
||||
|
||||
|
@ -198,23 +199,14 @@ describe("Screens store", () => {
|
|||
components.push(formTwo)
|
||||
|
||||
//Take the array and turn it into a deeply nested tree
|
||||
let nested
|
||||
do {
|
||||
const current = components.pop()
|
||||
if (!nested) {
|
||||
nested = current
|
||||
continue
|
||||
}
|
||||
current.addChild(nested)
|
||||
nested = current
|
||||
} while (components.length)
|
||||
let nested = componentsToNested(components)
|
||||
|
||||
coreScreen.addChild(nested)
|
||||
|
||||
const defSpy = vi
|
||||
.spyOn(componentStore, "getDefinition")
|
||||
.mockImplementation(comp => {
|
||||
const defMap = componentMap()
|
||||
const defMap = componentDefinitionMap()
|
||||
return defMap[comp]
|
||||
})
|
||||
|
||||
|
|
Loading…
Reference in New Issue