@itwin/core-backend
Version:
iTwin.js backend components
490 lines • 26.8 kB
JavaScript
/*---------------------------------------------------------------------------------------------
* 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