@itwin/core-backend 5.9.0-dev.3 → 5.9.0-dev.5

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.
Files changed (426) hide show
  1. package/lib/cjs/BriefcaseManager.js +3 -3
  2. package/lib/cjs/BriefcaseManager.js.map +1 -1
  3. package/lib/cjs/Category.d.ts +37 -7
  4. package/lib/cjs/Category.d.ts.map +1 -1
  5. package/lib/cjs/Category.js +33 -42
  6. package/lib/cjs/Category.js.map +1 -1
  7. package/lib/cjs/ChangesetECAdaptor.d.ts.map +1 -1
  8. package/lib/cjs/ChangesetECAdaptor.js +5 -2
  9. package/lib/cjs/ChangesetECAdaptor.js.map +1 -1
  10. package/lib/cjs/ChannelControl.d.ts +29 -0
  11. package/lib/cjs/ChannelControl.d.ts.map +1 -1
  12. package/lib/cjs/ChannelControl.js.map +1 -1
  13. package/lib/cjs/CheckpointManager.js +1 -1
  14. package/lib/cjs/CheckpointManager.js.map +1 -1
  15. package/lib/cjs/CodeSpecs.d.ts +32 -3
  16. package/lib/cjs/CodeSpecs.d.ts.map +1 -1
  17. package/lib/cjs/CodeSpecs.js +48 -20
  18. package/lib/cjs/CodeSpecs.js.map +1 -1
  19. package/lib/cjs/DisplayStyle.d.ts +18 -3
  20. package/lib/cjs/DisplayStyle.d.ts.map +1 -1
  21. package/lib/cjs/DisplayStyle.js +10 -21
  22. package/lib/cjs/DisplayStyle.js.map +1 -1
  23. package/lib/cjs/EditTxn.d.ts +262 -0
  24. package/lib/cjs/EditTxn.d.ts.map +1 -0
  25. package/lib/cjs/EditTxn.js +548 -0
  26. package/lib/cjs/EditTxn.js.map +1 -0
  27. package/lib/cjs/Element.d.ts +74 -5
  28. package/lib/cjs/Element.d.ts.map +1 -1
  29. package/lib/cjs/Element.js +63 -75
  30. package/lib/cjs/Element.js.map +1 -1
  31. package/lib/cjs/ElementAspect.d.ts +10 -0
  32. package/lib/cjs/ElementAspect.d.ts.map +1 -1
  33. package/lib/cjs/ElementAspect.js +21 -12
  34. package/lib/cjs/ElementAspect.js.map +1 -1
  35. package/lib/cjs/ElementTreeWalker.d.ts +56 -3
  36. package/lib/cjs/ElementTreeWalker.d.ts.map +1 -1
  37. package/lib/cjs/ElementTreeWalker.js +53 -40
  38. package/lib/cjs/ElementTreeWalker.js.map +1 -1
  39. package/lib/cjs/ExternalSource.d.ts +11 -2
  40. package/lib/cjs/ExternalSource.d.ts.map +1 -1
  41. package/lib/cjs/ExternalSource.js +10 -8
  42. package/lib/cjs/ExternalSource.js.map +1 -1
  43. package/lib/cjs/IModelDb.d.ts +66 -22
  44. package/lib/cjs/IModelDb.d.ts.map +1 -1
  45. package/lib/cjs/IModelDb.js +174 -288
  46. package/lib/cjs/IModelDb.js.map +1 -1
  47. package/lib/cjs/IModelHost.d.ts +22 -0
  48. package/lib/cjs/IModelHost.d.ts.map +1 -1
  49. package/lib/cjs/IModelHost.js +9 -0
  50. package/lib/cjs/IModelHost.js.map +1 -1
  51. package/lib/cjs/IpcHost.js +2 -2
  52. package/lib/cjs/IpcHost.js.map +1 -1
  53. package/lib/cjs/LineStyle.d.ts +47 -7
  54. package/lib/cjs/LineStyle.d.ts.map +1 -1
  55. package/lib/cjs/LineStyle.js +38 -33
  56. package/lib/cjs/LineStyle.js.map +1 -1
  57. package/lib/cjs/Material.d.ts +8 -1
  58. package/lib/cjs/Material.d.ts.map +1 -1
  59. package/lib/cjs/Material.js +6 -12
  60. package/lib/cjs/Material.js.map +1 -1
  61. package/lib/cjs/Model.d.ts +59 -20
  62. package/lib/cjs/Model.d.ts.map +1 -1
  63. package/lib/cjs/Model.js +38 -80
  64. package/lib/cjs/Model.js.map +1 -1
  65. package/lib/cjs/Relationship.d.ts +72 -7
  66. package/lib/cjs/Relationship.d.ts.map +1 -1
  67. package/lib/cjs/Relationship.js +39 -21
  68. package/lib/cjs/Relationship.js.map +1 -1
  69. package/lib/cjs/SchemaSync.js +4 -4
  70. package/lib/cjs/SchemaSync.js.map +1 -1
  71. package/lib/cjs/SheetIndex.d.ts +9 -0
  72. package/lib/cjs/SheetIndex.d.ts.map +1 -1
  73. package/lib/cjs/SheetIndex.js +38 -35
  74. package/lib/cjs/SheetIndex.js.map +1 -1
  75. package/lib/cjs/StashManager.js +1 -1
  76. package/lib/cjs/StashManager.js.map +1 -1
  77. package/lib/cjs/Texture.d.ts +6 -0
  78. package/lib/cjs/Texture.d.ts.map +1 -1
  79. package/lib/cjs/Texture.js +6 -14
  80. package/lib/cjs/Texture.js.map +1 -1
  81. package/lib/cjs/TxnManager.d.ts +5 -5
  82. package/lib/cjs/TxnManager.d.ts.map +1 -1
  83. package/lib/cjs/TxnManager.js +23 -7
  84. package/lib/cjs/TxnManager.js.map +1 -1
  85. package/lib/cjs/ViewDefinition.d.ts +21 -1
  86. package/lib/cjs/ViewDefinition.d.ts.map +1 -1
  87. package/lib/cjs/ViewDefinition.js +27 -66
  88. package/lib/cjs/ViewDefinition.js.map +1 -1
  89. package/lib/cjs/annotations/ElementDrivesTextAnnotation.d.ts +14 -6
  90. package/lib/cjs/annotations/ElementDrivesTextAnnotation.d.ts.map +1 -1
  91. package/lib/cjs/annotations/ElementDrivesTextAnnotation.js +33 -27
  92. package/lib/cjs/annotations/ElementDrivesTextAnnotation.js.map +1 -1
  93. package/lib/cjs/annotations/TextAnnotationElement.d.ts.map +1 -1
  94. package/lib/cjs/annotations/TextAnnotationElement.js +6 -5
  95. package/lib/cjs/annotations/TextAnnotationElement.js.map +1 -1
  96. package/lib/cjs/core-backend.d.ts +1 -0
  97. package/lib/cjs/core-backend.d.ts.map +1 -1
  98. package/lib/cjs/core-backend.js +1 -0
  99. package/lib/cjs/core-backend.js.map +1 -1
  100. package/lib/cjs/domains/FunctionalElements.d.ts +6 -2
  101. package/lib/cjs/domains/FunctionalElements.d.ts.map +1 -1
  102. package/lib/cjs/domains/FunctionalElements.js +8 -13
  103. package/lib/cjs/domains/FunctionalElements.js.map +1 -1
  104. package/lib/cjs/domains/GenericElements.d.ts +11 -4
  105. package/lib/cjs/domains/GenericElements.d.ts.map +1 -1
  106. package/lib/cjs/domains/GenericElements.js +13 -24
  107. package/lib/cjs/domains/GenericElements.js.map +1 -1
  108. package/lib/cjs/internal/ChannelAdmin.d.ts +15 -0
  109. package/lib/cjs/internal/ChannelAdmin.d.ts.map +1 -1
  110. package/lib/cjs/internal/ChannelAdmin.js +5 -3
  111. package/lib/cjs/internal/ChannelAdmin.js.map +1 -1
  112. package/lib/cjs/internal/Symbols.d.ts +4 -0
  113. package/lib/cjs/internal/Symbols.d.ts.map +1 -1
  114. package/lib/cjs/internal/Symbols.js +5 -1
  115. package/lib/cjs/internal/Symbols.js.map +1 -1
  116. package/lib/cjs/internal/annotations/fields.d.ts +3 -2
  117. package/lib/cjs/internal/annotations/fields.d.ts.map +1 -1
  118. package/lib/cjs/internal/annotations/fields.js +7 -6
  119. package/lib/cjs/internal/annotations/fields.js.map +1 -1
  120. package/lib/cjs/rpc-impl/RpcBriefcaseUtility.d.ts.map +1 -1
  121. package/lib/cjs/rpc-impl/RpcBriefcaseUtility.js.map +1 -1
  122. package/lib/esm/BriefcaseManager.js +3 -3
  123. package/lib/esm/BriefcaseManager.js.map +1 -1
  124. package/lib/esm/Category.d.ts +37 -7
  125. package/lib/esm/Category.d.ts.map +1 -1
  126. package/lib/esm/Category.js +33 -42
  127. package/lib/esm/Category.js.map +1 -1
  128. package/lib/esm/ChangesetECAdaptor.d.ts.map +1 -1
  129. package/lib/esm/ChangesetECAdaptor.js +5 -2
  130. package/lib/esm/ChangesetECAdaptor.js.map +1 -1
  131. package/lib/esm/ChannelControl.d.ts +29 -0
  132. package/lib/esm/ChannelControl.d.ts.map +1 -1
  133. package/lib/esm/ChannelControl.js.map +1 -1
  134. package/lib/esm/CheckpointManager.js +1 -1
  135. package/lib/esm/CheckpointManager.js.map +1 -1
  136. package/lib/esm/CodeSpecs.d.ts +32 -3
  137. package/lib/esm/CodeSpecs.d.ts.map +1 -1
  138. package/lib/esm/CodeSpecs.js +49 -21
  139. package/lib/esm/CodeSpecs.js.map +1 -1
  140. package/lib/esm/DisplayStyle.d.ts +18 -3
  141. package/lib/esm/DisplayStyle.d.ts.map +1 -1
  142. package/lib/esm/DisplayStyle.js +10 -21
  143. package/lib/esm/DisplayStyle.js.map +1 -1
  144. package/lib/esm/EditTxn.d.ts +262 -0
  145. package/lib/esm/EditTxn.d.ts.map +1 -0
  146. package/lib/esm/EditTxn.js +543 -0
  147. package/lib/esm/EditTxn.js.map +1 -0
  148. package/lib/esm/Element.d.ts +74 -5
  149. package/lib/esm/Element.d.ts.map +1 -1
  150. package/lib/esm/Element.js +64 -76
  151. package/lib/esm/Element.js.map +1 -1
  152. package/lib/esm/ElementAspect.d.ts +10 -0
  153. package/lib/esm/ElementAspect.d.ts.map +1 -1
  154. package/lib/esm/ElementAspect.js +22 -13
  155. package/lib/esm/ElementAspect.js.map +1 -1
  156. package/lib/esm/ElementTreeWalker.d.ts +56 -3
  157. package/lib/esm/ElementTreeWalker.d.ts.map +1 -1
  158. package/lib/esm/ElementTreeWalker.js +53 -40
  159. package/lib/esm/ElementTreeWalker.js.map +1 -1
  160. package/lib/esm/ExternalSource.d.ts +11 -2
  161. package/lib/esm/ExternalSource.d.ts.map +1 -1
  162. package/lib/esm/ExternalSource.js +10 -8
  163. package/lib/esm/ExternalSource.js.map +1 -1
  164. package/lib/esm/IModelDb.d.ts +66 -22
  165. package/lib/esm/IModelDb.d.ts.map +1 -1
  166. package/lib/esm/IModelDb.js +176 -290
  167. package/lib/esm/IModelDb.js.map +1 -1
  168. package/lib/esm/IModelHost.d.ts +22 -0
  169. package/lib/esm/IModelHost.d.ts.map +1 -1
  170. package/lib/esm/IModelHost.js +9 -0
  171. package/lib/esm/IModelHost.js.map +1 -1
  172. package/lib/esm/IpcHost.js +3 -3
  173. package/lib/esm/IpcHost.js.map +1 -1
  174. package/lib/esm/LineStyle.d.ts +47 -7
  175. package/lib/esm/LineStyle.d.ts.map +1 -1
  176. package/lib/esm/LineStyle.js +38 -33
  177. package/lib/esm/LineStyle.js.map +1 -1
  178. package/lib/esm/Material.d.ts +8 -1
  179. package/lib/esm/Material.d.ts.map +1 -1
  180. package/lib/esm/Material.js +6 -12
  181. package/lib/esm/Material.js.map +1 -1
  182. package/lib/esm/Model.d.ts +59 -20
  183. package/lib/esm/Model.d.ts.map +1 -1
  184. package/lib/esm/Model.js +39 -81
  185. package/lib/esm/Model.js.map +1 -1
  186. package/lib/esm/Relationship.d.ts +72 -7
  187. package/lib/esm/Relationship.d.ts.map +1 -1
  188. package/lib/esm/Relationship.js +40 -22
  189. package/lib/esm/Relationship.js.map +1 -1
  190. package/lib/esm/SchemaSync.js +5 -5
  191. package/lib/esm/SchemaSync.js.map +1 -1
  192. package/lib/esm/SheetIndex.d.ts +9 -0
  193. package/lib/esm/SheetIndex.d.ts.map +1 -1
  194. package/lib/esm/SheetIndex.js +38 -35
  195. package/lib/esm/SheetIndex.js.map +1 -1
  196. package/lib/esm/StashManager.js +1 -1
  197. package/lib/esm/StashManager.js.map +1 -1
  198. package/lib/esm/Texture.d.ts +6 -0
  199. package/lib/esm/Texture.d.ts.map +1 -1
  200. package/lib/esm/Texture.js +6 -14
  201. package/lib/esm/Texture.js.map +1 -1
  202. package/lib/esm/TxnManager.d.ts +5 -5
  203. package/lib/esm/TxnManager.d.ts.map +1 -1
  204. package/lib/esm/TxnManager.js +23 -7
  205. package/lib/esm/TxnManager.js.map +1 -1
  206. package/lib/esm/ViewDefinition.d.ts +21 -1
  207. package/lib/esm/ViewDefinition.d.ts.map +1 -1
  208. package/lib/esm/ViewDefinition.js +27 -66
  209. package/lib/esm/ViewDefinition.js.map +1 -1
  210. package/lib/esm/annotations/ElementDrivesTextAnnotation.d.ts +14 -6
  211. package/lib/esm/annotations/ElementDrivesTextAnnotation.d.ts.map +1 -1
  212. package/lib/esm/annotations/ElementDrivesTextAnnotation.js +33 -27
  213. package/lib/esm/annotations/ElementDrivesTextAnnotation.js.map +1 -1
  214. package/lib/esm/annotations/TextAnnotationElement.d.ts.map +1 -1
  215. package/lib/esm/annotations/TextAnnotationElement.js +6 -5
  216. package/lib/esm/annotations/TextAnnotationElement.js.map +1 -1
  217. package/lib/esm/core-backend.d.ts +1 -0
  218. package/lib/esm/core-backend.d.ts.map +1 -1
  219. package/lib/esm/core-backend.js +1 -0
  220. package/lib/esm/core-backend.js.map +1 -1
  221. package/lib/esm/domains/FunctionalElements.d.ts +6 -2
  222. package/lib/esm/domains/FunctionalElements.d.ts.map +1 -1
  223. package/lib/esm/domains/FunctionalElements.js +8 -13
  224. package/lib/esm/domains/FunctionalElements.js.map +1 -1
  225. package/lib/esm/domains/GenericElements.d.ts +11 -4
  226. package/lib/esm/domains/GenericElements.d.ts.map +1 -1
  227. package/lib/esm/domains/GenericElements.js +13 -24
  228. package/lib/esm/domains/GenericElements.js.map +1 -1
  229. package/lib/esm/internal/ChannelAdmin.d.ts +15 -0
  230. package/lib/esm/internal/ChannelAdmin.d.ts.map +1 -1
  231. package/lib/esm/internal/ChannelAdmin.js +6 -4
  232. package/lib/esm/internal/ChannelAdmin.js.map +1 -1
  233. package/lib/esm/internal/Symbols.d.ts +4 -0
  234. package/lib/esm/internal/Symbols.d.ts.map +1 -1
  235. package/lib/esm/internal/Symbols.js +4 -0
  236. package/lib/esm/internal/Symbols.js.map +1 -1
  237. package/lib/esm/internal/annotations/fields.d.ts +3 -2
  238. package/lib/esm/internal/annotations/fields.d.ts.map +1 -1
  239. package/lib/esm/internal/annotations/fields.js +7 -6
  240. package/lib/esm/internal/annotations/fields.js.map +1 -1
  241. package/lib/esm/rpc-impl/RpcBriefcaseUtility.d.ts.map +1 -1
  242. package/lib/esm/rpc-impl/RpcBriefcaseUtility.js.map +1 -1
  243. package/lib/esm/test/AnnotationTestUtils.js.map +1 -1
  244. package/lib/esm/test/ElementDrivesElement.test.d.ts +20 -19
  245. package/lib/esm/test/ElementDrivesElement.test.d.ts.map +1 -1
  246. package/lib/esm/test/ElementDrivesElement.test.js +111 -96
  247. package/lib/esm/test/ElementDrivesElement.test.js.map +1 -1
  248. package/lib/esm/test/ElementLRUCache.test.js.map +1 -1
  249. package/lib/esm/test/IModelHost.test.js +56 -2
  250. package/lib/esm/test/IModelHost.test.js.map +1 -1
  251. package/lib/esm/test/IModelTestUtils.d.ts +23 -23
  252. package/lib/esm/test/IModelTestUtils.d.ts.map +1 -1
  253. package/lib/esm/test/IModelTestUtils.js +466 -449
  254. package/lib/esm/test/IModelTestUtils.js.map +1 -1
  255. package/lib/esm/test/PropertyDb.test.js +2 -2
  256. package/lib/esm/test/PropertyDb.test.js.map +1 -1
  257. package/lib/esm/test/SquashSchemaAndDataChanges.test.js +27 -18
  258. package/lib/esm/test/SquashSchemaAndDataChanges.test.js.map +1 -1
  259. package/lib/esm/test/TestChangeSetUtility.d.ts.map +1 -1
  260. package/lib/esm/test/TestChangeSetUtility.js +11 -7
  261. package/lib/esm/test/TestChangeSetUtility.js.map +1 -1
  262. package/lib/esm/test/TestEditTxn.d.ts +8 -0
  263. package/lib/esm/test/TestEditTxn.d.ts.map +1 -0
  264. package/lib/esm/test/TestEditTxn.js +34 -0
  265. package/lib/esm/test/TestEditTxn.js.map +1 -0
  266. package/lib/esm/test/TestUtils.d.ts +1 -0
  267. package/lib/esm/test/TestUtils.d.ts.map +1 -1
  268. package/lib/esm/test/TestUtils.js +8 -1
  269. package/lib/esm/test/TestUtils.js.map +1 -1
  270. package/lib/esm/test/annotations/Fields.test.js +82 -90
  271. package/lib/esm/test/annotations/Fields.test.js.map +1 -1
  272. package/lib/esm/test/annotations/FrameGeometry.test.js.map +1 -1
  273. package/lib/esm/test/annotations/TextAnnotation.test.js +156 -99
  274. package/lib/esm/test/annotations/TextAnnotation.test.js.map +1 -1
  275. package/lib/esm/test/annotations/TextBlock.test.js +5 -3
  276. package/lib/esm/test/annotations/TextBlock.test.js.map +1 -1
  277. package/lib/esm/test/assets/IncrementalSchemaLocater/configs/old.config.js.map +1 -1
  278. package/lib/esm/test/assets/IncrementalSchemaLocater/configs/simple.config.js.map +1 -1
  279. package/lib/esm/test/categories/Category.test.js +63 -3
  280. package/lib/esm/test/categories/Category.test.js.map +1 -1
  281. package/lib/esm/test/codespec/CodeSpec.test.js +88 -5
  282. package/lib/esm/test/codespec/CodeSpec.test.js.map +1 -1
  283. package/lib/esm/test/ecdb/ECDb.test.js.map +1 -1
  284. package/lib/esm/test/ecdb/ECSqlAst.test.js +3 -2
  285. package/lib/esm/test/ecdb/ECSqlAst.test.js.map +1 -1
  286. package/lib/esm/test/ecdb/ECSqlQuery.test.js +2 -2
  287. package/lib/esm/test/ecdb/ECSqlQuery.test.js.map +1 -1
  288. package/lib/esm/test/ecdb/ECSqlStatement.test.js +0 -1
  289. package/lib/esm/test/ecdb/ECSqlStatement.test.js.map +1 -1
  290. package/lib/esm/test/ecdb/QueryReaders.test.js +17 -14
  291. package/lib/esm/test/ecdb/QueryReaders.test.js.map +1 -1
  292. package/lib/esm/test/ecdb/SqliteStatement.test.js +2 -2
  293. package/lib/esm/test/ecdb/SqliteStatement.test.js.map +1 -1
  294. package/lib/esm/test/ecsql/dataset/ECSqlDatasets.d.ts.map +1 -1
  295. package/lib/esm/test/ecsql/dataset/ECSqlDatasets.js +30 -28
  296. package/lib/esm/test/ecsql/dataset/ECSqlDatasets.js.map +1 -1
  297. package/lib/esm/test/ecsql/src/ECSqlTestGenerator.js.map +1 -1
  298. package/lib/esm/test/ecsql/src/ECSqlTestParser.js.map +1 -1
  299. package/lib/esm/test/ecsql/src/ECSqlTestRunner.test.js.map +1 -1
  300. package/lib/esm/test/element/DeleteDefinitionElements.test.js +159 -143
  301. package/lib/esm/test/element/DeleteDefinitionElements.test.js.map +1 -1
  302. package/lib/esm/test/element/ElementAspect.test.js +68 -60
  303. package/lib/esm/test/element/ElementAspect.test.js.map +1 -1
  304. package/lib/esm/test/element/ElementDependencyGraph.test.d.ts.map +1 -1
  305. package/lib/esm/test/element/ElementDependencyGraph.test.js +51 -43
  306. package/lib/esm/test/element/ElementDependencyGraph.test.js.map +1 -1
  307. package/lib/esm/test/element/ElementRoundTrip.test.js +37 -38
  308. package/lib/esm/test/element/ElementRoundTrip.test.js.map +1 -1
  309. package/lib/esm/test/element/ExcludedElements.test.js +2 -2
  310. package/lib/esm/test/element/ExcludedElements.test.js.map +1 -1
  311. package/lib/esm/test/element/ExternalSource.test.js +40 -38
  312. package/lib/esm/test/element/ExternalSource.test.js.map +1 -1
  313. package/lib/esm/test/element/NullStructArray.test.js +10 -9
  314. package/lib/esm/test/element/NullStructArray.test.js.map +1 -1
  315. package/lib/esm/test/element/ProjectInformationRecord.test.js +5 -2
  316. package/lib/esm/test/element/ProjectInformationRecord.test.js.map +1 -1
  317. package/lib/esm/test/element/SheetInformationAspect.test.js +43 -11
  318. package/lib/esm/test/element/SheetInformationAspect.test.js.map +1 -1
  319. package/lib/esm/test/element/UrlLink.test.js +2 -2
  320. package/lib/esm/test/element/UrlLink.test.js.map +1 -1
  321. package/lib/esm/test/font/IModelDbFonts.test.js +87 -73
  322. package/lib/esm/test/font/IModelDbFonts.test.js.map +1 -1
  323. package/lib/esm/test/hubaccess/ApplyChangeset.test.js +164 -125
  324. package/lib/esm/test/hubaccess/ApplyChangeset.test.js.map +1 -1
  325. package/lib/esm/test/hubaccess/BriefcaseManager.test.js +2 -4
  326. package/lib/esm/test/hubaccess/BriefcaseManager.test.js.map +1 -1
  327. package/lib/esm/test/hubaccess/Rebase.test.js +313 -247
  328. package/lib/esm/test/hubaccess/Rebase.test.js.map +1 -1
  329. package/lib/esm/test/hubaccess/SemanticRebase.test.js +322 -247
  330. package/lib/esm/test/hubaccess/SemanticRebase.test.js.map +1 -1
  331. package/lib/esm/test/imodel/Code.test.js +31 -31
  332. package/lib/esm/test/imodel/Code.test.js.map +1 -1
  333. package/lib/esm/test/imodel/ElementTreeWalker.test.js +57 -48
  334. package/lib/esm/test/imodel/ElementTreeWalker.test.js.map +1 -1
  335. package/lib/esm/test/imodel/IModel.test.js +419 -344
  336. package/lib/esm/test/imodel/IModel.test.js.map +1 -1
  337. package/lib/esm/test/imodel/ProjectExtents.test.js +2 -2
  338. package/lib/esm/test/imodel/ProjectExtents.test.js.map +1 -1
  339. package/lib/esm/test/incrementalSchemaLocater/ECSqlQueries.test.js.map +1 -1
  340. package/lib/esm/test/incrementalSchemaLocater/TestContext.d.ts.map +1 -1
  341. package/lib/esm/test/incrementalSchemaLocater/TestContext.js +2 -2
  342. package/lib/esm/test/incrementalSchemaLocater/TestContext.js.map +1 -1
  343. package/lib/esm/test/index.d.ts +1 -0
  344. package/lib/esm/test/index.d.ts.map +1 -1
  345. package/lib/esm/test/index.js +1 -0
  346. package/lib/esm/test/index.js.map +1 -1
  347. package/lib/esm/test/schema/ClassRegistry.test.js +23 -22
  348. package/lib/esm/test/schema/ClassRegistry.test.js.map +1 -1
  349. package/lib/esm/test/schema/FunctionalDomain.test.js +36 -34
  350. package/lib/esm/test/schema/FunctionalDomain.test.js.map +1 -1
  351. package/lib/esm/test/schema/GenericDomain.test.js +114 -94
  352. package/lib/esm/test/schema/GenericDomain.test.js.map +1 -1
  353. package/lib/esm/test/schema/IModelSchemaContext.test.js +2 -1
  354. package/lib/esm/test/schema/IModelSchemaContext.test.js.map +1 -1
  355. package/lib/esm/test/schema/SchemaImportCallbacks.test.js +207 -192
  356. package/lib/esm/test/schema/SchemaImportCallbacks.test.js.map +1 -1
  357. package/lib/esm/test/sheetindex/SheetIndex.test.d.ts +1 -3
  358. package/lib/esm/test/sheetindex/SheetIndex.test.d.ts.map +1 -1
  359. package/lib/esm/test/sheetindex/SheetIndex.test.js +312 -247
  360. package/lib/esm/test/sheetindex/SheetIndex.test.js.map +1 -1
  361. package/lib/esm/test/standalone/ChangeMerge.test.js +101 -82
  362. package/lib/esm/test/standalone/ChangeMerge.test.js.map +1 -1
  363. package/lib/esm/test/standalone/ChangesetReader.test.js +114 -85
  364. package/lib/esm/test/standalone/ChangesetReader.test.js.map +1 -1
  365. package/lib/esm/test/standalone/DisplayStyle.test.js +43 -40
  366. package/lib/esm/test/standalone/DisplayStyle.test.js.map +1 -1
  367. package/lib/esm/test/standalone/Drawing.test.js +4 -3
  368. package/lib/esm/test/standalone/Drawing.test.js.map +1 -1
  369. package/lib/esm/test/standalone/EditTxn.test.d.ts +2 -0
  370. package/lib/esm/test/standalone/EditTxn.test.d.ts.map +1 -0
  371. package/lib/esm/test/standalone/EditTxn.test.js +219 -0
  372. package/lib/esm/test/standalone/EditTxn.test.js.map +1 -0
  373. package/lib/esm/test/standalone/ElementMesh.test.js +16 -13
  374. package/lib/esm/test/standalone/ElementMesh.test.js.map +1 -1
  375. package/lib/esm/test/standalone/ExportGraphics.test.js +26 -20
  376. package/lib/esm/test/standalone/ExportGraphics.test.js.map +1 -1
  377. package/lib/esm/test/standalone/GeometryChangeEvents.test.js +11 -15
  378. package/lib/esm/test/standalone/GeometryChangeEvents.test.js.map +1 -1
  379. package/lib/esm/test/standalone/GeometryStream.test.js +212 -165
  380. package/lib/esm/test/standalone/GeometryStream.test.js.map +1 -1
  381. package/lib/esm/test/standalone/HubMock.test.js +31 -25
  382. package/lib/esm/test/standalone/HubMock.test.js.map +1 -1
  383. package/lib/esm/test/standalone/IModelLimits.test.js +11 -8
  384. package/lib/esm/test/standalone/IModelLimits.test.js.map +1 -1
  385. package/lib/esm/test/standalone/IModelWrite.test.d.ts +2 -2
  386. package/lib/esm/test/standalone/IModelWrite.test.d.ts.map +1 -1
  387. package/lib/esm/test/standalone/IModelWrite.test.js +184 -142
  388. package/lib/esm/test/standalone/IModelWrite.test.js.map +1 -1
  389. package/lib/esm/test/standalone/InlineGeometryPartReferences.test.js +25 -22
  390. package/lib/esm/test/standalone/InlineGeometryPartReferences.test.js.map +1 -1
  391. package/lib/esm/test/standalone/IntegrityCheck.test.js +20 -18
  392. package/lib/esm/test/standalone/IntegrityCheck.test.js.map +1 -1
  393. package/lib/esm/test/standalone/MergeConflict.test.d.ts +2 -2
  394. package/lib/esm/test/standalone/MergeConflict.test.d.ts.map +1 -1
  395. package/lib/esm/test/standalone/MergeConflict.test.js +49 -33
  396. package/lib/esm/test/standalone/MergeConflict.test.js.map +1 -1
  397. package/lib/esm/test/standalone/RenderMaterialElement.test.js +5 -5
  398. package/lib/esm/test/standalone/RenderMaterialElement.test.js.map +1 -1
  399. package/lib/esm/test/standalone/RenderTimeline.test.js +3 -2
  400. package/lib/esm/test/standalone/RenderTimeline.test.js.map +1 -1
  401. package/lib/esm/test/standalone/SectionDrawing.test.js +7 -7
  402. package/lib/esm/test/standalone/SectionDrawing.test.js.map +1 -1
  403. package/lib/esm/test/standalone/ServerBasedLocks.test.js +21 -19
  404. package/lib/esm/test/standalone/ServerBasedLocks.test.js.map +1 -1
  405. package/lib/esm/test/standalone/Settings.test.js +7 -4
  406. package/lib/esm/test/standalone/Settings.test.js.map +1 -1
  407. package/lib/esm/test/standalone/SettingsSchemas.test.js +2 -1
  408. package/lib/esm/test/standalone/SettingsSchemas.test.js.map +1 -1
  409. package/lib/esm/test/standalone/SnapshotDb.test.js +3 -1
  410. package/lib/esm/test/standalone/SnapshotDb.test.js.map +1 -1
  411. package/lib/esm/test/standalone/StandaloneDb.test.js +7 -6
  412. package/lib/esm/test/standalone/StandaloneDb.test.js.map +1 -1
  413. package/lib/esm/test/standalone/Texture.test.js +5 -4
  414. package/lib/esm/test/standalone/Texture.test.js.map +1 -1
  415. package/lib/esm/test/standalone/TileCache.test.d.ts.map +1 -1
  416. package/lib/esm/test/standalone/TileCache.test.js +5 -3
  417. package/lib/esm/test/standalone/TileCache.test.js.map +1 -1
  418. package/lib/esm/test/standalone/TileTree.test.js +35 -31
  419. package/lib/esm/test/standalone/TileTree.test.js.map +1 -1
  420. package/lib/esm/test/standalone/TxnManager.test.js +700 -653
  421. package/lib/esm/test/standalone/TxnManager.test.js.map +1 -1
  422. package/lib/esm/test/standalone/ViewDefinition.test.js +295 -229
  423. package/lib/esm/test/standalone/ViewDefinition.test.js.map +1 -1
  424. package/lib/esm/test/standalone/Workspace.test.js +25 -23
  425. package/lib/esm/test/standalone/Workspace.test.js.map +1 -1
  426. package/package.json +12 -12
