@sapui5/sap.fe.test 1.102.2 → 1.104.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 (65) hide show
  1. package/package.json +3 -4
  2. package/src/sap/fe/test/.library +1 -1
  3. package/src/sap/fe/test/BaseActions.js +103 -97
  4. package/src/sap/fe/test/BaseArrangements.js +4 -15
  5. package/src/sap/fe/test/BaseAssertions.js +39 -46
  6. package/src/sap/fe/test/CollaborationClient.js +5 -5
  7. package/src/sap/fe/test/CollaborationClient.ts +5 -5
  8. package/src/sap/fe/test/FeMocks.js +1 -1
  9. package/src/sap/fe/test/FeMocks.ts +4 -3
  10. package/src/sap/fe/test/Flexibility.js +115 -162
  11. package/src/sap/fe/test/JestTemplatingHelper.js +99 -7
  12. package/src/sap/fe/test/JestTemplatingHelper.ts +91 -14
  13. package/src/sap/fe/test/JourneyRunner.js +31 -37
  14. package/src/sap/fe/test/ListReport.js +9 -13
  15. package/src/sap/fe/test/ObjectPage.js +35 -33
  16. package/src/sap/fe/test/Shell.js +0 -6
  17. package/src/sap/fe/test/TemplatePage.js +0 -2
  18. package/src/sap/fe/test/TemplatingTestUtils.js +20 -20
  19. package/src/sap/fe/test/UI5MockHelper.js +16 -10
  20. package/src/sap/fe/test/UI5MockHelper.ts +28 -18
  21. package/src/sap/fe/test/Utils.js +19 -20
  22. package/src/sap/fe/test/api/BaseAPI.js +11 -26
  23. package/src/sap/fe/test/api/ChartActions.js +116 -119
  24. package/src/sap/fe/test/api/ChartAssertions.js +18 -22
  25. package/src/sap/fe/test/api/CollaborationAPI.js +6 -6
  26. package/src/sap/fe/test/api/CollaborationAPI.ts +9 -11
  27. package/src/sap/fe/test/api/DialogAPI.js +11 -13
  28. package/src/sap/fe/test/api/DialogActions.js +8 -8
  29. package/src/sap/fe/test/api/DialogAssertions.js +9 -10
  30. package/src/sap/fe/test/api/DialogHelper.js +63 -0
  31. package/src/sap/fe/test/api/DialogMessageActions.js +2 -1
  32. package/src/sap/fe/test/api/DialogValueHelpActions.js +17 -39
  33. package/src/sap/fe/test/api/DialogValueHelpAssertions.js +47 -19
  34. package/src/sap/fe/test/api/FilterBarAPI.js +11 -12
  35. package/src/sap/fe/test/api/FilterBarActions.js +26 -40
  36. package/src/sap/fe/test/api/FilterBarAssertions.js +28 -47
  37. package/src/sap/fe/test/api/FooterActionsBase.js +3 -4
  38. package/src/sap/fe/test/api/FooterActionsOP.js +6 -10
  39. package/src/sap/fe/test/api/FooterAssertionsBase.js +5 -10
  40. package/src/sap/fe/test/api/FooterAssertionsOP.js +8 -13
  41. package/src/sap/fe/test/api/FormAPI.js +5 -6
  42. package/src/sap/fe/test/api/FormActions.js +11 -21
  43. package/src/sap/fe/test/api/FormAssertions.js +14 -20
  44. package/src/sap/fe/test/api/HeaderAPI.js +12 -27
  45. package/src/sap/fe/test/api/HeaderActions.js +10 -12
  46. package/src/sap/fe/test/api/HeaderActionsLR.js +76 -82
  47. package/src/sap/fe/test/api/HeaderAssertions.js +2 -20
  48. package/src/sap/fe/test/api/HeaderAssertionsLR.js +5 -8
  49. package/src/sap/fe/test/api/HeaderLR.js +7 -19
  50. package/src/sap/fe/test/api/KPICardAssertions.js +18 -37
  51. package/src/sap/fe/test/api/TableAPI.js +3 -13
  52. package/src/sap/fe/test/api/TableActions.js +2 -34
  53. package/src/sap/fe/test/api/TableAssertions.js +1 -24
  54. package/src/sap/fe/test/builder/FEBuilder.js +71 -75
  55. package/src/sap/fe/test/builder/MacroFieldBuilder.js +4 -3
  56. package/src/sap/fe/test/builder/MdcFieldBuilder.js +33 -29
  57. package/src/sap/fe/test/builder/MdcFilterBarBuilder.js +102 -104
  58. package/src/sap/fe/test/builder/MdcFilterFieldBuilder.js +9 -7
  59. package/src/sap/fe/test/builder/MdcTableBuilder.js +8 -4
  60. package/src/sap/fe/test/builder/VMBuilder.js +39 -55
  61. package/src/sap/fe/test/internal/ConsoleErrorChecker.js +241 -0
  62. package/src/sap/fe/test/internal/ConsoleErrorChecker.ts +206 -0
  63. package/src/sap/fe/test/internal/FEArrangements.js +28 -8
  64. package/src/sap/fe/test/library.js +3 -2
  65. package/src/sap/fe/test/library.ts +1 -0
