@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":"FieldFormat.test.js","sourceRoot":"","sources":["../../../../src/test/annotations/FieldFormat.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 * as sinon from \"sinon\";\r\nimport { Code, FieldRun, PhysicalElementProps, SubCategoryAppearance, TextBlock } from \"@itwin/core-common\";\r\nimport { FormatSet } from \"@itwin/ecschema-metadata\";\r\nimport { FormatterSpec } from \"@itwin/core-quantity\";\r\nimport { Id64String, Logger } from \"@itwin/core-bentley\";\r\nimport { Point3d, XYAndZ, 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 { ElementDrivesTextAnnotation } from \"../../annotations/ElementDrivesTextAnnotation\";\r\nimport { decimalFormat, toFormatSet } from \"../AnnotationTestUtils\";\r\nimport { withEditTxn } from \"../../EditTxn\";\r\n\r\n/**\r\n * How a [FieldRun]'s quantity format is resolved.\r\n *\r\n * Each test varies the three inputs on `formatOptions.quantity` -- `formatSet`,\r\n * `kindOfQuantity`, and `persistenceUnit` -- and applies them to properties chosen so that some\r\n * carry their own KindOfQuantity and some do not. That split is the dominant axis: a failed\r\n * override is silently rescued by the property-side candidate when the property carries a KoQ,\r\n * and only falls through to the raw string when it does not.\r\n *\r\n * Formats resolve in this order, and the tests below walk it from the bottom up: the FormatSet a\r\n * field names, then the FormatSet adopted for the iModel, then the schema's own presentation\r\n * format for the property's KindOfQuantity, then the raw value.\r\n *\r\n * Each test registers the FormatSets it depends on inline, so what produced a given string is\r\n * visible without cross-referencing a table.\r\n */\r\n\r\n// cspell: ignore koqs\r\n\r\nconst ADOPTED_SET = \"adopted-set\";\r\nconst ALT_SET = \"alternate-set\";\r\n/** Deliberately never registered, so lookup falls back to the adopted FormatSet. */\r\nconst UNREGISTERED_SET = \"unregistered-set\";\r\n\r\n// ---------------------------------------------------------------------------------------------\r\n// Test schema\r\n// ---------------------------------------------------------------------------------------------\r\n\r\n/**\r\n * Two families of KindOfQuantity live here:\r\n *\r\n * - `*_PROP` are attached to properties. They give the property-side candidate something to\r\n * resolve, and are left undefined by the FormatSets in most tests below, so a field that names\r\n * nothing exercises the schema. \"routes a field with no KindOfQuantity override through a\r\n * FormatSet keyed on the property's own KindOfQuantity\" is the deliberate exception.\r\n * - `SCHEMA_*` are declared but never attached. Tests name them to force the\r\n * FormatSet -> SchemaFormatsProvider fallthrough.\r\n *\r\n * Anything else a field names (`Example.LENGTH`, `Example.AREA`, ...) is defined by the FormatSet\r\n * that test registers, and exists nowhere in the schema.\r\n */\r\nconst exampleSchemaXml = `<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n<ECSchema schemaName=\"FieldExample\" alias=\"fex\" 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_PROP\" displayLabel=\"Length\" persistenceUnit=\"u:M\" relativeError=\"0.0001\" presentationUnits=\"f:DefaultRealU(4)[u:M]\"/>\r\n <KindOfQuantity typeName=\"AREA_PROP\" displayLabel=\"Area\" persistenceUnit=\"u:SQ_M\" relativeError=\"0.0001\" presentationUnits=\"f:DefaultRealU(4)[u:SQ_M]\"/>\r\n <KindOfQuantity typeName=\"SLOPE_PROP\" displayLabel=\"Slope\" persistenceUnit=\"u:M_PER_M\" relativeError=\"0.0001\" presentationUnits=\"f:DefaultRealU(4)[u:M_PER_M]\"/>\r\n\r\n <KindOfQuantity typeName=\"INT_LENGTH_PROP\" displayLabel=\"Integer Length\" persistenceUnit=\"u:MM\" relativeError=\"0.0001\" presentationUnits=\"f:DefaultRealU(4)[u:M]\"/>\r\n\r\n <Unit typeName=\"DOUBLE_M\" displayLabel=\"dm\" phenomenon=\"u:LENGTH\" unitSystem=\"u:SI\" definition=\"u:M\" numerator=\"2\"/>\r\n <KindOfQuantity typeName=\"CUSTOM_UNIT_PROP\" displayLabel=\"Custom-unit Length\" persistenceUnit=\"DOUBLE_M\" relativeError=\"0.0001\" presentationUnits=\"f:DefaultRealU(4)[u:M]\"/>\r\n\r\n <KindOfQuantity typeName=\"SCHEMA_LENGTH\" displayLabel=\"Schema Length\" persistenceUnit=\"u:M\" relativeError=\"0.0001\" presentationUnits=\"f:DefaultRealU(2)[u:M]\"/>\r\n <KindOfQuantity typeName=\"SCHEMA_AREA\" displayLabel=\"Schema Area\" persistenceUnit=\"u:SQ_M\" relativeError=\"0.0001\" presentationUnits=\"f:DefaultRealU(2)[u:SQ_M]\"/>\r\n <KindOfQuantity typeName=\"SCHEMA_ANGLE\" displayLabel=\"Schema Angle\" persistenceUnit=\"u:ARC_DEG\" relativeError=\"0.0001\" presentationUnits=\"f:DefaultRealU(2)[u:ARC_DEG]\"/>\r\n <KindOfQuantity typeName=\"SCHEMA_SLOPE\" displayLabel=\"Schema Slope\" persistenceUnit=\"u:M_PER_M\" relativeError=\"0.0001\" presentationUnits=\"f:DefaultRealU(2)[u:M_PER_M]\"/>\r\n <KindOfQuantity typeName=\"SCHEMA_RATIO\" displayLabel=\"Schema Ratio\" persistenceUnit=\"u:ONE\" relativeError=\"0.0001\" presentationUnits=\"f:DefaultRealU(2)[u:ONE]\"/>\r\n\r\n <ECEntityClass typeName=\"ExampleElement\" modifier=\"None\">\r\n <BaseClass>bis:PhysicalElement</BaseClass>\r\n <ECProperty propertyName=\"lengthProp\" typeName=\"double\" kindOfQuantity=\"LENGTH_PROP\"/>\r\n <ECProperty propertyName=\"areaProp\" typeName=\"double\" kindOfQuantity=\"AREA_PROP\"/>\r\n <ECProperty propertyName=\"slopeProp\" typeName=\"double\" kindOfQuantity=\"SLOPE_PROP\"/>\r\n <ECProperty propertyName=\"angleProp\" typeName=\"double\"/>\r\n <ECProperty propertyName=\"ratioProp\" typeName=\"double\"/>\r\n <ECProperty propertyName=\"point\" typeName=\"point3d\"/>\r\n <ECProperty propertyName=\"intLengthProp\" typeName=\"int\" kindOfQuantity=\"INT_LENGTH_PROP\"/>\r\n <ECProperty propertyName=\"intCountProp\" typeName=\"int\"/>\r\n <ECProperty propertyName=\"customUnitProp\" typeName=\"double\" kindOfQuantity=\"CUSTOM_UNIT_PROP\"/>\r\n </ECEntityClass>\r\n</ECSchema>\r\n`;\r\n\r\ninterface ExampleElementProps extends PhysicalElementProps {\r\n lengthProp: number;\r\n areaProp: number;\r\n slopeProp: number;\r\n angleProp: number;\r\n ratioProp: number;\r\n point: XYAndZ;\r\n intLengthProp: number;\r\n intCountProp: number;\r\n customUnitProp: number;\r\n}\r\n\r\nclass ExampleElement extends PhysicalElement {\r\n public static override get className() { return \"ExampleElement\"; }\r\n declare public lengthProp: number;\r\n declare public areaProp: number;\r\n declare public slopeProp: number;\r\n declare public angleProp: number;\r\n declare public ratioProp: number;\r\n declare public point: XYAndZ;\r\n declare public intLengthProp: number;\r\n declare public intCountProp: number;\r\n declare public customUnitProp: number;\r\n}\r\n\r\nclass FieldExampleSchema extends Schema {\r\n public static override get schemaName() { return \"FieldExample\"; }\r\n}\r\n\r\n// ---------------------------------------------------------------------------------------------\r\n// FormatSets\r\n// ---------------------------------------------------------------------------------------------\r\n\r\ntype QuantityOptions = NonNullable<NonNullable<FieldRun[\"formatOptions\"]>[\"quantity\"]>;\r\n\r\ndescribe(\"Field format resolution example\", () => {\r\n let imodel: StandaloneDb;\r\n let elementId: Id64String;\r\n\r\n before(async () => {\r\n const iModelPath = IModelTestUtils.prepareOutputFile(\"FieldFormatExample\", \"test.bim\");\r\n imodel = StandaloneDb.createEmpty(iModelPath, { rootSubject: { name: \"FieldFormatExample\" }, enableTransactions: true });\r\n\r\n if (!Schemas.getRegisteredSchema(\"FieldExample\")) {\r\n Schemas.registerSchema(FieldExampleSchema);\r\n ClassRegistry.register(ExampleElement, FieldExampleSchema);\r\n }\r\n await imodel.importSchemaStrings([exampleSchemaXml]);\r\n\r\n await withEditTxn(imodel, async (txn) => {\r\n const model = IModelTestUtils.createAndInsertPhysicalPartitionAndModel(txn, Code.createEmpty(), true)[1];\r\n const category = SpatialCategory.insert(txn, StandaloneDb.dictionaryId, \"FieldFormatExampleCategory\", new SubCategoryAppearance());\r\n\r\n const props: ExampleElementProps = {\r\n classFullName: \"FieldExample:ExampleElement\",\r\n model,\r\n category,\r\n code: Code.createEmpty(),\r\n // Seed values chosen to convert cleanly, so a wrong conversion is obvious by inspection.\r\n // Every test restates the properties it uses in a \"Persisted on the element:\" comment, so its\r\n // expected strings can be checked without scrolling back here. Changing a value below\r\n // means updating those comments too -- grep \"Persisted on the element\" to find them all.\r\n lengthProp: 2.5, // m -> 2500 mm, 250 cm, 8.202 ft\r\n areaProp: 100, // m2 -> 1076.391 ft2\r\n slopeProp: 0.01, // m/m -> 100 :1\r\n angleProp: 90, // deg -> 1.5708 rad\r\n ratioProp: 0.9, // one\r\n point: { x: 1, y: 2, z: 3 },\r\n intLengthProp: 2500, // mm, int-typed -> 2.5 m\r\n intCountProp: 42, // no KoQ; a count, not something with units\r\n customUnitProp: 1.25, // DOUBLE_M, a unit only this schema defines -> would be 2.5 m\r\n placement: { origin: new Point3d(0, 0, 0), angles: new YawPitchRollAngles() },\r\n };\r\n elementId = txn.insertElement(props);\r\n });\r\n });\r\n\r\n after(() => {\r\n imodel.close();\r\n });\r\n\r\n afterEach(() => {\r\n sinon.restore();\r\n logWarningSpy = undefined;\r\n });\r\n\r\n interface FormatWarning { elementId: string, propertyName: string, formatSet: string | undefined, tried: string[] }\r\n interface UnitOverrideWarning { elementId: string, propertyName: string, persistenceUnit: string, propertyPersistenceUnit: string }\r\n\r\n // Sinon refuses to wrap a method twice, so tests spying on both messages share one spy.\r\n // Reset alongside `sinon.restore()` in afterEach.\r\n let logWarningSpy: sinon.SinonSpy | undefined;\r\n\r\n function spyOnWarnings<T>(messagePrefix: string): () => T[] {\r\n const spy = logWarningSpy ??= sinon.spy(Logger, \"logWarning\");\r\n return () => spy.getCalls()\r\n .filter((call) => typeof call.args[1] === \"string\" && call.args[1].startsWith(messagePrefix))\r\n .map((call) => (call.args[2] as () => T)());\r\n }\r\n\r\n /** Spies on the warning [[render]] logs for each field it could not resolve a format for.\r\n * Returns the (KindOfQuantity, persistence unit) pairs those warnings report as tried.\r\n */\r\n function spyOnFormatWarnings(): () => FormatWarning[] {\r\n return spyOnWarnings<FormatWarning>(\"No format resolved for text annotation field\");\r\n }\r\n\r\n /** Warnings that a `persistenceUnit` override disagreed with the property's unit and was ignored. */\r\n function spyOnUnitOverrideWarnings(): () => UnitOverrideWarning[] {\r\n return spyOnWarnings<UnitOverrideWarning>(\"Ignoring persistenceUnit override\");\r\n }\r\n\r\n /** Appends a field on `propertyName` of the seeded element, and hands it back so the test can\r\n * read its content after [[render]].\r\n */\r\n function appendField(block: TextBlock, propertyName: string, quantity?: QuantityOptions): FieldRun {\r\n const field = FieldRun.create({\r\n propertyHost: { elementId, schemaName: \"FieldExample\", className: \"ExampleElement\" },\r\n propertyPath: { propertyName },\r\n formatOptions: quantity ? { quantity } : undefined,\r\n cachedContent: \"unevaluated\",\r\n });\r\n block.appendRun(field);\r\n return field;\r\n }\r\n\r\n /** Registers a provider with the given FormatSets, then evaluates `block`'s fields in place.\r\n * Each call replaces the previous registration, so a test's FormatSets never leak into the\r\n * next one.\r\n */\r\n function render(\r\n block: TextBlock,\r\n formatSets: { readonly adopted?: FormatSet, readonly byId?: ReadonlyArray<{ id: string, formatSet: FormatSet }> } = {},\r\n ): void {\r\n ElementDrivesTextAnnotation.registerFieldFormatting({\r\n iModel: imodel,\r\n formatSet: formatSets.adopted,\r\n formatSets: formatSets.byId,\r\n });\r\n\r\n ElementDrivesTextAnnotation.evaluateFields({ iModel: imodel, block });\r\n }\r\n\r\n it(\"formats integer-valued properties as quantities, leaving those that resolve no format raw\", async () => {\r\n // Classifying a numeric property as \"quantity\" only decides whether the KoQ/units pipeline is\r\n // consulted; it is not a claim that the value has units. A count that names no\r\n // KindOfQuantity anywhere resolves no spec and falls back to the same bare `toString()` the\r\n // \"string\" formatter would have produced, so the classification costs it nothing -- while\r\n // leaving the field-level override hatch open for callers who do declare one.\r\n //\r\n // Persisted on the element: intLengthProp 2500 (mm, int), intCountProp 42 (no KoQ)\r\n const block = TextBlock.create();\r\n const measured = appendField(block, \"intLengthProp\");\r\n const counted = appendField(block, \"intCountProp\");\r\n const overridden = appendField(block, \"intCountProp\", { kindOfQuantity: \"FieldExample.SCHEMA_LENGTH\", persistenceUnit: \"Units.M\" });\r\n\r\n render(block);\r\n\r\n // The property's own KoQ persists millimeters and presents meters: converted, not relabelled.\r\n expect(measured.cachedContent).to.equal(\"2.5 m\");\r\n\r\n // Nothing names a KindOfQuantity, so no spec resolves and the count renders with no stray unit.\r\n expect(counted.cachedContent).to.equal(\"42\");\r\n\r\n // Same property, but the field declares both halves of the override, which is the documented\r\n // way to format a property that carries no KindOfQuantity of its own.\r\n expect(overridden.cachedContent).to.equal(\"42.0 m\");\r\n });\r\n\r\n it(\"renders a field with no format options from the property's own KindOfQuantity, and raw where it has none\", async () => {\r\n // Persisted on the element: lengthProp 2.5 m, areaProp 100 m², slopeProp 0.01 m/m, angleProp 90°,\r\n // ratioProp 0.9 (dimensionless), point (1, 2, 3) m\r\n const block = TextBlock.create();\r\n const length = appendField(block, \"lengthProp\");\r\n const area = appendField(block, \"areaProp\");\r\n const slope = appendField(block, \"slopeProp\");\r\n const angle = appendField(block, \"angleProp\");\r\n const ratio = appendField(block, \"ratioProp\");\r\n const point = appendField(block, \"point\");\r\n\r\n render(block);\r\n\r\n // lengthProp, areaProp and slopeProp each declare a KindOfQuantity, so with no FormatSet\r\n // adopted the schema's own presentation format supplies the unit and precision.\r\n expect(length.cachedContent).to.equal(\"2.5 m\");\r\n expect(area.cachedContent).to.equal(\"100.0 m²\");\r\n expect(slope.cachedContent).to.equal(\"0.01 m/m\");\r\n\r\n // angleProp, ratioProp and point declare none, and the field names none either, so there is\r\n // nothing to resolve against and the persisted value is rendered as-is.\r\n expect(angle.cachedContent).to.equal(\"90\");\r\n expect(ratio.cachedContent).to.equal(\"0.9\");\r\n expect(point.cachedContent).to.equal(\"(1, 2, 3)\");\r\n });\r\n\r\n it(\"resolves a schema-defined KindOfQuantity for every property when the field supplies both the format and the unit\", async () => {\r\n // The only row in this group that resolves anything the baseline did not. Naming a KoQ the\r\n // schema declares but no FormatSet defines reaches the SchemaFormatsProvider, and because the\r\n // field also names the persistence unit, the properties that carry no KoQ of their own\r\n // resolve too -- both halves of the key came from the field.\r\n // Persisted on the element: lengthProp 2.5 m, areaProp 100 m², slopeProp 0.01 m/m, angleProp 90°,\r\n // ratioProp 0.9 (dimensionless), point (1, 2, 3) m\r\n const block = TextBlock.create();\r\n const length = appendField(block, \"lengthProp\", { kindOfQuantity: \"FieldExample.SCHEMA_LENGTH\", persistenceUnit: \"Units.M\" });\r\n const area = appendField(block, \"areaProp\", { kindOfQuantity: \"FieldExample.SCHEMA_AREA\", persistenceUnit: \"Units.SQ_M\" });\r\n const slope = appendField(block, \"slopeProp\", { kindOfQuantity: \"FieldExample.SCHEMA_SLOPE\", persistenceUnit: \"Units.M_PER_M\" });\r\n const angle = appendField(block, \"angleProp\", { kindOfQuantity: \"FieldExample.SCHEMA_ANGLE\", persistenceUnit: \"Units.ARC_DEG\" });\r\n const ratio = appendField(block, \"ratioProp\", { kindOfQuantity: \"FieldExample.SCHEMA_RATIO\", persistenceUnit: \"Units.ONE\" });\r\n const point = appendField(block, \"point\", { kindOfQuantity: \"FieldExample.SCHEMA_LENGTH\", persistenceUnit: \"Units.M\" });\r\n\r\n render(block);\r\n\r\n // The SCHEMA_* KoQs declare the same units as the properties do, at precision 2 rather than 4,\r\n // so the KoQ-bearing properties look unchanged from the baseline.\r\n expect(length.cachedContent).to.equal(\"2.5 m\");\r\n expect(area.cachedContent).to.equal(\"100.0 m²\");\r\n expect(slope.cachedContent).to.equal(\"0.01 m/m\");\r\n\r\n // These three are the ones that moved: raw in the baseline, formatted here.\r\n expect(angle.cachedContent).to.equal(\"90.0 °\");\r\n expect(ratio.cachedContent).to.equal(\"0.9 one\");\r\n expect(point.cachedContent).to.equal(\"(1.0 m, 2.0 m, 3.0 m)\");\r\n });\r\n\r\n it(\"ignores a KindOfQuantity named without a persistence unit unless the property supplies one\", async () => {\r\n // Same KoQs as above with the unit leg dropped. A format alone cannot be bound to a value --\r\n // something has to say what unit the persisted number is in. The property's own KoQ answers\r\n // that for the first three; nothing answers it for the rest.\r\n // Persisted on the element: lengthProp 2.5 m, areaProp 100 m², slopeProp 0.01 m/m, angleProp 90°,\r\n // ratioProp 0.9 (dimensionless), point (1, 2, 3) m\r\n const block = TextBlock.create();\r\n const length = appendField(block, \"lengthProp\", { kindOfQuantity: \"FieldExample.SCHEMA_LENGTH\" });\r\n const area = appendField(block, \"areaProp\", { kindOfQuantity: \"FieldExample.SCHEMA_AREA\" });\r\n const slope = appendField(block, \"slopeProp\", { kindOfQuantity: \"FieldExample.SCHEMA_SLOPE\" });\r\n const angle = appendField(block, \"angleProp\", { kindOfQuantity: \"FieldExample.SCHEMA_ANGLE\" });\r\n const ratio = appendField(block, \"ratioProp\", { kindOfQuantity: \"FieldExample.SCHEMA_RATIO\" });\r\n const point = appendField(block, \"point\", { kindOfQuantity: \"FieldExample.SCHEMA_LENGTH\" });\r\n\r\n render(block);\r\n\r\n expect(length.cachedContent).to.equal(\"2.5 m\");\r\n expect(area.cachedContent).to.equal(\"100.0 m²\");\r\n expect(slope.cachedContent).to.equal(\"0.01 m/m\");\r\n\r\n // Back to raw, unlike the test above -- dropping the unit cost these three their formatting.\r\n expect(angle.cachedContent).to.equal(\"90\");\r\n expect(ratio.cachedContent).to.equal(\"0.9\");\r\n expect(point.cachedContent).to.equal(\"(1, 2, 3)\");\r\n });\r\n\r\n it(\"treats an empty persistenceUnit like an absent one\", async () => {\r\n // `persistenceUnit: \"\"` used to silently discard the kindOfQuantity override.\r\n // Persisted on the element: lengthProp 2.5 m\r\n const block = TextBlock.create();\r\n const emptyUnit = appendField(block, \"lengthProp\", { kindOfQuantity: \"FieldExample.SCHEMA_LENGTH\", persistenceUnit: \"\" });\r\n\r\n render(block);\r\n\r\n expect(emptyUnit.cachedContent).to.equal(\"2.5 m\");\r\n });\r\n\r\n it(\"accepts any case and either separator for names the iModel's schemas define\", async () => {\r\n // Override names are normalized to the schema's spelling before anything compares or looks\r\n // them up. Without that, `units:m` on a meters property would be flagged as disagreeing with\r\n // `Units.M`, and `fieldexample:schema_angle` would fail the exact-spelling unit lookup.\r\n // Persisted on the element: lengthProp 2.5 m, angleProp 90° (no KindOfQuantity)\r\n const block = TextBlock.create();\r\n const respelledUnit = appendField(block, \"lengthProp\", { persistenceUnit: \"units:m\" });\r\n const respelledBoth = appendField(block, \"lengthProp\", { kindOfQuantity: \"fieldexample:schema_length\", persistenceUnit: \"UNITS.M\" });\r\n const respelledKoq = appendField(block, \"angleProp\", { kindOfQuantity: \"fieldexample:schema_angle\", persistenceUnit: \"units:arc_deg\" });\r\n\r\n const ignored = spyOnUnitOverrideWarnings();\r\n render(block);\r\n\r\n expect(respelledUnit.cachedContent).to.equal(\"2.5 m\");\r\n expect(respelledBoth.cachedContent).to.equal(\"2.5 m\");\r\n expect(respelledKoq.cachedContent).to.equal(\"90.0 °\");\r\n expect(ignored()).to.be.empty;\r\n });\r\n\r\n it(\"ignores a persistence unit named without a KindOfQuantity\", async () => {\r\n // The mirror image: a unit with no format to apply to it. Nothing names a format, so the\r\n // property-side candidate is all that is left and the result is the baseline exactly.\r\n // Persisted on the element: lengthProp 2.5 m, areaProp 100 m², slopeProp 0.01 m/m, angleProp 90°,\r\n // ratioProp 0.9 (dimensionless), point (1, 2, 3) m\r\n const block = TextBlock.create();\r\n const length = appendField(block, \"lengthProp\", { persistenceUnit: \"Units.M\" });\r\n const area = appendField(block, \"areaProp\", { persistenceUnit: \"Units.SQ_M\" });\r\n const slope = appendField(block, \"slopeProp\", { persistenceUnit: \"Units.M_PER_M\" });\r\n const angle = appendField(block, \"angleProp\", { persistenceUnit: \"Units.ARC_DEG\" });\r\n const ratio = appendField(block, \"ratioProp\", { persistenceUnit: \"Units.ONE\" });\r\n const point = appendField(block, \"point\", { persistenceUnit: \"Units.M\" });\r\n\r\n render(block);\r\n\r\n expect(length.cachedContent).to.equal(\"2.5 m\");\r\n expect(area.cachedContent).to.equal(\"100.0 m²\");\r\n expect(slope.cachedContent).to.equal(\"0.01 m/m\");\r\n expect(angle.cachedContent).to.equal(\"90\");\r\n expect(ratio.cachedContent).to.equal(\"0.9\");\r\n expect(point.cachedContent).to.equal(\"(1, 2, 3)\");\r\n });\r\n\r\n it(\"logs a warning when the persistence unit override does not exist\", async () => {\r\n // The format leg is fine and the unit leg is garbage. A property that carries its own unit\r\n // ignores the override (and says so), so its KoQ override still formats. A property with no\r\n // unit of its own has nothing else to try, so it goes raw and the shortfall is logged.\r\n // Persisted on the element: lengthProp 2.5 m, areaProp 100 m², slopeProp 0.01 m/m, angleProp 90°,\r\n // ratioProp 0.9 (dimensionless), point (1, 2, 3) m\r\n const block = TextBlock.create();\r\n const length = appendField(block, \"lengthProp\", { kindOfQuantity: \"FieldExample.SCHEMA_LENGTH\", persistenceUnit: \"Units.NOT_A_UNIT\" });\r\n const area = appendField(block, \"areaProp\", { kindOfQuantity: \"FieldExample.SCHEMA_AREA\", persistenceUnit: \"Units.NOT_A_UNIT\" });\r\n const slope = appendField(block, \"slopeProp\", { kindOfQuantity: \"FieldExample.SCHEMA_SLOPE\", persistenceUnit: \"Units.NOT_A_UNIT\" });\r\n const angle = appendField(block, \"angleProp\", { kindOfQuantity: \"FieldExample.SCHEMA_ANGLE\", persistenceUnit: \"Units.NOT_A_UNIT\" });\r\n const ratio = appendField(block, \"ratioProp\", { kindOfQuantity: \"FieldExample.SCHEMA_RATIO\", persistenceUnit: \"Units.NOT_A_UNIT\" });\r\n const point = appendField(block, \"point\", { kindOfQuantity: \"FieldExample.SCHEMA_LENGTH\", persistenceUnit: \"Units.NOT_A_UNIT\" });\r\n\r\n const warnings = spyOnFormatWarnings();\r\n const ignored = spyOnUnitOverrideWarnings();\r\n render(block);\r\n\r\n expect(length.cachedContent).to.equal(\"2.5 m\");\r\n expect(area.cachedContent).to.equal(\"100.0 m²\");\r\n expect(slope.cachedContent).to.equal(\"0.01 m/m\");\r\n expect(angle.cachedContent).to.equal(\"90\");\r\n expect(ratio.cachedContent).to.equal(\"0.9\");\r\n expect(point.cachedContent).to.equal(\"(1, 2, 3)\");\r\n\r\n expect(ignored().map((w) => w.propertyName)).to.deep.equal([\"lengthProp\", \"areaProp\", \"slopeProp\"]);\r\n expect(ignored()[0]).to.deep.equal({\r\n elementId,\r\n propertyName: \"lengthProp\",\r\n persistenceUnit: \"Units.NOT_A_UNIT\",\r\n propertyPersistenceUnit: \"Units.M\",\r\n });\r\n\r\n expect(warnings()).to.have.length(3);\r\n expect(warnings().every((w) => w.tried.some((t) => t.endsWith(\" in Units.NOT_A_UNIT\")))).to.be.true;\r\n // The metadata identifies the field so a host can find it, and lists exactly what was tried.\r\n expect(warnings()[0]).to.deep.equal({\r\n elementId,\r\n propertyName: \"angleProp\",\r\n formatSet: undefined,\r\n tried: [\"FieldExample.SCHEMA_ANGLE in Units.NOT_A_UNIT\"],\r\n });\r\n });\r\n\r\n it(\"ignores a valid persistence-unit override that disagrees with the property's own unit\", async () => {\r\n // The field claims the 2.5 stored on lengthProp is 2.5 *feet*. The schema says the property\r\n // persists meters, and the schema is the authority on what a stored magnitude means: the\r\n // override is a formatting hint, not a reinterpretation. The property's unit is used, and the\r\n // disagreement is logged so the author can find the mistake.\r\n // Persisted on the element: lengthProp 2.5 m\r\n const block = TextBlock.create();\r\n const claimsFeet = appendField(block, \"lengthProp\", { persistenceUnit: \"Units.FT\" });\r\n\r\n const warnings = spyOnFormatWarnings();\r\n const ignored = spyOnUnitOverrideWarnings();\r\n render(block);\r\n\r\n expect(claimsFeet.cachedContent).to.equal(\"2.5 m\");\r\n expect(warnings()).to.be.empty;\r\n expect(ignored()).to.deep.equal([{\r\n elementId,\r\n propertyName: \"lengthProp\",\r\n persistenceUnit: \"Units.FT\",\r\n propertyPersistenceUnit: \"Units.M\",\r\n }]);\r\n });\r\n\r\n it(\"falls back to the property's own format when the KindOfQuantity does not exist\", async () => {\r\n // The other leg fails this time -- a KoQ no FormatSet and no schema defines, paired with a\r\n // perfectly good unit. The counterpart to the failed-unit case above, and deliberately not\r\n // the same outcome: a unit override makes a claim about the stored magnitude, so failing it\r\n // forfeits the property-side pair, while a KoQ override only chooses a presentation, so\r\n // failing it still falls back to the property's own format.\r\n // Persisted on the element: lengthProp 2.5 m, areaProp 100 m², slopeProp 0.01 m/m, angleProp 90°,\r\n // ratioProp 0.9 (dimensionless), point (1, 2, 3) m\r\n const block = TextBlock.create();\r\n const length = appendField(block, \"lengthProp\", { kindOfQuantity: \"Example.DOES_NOT_EXIST\", persistenceUnit: \"Units.M\" });\r\n const area = appendField(block, \"areaProp\", { kindOfQuantity: \"Example.DOES_NOT_EXIST\", persistenceUnit: \"Units.SQ_M\" });\r\n const slope = appendField(block, \"slopeProp\", { kindOfQuantity: \"Example.DOES_NOT_EXIST\", persistenceUnit: \"Units.M_PER_M\" });\r\n const angle = appendField(block, \"angleProp\", { kindOfQuantity: \"Example.DOES_NOT_EXIST\", persistenceUnit: \"Units.ARC_DEG\" });\r\n const ratio = appendField(block, \"ratioProp\", { kindOfQuantity: \"Example.DOES_NOT_EXIST\", persistenceUnit: \"Units.ONE\" });\r\n const point = appendField(block, \"point\", { kindOfQuantity: \"Example.DOES_NOT_EXIST\", persistenceUnit: \"Units.M\" });\r\n\r\n render(block);\r\n\r\n expect(length.cachedContent).to.equal(\"2.5 m\");\r\n expect(area.cachedContent).to.equal(\"100.0 m²\");\r\n expect(slope.cachedContent).to.equal(\"0.01 m/m\");\r\n expect(angle.cachedContent).to.equal(\"90\");\r\n expect(ratio.cachedContent).to.equal(\"0.9\");\r\n expect(point.cachedContent).to.equal(\"(1, 2, 3)\");\r\n });\r\n\r\n it(\"falls back to the property's own pair when both legs of the override fail\", async () => {\r\n // The most degenerate row: nothing the field says can be resolved. The bad persistence unit\r\n // is ignored in favor of the property's, and the unknown KoQ fails, so the property's own pair\r\n // is the only candidate left. The three KoQ-bearing properties render their baseline; the\r\n // three KoQ-less ones have nothing to fall back to and go raw. Asserted against baselines\r\n // rendered in the same pass rather than captured literals, so the claim survives any change\r\n // to the seed values or the schema.\r\n // Persisted on the element: lengthProp 2.5 m, areaProp 100 m², slopeProp 0.01 m/m, angleProp 90°,\r\n // ratioProp 0.9 (dimensionless), point (1, 2, 3) m\r\n const block = TextBlock.create();\r\n const bothLegsFail = { kindOfQuantity: \"Example.DOES_NOT_EXIST\", persistenceUnit: \"Units.NOT_A_UNIT\" };\r\n\r\n const length = appendField(block, \"lengthProp\", bothLegsFail);\r\n const area = appendField(block, \"areaProp\", bothLegsFail);\r\n const slope = appendField(block, \"slopeProp\", bothLegsFail);\r\n const angle = appendField(block, \"angleProp\", bothLegsFail);\r\n const ratio = appendField(block, \"ratioProp\", bothLegsFail);\r\n const point = appendField(block, \"point\", bothLegsFail);\r\n\r\n const lengthBaseline = appendField(block, \"lengthProp\");\r\n const areaBaseline = appendField(block, \"areaProp\");\r\n const slopeBaseline = appendField(block, \"slopeProp\");\r\n const angleBaseline = appendField(block, \"angleProp\");\r\n const ratioBaseline = appendField(block, \"ratioProp\");\r\n const pointBaseline = appendField(block, \"point\");\r\n\r\n render(block);\r\n\r\n expect(length.cachedContent).to.equal(lengthBaseline.cachedContent); // both \"2.5 m\"\r\n expect(area.cachedContent).to.equal(areaBaseline.cachedContent); // both \"100.0 m²\"\r\n expect(slope.cachedContent).to.equal(slopeBaseline.cachedContent); // both \"0.01 m/m\"\r\n expect(angle.cachedContent).to.equal(angleBaseline.cachedContent); // both \"90\"\r\n expect(ratio.cachedContent).to.equal(ratioBaseline.cachedContent); // both \"0.9\"\r\n expect(point.cachedContent).to.equal(pointBaseline.cachedContent); // both \"(1, 2, 3)\"\r\n });\r\n\r\n it(\"prefers the adopted FormatSet's format over the property's own schema format\", async () => {\r\n // The happy path. Every field names a KoQ the adopted set defines and the unit its value is\r\n // persisted in, so the FormatSet answers for all six -- including the three properties that\r\n // carry no KoQ of their own, which the field has now supplied both halves of the key for.\r\n // Persisted on the element: lengthProp 2.5 m, areaProp 100 m², slopeProp 0.01 m/m, angleProp 90°,\r\n // ratioProp 0.9 (dimensionless), point (1, 2, 3) m\r\n const block = TextBlock.create();\r\n const length = appendField(block, \"lengthProp\", { kindOfQuantity: \"Example.LENGTH\", persistenceUnit: \"Units.M\" });\r\n const area = appendField(block, \"areaProp\", { kindOfQuantity: \"Example.AREA\", persistenceUnit: \"Units.SQ_M\" });\r\n const slope = appendField(block, \"slopeProp\", { kindOfQuantity: \"Example.SLOPE\", persistenceUnit: \"Units.M_PER_M\" });\r\n const angle = appendField(block, \"angleProp\", { kindOfQuantity: \"Example.ANGLE\", persistenceUnit: \"Units.ARC_DEG\" });\r\n const ratio = appendField(block, \"ratioProp\", { kindOfQuantity: \"Example.RATIO\", persistenceUnit: \"Units.ONE\" });\r\n const point = appendField(block, \"point\", { kindOfQuantity: \"Example.LENGTH\", persistenceUnit: \"Units.M\" });\r\n\r\n render(block, {\r\n adopted: toFormatSet(\"Adopted\", {\r\n \"Example.LENGTH\": decimalFormat(\"Units.CM\", \"cm\", 2),\r\n \"Example.AREA\": decimalFormat(\"Units.SQ_FT\", \"ft2\", 2),\r\n \"Example.SLOPE\": decimalFormat(\"Units.HORIZONTAL_PER_VERTICAL\", \":1\", 2),\r\n \"Example.ANGLE\": decimalFormat(\"Units.RAD\", \"rad\", 4),\r\n \"Example.RATIO\": decimalFormat(\"Units.ONE\", \"r\", 4),\r\n }),\r\n });\r\n\r\n // Each value is converted out of its persistence unit into the adopted format's unit -- the\r\n // schema's own meter/square-meter/degree presentations no longer apply.\r\n expect(length.cachedContent).to.equal(\"250 cm\");\r\n expect(area.cachedContent).to.equal(\"1076.39 ft2\");\r\n expect(slope.cachedContent).to.equal(\"100 :1\");\r\n expect(angle.cachedContent).to.equal(\"1.5708 rad\");\r\n expect(ratio.cachedContent).to.equal(\"0.9 r\");\r\n expect(point.cachedContent).to.equal(\"(100 cm, 200 cm, 300 cm)\");\r\n });\r\n\r\n it(\"applies an adopted FormatSet format without a persistence unit only where the property declares one\", async () => {\r\n // Same adopted set, unit leg dropped. The property's KoQ supplies the missing half for the\r\n // first three; the rest have nothing to convert from and stay raw. Note this is not the\r\n // schema's *format* being used -- only its persistence unit, with the FormatSet's format.\r\n // Persisted on the element: lengthProp 2.5 m, areaProp 100 m², slopeProp 0.01 m/m, angleProp 90°,\r\n // ratioProp 0.9 (dimensionless), point (1, 2, 3) m\r\n const block = TextBlock.create();\r\n const length = appendField(block, \"lengthProp\", { kindOfQuantity: \"Example.LENGTH\" });\r\n const area = appendField(block, \"areaProp\", { kindOfQuantity: \"Example.AREA\" });\r\n const slope = appendField(block, \"slopeProp\", { kindOfQuantity: \"Example.SLOPE\" });\r\n const angle = appendField(block, \"angleProp\", { kindOfQuantity: \"Example.ANGLE\" });\r\n const ratio = appendField(block, \"ratioProp\", { kindOfQuantity: \"Example.RATIO\" });\r\n const point = appendField(block, \"point\", { kindOfQuantity: \"Example.LENGTH\" });\r\n\r\n render(block, {\r\n adopted: toFormatSet(\"Adopted\", {\r\n \"Example.LENGTH\": decimalFormat(\"Units.CM\", \"cm\", 2),\r\n \"Example.AREA\": decimalFormat(\"Units.SQ_FT\", \"ft2\", 2),\r\n \"Example.SLOPE\": decimalFormat(\"Units.HORIZONTAL_PER_VERTICAL\", \":1\", 2),\r\n \"Example.ANGLE\": decimalFormat(\"Units.RAD\", \"rad\", 4),\r\n \"Example.RATIO\": decimalFormat(\"Units.ONE\", \"r\", 4),\r\n }),\r\n });\r\n\r\n expect(length.cachedContent).to.equal(\"250 cm\");\r\n expect(area.cachedContent).to.equal(\"1076.39 ft2\");\r\n expect(slope.cachedContent).to.equal(\"100 :1\");\r\n\r\n // Unchanged from the baseline: no persistence unit from either side.\r\n expect(angle.cachedContent).to.equal(\"90\");\r\n expect(ratio.cachedContent).to.equal(\"0.9\");\r\n expect(point.cachedContent).to.equal(\"(1, 2, 3)\");\r\n });\r\n\r\n it(\"routes to the adopted FormatSet whether the field names its id, names nothing, or names an id that was never registered\", async () => {\r\n // Three ways of addressing the default formats, which must be indistinguishable: the adopted\r\n // set is what a field gets when it asks for nothing, and an id absent from the registration\r\n // falls back to it rather than failing or searching the other sets. Asserted against each\r\n // other rather than against literals so it cannot drift with the formats above.\r\n // Persisted on the element: lengthProp 2.5 m, angleProp 90°\r\n const block = TextBlock.create();\r\n const namesNothing = appendField(block, \"lengthProp\", { kindOfQuantity: \"Example.LENGTH\", persistenceUnit: \"Units.M\" });\r\n const namesAdopted = appendField(block, \"lengthProp\", { formatSet: ADOPTED_SET, kindOfQuantity: \"Example.LENGTH\", persistenceUnit: \"Units.M\" });\r\n const namesUnregistered = appendField(block, \"lengthProp\", { formatSet: UNREGISTERED_SET, kindOfQuantity: \"Example.LENGTH\", persistenceUnit: \"Units.M\" });\r\n\r\n // Repeated on a property with no KoQ of its own, where a routing failure would fall all the\r\n // way to raw rather than being masked by the property-side format.\r\n const koqLessNamesNothing = appendField(block, \"angleProp\", { kindOfQuantity: \"Example.ANGLE\", persistenceUnit: \"Units.ARC_DEG\" });\r\n const koqLessNamesAdopted = appendField(block, \"angleProp\", { formatSet: ADOPTED_SET, kindOfQuantity: \"Example.ANGLE\", persistenceUnit: \"Units.ARC_DEG\" });\r\n const koqLessNamesUnregistered = appendField(block, \"angleProp\", { formatSet: UNREGISTERED_SET, kindOfQuantity: \"Example.ANGLE\", persistenceUnit: \"Units.ARC_DEG\" });\r\n\r\n const adopted = toFormatSet(\"Adopted\", {\r\n \"Example.LENGTH\": decimalFormat(\"Units.CM\", \"cm\", 2),\r\n \"Example.ANGLE\": decimalFormat(\"Units.RAD\", \"rad\", 4),\r\n });\r\n render(block, { adopted, byId: [{ id: ADOPTED_SET, formatSet: adopted }] });\r\n\r\n expect(namesNothing.cachedContent).to.equal(\"250 cm\");\r\n expect(namesAdopted.cachedContent).to.equal(namesNothing.cachedContent); // both \"250 cm\"\r\n expect(namesUnregistered.cachedContent).to.equal(namesNothing.cachedContent); // both \"250 cm\"\r\n\r\n expect(koqLessNamesNothing.cachedContent).to.equal(\"1.5708 rad\");\r\n expect(koqLessNamesAdopted.cachedContent).to.equal(koqLessNamesNothing.cachedContent); // both \"1.5708 rad\"\r\n expect(koqLessNamesUnregistered.cachedContent).to.equal(koqLessNamesNothing.cachedContent); // both \"1.5708 rad\"\r\n });\r\n\r\n it(\"uses an alternate FormatSet's format when the field names it, in preference to the adopted one\", async () => {\r\n // Mixing presentations within one iModel -- imperial callouts on an otherwise metric drawing.\r\n // Both sets define every key here, so the alternate's redefinition is what must win.\r\n // Persisted on the element: lengthProp 2.5 m, areaProp 100 m², slopeProp 0.01 m/m, angleProp 90°,\r\n // ratioProp 0.9 (dimensionless), point (1, 2, 3) m\r\n const block = TextBlock.create();\r\n const length = appendField(block, \"lengthProp\", { formatSet: ALT_SET, kindOfQuantity: \"Example.LENGTH\", persistenceUnit: \"Units.M\" });\r\n const area = appendField(block, \"areaProp\", { formatSet: ALT_SET, kindOfQuantity: \"Example.AREA\", persistenceUnit: \"Units.SQ_M\" });\r\n const slope = appendField(block, \"slopeProp\", { formatSet: ALT_SET, kindOfQuantity: \"Example.SLOPE\", persistenceUnit: \"Units.M_PER_M\" });\r\n const angle = appendField(block, \"angleProp\", { formatSet: ALT_SET, kindOfQuantity: \"Example.ANGLE\", persistenceUnit: \"Units.ARC_DEG\" });\r\n const ratio = appendField(block, \"ratioProp\", { formatSet: ALT_SET, kindOfQuantity: \"Example.RATIO\", persistenceUnit: \"Units.ONE\" });\r\n const point = appendField(block, \"point\", { formatSet: ALT_SET, kindOfQuantity: \"Example.LENGTH\", persistenceUnit: \"Units.M\" });\r\n\r\n // The same field routed to the default formats, to prove the alternate set actually displaced\r\n // the adopted one rather than both happening to agree.\r\n const adoptedLength = appendField(block, \"lengthProp\", { kindOfQuantity: \"Example.LENGTH\", persistenceUnit: \"Units.M\" });\r\n\r\n render(block, {\r\n adopted: toFormatSet(\"Adopted\", {\r\n \"Example.LENGTH\": decimalFormat(\"Units.CM\", \"cm\", 2),\r\n \"Example.AREA\": decimalFormat(\"Units.SQ_M\", \"m2\", 2),\r\n \"Example.SLOPE\": decimalFormat(\"Units.M_PER_M\", \"m/m\", 4),\r\n \"Example.ANGLE\": decimalFormat(\"Units.ARC_DEG\", \"deg\", 2),\r\n \"Example.RATIO\": decimalFormat(\"Units.ONE\", \"ratio\", 2),\r\n }),\r\n byId: [{\r\n id: ALT_SET, formatSet: toFormatSet(\"Alternate\", {\r\n \"Example.LENGTH\": decimalFormat(\"Units.FT\", \"[alt]ft\", 3),\r\n \"Example.AREA\": decimalFormat(\"Units.SQ_FT\", \"[alt]ft2\", 3),\r\n \"Example.SLOPE\": decimalFormat(\"Units.HORIZONTAL_PER_VERTICAL\", \"[alt]:1\", 2),\r\n \"Example.ANGLE\": decimalFormat(\"Units.ARC_DEG\", \"[alt]deg\", 1),\r\n \"Example.RATIO\": decimalFormat(\"Units.ONE\", \"[alt]ratio\", 1),\r\n }),\r\n }],\r\n });\r\n\r\n expect(length.cachedContent).to.equal(\"8.202 [alt]ft\");\r\n expect(area.cachedContent).to.equal(\"1076.391 [alt]ft2\");\r\n expect(slope.cachedContent).to.equal(\"100 [alt]:1\");\r\n expect(angle.cachedContent).to.equal(\"90 [alt]deg\");\r\n expect(ratio.cachedContent).to.equal(\"0.9 [alt]ratio\");\r\n expect(point.cachedContent).to.equal(\"(3.281 [alt]ft, 6.562 [alt]ft, 9.843 [alt]ft)\");\r\n\r\n expect(adoptedLength.cachedContent).to.equal(\"250 cm\");\r\n });\r\n\r\n it(\"falls through from the named alternate FormatSet to the adopted one when the alternate omits the key\", async () => {\r\n // The alternate set is a partial overlay, not a replacement: a field routed to it still sees\r\n // every key the adopted set defines.\r\n // Persisted on the element: lengthProp 2.5 m, areaProp 100 m²\r\n const block = TextBlock.create();\r\n const redefined = appendField(block, \"lengthProp\", { formatSet: ALT_SET, kindOfQuantity: \"Example.LENGTH\", persistenceUnit: \"Units.M\" });\r\n const omitted = appendField(block, \"areaProp\", { formatSet: ALT_SET, kindOfQuantity: \"Example.AREA\", persistenceUnit: \"Units.SQ_M\" });\r\n\r\n render(block, {\r\n adopted: toFormatSet(\"Adopted\", {\r\n \"Example.LENGTH\": decimalFormat(\"Units.CM\", \"cm\", 2),\r\n \"Example.AREA\": decimalFormat(\"Units.SQ_FT\", \"ft2\", 2),\r\n }),\r\n byId: [{\r\n id: ALT_SET, formatSet: toFormatSet(\"Alternate\", {\r\n // Redefines the length key and says nothing about area.\r\n \"Example.LENGTH\": decimalFormat(\"Units.FT\", \"[alt]ft\", 3),\r\n }),\r\n }],\r\n });\r\n\r\n expect(redefined.cachedContent).to.equal(\"8.202 [alt]ft\");\r\n expect(omitted.cachedContent).to.equal(\"1076.39 ft2\");\r\n });\r\n\r\n it(\"routes a colon-separated KindOfQuantity name to the FormatSet that defines it in dot-separated form\", async () => {\r\n // Pins a normalization in another package. `FormatSetFormatsProvider.getFormatSync` in\r\n // ecschema-metadata normalizes the name it is asked for via `SchemaItem.parseFullName`, which\r\n // accepts `schemaName:itemName` (the form the node addon emits) as well as\r\n // `schemaName.itemName`, so a set keyed in dot form must answer a field that names its KoQ in\r\n // colon form.\r\n //\r\n // If that normalization ever regresses, the alternate set misses a key it does define and the\r\n // field silently falls through to the *adopted* set instead -- no throw, no warning, because a\r\n // spec did resolve. This test is the tripwire: it fails with the adopted set's \"250 cm\" rather\r\n // than the alternate's \"[alt]ft\".\r\n // Persisted on the element: lengthProp 2.5 m\r\n const block = TextBlock.create();\r\n const colonNamed = appendField(block, \"lengthProp\", { formatSet: ALT_SET, kindOfQuantity: \"Example:LENGTH\", persistenceUnit: \"Units.M\" });\r\n const dotNamed = appendField(block, \"lengthProp\", { formatSet: ALT_SET, kindOfQuantity: \"Example.LENGTH\", persistenceUnit: \"Units.M\" });\r\n\r\n render(block, {\r\n // Defines the same key, so a regressed alternate set falls through to here and produces a\r\n // plausible-looking string instead of an obvious failure.\r\n adopted: toFormatSet(\"Adopted\", { \"Example.LENGTH\": decimalFormat(\"Units.CM\", \"cm\", 2) }),\r\n byId: [{ id: ALT_SET, formatSet: toFormatSet(\"Alternate\", { \"Example.LENGTH\": decimalFormat(\"Units.FT\", \"[alt]ft\", 3) }) }],\r\n });\r\n\r\n expect(colonNamed.cachedContent).to.equal(\"8.202 [alt]ft\");\r\n expect(colonNamed.cachedContent).to.equal(dotNamed.cachedContent); // both \"8.202 [alt]ft\"\r\n });\r\n\r\n it(\"resolves a key only the alternate FormatSet defines only when the field names that set\", async () => {\r\n // The complement of the fallthrough above: the adopted set cannot see into the alternate\r\n // one. An unregistered id lands on the adopted set, so it fails the same way naming\r\n // nothing does -- the fallback is one specific set, not a search of every registered set.\r\n // Persisted on the element: lengthProp 2.5 m\r\n const block = TextBlock.create();\r\n const namesAlternate = appendField(block, \"lengthProp\", { formatSet: ALT_SET, kindOfQuantity: \"Example.ALT_ONLY_LENGTH\", persistenceUnit: \"Units.M\" });\r\n const namesNothing = appendField(block, \"lengthProp\", { kindOfQuantity: \"Example.ALT_ONLY_LENGTH\", persistenceUnit: \"Units.M\" });\r\n const namesUnregistered = appendField(block, \"lengthProp\", { formatSet: UNREGISTERED_SET, kindOfQuantity: \"Example.ALT_ONLY_LENGTH\", persistenceUnit: \"Units.M\" });\r\n\r\n render(block, {\r\n adopted: toFormatSet(\"Adopted\", { \"Example.LENGTH\": decimalFormat(\"Units.CM\", \"cm\", 2) }),\r\n byId: [{\r\n id: ALT_SET, formatSet: toFormatSet(\"Alternate\", {\r\n \"Example.ALT_ONLY_LENGTH\": decimalFormat(\"Units.IN\", \"[alt-only]in\", 2),\r\n }),\r\n }],\r\n });\r\n\r\n expect(namesAlternate.cachedContent).to.equal(\"98.43 [alt-only]in\");\r\n\r\n // Neither the adopted set nor the schema defines this key, so the property's own KoQ decides.\r\n expect(namesNothing.cachedContent).to.equal(\"2.5 m\");\r\n expect(namesUnregistered.cachedContent).to.equal(namesNothing.cachedContent); // both \"2.5 m\"\r\n });\r\n\r\n it(\"reaches the schema from a named alternate FormatSet when neither set defines the key\", async () => {\r\n // The full chain in one field: alternate -> adopted -> SchemaFormatsProvider. Asserted on a\r\n // property with no KoQ of its own, so a break anywhere in the chain shows up as a raw value\r\n // rather than being masked by the property-side format.\r\n // Persisted on the element: angleProp 90°\r\n const block = TextBlock.create();\r\n const angle = appendField(block, \"angleProp\", { formatSet: ALT_SET, kindOfQuantity: \"FieldExample.SCHEMA_ANGLE\", persistenceUnit: \"Units.ARC_DEG\" });\r\n\r\n render(block, {\r\n adopted: toFormatSet(\"Adopted\", { \"Example.LENGTH\": decimalFormat(\"Units.CM\", \"cm\", 2) }),\r\n byId: [{ id: ALT_SET, formatSet: toFormatSet(\"Alternate\", { \"Example.LENGTH\": decimalFormat(\"Units.FT\", \"[alt]ft\", 3) }) }],\r\n });\r\n\r\n expect(angle.cachedContent).to.equal(\"90.0 °\");\r\n });\r\n\r\n it(\"renders raw and logs a warning when the persistence unit is defined only by the iModel's schemas\", async () => {\r\n // Synchronous resolution goes through the bundled BIS units alone; SchemaUnitProvider has no\r\n // synchronous path. A property persisted in a unit its own schema defines is therefore a\r\n // known limitation: it renders raw and is logged, rather than being silently formatted\r\n // through the wrong unit.\r\n // Persisted on the element: customUnitProp 1.25 DOUBLE_M (= 2.5 m)\r\n const block = TextBlock.create();\r\n const custom = appendField(block, \"customUnitProp\");\r\n\r\n const warnings = spyOnFormatWarnings();\r\n render(block);\r\n\r\n expect(custom.cachedContent).to.equal(\"1.25\");\r\n expect(warnings()).to.deep.equal([{ elementId, propertyName: \"customUnitProp\", formatSet: undefined, tried: [\"FieldExample.CUSTOM_UNIT_PROP in FieldExample.DOUBLE_M\"] }]);\r\n });\r\n\r\n it(\"routes a field with no KindOfQuantity override through a FormatSet keyed on the property's own KindOfQuantity\", async () => {\r\n // The shape a deployment is most likely to take: FormatSets keyed on the KindOfQuantities the\r\n // schemas already declare, and fields that carry no overrides at all -- at most a FormatSet id\r\n // to pick a presentation. Nothing here names a KoQ, so every candidate's `name` is the\r\n // property's own `FieldExample.LENGTH_PROP`, supplied by the property-side pair. A FormatSet\r\n // that defines that key is therefore reached without the field mentioning it.\r\n //\r\n // This is what makes `FieldSpecBucket.definesOwnFormat` load-bearing rather than incidental:\r\n // it is asked about the property's KoQ, not only about an override.\r\n // Persisted on the element: lengthProp 2.5 m\r\n const block = TextBlock.create();\r\n const namesAlternate = appendField(block, \"lengthProp\", { formatSet: ALT_SET });\r\n const namesNothing = appendField(block, \"lengthProp\");\r\n\r\n render(block, {\r\n adopted: toFormatSet(\"Adopted\", { \"FieldExample.LENGTH_PROP\": decimalFormat(\"Units.CM\", \"cm\", 2) }),\r\n byId: [{ id: ALT_SET, formatSet: toFormatSet(\"Alternate\", { \"FieldExample.LENGTH_PROP\": decimalFormat(\"Units.FT\", \"[alt]ft\", 3) }) }],\r\n });\r\n\r\n // Both are routed by their FormatSet, and neither falls back to the schema's own\r\n // presentation format for LENGTH_PROP, which would render \"2.5 m\".\r\n expect(namesAlternate.cachedContent).to.equal(\"8.202 [alt]ft\");\r\n expect(namesNothing.cachedContent).to.equal(\"250 cm\");\r\n });\r\n\r\n it(\"does nothing when a field names a FormatSet whose formats it shares no key with\", async () => {\r\n // The complement of the test above. Naming a FormatSet does not by itself select anything:\r\n // routing happens on the candidate's KoQ name, which here is the property's own\r\n // `FieldExample.LENGTH_PROP` -- a key neither set defines. So both fields land on the\r\n // schema's presentation format, and naming the alternate set changes nothing.\r\n // Persisted on the element: lengthProp 2.5 m\r\n const block = TextBlock.create();\r\n const namesAlternate = appendField(block, \"lengthProp\", { formatSet: ALT_SET });\r\n const noOptions = appendField(block, \"lengthProp\");\r\n\r\n render(block, {\r\n adopted: toFormatSet(\"Adopted\", { \"Example.LENGTH\": decimalFormat(\"Units.CM\", \"cm\", 2) }),\r\n byId: [{ id: ALT_SET, formatSet: toFormatSet(\"Alternate\", { \"Example.LENGTH\": decimalFormat(\"Units.FT\", \"[alt]ft\", 3) }) }],\r\n });\r\n\r\n expect(namesAlternate.cachedContent).to.equal(noOptions.cachedContent); // both \"2.5 m\"\r\n expect(namesAlternate.cachedContent).to.equal(\"2.5 m\");\r\n });\r\n\r\n it(\"declines a format whose unit belongs to a different phenomenon than the persisted value\", async () => {\r\n // A format can only be applied if its unit is convertible from the persistence unit. An angle\r\n // format cannot present a length, so the override is refused and the field falls back exactly\r\n // as it would for any other unresolved format -- here, to the schema's own presentation format.\r\n // Without that check the magnitude would be relabelled rather than converted (\"2.5 deg\"),\r\n // because UnitsProvider.getConversion reports the mismatch by returning the *identity*\r\n // conversion tagged `error: true`. See buildFieldFormatterSpec in fieldSpecs.ts.\r\n // Persisted on the element: lengthProp 2.5 m\r\n const block = TextBlock.create();\r\n const crossed = appendField(block, \"lengthProp\", { kindOfQuantity: \"Example.ANGLE\", persistenceUnit: \"Units.M\" });\r\n const baseline = appendField(block, \"lengthProp\");\r\n\r\n render(block, {\r\n adopted: toFormatSet(\"Adopted\", { \"Example.ANGLE\": decimalFormat(\"Units.ARC_DEG\", \"deg\", 2) }),\r\n });\r\n\r\n expect(baseline.cachedContent).to.equal(\"2.5 m\");\r\n expect(crossed.cachedContent).to.equal(\"2.5 m\");\r\n // Stated as an equality too, so the test keeps meaning \"fell back to the baseline\" rather\r\n // than \"happens to equal this literal\" if the seed value ever changes.\r\n expect(crossed.cachedContent).to.equal(baseline.cachedContent); // both \"2.5 m\"\r\n });\r\n\r\n it(\"degrades only the offending field when applying a format throws\", async () => {\r\n // FormatterSpec.applyFormatting is a much larger exception surface than the `toString()` it\r\n // replaced. A throw used to escape updateField and updateFields, get swallowed by\r\n // doUpdateFields, and abandon the whole element -- earlier fields mutated in memory, nothing\r\n // persisted, one log line as the only trace. It must degrade this field alone.\r\n // Persisted on the element: lengthProp 2.5 m, areaProp 100 m², slopeProp 0.01 m/m\r\n const block = TextBlock.create();\r\n const before = appendField(block, \"areaProp\");\r\n const thrower = appendField(block, \"lengthProp\");\r\n const after = appendField(block, \"slopeProp\");\r\n\r\n ElementDrivesTextAnnotation.registerFieldFormatting({ iModel: imodel });\r\n\r\n const applyFormatting = sinon.stub(FormatterSpec.prototype, \"applyFormatting\");\r\n applyFormatting.callsFake(function (this: FormatterSpec, magnitude: number) {\r\n if (2.5 === magnitude)\r\n throw new Error(\"malformed format\");\r\n return applyFormatting.wrappedMethod.call(this, magnitude);\r\n });\r\n\r\n expect(() => ElementDrivesTextAnnotation.evaluateFields({ iModel: imodel, block })).to.not.throw();\r\n\r\n expect(thrower.cachedContent).to.equal(FieldRun.invalidContentIndicator); // \"####\"\r\n // The fields on either side of the failure still resolved and still persisted.\r\n expect(before.cachedContent).to.equal(\"100.0 m²\");\r\n expect(after.cachedContent).to.equal(\"0.01 m/m\");\r\n });\r\n});\r\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"FieldFormattingLifecycle.test.d.ts","sourceRoot":"","sources":["../../../../src/test/annotations/FieldFormattingLifecycle.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,323 @@
|
|
|
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 { Code, FieldRun, SubCategoryAppearance, TextAnnotation, TextBlock, traverseTextBlockComponent } from "@itwin/core-common";
|
|
7
|
+
import { DbResult, UnexpectedErrors } from "@itwin/core-bentley";
|
|
8
|
+
import { Point3d, YawPitchRollAngles } from "@itwin/core-geometry";
|
|
9
|
+
import { StandaloneDb } from "../../IModelDb";
|
|
10
|
+
import { IModelTestUtils } from "../IModelTestUtils";
|
|
11
|
+
import { SpatialCategory } from "../../Category";
|
|
12
|
+
import { Schema, Schemas } from "../../Schema";
|
|
13
|
+
import { ClassRegistry } from "../../ClassRegistry";
|
|
14
|
+
import { PhysicalElement } from "../../Element";
|
|
15
|
+
import { FontFile, TextAnnotation3d } from "../../core-backend";
|
|
16
|
+
import { ElementDrivesTextAnnotation, TextAnnotationUsesTextStyleByDefault } from "../../annotations/ElementDrivesTextAnnotation";
|
|
17
|
+
import { decimalFormat, toFormatSet } from "../AnnotationTestUtils";
|
|
18
|
+
import { withEditTxn } from "../../EditTxn";
|
|
19
|
+
/**
|
|
20
|
+
* The lifecycle of [[ElementDrivesTextAnnotation.registerFieldFormatting]] and the persistence
|
|
21
|
+
* contract of [[ElementDrivesTextAnnotation.evaluateFields]]: when the change event fires, what a
|
|
22
|
+
* registration does (and does not do) to already-persisted `cachedContent`, and which code paths
|
|
23
|
+
* consult the registered FormatSets.
|
|
24
|
+
*
|
|
25
|
+
* Which format a given field resolves to is covered exhaustively by FieldFormat.test.ts; nothing
|
|
26
|
+
* here varies `kindOfQuantity` or `persistenceUnit`. The registration-level `unitSystem` argument
|
|
27
|
+
* is covered here because it is part of the registration contract rather than the per-field one.
|
|
28
|
+
*/
|
|
29
|
+
const lifecycleSchemaXml = `<?xml version="1.0" encoding="UTF-8"?>
|
|
30
|
+
<ECSchema schemaName="FieldLifecycle" alias="flc" version="01.00.00" xmlns="http://www.bentley.com/schemas/Bentley.ECXML.3.2">
|
|
31
|
+
<ECSchemaReference name="BisCore" version="01.00.04" alias="bis"/>
|
|
32
|
+
<ECSchemaReference name="Formats" version="01.00.00" alias="f"/>
|
|
33
|
+
<ECSchemaReference name="Units" version="01.00.09" alias="u"/>
|
|
34
|
+
|
|
35
|
+
<KindOfQuantity typeName="LENGTH" displayLabel="Length" persistenceUnit="u:M" relativeError="0.0001" presentationUnits="f:DefaultRealU(4)[u:M]"/>
|
|
36
|
+
<KindOfQuantity typeName="DUAL_LENGTH" displayLabel="Dual Length" persistenceUnit="u:M" relativeError="0.0001" presentationUnits="f:DefaultRealU(4)[u:M];f:DefaultRealU(4)[u:FT]"/>
|
|
37
|
+
|
|
38
|
+
<ECEntityClass typeName="LifecycleElement" modifier="None">
|
|
39
|
+
<BaseClass>bis:PhysicalElement</BaseClass>
|
|
40
|
+
<ECProperty propertyName="lengthProp" typeName="double" kindOfQuantity="LENGTH"/>
|
|
41
|
+
<ECProperty propertyName="dualLengthProp" typeName="double" kindOfQuantity="DUAL_LENGTH"/>
|
|
42
|
+
<ECProperty propertyName="maybeNull" typeName="double"/>
|
|
43
|
+
<ECArrayProperty propertyName="strings" typeName="string"/>
|
|
44
|
+
</ECEntityClass>
|
|
45
|
+
</ECSchema>
|
|
46
|
+
`;
|
|
47
|
+
class LifecycleElement extends PhysicalElement {
|
|
48
|
+
static get className() { return "LifecycleElement"; }
|
|
49
|
+
}
|
|
50
|
+
class FieldLifecycleSchema extends Schema {
|
|
51
|
+
static get schemaName() { return "FieldLifecycle"; }
|
|
52
|
+
}
|
|
53
|
+
describe("Field formatting lifecycle", () => {
|
|
54
|
+
let imodel;
|
|
55
|
+
let model;
|
|
56
|
+
let category;
|
|
57
|
+
let sourceElementId;
|
|
58
|
+
const propertyHost = (elementId) => ({ elementId, schemaName: "FieldLifecycle", className: "LifecycleElement" });
|
|
59
|
+
// Id referenced by fields via formatOptions.quantity.formatSet and supplied through the
|
|
60
|
+
// `formatSets` array at registration time.
|
|
61
|
+
const PRIMARY_FORMAT_SET = "0x111";
|
|
62
|
+
before(async () => {
|
|
63
|
+
const iModelPath = IModelTestUtils.prepareOutputFile("FieldFormattingLifecycle", "test.bim");
|
|
64
|
+
imodel = StandaloneDb.createEmpty(iModelPath, { rootSubject: { name: "FieldFormattingLifecycle" }, enableTransactions: true });
|
|
65
|
+
if (!Schemas.getRegisteredSchema("FieldLifecycle")) {
|
|
66
|
+
Schemas.registerSchema(FieldLifecycleSchema);
|
|
67
|
+
ClassRegistry.register(LifecycleElement, FieldLifecycleSchema);
|
|
68
|
+
}
|
|
69
|
+
await imodel.importSchemaStrings([lifecycleSchemaXml]);
|
|
70
|
+
await withEditTxn(imodel, async (txn) => {
|
|
71
|
+
model = IModelTestUtils.createAndInsertPhysicalPartitionAndModel(txn, Code.createEmpty(), true)[1];
|
|
72
|
+
category = SpatialCategory.insert(txn, StandaloneDb.dictionaryId, "FieldFormattingLifecycleCategory", new SubCategoryAppearance());
|
|
73
|
+
await imodel.fonts.embedFontFile({
|
|
74
|
+
file: FontFile.createFromTrueTypeFileName(IModelTestUtils.resolveFontFile("Karla-Regular.ttf")),
|
|
75
|
+
});
|
|
76
|
+
sourceElementId = txn.insertElement(sourceElementProps());
|
|
77
|
+
});
|
|
78
|
+
});
|
|
79
|
+
after(() => {
|
|
80
|
+
imodel.close();
|
|
81
|
+
});
|
|
82
|
+
afterEach(() => {
|
|
83
|
+
ElementDrivesTextAnnotation.registerFieldFormatting({ iModel: imodel });
|
|
84
|
+
// Drop the annotations inserted below so their ElementDrivesTextAnnotation relationships do
|
|
85
|
+
// not leak into the next test.
|
|
86
|
+
const ids = [];
|
|
87
|
+
// eslint-disable-next-line @typescript-eslint/no-deprecated
|
|
88
|
+
imodel.withPreparedStatement("SELECT ECInstanceId FROM BisCore.TextAnnotation3d", (stmt) => {
|
|
89
|
+
while (stmt.step() === DbResult.BE_SQLITE_ROW)
|
|
90
|
+
ids.push(stmt.getValue(0).getId());
|
|
91
|
+
});
|
|
92
|
+
if (ids.length > 0)
|
|
93
|
+
withEditTxn(imodel, (txn) => { for (const id of ids)
|
|
94
|
+
txn.deleteElement(id); });
|
|
95
|
+
});
|
|
96
|
+
function sourceElementProps() {
|
|
97
|
+
return {
|
|
98
|
+
classFullName: "FieldLifecycle:LifecycleElement",
|
|
99
|
+
model,
|
|
100
|
+
category,
|
|
101
|
+
code: Code.createEmpty(),
|
|
102
|
+
lengthProp: 2.5,
|
|
103
|
+
dualLengthProp: 2.5,
|
|
104
|
+
strings: ["a", "b"],
|
|
105
|
+
placement: { origin: new Point3d(0, 0, 0), angles: new YawPitchRollAngles() },
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
// Configures `imodel` with the given per-field FormatSets.
|
|
109
|
+
function registerSets(formatSets) {
|
|
110
|
+
ElementDrivesTextAnnotation.registerFieldFormatting({
|
|
111
|
+
iModel: imodel,
|
|
112
|
+
formatSets: formatSets.map(({ id, formats }) => ({ id, formatSet: toFormatSet("TestSet", formats) })),
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
// A mm FormatSet: 2.5 m -> "2500 mm".
|
|
116
|
+
function mmSet() {
|
|
117
|
+
return { "FieldLifecycle.LENGTH": decimalFormat("Units.MM", "mm", 2) };
|
|
118
|
+
}
|
|
119
|
+
function readFieldCachedContent(block) {
|
|
120
|
+
for (const { child } of traverseTextBlockComponent(block)) {
|
|
121
|
+
if (child.type === "field")
|
|
122
|
+
return child.cachedContent;
|
|
123
|
+
}
|
|
124
|
+
return undefined;
|
|
125
|
+
}
|
|
126
|
+
function readFieldCachedContentById(annotationElementId) {
|
|
127
|
+
const reloaded = imodel.elements.getElement(annotationElementId);
|
|
128
|
+
const reloadedBlock = reloaded.getAnnotation()?.textBlock;
|
|
129
|
+
return reloadedBlock ? readFieldCachedContent(reloadedBlock) : undefined;
|
|
130
|
+
}
|
|
131
|
+
function insertAnnotationElement(textBlock) {
|
|
132
|
+
const elem = TextAnnotation3d.fromJSON({
|
|
133
|
+
model,
|
|
134
|
+
category,
|
|
135
|
+
code: Code.createEmpty(),
|
|
136
|
+
placement: {
|
|
137
|
+
origin: { x: 0, y: 0, z: 0 },
|
|
138
|
+
angles: YawPitchRollAngles.createDegrees(0, 0, 0).toJSON(),
|
|
139
|
+
},
|
|
140
|
+
classFullName: TextAnnotation3d.classFullName,
|
|
141
|
+
defaultTextStyle: new TextAnnotationUsesTextStyleByDefault("0x123").toJSON(),
|
|
142
|
+
}, imodel);
|
|
143
|
+
elem.setAnnotation(TextAnnotation.fromJSON({ textBlock: textBlock.toJSON() }));
|
|
144
|
+
return withEditTxn(imodel, (txn) => txn.insertElement(elem.toJSON()));
|
|
145
|
+
}
|
|
146
|
+
/** Inserts an annotation whose single field reads `lengthProp` of `sourceId` through
|
|
147
|
+
* `PRIMARY_FORMAT_SET`, and wires up its dependency so txn callbacks evaluate it.
|
|
148
|
+
*/
|
|
149
|
+
function insertAnnotationWithLengthField(sourceId, formatSet = PRIMARY_FORMAT_SET) {
|
|
150
|
+
const textBlock = TextBlock.create();
|
|
151
|
+
const field = FieldRun.create({
|
|
152
|
+
styleOverrides: { font: { name: "Karla" } },
|
|
153
|
+
propertyHost: propertyHost(sourceId),
|
|
154
|
+
propertyPath: { propertyName: "lengthProp" },
|
|
155
|
+
formatOptions: formatSet ? { quantity: { formatSet } } : undefined,
|
|
156
|
+
cachedContent: "old",
|
|
157
|
+
});
|
|
158
|
+
textBlock.appendRun(field);
|
|
159
|
+
const annotationElementId = insertAnnotationElement(textBlock);
|
|
160
|
+
withEditTxn(imodel, (txn) => {
|
|
161
|
+
ElementDrivesTextAnnotation.updateFieldDependencies(txn, annotationElementId);
|
|
162
|
+
});
|
|
163
|
+
return annotationElementId;
|
|
164
|
+
}
|
|
165
|
+
describe("evaluateFields", () => {
|
|
166
|
+
it("preserves non-quantity field formatting", () => {
|
|
167
|
+
const textBlock = TextBlock.create();
|
|
168
|
+
const stringField = FieldRun.create({
|
|
169
|
+
propertyHost: propertyHost(sourceElementId),
|
|
170
|
+
propertyPath: { propertyName: "strings", accessors: [0] },
|
|
171
|
+
formatOptions: { prefix: "[", suffix: "]" },
|
|
172
|
+
cachedContent: "old",
|
|
173
|
+
});
|
|
174
|
+
textBlock.appendRun(stringField);
|
|
175
|
+
registerSets([{ id: PRIMARY_FORMAT_SET, formats: mmSet() }]);
|
|
176
|
+
const updatedCount = ElementDrivesTextAnnotation.evaluateFields({ iModel: imodel, block: textBlock });
|
|
177
|
+
expect(updatedCount).to.equal(1);
|
|
178
|
+
expect(stringField.cachedContent).to.equal("[a]");
|
|
179
|
+
});
|
|
180
|
+
it("marks the field invalid when a quantity property value is missing", () => {
|
|
181
|
+
const textBlock = TextBlock.create();
|
|
182
|
+
const field = FieldRun.create({
|
|
183
|
+
propertyHost: propertyHost(sourceElementId),
|
|
184
|
+
// maybeNull has no value on the source element.
|
|
185
|
+
propertyPath: { propertyName: "maybeNull" },
|
|
186
|
+
formatOptions: { quantity: { persistenceUnit: "Units.M" } },
|
|
187
|
+
cachedContent: "old",
|
|
188
|
+
});
|
|
189
|
+
textBlock.appendRun(field);
|
|
190
|
+
const updatedCount = ElementDrivesTextAnnotation.evaluateFields({ iModel: imodel, block: textBlock });
|
|
191
|
+
expect(updatedCount).to.equal(1);
|
|
192
|
+
expect(field.cachedContent).to.equal(FieldRun.invalidContentIndicator);
|
|
193
|
+
});
|
|
194
|
+
it("mutates the in-memory TextBlock but does not persist to the element on its own", () => {
|
|
195
|
+
// Contract: evaluateFields formats in place and returns a count. Persistence is the
|
|
196
|
+
// caller's responsibility (setAnnotation + element.update inside an EditTxn).
|
|
197
|
+
const annotationElementId = insertAnnotationWithLengthField(sourceElementId);
|
|
198
|
+
const persistedBefore = readFieldCachedContentById(annotationElementId);
|
|
199
|
+
expect(persistedBefore).to.equal("2.5 m");
|
|
200
|
+
registerSets([{ id: PRIMARY_FORMAT_SET, formats: mmSet() }]);
|
|
201
|
+
const reloaded = imodel.elements.getElement(annotationElementId);
|
|
202
|
+
const inMemoryBlock = reloaded.getAnnotation().textBlock;
|
|
203
|
+
const updated = ElementDrivesTextAnnotation.evaluateFields({ iModel: imodel, block: inMemoryBlock });
|
|
204
|
+
// The in-memory half of the contract: the field resolved through the registered FormatSet
|
|
205
|
+
// and the call reported the one field it changed.
|
|
206
|
+
expect(updated).to.equal(1);
|
|
207
|
+
expect(readFieldCachedContent(inMemoryBlock)).to.equal("2500 mm");
|
|
208
|
+
// The persisted half: the disk copy is untouched, because nothing called `update`.
|
|
209
|
+
expect(readFieldCachedContentById(annotationElementId)).to.equal(persistedBefore);
|
|
210
|
+
});
|
|
211
|
+
});
|
|
212
|
+
describe("registerFieldFormatting", () => {
|
|
213
|
+
it("raises onFieldFormattingChanged on every register call", () => {
|
|
214
|
+
let events = 0;
|
|
215
|
+
const drop = ElementDrivesTextAnnotation.onFieldFormattingChanged.addListener((args) => {
|
|
216
|
+
expect(args.iModel).to.equal(imodel);
|
|
217
|
+
++events;
|
|
218
|
+
});
|
|
219
|
+
try {
|
|
220
|
+
registerSets([{ id: PRIMARY_FORMAT_SET, formats: mmSet() }]);
|
|
221
|
+
// Re-registering the same configuration still reports.
|
|
222
|
+
registerSets([{ id: PRIMARY_FORMAT_SET, formats: mmSet() }]);
|
|
223
|
+
// Reverting to the schema default is a register call too.
|
|
224
|
+
ElementDrivesTextAnnotation.registerFieldFormatting({ iModel: imodel });
|
|
225
|
+
expect(events).to.equal(3);
|
|
226
|
+
}
|
|
227
|
+
finally {
|
|
228
|
+
drop();
|
|
229
|
+
}
|
|
230
|
+
});
|
|
231
|
+
it("keeps the registration when a listener throws", () => {
|
|
232
|
+
// BeEvent routes listener exceptions to UnexpectedErrors rather than to the caller.
|
|
233
|
+
const unexpected = [];
|
|
234
|
+
const previousHandler = UnexpectedErrors.setHandler((e) => unexpected.push(e));
|
|
235
|
+
const drop = ElementDrivesTextAnnotation.onFieldFormattingChanged.addListener(() => {
|
|
236
|
+
throw new Error("listener failure");
|
|
237
|
+
});
|
|
238
|
+
try {
|
|
239
|
+
expect(() => registerSets([{ id: PRIMARY_FORMAT_SET, formats: mmSet() }])).not.to.throw();
|
|
240
|
+
}
|
|
241
|
+
finally {
|
|
242
|
+
drop();
|
|
243
|
+
UnexpectedErrors.setHandler(previousHandler);
|
|
244
|
+
}
|
|
245
|
+
expect(unexpected.length).to.equal(1);
|
|
246
|
+
const annotationElementId = insertAnnotationWithLengthField(sourceElementId);
|
|
247
|
+
expect(readFieldCachedContentById(annotationElementId)).to.equal("2500 mm");
|
|
248
|
+
});
|
|
249
|
+
it("selects the KindOfQuantity's presentation format for the registered unitSystem", () => {
|
|
250
|
+
// DUAL_LENGTH lists a metric and an imperial presentation format. The unit system chosen
|
|
251
|
+
// at registration decides which one the schema provider hands back, and an explicit
|
|
252
|
+
// `unitSystem` wins over the adopted FormatSet's own.
|
|
253
|
+
// Persisted on the element: dualLengthProp 2.5 m
|
|
254
|
+
const evaluate = () => {
|
|
255
|
+
const block = TextBlock.create();
|
|
256
|
+
const field = FieldRun.create({
|
|
257
|
+
propertyHost: propertyHost(sourceElementId),
|
|
258
|
+
propertyPath: { propertyName: "dualLengthProp" },
|
|
259
|
+
cachedContent: "old",
|
|
260
|
+
});
|
|
261
|
+
block.appendRun(field);
|
|
262
|
+
ElementDrivesTextAnnotation.evaluateFields({ iModel: imodel, block });
|
|
263
|
+
return field.cachedContent;
|
|
264
|
+
};
|
|
265
|
+
// Nothing registered: metric.
|
|
266
|
+
expect(evaluate()).to.equal("2.5 m");
|
|
267
|
+
// An explicit unit system alone.
|
|
268
|
+
ElementDrivesTextAnnotation.registerFieldFormatting({ iModel: imodel, unitSystem: "imperial" });
|
|
269
|
+
expect(evaluate()).to.equal("8.2021 ft");
|
|
270
|
+
// An adopted metric FormatSet that does not define the key defers to the schema, using the
|
|
271
|
+
// set's own system when none is given and the explicit one when it is.
|
|
272
|
+
const metricSet = toFormatSet("Adopted", mmSet());
|
|
273
|
+
ElementDrivesTextAnnotation.registerFieldFormatting({ iModel: imodel, formatSet: metricSet });
|
|
274
|
+
expect(evaluate()).to.equal("2.5 m");
|
|
275
|
+
ElementDrivesTextAnnotation.registerFieldFormatting({ iModel: imodel, formatSet: metricSet, unitSystem: "imperial" });
|
|
276
|
+
expect(evaluate()).to.equal("8.2021 ft");
|
|
277
|
+
});
|
|
278
|
+
it("formats through the schema default on the txn callback path when nothing is registered", () => {
|
|
279
|
+
const annotationElementId = insertAnnotationWithLengthField(sourceElementId, undefined);
|
|
280
|
+
expect(readFieldCachedContentById(annotationElementId)).to.equal("2.5 m");
|
|
281
|
+
});
|
|
282
|
+
it("routes txn-driven field updates through the registered FormatSet", () => {
|
|
283
|
+
registerSets([{ id: PRIMARY_FORMAT_SET, formats: mmSet() }]);
|
|
284
|
+
const annotationElementId = insertAnnotationWithLengthField(sourceElementId);
|
|
285
|
+
expect(readFieldCachedContentById(annotationElementId)).to.equal("2500 mm");
|
|
286
|
+
});
|
|
287
|
+
it("documents the registration-lifecycle contract for persisted cachedContent", () => {
|
|
288
|
+
// Deliberately one narrative test rather than one per step: this is documentation of an
|
|
289
|
+
// accepted contract, not a bug under guard. The contract is that registering never walks
|
|
290
|
+
// existing annotations, so persisted cachedContent changes only on the next source-element
|
|
291
|
+
// edit -- which formats according to whatever happens to be registered at that moment.
|
|
292
|
+
const sourceId = withEditTxn(imodel, (txn) => txn.insertElement(sourceElementProps()));
|
|
293
|
+
// 1. Nothing registered: insert persists the schema's presentation format.
|
|
294
|
+
const annotationElementId = insertAnnotationWithLengthField(sourceId);
|
|
295
|
+
expect(readFieldCachedContentById(annotationElementId)).to.equal("2.5 m");
|
|
296
|
+
// 2. Registering is not retroactive; persisted content is untouched.
|
|
297
|
+
registerSets([{ id: PRIMARY_FORMAT_SET, formats: mmSet() }]);
|
|
298
|
+
expect(readFieldCachedContentById(annotationElementId)).to.equal("2.5 m");
|
|
299
|
+
// 3. The next source edit fires the txn callback, which routes through the FormatSet.
|
|
300
|
+
const source = imodel.elements.getElement(sourceId);
|
|
301
|
+
source.lengthProp = 4.25;
|
|
302
|
+
withEditTxn(imodel, "source update", (txn) => {
|
|
303
|
+
source.update(txn);
|
|
304
|
+
txn.saveChanges("source update");
|
|
305
|
+
});
|
|
306
|
+
expect(readFieldCachedContentById(annotationElementId)).to.equal("4250 mm");
|
|
307
|
+
// 4. Reverting to the schema default is likewise not retroactive...
|
|
308
|
+
ElementDrivesTextAnnotation.registerFieldFormatting({ iModel: imodel });
|
|
309
|
+
expect(readFieldCachedContentById(annotationElementId)).to.equal("4250 mm");
|
|
310
|
+
// ...but the following edit falls back to the schema default and overwrites the FormatSet's
|
|
311
|
+
// millimeters with the schema's meters. This is why registerFieldFormatting's docs tell
|
|
312
|
+
// hosts to swap FormatSets with a single re-register rather than reverting first.
|
|
313
|
+
const reloadedSource = imodel.elements.getElement(sourceId);
|
|
314
|
+
reloadedSource.lengthProp = 3.5;
|
|
315
|
+
withEditTxn(imodel, "source update after revert", (txn) => {
|
|
316
|
+
reloadedSource.update(txn);
|
|
317
|
+
txn.saveChanges("source update after revert");
|
|
318
|
+
});
|
|
319
|
+
expect(readFieldCachedContentById(annotationElementId)).to.equal("3.5 m");
|
|
320
|
+
});
|
|
321
|
+
});
|
|
322
|
+
});
|
|
323
|
+
//# sourceMappingURL=FieldFormattingLifecycle.test.js.map
|