@@ -58,11 +58,34 @@ import { assert, expect } from "chai";
58
58
  import { BeDuration, Guid, Id64, IModelStatus, OpenMode } from "@itwin/core-bentley";
59
59
  import { LineSegment3d, Point3d, YawPitchRollAngles } from "@itwin/core-geometry";
60
60
  import { Code, ColorByName, DomainOptions, GeometryStreamBuilder, IModel, IModelError, SubCategoryAppearance, TxnAction, } from "@itwin/core-common";
61
- import { _nativeDb, ChannelControl, IModelJsFs, PhysicalModel, setMaxEntitiesPerEvent, SpatialCategory, StandaloneDb, } from "../../core-backend";
61
+ import { _nativeDb, ChannelControl, IModelDb, IModelJsFs, PhysicalModel, PhysicalPartition, setMaxEntitiesPerEvent, SpatialCategory, StandaloneDb, SubjectOwnsPartitionElements, withEditTxn, } from "../../core-backend";
62
62
  import { IModelTestUtils, TestElementDrivesElement, TestPhysicalObject } from "../IModelTestUtils";
63
63
  import { IModelNative } from "../../internal/NativePlatform";
64
64
  import { EntityClass, SchemaItemKey, SchemaKey } from "@itwin/ecschema-metadata";