@@ -0,0 +1,241 @@
1
+ /*!
2
+ * SAP UI development toolkit for HTML5 (SAPUI5)
3
+ * (c) Copyright 2009-2021 SAP SE. All rights reserved
4
+ */
5
+ sap.ui.define([], function () {
6
+ "use strict";
7
+
8
+ function wrapPatterns(pattern) {
9
+ if (pattern instanceof RegExp) {
10
+ return function (message) {
11
+ return message.match(pattern) !== null;
12
+ };
13
+ } else {
14
+ return function (message) {
15
+ return message.includes(pattern);
16
+ };
17
+ }
18
+ }
19
+ /**
20
+ * List of error message patterns that are always accepted.
21
+ */
22
+
23
+
24
+ var GLOBALLY_ACCEPTED_ERRORS = ["failed to load JavaScript resource: sap/esh/search/ui/i18n.js" // shell
25
+ ].map(wrapPatterns);
26
+
27
+ var ConsoleErrorChecker = /*#__PURE__*/function () {
28
+ function ConsoleErrorChecker(window) {
29
+ var _this = this;
30
+
31
+ this.matchers = [];
32
+ this.messages = [];
33
+ this.observer = new MutationObserver(function (mutations) {
34
+ var opaFrame = mutations.reduce(function (iFrame, mutation) {
35
+ if (iFrame !== null) {
36
+ return iFrame;
37
+ }
38
+
39
+ for (var _i = 0, _Array$from = Array.from(mutation.addedNodes); _i < _Array$from.length; _i++) {
40
+ var node = _Array$from[_i];
41
+
42
+ if (node instanceof Element) {
43
+ var element = node.querySelector("#OpaFrame");
44
+
45
+ if (element instanceof HTMLIFrameElement && element.contentWindow) {
46
+ return element;
47
+ }
48
+ }
49
+ }
50
+
51
+ return iFrame;
52
+ }, null);
53
+
54
+ if (opaFrame && opaFrame.contentWindow) {
55
+ _this.prepareWindow(opaFrame.contentWindow);
56
+ }
57
+ });
58
+ QUnit.moduleStart(function () {
59
+ _this.observer.observe(window.document.body, {
60
+ childList: true
61
+ });
62
+ });
63
+ QUnit.moduleDone(function () {
64
+ _this.observer.disconnect();
65
+ });
66
+ QUnit.testStart(function () {
67
+ _this.reset();
68
+ });
69
+ QUnit.log(function () {
70
+ _this.handleFailedMessages();
71
+ });
72
+ this.karma = window.__karma__; // either go for Karma config option "ui5.config.strictConsoleErrors" or use URL query parameter "strict"
73
+
74
+ var search = new URLSearchParams(window.location.search);
75
+ var urlParam = search.get("strictConsoleErrors");
76
+
77
+ if (urlParam !== null) {
78
+ this.isStrict = urlParam === "true";
79
+ } else {
80
+ var _this$karma$config$ui, _this$karma, _this$karma$config$ui2;
81
+
82
+ this.isStrict = (_this$karma$config$ui = (_this$karma = this.karma) === null || _this$karma === void 0 ? void 0 : (_this$karma$config$ui2 = _this$karma.config.ui5) === null || _this$karma$config$ui2 === void 0 ? void 0 : _this$karma$config$ui2.config.strictconsoleerrors) !== null && _this$karma$config$ui !== void 0 ? _this$karma$config$ui : false;
83
+ }
84
+
85
+ this.reset();
86
+ }
87
+
88
+ var _proto = ConsoleErrorChecker.prototype;
89
+
90
+ _proto.handleFailedMessages = function handleFailedMessages() {
91
+ var failedMessages = this.messages;
92
+ this.messages = [];
93
+
94
+ if (failedMessages.length > 0) {
95
+ QUnit.assert.pushResult({
96
+ result: false,
97
+ source: "FE Console Log Check",
98
+ message: "There were ".concat(failedMessages.length, " unexpected console errors"),
99
+ actual: failedMessages,
100
+ expected: []
101
+ });
102
+ }
103
+ };
104
+
105
+ _proto.reset = function reset() {
106
+ this.messages = []; // this sets the default to apply if no allowed patterns are set via setAcceptedErrorPatterns().
107
+
108
+ if (this.isStrict) {
109
+ this.matchers = GLOBALLY_ACCEPTED_ERRORS;
110
+ } else {
111
+ this.matchers = [function () {
112
+ return true;
113
+ }];
114
+ }
115
+ };
116
+
117
+ _proto.setAcceptedErrorPatterns = function setAcceptedErrorPatterns(patterns) {
118
+ if (!patterns || patterns.length === 0) {
119
+ this.matchers = GLOBALLY_ACCEPTED_ERRORS;
120
+ } else {
121
+ this.matchers = patterns.map(wrapPatterns).concat(GLOBALLY_ACCEPTED_ERRORS);
122
+ }
123
+ };
124
+
125
+ _proto.checkAndLog = function checkAndLog(type) {
126
+ for (var _len = arguments.length, data = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
127
+ data[_key - 1] = arguments[_key];
128
+ }
129
+
130
+ // only check the error messages
131
+ if (type === "error") {
132
+ var messageText = data[0];
133
+ var isAllowed = this.matchers.some(function (matcher) {
134
+ return matcher(messageText);
135
+ });
136
+
137
+ if (!isAllowed) {
138
+ this.messages.push(messageText);
139
+ }
140
+ }
141
+
142
+ if (this.karma) {
143
+ // wrap the data to facilitate parsing in the backend
144
+ var wrappedData = data.map(function (d) {
145
+ return [d];
146
+ });
147
+ this.karma.log(type, wrappedData);
148
+ }
149
+ };
150
+
151
+ _proto.prepareWindow = function prepareWindow(window) {
152
+ var _this2 = this;
153
+
154
+ var console = window.console; // capture console.log(), console.debug(), etc.
155
+
156
+ var patchConsoleMethod = function (method) {
157
+ var fnOriginal = console[method];
158
+
159
+ console[method] = function () {
160
+ for (var _len2 = arguments.length, data = new Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
161
+ data[_key2] = arguments[_key2];
162
+ }
163
+
164
+ _this2.checkAndLog.apply(_this2, [method].concat(data));
165
+
166
+ return fnOriginal.apply(console, data);
167
+ };
168
+ };
169
+
170
+ patchConsoleMethod("log");
171
+ patchConsoleMethod("debug");
172
+ patchConsoleMethod("info");
173
+ patchConsoleMethod("warn");
174
+ patchConsoleMethod("error"); // capture console.assert()
175
+ // see https://console.spec.whatwg.org/#assert
176
+
177
+ console.assert = function () {
178
+ var condition = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;
179
+
180
+ if (condition) {
181
+ return;
182
+ }
183
+
184
+ var message = "Assertion failed";
185
+
186
+ for (var _len3 = arguments.length, data = new Array(_len3 > 1 ? _len3 - 1 : 0), _key3 = 1; _key3 < _len3; _key3++) {
187
+ data[_key3 - 1] = arguments[_key3];
188
+ }
189
+
190
+ if (data.length === 0) {
191
+ data.push(message);
192
+ } else {
193
+ var first = data[0];
194
+
195
+ if (typeof first !== "string") {
196
+ data.unshift(message);
197
+ } else {
198
+ first = "".concat(message, ": ").concat(first);
199
+ data[0] = first;
200
+ }
201
+ }
202
+
203
+ console.error.apply(console, data);
204
+ }; // capture errors
205
+
206
+
207
+ function onPromiseRejection(event) {
208
+ var _event$reason;
209
+
210
+ var message = "UNHANDLED PROMISE REJECTION: ".concat(event.reason);
211
+ this.checkAndLog("error", message, (_event$reason = event.reason) === null || _event$reason === void 0 ? void 0 : _event$reason.stack);
212
+ }
213
+
214
+ function onError(event) {
215
+ var message = event.message;
216
+ this.checkAndLog("error", message, event.filename);
217
+ }
218
+
219
+ window.addEventListener("error", onError.bind(this), {
220
+ passive: true
221
+ });
222
+ window.addEventListener("unhandledrejection", onPromiseRejection.bind(this), {
223
+ passive: true
224
+ });
225
+ };
226
+
227
+ ConsoleErrorChecker.getInstance = function getInstance(window) {
228
+ // the global instance is needed to support multiple tests in a row (in Karma)
229
+ if (!window.sapFEConsoleErrorChecker) {
230
+ window.sapFEConsoleErrorChecker = new ConsoleErrorChecker(window);
231
+ }
232
+
233
+ return window.sapFEConsoleErrorChecker;
234
+ };
235
+
236
+ return ConsoleErrorChecker;
237
+ }();
238
+
239
+ return ConsoleErrorChecker.getInstance(window);
240
+ }, false);
241
+ //# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIkNvbnNvbGVFcnJvckNoZWNrZXIudHMiXSwibmFtZXMiOlsid3JhcFBhdHRlcm5zIiwicGF0dGVybiIsIlJlZ0V4cCIsIm1lc3NhZ2UiLCJtYXRjaCIsImluY2x1ZGVzIiwiR0xPQkFMTFlfQUNDRVBURURfRVJST1JTIiwibWFwIiwiQ29uc29sZUVycm9yQ2hlY2tlciIsIndpbmRvdyIsIm1hdGNoZXJzIiwibWVzc2FnZXMiLCJvYnNlcnZlciIsIk11dGF0aW9uT2JzZXJ2ZXIiLCJtdXRhdGlvbnMiLCJvcGFGcmFtZSIsInJlZHVjZSIsImlGcmFtZSIsIm11dGF0aW9uIiwiQXJyYXkiLCJmcm9tIiwiYWRkZWROb2RlcyIsIm5vZGUiLCJFbGVtZW50IiwiZWxlbWVudCIsInF1ZXJ5U2VsZWN0b3IiLCJIVE1MSUZyYW1lRWxlbWVudCIsImNvbnRlbnRXaW5kb3ciLCJwcmVwYXJlV2luZG93IiwiUVVuaXQiLCJtb2R1bGVTdGFydCIsIm9ic2VydmUiLCJkb2N1bWVudCIsImJvZHkiLCJjaGlsZExpc3QiLCJtb2R1bGVEb25lIiwiZGlzY29ubmVjdCIsInRlc3RTdGFydCIsInJlc2V0IiwibG9nIiwiaGFuZGxlRmFpbGVkTWVzc2FnZXMiLCJrYXJtYSIsIl9fa2FybWFfXyIsInNlYXJjaCIsIlVSTFNlYXJjaFBhcmFtcyIsImxvY2F0aW9uIiwidXJsUGFyYW0iLCJnZXQiLCJpc1N0cmljdCIsImNvbmZpZyIsInVpNSIsInN0cmljdGNvbnNvbGVlcnJvcnMiLCJmYWlsZWRNZXNzYWdlcyIsImxlbmd0aCIsImFzc2VydCIsInB1c2hSZXN1bHQiLCJyZXN1bHQiLCJzb3VyY2UiLCJhY3R1YWwiLCJleHBlY3RlZCIsInNldEFjY2VwdGVkRXJyb3JQYXR0ZXJucyIsInBhdHRlcm5zIiwiY29uY2F0IiwiY2hlY2tBbmRMb2ciLCJ0eXBlIiwiZGF0YSIsIm1lc3NhZ2VUZXh0IiwiaXNBbGxvd2VkIiwic29tZSIsIm1hdGNoZXIiLCJwdXNoIiwid3JhcHBlZERhdGEiLCJkIiwiY29uc29sZSIsInBhdGNoQ29uc29sZU1ldGhvZCIsIm1ldGhvZCIsImZuT3JpZ2luYWwiLCJhcHBseSIsImNvbmRpdGlvbiIsImZpcnN0IiwidW5zaGlmdCIsImVycm9yIiwib25Qcm9taXNlUmVqZWN0aW9uIiwiZXZlbnQiLCJyZWFzb24iLCJzdGFjayIsIm9uRXJyb3IiLCJmaWxlbmFtZSIsImFkZEV2ZW50TGlzdGVuZXIiLCJiaW5kIiwicGFzc2l2ZSIsImdldEluc3RhbmNlIiwic2FwRkVDb25zb2xlRXJyb3JDaGVja2VyIl0sIm1hcHBpbmdzIjoiO0FBQUE7QUFDQTtBQUNBOzs7O0FBWUEsV0FBU0EsWUFBVCxDQUFzQkMsT0FBdEIsRUFBd0U7QUFDdkUsUUFBSUEsT0FBTyxZQUFZQyxNQUF2QixFQUErQjtBQUM5QixhQUFPLFVBQUNDLE9BQUQ7QUFBQSxlQUFhQSxPQUFPLENBQUNDLEtBQVIsQ0FBY0gsT0FBZCxNQUEyQixJQUF4QztBQUFBLE9BQVA7QUFDQSxLQUZELE1BRU87QUFDTixhQUFPLFVBQUNFLE9BQUQ7QUFBQSxlQUFhQSxPQUFPLENBQUNFLFFBQVIsQ0FBaUJKLE9BQWpCLENBQWI7QUFBQSxPQUFQO0FBQ0E7QUFDRDtBQUVEO0FBQ0E7QUFDQTs7O0FBQ0EsTUFBTUssd0JBQXdCLEdBQUcsQ0FDaEMsK0RBRGdDLENBQ2dDO0FBRGhDLElBRS9CQyxHQUYrQixDQUUzQlAsWUFGMkIsQ0FBakM7O01BSU1RLG1CO0FBNkJMLGlDQUFZQyxNQUFaLEVBQW9EO0FBQUE7O0FBQUEsV0E1QjVDQyxRQTRCNEMsR0E1QlAsRUE0Qk87QUFBQSxXQTNCNUNDLFFBMkI0QyxHQTNCdkIsRUEyQnVCO0FBQUEsV0F2Qm5DQyxRQXVCbUMsR0F2QnhCLElBQUlDLGdCQUFKLENBQXFCLFVBQUNDLFNBQUQsRUFBZTtBQUMvRCxZQUFNQyxRQUFRLEdBQUdELFNBQVMsQ0FBQ0UsTUFBVixDQUFpQixVQUFDQyxNQUFELEVBQW1DQyxRQUFuQyxFQUFnRTtBQUNqRyxjQUFJRCxNQUFNLEtBQUssSUFBZixFQUFx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QW1CbEYsTUFBbkIsRUFBNkc7QUFDNUc7QUFDQSxVQUFJLENBQUNBLE1BQU0sQ0FBQ21GLHdCQUFaLEVBQXNDO0FBQ3JDbkYsUUFBQUEsTUFBTSxDQUFDbUYsd0JBQVAsR0FBa0MsSUFBSXBGLG1CQUFKLENBQXdCQyxNQUF4QixDQUFsQztBQUNBOztBQUNELGFBQU9BLE1BQU0sQ0FBQ21GLHdCQUFkO0FBQ0EsSzs7Ozs7U0FHYXBGLG1CQUFtQixDQUFDbUYsV0FBcEIsQ0FBZ0NsRixNQUFoQyxDIiwic291cmNlUm9vdCI6Ii4iLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgdHlwZSB7IEJyb3dzZXJDb25zb2xlTG9nT3B0aW9ucyB9IGZyb20gXCJrYXJtYVwiO1xuXG50eXBlIE1lc3NhZ2VNYXRjaGVyRnVuY3Rpb24gPSAobWVzc2FnZTogc3RyaW5nKSA9PiBib29sZWFuO1xudHlwZSBLYXJtYSA9IHtcblx0bG9nOiAobGV2ZWw6IEJyb3dzZXJDb25zb2xlTG9nT3B0aW9uc1tcImxldmVsXCJdLCAuLi5kYXRhOiBhbnlbXSkgPT4gdm9pZDtcblx0Y29uZmlnOiB7XG5cdFx0dWk1Pzoge1xuXHRcdFx0Y29uZmlnOiB7XG5cdFx0XHRcdHN0cmljdGNvbnNvbGVlcnJvcnM/OiBib29sZWFuOyAvLyBLYXJtYSBvcHRpb25zIGFyZSBhbGwgbG93ZXJjYXNlIGF0IHJ1bnRpbWUhXG5cdFx0XHR9O1xuXHRcdH07XG5cdH07XG59O1xuXG5mdW5jdGlvbiB3cmFwUGF0dGVybnMocGF0dGVybjogUmVnRXhwIHwgc3RyaW5nKTogTWVzc2FnZU1hdGNoZXJGdW5jdGlvbiB7XG5cdGlmIChwYXR0ZXJuIGluc3RhbmNlb2YgUmVnRXhwKSB7XG5cdFx0cmV0dXJuIChtZXNzYWdlKSA9PiBtZXNzYWdlLm1hdGNoKHBhdHRlcm4pICE9PSBudWxsO1xuXHR9IGVsc2Uge1xuXHRcdHJldHVybiAobWVzc2FnZSkgPT4gbWVzc2FnZS5pbmNsdWRlcyhwYXR0ZXJuKTtcblx0fVxufVxuXG4vKipcbiAqIExpc3Qgb2YgZXJyb3IgbWVzc2FnZSBwYXR0ZXJucyB0aGF0IGFyZSBhbHdheXMgYWNjZXB0ZWQuXG4gKi9cbmNvbnN0IEdMT0JBTExZX0FDQ0VQVEVEX0VSUk9SUyA9IFtcblx0XCJmYWlsZWQgdG8gbG9hZCBKYXZhU2NyaXB0IHJlc291cmNlOiBzYXAvZXNoL3NlYXJjaC91aS9pMThuLmpzXCIgLy8gc2hlbGxcbl0ubWFwKHdyYXBQYXR0ZXJucyk7XG5cbmNsYXNzIENvbnNvbGVFcnJvckNoZWNrZXIge1xuXHRwcml2YXRlIG1hdGNoZXJzOiBNZXNzYWdlTWF0Y2hlckZ1bmN0aW9uW10gPSBbXTtcblx0cHJpdmF0ZSBtZXNzYWdlczogc3RyaW5nW10gPSBbXTtcblx0cHJpdmF0ZSByZWFkb25seSBrYXJtYTogS2FybWEgfCB1bmRlZmluZWQ7XG5cdHByaXZhdGUgcmVhZG9ubHkgaXNTdHJpY3Q6IGJvb2xlYW47XG5cblx0cHJpdmF0ZSByZWFkb25seSBvYnNlcnZlciA9IG5ldyBNdXRhdGlvbk9ic2VydmVyKChtdXRhdGlvbnMpID0+IHtcblx0XHRjb25zdCBvcGFGcmFtZSA9IG11dGF0aW9ucy5yZWR1Y2UoKGlGcmFtZTogSFRNTElGcmFtZUVsZW1lbnQgfCBudWxsLCBtdXRhdGlvbjogTXV0YXRpb25SZWNvcmQpID0+IHtcblx0XHRcdGlmIChpRnJhbWUgIT09IG51bGwpIHtcblx0XHRcdFx0cmV0dXJuIGlGcmFtZTtcblx0XHRcdH1cblxuXHRcdFx0Zm9yIChjb25zdCBub2RlIG9mIEFycmF5LmZyb20obXV0YXRpb24uYWRkZWROb2RlcykpIHtcblx0XHRcdFx0aWYgKG5vZGUgaW5zdGFuY2VvZiBFbGVtZW50KSB7XG5cdFx0XHRcdFx0Y29uc3QgZWxlbWVudCA9IG5vZGUucXVlcnlTZWxlY3RvcihcIiNPcGFGcmFtZVwiKTtcblx0XHRcdFx0XHRpZiAoZWxlbWVudCBpbnN0YW5jZW9mIEhUTUxJRnJhbWVFbGVtZW50ICYmIGVsZW1lbnQuY29udGVudFdpbmRvdykge1xuXHRcdFx0XHRcdFx0cmV0dXJuIGVsZW1lbnQ7XG5cdFx0XHRcdFx0fVxuXHRcdFx0XHR9XG5cdFx0XHR9XG5cblx0XHRcdHJldHVybiBpRnJhbWU7XG5cdFx0fSwgbnVsbCk7XG5cblx0XHRpZiAob3BhRnJhbWUgJiYgb3BhRnJhbWUuY29udGVudFdpbmRvdykge1xuXHRcdFx0dGhpcy5wcmVwYXJlV2luZG93KG9wYUZyYW1lLmNvbnRlbnRXaW5kb3cpO1xuXHRcdH1cblx0fSk7XG5cblx0Y29uc3RydWN0b3Iod2luZG93OiBXaW5kb3cgJiB7IF9fa2FybWFfXz86IEthcm1hIH0pIHtcblx0XHRRVW5pdC5tb2R1bGVTdGFydCgoKSA9PiB7XG5cdFx0XHR0aGlzLm9ic2VydmVyLm9ic2VydmUod2luZG93LmRvY3VtZW50LmJvZHksIHsgY2hpbGRMaXN0OiB0cnVlIH0pO1xuXHRcdH0pO1xuXG5cdFx0UVVuaXQubW9kdWxlRG9uZSgoKSA9PiB7XG5cdFx0XHR0aGlzLm9ic2VydmVyLmRpc2Nvbm5lY3QoKTtcblx0XHR9KTtcblxuXHRcdFFVbml0LnRlc3RTdGFydCgoKSA9PiB7XG5cdFx0XHR0aGlzLnJlc2V0KCk7XG5cdFx0fSk7XG5cblx0XHRRVW5pdC5sb2coKCkgPT4ge1xuXHRcdFx0dGhpcy5oYW5kbGVGYWlsZWRNZXNzYWdlcygpO1xuXHRcdH0pO1xuXG5cdFx0dGhpcy5rYXJtYSA9IHdpbmRvdy5fX2thcm1hX187XG5cblx0XHQvLyBlaXRoZXIgZ28gZm9yIEthcm1hIGNvbmZpZyBvcHRpb24gXCJ1aTUuY29uZmlnLnN0cmljdENvbnNvbGVFcnJvcnNcIiBvciB1c2UgVVJMIHF1ZXJ5IHBhcmFtZXRlciBcInN0cmljdFwiXG5cdFx0Y29uc3Qgc2VhcmNoID0gbmV3IFVSTFNlYXJjaFBhcmFtcyh3aW5kb3cubG9jYXRpb24uc2VhcmNoKTtcblx0XHRjb25zdCB1cmxQYXJhbSA9IHNlYXJjaC5nZXQoXCJzdHJpY3RDb25zb2xlRXJyb3JzXCIpO1xuXHRcdGlmICh1cmxQYXJhbSAhPT0gbnVsbCkge1xuXHRcdFx0dGhpcy5pc1N0cmljdCA9IHVybFBhcmFtID09PSBcInRydWVcIjtcblx0XHR9IGVsc2Uge1xuXHRcdFx0dGhpcy5pc1N0cmljdCA9IHRoaXMua2FybWE/LmNvbmZpZy51aTU/LmNvbmZpZy5zdHJpY3Rjb25zb2xlZXJyb3JzID8/IGZhbHNlO1xuXHRcdH1cblxuXHRcdHRoaXMucmVzZXQoKTtcblx0fVxuXG5cdHByaXZhdGUgaGFuZGxlRmFpbGVkTWVzc2FnZXMoKSB7XG5cdFx0Y29uc3QgZmFpbGVkTWVzc2FnZXMgPSB0aGlzLm1lc3NhZ2VzO1xuXHRcdHRoaXMubWVzc2FnZXMgPSBbXTtcblxuXHRcdGlmIChmYWlsZWRNZXNzYWdlcy5sZW5ndGggPiAwKSB7XG5cdFx0XHRRVW5pdC5hc3NlcnQucHVzaFJlc3VsdCh7XG5cdFx0XHRcdHJlc3VsdDogZmFsc2UsXG5cdFx0XHRcdHNvdXJjZTogXCJGRSBDb25zb2xlIExvZyBDaGVja1wiLFxuXHRcdFx0XHRtZXNzYWdlOiBgVGhlcmUgd2VyZSAke2ZhaWxlZE1lc3NhZ2VzLmxlbmd0aH0gdW5leHBlY3RlZCBjb25zb2xlIGVycm9yc2AsXG5cdFx0XHRcdGFjdHVhbDogZmFpbGVkTWVzc2FnZXMsXG5cdFx0XHRcdGV4cGVjdGVkOiBbXVxuXHRcdFx0fSk7XG5cdFx0fVxuXHR9XG5cblx0cHJpdmF0ZSByZXNldCgpIHtcblx0XHR0aGlzLm1lc3NhZ2VzID0gW107XG5cblx0XHQvLyB0aGlzIHNldHMgdGhlIGRlZmF1bHQgdG8gYXBwbHkgaWYgbm8gYWxsb3dlZCBwYXR0ZXJucyBhcmUgc2V0IHZpYSBzZXRBY2NlcHRlZEVycm9yUGF0dGVybnMoKS5cblx0XHRpZiAodGhpcy5pc1N0cmljdCkge1xuXHRcdFx0dGhpcy5tYXRjaGVycyA9IEdMT0JBTExZX0FDQ0VQVEVEX0VSUk9SUztcblx0XHR9IGVsc2Uge1xuXHRcdFx0dGhpcy5tYXRjaGVycyA9IFsoKSA9PiB0cnVlXTtcblx0XHR9XG5cdH1cblxuXHRzZXRBY2NlcHRlZEVycm9yUGF0dGVybnMocGF0dGVybnM/OiAoUmVnRXhwIHwgc3RyaW5nKVtdKSB7XG5cdFx0aWYgKCFwYXR0ZXJucyB8fCBwYXR0ZXJucy5sZW5ndGggPT09IDApIHtcblx0XHRcdHRoaXMubWF0Y2hlcnMgPSBHTE9CQUxMWV9BQ0NFUFRFRF9FUlJPUlM7XG5cdFx0fSBlbHNlIHtcblx0XHRcdHRoaXMubWF0Y2hlcnMgPSBwYXR0ZXJucy5tYXAod3JhcFBhdHRlcm5zKS5jb25jYXQoR0xPQkFMTFlfQUNDRVBURURfRVJST1JTKTtcblx0XHR9XG5cdH1cblxuXHRwcml2YXRlIGNoZWNrQW5kTG9nKHR5cGU6IEJyb3dzZXJDb25zb2xlTG9nT3B0aW9uc1tcImxldmVsXCJdLCAuLi5kYXRhOiBhbnlbXSkge1xuXHRcdC8vIG9ubHkgY2hlY2sgdGhlIGVycm9yIG1lc3NhZ2VzXG5cdFx0aWYgKHR5cGUgPT09IFwiZXJyb3JcIikge1xuXHRcdFx0Y29uc3QgbWVzc2FnZVRleHQgPSBkYXRhWzBdO1xuXHRcdFx0Y29uc3QgaXNBbGxvd2VkID0gdGhpcy5tYXRjaGVycy5zb21lKChtYXRjaGVyKSA9PiBtYXRjaGVyKG1lc3NhZ2VUZXh0KSk7XG5cdFx0XHRpZiAoIWlzQWxsb3dlZCkge1xuXHRcdFx0XHR0aGlzLm1lc3NhZ2VzLnB1c2gobWVzc2FnZVRleHQpO1xuXHRcdFx0fVxuXHRcdH1cblxuXHRcdGlmICh0aGlzLmthcm1hKSB7XG5cdFx0XHQvLyB3cmFwIHRoZSBkYXRhIHRvIGZhY2lsaXRhdGUgcGFyc2luZyBpbiB0aGUgYmFja2VuZFxuXHRcdFx0Y29uc3Qgd3JhcHBlZERhdGEgPSBkYXRhLm1hcCgoZCkgPT4gW2RdKTtcblx0XHRcdHRoaXMua2FybWEubG9nKHR5cGUsIHdyYXBwZWREYXRhKTtcblx0XHR9XG5cdH1cblxuXHRwcml2YXRlIHByZXBhcmVXaW5kb3cod2luZG93OiBXaW5kb3cpIHtcblx0XHRjb25zdCBjb25zb2xlOiBDb25zb2xlID0gKHdpbmRvdyBhcyBhbnkpLmNvbnNvbGU7XG5cblx0XHQvLyBjYXB0dXJlIGNvbnNvbGUubG9nKCksIGNvbnNvbGUuZGVidWcoKSwgZXRjLlxuXHRcdGNvbnN0IHBhdGNoQ29uc29sZU1ldGhvZCA9IChtZXRob2Q6IFwibG9nXCIgfCBcImluZm9cIiB8IFwid2FyblwiIHwgXCJlcnJvclwiIHwgXCJkZWJ1Z1wiKSA9PiB7XG5cdFx0XHRjb25zdCBmbk9yaWdpbmFsID0gY29uc29sZVttZXRob2RdO1xuXHRcdFx0Y29uc29sZVttZXRob2RdID0gKC4uLmRhdGE6IGFueVtdKTogdm9pZCA9PiB7XG5cdFx0XHRcdHRoaXMuY2hlY2tBbmRMb2cobWV0aG9kLCAuLi5kYXRhKTtcblx0XHRcdFx0cmV0dXJuIGZuT3JpZ2luYWwuYXBwbHkoY29uc29sZSwgZGF0YSk7XG5cdFx0XHR9O1xuXHRcdH07XG5cblx0XHRwYXRjaENvbnNvbGVNZXRob2QoXCJsb2dcIik7XG5cdFx0cGF0Y2hDb25zb2xlTWV0aG9kKFwiZGVidWdcIik7XG5cdFx0cGF0Y2hDb25zb2xlTWV0aG9kKFwiaW5mb1wiKTtcblx0XHRwYXRjaENvbnNvbGVNZXRob2QoXCJ3YXJuXCIpO1xuXHRcdHBhdGNoQ29uc29sZU1ldGhvZChcImVycm9yXCIpO1xuXG5cdFx0Ly8gY2FwdHVyZSBjb25zb2xlLmFzc2VydCgpXG5cdFx0Ly8gc2VlIGh0dHBzOi8vY29uc29sZS5zcGVjLndoYXR3Zy5vcmcvI2Fzc2VydFxuXHRcdGNvbnNvbGUuYXNzZXJ0ID0gZnVuY3Rpb24gKGNvbmRpdGlvbiA9IGZhbHNlLCAuLi5kYXRhOiBhbnlbXSkge1xuXHRcdFx0aWYgKGNvbmRpdGlvbikge1xuXHRcdFx0XHRyZXR1cm47XG5cdFx0XHR9XG5cblx0XHRcdGNvbnN0IG1lc3NhZ2UgPSBcIkFzc2VydGlvbiBmYWlsZWRcIjtcblx0XHRcdGlmIChkYXRhLmxlbmd0aCA9PT0gMCkge1xuXHRcdFx0XHRkYXRhLnB1c2gobWVzc2FnZSk7XG5cdFx0XHR9IGVsc2Uge1xuXHRcdFx0XHRsZXQgZmlyc3QgPSBkYXRhWzBdO1xuXHRcdFx0XHRpZiAodHlwZW9mIGZpcnN0ICE9PSBcInN0cmluZ1wiKSB7XG5cdFx0XHRcdFx0ZGF0YS51bnNoaWZ0KG1lc3NhZ2UpO1xuXHRcdFx0XHR9IGVsc2Uge1xuXHRcdFx0XHRcdGZpcnN0ID0gYCR7bWVzc2FnZX06ICR7Zmlyc3R9YDtcblx0XHRcdFx0XHRkYXRhWzBdID0gZmlyc3Q7XG5cdFx0XHRcdH1cblx0XHRcdH1cblxuXHRcdFx0Y29uc29sZS5lcnJvciguLi5kYXRhKTtcblx0XHR9O1xuXG5cdFx0Ly8gY2FwdHVyZSBlcnJvcnNcblx0XHRmdW5jdGlvbiBvblByb21pc2VSZWplY3Rpb24odGhpczogQ29uc29sZUVycm9yQ2hlY2tlciwgZXZlbnQ6IFByb21pc2VSZWplY3Rpb25FdmVudCkge1xuXHRcdFx0Y29uc3QgbWVzc2FnZSA9IGBVTkhBTkRMRUQgUFJPTUlTRSBSRUpFQ1RJT046ICR7ZXZlbnQucmVhc29ufWA7XG5cdFx0XHR0aGlzLmNoZWNrQW5kTG9nKFwiZXJyb3JcIiwgbWVzc2FnZSwgZXZlbnQucmVhc29uPy5zdGFjayk7XG5cdFx0fVxuXG5cdFx0ZnVuY3Rpb24gb25FcnJvcih0aGlzOiBDb25zb2xlRXJyb3JDaGVja2VyLCBldmVudDogRXJyb3JFdmVudCkge1xuXHRcdFx0Y29uc3QgbWVzc2FnZSA9IGV2ZW50Lm1lc3NhZ2U7XG5cdFx0XHR0aGlzLmNoZWNrQW5kTG9nKFwiZXJyb3JcIiwgbWVzc2FnZSwgZXZlbnQuZmlsZW5hbWUpO1xuXHRcdH1cblxuXHRcdHdpbmRvdy5hZGRFdmVudExpc3RlbmVyKFwiZXJyb3JcIiwgb25FcnJvci5iaW5kKHRoaXMpLCB7IHBhc3NpdmU6IHRydWUgfSk7XG5cdFx0d2luZG93LmFkZEV2ZW50TGlzdGVuZXIoXCJ1bmhhbmRsZWRyZWplY3Rpb25cIiwgb25Qcm9taXNlUmVqZWN0aW9uLmJpbmQodGhpcyksIHsgcGFzc2l2ZTogdHJ1ZSB9KTtcblx0fVxuXG5cdHN0YXRpYyBnZXRJbnN0YW5jZSh3aW5kb3c6IFdpbmRvdyAmIHsgc2FwRkVDb25zb2xlRXJyb3JDaGVja2VyPzogQ29uc29sZUVycm9yQ2hlY2tlciB9KTogQ29uc29sZUVycm9yQ2hlY2tlciB7XG5cdFx0Ly8gdGhlIGdsb2JhbCBpbnN0YW5jZSBpcyBuZWVkZWQgdG8gc3VwcG9ydCBtdWx0aXBsZSB0ZXN0cyBpbiBhIHJvdyAoaW4gS2FybWEpXG5cdFx0aWYgKCF3aW5kb3cuc2FwRkVDb25zb2xlRXJyb3JDaGVja2VyKSB7XG5cdFx0XHR3aW5kb3cuc2FwRkVDb25zb2xlRXJyb3JDaGVja2VyID0gbmV3IENvbnNvbGVFcnJvckNoZWNrZXIod2luZG93KTtcblx0XHR9XG5cdFx0cmV0dXJuIHdpbmRvdy5zYXBGRUNvbnNvbGVFcnJvckNoZWNrZXI7XG5cdH1cbn1cblxuZXhwb3J0IGRlZmF1bHQgQ29uc29sZUVycm9yQ2hlY2tlci5nZXRJbnN0YW5jZSh3aW5kb3cpO1xuIl19
@@ -0,0 +1,206 @@
1
+ import type { BrowserConsoleLogOptions } from "karma";
2
+
3
+ type MessageMatcherFunction = (message: string) => boolean;
4
+ type Karma = {
5
+ log: (level: BrowserConsoleLogOptions["level"], ...data: any[]) => void;
6
+ config: {
7
+ ui5?: {
8
+ config: {
9
+ strictconsoleerrors?: boolean; // Karma options are all lowercase at runtime!
10
+ };
11
+ };
12
+ };
13
+ };
14
+
15
+ function wrapPatterns(pattern: RegExp | string): MessageMatcherFunction {
16
+ if (pattern instanceof RegExp) {
17
+ return (message) => message.match(pattern) !== null;
18
+ } else {
19
+ return (message) => message.includes(pattern);
20
+ }
21
+ }
22
+
23
+ /**
24
+ * List of error message patterns that are always accepted.
25
+ */
26
+ const GLOBALLY_ACCEPTED_ERRORS = [
27
+ "failed to load JavaScript resource: sap/esh/search/ui/i18n.js" // shell
28
+ ].map(wrapPatterns);
29
+
30
+ class ConsoleErrorChecker {
31
+ private matchers: MessageMatcherFunction[] = [];
32
+ private messages: string[] = [];
33
+ private readonly karma: Karma | undefined;
34
+ private readonly isStrict: boolean;
35
+
36
+ private readonly observer = new MutationObserver((mutations) => {
37
+ const opaFrame = mutations.reduce((iFrame: HTMLIFrameElement | null, mutation: MutationRecord) => {
38
+ if (iFrame !== null) {
39
+ return iFrame;
40
+ }
41
+
42
+ for (const node of Array.from(mutation.addedNodes)) {
43
+ if (node instanceof Element) {
44
+ const element = node.querySelector("#OpaFrame");
45
+ if (element instanceof HTMLIFrameElement && element.contentWindow) {
46
+ return element;
47
+ }
48
+ }
49
+ }
50
+
51
+ return iFrame;
52
+ }, null);
53
+
54
+ if (opaFrame && opaFrame.contentWindow) {
55
+ this.prepareWindow(opaFrame.contentWindow);
56
+ }
57
+ });
58
+
59
+ constructor(window: Window & { __karma__?: Karma }) {
60
+ QUnit.moduleStart(() => {
61
+ this.observer.observe(window.document.body, { childList: true });
62
+ });
63
+
64
+ QUnit.moduleDone(() => {
65
+ this.observer.disconnect();
66
+ });
67
+
68
+ QUnit.testStart(() => {
69
+ this.reset();
70
+ });
71
+
72
+ QUnit.log(() => {
73
+ this.handleFailedMessages();
74
+ });
75
+
76
+ this.karma = window.__karma__;
77
+
78
+ // either go for Karma config option "ui5.config.strictConsoleErrors" or use URL query parameter "strict"
79
+ const search = new URLSearchParams(window.location.search);
80
+ const urlParam = search.get("strictConsoleErrors");
81
+ if (urlParam !== null) {
82
+ this.isStrict = urlParam === "true";
83
+ } else {
84
+ this.isStrict = this.karma?.config.ui5?.config.strictconsoleerrors ?? false;
85
+ }
86
+
87
+ this.reset();
88
+ }
89
+
90
+ private handleFailedMessages() {
91
+ const failedMessages = this.messages;
92
+ this.messages = [];
93
+
94
+ if (failedMessages.length > 0) {
95
+ QUnit.assert.pushResult({
96
+ result: false,
97
+ source: "FE Console Log Check",
98
+ message: `There were ${failedMessages.length} unexpected console errors`,
99
+ actual: failedMessages,
100
+ expected: []
101
+ });
102
+ }
103
+ }
104
+
105
+ private reset() {
106
+ this.messages = [];
107
+
108
+ // this sets the default to apply if no allowed patterns are set via setAcceptedErrorPatterns().
109
+ if (this.isStrict) {
110
+ this.matchers = GLOBALLY_ACCEPTED_ERRORS;
111
+ } else {
112
+ this.matchers = [() => true];
113
+ }
114
+ }
115
+
116
+ setAcceptedErrorPatterns(patterns?: (RegExp | string)[]) {
117
+ if (!patterns || patterns.length === 0) {
118
+ this.matchers = GLOBALLY_ACCEPTED_ERRORS;
119
+ } else {
120
+ this.matchers = patterns.map(wrapPatterns).concat(GLOBALLY_ACCEPTED_ERRORS);
121
+ }
122
+ }
123
+
124
+ private checkAndLog(type: BrowserConsoleLogOptions["level"], ...data: any[]) {
125
+ // only check the error messages
126
+ if (type === "error") {
127
+ const messageText = data[0];
128
+ const isAllowed = this.matchers.some((matcher) => matcher(messageText));
129
+ if (!isAllowed) {
130
+ this.messages.push(messageText);
131
+ }
132
+ }
133
+
134
+ if (this.karma) {
135
+ // wrap the data to facilitate parsing in the backend
136
+ const wrappedData = data.map((d) => [d]);
137
+ this.karma.log(type, wrappedData);
138
+ }
139
+ }
140
+
141
+ private prepareWindow(window: Window) {
142
+ const console: Console = (window as any).console;
143
+
144
+ // capture console.log(), console.debug(), etc.
145
+ const patchConsoleMethod = (method: "log" | "info" | "warn" | "error" | "debug") => {
146
+ const fnOriginal = console[method];
147
+ console[method] = (...data: any[]): void => {
148
+ this.checkAndLog(method, ...data);
149
+ return fnOriginal.apply(console, data);
150
+ };
151
+ };
152
+
153
+ patchConsoleMethod("log");
154
+ patchConsoleMethod("debug");
155
+ patchConsoleMethod("info");
156
+ patchConsoleMethod("warn");
157
+ patchConsoleMethod("error");
158
+
159
+ // capture console.assert()
160
+ // see https://console.spec.whatwg.org/#assert
161
+ console.assert = function (condition = false, ...data: any[]) {
162
+ if (condition) {
163
+ return;
164
+ }
165
+
166
+ const message = "Assertion failed";
167
+ if (data.length === 0) {
168
+ data.push(message);
169
+ } else {
170
+ let first = data[0];
171
+ if (typeof first !== "string") {
172
+ data.unshift(message);
173
+ } else {
174
+ first = `${message}: ${first}`;
175
+ data[0] = first;
176
+ }
177
+ }
178
+
179
+ console.error(...data);
180
+ };
181
+
182
+ // capture errors
183
+ function onPromiseRejection(this: ConsoleErrorChecker, event: PromiseRejectionEvent) {
184
+ const message = `UNHANDLED PROMISE REJECTION: ${event.reason}`;
185
+ this.checkAndLog("error", message, event.reason?.stack);
186
+ }
187
+
188
+ function onError(this: ConsoleErrorChecker, event: ErrorEvent) {
189
+ const message = event.message;
190
+ this.checkAndLog("error", message, event.filename);
191
+ }
192
+
193
+ window.addEventListener("error", onError.bind(this), { passive: true });
194
+ window.addEventListener("unhandledrejection", onPromiseRejection.bind(this), { passive: true });
195
+ }
196
+
197
+ static getInstance(window: Window & { sapFEConsoleErrorChecker?: ConsoleErrorChecker }): ConsoleErrorChecker {
198
+ // the global instance is needed to support multiple tests in a row (in Karma)
199
+ if (!window.sapFEConsoleErrorChecker) {
200
+ window.sapFEConsoleErrorChecker = new ConsoleErrorChecker(window);
201
+ }
202
+ return window.sapFEConsoleErrorChecker;
203
+ }
204
+ }
205
+
206
+ export default ConsoleErrorChecker.getInstance(window);
@@ -10,13 +10,14 @@ sap.ui.define(
10
10
  "sap/base/util/UriParameters",
11
11
  "sap/fe/test/Utils",
12
12
  "sap/fe/test/Stubs",
13
- "sap/fe/test/BaseArrangements"
13
+ "sap/fe/test/BaseArrangements",
14
+ "sap/fe/test/internal/ConsoleErrorChecker"
14
15
  ],
