@sapui5/sap.fe.test 1.100.0 → 1.102.0

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 (31) hide show
  1. package/package.json +2 -3
  2. package/src/sap/fe/test/.library +1 -1
  3. package/src/sap/fe/test/BaseArrangements.js +65 -18
  4. package/src/sap/fe/test/CollaborationClient.js +77 -0
  5. package/src/sap/fe/test/CollaborationClient.ts +105 -0
  6. package/src/sap/fe/test/FeMocks.js +3 -3
  7. package/src/sap/fe/test/FeMocks.ts +10 -6
  8. package/src/sap/fe/test/FlexibleColumnLayout.js +148 -141
  9. package/src/sap/fe/test/JestTemplatingHelper.js +31 -10
  10. package/src/sap/fe/test/JestTemplatingHelper.ts +62 -48
  11. package/src/sap/fe/test/ListReport.js +72 -77
  12. package/src/sap/fe/test/LocationUtil.js +12 -16
  13. package/src/sap/fe/test/ObjectPage.js +116 -124
  14. package/src/sap/fe/test/Shell.js +94 -34
  15. package/src/sap/fe/test/TemplatePage.js +49 -27
  16. package/src/sap/fe/test/UI5MockHelper.js +100 -20
  17. package/src/sap/fe/test/UI5MockHelper.ts +79 -14
  18. package/src/sap/fe/test/api/APIHelper.js +26 -15
  19. package/src/sap/fe/test/api/CollaborationAPI.js +215 -0
  20. package/src/sap/fe/test/api/CollaborationAPI.ts +240 -0
  21. package/src/sap/fe/test/api/DialogActions.js +3 -1
  22. package/src/sap/fe/test/api/DialogMessageActions.js +78 -64
  23. package/src/sap/fe/test/api/HeaderActions.js +229 -226
  24. package/src/sap/fe/test/api/HeaderAssertions.js +76 -41
  25. package/src/sap/fe/test/api/TableAPI.js +30 -34
  26. package/src/sap/fe/test/api/TableActions.js +58 -70
  27. package/src/sap/fe/test/api/TableAssertions.js +51 -46
  28. package/src/sap/fe/test/builder/MacroFieldBuilder.js +161 -153
  29. package/src/sap/fe/test/builder/MdcTableBuilder.js +247 -191
  30. package/src/sap/fe/test/library.js +26 -35
  31. package/src/sap/fe/test/library.ts +28 -0
