import {Testables} from "./testables"; // @ts-ignore import {ComponentDataType, TestableCompositeData, TestableData} from "./store"; import {NestedTestableNodeData} from "./InMemoryTestableNode"; import {cloneDeep} from "lodash"; import {ExportImportTarget} from "./export/Exporters"; import {removeIds} from "./NodeHelpers"; import {ComponentsMeta} from "./ComponentsMeta"; import {exporterImporters} from "./export/CustomImportersAndExporters"; import { currentPreloadedDynamicData, PreloadedDynamicData, PreloadedDynamicValues } from "./export/ComponentExporterImporter"; import {isTestableCompositeGroupType} from "./testableGroupTypes"; export interface TestableInterface { readonly id: string; readonly isRoot: boolean; /** * Returns the original reference to the testable data in the state! * * Other code is relying on this reference so don't change it! */ getData(): Options extends false ? TestableCompositeData : TestableData; /** * A deep copy of the testable data is returned! */ getNestedData(): NestedTestableNodeData; isDefault(): boolean; getMode(): TestableCompositeData['mode']; isPartial(): any; } export class Testable implements TestableInterface { private testable?: Options extends false ? TestableCompositeData : TestableData constructor( private testables: Testables, readonly id: string, readonly isRoot: boolean = false, ) { } /** * Returns the original reference to the testable data in the state! * * Other code is relying on this reference so don't change it! */ getData(): typeof this.testable { if (!this.testable) { this.testable = this.testables.getNode(this.id); } return this.testable; } /** * A deep copy of the testable data is returned! */ getNestedData(): NestedTestableNodeData { let dataToReturn: NestedTestableNodeData if (this.isPartial()) { dataToReturn = this.testables.getNode(this.id)! } else { dataToReturn = this.testables.getNodeWithChildren(this.id)! } return cloneDeep(dataToReturn) } getChildren(): Testable[] { return this.testables.getChildren(this.id).map(child => new Testable(this.testables, child.id)); } isDefault(): boolean { // it is a composite group // and ony has one child const testable = this.getData(); return testable?.type !== 'context' && this.testables.getChildren(testable?.id || '').length === 1; } getMode(): TestableCompositeData['mode'] { // get me the parent testable composite and then get me the mode from that // IF THIS IS a partial though, just get the mode based on the testable type (eg: filter for filters, test for conditions) // check whether this is a partial const parent = this.getParentData(); if (this.isPartial()) { return (parent as TestableData).testableType === 'filter' ? 'filter' : 'test'; } if (parent) { return (parent as TestableCompositeData).mode; } // we have no parent if (this.isGroup()) { return (this.getData() as TestableCompositeData).mode } // this is a testable and we have no parent so the mode will have to be its type (if a filter, then 'filter' else 'test') return (this.getData() as TestableData).testableType === 'filter'? 'filter' : 'test'; } getType() : string { if (this.isPartial()) { return (this.getParent() as Testable).getType() } return (this.getData() as TestableData).type; } getTestableType(): ComponentDataType { return (this.getData() as TestableData).testableType; } getParent = () => { const parentData = this.getParentData(); if (!parentData) { return undefined; } return new Testable(this.testables, parentData.id); } private getParentData = () => { return this.testables.getParent(this.id); } isPartial() { return this.testables.isPartial(this.id); } exists() { return !!this.testables.getNode(this.id); } export(target: ExportImportTarget) { let nestedData = this.getNestedData(); if ('children' in nestedData) { nestedData.children = nestedData.children.map(testableData => { let testable: TestableInterface if (!testableData.id) { testable = new LocalTestable(testableData as TestableData); } else { testable = new Testable(this.testables, testableData.id); } return testable.export(target); }) } else { let type = (nestedData as TestableData).type if (!('type' in nestedData)) { // this is partial, need to add support for it, we need to type = (this.getParent() as Testable).getType() } const customExportedData = exporterImporters[type]?.exportData?.(cloneDeep(nestedData), target) /*const customExportedData = exporters[testableType]?.(nestedData, target)*/ if (customExportedData) { nestedData = customExportedData as typeof nestedData } if (target === 'server') { const preloadedDynamicValues = this.getDynamicValues() if (preloadedDynamicValues) { currentPreloadedDynamicData.addPreloadedDynamicData(preloadedDynamicValues) } } } if (!this.isGroup()) { if ('children' in nestedData) { delete (nestedData as any).children } } if (target === 'preset') { // @ts-ignore nestedData = removeIds(nestedData) } return nestedData } isGroup(): boolean { return isTestableCompositeGroupType(this.getData()?.type) } getDynamicValues(): PreloadedDynamicValues[] { const dynamicValues = new PreloadedDynamicData([]) if (this.isGroup()) { // this is a group so let's get the dynamic values from all children const children = this.getChildren(); children.forEach(child => { const dynamicChildValues = child.getDynamicValues(); if (dynamicChildValues.length) { dynamicValues.addPreloadedDynamicData(dynamicChildValues) } }) } else { // this is a testable so let's get the dynamic values for this testable if any const preloadedValues = exporterImporters[this.getType()]?.tagDynamicValues?.(this.getData(), 'server') if (preloadedValues) { dynamicValues.addPreloadedDynamicData(preloadedValues) } } return dynamicValues.items; } } export class LocalTestable implements TestableInterface { private localData: TestableData; constructor( localData: TestableData, ) { this.localData = cloneDeep(localData); } get id(): string { return this.localData?.id; } get isRoot(): boolean { return false; } getData() { return this.localData; } getMode(): TestableCompositeData["mode"] { return this.localData.testableType === 'filter' ? 'filter' : 'test'; } getNestedData(): NestedTestableNodeData { return (this.localData); } isDefault(): boolean { return false; } isPartial(): any { return false; } }