Browse Source

Remove DataCollection componentDef

pull/6482/head
mohamedsalem401 1 year ago
parent
commit
4477d066e6
  1. 8
      packages/core/src/data_sources/model/conditional_variables/ComponentDataOutput.ts
  2. 161
      packages/core/src/data_sources/model/data_collection/ComponentDataCollection.ts
  3. 1
      packages/core/src/data_sources/model/data_collection/constants.ts
  4. 13
      packages/core/src/data_sources/model/data_collection/types.ts
  5. 7
      packages/core/src/data_sources/utils.ts
  6. 17
      packages/core/src/dom_components/index.ts
  7. 162
      packages/core/test/specs/data_sources/model/data_collection/ComponentDataCollection.getters-setters.ts
  8. 548
      packages/core/test/specs/data_sources/model/data_collection/ComponentDataCollection.ts
  9. 165
      packages/core/test/specs/data_sources/model/data_collection/ComponentDataCollectionVariable.ts
  10. 80
      packages/core/test/specs/data_sources/model/data_collection/__snapshots__/ComponentDataCollection.ts.snap
  11. 80
      packages/core/test/specs/data_sources/model/data_collection/__snapshots__/ComponentDataCollectionVariable.ts.snap
  12. 59
      packages/core/test/specs/data_sources/model/data_collection/__snapshots__/nestedComponentDataCollections.ts.snap
  13. 450
      packages/core/test/specs/data_sources/model/data_collection/nestedComponentDataCollections.ts

8
packages/core/src/data_sources/model/conditional_variables/ConditionalOutputBase.ts → packages/core/src/data_sources/model/conditional_variables/ComponentDataOutput.ts