15
- function(Opa5, OpaBuilder, UriParameters, Utils, Stubs, BaseArrangements) {
16
+ function (Opa5, OpaBuilder, UriParameters, Utils, Stubs, BaseArrangements, ConsoleErrorChecker) {
16
17
  "use strict";
17
18
 
18
19
  return BaseArrangements.extend("sap.fe.test.internal.FEArrangements", {
19
- constructor: function(mSettings) {
20
+ constructor: function (mSettings) {
20
21
  BaseArrangements.call(
21
22
  this,
22
23
  Utils.mergeObjects(
@@ -28,7 +29,7 @@ sap.ui.define(
28
29
  );
29
30
  },
30
31
 
31
- iResetTestData: function(bIgnoreRedeploy) {
32
+ iResetTestData: function (bIgnoreRedeploy) {
32
33
  var that = this,
33
34
  oUriParams = new UriParameters(window.location.href),
34
35
  sBackendUrl = oUriParams.get("useBackendUrl"),
@@ -40,26 +41,45 @@ sap.ui.define(
40
41
  : "default";
41
42
 
42
43
  return OpaBuilder.create(this)
43
- .success(function() {
44
+ .success(function () {
44
45
  var oResetData = that.resetTestData(),
45
46
  oRedeploy = bIgnoreRedeploy ? Promise.resolve() : jQuery.post(sProxyPrefix + "/redeploy?tenant=" + sTenantID);
46
47
 
47
48
  Promise.all([oResetData, oRedeploy])
48
- .finally(function() {
49
+ .finally(function () {
49
50
  bSuccess = true;
50
51
  })
51
- .catch(function(oError) {
52
+ .catch(function (oError) {
52
53
  throw oError;
53
54
  });
54
55
 
55
56
  return OpaBuilder.create(this)
56
- .check(function() {
57
+ .check(function () {
57
58
  return bSuccess;
58
59
  })
59
60
  .execute();
60
61
  })
61
62
  .description(Utils.formatMessage("Reset test data on tenant '{0}'", sTenantID))
62
63
  .execute();
64
+ },
65
+
66
+ /**
67
+ * Fail the test if there are errors logged to the browser console.
68
+ *
69
+ * @param {Array<RegExp|string>} [aErrors] The allowed error messages. Either use regular expressions or strings. Pass undefined or an empty array to reject all error messages.
70
+ * @returns {*}
71
+ */
72
+ iAcceptTheseErrors: function (aErrors) {
73
+ return OpaBuilder.create(this)
74
+ .do(function () {
75
+ ConsoleErrorChecker.setAcceptedErrorPatterns(aErrors);
76
+ })
77
+ .description(
78
+ !aErrors || aErrors.length === 0
79
+ ? "Do not accept error messages"
80
+ : Utils.formatMessage("Only accept these error message patterns: {0}", aErrors)
81
+ )
82
+ .execute();
63
83
  }
64
84
  });
65
85
  }
@@ -20,9 +20,10 @@ sap.ui.define(["sap/ui/core/Core", "sap/ui/core/library"], function (Core, _libr
20
20
  interfaces: [],
21
21
  controls: [],
22
22
  elements: [],
23
- version: "1.102.2",
23
+ // eslint-disable-next-line no-template-curly-in-string
24
+ version: "1.104.1",
24
25
  noLibraryCSS: true
25
26
  });
26
27
  return thisLib;
27
28
  }, false);
28
- //# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbImxpYnJhcnkudHMiXSwibmFtZXMiOlsidGhpc0xpYiIsIkNvcmUiLCJpbml0TGlicmFyeSIsIm5hbWUiLCJkZXBlbmRlbmNpZXMiLCJ0eXBlcyIsImludGVyZmFjZXMiLCJjb250cm9scyIsImVsZW1lbnRzIiwidmVyc2lvbiIsIm5vTGlicmFyeUNTUyJdLCJtYXBwaW5ncyI6IjtBQUFBO0FBQ0E7QUFDQTs7OztBQUtBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBRUE7QUFDQSxNQUFNQSxPQUFPLEdBQUdDLElBQUksQ0FBQ0MsV0FBTCxDQUFpQjtBQUNoQ0MsSUFBQUEsSUFBSSxFQUFFLGFBRDBCO0FBRWhDQyxJQUFBQSxZQUFZLEVBQUUsQ0FBQyxhQUFELENBRmtCO0FBR2hDQyxJQUFBQSxLQUFLLEVBQUUsRUFIeUI7QUFJaENDLElBQUFBLFVBQVUsRUFBRSxFQUpvQjtBQUtoQ0MsSUFBQUEsUUFBUSxFQUFFLEVBTHNCO0FBTWhDQyxJQUFBQSxRQUFRLEVBQUUsRUFOc0I7QUFPaENDLElBQUFBLE9BQU8sRUFBRSxZQVB1QjtBQVFoQ0MsSUFBQUEsWUFBWSxFQUFFO0FBUmtCLEdBQWpCLENBQWhCO1NBV2VWLE8iLCJzb3VyY2VSb290IjoiLiIsInNvdXJjZXNDb250ZW50IjpbIi8qXG4gKiAhICR7Y29weXJpZ2h0fVxuICovXG5cbmltcG9ydCBDb3JlIGZyb20gXCJzYXAvdWkvY29yZS9Db3JlXCI7XG5pbXBvcnQgXCJzYXAvdWkvY29yZS9saWJyYXJ5XCI7XG5cbi8qKlxuICogVGVzdCBsaWJyYXJ5IGZvciBTQVAgRmlvcmkgZWxlbWVudHNcbiAqXG4gKiBAbmFtZXNwYWNlXG4gKiBAbmFtZSBzYXAuZmUudGVzdFxuICogQHB1YmxpY1xuICovXG5cbi8vIGxpYnJhcnkgZGVwZW5kZW5jaWVzXG5jb25zdCB0aGlzTGliID0gQ29yZS5pbml0TGlicmFyeSh7XG5cdG5hbWU6IFwic2FwLmZlLnRlc3RcIixcblx0ZGVwZW5kZW5jaWVzOiBbXCJzYXAudWkuY29yZVwiXSxcblx0dHlwZXM6IFtdLFxuXHRpbnRlcmZhY2VzOiBbXSxcblx0Y29udHJvbHM6IFtdLFxuXHRlbGVtZW50czogW10sXG5cdHZlcnNpb246IFwiJHt2ZXJzaW9ufVwiLFxuXHRub0xpYnJhcnlDU1M6IHRydWVcbn0pO1xuXG5leHBvcnQgZGVmYXVsdCB0aGlzTGliO1xuIl19
29
+ //# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbImxpYnJhcnkudHMiXSwibmFtZXMiOlsidGhpc0xpYiIsIkNvcmUiLCJpbml0TGlicmFyeSIsIm5hbWUiLCJkZXBlbmRlbmNpZXMiLCJ0eXBlcyIsImludGVyZmFjZXMiLCJjb250cm9scyIsImVsZW1lbnRzIiwidmVyc2lvbiIsIm5vTGlicmFyeUNTUyJdLCJtYXBwaW5ncyI6IjtBQUFBO0FBQ0E7QUFDQTs7OztBQUtBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBRUE7QUFDQSxNQUFNQSxPQUFPLEdBQUdDLElBQUksQ0FBQ0MsV0FBTCxDQUFpQjtBQUNoQ0MsSUFBQUEsSUFBSSxFQUFFLGFBRDBCO0FBRWhDQyxJQUFBQSxZQUFZLEVBQUUsQ0FBQyxhQUFELENBRmtCO0FBR2hDQyxJQUFBQSxLQUFLLEVBQUUsRUFIeUI7QUFJaENDLElBQUFBLFVBQVUsRUFBRSxFQUpvQjtBQUtoQ0MsSUFBQUEsUUFBUSxFQUFFLEVBTHNCO0FBTWhDQyxJQUFBQSxRQUFRLEVBQUUsRUFOc0I7QUFPaEM7QUFDQUMsSUFBQUEsT0FBTyxFQUFFLFlBUnVCO0FBU2hDQyxJQUFBQSxZQUFZLEVBQUU7QUFUa0IsR0FBakIsQ0FBaEI7U0FZZVYsTyIsInNvdXJjZVJvb3QiOiIuIiwic291cmNlc0NvbnRlbnQiOlsiLypcbiAqICEgJHtjb3B5cmlnaHR9XG4gKi9cblxuaW1wb3J0IENvcmUgZnJvbSBcInNhcC91aS9jb3JlL0NvcmVcIjtcbmltcG9ydCBcInNhcC91aS9jb3JlL2xpYnJhcnlcIjtcblxuLyoqXG4gKiBUZXN0IGxpYnJhcnkgZm9yIFNBUCBGaW9yaSBlbGVtZW50c1xuICpcbiAqIEBuYW1lc3BhY2VcbiAqIEBuYW1lIHNhcC5mZS50ZXN0XG4gKiBAcHVibGljXG4gKi9cblxuLy8gbGlicmFyeSBkZXBlbmRlbmNpZXNcbmNvbnN0IHRoaXNMaWIgPSBDb3JlLmluaXRMaWJyYXJ5KHtcblx0bmFtZTogXCJzYXAuZmUudGVzdFwiLFxuXHRkZXBlbmRlbmNpZXM6IFtcInNhcC51aS5jb3JlXCJdLFxuXHR0eXBlczogW10sXG5cdGludGVyZmFjZXM6IFtdLFxuXHRjb250cm9sczogW10sXG5cdGVsZW1lbnRzOiBbXSxcblx0Ly8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIG5vLXRlbXBsYXRlLWN1cmx5LWluLXN0cmluZ1xuXHR2ZXJzaW9uOiBcIiR7dmVyc2lvbn1cIixcblx0bm9MaWJyYXJ5Q1NTOiB0cnVlXG59KTtcblxuZXhwb3J0IGRlZmF1bHQgdGhpc0xpYjtcbiJdfQ==
@@ -21,6 +21,7 @@ const thisLib = Core.initLibrary({
21
21
  interfaces: [],
22
22
  controls: [],
23
23
  elements: [],
24
+ // eslint-disable-next-line no-template-curly-in-string
24
25
  version: "${version}",
25
26
  noLibraryCSS: true
26
27
  });