@itwin/core-backend 5.14.0-dev.21 → 5.14.0-dev.23
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/cjs/ECDb.d.ts +11 -6
- package/lib/cjs/ECDb.d.ts.map +1 -1
- package/lib/cjs/ECDb.js +11 -6
- package/lib/cjs/ECDb.js.map +1 -1
- package/lib/cjs/ECSqlStatement.d.ts +2 -2
- package/lib/cjs/ECSqlStatement.js +2 -2
- package/lib/cjs/ECSqlStatement.js.map +1 -1
- package/lib/cjs/IModelDb.d.ts +12 -10
- package/lib/cjs/IModelDb.d.ts.map +1 -1
- package/lib/cjs/IModelDb.js +19 -12
- package/lib/cjs/IModelDb.js.map +1 -1
- package/lib/cjs/annotations/ElementDrivesTextAnnotation.d.ts +90 -5
- package/lib/cjs/annotations/ElementDrivesTextAnnotation.d.ts.map +1 -1
- package/lib/cjs/annotations/ElementDrivesTextAnnotation.js +59 -1
- package/lib/cjs/annotations/ElementDrivesTextAnnotation.js.map +1 -1
- package/lib/cjs/internal/annotations/fieldSpecs.d.ts +82 -0
- package/lib/cjs/internal/annotations/fieldSpecs.d.ts.map +1 -0
- package/lib/cjs/internal/annotations/fieldSpecs.js +187 -0
- package/lib/cjs/internal/annotations/fieldSpecs.js.map +1 -0
- package/lib/cjs/internal/annotations/fields.d.ts +39 -2
- package/lib/cjs/internal/annotations/fields.d.ts.map +1 -1
- package/lib/cjs/internal/annotations/fields.js +140 -13
- package/lib/cjs/internal/annotations/fields.js.map +1 -1
- package/lib/esm/ECDb.d.ts +11 -6
- package/lib/esm/ECDb.d.ts.map +1 -1
- package/lib/esm/ECDb.js +11 -6
- package/lib/esm/ECDb.js.map +1 -1
- package/lib/esm/ECSqlStatement.d.ts +2 -2
- package/lib/esm/ECSqlStatement.js +2 -2
- package/lib/esm/ECSqlStatement.js.map +1 -1
- package/lib/esm/IModelDb.d.ts +12 -10
- package/lib/esm/IModelDb.d.ts.map +1 -1
- package/lib/esm/IModelDb.js +19 -12
- package/lib/esm/IModelDb.js.map +1 -1
- package/lib/esm/annotations/ElementDrivesTextAnnotation.d.ts +90 -5
- package/lib/esm/annotations/ElementDrivesTextAnnotation.d.ts.map +1 -1
- package/lib/esm/annotations/ElementDrivesTextAnnotation.js +60 -2
- package/lib/esm/annotations/ElementDrivesTextAnnotation.js.map +1 -1
- package/lib/esm/internal/annotations/fieldSpecs.d.ts +82 -0
- package/lib/esm/internal/annotations/fieldSpecs.d.ts.map +1 -0
- package/lib/esm/internal/annotations/fieldSpecs.js +179 -0
- package/lib/esm/internal/annotations/fieldSpecs.js.map +1 -0
- package/lib/esm/internal/annotations/fields.d.ts +39 -2
- package/lib/esm/internal/annotations/fields.d.ts.map +1 -1
- package/lib/esm/internal/annotations/fields.js +140 -13
- package/lib/esm/internal/annotations/fields.js.map +1 -1
- package/lib/esm/test/AnnotationTestUtils.d.ts +9 -0
- package/lib/esm/test/AnnotationTestUtils.d.ts.map +1 -1
- package/lib/esm/test/AnnotationTestUtils.js +17 -0
- package/lib/esm/test/AnnotationTestUtils.js.map +1 -1
- package/lib/esm/test/annotations/FieldFormat.test.d.ts +2 -0
- package/lib/esm/test/annotations/FieldFormat.test.d.ts.map +1 -0
- package/lib/esm/test/annotations/FieldFormat.test.js +738 -0
- package/lib/esm/test/annotations/FieldFormat.test.js.map +1 -0
- package/lib/esm/test/annotations/FieldFormattingLifecycle.test.d.ts +2 -0
- package/lib/esm/test/annotations/FieldFormattingLifecycle.test.d.ts.map +1 -0
- package/lib/esm/test/annotations/FieldFormattingLifecycle.test.js +323 -0
- package/lib/esm/test/annotations/FieldFormattingLifecycle.test.js.map +1 -0
- package/lib/esm/test/annotations/FieldSpecs.test.d.ts +2 -0
- package/lib/esm/test/annotations/FieldSpecs.test.d.ts.map +1 -0
- package/lib/esm/test/annotations/FieldSpecs.test.js +54 -0
- package/lib/esm/test/annotations/FieldSpecs.test.js.map +1 -0
- package/lib/esm/test/annotations/Fields.test.js +221 -27
- package/lib/esm/test/annotations/Fields.test.js.map +1 -1
- package/lib/esm/test/ecdb/QueryReaders.test.js +105 -30
- package/lib/esm/test/ecdb/QueryReaders.test.js.map +1 -1
- package/package.json +15 -13
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"FieldFormattingLifecycle.test.js","sourceRoot":"","sources":["../../../../src/test/annotations/FieldFormattingLifecycle.test.ts"],"names":[],"mappings":"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","sourcesContent":["/*---------------------------------------------------------------------------------------------\r\n* Copyright (c) Bentley Systems, Incorporated. All rights reserved.\r\n* See LICENSE.md in the project root for license terms and full copyright notice.\r\n*--------------------------------------------------------------------------------------------*/\r\nimport { expect } from \"chai\";\r\nimport { Code, FieldRun, PhysicalElementProps, SubCategoryAppearance, TextAnnotation, TextBlock, traverseTextBlockComponent } from \"@itwin/core-common\";\r\nimport { FormatDefinition } from \"@itwin/core-quantity\";\r\nimport { DbResult, Id64String, UnexpectedErrors } from \"@itwin/core-bentley\";\r\nimport { Point3d, YawPitchRollAngles } from \"@itwin/core-geometry\";\r\nimport { StandaloneDb } from \"../../IModelDb\";\r\nimport { IModelTestUtils } from \"../IModelTestUtils\";\r\nimport { SpatialCategory } from \"../../Category\";\r\nimport { Schema, Schemas } from \"../../Schema\";\r\nimport { ClassRegistry } from \"../../ClassRegistry\";\r\nimport { PhysicalElement } from \"../../Element\";\r\nimport { FontFile, TextAnnotation3d } from \"../../core-backend\";\r\nimport { ElementDrivesTextAnnotation, TextAnnotationUsesTextStyleByDefault } from \"../../annotations/ElementDrivesTextAnnotation\";\r\nimport { decimalFormat, toFormatSet } from \"../AnnotationTestUtils\";\r\nimport { withEditTxn } from \"../../EditTxn\";\r\n\r\n/**\r\n * The lifecycle of [[ElementDrivesTextAnnotation.registerFieldFormatting]] and the persistence\r\n * contract of [[ElementDrivesTextAnnotation.evaluateFields]]: when the change event fires, what a\r\n * registration does (and does not do) to already-persisted `cachedContent`, and which code paths\r\n * consult the registered FormatSets.\r\n *\r\n * Which format a given field resolves to is covered exhaustively by FieldFormat.test.ts; nothing\r\n * here varies `kindOfQuantity` or `persistenceUnit`. The registration-level `unitSystem` argument\r\n * is covered here because it is part of the registration contract rather than the per-field one.\r\n */\r\n\r\nconst lifecycleSchemaXml = `<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n<ECSchema schemaName=\"FieldLifecycle\" alias=\"flc\" version=\"01.00.00\" xmlns=\"http://www.bentley.com/schemas/Bentley.ECXML.3.2\">\r\n <ECSchemaReference name=\"BisCore\" version=\"01.00.04\" alias=\"bis\"/>\r\n <ECSchemaReference name=\"Formats\" version=\"01.00.00\" alias=\"f\"/>\r\n <ECSchemaReference name=\"Units\" version=\"01.00.09\" alias=\"u\"/>\r\n\r\n <KindOfQuantity typeName=\"LENGTH\" displayLabel=\"Length\" persistenceUnit=\"u:M\" relativeError=\"0.0001\" presentationUnits=\"f:DefaultRealU(4)[u:M]\"/>\r\n <KindOfQuantity typeName=\"DUAL_LENGTH\" displayLabel=\"Dual Length\" persistenceUnit=\"u:M\" relativeError=\"0.0001\" presentationUnits=\"f:DefaultRealU(4)[u:M];f:DefaultRealU(4)[u:FT]\"/>\r\n\r\n <ECEntityClass typeName=\"LifecycleElement\" modifier=\"None\">\r\n <BaseClass>bis:PhysicalElement</BaseClass>\r\n <ECProperty propertyName=\"lengthProp\" typeName=\"double\" kindOfQuantity=\"LENGTH\"/>\r\n <ECProperty propertyName=\"dualLengthProp\" typeName=\"double\" kindOfQuantity=\"DUAL_LENGTH\"/>\r\n <ECProperty propertyName=\"maybeNull\" typeName=\"double\"/>\r\n <ECArrayProperty propertyName=\"strings\" typeName=\"string\"/>\r\n </ECEntityClass>\r\n</ECSchema>\r\n`;\r\n\r\ninterface LifecycleElementProps extends PhysicalElementProps {\r\n lengthProp: number;\r\n dualLengthProp: number;\r\n maybeNull?: number;\r\n strings: string[];\r\n}\r\n\r\nclass LifecycleElement extends PhysicalElement {\r\n public static override get className() { return \"LifecycleElement\"; }\r\n declare public lengthProp: number;\r\n declare public dualLengthProp: number;\r\n declare public maybeNull?: number;\r\n declare public strings: string[];\r\n}\r\n\r\nclass FieldLifecycleSchema extends Schema {\r\n public static override get schemaName() { return \"FieldLifecycle\"; }\r\n}\r\n\r\ndescribe(\"Field formatting lifecycle\", () => {\r\n let imodel: StandaloneDb;\r\n let model: Id64String;\r\n let category: Id64String;\r\n let sourceElementId: Id64String;\r\n\r\n const propertyHost = (elementId: Id64String) => ({ elementId, schemaName: \"FieldLifecycle\", className: \"LifecycleElement\" });\r\n\r\n // Id referenced by fields via formatOptions.quantity.formatSet and supplied through the\r\n // `formatSets` array at registration time.\r\n const PRIMARY_FORMAT_SET = \"0x111\";\r\n\r\n before(async () => {\r\n const iModelPath = IModelTestUtils.prepareOutputFile(\"FieldFormattingLifecycle\", \"test.bim\");\r\n imodel = StandaloneDb.createEmpty(iModelPath, { rootSubject: { name: \"FieldFormattingLifecycle\" }, enableTransactions: true });\r\n\r\n if (!Schemas.getRegisteredSchema(\"FieldLifecycle\")) {\r\n Schemas.registerSchema(FieldLifecycleSchema);\r\n ClassRegistry.register(LifecycleElement, FieldLifecycleSchema);\r\n }\r\n await imodel.importSchemaStrings([lifecycleSchemaXml]);\r\n\r\n await withEditTxn(imodel, async (txn) => {\r\n model = IModelTestUtils.createAndInsertPhysicalPartitionAndModel(txn, Code.createEmpty(), true)[1];\r\n category = SpatialCategory.insert(txn, StandaloneDb.dictionaryId, \"FieldFormattingLifecycleCategory\", new SubCategoryAppearance());\r\n await imodel.fonts.embedFontFile({\r\n file: FontFile.createFromTrueTypeFileName(IModelTestUtils.resolveFontFile(\"Karla-Regular.ttf\")),\r\n });\r\n sourceElementId = txn.insertElement(sourceElementProps());\r\n });\r\n });\r\n\r\n after(() => {\r\n imodel.close();\r\n });\r\n\r\n afterEach(() => {\r\n ElementDrivesTextAnnotation.registerFieldFormatting({ iModel: imodel });\r\n // Drop the annotations inserted below so their ElementDrivesTextAnnotation relationships do\r\n // not leak into the next test.\r\n const ids: Id64String[] = [];\r\n // eslint-disable-next-line @typescript-eslint/no-deprecated\r\n imodel.withPreparedStatement(\"SELECT ECInstanceId FROM BisCore.TextAnnotation3d\", (stmt) => {\r\n while (stmt.step() === DbResult.BE_SQLITE_ROW)\r\n ids.push(stmt.getValue(0).getId());\r\n });\r\n if (ids.length > 0)\r\n withEditTxn(imodel, (txn) => { for (const id of ids) txn.deleteElement(id); });\r\n });\r\n\r\n function sourceElementProps(): LifecycleElementProps {\r\n return {\r\n classFullName: \"FieldLifecycle:LifecycleElement\",\r\n model,\r\n category,\r\n code: Code.createEmpty(),\r\n lengthProp: 2.5,\r\n dualLengthProp: 2.5,\r\n strings: [\"a\", \"b\"],\r\n placement: { origin: new Point3d(0, 0, 0), angles: new YawPitchRollAngles() },\r\n };\r\n }\r\n\r\n // Configures `imodel` with the given per-field FormatSets.\r\n function registerSets(formatSets: ReadonlyArray<{ id: string, formats: Record<string, FormatDefinition> }>): void {\r\n ElementDrivesTextAnnotation.registerFieldFormatting({\r\n iModel: imodel,\r\n formatSets: formatSets.map(({ id, formats }) => ({ id, formatSet: toFormatSet(\"TestSet\", formats) })),\r\n });\r\n }\r\n\r\n // A mm FormatSet: 2.5 m -> \"2500 mm\".\r\n function mmSet(): Record<string, FormatDefinition> {\r\n return { \"FieldLifecycle.LENGTH\": decimalFormat(\"Units.MM\", \"mm\", 2) };\r\n }\r\n\r\n function readFieldCachedContent(block: TextBlock): string | undefined {\r\n for (const { child } of traverseTextBlockComponent(block)) {\r\n if (child.type === \"field\")\r\n return child.cachedContent;\r\n }\r\n return undefined;\r\n }\r\n\r\n function readFieldCachedContentById(annotationElementId: Id64String): string | undefined {\r\n const reloaded = imodel.elements.getElement<TextAnnotation3d>(annotationElementId);\r\n const reloadedBlock = reloaded.getAnnotation()?.textBlock;\r\n return reloadedBlock ? readFieldCachedContent(reloadedBlock) : undefined;\r\n }\r\n\r\n function insertAnnotationElement(textBlock: TextBlock): Id64String {\r\n const elem = TextAnnotation3d.fromJSON({\r\n model,\r\n category,\r\n code: Code.createEmpty(),\r\n placement: {\r\n origin: { x: 0, y: 0, z: 0 },\r\n angles: YawPitchRollAngles.createDegrees(0, 0, 0).toJSON(),\r\n },\r\n classFullName: TextAnnotation3d.classFullName,\r\n defaultTextStyle: new TextAnnotationUsesTextStyleByDefault(\"0x123\").toJSON(),\r\n }, imodel);\r\n elem.setAnnotation(TextAnnotation.fromJSON({ textBlock: textBlock.toJSON() }));\r\n return withEditTxn(imodel, (txn) => txn.insertElement(elem.toJSON()));\r\n }\r\n\r\n /** Inserts an annotation whose single field reads `lengthProp` of `sourceId` through\r\n * `PRIMARY_FORMAT_SET`, and wires up its dependency so txn callbacks evaluate it.\r\n */\r\n function insertAnnotationWithLengthField(sourceId: Id64String, formatSet: string | undefined = PRIMARY_FORMAT_SET): Id64String {\r\n const textBlock = TextBlock.create();\r\n const field = FieldRun.create({\r\n styleOverrides: { font: { name: \"Karla\" } },\r\n propertyHost: propertyHost(sourceId),\r\n propertyPath: { propertyName: \"lengthProp\" },\r\n formatOptions: formatSet ? { quantity: { formatSet } } : undefined,\r\n cachedContent: \"old\",\r\n });\r\n textBlock.appendRun(field);\r\n const annotationElementId = insertAnnotationElement(textBlock);\r\n withEditTxn(imodel, (txn) => {\r\n ElementDrivesTextAnnotation.updateFieldDependencies(txn, annotationElementId);\r\n });\r\n return annotationElementId;\r\n }\r\n\r\n describe(\"evaluateFields\", () => {\r\n it(\"preserves non-quantity field formatting\", () => {\r\n const textBlock = TextBlock.create();\r\n const stringField = FieldRun.create({\r\n propertyHost: propertyHost(sourceElementId),\r\n propertyPath: { propertyName: \"strings\", accessors: [0] },\r\n formatOptions: { prefix: \"[\", suffix: \"]\" },\r\n cachedContent: \"old\",\r\n });\r\n textBlock.appendRun(stringField);\r\n\r\n registerSets([{ id: PRIMARY_FORMAT_SET, formats: mmSet() }]);\r\n const updatedCount = ElementDrivesTextAnnotation.evaluateFields({ iModel: imodel, block: textBlock });\r\n\r\n expect(updatedCount).to.equal(1);\r\n expect(stringField.cachedContent).to.equal(\"[a]\");\r\n });\r\n\r\n it(\"marks the field invalid when a quantity property value is missing\", () => {\r\n const textBlock = TextBlock.create();\r\n const field = FieldRun.create({\r\n propertyHost: propertyHost(sourceElementId),\r\n // maybeNull has no value on the source element.\r\n propertyPath: { propertyName: \"maybeNull\" },\r\n formatOptions: { quantity: { persistenceUnit: \"Units.M\" } },\r\n cachedContent: \"old\",\r\n });\r\n textBlock.appendRun(field);\r\n\r\n const updatedCount = ElementDrivesTextAnnotation.evaluateFields({ iModel: imodel, block: textBlock });\r\n\r\n expect(updatedCount).to.equal(1);\r\n expect(field.cachedContent).to.equal(FieldRun.invalidContentIndicator);\r\n });\r\n\r\n it(\"mutates the in-memory TextBlock but does not persist to the element on its own\", () => {\r\n // Contract: evaluateFields formats in place and returns a count. Persistence is the\r\n // caller's responsibility (setAnnotation + element.update inside an EditTxn).\r\n const annotationElementId = insertAnnotationWithLengthField(sourceElementId);\r\n const persistedBefore = readFieldCachedContentById(annotationElementId);\r\n expect(persistedBefore).to.equal(\"2.5 m\");\r\n\r\n registerSets([{ id: PRIMARY_FORMAT_SET, formats: mmSet() }]);\r\n\r\n const reloaded = imodel.elements.getElement<TextAnnotation3d>(annotationElementId);\r\n const inMemoryBlock = reloaded.getAnnotation()!.textBlock;\r\n\r\n const updated = ElementDrivesTextAnnotation.evaluateFields({ iModel: imodel, block: inMemoryBlock });\r\n\r\n // The in-memory half of the contract: the field resolved through the registered FormatSet\r\n // and the call reported the one field it changed.\r\n expect(updated).to.equal(1);\r\n expect(readFieldCachedContent(inMemoryBlock)).to.equal(\"2500 mm\");\r\n\r\n // The persisted half: the disk copy is untouched, because nothing called `update`.\r\n expect(readFieldCachedContentById(annotationElementId)).to.equal(persistedBefore);\r\n });\r\n });\r\n\r\n describe(\"registerFieldFormatting\", () => {\r\n it(\"raises onFieldFormattingChanged on every register call\", () => {\r\n let events = 0;\r\n const drop = ElementDrivesTextAnnotation.onFieldFormattingChanged.addListener((args) => {\r\n expect(args.iModel).to.equal(imodel);\r\n ++events;\r\n });\r\n\r\n try {\r\n registerSets([{ id: PRIMARY_FORMAT_SET, formats: mmSet() }]);\r\n // Re-registering the same configuration still reports.\r\n registerSets([{ id: PRIMARY_FORMAT_SET, formats: mmSet() }]);\r\n // Reverting to the schema default is a register call too.\r\n ElementDrivesTextAnnotation.registerFieldFormatting({ iModel: imodel });\r\n\r\n expect(events).to.equal(3);\r\n } finally {\r\n drop();\r\n }\r\n });\r\n\r\n it(\"keeps the registration when a listener throws\", () => {\r\n // BeEvent routes listener exceptions to UnexpectedErrors rather than to the caller.\r\n const unexpected: unknown[] = [];\r\n const previousHandler = UnexpectedErrors.setHandler((e) => unexpected.push(e));\r\n const drop = ElementDrivesTextAnnotation.onFieldFormattingChanged.addListener(() => {\r\n throw new Error(\"listener failure\");\r\n });\r\n\r\n try {\r\n expect(() => registerSets([{ id: PRIMARY_FORMAT_SET, formats: mmSet() }])).not.to.throw();\r\n } finally {\r\n drop();\r\n UnexpectedErrors.setHandler(previousHandler);\r\n }\r\n\r\n expect(unexpected.length).to.equal(1);\r\n const annotationElementId = insertAnnotationWithLengthField(sourceElementId);\r\n expect(readFieldCachedContentById(annotationElementId)).to.equal(\"2500 mm\");\r\n });\r\n\r\n it(\"selects the KindOfQuantity's presentation format for the registered unitSystem\", () => {\r\n // DUAL_LENGTH lists a metric and an imperial presentation format. The unit system chosen\r\n // at registration decides which one the schema provider hands back, and an explicit\r\n // `unitSystem` wins over the adopted FormatSet's own.\r\n // Persisted on the element: dualLengthProp 2.5 m\r\n const evaluate = () => {\r\n const block = TextBlock.create();\r\n const field = FieldRun.create({\r\n propertyHost: propertyHost(sourceElementId),\r\n propertyPath: { propertyName: \"dualLengthProp\" },\r\n cachedContent: \"old\",\r\n });\r\n block.appendRun(field);\r\n ElementDrivesTextAnnotation.evaluateFields({ iModel: imodel, block });\r\n return field.cachedContent;\r\n };\r\n\r\n // Nothing registered: metric.\r\n expect(evaluate()).to.equal(\"2.5 m\");\r\n\r\n // An explicit unit system alone.\r\n ElementDrivesTextAnnotation.registerFieldFormatting({ iModel: imodel, unitSystem: \"imperial\" });\r\n expect(evaluate()).to.equal(\"8.2021 ft\");\r\n\r\n // An adopted metric FormatSet that does not define the key defers to the schema, using the\r\n // set's own system when none is given and the explicit one when it is.\r\n const metricSet = toFormatSet(\"Adopted\", mmSet());\r\n ElementDrivesTextAnnotation.registerFieldFormatting({ iModel: imodel, formatSet: metricSet });\r\n expect(evaluate()).to.equal(\"2.5 m\");\r\n ElementDrivesTextAnnotation.registerFieldFormatting({ iModel: imodel, formatSet: metricSet, unitSystem: \"imperial\" });\r\n expect(evaluate()).to.equal(\"8.2021 ft\");\r\n });\r\n\r\n it(\"formats through the schema default on the txn callback path when nothing is registered\", () => {\r\n const annotationElementId = insertAnnotationWithLengthField(sourceElementId, undefined);\r\n expect(readFieldCachedContentById(annotationElementId)).to.equal(\"2.5 m\");\r\n });\r\n\r\n it(\"routes txn-driven field updates through the registered FormatSet\", () => {\r\n registerSets([{ id: PRIMARY_FORMAT_SET, formats: mmSet() }]);\r\n\r\n const annotationElementId = insertAnnotationWithLengthField(sourceElementId);\r\n\r\n expect(readFieldCachedContentById(annotationElementId)).to.equal(\"2500 mm\");\r\n });\r\n\r\n it(\"documents the registration-lifecycle contract for persisted cachedContent\", () => {\r\n // Deliberately one narrative test rather than one per step: this is documentation of an\r\n // accepted contract, not a bug under guard. The contract is that registering never walks\r\n // existing annotations, so persisted cachedContent changes only on the next source-element\r\n // edit -- which formats according to whatever happens to be registered at that moment.\r\n const sourceId = withEditTxn(imodel, (txn) => txn.insertElement(sourceElementProps()));\r\n\r\n // 1. Nothing registered: insert persists the schema's presentation format.\r\n const annotationElementId = insertAnnotationWithLengthField(sourceId);\r\n expect(readFieldCachedContentById(annotationElementId)).to.equal(\"2.5 m\");\r\n\r\n // 2. Registering is not retroactive; persisted content is untouched.\r\n registerSets([{ id: PRIMARY_FORMAT_SET, formats: mmSet() }]);\r\n expect(readFieldCachedContentById(annotationElementId)).to.equal(\"2.5 m\");\r\n\r\n // 3. The next source edit fires the txn callback, which routes through the FormatSet.\r\n const source = imodel.elements.getElement<LifecycleElement>(sourceId);\r\n source.lengthProp = 4.25;\r\n withEditTxn(imodel, \"source update\", (txn) => {\r\n source.update(txn);\r\n txn.saveChanges(\"source update\");\r\n });\r\n expect(readFieldCachedContentById(annotationElementId)).to.equal(\"4250 mm\");\r\n\r\n // 4. Reverting to the schema default is likewise not retroactive...\r\n ElementDrivesTextAnnotation.registerFieldFormatting({ iModel: imodel });\r\n expect(readFieldCachedContentById(annotationElementId)).to.equal(\"4250 mm\");\r\n\r\n // ...but the following edit falls back to the schema default and overwrites the FormatSet's\r\n // millimeters with the schema's meters. This is why registerFieldFormatting's docs tell\r\n // hosts to swap FormatSets with a single re-register rather than reverting first.\r\n const reloadedSource = imodel.elements.getElement<LifecycleElement>(sourceId);\r\n reloadedSource.lengthProp = 3.5;\r\n withEditTxn(imodel, \"source update after revert\", (txn) => {\r\n reloadedSource.update(txn);\r\n txn.saveChanges(\"source update after revert\");\r\n });\r\n expect(readFieldCachedContentById(annotationElementId)).to.equal(\"3.5 m\");\r\n });\r\n });\r\n});\r\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"FieldSpecs.test.d.ts","sourceRoot":"","sources":["../../../../src/test/annotations/FieldSpecs.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/*---------------------------------------------------------------------------------------------
|
|
2
|
+
* Copyright (c) Bentley Systems, Incorporated. All rights reserved.
|
|
3
|
+
* See LICENSE.md in the project root for license terms and full copyright notice.
|
|
4
|
+
*--------------------------------------------------------------------------------------------*/
|
|
5
|
+
import { expect } from "chai";
|
|
6
|
+
import { collectFieldQuantityPairs } from "../../internal/annotations/fieldSpecs";
|
|
7
|
+
describe("collectFieldQuantityPairs candidate priority", () => {
|
|
8
|
+
const PROPERTY_PAIR = { name: "P.KOQ", persistenceUnitName: "Units.M" };
|
|
9
|
+
it("keeps the property-side fallback for a kindOfQuantity-only override", () => {
|
|
10
|
+
expect(collectFieldQuantityPairs({
|
|
11
|
+
overrideName: "A.KOQ", propertyName: "P.KOQ", propertyPersistence: "Units.M",
|
|
12
|
+
})).to.deep.equal([{ name: "A.KOQ", persistenceUnitName: "Units.M" }, PROPERTY_PAIR]);
|
|
13
|
+
});
|
|
14
|
+
it("keeps the property-side fallback when the persistence override restates the property's unit", () => {
|
|
15
|
+
expect(collectFieldQuantityPairs({
|
|
16
|
+
overrideName: "A.KOQ", overridePersistence: "Units.M", propertyName: "P.KOQ", propertyPersistence: "Units.M",
|
|
17
|
+
})).to.deep.equal([{ name: "A.KOQ", persistenceUnitName: "Units.M" }, PROPERTY_PAIR]);
|
|
18
|
+
});
|
|
19
|
+
it("keeps the property's own persistence unit over a persistence override that disagrees", () => {
|
|
20
|
+
// The override only supplies a unit the property lacks; it never reinterprets a stored value.
|
|
21
|
+
expect(collectFieldQuantityPairs({
|
|
22
|
+
overridePersistence: "Units.FT", propertyName: "P.KOQ", propertyPersistence: "Units.M",
|
|
23
|
+
})).to.deep.equal([PROPERTY_PAIR]);
|
|
24
|
+
expect(collectFieldQuantityPairs({
|
|
25
|
+
overrideName: "A.KOQ", overridePersistence: "Units.FT", propertyName: "P.KOQ", propertyPersistence: "Units.M",
|
|
26
|
+
})).to.deep.equal([{ name: "A.KOQ", persistenceUnitName: "Units.M" }, PROPERTY_PAIR]);
|
|
27
|
+
});
|
|
28
|
+
it("uses the persistence override when the property has no unit of its own", () => {
|
|
29
|
+
expect(collectFieldQuantityPairs({
|
|
30
|
+
overrideName: "A.KOQ", overridePersistence: "Units.FT", propertyName: undefined, propertyPersistence: undefined,
|
|
31
|
+
})).to.deep.equal([{ name: "A.KOQ", persistenceUnitName: "Units.FT" }]);
|
|
32
|
+
});
|
|
33
|
+
it("emits a single pair when the overrides only restate the property's own pair", () => {
|
|
34
|
+
expect(collectFieldQuantityPairs({
|
|
35
|
+
overrideName: "P.KOQ", overridePersistence: "Units.M", propertyName: "P.KOQ", propertyPersistence: "Units.M",
|
|
36
|
+
})).to.deep.equal([PROPERTY_PAIR]);
|
|
37
|
+
});
|
|
38
|
+
it("compares names exactly; callers normalize spellings to the schema's before building pairs", () => {
|
|
39
|
+
// `lookupFieldSpec` resolves `p:koq` to `P.KOQ` first. Anything that reaches here unnormalized
|
|
40
|
+
// is a name no schema defines, and those really are different keys.
|
|
41
|
+
expect(collectFieldQuantityPairs({
|
|
42
|
+
overrideName: "p:koq", propertyName: "P.KOQ", propertyPersistence: "Units.M",
|
|
43
|
+
})).to.deep.equal([{ name: "p:koq", persistenceUnitName: "Units.M" }, PROPERTY_PAIR]);
|
|
44
|
+
});
|
|
45
|
+
it("emits no property-side pair at all when the property has no persistence unit", () => {
|
|
46
|
+
expect(collectFieldQuantityPairs({
|
|
47
|
+
overrideName: "A.KOQ", overridePersistence: "Units.ARC_DEG", propertyName: undefined, propertyPersistence: undefined,
|
|
48
|
+
})).to.deep.equal([{ name: "A.KOQ", persistenceUnitName: "Units.ARC_DEG" }]);
|
|
49
|
+
for (const overridePersistence of [undefined, "", "Units.ARC_DEG"]) {
|
|
50
|
+
expect(collectFieldQuantityPairs({ overridePersistence, propertyName: undefined, propertyPersistence: undefined })).to.deep.equal([]);
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
});
|
|
54
|
+
//# sourceMappingURL=FieldSpecs.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"FieldSpecs.test.js","sourceRoot":"","sources":["../../../../src/test/annotations/FieldSpecs.test.ts"],"names":[],"mappings":"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","sourcesContent":["/*---------------------------------------------------------------------------------------------\r\n* Copyright (c) Bentley Systems, Incorporated. All rights reserved.\r\n* See LICENSE.md in the project root for license terms and full copyright notice.\r\n*--------------------------------------------------------------------------------------------*/\r\n\r\nimport { expect } from \"chai\";\r\nimport { collectFieldQuantityPairs } from \"../../internal/annotations/fieldSpecs\";\r\n\r\ndescribe(\"collectFieldQuantityPairs candidate priority\", () => {\r\n const PROPERTY_PAIR = { name: \"P.KOQ\", persistenceUnitName: \"Units.M\" };\r\n\r\n it(\"keeps the property-side fallback for a kindOfQuantity-only override\", () => {\r\n expect(collectFieldQuantityPairs({\r\n overrideName: \"A.KOQ\", propertyName: \"P.KOQ\", propertyPersistence: \"Units.M\",\r\n })).to.deep.equal([{ name: \"A.KOQ\", persistenceUnitName: \"Units.M\" }, PROPERTY_PAIR]);\r\n });\r\n\r\n it(\"keeps the property-side fallback when the persistence override restates the property's unit\", () => {\r\n expect(collectFieldQuantityPairs({\r\n overrideName: \"A.KOQ\", overridePersistence: \"Units.M\", propertyName: \"P.KOQ\", propertyPersistence: \"Units.M\",\r\n })).to.deep.equal([{ name: \"A.KOQ\", persistenceUnitName: \"Units.M\" }, PROPERTY_PAIR]);\r\n });\r\n\r\n it(\"keeps the property's own persistence unit over a persistence override that disagrees\", () => {\r\n // The override only supplies a unit the property lacks; it never reinterprets a stored value.\r\n expect(collectFieldQuantityPairs({\r\n overridePersistence: \"Units.FT\", propertyName: \"P.KOQ\", propertyPersistence: \"Units.M\",\r\n })).to.deep.equal([PROPERTY_PAIR]);\r\n expect(collectFieldQuantityPairs({\r\n overrideName: \"A.KOQ\", overridePersistence: \"Units.FT\", propertyName: \"P.KOQ\", propertyPersistence: \"Units.M\",\r\n })).to.deep.equal([{ name: \"A.KOQ\", persistenceUnitName: \"Units.M\" }, PROPERTY_PAIR]);\r\n });\r\n\r\n it(\"uses the persistence override when the property has no unit of its own\", () => {\r\n expect(collectFieldQuantityPairs({\r\n overrideName: \"A.KOQ\", overridePersistence: \"Units.FT\", propertyName: undefined, propertyPersistence: undefined,\r\n })).to.deep.equal([{ name: \"A.KOQ\", persistenceUnitName: \"Units.FT\" }]);\r\n });\r\n\r\n it(\"emits a single pair when the overrides only restate the property's own pair\", () => {\r\n expect(collectFieldQuantityPairs({\r\n overrideName: \"P.KOQ\", overridePersistence: \"Units.M\", propertyName: \"P.KOQ\", propertyPersistence: \"Units.M\",\r\n })).to.deep.equal([PROPERTY_PAIR]);\r\n });\r\n\r\n it(\"compares names exactly; callers normalize spellings to the schema's before building pairs\", () => {\r\n // `lookupFieldSpec` resolves `p:koq` to `P.KOQ` first. Anything that reaches here unnormalized\r\n // is a name no schema defines, and those really are different keys.\r\n expect(collectFieldQuantityPairs({\r\n overrideName: \"p:koq\", propertyName: \"P.KOQ\", propertyPersistence: \"Units.M\",\r\n })).to.deep.equal([{ name: \"p:koq\", persistenceUnitName: \"Units.M\" }, PROPERTY_PAIR]);\r\n });\r\n\r\n it(\"emits no property-side pair at all when the property has no persistence unit\", () => {\r\n expect(collectFieldQuantityPairs({\r\n overrideName: \"A.KOQ\", overridePersistence: \"Units.ARC_DEG\", propertyName: undefined, propertyPersistence: undefined,\r\n })).to.deep.equal([{ name: \"A.KOQ\", persistenceUnitName: \"Units.ARC_DEG\" }]);\r\n\r\n for (const overridePersistence of [undefined, \"\", \"Units.ARC_DEG\"]) {\r\n expect(collectFieldQuantityPairs({ overridePersistence, propertyName: undefined, propertyPersistence: undefined })).to.deep.equal([]);\r\n }\r\n });\r\n});\r\n"]}
|
|
@@ -3,11 +3,13 @@
|
|
|
3
3
|
* See LICENSE.md in the project root for license terms and full copyright notice.
|
|
4
4
|
*--------------------------------------------------------------------------------------------*/
|
|
5
5
|
import { expect } from "chai";
|
|
6
|
+
import * as sinon from "sinon";
|
|
6
7
|
import { Code, FieldRun, SubCategoryAppearance, TextAnnotation, TextBlock, TextRun } from "@itwin/core-common";
|
|
7
8
|
import { StandaloneDb } from "../../IModelDb";
|
|
8
9
|
import { IModelTestUtils } from "../IModelTestUtils";
|
|
9
10
|
import { createUpdateContext, updateField, updateFields } from "../../internal/annotations/fields";
|
|
10
|
-
import {
|
|
11
|
+
import { createFieldFormatting } from "../../internal/annotations/fieldSpecs";
|
|
12
|
+
import { Id64, Logger, ProcessDetector } from "@itwin/core-bentley";
|
|
11
13
|
import { SpatialCategory } from "../../Category";
|
|
12
14
|
import { Point3d, YawPitchRollAngles } from "@itwin/core-geometry";
|
|
13
15
|
import { Schema, Schemas } from "../../Schema";
|
|
@@ -22,6 +24,7 @@ function isIntlSupported() {
|
|
|
22
24
|
// solution.
|
|
23
25
|
return !ProcessDetector.isMobileAppBackend;
|
|
24
26
|
}
|
|
27
|
+
//cspell: ignore classid ecdbmap oldval reqs uppercased
|
|
25
28
|
function insertTestElement(txn, model, category, overrides, aspectProp = 999) {
|
|
26
29
|
const props = {
|
|
27
30
|
classFullName: "Fields:TestElement",
|
|
@@ -31,10 +34,17 @@ function insertTestElement(txn, model, category, overrides, aspectProp = 999) {
|
|
|
31
34
|
intProp: 100,
|
|
32
35
|
point: { x: 1, y: 2, z: 3 },
|
|
33
36
|
strings: ["a", "b", `"name": "c"`],
|
|
37
|
+
dateStrings: ["2025-08-28T13:45:30.123Z"],
|
|
34
38
|
datetime: new Date("2025-08-28T13:45:30.123Z"),
|
|
39
|
+
lengthProp: 2.5,
|
|
35
40
|
intEnum: 1,
|
|
36
41
|
outerStruct: {
|
|
37
|
-
innerStruct: {
|
|
42
|
+
innerStruct: {
|
|
43
|
+
bool: false,
|
|
44
|
+
doubles: [1, 2, 3],
|
|
45
|
+
json: JSON.stringify({ contactInfo: { email: "duck@pond.example", phoneNumbers: [{ areaCode: 555 }] } }),
|
|
46
|
+
notJson: JSON.stringify({ contactInfo: { email: "not-indexable" } }),
|
|
47
|
+
},
|
|
38
48
|
innerStructs: [{ bool: true, doubles: [] }, { bool: false, doubles: [5, 4, 3, 2, 1] }],
|
|
39
49
|
},
|
|
40
50
|
outerStructs: [{
|
|
@@ -49,6 +59,11 @@ function insertTestElement(txn, model, category, overrides, aspectProp = 999) {
|
|
|
49
59
|
stringProp: "abc",
|
|
50
60
|
ints: [10, 11, 12, 13],
|
|
51
61
|
bool: true,
|
|
62
|
+
// Deliberately present-but-null, to check that a JSON null resolves to no value rather than
|
|
63
|
+
// to a FieldValue the formatters would have to stringify.
|
|
64
|
+
nullProp: null,
|
|
65
|
+
lengthMeters: 2.5,
|
|
66
|
+
readings: [1.5, null, "text"],
|
|
52
67
|
zoo: {
|
|
53
68
|
address: {
|
|
54
69
|
zipcode: 12345,
|
|
@@ -157,6 +172,10 @@ const fieldsSchemaXml = `
|
|
|
157
172
|
<ECSchema schemaName="Fields" alias="ts" version="01.00.00" xmlns="http://www.bentley.com/schemas/Bentley.ECXML.3.2">
|
|
158
173
|
<ECSchemaReference name="BisCore" version="01.00.04" alias="bis"/>
|
|
159
174
|
<ECSchemaReference name='ECDbMap' version='02.00.04' alias='ecdbmap' />
|
|
175
|
+
<ECSchemaReference name="Formats" version="01.00.00" alias="f"/>
|
|
176
|
+
<ECSchemaReference name="Units" version="01.00.09" alias="u"/>
|
|
177
|
+
|
|
178
|
+
<KindOfQuantity typeName="LENGTH" displayLabel="Length" persistenceUnit="u:M" relativeError="0.0001" presentationUnits="f:DefaultRealU(4)[u:M]"/>
|
|
160
179
|
|
|
161
180
|
<ECEnumeration typeName="IntEnum" backingTypeName="int">
|
|
162
181
|
<ECEnumerator name="one" displayLabel="One" value="1" />
|
|
@@ -165,6 +184,8 @@ const fieldsSchemaXml = `
|
|
|
165
184
|
|
|
166
185
|
<ECStructClass typeName="InnerStruct" modifier="None">
|
|
167
186
|
<ECProperty propertyName="bool" typeName="boolean"/>
|
|
187
|
+
<ECProperty propertyName="json" typeName="string" extendedTypeName="Json"/>
|
|
188
|
+
<ECProperty propertyName="notJson" typeName="string"/>
|
|
168
189
|
<ECArrayProperty propertyName="doubles" typeName="double" minOccurs="0" maxOccurs="unbounded"/>
|
|
169
190
|
</ECStructClass>
|
|
170
191
|
|
|
@@ -179,7 +200,9 @@ const fieldsSchemaXml = `
|
|
|
179
200
|
<ECProperty propertyName="point" typeName="point3d"/>
|
|
180
201
|
<ECProperty propertyName="maybeNull" typeName="int"/>
|
|
181
202
|
<ECProperty propertyName="datetime" typeName="dateTime"/>
|
|
203
|
+
<ECProperty propertyName="lengthProp" typeName="double" kindOfQuantity="LENGTH"/>
|
|
182
204
|
<ECArrayProperty propertyName="strings" typeName="string" minOccurs="0" maxOccurs="unbounded"/>
|
|
205
|
+
<ECArrayProperty propertyName="dateStrings" typeName="string" extendedTypeName="DateTime" minOccurs="0" maxOccurs="unbounded"/>
|
|
183
206
|
<ECStructProperty propertyName="outerStruct" typeName="OuterStruct"/>
|
|
184
207
|
<ECStructArrayProperty propertyName="outerStructs" typeName="OuterStruct" minOccurs="0" maxOccurs="unbounded"/>
|
|
185
208
|
<ECProperty propertyName="intEnum" typeName="IntEnum"/>
|
|
@@ -244,13 +267,14 @@ describe("Field evaluation", () => {
|
|
|
244
267
|
after(() => {
|
|
245
268
|
imodel.close();
|
|
246
269
|
});
|
|
247
|
-
function evaluateField(propertyPath, propertyHost, deletedDependency = false) {
|
|
270
|
+
function evaluateField(propertyPath, propertyHost, deletedDependency = false, formatOptions) {
|
|
248
271
|
if (typeof propertyHost === "string") {
|
|
249
272
|
propertyHost = { schemaName: "Fields", className: "TestElement", elementId: propertyHost };
|
|
250
273
|
}
|
|
251
274
|
const field = FieldRun.create({
|
|
252
275
|
propertyPath,
|
|
253
276
|
propertyHost,
|
|
277
|
+
formatOptions,
|
|
254
278
|
});
|
|
255
279
|
const context = createUpdateContext(propertyHost.elementId, imodel, deletedDependency);
|
|
256
280
|
return context.getProperty(field);
|
|
@@ -354,7 +378,10 @@ describe("Field evaluation", () => {
|
|
|
354
378
|
}
|
|
355
379
|
it("deduces type for primitive properties", () => {
|
|
356
380
|
const propertyHost = { elementId: sourceElementId, schemaName: "Fields", className: "TestElement" };
|
|
357
|
-
|
|
381
|
+
// "quantity" here is a routing decision, not a claim that this count has units: intProp
|
|
382
|
+
// names no KindOfQuantity, so it resolves no format and still renders as a bare "100".
|
|
383
|
+
// Classifying it this way is what lets a field supply its own KindOfQuantity to format it.
|
|
384
|
+
expect(getPropertyType(propertyHost, "intProp")).to.equal("quantity");
|
|
358
385
|
expect(getPropertyType(propertyHost, "point")).to.equal("coordinate");
|
|
359
386
|
expect(getPropertyType(propertyHost, { propertyName: "strings", accessors: [0] })).to.equal("string");
|
|
360
387
|
expect(getPropertyType(propertyHost, "intEnum")).to.equal("int-enum");
|
|
@@ -365,6 +392,15 @@ describe("Field evaluation", () => {
|
|
|
365
392
|
expect(getPropertyType(propertyHost, "LastMod")).to.equal("datetime");
|
|
366
393
|
expect(getPropertyType(propertyHost, "FederationGuid")).to.equal("string");
|
|
367
394
|
});
|
|
395
|
+
it("deduces the type of an array leaf from the schema rather than from the value", () => {
|
|
396
|
+
// Regression: the type used to be inferred by sniffing `typeof value` whenever the
|
|
397
|
+
// terminal EC property was a String primitive reached through accessors. Since a String
|
|
398
|
+
// *array* property also reports isPrimitive(), legitimate array leaves took the
|
|
399
|
+
// JSON-in-string branch and lost their schema-declared extended type.
|
|
400
|
+
const propertyHost = { elementId: sourceElementId, schemaName: "Fields", className: "TestElement" };
|
|
401
|
+
expect(getPropertyType(propertyHost, { propertyName: "strings", accessors: [0] })).to.equal("string");
|
|
402
|
+
expect(getPropertyType(propertyHost, { propertyName: "dateStrings", accessors: [0] })).to.equal("datetime");
|
|
403
|
+
});
|
|
368
404
|
it("returns undefined for non-primitive properties", () => {
|
|
369
405
|
const propertyHost = { elementId: sourceElementId, schemaName: "Fields", className: "TestElement" };
|
|
370
406
|
expect(getPropertyType(propertyHost, "outerStruct")).to.equal(undefined);
|
|
@@ -427,6 +463,162 @@ describe("Field evaluation", () => {
|
|
|
427
463
|
expect(fieldRun2.cachedContent).to.equal("a");
|
|
428
464
|
});
|
|
429
465
|
});
|
|
466
|
+
/** Drives the production format path for a hand-built [[ResolvedFieldValue]]: `updateField` builds a
|
|
467
|
+
* [FormatterSpec]($core-quantity) from the iModel's schema formats and hands `formatFieldValue`
|
|
468
|
+
* a magnitude callback bound to it. Returns the resulting cached content.
|
|
469
|
+
*
|
|
470
|
+
* Going through `updateField` rather than reimplementing the composition here is the point --
|
|
471
|
+
* these tests pin how the backend wires spec lookup to formatting, so a change to that wiring
|
|
472
|
+
* fails them. `onMiss` fires when the field produced candidates but none resolved.
|
|
473
|
+
*/
|
|
474
|
+
function formatThroughSchema(value, options, onMiss) {
|
|
475
|
+
const field = FieldRun.create({
|
|
476
|
+
propertyHost: { elementId: "0x1", schemaName: "Fields", className: "TestElement" },
|
|
477
|
+
propertyPath: { propertyName: "unused" },
|
|
478
|
+
formatOptions: options,
|
|
479
|
+
});
|
|
480
|
+
const context = {
|
|
481
|
+
hostElementId: undefined,
|
|
482
|
+
getProperty: () => value,
|
|
483
|
+
formatting: createFieldFormatting({ iModel: imodel }),
|
|
484
|
+
};
|
|
485
|
+
const logWarning = sinon.stub(Logger, "logWarning").callsFake((_category, message) => {
|
|
486
|
+
if (message.startsWith("No format resolved for text annotation field"))
|
|
487
|
+
onMiss?.();
|
|
488
|
+
});
|
|
489
|
+
try {
|
|
490
|
+
updateField(field, context);
|
|
491
|
+
}
|
|
492
|
+
finally {
|
|
493
|
+
logWarning.restore();
|
|
494
|
+
}
|
|
495
|
+
return field.cachedContent;
|
|
496
|
+
}
|
|
497
|
+
describe("JSON-in-string properties", () => {
|
|
498
|
+
// `JsonProperties` is a String column of extended type `Json`; `jsonAccessors` index into the
|
|
499
|
+
// parsed JSON, so none of these paths have an EC property behind the leaf.
|
|
500
|
+
function jsonPath(...jsonAccessors) {
|
|
501
|
+
return { propertyName: "JsonProperties", jsonAccessors };
|
|
502
|
+
}
|
|
503
|
+
function evaluateJson(accessors, formatOptions) {
|
|
504
|
+
return evaluateField(jsonPath(...accessors), sourceElementId, false, formatOptions);
|
|
505
|
+
}
|
|
506
|
+
it("indexes into a deserialized JSON object", () => {
|
|
507
|
+
expect(evaluateJson(["stringProp"])?.value).to.equal("abc");
|
|
508
|
+
expect(evaluateJson(["bool"])?.value).to.equal(true);
|
|
509
|
+
expect(evaluateJson(["zoo", "address", "zipcode"])?.value).to.equal(12345);
|
|
510
|
+
});
|
|
511
|
+
it("indexes into a deserialized JSON array, including negative indices", () => {
|
|
512
|
+
expect(evaluateJson(["ints", 0])?.value).to.equal(10);
|
|
513
|
+
expect(evaluateJson(["ints", 3])?.value).to.equal(13);
|
|
514
|
+
expect(evaluateJson(["ints", -1])?.value).to.equal(13);
|
|
515
|
+
expect(evaluateJson(["zoo", "birds", 1, "sound"])?.value).to.equal("scree!");
|
|
516
|
+
});
|
|
517
|
+
it("returns undefined for a JSON null leaf rather than an unformattable value", () => {
|
|
518
|
+
// A JSON null is not a FieldPrimitiveValue. Producing one here would yield a
|
|
519
|
+
// `{ value: null, type: "string" }` FieldValue whose formatter called `null.toString()`.
|
|
520
|
+
expect(evaluateJson(["nullProp"])).to.be.undefined;
|
|
521
|
+
expect(evaluateJson(["readings", 1])).to.be.undefined;
|
|
522
|
+
});
|
|
523
|
+
it("does not throw when a field resolves to a JSON null", () => {
|
|
524
|
+
const fieldRun = FieldRun.create({
|
|
525
|
+
propertyHost: { elementId: sourceElementId, schemaName: "Fields", className: "TestElement" },
|
|
526
|
+
propertyPath: jsonPath("nullProp"),
|
|
527
|
+
cachedContent: "stale",
|
|
528
|
+
});
|
|
529
|
+
const context = createUpdateContext(sourceElementId, imodel, false);
|
|
530
|
+
expect(() => updateField(fieldRun, context)).to.not.throw();
|
|
531
|
+
expect(fieldRun.cachedContent).to.equal(FieldRun.invalidContentIndicator);
|
|
532
|
+
});
|
|
533
|
+
it("returns undefined for a missing key or an out-of-range index", () => {
|
|
534
|
+
expect(evaluateJson(["nope"])).to.be.undefined;
|
|
535
|
+
expect(evaluateJson(["toString"])).to.be.undefined;
|
|
536
|
+
expect(evaluateJson(["zoo", "address", "street"])).to.be.undefined;
|
|
537
|
+
expect(evaluateJson(["ints", 4])).to.be.undefined;
|
|
538
|
+
expect(evaluateJson(["ints", -5])).to.be.undefined;
|
|
539
|
+
});
|
|
540
|
+
it("returns undefined when the path stops on a JSON object or array", () => {
|
|
541
|
+
expect(evaluateJson(["zoo"])).to.be.undefined;
|
|
542
|
+
expect(evaluateJson(["ints"])).to.be.undefined;
|
|
543
|
+
});
|
|
544
|
+
it("returns undefined when indexing past a JSON scalar", () => {
|
|
545
|
+
expect(evaluateJson(["stringProp", "more"])).to.be.undefined;
|
|
546
|
+
expect(evaluateJson(["bool", 0])).to.be.undefined;
|
|
547
|
+
});
|
|
548
|
+
it("keeps the raw string when the property is not indexed", () => {
|
|
549
|
+
const value = evaluateField({ propertyName: "JsonProperties" }, sourceElementId);
|
|
550
|
+
expect(value?.type).to.equal("string");
|
|
551
|
+
expect(value?.value).to.be.a("string").and.to.contain("stringProp");
|
|
552
|
+
});
|
|
553
|
+
it("does not treat plain accessors as JSON accessors", () => {
|
|
554
|
+
// A String property cannot be walked with `accessors`; only `jsonAccessors` continue into the JSON.
|
|
555
|
+
expect(evaluateField({ propertyName: "JsonProperties", accessors: ["stringProp"] }, sourceElementId)).to.be.undefined;
|
|
556
|
+
});
|
|
557
|
+
it("indexes into a JSON property reached through accessors", () => {
|
|
558
|
+
const path = (...jsonAccessors) => ({
|
|
559
|
+
propertyName: "outerStruct",
|
|
560
|
+
accessors: ["innerStruct", "json"],
|
|
561
|
+
jsonAccessors,
|
|
562
|
+
});
|
|
563
|
+
expect(evaluateField(path("contactInfo", "email"), sourceElementId)?.value).to.equal("duck@pond.example");
|
|
564
|
+
expect(evaluateField(path("contactInfo", "phoneNumbers", 0, "areaCode"), sourceElementId)?.value).to.equal(555);
|
|
565
|
+
expect(evaluateField(path("contactInfo", "missing"), sourceElementId)).to.be.undefined;
|
|
566
|
+
});
|
|
567
|
+
it("requires the property to have the Json extended type", () => {
|
|
568
|
+
const path = {
|
|
569
|
+
propertyName: "outerStruct",
|
|
570
|
+
accessors: ["innerStruct", "notJson"],
|
|
571
|
+
jsonAccessors: ["contactInfo", "email"],
|
|
572
|
+
};
|
|
573
|
+
expect(evaluateField(path, sourceElementId)).to.be.undefined;
|
|
574
|
+
// Without jsonAccessors the plain string still resolves to itself.
|
|
575
|
+
expect(evaluateField({ ...path, jsonAccessors: undefined }, sourceElementId)?.value).to.be.a("string").and.to.contain("not-indexable");
|
|
576
|
+
});
|
|
577
|
+
it("ignores an empty jsonAccessors array", () => {
|
|
578
|
+
const value = evaluateField({ propertyName: "JsonProperties", jsonAccessors: [] }, sourceElementId);
|
|
579
|
+
expect(value?.type).to.equal("string");
|
|
580
|
+
});
|
|
581
|
+
it("types string and boolean leaves from the parsed JSON", () => {
|
|
582
|
+
expect(evaluateJson(["stringProp"])?.type).to.equal("string");
|
|
583
|
+
expect(evaluateJson(["bool"])?.type).to.equal("boolean");
|
|
584
|
+
expect(evaluateJson(["readings", 2])?.type).to.equal("string");
|
|
585
|
+
});
|
|
586
|
+
it("types a numeric leaf as a quantity", () => {
|
|
587
|
+
const value = evaluateJson(["lengthMeters"]);
|
|
588
|
+
expect(value?.type).to.equal("quantity");
|
|
589
|
+
expect(value?.value).to.equal(2.5);
|
|
590
|
+
});
|
|
591
|
+
it("renders a numeric leaf as its raw value", () => {
|
|
592
|
+
const fieldRun = FieldRun.create({
|
|
593
|
+
propertyHost: { elementId: sourceElementId, schemaName: "Fields", className: "TestElement" },
|
|
594
|
+
propertyPath: jsonPath("lengthMeters"),
|
|
595
|
+
});
|
|
596
|
+
const context = createUpdateContext(sourceElementId, imodel, false);
|
|
597
|
+
expect(updateField(fieldRun, context)).to.be.true;
|
|
598
|
+
expect(fieldRun.cachedContent).to.equal("2.5");
|
|
599
|
+
});
|
|
600
|
+
it("carries no property-side KoQ for a numeric leaf", () => {
|
|
601
|
+
// There is no EC property behind a JSON leaf, so the only pair the formatter can build is
|
|
602
|
+
// the one the field supplies.
|
|
603
|
+
const value = evaluateJson(["lengthMeters"], { quantity: { kindOfQuantity: "AecUnits.LENGTH", persistenceUnit: "Units.M" } });
|
|
604
|
+
expect(value?.type).to.equal("quantity");
|
|
605
|
+
expect(value?.value).to.equal(2.5);
|
|
606
|
+
expect(value?.kindOfQuantityFullName).to.be.undefined;
|
|
607
|
+
expect(value?.persistenceUnitFullName).to.be.undefined;
|
|
608
|
+
});
|
|
609
|
+
it("renders a numeric leaf as its raw value when the field supplies an incomplete key", () => {
|
|
610
|
+
// "quantity" with no resolvable (KoQ, unit) pair is indistinguishable from the plain string
|
|
611
|
+
// rendering, and logs no warning.
|
|
612
|
+
let missed = false;
|
|
613
|
+
for (const quantity of [undefined, { kindOfQuantity: "Fields.LENGTH" }, { persistenceUnit: "Units.M" }]) {
|
|
614
|
+
const options = quantity ? { quantity } : undefined;
|
|
615
|
+
const value = evaluateJson(["lengthMeters"], options);
|
|
616
|
+
expect(value?.type).to.equal("quantity");
|
|
617
|
+
expect(formatThroughSchema(value, options, () => { missed = true; })).to.equal("2.5");
|
|
618
|
+
}
|
|
619
|
+
expect(missed, "an unformattable JSON leaf is not an unresolved requirement").to.be.false;
|
|
620
|
+
});
|
|
621
|
+
});
|
|
430
622
|
function createAnnotationElement(textBlock) {
|
|
431
623
|
const elem = TextAnnotation3d.fromJSON({
|
|
432
624
|
model,
|
|
@@ -450,19 +642,21 @@ describe("Field evaluation", () => {
|
|
|
450
642
|
return withEditTxn(imodel, (txn) => txn.insertElement(elem.toJSON()));
|
|
451
643
|
}
|
|
452
644
|
describe("ElementDrivesTextAnnotation", () => {
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
645
|
+
/** Counts relationships targeting `targetId`. Deliberately requires a target: a global count
|
|
646
|
+
* would silently depend on cleanup performed by other describe blocks.
|
|
647
|
+
*/
|
|
648
|
+
async function expectNumRelationships(expected, targetId) {
|
|
649
|
+
const ecsql = `SELECT COUNT(*) FROM BisCore.ElementDrivesTextAnnotation WHERE TargetECInstanceId=${targetId}`;
|
|
650
|
+
const reader = imodel.createQueryReader(ecsql);
|
|
651
|
+
expect(await reader.step()).to.be.true;
|
|
652
|
+
expect(reader.current[0]).to.equal(expected);
|
|
461
653
|
}
|
|
462
|
-
it("can be inserted", () => {
|
|
463
|
-
expectNumRelationships(0);
|
|
654
|
+
it("can be inserted", async () => {
|
|
464
655
|
const targetId = insertAnnotationElement(undefined);
|
|
465
656
|
expect(targetId).not.to.equal(Id64.invalid);
|
|
657
|
+
// Scoped to this target rather than the whole iModel: other describe blocks insert
|
|
658
|
+
// annotations of their own, so a global count would couple this test to their cleanup.
|
|
659
|
+
await expectNumRelationships(0, targetId);
|
|
466
660
|
const target = imodel.elements.getElement(targetId);
|
|
467
661
|
expect(target.classFullName).to.equal("BisCore:TextAnnotation3d");
|
|
468
662
|
expect(target).instanceof(TextAnnotation3d);
|
|
@@ -471,7 +665,7 @@ describe("Field evaluation", () => {
|
|
|
471
665
|
const rel = ElementDrivesTextAnnotation.create(imodel, sourceElementId, targetId);
|
|
472
666
|
const relId = withEditTxn(imodel, (txn) => txn.insertRelationship(rel.toJSON()));
|
|
473
667
|
expect(relId).not.to.equal(Id64.invalid);
|
|
474
|
-
expectNumRelationships(1);
|
|
668
|
+
await expectNumRelationships(1, targetId);
|
|
475
669
|
const relationship = imodel.relationships.getInstance("BisCore:ElementDrivesTextAnnotation", relId);
|
|
476
670
|
expect(relationship.sourceId).to.equal(sourceElementId);
|
|
477
671
|
expect(relationship.targetId).to.equal(targetId);
|
|
@@ -488,37 +682,37 @@ describe("Field evaluation", () => {
|
|
|
488
682
|
});
|
|
489
683
|
}
|
|
490
684
|
describe("updateFieldDependencies", () => {
|
|
491
|
-
it("creates exactly one relationship for each unique source element on insert and update", () => {
|
|
685
|
+
it("creates exactly one relationship for each unique source element on insert and update", async () => {
|
|
492
686
|
const source1 = withEditTxn(imodel, (editTxn) => insertTestElement(editTxn, model, category));
|
|
493
687
|
const block = TextBlock.create();
|
|
494
688
|
block.appendRun(createField(source1, "1"));
|
|
495
689
|
const targetId = insertAnnotationElement(block);
|
|
496
|
-
expectNumRelationships(1, targetId);
|
|
690
|
+
await expectNumRelationships(1, targetId);
|
|
497
691
|
const source2 = withEditTxn(imodel, (editTxn) => insertTestElement(editTxn, model, category));
|
|
498
692
|
const target = imodel.elements.getElement(targetId);
|
|
499
693
|
const anno = target.getAnnotation();
|
|
500
694
|
anno.textBlock.appendRun(createField(source2, "2a"));
|
|
501
695
|
target.setAnnotation(anno);
|
|
502
696
|
withEditTxn(imodel, (txn) => target.update(txn));
|
|
503
|
-
expectNumRelationships(2, targetId);
|
|
697
|
+
await expectNumRelationships(2, targetId);
|
|
504
698
|
anno.textBlock.appendRun(createField(source2, "2b"));
|
|
505
699
|
target.setAnnotation(anno);
|
|
506
700
|
withEditTxn(imodel, (txn) => target.update(txn));
|
|
507
|
-
expectNumRelationships(2, targetId);
|
|
701
|
+
await expectNumRelationships(2, targetId);
|
|
508
702
|
const source3 = withEditTxn(imodel, (editTxn) => insertTestElement(editTxn, model, category));
|
|
509
703
|
anno.textBlock.appendRun(createField(source3, "3"));
|
|
510
704
|
target.setAnnotation(anno);
|
|
511
705
|
withEditTxn(imodel, (txn) => target.update(txn));
|
|
512
|
-
expectNumRelationships(3, targetId);
|
|
706
|
+
await expectNumRelationships(3, targetId);
|
|
513
707
|
});
|
|
514
|
-
it("deletes stale relationships", () => {
|
|
708
|
+
it("deletes stale relationships", async () => {
|
|
515
709
|
const sourceA = withEditTxn(imodel, (editTxn) => insertTestElement(editTxn, model, category));
|
|
516
710
|
const sourceB = withEditTxn(imodel, (editTxn) => insertTestElement(editTxn, model, category));
|
|
517
711
|
const block = TextBlock.create();
|
|
518
712
|
block.appendRun(createField(sourceA, "A"));
|
|
519
713
|
block.appendRun(createField(sourceB, "B"));
|
|
520
714
|
const targetId = insertAnnotationElement(block);
|
|
521
|
-
expectNumRelationships(2, targetId);
|
|
715
|
+
await expectNumRelationships(2, targetId);
|
|
522
716
|
expect(imodel.relationships.tryGetInstance(ElementDrivesTextAnnotation.classFullName, { targetId, sourceId: sourceA })).not.to.be.undefined;
|
|
523
717
|
expect(imodel.relationships.tryGetInstance(ElementDrivesTextAnnotation.classFullName, { targetId, sourceId: sourceB })).not.to.be.undefined;
|
|
524
718
|
const target = imodel.elements.getElement(targetId);
|
|
@@ -528,14 +722,14 @@ describe("Field evaluation", () => {
|
|
|
528
722
|
p1.children.shift();
|
|
529
723
|
target.setAnnotation(anno);
|
|
530
724
|
withEditTxn(imodel, (txn) => target.update(txn));
|
|
531
|
-
expectNumRelationships(1, targetId);
|
|
725
|
+
await expectNumRelationships(1, targetId);
|
|
532
726
|
expect(imodel.relationships.tryGetInstance(ElementDrivesTextAnnotation.classFullName, { targetId, sourceId: sourceA })).to.be.undefined;
|
|
533
727
|
expect(imodel.relationships.tryGetInstance(ElementDrivesTextAnnotation.classFullName, { targetId, sourceId: sourceB })).not.to.be.undefined;
|
|
534
728
|
anno.textBlock.children.length = 0;
|
|
535
729
|
anno.textBlock.appendRun(createField(sourceA, "A2"));
|
|
536
730
|
target.setAnnotation(anno);
|
|
537
731
|
withEditTxn(imodel, (txn) => target.update(txn));
|
|
538
|
-
expectNumRelationships(1, targetId);
|
|
732
|
+
await expectNumRelationships(1, targetId);
|
|
539
733
|
expect(imodel.relationships.tryGetInstance(ElementDrivesTextAnnotation.classFullName, { targetId, sourceId: sourceA })).not.to.be.undefined;
|
|
540
734
|
expect(imodel.relationships.tryGetInstance(ElementDrivesTextAnnotation.classFullName, { targetId, sourceId: sourceB })).to.be.undefined;
|
|
541
735
|
anno.textBlock.children.length = 0;
|
|
@@ -545,18 +739,18 @@ describe("Field evaluation", () => {
|
|
|
545
739
|
}));
|
|
546
740
|
target.setAnnotation(anno);
|
|
547
741
|
withEditTxn(imodel, (txn) => target.update(txn));
|
|
548
|
-
expectNumRelationships(0, targetId);
|
|
742
|
+
await expectNumRelationships(0, targetId);
|
|
549
743
|
expect(imodel.relationships.tryGetInstance(ElementDrivesTextAnnotation.classFullName, { targetId, sourceId: sourceA })).to.be.undefined;
|
|
550
744
|
expect(imodel.relationships.tryGetInstance(ElementDrivesTextAnnotation.classFullName, { targetId, sourceId: sourceB })).to.be.undefined;
|
|
551
745
|
});
|
|
552
|
-
it("ignores invalid source element Ids", () => {
|
|
746
|
+
it("ignores invalid source element Ids", async () => {
|
|
553
747
|
const source = withEditTxn(imodel, (editTxn) => insertTestElement(editTxn, model, category));
|
|
554
748
|
const block = TextBlock.create();
|
|
555
749
|
block.appendRun(createField(Id64.invalid, "invalid"));
|
|
556
750
|
block.appendRun(createField("0xbaadf00d", "non-existent"));
|
|
557
751
|
block.appendRun(createField(source, "valid"));
|
|
558
752
|
const targetId = insertAnnotationElement(block);
|
|
559
|
-
expectNumRelationships(1, targetId);
|
|
753
|
+
await expectNumRelationships(1, targetId);
|
|
560
754
|
});
|
|
561
755
|
});
|
|
562
756
|
function expectText(expected, elemId, db) {
|