@@ -0,0 +1,215 @@
1
+ /*!
2
+ * SAP UI development toolkit for HTML5 (SAPUI5)
3
+ * (c) Copyright 2009-2021 SAP SE. All rights reserved
4
+ */
5
+ sap.ui.define(["sap/base/Log", "sap/fe/core/controllerextensions/collaboration/ActivityBase", "sap/fe/core/controllerextensions/collaboration/CollaborationCommon", "sap/fe/core/controllerextensions/editFlow/draft", "sap/ui/model/json/JSONModel"], function (Log, ActivityBase, CollaborationCommon, draft, JSONModel) {
6
+ "use strict";
7
+
8
+ var Activity = CollaborationCommon.Activity;
9
+ var initializeCollaboration = ActivityBase.initializeCollaboration;
10
+ var endCollaboration = ActivityBase.endCollaboration;
11
+ var broadcastCollaborationMessage = ActivityBase.broadcastCollaborationMessage;
12
+
13
+ var CollaborationAPI = {
14
+ _lastReceivedMessage: undefined,
15
+ _rootPath: "",
16
+ _oModel: undefined,
17
+ _lockedPropertyPath: "",
18
+ _internalModel: undefined,
19
+
20
+ /**
21
+ * Open an existing collaborative draft with a new user, and creates a 'ghost client' for this user.
22
+ *
23
+ * @param oContext The context of the collaborative draft
24
+ * @param userID The ID of the user
25
+ * @param userName The name of the user
26
+ */
27
+ enterDraft: function (oContext, userID, userName) {
28
+ var webSocketBaseURL = oContext.getModel().getMetaModel().getObject("/@com.sap.vocabularies.Common.v1.WebSocketBaseURL");
29
+
30
+ if (!webSocketBaseURL) {
31
+ Log.error("Cannot find WebSocketBaseURL annotation");
32
+ return;
33
+ }
34
+
35
+ var sDraftUUID = oContext.getProperty("DraftAdministrativeData/DraftUUID");
36
+ this._internalModel = new JSONModel({});
37
+ initializeCollaboration({
38
+ id: userID,
39
+ name: userName,
40
+ initialName: userName
41
+ }, webSocketBaseURL, sDraftUUID, this._internalModel, this._onMessageReceived.bind(this), true);
42
+ this._rootPath = oContext.getPath();
43
+ this._oModel = oContext.getModel();
44
+ },
45
+
46
+ /**
47
+ * Checks if the ghost client has revieved a given message.
48
+ *
49
+ * @param message The message content to be looked for
50
+ * @returns True if the last recieved message matches the content
51
+ */
52
+ checkReceived: function (message) {
53
+ if (!this._lastReceivedMessage) {
54
+ return false;
55
+ }
56
+
57
+ var found = (!message.userID || message.userID === this._lastReceivedMessage.userID) && (!message.userAction || message.userAction === this._lastReceivedMessage.userAction) && (!message.clientContent || message.clientContent === this._lastReceivedMessage.clientContent);
58
+ this._lastReceivedMessage = undefined; // reset history to avoid finding the same message twice
59
+
60
+ return found;
61
+ },
62
+
63
+ /**
64
+ * Closes the ghost client and removes the user from the collaborative draft.
65
+ */
66
+ leaveDraft: function () {
67
+ if (this._internalModel) {
68
+ endCollaboration(this._internalModel);
69
+
70
+ this._internalModel.destroy();
71
+
72
+ this._internalModel = undefined;
73
+ }
74
+ },
75
+
76
+ /**
77
+ * Simulates that the user starts typing in an input (live change).
78
+ *
79
+ * @param sPropertyPath The path of the property being modified
80
+ */
81
+ startLiveChange: function (sPropertyPath) {
82
+ if (this._internalModel) {
83
+ if (this._lockedPropertyPath) {
84
+ // Unlock previous property path
85
+ this.undoChange();
86
+ }
87
+
88
+ this._lockedPropertyPath = sPropertyPath;
89
+ broadcastCollaborationMessage(Activity.LiveChange, this._rootPath + "/" + sPropertyPath, this._internalModel);
90
+ }
91
+ },
92
+
93
+ /**
94
+ * Simulates that the user has modified a property.
95
+ *
96
+ * @param sPropertyPath The path of the property being modified
97
+ * @param value The new value of the property being modified
98
+ */
99
+ updatePropertyValue: function (sPropertyPath, value) {
100
+ var _this = this;
101
+
102
+ if (this._internalModel) {
103
+ if (this._lockedPropertyPath !== sPropertyPath) {
104
+ this.startLiveChange(sPropertyPath);
105
+ }
106
+
107
+ var oContextBinding = this._oModel.bindContext(this._rootPath, undefined, {
108
+ $$patchWithoutSideEffects: true,
109
+ $$groupId: "$auto",
110
+ $$updateGroupId: "$auto"
111
+ });
112
+
113
+ var oPropertyBinding = this._oModel.bindProperty(sPropertyPath, oContextBinding.getBoundContext());
114
+
115
+ oPropertyBinding.requestValue().then(function () {
116
+ oPropertyBinding.setValue(value);
117
+ oContextBinding.attachEventOnce("patchCompleted", function () {
118
+ broadcastCollaborationMessage(Activity.Change, _this._rootPath + "/" + sPropertyPath, _this._internalModel);
119
+ _this._lockedPropertyPath = "";
120
+ });
121
+ }).catch(function (err) {
122
+ Log.error(err);
123
+ });
124
+ }
125
+ },
126
+
127
+ /**
128
+ * Simulates that the user did an 'undo' (to be called after startLiveChange).
129
+ */
130
+ undoChange: function () {
131
+ if (this._lockedPropertyPath) {
132
+ broadcastCollaborationMessage(Activity.Undo, this._rootPath + "/" + this._lockedPropertyPath, this._internalModel);
133
+ this._lockedPropertyPath = "";
134
+ }
135
+ },
136
+
137
+ /**
138
+ * Simulates that the user has discarded the draft.
139
+ */
140
+ discardDraft: function () {
141
+ var _this2 = this;
142
+
143
+ if (this._internalModel) {
144
+ var draftContext = this._getDraftContext();
145
+
146
+ draftContext.requestProperty("IsActiveEntity").then(function () {
147
+ draft.deleteDraft(draftContext);
148
+ }).then(function () {
149
+ broadcastCollaborationMessage(Activity.Discard, _this2._rootPath.replace("IsActiveEntity=false", "IsActiveEntity=true"), _this2._internalModel);
150
+
151
+ _this2._internalModel.destroy();
152
+
153
+ _this2._internalModel = undefined;
154
+ }).catch(function (err) {
155
+ Log.error(err);
156
+ });
157
+ }
158
+ },
159
+
160
+ /**
161
+ * Simulates that the user has deleted the draft.
162
+ */
163
+ deleteDraft: function () {
164
+ var _this3 = this;
165
+
166
+ if (this._internalModel) {
167
+ var draftContext = this._getDraftContext();
168
+
169
+ var activeContext;
170
+ draftContext.requestProperty("IsActiveEntity").then(function () {
171
+ return draftContext.getModel().bindContext(_this3._rootPath + "/SiblingEntity").getBoundContext();
172
+ }).then(function (context) {
173
+ activeContext = context;
174
+ return context.requestCanonicalPath();
175
+ }).then(function () {
176
+ return draft.deleteDraft(draftContext);
177
+ }).then(function () {
178
+ activeContext.delete();
179
+ broadcastCollaborationMessage(Activity.Delete, _this3._rootPath, _this3._internalModel);
180
+
181
+ _this3._internalModel.destroy();
182
+
183
+ _this3._internalModel = undefined;
184
+ }).catch(function (err) {
185
+ Log.error(err);
186
+ });
187
+ }
188
+ },
189
+ // /////////////////////////////
190
+ // Private methods
191
+ _getDraftContext: function () {
192
+ return this._oModel.bindContext(this._rootPath, undefined, {
193
+ $$patchWithoutSideEffects: true,
194
+ $$groupId: "$auto",
195
+ $$updateGroupId: "$auto"
196
+ }).getBoundContext();
197
+ },
198
+
199
+ /**
200
+ * Callback of the ghost client when receiving a message on the web socket.
201
+ *
202
+ * @param oMessage The message
203
+ */
204
+ _onMessageReceived: function (oMessage) {
205
+ oMessage.userAction = oMessage.userAction || oMessage.clientAction;
206
+ this._lastReceivedMessage = oMessage;
207
+
208
+ if (oMessage.userAction === Activity.Join) {
209
+ broadcastCollaborationMessage(Activity.JoinEcho, this._lockedPropertyPath ? this._rootPath + "/" + this._lockedPropertyPath : undefined, this._internalModel);
210
+ }
211
+ }
212
+ };
213
+ return CollaborationAPI;
214
+ }, false);
215
+ //# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIkNvbGxhYm9yYXRpb25BUEkudHMiXSwibmFtZXMiOlsiQ29sbGFib3JhdGlvbkFQSSIsIl9sYXN0UmVjZWl2ZWRNZXNzYWdlIiwidW5kZWZpbmVkIiwiX3Jvb3RQYXRoIiwiX29Nb2RlbCIsIl9sb2NrZWRQcm9wZXJ0eVBhdGgiLCJfaW50ZXJuYWxNb2RlbCIsImVudGVyRHJhZnQiLCJvQ29udGV4dCIsInVzZXJJRCIsInVzZXJOYW1lIiwid2ViU29ja2V0QmFzZVVSTCIsImdldE1vZGVsIiwiZ2V0TWV0YU1vZGVsIiwiZ2V0T2JqZWN0IiwiTG9nIiwiZXJyb3IiLCJzRHJhZnRVVUlEIiwiZ2V0UHJvcGVydHkiLCJKU09OTW9kZWwiLCJpbml0aWFsaXplQ29sbGFib3JhdGlvbiIsImlkIiwibmFtZSIsImluaXRpYWxOYW1lIiwiX29uTWVzc2FnZVJlY2VpdmVkIiwiYmluZCIsImdldFBhdGgiLCJjaGVja1JlY2VpdmVkIiwibWVzc2FnZSIsImZvdW5kIiwidXNlckFjdGlvbiIsImNsaWVudENvbnRlbnQiLCJsZWF2ZURyYWZ0IiwiZW5kQ29sbGFib3JhdGlvbiIsImRlc3Ryb3kiLCJzdGFydExpdmVDaGFuZ2UiLCJzUHJvcGVydHlQYXRoIiwidW5kb0NoYW5nZSIsImJyb2FkY2FzdENvbGxhYm9yYXRpb25NZXNzYWdlIiwiQWN0aXZpdHkiLCJMaXZlQ2hhbmdlIiwidXBkYXRlUHJvcGVydHlWYWx1ZSIsInZhbHVlIiwib0NvbnRleHRCaW5kaW5nIiwiYmluZENvbnRleHQiLCIkJHBhdGNoV2l0aG91dFNpZGVFZmZlY3RzIiwiJCRncm91cElkIiwiJCR1cGRhdGVHcm91cElkIiwib1Byb3BlcnR5QmluZGluZyIsImJpbmRQcm9wZXJ0eSIsImdldEJvdW5kQ29udGV4dCIsInJlcXVlc3RWYWx1ZSIsInRoZW4iLCJzZXRWYWx1ZSIsImF0dGFjaEV2ZW50T25jZSIsIkNoYW5nZSIsImNhdGNoIiwiZXJyIiwiVW5kbyIsImRpc2NhcmREcmFmdCIsImRyYWZ0Q29udGV4dCIsIl9nZXREcmFmdENvbnRleHQiLCJyZXF1ZXN0UHJvcGVydHkiLCJkcmFmdCIsImRlbGV0ZURyYWZ0IiwiRGlzY2FyZCIsInJlcGxhY2UiLCJhY3RpdmVDb250ZXh0IiwiY29udGV4dCIsInJlcXVlc3RDYW5vbmljYWxQYXRoIiwiZGVsZXRlIiwiRGVsZXRlIiwib01lc3NhZ2UiLCJjbGllbnRBY3Rpb24iLCJKb2luIiwiSm9pbkVjaG8iXSwibWFwcGluZ3MiOiI7QUFBQTtBQUNBO0FBQ0E7Ozs7Ozs7OztBQVVBLE1BQU1BLGdCQUFnQixHQUFHO0FBQ3hCQyxJQUFBQSxvQkFBb0IsRUFBRUMsU0FERTtBQUV4QkMsSUFBQUEsU0FBUyxFQUFFLEVBRmE7QUFHeEJDLElBQUFBLE9BQU8sRUFBRUYsU0FIZTtBQUl4QkcsSUFBQUEsbUJBQW1CLEVBQUUsRUFKRztBQUt4QkMsSUFBQUEsY0FBYyxFQUFFSixTQUxROztBQU94QjtBQUNEO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNDSyxJQUFBQSxVQUFVLEVBQUUsVUFBVUMsUUFBVixFQUE2QkMsTUFBN0IsRUFBNkNDLFFBQTdDLEVBQStEO0FBQzFFLFVBQU1DLGdCQUF3QixHQUFHSCxRQUFRLENBQUNJLFFBQVQsR0FBb0JDLFlBQXBCLEdBQW1DQyxTQUFuQyxDQUE2QyxtREFBN0MsQ0FBakM7O0FBRUEsVUFBSSxDQUFDSCxnQkFBTCxFQUF1QjtBQUN0QkksUUFBQUEsR0FBRyxDQUFDQyxLQUFKLENBQVUseUNBQVY7QUFDQTtBQUNBOztBQUVELFVBQU1DLFVBQWtCLEdBQUdULFFBQVEsQ0FBQ1UsV0FBVCxDQUFxQixtQ0FBckIsQ0FBM0I7QUFDQSxXQUFLWixjQUFMLEdBQXNCLElBQUlhLFNBQUosQ0FBYyxFQUFkLENBQXRCO0FBRUFDLE1BQUFBLHVCQUF1QixDQUN0QjtBQUNDQyxRQUFBQSxFQUFFLEVBQUVaLE1BREw7QUFFQ2EsUUFBQUEsSUFBSSxFQUFFWixRQUZQO0FBR0NhLFFBQUFBLFdBQVcsRUFBRWI7QUFIZCxPQURzQixFQU10QkMs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b2cgZnJvbSBcInNhcC9iYXNlL0xvZ1wiO1xuaW1wb3J0IHtcblx0YnJvYWRjYXN0Q29sbGFib3JhdGlvbk1lc3NhZ2UsXG5cdGVuZENvbGxhYm9yYXRpb24sXG5cdGluaXRpYWxpemVDb2xsYWJvcmF0aW9uXG59IGZyb20gXCJzYXAvZmUvY29yZS9jb250cm9sbGVyZXh0ZW5zaW9ucy9jb2xsYWJvcmF0aW9uL0FjdGl2aXR5QmFzZVwiO1xuaW1wb3J0IHsgQWN0aXZpdHksIE1lc3NhZ2UgfSBmcm9tIFwic2FwL2ZlL2NvcmUvY29udHJvbGxlcmV4dGVuc2lvbnMvY29sbGFib3JhdGlvbi9Db2xsYWJvcmF0aW9uQ29tbW9uXCI7XG5pbXBvcnQgZHJhZnQgZnJvbSBcInNhcC9mZS9jb3JlL2NvbnRyb2xsZXJleHRlbnNpb25zL2VkaXRGbG93L2RyYWZ0XCI7XG5pbXBvcnQgQ29udGV4dCBmcm9tIFwic2FwL3VpL21vZGVsL0NvbnRleHRcIjtcbmltcG9ydCBKU09OTW9kZWwgZnJvbSBcInNhcC91aS9tb2RlbC9qc29uL0pTT05Nb2RlbFwiO1xuaW1wb3J0IE9EYXRhTW9kZWwgZnJvbSBcInNhcC91aS9tb2RlbC9vZGF0YS92NC9PRGF0YU1vZGVsXCI7XG5cbmNvbnN0IENvbGxhYm9yYXRpb25BUEkgPSB7XG5cdF9sYXN0UmVjZWl2ZWRNZXNzYWdlOiB1bmRlZmluZWQgYXMgTWVzc2FnZSB8IHVuZGVmaW5lZCxcblx0X3Jvb3RQYXRoOiBcIlwiLFxuXHRfb01vZGVsOiB1bmRlZmluZWQgYXMgT0RhdGFNb2RlbCB8IHVuZGVmaW5lZCxcblx0X2xvY2tlZFByb3BlcnR5UGF0aDogXCJcIixcblx0X2ludGVybmFsTW9kZWw6IHVuZGVmaW5lZCBhcyBKU09OTW9kZWwgfCB1bmRlZmluZWQsXG5cblx0LyoqXG5cdCAqIE9wZW4gYW4gZXhpc3RpbmcgY29sbGFib3JhdGl2ZSBkcmFmdCB3aXRoIGEgbmV3IHVzZXIsIGFuZCBjcmVhdGVzIGEgJ2dob3N0IGNsaWVudCcgZm9yIHRoaXMgdXNlci5cblx0ICpcblx0ICogQHBhcmFtIG9Db250ZXh0IFRoZSBjb250ZXh0IG9mIHRoZSBjb2xsYWJvcmF0aXZlIGRyYWZ0XG5cdCAqIEBwYXJhbSB1c2VySUQgVGhlIElEIG9mIHRoZSB1c2VyXG5cdCAqIEBwYXJhbSB1c2VyTmFtZSBUaGUgbmFtZSBvZiB0aGUgdXNlclxuXHQgKi9cblx0ZW50ZXJEcmFmdDogZnVuY3Rpb24gKG9Db250ZXh0OiBDb250ZXh0LCB1c2VySUQ6IHN0cmluZywgdXNlck5hbWU6IHN0cmluZykge1xuXHRcdGNvbnN0IHdlYlNvY2tldEJhc2VVUkw6IHN0cmluZyA9IG9Db250ZXh0LmdldE1vZGVsKCkuZ2V0TWV0YU1vZGVsKCkuZ2V0T2JqZWN0KFwiL0Bjb20uc2FwLnZvY2FidWxhcmllcy5Db21tb24udjEuV2ViU29ja2V0QmFzZVVSTFwiKTtcblxuXHRcdGlmICghd2ViU29ja2V0QmFzZVVSTCkge1xuXHRcdFx0TG9nLmVycm9yKFwiQ2Fubm90IGZpbmQgV2ViU29ja2V0QmFzZVVSTCBhbm5vdGF0aW9uXCIpO1xuXHRcdFx0cmV0dXJuO1xuXHRcdH1cblxuXHRcdGNvbnN0IHNEcmFmdFVVSUQ6IHN0cmluZyA9IG9Db250ZXh0LmdldFByb3BlcnR5KFwiRHJhZnRBZG1pbmlzdHJhdGl2ZURhdGEvRHJhZnRVVUlEXCIpO1xuXHRcdHRoaXMuX2ludGVybmFsTW9kZWwgPSBuZXcgSlNPTk1vZGVsKHt9KTtcblxuXHRcdGluaXRpYWxpemVDb2xsYWJvcmF0aW9uKFxuXHRcdFx0e1xuXHRcdFx0XHRpZDogdXNlcklELFxuXHRcdFx0XHRuYW1lOiB1c2VyTmFtZSxcblx0XHRcdFx0aW5pdGlhbE5hbWU6IHVzZXJOYW1lXG5cdFx0XHR9LFxuXHRcdFx0d2ViU29ja2V0QmFzZVVSTCxcblx0XHRcdHNEcmFmdFVVSUQsXG5cdFx0XHR0aGlzLl9pbnRlcm5hbE1vZGVsLFxuXHRcdFx0dGhpcy5fb25NZXNzYWdlUmVjZWl2ZWQuYmluZCh0aGlzKSxcblx0XHRcdHRydWVcblx0XHQpO1xuXG5cdFx0dGhpcy5fcm9vdFBhdGggPSBvQ29udGV4dC5nZXRQYXRoKCk7XG5cdFx0dGhpcy5fb01vZGVsID0gb0NvbnRleHQuZ2V0TW9kZWwoKSBhcyBPRGF0YU1vZGVsO1xuXHR9LFxuXG5cdC8qKlxuXHQgKiBDaGVja3MgaWYgdGhlIGdob3N0IGNsaWVudCBoYXMgcmV2aWV2ZWQgYSBnaXZlbiBtZXNzYWdlLlxuXHQgKlxuXHQgKiBAcGFyYW0gbWVzc2FnZSBUaGUgbWVzc2FnZSBjb250ZW50IHRvIGJlIGxvb2tlZCBmb3Jcblx0ICogQHJldHVybnMgVHJ1ZSBpZiB0aGUgbGFzdCByZWNpZXZlZCBtZXNzYWdlIG1hdGNoZXMgdGhlIGNvbnRlbnRcblx0ICovXG5cdGNoZWNrUmVjZWl2ZWQ6IGZ1bmN0aW9uIChtZXNzYWdlOiBQYXJ0aWFsPE1lc3NhZ2U+KTogYm9vbGVhbiB7XG5cdFx0aWYgKCF0aGlzLl9sYXN0UmVjZWl2ZWRNZXNzYWdlKSB7XG5cdFx0XHRyZXR1cm4gZmFsc2U7XG5cdFx0fVxuXG5cdFx0Y29uc3QgZm91bmQgPVxuXHRcdFx0KCFtZXNzYWdlLnVzZXJJRCB8fCBtZXNzYWdlLnVzZXJJRCA9PT0gdGhpcy5fbGFzdFJlY2VpdmVkTWVzc2FnZS51c2VySUQpICYmXG5cdFx0XHQoIW1lc3NhZ2UudXNlckFjdGlvbiB8fCBtZXNzYWdlLnVzZXJBY3Rpb24gPT09IHRoaXMuX2xhc3RSZWNlaXZlZE1lc3NhZ2UudXNlckFjdGlvbikgJiZcblx0XHRcdCghbWVzc2FnZS5jbGllbnRDb250ZW50IHx8IG1lc3NhZ2UuY2xpZW50Q29udGVudCA9PT0gdGhpcy5fbGFzdFJlY2VpdmVkTWVzc2FnZS5jbGllbnRDb250ZW50KTtcblxuXHRcdHRoaXMuX2xhc3RSZWNlaXZlZE1lc3NhZ2UgPSB1bmRlZmluZWQ7IC8vIHJlc2V0IGhpc3RvcnkgdG8gYXZvaWQgZmluZGluZyB0aGUgc2FtZSBtZXNzYWdlIHR3aWNlXG5cblx0XHRyZXR1cm4gZm91bmQ7XG5cdH0sXG5cblx0LyoqXG5cdCAqIENsb3NlcyB0aGUgZ2hvc3QgY2xpZW50IGFuZCByZW1vdmVzIHRoZSB1c2VyIGZyb20gdGhlIGNvbGxhYm9yYXRpdmUgZHJhZnQuXG5cdCAqL1xuXHRsZWF2ZURyYWZ0OiBmdW5jdGlvbiAoKSB7XG5cdFx0aWYgKHRoaXMuX2ludGVybmFsTW9kZWwpIHtcblx0XHRcdGVuZENvbGxhYm9yYXRpb24odGhpcy5faW50ZXJuYWxNb2RlbCk7XG5cdFx0XHR0aGlzLl9pbnRlcm5hbE1vZGVsLmRlc3Ryb3koKTtcblx0XHRcdHRoaXMuX2ludGVybmFsTW9kZWwgPSB1bmRlZmluZWQ7XG5cdFx0fVxuXHR9LFxuXG5cdC8qKlxuXHQgKiBTaW11bGF0ZXMgdGhhdCB0aGUgdXNlciBzdGFydHMgdHlwaW5nIGluIGFuIGlucHV0IChsaXZlIGNoYW5nZSkuXG5cdCAqXG5cdCAqIEBwYXJhbSBzUHJvcGVydHlQYXRoIFRoZSBwYXRoIG9mIHRoZSBwcm9wZXJ0eSBiZWluZyBtb2RpZmllZFxuXHQgKi9cblx0c3RhcnRMaXZlQ2hhbmdlOiBmdW5jdGlvbiAoc1Byb3BlcnR5UGF0aDogc3RyaW5nKSB7XG5cdFx0aWYgKHRoaXMuX2ludGVybmFsTW9kZWwpIHtcblx0XHRcdGlmICh0aGlzLl9sb2NrZWRQcm9wZXJ0eVBhdGgpIHtcblx0XHRcdFx0Ly8gVW5sb2NrIHByZXZpb3VzIHByb3BlcnR5IHBhdGhcblx0XHRcdFx0dGhpcy51bmRvQ2hhbmdlKCk7XG5cdFx0XHR9XG5cdFx0XHR0aGlzLl9sb2NrZWRQcm9wZXJ0eVBhdGggPSBzUHJvcGVydHlQYXRoO1xuXHRcdFx0YnJvYWRjYXN0Q29sbGFib3JhdGlvbk1lc3NhZ2UoQWN0aXZpdHkuTGl2ZUNoYW5nZSwgdGhpcy5fcm9vdFBhdGggKyBcIi9cIiArIHNQcm9wZXJ0eVBhdGgsIHRoaXMuX2ludGVybmFsTW9kZWwpO1xuXHRcdH1cblx0fSxcblxuXHQvKipcblx0ICogU2ltdWxhdGVzIHRoYXQgdGhlIHVzZXIgaGFzIG1vZGlmaWVkIGEgcHJvcGVydHkuXG5cdCAqXG5cdCAqIEBwYXJhbSBzUHJvcGVydHlQYXRoIFRoZSBwYXRoIG9mIHRoZSBwcm9wZXJ0eSBiZWluZyBtb2RpZmllZFxuXHQgKiBAcGFyYW0gdmFsdWUgVGhlIG5ldyB2YWx1ZSBvZiB0aGUgcHJvcGVydHkgYmVpbmcgbW9kaWZpZWRcblx0ICovXG5cdHVwZGF0ZVByb3BlcnR5VmFsdWU6IGZ1bmN0aW9uIChzUHJvcGVydHlQYXRoOiBzdHJpbmcsIHZhbHVlOiBhbnkpIHtcblx0XHRpZiAodGhpcy5faW50ZXJuYWxNb2RlbCkge1xuXHRcdFx0aWYgKHRoaXMuX2xvY2tlZFByb3BlcnR5UGF0aCAhPT0gc1Byb3BlcnR5UGF0aCkge1xuXHRcdFx0XHR0aGlzLnN0YXJ0TGl2ZUNoYW5nZShzUHJvcGVydHlQYXRoKTtcblx0XHRcdH1cblxuXHRcdFx0Y29uc3Qgb0NvbnRleHRCaW5kaW5nID0gdGhpcy5fb01vZGVsIS5iaW5kQ29udGV4dCh0aGlzLl9yb290UGF0aCwgdW5kZWZpbmVkLCB7XG5cdFx0XHRcdCQkcGF0Y2hXaXRob3V0U2lkZUVmZmVjdHM6IHRydWUsXG5cdFx0XHRcdCQkZ3JvdXBJZDogXCIkYXV0b1wiLFxuXHRcdFx0XHQkJHVwZGF0ZUdyb3VwSWQ6IFwiJGF1dG9cIlxuXHRcdFx0fSk7XG5cblx0XHRcdGNvbnN0IG9Qcm9wZXJ0eUJpbmRpbmcgPSB0aGlzLl9vTW9kZWwhLmJpbmRQcm9wZXJ0eShzUHJvcGVydHlQYXRoLCBvQ29udGV4dEJpbmRpbmcuZ2V0Qm91bmRDb250ZXh0KCkpO1xuXG5cdFx0XHRvUHJvcGVydHlCaW5kaW5nXG5cdFx0XHRcdC5yZXF1ZXN0VmFsdWUoKVxuXHRcdFx0XHQudGhlbigoKSA9PiB7XG5cdFx0XHRcdFx0b1Byb3BlcnR5QmluZGluZy5zZXRWYWx1ZSh2YWx1ZSk7XG5cdFx0XHRcdFx0b0NvbnRleHRCaW5kaW5nLmF0dGFjaEV2ZW50T25jZShcInBhdGNoQ29tcGxldGVkXCIsICgpID0+IHtcblx0XHRcdFx0XHRcdGJyb2FkY2FzdENvbGxhYm9yYXRpb25NZXNzYWdlKEFjdGl2aXR5LkNoYW5nZSwgdGhpcy5fcm9vdFBhdGggKyBcIi9cIiArIHNQcm9wZXJ0eVBhdGgsIHRoaXMuX2ludGVybmFsTW9kZWwhKTtcblx0XHRcdFx0XHRcdHRoaXMuX2xvY2tlZFByb3BlcnR5UGF0aCA9IFwiXCI7XG5cdFx0XHRcdFx0fSk7XG5cdFx0XHRcdH0pXG5cdFx0XHRcdC5jYXRjaChmdW5jdGlvbiAoZXJyKSB7XG5cdFx0XHRcdFx0TG9nLmVycm9yKGVycik7XG5cdFx0XHRcdH0pO1xuXHRcdH1cblx0fSxcblxuXHQvKipcblx0ICogU2ltdWxhdGVzIHRoYXQgdGhlIHVzZXIgZGlkIGFuICd1bmRvJyAodG8gYmUgY2FsbGVkIGFmdGVyIHN0YXJ0TGl2ZUNoYW5nZSkuXG5cdCAqL1xuXHR1bmRvQ2hhbmdlOiBmdW5jdGlvbiAoKSB7XG5cdFx0aWYgKHRoaXMuX2xvY2tlZFByb3BlcnR5UGF0aCkge1xuXHRcdFx0YnJvYWRjYXN0Q29sbGFib3JhdGlvbk1lc3NhZ2UoQWN0aXZpdHkuVW5kbywgdGhpcy5fcm9vdFBhdGggKyBcIi9cIiArIHRoaXMuX2xvY2tlZFByb3BlcnR5UGF0aCwgdGhpcy5faW50ZXJuYWxNb2RlbCEpO1xuXHRcdFx0dGhpcy5fbG9ja2VkUHJvcGVydHlQYXRoID0gXCJcIjtcblx0XHR9XG5cdH0sXG5cblx0LyoqXG5cdCAqIFNpbXVsYXRlcyB0aGF0IHRoZSB1c2VyIGhhcyBkaXNjYXJkZWQgdGhlIGRyYWZ0LlxuXHQgKi9cblx0ZGlzY2FyZERyYWZ0OiBmdW5jdGlvbiAoKSB7XG5cdFx0aWYgKHRoaXMuX2ludGVybmFsTW9kZWwpIHtcblx0XHRcdGNvbnN0IGRyYWZ0Q29udGV4dCA9IHRoaXMuX2dldERyYWZ0Q29udGV4dCgpO1xuXG5cdFx0XHRkcmFmdENvbnRleHRcblx0XHRcdFx0LnJlcXVlc3RQcm9wZXJ0eShcIklzQWN0aXZlRW50aXR5XCIpXG5cdFx0XHRcdC50aGVuKCgpID0+IHtcblx0XHRcdFx0XHRkcmFmdC5kZWxldGVEcmFmdChkcmFmdENvbnRleHQpO1xuXHRcdFx0XHR9KVxuXHRcdFx0XHQudGhlbigoKSA9PiB7XG5cdFx0XHRcdFx0YnJvYWRjYXN0Q29sbGFib3JhdGlvbk1lc3NhZ2UoXG5cdFx0XHRcdFx0XHRBY3Rpdml0eS5EaXNjYXJkLFxuXHRcdFx0XHRcdFx0dGhpcy5fcm9vdFBhdGgucmVwbGFjZShcIklzQWN0aXZlRW50aXR5PWZhbHNlXCIsIFwiSXNBY3RpdmVFbnRpdHk9dHJ1ZVwiKSxcblx0XHRcdFx0XHRcdHRoaXMuX2ludGVybmFsTW9kZWwhXG5cdFx0XHRcdFx0KTtcblx0XHRcdFx0XHR0aGlzLl9pbnRlcm5hbE1vZGVsIS5kZXN0cm95KCk7XG5cdFx0XHRcdFx0dGhpcy5faW50ZXJuYWxNb2RlbCA9IHVuZGVmaW5lZDtcblx0XHRcdFx0fSlcblx0XHRcdFx0LmNhdGNoKGZ1bmN0aW9uIChlcnI6IGFueSkge1xuXHRcdFx0XHRcdExvZy5lcnJvcihlcnIpO1xuXHRcdFx0XHR9KTtcblx0XHR9XG5cdH0sXG5cblx0LyoqXG5cdCAqIFNpbXVsYXRlcyB0aGF0IHRoZSB1c2VyIGhhcyBkZWxldGVkIHRoZSBkcmFmdC5cblx0ICovXG5cdGRlbGV0ZURyYWZ0OiBmdW5jdGlvbiAoKSB7XG5cdFx0aWYgKHRoaXMuX2ludGVybmFsTW9kZWwpIHtcblx0XHRcdGNvbnN0IGRyYWZ0Q29udGV4dCA9IHRoaXMuX2dldERyYWZ0Q29udGV4dCgpO1xuXHRcdFx0bGV0IGFjdGl2ZUNvbnRleHQ6IENvbnRleHQ7XG5cblx0XHRcdGRyYWZ0Q29udGV4dFxuXHRcdFx0XHQucmVxdWVzdFByb3BlcnR5KFwiSXNBY3RpdmVFbnRpdHlcIilcblx0XHRcdFx0LnRoZW4oKCkgPT4ge1xuXHRcdFx0XHRcdHJldHVybiBkcmFmdENvbnRleHRcblx0XHRcdFx0XHRcdC5nZXRNb2RlbCgpXG5cdFx0XHRcdFx0XHQuYmluZENvbnRleHQodGhpcy5fcm9vdFBhdGggKyBcIi9TaWJsaW5nRW50aXR5XCIpXG5cdFx0XHRcdFx0XHQuZ2V0Qm91bmRDb250ZXh0KCk7XG5cdFx0XHRcdH0pXG5cdFx0XHRcdC50aGVuKChjb250ZXh0OiBhbnkpID0+IHtcblx0XHRcdFx0XHRhY3RpdmVDb250ZXh0ID0gY29udGV4dDtcblx0XHRcdFx0XHRyZXR1cm4gY29udGV4dC5yZXF1ZXN0Q2Fub25pY2FsUGF0aCgpO1xuXHRcdFx0XHR9KVxuXHRcdFx0XHQudGhlbigoKSA9PiB7XG5cdFx0XHRcdFx0cmV0dXJuIGRyYWZ0LmRlbGV0ZURyYWZ0KGRyYWZ0Q29udGV4dCk7XG5cdFx0XHRcdH0pXG5cdFx0XHRcdC50aGVuKCgpID0+IHtcblx0XHRcdFx0XHQoYWN0aXZlQ29udGV4dCBhcyBhbnkpLmRlbGV0ZSgpO1xuXHRcdFx0XHRcdGJyb2FkY2FzdENvbGxhYm9yYXRpb25NZXNzYWdlKEFjdGl2aXR5LkRlbGV0ZSwgdGhpcy5fcm9vdFBhdGgsIHRoaXMuX2ludGVybmFsTW9kZWwhKTtcblx0XHRcdFx0XHR0aGlzLl9pbnRlcm5hbE1vZGVsIS5kZXN0cm95KCk7XG5cdFx0XHRcdFx0dGhpcy5faW50ZXJuYWxNb2RlbCA9IHVuZGVmaW5lZDtcblx0XHRcdFx0fSlcblx0XHRcdFx0LmNhdGNoKGZ1bmN0aW9uIChlcnI6IGFueSkge1xuXHRcdFx0XHRcdExvZy5lcnJvcihlcnIpO1xuXHRcdFx0XHR9KTtcblx0XHR9XG5cdH0sXG5cblx0Ly8gLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy9cblx0Ly8gUHJpdmF0ZSBtZXRob2RzXG5cblx0X2dldERyYWZ0Q29udGV4dDogZnVuY3Rpb24gKCk6IGFueSB7XG5cdFx0cmV0dXJuIHRoaXMuX29Nb2RlbCEuYmluZENvbnRleHQodGhpcy5fcm9vdFBhdGgsIHVuZGVmaW5lZCwge1xuXHRcdFx0JCRwYXRjaFdpdGhvdXRTaWRlRWZmZWN0czogdHJ1ZSxcblx0XHRcdCQkZ3JvdXBJZDogXCIkYXV0b1wiLFxuXHRcdFx0JCR1cGRhdGVHcm91cElkOiBcIiRhdXRvXCJcblx0XHR9KS5nZXRCb3VuZENvbnRleHQoKTtcblx0fSxcblxuXHQvKipcblx0ICogQ2FsbGJhY2sgb2YgdGhlIGdob3N0IGNsaWVudCB3aGVuIHJlY2VpdmluZyBhIG1lc3NhZ2Ugb24gdGhlIHdlYiBzb2NrZXQuXG5cdCAqXG5cdCAqIEBwYXJhbSBvTWVzc2FnZSBUaGUgbWVzc2FnZVxuXHQgKi9cblx0X29uTWVzc2FnZVJlY2VpdmVkOiBmdW5jdGlvbiAob01lc3NhZ2U6IE1lc3NhZ2UpIHtcblx0XHRvTWVzc2FnZS51c2VyQWN0aW9uID0gb01lc3NhZ2UudXNlckFjdGlvbiB8fCBvTWVzc2FnZS5jbGllbnRBY3Rpb247XG5cdFx0dGhpcy5fbGFzdFJlY2VpdmVkTWVzc2FnZSA9IG9NZXNzYWdlO1xuXG5cdFx0aWYgKG9NZXNzYWdlLnVzZXJBY3Rpb24gPT09IEFjdGl2aXR5LkpvaW4pIHtcblx0XHRcdGJyb2FkY2FzdENvbGxhYm9yYXRpb25NZXNzYWdlKFxuXHRcdFx0XHRBY3Rpdml0eS5Kb2luRWNobyxcblx0XHRcdFx0dGhpcy5fbG9ja2VkUHJvcGVydHlQYXRoID8gdGhpcy5fcm9vdFBhdGggKyBcIi9cIiArIHRoaXMuX2xvY2tlZFByb3BlcnR5UGF0aCA6IHVuZGVmaW5lZCxcblx0XHRcdFx0dGhpcy5faW50ZXJuYWxNb2RlbCFcblx0XHRcdCk7XG5cdFx0fVxuXHR9XG59O1xuXG5leHBvcnQgZGVmYXVsdCBDb2xsYWJvcmF0aW9uQVBJO1xuIl19
@@ -0,0 +1,240 @@
1
+ import Log from "sap/base/Log";
2
+ import {
3
+ broadcastCollaborationMessage,
4
+ endCollaboration,
5
+ initializeCollaboration
6
+ } from "sap/fe/core/controllerextensions/collaboration/ActivityBase";
7
+ import { Activity, Message } from "sap/fe/core/controllerextensions/collaboration/CollaborationCommon";
8
+ import draft from "sap/fe/core/controllerextensions/editFlow/draft";
9
+ import Context from "sap/ui/model/Context";
10
+ import JSONModel from "sap/ui/model/json/JSONModel";
11
+ import ODataModel from "sap/ui/model/odata/v4/ODataModel";
12
+
13
+ const CollaborationAPI = {
14
+ _lastReceivedMessage: undefined as Message | undefined,
15
+ _rootPath: "",
16
+ _oModel: undefined as ODataModel | undefined,
17
+ _lockedPropertyPath: "",
18
+ _internalModel: undefined as JSONModel | undefined,
19
+
20
+ /**
21
+ * Open an existing collaborative draft with a new user, and creates a 'ghost client' for this user.
22
+ *
23
+ * @param oContext The context of the collaborative draft
24
+ * @param userID The ID of the user
25
+ * @param userName The name of the user
26
+ */
27
+ enterDraft: function (oContext: Context, userID: string, userName: string) {
28
+ const webSocketBaseURL: string = oContext.getModel().getMetaModel().getObject("/@com.sap.vocabularies.Common.v1.WebSocketBaseURL");
29
+
30
+ if (!webSocketBaseURL) {
31
+ Log.error("Cannot find WebSocketBaseURL annotation");
32
+ return;
33
+ }
34
+
35
+ const sDraftUUID: string = oContext.getProperty("DraftAdministrativeData/DraftUUID");
36
+ this._internalModel = new JSONModel({});
37
+
38
+ initializeCollaboration(
39
+ {
40
+ id: userID,
41
+ name: userName,
42
+ initialName: userName
43
+ },
44
+ webSocketBaseURL,
45
+ sDraftUUID,
46
+ this._internalModel,
47
+ this._onMessageReceived.bind(this),
48
+ true
49
+ );
50
+
51
+ this._rootPath = oContext.getPath();
52
+ this._oModel = oContext.getModel() as ODataModel;
53
+ },
54
+
55
+ /**
56
+ * Checks if the ghost client has revieved a given message.
57
+ *
58
+ * @param message The message content to be looked for
59
+ * @returns True if the last recieved message matches the content
60
+ */
61
+ checkReceived: function (message: Partial<Message>): boolean {
62
+ if (!this._lastReceivedMessage) {
63
+ return false;
64
+ }
65
+
66
+ const found =
67
+ (!message.userID || message.userID === this._lastReceivedMessage.userID) &&
68
+ (!message.userAction || message.userAction === this._lastReceivedMessage.userAction) &&
69
+ (!message.clientContent || message.clientContent === this._lastReceivedMessage.clientContent);
70
+
71
+ this._lastReceivedMessage = undefined; // reset history to avoid finding the same message twice
72
+
73
+ return found;
74
+ },
75
+
76
+ /**
77
+ * Closes the ghost client and removes the user from the collaborative draft.
78
+ */
79
+ leaveDraft: function () {
80
+ if (this._internalModel) {
81
+ endCollaboration(this._internalModel);
82
+ this._internalModel.destroy();
83
+ this._internalModel = undefined;
84
+ }
85
+ },
86
+
87
+ /**
88
+ * Simulates that the user starts typing in an input (live change).
89
+ *
90
+ * @param sPropertyPath The path of the property being modified
91
+ */
92
+ startLiveChange: function (sPropertyPath: string) {
93
+ if (this._internalModel) {
94
+ if (this._lockedPropertyPath) {
95
+ // Unlock previous property path
96
+ this.undoChange();
97
+ }
98
+ this._lockedPropertyPath = sPropertyPath;
99
+ broadcastCollaborationMessage(Activity.LiveChange, this._rootPath + "/" + sPropertyPath, this._internalModel);
100
+ }
101
+ },
102
+
103
+ /**
104
+ * Simulates that the user has modified a property.
105
+ *
106
+ * @param sPropertyPath The path of the property being modified
107
+ * @param value The new value of the property being modified
108
+ */
109
+ updatePropertyValue: function (sPropertyPath: string, value: any) {
110
+ if (this._internalModel) {
111
+ if (this._lockedPropertyPath !== sPropertyPath) {
112
+ this.startLiveChange(sPropertyPath);
113
+ }
114
+
115
+ const oContextBinding = this._oModel!.bindContext(this._rootPath, undefined, {
116
+ $$patchWithoutSideEffects: true,
117
+ $$groupId: "$auto",
118
+ $$updateGroupId: "$auto"
119
+ });
120
+
121
+ const oPropertyBinding = this._oModel!.bindProperty(sPropertyPath, oContextBinding.getBoundContext());
122
+
123
+ oPropertyBinding
124
+ .requestValue()
125
+ .then(() => {
126
+ oPropertyBinding.setValue(value);
127
+ oContextBinding.attachEventOnce("patchCompleted", () => {
128
+ broadcastCollaborationMessage(Activity.Change, this._rootPath + "/" + sPropertyPath, this._internalModel!);
129
+ this._lockedPropertyPath = "";
130
+ });
131
+ })
132
+ .catch(function (err) {
133
+ Log.error(err);
134
+ });
135
+ }
136
+ },
137
+
138
+ /**
139
+ * Simulates that the user did an 'undo' (to be called after startLiveChange).
140
+ */
141
+ undoChange: function () {
142
+ if (this._lockedPropertyPath) {
143
+ broadcastCollaborationMessage(Activity.Undo, this._rootPath + "/" + this._lockedPropertyPath, this._internalModel!);
144
+ this._lockedPropertyPath = "";
145
+ }
146
+ },
147
+
148
+ /**
149
+ * Simulates that the user has discarded the draft.
150
+ */
151
+ discardDraft: function () {
152
+ if (this._internalModel) {
153
+ const draftContext = this._getDraftContext();
154
+
155
+ draftContext
156
+ .requestProperty("IsActiveEntity")
157
+ .then(() => {
158
+ draft.deleteDraft(draftContext);
159
+ })
160
+ .then(() => {
161
+ broadcastCollaborationMessage(
162
+ Activity.Discard,
163
+ this._rootPath.replace("IsActiveEntity=false", "IsActiveEntity=true"),
164
+ this._internalModel!
165
+ );
166
+ this._internalModel!.destroy();
167
+ this._internalModel = undefined;
168
+ })
169
+ .catch(function (err: any) {
170
+ Log.error(err);
171
+ });
172
+ }
173
+ },
174
+
175
+ /**
176
+ * Simulates that the user has deleted the draft.
177
+ */
178
+ deleteDraft: function () {
179
+ if (this._internalModel) {
180
+ const draftContext = this._getDraftContext();
181
+ let activeContext: Context;
182
+
183
+ draftContext
184
+ .requestProperty("IsActiveEntity")
185
+ .then(() => {
186
+ return draftContext
187
+ .getModel()
188
+ .bindContext(this._rootPath + "/SiblingEntity")
189
+ .getBoundContext();
190
+ })
191
+ .then((context: any) => {
192
+ activeContext = context;
193
+ return context.requestCanonicalPath();
194
+ })
195
+ .then(() => {
196
+ return draft.deleteDraft(draftContext);
197
+ })
198
+ .then(() => {
199
+ (activeContext as any).delete();
200
+ broadcastCollaborationMessage(Activity.Delete, this._rootPath, this._internalModel!);
201
+ this._internalModel!.destroy();
202
+ this._internalModel = undefined;
203
+ })
204
+ .catch(function (err: any) {
205
+ Log.error(err);
206
+ });
207
+ }
208
+ },
209
+
210
+ // /////////////////////////////
211
+ // Private methods
212
+
213
+ _getDraftContext: function (): any {
214
+ return this._oModel!.bindContext(this._rootPath, undefined, {
215
+ $$patchWithoutSideEffects: true,
216
+ $$groupId: "$auto",
217
+ $$updateGroupId: "$auto"
218
+ }).getBoundContext();
219
+ },
220
+
221
+ /**
222
+ * Callback of the ghost client when receiving a message on the web socket.
223
+ *
224
+ * @param oMessage The message
225
+ */
226
+ _onMessageReceived: function (oMessage: Message) {
227
+ oMessage.userAction = oMessage.userAction || oMessage.clientAction;
228
+ this._lastReceivedMessage = oMessage;
229
+
230
+ if (oMessage.userAction === Activity.Join) {
231
+ broadcastCollaborationMessage(
232
+ Activity.JoinEcho,
233
+ this._lockedPropertyPath ? this._rootPath + "/" + this._lockedPropertyPath : undefined,
234
+ this._internalModel!
235
+ );
236
+ }
237
+ }
238
+ };
239
+
240
+ export default CollaborationAPI;
@@ -100,11 +100,13 @@ sap.ui.define(["./DialogAPI", "sap/fe/test/Utils", "sap/ui/test/OpaBuilder"], fu
100
100
  /**
101
101
  * Changes the content of a field in an action parameter dialog.
102
102
  *
103
+ * Deprecated: Please use dialog action iChangeDialogField.
104
+ *
103
105
  * @param {sap.fe.test.api.ActionDialogFieldIdentifier} vFieldIdentifier The identifier of the field
104
106
  * @param {string} [vValue] The new target value.
105
107
  * @param {boolean} [bClearFirst] Set to <code>true</code> to clear previously set filters, otherwise all previously set values will be kept
106
108
  * @returns {object} The result of the {@link sap.ui.test.Opa5#waitFor} function, that can be used for chaining statements
107
- * @public
109
+ * @ui5-restricted
108
110
  */
109
111
  DialogActions.prototype.iChangeActionParameterDialogField = function(vFieldIdentifier, vValue, bClearFirst) {
110
112
  return this.iChangeDialogField(vFieldIdentifier, vValue, bClearFirst);
@@ -3,72 +3,86 @@
3
3
  (c) Copyright 2009-2021 SAP SE. All rights reserved
4
4
 
5
5
  */
6
- sap.ui.define(["./DialogActions", "sap/fe/test/Utils", "sap/ui/test/OpaBuilder"], function(DialogActions, Utils, OpaBuilder) {
7
- "use strict";
6
+ sap.ui.define(
7
+ ["./DialogActions", "sap/fe/test/Utils", "sap/ui/test/OpaBuilder", "sap/fe/test/builder/FEBuilder"],
8
+ function (DialogActions, Utils, OpaBuilder, FEBuilder) {
9
+ "use strict";
8
10
 
9
- /**
10
- * Constructs a new DialogMessageActions instance.
11
- *
12
- * @param {sap.fe.test.builder.DialogBuilder} oDialogBuilder The {@link sap.fe.test.builder.DialogBuilder} instance used to interact with the UI
13
- * @param {string} [vDialogDescription] Description (optional) of the dialog to be used for logging messages
14
- * @returns {sap.fe.test.api.DialogMessageActions} The new instance
15
- * @extends sap.fe.test.api.DialogActions
16
- * @alias sap.fe.test.api.DialogMessageActions
17
- * @class
18
- * @hideconstructor
19
- * @public
20
- */
21
- var DialogMessageActions = function(oDialogBuilder, vDialogDescription) {
22
- return DialogActions.call(this, oDialogBuilder, vDialogDescription, 0);
23
- };
24
- DialogMessageActions.prototype = Object.create(DialogActions.prototype);
25
- DialogMessageActions.prototype.constructor = DialogMessageActions;
26
- DialogMessageActions.prototype.isAction = true;
11
+ /**
12
+ * Constructs a new DialogMessageActions instance.
13
+ *
14
+ * @param {sap.fe.test.builder.DialogBuilder} oDialogBuilder The {@link sap.fe.test.builder.DialogBuilder} instance used to interact with the UI
15
+ * @param {string} [vDialogDescription] Description (optional) of the dialog to be used for logging messages
16
+ * @returns {sap.fe.test.api.DialogMessageActions} The new instance
17
+ * @extends sap.fe.test.api.DialogActions
18
+ * @alias sap.fe.test.api.DialogMessageActions
19
+ * @class
20
+ * @hideconstructor
21
+ * @public
22
+ */
23
+ var DialogMessageActions = function (oDialogBuilder, vDialogDescription) {
24
+ return DialogActions.call(this, oDialogBuilder, vDialogDescription, 0);
25
+ };
26
+ DialogMessageActions.prototype = Object.create(DialogActions.prototype);
27
+ DialogMessageActions.prototype.constructor = DialogMessageActions;
28
+ DialogMessageActions.prototype.isAction = true;
27
29
 
28
- /**
29
- * Executes the <code>Back</code> action on the message dialog.
30
- *
31
- * @returns {object} The result of the {@link sap.ui.test.Opa5#waitFor} function, to be used for chained statements
32
- * @public
33
- */
34
- DialogMessageActions.prototype.iExecuteBack = function() {
35
- return this.prepareResult(
36
- this.getBuilder()
37
- .doPressHeaderButton(OpaBuilder.Matchers.properties({ icon: "sap-icon://nav-back" }))
38
- .description(Utils.formatMessage("Pressing back button on dialog '{0}'", this.getIdentifier()))
39
- .execute()
40
- );
41
- };
30
+ /**
31
+ * Executes the <code>Back</code> action on the message dialog.
32
+ *
33
+ * @returns {object} The result of the {@link sap.ui.test.Opa5#waitFor} function, to be used for chained statements
34
+ * @public
35
+ */
36
+ DialogMessageActions.prototype.iExecuteBack = function () {
37
+ return this.prepareResult(
38
+ this.getBuilder()
39
+ .doPressHeaderButton(OpaBuilder.Matchers.properties({ icon: "sap-icon://nav-back" }))
40
+ .description(Utils.formatMessage("Pressing back button on dialog '{0}'", this.getIdentifier()))
41
+ .execute()
42
+ );
43
+ };
42
44
 
43
- /**
44
- * Executes the <code>Refresh</code> action on the message dialog.
45
- *
46
- * @returns {object} The result of the {@link sap.ui.test.Opa5#waitFor} function, to be used for chained statements
47
- * @public
48
- */
49
- DialogMessageActions.prototype.iExecuteRefresh = function() {
50
- return this.prepareResult(
51
- this.getBuilder()
52
- .doPressFooterButton(OpaBuilder.Matchers.resourceBundle("text", "sap.fe.core", "C_COMMON_SAPFE_REFRESH"))
53
- .description(Utils.formatMessage("Pressing refresh button on dialog '{0}'", this.getIdentifier()))
54
- .execute()
55
- );
56
- };
45
+ /**
46
+ * Executes the <code>Refresh</code> action on the message dialog.
47
+ *
48
+ * @returns {object} The result of the {@link sap.ui.test.Opa5#waitFor} function, to be used for chained statements
49
+ * @public
50
+ */
51
+ DialogMessageActions.prototype.iExecuteRefresh = function () {
52
+ return this.prepareResult(
53
+ this.getBuilder()
54
+ .doPressFooterButton(OpaBuilder.Matchers.resourceBundle("text", "sap.fe.core", "C_COMMON_SAPFE_REFRESH"))
55
+ .description(Utils.formatMessage("Pressing refresh button on dialog '{0}'", this.getIdentifier()))
56
+ .execute()
57
+ );
58
+ };
57
59
 
58
- /**
59
- * Confirms the draft message with Discard Draft when navigating back to the list-report.
60
- *
61
- * @returns {object} The result of the {@link sap.ui.test.Opa5#waitFor} function, to be used for chained statements
62
- * @public
63
- */
64
- DialogMessageActions.prototype.iConfirmDiscardDraft = function() {
65
- return this.prepareResult(
66
- this.getBuilder()
67
- .doPressFooterButton(this._getConfirmDiscardDraftButtonMatcher())
68
- .description(Utils.formatMessage("Confirming dialog '{0}' with Discard Draft", this.getIdentifier()))
69
- .execute()
70
- );
71
- };
60
+ /**
61
+ * Selects the specified entry in the draft data loss popup
62
+ *
63
+ * @returns {object} The result of the {@link sap.ui.test.Opa5#waitFor} function, to be used for chained statements
64
+ * @public
65
+ */
66
+ DialogMessageActions.prototype.iSelectDraftDataLossOption = function (optionKey) {
67
+ // The logic below uses customData for identifying the entry in list in the dialog
68
+ // which needs to be pressed
69
+ // The dialog's XML fragment for the custom data and the used keys
70
+ return this.prepareResult(
71
+ FEBuilder.create()
72
+ .hasType("sap.m.List")
73
+ .isDialogElement(true)
74
+ .has(OpaBuilder.Matchers.aggregation("items"))
75
+ .has(function (oItem) {
76
+ return oItem.find(function (element) {
77
+ return element.data("itemKey") === optionKey;
78
+ });
79
+ })
80
+ .doPress()
81
+ .description("Selecting option with key {0} in draft data loss popup")
82
+ .execute()
83
+ );
84
+ };
72
85
 
73
- return DialogMessageActions;
74
- });
86
+ return DialogMessageActions;
87
+ }
88
+ );