@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.
- package/package.json +3 -4
- package/src/sap/fe/test/.library +1 -1
- package/src/sap/fe/test/BaseActions.js +103 -97
- package/src/sap/fe/test/BaseArrangements.js +4 -15
- package/src/sap/fe/test/BaseAssertions.js +39 -46
- package/src/sap/fe/test/CollaborationClient.js +5 -5
- package/src/sap/fe/test/CollaborationClient.ts +5 -5
- package/src/sap/fe/test/FeMocks.js +1 -1
- package/src/sap/fe/test/FeMocks.ts +4 -3
- package/src/sap/fe/test/Flexibility.js +115 -162
- package/src/sap/fe/test/JestTemplatingHelper.js +99 -7
- package/src/sap/fe/test/JestTemplatingHelper.ts +91 -14
- package/src/sap/fe/test/JourneyRunner.js +31 -37
- package/src/sap/fe/test/ListReport.js +9 -13
- package/src/sap/fe/test/ObjectPage.js +35 -33
- package/src/sap/fe/test/Shell.js +0 -6
- package/src/sap/fe/test/TemplatePage.js +0 -2
- package/src/sap/fe/test/TemplatingTestUtils.js +20 -20
- package/src/sap/fe/test/UI5MockHelper.js +16 -10
- package/src/sap/fe/test/UI5MockHelper.ts +28 -18
- package/src/sap/fe/test/Utils.js +19 -20
- package/src/sap/fe/test/api/BaseAPI.js +11 -26
- package/src/sap/fe/test/api/ChartActions.js +116 -119
- package/src/sap/fe/test/api/ChartAssertions.js +18 -22
- package/src/sap/fe/test/api/CollaborationAPI.js +6 -6
- package/src/sap/fe/test/api/CollaborationAPI.ts +9 -11
- package/src/sap/fe/test/api/DialogAPI.js +11 -13
- package/src/sap/fe/test/api/DialogActions.js +8 -8
- package/src/sap/fe/test/api/DialogAssertions.js +9 -10
- package/src/sap/fe/test/api/DialogHelper.js +63 -0
- package/src/sap/fe/test/api/DialogMessageActions.js +2 -1
- package/src/sap/fe/test/api/DialogValueHelpActions.js +17 -39
- package/src/sap/fe/test/api/DialogValueHelpAssertions.js +47 -19
- package/src/sap/fe/test/api/FilterBarAPI.js +11 -12
- package/src/sap/fe/test/api/FilterBarActions.js +26 -40
- package/src/sap/fe/test/api/FilterBarAssertions.js +28 -47
- package/src/sap/fe/test/api/FooterActionsBase.js +3 -4
- package/src/sap/fe/test/api/FooterActionsOP.js +6 -10
- package/src/sap/fe/test/api/FooterAssertionsBase.js +5 -10
- package/src/sap/fe/test/api/FooterAssertionsOP.js +8 -13
- package/src/sap/fe/test/api/FormAPI.js +5 -6
- package/src/sap/fe/test/api/FormActions.js +11 -21
- package/src/sap/fe/test/api/FormAssertions.js +14 -20
- package/src/sap/fe/test/api/HeaderAPI.js +12 -27
- package/src/sap/fe/test/api/HeaderActions.js +10 -12
- package/src/sap/fe/test/api/HeaderActionsLR.js +76 -82
- package/src/sap/fe/test/api/HeaderAssertions.js +2 -20
- package/src/sap/fe/test/api/HeaderAssertionsLR.js +5 -8
- package/src/sap/fe/test/api/HeaderLR.js +7 -19
- package/src/sap/fe/test/api/KPICardAssertions.js +18 -37
- package/src/sap/fe/test/api/TableAPI.js +3 -13
- package/src/sap/fe/test/api/TableActions.js +2 -34
- package/src/sap/fe/test/api/TableAssertions.js +1 -24
- package/src/sap/fe/test/builder/FEBuilder.js +71 -75
- package/src/sap/fe/test/builder/MacroFieldBuilder.js +4 -3
- package/src/sap/fe/test/builder/MdcFieldBuilder.js +33 -29
- package/src/sap/fe/test/builder/MdcFilterBarBuilder.js +102 -104
- package/src/sap/fe/test/builder/MdcFilterFieldBuilder.js +9 -7
- package/src/sap/fe/test/builder/MdcTableBuilder.js +8 -4
- package/src/sap/fe/test/builder/VMBuilder.js +39 -55
- package/src/sap/fe/test/internal/ConsoleErrorChecker.js +241 -0
- package/src/sap/fe/test/internal/ConsoleErrorChecker.ts +206 -0
- package/src/sap/fe/test/internal/FEArrangements.js +28 -8
- package/src/sap/fe/test/library.js +3 -2
- 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
|
-
|
|
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,
|
|
29
|
+
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbImxpYnJhcnkudHMiXSwibmFtZXMiOlsidGhpc0xpYiIsIkNvcmUiLCJpbml0TGlicmFyeSIsIm5hbWUiLCJkZXBlbmRlbmNpZXMiLCJ0eXBlcyIsImludGVyZmFjZXMiLCJjb250cm9scyIsImVsZW1lbnRzIiwidmVyc2lvbiIsIm5vTGlicmFyeUNTUyJdLCJtYXBwaW5ncyI6IjtBQUFBO0FBQ0E7QUFDQTs7OztBQUtBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBRUE7QUFDQSxNQUFNQSxPQUFPLEdBQUdDLElBQUksQ0FBQ0MsV0FBTCxDQUFpQjtBQUNoQ0MsSUFBQUEsSUFBSSxFQUFFLGFBRDBCO0FBRWhDQyxJQUFBQSxZQUFZLEVBQUUsQ0FBQyxhQUFELENBRmtCO0FBR2hDQyxJQUFBQSxLQUFLLEVBQUUsRUFIeUI7QUFJaENDLElBQUFBLFVBQVUsRUFBRSxFQUpvQjtBQUtoQ0MsSUFBQUEsUUFBUSxFQUFFLEVBTHNCO0FBTWhDQyxJQUFBQSxRQUFRLEVBQUUsRUFOc0I7QUFPaEM7QUFDQUMsSUFBQUEsT0FBTyxFQUFFLFlBUnVCO0FBU2hDQyxJQUFBQSxZQUFZLEVBQUU7QUFUa0IsR0FBakIsQ0FBaEI7U0FZZVYsTyIsInNvdXJjZVJvb3QiOiIuIiwic291cmNlc0NvbnRlbnQiOlsiLypcbiAqICEgJHtjb3B5cmlnaHR9XG4gKi9cblxuaW1wb3J0IENvcmUgZnJvbSBcInNhcC91aS9jb3JlL0NvcmVcIjtcbmltcG9ydCBcInNhcC91aS9jb3JlL2xpYnJhcnlcIjtcblxuLyoqXG4gKiBUZXN0IGxpYnJhcnkgZm9yIFNBUCBGaW9yaSBlbGVtZW50c1xuICpcbiAqIEBuYW1lc3BhY2VcbiAqIEBuYW1lIHNhcC5mZS50ZXN0XG4gKiBAcHVibGljXG4gKi9cblxuLy8gbGlicmFyeSBkZXBlbmRlbmNpZXNcbmNvbnN0IHRoaXNMaWIgPSBDb3JlLmluaXRMaWJyYXJ5KHtcblx0bmFtZTogXCJzYXAuZmUudGVzdFwiLFxuXHRkZXBlbmRlbmNpZXM6IFtcInNhcC51aS5jb3JlXCJdLFxuXHR0eXBlczogW10sXG5cdGludGVyZmFjZXM6IFtdLFxuXHRjb250cm9sczogW10sXG5cdGVsZW1lbnRzOiBbXSxcblx0Ly8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIG5vLXRlbXBsYXRlLWN1cmx5LWluLXN0cmluZ1xuXHR2ZXJzaW9uOiBcIiR7dmVyc2lvbn1cIixcblx0bm9MaWJyYXJ5Q1NTOiB0cnVlXG59KTtcblxuZXhwb3J0IGRlZmF1bHQgdGhpc0xpYjtcbiJdfQ==
|