@openui5/sap.ui.core 1.125.0 → 1.126.1

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 (464) hide show
  1. package/.dtsgenrc +76 -0
  2. package/.eslintrc.json +4 -1
  3. package/.reuse/dep5 +4 -4
  4. package/THIRDPARTY.txt +6 -6
  5. package/package.json +1 -1
  6. package/src/jquery.sap.global.js +1 -1
  7. package/src/jquery.sap.properties.js +1 -1
  8. package/src/jquery.sap.resources.js +1 -1
  9. package/src/jquery.sap.script.js +1 -1
  10. package/src/jquery.sap.storage.js +3 -3
  11. package/src/sap/base/Event.js +1 -1
  12. package/src/sap/base/Eventing.js +1 -1
  13. package/src/sap/base/Log.js +0 -16
  14. package/src/sap/base/config.js +1 -1
  15. package/src/sap/base/i18n/LanguageTag.js +1 -1
  16. package/src/sap/base/i18n/date/TimezoneUtils.js +1 -1
  17. package/src/sap/base/util/ObjectPath.js +6 -6
  18. package/src/sap/base/util/restricted/_CancelablePromise.js +1 -1
  19. package/src/sap/base/util/restricted/_castArray.js +1 -1
  20. package/src/sap/base/util/restricted/_compact.js +1 -1
  21. package/src/sap/base/util/restricted/_curry.js +1 -1
  22. package/src/sap/base/util/restricted/_debounce.js +1 -1
  23. package/src/sap/base/util/restricted/_difference.js +1 -1
  24. package/src/sap/base/util/restricted/_differenceBy.js +1 -1
  25. package/src/sap/base/util/restricted/_differenceWith.js +1 -1
  26. package/src/sap/base/util/restricted/_flatMap.js +1 -1
  27. package/src/sap/base/util/restricted/_flatMapDeep.js +1 -1
  28. package/src/sap/base/util/restricted/_flatMapDepth.js +1 -1
  29. package/src/sap/base/util/restricted/_flatten.js +1 -1
  30. package/src/sap/base/util/restricted/_flattenDeep.js +1 -1
  31. package/src/sap/base/util/restricted/_flattenDepth.js +1 -1
  32. package/src/sap/base/util/restricted/_intersection.js +1 -1
  33. package/src/sap/base/util/restricted/_intersectionBy.js +1 -1
  34. package/src/sap/base/util/restricted/_intersectionWith.js +1 -1
  35. package/src/sap/base/util/restricted/_isEqual.js +1 -1
  36. package/src/sap/base/util/restricted/_isEqualWith.js +1 -1
  37. package/src/sap/base/util/restricted/_isNil.js +1 -1
  38. package/src/sap/base/util/restricted/_max.js +1 -1
  39. package/src/sap/base/util/restricted/_merge.js +1 -1
  40. package/src/sap/base/util/restricted/_mergeWith.js +1 -1
  41. package/src/sap/base/util/restricted/_min.js +1 -1
  42. package/src/sap/base/util/restricted/_omit.js +1 -1
  43. package/src/sap/base/util/restricted/_pick.js +1 -1
  44. package/src/sap/base/util/restricted/_pickBy.js +1 -1
  45. package/src/sap/base/util/restricted/_throttle.js +1 -1
  46. package/src/sap/base/util/restricted/_toArray.js +1 -1
  47. package/src/sap/base/util/restricted/_union.js +1 -1
  48. package/src/sap/base/util/restricted/_unionBy.js +1 -1
  49. package/src/sap/base/util/restricted/_unionWith.js +1 -1
  50. package/src/sap/base/util/restricted/_uniq.js +1 -1
  51. package/src/sap/base/util/restricted/_uniqBy.js +1 -1
  52. package/src/sap/base/util/restricted/_uniqWith.js +1 -1
  53. package/src/sap/base/util/restricted/_without.js +1 -1
  54. package/src/sap/base/util/restricted/_xor.js +1 -1
  55. package/src/sap/base/util/restricted/_xorBy.js +1 -1
  56. package/src/sap/base/util/restricted/_xorWith.js +1 -1
  57. package/src/sap/base/util/restricted/_zipObject.js +1 -1
  58. package/src/sap/base/util/restricted/_zipObjectDeep.js +1 -1
  59. package/src/sap/ui/Device.js +35 -10
  60. package/src/sap/ui/Global.js +6 -6
  61. package/src/sap/ui/base/Event.js +1 -1
  62. package/src/sap/ui/base/EventProvider.js +1 -1
  63. package/src/sap/ui/base/Interface.js +1 -1
  64. package/src/sap/ui/base/ManagedObject.js +16 -1
  65. package/src/sap/ui/base/ManagedObjectMetadata.js +1 -1
  66. package/src/sap/ui/base/Metadata.js +1 -1
  67. package/src/sap/ui/base/Object.js +1 -1
  68. package/src/sap/ui/base/ObjectPool.js +1 -1
  69. package/src/sap/ui/core/.library +1 -8
  70. package/src/sap/ui/core/BusyIndicator.js +1 -1
  71. package/src/sap/ui/core/Component.js +17 -3
  72. package/src/sap/ui/core/ComponentContainer.js +1 -1
  73. package/src/sap/ui/core/ComponentMetadata.js +1 -1
  74. package/src/sap/ui/core/ComponentSupport.js +1 -1
  75. package/src/sap/ui/core/Configuration.js +1 -1
  76. package/src/sap/ui/core/Control.js +1 -1
  77. package/src/sap/ui/core/Core.js +14 -5
  78. package/src/sap/ui/core/CustomData.js +1 -1
  79. package/src/sap/ui/core/DeclarativeSupport.js +1 -1
  80. package/src/sap/ui/core/Element.js +1 -1
  81. package/src/sap/ui/core/ElementMetadata.js +1 -1
  82. package/src/sap/ui/core/EnabledPropagator.js +1 -1
  83. package/src/sap/ui/core/EventBus.js +1 -1
  84. package/src/sap/ui/core/Fragment.js +1 -1
  85. package/src/sap/ui/core/HTML.js +1 -1
  86. package/src/sap/ui/core/History.js +1 -1
  87. package/src/sap/ui/core/Icon.js +1 -1
  88. package/src/sap/ui/core/IndicationColorSupport.js +1 -1
  89. package/src/sap/ui/core/IntervalTrigger.js +1 -1
  90. package/src/sap/ui/core/InvisibleMessage.js +1 -1
  91. package/src/sap/ui/core/InvisibleRenderer.js +1 -1
  92. package/src/sap/ui/core/InvisibleText.js +1 -1
  93. package/src/sap/ui/core/Item.js +1 -1
  94. package/src/sap/ui/core/LabelEnablement.js +1 -1
  95. package/src/sap/ui/core/LayoutData.js +1 -1
  96. package/src/sap/ui/core/Lib.js +2 -2
  97. package/src/sap/ui/core/ListItem.js +1 -1
  98. package/src/sap/ui/core/LocalBusyIndicator.js +1 -1
  99. package/src/sap/ui/core/Locale.js +1 -1
  100. package/src/sap/ui/core/LocaleData.js +148 -102
  101. package/src/sap/ui/core/Manifest.js +1 -1
  102. package/src/sap/ui/core/Message.js +1 -1
  103. package/src/sap/ui/core/RenderManager.js +1 -1
  104. package/src/sap/ui/core/Renderer.js +1 -1
  105. package/src/sap/ui/core/ResizeHandler.js +1 -1
  106. package/src/sap/ui/core/ScrollBar.js +1 -1
  107. package/src/sap/ui/core/SeparatorItem.js +1 -1
  108. package/src/sap/ui/core/Title.js +1 -1
  109. package/src/sap/ui/core/TooltipBase.js +1 -1
  110. package/src/sap/ui/core/UIArea.js +2 -2
  111. package/src/sap/ui/core/UIComponent.js +1 -1
  112. package/src/sap/ui/core/UIComponentMetadata.js +1 -1
  113. package/src/sap/ui/core/ValueStateSupport.js +1 -1
  114. package/src/sap/ui/core/VariantLayoutData.js +1 -1
  115. package/src/sap/ui/core/XMLComposite.js +1 -1
  116. package/src/sap/ui/core/XMLCompositeMetadata.js +1 -1
  117. package/src/sap/ui/core/boot.js +1 -6
  118. package/src/sap/ui/core/date/UI5Date.js +1 -1
  119. package/src/sap/ui/core/delegate/ItemNavigation.js +1 -1
  120. package/src/sap/ui/core/delegate/ScrollEnablement.js +2 -2
  121. package/src/sap/ui/core/dnd/DragAndDrop.js +151 -10
  122. package/src/sap/ui/core/dnd/DragDropBase.js +13 -2
  123. package/src/sap/ui/core/dnd/DragDropInfo.js +1 -1
  124. package/src/sap/ui/core/dnd/DragInfo.js +1 -1
  125. package/src/sap/ui/core/dnd/DropInfo.js +1 -1
  126. package/src/sap/ui/{model → core/fieldhelp}/FieldHelp.js +92 -65
  127. package/src/sap/ui/core/fieldhelp/FieldHelpUtil.js +62 -0
  128. package/src/sap/ui/core/format/FormatUtils.js +1 -1
  129. package/src/sap/ui/core/format/TimezoneUtil.js +1 -1
  130. package/src/sap/ui/core/getCompatibilityVersion.js +1 -1
  131. package/src/sap/ui/core/hyphenation/Hyphenation.js +2 -1
  132. package/src/sap/ui/core/library.js +49 -9
  133. package/src/sap/ui/core/message/ControlMessageProcessor.js +1 -1
  134. package/src/sap/ui/core/message/Message.js +1 -1
  135. package/src/sap/ui/core/message/MessageManager.js +1 -1
  136. package/src/sap/ui/core/message/MessageParser.js +1 -1
  137. package/src/sap/ui/core/message/MessageProcessor.js +1 -1
  138. package/src/sap/ui/core/mvc/ControllerExtension.js +16 -0
  139. package/src/sap/ui/core/mvc/HTMLView.js +1 -1
  140. package/src/sap/ui/core/mvc/JSONView.js +1 -1
  141. package/src/sap/ui/core/mvc/JSView.js +1 -1
  142. package/src/sap/ui/core/mvc/TemplateView.js +1 -1
  143. package/src/sap/ui/core/mvc/View.js +1 -1
  144. package/src/sap/ui/core/mvc/XMLView.js +1 -1
  145. package/src/sap/ui/core/plugin/DeclarativeSupport.js +1 -1
  146. package/src/sap/ui/core/plugin/LessSupport.js +1 -1
  147. package/src/sap/ui/core/plugin/TemplatingSupport.js +1 -1
  148. package/src/sap/ui/core/postmessage/Bus.js +1 -1
  149. package/src/sap/ui/core/postmessage/confirmationDialog.js +1 -1
  150. package/src/sap/ui/core/search/OpenSearchProvider.js +1 -1
  151. package/src/sap/ui/core/search/SearchProvider.js +1 -1
  152. package/src/sap/ui/core/service/Service.js +1 -1
  153. package/src/sap/ui/core/service/ServiceFactory.js +1 -1
  154. package/src/sap/ui/core/service/ServiceFactoryRegistry.js +1 -1
  155. package/src/sap/ui/core/support/Plugin.js +1 -1
  156. package/src/sap/ui/core/support/Support.js +1 -1
  157. package/src/sap/ui/core/support/plugins/ControlTree.js +1 -1
  158. package/src/sap/ui/core/support/plugins/Interaction.js +1 -1
  159. package/src/sap/ui/core/support/plugins/LocalStorage.js +1 -1
  160. package/src/sap/ui/core/support/plugins/Performance.js +1 -1
  161. package/src/sap/ui/core/support/plugins/Selector.js +1 -1
  162. package/src/sap/ui/core/support/plugins/TechInfo.js +1 -1
  163. package/src/sap/ui/core/support/plugins/Trace.js +1 -1
  164. package/src/sap/ui/core/support/plugins/ViewInfo.js +1 -1
  165. package/src/sap/ui/core/support/trace/E2eTraceLib.js +1 -1
  166. package/src/sap/ui/core/themes/sap_hcb/base_BlindLayer.less +37 -0
  167. package/src/sap/ui/core/themes/sap_hcb/base_BrowserScrollbar.less +36 -0
  168. package/src/sap/ui/core/themes/sap_hcb/base_BusyIndicator.less +19 -0
  169. package/src/sap/ui/core/themes/sap_hcb/base_CommandShortcutHints.less +20 -0
  170. package/src/sap/ui/core/themes/sap_hcb/base_DragAndDrop.less +76 -0
  171. package/src/sap/ui/core/themes/sap_hcb/base_Icon.less +144 -0
  172. package/src/sap/ui/core/themes/sap_hcb/base_LocalBusyIndicator.less +292 -0
  173. package/src/sap/ui/core/themes/sap_hcb/base_SAP-icons.less +10 -0
  174. package/src/sap/ui/core/themes/sap_hcb/base_ScrollBar.less +20 -0
  175. package/src/sap/ui/core/themes/sap_hcb/base_Shadow.less +3 -0
  176. package/src/sap/ui/core/themes/sap_hcb/base_TechnicalInfo.less +157 -0
  177. package/src/sap/ui/core/themes/sap_hcb/base_View.less +12 -0
  178. package/src/sap/ui/core/themes/sap_hcb/base_media.less +36 -0
  179. package/src/sap/ui/core/themes/sap_hcb/base_parameterClasses.less +83 -0
  180. package/src/sap/ui/core/themes/sap_hcb/base_shared.less +340 -0
  181. package/src/sap/ui/core/themes/sap_hcb/library.source.less +27 -1
  182. package/src/sap/ui/core/theming/Parameters.js +68 -8
  183. package/src/sap/ui/core/tmpl/DOMAttribute.js +1 -1
  184. package/src/sap/ui/core/tmpl/DOMElement.js +1 -1
  185. package/src/sap/ui/core/tmpl/HandlebarsTemplate.js +1 -1
  186. package/src/sap/ui/core/tmpl/Template.js +1 -1
  187. package/src/sap/ui/core/tmpl/TemplateControl.js +1 -1
  188. package/src/sap/ui/core/util/AsyncHintsHelper.js +1 -1
  189. package/src/sap/ui/core/util/Export.js +1 -1
  190. package/src/sap/ui/core/util/ExportCell.js +1 -1
  191. package/src/sap/ui/core/util/ExportColumn.js +1 -1
  192. package/src/sap/ui/core/util/ExportRow.js +1 -1
  193. package/src/sap/ui/core/util/ExportType.js +1 -1
  194. package/src/sap/ui/core/util/ExportTypeCSV.js +1 -1
  195. package/src/sap/ui/core/util/File.js +1 -1
  196. package/src/sap/ui/core/util/LibraryInfo.js +2 -2
  197. package/src/sap/ui/core/util/MockServer.js +1 -2
  198. package/src/sap/ui/core/util/PasteHelper.js +1 -1
  199. package/src/sap/ui/core/util/XMLPreprocessor.js +14 -15
  200. package/src/sap/ui/core/util/serializer/HTMLViewSerializer.js +1 -1
  201. package/src/sap/ui/core/util/serializer/Serializer.js +1 -1
  202. package/src/sap/ui/core/util/serializer/ViewSerializer.js +1 -1
  203. package/src/sap/ui/core/util/serializer/XMLViewSerializer.js +1 -1
  204. package/src/sap/ui/core/util/serializer/delegate/Delegate.js +1 -1
  205. package/src/sap/ui/core/util/serializer/delegate/HTML.js +1 -1
  206. package/src/sap/ui/core/util/serializer/delegate/XML.js +1 -1
  207. package/src/sap/ui/core/webc/WebComponent.js +1 -1
  208. package/src/sap/ui/core/webc/WebComponentMetadata.js +1 -1
  209. package/src/sap/ui/core/ws/ReadyState.js +1 -1
  210. package/src/sap/ui/core/ws/SapPcpWebSocket.js +1 -1
  211. package/src/sap/ui/core/ws/WebSocket.js +1 -1
  212. package/src/sap/ui/debug/ControlTree.js +1 -1
  213. package/src/sap/ui/debug/DebugEnv.js +1 -1
  214. package/src/sap/ui/debug/PropertyList.js +1 -1
  215. package/src/sap/ui/model/Binding.js +28 -0
  216. package/src/sap/ui/model/ClientModel.js +1 -1
  217. package/src/sap/ui/model/CompositeDataState.js +1 -1
  218. package/src/sap/ui/model/CompositeType.js +1 -1
  219. package/src/sap/ui/model/DataState.js +1 -1
  220. package/src/sap/ui/model/ManagedObjectBindingSupport.js +5 -0
  221. package/src/sap/ui/model/MetaModel.js +1 -1
  222. package/src/sap/ui/model/Model.js +1 -1
  223. package/src/sap/ui/model/SelectionModel.js +1 -1
  224. package/src/sap/ui/model/SimpleType.js +1 -1
  225. package/src/sap/ui/model/TreeAutoExpandMode.js +1 -1
  226. package/src/sap/ui/model/Type.js +1 -1
  227. package/src/sap/ui/model/base/ManagedObjectModel.js +12 -2
  228. package/src/sap/ui/model/json/JSONModel.js +16 -11
  229. package/src/sap/ui/model/message/MessageModel.js +1 -1
  230. package/src/sap/ui/model/odata/ODataAnnotations.js +1 -1
  231. package/src/sap/ui/model/odata/ODataMessageParser.js +1 -1
  232. package/src/sap/ui/model/odata/ODataMetaModel.js +1 -1
  233. package/src/sap/ui/model/odata/ODataMetadata.js +1 -1
  234. package/src/sap/ui/model/odata/ODataModel.js +1 -1
  235. package/src/sap/ui/model/odata/ODataTreeBindingFlat.js +7 -2
  236. package/src/sap/ui/model/odata/ODataUtils.js +22 -14
  237. package/src/sap/ui/model/odata/type/Boolean.js +1 -1
  238. package/src/sap/ui/model/odata/type/Byte.js +1 -1
  239. package/src/sap/ui/model/odata/type/Currency.js +1 -1
  240. package/src/sap/ui/model/odata/type/Date.js +1 -1
  241. package/src/sap/ui/model/odata/type/DateTime.js +1 -1
  242. package/src/sap/ui/model/odata/type/DateTimeBase.js +1 -1
  243. package/src/sap/ui/model/odata/type/DateTimeOffset.js +1 -1
  244. package/src/sap/ui/model/odata/type/DateTimeWithTimezone.js +1 -1
  245. package/src/sap/ui/model/odata/type/Decimal.js +1 -1
  246. package/src/sap/ui/model/odata/type/Double.js +1 -1
  247. package/src/sap/ui/model/odata/type/Guid.js +1 -1
  248. package/src/sap/ui/model/odata/type/Int.js +1 -1
  249. package/src/sap/ui/model/odata/type/Int16.js +1 -1
  250. package/src/sap/ui/model/odata/type/Int32.js +1 -1
  251. package/src/sap/ui/model/odata/type/Int64.js +1 -1
  252. package/src/sap/ui/model/odata/type/ODataType.js +1 -1
  253. package/src/sap/ui/model/odata/type/Raw.js +1 -1
  254. package/src/sap/ui/model/odata/type/SByte.js +1 -1
  255. package/src/sap/ui/model/odata/type/Single.js +1 -1
  256. package/src/sap/ui/model/odata/type/Stream.js +1 -1
  257. package/src/sap/ui/model/odata/type/String.js +1 -1
  258. package/src/sap/ui/model/odata/type/Time.js +1 -1
  259. package/src/sap/ui/model/odata/type/TimeOfDay.js +1 -1
  260. package/src/sap/ui/model/odata/type/Unit.js +1 -1
  261. package/src/sap/ui/model/odata/v2/Context.js +1 -1
  262. package/src/sap/ui/model/odata/v2/ODataAnnotations.js +1 -1
  263. package/src/sap/ui/model/odata/v2/ODataModel.js +1 -1
  264. package/src/sap/ui/model/odata/v2/ODataTreeBinding.js +1 -1
  265. package/src/sap/ui/model/odata/v4/Context.js +92 -43
  266. package/src/sap/ui/model/odata/v4/ODataBinding.js +20 -20
  267. package/src/sap/ui/model/odata/v4/ODataContextBinding.js +6 -7
  268. package/src/sap/ui/model/odata/v4/ODataListBinding.js +176 -87
  269. package/src/sap/ui/model/odata/v4/ODataMetaModel.js +9 -9
  270. package/src/sap/ui/model/odata/v4/ODataModel.js +36 -9
  271. package/src/sap/ui/model/odata/v4/ODataParentBinding.js +3 -3
  272. package/src/sap/ui/model/odata/v4/ODataPropertyBinding.js +12 -11
  273. package/src/sap/ui/model/odata/v4/lib/_AggregationCache.js +176 -75
  274. package/src/sap/ui/model/odata/v4/lib/_AggregationHelper.js +48 -9
  275. package/src/sap/ui/model/odata/v4/lib/_Cache.js +15 -17
  276. package/src/sap/ui/model/odata/v4/lib/_Helper.js +49 -14
  277. package/src/sap/ui/model/odata/v4/lib/_Parser.js +3 -3
  278. package/src/sap/ui/model/odata/v4/lib/_Requestor.js +20 -12
  279. package/src/sap/ui/model/odata/v4/lib/_V2MetadataConverter.js +2 -2
  280. package/src/sap/ui/model/odata/v4/lib/_V2Requestor.js +3 -3
  281. package/src/sap/ui/model/resource/ResourceModel.js +1 -1
  282. package/src/sap/ui/model/type/Boolean.js +1 -1
  283. package/src/sap/ui/model/type/Currency.js +1 -1
  284. package/src/sap/ui/model/type/Date.js +1 -1
  285. package/src/sap/ui/model/type/DateInterval.js +1 -1
  286. package/src/sap/ui/model/type/DateTime.js +1 -1
  287. package/src/sap/ui/model/type/DateTimeInterval.js +1 -1
  288. package/src/sap/ui/model/type/FileSize.js +1 -1
  289. package/src/sap/ui/model/type/Float.js +1 -1
  290. package/src/sap/ui/model/type/Integer.js +1 -1
  291. package/src/sap/ui/model/type/String.js +1 -1
  292. package/src/sap/ui/model/type/Time.js +1 -1
  293. package/src/sap/ui/model/type/TimeInterval.js +1 -1
  294. package/src/sap/ui/model/type/Unit.js +1 -1
  295. package/src/sap/ui/model/xml/XMLModel.js +1 -1
  296. package/src/sap/ui/performance/trace/FESR.js +2 -2
  297. package/src/sap/ui/performance/trace/Interaction.js +1 -1
  298. package/src/sap/ui/performance/trace/Passport.js +1 -1
  299. package/src/sap/ui/qunit/utils/ControlIterator.js +1 -1
  300. package/src/sap/ui/qunit/utils/MemoryLeakCheck.js +1 -1
  301. package/src/sap/ui/test/generic/TestBase.js +1 -1
  302. package/src/sap/ui/test/starter/_utils.js +18 -3
  303. package/src/sap/ui/util/Mobile.js +7 -4
  304. package/src/sap/ui/util/Storage.js +1 -1
  305. package/src/ui5loader-autoconfig.js +0 -25
  306. package/test/sap/ui/core/BindableResponseHeaders.html +1 -2
  307. package/test/sap/ui/core/ODataV2CanonicalRequests.html +8 -5
  308. package/test/sap/ui/core/ODataV2Messages.html +14 -11
  309. package/test/sap/ui/core/ODataV2MessagesPerf.html +34 -9
  310. package/test/sap/ui/core/demokit/sample/Messaging/ODataBackendMessagesComp/ui5.yaml +1 -1
  311. package/test/sap/ui/core/demokit/sample/OpaStartup/iStartMyAppInAFrame/iStartMyAppInAFrame.html +2 -1
  312. package/test/sap/ui/core/demokit/sample/OpaStartup/iStartMyAppInAFrame/iStartMyAppInAFrame.js +2 -12
  313. package/test/sap/ui/core/demokit/sample/common/Controller.js +2 -2
  314. package/test/sap/ui/core/demokit/sample/common/ValueHelp.js +2 -2
  315. package/test/sap/ui/core/demokit/sample/matcher/BindingPath/ui5.yaml +1 -1
  316. package/test/sap/ui/core/demokit/sample/matcher/Descendant/ui5.yaml +1 -1
  317. package/test/sap/ui/core/demokit/sample/matcher/I18NText/ui5.yaml +1 -1
  318. package/test/sap/ui/core/demokit/sample/matcher/LabelFor/ui5.yaml +1 -1
  319. package/test/sap/ui/core/demokit/sample/odata/v4/GridTable/SandboxModel.js +6 -6
  320. package/test/sap/ui/core/demokit/sample/odata/v4/HierarchyBindAction/Main.controller.js +110 -21
  321. package/test/sap/ui/core/demokit/sample/odata/v4/HierarchyBindAction/Main.view.xml +35 -43
  322. package/test/sap/ui/core/demokit/sample/odata/v4/RecursiveHierarchy/RecursiveHierarchy.controller.js +118 -26
  323. package/test/sap/ui/core/demokit/sample/odata/v4/RecursiveHierarchy/RecursiveHierarchy.view.xml +99 -90
  324. package/test/sap/ui/core/demokit/sample/odata/v4/RecursiveHierarchy/SandboxModel.js +87 -40
  325. package/test/sap/ui/core/demokit/sample/odata/v4/RecursiveHierarchy/pages/Main.js +4 -3
  326. package/test/sap/ui/core/demokit/sample/odata/v4/SalesOrders/Component.js +18 -0
  327. package/test/sap/ui/core/demokit/sample/odata/v4/SalesOrders/tests/CreateRelative.js +1 -8
  328. package/test/sap/ui/core/demokit/sample/odata/v4/SalesOrdersRTATest/Main.controller.js +1 -2
  329. package/test/sap/ui/core/demokit/tutorial/databinding/01/ui5.yaml +1 -1
  330. package/test/sap/ui/core/demokit/tutorial/databinding/02/ui5.yaml +1 -1
  331. package/test/sap/ui/core/demokit/tutorial/databinding/03/ui5.yaml +1 -1
  332. package/test/sap/ui/core/demokit/tutorial/databinding/04/ui5.yaml +1 -1
  333. package/test/sap/ui/core/demokit/tutorial/databinding/05/ui5.yaml +1 -1
  334. package/test/sap/ui/core/demokit/tutorial/databinding/06/ui5.yaml +1 -1
  335. package/test/sap/ui/core/demokit/tutorial/databinding/07/ui5.yaml +1 -1
  336. package/test/sap/ui/core/demokit/tutorial/databinding/08/ui5.yaml +1 -1
  337. package/test/sap/ui/core/demokit/tutorial/databinding/09/ui5.yaml +1 -1
  338. package/test/sap/ui/core/demokit/tutorial/databinding/10/ui5.yaml +1 -1
  339. package/test/sap/ui/core/demokit/tutorial/databinding/11/ui5.yaml +1 -1
  340. package/test/sap/ui/core/demokit/tutorial/databinding/12/ui5.yaml +1 -1
  341. package/test/sap/ui/core/demokit/tutorial/databinding/13/ui5.yaml +1 -1
  342. package/test/sap/ui/core/demokit/tutorial/databinding/14/ui5.yaml +1 -1
  343. package/test/sap/ui/core/demokit/tutorial/databinding/15/ui5.yaml +1 -1
  344. package/test/sap/ui/core/demokit/tutorial/mockserver/01/ui5.yaml +1 -1
  345. package/test/sap/ui/core/demokit/tutorial/mockserver/02/ui5.yaml +1 -1
  346. package/test/sap/ui/core/demokit/tutorial/mockserver/03/ui5.yaml +1 -1
  347. package/test/sap/ui/core/demokit/tutorial/mockserver/04/ui5.yaml +1 -1
  348. package/test/sap/ui/core/demokit/tutorial/navigation/01/ui5.yaml +1 -1
  349. package/test/sap/ui/core/demokit/tutorial/navigation/02/ui5.yaml +1 -1
  350. package/test/sap/ui/core/demokit/tutorial/navigation/03/ui5.yaml +1 -1
  351. package/test/sap/ui/core/demokit/tutorial/navigation/04/ui5.yaml +1 -1
  352. package/test/sap/ui/core/demokit/tutorial/navigation/05/ui5.yaml +1 -1
  353. package/test/sap/ui/core/demokit/tutorial/navigation/06/ui5.yaml +1 -1
  354. package/test/sap/ui/core/demokit/tutorial/navigation/07/ui5.yaml +1 -1
  355. package/test/sap/ui/core/demokit/tutorial/navigation/08/ui5.yaml +1 -1
  356. package/test/sap/ui/core/demokit/tutorial/navigation/09/ui5.yaml +1 -1
  357. package/test/sap/ui/core/demokit/tutorial/navigation/10/ui5.yaml +1 -1
  358. package/test/sap/ui/core/demokit/tutorial/navigation/11/ui5.yaml +1 -1
  359. package/test/sap/ui/core/demokit/tutorial/navigation/12/ui5.yaml +1 -1
  360. package/test/sap/ui/core/demokit/tutorial/navigation/13/ui5.yaml +1 -1
  361. package/test/sap/ui/core/demokit/tutorial/navigation/14/ui5.yaml +1 -1
  362. package/test/sap/ui/core/demokit/tutorial/navigation/15/ui5.yaml +1 -1
  363. package/test/sap/ui/core/demokit/tutorial/navigation/16/ui5.yaml +1 -1
  364. package/test/sap/ui/core/demokit/tutorial/navigation/17/ui5.yaml +1 -1
  365. package/test/sap/ui/core/demokit/tutorial/odatav4/01/ui5.yaml +1 -1
  366. package/test/sap/ui/core/demokit/tutorial/odatav4/01/webapp/localService/mockserver.js +4 -4
  367. package/test/sap/ui/core/demokit/tutorial/odatav4/02/ui5.yaml +1 -1
  368. package/test/sap/ui/core/demokit/tutorial/odatav4/02/webapp/localService/mockserver.js +4 -4
  369. package/test/sap/ui/core/demokit/tutorial/odatav4/03/ui5.yaml +1 -1
  370. package/test/sap/ui/core/demokit/tutorial/odatav4/03/webapp/localService/mockserver.js +4 -4
  371. package/test/sap/ui/core/demokit/tutorial/odatav4/04/ui5.yaml +1 -1
  372. package/test/sap/ui/core/demokit/tutorial/odatav4/04/webapp/localService/mockserver.js +4 -4
  373. package/test/sap/ui/core/demokit/tutorial/odatav4/05/ui5.yaml +1 -1
  374. package/test/sap/ui/core/demokit/tutorial/odatav4/05/webapp/localService/mockserver.js +4 -4
  375. package/test/sap/ui/core/demokit/tutorial/odatav4/06/ui5.yaml +1 -1
  376. package/test/sap/ui/core/demokit/tutorial/odatav4/06/webapp/localService/mockserver.js +4 -4
  377. package/test/sap/ui/core/demokit/tutorial/odatav4/07/ui5.yaml +1 -1
  378. package/test/sap/ui/core/demokit/tutorial/odatav4/07/webapp/localService/mockserver.js +4 -4
  379. package/test/sap/ui/core/demokit/tutorial/odatav4/08/ui5.yaml +1 -1
  380. package/test/sap/ui/core/demokit/tutorial/odatav4/08/webapp/localService/mockserver.js +4 -4
  381. package/test/sap/ui/core/demokit/tutorial/odatav4/09/ui5.yaml +1 -1
  382. package/test/sap/ui/core/demokit/tutorial/odatav4/09/webapp/localService/mockserver.js +4 -4
  383. package/test/sap/ui/core/demokit/tutorial/odatav4/10/ui5.yaml +1 -1
  384. package/test/sap/ui/core/demokit/tutorial/odatav4/10/webapp/localService/mockserver.js +4 -4
  385. package/test/sap/ui/core/demokit/tutorial/odatav4/11/ui5.yaml +1 -1
  386. package/test/sap/ui/core/demokit/tutorial/odatav4/11/webapp/localService/mockserver.js +4 -4
  387. package/test/sap/ui/core/demokit/tutorial/troubleshooting/01/ui5.yaml +1 -1
  388. package/test/sap/ui/core/internal/samples/odata/v2/TreeTable/Main.view.xml +3 -3
  389. package/test/sap/ui/core/qunit/CommandExecution.qunit.js +96 -92
  390. package/test/sap/ui/core/qunit/ManagedObjectModel.qunit.js +24 -0
  391. package/test/sap/ui/core/qunit/ThemeParameters.qunit.js +80 -0
  392. package/test/sap/ui/core/qunit/ThemeParameters_legacyAPIs.qunit.js +1 -0
  393. package/test/sap/ui/core/qunit/analytics/AnalyticalBinding.qunit.js +1 -1
  394. package/test/sap/ui/core/qunit/app/testsuite.app.qunit.js +2 -1
  395. package/test/sap/ui/core/qunit/base/Config.qunit.js +117 -114
  396. package/test/sap/ui/core/qunit/base/Config_bootstrap.qunit.html +3 -3
  397. package/test/sap/ui/core/qunit/base/Config_bootstrap.qunit.js +98 -95
  398. package/test/sap/ui/core/qunit/base/Config_cascade.qunit.js +20 -45
  399. package/test/sap/ui/core/qunit/base/Config_global.qunit.html +2 -2
  400. package/test/sap/ui/core/qunit/base/Config_meta.qunit.html +2 -2
  401. package/test/sap/ui/core/qunit/base/Config_noUrl.qunit.html +2 -2
  402. package/test/sap/ui/core/qunit/base/Config_noUrl.qunit.js +17 -16
  403. package/test/sap/ui/core/qunit/base/Config_url.qunit.html +2 -2
  404. package/test/sap/ui/core/qunit/bootstrap/BootstrapMainModule.beforeBootstrap.js +6 -0
  405. package/test/sap/ui/core/qunit/bootstrap/BootstrapMainModule.qunit.js +4 -4
  406. package/test/sap/ui/core/qunit/bootstrap/BootstrapMinimal.qunit.js +30 -10
  407. package/test/sap/ui/core/qunit/bootstrap/testsuite.bootstrap.qunit.js +5 -1
  408. package/test/sap/ui/core/qunit/dnd/DragAndDrop.qunit.js +159 -3
  409. package/test/sap/ui/core/qunit/{model → fieldhelp}/FieldHelp.qunit.js +306 -24
  410. package/test/sap/ui/core/qunit/fieldhelp/FieldHelpUtil.qunit.js +55 -0
  411. package/test/sap/ui/core/qunit/fieldhelp/testsuite.fieldhelp.qunit.html +12 -0
  412. package/test/sap/ui/core/qunit/fieldhelp/testsuite.fieldhelp.qunit.js +50 -0
  413. package/test/sap/ui/core/qunit/internal/1RingModels.qunit.html +1 -1
  414. package/test/sap/ui/core/qunit/internal/1RingModels.qunit.js +6 -1
  415. package/test/sap/ui/core/qunit/internal/testsuite.feature-odata-v4.qunit.js +1 -1
  416. package/test/sap/ui/core/qunit/jquery-mobile-custom.beforeBootstrap.js +11 -0
  417. package/test/sap/ui/core/qunit/jquery-mobile-custom.qunit.js +3 -3
  418. package/test/sap/ui/core/qunit/json/JSONModel.qunit.js +21 -0
  419. package/test/sap/ui/core/qunit/loader/asyncMode.qunit.js +2 -6
  420. package/test/sap/ui/core/qunit/loader/config.qunit.js +41 -2
  421. package/test/sap/ui/core/qunit/mvc/testsuite.mvc.qunit.js +9 -4
  422. package/test/sap/ui/core/qunit/odata/ODataUtils.qunit.js +5 -12
  423. package/test/sap/ui/core/qunit/odata/type/testsuite.odata.types.qunit.js +1 -0
  424. package/test/sap/ui/core/qunit/odata/v2/ODataModel.integration.qunit.js +202 -73
  425. package/test/sap/ui/core/qunit/odata/v2/ODataTreeBindingFlatNoFakeService.qunit.js +94 -2
  426. package/test/sap/ui/core/qunit/odata/v2/data/ZUI5_GWSAMPLE_BASIC.metadata.xml +4 -0
  427. package/test/sap/ui/core/qunit/odata/v4/Context.qunit.js +106 -103
  428. package/test/sap/ui/core/qunit/odata/v4/ODataBinding.qunit.js +3 -3
  429. package/test/sap/ui/core/qunit/odata/v4/ODataContextBinding.qunit.js +20 -20
  430. package/test/sap/ui/core/qunit/odata/v4/ODataListBinding.qunit.js +370 -109
  431. package/test/sap/ui/core/qunit/odata/v4/ODataMetaModel.qunit.js +42 -42
  432. package/test/sap/ui/core/qunit/odata/v4/ODataModel.integration.qunit.js +2260 -177
  433. package/test/sap/ui/core/qunit/odata/v4/ODataModel.qunit.js +39 -7
  434. package/test/sap/ui/core/qunit/odata/v4/ODataModel.realOData.qunit.js +7 -6
  435. package/test/sap/ui/core/qunit/odata/v4/ODataPropertyBinding.qunit.js +13 -14
  436. package/test/sap/ui/core/qunit/odata/v4/_AnnotationHelperExpression.qunit.js +3 -3
  437. package/test/sap/ui/core/qunit/odata/v4/lib/_AggregationCache.qunit.js +592 -169
  438. package/test/sap/ui/core/qunit/odata/v4/lib/_AggregationHelper.qunit.js +82 -2
  439. package/test/sap/ui/core/qunit/odata/v4/lib/_Cache.qunit.js +44 -47
  440. package/test/sap/ui/core/qunit/odata/v4/lib/_Helper.qunit.js +80 -20
  441. package/test/sap/ui/core/qunit/odata/v4/lib/_MetadataRequestor.qunit.js +2 -2
  442. package/test/sap/ui/core/qunit/odata/v4/lib/_Requestor.qunit.js +175 -69
  443. package/test/sap/ui/core/qunit/odata/v4/lib/_V2Requestor.qunit.js +3 -3
  444. package/test/sap/ui/core/qunit/odata/v4/testsuite.odatav4.qunit.js +1 -1
  445. package/test/sap/ui/core/qunit/performance/trace/FESR.qunit.js +9 -7
  446. package/test/sap/ui/core/qunit/rule/testsuite.rule.qunit.js +6 -1
  447. package/test/sap/ui/core/qunit/testdata/libraries/themeParameters/lib4/themes/sap_horizon_hcb/library.css +2 -0
  448. package/test/sap/ui/core/qunit/testsuite.qunit.js +1 -0
  449. package/test/sap/ui/core/qunit/testsuites/testsuite.browser.runtime.qunit.js +5 -1
  450. package/test/sap/ui/core/qunit/testsuites/testsuite.databinding.qunit.js +0 -3
  451. package/test/sap/ui/core/qunit/testsuites/testsuite.theming.qunit.js +4 -1
  452. package/test/sap/ui/core/qunit/ui/Device.qunit.js +68 -25
  453. package/test/sap/ui/core/qunit/ui5classes/ManagedObjectMetadata.qunit.js +3 -0
  454. package/test/sap/ui/core/qunit/util/Mobile.qunit.js +12 -7
  455. package/test/sap/ui/core/qunit/util/XMLPreprocessor.qunit.js +20 -20
  456. package/test/sap/ui/core/relnotes/changes-1.125.json +1 -6
  457. package/test/sap/ui/core/relnotes/changes-1.126.json +63 -0
  458. package/test/sap/ui/core/samples/databinding/UnitTable/ui5.yaml +1 -1
  459. package/test/sap/ui/core/visual/images/ContextMenuSupport/windows/1600x1200/chrome/horizon/ltr/cozy/firstItem-contextMenu.ref.lnk +1 -1
  460. package/test/sap/ui/core/visual/images/ContextMenuSupport/windows/1600x1200/chrome/horizon/ltr/cozy/initial.ref.lnk +1 -1
  461. package/test/sap/ui/core/visual/images/ContextMenuSupport/windows/1600x1200/chrome/horizon/ltr/cozy/lastItem-contextMenu.ref.lnk +1 -1
  462. package/test/sap/ui/core/visual/images/ContextMenuSupport/windows/1600x1200/chrome/horizon/ltr/cozy/leftDownBtn-contextMenu.ref.lnk +1 -1
  463. package/test/sap/ui/core/visual/images/ContextMenuSupport/windows/1600x1200/chrome/horizon/ltr/cozy/myButtonSample-contextMenu.ref.lnk +1 -1
  464. package/test/sap/ui/core/visual/images/ContextMenuSupport/windows/1600x1200/chrome/horizon/ltr/cozy/rightDownBtn-contextMenu.ref.lnk +1 -1