@ -1,9 +1,9 @@
import Component from '../../../dom_components/model/Component';
import { ComponentDefinitionDefined, ToHTMLOptions } from '../../../dom_components/model/types';
import { ComponentDefinitionDefined } from '../../../dom_components/model/types';
import { toLowerCase } from '../../../utils/mixins';
import { isDataConditionDisplayType } from '../../utils';
import { isComponentDataOutputType } from '../../utils';
export default class ConditionalOutputBase extends Component {
export default class ComponentDataOutput extends Component {
get defaults(): ComponentDefinitionDefined {
return {
// @ts-ignore
@ -14,6 +14,6 @@ export default class ConditionalOutputBase extends Component {
}
static isComponent(el: HTMLElement) {
return isDataConditionDisplayType(toLowerCase(el.tagName));
return isComponentDataOutputType(toLowerCase(el.tagName));
}
}

161
packages/core/src/data_sources/model/data_collection/ComponentDataCollection.ts

@ -1,17 +1,27 @@
import { bindAll, isArray } from 'underscore';
import { ObjectAny } from '../../../common';
import Component from '../../../dom_components/model/Component';
import { ComponentDefinition, ComponentOptions } from '../../../dom_components/model/types';
import {
ComponentAddType,
ComponentDefinition,
ComponentDefinitionDefined,
ComponentOptions,
} from '../../../dom_components/model/types';
import EditorModel from '../../../editor/model/Editor';
import { isObject, serialize, toLowerCase } from '../../../utils/mixins';
import DataResolverListener from '../DataResolverListener';
import DataSource from '../DataSource';
import DataVariable, { DataVariableProps, DataVariableType } from '../DataVariable';
import { ensureComponentInstance, isDataVariable } from '../../utils';
import { DataCollectionType, keyCollectionDefinition, keyCollectionsStateMap, keyIsCollectionItem } from './constants';
import {
DataCollectionItemType,
DataCollectionType,
keyCollectionDefinition,
keyCollectionsStateMap,
keyIsCollectionItem,
} from './constants';
import {
ComponentDataCollectionProps,
DataCollectionConfig,
DataCollectionDataSource,
DataCollectionProps,
DataCollectionState,
@ -20,10 +30,26 @@ import {
import { getSymbolsToUpdate } from '../../../dom_components/model/SymbolUtils';
import { StyleProps, UpdateStyleOptions } from '../../../domain_abstract/model/StyleableModel';
import { updateFromWatcher } from '../../../dom_components/model/ComponentDataResolverWatchers';
import Components from '../../../dom_components/model/Components';
import ComponentDataOutput from '../conditional_variables/ComponentDataOutput';
export default class ComponentDataCollection extends Component {
dataSourceWatcher?: DataResolverListener;
get defaults(): ComponentDefinitionDefined {
return {
// @ts-ignore
...super.defaults,
droppable: false,
type: DataCollectionType,
components: [
{
type: DataCollectionItemType,
},
],
};
}
constructor(props: ComponentDataCollectionProps, opt: ComponentOptions) {
const collectionDef = props[keyCollectionDefinition];
@ -32,9 +58,8 @@ export default class ComponentDataCollection extends Component {
}
const em = opt.em;
const newProps = { ...props, components: undefined, droppable: false } as any;
const newProps = { ...props, droppable: false } as any;
const cmp: ComponentDataCollection = super(newProps, opt) as unknown as ComponentDataCollection;
if (!collectionDef) {
em.logError('missing collection definition');
return cmp;
@ -59,27 +84,23 @@ export default class ComponentDataCollection extends Component {
}
getConfigStartIndex() {
return this.collectionConfig.startIndex;
return this.collectionDef.startIndex;
}
getConfigEndIndex() {
return this.collectionConfig.endIndex;
}
getComponentDef(): ComponentDefinition {
return this.getFirstChildJSON();
return this.collectionDef.endIndex;
}
getDataSource(): DataCollectionDataSource {
return this.collectionDef?.collectionConfig?.dataSource;
return this.collectionDef?.dataSource;
}
getCollectionId(): string {
return this.collectionDef?.collectionConfig?.collectionId;
return this.collectionDef?.collectionId;
}
setComponentDef(componentDef: ComponentDefinition) {
this.set(keyCollectionDefinition, { ...this.collectionDef, componentDef });
getCollectionItemComponents() {
return this.components().at(0).components();
}
setStartIndex(startIndex: number): void {
@ -95,25 +116,26 @@ export default class ComponentDataCollection extends Component {
this.updateCollectionConfig({ endIndex });
}
private updateCollectionConfig(updates: Partial<DataCollectionConfig>): void {
private updateCollectionConfig(updates: Partial<DataCollectionProps>): void {
this.set(keyCollectionDefinition, {
...this.collectionDef,
collectionConfig: {
...this.collectionConfig,
...updates,
},
...updates,
});
}
setDataSource(dataSource: DataCollectionDataSource) {
this.set(keyCollectionDefinition, {
...this.collectionDef,
collectionConfig: { ...this.collectionConfig, dataSource },
dataSource,
});
}
setCollectionItemComponents(content: ComponentAddType) {
this.components().at(0).components(content);
}
private getDataSourceItems() {
return this.collectionDef?.collectionConfig ? getDataSourceItems(this.collectionConfig.dataSource, this.em) : [];
return getDataSourceItems(this.collectionDef.dataSource, this.em);
}
private getCollectionStateMap() {
@ -124,20 +146,8 @@ export default class ComponentDataCollection extends Component {
return (this.get(keyCollectionDefinition) || {}) as DataCollectionProps;
}
private get collectionConfig() {
return (this.collectionDef?.collectionConfig || {}) as DataCollectionConfig;
}
private get collectionDataSource() {
return this.collectionConfig.dataSource;
}
private getFirstChildJSON() {
const firstChild = this.components().at(0);
const firstChildJSON = firstChild ? serialize(firstChild) : this.collectionDef.componentDef;
delete firstChildJSON?.draggable;
delete firstChildJSON?.removable;
return firstChildJSON;
return this.collectionDef.dataSource;
}
private listenToDataSource() {
@ -155,16 +165,26 @@ export default class ComponentDataCollection extends Component {
this.components().reset(this.getCollectionItems(), updateFromWatcher as any);
}
getCollectionItems() {
const { componentDef, collectionConfig } = this.collectionDef;
const result = validateCollectionConfig(collectionConfig, componentDef, this.em);
private getCollectionItems() {
const dataConditionItemModel = this.em.Components.getType(DataCollectionItemType)!.model;
const firstChild =
this.components().at(0) ||
new dataConditionItemModel(
{
type: DataCollectionItemType,
},
this.opt,
);
// TODO: Move to component view
firstChild.addStyle({ display: 'none' });
const components: Component[] = [firstChild];
const result = validateCollectionDef(this.collectionDef, this.em);
if (!result) {
return [];
return components;
}
const components: Component[] = [];
const collectionId = collectionConfig.collectionId;
const collectionId = this.collectionDef.collectionId;
const items = this.getDataSourceItems();
const startIndex = this.getConfigStartIndex() ?? 0;
@ -172,8 +192,14 @@ export default class ComponentDataCollection extends Component {
const endIndex = Math.min(items.length - 1, configEndIndex);
const totalItems = endIndex - startIndex + 1;
const parentCollectionStateMap = this.getCollectionStateMap();
if (parentCollectionStateMap[collectionId]) {
this.em.logError(
`The collection ID "${collectionId}" already exists in the parent collection state. Overriding it is not allowed.`,
);
return components;
}
let symbolMain: Component;
for (let index = startIndex; index <= endIndex; index++) {
const item = items[index];
const isFirstItem = index === startIndex;
@ -187,31 +213,21 @@ export default class ComponentDataCollection extends Component {
remainingItems: totalItems - (index + 1),
};
if (parentCollectionStateMap[collectionId]) {
this.em.logError(
`The collection ID "${collectionId}" already exists in the parent collection state. Overriding it is not allowed.`,
);
return [];
}
const collectionsStateMap: DataCollectionStateMap = {
...parentCollectionStateMap,
[collectionId]: collectionState,
};
if (isFirstItem) {
symbolMain = ensureComponentInstance(
{
...componentDef,
draggable: false,
removable: false,
},
this.opt,
);
setCollectionStateMapAndPropagate(collectionsStateMap, collectionId)(firstChild);
// TODO: Move to component view
firstChild.addStyle({ display: '' });
continue;
}
const instance = symbolMain!.clone({ symbol: true });
!isFirstItem && instance.set('locked', true);
const instance = firstChild!.clone({ symbol: true });
instance.set('locked', true);
setCollectionStateMapAndPropagate(collectionsStateMap, collectionId)(instance);
components.push(instance);
}
@ -225,10 +241,10 @@ export default class ComponentDataCollection extends Component {
toJSON(opts?: ObjectAny) {
const json = super.toJSON.call(this, opts) as ComponentDataCollectionProps;
json[keyCollectionDefinition].componentDef = this.getFirstChildJSON();
delete json.components;
delete json.droppable;
return json;
const components = new Components(this.components().at(0), this.opt) as any;
return { ...json, components };
}
}
@ -290,25 +306,19 @@ function logErrorIfMissing(property: any, propertyPath: string, em: EditorModel)
return true;
}
function validateCollectionConfig(
collectionConfig: DataCollectionConfig,
componentDef: ComponentDefinition,
em: EditorModel,
) {
function validateCollectionDef(collectionDef: DataCollectionProps, em: EditorModel) {
const validations = [
{ property: collectionConfig, propertyPath: 'collectionConfig' },
{ property: componentDef, propertyPath: 'componentDef' },
{ property: collectionConfig?.collectionId, propertyPath: 'collectionConfig.collectionId' },
{ property: collectionConfig?.dataSource, propertyPath: 'collectionConfig.dataSource' },
{ property: collectionDef?.collectionId, propertyPath: 'collectionDef.collectionId' },
{ property: collectionDef?.dataSource, propertyPath: 'collectionDef.dataSource' },
];
for (const { property, propertyPath } of validations) {
if (!logErrorIfMissing(property, propertyPath, em)) {
for (const { propertyPath } of validations) {
if (!logErrorIfMissing(collectionDef, propertyPath, em)) {
return [];
}
}
const startIndex = collectionConfig?.startIndex;
const startIndex = collectionDef?.startIndex;
if (startIndex !== undefined && (startIndex < 0 || !Number.isInteger(startIndex))) {
em.logError(`Invalid startIndex: ${startIndex}. It must be a non-negative integer.`);
@ -374,6 +384,7 @@ function getDataSourceItems(dataSource: DataCollectionDataSource, em: EditorMode
break;
}
default:
break;
}
return items;

1
packages/core/src/data_sources/model/data_collection/constants.ts

@ -1,4 +1,5 @@
export const DataCollectionType = 'data-collection';
export const DataCollectionItemType = 'data-collection-item';
export const DataCollectionVariableType = 'data-collection-variable';
export const keyCollectionDefinition = 'collectionDef';
export const keyIsCollectionItem = '__is_data_collection_item';

13
packages/core/src/data_sources/model/data_collection/types.ts

@ -4,13 +4,6 @@ import { DataVariableProps } from '../DataVariable';
export type DataCollectionDataSource = DataVariableProps | DataCollectionVariableProps;
export interface DataCollectionConfig {
collectionId: string;
startIndex?: number;
endIndex?: number;
dataSource: DataCollectionDataSource;
}
export enum DataCollectionStateVariableType {
currentIndex = 'currentIndex',
startIndex = 'startIndex',
@ -45,8 +38,10 @@ export interface ComponentDataCollectionVariableProps
Omit<ComponentProperties, 'type'> {}
export interface DataCollectionProps {
collectionConfig: DataCollectionConfig;
componentDef: ComponentDefinition;
collectionId: string;
startIndex?: number;
endIndex?: number;
dataSource: DataCollectionDataSource;
}
export interface DataCollectionVariableProps {

7
packages/core/src/data_sources/utils.ts

@ -1,9 +1,8 @@
import EditorModel from '../editor/model/Editor';
import { DataResolver, DataResolverProps } from './types';
import { DataConditionDisplayType } from './model/conditional_variables/ComponentDataCondition';
import { DataCollectionStateMap } from './model/data_collection/types';
import DataCollectionVariable from './model/data_collection/DataCollectionVariable';
import { DataCollectionVariableType } from './model/data_collection/constants';
import { DataCollectionItemType, DataCollectionVariableType } from './model/data_collection/constants';
import { DataConditionType, DataCondition } from './model/conditional_variables/DataCondition';
import DataVariable, { DataVariableProps, DataVariableType } from './model/DataVariable';
import Component from '../dom_components/model/Component';
@ -86,6 +85,6 @@ export const ensureComponentInstance = (
return new Model(serialize(cmp ?? {}), opt);
};
export const isDataConditionDisplayType = (type: string | undefined): type is DataConditionDisplayType => {
return !!type && [DataConditionIfTrueType, DataConditionIfFalseType].includes(type);
export const isComponentDataOutputType = (type: string | undefined) => {
return !!type && [DataCollectionItemType, DataConditionIfTrueType, DataConditionIfFalseType].includes(type);
};

17
packages/core/src/dom_components/index.ts

@ -128,7 +128,11 @@ import { DataVariableType } from '../data_sources/model/DataVariable';
import { DataConditionType } from '../data_sources/model/conditional_variables/DataCondition';
import ComponentDataConditionView from '../data_sources/view/ComponentDataConditionView';
import ComponentDataCollection from '../data_sources/model/data_collection/ComponentDataCollection';
import { DataCollectionType, DataCollectionVariableType } from '../data_sources/model/data_collection/constants';
import {
DataCollectionItemType,
DataCollectionType,
DataCollectionVariableType,
} from '../data_sources/model/data_collection/constants';
import ComponentDataCollectionVariable from '../data_sources/model/data_collection/ComponentDataCollectionVariable';
import ComponentDataCollectionVariableView from '../data_sources/view/ComponentDataCollectionVariableView';
import ComponentDataCollectionView from '../data_sources/view/ComponentDataCollectionView';
@ -137,7 +141,7 @@ import {
DataConditionIfFalseType,
DataConditionIfTrueType,
} from '../data_sources/model/conditional_variables/constants';
import ConditionalOutputBase from '../data_sources/model/conditional_variables/ConditionalOutputBase';
import ComponentDataOutput from '../data_sources/model/conditional_variables/ComponentDataOutput';
export type ComponentEvent =
| 'component:create'
@ -203,14 +207,19 @@ export interface CanMoveResult {
export default class ComponentManager extends ItemManagerModule<DomComponentsConfig, any> {
componentTypes: ComponentStackItem[] = [
{
id: DataCollectionItemType,
model: ComponentDataOutput,
view: ComponentView,
},
{
id: DataConditionIfTrueType,
model: ConditionalOutputBase,
model: ComponentDataOutput,
view: ComponentView,
},
{
id: DataConditionIfFalseType,
model: ConditionalOutputBase,
model: ComponentDataOutput,
view: ComponentView,
},
{

162
packages/core/test/specs/data_sources/model/data_collection/ComponentDataCollection.getters-setters.ts

@ -1,10 +1,14 @@
import { Component, DataRecord, DataSource, DataSourceManager, Editor } from '../../../../../src';
import { Component, DataSource, DataSourceManager } from '../../../../../src';
import { DataVariableType } from '../../../../../src/data_sources/model/DataVariable';
import {
DataCollectionItemType,
DataCollectionType,
DataCollectionVariableType,
} from '../../../../../src/data_sources/model/data_collection/constants';
import { DataCollectionStateVariableType } from '../../../../../src/data_sources/model/data_collection/types';
import {
ComponentDataCollectionProps,
DataCollectionStateVariableType,
} from '../../../../../src/data_sources/model/data_collection/types';
import EditorModel from '../../../../../src/editor/model/Editor';
import { setupTestEditor } from '../../../../common';
import ComponentDataCollection from '../../../../../src/data_sources/model/data_collection/ComponentDataCollection';
@ -14,8 +18,6 @@ describe('Collection component getters and setters', () => {
let dsm: DataSourceManager;
let dataSource: DataSource;
let wrapper: Component;
let firstRecord: DataRecord;
let secondRecord: DataRecord;
beforeEach(() => {
({ em, dsm } = setupTestEditor());
@ -28,8 +30,6 @@ describe('Collection component getters and setters', () => {
{ id: 'user3', user: 'user3', firstName: 'Name3', age: '16' },
],
});
firstRecord = dataSource.getRecord('user1')!;
secondRecord = dataSource.getRecord('user2')!;
});
afterEach(() => {
@ -40,10 +40,11 @@ describe('Collection component getters and setters', () => {
let cmp: ComponentDataCollection;
beforeEach(() => {
cmp = wrapper.components({
const cmpDef = {
type: DataCollectionType,
collectionDef: {
componentDef: {
components: {
type: DataCollectionItemType,
components: {
type: 'default',
components: [
{
@ -60,17 +61,19 @@ describe('Collection component getters and setters', () => {
},
],
},
collectionConfig: {
collectionId: 'my_collection',
startIndex: 1,
endIndex: 2,
dataSource: {
type: DataVariableType,
path: 'my_data_source_id',
},
},
collectionDef: {
collectionId: 'my_collection',
startIndex: 1,
endIndex: 2,
dataSource: {
type: DataVariableType,
path: 'my_data_source_id',
},
},
})[0] as ComponentDataCollection;
} as ComponentDataCollectionProps;
cmp = wrapper.components(cmpDef)[0] as unknown as ComponentDataCollection;
});
test('getItemsCount should return the correct number of items', () => {
@ -85,12 +88,12 @@ describe('Collection component getters and setters', () => {
expect(cmp.getConfigEndIndex()).toBe(2);
});
test('getComponentDef should return the correct component definition', () => {
const componentDef = cmp.getComponentDef();
test('components should return the correct component definition', () => {
const firstChildJSON = JSON.parse(JSON.stringify(cmp.getCollectionItemComponents().at(0)));
expect(componentDef.type).toBe('default');
expect(componentDef.components).toHaveLength(1);
expect(componentDef?.components?.[0].attributes?.['dataUser']).toEqual({
expect(firstChildJSON['type']).toBe('default');
expect(firstChildJSON.components).toHaveLength(1);
expect(firstChildJSON.components?.[0].attributes?.['dataUser']).toEqual({
type: DataCollectionVariableType,
variableType: DataCollectionStateVariableType.currentItem,
collectionId: 'my_collection',
@ -138,10 +141,11 @@ describe('Collection component getters and setters', () => {
let cmp: ComponentDataCollection;
beforeEach(() => {
cmp = wrapper.components({
const cmpDef = {
type: DataCollectionType,
collectionDef: {
componentDef: {
components: {
type: DataCollectionItemType,
components: {
type: 'default',
components: [
{
@ -158,22 +162,24 @@ describe('Collection component getters and setters', () => {
},
],
},
collectionConfig: {
collectionId: 'my_collection',
startIndex: 1,
endIndex: 2,
dataSource: {
type: DataVariableType,
path: 'my_data_source_id',
},
},
collectionDef: {
collectionId: 'my_collection',
startIndex: 1,
endIndex: 2,
dataSource: {
type: DataVariableType,
path: 'my_data_source_id',
},
},
})[0] as ComponentDataCollection;
} as ComponentDataCollectionProps;
cmp = wrapper.components(cmpDef)[0] as unknown as ComponentDataCollection;
});
test('setComponentDef should update the component definition and reflect in children', () => {
const newComponentDef = {
type: 'newType',
test('components should update the component definition and reflect in children', () => {
const newItemCmpDef = {
type: 'text',
components: [
{
type: 'default',
@ -189,34 +195,40 @@ describe('Collection component getters and setters', () => {
},
],
};
cmp.setComponentDef(newComponentDef);
cmp.setCollectionItemComponents(newItemCmpDef);
expect(cmp.getItemsCount()).toBe(2);
const children = cmp.components();
expect(children).toHaveLength(2);
expect(children.at(0).get('type')).toBe('newType');
expect(children.at(0).components().at(0).get('tagName')).toBe('span');
expect(children.at(0).components().at(0).getAttributes()['data-name']).toBe('Name2');
const firstItemCmp = children.at(0).components().at(0);
expect(firstItemCmp.get('type')).toBe('text');
expect(firstItemCmp.components().at(0).get('tagName')).toBe('span');
expect(firstItemCmp.components().at(0).getAttributes()['data-name']).toBe('Name2');
});
test('setStartIndex should update the start index and reflect in children', () => {
cmp.setStartIndex(0);
expect(cmp.getConfigStartIndex()).toBe(0);
expect(cmp.getItemsCount()).toBe(3);
const children = cmp.components();
expect(children).toHaveLength(3);
expect(children.at(0).components().at(0).getAttributes()['dataUser']).toBe('user1');
expect(children.at(1).components().at(0).getAttributes()['dataUser']).toBe('user2');
expect(children.at(2).components().at(0).getAttributes()['dataUser']).toBe('user3');
const firstItemCmp = children.at(0).components().at(0);
expect(firstItemCmp.components().at(0).getAttributes()['dataUser']).toBe('user1');
const secondItemCmp = children.at(1).components().at(0);
expect(secondItemCmp.components().at(0).getAttributes()['dataUser']).toBe('user2');
const thirdItemCmp = children.at(2).components().at(0);
expect(thirdItemCmp.components().at(0).getAttributes()['dataUser']).toBe('user3');
});
test('setEndIndex should update the end index and reflect in children', () => {
cmp.setEndIndex(3);
expect(cmp.getConfigEndIndex()).toBe(3);
expect(cmp.getItemsCount()).toBe(2);
const children = cmp.components();
expect(children).toHaveLength(2);
expect(children.at(0).components().at(0).getAttributes()['dataUser']).toBe('user2');
expect(children.at(1).components().at(0).getAttributes()['dataUser']).toBe('user3');
const firstItemCmp = children.at(0).components().at(0);
expect(firstItemCmp.components().at(0).getAttributes()['dataUser']).toBe('user2');
const secondItemCmp = children.at(1).components().at(0);
expect(secondItemCmp.components().at(0).getAttributes()['dataUser']).toBe('user3');
});
test('setDataSource should update the data source and reflect in children', () => {
@ -233,24 +245,25 @@ describe('Collection component getters and setters', () => {
path: 'new_data_source_id',
});
expect(cmp.getItemsCount()).toBe(1);
const children = cmp.components();
expect(children).toHaveLength(1);
expect(children.at(0).components().at(0).getAttributes()['dataUser']).toBe('user5');
const firstItemCmp = children.at(0).components().at(0);
expect(firstItemCmp.components().at(0).getAttributes()['dataUser']).toBe('user5');
});
test('setStartIndex with zero should include the first record', () => {
cmp.setStartIndex(0);
expect(cmp.getItemsCount()).toBe(3);
const children = cmp.components();
expect(children).toHaveLength(3);
expect(children.at(0).components().at(0).getAttributes()['dataUser']).toBe('user1');
const firstItemCmp = children.at(0).components().at(0);
expect(firstItemCmp.components().at(0).getAttributes()['dataUser']).toBe('user1');
});
test('setEndIndex with zero should result in no children', () => {
cmp.setEndIndex(0);
const children = cmp.components();
expect(children).toHaveLength(0);
expect(cmp.getItemsCount()).toBe(0);
});
test('setDataSource with an empty data source should result in no children', () => {
@ -264,8 +277,7 @@ describe('Collection component getters and setters', () => {
path: 'empty_data_source_id',
});
const children = cmp.components();
expect(children).toHaveLength(0);
expect(cmp.getItemsCount()).toBe(0);
});
});
@ -273,10 +285,11 @@ describe('Collection component getters and setters', () => {
let cmp: ComponentDataCollection;
beforeEach(() => {
cmp = wrapper.components({
const cmpDef = {
type: DataCollectionType,
collectionDef: {
componentDef: {
components: {
type: DataCollectionItemType,
components: {
type: 'default',
components: [
{
@ -293,17 +306,18 @@ describe('Collection component getters and setters', () => {
},
],
},
collectionConfig: {
collectionId: 'my_collection',
startIndex: 1,
endIndex: 2,
dataSource: {
type: DataVariableType,
path: 'my_data_source_id',
},
},
collectionDef: {
collectionId: 'my_collection',
startIndex: 1,
endIndex: 2,
dataSource: {
type: DataVariableType,
path: 'my_data_source_id',
},
},
})[0] as ComponentDataCollection;
} as ComponentDataCollectionProps;
cmp = wrapper.components(cmpDef)[0] as unknown as ComponentDataCollection;
});
test('HTML output should reflect changes in startIndex', () => {
@ -319,6 +333,7 @@ describe('Collection component getters and setters', () => {
cmp.setEndIndex(3);
const html = cmp.toHTML();
expect(html).not.toContain('dataUser="user1"');
expect(html).toContain('dataUser="user2"');
expect(html).toContain('dataUser="user3"');
});
@ -337,14 +352,17 @@ describe('Collection component getters and setters', () => {
});
const html = cmp.toHTML();
expect(html).not.toContain('dataUser="user1"');
expect(html).not.toContain('dataUser="user2"');
expect(html).not.toContain('dataUser="user3"');
expect(html).toContain('dataUser="user5"');
});
test('HTML output should be empty when endIndex is zero', () => {
cmp.setEndIndex(0);
const html = cmp.toHTML();
expect(html).not.toContain('dataUser');
const dataOutputCmp = cmp.components().at(0);
expect(dataOutputCmp.getStyle().display).toBe('none');
});
});
});

548
packages/core/test/specs/data_sources/model/data_collection/ComponentDataCollection.ts

@ -1,14 +1,19 @@
import { Component, DataRecord, DataSource, DataSourceManager, Editor } from '../../../../../src';
import { DataVariableType } from '../../../../../src/data_sources/model/DataVariable';
import {
DataCollectionItemType,
DataCollectionType,
DataCollectionVariableType,
} from '../../../../../src/data_sources/model/data_collection/constants';
import { DataCollectionStateVariableType } from '../../../../../src/data_sources/model/data_collection/types';
import {
ComponentDataCollectionProps,
DataCollectionStateVariableType,
} from '../../../../../src/data_sources/model/data_collection/types';
import EditorModel from '../../../../../src/editor/model/Editor';
import { setupTestEditor } from '../../../../common';
import { getSymbolMain } from '../../../../../src/dom_components/model/SymbolUtils';
import { ProjectData } from '../../../../../src/storage_manager';
import ComponentDataCollection from '../../../../../src/data_sources/model/data_collection/ComponentDataCollection';
describe('Collection component', () => {
let em: EditorModel;
@ -40,82 +45,55 @@ describe('Collection component', () => {
});
test('Collection component should be undroppable', () => {
const cmp = wrapper.components({
const cmpDef = {
type: DataCollectionType,
collectionDef: {
componentDef: {
components: {
type: DataCollectionItemType,
components: {
type: 'default',
},
collectionConfig: {
collectionId: 'my_collection',
dataSource: {
type: DataVariableType,
path: 'my_data_source_id',
},
},
collectionDef: {
collectionId: 'my_collection',
dataSource: {
type: DataVariableType,
path: 'my_data_source_id',
},
},
})[0];
} as ComponentDataCollectionProps;
const cmp = wrapper.components(cmpDef)[0];
expect(cmp.get('droppable')).toBe(false);
});
test('Collection items should be undraggable', () => {
const cmp = wrapper.components({
test('Collection items should be undraggable and unremovable', () => {
const cmpDef = {
type: DataCollectionType,
collectionDef: {
componentDef: {
components: {
type: DataCollectionItemType,
components: {
type: 'default',
},
collectionConfig: {
collectionId: 'my_collection',
dataSource: {
type: DataVariableType,
path: 'my_data_source_id',
},
},
},
})[0];
cmp.components().forEach((child) => {
expect(child.get('draggable')).toBe(false);
});
});
test('Collection items should be symbols', () => {
const cmp = wrapper.components({
type: DataCollectionType,
collectionDef: {
componentDef: {
type: 'default',
components: [
{
type: 'default',
},
],
},
collectionConfig: {
collectionId: 'my_collection',
dataSource: {
type: DataVariableType,
path: 'my_data_source_id',
},
collectionId: 'my_collection',
dataSource: {
type: DataVariableType,
path: 'my_data_source_id',
},
},
})[0];
} as ComponentDataCollectionProps;
const cmp = wrapper.components(cmpDef)[0];
expect(cmp.components()).toHaveLength(3);
cmp.components().forEach((child) => expect(child.get('type')).toBe('default'));
const children = cmp.components();
const firstChild = children.at(0);
children.slice(1).forEach((component) => {
expect(getSymbolMain(component)).toBe(firstChild);
cmp.components().forEach((child) => {
expect(child.get('draggable')).toBe(false);
expect(child.get('removable')).toBe(false);
});
});
describe('Collection variables', () => {
describe('Properties', () => {
let cmp: Component;
let cmp: ComponentDataCollection;
let firstChild!: Component;
let firstGrandchild!: Component;
let secondChild!: Component;
@ -131,16 +109,17 @@ describe('Collection component', () => {
const checkRecordsWithInnerCmp = () => {
dataSource.getRecords().forEach((record, i) => {
const innerCmp = cmp.components().at(i).components().at(1);
const innerCmp = cmp.components().at(i).components().at(0).components().at(1);
checkHtmlModelAndView({ cmp: innerCmp, innerHTML: record.get('firstName') });
});
};
beforeEach(() => {
cmp = wrapper.components({
const cmpDef = {
type: DataCollectionType,
collectionDef: {
componentDef: {
components: {
type: DataCollectionItemType,
components: {
type: 'default',
components: [
{
@ -173,21 +152,22 @@ describe('Collection component', () => {
path: 'user',
},
},
collectionConfig: {
collectionId: 'my_collection',
dataSource: {
type: DataVariableType,
path: 'my_data_source_id',
},
},
collectionDef: {
collectionId: 'my_collection',
dataSource: {
type: DataVariableType,
path: 'my_data_source_id',
},
},
})[0];
} as ComponentDataCollectionProps;
cmp = wrapper.components(cmpDef)[0] as unknown as ComponentDataCollection;
firstChild = cmp.components().at(0);
firstChild = cmp.components().at(0).components().at(0);
firstGrandchild = firstChild.components().at(0);
secondChild = cmp.components().at(1);
secondChild = cmp.components().at(1).components().at(0);
secondGrandchild = secondChild.components().at(0);
thirdChild = cmp.components().at(2);
thirdChild = cmp.components().at(2).components().at(0);
});
test('Evaluating to static value', () => {
@ -222,8 +202,8 @@ describe('Collection component', () => {
expect(cmp.components().length).toBe(2);
const updatedFirstChild = cmp.components().at(0);
const updatedSecondChild = cmp.components().at(1);
const updatedFirstChild = cmp.components().at(0).components().at(0);
const updatedSecondChild = cmp.components().at(1).components().at(0);
expect(updatedFirstChild.get('name')).toBe('user2');
expect(updatedSecondChild.get('name')).toBe('user3');
@ -240,18 +220,14 @@ describe('Collection component', () => {
test('Adding a record updates the collection component correctly', () => {
dataSource.addRecord({ id: 'user4', user: 'user4', firstName: 'Name4', age: '20' });
expect(cmp.components().length).toBe(4);
expect(cmp.getItemsCount()).toBe(4);
const newChild = cmp.components().at(3);
const newChild = cmp.components().at(3).components().at(0);
expect(newChild.get('name')).toBe('user4');
const newGrandchild = newChild.components().at(0);
expect(newGrandchild.get('name')).toBe('user4');
const firstChild = cmp.components().at(0);
const secondChild = cmp.components().at(1);
const thirdChild = cmp.components().at(2);
expect(firstChild.get('name')).toBe('user1');
expect(secondChild.get('name')).toBe('user2');
expect(thirdChild.get('name')).toBe('user3');
@ -353,10 +329,11 @@ describe('Collection component', () => {
let thirdChild!: Component;
beforeEach(() => {
cmp = wrapper.components({
const cmpDef = {
type: DataCollectionType,
collectionDef: {
componentDef: {
components: {
type: DataCollectionItemType,
components: {
type: 'default',
components: [
{
@ -380,21 +357,22 @@ describe('Collection component', () => {
},
},
},
collectionConfig: {
collectionId: 'my_collection',
dataSource: {
type: DataVariableType,
path: 'my_data_source_id',
},
},
collectionDef: {
collectionId: 'my_collection',
dataSource: {
type: DataVariableType,
path: 'my_data_source_id',
},
},
})[0];
} as ComponentDataCollectionProps;
cmp = wrapper.components(cmpDef)[0];
firstChild = cmp.components().at(0);
firstChild = cmp.components().at(0).components().at(0);
firstGrandchild = firstChild.components().at(0);
secondChild = cmp.components().at(1);
secondChild = cmp.components().at(1).components().at(0);
secondGrandchild = secondChild.components().at(0);
thirdChild = cmp.components().at(2);
thirdChild = cmp.components().at(2).components().at(0);
});
test('Evaluating to static value', () => {
@ -532,10 +510,11 @@ describe('Collection component', () => {
});
test('Traits', () => {
const cmp = wrapper.components({
const cmpDef = {
type: DataCollectionType,
collectionDef: {
componentDef: {
components: {
type: DataCollectionItemType,
components: {
type: 'default',
traits: [
{
@ -559,19 +538,20 @@ describe('Collection component', () => {
},
],
},
collectionConfig: {
collectionId: 'my_collection',
dataSource: {
type: DataVariableType,
path: 'my_data_source_id',
},
},
collectionDef: {
collectionId: 'my_collection',
dataSource: {
type: DataVariableType,
path: 'my_data_source_id',
},
},
})[0];
} as ComponentDataCollectionProps;
const cmp = wrapper.components(cmpDef)[0] as unknown as ComponentDataCollection;
expect(cmp.components()).toHaveLength(3);
const firstChild = cmp.components().at(0);
const secondChild = cmp.components().at(1);
expect(cmp.getItemsCount()).toBe(3);
const firstChild = cmp.components().at(0).components().at(0);
const secondChild = cmp.components().at(1).components().at(0);
expect(firstChild.getAttributes()['attribute_trait']).toBe('user1');
expect(firstChild.getEl()?.getAttribute('attribute_trait')).toBe('user1');
@ -593,10 +573,10 @@ describe('Collection component', () => {
});
describe('Serialization', () => {
let cmp: Component;
let cmp: ComponentDataCollection;
beforeEach(() => {
const cmpDefinition = {
const childCmpDef = {
type: 'default',
name: {
type: DataCollectionVariableType,
@ -641,32 +621,32 @@ describe('Collection component', () => {
],
};
const collectionComponentDefinition = {
const collectionCmpDef = {
type: DataCollectionType,
collectionDef: {
componentDef: {
...cmpDefinition,
components: [cmpDefinition, cmpDefinition],
components: [
{
...childCmpDef,
components: [childCmpDef, childCmpDef],
},
collectionConfig: {
collectionId: 'my_collection',
startIndex: 0,
endIndex: 1,
dataSource: {
type: DataVariableType,
path: 'my_data_source_id',
},
],
collectionDef: {
collectionId: 'my_collection',
startIndex: 0,
endIndex: 1,
dataSource: {
type: DataVariableType,
path: 'my_data_source_id',
},
},
};
} as ComponentDataCollectionProps;
cmp = wrapper.components(collectionComponentDefinition)[0];
cmp = wrapper.components(collectionCmpDef)[0] as unknown as ComponentDataCollection;
});
test('Serializion with Collection Variables to JSON', () => {
expect(cmp.toJSON()).toMatchSnapshot(`Collection with no grandchildren`);
const firstChild = cmp.components().at(0);
const firstItemCmp = cmp.getCollectionItemComponents().at(0);
const newChildDefinition = {
type: 'default',
name: {
@ -676,7 +656,7 @@ describe('Collection component', () => {
path: 'user',
},
};
firstChild.components().at(0).components(newChildDefinition);
firstItemCmp.components(newChildDefinition);
expect(cmp.toJSON()).toMatchSnapshot(`Collection with grandchildren`);
});
@ -684,11 +664,11 @@ describe('Collection component', () => {
const projectData = editor.getProjectData();
const page = projectData.pages[0];
const frame = page.frames[0];
const component = frame.component.components[0];
const component = frame.component.components[0] as ComponentDataCollection;
expect(component).toMatchSnapshot(`Collection with no grandchildren`);
const firstChild = cmp.components().at(0);
const firstItemCmp = cmp.getCollectionItemComponents().at(0);
const newChildDefinition = {
type: 'default',
name: {
@ -698,11 +678,135 @@ describe('Collection component', () => {
path: 'user',
},
};
firstChild.components().at(0).components(newChildDefinition);
firstItemCmp.components(newChildDefinition);
expect(cmp.toJSON()).toMatchSnapshot(`Collection with grandchildren`);
});
test('Loading', () => {
const cmpDef = {
type: DataCollectionType,
components: [
{
type: DataCollectionItemType,
components: {
attributes: {
attribute_trait: {
path: 'user',
type: DataCollectionVariableType,
variableType: DataCollectionStateVariableType.currentItem,
},
name: {
path: 'user',
type: DataCollectionVariableType,
collectionId: 'my_collection',
variableType: DataCollectionStateVariableType.currentItem,
},
},
components: [
{
attributes: {
attribute_trait: {
path: 'user',
type: DataCollectionVariableType,
collectionId: 'my_collection',
variableType: DataCollectionStateVariableType.currentItem,
},
name: {
path: 'user',
type: DataCollectionVariableType,
collectionId: 'my_collection',
variableType: DataCollectionStateVariableType.currentItem,
},
},
name: {
path: 'user',
type: DataCollectionVariableType,
collectionId: 'my_collection',
variableType: DataCollectionStateVariableType.currentItem,
},
custom_prop: {
path: 'user',
type: DataCollectionVariableType,
collectionId: 'my_collection',
variableType: 'currentIndex',
},
property_trait: {
path: 'user',
type: DataCollectionVariableType,
collectionId: 'my_collection',
variableType: DataCollectionStateVariableType.currentItem,
},
type: 'default',
},
{
attributes: {
attribute_trait: {
path: 'user',
type: DataCollectionVariableType,
collectionId: 'my_collection',
variableType: DataCollectionStateVariableType.currentItem,
},
name: {
path: 'user',
type: DataCollectionVariableType,
collectionId: 'my_collection',
variableType: DataCollectionStateVariableType.currentItem,
},
},
name: {
path: 'user',
type: DataCollectionVariableType,
collectionId: 'my_collection',
variableType: DataCollectionStateVariableType.currentItem,
},
custom_prop: {
path: 'user',
type: DataCollectionVariableType,
collectionId: 'my_collection',
variableType: 'currentIndex',
},
property_trait: {
path: 'user',
type: DataCollectionVariableType,
collectionId: 'my_collection',
variableType: DataCollectionStateVariableType.currentItem,
},
type: 'default',
},
],
name: {
path: 'user',
type: DataCollectionVariableType,
collectionId: 'my_collection',
variableType: DataCollectionStateVariableType.currentItem,
},
custom_prop: {
path: 'user',
type: DataCollectionVariableType,
collectionId: 'my_collection',
variableType: 'currentIndex',
},
property_trait: {
path: 'user',
type: DataCollectionVariableType,
collectionId: 'my_collection',
variableType: DataCollectionStateVariableType.currentItem,
},
type: 'default',
},
},
],
collectionDef: {
collectionId: 'my_collection',
dataSource: {
path: 'my_data_source_id',
type: DataVariableType,
},
endIndex: 1,
startIndex: 0,
},
} as ComponentDataCollectionProps;
const componentProjectData: ProjectData = {
assets: [],
pages: [
@ -710,128 +814,7 @@ describe('Collection component', () => {
frames: [
{
component: {
components: [
{
collectionDef: {
componentDef: {
attributes: {
attribute_trait: {
path: 'user',
type: DataCollectionVariableType,
variableType: DataCollectionStateVariableType.currentItem,
},
name: {
path: 'user',
type: DataCollectionVariableType,
collectionId: 'my_collection',
variableType: DataCollectionStateVariableType.currentItem,
},
},
components: [
{
attributes: {
attribute_trait: {
path: 'user',
type: DataCollectionVariableType,
collectionId: 'my_collection',
variableType: DataCollectionStateVariableType.currentItem,
},
name: {
path: 'user',
type: DataCollectionVariableType,
collectionId: 'my_collection',
variableType: DataCollectionStateVariableType.currentItem,
},
},
name: {
path: 'user',
type: DataCollectionVariableType,
collectionId: 'my_collection',
variableType: DataCollectionStateVariableType.currentItem,
},
custom_prop: {
path: 'user',
type: DataCollectionVariableType,
collectionId: 'my_collection',
variableType: 'currentIndex',
},
property_trait: {
path: 'user',
type: DataCollectionVariableType,
collectionId: 'my_collection',
variableType: DataCollectionStateVariableType.currentItem,
},
type: 'default',
},
{
attributes: {
attribute_trait: {
path: 'user',
type: DataCollectionVariableType,
collectionId: 'my_collection',
variableType: DataCollectionStateVariableType.currentItem,
},
name: {
path: 'user',
type: DataCollectionVariableType,
collectionId: 'my_collection',
variableType: DataCollectionStateVariableType.currentItem,
},
},
name: {
path: 'user',
type: DataCollectionVariableType,
collectionId: 'my_collection',
variableType: DataCollectionStateVariableType.currentItem,
},
custom_prop: {
path: 'user',
type: DataCollectionVariableType,
collectionId: 'my_collection',
variableType: 'currentIndex',
},
property_trait: {
path: 'user',
type: DataCollectionVariableType,
collectionId: 'my_collection',
variableType: DataCollectionStateVariableType.currentItem,
},
type: 'default',
},
],
name: {
path: 'user',
type: DataCollectionVariableType,
collectionId: 'my_collection',
variableType: DataCollectionStateVariableType.currentItem,
},
custom_prop: {
path: 'user',
type: DataCollectionVariableType,
collectionId: 'my_collection',
variableType: 'currentIndex',
},
property_trait: {
path: 'user',
type: DataCollectionVariableType,
collectionId: 'my_collection',
variableType: DataCollectionStateVariableType.currentItem,
},
type: 'default',
},
collectionConfig: {
collectionId: 'my_collection',
dataSource: {
path: 'my_data_source_id',
type: DataVariableType,
},
endIndex: 1,
startIndex: 0,
},
},
type: DataCollectionType,
},
],
components: [cmpDef],
docEl: {
tagName: 'html',
},
@ -863,10 +846,10 @@ describe('Collection component', () => {
editor.loadProjectData(componentProjectData);
const components = editor.getComponents();
const component = components.models[0];
const firstChild = component.components().at(0);
const component = components.models[0] as ComponentDataCollection;
const firstChild = component.components().at(0).components().at(0);
const firstGrandchild = firstChild.components().at(0);
const secondChild = component.components().at(1);
const secondChild = component.components().at(1).components().at(0);
const secondGrandchild = secondChild.components().at(0);
expect(firstChild.get('name')).toBe('user1');
@ -894,10 +877,11 @@ describe('Collection component', () => {
describe('Configuration options', () => {
test('Collection with start and end indexes', () => {
const cmp = wrapper.components({
const cmpDef = {
type: DataCollectionType,
collectionDef: {
componentDef: {
components: {
type: DataCollectionItemType,
components: {
type: 'default',
name: {
type: DataCollectionVariableType,
@ -906,21 +890,22 @@ describe('Collection component', () => {
path: 'user',
},
},
collectionConfig: {
startIndex: 1,
endIndex: 2,
collectionId: 'my_collection',
dataSource: {
type: DataVariableType,
path: 'my_data_source_id',
},
},
collectionDef: {
startIndex: 1,
endIndex: 2,
collectionId: 'my_collection',
dataSource: {
type: DataVariableType,
path: 'my_data_source_id',
},
},
})[0];
} as ComponentDataCollectionProps;
const cmp = wrapper.components(cmpDef)[0] as unknown as ComponentDataCollection;
expect(cmp.components()).toHaveLength(2);
const firstChild = cmp.components().at(0);
const secondChild = cmp.components().at(1);
const firstChild = cmp.components().at(0).components().at(0);
const secondChild = cmp.components().at(1).components().at(0);
expect(firstChild.get('name')).toBe('user2');
expect(secondChild.get('name')).toBe('user3');
@ -942,10 +927,11 @@ describe('Collection component', () => {
stateVariableTests.forEach(({ variableType, expectedValues }) => {
test(`Variable type: ${variableType}`, () => {
const cmp = wrapper.components({
const cmpDef = {
type: DataCollectionType,
collectionDef: {
componentDef: {
components: {
type: DataCollectionItemType,
components: {
type: 'default',
name: {
type: DataCollectionVariableType,
@ -979,24 +965,26 @@ describe('Collection component', () => {
},
],
},
collectionConfig: {
collectionId: 'my_collection',
dataSource: {
type: DataVariableType,
path: 'my_data_source_id',
},
},
collectionDef: {
collectionId: 'my_collection',
dataSource: {
type: DataVariableType,
path: 'my_data_source_id',
},
},
})[0];
} as ComponentDataCollectionProps;
const cmp = wrapper.components(cmpDef)[0] as unknown as ComponentDataCollection;
const children = cmp.components();
expect(children).toHaveLength(3);
expect(cmp.getItemsCount()).toBe(3);
const children = cmp.components();
children.each((child, index) => {
expect(child.get('name')).toBe(expectedValues[index]);
expect(child.get('property_trait')).toBe(expectedValues[index]);
expect(child.getAttributes()['custom_attribute']).toBe(expectedValues[index]);
expect(child.getAttributes()['attribute_trait']).toBe(expectedValues[index]);
const content = child.components().at(0);
expect(content.get('name')).toBe(expectedValues[index]);
expect(content.get('property_trait')).toBe(expectedValues[index]);
expect(content.getAttributes()['custom_attribute']).toBe(expectedValues[index]);
expect(content.getAttributes()['attribute_trait']).toBe(expectedValues[index]);
});
});
});

165
packages/core/test/specs/data_sources/model/data_collection/ComponentDataCollectionVariable.ts

@ -4,7 +4,10 @@ import {
DataCollectionType,
DataCollectionVariableType,
} from '../../../../../src/data_sources/model/data_collection/constants';
import { DataCollectionStateVariableType } from '../../../../../src/data_sources/model/data_collection/types';
import {
ComponentDataCollectionProps,
DataCollectionStateVariableType,
} from '../../../../../src/data_sources/model/data_collection/types';
import EditorModel from '../../../../../src/editor/model/Editor';
import { ProjectData } from '../../../../../src/storage_manager';
import { setupTestEditor } from '../../../../common';
@ -39,29 +42,28 @@ describe('Collection variable components', () => {
});
test('Gets the correct static value', async () => {
const cmp = wrapper.components({
const cmpDef = {
type: DataCollectionType,
collectionDef: {
componentDef: {
type: 'default',
components: [
{
type: DataCollectionVariableType,
variableType: DataCollectionStateVariableType.currentItem,
collectionId: 'my_collection',
path: 'user',
},
],
},
collectionConfig: {
collectionId: 'my_collection',
dataSource: {
type: DataVariableType,
path: 'my_data_source_id',
components: {
type: 'default',
components: [
{
type: DataCollectionVariableType,
variableType: DataCollectionStateVariableType.currentItem,
collectionId: 'my_collection',
path: 'user',
},
],
},
collectionDef: {
collectionId: 'my_collection',
dataSource: {
type: DataVariableType,
path: 'my_data_source_id',
},
},
})[0];
} as ComponentDataCollectionProps;
const cmp = wrapper.components(cmpDef)[0];
const firstGrandchild = cmp.components().at(0).components().at(0);
expect(firstGrandchild.getInnerHTML()).toContain('user1');
@ -73,27 +75,26 @@ describe('Collection variable components', () => {
});
test('Watches collection variable changes', async () => {
const cmp = wrapper.components({
const cmpDef = {
type: DataCollectionType,
collectionDef: {
componentDef: {
type: 'default',
components: {
type: DataCollectionVariableType,
variableType: DataCollectionStateVariableType.currentItem,
collectionId: 'my_collection',
path: 'user',
},
},
collectionConfig: {
components: {
type: 'default',
components: {
type: DataCollectionVariableType,
variableType: DataCollectionStateVariableType.currentItem,
collectionId: 'my_collection',
dataSource: {
type: DataVariableType,
path: 'my_data_source_id',
},
path: 'user',
},
},
collectionDef: {
collectionId: 'my_collection',
dataSource: {
type: DataVariableType,
path: 'my_data_source_id',
},
},
})[0];
} as ComponentDataCollectionProps;
const cmp = wrapper.components(cmpDef)[0];
firstRecord.set('user', 'new_correct_value');
const firstGrandchild = cmp.components().at(0).components().at(0);
@ -116,31 +117,29 @@ describe('Collection variable components', () => {
path: 'user',
};
const collectionComponentDefinition = {
const collectionCmpDef = {
type: DataCollectionType,
collectionDef: {
componentDef: {
type: 'default',
components: [
{
type: 'default',
},
variableCmpDef,
],
},
collectionConfig: {
collectionId: 'my_collection',
startIndex: 0,
endIndex: 2,
dataSource: {
type: DataVariableType,
path: 'my_data_source_id',
components: {
type: 'default',
components: [
{
type: 'default',
},
variableCmpDef,
],
},
collectionDef: {
collectionId: 'my_collection',
startIndex: 0,
endIndex: 2,
dataSource: {
type: DataVariableType,
path: 'my_data_source_id',
},
},
};
} as ComponentDataCollectionProps;
cmp = wrapper.components(collectionComponentDefinition)[0];
cmp = wrapper.components(collectionCmpDef)[0];
});
test('Serializion to JSON', () => {
@ -178,6 +177,30 @@ describe('Collection variable components', () => {
});
test('Loading', () => {
const cmpDef = {
components: {
components: [
{
type: DataCollectionVariableType,
variableType: DataCollectionStateVariableType.currentItem,
collectionId: 'my_collection',
path: 'user',
},
],
type: 'default',
},
collectionDef: {
collectionId: 'my_collection',
dataSource: {
path: 'my_data_source_id',
type: DataVariableType,
},
endIndex: 1,
startIndex: 0,
},
type: DataCollectionType,
} as ComponentDataCollectionProps;
const componentProjectData: ProjectData = {
assets: [],
pages: [
@ -185,33 +208,7 @@ describe('Collection variable components', () => {
frames: [
{
component: {
components: [
{
collectionDef: {
componentDef: {
type: 'default',
components: [
{
type: DataCollectionVariableType,
variableType: DataCollectionStateVariableType.currentItem,
collectionId: 'my_collection',
path: 'user',
},
],
},
collectionConfig: {
collectionId: 'my_collection',
dataSource: {
path: 'my_data_source_id',
type: DataVariableType,
},
endIndex: 1,
startIndex: 0,
},
},
type: DataCollectionType,
},
],
components: [cmpDef],
docEl: {
tagName: 'html',
},

80
packages/core/test/specs/data_sources/model/data_collection/__snapshots__/ComponentDataCollection.ts.snap

@ -3,16 +3,16 @@
exports[`Collection component Serialization Saving: Collection with grandchildren 1`] = `
{
"collectionDef": {
"collectionConfig": {
"collectionId": "my_collection",
"dataSource": {
"path": "my_data_source_id",
"type": "data-variable",
},
"endIndex": 1,
"startIndex": 0,
"collectionId": "my_collection",
"dataSource": {
"path": "my_data_source_id",
"type": "data-variable",
},
"componentDef": {
"endIndex": 1,
"startIndex": 0,
},
"components": [
{
"attributes": {
"attribute_trait": {
"collectionId": "my_collection",
@ -130,7 +130,7 @@ exports[`Collection component Serialization Saving: Collection with grandchildre
},
"type": "default",
},
},
],
"type": "data-collection",
}
`;
@ -138,16 +138,16 @@ exports[`Collection component Serialization Saving: Collection with grandchildre
exports[`Collection component Serialization Saving: Collection with no grandchildren 1`] = `
{
"collectionDef": {
"collectionConfig": {
"collectionId": "my_collection",
"dataSource": {
"path": "my_data_source_id",
"type": "data-variable",
},
"endIndex": 1,
"startIndex": 0,
"collectionId": "my_collection",
"dataSource": {
"path": "my_data_source_id",
"type": "data-variable",
},
"componentDef": {
"endIndex": 1,
"startIndex": 0,
},
"components": [
{
"attributes": {
"attribute_trait": {
"collectionId": "my_collection",
@ -254,7 +254,7 @@ exports[`Collection component Serialization Saving: Collection with no grandchil
},
"type": "default",
},
},
],
"type": "data-collection",
}
`;
@ -262,16 +262,16 @@ exports[`Collection component Serialization Saving: Collection with no grandchil
exports[`Collection component Serialization Serializion with Collection Variables to JSON: Collection with grandchildren 1`] = `
{
"collectionDef": {
"collectionConfig": {
"collectionId": "my_collection",
"dataSource": {
"path": "my_data_source_id",
"type": "data-variable",
},
"endIndex": 1,
"startIndex": 0,
"collectionId": "my_collection",
"dataSource": {
"path": "my_data_source_id",
"type": "data-variable",
},
"componentDef": {
"endIndex": 1,
"startIndex": 0,
},
"components": [
{
"attributes": {
"attribute_trait": {
"collectionId": "my_collection",
@ -389,7 +389,7 @@ exports[`Collection component Serialization Serializion with Collection Variable
},
"type": "default",
},
},
],
"type": "data-collection",
}
`;
@ -397,16 +397,16 @@ exports[`Collection component Serialization Serializion with Collection Variable
exports[`Collection component Serialization Serializion with Collection Variables to JSON: Collection with no grandchildren 1`] = `
{
"collectionDef": {
"collectionConfig": {
"collectionId": "my_collection",
"dataSource": {
"path": "my_data_source_id",
"type": "data-variable",
},
"endIndex": 1,
"startIndex": 0,
"collectionId": "my_collection",
"dataSource": {
"path": "my_data_source_id",
"type": "data-variable",
},
"componentDef": {
"endIndex": 1,
"startIndex": 0,
},
"components": [
{
"attributes": {
"attribute_trait": {
"collectionId": "my_collection",
@ -513,7 +513,7 @@ exports[`Collection component Serialization Serializion with Collection Variable
},
"type": "default",
},
},
],
"type": "data-collection",
}
`;

80
packages/core/test/specs/data_sources/model/data_collection/__snapshots__/ComponentDataCollectionVariable.ts.snap

@ -3,16 +3,16 @@
exports[`Collection variable components Serialization Saving: Collection with collection variable component ( no grandchildren ) 1`] = `
{
"collectionDef": {
"collectionConfig": {
"collectionId": "my_collection",
"dataSource": {
"path": "my_data_source_id",
"type": "data-variable",
},
"endIndex": 2,
"startIndex": 0,
"collectionId": "my_collection",
"dataSource": {
"path": "my_data_source_id",
"type": "data-variable",
},
"componentDef": {
"endIndex": 2,
"startIndex": 0,
},
"components": [
{
"components": [
{
"type": "default",
@ -26,7 +26,7 @@ exports[`Collection variable components Serialization Saving: Collection with co
],
"type": "default",
},
},
],
"type": "data-collection",
}
`;
@ -34,16 +34,16 @@ exports[`Collection variable components Serialization Saving: Collection with co
exports[`Collection variable components Serialization Saving: Collection with collection variable component ( with grandchildren ) 1`] = `
{
"collectionDef": {
"collectionConfig": {
"collectionId": "my_collection",
"dataSource": {
"path": "my_data_source_id",
"type": "data-variable",
},
"endIndex": 2,
"startIndex": 0,
"collectionId": "my_collection",
"dataSource": {
"path": "my_data_source_id",
"type": "data-variable",
},
"componentDef": {
"endIndex": 2,
"startIndex": 0,
},
"components": [
{
"components": [
{
"components": [
@ -65,7 +65,7 @@ exports[`Collection variable components Serialization Saving: Collection with co
],
"type": "default",
},
},
],
"type": "data-collection",
}
`;
@ -73,16 +73,16 @@ exports[`Collection variable components Serialization Saving: Collection with co
exports[`Collection variable components Serialization Serializion to JSON: Collection with collection variable component ( no grandchildren ) 1`] = `
{
"collectionDef": {
"collectionConfig": {
"collectionId": "my_collection",
"dataSource": {
"path": "my_data_source_id",
"type": "data-variable",
},
"endIndex": 2,
"startIndex": 0,
"collectionId": "my_collection",
"dataSource": {
"path": "my_data_source_id",
"type": "data-variable",
},
"componentDef": {
"endIndex": 2,
"startIndex": 0,
},
"components": [
{
"components": [
{
"type": "default",
@ -96,7 +96,7 @@ exports[`Collection variable components Serialization Serializion to JSON: Colle
],
"type": "default",
},
},
],
"type": "data-collection",
}
`;
@ -104,16 +104,16 @@ exports[`Collection variable components Serialization Serializion to JSON: Colle
exports[`Collection variable components Serialization Serializion to JSON: Collection with collection variable component ( with grandchildren ) 1`] = `
{
"collectionDef": {
"collectionConfig": {
"collectionId": "my_collection",
"dataSource": {
"path": "my_data_source_id",
"type": "data-variable",
},
"endIndex": 2,
"startIndex": 0,
"collectionId": "my_collection",
"dataSource": {
"path": "my_data_source_id",
"type": "data-variable",
},
"componentDef": {
"endIndex": 2,
"startIndex": 0,
},
"components": [
{
"components": [
{
"components": [
@ -135,7 +135,7 @@ exports[`Collection variable components Serialization Serializion to JSON: Colle
],
"type": "default",
},
},
],
"type": "data-collection",
}
`;

59
packages/core/test/specs/data_sources/model/data_collection/__snapshots__/nestedComponentDataCollections.ts.snap

@ -3,34 +3,45 @@
exports[`Collection component Nested collections are correctly serialized 1`] = `
{
"collectionDef": {
"collectionConfig": {
"collectionId": "parent_collection",
"dataSource": {
"path": "my_data_source_id",
"type": "data-variable",
},
"collectionId": "parent_collection",
"dataSource": {
"path": "my_data_source_id",
"type": "data-variable",
},
"componentDef": {
"collectionDef": {
"collectionConfig": {
"collectionId": "nested_collection",
"dataSource": {
"path": "nested_data_source_id",
"type": "data-variable",
},
},
"componentDef": {
"name": {
"path": "user",
"type": "data-collection-variable",
"variableType": "currentItem",
},
"components": [
{
"components": [
{
"collectionDef": {
"collectionId": "nested_collection",
"dataSource": {
"path": "nested_data_source_id",
"type": "data-variable",
},
},
"type": "default",
"components": [
{
"components": [
{
"name": {
"collectionId": "nested_collection",
"path": "user",
"type": "data-collection-variable",
"variableType": "currentItem",
},
"type": "default",
},
],
"type": "data-collection-item",
},
],
"type": "data-collection",
},
},
"type": "data-collection",
],
"type": "data-collection-item",
},
},
],
"type": "data-collection",
}
`;

450
packages/core/test/specs/data_sources/model/data_collection/nestedComponentDataCollections.ts

@ -1,27 +1,47 @@
import { Component, DataRecord, DataSource, DataSourceManager, Editor } from '../../../../../src';
import { DataVariableType } from '../../../../../src/data_sources/model/DataVariable';
import ComponentDataCollection from '../../../../../src/data_sources/model/data_collection/ComponentDataCollection';
import {
DataCollectionItemType,
DataCollectionType,
DataCollectionVariableType,
} from '../../../../../src/data_sources/model/data_collection/constants';
import { DataCollectionStateVariableType } from '../../../../../src/data_sources/model/data_collection/types';
import {
ComponentDataCollectionProps,
DataCollectionStateVariableType,
} from '../../../../../src/data_sources/model/data_collection/types';
import EditorModel from '../../../../../src/editor/model/Editor';
import { setupTestEditor } from '../../../../common';
describe('Collection component', () => {
let em: EditorModel;
let editor: Editor;
let dsm: DataSourceManager;
let dataSource: DataSource;
let nestedDataSource: DataSource;
let wrapper: Component;
let firstRecord: DataRecord;
let secondRecord: DataRecord;
let firstNestedRecord: DataRecord;
let secondNestedRecord: DataRecord;
let cmpDef: ComponentDataCollectionProps | undefined;
let nestedCmpDef: ComponentDataCollectionProps | undefined;
let parentCmp: ComponentDataCollection;
let nestedCmp: ComponentDataCollection;
function getCmpDef(nestedCmpDef: ComponentDataCollectionProps): ComponentDataCollectionProps {
return {
type: DataCollectionType,
components: { type: DataCollectionItemType, components: nestedCmpDef },
collectionDef: {
collectionId: 'parent_collection',
dataSource: {
type: DataVariableType,
path: 'my_data_source_id',
},
},
};
}
beforeEach(() => {
({ em, editor, dsm } = setupTestEditor());
({ em, dsm } = setupTestEditor());
wrapper = em.getWrapper()!;
dataSource = dsm.add({
id: 'my_data_source_id',
@ -41,96 +61,54 @@ describe('Collection component', () => {
});
firstRecord = dataSource.getRecord('user1')!;
secondRecord = dataSource.getRecord('user2')!;
firstNestedRecord = nestedDataSource.getRecord('nested_user1')!;
secondNestedRecord = nestedDataSource.getRecord('nested_user2')!;
});
afterEach(() => {
em.destroy();
});
test('Nested collections bind to correct data sources', () => {
const parentCollection = wrapper.components({
nestedCmpDef = {
type: DataCollectionType,
collectionDef: {
componentDef: {
type: DataCollectionType,
collectionDef: {
componentDef: {
type: 'default',
name: {
type: DataCollectionVariableType,
variableType: DataCollectionStateVariableType.currentItem,
collectionId: 'nested_collection',
path: 'user',
},
},
collectionConfig: {
collectionId: 'nested_collection',
dataSource: {
type: DataVariableType,
path: 'nested_data_source_id',
},
},
components: {
type: DataCollectionItemType,
components: {
type: 'default',
name: {
type: DataCollectionVariableType,
variableType: DataCollectionStateVariableType.currentItem,
collectionId: 'nested_collection',
path: 'user',
},
},
collectionConfig: {
collectionId: 'parent_collection',
dataSource: {
type: DataVariableType,
path: 'my_data_source_id',
},
},
collectionDef: {
collectionId: 'nested_collection',
dataSource: {
type: DataVariableType,
path: 'nested_data_source_id',
},
},
})[0];
};
cmpDef = getCmpDef(nestedCmpDef);
parentCmp = wrapper.components(cmpDef)[0] as unknown as ComponentDataCollection;
nestedCmp = parentCmp.getCollectionItemComponents().at(0) as ComponentDataCollection;
});
afterEach(() => {
em.destroy();
nestedCmpDef = undefined;
cmpDef = undefined;
});
const nestedCollection = parentCollection.components().at(0);
const nestedFirstChild = nestedCollection.components().at(0);
const nestedSecondChild = nestedCollection.components().at(1);
test('Nested collections bind to correct data sources', () => {
const nestedFirstChild = nestedCmp.components().at(0).components().at(0);
const nestedSecondChild = nestedCmp.components().at(1).components().at(0);
expect(nestedFirstChild.get('name')).toBe('nested_user1');
expect(nestedSecondChild.get('name')).toBe('nested_user2');
});
test('Updates in parent collection propagate to nested collections', () => {
const parentCollection = wrapper.components({
type: DataCollectionType,
collectionDef: {
componentDef: {
type: DataCollectionType,
collectionDef: {
componentDef: {
type: 'default',
name: {
type: DataCollectionVariableType,
variableType: DataCollectionStateVariableType.currentItem,
collectionId: 'nested_collection',
path: 'user',
},
},
collectionConfig: {
collectionId: 'nested_collection',
dataSource: {
type: DataVariableType,
path: 'nested_data_source_id',
},
},
},
},
collectionConfig: {
collectionId: 'parent_collection',
dataSource: {
type: DataVariableType,
path: 'my_data_source_id',
},
},
},
})[0];
const nestedCollection = parentCollection.components().at(0);
const nestedFirstChild = nestedCollection.components().at(0);
const nestedSecondChild = nestedCollection.components().at(1);
const nestedFirstChild = nestedCmp.components().at(0).components().at(0);
const nestedSecondChild = nestedCmp.components().at(1).components().at(0);
firstNestedRecord.set('user', 'updated_user1');
expect(nestedFirstChild.get('name')).toBe('updated_user1');
@ -138,169 +116,79 @@ describe('Collection component', () => {
});
test('Nested collections are correctly serialized', () => {
const parentCollection = wrapper.components({
type: DataCollectionType,
collectionDef: {
componentDef: {
type: DataCollectionType,
collectionDef: {
componentDef: {
type: 'default',
name: {
type: DataCollectionVariableType,
variableType: DataCollectionStateVariableType.currentItem,
path: 'user',
},
},
collectionConfig: {
collectionId: 'nested_collection',
dataSource: {
type: DataVariableType,
path: 'nested_data_source_id',
},
},
},
},
collectionConfig: {
collectionId: 'parent_collection',
dataSource: {
type: DataVariableType,
path: 'my_data_source_id',
},
},
},
})[0];
const serialized = parentCollection.toJSON();
const serialized = parentCmp.toJSON();
expect(serialized).toMatchSnapshot();
});
test('Nested collections respect startIndex and endIndex', () => {
const parentCollection = wrapper.components({
nestedCmpDef = {
type: DataCollectionType,
collectionDef: {
componentDef: {
type: DataCollectionType,
collectionDef: {
componentDef: {
type: 'default',
name: {
type: DataCollectionVariableType,
variableType: DataCollectionStateVariableType.currentItem,
collectionId: 'nested_collection',
path: 'user',
},
},
collectionConfig: {
collectionId: 'nested_collection',
startIndex: 0,
endIndex: 1,
dataSource: {
type: DataVariableType,
path: 'nested_data_source_id',
},
},
components: {
type: DataCollectionItemType,
components: {
type: 'default',
name: {
type: DataCollectionVariableType,
variableType: DataCollectionStateVariableType.currentItem,
collectionId: 'nested_collection',
path: 'user',
},
},
collectionConfig: {
collectionId: 'parent_collection',
dataSource: {
type: DataVariableType,
path: 'my_data_source_id',
},
},
collectionDef: {
collectionId: 'nested_collection',
startIndex: 0,
endIndex: 1,
dataSource: {
type: DataVariableType,
path: 'nested_data_source_id',
},
},
})[0];
};
const nestedCollection = parentCollection.components().at(0);
expect(nestedCollection.components().length).toBe(2);
const updatedParentCmp = wrapper.components(getCmpDef(nestedCmpDef))[0] as unknown as ComponentDataCollection;
const updatedNestedCmp = updatedParentCmp.getCollectionItemComponents().at(0) as ComponentDataCollection;
expect(updatedNestedCmp.getItemsCount()).toBe(2);
});
test('Nested collection gets and watches value from the parent collection', () => {
const parentCollection = wrapper.components({
nestedCmpDef = {
type: DataCollectionType,
collectionDef: {
componentDef: {
type: DataCollectionType,
collectionDef: {
componentDef: {
type: 'default',
name: {
type: DataCollectionVariableType,
variableType: DataCollectionStateVariableType.currentItem,
collectionId: 'parent_collection',
path: 'user',
},
},
collectionConfig: {
collectionId: 'nested_collection',
dataSource: {
type: DataVariableType,
path: 'nested_data_source_id',
},
},
components: {
type: DataCollectionItemType,
components: {
type: 'default',
name: {
type: DataCollectionVariableType,
variableType: DataCollectionStateVariableType.currentItem,
collectionId: 'parent_collection',
path: 'user',
},
},
collectionConfig: {
collectionId: 'parent_collection',
dataSource: {
type: DataVariableType,
path: 'my_data_source_id',
},
},
collectionDef: {
collectionId: 'nested_collection',
startIndex: 0,
endIndex: 1,
dataSource: {
type: DataVariableType,
path: 'nested_data_source_id',
},
},
})[0];
};
const nestedCollection = parentCollection.components().at(0);
const firstNestedChild = nestedCollection.components().at(0);
const updatedParentCmp = wrapper.components(getCmpDef(nestedCmpDef))[0] as unknown as ComponentDataCollection;
const updatedNestedCmp = updatedParentCmp.getCollectionItemComponents().at(0) as ComponentDataCollection;
const firstNestedChild = updatedNestedCmp.getCollectionItemComponents().at(0);
// Verify initial value
expect(firstNestedChild.get('name')).toBe('user1');
// Update value in parent collection and verify nested collection updates
firstRecord.set('user', 'updated_user1');
expect(firstNestedChild.get('name')).toBe('updated_user1');
});
test('Nested collection switches to using its own collection variable', () => {
const parentCollection = wrapper.components({
type: DataCollectionType,
collectionDef: {
componentDef: {
type: DataCollectionType,
collectionDef: {
componentDef: {
type: 'default',
name: {
type: DataCollectionVariableType,
variableType: DataCollectionStateVariableType.currentItem,
path: 'user',
collectionId: 'parent_collection',
},
},
collectionConfig: {
collectionId: 'nested_collection',
dataSource: {
type: DataVariableType,
path: 'nested_data_source_id',
},
},
},
},
collectionConfig: {
collectionId: 'parent_collection',
dataSource: {
type: DataVariableType,
path: 'my_data_source_id',
},
},
},
})[0];
const firstChild = nestedCmp.components().at(0).components().at(0);
const nestedCollection = parentCollection.components().at(0);
const firstChild = nestedCollection.components().at(0);
// Replace the collection variable with one from the inner collection
firstChild.set('name', {
// @ts-ignore
type: DataCollectionVariableType,
@ -313,118 +201,78 @@ describe('Collection component', () => {
});
describe('Nested Collection Component with Parent and Nested Data Sources', () => {
let parentCollection: Component;
let nestedCollection: Component;
beforeEach(() => {
// Initialize the parent and nested collections
parentCollection = wrapper.components({
nestedCmpDef = {
type: DataCollectionType,
collectionDef: {
componentDef: {
type: DataCollectionType,
name: {
type: DataCollectionVariableType,
variableType: DataCollectionStateVariableType.currentItem,
collectionId: 'parent_collection',
path: 'user',
},
components: {
type: DataCollectionItemType,
components: {
type: 'default',
name: {
type: DataCollectionVariableType,
variableType: DataCollectionStateVariableType.currentItem,
collectionId: 'parent_collection',
collectionId: 'nested_collection',
path: 'user',
},
collectionDef: {
componentDef: {
type: 'default',
name: {
type: DataCollectionVariableType,
variableType: DataCollectionStateVariableType.currentItem,
collectionId: 'nested_collection',
path: 'user',
},
},
collectionConfig: {
collectionId: 'nested_collection',
dataSource: {
type: DataVariableType,
path: 'nested_data_source_id',
},
},
},
},
collectionConfig: {
collectionId: 'parent_collection',
dataSource: {
type: DataVariableType,
path: 'my_data_source_id',
},
},
collectionDef: {
collectionId: 'nested_collection',
dataSource: {
type: DataVariableType,
path: 'nested_data_source_id',
},
},
})[0];
};
nestedCollection = parentCollection.components().at(0);
parentCmp = wrapper.components(getCmpDef(nestedCmpDef))[0] as unknown as ComponentDataCollection;
nestedCmp = parentCmp.getCollectionItemComponents().at(0) as ComponentDataCollection;
});
test('Removing a record from the parent data source updates the parent collection correctly', () => {
// Verify initial state
expect(parentCollection.components().length).toBe(2); // 2 parent records initially
// Remove a record from the parent data source
expect(parentCmp.getItemsCount()).toBe(2);
dataSource.removeRecord('user1');
// Verify that the parent collection updates correctly
expect(parentCollection.components().length).toBe(1); // Only 1 parent record remains
expect(parentCollection.components().at(0).get('name')).toBe('user2'); // Verify updated name
// Verify that the nested collection is unaffected
expect(nestedCollection.components().length).toBe(3); // Nested records remain the same
expect(nestedCollection.components().at(0).get('name')).toBe('nested_user1'); // Verify nested name
expect(parentCmp.getItemsCount()).toBe(1);
expect(parentCmp.components().at(0).components().at(0).get('name')).toBe('user2');
expect(nestedCmp.getItemsCount()).toBe(3);
expect(nestedCmp.components().at(0).components().at(0).get('name')).toBe('nested_user1');
});
test('Adding a record to the parent data source updates the parent collection correctly', () => {
// Verify initial state
expect(parentCollection.components().length).toBe(2); // 2 parent records initially
// Add a new record to the parent data source
expect(parentCmp.getItemsCount()).toBe(2);
dataSource.addRecord({ id: 'user3', user: 'user3', age: '16' });
// Verify that the parent collection updates correctly
expect(parentCollection.components().length).toBe(3); // 3 parent records now
expect(parentCollection.components().at(2).get('name')).toBe('user3'); // Verify new name
// Verify that the nested collection is unaffected
expect(nestedCollection.components().length).toBe(3); // Nested records remain the same
expect(nestedCollection.components().at(0).get('name')).toBe('nested_user1'); // Verify nested name
expect(parentCollection.components().at(2).components().at(0).get('name')).toBe('nested_user1'); // Verify nested name
expect(parentCmp.getItemsCount()).toBe(3);
expect(parentCmp.components().at(2).components().at(0).get('name')).toBe('user3');
expect(nestedCmp.getItemsCount()).toBe(3);
expect(nestedCmp.components().at(0).components().at(0).get('name')).toBe('nested_user1');
});
test('Removing a record from the nested data source updates the nested collection correctly', () => {
// Verify initial state
expect(nestedCollection.components().length).toBe(3); // 3 nested records initially
// Remove a record from the nested data source
expect(nestedCmp.getItemsCount()).toBe(3);
nestedDataSource.removeRecord('nested_user1');
// Verify that the nested collection updates correctly
expect(nestedCollection.components().length).toBe(2); // Only 2 nested records remain
expect(nestedCollection.components().at(0).get('name')).toBe('nested_user2'); // Verify updated name
expect(nestedCollection.components().at(1).get('name')).toBe('nested_user3'); // Verify updated name
expect(nestedCmp.getItemsCount()).toBe(2);
expect(nestedCmp.components().at(0).components().at(0).get('name')).toBe('nested_user2');
expect(nestedCmp.components().at(1).components().at(0).get('name')).toBe('nested_user3');
});
test('Adding a record to the nested data source updates the nested collection correctly', () => {
// Verify initial state
expect(nestedCollection.components().length).toBe(3); // 3 nested records initially
expect(nestedCollection.components().at(0).get('name')).toBe('nested_user1'); // Verify initial name
expect(nestedCollection.components().at(1).get('name')).toBe('nested_user2'); // Verify initial name
expect(nestedCollection.components().at(2).get('name')).toBe('nested_user3'); // Verify initial name
expect(nestedCmp.getItemsCount()).toBe(3);
expect(nestedCmp.components().at(0).components().at(0).get('name')).toBe('nested_user1');
expect(nestedCmp.components().at(1).components().at(0).get('name')).toBe('nested_user2');
expect(nestedCmp.components().at(2).components().at(0).get('name')).toBe('nested_user3');
// Add a new record to the nested data source
nestedDataSource.addRecord({ id: 'user4', user: 'nested_user4', age: '18' });
// Verify that the nested collection updates correctly
expect(nestedCollection.components().length).toBe(4); // 4 nested records now
expect(nestedCollection.components().at(3).get('name')).toBe('nested_user4'); // Verify new name
// Verify existing records are unaffected
expect(nestedCollection.components().at(0).get('name')).toBe('nested_user1'); // Verify existing name
expect(nestedCollection.components().at(1).get('name')).toBe('nested_user2'); // Verify existing name
expect(nestedCollection.components().at(2).get('name')).toBe('nested_user3'); // Verify existing name
expect(nestedCmp.getItemsCount()).toBe(4);
expect(nestedCmp.components().at(3).components().at(0).get('name')).toBe('nested_user4');
expect(nestedCmp.components().at(0).components().at(0).get('name')).toBe('nested_user1');
expect(nestedCmp.components().at(1).components().at(0).get('name')).toBe('nested_user2');
expect(nestedCmp.components().at(2).components().at(0).get('name')).toBe('nested_user3');
});
});
});

Loading…
Cancel
Save