UNPKG

@itwin/core-backend

Version:
490 lines • 26.8 kB
/*--------------------------------------------------------------------------------------------- * Copyright (c) Bentley Systems, Incorporated. All rights reserved. * See LICENSE.md in the project root for license terms and full copyright notice. *--------------------------------------------------------------------------------------------*/ import { assert, expect } from "chai"; import { join } from "path"; import { restore as sinonRestore, spy as sinonSpy } from "sinon"; import { Guid, Id64 } from "@itwin/core-bentley"; import { CodeScopeSpec, CodeSpec, IModel } from "@itwin/core-common"; import { ClassRegistry } from "../../ClassRegistry"; import { ElementUniqueAspect } from "../../ElementAspect"; import { _nativeDb, ChannelControl, FunctionalBreakdownElement, FunctionalComponentElement, FunctionalModel, FunctionalPartition, FunctionalSchema, InformationPartitionElement, Schemas, StandaloneDb, } from "../../core-backend"; import { ElementOwnsChildElements, ElementOwnsUniqueAspect, SubjectOwnsPartitionElements } from "../../NavigationRelationship"; import { IModelTestUtils } from "../IModelTestUtils"; import { KnownTestLocations } from "../KnownTestLocations"; let iModelDb; const insertedLabel = "inserted label"; const updatedLabel = "updated label"; /** test schema for supplying element/model/aspect classes */ class TestSchema extends FunctionalSchema { static get schemaName() { return "TestFunctional"; } } /** partition element for testing `Element.onSubModelXxx` methods */ class TestFuncPartition extends InformationPartitionElement { static get className() { return "TestFuncPartition"; } static onSubModelInsert(arg) { super.onSubModelInsert(arg); assert.equal(arg.iModel, iModelDb); assert.equal(arg.subModelProps.classFullName, TestFuncModel.classFullName); } static onSubModelInserted(arg) { super.onSubModelInserted(arg); assert.equal(arg.iModel, iModelDb); } static onSubModelDelete(arg) { super.onSubModelDelete(arg); assert.equal(arg.iModel, iModelDb); } static onSubModelDeleted(arg) { super.onSubModelDeleted(arg); assert.equal(arg.iModel, iModelDb); } } /** for testing `Model.onXxx` methods */ class TestFuncModel extends FunctionalModel { static get className() { return "TestFuncModel"; } static dontDelete = ""; static onInsert(arg) { super.onInsert(arg); assert.equal(arg.iModel, iModelDb); assert.equal(arg.props.classFullName, this.classFullName); } static onInserted(arg) { super.onInserted(arg); assert.equal(arg.iModel, iModelDb); } static onUpdate(arg) { super.onUpdate(arg); assert.equal(arg.iModel, iModelDb); } static onUpdated(arg) { super.onUpdated(arg); assert.equal(arg.iModel, iModelDb); } static onDelete(arg) { super.onDelete(arg); assert.equal(arg.iModel, iModelDb); } static onDeleted(arg) { super.onDeleted(arg); assert.equal(arg.iModel, iModelDb); } static onInsertElement(arg) { super.onInsertElement(arg); assert.equal(arg.iModel, iModelDb); if (arg.elementProps.code.value === "badval") throw new Error("bad element"); } static onInsertedElement(arg) { super.onInsertedElement(arg); assert.equal(arg.iModel, iModelDb); } static onUpdateElement(arg) { super.onUpdateElement(arg); assert.equal(arg.iModel, iModelDb); } static onUpdatedElement(arg) { super.onUpdatedElement(arg); assert.equal(arg.iModel, iModelDb); } static onDeleteElement(arg) { super.onDeleteElement(arg); assert.equal(arg.iModel, iModelDb); if (arg.elementId === this.dontDelete) throw new Error("dont delete my element"); } static onDeletedElement(arg) { super.onDeletedElement(arg); assert.equal(arg.iModel, iModelDb); } } /** for testing `Element.onXxx` methods */ class Breakdown extends FunctionalBreakdownElement { static get className() { return "Breakdown"; } static dontDeleteChild = ""; static onInsert(arg) { arg.props.userLabel = insertedLabel; super.onInsert(arg); assert.equal(arg.iModel, iModelDb); assert.equal(arg.props.classFullName, this.classFullName); } static onInserted(arg) { super.onInserted(arg); assert.equal(arg.iModel, iModelDb); } static onUpdate(arg) { arg.props.userLabel = updatedLabel; super.onUpdate(arg); assert.equal(arg.iModel, iModelDb); assert.equal(arg.props.classFullName, this.classFullName); } static onUpdated(arg) { super.onUpdated(arg); assert.equal(arg.iModel, iModelDb); } static onDelete(arg) { super.onDelete(arg); assert.equal(arg.iModel, iModelDb); } static onDeleted(arg) { super.onDeleted(arg); assert.equal(arg.iModel, iModelDb); } static onChildDelete(arg) { super.onChildDelete(arg); assert.equal(arg.iModel, iModelDb); if (arg.childId === this.dontDeleteChild) throw new Error("dont delete my child"); } static onChildDeleted(arg) { super.onChildDeleted(arg); assert.equal(arg.iModel, iModelDb); } static onChildInsert(arg) { super.onChildInsert(arg); assert.equal(arg.iModel, iModelDb); } static onChildInserted(arg) { super.onChildInserted(arg); assert.equal(arg.iModel, iModelDb); } static onChildUpdate(arg) { super.onChildUpdate(arg); assert.equal(arg.iModel, iModelDb); } static onChildUpdated(arg) { super.onChildUpdated(arg); assert.equal(arg.iModel, iModelDb); } static onChildAdd(arg) { super.onChildAdd(arg); assert.equal(arg.iModel, iModelDb); } static onChildAdded(arg) { super.onChildAdded(arg); assert.equal(arg.iModel, iModelDb); } static onChildDrop(arg) { super.onChildDrop(arg); assert.equal(arg.iModel, iModelDb); } static onChildDropped(arg) { super.onChildDropped(arg); assert.equal(arg.iModel, iModelDb); } } /** for testing `ElementAspect.onXxx` methods */ class TestFuncAspect extends ElementUniqueAspect { static get className() { return "TestFuncAspect"; } static expectedVal = ""; static onInsert(arg) { super.onInsert(arg); assert.equal(arg.iModel, iModelDb); assert.equal(arg.props.strProp, this.expectedVal); } static onInserted(arg) { super.onInserted(arg); assert.equal(arg.iModel, iModelDb); assert.equal(arg.props.strProp, this.expectedVal); } static onUpdate(arg) { super.onUpdate(arg); assert.equal(arg.iModel, iModelDb); assert.equal(arg.props.strProp, this.expectedVal); } static onUpdated(arg) { super.onUpdated(arg); assert.equal(arg.iModel, iModelDb); assert.equal(arg.props.strProp, this.expectedVal); } static onDelete(arg) { super.onDelete(arg); assert.equal(arg.iModel, iModelDb); } static onDeleted(arg) { super.onDeleted(arg); assert.equal(arg.iModel, iModelDb); } } class Component extends FunctionalComponentElement { static get className() { return "Component"; } } describe("Functional Domain", () => { afterEach(() => { sinonRestore(); }); it("should populate FunctionalModel and test Element, Model, and ElementAspect callbacks", async () => { iModelDb = StandaloneDb.createEmpty(IModelTestUtils.prepareOutputFile("FunctionalDomain", "FunctionalTest.bim"), { rootSubject: { name: "FunctionalTest", description: "Test of the Functional domain schema." }, client: "Functional", globalOrigin: { x: 0, y: 0 }, projectExtents: { low: { x: -500, y: -500, z: -50 }, high: { x: 500, y: 500, z: 50 } }, guid: Guid.createValue(), }); iModelDb[_nativeDb].resetBriefcaseId(100); // Import the Functional schema FunctionalSchema.registerSchema(); Schemas.registerSchema(TestSchema); // eslint-disable-next-line @typescript-eslint/naming-convention ClassRegistry.registerModule({ TestFuncPartition, TestFuncModel, Breakdown, Component, TestFuncAspect }, TestSchema); await FunctionalSchema.importSchema(iModelDb); let commits = 0; let committed = 0; const elements = iModelDb.elements; const dropCommit = iModelDb.txns.onCommit.addListener(() => commits++); const dropCommitted = iModelDb.txns.onCommitted.addListener(() => committed++); iModelDb.saveChanges("Import Functional schema"); assert.equal(commits, 1); assert.equal(committed, 1); dropCommit(); dropCommitted(); IModelTestUtils.flushTxns(iModelDb); // importSchema below will fail if this is not called to flush local changes await iModelDb.importSchemas([join(KnownTestLocations.assetsDir, "TestFunctional.ecschema.xml")]); iModelDb.saveChanges("Import TestFunctional schema"); assert.equal(commits, 1); assert.equal(committed, 1); const testChannelKey1 = "channel 1 for tests"; const testChannelKey2 = "channel 2 for tests"; function testChannel(channelKey, fn, spies) { iModelDb.channels.removeAllowedChannel(channelKey); expect(fn).throws("not allowed"); iModelDb.channels.addAllowedChannel(channelKey); spies.forEach((s) => s.resetHistory()); return fn(); } const spy = { model: { onInsert: sinonSpy(TestFuncModel, "onInsert"), onInserted: sinonSpy(TestFuncModel, "onInserted"), onUpdate: sinonSpy(TestFuncModel, "onUpdate"), onUpdated: sinonSpy(TestFuncModel, "onUpdated"), onDelete: sinonSpy(TestFuncModel, "onDelete"), onDeleted: sinonSpy(TestFuncModel, "onDeleted"), onInsertElement: sinonSpy(TestFuncModel, "onInsertElement"), onInsertedElement: sinonSpy(TestFuncModel, "onInsertedElement"), onUpdateElement: sinonSpy(TestFuncModel, "onUpdateElement"), onUpdatedElement: sinonSpy(TestFuncModel, "onUpdatedElement"), onDeleteElement: sinonSpy(TestFuncModel, "onDeleteElement"), onDeletedElement: sinonSpy(TestFuncModel, "onDeletedElement"), }, partition: { onSubModelInsert: sinonSpy(TestFuncPartition, "onSubModelInsert"), onSubModelInserted: sinonSpy(TestFuncPartition, "onSubModelInserted"), onSubModelDelete: sinonSpy(TestFuncPartition, "onSubModelDelete"), onSubModelDeleted: sinonSpy(TestFuncPartition, "onSubModelDeleted"), }, breakdown: { onInsert: sinonSpy(Breakdown, "onInsert"), onInserted: sinonSpy(Breakdown, "onInserted"), onUpdate: sinonSpy(Breakdown, "onUpdate"), onUpdated: sinonSpy(Breakdown, "onUpdated"), onDelete: sinonSpy(Breakdown, "onDelete"), onDeleted: sinonSpy(Breakdown, "onDeleted"), onChildDelete: sinonSpy(Breakdown, "onChildDelete"), onChildDeleted: sinonSpy(Breakdown, "onChildDeleted"), onChildInsert: sinonSpy(Breakdown, "onChildInsert"), onChildInserted: sinonSpy(Breakdown, "onChildInserted"), onChildUpdate: sinonSpy(Breakdown, "onChildUpdate"), onChildUpdated: sinonSpy(Breakdown, "onChildUpdated"), onChildAdd: sinonSpy(Breakdown, "onChildAdd"), onChildAdded: sinonSpy(Breakdown, "onChildAdded"), onChildDrop: sinonSpy(Breakdown, "onChildDrop"), onChildDropped: sinonSpy(Breakdown, "onChildDropped"), }, aspect: { onInsert: sinonSpy(TestFuncAspect, "onInsert"), onInserted: sinonSpy(TestFuncAspect, "onInserted"), onUpdate: sinonSpy(TestFuncAspect, "onUpdate"), onUpdated: sinonSpy(TestFuncAspect, "onUpdated"), onDelete: sinonSpy(TestFuncAspect, "onDelete"), onDeleted: sinonSpy(TestFuncAspect, "onDeleted"), }, }; assert.equal(iModelDb.channels.queryChannelRoot(ChannelControl.sharedChannelName), IModel.rootSubjectId); const codeSpec = CodeSpec.create(iModelDb, "Test Functional Elements", CodeScopeSpec.Type.Model); iModelDb.codeSpecs.insert(codeSpec); assert.isTrue(Id64.isValidId64(codeSpec.id)); // create a channel for all elements in this test assert.isUndefined(iModelDb.channels.queryChannelRoot(testChannelKey1)); const subject1Id = iModelDb.channels.insertChannelSubject({ subjectName: "Test Functional Subject", channelKey: testChannelKey1 }); assert.equal(iModelDb.channels.queryChannelRoot(testChannelKey1), subject1Id); const partitionCode = FunctionalPartition.createCode(iModelDb, subject1Id, "Test Functional Model"); const partitionProps = { classFullName: TestFuncPartition.classFullName, model: IModel.repositoryModelId, parent: new SubjectOwnsPartitionElements(subject1Id), code: partitionCode, }; iModelDb.channels.addAllowedChannel(testChannelKey1); let partitionId = iModelDb.elements.insertElement(partitionProps); const modelId = testChannel(testChannelKey1, () => iModelDb.models.insertModel({ classFullName: TestFuncModel.classFullName, modeledElement: { id: partitionId } }), [spy.model.onInsert]); assert.isTrue(Id64.isValidId64(modelId)); assert.isTrue(spy.model.onInsert.calledOnce); assert.isTrue(spy.model.onInserted.calledOnce); assert.equal(spy.model.onInserted.getCall(0).args[0].id, modelId); assert.isFalse(spy.model.onUpdate.called, "model insert should not call onUpdate"); assert.isFalse(spy.model.onUpdated.called, "model insert should not call onUpdated"); assert.isTrue(spy.partition.onSubModelInsert.calledOnce); assert.isTrue(spy.partition.onSubModelInserted.calledOnce); assert.equal(spy.partition.onSubModelInserted.getCall(0).args[0].subModelId, modelId, "Element.onSubModelInserted should have correct subModelId"); expect(() => iModelDb.channels.insertChannelSubject({ subjectName: "Test Functional Subject 2", channelKey: testChannelKey1 })).to.throw(`Channel ${testChannelKey1} root already exist`); const subject2Id = iModelDb.channels.insertChannelSubject({ subjectName: "Test Functional Subject 2", channelKey: testChannelKey2 }); iModelDb.channels.addAllowedChannel(testChannelKey2); expect(() => iModelDb.channels.insertChannelSubject({ subjectName: "Nested Subject", parentSubjectId: subject2Id, channelKey: "nested channel" })).to.throw("may not nest"); partitionProps.code.value = "Test Func 2"; partitionProps.parent = new SubjectOwnsPartitionElements(subject2Id); partitionId = iModelDb.elements.insertElement(partitionProps); const modelId2 = iModelDb.models.insertModel({ classFullName: TestFuncModel.classFullName, modeledElement: { id: partitionId } }); assert.isTrue(Id64.isValidId64(modelId2)); assert.equal(spy.model.onInserted.getCall(1).args[0].id, modelId2, "second insert should set new id"); assert.equal(spy.model.onInsert.callCount, 2); assert.equal(spy.model.onInserted.callCount, 2); assert.equal(spy.partition.onSubModelInserted.getCall(1).args[0].subModelId, modelId2, "Element.onSubModelInserted should have correct subModelId"); const model2 = iModelDb.models.getModel(modelId2); testChannel(testChannelKey2, () => model2.update(), []); assert.equal(spy.model.onUpdated.getCall(0).args[0].id, modelId2); assert.equal(spy.model.onUpdate.callCount, 2); assert.equal(spy.model.onUpdated.callCount, 1); testChannel(testChannelKey2, () => model2.delete(), [spy.model.onDelete, spy.partition.onSubModelDelete]); assert.isTrue(spy.model.onDelete.calledOnce); assert.isTrue(spy.model.onDeleted.calledOnce); assert.equal(spy.model.onDeleted.getCall(0).args[0].id, modelId2); assert.isTrue(spy.partition.onSubModelDelete.calledOnce); assert.isTrue(spy.partition.onSubModelDeleted.calledOnce); assert.equal(spy.partition.onSubModelDeleted.getCall(0).args[0].subModelId, modelId2); const breakdownProps = { classFullName: Breakdown.classFullName, model: modelId, code: { spec: codeSpec.id, scope: modelId, value: "Breakdown1" } }; const breakdownId = testChannel(testChannelKey1, () => elements.insertElement(breakdownProps), [spy.model.onInsertElement, spy.breakdown.onInsert]); assert.isTrue(Id64.isValidId64(breakdownId)); assert.isTrue(spy.model.onInsertElement.calledOnce); assert.isTrue(spy.model.onInsertedElement.calledOnce); assert.equal(spy.model.onInsertedElement.getCall(0).args[0].elementId, breakdownId); assert.isTrue(spy.breakdown.onInsert.calledOnce); assert.isTrue(spy.breakdown.onInserted.calledOnce); assert.equal(spy.breakdown.onInserted.getCall(0).args[0].id, breakdownId); assert.equal(spy.breakdown.onInsert.getCall(0).args[0].props, breakdownProps); const breakdown2Props = { classFullName: Breakdown.classFullName, model: modelId, code: { spec: codeSpec.id, scope: modelId, value: "badval" } }; // TestFuncModel.onInsertElement throws for this code.value expect(() => elements.insertElement(breakdown2Props)).to.throw("bad element"); breakdown2Props.code.value = "Breakdown2"; breakdown2Props.userLabel = "start label"; // gets overwritten in `onInsert` const bd2 = elements.insertElement(breakdown2Props); const aspect = { classFullName: TestFuncAspect.classFullName, element: new ElementOwnsUniqueAspect(bd2), strProp: "prop 1" }; TestFuncAspect.expectedVal = aspect.strProp; testChannel(testChannelKey1, () => elements.insertAspect(aspect), [spy.aspect.onInsert]); assert.isTrue(spy.aspect.onInsert.calledOnce); assert.isTrue(spy.aspect.onInserted.calledOnce); assert.isFalse(spy.aspect.onUpdate.called); assert.isFalse(spy.aspect.onUpdated.called); assert.equal(spy.aspect.onInserted.getCall(0).args[0].props.element.id, bd2, "elemId from ElementAspect.onInserted"); aspect.strProp = "prop 2"; TestFuncAspect.expectedVal = aspect.strProp; testChannel(testChannelKey1, () => elements.updateAspect(aspect), [spy.aspect.onUpdate]); assert.equal(spy.aspect.onInsert.callCount, 1, "ElementAspect.onInsert should not be called on update"); assert.equal(spy.aspect.onInserted.callCount, 1, "ElementAspect.onInserted should should not be called on update"); assert.equal(spy.aspect.onUpdate.callCount, 1); assert.equal(spy.aspect.onUpdated.callCount, 1); assert.equal(spy.aspect.onUpdated.getCall(0).args[0].props.element.id, bd2, "from ElementAspect.onUpdated"); const aspects = elements.getAspects(bd2, TestFuncAspect.classFullName); assert.equal(aspects.length, 1); testChannel(testChannelKey1, () => elements.deleteAspect(aspects[0].id), [spy.aspect.onDelete]); assert.equal(spy.aspect.onDelete.callCount, 1); assert.equal(spy.aspect.onDeleted.callCount, 1); assert.equal(spy.aspect.onDelete.getCall(0).args[0].aspectId, aspects[0].id); assert.equal(spy.aspect.onDeleted.getCall(0).args[0].aspectId, aspects[0].id); let bd2el = elements.getElement(bd2); assert.equal(bd2el.userLabel, insertedLabel, "label was modified by onInsert"); expect(iModelDb.channels.getChannelKey(bd2)).equals(testChannelKey1); bd2el.userLabel = "nothing"; testChannel(testChannelKey1, () => bd2el.update(), [spy.breakdown.onUpdate, spy.breakdown.onUpdated]); bd2el = elements.getElement(bd2); assert.equal(bd2el.userLabel, updatedLabel, "label was modified in onUpdate"); assert.equal(spy.breakdown.onUpdate.callCount, 1); assert.equal(spy.breakdown.onUpdated.callCount, 1); assert.equal(spy.breakdown.onUpdate.getCall(0).args[0].props.id, bd2); assert.equal(spy.breakdown.onUpdated.getCall(0).args[0].id, bd2); testChannel(testChannelKey1, () => bd2el.delete(), [spy.breakdown.onDelete]); assert.equal(spy.breakdown.onDelete.callCount, 1); assert.equal(spy.breakdown.onDeleted.callCount, 1); const deleteArg = spy.breakdown.onDelete.getCall(0).args[0]; assert.equal(deleteArg.id, bd2); assert.equal(deleteArg.model, bd2el.model); assert.equal(deleteArg.federationGuid, bd2el.federationGuid); assert.equal(spy.breakdown.onDeleted.getCall(0).args[0].id, bd2); const breakdown3Props = { classFullName: Breakdown.classFullName, model: modelId, code: { spec: codeSpec.id, scope: modelId, value: "bd3" }, }; const bd3 = elements.insertElement(breakdown3Props); const componentProps = { classFullName: Component.classFullName, model: modelId, parent: { id: breakdownId, relClassName: ElementOwnsChildElements.classFullName }, code: { spec: codeSpec.id, scope: modelId, value: "Component1" }, }; const componentId = testChannel(testChannelKey1, () => elements.insertElement(componentProps), []); assert.isTrue(Id64.isValidId64(componentId)); assert.equal(spy.breakdown.onChildInserted.callCount, 1); assert.equal(spy.breakdown.onChildInserted.getCall(0).args[0].childId, componentId); // test model and element callbacks for updateElement const component1 = elements.getElement(componentId); testChannel(testChannelKey1, () => component1.update(), [spy.model.onUpdateElement, spy.model.onUpdatedElement]); assert.equal(spy.model.onUpdateElement.callCount, 1); assert.equal(spy.model.onUpdatedElement.callCount, 1); assert.equal(spy.model.onUpdatedElement.getCall(0).args[0].elementId, componentId); assert.equal(spy.breakdown.onChildUpdate.callCount, 1); assert.equal(spy.breakdown.onChildUpdated.callCount, 1); assert.equal(spy.breakdown.onChildUpdate.getCall(0).args[0].parentId, breakdownId); assert.equal(spy.breakdown.onChildUpdated.getCall(0).args[0].childId, componentId); componentProps.code.value = "comp2"; const comp2 = elements.insertElement(componentProps); assert.equal(spy.breakdown.onChildInserted.callCount, 2); assert.equal(spy.breakdown.onChildInserted.getCall(1).args[0].childId, comp2); const el2 = elements.getElement(comp2); spy.model.onDeleteElement.resetHistory(); spy.model.onDeletedElement.resetHistory(); TestFuncModel.dontDelete = comp2; // block deletion through model expect(() => el2.delete()).to.throw("dont delete my element"); TestFuncModel.dontDelete = ""; // allow deletion through model Breakdown.dontDeleteChild = comp2; // but block through parent expect(() => el2.delete()).to.throw("dont delete my child"); // nope assert.equal(spy.model.onDeleteElement.callCount, 2, "Model.onElementDelete gets called even though element is not really deleted"); assert.equal(spy.model.onDeletedElement.callCount, 0, "make sure Model.onElementDeleted did not get called"); Breakdown.dontDeleteChild = ""; // now fully allow delete el2.delete(); assert.equal(spy.model.onDeleteElement.callCount, 3, "Model.onElementDelete should be called again"); assert.equal(spy.model.onDeletedElement.callCount, 1); assert.equal(spy.model.onDeletedElement.getCall(0).args[0].elementId, comp2); assert.equal(spy.breakdown.onChildDeleted.callCount, 1); assert.equal(spy.breakdown.onChildDeleted.getCall(0).args[0].childId, comp2); // next we make sure that changing the parent of an element calls the "onChildAdd/Drop/Added/Dropped" callbacks. // To do this we switch a component's parent from "breakDownId" to "bc3" componentProps.parent.id = bd3; const comp3 = elements.insertElement(componentProps); const compEl3 = elements.getElementProps(comp3); compEl3.parent.id = breakdownId; testChannel(testChannelKey1, () => elements.updateElement(compEl3), []); assert.equal(spy.breakdown.onChildAdd.callCount, 1); assert.equal(spy.breakdown.onChildAdd.getCall(0).args[0].parentId, breakdownId); assert.equal(spy.breakdown.onChildAdd.getCall(0).args[0].childProps.id, comp3); assert.equal(spy.breakdown.onChildDrop.callCount, 1); assert.equal(spy.breakdown.onChildDrop.getCall(0).args[0].parentId, bd3); assert.equal(spy.breakdown.onChildDrop.getCall(0).args[0].childId, comp3); assert.equal(spy.breakdown.onChildAdded.callCount, 1); assert.equal(spy.breakdown.onChildAdded.getCall(0).args[0].parentId, breakdownId); assert.equal(spy.breakdown.onChildAdded.getCall(0).args[0].childId, comp3); assert.equal(spy.breakdown.onChildDropped.callCount, 1); assert.equal(spy.breakdown.onChildDropped.getCall(0).args[0].parentId, bd3); assert.equal(spy.breakdown.onChildDropped.getCall(0).args[0].childId, comp3); iModelDb.saveChanges("Insert Functional elements"); // unregister test schema to make sure it will throw exceptions if it is not present (since it has the "SchemaHasBehavior" custom attribute) Schemas.unregisterSchema(TestSchema.schemaName); const errMsg = "Schema [TestFunctional] not registered, but is marked with SchemaHasBehavior"; expect(() => elements.deleteElement(breakdownId)).to.throw(errMsg); assert.isDefined(elements.getElement(breakdownId), "should not have been deleted"); expect(() => elements.updateElement(breakdownProps)).to.throw(errMsg); breakdownProps.code.value = "Breakdown 2"; expect(() => elements.insertElement(breakdownProps)).to.throw(errMsg); iModelDb.close(); }); }); //# sourceMappingURL=FunctionalDomain.test.js.map