@@ -99,6 +99,7 @@ sap.ui.define([
99
99
  */
100
100
  function checkAggregationCache(sTitle, assert, oListBinding) {
101
101
  const aParentByLevel = [];
102
+ const bUnifiedCache = oListBinding.oCache.bUnifiedCache;
102
103
 
103
104
  function checkCache(oCache) {
104
105
  for (let i = 0, n = oCache.aElements.$created; i < n; i += 1) {
@@ -117,6 +118,11 @@ sap.ui.define([
117
118
  }
118
119
  strictEqual(oCache.aElements.$count === oCache.iLimit + oCache.iActiveElements, true,
119
120
  `${oCache.aElements.$count} === ${oCache.iLimit} + ${oCache.iActiveElements}`);
121
+ for (const sPredicate in oCache.aElements.$byPredicate) {
122
+ const oElement = oCache.aElements.$byPredicate[sPredicate];
123
+ strictEqual(oCache.aElements.includes(oElement), true,
124
+ `$byPredicate[${sPredicate}] in aElements`, oElement);
125
+ }
120
126
  }
121
127
 
122
128
  function isKeepAlive(sPredicate) {
@@ -172,7 +178,7 @@ sap.ui.define([
172
178
  } else if (bPlaceholder) {
173
179
  strictEqual(oParent.aElements.indexOf(oElement), -1,
174
180
  `placeholder @ level ${iLevel}`, oElement);
175
- } else {
181
+ } else if (iRank !== undefined) {
176
182
  strictEqual(iRank,
177
183
  oParent.aElements.indexOf(oElement) - oParent.aElements.$created,
178
184
  `$skip index @ level ${iLevel}`, oElement);
@@ -197,6 +203,8 @@ sap.ui.define([
197
203
 
198
204
  const oCache = _Helper.getPrivateAnnotation(oElement, "cache");
199
205
  if (oCache) {
206
+ strictEqual(bUnifiedCache, false,
207
+ "must not have a group level cache", oElement);
200
208
  checkCache(oCache);
201
209
  aParentByLevel[iLevel + 1] = oCache;
202
210
  }
@@ -222,7 +230,7 @@ sap.ui.define([
222
230
  }
223
231
 
224
232
  let iExpandTo = oListBinding.getAggregation().expandTo || 1;
225
- if (iExpandTo >= Number.MAX_SAFE_INTEGER || oListBinding.oCache.bUnifiedCache) {
233
+ if (iExpandTo >= Number.MAX_SAFE_INTEGER || bUnifiedCache) {
226
234
  iExpandTo = 99; // avoid "Invalid array length" :-)
227
235
  }
228
236
  for (let i = 0; i <= iExpandTo; i += 1) {
@@ -233,17 +241,17 @@ sap.ui.define([
233
241
  visitElements(aElements);
234
242
 
235
243
  if (iExpandTo > 1) {
236
- const aElements = oListBinding.oCache.oFirstLevel.aElements;
237
- if (!aElements.$created && aElements.$count === aElements.length) {
238
- aElements.forEach((oElement, i) => {
244
+ const aElements0 = oListBinding.oCache.oFirstLevel.aElements;
245
+ if (!aElements0.$created && aElements0.$count === aElements0.length) {
246
+ aElements0.forEach((oElement, i) => {
239
247
  if (oElement["@$ui5.node.isExpanded"]) {
240
248
  const iLevel = oElement["@$ui5.node.level"];
241
249
  let iDescendants = 0;
242
- for (let j = i + 1; j < aElements.length; j += 1) {
243
- if (!aElements[j]) {
250
+ for (let j = i + 1; j < aElements0.length; j += 1) {
251
+ if (!aElements0[j]) {
244
252
  return; // cannot count "descendants" for this oElement
245
253
  }
246
- if (iLevel >= aElements[j]["@$ui5.node.level"]) {
254
+ if (iLevel >= aElements0[j]["@$ui5.node.level"]) {
247
255
  break; // end of "descendants"
248
256
  }
249
257
  iDescendants += 1;
@@ -553,9 +561,9 @@ sap.ui.define([
553
561
  var sControlId, sRequest,
554
562
  that = this;
555
563
 
556
- function expectChanges(mValueByControl) {
557
- for (sControlId in mValueByControl) {
558
- that.expectChange(sControlId, mValueByControl[sControlId]);
564
+ function expectChanges(mValueByControl0) {
565
+ for (sControlId in mValueByControl0) {
566
+ that.expectChange(sControlId, mValueByControl0[sControlId]);
559
567
  }
560
568
  }
561
569
 
@@ -648,7 +656,9 @@ sap.ui.define([
648
656
  /**
649
657
  * Converts the sap.ui.table.Table controls within the document. Embeds all inner controls into
650
658
  * a <t:Column> with <t:template> each. <t:Column> may still be used however. Do not use <rows>,
651
- * it breaks this automatic conversion (and is unnecessary anyway).
659
+ * it breaks this automatic conversion (and is unnecessary anyway). Replaces the deprecated
660
+ * table property "visibleRowCount" by the correct <rowMode><Fixed rowCount="..."></rowMode>,
661
+ * allowing the tests to still use it.
652
662
  *
653
663
  * @param {Document} oDocument The view as XML document
654
664
  */
@@ -984,6 +994,7 @@ sap.ui.define([
984
994
  }
985
995
 
986
996
  if (Rendering.isPending() || this.oModel && this.oModel.aPrerenderingTasks
997
+ || this.aExpectedEvents.length > 0
987
998
  || Messaging.getMessageModel().getObject("/").length < this.aMessages.length) {
988
999
  setTimeout(this.checkFinish.bind(this, assert), 10);
989
1000
  } else if (this.resolve) {
@@ -1750,8 +1761,8 @@ sap.ui.define([
1750
1761
  }
1751
1762
  }
1752
1763
 
1753
- function readableUrl(sUrl) {
1754
- return sUrl.replaceAll(/%([0-9a-fA-F]{2})/g,
1764
+ function readableUrl(sUrl0) {
1765
+ return sUrl0.replaceAll(/%([0-9a-fA-F]{2})/g,
1755
1766
  (_s, n) => String.fromCharCode(Number.parseInt(n, 16)));
1756
1767
  }
1757
1768
 
@@ -3219,7 +3230,7 @@ sap.ui.define([
3219
3230
  oTypeKeepsEmptyString,
3220
3231
  "parseKeepsEmptyString ignored, type is cached and used on UI already");
3221
3232
 
3222
- // code under test (CPOUI5ODATAV4-1402)
3233
+ // code under test (JIRA: CPOUI5ODATAV4-1402)
3223
3234
  assert.deepEqual(oTable.getBinding("items").getAllCurrentContexts().map(getPath), [
3224
3235
  "/TEAMS('1')/TEAM_2_EMPLOYEES('2')"
3225
3236
  ]);
@@ -5660,6 +5671,274 @@ sap.ui.define([
5660
5671
  });
5661
5672
  });
5662
5673
 
5674
+ //*********************************************************************************************
5675
+ // Scenario: One-way property binding for a collection of complex type. Check that refresh and
5676
+ // side effects works.
5677
+ // JIRA: CPOUI5ODATAV4-2638
5678
+ QUnit.test("CPOUI5ODATAV4-2638: OneWay - Collection(ComplexType)", async function (assert) {
5679
+ const oModel = this.createTeaBusiModel({autoExpandSelect : true});
5680
+ const sView = `
5681
+ <FlexBox id="form" binding="{/EMPLOYEES('1')}">
5682
+ <Text id="name" text="{Name}"/>
5683
+ <Text id="messages" text="{
5684
+ formatter : '.myFormatter',
5685
+ mode : 'OneWay',
5686
+ path : '__CT__FAKE__Message/__FAKE__Messages',
5687
+ targetType : 'any'
5688
+ }"/>
5689
+ </FlexBox>`;
5690
+ const oController = {
5691
+ myFormatter : (aMessages) => {
5692
+ return aMessages.map((oMessage) => oMessage.message).join(" ");
5693
+ }
5694
+ };
5695
+
5696
+ const expectMessages = (aMessages) => {
5697
+ Messaging.removeAllMessages(); // clean up
5698
+ this.expectMessages(aMessages.map((sMessage) => ({
5699
+ message : sMessage,
5700
+ persistent : true,
5701
+ type : "None"
5702
+ })));
5703
+ };
5704
+
5705
+ this.expectRequest("EMPLOYEES('1')?$select=ID,Name,__CT__FAKE__Message/__FAKE__Messages", {
5706
+ ID : "1",
5707
+ Name : "Frederic Fall",
5708
+ __CT__FAKE__Message : {
5709
+ __FAKE__Messages : [{
5710
+ message : "You're looking younger than ever!"
5711
+ }]
5712
+ }
5713
+ })
5714
+ .expectChange("name", "Frederic Fall")
5715
+ .expectChange("messages", "You're looking younger than ever!");
5716
+ expectMessages(["You're looking younger than ever!"]);
5717
+
5718
+ await this.createView(assert, sView, oModel, oController);
5719
+
5720
+ this.expectRequest("EMPLOYEES('1')?$select=ID,Name,__CT__FAKE__Message/__FAKE__Messages", {
5721
+ ID : "1",
5722
+ Name : "Frederic Fall",
5723
+ __CT__FAKE__Message : {
5724
+ __FAKE__Messages : [{
5725
+ message : "And"
5726
+ }, {
5727
+ message : "so"
5728
+ }, {
5729
+ message : "on"
5730
+ }]
5731
+ }
5732
+ })
5733
+ .expectChange("messages", "And so on");
5734
+ expectMessages(["And", "so", "on"]);
5735
+
5736
+ await Promise.all([
5737
+ this.oView.byId("form").getBindingContext().requestRefresh(),
5738
+ this.waitForChanges(assert, "refresh #1")
5739
+ ]);
5740
+
5741
+ this.expectRequest("EMPLOYEES('1')?$select=ID,Name,__CT__FAKE__Message/__FAKE__Messages", {
5742
+ ID : "1",
5743
+ Name : "Frederic Fall",
5744
+ __CT__FAKE__Message : {
5745
+ __FAKE__Messages : [{
5746
+ message : "And"
5747
+ }, {
5748
+ message : "NOT"
5749
+ }, {
5750
+ message : "on"
5751
+ }]
5752
+ }
5753
+ })
5754
+ .expectChange("messages", "And NOT on");
5755
+ expectMessages(["And", "NOT", "on"]);
5756
+
5757
+ await Promise.all([
5758
+ this.oView.byId("form").getBindingContext().requestRefresh(),
5759
+ this.waitForChanges(assert, "refresh #2")
5760
+ ]);
5761
+
5762
+ this.expectRequest("EMPLOYEES('1')?$select=__CT__FAKE__Message/__FAKE__Messages", {
5763
+ __CT__FAKE__Message : {
5764
+ __FAKE__Messages : [{
5765
+ message : "And"
5766
+ }, {
5767
+ message : "further"
5768
+ }, {
5769
+ message : "on"
5770
+ }]
5771
+ }
5772
+ })
5773
+ .expectChange("messages", "And further on");
5774
+ expectMessages(["And", "further", "on"]);
5775
+
5776
+ await Promise.all([
5777
+ this.oView.byId("form").getBindingContext()
5778
+ .requestSideEffects(["__CT__FAKE__Message/__FAKE__Messages"]),
5779
+ this.waitForChanges(assert, "side effects")
5780
+ ]);
5781
+ });
5782
+ //TODO _Helper.updateExisting
5783
+ // => PATCH/updating a property probably doesn't work - use $$patchWithoutSideEffects instead!
5784
+ //TODO _Helper.updateAll
5785
+ // => _Cache#setProperty
5786
+ // => _Cache#update
5787
+ // => update operation's $Parameter
5788
+
5789
+ //*********************************************************************************************
5790
+ // Scenario: One-way property binding for an object of complex type. Check that refresh and
5791
+ // side effects works, although there may be "change" events w/o real changes. Do this in the
5792
+ // presence of a property binding for only a part of that object.
5793
+ // JIRA: CPOUI5ODATAV4-2638
5794
+ QUnit.test("CPOUI5ODATAV4-2638: OneWay - object w/ ComplexType, #1", async function (assert) {
5795
+ const oModel = this.createTeaBusiModel({autoExpandSelect : true});
5796
+ const sView = `
5797
+ <FlexBox id="form" binding="{/EMPLOYEES('1')}">
5798
+ <Text id="name" text="{Name}"/>
5799
+ <Text id="salary" text="{
5800
+ formatter : '.myFormatter',
5801
+ mode : 'OneWay',
5802
+ path : 'SALARY',
5803
+ targetType : 'any'
5804
+ }"/>
5805
+ <Text id="monthly" text="{SALARY/MONTHLY_BASIC_SALARY_AMOUNT}"/>
5806
+ </FlexBox>`;
5807
+ const oController = {
5808
+ myFormatter : (oSalary) => {
5809
+ return `${oSalary.MONTHLY_BASIC_SALARY_AMOUNT} ${oSalary.BASIC_SALARY_CURR}`;
5810
+ }
5811
+ };
5812
+
5813
+ this.expectRequest("EMPLOYEES('1')"
5814
+ //TODO $select could be much smarter!
5815
+ + "?$select=ID,Name,SALARY,SALARY/MONTHLY_BASIC_SALARY_AMOUNT", {
5816
+ ID : "1",
5817
+ Name : "Frederic Fall",
5818
+ SALARY : {
5819
+ MONTHLY_BASIC_SALARY_AMOUNT : "1234",
5820
+ BASIC_SALARY_CURR : "EUR",
5821
+ YEARLY_BONUS_AMOUNT : "567",
5822
+ BONUS_CURR : "DEM"
5823
+ }
5824
+ })
5825
+ .expectChange("salary", "1234 EUR")
5826
+ .expectChange("monthly", "1,234");
5827
+
5828
+ await this.createView(assert, sView, oModel, oController);
5829
+
5830
+ const oContext = this.oView.byId("form").getBindingContext();
5831
+ const oNewSalary = {
5832
+ MONTHLY_BASIC_SALARY_AMOUNT : "1234.89",
5833
+ BASIC_SALARY_CURR : "EUR",
5834
+ YEARLY_BONUS_AMOUNT : "567",
5835
+ BONUS_CURR : "DEM"
5836
+ };
5837
+
5838
+ //TODO $select could be much smarter!
5839
+ this.expectRequest("EMPLOYEES('1')?$select=SALARY,SALARY/MONTHLY_BASIC_SALARY_AMOUNT", {
5840
+ SALARY : oNewSalary
5841
+ })
5842
+ .expectChange("salary", "1234.89 EUR")
5843
+ .expectChange("monthly", "1,234.89");
5844
+
5845
+ await Promise.all([
5846
+ oContext.requestSideEffects(["SALARY"]),
5847
+ this.waitForChanges(assert, "side effects")
5848
+ ]);
5849
+
5850
+ assert.deepEqual(oContext.getObject(), {
5851
+ ID : "1",
5852
+ Name : "Frederic Fall",
5853
+ SALARY : oNewSalary
5854
+ });
5855
+
5856
+ this.expectRequest("EMPLOYEES('1')"
5857
+ //TODO $select could be much smarter!
5858
+ + "?$select=ID,Name,SALARY,SALARY/MONTHLY_BASIC_SALARY_AMOUNT", {
5859
+ ID : "1",
5860
+ Name : "Frederic Fall",
5861
+ SALARY : oNewSalary
5862
+ })
5863
+ .expectChange("salary", "1234.89 EUR"); // this is accepted!
5864
+
5865
+ await Promise.all([
5866
+ oContext.requestRefresh(),
5867
+ this.waitForChanges(assert, "refresh")
5868
+ ]);
5869
+ });
5870
+
5871
+ //*********************************************************************************************
5872
+ // Scenario: One-way property binding for an object of complex type. Check that refresh of a
5873
+ // kept-alive element works.
5874
+ // JIRA: CPOUI5ODATAV4-2638
5875
+ QUnit.test("CPOUI5ODATAV4-2638: OneWay - object w/ ComplexType, #2", async function (assert) {
5876
+ const oModel = this.createTeaBusiModel({autoExpandSelect : true});
5877
+ const sView = `
5878
+ <FlexBox id="form">
5879
+ <Text id="salary" text="{
5880
+ formatter : '.myFormatter',
5881
+ mode : 'OneWay',
5882
+ path : 'SALARY',
5883
+ targetType : 'any'
5884
+ }"/>
5885
+ </FlexBox>`;
5886
+ const oController = {
5887
+ myFormatter : (oSalary) => {
5888
+ return oSalary
5889
+ ? `${oSalary.MONTHLY_BASIC_SALARY_AMOUNT} ${oSalary.BASIC_SALARY_CURR}`
5890
+ : "bad luck";
5891
+ }
5892
+ };
5893
+
5894
+ this.expectChange("salary");
5895
+
5896
+ await this.createView(assert, sView, oModel, oController);
5897
+
5898
+ this.expectRequest("EMPLOYEES('1')?$select=ID,SALARY", {
5899
+ ID : "1",
5900
+ SALARY : {
5901
+ MONTHLY_BASIC_SALARY_AMOUNT : "1234",
5902
+ BASIC_SALARY_CURR : "DEM"
5903
+ }
5904
+ })
5905
+ .expectChange("salary", "1234 DEM");
5906
+
5907
+ const oContext = oModel.getKeepAliveContext("/EMPLOYEES('1')");
5908
+ this.oView.byId("form").setBindingContext(oContext);
5909
+
5910
+ await this.waitForChanges(assert, "set binding context");
5911
+
5912
+ this.expectRequest("EMPLOYEES?$select=ID,SALARY&$filter=ID eq '1'", {
5913
+ value : [{
5914
+ ID : "1",
5915
+ SALARY : null
5916
+ }]
5917
+ })
5918
+ .expectChange("salary", "bad luck");
5919
+
5920
+ await Promise.all([
5921
+ oContext.getBinding().requestRefresh(), // don' try this at home, kids!
5922
+ this.waitForChanges(assert, "refresh to null")
5923
+ ]);
5924
+
5925
+ this.expectRequest("EMPLOYEES?$select=ID,SALARY&$filter=ID eq '1'", {
5926
+ value : [{
5927
+ ID : "1",
5928
+ SALARY : {
5929
+ MONTHLY_BASIC_SALARY_AMOUNT : "5678",
5930
+ BASIC_SALARY_CURR : "EUR"
5931
+ }
5932
+ }]
5933
+ })
5934
+ .expectChange("salary", "5678 EUR");
5935
+
5936
+ await Promise.all([
5937
+ oContext.getBinding().requestRefresh(), // don' try this at home, kids!
5938
+ this.waitForChanges(assert, "refresh from null")
5939
+ ]);
5940
+ });
5941
+
5663
5942
  //*********************************************************************************************
5664
5943
  // Scenario: A row is deleted. Afterwards, but before the rendering the list is refreshed. The
5665
5944
  // list has a nested list.
@@ -8376,6 +8655,59 @@ sap.ui.define([
8376
8655
  });
8377
8656
  });
8378
8657
 
8658
+ //*********************************************************************************************
8659
+ // Scenario: Table gets a binding context for which data was already loaded and then a refresh
8660
+ // is performed synchronously.
8661
+ // SNOW: CS20240007657519
8662
+ QUnit.test("CS20240007657519", async function (assert) {
8663
+ const oModel = this.createSalesOrdersModel({autoExpandSelect : true});
8664
+ const sView = `
8665
+ <FlexBox binding="{/SalesOrderList('1')}" id="form">
8666
+ <Table id="table" items="{path : 'SO_2_SOITEM', parameters : {$$ownRequest : true}}">
8667
+ <Text id="position" text="{ItemPosition}"/>
8668
+ </Table>
8669
+ </FlexBox>`;
8670
+
8671
+ this.expectRequest("SalesOrderList('1')/SO_2_SOITEM"
8672
+ + "?$select=ItemPosition,SalesOrderID&$skip=0&$top=100", {
8673
+ value : [ // implicitly sorted descending
8674
+ {SalesOrderID : "1", ItemPosition : "0020"},
8675
+ {SalesOrderID : "1", ItemPosition : "0010"}
8676
+ ]
8677
+ })
8678
+ .expectChange("position", ["0020", "0010"]);
8679
+
8680
+ await this.createView(assert, sView, oModel);
8681
+
8682
+ const oTable = this.oView.byId("table");
8683
+ const oFormContext = this.oView.byId("form").getBindingContext();
8684
+
8685
+ oTable.setBindingContext(null);
8686
+
8687
+ await this.waitForChanges(assert);
8688
+
8689
+ oTable.setBindingContext(oFormContext);
8690
+
8691
+ this.expectCanceledError("Failed to get contexts for " + sSalesOrderService
8692
+ + "SalesOrderList('1')/SO_2_SOITEM with start index 0 and length 100",
8693
+ sODLB + ": /SalesOrderList('1')|SO_2_SOITEM"
8694
+ + " is ignoring response from inactive cache: " + sSalesOrderService
8695
+ + "SalesOrderList('1')/SO_2_SOITEM?$select=ItemPosition,SalesOrderID")
8696
+ .expectRequest("SalesOrderList('1')/SO_2_SOITEM"
8697
+ + "?$select=ItemPosition,SalesOrderID&$skip=0&$top=100", {
8698
+ value : [
8699
+ {SalesOrderID : "1", ItemPosition : "0030"}, // a new one!
8700
+ {SalesOrderID : "1", ItemPosition : "0020"},
8701
+ {SalesOrderID : "1", ItemPosition : "0010"}
8702
+ ]
8703
+ })
8704
+ .expectChange("position", ["0030", "0020", "0010"]);
8705
+
8706
+ oFormContext.refresh();
8707
+
8708
+ await this.waitForChanges(assert);
8709
+ });
8710
+
8379
8711
  //*********************************************************************************************
8380
8712
  // Scenario: Read and modify an entity with key aliases
8381
8713
  // CPOUI5ODATAV4-1580: show usage of ODataModel#getKeyPredicate
@@ -9329,7 +9661,7 @@ sap.ui.define([
9329
9661
  return this.createView(assert, sView, oModel).then(function () {
9330
9662
  oBinding = that.oView.byId("table").getBinding("items");
9331
9663
 
9332
- // code under test (CPOUI5MODELS-741)
9664
+ // code under test (JIRA: CPOUI5MODELS-741)
9333
9665
  assert.deepEqual(oBinding.getAllCurrentContexts().map(getPath), [
9334
9666
  "/SalesOrderList('42')"
9335
9667
  ]);
@@ -9350,7 +9682,7 @@ sap.ui.define([
9350
9682
 
9351
9683
  assert.strictEqual(oBinding.getLength(), 2);
9352
9684
 
9353
- // code under test (CPOUI5MODELS-741)
9685
+ // code under test (JIRA: CPOUI5MODELS-741)
9354
9686
  assert.deepEqual(oBinding.getAllCurrentContexts().map(getPath), [
9355
9687
  oCreatedContext0.getPath(),
9356
9688
  "/SalesOrderList('42')"
@@ -9366,7 +9698,7 @@ sap.ui.define([
9366
9698
 
9367
9699
  assert.strictEqual(oBinding.getLength(), 3);
9368
9700
 
9369
- // code under test (CPOUI5MODELS-741)
9701
+ // code under test (JIRA: CPOUI5MODELS-741)
9370
9702
  assert.deepEqual(oBinding.getAllCurrentContexts().map(getPath), [
9371
9703
  oCreatedContext1.getPath(),
9372
9704
  oCreatedContext0.getPath(),
@@ -13331,7 +13663,7 @@ sap.ui.define([
13331
13663
 
13332
13664
  //*********************************************************************************************
13333
13665
  // Scenario: Avoid duplicate call to computed annotation (via global path)
13334
- /** @deprecated as of version 1.120 */
13666
+ /** @deprecated As of version 1.120 */
13335
13667
  QUnit.test("Avoid duplicate call to computed annotation, global path", function (assert) {
13336
13668
  var oModel = this.createTeaBusiModel().getMetaModel(),
13337
13669
  sView = '\
@@ -16407,10 +16739,9 @@ sap.ui.define([
16407
16739
  QUnit.test("read all data w/o a control on top", function (assert) {
16408
16740
  var aIDs = [],
16409
16741
  aValues = [],
16410
- i,
16411
16742
  that = this;
16412
16743
 
16413
- for (i = 0; i < 10000; i += 1) {
16744
+ for (let i = 0; i < 10000; i += 1) {
16414
16745
  aIDs[i] = "TEAM_" + i;
16415
16746
  aValues[i] = {Team_Id : aIDs[i]};
16416
16747
  }
@@ -16541,7 +16872,7 @@ sap.ui.define([
16541
16872
  // BCP: 2180279839
16542
16873
  // BCP: 2280024694
16543
16874
  //
16544
- // Same with prefetch of preceeding entries (SNOW: CS20230006538459)
16875
+ // Same with prefetch of preceding entries (SNOW: CS20230006538459)
16545
16876
  QUnit.test("BCP: 2180279839: dataReceived follows each dataRequested", function (assert) {
16546
16877
  var oListBinding,
16547
16878
  aValues = [],
@@ -16549,7 +16880,7 @@ sap.ui.define([
16549
16880
  that = this;
16550
16881
 
16551
16882
  for (i = 0; i < 1110; i += 1) {
16552
- if (i < 17 || i >= 1093) {
16883
+ if (i < 24 || i >= 1093) {
16553
16884
  aValues[i] = {Team_Id : "TEAM_" + i};
16554
16885
  }
16555
16886
  }
@@ -16568,7 +16899,7 @@ sap.ui.define([
16568
16899
 
16569
16900
  return that.waitForChanges(assert);
16570
16901
  }).then(function () {
16571
- that.expectRequest("TEAMS?$skip=7&$top=3", {value : aValues.slice(7, 10)})
16902
+ that.expectRequest("TEAMS?$skip=7&$top=5", {value : aValues.slice(7, 12)})
16572
16903
  .expectEvents(assert, oListBinding, [
16573
16904
  [, "dataRequested"],
16574
16905
  [, "dataReceived", {data : {}}]
@@ -16625,13 +16956,13 @@ sap.ui.define([
16625
16956
 
16626
16957
  return that.waitForChanges(assert);
16627
16958
  }).then(function () { // iIndex - iPrefetchLength becomes negative here
16628
- that.expectRequest("TEAMS?$skip=10&$top=7", {value : aValues.slice(10, 17)})
16959
+ that.expectRequest("TEAMS?$skip=12&$top=12", {value : aValues.slice(12, 24)})
16629
16960
  .expectEvents(assert, oListBinding, [
16630
16961
  [, "dataRequested"],
16631
16962
  [, "dataReceived", {data : {}}]
16632
16963
  ]);
16633
16964
 
16634
- assert.deepEqual(oListBinding.getContexts(0, 7, 10).map(getPath), [
16965
+ assert.deepEqual(oListBinding.getContexts(0, 7, 12).map(getPath), [
16635
16966
  "/TEAMS('TEAM_0')",
16636
16967
  "/TEAMS('TEAM_1')",
16637
16968
  "/TEAMS('TEAM_2')",
@@ -18655,7 +18986,7 @@ sap.ui.define([
18655
18986
  // code under test
18656
18987
  oBinding.resume();
18657
18988
 
18658
- // code under test (CPOUI5ODATAV4-102)
18989
+ // code under test (JIRA: CPOUI5ODATAV4-102)
18659
18990
  oContext = oBinding.create({BusinessPartnerID : "0100000099"});
18660
18991
 
18661
18992
  assert.strictEqual(oContext.getProperty("BusinessPartnerID"), undefined, "not now :-(");
@@ -19307,7 +19638,7 @@ sap.ui.define([
19307
19638
  "Request canceled: PATCH SalesOrderList('0500000000'); group: update");
19308
19639
  }
19309
19640
 
19310
- // code under test (CPOUI5ODATAV4-1884)
19641
+ // code under test (JIRA: CPOUI5ODATAV4-1884)
19311
19642
  oModel.resetChanges();
19312
19643
 
19313
19644
  assert.notOk(oDeletedContext.isDeleted()); // nevertheless it is destroyed!
@@ -20049,8 +20380,8 @@ sap.ui.define([
20049
20380
  that = this;
20050
20381
 
20051
20382
  function assertIDs(aExpectedIDs) {
20052
- assert.deepEqual(oBinding.getCurrentContexts().map(function (oContext) {
20053
- return oContext.getValue("SalesOrderID");
20383
+ assert.deepEqual(oBinding.getCurrentContexts().map(function (oContext0) {
20384
+ return oContext0.getValue("SalesOrderID");
20054
20385
  }), aExpectedIDs);
20055
20386
  }
20056
20387
 
@@ -21742,6 +22073,10 @@ sap.ui.define([
21742
22073
  // code under test (JIRA: CPOUI5ODATAV4-2337)
21743
22074
  oListBinding.getHeaderContext().isAncestorOf(/*don't care*/);
21744
22075
  }, new Error("Missing recursive hierarchy"));
22076
+ assert.throws(function () {
22077
+ // code under test (JIRA: CPOUI5ODATAV4-2558)
22078
+ oListBinding.getHeaderContext().getSibling(/*don't care*/);
22079
+ }, new Error("Missing recursive hierarchy"));
21745
22080
  });
21746
22081
  });
21747
22082
 
@@ -22205,7 +22540,7 @@ sap.ui.define([
22205
22540
  .expectChange("salesAmount", [,,, "30", "40", "200"])
22206
22541
  .expectChange("salesNumber", [,,, "3", "4", null]);
22207
22542
 
22208
- // code under test (CPOUI5ODATAV4-177)
22543
+ // code under test (JIRA: CPOUI5ODATAV4-177)
22209
22544
  oTable.requestItems();
22210
22545
 
22211
22546
  return that.waitForChanges(assert);
@@ -25574,7 +25909,7 @@ sap.ui.define([
25574
25909
  })
25575
25910
  .expectChange("count");
25576
25911
 
25577
- return this.createView(assert, sView, oModel).then(async function () {
25912
+ return this.createView(assert, sView, oModel).then(function () {
25578
25913
  oTable = that.oView.byId("table");
25579
25914
  oRoot = oTable.getRows()[0].getBindingContext();
25580
25915
  oListBinding = oRoot.getBinding();
@@ -25634,8 +25969,12 @@ sap.ui.define([
25634
25969
  }, "technical properties have been removed");
25635
25970
 
25636
25971
  // code under test
25637
- assert.strictEqual(await oRoot.requestSibling(-1), null, "CPOUI5ODATAV4-2558");
25638
- assert.strictEqual(await oRoot.requestSibling(+1), null, "CPOUI5ODATAV4-2558");
25972
+ assert.strictEqual(oRoot.getSibling(-1), null, "CPOUI5ODATAV4-2558");
25973
+ assert.strictEqual(oRoot.getSibling(+1), null, "CPOUI5ODATAV4-2558");
25974
+ assert.throws(function () {
25975
+ // code under test (JIRA: CPOUI5ODATAV4-2558)
25976
+ oRoot.getSibling(0);
25977
+ }, new Error("Unsupported offset: 0"));
25639
25978
 
25640
25979
  // code under test
25641
25980
  checkSelected(assert, oRoot, undefined);
@@ -25896,6 +26235,7 @@ sap.ui.define([
25896
26235
  "still kept alive");
25897
26236
  assert.deepEqual(oKeptAliveNode.getObject(), {
25898
26237
  "@$ui5.context.isSelected" : true,
26238
+ // NO! "@$ui5.node.level" : 1,
25899
26239
  "@odata.etag" : "etag0.4",
25900
26240
  ArtistID : "0",
25901
26241
  BestFriend : {
@@ -26784,7 +27124,7 @@ sap.ui.define([
26784
27124
  })
26785
27125
  .expectChange("count");
26786
27126
 
26787
- return this.createView(assert, sView, oModel).then(async function () {
27127
+ return this.createView(assert, sView, oModel).then(function () {
26788
27128
  oTable = that.oView.byId("table");
26789
27129
  oListBinding = oTable.getBinding("rows");
26790
27130
 
@@ -26816,9 +27156,9 @@ sap.ui.define([
26816
27156
 
26817
27157
  const [oBeta, oKappa, oLambda] = oListBinding.getCurrentContexts();
26818
27158
  // code under test
26819
- assert.strictEqual(await oKappa.requestSibling(-1), oBeta, "CPOUI5ODATAV4-2558");
26820
- assert.strictEqual(await oKappa.requestSibling(), oLambda, "CPOUI5ODATAV4-2558");
26821
- assert.strictEqual(await oKappa.requestSibling(+1), oLambda, "CPOUI5ODATAV4-2558");
27159
+ assert.strictEqual(oKappa.getSibling(-1), oBeta, "CPOUI5ODATAV4-2558");
27160
+ assert.strictEqual(oKappa.getSibling(), oLambda, "CPOUI5ODATAV4-2558");
27161
+ assert.strictEqual(oKappa.getSibling(+1), oLambda, "CPOUI5ODATAV4-2558");
26822
27162
 
26823
27163
  // code under test
26824
27164
  assert.strictEqual(oListBinding.getDownloadUrl(), sTeaBusi + "EMPLOYEES"
@@ -26851,7 +27191,7 @@ sap.ui.define([
26851
27191
  oTable.setFirstVisibleRow(0);
26852
27192
 
26853
27193
  return that.waitForChanges(assert, "scroll up");
26854
- }).then(async function () {
27194
+ }).then(function () {
26855
27195
  var oNameBinding;
26856
27196
 
26857
27197
  oRoot = oTable.getRows()[0].getBindingContext();
@@ -26875,11 +27215,11 @@ sap.ui.define([
26875
27215
  Name : "Alpha"
26876
27216
  }, "technical properties have been removed");
26877
27217
 
26878
- const [/*oAlpha*/, oBeta, oKappa, oLambda] = oListBinding.getAllCurrentContexts();
27218
+ const [oAlpha, oBeta, oKappa, oLambda] = oListBinding.getAllCurrentContexts();
26879
27219
  // code under test
26880
- //TODO assert.strictEqual(await oAlpha.requestSibling(), null, "CPOUI5ODATAV4-2558");
26881
- assert.strictEqual(await oBeta.requestSibling(-1), null, "CPOUI5ODATAV4-2558");
26882
- assert.strictEqual(await oKappa.requestSibling(), oLambda, "CPOUI5ODATAV4-2558");
27220
+ assert.strictEqual(oAlpha.getSibling(), null, "CPOUI5ODATAV4-2558");
27221
+ assert.strictEqual(oBeta.getSibling(-1), null, "CPOUI5ODATAV4-2558");
27222
+ assert.strictEqual(oKappa.getSibling(), oLambda, "CPOUI5ODATAV4-2558");
26883
27223
 
26884
27224
  that.expectRequest({
26885
27225
  batchNo : 3,
@@ -26929,7 +27269,7 @@ sap.ui.define([
26929
27269
 
26930
27270
  assert.throws(function () {
26931
27271
  // code under test
26932
- oBeta.requestSibling();
27272
+ oBeta.getSibling();
26933
27273
  }, new Error("Unsupported context: " + oBeta));
26934
27274
 
26935
27275
  return that.waitForChanges(assert, "collapse root");
@@ -26984,10 +27324,10 @@ sap.ui.define([
26984
27324
  oCollapsed.expand();
26985
27325
 
26986
27326
  return that.waitForChanges(assert, "expand initially collapsed node");
26987
- }).then(async function () {
27327
+ }).then(function () {
26988
27328
  const [, oBeta, oGamma] = oListBinding.getCurrentContexts();
26989
27329
  // code under test (JIRA: CPOUI5ODATAV4-2558)
26990
- const oZeta0 = await oGamma.requestSibling(); // Note: new context created here
27330
+ const oZeta0 = oGamma.getSibling(); // Note: new context created here
26991
27331
 
26992
27332
  // BEWARE: calls #getAllCurrentContexts!
26993
27333
  checkTable("initially collapsed node expanded", assert, oTable, [
@@ -27015,12 +27355,12 @@ sap.ui.define([
27015
27355
  const [,,, oZeta, oKappa] = oListBinding.getAllCurrentContexts();
27016
27356
  assert.strictEqual(oZeta0, oZeta);
27017
27357
  // code under test
27018
- assert.strictEqual(await oBeta.requestSibling(+1), oKappa, "CPOUI5ODATAV4-2558");
27019
- assert.strictEqual(await oKappa.requestSibling(-1), oBeta, "CPOUI5ODATAV4-2558");
27020
- assert.strictEqual(await oGamma.requestSibling(-1), null, "CPOUI5ODATAV4-2558");
27021
- assert.strictEqual(await oGamma.requestSibling(+1), oZeta, "CPOUI5ODATAV4-2558");
27022
- assert.strictEqual(await oZeta.requestSibling(-1), oGamma, "CPOUI5ODATAV4-2558");
27023
- assert.strictEqual(await oZeta.requestSibling(+1), null, "CPOUI5ODATAV4-2558");
27358
+ assert.strictEqual(oBeta.getSibling(+1), oKappa, "CPOUI5ODATAV4-2558");
27359
+ assert.strictEqual(oKappa.getSibling(-1), oBeta, "CPOUI5ODATAV4-2558");
27360
+ assert.strictEqual(oGamma.getSibling(-1), null, "CPOUI5ODATAV4-2558");
27361
+ assert.strictEqual(oGamma.getSibling(+1), oZeta, "CPOUI5ODATAV4-2558");
27362
+ assert.strictEqual(oZeta.getSibling(-1), oGamma, "CPOUI5ODATAV4-2558");
27363
+ assert.strictEqual(oZeta.getSibling(+1), null, "CPOUI5ODATAV4-2558");
27024
27364
 
27025
27365
  that.expectRequest(sTopLevelsSelectUrl + "&$skip=4&$top=2", {
27026
27366
  value : [{
@@ -27114,6 +27454,12 @@ sap.ui.define([
27114
27454
  oNewRoot = oListBinding.create({AGE : 99, Name : "Aleph"}, /*bSkipRefresh*/true);
27115
27455
 
27116
27456
  assert.strictEqual(oNewRoot.getIndex(), 0);
27457
+ assert.deepEqual(oNewRoot.getObject(), {
27458
+ "@$ui5.context.isTransient" : true,
27459
+ "@$ui5.node.level" : 1,
27460
+ AGE : 99,
27461
+ Name : "Aleph"
27462
+ }, "no LimitedRank");
27117
27463
 
27118
27464
  return Promise.all([
27119
27465
  oNewRoot.created(),
@@ -27121,7 +27467,7 @@ sap.ui.define([
27121
27467
  oListBinding.getHeaderContext().requestSideEffects(["AGE"]),
27122
27468
  that.waitForChanges(assert, "create new root, side effect: AGE for all rows")
27123
27469
  ]);
27124
- }).then(async function () {
27470
+ }).then(function () {
27125
27471
  checkTable("new root created, after side effect: AGE for all rows", assert, oTable, [
27126
27472
  "/EMPLOYEES('9')",
27127
27473
  "/EMPLOYEES('0')"
@@ -27133,9 +27479,9 @@ sap.ui.define([
27133
27479
 
27134
27480
  const [oAleph, oAlpha] = oListBinding.getCurrentContexts();
27135
27481
  // code under test
27136
- assert.strictEqual(await oAlpha.requestSibling(+1), oAleph,
27482
+ assert.strictEqual(oAlpha.getSibling(+1), oAleph,
27137
27483
  "CPOUI5ODATAV4-2558 ignoring out-of-place nodes");
27138
- assert.strictEqual(await oAleph.requestSibling(-1), oAlpha,
27484
+ assert.strictEqual(oAleph.getSibling(-1), oAlpha,
27139
27485
  "CPOUI5ODATAV4-2558 ignoring out-of-place nodes");
27140
27486
 
27141
27487
  that.expectRequest(sTopLevelsSelectUrl + "&$skip=1&$top=1", {
@@ -29284,8 +29630,8 @@ sap.ui.define([
29284
29630
  <Text id="name" text="{Name}"/>
29285
29631
  </Table>`;
29286
29632
 
29287
- function expectTable(sTitle, bExpanded) {
29288
- if (bExpanded) {
29633
+ function expectTable(sTitle, bExpanded0) {
29634
+ if (bExpanded0) {
29289
29635
  checkTable(sTitle, assert, oTable, [
29290
29636
  "/EMPLOYEES('0')",
29291
29637
  "/EMPLOYEES('1')",
@@ -30285,6 +30631,10 @@ sap.ui.define([
30285
30631
  //
30286
30632
  // Nodes affected by a move are "in place"; use "expand all" to avoid side-effects refresh
30287
30633
  // JIRA: CPOUI5ODATAV4-2466
30634
+ //
30635
+ // A selected (effectively kept alive) node which is not part of the hierarchy after the parent
30636
+ // is collapsed still holds its data
30637
+ // JIRA: CPOUI5ODATAV4-2539
30288
30638
  [false, true].forEach(function (bResetViaModel) {
30289
30639
  const sTitle = `Recursive Hierarchy: create new children, move 'em, model=${bResetViaModel}`;
30290
30640
  QUnit.test(sTitle, function (assert) {
@@ -30356,7 +30706,7 @@ sap.ui.define([
30356
30706
  }, new Error("Cannot move to " + sFriend));
30357
30707
  assert.throws(function () {
30358
30708
  // code under test
30359
- oListBinding.getHeaderContext().requestSibling();
30709
+ oListBinding.getHeaderContext().getSibling();
30360
30710
  }, new Error("Unsupported context: " + sFriend));
30361
30711
 
30362
30712
  checkTable("root is leaf", assert, oTable, [
@@ -30421,7 +30771,7 @@ sap.ui.define([
30421
30771
  }, new Error("Cannot move to " + oBeta), "too early");
30422
30772
  assert.throws(function () {
30423
30773
  // code under test
30424
- oBeta.requestSibling();
30774
+ oBeta.getSibling();
30425
30775
  }, new Error("Unsupported context: " + oBeta), "too early");
30426
30776
 
30427
30777
  return that.waitForChanges(assert, "create 1st child");
@@ -30761,7 +31111,6 @@ sap.ui.define([
30761
31111
  }
30762
31112
  });
30763
31113
  checkPersisted(assert, oGamma);
30764
- oGamma.setSelected(false); //TODO does not work together with collapse...
30765
31114
 
30766
31115
  assert.strictEqual(oBeta.getIndex(), 2);
30767
31116
  assert.deepEqual(oBeta.getObject(), {
@@ -30782,15 +31131,34 @@ sap.ui.define([
30782
31131
  return that.waitForChanges(assert, "collapse root");
30783
31132
  }).then(function () {
30784
31133
  checkTable("after collapse", assert, oTable, [
30785
- sFriend + "(ArtistID='0',IsActiveEntity=false)"
31134
+ sFriend + "(ArtistID='0',IsActiveEntity=false)",
31135
+ sFriend + "(ArtistID='2',IsActiveEntity=false)"
30786
31136
  ], [
30787
31137
  [undefined, false, 1, "etag0.1", "Alpha: Αα", "0,false"]
30788
31138
  ], 1);
30789
- // Note: this holds only for "created (persisted)"!
30790
31139
  assert.strictEqual(oBeta.getModel(), undefined, "destroyed by collapse");
30791
- assert.strictEqual(oGamma.getModel(), undefined, "destroyed by collapse");
31140
+ assert.deepEqual(oGamma.getObject(), {
31141
+ "@$ui5.context.isSelected" : true,
31142
+ "@$ui5.node.level" : 2,
31143
+ "@odata.etag" : "etag2.2",
31144
+ ArtistID : "2",
31145
+ IsActiveEntity : false,
31146
+ Name : "Gamma: Γγ",
31147
+ _ : {
31148
+ NodeID : "2,false"
31149
+ }
31150
+ }, "effectively kept alive");
31151
+
31152
+ assert.throws(function () {
31153
+ // code under test (JIRA: CPOUI5ODATAV4-2539)
31154
+ oGamma.requestParent();
31155
+ }, new Error("Not currently part of a recursive hierarchy: " + oGamma));
31156
+ assert.throws(function () {
31157
+ // code under test (JIRA: CPOUI5ODATAV4-2539)
31158
+ oGamma.requestSibling();
31159
+ }, new Error("Unsupported context: " + oGamma));
31160
+
30792
31161
  oBeta = null;
30793
- oGamma = null;
30794
31162
 
30795
31163
  that.expectRequest(sFriend.slice(1)
30796
31164
  + "?$filter=ArtistID eq '0' and IsActiveEntity eq false"
@@ -30861,13 +31229,13 @@ sap.ui.define([
30861
31229
  ]);
30862
31230
  const aCurrentContexts = oListBinding.getCurrentContexts();
30863
31231
  assert.strictEqual(oRoot, aCurrentContexts[0]);
30864
- //TODO assert.strictEqual(oGamma, aCurrentContexts[1]);
30865
- oGamma = aCurrentContexts[1];
31232
+ assert.strictEqual(oGamma, aCurrentContexts[1]);
31233
+
30866
31234
  oBeta = aCurrentContexts[2];
30867
31235
 
30868
31236
  assert.strictEqual(oGamma.getIndex(), 1);
30869
31237
  assert.deepEqual(oGamma.getObject(), {
30870
- //TODO "@$ui5.context.isSelected" : true,
31238
+ "@$ui5.context.isSelected" : true,
30871
31239
  "@$ui5.node.level" : 2,
30872
31240
  "@odata.etag" : "etag2.3",
30873
31241
  ArtistID : "2",
@@ -30957,7 +31325,7 @@ sap.ui.define([
30957
31325
 
30958
31326
  assert.strictEqual(oGamma.getIndex(), 1);
30959
31327
  assert.deepEqual(oGamma.getObject(), {
30960
- //TODO "@$ui5.context.isSelected" : true,
31328
+ "@$ui5.context.isSelected" : true,
30961
31329
  "@$ui5.node.isExpanded" : true,
30962
31330
  "@$ui5.node.level" : 2,
30963
31331
  "@odata.etag" : "etag2.3",
@@ -31200,6 +31568,10 @@ sap.ui.define([
31200
31568
  // At first, create a new root node with a LimitedRank beyond all currently loaded nodes.
31201
31569
  // Finally, it is also deleted.
31202
31570
  // JIRA: CPOUI5ODATAV4-2412
31571
+ //
31572
+ // A selected (effectively kept alive) node which is not part of the hierarchy is neither
31573
+ // collapsible nor expandable
31574
+ // JIRA: CPOUI5ODATAV4-2539
31203
31575
  ["OldChart", "OrgChart"].forEach((sHierarchyQualifier) => {
31204
31576
  const sTitle = `Recursive Hierarchy: expand all and create for ${sHierarchyQualifier}`;
31205
31577
  const sLimitedRank = sHierarchyQualifier === "OldChart" ? "LimitedRank" : "Limited_Rank";
@@ -31519,9 +31891,28 @@ sap.ui.define([
31519
31891
  ], 10);
31520
31892
  assert.deepEqual(oNewChild.getObject("_"), {NodeID : "9,false"});
31521
31893
 
31894
+ oZeta.setSelected(true);
31895
+ oAlpha.collapse();
31896
+
31897
+ assert.throws(function () {
31898
+ // code under test (JIRA: CPOUI5ODATAV4-2539)
31899
+ oZeta.expand();
31900
+ }, new Error("Not currently part of the hierarchy: " + oZeta));
31901
+
31902
+ oAlpha.expand();
31903
+ // reinsert oZeta into aContexts
31904
+ oAlpha.getBinding().getAllCurrentContexts();
31905
+
31522
31906
  // code under test
31523
31907
  oZeta.expand();
31908
+
31524
31909
  oAlpha.collapse();
31910
+
31911
+ assert.throws(function () {
31912
+ // code under test (JIRA: CPOUI5ODATAV4-2539)
31913
+ oZeta.collapse();
31914
+ }, new Error("Not currently part of the hierarchy: " + oZeta));
31915
+
31525
31916
  oAlpha.expand();
31526
31917
 
31527
31918
  // 0 Alpha
@@ -31631,7 +32022,7 @@ sap.ui.define([
31631
32022
  }, new Error("Cannot move to " + oNewRoot), "too late");
31632
32023
  assert.throws(function () {
31633
32024
  // code under test
31634
- oNewRoot.requestSibling();
32025
+ oNewRoot.getSibling();
31635
32026
  }, new Error("Unsupported context: " + oNewRoot), "too late");
31636
32027
 
31637
32028
  await Promise.all([
@@ -31657,6 +32048,415 @@ sap.ui.define([
31657
32048
  });
31658
32049
  });
31659
32050
 
32051
+ //*********************************************************************************************
32052
+ // Scenario: Create several new root and child nodes in a recursive hierarchy, some of which do
32053
+ // not match the current filter and thus do not have a limited rank. Still they are all shown in
32054
+ // their proper "out of place" position, respecting the order in which they were created. See
32055
+ // that #isAncestorOf and #collapse work if there are only nodes w/o rank, but also if there is
32056
+ // a mixture of nodes w/ and w/o rank.
32057
+ // JIRA: CPOUI5ODATAV4-2524
32058
+ QUnit.test("Recursive Hierarchy: create w/o rank", async function (assert) {
32059
+ const sBaseUrl = "EMPLOYEES?$apply=ancestors($root/EMPLOYEES,OrgChart,ID"
32060
+ + ",filter(not startswith(Name, 'Out')),keep start)"
32061
+ + "/com.sap.vocabularies.Hierarchy.v1.TopLevels(HierarchyNodes=$root/EMPLOYEES"
32062
+ + ",HierarchyQualifier='OrgChart',NodeProperty='ID')";
32063
+ const oModel = this.createTeaBusiModel({autoExpandSelect : true});
32064
+ const sView = `
32065
+ <t:Table id="table" rows="{path : '/EMPLOYEES',
32066
+ parameters : {
32067
+ $$aggregation : {
32068
+ expandTo : 1E16,
32069
+ hierarchyQualifier : 'OrgChart'
32070
+ },
32071
+ $filter : 'not startswith(Name, \\'Out\\')'
32072
+ }}" threshold="0" visibleRowCount="4">
32073
+ <Text text="{= %{@$ui5.node.isExpanded} }"/>
32074
+ <Text text="{= %{@$ui5.node.level} }"/>
32075
+ <Text text="{Name}"/>
32076
+ </t:Table>`;
32077
+
32078
+ // Server: UI:
32079
+ // 0 Alpha Out2 Out2 (created, no rank)
32080
+ // 1 Beta In1 In1 (created)
32081
+ // 1.1 Gamma Out1 Out1 (created , no rank)
32082
+ // In2 In2 0 Alpha
32083
+ // In3 In3 1 Beta
32084
+ // 2 Delta In3 In3 (created)
32085
+ // In1 In1 Out4 Out4 (created, no rank)
32086
+ // 3 Epsilon In2 In2 (created)
32087
+ // 4 Zeta Out3 Out3 (created, no rank)
32088
+ // 5 Eta 1.1 Gamma
32089
+ // Out5 Out5 (created, no rank)
32090
+ // 2 Delta
32091
+ // 3 Epsilon
32092
+ // 4 Zeta
32093
+ // 5 Eta
32094
+ this.expectRequest(sBaseUrl + "&$select=DescendantCount,DistanceFromRoot,DrillState,ID,Name"
32095
+ + "&$count=true&$skip=0&$top=4", {
32096
+ "@odata.count" : "7",
32097
+ value : [{
32098
+ DescendantCount : "3",
32099
+ DistanceFromRoot : "0",
32100
+ DrillState : "expanded",
32101
+ ID : "0",
32102
+ Name : "Alpha"
32103
+ }, {
32104
+ DescendantCount : "1",
32105
+ DistanceFromRoot : "1",
32106
+ DrillState : "expanded",
32107
+ ID : "1",
32108
+ Name : "Beta"
32109
+ }, {
32110
+ DescendantCount : "0",
32111
+ DistanceFromRoot : "2",
32112
+ DrillState : "leaf",
32113
+ ID : "1.1",
32114
+ Name : "Gamma"
32115
+ }, {
32116
+ DescendantCount : "0",
32117
+ DistanceFromRoot : "1",
32118
+ DrillState : "leaf",
32119
+ ID : "2",
32120
+ Name : "Delta"
32121
+ }]
32122
+ });
32123
+
32124
+ await this.createView(assert, sView, oModel);
32125
+
32126
+ const oTable = this.oView.byId("table");
32127
+ checkTable("initial page", assert, oTable, [
32128
+ "/EMPLOYEES('0')",
32129
+ "/EMPLOYEES('1')",
32130
+ "/EMPLOYEES('1.1')",
32131
+ "/EMPLOYEES('2')"
32132
+ ], [
32133
+ [true, 1, "Alpha"],
32134
+ [true, 2, "Beta"],
32135
+ [undefined, 3, "Gamma"],
32136
+ [undefined, 2, "Delta"]
32137
+ ], 7);
32138
+ const oListBinding = oTable.getBinding("rows");
32139
+ const [oAlpha, oBeta, oGamma] = oListBinding.getCurrentContexts();
32140
+
32141
+ const create = async (oParent, sName, iRank) => {
32142
+ const oPayload = {Name : sName};
32143
+ if (oParent) {
32144
+ oPayload["EMPLOYEE_2_MANAGER@odata.bind"] = oParent.sPath.slice(1);
32145
+ }
32146
+ this.expectRequest({
32147
+ method : "POST",
32148
+ url : "EMPLOYEES",
32149
+ payload : oPayload
32150
+ }, {
32151
+ ID : sName,
32152
+ Name : sName
32153
+ })
32154
+ .expectRequest(sBaseUrl + `&$filter=ID eq '${sName}'&$select=LimitedRank`, {
32155
+ value : iRank === undefined ? [] : [{LimitedRank : `${iRank}`}]
32156
+ });
32157
+
32158
+ // code under test
32159
+ const oChild = oListBinding.create({
32160
+ "@$ui5.node.parent" : oParent,
32161
+ Name : sName
32162
+ }, /*bSkipRefresh*/true);
32163
+
32164
+ await Promise.all([
32165
+ oChild.created(),
32166
+ this.waitForChanges(assert, `create ${sName}`)
32167
+ ]);
32168
+
32169
+ return oChild;
32170
+ };
32171
+
32172
+ await create(null, "Out1");
32173
+
32174
+ checkTable("after create Out1", assert, oTable, [
32175
+ "/EMPLOYEES('Out1')",
32176
+ "/EMPLOYEES('0')",
32177
+ "/EMPLOYEES('1')",
32178
+ "/EMPLOYEES('1.1')",
32179
+ "/EMPLOYEES('2')"
32180
+ ], [
32181
+ [undefined, 1, "Out1"],
32182
+ [true, 1, "Alpha"],
32183
+ [true, 2, "Beta"],
32184
+ [undefined, 3, "Gamma"]
32185
+ ], 8);
32186
+
32187
+ await create(null, "In1", 4);
32188
+
32189
+ checkTable("after create In1", assert, oTable, [
32190
+ "/EMPLOYEES('In1')",
32191
+ "/EMPLOYEES('Out1')",
32192
+ "/EMPLOYEES('0')",
32193
+ "/EMPLOYEES('1')",
32194
+ "/EMPLOYEES('1.1')",
32195
+ "/EMPLOYEES('2')"
32196
+ ], [
32197
+ [undefined, 1, "In1"],
32198
+ [undefined, 1, "Out1"],
32199
+ [true, 1, "Alpha"],
32200
+ [true, 2, "Beta"]
32201
+ ], 9);
32202
+
32203
+ await create(null, "Out2");
32204
+
32205
+ checkTable("after create Out2", assert, oTable, [
32206
+ "/EMPLOYEES('Out2')",
32207
+ "/EMPLOYEES('In1')",
32208
+ "/EMPLOYEES('Out1')",
32209
+ "/EMPLOYEES('0')",
32210
+ "/EMPLOYEES('1')",
32211
+ "/EMPLOYEES('1.1')",
32212
+ "/EMPLOYEES('2')"
32213
+ ], [
32214
+ [undefined, 1, "Out2"],
32215
+ [undefined, 1, "In1"],
32216
+ [undefined, 1, "Out1"],
32217
+ [true, 1, "Alpha"]
32218
+ ], 10);
32219
+
32220
+ oTable.setFirstVisibleRow(3);
32221
+
32222
+ await resolveLater(undefined, 0); // table update takes a moment
32223
+
32224
+ checkTable("first visible row 3", assert, oTable, [
32225
+ "/EMPLOYEES('Out2')",
32226
+ "/EMPLOYEES('In1')",
32227
+ "/EMPLOYEES('Out1')",
32228
+ "/EMPLOYEES('0')",
32229
+ "/EMPLOYEES('1')",
32230
+ "/EMPLOYEES('1.1')",
32231
+ "/EMPLOYEES('2')"
32232
+ ], [
32233
+ [true, 1, "Alpha"],
32234
+ [true, 2, "Beta"],
32235
+ [undefined, 3, "Gamma"],
32236
+ [undefined, 2, "Delta"]
32237
+ ], 10);
32238
+
32239
+ const oOut5 = await create(oGamma, "Out5");
32240
+
32241
+ assert.ok(oGamma.isAncestorOf(oOut5));
32242
+
32243
+ checkTable("after create Out5", assert, oTable, [
32244
+ "/EMPLOYEES('Out2')",
32245
+ "/EMPLOYEES('In1')",
32246
+ "/EMPLOYEES('Out1')",
32247
+ "/EMPLOYEES('0')",
32248
+ "/EMPLOYEES('1')",
32249
+ "/EMPLOYEES('1.1')",
32250
+ "/EMPLOYEES('Out5')",
32251
+ "/EMPLOYEES('2')"
32252
+ ], [
32253
+ [true, 1, "Alpha"],
32254
+ [true, 2, "Beta"],
32255
+ [true, 3, "Gamma"],
32256
+ [undefined, 4, "Out5"]
32257
+ ], 11);
32258
+
32259
+ // code under test
32260
+ oGamma.collapse();
32261
+
32262
+ await this.waitForChanges(assert, "collapse Gamma");
32263
+
32264
+ checkTable("after collapse Gamma", assert, oTable, [
32265
+ "/EMPLOYEES('Out2')",
32266
+ "/EMPLOYEES('In1')",
32267
+ "/EMPLOYEES('Out1')",
32268
+ "/EMPLOYEES('0')",
32269
+ "/EMPLOYEES('1')",
32270
+ "/EMPLOYEES('1.1')",
32271
+ "/EMPLOYEES('2')"
32272
+ ], [
32273
+ [true, 1, "Alpha"],
32274
+ [true, 2, "Beta"],
32275
+ [false, 3, "Gamma"],
32276
+ [undefined, 2, "Delta"]
32277
+ ], 10);
32278
+
32279
+ // code under test
32280
+ oGamma.expand();
32281
+
32282
+ await this.waitForChanges(assert, "expand Gamma");
32283
+
32284
+ checkTable("after expand Gamma", assert, oTable, [
32285
+ "/EMPLOYEES('Out2')",
32286
+ "/EMPLOYEES('In1')",
32287
+ "/EMPLOYEES('Out1')",
32288
+ "/EMPLOYEES('0')",
32289
+ "/EMPLOYEES('1')",
32290
+ "/EMPLOYEES('1.1')",
32291
+ "/EMPLOYEES('Out5')",
32292
+ "/EMPLOYEES('2')"
32293
+ ], [
32294
+ [true, 1, "Alpha"],
32295
+ [true, 2, "Beta"],
32296
+ [true, 3, "Gamma"],
32297
+ [undefined, 4, "Out5"]
32298
+ ], 11);
32299
+
32300
+ const oOut3 = await create(oBeta, "Out3");
32301
+
32302
+ assert.ok(oAlpha.isAncestorOf(oOut3));
32303
+ assert.ok(oBeta.isAncestorOf(oOut3));
32304
+
32305
+ checkTable("after create Out3", assert, oTable, [
32306
+ "/EMPLOYEES('Out2')",
32307
+ "/EMPLOYEES('In1')",
32308
+ "/EMPLOYEES('Out1')",
32309
+ "/EMPLOYEES('0')",
32310
+ "/EMPLOYEES('1')",
32311
+ "/EMPLOYEES('Out3')",
32312
+ "/EMPLOYEES('1.1')",
32313
+ "/EMPLOYEES('Out5')",
32314
+ "/EMPLOYEES('2')"
32315
+ ], [
32316
+ [true, 1, "Alpha"],
32317
+ [true, 2, "Beta"],
32318
+ [undefined, 3, "Out3"],
32319
+ [true, 3, "Gamma"]
32320
+ ], 12);
32321
+
32322
+ await create(oBeta, "In2", 3);
32323
+
32324
+ checkTable("after create In2", assert, oTable, [
32325
+ "/EMPLOYEES('Out2')",
32326
+ "/EMPLOYEES('In1')",
32327
+ "/EMPLOYEES('Out1')",
32328
+ "/EMPLOYEES('0')",
32329
+ "/EMPLOYEES('1')",
32330
+ "/EMPLOYEES('In2')",
32331
+ "/EMPLOYEES('Out3')",
32332
+ "/EMPLOYEES('1.1')",
32333
+ "/EMPLOYEES('Out5')",
32334
+ "/EMPLOYEES('2')"
32335
+ ], [
32336
+ [true, 1, "Alpha"],
32337
+ [true, 2, "Beta"],
32338
+ [undefined, 3, "In2"],
32339
+ [undefined, 3, "Out3"]
32340
+ ], 13);
32341
+
32342
+ const oOut4 = await create(oBeta, "Out4");
32343
+
32344
+ assert.ok(oAlpha.isAncestorOf(oOut4));
32345
+ assert.ok(oBeta.isAncestorOf(oOut4));
32346
+
32347
+ checkTable("after create Out4", assert, oTable, [
32348
+ "/EMPLOYEES('Out2')",
32349
+ "/EMPLOYEES('In1')",
32350
+ "/EMPLOYEES('Out1')",
32351
+ "/EMPLOYEES('0')",
32352
+ "/EMPLOYEES('1')",
32353
+ "/EMPLOYEES('Out4')",
32354
+ "/EMPLOYEES('In2')",
32355
+ "/EMPLOYEES('Out3')",
32356
+ "/EMPLOYEES('1.1')",
32357
+ "/EMPLOYEES('Out5')",
32358
+ "/EMPLOYEES('2')"
32359
+ ], [
32360
+ [true, 1, "Alpha"],
32361
+ [true, 2, "Beta"],
32362
+ [undefined, 3, "Out4"],
32363
+ [undefined, 3, "In2"]
32364
+ ], 14);
32365
+
32366
+ await create(oBeta, "In3", 4);
32367
+
32368
+ checkTable("after create In3", assert, oTable, [
32369
+ "/EMPLOYEES('Out2')",
32370
+ "/EMPLOYEES('In1')",
32371
+ "/EMPLOYEES('Out1')",
32372
+ "/EMPLOYEES('0')",
32373
+ "/EMPLOYEES('1')",
32374
+ "/EMPLOYEES('In3')",
32375
+ "/EMPLOYEES('Out4')",
32376
+ "/EMPLOYEES('In2')",
32377
+ "/EMPLOYEES('Out3')",
32378
+ "/EMPLOYEES('1.1')",
32379
+ "/EMPLOYEES('Out5')",
32380
+ "/EMPLOYEES('2')"
32381
+ ], [
32382
+ [true, 1, "Alpha"],
32383
+ [true, 2, "Beta"],
32384
+ [undefined, 3, "In3"],
32385
+ [undefined, 3, "Out4"]
32386
+ ], 15);
32387
+
32388
+ this.expectRequest(sBaseUrl + "&$select=DescendantCount,DistanceFromRoot,DrillState,ID,Name"
32389
+ + "&$skip=7&$top=3", {
32390
+ value : [{
32391
+ DescendantCount : "0",
32392
+ DistanceFromRoot : "0",
32393
+ DrillState : "leaf",
32394
+ ID : "3",
32395
+ Name : "Epsilon"
32396
+ }, {
32397
+ DescendantCount : "0",
32398
+ DistanceFromRoot : "0",
32399
+ DrillState : "leaf",
32400
+ ID : "4",
32401
+ Name : "Zeta"
32402
+ }, {
32403
+ DescendantCount : "0",
32404
+ DistanceFromRoot : "0",
32405
+ DrillState : "leaf",
32406
+ ID : "5",
32407
+ Name : "Eta"
32408
+ }]
32409
+ });
32410
+
32411
+ // code under test
32412
+ oAlpha.collapse();
32413
+
32414
+ await this.waitForChanges(assert, "collapse Alpha");
32415
+
32416
+ checkTable("after collapse Alpha", assert, oTable, [
32417
+ "/EMPLOYEES('Out2')",
32418
+ "/EMPLOYEES('In1')",
32419
+ "/EMPLOYEES('Out1')",
32420
+ "/EMPLOYEES('0')",
32421
+ "/EMPLOYEES('3')",
32422
+ "/EMPLOYEES('4')",
32423
+ "/EMPLOYEES('5')"
32424
+ ], [
32425
+ [false, 1, "Alpha"],
32426
+ [undefined, 1, "Epsilon"],
32427
+ [undefined, 1, "Zeta"],
32428
+ [undefined, 1, "Eta"]
32429
+ ]);
32430
+
32431
+ // code under test
32432
+ oAlpha.expand();
32433
+
32434
+ await this.waitForChanges(assert, "expand Alpha");
32435
+
32436
+ checkTable("after expand Alpha", assert, oTable, [
32437
+ "/EMPLOYEES('Out2')",
32438
+ "/EMPLOYEES('In1')",
32439
+ "/EMPLOYEES('Out1')",
32440
+ "/EMPLOYEES('0')",
32441
+ "/EMPLOYEES('1')",
32442
+ "/EMPLOYEES('In3')",
32443
+ "/EMPLOYEES('Out4')",
32444
+ "/EMPLOYEES('In2')",
32445
+ "/EMPLOYEES('Out3')",
32446
+ "/EMPLOYEES('1.1')",
32447
+ "/EMPLOYEES('Out5')",
32448
+ "/EMPLOYEES('2')",
32449
+ "/EMPLOYEES('3')",
32450
+ "/EMPLOYEES('4')",
32451
+ "/EMPLOYEES('5')"
32452
+ ], [
32453
+ [true, 1, "Alpha"],
32454
+ [true, 2, "Beta"],
32455
+ [undefined, 3, "In3"],
32456
+ [undefined, 3, "Out4"]
32457
+ ]);
32458
+ });
32459
+
31660
32460
  //*********************************************************************************************
31661
32461
  // Scenario: Expand all levels of a recursive hierarchy with two roots ("Alpha", "Omega").
31662
32462
  // Collapse "Beta". Move "Alpha" so that "Omega" becomes its parent, either as an expanded node
@@ -33088,6 +33888,12 @@ sap.ui.define([
33088
33888
  [undefined, undefined, 4, "3.1.1", "Zeta"], // ...and not created anymore
33089
33889
  [undefined, undefined, 2, "2", "Gamma"]
33090
33890
  ]);
33891
+ assert.deepEqual(oAlpha.getObject(), {
33892
+ "@$ui5.node.isExpanded" : true,
33893
+ "@$ui5.node.level" : 1,
33894
+ ID : "0",
33895
+ Name : "Alpha"
33896
+ }, "no LimitedRank");
33091
33897
  break;
33092
33898
 
33093
33899
  case 4: // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
@@ -37812,28 +38618,41 @@ make root = ${bMakeRoot}`;
37812
38618
  // of the hierarchy as long as they are transient.
37813
38619
  // Create a node, but because of a filter this node doesn't become part of the hierarchy and is
37814
38620
  // therefore not displayed.
38621
+ // Create a node and immediately cancel it, nothing happens.
37815
38622
  // Create a child below an expanded parent on a level within the given expandTo range, it is
37816
38623
  // inserted "in place" at the last sibling position.
37817
38624
  // Create a child below a leaf, also on a level within the given expandTo range.
37818
38625
  // JIRA: CPOUI5ODATAV4-2560
38626
+ //
38627
+ // If a created node is filtered out, check that its parent doesn't become expanded, neither
38628
+ // during the pending creation nor after the creation is completed.
38629
+ // JIRA: CPOUI5ODATAV4-2623
38630
+ //
38631
+ // Create a child below a leaf, but as the child's level is greater than expandTo, the parent
38632
+ // needs to be expanded by enhancing the ExpandLevels. The created child directly becomes
38633
+ // visible in the current viewport. Create another child below a leaf (again enhancing
38634
+ // ExpandLevels), but this child does not become visible. Scroll to it after successful
38635
+ // creation. In both cases the created context is not reused as active context in the list
38636
+ // binding, but still knows the actual index to find the position of the new node.
38637
+ // JIRA: CPOUI5ODATAV4-2586
37819
38638
  QUnit.test("Recursive Hierarchy: createInPlace", async function (assert) {
37820
38639
  const oModel = this.createTeaBusiModel({autoExpandSelect : true});
37821
38640
  const sSelect = "&$select=DescendantCount,DistanceFromRoot,DrillState,ID,Name";
37822
38641
  const sUrl = "EMPLOYEES"
37823
38642
  + "?$apply=ancestors($root/EMPLOYEES,OrgChart,ID,filter(Is_Manager),keep start)"
37824
38643
  + "/com.sap.vocabularies.Hierarchy.v1.TopLevels(HierarchyNodes=$root/EMPLOYEES"
37825
- + ",HierarchyQualifier='OrgChart',NodeProperty='ID',Levels=2)";
38644
+ + ",HierarchyQualifier='OrgChart',NodeProperty='ID',Levels=3)";
37826
38645
  const sView = `
37827
38646
  <t:Table id="table" rows="{path : '/EMPLOYEES',
37828
38647
  parameters : {
37829
38648
  $$aggregation : {
37830
38649
  createInPlace : true,
37831
- expandTo : 2,
38650
+ expandTo : 3,
37832
38651
  hierarchyQualifier : 'OrgChart'
37833
38652
  },
37834
38653
  $count : true,
37835
38654
  $filter : 'Is_Manager'
37836
- }}" threshold="0" visibleRowCount="1">
38655
+ }}" threshold="0" visibleRowCount="2">
37837
38656
  <Text text="{= %{@$ui5.node.isExpanded} }"/>
37838
38657
  <Text text="{= %{@$ui5.node.level} }"/>
37839
38658
  <Text text="{ID}"/>
@@ -37843,11 +38662,13 @@ make root = ${bMakeRoot}`;
37843
38662
  // 1 Alpha
37844
38663
  // 3 Gamma
37845
38664
  // 4 Delta (created)
38665
+ // 5 Epsilon (created)
38666
+ // 6 Zeta (created)
38667
+ // 7 Eta (created)
37846
38668
  // 2 Beta
37847
- // 5 Epsilon (created)
37848
38669
  // (42 FilteredOut (created, but filtered out))
37849
38670
  this.expectRequest("EMPLOYEES/$count?$filter=Is_Manager", 3)
37850
- .expectRequest(sUrl + sSelect + "&$count=true&$skip=0&$top=1", {
38671
+ .expectRequest(sUrl + sSelect + "&$count=true&$skip=0&$top=2", {
37851
38672
  "@odata.count" : "3",
37852
38673
  value : [{
37853
38674
  DescendantCount : "1",
@@ -37855,6 +38676,12 @@ make root = ${bMakeRoot}`;
37855
38676
  DrillState : "expanded",
37856
38677
  ID : "1",
37857
38678
  Name : "Alpha"
38679
+ }, {
38680
+ DescendantCount : "0",
38681
+ DistanceFromRoot : "1",
38682
+ DrillState : "leaf",
38683
+ ID : "3",
38684
+ Name : "Gamma"
37858
38685
  }]
37859
38686
  });
37860
38687
 
@@ -37862,16 +38689,20 @@ make root = ${bMakeRoot}`;
37862
38689
 
37863
38690
  const oTable = this.oView.byId("table");
37864
38691
  checkTable("initial page", assert, oTable, [
37865
- "/EMPLOYEES('1')"
38692
+ "/EMPLOYEES('1')",
38693
+ "/EMPLOYEES('3')"
37866
38694
  ], [
37867
- [true, 1, "1", "Alpha"]
38695
+ [true, 1, "1", "Alpha"],
38696
+ [undefined, 2, "3", "Gamma"]
37868
38697
  ], 3);
37869
38698
  const oListBinding = oTable.getBinding("rows");
38699
+ const [oAlpha, oGamma] = oListBinding.getAllCurrentContexts();
37870
38700
 
37871
38701
  this.expectRequest({
37872
38702
  method : "POST",
37873
38703
  url : "EMPLOYEES",
37874
38704
  payload : {
38705
+ "EMPLOYEE_2_MANAGER@odata.bind" : "EMPLOYEES('3')",
37875
38706
  Name : "FilteredOut"
37876
38707
  }
37877
38708
  }, {
@@ -37884,22 +38715,23 @@ make root = ${bMakeRoot}`;
37884
38715
 
37885
38716
  // code under test
37886
38717
  const oFilteredOut = oListBinding.create({
38718
+ "@$ui5.node.parent" : oGamma,
37887
38719
  Name : "FilteredOut"
37888
38720
  }, /*bSkipRefresh*/true);
37889
38721
 
37890
38722
  assert.strictEqual(oFilteredOut.getIndex(), undefined);
37891
38723
  assert.strictEqual(oFilteredOut.isTransient(), true);
38724
+ assert.strictEqual(oFilteredOut.isExpanded(), undefined, "CPOUI5ODATAV4-2623");
37892
38725
  checkTable("while create FilteredOut is pending", assert, oTable, [
37893
- "/EMPLOYEES('1')"
38726
+ oAlpha,
38727
+ oGamma
37894
38728
  ], [
37895
- [true, 1, "1", "Alpha"]
38729
+ [true, 1, "1", "Alpha"],
38730
+ [undefined, 2, "3", "Gamma"]
37896
38731
  ], 3);
37897
38732
  assert.strictEqual(oListBinding.getCount(), 3);
37898
38733
 
37899
- await Promise.all([
37900
- oFilteredOut.created(),
37901
- this.waitForChanges(assert, "create FilteredOut")
37902
- ]);
38734
+ await oFilteredOut.created();
37903
38735
 
37904
38736
  assert.strictEqual(oFilteredOut.getIndex(), undefined, "not part of the hierarchy");
37905
38737
  assert.strictEqual(oFilteredOut.isTransient(), undefined);
@@ -37908,21 +38740,20 @@ make root = ${bMakeRoot}`;
37908
38740
  assert.throws(function () {
37909
38741
  oFilteredOut.setKeepAlive(true);
37910
38742
  }, "already destroyed");
38743
+
38744
+ await this.waitForChanges(assert, "create FilteredOut");
38745
+
37911
38746
  checkTable("after create FilteredOut", assert, oTable, [
37912
- "/EMPLOYEES('1')"
38747
+ oAlpha,
38748
+ oGamma
37913
38749
  ], [
37914
- [true, 1, "1", "Alpha"]
38750
+ [true, 1, "1", "Alpha"],
38751
+ [undefined, 2, "3", "Gamma"]
37915
38752
  ], 3);
37916
38753
  assert.strictEqual(oListBinding.getCount(), 3);
37917
38754
 
37918
- this.expectRequest(sUrl + sSelect + "&$skip=1&$top=2", {
38755
+ this.expectRequest(sUrl + sSelect + "&$skip=2&$top=1", {
37919
38756
  value : [{
37920
- DescendantCount : "0",
37921
- DistanceFromRoot : "1",
37922
- DrillState : "leaf",
37923
- ID : "3",
37924
- Name : "Gamma"
37925
- }, {
37926
38757
  DescendantCount : "0",
37927
38758
  DistanceFromRoot : "0",
37928
38759
  DrillState : "leaf",
@@ -37937,7 +38768,30 @@ make root = ${bMakeRoot}`;
37937
38768
  [undefined, 2, "3", "Gamma"],
37938
38769
  [undefined, 1, "2", "Beta"]
37939
38770
  ]);
37940
- const oAlpha = oListBinding.getAllCurrentContexts()[0];
38771
+
38772
+ // code under test
38773
+ const oLostChild = oListBinding.create({
38774
+ "@$ui5.node.parent" : oAlpha,
38775
+ Name : "n/a"
38776
+ }, /*bSkipRefresh*/true);
38777
+
38778
+ // code under test
38779
+ oModel.resetChanges();
38780
+
38781
+ await Promise.all([
38782
+ checkCanceled(assert, oLostChild.created()),
38783
+ this.waitForChanges(assert, "cancel creation")
38784
+ ]);
38785
+
38786
+ checkTable("after cancel creation", assert, oTable, [
38787
+ oAlpha,
38788
+ oGamma,
38789
+ "/EMPLOYEES('2')"
38790
+ ], [
38791
+ [true, 1, "1", "Alpha"],
38792
+ [undefined, 2, "3", "Gamma"]
38793
+ ]);
38794
+ assert.strictEqual(oListBinding.getCount(), 3);
37941
38795
 
37942
38796
  this.expectRequest({
37943
38797
  method : "POST",
@@ -37964,22 +38818,22 @@ make root = ${bMakeRoot}`;
37964
38818
  assert.strictEqual(oDelta.getIndex(), undefined);
37965
38819
  assert.strictEqual(oDelta.isTransient(), true);
37966
38820
  checkTable("while create Delta is pending", assert, oTable, [
37967
- "/EMPLOYEES('1')",
37968
- "/EMPLOYEES('3')",
38821
+ oAlpha,
38822
+ oGamma,
37969
38823
  "/EMPLOYEES('2')"
37970
38824
  ], [
37971
- [true, 1, "1", "Alpha"]
38825
+ [true, 1, "1", "Alpha"],
38826
+ [undefined, 2, "3", "Gamma"]
37972
38827
  ]);
37973
38828
  assert.strictEqual(oListBinding.getCount(), 3);
37974
38829
 
37975
- await Promise.all([
37976
- oDeltaCreated,
37977
- this.waitForChanges(assert, "create Delta")
37978
- ]);
38830
+ await oDeltaCreated;
37979
38831
 
37980
38832
  assert.strictEqual(oDelta.getIndex(), 2);
37981
- assert.strictEqual(oDelta.isTransient(), false);
37982
38833
  checkCreatedPersisted(assert, oDelta, oDeltaCreated);
38834
+
38835
+ await this.waitForChanges(assert, "create Delta");
38836
+
37983
38837
  await this.checkAllContexts("after create Delta", assert, oListBinding,
37984
38838
  ["@$ui5.node.isExpanded", "@$ui5.node.level", "ID", "Name"], [
37985
38839
  [true, 1, "1", "Alpha"],
@@ -37988,13 +38842,12 @@ make root = ${bMakeRoot}`;
37988
38842
  [undefined, 1, "2", "Beta"]
37989
38843
  ]);
37990
38844
  assert.strictEqual(oListBinding.getCount(), 3); // TODO: update $count; CPOUI5ODATAV4-2245
37991
- const oBeta = oListBinding.getAllCurrentContexts()[3];
37992
38845
 
37993
38846
  this.expectRequest({
37994
38847
  method : "POST",
37995
38848
  url : "EMPLOYEES",
37996
38849
  payload : {
37997
- "EMPLOYEE_2_MANAGER@odata.bind" : "EMPLOYEES('2')",
38850
+ "EMPLOYEE_2_MANAGER@odata.bind" : "EMPLOYEES('4')",
37998
38851
  Name : "Epsilon"
37999
38852
  }
38000
38853
  }, {
@@ -38002,12 +38855,12 @@ make root = ${bMakeRoot}`;
38002
38855
  Name : "Epsilon"
38003
38856
  })
38004
38857
  .expectRequest(sUrl + "&$filter=ID eq '5'&$select=LimitedRank", {
38005
- value : [{LimitedRank : "4"}]
38858
+ value : [{LimitedRank : "3"}]
38006
38859
  });
38007
38860
 
38008
38861
  // code under test
38009
38862
  const oEpsilon = oListBinding.create({
38010
- "@$ui5.node.parent" : oBeta,
38863
+ "@$ui5.node.parent" : oDelta,
38011
38864
  Name : "Epsilon"
38012
38865
  }, /*bSkipRefresh*/true);
38013
38866
  const oEpsilonCreated = oEpsilon.created();
@@ -38015,38 +38868,220 @@ make root = ${bMakeRoot}`;
38015
38868
  assert.strictEqual(oEpsilon.getIndex(), undefined);
38016
38869
  assert.strictEqual(oEpsilon.isTransient(), true);
38017
38870
  checkTable("while create Epsilon is pending", assert, oTable, [
38018
- "/EMPLOYEES('1')",
38019
- "/EMPLOYEES('3')",
38020
- "/EMPLOYEES('4')",
38871
+ oAlpha,
38872
+ oGamma,
38873
+ oDelta,
38021
38874
  "/EMPLOYEES('2')"
38022
38875
  ], [
38023
- [true, 1, "1", "Alpha"]
38876
+ [true, 1, "1", "Alpha"],
38877
+ [undefined, 2, "3", "Gamma"]
38024
38878
  ]);
38025
38879
  assert.strictEqual(oListBinding.getCount(), 3);
38026
38880
 
38027
- await Promise.all([
38028
- oEpsilonCreated,
38029
- this.waitForChanges(assert, "create Epsilon")
38030
- ]);
38881
+ await oEpsilonCreated;
38031
38882
 
38032
- assert.strictEqual(oEpsilon.getIndex(), 4);
38033
- assert.strictEqual(oEpsilon.isTransient(), false);
38883
+ assert.strictEqual(oEpsilon.getIndex(), 3);
38034
38884
  checkCreatedPersisted(assert, oEpsilon, oEpsilonCreated);
38885
+
38886
+ await this.waitForChanges(assert, "create Epsilon");
38887
+
38035
38888
  await this.checkAllContexts("after create Epsilon", assert, oListBinding,
38036
38889
  ["@$ui5.node.isExpanded", "@$ui5.node.level", "ID", "Name"], [
38037
38890
  [true, 1, "1", "Alpha"],
38038
38891
  [undefined, 2, "3", "Gamma"],
38039
- [undefined, 2, "4", "Delta"],
38040
- [true, 1, "2", "Beta"],
38041
- [undefined, 2, "5", "Epsilon"]
38892
+ [true, 2, "4", "Delta"],
38893
+ [undefined, 3, "5", "Epsilon"],
38894
+ [undefined, 1, "2", "Beta"]
38042
38895
  ]);
38043
38896
  assert.strictEqual(oListBinding.getCount(), 3); // TODO: update $count; CPOUI5ODATAV4-2245
38897
+
38898
+ oTable.setFirstVisibleRow(oEpsilon.getIndex()); // scroll to Epsilon
38899
+ await resolveLater(); // table update takes a moment
38900
+
38901
+ checkTable("after scroll to Epsilon", assert, oTable, [
38902
+ oAlpha,
38903
+ oGamma,
38904
+ oDelta,
38905
+ oEpsilon,
38906
+ "/EMPLOYEES('2')"
38907
+ ], [
38908
+ [undefined, 3, "5", "Epsilon"],
38909
+ [undefined, 1, "2", "Beta"]
38910
+ ]);
38911
+
38912
+ this.expectRequest({
38913
+ batchNo : 9,
38914
+ method : "POST",
38915
+ url : "EMPLOYEES",
38916
+ payload : {
38917
+ "EMPLOYEE_2_MANAGER@odata.bind" : "EMPLOYEES('5')",
38918
+ Name : "Zeta"
38919
+ }
38920
+ }, {
38921
+ ID : "6",
38922
+ Name : "Zeta"
38923
+ })
38924
+ .expectRequest({
38925
+ batchNo : 9,
38926
+ url : "EMPLOYEES/$count?$filter=Is_Manager"
38927
+ }, 6)
38928
+ .expectRequest({
38929
+ batchNo : 9,
38930
+ url : sUrl.slice(0, -1)
38931
+ + ",ExpandLevels=" + JSON.stringify([{NodeID : "5", Levels : 1}]) + ")"
38932
+ + sSelect + "&$count=true&$skip=3&$top=2"
38933
+ }, {
38934
+ "@odata.count" : "6",
38935
+ value : [{
38936
+ DescendantCount : "1",
38937
+ DistanceFromRoot : "2",
38938
+ DrillState : "expanded",
38939
+ ID : "5",
38940
+ Name : "Epsilon"
38941
+ }, {
38942
+ DescendantCount : "0",
38943
+ DistanceFromRoot : "3",
38944
+ DrillState : "leaf",
38945
+ ID : "6",
38946
+ Name : "Zeta"
38947
+ }]
38948
+ })
38949
+ .expectRequest({
38950
+ batchNo : 10,
38951
+ url : sUrl.slice(0, -1)
38952
+ + ",ExpandLevels=" + JSON.stringify([{NodeID : "5", Levels : 1}]) + ")"
38953
+ + "&$filter=ID eq '6'&$select=LimitedRank"
38954
+ }, {
38955
+ value : [{LimitedRank : "4"}]
38956
+ });
38957
+
38958
+ // code under test
38959
+ let oZeta = oListBinding.create({
38960
+ "@$ui5.node.parent" : oEpsilon,
38961
+ Name : "Zeta"
38962
+ }, /*bSkipRefresh*/true);
38963
+
38964
+ await oZeta.created();
38965
+
38966
+ assert.strictEqual(oZeta.getIndex(), 4);
38967
+ assert.strictEqual(oZeta.getPath(), "/EMPLOYEES('6')");
38968
+ assert.strictEqual(oZeta.getBinding(), undefined, "oZeta is destroyed");
38969
+ oZeta = null;
38970
+
38971
+ await this.waitForChanges(assert, "create Zeta");
38972
+
38973
+ assert.strictEqual(oListBinding.getCount(), 6);
38974
+ checkTable("after create Zeta", assert, oTable, [
38975
+ oEpsilon,
38976
+ "/EMPLOYEES('6')"
38977
+ ], [
38978
+ [true, 3, "5", "Epsilon"],
38979
+ [undefined, 4, "6", "Zeta"]
38980
+ ], 6);
38981
+ oZeta = oListBinding.getCurrentContexts()[1];
38982
+
38983
+ this.expectRequest({
38984
+ batchNo : 11,
38985
+ method : "POST",
38986
+ url : "EMPLOYEES",
38987
+ payload : {
38988
+ "EMPLOYEE_2_MANAGER@odata.bind" : "EMPLOYEES('6')",
38989
+ Name : "Eta"
38990
+ }
38991
+ }, {
38992
+ ID : "7",
38993
+ Name : "Eta"
38994
+ })
38995
+ .expectRequest({
38996
+ batchNo : 11,
38997
+ url : "EMPLOYEES/$count?$filter=Is_Manager"
38998
+ }, 7)
38999
+ .expectRequest({
39000
+ batchNo : 11,
39001
+ url : sUrl.slice(0, -1) + ",ExpandLevels="
39002
+ + JSON.stringify([{NodeID : "5", Levels : 1}, {NodeID : "6", Levels : 1}]) + ")"
39003
+ + sSelect + "&$count=true&$skip=3&$top=2"
39004
+ }, {
39005
+ "@odata.count" : "7",
39006
+ value : [{
39007
+ DescendantCount : "2",
39008
+ DistanceFromRoot : "2",
39009
+ DrillState : "expanded",
39010
+ ID : "5",
39011
+ Name : "Epsilon"
39012
+ }, {
39013
+ DescendantCount : "1",
39014
+ DistanceFromRoot : "3",
39015
+ DrillState : "expanded",
39016
+ ID : "6",
39017
+ Name : "Zeta"
39018
+ }]
39019
+ })
39020
+ .expectRequest({
39021
+ batchNo : 12,
39022
+ url : sUrl.slice(0, -1) + ",ExpandLevels="
39023
+ + JSON.stringify([{NodeID : "5", Levels : 1}, {NodeID : "6", Levels : 1}]) + ")"
39024
+ + "&$filter=ID eq '7'&$select=LimitedRank"
39025
+ }, {
39026
+ value : [{LimitedRank : "5"}]
39027
+ });
39028
+
39029
+ // code under test
39030
+ let oEta = oListBinding.create({
39031
+ "@$ui5.node.parent" : oZeta,
39032
+ Name : "Eta"
39033
+ }, /*bSkipRefresh*/true);
39034
+
39035
+ await oEta.created();
39036
+
39037
+ assert.strictEqual(oEta.getIndex(), 5);
39038
+ assert.strictEqual(oEta.getPath(), "/EMPLOYEES('7')");
39039
+ assert.strictEqual(oEta.getBinding(), undefined, "oEta is destroyed");
39040
+ oEta = null;
39041
+
39042
+ await this.waitForChanges(assert, "create Eta");
39043
+
39044
+ assert.strictEqual(oListBinding.getCount(), 7);
39045
+ checkTable("after create Eta", assert, oTable, [
39046
+ oEpsilon,
39047
+ "/EMPLOYEES('6')"
39048
+ ], [
39049
+ [true, 3, "5", "Epsilon"],
39050
+ [true, 4, "6", "Zeta"]
39051
+ ], 7);
39052
+
39053
+ this.expectRequest(sUrl.slice(0, -1) + ",ExpandLevels="
39054
+ + JSON.stringify([{NodeID : "5", Levels : 1}, {NodeID : "6", Levels : 1}]) + ")"
39055
+ + sSelect + "&$skip=5&$top=1", {
39056
+ value : [{
39057
+ DescendantCount : "0",
39058
+ DistanceFromRoot : "4",
39059
+ DrillState : "leaf",
39060
+ ID : "7",
39061
+ Name : "Eta"
39062
+ }]
39063
+ });
39064
+
39065
+ oTable.setFirstVisibleRow(oZeta.getIndex());
39066
+
39067
+ await this.waitForChanges(assert, "scroll to Zeta, makes Eta visible");
39068
+
39069
+ checkTable("after scroll to Zeta, makes Eta visible", assert, oTable, [
39070
+ oEpsilon,
39071
+ "/EMPLOYEES('6')",
39072
+ "/EMPLOYEES('7')"
39073
+ ], [
39074
+ [true, 4, "6", "Zeta"],
39075
+ [undefined, 5, "7", "Eta"]
39076
+ ], 7);
38044
39077
  });
38045
39078
 
38046
39079
  //*********************************************************************************************
38047
- // Scenario: A hierarchy uses "$$aggregation.createInPlace". Create a root node which is created
38048
- // and shown in-place between two existing nodes. Use the special cases model to ensure the node
38049
- // property is properly requested.
39080
+ // Scenario: A hierarchy uses "$$aggregation.createInPlace".
39081
+ // Create a node, but because of a filter this node doesn't become part of the hierarchy and is
39082
+ // therefore not displayed.
39083
+ // Create a root node which is created and shown in-place between two existing nodes. Use the
39084
+ // special cases model to ensure the node property is properly requested.
38050
39085
  // Expand a node to see that createInPlace activated a unified cache (side-effects expand).
38051
39086
  // JIRA: CPOUI5ODATAV4-2560
38052
39087
  QUnit.test("Recursive Hierarchy: createInPlace, root", async function (assert) {
@@ -38112,6 +39147,47 @@ make root = ${bMakeRoot}`;
38112
39147
  ]);
38113
39148
  const oListBinding = oTable.getBinding("rows");
38114
39149
 
39150
+ this.expectRequest({
39151
+ method : "POST",
39152
+ url : "Artists",
39153
+ payload : {
39154
+ Name : "FilteredOut"
39155
+ }
39156
+ }, {
39157
+ ArtistID : "42",
39158
+ IsActiveEntity : false,
39159
+ Name : "FilteredOut",
39160
+ _ : null // not available w/ RAP for a non-hierarchical request
39161
+ })
39162
+ .expectRequest(sUrl
39163
+ + "&$filter=ArtistID eq '42' and IsActiveEntity eq false"
39164
+ + "&$select=_/Limited_Rank,_/NodeID", {
39165
+ value : [] // filtered out
39166
+ });
39167
+
39168
+ // code under test
39169
+ const oFilteredOut = oListBinding.create({
39170
+ Name : "FilteredOut"
39171
+ }, /*bSkipRefresh*/true);
39172
+
39173
+ await oFilteredOut.created();
39174
+
39175
+ assert.strictEqual(oFilteredOut.getIndex(), undefined, "not part of the hierarchy");
39176
+ assert.strictEqual(oFilteredOut.isTransient(), undefined);
39177
+ assert.strictEqual(oFilteredOut.getPath(), "/Artists(ArtistID='42',IsActiveEntity=false)");
39178
+ assert.strictEqual(oFilteredOut.getBinding(), undefined, "FilteredOut is destroyed");
39179
+
39180
+ await this.waitForChanges(assert, "create FilteredOut");
39181
+
39182
+ checkTable("after create FilteredOut", assert, oTable, [
39183
+ "/Artists(ArtistID='1',IsActiveEntity=false)",
39184
+ "/Artists(ArtistID='2',IsActiveEntity=false)"
39185
+ ], [
39186
+ [undefined, 1, "1", "Alpha", "1,false"],
39187
+ [false, 1, "2", "Beta", "2,false"]
39188
+ ]);
39189
+ assert.strictEqual(oListBinding.getCount(), 3);
39190
+
38115
39191
  this.expectRequest({
38116
39192
  method : "POST",
38117
39193
  url : "Artists",
@@ -38151,14 +39227,13 @@ make root = ${bMakeRoot}`;
38151
39227
  ]);
38152
39228
  assert.strictEqual(oListBinding.getCount(), 3);
38153
39229
 
38154
- await Promise.all([
38155
- oGammaCreated,
38156
- this.waitForChanges(assert, "create Gamma")
38157
- ]);
39230
+ await oGammaCreated;
38158
39231
 
38159
39232
  assert.strictEqual(oGamma.getIndex(), 1);
38160
- assert.strictEqual(oGamma.isTransient(), false);
38161
39233
  checkCreatedPersisted(assert, oGamma, oGammaCreated);
39234
+
39235
+ await this.waitForChanges(assert, "create Gamma");
39236
+
38162
39237
  checkTable("after create Gamma", assert, oTable, [
38163
39238
  "/Artists(ArtistID='1',IsActiveEntity=false)",
38164
39239
  "/Artists(ArtistID='3',IsActiveEntity=false)",
@@ -38248,8 +39323,13 @@ make root = ${bMakeRoot}`;
38248
39323
  // JIRA: CPOUI5ODATAV4-2342
38249
39324
  //
38250
39325
  // Request next sibling via group level cache (JIRA: CPOUI5ODATAV4-2558)
39326
+ // Duplicate calls to #requestSibling (JIRA: CPOUI5ODATAV4-2618)
38251
39327
  QUnit.test("Recursive Hierarchy: getParent/requestParent after requestSideEffects",
38252
39328
  async function (assert) {
39329
+ const sBaseUrl = "EMPLOYEES?$apply=com.sap.vocabularies.Hierarchy.v1.TopLevels("
39330
+ + "HierarchyNodes=$root/EMPLOYEES,HierarchyQualifier='OrgChart'"
39331
+ + ",NodeProperty='ID',Levels=1)"
39332
+ + "&$select=DrillState,ID,Name";
38253
39333
  const oModel = this.createTeaBusiModel({autoExpandSelect : true});
38254
39334
  const sView = `
38255
39335
  <t:Table id="table" rows="{path : '/EMPLOYEES',
@@ -38268,10 +39348,7 @@ make root = ${bMakeRoot}`;
38268
39348
  // 3 Delta
38269
39349
  // 4 Epsilon
38270
39350
  // 9 Omega
38271
- this.expectRequest("EMPLOYEES?$apply=com.sap.vocabularies.Hierarchy.v1.TopLevels("
38272
- + "HierarchyNodes=$root/EMPLOYEES,HierarchyQualifier='OrgChart'"
38273
- + ",NodeProperty='ID',Levels=1)"
38274
- + "&$select=DrillState,ID,Name&$count=true&$skip=0&$top=2", {
39351
+ this.expectRequest(sBaseUrl + "&$count=true&$skip=0&$top=2", {
38275
39352
  "@odata.count" : "2",
38276
39353
  value : [{
38277
39354
  DrillState : "collapsed",
@@ -38331,7 +39408,8 @@ make root = ${bMakeRoot}`;
38331
39408
  "double check that index was right");
38332
39409
 
38333
39410
  // code under test
38334
- assert.strictEqual(await oGamma.requestSibling(-1), oBeta, "CPOUI5ODATAV4-2558");
39411
+ assert.strictEqual(oGamma.getSibling(-1), oBeta, "CPOUI5ODATAV4-2558");
39412
+ assert.strictEqual(oGamma.getSibling(+1), undefined, "CPOUI5ODATAV4-2558");
38335
39413
 
38336
39414
  this.expectRequest("EMPLOYEES?$apply=descendants($root/EMPLOYEES,OrgChart,ID"
38337
39415
  + ",filter(ID eq '0'),1)"
@@ -38343,10 +39421,17 @@ make root = ${bMakeRoot}`;
38343
39421
  }]
38344
39422
  });
38345
39423
 
38346
- // code under test
38347
- const oDelta = await oGamma.requestSibling(+1);
39424
+ const [oDelta, oDelta0] = await Promise.all([
39425
+ // code under test
39426
+ oGamma.requestSibling(+1),
39427
+ oGamma.requestSibling(+1),
39428
+ this.waitForChanges(assert, "request Gamma's next sibling")
39429
+ ]);
38348
39430
 
39431
+ assert.strictEqual(oDelta, oDelta0, "CPOUI5ODATAV4-2618");
38349
39432
  assert.strictEqual(oDelta.getProperty("Name"), "Delta", "CPOUI5ODATAV4-2558");
39433
+ // code under test
39434
+ assert.strictEqual(oGamma.getSibling(+1), oDelta, "CPOUI5ODATAV4-2558");
38350
39435
 
38351
39436
  this.expectRequest("EMPLOYEES?$apply=descendants($root/EMPLOYEES,OrgChart,ID"
38352
39437
  + ",filter(ID eq '0'),1)"
@@ -38376,6 +39461,10 @@ make root = ${bMakeRoot}`;
38376
39461
  ]);
38377
39462
  assert.strictEqual(oDelta, oTable.getRows()[0].getBindingContext());
38378
39463
 
39464
+ // code under test
39465
+ assert.strictEqual(oAlpha.getParent(), null);
39466
+ assert.strictEqual(await oAlpha.requestParent(), null);
39467
+
38379
39468
  this.expectRequest("EMPLOYEES?$select=ID,Name&$filter=ID eq '3' or ID eq '4'"
38380
39469
  + "&$top=2", {
38381
39470
  value : [{
@@ -38389,10 +39478,6 @@ make root = ${bMakeRoot}`;
38389
39478
  }]
38390
39479
  });
38391
39480
 
38392
- // code under test
38393
- assert.strictEqual(oAlpha.getParent(), null);
38394
- assert.strictEqual(await oAlpha.requestParent(), null);
38395
-
38396
39481
  await Promise.all([
38397
39482
  oDelta.getBinding().getHeaderContext().requestSideEffects(["Name"]),
38398
39483
  this.waitForChanges(assert, "request side effects for name")
@@ -38409,20 +39494,19 @@ make root = ${bMakeRoot}`;
38409
39494
  // code under test
38410
39495
  assert.strictEqual(oDelta.getParent(), undefined, "JIRA: CPOUI5ODATAV4-2323");
38411
39496
 
38412
- this.expectRequest("EMPLOYEES?$apply=com.sap.vocabularies.Hierarchy.v1.TopLevels("
38413
- + "HierarchyNodes=$root/EMPLOYEES,HierarchyQualifier='OrgChart'"
38414
- + ",NodeProperty='ID',Levels=1)"
38415
- + "&$select=DrillState,ID,Name&$skip=0&$top=1", {
39497
+ this.expectRequest(sBaseUrl + "&$skip=0&$top=1", {
38416
39498
  value : [{
38417
39499
  DrillState : "collapsed",
38418
39500
  ID : "0",
38419
39501
  Name : "Alpha"
38420
39502
  }]
38421
- })
38422
- .expectChange("id", ["0", "9"]);
39503
+ });
38423
39504
 
38424
- // code under test
38425
- const oResult = await oDelta.requestParent();
39505
+ const [oResult] = await Promise.all([
39506
+ // code under test
39507
+ oDelta.requestParent(),
39508
+ this.waitForChanges(assert, "request Delta's parent")
39509
+ ]);
38426
39510
 
38427
39511
  assert.strictEqual(oAlpha.getModel(), undefined, "Alpha is destroyed");
38428
39512
  assert.notStrictEqual(oResult, oAlpha);
@@ -38439,6 +39523,30 @@ make root = ${bMakeRoot}`;
38439
39523
  [undefined, 2, "3", "Delta"],
38440
39524
  [undefined, 2, "4", "Epsilon"]
38441
39525
  ], 6);
39526
+
39527
+ this.expectRequest(sBaseUrl + "&$skip=1&$top=1", {
39528
+ value : [{
39529
+ DrillState : "leaf",
39530
+ ID : "9",
39531
+ Name : "Omega"
39532
+ }]
39533
+ });
39534
+
39535
+ const [oOmega, oOmega0] = await Promise.all([
39536
+ // code under test
39537
+ oResult.requestSibling(),
39538
+ oResult.requestSibling(),
39539
+ this.waitForChanges(assert, "request Alpha's next sibling")
39540
+ ]);
39541
+
39542
+ assert.strictEqual(oOmega, oOmega0, "CPOUI5ODATAV4-2618");
39543
+ assert.strictEqual(oOmega.getIndex(), 5, "CPOUI5ODATAV4-2558");
39544
+ assert.strictEqual(oOmega.getPath(), "/EMPLOYEES('9')");
39545
+ assert.deepEqual(oOmega.getObject(), {
39546
+ "@$ui5.node.level" : 1,
39547
+ ID : "9",
39548
+ Name : "Omega"
39549
+ });
38442
39550
  });
38443
39551
 
38444
39552
  //*********************************************************************************************
@@ -39348,6 +40456,871 @@ make root = ${bMakeRoot}`;
39348
40456
  assert.strictEqual(oXi, oTable.getRows()[0].getBindingContext());
39349
40457
  });
39350
40458
 
40459
+ //*********************************************************************************************
40460
+ // Scenario: Request previous and next sibling via first level cache. Check that it properly
40461
+ // works together with paging. Turn siblings into placeholders again and request a previous
40462
+ // sibling which is known, but there are children in between which have not yet been loaded.
40463
+ // JIRA: CPOUI5ODATAV4-2558
40464
+ //
40465
+ // Duplicate calls to #requestSibling (JIRA: CPOUI5ODATAV4-2618)
40466
+ // Update next sibling's index when selected (JIRA: CPOUI5ODATAV4-2619)
40467
+ QUnit.test("Recursive Hierarchy: requestSibling via 1st level cache", async function (assert) {
40468
+ const sFriend = "/Artists(ArtistID='99',IsActiveEntity=false)/_Friend";
40469
+ const sBaseUrl = sFriend.slice(1) + "?custom=foo&$apply=ancestors($root" + sFriend
40470
+ + ",OrgChart,_/NodeID,filter(sendsAutographs)/search(covfefe),keep start)"
40471
+ + "/orderby(defaultChannel desc)"
40472
+ + "/com.sap.vocabularies.Hierarchy.v1.TopLevels(HierarchyNodes=$root" + sFriend
40473
+ + ",HierarchyQualifier='OrgChart',NodeProperty='_/NodeID',Levels=3)";
40474
+ const sExpand = "&$expand=BestFriend($select=ArtistID,IsActiveEntity,Name)";
40475
+ const sSelect = "&$select=ArtistID,IsActiveEntity,Name"
40476
+ + ",_/DescendantCount,_/DistanceFromRoot,_/DrillState,_/NodeID";
40477
+ const oModel = this.createSpecialCasesModel({autoExpandSelect : true});
40478
+ const sView = `
40479
+ <FlexBox binding="{/Artists(ArtistID='99',IsActiveEntity=false)}">
40480
+ <t:Table firstVisibleRow="3" id="table" rows="{path : '_Friend',
40481
+ parameters : {
40482
+ $$aggregation : {
40483
+ expandTo : 3,
40484
+ hierarchyQualifier : 'OrgChart',
40485
+ search : 'covfefe'
40486
+ },
40487
+ $$ownRequest : true,
40488
+ $filter : 'sendsAutographs',
40489
+ $orderby : 'defaultChannel desc',
40490
+ custom : 'foo'
40491
+ }}" threshold="0" visibleRowCount="2">
40492
+ <Text text="{= %{@$ui5.node.isExpanded} }"/>
40493
+ <Text text="{= %{@$ui5.node.level} }"/>
40494
+ <Text text="{= %{@odata.etag} }"/>
40495
+ <Text text="{ArtistID}"/>
40496
+ <Text text="{Name}"/>
40497
+ <Text text="{_/NodeID}"/>
40498
+ <Text text="{BestFriend/Name}"/>
40499
+ </t:Table>
40500
+ </FlexBox>`;
40501
+
40502
+ // 0 Alpha (not loaded)
40503
+ // 1 Beta (loaded later - Delta's previous sibling)
40504
+ // 2 Gamma (not loaded)
40505
+ // 3 Delta
40506
+ // 4 Epsilon
40507
+ // 5 Zeta (not loaded)
40508
+ // 6 Eta (loaded later - Epsilon's next sibling)
40509
+ this.expectRequest(sBaseUrl + sSelect + sExpand + "&$count=true&$skip=3&$top=2", {
40510
+ "@odata.count" : "7",
40511
+ value : [{
40512
+ "@odata.etag" : "etag3.0",
40513
+ ArtistID : "3",
40514
+ BestFriend : {
40515
+ ArtistID : "3*",
40516
+ IsActiveEntity : false,
40517
+ Name : "Delta's Friend"
40518
+ },
40519
+ IsActiveEntity : false,
40520
+ Name : "Delta",
40521
+ _ : {
40522
+ DescendantCount : "0",
40523
+ DistanceFromRoot : "1",
40524
+ DrillState : "leaf",
40525
+ NodeID : "3,false"
40526
+ }
40527
+ }, {
40528
+ "@odata.etag" : "etag4.0",
40529
+ ArtistID : "4",
40530
+ BestFriend : {
40531
+ ArtistID : "4*",
40532
+ IsActiveEntity : false,
40533
+ Name : "Epsilon's Friend"
40534
+ },
40535
+ IsActiveEntity : false,
40536
+ Name : "Epsilon",
40537
+ _ : {
40538
+ DescendantCount : "1",
40539
+ DistanceFromRoot : "1",
40540
+ DrillState : "expanded",
40541
+ NodeID : "4,false"
40542
+ }
40543
+ }]
40544
+ });
40545
+
40546
+ await this.createView(assert, sView, oModel);
40547
+
40548
+ const oTable = this.oView.byId("table");
40549
+ checkTable("initial page", assert, oTable, [
40550
+ sFriend + "(ArtistID='3',IsActiveEntity=false)",
40551
+ sFriend + "(ArtistID='4',IsActiveEntity=false)"
40552
+ ], [
40553
+ [undefined, 2, "etag3.0", "3", "Delta", "3,false", "Delta's Friend"],
40554
+ [true, 2, "etag4.0", "4", "Epsilon", "4,false", "Epsilon's Friend"]
40555
+ ], 7);
40556
+ const oListBinding = oTable.getBinding("rows");
40557
+ const [oDelta, oEpsilon] = oListBinding.getCurrentContexts();
40558
+ // code under test
40559
+ assert.strictEqual(oDelta.getSibling(-1), undefined, "CPOUI5ODATAV4-2558");
40560
+
40561
+ // 0 Alpha (not loaded)
40562
+ // 1 Beta (loaded now)
40563
+ // 2 Gamma (not loaded)
40564
+ // 3 Delta
40565
+ // 4 Epsilon
40566
+ // 5 Zeta (not loaded)
40567
+ // 6 Eta (loaded later)
40568
+ for (let i = 0; i < 2; i += 1) {
40569
+ this.expectRequest(sBaseUrl + sExpand
40570
+ + "&$filter=_/Limited_Rank lt 3 and _/DistanceFromRoot lt 2"
40571
+ + "&$orderby=_/Limited_Rank desc&$select=ArtistID,IsActiveEntity,Name"
40572
+ + ",_/DescendantCount,_/DistanceFromRoot,_/DrillState,_/Limited_Rank,_/NodeID"
40573
+ + "&$top=1", {
40574
+ value : [{
40575
+ "@odata.etag" : "etag1.0",
40576
+ ArtistID : "1",
40577
+ BestFriend : {
40578
+ ArtistID : "1*",
40579
+ IsActiveEntity : false,
40580
+ Name : "Beta's Friend"
40581
+ },
40582
+ IsActiveEntity : false,
40583
+ Name : "Beta",
40584
+ _ : {
40585
+ DescendantCount : "1",
40586
+ DistanceFromRoot : "1",
40587
+ DrillState : "expanded",
40588
+ Limited_Rank : "1",
40589
+ NodeID : "1,false"
40590
+ }
40591
+ }]
40592
+ });
40593
+ }
40594
+
40595
+ // eslint-disable-next-line prefer-const
40596
+ let [oBeta, oBeta0] = await Promise.all([
40597
+ // code under test
40598
+ oDelta.requestSibling(-1),
40599
+ oDelta.requestSibling(-1),
40600
+ this.waitForChanges(assert, "request Delta's previous sibling")
40601
+ ]);
40602
+
40603
+ checkTable("after request Delta's previous sibling", assert, oTable, [
40604
+ oBeta,
40605
+ oDelta,
40606
+ oEpsilon
40607
+ ], [
40608
+ [undefined, 2, "etag3.0", "3", "Delta", "3,false", "Delta's Friend"],
40609
+ [true, 2, "etag4.0", "4", "Epsilon", "4,false", "Epsilon's Friend"]
40610
+ ], 7);
40611
+ assert.strictEqual(oBeta, oBeta0, "CPOUI5ODATAV4-2618");
40612
+ assert.strictEqual(oBeta.getIndex(), 1, "CPOUI5ODATAV4-2558");
40613
+ assert.strictEqual(oBeta.getPath(), sFriend + "(ArtistID='1',IsActiveEntity=false)");
40614
+ assert.deepEqual(oBeta.getObject(), {
40615
+ "@$ui5.node.isExpanded" : true,
40616
+ "@$ui5.node.level" : 2,
40617
+ "@odata.etag" : "etag1.0",
40618
+ ArtistID : "1",
40619
+ BestFriend : {
40620
+ ArtistID : "1*",
40621
+ IsActiveEntity : false,
40622
+ Name : "Beta's Friend"
40623
+ },
40624
+ IsActiveEntity : false,
40625
+ Name : "Beta",
40626
+ _ : {
40627
+ NodeID : "1,false"
40628
+ }
40629
+ }, "no Limited_Rank");
40630
+ // code under test
40631
+ assert.strictEqual(oDelta.getSibling(-1), oBeta, "CPOUI5ODATAV4-2558");
40632
+
40633
+ //TODO what a stupid request :-(
40634
+ this.expectRequest(sFriend.slice(1) + "(ArtistID='6',IsActiveEntity=false)"
40635
+ + "?custom=foo&$select=ArtistID,IsActiveEntity", {
40636
+ ArtistID : "6",
40637
+ IsActiveEntity : false
40638
+ });
40639
+
40640
+ const oEta0
40641
+ = oListBinding.getKeepAliveContext(sFriend + "(ArtistID='6',IsActiveEntity=false)");
40642
+
40643
+ await this.waitForChanges(assert, "request Eta as keep alive outside the collection");
40644
+
40645
+ // 0 Alpha (not loaded)
40646
+ // 1 Beta
40647
+ // 2 Gamma (not loaded)
40648
+ // 3 Delta
40649
+ // 4 Epsilon
40650
+ // 5 Zeta (not loaded)
40651
+ // 6 Eta (loaded now)
40652
+ this.expectRequest(sBaseUrl + sExpand
40653
+ + "&$filter=_/Limited_Rank gt 4 and _/DistanceFromRoot lt 2"
40654
+ + "&$select=ArtistID,IsActiveEntity,Name"
40655
+ + ",_/DescendantCount,_/DistanceFromRoot,_/DrillState,_/Limited_Rank,_/NodeID"
40656
+ + "&$top=1", {
40657
+ value : [{
40658
+ "@odata.etag" : "etag6.0",
40659
+ ArtistID : "6",
40660
+ BestFriend : {
40661
+ ArtistID : "6*",
40662
+ IsActiveEntity : false,
40663
+ Name : "Eta's Friend"
40664
+ },
40665
+ IsActiveEntity : false,
40666
+ Name : "Eta",
40667
+ _ : {
40668
+ DescendantCount : "0",
40669
+ DistanceFromRoot : "1",
40670
+ DrillState : "leaf",
40671
+ Limited_Rank : "6",
40672
+ NodeID : "6,false"
40673
+ }
40674
+ }]
40675
+ });
40676
+
40677
+ const [oEta] = await Promise.all([
40678
+ // code under test
40679
+ oEpsilon.requestSibling(+1),
40680
+ this.waitForChanges(assert, "request Epsilon's next sibling")
40681
+ ]);
40682
+
40683
+ checkTable("after request Epsilon's next sibling", assert, oTable, [
40684
+ oBeta,
40685
+ oDelta,
40686
+ oEpsilon,
40687
+ oEta
40688
+ ], [
40689
+ [undefined, 2, "etag3.0", "3", "Delta", "3,false", "Delta's Friend"],
40690
+ [true, 2, "etag4.0", "4", "Epsilon", "4,false", "Epsilon's Friend"]
40691
+ ], 7);
40692
+ assert.strictEqual(oEta.getIndex(), 6, "CPOUI5ODATAV4-2558");
40693
+ assert.strictEqual(oEta.getPath(), sFriend + "(ArtistID='6',IsActiveEntity=false)");
40694
+ assert.deepEqual(oEta.getObject(), {
40695
+ "@$ui5.node.level" : 2,
40696
+ "@odata.etag" : "etag6.0",
40697
+ ArtistID : "6",
40698
+ BestFriend : {
40699
+ ArtistID : "6*",
40700
+ IsActiveEntity : false,
40701
+ Name : "Eta's Friend"
40702
+ },
40703
+ IsActiveEntity : false,
40704
+ Name : "Eta",
40705
+ _ : {
40706
+ NodeID : "6,false"
40707
+ }
40708
+ }, "no Limited_Rank");
40709
+ assert.strictEqual(oEta, oEta0, "keep alive context reused");
40710
+ oEta0.setKeepAlive(false); // not needed anymore...
40711
+
40712
+ this.expectRequest(sBaseUrl + sSelect + sExpand + "&$skip=0&$top=1", {
40713
+ value : [{
40714
+ "@odata.etag" : "etag0.0",
40715
+ ArtistID : "0",
40716
+ BestFriend : {
40717
+ ArtistID : "0*",
40718
+ IsActiveEntity : false,
40719
+ Name : "Alpha's Friend"
40720
+ },
40721
+ IsActiveEntity : false,
40722
+ Name : "Alpha",
40723
+ _ : {
40724
+ DescendantCount : "6",
40725
+ DistanceFromRoot : "0",
40726
+ DrillState : "expanded",
40727
+ NodeID : "0,false"
40728
+ }
40729
+ }]
40730
+ })
40731
+ .expectRequest(sBaseUrl + sSelect + sExpand + "&$skip=2&$top=1", {
40732
+ value : [{
40733
+ "@odata.etag" : "etag2.0",
40734
+ ArtistID : "2",
40735
+ BestFriend : {
40736
+ ArtistID : "2*",
40737
+ IsActiveEntity : false,
40738
+ Name : "Gamma's Friend"
40739
+ },
40740
+ IsActiveEntity : false,
40741
+ Name : "Gamma",
40742
+ _ : {
40743
+ DescendantCount : "0",
40744
+ DistanceFromRoot : "2",
40745
+ DrillState : "leaf",
40746
+ NodeID : "2,false"
40747
+ }
40748
+ }]
40749
+ })
40750
+ .expectRequest(sBaseUrl + sSelect + sExpand + "&$skip=5&$top=1", {
40751
+ value : [{
40752
+ "@odata.etag" : "etag5.0",
40753
+ ArtistID : "5",
40754
+ BestFriend : {
40755
+ ArtistID : "5*",
40756
+ IsActiveEntity : false,
40757
+ Name : "Zeta's Friend"
40758
+ },
40759
+ IsActiveEntity : false,
40760
+ Name : "Zeta",
40761
+ _ : {
40762
+ DescendantCount : "0",
40763
+ DistanceFromRoot : "2",
40764
+ DrillState : "leaf",
40765
+ NodeID : "5,false"
40766
+ }
40767
+ }]
40768
+ });
40769
+
40770
+ await this.checkAllContexts("after requesting siblings", assert, oListBinding,
40771
+ ["@$ui5.node.isExpanded", "@$ui5.node.level", "@odata.etag", "ArtistID", "Name",
40772
+ "_/NodeID"], [
40773
+ [true, 1, "etag0.0", "0", "Alpha", "0,false", "Alpha's Friend"],
40774
+ [true, 2, "etag1.0", "1", "Beta", "1,false", "Beta's Friend"],
40775
+ [undefined, 3, "etag2.0", "2", "Gamma", "2,false", "Gamma's Friend"],
40776
+ [undefined, 2, "etag3.0", "3", "Delta", "3,false", "Delta's Friend"],
40777
+ [true, 2, "etag4.0", "4", "Epsilon", "4,false", "Epsilon's Friend"],
40778
+ [undefined, 3, "etag5.0", "5", "Zeta", "5,false", "Zeta's Friend"],
40779
+ [undefined, 2, "etag6.0", "6", "Eta", "6,false", "Eta's Friend"]
40780
+ ]);
40781
+
40782
+ checkTable("final page", assert, oTable, [
40783
+ sFriend + "(ArtistID='0',IsActiveEntity=false)",
40784
+ oBeta, // "reused"
40785
+ sFriend + "(ArtistID='2',IsActiveEntity=false)",
40786
+ oDelta,
40787
+ oEpsilon,
40788
+ sFriend + "(ArtistID='5',IsActiveEntity=false)",
40789
+ oEta // "reused"
40790
+ ], [
40791
+ [undefined, 2, "etag3.0", "3", "Delta", "3,false", "Delta's Friend"],
40792
+ [true, 2, "etag4.0", "4", "Epsilon", "4,false", "Epsilon's Friend"]
40793
+ ]);
40794
+
40795
+ // 0 Alpha (loaded later)
40796
+ // 1 Beta (loaded later - Delta's previous sibling)
40797
+ // 2 Gamma (not loaded)
40798
+ // 3 Delta
40799
+ // 4 Epsilon
40800
+ // 5 Zeta (not loaded)
40801
+ // 6 Eta (not loaded)
40802
+ this.expectRequest(sFriend.slice(1)
40803
+ + "?$filter=ArtistID eq '3' and IsActiveEntity eq false"
40804
+ + " or ArtistID eq '4' and IsActiveEntity eq false"
40805
+ + "&custom=foo&$select=ArtistID,IsActiveEntity,Name,_/NodeID&$top=2", {
40806
+ value : [{
40807
+ "@odata.etag" : "etag3.1",
40808
+ ArtistID : "3",
40809
+ IsActiveEntity : false,
40810
+ Name : "Delta #1",
40811
+ _ : null // not available w/ RAP for a non-hierarchical request
40812
+ }, {
40813
+ "@odata.etag" : "etag4.1",
40814
+ ArtistID : "4",
40815
+ IsActiveEntity : false,
40816
+ Name : "Epsilon #1",
40817
+ _ : null // not available w/ RAP for a non-hierarchical request
40818
+ }]
40819
+ });
40820
+
40821
+ await Promise.all([
40822
+ oListBinding.getHeaderContext().requestSideEffects(["Name"]),
40823
+ this.waitForChanges(assert, "side effect: Name for all rows")
40824
+ ]);
40825
+
40826
+ checkTable("after side effect: Name for all rows", assert, oTable, [
40827
+ oDelta,
40828
+ oEpsilon
40829
+ ], [
40830
+ [undefined, 2, "etag3.1", "3", "Delta #1", "3,false", "Delta's Friend"],
40831
+ [true, 2, "etag4.1", "4", "Epsilon #1", "4,false", "Epsilon's Friend"]
40832
+ ], 7);
40833
+
40834
+ this.expectRequest(sBaseUrl + sSelect + sExpand + "&$skip=0&$top=2", {
40835
+ value : [{
40836
+ "@odata.etag" : "etag0.1",
40837
+ ArtistID : "0",
40838
+ BestFriend : {
40839
+ ArtistID : "0*",
40840
+ IsActiveEntity : false,
40841
+ Name : "Alpha's Friend"
40842
+ },
40843
+ IsActiveEntity : false,
40844
+ Name : "Alpha #1",
40845
+ _ : {
40846
+ DescendantCount : "6",
40847
+ DistanceFromRoot : "0",
40848
+ DrillState : "expanded",
40849
+ NodeID : "0,false"
40850
+ }
40851
+ }, {
40852
+ "@odata.etag" : "etag1.1",
40853
+ ArtistID : "1",
40854
+ BestFriend : {
40855
+ ArtistID : "1*",
40856
+ IsActiveEntity : false,
40857
+ Name : "Beta's Friend"
40858
+ },
40859
+ IsActiveEntity : false,
40860
+ Name : "Beta #1",
40861
+ _ : {
40862
+ DescendantCount : "1",
40863
+ DistanceFromRoot : "1",
40864
+ DrillState : "expanded",
40865
+ Limited_Rank : "1",
40866
+ NodeID : "1,false"
40867
+ }
40868
+ }]
40869
+ });
40870
+
40871
+ oTable.setFirstVisibleRow(0);
40872
+
40873
+ await this.waitForChanges(assert, "scroll up");
40874
+
40875
+ checkTable("after scroll up", assert, oTable, [
40876
+ sFriend + "(ArtistID='0',IsActiveEntity=false)",
40877
+ sFriend + "(ArtistID='1',IsActiveEntity=false)",
40878
+ oDelta,
40879
+ oEpsilon
40880
+ ], [
40881
+ [true, 1, "etag0.1", "0", "Alpha #1", "0,false", "Alpha's Friend"],
40882
+ [true, 2, "etag1.1", "1", "Beta #1", "1,false", "Beta's Friend"]
40883
+ ], 7);
40884
+ assert.strictEqual(oBeta.getBinding(), undefined, "destroyed by side effect");
40885
+ const [oAlpha, oBeta1] = oListBinding.getCurrentContexts();
40886
+ oBeta = oBeta1;
40887
+
40888
+ // code under test
40889
+ assert.strictEqual(oDelta.getSibling(-1), oBeta);
40890
+
40891
+ oDelta.setSelected(true); // #move, please update nextSibling's index (CPOUI5ODATAV4-2619)
40892
+ assert.deepEqual(oDelta.getObject(), {
40893
+ "@$ui5.context.isSelected" : true,
40894
+ "@$ui5.node.level" : 2,
40895
+ "@odata.etag" : "etag3.1",
40896
+ ArtistID : "3",
40897
+ BestFriend : {
40898
+ ArtistID : "3*",
40899
+ IsActiveEntity : false,
40900
+ Name : "Delta's Friend"
40901
+ },
40902
+ IsActiveEntity : false,
40903
+ Name : "Delta #1",
40904
+ _ : {
40905
+ NodeID : "3,false"
40906
+ }
40907
+ });
40908
+
40909
+ // 0 Alpha
40910
+ // 1 Beta
40911
+ // 2 Gamma (not loaded)
40912
+ // 4 Epsilon
40913
+ // 5 Zeta (not loaded)
40914
+ // 3 Delta
40915
+ // 6 Eta (not loaded)
40916
+ this.expectRequest({
40917
+ batchNo : 8,
40918
+ headers : {
40919
+ "If-Match" : "etag4.1",
40920
+ Prefer : "return=minimal"
40921
+ },
40922
+ method : "PATCH",
40923
+ payload : {
40924
+ "BestFriend@odata.bind" : "Artists(ArtistID='0',IsActiveEntity=false)"
40925
+ },
40926
+ url : "Artists(ArtistID='4',IsActiveEntity=false)"
40927
+ }) // 204 No Content
40928
+ .expectRequest({
40929
+ batchNo : 8,
40930
+ headers : {
40931
+ "If-Match" : "etag4.1",
40932
+ Prefer : "return=minimal"
40933
+ },
40934
+ method : "POST",
40935
+ payload : {
40936
+ // Note: add a non-key property for demonstrating the multi key scenario
40937
+ NextSibling : {ArtistID : "3", Name : "Delta #1"}
40938
+ },
40939
+ url : sFriend.slice(1)
40940
+ + "(ArtistID='4',IsActiveEntity=false)/special.cases.ChangeNextSibling"
40941
+ }) // 204 No Content
40942
+ .expectRequest({
40943
+ batchNo : 8,
40944
+ url : sBaseUrl + "&$filter=ArtistID eq '4' and IsActiveEntity eq false"
40945
+ + "&$select=_/Limited_Rank"
40946
+ }, {
40947
+ value : [{
40948
+ _ : {Limited_Rank : "3"}
40949
+ }]
40950
+ })
40951
+ .expectRequest({
40952
+ batchNo : 8,
40953
+ url : sBaseUrl + "&$filter=ArtistID eq '3' and IsActiveEntity eq false"
40954
+ + "&$select=_/Limited_Rank"
40955
+ }, {
40956
+ value : [{
40957
+ _ : {Limited_Rank : "5"}
40958
+ }]
40959
+ })
40960
+ .expectRequest({
40961
+ batchNo : 8,
40962
+ url : sFriend.slice(1) + "?$filter=ArtistID eq '3' and IsActiveEntity eq false"
40963
+ + "&custom=foo&$select=ArtistID,IsActiveEntity,Name,_/NodeID" + sExpand
40964
+ }, {
40965
+ value : [{
40966
+ "@odata.etag" : "etag3.2",
40967
+ ArtistID : "3",
40968
+ BestFriend : {
40969
+ ArtistID : "3*",
40970
+ IsActiveEntity : false,
40971
+ Name : "Delta's Friend"
40972
+ },
40973
+ IsActiveEntity : false,
40974
+ Name : "Delta #2",
40975
+ _ : null // not available w/ RAP for a non-hierarchical request
40976
+ }]
40977
+ })
40978
+ .expectRequest({
40979
+ batchNo : 8,
40980
+ url : sBaseUrl + sSelect + sExpand + "&$count=true&$skip=0&$top=2"
40981
+ }, {
40982
+ "@odata.count" : "7",
40983
+ value : [{
40984
+ "@odata.etag" : "etag0.2",
40985
+ ArtistID : "0",
40986
+ BestFriend : {
40987
+ ArtistID : "0*",
40988
+ IsActiveEntity : false,
40989
+ Name : "Alpha's Friend"
40990
+ },
40991
+ IsActiveEntity : false,
40992
+ Name : "Alpha #2",
40993
+ _ : {
40994
+ DescendantCount : "6",
40995
+ DistanceFromRoot : "0",
40996
+ DrillState : "expanded",
40997
+ NodeID : "0,false"
40998
+ }
40999
+ }, {
41000
+ "@odata.etag" : "etag1.2",
41001
+ ArtistID : "1",
41002
+ BestFriend : {
41003
+ ArtistID : "1*",
41004
+ IsActiveEntity : false,
41005
+ Name : "Beta's Friend"
41006
+ },
41007
+ IsActiveEntity : false,
41008
+ Name : "Beta #2",
41009
+ _ : {
41010
+ DescendantCount : "1",
41011
+ DistanceFromRoot : "1",
41012
+ DrillState : "expanded",
41013
+ Limited_Rank : "1",
41014
+ NodeID : "1,false"
41015
+ }
41016
+ }]
41017
+ });
41018
+
41019
+ // code under test (JIRA: CPOUI5ODATAV4-2619)
41020
+ await oEpsilon.move({nextSibling : oDelta, parent : oAlpha});
41021
+
41022
+ // Note: index needs to be retrieved as soon as possible!
41023
+ assert.strictEqual(oDelta.getIndex(), 5, "CPOUI5ODATAV4-2619");
41024
+
41025
+ await this.waitForChanges(assert, "move down 3 (Delta) after 4 (Epsilon)");
41026
+
41027
+ assert.strictEqual(oEpsilon.getIndex(), 3, "CPOUI5ODATAV4-2619");
41028
+ assert.strictEqual(oEpsilon.getBinding(), undefined, "destroyed by side-effects refresh");
41029
+ assert.strictEqual(oDelta.getIndex(), undefined, "not in the collection anymore");
41030
+ assert.strictEqual(oDelta.getBinding(), oListBinding);
41031
+ assert.deepEqual(oDelta.getObject(), {
41032
+ "@$ui5.context.isSelected" : true,
41033
+ // "@$ui5.node.level" : 2, // not currently part of the hierarchy
41034
+ "@odata.etag" : "etag3.2",
41035
+ ArtistID : "3",
41036
+ BestFriend : {
41037
+ ArtistID : "3*",
41038
+ IsActiveEntity : false,
41039
+ Name : "Delta's Friend"
41040
+ },
41041
+ IsActiveEntity : false,
41042
+ Name : "Delta #2",
41043
+ _ : null // not currently part of the hierarchy
41044
+ });
41045
+ checkTable("after move down 3 (Delta) after 4 (Epsilon)", assert, oTable, [
41046
+ oAlpha,
41047
+ oBeta,
41048
+ oDelta
41049
+ ], [
41050
+ [true, 1, "etag0.2", "0", "Alpha #2", "0,false", "Alpha's Friend"],
41051
+ [true, 2, "etag1.2", "1", "Beta #2", "1,false", "Beta's Friend"]
41052
+ ], 7);
41053
+
41054
+ // 0 Alpha
41055
+ // 1 Beta
41056
+ // 2 Gamma (now loaded)
41057
+ // 4 Epsilon (now loaded)
41058
+ // 5 Zeta (now loaded)
41059
+ // 3 Delta (now loaded)
41060
+ // 6 Eta (now loaded)
41061
+ this.expectRequest(sBaseUrl + sSelect + sExpand + "&$skip=2&$top=5", {
41062
+ value : [{
41063
+ "@odata.etag" : "etag2.2",
41064
+ ArtistID : "2",
41065
+ BestFriend : {
41066
+ ArtistID : "2*",
41067
+ IsActiveEntity : false,
41068
+ Name : "Gamma's Friend"
41069
+ },
41070
+ IsActiveEntity : false,
41071
+ Name : "Gamma #2",
41072
+ _ : {
41073
+ DescendantCount : "0",
41074
+ DistanceFromRoot : "2",
41075
+ DrillState : "leaf",
41076
+ NodeID : "2,false"
41077
+ }
41078
+ }, {
41079
+ "@odata.etag" : "etag4.2",
41080
+ ArtistID : "4",
41081
+ BestFriend : {
41082
+ ArtistID : "4*",
41083
+ IsActiveEntity : false,
41084
+ Name : "Epsilon's Friend"
41085
+ },
41086
+ IsActiveEntity : false,
41087
+ Name : "Epsilon #2",
41088
+ _ : {
41089
+ DescendantCount : "1",
41090
+ DistanceFromRoot : "1",
41091
+ DrillState : "expanded",
41092
+ NodeID : "4,false"
41093
+ }
41094
+ }, {
41095
+ "@odata.etag" : "etag5.2",
41096
+ ArtistID : "5",
41097
+ BestFriend : {
41098
+ ArtistID : "5*",
41099
+ IsActiveEntity : false,
41100
+ Name : "Zeta's Friend"
41101
+ },
41102
+ IsActiveEntity : false,
41103
+ Name : "Zeta #2",
41104
+ _ : {
41105
+ DescendantCount : "0",
41106
+ DistanceFromRoot : "2",
41107
+ DrillState : "leaf",
41108
+ NodeID : "5,false"
41109
+ }
41110
+ }, {
41111
+ "@odata.etag" : "etag3.2",
41112
+ ArtistID : "3",
41113
+ BestFriend : {
41114
+ ArtistID : "3*",
41115
+ IsActiveEntity : false,
41116
+ Name : "Delta's Friend"
41117
+ },
41118
+ IsActiveEntity : false,
41119
+ Name : "Delta #2",
41120
+ _ : {
41121
+ DescendantCount : "0",
41122
+ DistanceFromRoot : "1",
41123
+ DrillState : "leaf",
41124
+ NodeID : "3,false"
41125
+ }
41126
+ }, {
41127
+ "@odata.etag" : "etag6.2",
41128
+ ArtistID : "6",
41129
+ BestFriend : {
41130
+ ArtistID : "6*",
41131
+ IsActiveEntity : false,
41132
+ Name : "Eta's Friend"
41133
+ },
41134
+ IsActiveEntity : false,
41135
+ Name : "Eta #2",
41136
+ _ : {
41137
+ DescendantCount : "0",
41138
+ DistanceFromRoot : "1",
41139
+ DrillState : "leaf",
41140
+ NodeID : "6,false"
41141
+ }
41142
+ }]
41143
+ });
41144
+
41145
+ await this.checkAllContexts("after move down 3 (Delta) after 4 (Epsilon)", assert,
41146
+ oListBinding, ["@$ui5.node.isExpanded", "@$ui5.node.level", "@odata.etag", "ArtistID",
41147
+ "Name", "_/NodeID", "BestFriend/Name"], [
41148
+ [true, 1, "etag0.2", "0", "Alpha #2", "0,false", "Alpha's Friend"],
41149
+ [true, 2, "etag1.2", "1", "Beta #2", "1,false", "Beta's Friend"],
41150
+ [undefined, 3, "etag2.2", "2", "Gamma #2", "2,false", "Gamma's Friend"],
41151
+ [true, 2, "etag4.2", "4", "Epsilon #2", "4,false", "Epsilon's Friend"],
41152
+ [undefined, 3, "etag5.2", "5", "Zeta #2", "5,false", "Zeta's Friend"],
41153
+ [undefined, 2, "etag3.2", "3", "Delta #2", "3,false", "Delta's Friend"],
41154
+ [undefined, 2, "etag6.2", "6", "Eta #2", "6,false", "Eta's Friend"]
41155
+ ]);
41156
+
41157
+ checkTable("final page", assert, oTable, [
41158
+ oAlpha, // sFriend + "(ArtistID='0',IsActiveEntity=false)",
41159
+ oBeta, // sFriend + "(ArtistID='1',IsActiveEntity=false)",
41160
+ sFriend + "(ArtistID='2',IsActiveEntity=false)",
41161
+ sFriend + "(ArtistID='4',IsActiveEntity=false)", // Note: oEpsilon already destroyed
41162
+ sFriend + "(ArtistID='5',IsActiveEntity=false)",
41163
+ oDelta, // sFriend + "(ArtistID='3',IsActiveEntity=false)",
41164
+ sFriend + "(ArtistID='6',IsActiveEntity=false)"
41165
+ ], [
41166
+ [true, 1, "etag0.2", "0", "Alpha #2", "0,false", "Alpha's Friend"],
41167
+ [true, 2, "etag1.2", "1", "Beta #2", "1,false", "Beta's Friend"]
41168
+ ]);
41169
+ });
41170
+
41171
+ //*********************************************************************************************
41172
+ // Scenario: Request sibling via unified cache with ExpandLevels. Handle case where request
41173
+ // shows that there's no such sibling. Store that node inside the cache nevertheless and thus
41174
+ // avoid further requests.
41175
+ // JIRA: CPOUI5ODATAV4-2610
41176
+ QUnit.test("Recursive Hierarchy: requestSibling w/ ExpandLevels", async function (assert) {
41177
+ const sBaseUrl = "EMPLOYEES?$apply=com.sap.vocabularies.Hierarchy.v1.TopLevels("
41178
+ + "HierarchyNodes=$root/EMPLOYEES,HierarchyQualifier='OrgChart'"
41179
+ + ",NodeProperty='ID',Levels=1)";
41180
+ const oModel = this.createTeaBusiModel({autoExpandSelect : true});
41181
+ const sView = `
41182
+ <t:Table id="table" rows="{path : '/EMPLOYEES',
41183
+ parameters : {
41184
+ $$aggregation : {
41185
+ createInPlace : true,
41186
+ expandTo : 1,
41187
+ hierarchyQualifier : 'OrgChart'
41188
+ }
41189
+ }}" threshold="0" visibleRowCount="1">
41190
+ <Text text="{= %{@$ui5.node.isExpanded} }"/>
41191
+ <Text text="{= %{@$ui5.node.level} }"/>
41192
+ <Text id="id" text="{ID}"/>
41193
+ <Text text="{Name}"/>
41194
+ </t:Table>`;
41195
+
41196
+ // 0 Alpha (later expanded)
41197
+ // 1 Beta
41198
+ // 9 Omega (not loaded)
41199
+ this.expectRequest(sBaseUrl + "&$select=DrillState,ID,Name&$count=true&$skip=0&$top=1", {
41200
+ "@odata.count" : "2",
41201
+ value : [{
41202
+ DrillState : "collapsed",
41203
+ ID : "0",
41204
+ Name : "Alpha"
41205
+ }]
41206
+ })
41207
+ .expectChange("id", ["0"]);
41208
+
41209
+ await this.createView(assert, sView, oModel);
41210
+
41211
+ const oTable = this.oView.byId("table");
41212
+ checkTable("initial page", assert, oTable, [
41213
+ "/EMPLOYEES('0')"
41214
+ ], [
41215
+ [false, 1, "0", "Alpha"]
41216
+ ], 2);
41217
+ const oListBinding = oTable.getBinding("rows");
41218
+ const [oAlpha] = oListBinding.getCurrentContexts();
41219
+
41220
+ // 0 Alpha (expanded)
41221
+ // 1 Beta
41222
+ // 9 Omega (not loaded)
41223
+ this.expectRequest(sBaseUrl.slice(0, -1)
41224
+ + ",ExpandLevels=" + JSON.stringify([{NodeID : "0", Levels : 1}])
41225
+ + ")&$select=DescendantCount,DistanceFromRoot,DrillState,ID,Name"
41226
+ + "&$count=true&$skip=0&$top=1", {
41227
+ "@odata.count" : "3",
41228
+ value : [{
41229
+ DescendantCount : "1",
41230
+ DistanceFromRoot : "0",
41231
+ DrillState : "expanded",
41232
+ ID : "0",
41233
+ Name : "Alpha"
41234
+ }]
41235
+ });
41236
+
41237
+ // code under test
41238
+ oAlpha.expand();
41239
+
41240
+ await this.waitForChanges(assert, "expand 0 (Alpha)");
41241
+
41242
+ checkTable("after expand 0 (Alpha)", assert, oTable, [
41243
+ "/EMPLOYEES('0')"
41244
+ ], [
41245
+ [true, 1, "0", "Alpha"]
41246
+ ], 3);
41247
+
41248
+ this.expectRequest(sBaseUrl.slice(0, -1)
41249
+ + ",ExpandLevels=" + JSON.stringify([{NodeID : "0", Levels : 1}])
41250
+ + ")&$select=DescendantCount,DistanceFromRoot,DrillState,ID,Name"
41251
+ + "&$skip=1&$top=1", {
41252
+ value : [{
41253
+ DescendantCount : "0",
41254
+ DistanceFromRoot : "1",
41255
+ DrillState : "leaf",
41256
+ ID : "1",
41257
+ Name : "Beta"
41258
+ }]
41259
+ })
41260
+ .expectChange("id", [, "1"]);
41261
+
41262
+ // code under test
41263
+ oTable.setFirstVisibleRow(1);
41264
+
41265
+ await this.waitForChanges(assert, "scroll down");
41266
+
41267
+ checkTable("after scroll down", assert, oTable, [
41268
+ "/EMPLOYEES('0')",
41269
+ "/EMPLOYEES('1')"
41270
+ ], [
41271
+ [undefined, 2, "1", "Beta"]
41272
+ ], 3);
41273
+ const [oBeta] = oListBinding.getCurrentContexts();
41274
+
41275
+ // 0 Alpha (expanded)
41276
+ // 1 Beta
41277
+ // 9 Omega (not Beta's sibling)
41278
+ this.expectRequest(sBaseUrl.slice(0, -1)
41279
+ + ",ExpandLevels=" + JSON.stringify([{NodeID : "0", Levels : 1}])
41280
+ + ")&$filter=LimitedRank gt 1 and DistanceFromRoot lt 2"
41281
+ + "&$select=DescendantCount,DistanceFromRoot,DrillState,ID,LimitedRank,Name"
41282
+ + "&$top=1", {
41283
+ value : [{
41284
+ DescendantCount : "0",
41285
+ DistanceFromRoot : "0",
41286
+ DrillState : "leaf",
41287
+ ID : "9",
41288
+ LimitedRank : "2",
41289
+ Name : "Omega"
41290
+ }]
41291
+ });
41292
+
41293
+ const [oSibling] = await Promise.all([
41294
+ // code under test
41295
+ oBeta.requestSibling(+1),
41296
+ this.waitForChanges(assert, "request Beta's next sibling")
41297
+ ]);
41298
+
41299
+ assert.strictEqual(oSibling, null);
41300
+
41301
+ // code under test
41302
+ const oOmega = oAlpha.getSibling();
41303
+ assert.strictEqual(await oAlpha.requestSibling(), oOmega, "no request needed");
41304
+
41305
+ assert.strictEqual(oOmega.getIndex(), 2);
41306
+ assert.deepEqual(oOmega.getObject(), {
41307
+ "@$ui5.node.level" : 1,
41308
+ ID : "9",
41309
+ Name : "Omega"
41310
+ });
41311
+ checkTable("after request Beta's next sibling", assert, oTable, [
41312
+ oAlpha, // "/EMPLOYEES('0')",
41313
+ oBeta, // "/EMPLOYEES('1')",
41314
+ oOmega // "/EMPLOYEES('2')"
41315
+ ], [
41316
+ [undefined, 2, "1", "Beta"]
41317
+ ]);
41318
+
41319
+ // code under test
41320
+ assert.strictEqual(oAlpha.getSibling(-1), null, "CPOUI5ODATAV4-2558");
41321
+ assert.strictEqual(await oAlpha.requestSibling(-1), null, "CPOUI5ODATAV4-2558");
41322
+ });
41323
+
39351
41324
  //*********************************************************************************************
39352
41325
  // Scenario: Show the single root node of a recursive hierarchy and expand it. Not all children
39353
41326
  // are loaded, but some placeholders remain. Create two new child nodes underneath the root.
@@ -40098,20 +42071,20 @@ make root = ${bMakeRoot}`;
40098
42071
  // forward
40099
42072
  await scroll(31);
40100
42073
  await scroll(32);
40101
- await scroll(33, 38, 3); // 3 after (> threshold/2)
42074
+ await scroll(33, 38, 5); // 5 after (> threshold/2)
40102
42075
  await scroll(34);
40103
42076
  await scroll(35);
40104
- scroll(36, 41, 3, true); // 3 after
42077
+ scroll(37, 43, 5, true); // 5 after
40105
42078
  await Promise.resolve();
40106
42079
  // this scroll happens before the response arrived, because we did not wait for the changes
40107
- await scroll(37);
42080
+ await scroll(38);
40108
42081
  // backward
40109
42082
  await scroll(29);
40110
42083
  await scroll(28);
40111
- await scroll(27, 22, 3); // 3 before
42084
+ await scroll(27, 20, 5); // 5 before
40112
42085
  await scroll(26);
40113
42086
  await scroll(25);
40114
- await scroll(24, 19, 3); // 3 before
42087
+ await scroll(22, 15, 5); // 5 before
40115
42088
  });
40116
42089
  });
40117
42090
 
@@ -41126,7 +43099,6 @@ make root = ${bMakeRoot}`;
41126
43099
  "42" : "9.99"
41127
43100
  },
41128
43101
  oObjectPage,
41129
- oRowContext,
41130
43102
  sView = '\
41131
43103
  <Table id="table" items="{path : \'/Artists\', \
41132
43104
  parameters : {$filter : \'IsActiveEntity\', $$patchWithoutSideEffects : true}}">\
@@ -41286,7 +43258,7 @@ make root = ${bMakeRoot}`;
41286
43258
  expectPublicationRequest("42", true);
41287
43259
 
41288
43260
  // first start with the list
41289
- oRowContext = that.oView.byId("table").getItems()[0].getBindingContext();
43261
+ const oRowContext = that.oView.byId("table").getItems()[0].getBindingContext();
41290
43262
  oRowContext.setKeepAlive(oFixture.keepAlive);
41291
43263
  return bindObjectPage(oRowContext, oFixture.hiddenBinding);
41292
43264
  }).then(function () {
@@ -41344,8 +43316,6 @@ make root = ${bMakeRoot}`;
41344
43316
  }).then(function () {
41345
43317
  return action("EditAction", "23");
41346
43318
  }).then(function () {
41347
- var oRowContext;
41348
-
41349
43319
  that.expectChange("id", "42")
41350
43320
  .expectChange("isActive", "Yes")
41351
43321
  .expectChange("name",
@@ -41354,7 +43324,7 @@ make root = ${bMakeRoot}`;
41354
43324
 
41355
43325
  // Now return to the artist from the list.
41356
43326
  // There is no request; the caches are reused.
41357
- oRowContext = that.oView.byId("table").getItems()[0].getBindingContext();
43327
+ const oRowContext = that.oView.byId("table").getItems()[0].getBindingContext();
41358
43328
  return bindObjectPage(oRowContext, oFixture.hiddenBinding);
41359
43329
  }).then(function () {
41360
43330
  // clear the object page
@@ -42633,7 +44603,7 @@ make root = ${bMakeRoot}`;
42633
44603
  oParentContext,
42634
44604
  that = this;
42635
44605
 
42636
- // code under test (CPOUI5ODATAV4-1580)
44606
+ // code under test (JIRA: CPOUI5ODATAV4-1580)
42637
44607
  return oModel.requestKeyPredicate("/Artists", oEntity).then(function (sKeyPredicate) {
42638
44608
  oParentContext = oModel.bindContext("/Artists" + sKeyPredicate).getBoundContext();
42639
44609
 
@@ -42658,7 +44628,7 @@ make root = ${bMakeRoot}`;
42658
44628
 
42659
44629
  assert.strictEqual(oInactiveArtistContext.getProperty("IsActiveEntity"), false);
42660
44630
  assert.strictEqual(
42661
- // code under test (CPOUI5ODATAV4-1580)
44631
+ // code under test (JIRA: CPOUI5ODATAV4-1580)
42662
44632
  oModel.getKeyPredicate("/Artists",
42663
44633
  oInactiveArtistContext.getObject()), // use back-end key odering
42664
44634
  "(ArtistID='4%2F2',IsActiveEntity=false)"
@@ -48751,8 +50721,7 @@ make root = ${bMakeRoot}`;
48751
50721
  return this.createView(assert, sView).then(function () {
48752
50722
  that.expectCanceledError("Failed to get contexts for " + sTeaBusi
48753
50723
  + "EMPLOYEES with start index 0 and length 100",
48754
- sODLB + ": /EMPLOYEES is ignoring response from inactive cache: " + sTeaBusi
48755
- + "EMPLOYEES");
50724
+ "Binding already destroyed");
48756
50725
 
48757
50726
  that.oView.destroy();
48758
50727
  delete that.oView;
@@ -51103,13 +53072,13 @@ make root = ${bMakeRoot}`;
51103
53072
  }, true);
51104
53073
 
51105
53074
  return Promise.all([
51106
- // code under test (CPOUI5ODATAV4-14)
53075
+ // code under test (JIRA: CPOUI5ODATAV4-14)
51107
53076
  oCreatedContext.setProperty("Address/City", "St. Ingbert", "$direct")
51108
53077
  .then(mustFail(assert), function (oError) {
51109
53078
  assert.strictEqual(oError.message, "The entity will be created via group"
51110
53079
  + " 'update'. Cannot patch via group '$direct'");
51111
53080
  }),
51112
- // code under test (CPOUI5ODATAV4-114)
53081
+ // code under test (JIRA: CPOUI5ODATAV4-114)
51113
53082
  oCreatedContext.setProperty("Address/AddressType", "42", null),
51114
53083
  oCreatedContext.setProperty("CompanyName", "Nestle", null),
51115
53084
  that.waitForChanges(assert)
@@ -52554,7 +54523,7 @@ make root = ${bMakeRoot}`;
52554
54523
  oTable = that.oView.byId("table");
52555
54524
  oListBinding = oTable.getBinding("rows");
52556
54525
 
52557
- // code under test (CPOUI5MODELS-741)
54526
+ // code under test (JIRA: CPOUI5MODELS-741)
52558
54527
  assert.deepEqual(oListBinding.getAllCurrentContexts().map(getPath), [
52559
54528
  "/EMPLOYEES('1')",
52560
54529
  "/EMPLOYEES('2')",
@@ -52579,7 +54548,7 @@ make root = ${bMakeRoot}`;
52579
54548
 
52580
54549
  return that.waitForChanges(assert);
52581
54550
  }).then(function () {
52582
- // code under test (CPOUI5MODELS-741)
54551
+ // code under test (JIRA: CPOUI5MODELS-741)
52583
54552
  assert.deepEqual(oListBinding.getAllCurrentContexts().map(getPath), [
52584
54553
  "/EMPLOYEES('1')",
52585
54554
  "/EMPLOYEES('2')",
@@ -52940,8 +54909,7 @@ make root = ${bMakeRoot}`;
52940
54909
  that = this;
52941
54910
 
52942
54911
  return this.createView(assert, "", oModel).then(function () {
52943
- var oModel = that.oModel,
52944
- oContextBinding = oModel.bindContext("Manager_to_Team"),
54912
+ var oContextBinding = oModel.bindContext("Manager_to_Team"),
52945
54913
  fnRespond;
52946
54914
 
52947
54915
  oContextBinding.setContext(
@@ -54004,24 +55972,24 @@ make root = ${bMakeRoot}`;
54004
55972
  return Promise.all([
54005
55973
  oAction0Promise.then(function () {
54006
55974
  assert.ok(bConfirm);
54007
- }, function (oError) {
55975
+ }, function (oError0) {
54008
55976
  assert.notOk(bConfirm);
54009
- assert.strictEqual(oError.message, "Action canceled due to strict handling");
54010
- assert.strictEqual(oError.canceled, true);
55977
+ assert.strictEqual(oError0.message, "Action canceled due to strict handling");
55978
+ assert.strictEqual(oError0.canceled, true);
54011
55979
  }),
54012
55980
  oAction1Promise.then(function () {
54013
55981
  assert.ok(bConfirm);
54014
- }, function (oError) {
55982
+ }, function (oError0) {
54015
55983
  assert.notOk(bConfirm);
54016
- assert.strictEqual(oError.message, "Action canceled due to strict handling");
54017
- assert.strictEqual(oError.canceled, true);
55984
+ assert.strictEqual(oError0.message, "Action canceled due to strict handling");
55985
+ assert.strictEqual(oError0.canceled, true);
54018
55986
  }),
54019
55987
  oAction2Promise.then(function () {
54020
55988
  assert.ok(bConfirm);
54021
- }, function (oError) {
55989
+ }, function (oError0) {
54022
55990
  assert.notOk(bConfirm);
54023
- assert.strictEqual(oError.message, "Action canceled due to strict handling");
54024
- assert.strictEqual(oError.canceled, true);
55991
+ assert.strictEqual(oError0.message, "Action canceled due to strict handling");
55992
+ assert.strictEqual(oError0.canceled, true);
54025
55993
  }),
54026
55994
  that.waitForChanges(assert)
54027
55995
  ]);
@@ -56469,7 +58437,7 @@ make root = ${bMakeRoot}`;
56469
58437
  oKeptContext.setKeepAlive(true);
56470
58438
  that.oView.byId("objectPage").setBindingContext(oKeptContext);
56471
58439
 
56472
- // code under test (CPOUI5MODELS-782)
58440
+ // code under test (JIRA: CPOUI5MODELS-782)
56473
58441
  assert.deepEqual(oTableBinding.getAllCurrentContexts().map(getPath),
56474
58442
  ["/SalesOrderList('1')"]);
56475
58443
 
@@ -56485,7 +58453,7 @@ make root = ${bMakeRoot}`;
56485
58453
 
56486
58454
  return that.waitForChanges(assert);
56487
58455
  }).then(function () {
56488
- // code under test (CPOUI5MODELS-782)
58456
+ // code under test (JIRA: CPOUI5MODELS-782)
56489
58457
  assert.deepEqual(oTableBinding.getAllCurrentContexts().map(getPath),
56490
58458
  ["/SalesOrderList('2')", "/SalesOrderList('1')"]);
56491
58459
 
@@ -62780,7 +64748,7 @@ make root = ${bMakeRoot}`;
62780
64748
  additionalTargets : [
62781
64749
  "SO_2_SOITEM(ItemPosition='0010',SalesOrderID='42%2f')/Note"
62782
64750
  ]
62783
- }, { // only one key property (changing BO is intended, due insufficient meta data)
64751
+ }, { // only one key property (changing BO is intended, due insufficient metadata)
62784
64752
  code : "code 3",
62785
64753
  message : "Not more than twice as high as wide",
62786
64754
  numericSeverity : 3,
@@ -67260,6 +69228,8 @@ make root = ${bMakeRoot}`;
67260
69228
  // inherit the selection from the header context. Swap the context of the "select all" property
67261
69229
  // binding and check if change listeners are correctly removed.
67262
69230
  // JIRA: CPOUI5ODATAV4-2493
69231
+ //
69232
+ // Check for selectionChanged events (JIRA: CPOUI5ODATAV4-2198)
67263
69233
  QUnit.test("Selection on header context and row context", async function (assert) {
67264
69234
  const oModel = this.createSalesOrdersModel({autoExpandSelect : true});
67265
69235
  const sView = `
@@ -67283,11 +69253,23 @@ make root = ${bMakeRoot}`;
67283
69253
  await this.createView(assert, sView, oModel);
67284
69254
 
67285
69255
  const oTable = this.oView.byId("table");
69256
+ const oListBinding = oTable.getBinding("items");
67286
69257
  const oPropertyBinding = oTable.getItems()[0].getCells()[1].getBinding("value");
67287
69258
  const oContext = oPropertyBinding.getContext();
69259
+ const aEventHandlers = [];
69260
+
69261
+ oListBinding.attachEvent("selectionChanged", function (oEvent) {
69262
+ aEventHandlers.shift()(oEvent.getParameters().context);
69263
+ assert.strictEqual(aEventHandlers.length, 0, "selectionChanged event received");
69264
+ });
67288
69265
 
67289
69266
  this.expectChange("selected", [true]);
67290
69267
 
69268
+ aEventHandlers.push((oContext0) => {
69269
+ assert.strictEqual(oContext0.isSelected(), true);
69270
+ assert.strictEqual(oContext, oContext0);
69271
+ });
69272
+
67291
69273
  // code under test
67292
69274
  oPropertyBinding.setValue(true);
67293
69275
 
@@ -67296,13 +69278,17 @@ make root = ${bMakeRoot}`;
67296
69278
 
67297
69279
  this.expectChange("selected", [false]);
67298
69280
 
69281
+ aEventHandlers.push((oContext0) => {
69282
+ assert.strictEqual(oContext0.isSelected(), false);
69283
+ assert.strictEqual(oContext, oContext0);
69284
+ });
69285
+
67299
69286
  // code under test
67300
69287
  oPropertyBinding.setValue(false);
67301
69288
 
67302
69289
  checkSelected(assert, oContext, false);
67303
69290
  await this.waitForChanges(assert, "rowContext deselected");
67304
69291
 
67305
- const oListBinding = oTable.getBinding("items");
67306
69292
  const oHeaderContext = oListBinding.getHeaderContext();
67307
69293
  const oSelectAllInput = this.oView.byId("selectAll");
67308
69294
  oSelectAllInput.setBindingContext(oHeaderContext);
@@ -67311,6 +69297,11 @@ make root = ${bMakeRoot}`;
67311
69297
  this.expectChange("selectAll", true)
67312
69298
  .expectChange("selected", [true, true, true]);
67313
69299
 
69300
+ aEventHandlers.push((oContext0) => {
69301
+ assert.strictEqual(oContext0.isSelected(), true);
69302
+ assert.strictEqual(oHeaderContext, oContext0);
69303
+ });
69304
+
67314
69305
  // code under test
67315
69306
  oSelectAllBinding.setValue(true);
67316
69307
 
@@ -67332,6 +69323,11 @@ make root = ${bMakeRoot}`;
67332
69323
  this.expectChange("selectAll", false)
67333
69324
  .expectChange("selected", [false, false, false]);
67334
69325
 
69326
+ aEventHandlers.push((oContext0) => {
69327
+ assert.strictEqual(oContext0.isSelected(), false);
69328
+ assert.strictEqual(oHeaderContext, oContext0);
69329
+ });
69330
+
67335
69331
  // code under test
67336
69332
  oSelectAllBinding.setValue(false);
67337
69333
 
@@ -67341,6 +69337,11 @@ make root = ${bMakeRoot}`;
67341
69337
  this.expectChange("selectAll", true)
67342
69338
  .expectChange("selected", [true, true, true]);
67343
69339
 
69340
+ aEventHandlers.push((oContext0) => {
69341
+ assert.strictEqual(oContext0.isSelected(), true);
69342
+ assert.strictEqual(oHeaderContext, oContext0);
69343
+ });
69344
+
67344
69345
  // code under test
67345
69346
  oHeaderContext.setProperty("@$ui5.context.isSelected", true);
67346
69347
 
@@ -67350,6 +69351,11 @@ make root = ${bMakeRoot}`;
67350
69351
  this.expectChange("selectAll", false)
67351
69352
  .expectChange("selected", [false, false, false]);
67352
69353
 
69354
+ aEventHandlers.push((oContext0) => {
69355
+ assert.strictEqual(oContext0.isSelected(), false);
69356
+ assert.strictEqual(oHeaderContext, oContext0);
69357
+ });
69358
+
67353
69359
  // code under test
67354
69360
  oHeaderContext.setProperty("@$ui5.context.isSelected", false);
67355
69361
 
@@ -67359,6 +69365,11 @@ make root = ${bMakeRoot}`;
67359
69365
  this.expectChange("selectAll", true)
67360
69366
  .expectChange("selected", [true, true, true]);
67361
69367
 
69368
+ aEventHandlers.push((oContext0) => {
69369
+ assert.strictEqual(oContext0.isSelected(), true);
69370
+ assert.strictEqual(oHeaderContext, oContext0);
69371
+ });
69372
+
67362
69373
  // code under test
67363
69374
  oHeaderContext.setSelected(true);
67364
69375
 
@@ -67382,6 +69393,11 @@ make root = ${bMakeRoot}`;
67382
69393
  this.expectChange("selectAll", false)
67383
69394
  .expectChange("selected", [false, false, false, false, false, false]);
67384
69395
 
69396
+ aEventHandlers.push((oContext0) => {
69397
+ assert.strictEqual(oContext0.isSelected(), false);
69398
+ assert.strictEqual(oHeaderContext, oContext0);
69399
+ });
69400
+
67385
69401
  // code under test
67386
69402
  oHeaderContext.setSelected(false);
67387
69403
 
@@ -67397,6 +69413,7 @@ make root = ${bMakeRoot}`;
67397
69413
  // code under test - no further change expected for "select all" binding
67398
69414
  oHeaderContext.setSelected(false);
67399
69415
 
69416
+ assert.strictEqual(aEventHandlers.length, 0); // all event handlers used
67400
69417
  await this.waitForChanges(assert, "swap context of property binding");
67401
69418
  });
67402
69419
 
@@ -67408,6 +69425,8 @@ make root = ${bMakeRoot}`;
67408
69425
  // (3) : Select two row contexts (no "select all")
67409
69426
  // JIRA: CPOUI5ODATAV4-2203
67410
69427
  // SNOW: CS20240007001494
69428
+ //
69429
+ // Check for selectionChanged events (JIRA: CPOUI5ODATAV4-2198)
67411
69430
  [
67412
69431
  {method : "filter", value : FilterType.Application, query : "GrossAmount ge 0"},
67413
69432
  {method : "filter", value : FilterType.Control, query : "GrossAmount ge 0"},
@@ -67442,11 +69461,28 @@ make root = ${bMakeRoot}`;
67442
69461
  const oListBinding = oTable.getBinding("items");
67443
69462
  const oHeaderContext = oListBinding.getHeaderContext();
67444
69463
  const oItems = oTable.getItems();
69464
+ const aEventHandlers = [];
69465
+
69466
+ oListBinding.attachEvent("selectionChanged", function (oEvent) {
69467
+ aEventHandlers.shift()(oEvent.getParameters().context);
69468
+ assert.strictEqual(aEventHandlers.length, 0, "selectionChanged event received");
69469
+ });
67445
69470
 
67446
69471
  this.expectChange("selected", [true, true, true]);
67447
69472
  this.expectChange("selected", [, false]);
67448
69473
 
69474
+ aEventHandlers.push((oContext) => {
69475
+ assert.strictEqual(oContext.isSelected(), true);
69476
+ assert.strictEqual(oHeaderContext, oContext);
69477
+ });
69478
+
67449
69479
  oHeaderContext.setSelected(true);
69480
+
69481
+ aEventHandlers.push((oContext) => {
69482
+ assert.strictEqual(oContext.isSelected(), false);
69483
+ assert.strictEqual(oItems[1].getBindingContext(), oContext);
69484
+ });
69485
+
67450
69486
  oItems[1].getBindingContext().setSelected(false);
67451
69487
 
67452
69488
  assert.strictEqual(oHeaderContext.isSelected(), true);
@@ -67464,6 +69500,11 @@ make root = ${bMakeRoot}`;
67464
69500
  .expectChange("selected", [false, /*false*/, false])
67465
69501
  .expectChange("selected", [undefined, undefined, undefined]);
67466
69502
 
69503
+ aEventHandlers.push((oContext) => {
69504
+ assert.strictEqual(oContext.isSelected(), false);
69505
+ assert.strictEqual(oHeaderContext, oContext);
69506
+ });
69507
+
67467
69508
  if (bSuspend) {
67468
69509
  oListBinding.suspend();
67469
69510
  }
@@ -67487,18 +69528,52 @@ make root = ${bMakeRoot}`;
67487
69528
  this.expectChange("selected", [true]);
67488
69529
  this.expectChange("selected", [false, false, false]); // preparation for test below
67489
69530
 
69531
+ aEventHandlers.push((oContext) => {
69532
+ assert.strictEqual(oContext.isSelected(), true);
69533
+ assert.strictEqual(oHeaderContext, oContext);
69534
+ });
69535
+
67490
69536
  oHeaderContext.setSelected(true);
69537
+
69538
+ aEventHandlers.push((oContext) => {
69539
+ assert.strictEqual(oContext.isSelected(), false);
69540
+ assert.strictEqual(oItems[0].getBindingContext(), oContext);
69541
+ });
67491
69542
  oItems[0].getBindingContext().setSelected(false);
69543
+
69544
+ // a row context selection was changed
69545
+ aEventHandlers.push((oContext) => {
69546
+ assert.strictEqual(oContext.isSelected(), true);
69547
+ assert.strictEqual(oHeaderContext, oContext);
69548
+ });
69549
+
67492
69550
  // code under test - "select all" again selects all row contexts, even if the header context
67493
69551
  // is already selected
67494
69552
  oHeaderContext.setSelected(true);
69553
+
69554
+ aEventHandlers.push((oContext) => {
69555
+ assert.strictEqual(oContext.isSelected(), false);
69556
+ assert.strictEqual(oHeaderContext, oContext);
69557
+ });
69558
+
67495
69559
  oHeaderContext.setSelected(false); // preparation for test below
67496
69560
 
67497
69561
  await this.waitForChanges(assert, "reselect all (2)");
67498
69562
 
67499
69563
  this.expectChange("selected", [true, , true]);
67500
69564
 
69565
+ aEventHandlers.push((oContext) => {
69566
+ assert.strictEqual(oContext.isSelected(), true);
69567
+ assert.strictEqual(oItems[0].getBindingContext(), oContext);
69568
+ });
69569
+
67501
69570
  oItems[0].getBindingContext().setSelected(true);
69571
+
69572
+ aEventHandlers.push((oContext) => {
69573
+ assert.strictEqual(oContext.isSelected(), true);
69574
+ assert.strictEqual(oItems[2].getBindingContext(), oContext);
69575
+ });
69576
+
67502
69577
  oItems[2].getBindingContext().setSelected(true);
67503
69578
 
67504
69579
  // same filter/search value will do nothing
@@ -67516,6 +69591,13 @@ make root = ${bMakeRoot}`;
67516
69591
  .expectChange("selected", [false, , false])
67517
69592
  .expectChange("selected", [undefined, undefined, undefined]);
67518
69593
 
69594
+ // selection state of header context did not change, but the selection state of row contexts
69595
+ // was changed
69596
+ aEventHandlers.push((oContext) => {
69597
+ assert.strictEqual(oContext.isSelected(), false);
69598
+ assert.strictEqual(oHeaderContext, oContext);
69599
+ });
69600
+
67519
69601
  if (bSuspend) {
67520
69602
  oListBinding.suspend();
67521
69603
  }
@@ -67529,6 +69611,7 @@ make root = ${bMakeRoot}`;
67529
69611
  if (bSuspend) {
67530
69612
  oListBinding.resume();
67531
69613
  }
69614
+ assert.strictEqual(aEventHandlers.length, 0); // all event handlers used
67532
69615
 
67533
69616
  await this.waitForChanges(assert, "after (2)");
67534
69617
  });