Screen store to ts and test updates. Minor changes required for component store
This commit is contained in:
parent
d1c4c664b3
commit
401bfa812c
|
@ -33,7 +33,16 @@ import { Utils } from "@budibase/frontend-core"
|
||||||
import { Component, FieldType, Screen, Table } from "@budibase/types"
|
import { Component, FieldType, Screen, Table } from "@budibase/types"
|
||||||
import { utils } from "@budibase/shared-core"
|
import { utils } from "@budibase/shared-core"
|
||||||
|
|
||||||
interface ComponentDefinition {
|
export interface ComponentState {
|
||||||
|
components: Record<string, ComponentDefinition>
|
||||||
|
customComponents: string[]
|
||||||
|
selectedComponentId?: string | null
|
||||||
|
componentToPaste?: Component | null
|
||||||
|
settingsCache: Record<string, ComponentSetting[]>
|
||||||
|
selectedScreenId?: string | null
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ComponentDefinition {
|
||||||
component: string
|
component: string
|
||||||
name: string
|
name: string
|
||||||
friendlyName?: string
|
friendlyName?: string
|
||||||
|
@ -41,9 +50,11 @@ interface ComponentDefinition {
|
||||||
settings?: ComponentSetting[]
|
settings?: ComponentSetting[]
|
||||||
features?: Record<string, boolean>
|
features?: Record<string, boolean>
|
||||||
typeSupportPresets?: Record<string, any>
|
typeSupportPresets?: Record<string, any>
|
||||||
|
legalDirectChildren: string[]
|
||||||
|
illegalChildren: string[]
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ComponentSetting {
|
export interface ComponentSetting {
|
||||||
key: string
|
key: string
|
||||||
type: string
|
type: string
|
||||||
section?: string
|
section?: string
|
||||||
|
@ -54,15 +65,6 @@ interface ComponentSetting {
|
||||||
settings?: ComponentSetting[]
|
settings?: ComponentSetting[]
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ComponentState {
|
|
||||||
components: Record<string, ComponentDefinition>
|
|
||||||
customComponents: string[]
|
|
||||||
selectedComponentId: string | null
|
|
||||||
componentToPaste?: Component | null
|
|
||||||
settingsCache: Record<string, ComponentSetting[]>
|
|
||||||
selectedScreenId?: string | null
|
|
||||||
}
|
|
||||||
|
|
||||||
export const INITIAL_COMPONENTS_STATE: ComponentState = {
|
export const INITIAL_COMPONENTS_STATE: ComponentState = {
|
||||||
components: {},
|
components: {},
|
||||||
customComponents: [],
|
customComponents: [],
|
||||||
|
@ -440,6 +442,11 @@ export class ComponentStore extends BudiStore<ComponentState> {
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
createInstance(componentName: string, presetProps: any, parent: any) {
|
createInstance(componentName: string, presetProps: any, parent: any) {
|
||||||
|
const screen = get(selectedScreen)
|
||||||
|
if (!screen || !selectedScreen) {
|
||||||
|
throw "A valid screen must be selected"
|
||||||
|
}
|
||||||
|
|
||||||
const definition = this.getDefinition(componentName)
|
const definition = this.getDefinition(componentName)
|
||||||
if (!definition) {
|
if (!definition) {
|
||||||
return null
|
return null
|
||||||
|
@ -461,7 +468,7 @@ export class ComponentStore extends BudiStore<ComponentState> {
|
||||||
// Standard post processing
|
// Standard post processing
|
||||||
this.enrichEmptySettings(instance, {
|
this.enrichEmptySettings(instance, {
|
||||||
parent,
|
parent,
|
||||||
screen: get(selectedScreen),
|
screen,
|
||||||
useDefaultValues: true,
|
useDefaultValues: true,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -481,7 +488,7 @@ export class ComponentStore extends BudiStore<ComponentState> {
|
||||||
// Add step name to form steps
|
// Add step name to form steps
|
||||||
if (componentName.endsWith("/formstep")) {
|
if (componentName.endsWith("/formstep")) {
|
||||||
const parentForm = findClosestMatchingComponent(
|
const parentForm = findClosestMatchingComponent(
|
||||||
get(selectedScreen).props,
|
screen.props,
|
||||||
get(selectedComponent)._id,
|
get(selectedComponent)._id,
|
||||||
(component: Component) => component._component.endsWith("/form")
|
(component: Component) => component._component.endsWith("/form")
|
||||||
)
|
)
|
||||||
|
@ -841,6 +848,9 @@ export class ComponentStore extends BudiStore<ComponentState> {
|
||||||
const state = get(this.store)
|
const state = get(this.store)
|
||||||
const componentId = state.selectedComponentId
|
const componentId = state.selectedComponentId
|
||||||
const screen = get(selectedScreen)
|
const screen = get(selectedScreen)
|
||||||
|
if (!screen) {
|
||||||
|
throw "A valid screen must be selected"
|
||||||
|
}
|
||||||
const parent = findComponentParent(screen.props, componentId)
|
const parent = findComponentParent(screen.props, componentId)
|
||||||
const index = parent?._children.findIndex(
|
const index = parent?._children.findIndex(
|
||||||
(x: Component) => x._id === componentId
|
(x: Component) => x._id === componentId
|
||||||
|
@ -890,6 +900,9 @@ export class ComponentStore extends BudiStore<ComponentState> {
|
||||||
const component = get(selectedComponent)
|
const component = get(selectedComponent)
|
||||||
const componentId = component?._id
|
const componentId = component?._id
|
||||||
const screen = get(selectedScreen)
|
const screen = get(selectedScreen)
|
||||||
|
if (!screen) {
|
||||||
|
throw "A valid screen must be selected"
|
||||||
|
}
|
||||||
const parent = findComponentParent(screen.props, componentId)
|
const parent = findComponentParent(screen.props, componentId)
|
||||||
const index = parent?._children.findIndex(
|
const index = parent?._children.findIndex(
|
||||||
(x: Component) => x._id === componentId
|
(x: Component) => x._id === componentId
|
||||||
|
|
|
@ -3,7 +3,7 @@ import { appStore } from "./app.js"
|
||||||
import { componentStore, selectedComponent } from "./components"
|
import { componentStore, selectedComponent } from "./components"
|
||||||
import { navigationStore } from "./navigation.js"
|
import { navigationStore } from "./navigation.js"
|
||||||
import { themeStore } from "./theme.js"
|
import { themeStore } from "./theme.js"
|
||||||
import { screenStore, selectedScreen, sortedScreens } from "./screens.js"
|
import { screenStore, selectedScreen, sortedScreens } from "./screens"
|
||||||
import { builderStore } from "./builder.js"
|
import { builderStore } from "./builder.js"
|
||||||
import { hoverStore } from "./hover.js"
|
import { hoverStore } from "./hover.js"
|
||||||
import { previewStore } from "./preview.js"
|
import { previewStore } from "./preview.js"
|
||||||
|
|
|
@ -13,15 +13,32 @@ import {
|
||||||
import { createHistoryStore } from "@/stores/builder/history"
|
import { createHistoryStore } from "@/stores/builder/history"
|
||||||
import { API } from "@/api"
|
import { API } from "@/api"
|
||||||
import { BudiStore } from "../BudiStore"
|
import { BudiStore } from "../BudiStore"
|
||||||
|
import {
|
||||||
|
FetchAppPackageResponse,
|
||||||
|
DeleteScreenResponse,
|
||||||
|
Screen,
|
||||||
|
Component,
|
||||||
|
} from "@budibase/types"
|
||||||
|
import { ComponentDefinition } from "./components"
|
||||||
|
|
||||||
export const INITIAL_SCREENS_STATE = {
|
interface ScreenState {
|
||||||
screens: [],
|
screens: Screen[]
|
||||||
selectedScreenId: null,
|
selectedScreenId?: string
|
||||||
|
selected?: Screen
|
||||||
}
|
}
|
||||||
|
|
||||||
export class ScreenStore extends BudiStore {
|
export const initialScreenState: ScreenState = {
|
||||||
|
screens: [],
|
||||||
|
}
|
||||||
|
|
||||||
|
// Review the nulls
|
||||||
|
export class ScreenStore extends BudiStore<ScreenState> {
|
||||||
|
history: any
|
||||||
|
delete: any
|
||||||
|
save: any
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super(INITIAL_SCREENS_STATE)
|
super(initialScreenState)
|
||||||
|
|
||||||
// Bind scope
|
// Bind scope
|
||||||
this.select = this.select.bind(this)
|
this.select = this.select.bind(this)
|
||||||
|
@ -34,12 +51,15 @@ export class ScreenStore extends BudiStore {
|
||||||
this.deleteScreen = this.deleteScreen.bind(this)
|
this.deleteScreen = this.deleteScreen.bind(this)
|
||||||
this.syncScreenData = this.syncScreenData.bind(this)
|
this.syncScreenData = this.syncScreenData.bind(this)
|
||||||
this.updateSetting = this.updateSetting.bind(this)
|
this.updateSetting = this.updateSetting.bind(this)
|
||||||
|
// TODO review this behaviour
|
||||||
this.sequentialScreenPatch = this.sequentialScreenPatch.bind(this)
|
this.sequentialScreenPatch = this.sequentialScreenPatch.bind(this)
|
||||||
this.removeCustomLayout = this.removeCustomLayout.bind(this)
|
this.removeCustomLayout = this.removeCustomLayout.bind(this)
|
||||||
|
|
||||||
this.history = createHistoryStore({
|
this.history = createHistoryStore({
|
||||||
getDoc: id => get(this.store).screens?.find(screen => screen._id === id),
|
getDoc: (id: string) =>
|
||||||
|
get(this.store).screens?.find(screen => screen._id === id),
|
||||||
selectDoc: this.select,
|
selectDoc: this.select,
|
||||||
|
beforeAction: () => {},
|
||||||
afterAction: () => {
|
afterAction: () => {
|
||||||
// Ensure a valid component is selected
|
// Ensure a valid component is selected
|
||||||
if (!get(selectedComponent)) {
|
if (!get(selectedComponent)) {
|
||||||
|
@ -59,14 +79,14 @@ export class ScreenStore extends BudiStore {
|
||||||
* Reset entire store back to base config
|
* Reset entire store back to base config
|
||||||
*/
|
*/
|
||||||
reset() {
|
reset() {
|
||||||
this.store.set({ ...INITIAL_SCREENS_STATE })
|
this.store.set({ ...initialScreenState })
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Replace ALL store screens with application package screens
|
* Replace ALL store screens with application package screens
|
||||||
* @param {object} pkg
|
* @param {object} pkg
|
||||||
*/
|
*/
|
||||||
syncAppScreens(pkg) {
|
syncAppScreens(pkg: FetchAppPackageResponse) {
|
||||||
this.update(state => ({
|
this.update(state => ({
|
||||||
...state,
|
...state,
|
||||||
screens: [...pkg.screens],
|
screens: [...pkg.screens],
|
||||||
|
@ -79,7 +99,7 @@ export class ScreenStore extends BudiStore {
|
||||||
* @param {string} screenId
|
* @param {string} screenId
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
select(screenId) {
|
select(screenId: string) {
|
||||||
// Check this screen exists
|
// Check this screen exists
|
||||||
const state = get(this.store)
|
const state = get(this.store)
|
||||||
const screen = state.screens.find(screen => screen._id === screenId)
|
const screen = state.screens.find(screen => screen._id === screenId)
|
||||||
|
@ -107,14 +127,14 @@ export class ScreenStore extends BudiStore {
|
||||||
* @throws Will throw an error containing the name of the component causing
|
* @throws Will throw an error containing the name of the component causing
|
||||||
* the invalid screen state
|
* the invalid screen state
|
||||||
*/
|
*/
|
||||||
validate(screen) {
|
validate(screen: Screen) {
|
||||||
// Recursive function to find any illegal children in component trees
|
// Recursive function to find any illegal children in component trees
|
||||||
const findIllegalChild = (
|
const findIllegalChild = (
|
||||||
component,
|
component: Component,
|
||||||
illegalChildren = [],
|
illegalChildren: string[] = [],
|
||||||
legalDirectChildren = []
|
legalDirectChildren: string[] = []
|
||||||
) => {
|
): string | undefined => {
|
||||||
const type = component._component
|
const type: string = component._component
|
||||||
|
|
||||||
if (illegalChildren.includes(type)) {
|
if (illegalChildren.includes(type)) {
|
||||||
return type
|
return type
|
||||||
|
@ -137,7 +157,13 @@ export class ScreenStore extends BudiStore {
|
||||||
illegalChildren = []
|
illegalChildren = []
|
||||||
}
|
}
|
||||||
|
|
||||||
const definition = componentStore.getDefinition(component._component)
|
const definition: ComponentDefinition | null =
|
||||||
|
componentStore.getDefinition(component._component)
|
||||||
|
|
||||||
|
if (definition == null) {
|
||||||
|
throw `Invalid defintion ${component._component}`
|
||||||
|
}
|
||||||
|
|
||||||
// Reset whitelist for direct children
|
// Reset whitelist for direct children
|
||||||
legalDirectChildren = []
|
legalDirectChildren = []
|
||||||
if (definition?.legalDirectChildren?.length) {
|
if (definition?.legalDirectChildren?.length) {
|
||||||
|
@ -172,7 +198,7 @@ export class ScreenStore extends BudiStore {
|
||||||
const illegalChild = findIllegalChild(screen.props)
|
const illegalChild = findIllegalChild(screen.props)
|
||||||
if (illegalChild) {
|
if (illegalChild) {
|
||||||
const def = componentStore.getDefinition(illegalChild)
|
const def = componentStore.getDefinition(illegalChild)
|
||||||
throw `You can't place a ${def.name} here`
|
throw `You can't place a ${def?.name} here`
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -183,7 +209,7 @@ export class ScreenStore extends BudiStore {
|
||||||
* @param {object} screen
|
* @param {object} screen
|
||||||
* @returns {object}
|
* @returns {object}
|
||||||
*/
|
*/
|
||||||
async saveScreen(screen) {
|
async saveScreen(screen: Screen) {
|
||||||
const appState = get(appStore)
|
const appState = get(appStore)
|
||||||
|
|
||||||
// Validate screen structure if the app supports it
|
// Validate screen structure if the app supports it
|
||||||
|
@ -230,7 +256,7 @@ export class ScreenStore extends BudiStore {
|
||||||
* After saving a screen, sync plugins and routes to the appStore
|
* After saving a screen, sync plugins and routes to the appStore
|
||||||
* @param {object} savedScreen
|
* @param {object} savedScreen
|
||||||
*/
|
*/
|
||||||
async syncScreenData(savedScreen) {
|
async syncScreenData(savedScreen: Screen) {
|
||||||
const appState = get(appStore)
|
const appState = get(appStore)
|
||||||
// If plugins changed we need to fetch the latest app metadata
|
// If plugins changed we need to fetch the latest app metadata
|
||||||
let usedPlugins = appState.usedPlugins
|
let usedPlugins = appState.usedPlugins
|
||||||
|
@ -256,7 +282,29 @@ export class ScreenStore extends BudiStore {
|
||||||
* This is slightly better than just a traditional "patch" endpoint and this
|
* This is slightly better than just a traditional "patch" endpoint and this
|
||||||
* supports deeply mutating the current doc rather than just appending data.
|
* supports deeply mutating the current doc rather than just appending data.
|
||||||
*/
|
*/
|
||||||
sequentialScreenPatch = Utils.sequential(async (patchFn, screenId) => {
|
// sequentialScreenPatch = (
|
||||||
|
// patchFn: (screen: Screen) => any,
|
||||||
|
// screenId: string
|
||||||
|
// ) => {
|
||||||
|
// return Utils.sequential(async () => {
|
||||||
|
// const state = get(this.store)
|
||||||
|
// const screen = state.screens.find(screen => screen._id === screenId)
|
||||||
|
// if (!screen) {
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
// let clone = cloneDeep(screen)
|
||||||
|
// const result = patchFn(clone)
|
||||||
|
|
||||||
|
// // An explicit false result means skip this change
|
||||||
|
// if (result === false) {
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
// return this.save(clone)
|
||||||
|
// })
|
||||||
|
// }
|
||||||
|
|
||||||
|
sequentialScreenPatch = Utils.sequential(
|
||||||
|
async (patchFn: (screen: Screen) => any, screenId: string) => {
|
||||||
const state = get(this.store)
|
const state = get(this.store)
|
||||||
const screen = state.screens.find(screen => screen._id === screenId)
|
const screen = state.screens.find(screen => screen._id === screenId)
|
||||||
if (!screen) {
|
if (!screen) {
|
||||||
|
@ -270,14 +318,15 @@ export class ScreenStore extends BudiStore {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
return this.save(clone)
|
return this.save(clone)
|
||||||
})
|
}
|
||||||
|
)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {function} patchFn
|
* @param {function} patchFn
|
||||||
* @param {string | null} screenId
|
* @param {string | null} screenId
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
async patch(patchFn, screenId) {
|
async patch(patchFn: (screen: Screen) => any, screenId?: string | null) {
|
||||||
// Default to the currently selected screen
|
// Default to the currently selected screen
|
||||||
if (!screenId) {
|
if (!screenId) {
|
||||||
const state = get(this.store)
|
const state = get(this.store)
|
||||||
|
@ -298,7 +347,7 @@ export class ScreenStore extends BudiStore {
|
||||||
* @param {object} screen
|
* @param {object} screen
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
async replace(screenId, screen) {
|
async replace(screenId: string, screen: Screen) {
|
||||||
if (!screenId) {
|
if (!screenId) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -337,12 +386,20 @@ export class ScreenStore extends BudiStore {
|
||||||
* @param {object | array} screens
|
* @param {object | array} screens
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
async deleteScreen(screens) {
|
async deleteScreen(screens: Screen[]) {
|
||||||
const screensToDelete = Array.isArray(screens) ? screens : [screens]
|
const screensToDelete = Array.isArray(screens) ? screens : [screens]
|
||||||
// Build array of promises to speed up bulk deletions
|
// Build array of promises to speed up bulk deletions
|
||||||
let promises = []
|
let promises: Promise<DeleteScreenResponse>[] = []
|
||||||
let deleteUrls = []
|
let deleteUrls: string[] = []
|
||||||
screensToDelete.forEach(screen => {
|
|
||||||
|
// In this instance _id will have been set
|
||||||
|
// Underline the expectation that _id and _rev will be set after filtering
|
||||||
|
screensToDelete
|
||||||
|
.filter(
|
||||||
|
(screen): screen is Screen & { _id: string; _rev: string } =>
|
||||||
|
!!screen._id || !!screen._rev
|
||||||
|
)
|
||||||
|
.forEach(screen => {
|
||||||
// Delete the screen
|
// Delete the screen
|
||||||
promises.push(API.deleteScreen(screen._id, screen._rev))
|
promises.push(API.deleteScreen(screen._id, screen._rev))
|
||||||
// Remove links to this screen
|
// Remove links to this screen
|
||||||
|
@ -359,8 +416,11 @@ export class ScreenStore extends BudiStore {
|
||||||
})
|
})
|
||||||
|
|
||||||
// Deselect the current screen if it was deleted
|
// Deselect the current screen if it was deleted
|
||||||
if (deletedIds.includes(state.selectedScreenId)) {
|
if (
|
||||||
state.selectedScreenId = null
|
state.selectedScreenId &&
|
||||||
|
deletedIds.includes(state.selectedScreenId)
|
||||||
|
) {
|
||||||
|
delete state.selectedScreenId
|
||||||
componentStore.update(state => ({
|
componentStore.update(state => ({
|
||||||
...state,
|
...state,
|
||||||
selectedComponentId: null,
|
selectedComponentId: null,
|
||||||
|
@ -389,13 +449,13 @@ export class ScreenStore extends BudiStore {
|
||||||
* @param {any} value
|
* @param {any} value
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
async updateSetting(screen, name, value) {
|
async updateSetting(screen: Screen, name: string, value: any) {
|
||||||
if (!screen || !name) {
|
if (!screen || !name) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Apply setting update
|
// Apply setting update
|
||||||
const patchFn = screen => {
|
const patchFn = (screen: Screen) => {
|
||||||
if (!screen) {
|
if (!screen) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
@ -422,7 +482,7 @@ export class ScreenStore extends BudiStore {
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
if (otherHomeScreens.length && updatedScreen.routing.homeScreen) {
|
if (otherHomeScreens.length && updatedScreen.routing.homeScreen) {
|
||||||
const patchFn = screen => {
|
const patchFn = (screen: Screen) => {
|
||||||
screen.routing.homeScreen = false
|
screen.routing.homeScreen = false
|
||||||
}
|
}
|
||||||
for (let otherHomeScreen of otherHomeScreens) {
|
for (let otherHomeScreen of otherHomeScreens) {
|
||||||
|
@ -432,11 +492,11 @@ export class ScreenStore extends BudiStore {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Move to layouts store
|
// Move to layouts store
|
||||||
async removeCustomLayout(screen) {
|
async removeCustomLayout(screen: Screen) {
|
||||||
// Pull relevant settings from old layout, if required
|
// Pull relevant settings from old layout, if required
|
||||||
const layout = get(layoutStore).layouts.find(x => x._id === screen.layoutId)
|
const layout = get(layoutStore).layouts.find(x => x._id === screen.layoutId)
|
||||||
const patchFn = screen => {
|
const patchFn = (screen: Screen) => {
|
||||||
screen.layoutId = null
|
delete screen.layoutId
|
||||||
screen.showNavigation = layout?.props.navigation !== "None"
|
screen.showNavigation = layout?.props.navigation !== "None"
|
||||||
screen.width = layout?.props.width || "Large"
|
screen.width = layout?.props.width || "Large"
|
||||||
}
|
}
|
||||||
|
@ -448,9 +508,12 @@ export class ScreenStore extends BudiStore {
|
||||||
* and up-to-date. Ensures stability after a product update.
|
* and up-to-date. Ensures stability after a product update.
|
||||||
* @param {object} screen
|
* @param {object} screen
|
||||||
*/
|
*/
|
||||||
async enrichEmptySettings(screen) {
|
async enrichEmptySettings(screen: Screen) {
|
||||||
// Flatten the recursive component tree
|
// Flatten the recursive component tree
|
||||||
const components = findAllMatchingComponents(screen.props, x => x)
|
const components = findAllMatchingComponents(
|
||||||
|
screen.props,
|
||||||
|
(x: Component) => x
|
||||||
|
)
|
||||||
|
|
||||||
// Iterate over all components and run checks
|
// Iterate over all components and run checks
|
||||||
components.forEach(component => {
|
components.forEach(component => {
|
|
@ -3,7 +3,7 @@ import { get, writable } from "svelte/store"
|
||||||
import { API } from "@/api"
|
import { API } from "@/api"
|
||||||
import { Constants } from "@budibase/frontend-core"
|
import { Constants } from "@budibase/frontend-core"
|
||||||
import { componentStore, appStore } from "@/stores/builder"
|
import { componentStore, appStore } from "@/stores/builder"
|
||||||
import { INITIAL_SCREENS_STATE, ScreenStore } from "@/stores/builder/screens"
|
import { initialScreenState, ScreenStore } from "@/stores/builder/screens"
|
||||||
import {
|
import {
|
||||||
getScreenFixture,
|
getScreenFixture,
|
||||||
getComponentFixture,
|
getComponentFixture,
|
||||||
|
@ -73,7 +73,7 @@ describe("Screens store", () => {
|
||||||
vi.clearAllMocks()
|
vi.clearAllMocks()
|
||||||
|
|
||||||
const screenStore = new ScreenStore()
|
const screenStore = new ScreenStore()
|
||||||
ctx.test = {
|
ctx.bb = {
|
||||||
get store() {
|
get store() {
|
||||||
return get(screenStore)
|
return get(screenStore)
|
||||||
},
|
},
|
||||||
|
@ -81,74 +81,76 @@ describe("Screens store", () => {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
it("Create base screen store with defaults", ctx => {
|
it("Create base screen store with defaults", ({ bb }) => {
|
||||||
expect(ctx.test.store).toStrictEqual(INITIAL_SCREENS_STATE)
|
expect(bb.store).toStrictEqual(initialScreenState)
|
||||||
})
|
})
|
||||||
|
|
||||||
it("Syncs all screens from the app package", ctx => {
|
it("Syncs all screens from the app package", ({ bb }) => {
|
||||||
expect(ctx.test.store.screens.length).toBe(0)
|
expect(bb.store.screens.length).toBe(0)
|
||||||
|
|
||||||
const screens = Array(2)
|
const screens = Array(2)
|
||||||
.fill()
|
.fill()
|
||||||
.map(() => getScreenFixture().json())
|
.map(() => getScreenFixture().json())
|
||||||
|
|
||||||
ctx.test.screenStore.syncAppScreens({ screens })
|
bb.screenStore.syncAppScreens({ screens })
|
||||||
|
|
||||||
expect(ctx.test.store.screens).toStrictEqual(screens)
|
expect(bb.store.screens).toStrictEqual(screens)
|
||||||
})
|
})
|
||||||
|
|
||||||
it("Reset the screen store back to the default state", ctx => {
|
it("Reset the screen store back to the default state", ({ bb }) => {
|
||||||
expect(ctx.test.store.screens.length).toBe(0)
|
expect(bb.store.screens.length).toBe(0)
|
||||||
|
|
||||||
const screens = Array(2)
|
const screens = Array(2)
|
||||||
.fill()
|
.fill()
|
||||||
.map(() => getScreenFixture().json())
|
.map(() => getScreenFixture().json())
|
||||||
|
|
||||||
ctx.test.screenStore.syncAppScreens({ screens })
|
bb.screenStore.syncAppScreens({ screens })
|
||||||
expect(ctx.test.store.screens).toStrictEqual(screens)
|
expect(bb.store.screens).toStrictEqual(screens)
|
||||||
|
|
||||||
ctx.test.screenStore.update(state => ({
|
bb.screenStore.update(state => ({
|
||||||
...state,
|
...state,
|
||||||
selectedScreenId: screens[0]._id,
|
selectedScreenId: screens[0]._id,
|
||||||
}))
|
}))
|
||||||
|
|
||||||
ctx.test.screenStore.reset()
|
bb.screenStore.reset()
|
||||||
|
|
||||||
expect(ctx.test.store).toStrictEqual(INITIAL_SCREENS_STATE)
|
expect(bb.store).toStrictEqual(initialScreenState)
|
||||||
})
|
})
|
||||||
|
|
||||||
it("Marks a valid screen as selected", ctx => {
|
it("Marks a valid screen as selected", ({ bb }) => {
|
||||||
const screens = Array(2)
|
const screens = Array(2)
|
||||||
.fill()
|
.fill()
|
||||||
.map(() => getScreenFixture().json())
|
.map(() => getScreenFixture().json())
|
||||||
|
|
||||||
ctx.test.screenStore.syncAppScreens({ screens })
|
bb.screenStore.syncAppScreens({ screens })
|
||||||
expect(ctx.test.store.screens.length).toBe(2)
|
expect(bb.store.screens.length).toBe(2)
|
||||||
|
|
||||||
ctx.test.screenStore.select(screens[0]._id)
|
bb.screenStore.select(screens[0]._id)
|
||||||
|
|
||||||
expect(ctx.test.store.selectedScreenId).toEqual(screens[0]._id)
|
expect(bb.store.selectedScreenId).toEqual(screens[0]._id)
|
||||||
})
|
})
|
||||||
|
|
||||||
it("Skip selecting a screen if it is not present", ctx => {
|
it("Skip selecting a screen if it is not present", ({ bb }) => {
|
||||||
const screens = Array(2)
|
const screens = Array(2)
|
||||||
.fill()
|
.fill()
|
||||||
.map(() => getScreenFixture().json())
|
.map(() => getScreenFixture().json())
|
||||||
|
|
||||||
ctx.test.screenStore.syncAppScreens({ screens })
|
bb.screenStore.syncAppScreens({ screens })
|
||||||
expect(ctx.test.store.screens.length).toBe(2)
|
expect(bb.store.screens.length).toBe(2)
|
||||||
|
|
||||||
ctx.test.screenStore.select("screen_abc")
|
bb.screenStore.select("screen_abc")
|
||||||
|
|
||||||
expect(ctx.test.store.selectedScreenId).toBeNull()
|
expect(bb.store.selectedScreenId).toBeUndefined()
|
||||||
})
|
})
|
||||||
|
|
||||||
it("Approve a valid empty screen config", ctx => {
|
it("Approve a valid empty screen config", ({ bb }) => {
|
||||||
const coreScreen = getScreenFixture()
|
const coreScreen = getScreenFixture()
|
||||||
ctx.test.screenStore.validate(coreScreen.json())
|
bb.screenStore.validate(coreScreen.json())
|
||||||
})
|
})
|
||||||
|
|
||||||
it("Approve a valid screen config with one component and no illegal children", ctx => {
|
it("Approve a valid screen config with one component and no illegal children", ({
|
||||||
|
bb,
|
||||||
|
}) => {
|
||||||
const coreScreen = getScreenFixture()
|
const coreScreen = getScreenFixture()
|
||||||
const formBlock = getComponentFixture(`${COMP_PREFIX}/formblock`)
|
const formBlock = getComponentFixture(`${COMP_PREFIX}/formblock`)
|
||||||
|
|
||||||
|
@ -157,12 +159,12 @@ describe("Screens store", () => {
|
||||||
const defSpy = vi.spyOn(componentStore, "getDefinition")
|
const defSpy = vi.spyOn(componentStore, "getDefinition")
|
||||||
defSpy.mockReturnValueOnce(COMPONENT_DEFINITIONS.formblock)
|
defSpy.mockReturnValueOnce(COMPONENT_DEFINITIONS.formblock)
|
||||||
|
|
||||||
ctx.test.screenStore.validate(coreScreen.json())
|
bb.screenStore.validate(coreScreen.json())
|
||||||
|
|
||||||
expect(defSpy).toHaveBeenCalled()
|
expect(defSpy).toHaveBeenCalled()
|
||||||
})
|
})
|
||||||
|
|
||||||
it("Reject an attempt to nest invalid components", ctx => {
|
it("Reject an attempt to nest invalid components", ({ bb }) => {
|
||||||
const coreScreen = getScreenFixture()
|
const coreScreen = getScreenFixture()
|
||||||
|
|
||||||
const formOne = getComponentFixture(`${COMP_PREFIX}/form`)
|
const formOne = getComponentFixture(`${COMP_PREFIX}/form`)
|
||||||
|
@ -178,14 +180,14 @@ describe("Screens store", () => {
|
||||||
return defMap[comp]
|
return defMap[comp]
|
||||||
})
|
})
|
||||||
|
|
||||||
expect(() => ctx.test.screenStore.validate(coreScreen.json())).toThrowError(
|
expect(() => bb.screenStore.validate(coreScreen.json())).toThrowError(
|
||||||
`You can't place a ${COMPONENT_DEFINITIONS.form.name} here`
|
`You can't place a ${COMPONENT_DEFINITIONS.form.name} here`
|
||||||
)
|
)
|
||||||
|
|
||||||
expect(defSpy).toHaveBeenCalled()
|
expect(defSpy).toHaveBeenCalled()
|
||||||
})
|
})
|
||||||
|
|
||||||
it("Reject an attempt to deeply nest invalid components", ctx => {
|
it("Reject an attempt to deeply nest invalid components", ({ bb }) => {
|
||||||
const coreScreen = getScreenFixture()
|
const coreScreen = getScreenFixture()
|
||||||
|
|
||||||
const formOne = getComponentFixture(`${COMP_PREFIX}/form`)
|
const formOne = getComponentFixture(`${COMP_PREFIX}/form`)
|
||||||
|
@ -210,14 +212,16 @@ describe("Screens store", () => {
|
||||||
return defMap[comp]
|
return defMap[comp]
|
||||||
})
|
})
|
||||||
|
|
||||||
expect(() => ctx.test.screenStore.validate(coreScreen.json())).toThrowError(
|
expect(() => bb.screenStore.validate(coreScreen.json())).toThrowError(
|
||||||
`You can't place a ${COMPONENT_DEFINITIONS.form.name} here`
|
`You can't place a ${COMPONENT_DEFINITIONS.form.name} here`
|
||||||
)
|
)
|
||||||
|
|
||||||
expect(defSpy).toHaveBeenCalled()
|
expect(defSpy).toHaveBeenCalled()
|
||||||
})
|
})
|
||||||
|
|
||||||
it("Save a brand new screen and add it to the store. No validation", async ctx => {
|
it("Save a brand new screen and add it to the store. No validation", async ({
|
||||||
|
bb,
|
||||||
|
}) => {
|
||||||
const coreScreen = getScreenFixture()
|
const coreScreen = getScreenFixture()
|
||||||
const formOne = getComponentFixture(`${COMP_PREFIX}/form`)
|
const formOne = getComponentFixture(`${COMP_PREFIX}/form`)
|
||||||
|
|
||||||
|
@ -225,7 +229,7 @@ describe("Screens store", () => {
|
||||||
|
|
||||||
appStore.set({ features: { componentValidation: false } })
|
appStore.set({ features: { componentValidation: false } })
|
||||||
|
|
||||||
expect(ctx.test.store.screens.length).toBe(0)
|
expect(bb.store.screens.length).toBe(0)
|
||||||
|
|
||||||
const newDocId = getScreenDocId()
|
const newDocId = getScreenDocId()
|
||||||
const newDoc = { ...coreScreen.json(), _id: newDocId }
|
const newDoc = { ...coreScreen.json(), _id: newDocId }
|
||||||
|
@ -235,15 +239,15 @@ describe("Screens store", () => {
|
||||||
vi.spyOn(API, "fetchAppRoutes").mockResolvedValue({
|
vi.spyOn(API, "fetchAppRoutes").mockResolvedValue({
|
||||||
routes: [],
|
routes: [],
|
||||||
})
|
})
|
||||||
await ctx.test.screenStore.save(coreScreen.json())
|
await bb.screenStore.save(coreScreen.json())
|
||||||
|
|
||||||
expect(saveSpy).toHaveBeenCalled()
|
expect(saveSpy).toHaveBeenCalled()
|
||||||
|
|
||||||
expect(ctx.test.store.screens.length).toBe(1)
|
expect(bb.store.screens.length).toBe(1)
|
||||||
|
|
||||||
expect(ctx.test.store.screens[0]).toStrictEqual(newDoc)
|
expect(bb.store.screens[0]).toStrictEqual(newDoc)
|
||||||
|
|
||||||
expect(ctx.test.store.selectedScreenId).toBe(newDocId)
|
expect(bb.store.selectedScreenId).toBe(newDocId)
|
||||||
|
|
||||||
// The new screen should be selected
|
// The new screen should be selected
|
||||||
expect(get(componentStore).selectedComponentId).toBe(
|
expect(get(componentStore).selectedComponentId).toBe(
|
||||||
|
@ -251,7 +255,7 @@ describe("Screens store", () => {
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
it("Sync an updated screen to the screen store on save", async ctx => {
|
it("Sync an updated screen to the screen store on save", async ({ bb }) => {
|
||||||
const existingScreens = Array(4)
|
const existingScreens = Array(4)
|
||||||
.fill()
|
.fill()
|
||||||
.map(() => {
|
.map(() => {
|
||||||
|
@ -261,7 +265,7 @@ describe("Screens store", () => {
|
||||||
return screenDoc
|
return screenDoc
|
||||||
})
|
})
|
||||||
|
|
||||||
ctx.test.screenStore.update(state => ({
|
bb.screenStore.update(state => ({
|
||||||
...state,
|
...state,
|
||||||
screens: existingScreens.map(screen => screen.json()),
|
screens: existingScreens.map(screen => screen.json()),
|
||||||
}))
|
}))
|
||||||
|
@ -279,16 +283,18 @@ describe("Screens store", () => {
|
||||||
})
|
})
|
||||||
|
|
||||||
// Saved the existing screen having modified it.
|
// Saved the existing screen having modified it.
|
||||||
await ctx.test.screenStore.save(existingScreens[2].json())
|
await bb.screenStore.save(existingScreens[2].json())
|
||||||
|
|
||||||
expect(routeSpy).toHaveBeenCalled()
|
expect(routeSpy).toHaveBeenCalled()
|
||||||
expect(saveSpy).toHaveBeenCalled()
|
expect(saveSpy).toHaveBeenCalled()
|
||||||
|
|
||||||
// On save, the screen is spliced back into the store with the saved content
|
// On save, the screen is spliced back into the store with the saved content
|
||||||
expect(ctx.test.store.screens[2]).toStrictEqual(existingScreens[2].json())
|
expect(bb.store.screens[2]).toStrictEqual(existingScreens[2].json())
|
||||||
})
|
})
|
||||||
|
|
||||||
it("Sync API data to relevant stores on save. Updated plugins", async ctx => {
|
it("Sync API data to relevant stores on save. Updated plugins", async ({
|
||||||
|
bb,
|
||||||
|
}) => {
|
||||||
const coreScreen = getScreenFixture()
|
const coreScreen = getScreenFixture()
|
||||||
|
|
||||||
const newDocId = getScreenDocId()
|
const newDocId = getScreenDocId()
|
||||||
|
@ -318,7 +324,7 @@ describe("Screens store", () => {
|
||||||
routes: [],
|
routes: [],
|
||||||
})
|
})
|
||||||
|
|
||||||
await ctx.test.screenStore.syncScreenData(newDoc)
|
await bb.screenStore.syncScreenData(newDoc)
|
||||||
|
|
||||||
expect(routeSpy).toHaveBeenCalled()
|
expect(routeSpy).toHaveBeenCalled()
|
||||||
expect(appPackageSpy).toHaveBeenCalled()
|
expect(appPackageSpy).toHaveBeenCalled()
|
||||||
|
@ -326,7 +332,9 @@ describe("Screens store", () => {
|
||||||
expect(get(appStore).usedPlugins).toStrictEqual(plugins)
|
expect(get(appStore).usedPlugins).toStrictEqual(plugins)
|
||||||
})
|
})
|
||||||
|
|
||||||
it("Sync API updates to relevant stores on save. Plugins unchanged", async ctx => {
|
it("Sync API updates to relevant stores on save. Plugins unchanged", async ({
|
||||||
|
bb,
|
||||||
|
}) => {
|
||||||
const coreScreen = getScreenFixture()
|
const coreScreen = getScreenFixture()
|
||||||
|
|
||||||
const newDocId = getScreenDocId()
|
const newDocId = getScreenDocId()
|
||||||
|
@ -343,7 +351,7 @@ describe("Screens store", () => {
|
||||||
routes: [],
|
routes: [],
|
||||||
})
|
})
|
||||||
|
|
||||||
await ctx.test.screenStore.syncScreenData(newDoc)
|
await bb.screenStore.syncScreenData(newDoc)
|
||||||
|
|
||||||
expect(routeSpy).toHaveBeenCalled()
|
expect(routeSpy).toHaveBeenCalled()
|
||||||
expect(appPackageSpy).not.toHaveBeenCalled()
|
expect(appPackageSpy).not.toHaveBeenCalled()
|
||||||
|
@ -352,46 +360,48 @@ describe("Screens store", () => {
|
||||||
expect(get(appStore).usedPlugins).toStrictEqual([plugin])
|
expect(get(appStore).usedPlugins).toStrictEqual([plugin])
|
||||||
})
|
})
|
||||||
|
|
||||||
it("Proceed to patch if appropriate config are supplied", async ctx => {
|
it("Proceed to patch if appropriate config are supplied", async ({ bb }) => {
|
||||||
vi.spyOn(ctx.test.screenStore, "sequentialScreenPatch").mockImplementation(
|
vi.spyOn(bb.screenStore, "sequentialScreenPatch").mockImplementation(() => {
|
||||||
() => {
|
|
||||||
return false
|
return false
|
||||||
}
|
})
|
||||||
)
|
|
||||||
const noop = () => {}
|
const noop = () => {}
|
||||||
|
|
||||||
await ctx.test.screenStore.patch(noop, "test")
|
await bb.screenStore.patch(noop, "test")
|
||||||
expect(ctx.test.screenStore.sequentialScreenPatch).toHaveBeenCalledWith(
|
expect(bb.screenStore.sequentialScreenPatch).toHaveBeenCalledWith(
|
||||||
noop,
|
noop,
|
||||||
"test"
|
"test"
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
it("Return from the patch if all valid config are not present", async ctx => {
|
it("Return from the patch if all valid config are not present", async ({
|
||||||
vi.spyOn(ctx.test.screenStore, "sequentialScreenPatch")
|
bb,
|
||||||
await ctx.test.screenStore.patch()
|
}) => {
|
||||||
expect(ctx.test.screenStore.sequentialScreenPatch).not.toBeCalled()
|
vi.spyOn(bb.screenStore, "sequentialScreenPatch")
|
||||||
|
await bb.screenStore.patch()
|
||||||
|
expect(bb.screenStore.sequentialScreenPatch).not.toBeCalled()
|
||||||
})
|
})
|
||||||
|
|
||||||
it("Acquire the currently selected screen on patch, if not specified", async ctx => {
|
it("Acquire the currently selected screen on patch, if not specified", async ({
|
||||||
vi.spyOn(ctx.test.screenStore, "sequentialScreenPatch")
|
bb,
|
||||||
await ctx.test.screenStore.patch()
|
}) => {
|
||||||
|
vi.spyOn(bb.screenStore, "sequentialScreenPatch")
|
||||||
|
await bb.screenStore.patch()
|
||||||
|
|
||||||
const noop = () => {}
|
const noop = () => {}
|
||||||
ctx.test.screenStore.update(state => ({
|
bb.screenStore.update(state => ({
|
||||||
...state,
|
...state,
|
||||||
selectedScreenId: "screen_123",
|
selectedScreenId: "screen_123",
|
||||||
}))
|
}))
|
||||||
|
|
||||||
await ctx.test.screenStore.patch(noop)
|
await bb.screenStore.patch(noop)
|
||||||
expect(ctx.test.screenStore.sequentialScreenPatch).toHaveBeenCalledWith(
|
expect(bb.screenStore.sequentialScreenPatch).toHaveBeenCalledWith(
|
||||||
noop,
|
noop,
|
||||||
"screen_123"
|
"screen_123"
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
// Used by the websocket
|
// Used by the websocket
|
||||||
it("Ignore a call to replace if no screenId is provided", ctx => {
|
it("Ignore a call to replace if no screenId is provided", ({ bb }) => {
|
||||||
const existingScreens = Array(4)
|
const existingScreens = Array(4)
|
||||||
.fill()
|
.fill()
|
||||||
.map(() => {
|
.map(() => {
|
||||||
|
@ -400,14 +410,16 @@ describe("Screens store", () => {
|
||||||
screenDoc._json._id = existingDocId
|
screenDoc._json._id = existingDocId
|
||||||
return screenDoc.json()
|
return screenDoc.json()
|
||||||
})
|
})
|
||||||
ctx.test.screenStore.syncAppScreens({ screens: existingScreens })
|
bb.screenStore.syncAppScreens({ screens: existingScreens })
|
||||||
|
|
||||||
ctx.test.screenStore.replace()
|
bb.screenStore.replace()
|
||||||
|
|
||||||
expect(ctx.test.store.screens).toStrictEqual(existingScreens)
|
expect(bb.store.screens).toStrictEqual(existingScreens)
|
||||||
})
|
})
|
||||||
|
|
||||||
it("Remove a screen from the store if a single screenId is supplied", ctx => {
|
it("Remove a screen from the store if a single screenId is supplied", ({
|
||||||
|
bb,
|
||||||
|
}) => {
|
||||||
const existingScreens = Array(4)
|
const existingScreens = Array(4)
|
||||||
.fill()
|
.fill()
|
||||||
.map(() => {
|
.map(() => {
|
||||||
|
@ -416,17 +428,17 @@ describe("Screens store", () => {
|
||||||
screenDoc._json._id = existingDocId
|
screenDoc._json._id = existingDocId
|
||||||
return screenDoc.json()
|
return screenDoc.json()
|
||||||
})
|
})
|
||||||
ctx.test.screenStore.syncAppScreens({ screens: existingScreens })
|
bb.screenStore.syncAppScreens({ screens: existingScreens })
|
||||||
|
|
||||||
ctx.test.screenStore.replace(existingScreens[1]._id)
|
bb.screenStore.replace(existingScreens[1]._id)
|
||||||
|
|
||||||
const filtered = existingScreens.filter(
|
const filtered = existingScreens.filter(
|
||||||
screen => screen._id != existingScreens[1]._id
|
screen => screen._id != existingScreens[1]._id
|
||||||
)
|
)
|
||||||
expect(ctx.test.store.screens).toStrictEqual(filtered)
|
expect(bb.store.screens).toStrictEqual(filtered)
|
||||||
})
|
})
|
||||||
|
|
||||||
it("Replace an existing screen with a new version of itself", ctx => {
|
it("Replace an existing screen with a new version of itself", ({ bb }) => {
|
||||||
const existingScreens = Array(4)
|
const existingScreens = Array(4)
|
||||||
.fill()
|
.fill()
|
||||||
.map(() => {
|
.map(() => {
|
||||||
|
@ -436,7 +448,7 @@ describe("Screens store", () => {
|
||||||
return screenDoc
|
return screenDoc
|
||||||
})
|
})
|
||||||
|
|
||||||
ctx.test.screenStore.update(state => ({
|
bb.screenStore.update(state => ({
|
||||||
...state,
|
...state,
|
||||||
screens: existingScreens.map(screen => screen.json()),
|
screens: existingScreens.map(screen => screen.json()),
|
||||||
}))
|
}))
|
||||||
|
@ -444,15 +456,14 @@ describe("Screens store", () => {
|
||||||
const formBlock = getComponentFixture(`${COMP_PREFIX}/formblock`)
|
const formBlock = getComponentFixture(`${COMP_PREFIX}/formblock`)
|
||||||
existingScreens[2].addChild(formBlock)
|
existingScreens[2].addChild(formBlock)
|
||||||
|
|
||||||
ctx.test.screenStore.replace(
|
bb.screenStore.replace(existingScreens[2]._id, existingScreens[2].json())
|
||||||
existingScreens[2]._id,
|
|
||||||
existingScreens[2].json()
|
|
||||||
)
|
|
||||||
|
|
||||||
expect(ctx.test.store.screens.length).toBe(4)
|
expect(bb.store.screens.length).toBe(4)
|
||||||
})
|
})
|
||||||
|
|
||||||
it("Add a screen when attempting to replace one not present in the store", ctx => {
|
it("Add a screen when attempting to replace one not present in the store", ({
|
||||||
|
bb,
|
||||||
|
}) => {
|
||||||
const existingScreens = Array(4)
|
const existingScreens = Array(4)
|
||||||
.fill()
|
.fill()
|
||||||
.map(() => {
|
.map(() => {
|
||||||
|
@ -462,7 +473,7 @@ describe("Screens store", () => {
|
||||||
return screenDoc
|
return screenDoc
|
||||||
})
|
})
|
||||||
|
|
||||||
ctx.test.screenStore.update(state => ({
|
bb.screenStore.update(state => ({
|
||||||
...state,
|
...state,
|
||||||
screens: existingScreens.map(screen => screen.json()),
|
screens: existingScreens.map(screen => screen.json()),
|
||||||
}))
|
}))
|
||||||
|
@ -470,13 +481,13 @@ describe("Screens store", () => {
|
||||||
const newScreenDoc = getScreenFixture()
|
const newScreenDoc = getScreenFixture()
|
||||||
newScreenDoc._json._id = getScreenDocId()
|
newScreenDoc._json._id = getScreenDocId()
|
||||||
|
|
||||||
ctx.test.screenStore.replace(newScreenDoc._json._id, newScreenDoc.json())
|
bb.screenStore.replace(newScreenDoc._json._id, newScreenDoc.json())
|
||||||
|
|
||||||
expect(ctx.test.store.screens.length).toBe(5)
|
expect(bb.store.screens.length).toBe(5)
|
||||||
expect(ctx.test.store.screens[4]).toStrictEqual(newScreenDoc.json())
|
expect(bb.store.screens[4]).toStrictEqual(newScreenDoc.json())
|
||||||
})
|
})
|
||||||
|
|
||||||
it("Delete a single screen and remove it from the store", async ctx => {
|
it("Delete a single screen and remove it from the store", async ({ bb }) => {
|
||||||
const existingScreens = Array(3)
|
const existingScreens = Array(3)
|
||||||
.fill()
|
.fill()
|
||||||
.map(() => {
|
.map(() => {
|
||||||
|
@ -486,14 +497,14 @@ describe("Screens store", () => {
|
||||||
return screenDoc
|
return screenDoc
|
||||||
})
|
})
|
||||||
|
|
||||||
ctx.test.screenStore.update(state => ({
|
bb.screenStore.update(state => ({
|
||||||
...state,
|
...state,
|
||||||
screens: existingScreens.map(screen => screen.json()),
|
screens: existingScreens.map(screen => screen.json()),
|
||||||
}))
|
}))
|
||||||
|
|
||||||
const deleteSpy = vi.spyOn(API, "deleteScreen")
|
const deleteSpy = vi.spyOn(API, "deleteScreen")
|
||||||
|
|
||||||
await ctx.test.screenStore.delete(existingScreens[2].json())
|
await bb.screenStore.delete(existingScreens[2].json())
|
||||||
|
|
||||||
vi.spyOn(API, "fetchAppRoutes").mockResolvedValue({
|
vi.spyOn(API, "fetchAppRoutes").mockResolvedValue({
|
||||||
routes: [],
|
routes: [],
|
||||||
|
@ -501,13 +512,15 @@ describe("Screens store", () => {
|
||||||
|
|
||||||
expect(deleteSpy).toBeCalled()
|
expect(deleteSpy).toBeCalled()
|
||||||
|
|
||||||
expect(ctx.test.store.screens.length).toBe(2)
|
expect(bb.store.screens.length).toBe(2)
|
||||||
|
|
||||||
// Just confirm that the routes at are being initialised
|
// Just confirm that the routes at are being initialised
|
||||||
expect(get(appStore).routes).toEqual([])
|
expect(get(appStore).routes).toEqual([])
|
||||||
})
|
})
|
||||||
|
|
||||||
it("Upon delete, reset selected screen and component ids if the screen was selected", async ctx => {
|
it("Upon delete, reset selected screen and component ids if the screen was selected", async ({
|
||||||
|
bb,
|
||||||
|
}) => {
|
||||||
const existingScreens = Array(3)
|
const existingScreens = Array(3)
|
||||||
.fill()
|
.fill()
|
||||||
.map(() => {
|
.map(() => {
|
||||||
|
@ -517,7 +530,7 @@ describe("Screens store", () => {
|
||||||
return screenDoc
|
return screenDoc
|
||||||
})
|
})
|
||||||
|
|
||||||
ctx.test.screenStore.update(state => ({
|
bb.screenStore.update(state => ({
|
||||||
...state,
|
...state,
|
||||||
screens: existingScreens.map(screen => screen.json()),
|
screens: existingScreens.map(screen => screen.json()),
|
||||||
selectedScreenId: existingScreens[2]._json._id,
|
selectedScreenId: existingScreens[2]._json._id,
|
||||||
|
@ -528,14 +541,16 @@ describe("Screens store", () => {
|
||||||
selectedComponentId: existingScreens[2]._json._id,
|
selectedComponentId: existingScreens[2]._json._id,
|
||||||
}))
|
}))
|
||||||
|
|
||||||
await ctx.test.screenStore.delete(existingScreens[2].json())
|
await bb.screenStore.delete(existingScreens[2].json())
|
||||||
|
|
||||||
expect(ctx.test.store.screens.length).toBe(2)
|
expect(bb.store.screens.length).toBe(2)
|
||||||
expect(get(componentStore).selectedComponentId).toBeNull()
|
expect(get(componentStore).selectedComponentId).toBeNull()
|
||||||
expect(ctx.test.store.selectedScreenId).toBeNull()
|
expect(bb.store.selectedScreenId).toBeUndefined()
|
||||||
})
|
})
|
||||||
|
|
||||||
it("Delete multiple is not supported and should leave the store unchanged", async ctx => {
|
it("Delete multiple is not supported and should leave the store unchanged", async ({
|
||||||
|
bb,
|
||||||
|
}) => {
|
||||||
const existingScreens = Array(3)
|
const existingScreens = Array(3)
|
||||||
.fill()
|
.fill()
|
||||||
.map(() => {
|
.map(() => {
|
||||||
|
@ -547,7 +562,7 @@ describe("Screens store", () => {
|
||||||
|
|
||||||
const storeScreens = existingScreens.map(screen => screen.json())
|
const storeScreens = existingScreens.map(screen => screen.json())
|
||||||
|
|
||||||
ctx.test.screenStore.update(state => ({
|
bb.screenStore.update(state => ({
|
||||||
...state,
|
...state,
|
||||||
screens: existingScreens.map(screen => screen.json()),
|
screens: existingScreens.map(screen => screen.json()),
|
||||||
}))
|
}))
|
||||||
|
@ -556,42 +571,40 @@ describe("Screens store", () => {
|
||||||
|
|
||||||
const deleteSpy = vi.spyOn(API, "deleteScreen")
|
const deleteSpy = vi.spyOn(API, "deleteScreen")
|
||||||
|
|
||||||
await ctx.test.screenStore.delete(targets)
|
await bb.screenStore.delete(targets)
|
||||||
|
|
||||||
expect(deleteSpy).not.toHaveBeenCalled()
|
expect(deleteSpy).not.toHaveBeenCalled()
|
||||||
expect(ctx.test.store.screens.length).toBe(3)
|
expect(bb.store.screens.length).toBe(3)
|
||||||
expect(ctx.test.store.screens).toStrictEqual(storeScreens)
|
expect(bb.store.screens).toStrictEqual(storeScreens)
|
||||||
})
|
})
|
||||||
|
|
||||||
it("Update a screen setting", async ctx => {
|
it("Update a screen setting", async ({ bb }) => {
|
||||||
const screenDoc = getScreenFixture()
|
const screenDoc = getScreenFixture()
|
||||||
const existingDocId = getScreenDocId()
|
const existingDocId = getScreenDocId()
|
||||||
screenDoc._json._id = existingDocId
|
screenDoc._json._id = existingDocId
|
||||||
|
|
||||||
await ctx.test.screenStore.update(state => ({
|
await bb.screenStore.update(state => ({
|
||||||
...state,
|
...state,
|
||||||
screens: [screenDoc.json()],
|
screens: [screenDoc.json()],
|
||||||
}))
|
}))
|
||||||
|
|
||||||
const patchedDoc = screenDoc.json()
|
const patchedDoc = screenDoc.json()
|
||||||
const patchSpy = vi
|
const patchSpy = vi
|
||||||
.spyOn(ctx.test.screenStore, "patch")
|
.spyOn(bb.screenStore, "patch")
|
||||||
.mockImplementation(async patchFn => {
|
.mockImplementation(async patchFn => {
|
||||||
patchFn(patchedDoc)
|
patchFn(patchedDoc)
|
||||||
return
|
return
|
||||||
})
|
})
|
||||||
|
|
||||||
await ctx.test.screenStore.updateSetting(
|
await bb.screenStore.updateSetting(patchedDoc, "showNavigation", false)
|
||||||
patchedDoc,
|
|
||||||
"showNavigation",
|
|
||||||
false
|
|
||||||
)
|
|
||||||
|
|
||||||
expect(patchSpy).toBeCalled()
|
expect(patchSpy).toBeCalled()
|
||||||
expect(patchedDoc.showNavigation).toBe(false)
|
expect(patchedDoc.showNavigation).toBe(false)
|
||||||
})
|
})
|
||||||
|
|
||||||
it("Ensure only one homescreen per role after updating setting. All screens same role", async ctx => {
|
it("Ensure only one homescreen per role after updating setting. All screens same role", async ({
|
||||||
|
bb,
|
||||||
|
}) => {
|
||||||
const existingScreens = Array(3)
|
const existingScreens = Array(3)
|
||||||
.fill()
|
.fill()
|
||||||
.map(() => {
|
.map(() => {
|
||||||
|
@ -611,23 +624,21 @@ describe("Screens store", () => {
|
||||||
// Set the 2nd screen as the home screen
|
// Set the 2nd screen as the home screen
|
||||||
storeScreens[1].routing.homeScreen = true
|
storeScreens[1].routing.homeScreen = true
|
||||||
|
|
||||||
await ctx.test.screenStore.update(state => ({
|
await bb.screenStore.update(state => ({
|
||||||
...state,
|
...state,
|
||||||
screens: storeScreens,
|
screens: storeScreens,
|
||||||
}))
|
}))
|
||||||
|
|
||||||
const patchSpy = vi
|
const patchSpy = vi
|
||||||
.spyOn(ctx.test.screenStore, "patch")
|
.spyOn(bb.screenStore, "patch")
|
||||||
.mockImplementation(async (patchFn, screenId) => {
|
.mockImplementation(async (patchFn, screenId) => {
|
||||||
const target = ctx.test.store.screens.find(
|
const target = bb.store.screens.find(screen => screen._id === screenId)
|
||||||
screen => screen._id === screenId
|
|
||||||
)
|
|
||||||
patchFn(target)
|
patchFn(target)
|
||||||
|
|
||||||
await ctx.test.screenStore.replace(screenId, target)
|
await bb.screenStore.replace(screenId, target)
|
||||||
})
|
})
|
||||||
|
|
||||||
await ctx.test.screenStore.updateSetting(
|
await bb.screenStore.updateSetting(
|
||||||
storeScreens[0],
|
storeScreens[0],
|
||||||
"routing.homeScreen",
|
"routing.homeScreen",
|
||||||
true
|
true
|
||||||
|
@ -637,13 +648,15 @@ describe("Screens store", () => {
|
||||||
expect(patchSpy).toBeCalledTimes(2)
|
expect(patchSpy).toBeCalledTimes(2)
|
||||||
|
|
||||||
// The new homescreen for BASIC
|
// The new homescreen for BASIC
|
||||||
expect(ctx.test.store.screens[0].routing.homeScreen).toBe(true)
|
expect(bb.store.screens[0].routing.homeScreen).toBe(true)
|
||||||
|
|
||||||
// The previous home screen for the BASIC role is now unset
|
// The previous home screen for the BASIC role is now unset
|
||||||
expect(ctx.test.store.screens[1].routing.homeScreen).toBe(false)
|
expect(bb.store.screens[1].routing.homeScreen).toBe(false)
|
||||||
})
|
})
|
||||||
|
|
||||||
it("Ensure only one homescreen per role when updating screen setting. Multiple screen roles", async ctx => {
|
it("Ensure only one homescreen per role when updating screen setting. Multiple screen roles", async ({
|
||||||
|
bb,
|
||||||
|
}) => {
|
||||||
const expectedRoles = [
|
const expectedRoles = [
|
||||||
Constants.Roles.BASIC,
|
Constants.Roles.BASIC,
|
||||||
Constants.Roles.POWER,
|
Constants.Roles.POWER,
|
||||||
|
@ -675,30 +688,24 @@ describe("Screens store", () => {
|
||||||
sorted[9].routing.homeScreen = true
|
sorted[9].routing.homeScreen = true
|
||||||
|
|
||||||
// Set screens state
|
// Set screens state
|
||||||
await ctx.test.screenStore.update(state => ({
|
await bb.screenStore.update(state => ({
|
||||||
...state,
|
...state,
|
||||||
screens: sorted,
|
screens: sorted,
|
||||||
}))
|
}))
|
||||||
|
|
||||||
const patchSpy = vi
|
const patchSpy = vi
|
||||||
.spyOn(ctx.test.screenStore, "patch")
|
.spyOn(bb.screenStore, "patch")
|
||||||
.mockImplementation(async (patchFn, screenId) => {
|
.mockImplementation(async (patchFn, screenId) => {
|
||||||
const target = ctx.test.store.screens.find(
|
const target = bb.store.screens.find(screen => screen._id === screenId)
|
||||||
screen => screen._id === screenId
|
|
||||||
)
|
|
||||||
patchFn(target)
|
patchFn(target)
|
||||||
|
|
||||||
await ctx.test.screenStore.replace(screenId, target)
|
await bb.screenStore.replace(screenId, target)
|
||||||
})
|
})
|
||||||
|
|
||||||
// ADMIN homeScreen updated from 0 to 2
|
// ADMIN homeScreen updated from 0 to 2
|
||||||
await ctx.test.screenStore.updateSetting(
|
await bb.screenStore.updateSetting(sorted[2], "routing.homeScreen", true)
|
||||||
sorted[2],
|
|
||||||
"routing.homeScreen",
|
|
||||||
true
|
|
||||||
)
|
|
||||||
|
|
||||||
const results = ctx.test.store.screens.reduce((acc, screen) => {
|
const results = bb.store.screens.reduce((acc, screen) => {
|
||||||
if (screen.routing.homeScreen) {
|
if (screen.routing.homeScreen) {
|
||||||
acc[screen.routing.roleId] = acc[screen.routing.roleId] || []
|
acc[screen.routing.roleId] = acc[screen.routing.roleId] || []
|
||||||
acc[screen.routing.roleId].push(screen)
|
acc[screen.routing.roleId].push(screen)
|
||||||
|
@ -706,7 +713,7 @@ describe("Screens store", () => {
|
||||||
return acc
|
return acc
|
||||||
}, {})
|
}, {})
|
||||||
|
|
||||||
const screens = ctx.test.store.screens
|
const screens = bb.store.screens
|
||||||
// Should still only be one of each homescreen
|
// Should still only be one of each homescreen
|
||||||
expect(results[Constants.Roles.ADMIN].length).toBe(1)
|
expect(results[Constants.Roles.ADMIN].length).toBe(1)
|
||||||
expect(screens[2].routing.homeScreen).toBe(true)
|
expect(screens[2].routing.homeScreen).toBe(true)
|
||||||
|
@ -724,74 +731,80 @@ describe("Screens store", () => {
|
||||||
expect(patchSpy).toBeCalledTimes(2)
|
expect(patchSpy).toBeCalledTimes(2)
|
||||||
})
|
})
|
||||||
|
|
||||||
it("Sequential patch check. Exit if the screenId is not valid.", async ctx => {
|
it("Sequential patch check. Exit if the screenId is not valid.", async ({
|
||||||
|
bb,
|
||||||
|
}) => {
|
||||||
const screenDoc = getScreenFixture()
|
const screenDoc = getScreenFixture()
|
||||||
const existingDocId = getScreenDocId()
|
const existingDocId = getScreenDocId()
|
||||||
screenDoc._json._id = existingDocId
|
screenDoc._json._id = existingDocId
|
||||||
|
|
||||||
const original = screenDoc.json()
|
const original = screenDoc.json()
|
||||||
|
|
||||||
await ctx.test.screenStore.update(state => ({
|
await bb.screenStore.update(state => ({
|
||||||
...state,
|
...state,
|
||||||
screens: [original],
|
screens: [original],
|
||||||
}))
|
}))
|
||||||
|
|
||||||
const saveSpy = vi
|
const saveSpy = vi
|
||||||
.spyOn(ctx.test.screenStore, "save")
|
.spyOn(bb.screenStore, "save")
|
||||||
.mockImplementation(async () => {
|
.mockImplementation(async () => {
|
||||||
return
|
return
|
||||||
})
|
})
|
||||||
|
|
||||||
// A screen with this Id does not exist
|
// A screen with this Id does not exist
|
||||||
await ctx.test.screenStore.sequentialScreenPatch(() => {}, "123")
|
await bb.screenStore.sequentialScreenPatch(() => {}, "123")
|
||||||
expect(saveSpy).not.toBeCalled()
|
expect(saveSpy).not.toBeCalled()
|
||||||
})
|
})
|
||||||
|
|
||||||
it("Sequential patch check. Exit if the patchFn result is false", async ctx => {
|
it("Sequential patch check. Exit if the patchFn result is false", async ({
|
||||||
|
bb,
|
||||||
|
}) => {
|
||||||
const screenDoc = getScreenFixture()
|
const screenDoc = getScreenFixture()
|
||||||
const existingDocId = getScreenDocId()
|
const existingDocId = getScreenDocId()
|
||||||
screenDoc._json._id = existingDocId
|
screenDoc._json._id = existingDocId
|
||||||
|
|
||||||
const original = screenDoc.json()
|
const original = screenDoc.json()
|
||||||
// Set screens state
|
// Set screens state
|
||||||
await ctx.test.screenStore.update(state => ({
|
await bb.screenStore.update(state => ({
|
||||||
...state,
|
...state,
|
||||||
screens: [original],
|
screens: [original],
|
||||||
}))
|
}))
|
||||||
|
|
||||||
const saveSpy = vi
|
const saveSpy = vi
|
||||||
.spyOn(ctx.test.screenStore, "save")
|
.spyOn(bb.screenStore, "save")
|
||||||
.mockImplementation(async () => {
|
.mockImplementation(async () => {
|
||||||
return
|
return
|
||||||
})
|
})
|
||||||
|
|
||||||
// Returning false from the patch will abort the save
|
// Returning false from the patch will abort the save
|
||||||
await ctx.test.screenStore.sequentialScreenPatch(() => {
|
await bb.screenStore.sequentialScreenPatch(() => {
|
||||||
return false
|
return false
|
||||||
}, "123")
|
}, "123")
|
||||||
|
|
||||||
expect(saveSpy).not.toBeCalled()
|
expect(saveSpy).not.toBeCalled()
|
||||||
})
|
})
|
||||||
|
|
||||||
it("Sequential patch check. Patch applied and save requested", async ctx => {
|
it("Sequential patch check. Patch applied and save requested", async ({
|
||||||
|
bb,
|
||||||
|
}) => {
|
||||||
const screenDoc = getScreenFixture()
|
const screenDoc = getScreenFixture()
|
||||||
const existingDocId = getScreenDocId()
|
const existingDocId = getScreenDocId()
|
||||||
screenDoc._json._id = existingDocId
|
screenDoc._json._id = existingDocId
|
||||||
|
|
||||||
const original = screenDoc.json()
|
const original = screenDoc.json()
|
||||||
|
|
||||||
await ctx.test.screenStore.update(state => ({
|
await bb.screenStore.update(state => ({
|
||||||
...state,
|
...state,
|
||||||
screens: [original],
|
screens: [original],
|
||||||
}))
|
}))
|
||||||
|
|
||||||
const saveSpy = vi
|
const saveSpy = vi
|
||||||
.spyOn(ctx.test.screenStore, "save")
|
.spyOn(bb.screenStore, "save")
|
||||||
.mockImplementation(async () => {
|
.mockImplementation(async () => {
|
||||||
return
|
return
|
||||||
})
|
})
|
||||||
|
|
||||||
await ctx.test.screenStore.sequentialScreenPatch(screen => {
|
await bb.screenStore.sequentialScreenPatch(screen => {
|
||||||
screen.name = "updated"
|
screen.name = "updated"
|
||||||
}, existingDocId)
|
}, existingDocId)
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,7 @@ export const sleep = ms => new Promise(resolve => setTimeout(resolve, ms))
|
||||||
* Utility to wrap an async function and ensure all invocations happen
|
* Utility to wrap an async function and ensure all invocations happen
|
||||||
* sequentially.
|
* sequentially.
|
||||||
* @param fn the async function to run
|
* @param fn the async function to run
|
||||||
* @return {Promise} a sequential version of the function
|
* @return {Function} a sequential version of the function
|
||||||
*/
|
*/
|
||||||
export const sequential = fn => {
|
export const sequential = fn => {
|
||||||
let queue = []
|
let queue = []
|
||||||
|
|
Loading…
Reference in New Issue