65
65
  /// cspell:ignore accum
66
+ function insertPhysicalModel(txn, parentSubjectId, name, privateModel = false) {
67
+ const partition = txn.iModel.elements.createElement({
68
+ classFullName: PhysicalPartition.classFullName,
69
+ parent: new SubjectOwnsPartitionElements(parentSubjectId),
70
+ model: IModel.repositoryModelId,
71
+ code: PhysicalPartition.createCode(txn.iModel, parentSubjectId, name),
72
+ });
73
+ const partitionId = txn.insertElement(partition.toJSON());
74
+ const model = txn.iModel.models.createModel({
75
+ modeledElement: { id: partitionId },
76
+ classFullName: PhysicalModel.classFullName,
77
+ isPrivate: privateModel,
78
+ });
79
+ return txn.insertModel(model.toJSON());
80
+ }
81
+ function insertSpatialCategory(txn, definitionModelId, name, appearance) {
82
+ const category = SpatialCategory.create(txn.iModel, definitionModelId, name);
83
+ category.id = txn.insertElement(category.toJSON());
84
+ const subCategory = txn.iModel.elements.getElement(IModelDb.getDefaultSubCategoryId(category.id));
85
+ subCategory.appearance = appearance;
86
+ txn.updateElement(subCategory.toJSON());
87
+ return category.id;
88
+ }
66
89
  describe("TxnManager", () => {
67
90
  let imodel;
68
91
  let roImodel;
@@ -91,19 +114,22 @@ describe("TxnManager", () => {
91
114
  imodel.channels.addAllowedChannel(ChannelControl.sharedChannelName);
92
115
  const builder = new GeometryStreamBuilder();
93
116
  builder.appendGeometry(LineSegment3d.create(Point3d.createZero(), Point3d.create(5, 0, 0)));
94
- props = {
95
- classFullName: "TestBim:TestPhysicalObject",
96
- model: PhysicalModel.insert(imodel, IModel.rootSubjectId, "TestModel"),
97
- category: SpatialCategory.insert(imodel, IModel.dictionaryId, "MySpatialCategory", new SubCategoryAppearance({ color: ColorByName.darkRed })),
98
- code: Code.createEmpty(),
99
- intProperty: 100,
100
- placement: {
101
- origin: new Point3d(1, 2, 0),
102
- angles: new YawPitchRollAngles(),
103
- },
104
- geom: builder.geometryStream,
105
- };
106
- imodel.saveChanges("schema change");
117
+ props = withEditTxn(imodel, "schema change", (txn) => {
118
+ const model = insertPhysicalModel(txn, IModel.rootSubjectId, "TestModel");
119
+ const category = insertSpatialCategory(txn, IModel.dictionaryId, "MySpatialCategory", new SubCategoryAppearance({ color: ColorByName.darkRed }));
120
+ return {
121
+ classFullName: "TestBim:TestPhysicalObject",
122
+ model,
123
+ category,
124
+ code: Code.createEmpty(),
125
+ intProperty: 100,
126
+ placement: {
127
+ origin: new Point3d(1, 2, 0),
128
+ angles: new YawPitchRollAngles(),
129
+ },
130
+ geom: builder.geometryStream,
131
+ };
132
+ });
107
133
  imodel[_nativeDb].deleteAllTxns();
108
134
  roImodel = StandaloneDb.openFile(testFileName, OpenMode.Readonly);
109
135
  });
@@ -134,215 +160,217 @@ describe("TxnManager", () => {
134
160
  const elements = imodel.elements;
135
161
  const modelId = props.model;
136
162
  const cleanup = [];
137
- let model = models.getModel(modelId);
138
- assert.isUndefined(model.geometryGuid, "geometryGuid starts undefined");
139
- assert.isDefined(await imodel.schemaContext.getSchemaItem(new SchemaItemKey("TestPhysicalObject", new SchemaKey("TestBim", 1, 0, 0)), EntityClass), "TestPhysicalObject is present");
140
- const txns = imodel.txns;
141
- assert.isFalse(txns.hasPendingTxns);
142
- const change1Msg = "change 1";
143
- const change2Msg = "change 2";
144
- let beforeUndo = 0;
145
- let afterUndo = 0;
146
- let undoAction = TxnAction.None;
147
- cleanup.push(txns.onBeforeUndoRedo.addListener(() => beforeUndo++));
148
- cleanup.push(txns.onAfterUndoRedo.addListener((isUndo) => {
149
- afterUndo++;
150
- undoAction = isUndo ? TxnAction.Reverse : TxnAction.Reinstate;
151
- }));
152
- let elementId = elements.insertElement(props);
153
- assert.isFalse(txns.isRedoPossible);
154
- assert.isFalse(txns.isUndoPossible);
155
- assert.isTrue(txns.hasUnsavedChanges);
156
- assert.isFalse(txns.hasPendingTxns);
157
- imodel.saveChanges(change1Msg);
158
- assert.isFalse(txns.hasUnsavedChanges);
159
- assert.isTrue(txns.hasPendingTxns);
160
- assert.isTrue(txns.hasLocalChanges);
161
- expect(imodel[_nativeDb].getCurrentTxnId()).not.equal(roImodel[_nativeDb].getCurrentTxnId());
162
- roImodel[_nativeDb].restartDefaultTxn();
163
- expect(imodel[_nativeDb].getCurrentTxnId()).equal(roImodel[_nativeDb].getCurrentTxnId());
164
- const classId = imodel[_nativeDb].classNameToId(props.classFullName);
165
- assert.isTrue(Id64.isValid(classId));
166
- const class2 = imodel[_nativeDb].classIdToName(classId);
167
- assert.equal(class2, props.classFullName);
168
- model = models.getModel(modelId);
169
- assert.isDefined(model.geometryGuid);
170
- txns.reverseSingleTxn();
171
- assert.isFalse(txns.hasPendingTxns, "should not have pending txns if they all are reversed");
172
- assert.isFalse(txns.hasLocalChanges);
173
- txns.reinstateTxn();
174
- assert.isTrue(txns.hasPendingTxns, "now there should be pending txns again");
175
- assert.isTrue(txns.hasLocalChanges);
176
- beforeUndo = afterUndo = 0; // reset this for tests below
177
- model = models.getModel(modelId);
178
- assert.isDefined(model.geometryGuid);
179
- const guid1 = model.geometryGuid;
180
- let element = elements.getElement(elementId);
181
- assert.equal(element.intProperty, 100, "int property should be 100");
182
- assert.isTrue(txns.isUndoPossible); // we have an undoable Txn, but nothing undone.
183
- assert.equal(change1Msg, txns.getUndoString());
184
- assert.equal(IModelStatus.Success, txns.reverseSingleTxn());
185
- model = models.getModel(modelId);
186
- assert.isUndefined(model.geometryGuid, "geometryGuid undefined after undo");
187
- assert.isTrue(txns.isRedoPossible);
188
- assert.equal(change1Msg, txns.getRedoString());
189
- assert.equal(beforeUndo, 1);
190
- assert.equal(afterUndo, 1);
191
- assert.equal(undoAction, TxnAction.Reverse);
192
- assert.throws(() => elements.getElementProps(elementId), IModelError, "element not found");
193
- assert.throws(() => elements.getElement(elementId), IModelError);
194
- assert.equal(IModelStatus.Success, txns.reinstateTxn());
195
- model = models.getModel(modelId);
196
- assert.equal(model.geometryGuid, guid1, "geometryGuid should return redo");
197
- assert.isTrue(txns.isUndoPossible);
198
- assert.isFalse(txns.isRedoPossible);
199
- assert.equal(beforeUndo, 2);
200
- assert.equal(afterUndo, 2);
201
- assert.equal(undoAction, TxnAction.Reinstate);
202
- element = elements.getElement(elementId);
203
- element.intProperty = 200;
204
- element.update();
205
- imodel.saveChanges(change2Msg);
206
- model = models.getModel(modelId);
207
- assert.equal(model.geometryGuid, guid1, "geometryGuid should not update with no geometry changes");
208
- element = elements.getElement(elementId);
209
- assert.equal(element.intProperty, 200, "int property should be 200");
210
- assert.equal(txns.getTxnDescription(txns.queryPreviousTxnId(txns.getCurrentTxnId())), change2Msg);
211
- assert.equal(IModelStatus.Success, txns.reverseSingleTxn());
212
- element = elements.getElement(elementId);
213
- assert.equal(element.intProperty, 100, "int property should be 100");
214
- // make sure abandon changes works.
215
- element.delete();
216
- assert.throws(() => elements.getElement(elementId), IModelError);
217
- imodel.abandonChanges(); //
218
- element = elements.getElement(elementId); // should be back now.
219
- elements.insertElement(props); // create a new element
220
- imodel.saveChanges(change2Msg);
221
- model = models.getModel(modelId);
222
- assert.isDefined(model.geometryGuid);
223
- assert.notEqual(model.geometryGuid, guid1, "geometryGuid should update with adds");
224
- elementId = elements.insertElement(props); // create a new element
225
- assert.isTrue(txns.hasUnsavedChanges);
226
- assert.equal(IModelStatus.Success, txns.reverseSingleTxn());
227
- assert.isFalse(txns.hasUnsavedChanges);
228
- assert.throws(() => elements.getElement(elementId), IModelError); // reversing a txn with pending uncommitted changes should abandon them.
229
- assert.equal(IModelStatus.Success, txns.reinstateTxn());
230
- assert.throws(() => elements.getElement(elementId), IModelError); // doesn't come back, wasn't committed
231
- // verify multi-txn operations are undone/redone together
232
- const el1 = elements.insertElement(props);
233
- imodel.saveChanges("step 1");
234
- txns.beginMultiTxnOperation();
235
- assert.equal(1, txns.getMultiTxnOperationDepth());
236
- const el2 = elements.insertElement(props);
237
- imodel.saveChanges("step 2");
238
- const el3 = elements.insertElement(props);
239
- imodel.saveChanges("step 3");
240
- txns.endMultiTxnOperation();
241
- assert.equal(0, txns.getMultiTxnOperationDepth());
242
- assert.equal(IModelStatus.Success, txns.reverseSingleTxn());
243
- assert.throws(() => elements.getElement(el2), IModelError);
244
- assert.throws(() => elements.getElement(el3), IModelError);
245
- elements.getElement(el1);
246
- assert.equal(IModelStatus.Success, txns.reverseSingleTxn());
247
- assert.throws(() => elements.getElement(el1), IModelError);
248
- assert.equal(IModelStatus.Success, txns.reinstateTxn());
249
- assert.throws(() => elements.getElement(el2), IModelError);
250
- assert.throws(() => elements.getElement(el3), IModelError);
251
- elements.getElement(el1);
252
- assert.equal(IModelStatus.Success, txns.reinstateTxn());
253
- elements.getElement(el1);
254
- elements.getElement(el2);
255
- elements.getElement(el3);
256
- function insert2Elements() {
257
- const id0 = elements.insertElement(props);
258
- imodel.saveChanges();
259
- const id1 = elements.insertElement(props);
260
- imodel.saveChanges();
261
- return [id0, id1];
262
- }
263
- function expectElementExistences(ids, expectExists) {
264
- expect(elements.tryGetElementProps(ids[0]) !== undefined).to.equal(expectExists);
265
- expect(elements.tryGetElementProps(ids[0]) !== undefined).to.equal(expectExists);
266
- }
267
- function expectTxnDepth(expected) {
268
- expect(txns.getMultiTxnOperationDepth()).to.equal(expected);
269
- }
270
- // verify nested multi-txn operations
271
- expectTxnDepth(0);
272
- txns.beginMultiTxnOperation();
273
- expectTxnDepth(1);
274
- txns.beginMultiTxnOperation();
275
- expectTxnDepth(2);
276
- const set1 = insert2Elements();
277
- expectElementExistences(set1, true);
278
- txns.endMultiTxnOperation();
279
- expectTxnDepth(1);
280
- txns.reverseSingleTxn();
281
- expectElementExistences(set1, false);
282
- txns.reinstateTxn();
283
- expectElementExistences(set1, true);
284
- expectTxnDepth(1);
285
- txns.beginMultiTxnOperation();
286
- expectTxnDepth(2);
287
- const set2 = insert2Elements();
288
- expectElementExistences(set2, true);
289
- txns.endMultiTxnOperation();
290
- expectTxnDepth(1);
291
- txns.endMultiTxnOperation();
292
- expectTxnDepth(0);
293
- txns.reverseSingleTxn();
294
- expectElementExistences(set2, false);
295
- expectElementExistences(set1, false);
296
- txns.reinstateTxn();
297
- expectElementExistences(set1, true);
298
- expectElementExistences(set2, true);
299
- assert.equal(IModelStatus.Success, txns.cancelTo(txns.queryFirstTxnId()));
300
- assert.isFalse(txns.hasUnsavedChanges);
301
- assert.isFalse(txns.hasPendingTxns);
302
- assert.isFalse(txns.hasLocalChanges);
303
- model = models.getModel(modelId);
304
- assert.isUndefined(model.geometryGuid, "undo all, geometryGuid goes back to undefined");
305
- const modifyId = elements.insertElement(props);
306
- imodel.saveChanges("check guid changes");
307
- model = models.getModel(modelId);
308
- const guid2 = model.geometryGuid;
309
- const toModify = elements.getElement(modifyId);
310
- toModify.placement.origin.x += 1;
311
- toModify.placement.origin.y += 1;
312
- toModify.update();
313
- const saveUpdateMsg = "save update to modify guid";
314
- imodel.saveChanges(saveUpdateMsg);
315
- model = models.getModel(modelId);
316
- assert.notEqual(guid2, model.geometryGuid, "update placement should change guid");
317
- const lastMod = models.queryLastModifiedTime(modelId);
318
- await BeDuration.wait(300); // we update the lastMod below, make sure it will be different by waiting .3 seconds
319
- const guid3 = model.geometryGuid;
320
- models.updateGeometryGuid(modelId);
321
- model = models.getModel(modelId);
322
- assert.notEqual(guid3, model.geometryGuid, "update model should change guid");
323
- const lastMod2 = models.queryLastModifiedTime(modelId);
324
- assert.notEqual(lastMod, lastMod2);
325
- // imodel.saveChanges("update geometry guid");
326
- // Deleting a geometric element updates model's GeometryGuid; deleting any element updates model's LastMod.
327
- await BeDuration.wait(300); // for lastMod...
328
- const guid4 = model.geometryGuid;
329
- toModify.delete();
330
- const deleteTxnMsg = "save deletion of element";
331
- imodel.saveChanges(deleteTxnMsg);
332
- assert.throws(() => elements.getElement(modifyId));
333
- model = models.getModel(modelId);
334
- expect(model.geometryGuid).not.to.equal(guid4);
335
- const lastMod3 = models.queryLastModifiedTime(modelId);
336
- expect(lastMod3).not.to.equal(lastMod2);
337
- assert.isTrue(txns.isUndoPossible);
338
- // test restarting the session, which should truncate undo history
339
- txns.restartSession();
340
- assert.isFalse(txns.isUndoPossible);
341
- assert.equal("", txns.getUndoString());
342
- assert.isFalse(txns.isRedoPossible);
343
- assert.isFalse(txns.hasUnsavedChanges);
344
- assert.isTrue(txns.hasPendingTxns); // these are from the previous session
345
- cleanup.forEach((drop) => drop());
163
+ await withEditTxn(imodel, "txn-manager", async (editTxn) => {
164
+ let model = models.getModel(modelId);
165
+ assert.isUndefined(model.geometryGuid, "geometryGuid starts undefined");
166
+ assert.isDefined(await imodel.schemaContext.getSchemaItem(new SchemaItemKey("TestPhysicalObject", new SchemaKey("TestBim", 1, 0, 0)), EntityClass), "TestPhysicalObject is present");
167
+ const txns = imodel.txns;
168
+ assert.isFalse(txns.hasPendingTxns);
169
+ const change1Msg = "change 1";
170
+ const change2Msg = "change 2";
171
+ let beforeUndo = 0;
172
+ let afterUndo = 0;
173
+ let undoAction = TxnAction.None;
174
+ cleanup.push(txns.onBeforeUndoRedo.addListener(() => beforeUndo++));
175
+ cleanup.push(txns.onAfterUndoRedo.addListener((isUndo) => {
176
+ afterUndo++;
177
+ undoAction = isUndo ? TxnAction.Reverse : TxnAction.Reinstate;
178
+ }));
179
+ let elementId = editTxn.insertElement(props);
180
+ assert.isFalse(txns.isRedoPossible);
181
+ assert.isFalse(txns.isUndoPossible);
182
+ assert.isTrue(txns.hasUnsavedChanges);
183
+ assert.isFalse(txns.hasPendingTxns);
184
+ editTxn.saveChanges(change1Msg);
185
+ assert.isFalse(txns.hasUnsavedChanges);
186
+ assert.isTrue(txns.hasPendingTxns);
187
+ assert.isTrue(txns.hasLocalChanges);
188
+ expect(imodel[_nativeDb].getCurrentTxnId()).not.equal(roImodel[_nativeDb].getCurrentTxnId());
189
+ roImodel[_nativeDb].restartDefaultTxn();
190
+ expect(imodel[_nativeDb].getCurrentTxnId()).equal(roImodel[_nativeDb].getCurrentTxnId());
191
+ const classId = imodel[_nativeDb].classNameToId(props.classFullName);
192
+ assert.isTrue(Id64.isValid(classId));
193
+ const class2 = imodel[_nativeDb].classIdToName(classId);
194
+ assert.equal(class2, props.classFullName);
195
+ model = models.getModel(modelId);
196
+ assert.isDefined(model.geometryGuid);
197
+ txns.reverseSingleTxn();
198
+ assert.isFalse(txns.hasPendingTxns, "should not have pending txns if they all are reversed");
199
+ assert.isFalse(txns.hasLocalChanges);
200
+ txns.reinstateTxn();
201
+ assert.isTrue(txns.hasPendingTxns, "now there should be pending txns again");
202
+ assert.isTrue(txns.hasLocalChanges);
203
+ beforeUndo = afterUndo = 0; // reset this for tests below
204
+ model = models.getModel(modelId);
205
+ assert.isDefined(model.geometryGuid);
206
+ const guid1 = model.geometryGuid;
207
+ let element = elements.getElement(elementId);
208
+ assert.equal(element.intProperty, 100, "int property should be 100");
209
+ assert.isTrue(txns.isUndoPossible); // we have an undoable Txn, but nothing undone.
210
+ assert.equal(change1Msg, txns.getUndoString());
211
+ assert.equal(IModelStatus.Success, txns.reverseSingleTxn());
212
+ model = models.getModel(modelId);
213
+ assert.isUndefined(model.geometryGuid, "geometryGuid undefined after undo");
214
+ assert.isTrue(txns.isRedoPossible);
215
+ assert.equal(change1Msg, txns.getRedoString());
216
+ assert.equal(beforeUndo, 1);
217
+ assert.equal(afterUndo, 1);
218
+ assert.equal(undoAction, TxnAction.Reverse);
219
+ assert.throws(() => elements.getElementProps(elementId), IModelError, "element not found");
220
+ assert.throws(() => elements.getElement(elementId), IModelError);
221
+ assert.equal(IModelStatus.Success, txns.reinstateTxn());
222
+ model = models.getModel(modelId);
223
+ assert.equal(model.geometryGuid, guid1, "geometryGuid should return redo");
224
+ assert.isTrue(txns.isUndoPossible);
225
+ assert.isFalse(txns.isRedoPossible);
226
+ assert.equal(beforeUndo, 2);
227
+ assert.equal(afterUndo, 2);
228
+ assert.equal(undoAction, TxnAction.Reinstate);
229
+ element = elements.getElement(elementId);
230
+ element.intProperty = 200;
231
+ element.update(editTxn);
232
+ editTxn.saveChanges(change2Msg);
233
+ model = models.getModel(modelId);
234
+ assert.equal(model.geometryGuid, guid1, "geometryGuid should not update with no geometry changes");
235
+ element = elements.getElement(elementId);
236
+ assert.equal(element.intProperty, 200, "int property should be 200");
237
+ assert.equal(txns.getTxnDescription(txns.queryPreviousTxnId(txns.getCurrentTxnId())), change2Msg);
238
+ assert.equal(IModelStatus.Success, txns.reverseSingleTxn());
239
+ element = elements.getElement(elementId);
240
+ assert.equal(element.intProperty, 100, "int property should be 100");
241
+ // make sure abandon changes works.
242
+ element.delete(editTxn);
243
+ assert.throws(() => elements.getElement(elementId), IModelError);
244
+ editTxn.abandonChanges();
245
+ element = elements.getElement(elementId); // should be back now.
246
+ editTxn.insertElement(props); // create a new element
247
+ editTxn.saveChanges(change2Msg);
248
+ model = models.getModel(modelId);
249
+ assert.isDefined(model.geometryGuid);
250
+ assert.notEqual(model.geometryGuid, guid1, "geometryGuid should update with adds");
251
+ elementId = editTxn.insertElement(props); // create a new element
252
+ assert.isTrue(txns.hasUnsavedChanges);
253
+ assert.equal(IModelStatus.Success, txns.reverseSingleTxn());
254
+ assert.isFalse(txns.hasUnsavedChanges);
255
+ assert.throws(() => elements.getElement(elementId), IModelError); // reversing a txn with pending uncommitted changes should abandon them.
256
+ assert.equal(IModelStatus.Success, txns.reinstateTxn());
257
+ assert.throws(() => elements.getElement(elementId), IModelError); // doesn't come back, wasn't committed
258
+ // verify multi-txn operations are undone/redone together
259
+ const el1 = editTxn.insertElement(props);
260
+ editTxn.saveChanges("step 1");
261
+ txns.beginMultiTxnOperation();
262
+ assert.equal(1, txns.getMultiTxnOperationDepth());
263
+ const el2 = editTxn.insertElement(props);
264
+ editTxn.saveChanges("step 2");
265
+ const el3 = editTxn.insertElement(props);
266
+ editTxn.saveChanges("step 3");
267
+ txns.endMultiTxnOperation();
268
+ assert.equal(0, txns.getMultiTxnOperationDepth());
269
+ assert.equal(IModelStatus.Success, txns.reverseSingleTxn());
270
+ assert.throws(() => elements.getElement(el2), IModelError);
271
+ assert.throws(() => elements.getElement(el3), IModelError);
272
+ elements.getElement(el1);
273
+ assert.equal(IModelStatus.Success, txns.reverseSingleTxn());
274
+ assert.throws(() => elements.getElement(el1), IModelError);
275
+ assert.equal(IModelStatus.Success, txns.reinstateTxn());
276
+ assert.throws(() => elements.getElement(el2), IModelError);
277
+ assert.throws(() => elements.getElement(el3), IModelError);
278
+ elements.getElement(el1);
279
+ assert.equal(IModelStatus.Success, txns.reinstateTxn());
280
+ elements.getElement(el1);
281
+ elements.getElement(el2);
282
+ elements.getElement(el3);
283
+ function insert2Elements() {
284
+ const id0 = editTxn.insertElement(props);
285
+ editTxn.saveChanges();
286
+ const id1 = editTxn.insertElement(props);
287
+ editTxn.saveChanges();
288
+ return [id0, id1];
289
+ }
290
+ function expectElementExistences(ids, expectExists) {
291
+ expect(elements.tryGetElementProps(ids[0]) !== undefined).to.equal(expectExists);
292
+ expect(elements.tryGetElementProps(ids[0]) !== undefined).to.equal(expectExists);
293
+ }
294
+ function expectTxnDepth(expected) {
295
+ expect(txns.getMultiTxnOperationDepth()).to.equal(expected);
296
+ }
297
+ // verify nested multi-txn operations
298
+ expectTxnDepth(0);
299
+ txns.beginMultiTxnOperation();
300
+ expectTxnDepth(1);
301
+ txns.beginMultiTxnOperation();
302
+ expectTxnDepth(2);
303
+ const set1 = insert2Elements();
304
+ expectElementExistences(set1, true);
305
+ txns.endMultiTxnOperation();
306
+ expectTxnDepth(1);
307
+ txns.reverseSingleTxn();
308
+ expectElementExistences(set1, false);
309
+ txns.reinstateTxn();
310
+ expectElementExistences(set1, true);
311
+ expectTxnDepth(1);
312
+ txns.beginMultiTxnOperation();
313
+ expectTxnDepth(2);
314
+ const set2 = insert2Elements();
315
+ expectElementExistences(set2, true);
316
+ txns.endMultiTxnOperation();
317
+ expectTxnDepth(1);
318
+ txns.endMultiTxnOperation();
319
+ expectTxnDepth(0);
320
+ txns.reverseSingleTxn();
321
+ expectElementExistences(set2, false);
322
+ expectElementExistences(set1, false);
323
+ txns.reinstateTxn();
324
+ expectElementExistences(set1, true);
325
+ expectElementExistences(set2, true);
326
+ assert.equal(IModelStatus.Success, txns.cancelTo(txns.queryFirstTxnId()));
327
+ assert.isFalse(txns.hasUnsavedChanges);
328
+ assert.isFalse(txns.hasPendingTxns);
329
+ assert.isFalse(txns.hasLocalChanges);
330
+ model = models.getModel(modelId);
331
+ assert.isUndefined(model.geometryGuid, "undo all, geometryGuid goes back to undefined");
332
+ const modifyId = editTxn.insertElement(props);
333
+ editTxn.saveChanges("check guid changes");
334
+ model = models.getModel(modelId);
335
+ const guid2 = model.geometryGuid;
336
+ const toModify = elements.getElement(modifyId);
337
+ toModify.placement.origin.x += 1;
338
+ toModify.placement.origin.y += 1;
339
+ editTxn.updateElement(toModify.toJSON());
340
+ const saveUpdateMsg = "save update to modify guid";
341
+ editTxn.saveChanges(saveUpdateMsg);
342
+ model = models.getModel(modelId);
343
+ assert.notEqual(guid2, model.geometryGuid, "update placement should change guid");
344
+ const lastMod = models.queryLastModifiedTime(modelId);
345
+ await BeDuration.wait(300); // we update the lastMod below, make sure it will be different by waiting .3 seconds
346
+ const guid3 = model.geometryGuid;
347
+ editTxn.updateGeometryGuid(modelId);
348
+ model = models.getModel(modelId);
349
+ assert.notEqual(guid3, model.geometryGuid, "update model should change guid");
350
+ const lastMod2 = models.queryLastModifiedTime(modelId);
351
+ assert.notEqual(lastMod, lastMod2);
352
+ // imodel.saveChanges("update geometry guid");
353
+ // Deleting a geometric element updates model's GeometryGuid; deleting any element updates model's LastMod.
354
+ await BeDuration.wait(300); // for lastMod...
355
+ const guid4 = model.geometryGuid;
356
+ editTxn.deleteElement(modifyId);
357
+ const deleteTxnMsg = "save deletion of element";
358
+ editTxn.saveChanges(deleteTxnMsg);
359
+ assert.throws(() => elements.getElement(modifyId));
360
+ model = models.getModel(modelId);
361
+ expect(model.geometryGuid).not.to.equal(guid4);
362
+ const lastMod3 = models.queryLastModifiedTime(modelId);
363
+ expect(lastMod3).not.to.equal(lastMod2);
364
+ assert.isTrue(txns.isUndoPossible);
365
+ // test restarting the session, which should truncate undo history
366
+ txns.restartSession();
367
+ assert.isFalse(txns.isUndoPossible);
368
+ assert.equal("", txns.getUndoString());
369
+ assert.isFalse(txns.isRedoPossible);
370
+ assert.isFalse(txns.hasUnsavedChanges);
371
+ assert.isTrue(txns.hasPendingTxns); // these are from the previous session
372
+ cleanup.forEach((drop) => drop());
373
+ });
346
374
  });
347
375
  class EventAccumulator {
348
376
  inserted = [];
@@ -439,472 +467,490 @@ describe("TxnManager", () => {
439
467
  }
440
468
  it("dispatches events when elements change", async () => {
441
469
  const elements = imodel.elements;
442
- let id1;
443
- let id2;
444
- EventAccumulator.testElements(imodel, (accum) => {
445
- id1 = elements.insertElement(props);
446
- id2 = elements.insertElement(props);
447
- imodel.saveChanges("2 inserts");
448
- accum.expectNumValidations(1);
449
- accum.expectChanges({ inserted: [physicalObjectEntity(id1), physicalObjectEntity(id2)] });
450
- });
451
- await BeDuration.wait(10); // we rely on updating the lastMod of the newly inserted element, make sure it will be different
452
- let elem1;
453
- let elem2;
454
- EventAccumulator.testElements(imodel, (accum) => {
455
- elem1 = elements.getElement(id1);
456
- elem2 = elements.getElement(id2);
457
- elem1.intProperty = 200;
458
- elem1.update();
459
- elem2.intProperty = 200;
460
- elem2.update();
461
- imodel.saveChanges("2 updates");
462
- accum.expectNumValidations(1);
463
- accum.expectChanges({ updated: [physicalObjectEntity(id1), physicalObjectEntity(id2)] });
464
- });
465
- EventAccumulator.testElements(imodel, (accum) => {
466
- elem1.delete();
467
- elem2.delete();
468
- imodel.saveChanges("2 deletes");
469
- accum.expectNumValidations(1);
470
- accum.expectChanges({ deleted: [physicalObjectEntity(id1), physicalObjectEntity(id2)] });
471
- });
472
- // Undo
473
- EventAccumulator.testElements(imodel, (accum) => {
474
- imodel.txns.reverseSingleTxn();
475
- accum.expectNumUndoRedo(1);
476
- accum.expectChanges({ inserted: [physicalObjectEntity(id1), physicalObjectEntity(id2)] });
477
- accum.expectNumApplyChanges(1);
478
- accum.expectNumValidations(0);
479
- });
480
- EventAccumulator.testElements(imodel, (accum) => {
481
- imodel.txns.reverseSingleTxn();
482
- accum.expectNumUndoRedo(1);
483
- accum.expectChanges({ updated: [physicalObjectEntity(id1), physicalObjectEntity(id2)] });
484
- });
485
- EventAccumulator.testElements(imodel, (accum) => {
486
- imodel.txns.reverseSingleTxn();
487
- accum.expectNumUndoRedo(1);
488
- accum.expectChanges({ deleted: [physicalObjectEntity(id1), physicalObjectEntity(id2)] });
489
- });
490
- // Redo
491
- EventAccumulator.testElements(imodel, (accum) => {
492
- imodel.txns.reinstateTxn();
493
- accum.expectNumUndoRedo(1);
494
- accum.expectChanges({ inserted: [physicalObjectEntity(id1), physicalObjectEntity(id2)] });
495
- });
496
- EventAccumulator.testElements(imodel, (accum) => {
497
- imodel.txns.reinstateTxn();
498
- accum.expectNumUndoRedo(1);
499
- accum.expectChanges({ updated: [physicalObjectEntity(id1), physicalObjectEntity(id2)] });
500
- });
501
- EventAccumulator.testElements(imodel, (accum) => {
502
- imodel.txns.reinstateTxn();
503
- accum.expectNumUndoRedo(1);
504
- accum.expectChanges({ deleted: [physicalObjectEntity(id1), physicalObjectEntity(id2)] });
505
- accum.expectNumApplyChanges(1);
506
- accum.expectNumValidations(0);
507
- });
508
- // Undo all
509
- EventAccumulator.testElements(imodel, (accum) => {
510
- imodel.txns.reverseTxns(3);
511
- accum.expectNumValidations(0);
512
- accum.expectNumUndoRedo(1);
513
- accum.expectNumApplyChanges(3);
514
- // We received 3 separate "elements changed" events - one for each txn - and just concatenated the lists.
515
- accum.expectChanges({
516
- inserted: [physicalObjectEntity(id1), physicalObjectEntity(id2)],
517
- updated: [physicalObjectEntity(id1), physicalObjectEntity(id2)],
518
- deleted: [physicalObjectEntity(id1), physicalObjectEntity(id2)],
470
+ await withEditTxn(imodel, "elements-events", async (editTxn) => {
471
+ let id1;
472
+ let id2;
473
+ EventAccumulator.testElements(imodel, (accum) => {
474
+ id1 = editTxn.insertElement(props);
475
+ id2 = editTxn.insertElement(props);
476
+ editTxn.saveChanges("2 inserts");
477
+ accum.expectNumValidations(1);
478
+ accum.expectChanges({ inserted: [physicalObjectEntity(id1), physicalObjectEntity(id2)] });
519
479
  });
520
- });
521
- // Redo all
522
- EventAccumulator.testElements(imodel, (accum) => {
523
- imodel.txns.reinstateTxn();
524
- accum.expectNumValidations(0);
525
- accum.expectNumUndoRedo(1);
526
- accum.expectNumApplyChanges(3);
527
- // We received 3 separate "elements changed" events - one for each txn - and just concatenated the lists.
528
- accum.expectChanges({
529
- inserted: [physicalObjectEntity(id1), physicalObjectEntity(id2)],
530
- updated: [physicalObjectEntity(id1), physicalObjectEntity(id2)],
531
- deleted: [physicalObjectEntity(id1), physicalObjectEntity(id2)],
480
+ await BeDuration.wait(10); // we rely on updating the lastMod of the newly inserted element, make sure it will be different
481
+ let elem1;
482
+ let elem2;
483
+ EventAccumulator.testElements(imodel, (accum) => {
484
+ elem1 = elements.getElement(id1);
485
+ elem2 = elements.getElement(id2);
486
+ elem1.intProperty = 200;
487
+ editTxn.updateElement(elem1.toJSON());
488
+ elem2.intProperty = 200;
489
+ editTxn.updateElement(elem2.toJSON());
490
+ editTxn.saveChanges("2 updates");
491
+ accum.expectNumValidations(1);
492
+ accum.expectChanges({ updated: [physicalObjectEntity(id1), physicalObjectEntity(id2)] });
532
493
  });
533
- });
534
- EventAccumulator.testElements(imodel, (accum) => {
535
- const elemId1 = imodel.elements.insertElement(props);
536
- const catId = SpatialCategory.insert(imodel, IModel.dictionaryId, Guid.createValue(), new SubCategoryAppearance({ color: ColorByName.green }));
537
- const elemId2 = imodel.elements.insertElement(props);
538
- imodel.saveChanges("2 physical elems and 1 spatial category");
539
- accum.expectNumValidations(1);
540
- accum.expectChanges({
541
- inserted: [
542
- physicalObjectEntity(elemId1),
543
- spatialCategoryEntity(catId),
544
- subCategoryEntity(catId),
545
- physicalObjectEntity(elemId2),
546
- ],
494
+ EventAccumulator.testElements(imodel, (accum) => {
495
+ editTxn.deleteElement(id1);
496
+ editTxn.deleteElement(id2);
497
+ editTxn.saveChanges("2 deletes");
498
+ accum.expectNumValidations(1);
499
+ accum.expectChanges({ deleted: [physicalObjectEntity(id1), physicalObjectEntity(id2)] });
500
+ });
501
+ // Undo
502
+ EventAccumulator.testElements(imodel, (accum) => {
503
+ imodel.txns.reverseSingleTxn();
504
+ accum.expectNumUndoRedo(1);
505
+ accum.expectChanges({ inserted: [physicalObjectEntity(id1), physicalObjectEntity(id2)] });
506
+ accum.expectNumApplyChanges(1);
507
+ accum.expectNumValidations(0);
508
+ });
509
+ EventAccumulator.testElements(imodel, (accum) => {
510
+ imodel.txns.reverseSingleTxn();
511
+ accum.expectNumUndoRedo(1);
512
+ accum.expectChanges({ updated: [physicalObjectEntity(id1), physicalObjectEntity(id2)] });
513
+ });
514
+ EventAccumulator.testElements(imodel, (accum) => {
515
+ imodel.txns.reverseSingleTxn();
516
+ accum.expectNumUndoRedo(1);
517
+ accum.expectChanges({ deleted: [physicalObjectEntity(id1), physicalObjectEntity(id2)] });
518
+ });
519
+ // Redo
520
+ EventAccumulator.testElements(imodel, (accum) => {
521
+ imodel.txns.reinstateTxn();
522
+ accum.expectNumUndoRedo(1);
523
+ accum.expectChanges({ inserted: [physicalObjectEntity(id1), physicalObjectEntity(id2)] });
524
+ });
525
+ EventAccumulator.testElements(imodel, (accum) => {
526
+ imodel.txns.reinstateTxn();
527
+ accum.expectNumUndoRedo(1);
528
+ accum.expectChanges({ updated: [physicalObjectEntity(id1), physicalObjectEntity(id2)] });
529
+ });
530
+ EventAccumulator.testElements(imodel, (accum) => {
531
+ imodel.txns.reinstateTxn();
532
+ accum.expectNumUndoRedo(1);
533
+ accum.expectChanges({ deleted: [physicalObjectEntity(id1), physicalObjectEntity(id2)] });
534
+ accum.expectNumApplyChanges(1);
535
+ accum.expectNumValidations(0);
536
+ });
537
+ // Undo all
538
+ EventAccumulator.testElements(imodel, (accum) => {
539
+ imodel.txns.reverseTxns(3);
540
+ accum.expectNumValidations(0);
541
+ accum.expectNumUndoRedo(1);
542
+ accum.expectNumApplyChanges(3);
543
+ // We received 3 separate "elements changed" events - one for each txn - and just concatenated the lists.
544
+ accum.expectChanges({
545
+ inserted: [physicalObjectEntity(id1), physicalObjectEntity(id2)],
546
+ updated: [physicalObjectEntity(id1), physicalObjectEntity(id2)],
547
+ deleted: [physicalObjectEntity(id1), physicalObjectEntity(id2)],
548
+ });
549
+ });
550
+ // Redo all
551
+ EventAccumulator.testElements(imodel, (accum) => {
552
+ imodel.txns.reinstateTxn();
553
+ accum.expectNumValidations(0);
554
+ accum.expectNumUndoRedo(1);
555
+ accum.expectNumApplyChanges(3);
556
+ // We received 3 separate "elements changed" events - one for each txn - and just concatenated the lists.
557
+ accum.expectChanges({
558
+ inserted: [physicalObjectEntity(id1), physicalObjectEntity(id2)],
559
+ updated: [physicalObjectEntity(id1), physicalObjectEntity(id2)],
560
+ deleted: [physicalObjectEntity(id1), physicalObjectEntity(id2)],
561
+ });
562
+ });
563
+ EventAccumulator.testElements(imodel, (accum) => {
564
+ const elemId1 = editTxn.insertElement(props);
565
+ const catId = insertSpatialCategory(editTxn, IModel.dictionaryId, Guid.createValue(), new SubCategoryAppearance({ color: ColorByName.green }));
566
+ const elemId2 = editTxn.insertElement(props);
567
+ editTxn.saveChanges("2 physical elems and 1 spatial category");
568
+ accum.expectNumValidations(1);
569
+ accum.expectChanges({
570
+ inserted: [
571
+ physicalObjectEntity(elemId1),
572
+ spatialCategoryEntity(catId),
573
+ subCategoryEntity(catId),
574
+ physicalObjectEntity(elemId2),
575
+ ],
576
+ });
547
577
  });
548
578
  });
549
579
  });
550
580
  it("dispatches events when models change", async () => {
551
- const existingModelId = props.model;
552
- let newModelId;
553
- EventAccumulator.testModels(imodel, (accum) => {
554
- newModelId = PhysicalModel.insert(imodel, IModel.rootSubjectId, Guid.createValue());
555
- imodel.saveChanges("1 insert");
556
- accum.expectNumValidations(1);
557
- accum.expectChanges({ inserted: [physicalModelEntity(newModelId)] });
558
- });
559
- EventAccumulator.testModels(roImodel, (accum) => {
560
- roImodel[_nativeDb].restartDefaultTxn();
561
- accum.expectChanges({ inserted: [physicalModelEntity(newModelId)] });
562
- });
563
- await BeDuration.wait(10); // we rely on updating the lastMod of the newly inserted element, make sure it will be different
564
- // NB: Updates to existing models never produce events. I don't think I want to change that as part of this PR.
565
- let newModel;
566
- EventAccumulator.testModels(imodel, (accum) => {
567
- newModel = imodel.models.getModel(newModelId);
568
- const newModelProps = newModel.toJSON();
569
- newModelProps.isNotSpatiallyLocated = newModel.isSpatiallyLocated;
570
- imodel.models.updateModel(newModelProps);
571
- imodel.models.updateGeometryGuid(existingModelId);
572
- imodel.saveChanges("1 update");
573
- accum.expectNumValidations(1);
574
- accum.expectChanges({});
575
- });
576
- EventAccumulator.testModels(imodel, (accum) => {
577
- imodel.elements.insertElement(props);
578
- imodel.saveChanges("insert 1 geometric element");
579
- accum.expectNumValidations(1);
580
- accum.expectChanges({});
581
- });
582
- EventAccumulator.testModels(imodel, (accum) => {
583
- newModel.delete();
584
- imodel.saveChanges("1 delete");
585
- accum.expectNumValidations(1);
586
- accum.expectChanges({ deleted: [physicalModelEntity(newModelId)] });
587
- accum.expectNumApplyChanges(0);
588
- });
589
- EventAccumulator.testModels(roImodel, (accum) => {
590
- roImodel[_nativeDb].restartDefaultTxn();
591
- accum.expectChanges({ deleted: [physicalModelEntity(newModelId)] });
592
- });
593
- // Undo
594
- EventAccumulator.testModels(imodel, (accum) => {
595
- imodel.txns.reverseSingleTxn();
596
- accum.expectNumUndoRedo(1);
597
- accum.expectNumApplyChanges(1);
598
- accum.expectChanges({ inserted: [physicalModelEntity(newModelId)] });
599
- });
600
- EventAccumulator.testModels(imodel, (accum) => {
601
- imodel.txns.reverseSingleTxn();
602
- accum.expectNumUndoRedo(1);
603
- accum.expectNumApplyChanges(1);
604
- accum.expectChanges({});
605
- });
606
- EventAccumulator.testModels(imodel, (accum) => {
607
- imodel.txns.reverseSingleTxn();
608
- accum.expectNumUndoRedo(1);
609
- accum.expectNumApplyChanges(1);
610
- accum.expectChanges({});
611
- });
612
- EventAccumulator.testModels(imodel, (accum) => {
613
- imodel.txns.reverseSingleTxn();
614
- accum.expectNumUndoRedo(1);
615
- accum.expectNumApplyChanges(1);
616
- accum.expectChanges({ deleted: [physicalModelEntity(newModelId)] });
617
- });
618
- // Redo
619
- EventAccumulator.testModels(imodel, (accum) => {
620
- for (let i = 0; i < 4; i++)
621
- imodel.txns.reinstateTxn();
622
- accum.expectNumUndoRedo(4);
623
- accum.expectNumApplyChanges(4);
624
- accum.expectChanges({ inserted: [physicalModelEntity(newModelId)], deleted: [physicalModelEntity(newModelId)] });
581
+ await withEditTxn(imodel, "models-events", async (editTxn) => {
582
+ const existingModelId = props.model;
583
+ let newModelId;
584
+ EventAccumulator.testModels(imodel, (accum) => {
585
+ newModelId = insertPhysicalModel(editTxn, IModel.rootSubjectId, Guid.createValue());
586
+ editTxn.saveChanges("1 insert");
587
+ accum.expectNumValidations(1);
588
+ accum.expectChanges({ inserted: [physicalModelEntity(newModelId)] });
589
+ });
590
+ EventAccumulator.testModels(roImodel, (accum) => {
591
+ roImodel[_nativeDb].restartDefaultTxn();
592
+ accum.expectChanges({ inserted: [physicalModelEntity(newModelId)] });
593
+ });
594
+ await BeDuration.wait(10); // we rely on updating the lastMod of the newly inserted element, make sure it will be different
595
+ // NB: Updates to existing models never produce events. I don't think I want to change that as part of this PR.
596
+ let newModel;
597
+ EventAccumulator.testModels(imodel, (accum) => {
598
+ newModel = imodel.models.getModel(newModelId);
599
+ const newModelProps = newModel.toJSON();
600
+ newModelProps.isNotSpatiallyLocated = newModel.isSpatiallyLocated;
601
+ editTxn.updateModel(newModelProps);
602
+ editTxn.updateGeometryGuid(existingModelId);
603
+ editTxn.saveChanges("1 update");
604
+ accum.expectNumValidations(1);
605
+ accum.expectChanges({});
606
+ });
607
+ EventAccumulator.testModels(imodel, (accum) => {
608
+ editTxn.insertElement(props);
609
+ editTxn.saveChanges("insert 1 geometric element");
610
+ accum.expectNumValidations(1);
611
+ accum.expectChanges({});
612
+ });
613
+ EventAccumulator.testModels(imodel, (accum) => {
614
+ editTxn.deleteModel(newModelId);
615
+ editTxn.saveChanges("1 delete");
616
+ accum.expectNumValidations(1);
617
+ accum.expectChanges({ deleted: [physicalModelEntity(newModelId)] });
618
+ accum.expectNumApplyChanges(0);
619
+ });
620
+ EventAccumulator.testModels(roImodel, (accum) => {
621
+ roImodel[_nativeDb].restartDefaultTxn();
622
+ accum.expectChanges({ deleted: [physicalModelEntity(newModelId)] });
623
+ });
624
+ // Undo
625
+ EventAccumulator.testModels(imodel, (accum) => {
626
+ imodel.txns.reverseSingleTxn();
627
+ accum.expectNumUndoRedo(1);
628
+ accum.expectNumApplyChanges(1);
629
+ accum.expectChanges({ inserted: [physicalModelEntity(newModelId)] });
630
+ });
631
+ EventAccumulator.testModels(imodel, (accum) => {
632
+ imodel.txns.reverseSingleTxn();
633
+ accum.expectNumUndoRedo(1);
634
+ accum.expectNumApplyChanges(1);
635
+ accum.expectChanges({});
636
+ });
637
+ EventAccumulator.testModels(imodel, (accum) => {
638
+ imodel.txns.reverseSingleTxn();
639
+ accum.expectNumUndoRedo(1);
640
+ accum.expectNumApplyChanges(1);
641
+ accum.expectChanges({});
642
+ });
643
+ EventAccumulator.testModels(imodel, (accum) => {
644
+ imodel.txns.reverseSingleTxn();
645
+ accum.expectNumUndoRedo(1);
646
+ accum.expectNumApplyChanges(1);
647
+ accum.expectChanges({ deleted: [physicalModelEntity(newModelId)] });
648
+ });
649
+ // Redo
650
+ EventAccumulator.testModels(imodel, (accum) => {
651
+ for (let i = 0; i < 4; i++)
652
+ imodel.txns.reinstateTxn();
653
+ accum.expectNumUndoRedo(4);
654
+ accum.expectNumApplyChanges(4);
655
+ accum.expectChanges({ inserted: [physicalModelEntity(newModelId)], deleted: [physicalModelEntity(newModelId)] });
656
+ });
625
657
  });
626
658
  });
627
659
  it("dispatches events when geometry guids change", () => {
628
- const modelId = props.model;
629
- const test = (func) => {
630
- const model = imodel.models.getModel(modelId);
631
- const prevGuid = model.geometryGuid;
632
- let newGuid;
633
- let numEvents = 0;
634
- let dropListener = imodel.txns.onModelGeometryChanged.addListener((changes) => {
635
- expect(numEvents).to.equal(0);
636
- ++numEvents;
637
- expect(changes.length).to.equal(1);
638
- expect(changes[0].id).to.equal(modelId);
639
- newGuid = changes[0].guid;
640
- expect(newGuid).not.to.equal(prevGuid);
660
+ withEditTxn(imodel, "geometry-guid-events", (editTxn) => {
661
+ const modelId = props.model;
662
+ const test = (func) => {
663
+ const model = imodel.models.getModel(modelId);
664
+ const prevGuid = model.geometryGuid;
665
+ let newGuid;
666
+ let numEvents = 0;
667
+ let dropListener = imodel.txns.onModelGeometryChanged.addListener((changes) => {
668
+ expect(numEvents).to.equal(0);
669
+ ++numEvents;
670
+ expect(changes.length).to.equal(1);
671
+ expect(changes[0].id).to.equal(modelId);
672
+ newGuid = changes[0].guid;
673
+ expect(newGuid).not.to.equal(prevGuid);
674
+ });
675
+ const expectEvent = func(model);
676
+ editTxn.saveChanges("");
677
+ expect(numEvents).to.equal(expectEvent ? 1 : 0);
678
+ dropListener();
679
+ if (!expectEvent)
680
+ return;
681
+ dropListener = imodel.txns.onModelGeometryChanged.addListener((changes) => {
682
+ ++numEvents;
683
+ expect(changes.length).to.equal(1);
684
+ expect(changes[0].id).to.equal(modelId);
685
+ expect(changes[0].guid).to.equal(prevGuid);
686
+ });
687
+ imodel.txns.reverseSingleTxn();
688
+ expect(numEvents).to.equal(2);
689
+ dropListener();
690
+ dropListener = imodel.txns.onModelGeometryChanged.addListener((changes) => {
691
+ ++numEvents;
692
+ expect(changes.length).to.equal(1);
693
+ expect(changes[0].id).to.equal(modelId);
694
+ expect(changes[0].guid).to.equal(newGuid);
695
+ });
696
+ imodel.txns.reinstateTxn();
697
+ expect(numEvents).to.equal(3);
698
+ dropListener();
699
+ };
700
+ test(() => {
701
+ editTxn.updateGeometryGuid(modelId);
702
+ return true;
641
703
  });
642
- const expectEvent = func(model);
643
- imodel.saveChanges("");
644
- expect(numEvents).to.equal(expectEvent ? 1 : 0);
645
- dropListener();
646
- if (!expectEvent)
647
- return;
648
- dropListener = imodel.txns.onModelGeometryChanged.addListener((changes) => {
649
- ++numEvents;
650
- expect(changes.length).to.equal(1);
651
- expect(changes[0].id).to.equal(modelId);
652
- expect(changes[0].guid).to.equal(prevGuid);
704
+ test((model) => {
705
+ const modelProps = model.toJSON();
706
+ modelProps.geometryGuid = Guid.createValue();
707
+ editTxn.updateModel(modelProps);
708
+ return false;
653
709
  });
654
- imodel.txns.reverseSingleTxn();
655
- expect(numEvents).to.equal(2);
656
- dropListener();
657
- dropListener = imodel.txns.onModelGeometryChanged.addListener((changes) => {
658
- ++numEvents;
710
+ let newElemId;
711
+ test(() => {
712
+ newElemId = editTxn.insertElement(props);
713
+ return true;
714
+ });
715
+ test(() => {
716
+ const elem = imodel.elements.getElement(newElemId);
717
+ elem.userLabel = "not a geometric change";
718
+ elem.intProperty = 42;
719
+ editTxn.updateElement(elem.toJSON());
720
+ return false;
721
+ });
722
+ test(() => {
723
+ const elem = imodel.elements.getElement(newElemId);
724
+ elem.placement.origin.x += 10;
725
+ editTxn.updateElement(elem.toJSON());
726
+ return true;
727
+ });
728
+ test(() => {
729
+ editTxn.deleteElement(newElemId);
730
+ return true;
731
+ });
732
+ editTxn.saveChanges();
733
+ // now test that all the changes we just made are seen by the readonly connection when we call `restartDefaultTxn`
734
+ let numRoEvents = 0;
735
+ const guid1 = imodel.models.getModel(modelId).geometryGuid;
736
+ const dropper = roImodel.txns.onModelGeometryChanged.addListener((changes) => {
737
+ ++numRoEvents;
659
738
  expect(changes.length).to.equal(1);
660
739
  expect(changes[0].id).to.equal(modelId);
661
- expect(changes[0].guid).to.equal(newGuid);
740
+ expect(changes[0].guid).to.equal(guid1);
662
741
  });
663
- imodel.txns.reinstateTxn();
664
- expect(numEvents).to.equal(3);
665
- dropListener();
666
- };
667
- test(() => {
668
- imodel.models.updateGeometryGuid(modelId);
669
- return true;
670
- });
671
- test((model) => {
672
- model.geometryGuid = Guid.createValue();
673
- model.update();
674
- return false;
675
- });
676
- let newElemId;
677
- test(() => {
678
- newElemId = imodel.elements.insertElement(props);
679
- return true;
680
- });
681
- test(() => {
682
- const elem = imodel.elements.getElement(newElemId);
683
- elem.userLabel = "not a geometric change";
684
- elem.intProperty = 42;
685
- elem.update();
686
- return false;
687
- });
688
- test(() => {
689
- const elem = imodel.elements.getElement(newElemId);
690
- elem.placement.origin.x += 10;
691
- elem.update();
692
- return true;
693
- });
694
- test(() => {
695
- imodel.elements.deleteElement(newElemId);
696
- return true;
697
- });
698
- imodel.saveChanges();
699
- // now test that all the changes we just made are seen by the readonly connection when we call `restartDefaultTxn`
700
- let numRoEvents = 0;
701
- const guid1 = imodel.models.getModel(modelId).geometryGuid;
702
- const dropper = roImodel.txns.onModelGeometryChanged.addListener((changes) => {
703
- ++numRoEvents;
704
- expect(changes.length).to.equal(1);
705
- expect(changes[0].id).to.equal(modelId);
706
- expect(changes[0].guid).to.equal(guid1);
742
+ roImodel[_nativeDb].restartDefaultTxn();
743
+ expect(numRoEvents).equal(4);
744
+ dropper();
707
745
  });
708
- roImodel[_nativeDb].restartDefaultTxn();
709
- expect(numRoEvents).equal(4);
710
- dropper();
711
746
  });
712
747
  it("dispatches events in batches", async () => {
713
- function entityCount(entities) {
714
- let count = 0;
715
- for (const _entity of entities)
716
- ++count;
717
- return count;
718
- }
719
- const test = (numChangesExpected, func) => {
720
- const numChanged = [];
721
- const prevMax = setMaxEntitiesPerEvent(2);
722
- const dropListener = imodel.txns.onElementsChanged.addListener((changes) => {
723
- const numEntities = entityCount(changes.inserts) + entityCount(changes.updates) + entityCount(changes.deletes);
724
- numChanged.push(numEntities);
725
- expect(numEntities).least(1);
726
- expect(numEntities <= 2).to.be.true;
727
- });
728
- func();
729
- imodel.saveChanges("");
730
- dropListener();
731
- setMaxEntitiesPerEvent(prevMax);
732
- expect(numChanged.length).to.equal(Math.ceil(numChangesExpected / 2));
733
- for (let i = 0; i < numChanged.length - 1; i++)
734
- expect(numChanged[i]).to.equal(2);
735
- if (numChangesExpected > 0)
736
- expect(numChanged[numChanged.length - 1]).to.equal(0 === numChangesExpected % 2 ? 2 : 1);
737
- };
738
- let elemId1;
739
- test(1, () => {
740
- elemId1 = imodel.elements.insertElement(props);
741
- });
742
- let elemId2;
743
- test(2, () => {
744
- elemId2 = imodel.elements.insertElement(props);
745
- imodel.elements.deleteElement(elemId1);
746
- });
747
- await BeDuration.wait(10); // we rely on updating the lastMod of the newly inserted element, make sure it will be different
748
- let elemId3;
749
- test(3, () => {
750
- elemId1 = imodel.elements.insertElement(props);
751
- elemId3 = imodel.elements.insertElement(props);
752
- const elem2 = imodel.elements.getElement(elemId2);
753
- elem2.intProperty = 321;
754
- elem2.update();
755
- });
756
- test(4, () => {
757
- imodel.elements.deleteElement(elemId1);
758
- imodel.elements.deleteElement(elemId2);
759
- imodel.elements.deleteElement(elemId3);
760
- imodel.elements.insertElement(props);
748
+ await withEditTxn(imodel, "batch-events", async (editTxn) => {
749
+ function entityCount(entities) {
750
+ let count = 0;
751
+ for (const _entity of entities)
752
+ ++count;
753
+ return count;
754
+ }
755
+ const test = (numChangesExpected, func) => {
756
+ const numChanged = [];
757
+ const prevMax = setMaxEntitiesPerEvent(2);
758
+ const dropListener = imodel.txns.onElementsChanged.addListener((changes) => {
759
+ const numEntities = entityCount(changes.inserts) + entityCount(changes.updates) + entityCount(changes.deletes);
760
+ numChanged.push(numEntities);
761
+ expect(numEntities).least(1);
762
+ expect(numEntities <= 2).to.be.true;
763
+ });
764
+ func();
765
+ editTxn.saveChanges("");
766
+ dropListener();
767
+ setMaxEntitiesPerEvent(prevMax);
768
+ expect(numChanged.length).to.equal(Math.ceil(numChangesExpected / 2));
769
+ for (let i = 0; i < numChanged.length - 1; i++)
770
+ expect(numChanged[i]).to.equal(2);
771
+ if (numChangesExpected > 0)
772
+ expect(numChanged[numChanged.length - 1]).to.equal(0 === numChangesExpected % 2 ? 2 : 1);
773
+ };
774
+ let elemId1;
775
+ test(1, () => {
776
+ elemId1 = editTxn.insertElement(props);
777
+ });
778
+ let elemId2;
779
+ test(2, () => {
780
+ elemId2 = editTxn.insertElement(props);
781
+ editTxn.deleteElement(elemId1);
782
+ });
783
+ await BeDuration.wait(10); // we rely on updating the lastMod of the newly inserted element, make sure it will be different
784
+ let elemId3;
785
+ test(3, () => {
786
+ elemId1 = editTxn.insertElement(props);
787
+ elemId3 = editTxn.insertElement(props);
788
+ const elem2 = imodel.elements.getElement(elemId2);
789
+ elem2.intProperty = 321;
790
+ editTxn.updateElement(elem2.toJSON());
791
+ });
792
+ test(4, () => {
793
+ editTxn.deleteElement(elemId1);
794
+ editTxn.deleteElement(elemId2);
795
+ editTxn.deleteElement(elemId3);
796
+ editTxn.insertElement(props);
797
+ });
761
798
  });
762
799
  });
763
800
  it("change propagation should leave txn empty", async () => {
764
- const elements = imodel.elements;
765
- // Insert elements root, child and dependency between them
766
- const rootProps = { ...props, intProperty: 0 };
767
- const rootId = elements.insertElement(rootProps);
768
- const childProps = { ...props, intProperty: 10 };
769
- const childId = elements.insertElement(childProps);
770
- const relationship = TestElementDrivesElement.create(imodel, rootId, childId);
771
- relationship.property1 = "Root drives child";
772
- relationship.insert();
773
- imodel.saveChanges("Inserted root, child element and dependency");
774
- await BeDuration.wait(10); // we rely on updating the lastMod of the newly inserted element, make sure it will be different
775
- // Setup dependency handler to update childElement
776
- let handlerCalled = false;
777
- const dropListener = TestPhysicalObject.allInputsHandled.addListener((id) => {
778
- handlerCalled = true;
779
- assert.equal(id, childId);
780
- const childEl = elements.getElement(childId);
781
- assert.equal(childEl.intProperty, 10, "int property should be 10");
782
- childEl.intProperty += 10;
783
- childEl.update();
801
+ await withEditTxn(imodel, "change-propagation", async (editTxn) => {
802
+ const elements = imodel.elements;
803
+ // Insert elements root, child and dependency between them
804
+ const rootProps = { ...props, intProperty: 0 };
805
+ const rootId = editTxn.insertElement(rootProps);
806
+ const childProps = { ...props, intProperty: 10 };
807
+ const childId = editTxn.insertElement(childProps);
808
+ const relationship = TestElementDrivesElement.create(imodel, rootId, childId);
809
+ relationship.property1 = "Root drives child";
810
+ editTxn.insertRelationship(relationship.toJSON());
811
+ editTxn.saveChanges("Inserted root, child element and dependency");
812
+ await BeDuration.wait(10); // we rely on updating the lastMod of the newly inserted element, make sure it will be different
813
+ // Setup dependency handler to update childElement
814
+ let handlerCalled = false;
815
+ const dropListener = TestPhysicalObject.allInputsHandled.addListener((arg) => {
816
+ handlerCalled = true;
817
+ assert.equal(arg.elId, childId);
818
+ const childEl = elements.getElement(childId);
819
+ assert.equal(childEl.intProperty, 10, "int property should be 10");
820
+ childEl.intProperty += 10;
821
+ editTxn.updateElement(childEl.toJSON());
822
+ });
823
+ // Validate state
824
+ const txns = imodel.txns;
825
+ assert.isFalse(txns.hasUnsavedChanges);
826
+ assert.isTrue(txns.hasPendingTxns);
827
+ assert.isTrue(txns.hasLocalChanges);
828
+ // Update rootElement and saveChanges
829
+ const rootEl = elements.getElement(rootId);
830
+ rootEl.intProperty += 10;
831
+ editTxn.updateElement(rootEl.toJSON());
832
+ editTxn.saveChanges("Updated root");
833
+ // Validate state
834
+ assert.isTrue(handlerCalled);
835
+ assert.isFalse(txns.hasUnsavedChanges, "should not have unsaved changes");
836
+ assert.isTrue(txns.hasPendingTxns);
837
+ assert.isTrue(txns.hasLocalChanges);
838
+ // Cleanup
839
+ dropListener();
784
840
  });
785
- // Validate state
786
- const txns = imodel.txns;
787
- assert.isFalse(txns.hasUnsavedChanges);
788
- assert.isTrue(txns.hasPendingTxns);
789
- assert.isTrue(txns.hasLocalChanges);
790
- // Update rootElement and saveChanges
791
- const rootEl = elements.getElement(rootId);
792
- rootEl.intProperty += 10;
793
- rootEl.update();
794
- imodel.saveChanges("Updated root");
795
- // Validate state
796
- assert.isTrue(handlerCalled);
797
- assert.isFalse(txns.hasUnsavedChanges, "should not have unsaved changes");
798
- assert.isTrue(txns.hasPendingTxns);
799
- assert.isTrue(txns.hasLocalChanges);
800
- // Cleanup
801
- dropListener();
802
841
  });
803
842
  // This bug occurred in one of the authoring apps. This test reproduced the problem, and now serves as a regression test.
804
843
  it("doesn't crash when reversing a single txn that inserts a model and a contained element while geometric model tracking is enabled", () => {
805
- imodel[_nativeDb].setGeometricModelTrackingEnabled(true);
806
- const model = PhysicalModel.insert(imodel, IModel.rootSubjectId, Guid.createValue());
807
- expect(Id64.isValidId64(model)).to.be.true;
808
- const elem = imodel.elements.insertElement({ ...props, model });
809
- expect(Id64.isValidId64(elem)).to.be.true;
810
- imodel.saveChanges("insert model and element");
811
- imodel.txns.reverseSingleTxn();
812
- imodel[_nativeDb].setGeometricModelTrackingEnabled(false);
844
+ withEditTxn(imodel, "reverse-single-txn", (editTxn) => {
845
+ imodel[_nativeDb].setGeometricModelTrackingEnabled(true);
846
+ const model = PhysicalModel.insert(editTxn, IModel.rootSubjectId, Guid.createValue());
847
+ expect(Id64.isValidId64(model)).to.be.true;
848
+ const elem = editTxn.insertElement({ ...props, model });
849
+ expect(Id64.isValidId64(elem)).to.be.true;
850
+ editTxn.saveChanges("insert model and element");
851
+ imodel.txns.reverseSingleTxn();
852
+ imodel[_nativeDb].setGeometricModelTrackingEnabled(false);
853
+ });
813
854
  });
814
855
  it("get local changes", async () => {
815
- const elements = imodel.elements;
816
- const txns = imodel.txns;
817
- const el1 = elements.insertElement(props);
818
- elements.insertElement(props);
819
- // Should not return any changes
820
- assert.deepEqual(Array.from(txns.queryLocalChanges({ includeUnsavedChanges: false })), []);
821
- // Should return change that are not saved yet
822
- const e0 = [
823
- {
824
- changeType: "inserted",
825
- classFullName: "TestBim:TestPhysicalObject",
826
- id: "0x40",
827
- },
828
- {
829
- changeType: "inserted",
830
- classFullName: "TestBim:TestPhysicalObject",
831
- id: "0x3f",
832
- },
833
- ];
834
- assert.deepEqual(Array.from(txns.queryLocalChanges({ includeUnsavedChanges: true })), e0);
835
- // Saved changes cause change propagation
836
- imodel.saveChanges("2 inserts");
837
- const e1 = [
838
- {
839
- changeType: "inserted",
840
- classFullName: "TestBim:TestPhysicalObject",
841
- id: "0x40",
842
- },
843
- {
844
- changeType: "inserted",
845
- classFullName: "TestBim:TestPhysicalObject",
846
- id: "0x3f",
847
- },
848
- {
849
- changeType: "updated",
850
- classFullName: "BisCore:PhysicalModel",
851
- id: "0x3c",
852
- },
853
- ];
854
- assert.deepEqual(Array.from(txns.queryLocalChanges({ includeUnsavedChanges: false })), e1);
855
- assert.deepEqual(Array.from(txns.queryLocalChanges({ includeUnsavedChanges: true })), e1);
856
- // delete the element.
857
- elements.deleteElement(el1);
858
- // Delete element (0x40) should never show up as it was inserted/deleted locally
859
- const e3 = [
860
- {
861
- changeType: "inserted",
862
- classFullName: "TestBim:TestPhysicalObject",
863
- id: "0x40",
864
- },
865
- {
866
- changeType: "updated",
867
- classFullName: "BisCore:PhysicalModel",
868
- id: "0x3c",
869
- },
870
- ];
871
- assert.deepEqual(Array.from(txns.queryLocalChanges({ includeUnsavedChanges: true })), e3);
872
- // Saved changes
873
- imodel.saveChanges("1 deleted");
874
- assert.deepEqual(Array.from(txns.queryLocalChanges({ includeUnsavedChanges: false })), e3);
875
- assert.deepEqual(Array.from(txns.queryLocalChanges({ includeUnsavedChanges: true })), e3);
856
+ await withEditTxn(imodel, "local-changes", async (editTxn) => {
857
+ const txns = imodel.txns;
858
+ const el1 = editTxn.insertElement(props);
859
+ editTxn.insertElement(props);
860
+ // Should not return any changes
861
+ assert.deepEqual(Array.from(txns.queryLocalChanges({ includeUnsavedChanges: false })), []);
862
+ // Should return change that are not saved yet
863
+ const e0 = [
864
+ {
865
+ changeType: "inserted",
866
+ classFullName: "TestBim:TestPhysicalObject",
867
+ id: "0x40",
868
+ },
869
+ {
870
+ changeType: "inserted",
871
+ classFullName: "TestBim:TestPhysicalObject",
872
+ id: "0x3f",
873
+ },
874
+ ];
875
+ assert.deepEqual(Array.from(txns.queryLocalChanges({ includeUnsavedChanges: true })), e0);
876
+ // Saved changes cause change propagation
877
+ editTxn.saveChanges("2 inserts");
878
+ const e1 = [
879
+ {
880
+ changeType: "inserted",
881
+ classFullName: "TestBim:TestPhysicalObject",
882
+ id: "0x40",
883
+ },
884
+ {
885
+ changeType: "inserted",
886
+ classFullName: "TestBim:TestPhysicalObject",
887
+ id: "0x3f",
888
+ },
889
+ {
890
+ changeType: "updated",
891
+ classFullName: "BisCore:PhysicalModel",
892
+ id: "0x3c",
893
+ },
894
+ ];
895
+ assert.deepEqual(Array.from(txns.queryLocalChanges({ includeUnsavedChanges: false })), e1);
896
+ assert.deepEqual(Array.from(txns.queryLocalChanges({ includeUnsavedChanges: true })), e1);
897
+ // delete the element.
898
+ editTxn.deleteElement(el1);
899
+ // Delete element (0x40) should never show up as it was inserted/deleted locally
900
+ const e3 = [
901
+ {
902
+ changeType: "inserted",
903
+ classFullName: "TestBim:TestPhysicalObject",
904
+ id: "0x40",
905
+ },
906
+ {
907
+ changeType: "updated",
908
+ classFullName: "BisCore:PhysicalModel",
909
+ id: "0x3c",
910
+ },
911
+ ];
912
+ assert.deepEqual(Array.from(txns.queryLocalChanges({ includeUnsavedChanges: true })), e3);
913
+ // Saved changes
914
+ editTxn.saveChanges("1 deleted");
915
+ assert.deepEqual(Array.from(txns.queryLocalChanges({ includeUnsavedChanges: false })), e3);
916
+ assert.deepEqual(Array.from(txns.queryLocalChanges({ includeUnsavedChanges: true })), e3);
917
+ });
876
918
  });
877
919
  describe("deleteAllTxns", () => {
878
920
  it("deletes pending and/or unsaved changes", () => {
879
921
  expect(imodel.txns.hasLocalChanges).to.be.false;
880
922
  expect(imodel.txns.hasPendingTxns).to.be.false;
881
923
  expect(imodel.txns.hasUnsavedChanges).to.be.false;
882
- imodel.elements.insertElement(props);
883
- expect(imodel.txns.hasLocalChanges).to.be.true;
884
- expect(imodel.txns.hasPendingTxns).to.be.false;
885
- expect(imodel.txns.hasUnsavedChanges).to.be.true;
924
+ withEditTxn(imodel, (txn) => {
925
+ txn.insertElement(props);
926
+ expect(imodel.txns.hasLocalChanges).to.be.true;
927
+ expect(imodel.txns.hasPendingTxns).to.be.false;
928
+ expect(imodel.txns.hasUnsavedChanges).to.be.true;
929
+ });
886
930
  imodel[_nativeDb].deleteAllTxns();
887
931
  expect(imodel.txns.hasLocalChanges).to.be.false;
888
- imodel.elements.insertElement(props);
889
- imodel.saveChanges();
932
+ withEditTxn(imodel, (txn) => {
933
+ txn.insertElement(props);
934
+ });
890
935
  expect(imodel.txns.hasLocalChanges).to.be.true;
891
936
  expect(imodel.txns.hasPendingTxns).to.be.true;
892
937
  expect(imodel.txns.hasUnsavedChanges).to.be.false;
893
938
  imodel[_nativeDb].deleteAllTxns();
894
939
  expect(imodel.txns.hasLocalChanges).to.be.false;
895
- imodel.elements.insertElement(props);
896
- imodel.saveChanges();
897
- imodel.elements.insertElement(props);
898
- expect(imodel.txns.hasLocalChanges).to.be.true;
899
- expect(imodel.txns.hasPendingTxns).to.be.true;
900
- expect(imodel.txns.hasUnsavedChanges).to.be.true;
901
- imodel[_nativeDb].deleteAllTxns();
902
- expect(imodel.txns.hasLocalChanges).to.be.false;
940
+ withEditTxn(imodel, (txn) => {
941
+ txn.insertElement(props);
942
+ txn.saveChanges();
943
+ txn.insertElement(props);
944
+ expect(imodel.txns.hasLocalChanges).to.be.true;
945
+ expect(imodel.txns.hasPendingTxns).to.be.true;
946
+ expect(imodel.txns.hasUnsavedChanges).to.be.true;
947
+ imodel[_nativeDb].deleteAllTxns();
948
+ expect(imodel.txns.hasLocalChanges).to.be.false;
949
+ });
903
950
  });
904
951
  it("discardChanges should revert local changes", async () => {
905
952
  // Insert and save an element
906
- const elId = imodel.elements.insertElement(props);
907
- imodel.saveChanges();
953
+ const elId = withEditTxn(imodel, (txn) => txn.insertElement(props));
908
954
  // Confirm element exists
909
955
  assert.isDefined(imodel.elements.tryGetElement(elId));
910
956
  // Discard the local changes
@@ -917,8 +963,7 @@ describe("TxnManager", () => {
917
963
  });
918
964
  it("TxnManager.deleteAllTxns does not revert local changes", () => {
919
965
  // Insert and save an element
920
- const elId = imodel.elements.insertElement(props);
921
- imodel.saveChanges();
966
+ const elId = withEditTxn(imodel, (txn) => txn.insertElement(props));
922
967
  // Confirm element exists
923
968
  assert.isDefined(imodel.elements.tryGetElement(elId));
924
969
  // Delete all txns from the TxnsTable
@@ -932,13 +977,15 @@ describe("TxnManager", () => {
932
977
  it("clears undo/redo history", () => {
933
978
  expect(imodel.txns.isRedoPossible).to.be.false;
934
979
  expect(imodel.txns.isUndoPossible).to.be.false;
935
- imodel.elements.insertElement(props);
936
- imodel.saveChanges();
980
+ withEditTxn(imodel, (txn) => {
981
+ txn.insertElement(props);
982
+ });
937
983
  expect(imodel.txns.isUndoPossible).to.be.true;
938
984
  imodel[_nativeDb].deleteAllTxns();
939
985
  expect(imodel.txns.isUndoPossible).to.be.false;
940
- imodel.elements.insertElement(props);
941
- imodel.saveChanges();
986
+ withEditTxn(imodel, (txn) => {
987
+ txn.insertElement(props);
988
+ });
942
989
  imodel.txns.reverseSingleTxn();
943
990
  expect(imodel.txns.isRedoPossible).to.be.true;
944
991
  imodel[_nativeDb].deleteAllTxns();