@sapui5/sap.fe.test 1.108.2 → 1.110.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.
- package/README.md +5 -1
- package/package.json +9 -7
- package/src/sap/fe/test/.library +17 -18
- package/src/sap/fe/test/BaseActions.js +3 -2
- package/src/sap/fe/test/BaseArrangements.js +3 -2
- package/src/sap/fe/test/BaseAssertions.js +26 -2
- package/src/sap/fe/test/CollaborationClient.js +21 -5
- package/src/sap/fe/test/CollaborationClient.ts +29 -1
- package/src/sap/fe/test/ConfirmDialog.js +12 -11
- package/src/sap/fe/test/FCLView.js +16 -15
- package/src/sap/fe/test/FeMocks.js +1 -11
- package/src/sap/fe/test/Flexibility.js +12 -7
- package/src/sap/fe/test/FlexibleColumnLayout.js +12 -11
- package/src/sap/fe/test/JestFlexibilityHelper.js +141 -0
- package/src/sap/fe/test/JestFlexibilityHelper.ts +135 -0
- package/src/sap/fe/test/JestTemplatingHelper.js +191 -207
- package/src/sap/fe/test/JestTemplatingHelper.ts +171 -91
- package/src/sap/fe/test/JourneyRunner.js +3 -2
- package/src/sap/fe/test/ListReport.js +3 -2
- package/src/sap/fe/test/LocationUtil.js +3 -2
- package/src/sap/fe/test/ObjectPage.js +129 -26
- package/src/sap/fe/test/Shell.js +9 -4
- package/src/sap/fe/test/Stubs.js +18 -17
- package/src/sap/fe/test/TemplatePage.js +3 -2
- package/src/sap/fe/test/TemplatingTestUtils.js +3 -2
- package/src/sap/fe/test/UI5MockHelper.js +39 -72
- package/src/sap/fe/test/UI5MockHelper.ts +3 -0
- package/src/sap/fe/test/Utils.js +4 -3
- package/src/sap/fe/test/api/APIHelper.js +3 -2
- package/src/sap/fe/test/api/BaseAPI.js +3 -2
- package/src/sap/fe/test/api/ChartActions.js +3 -2
- package/src/sap/fe/test/api/ChartAssertions.js +3 -2
- package/src/sap/fe/test/api/CollaborationAPI.js +2 -30
- package/src/sap/fe/test/api/DialogAPI.js +3 -2
- package/src/sap/fe/test/api/DialogActions.js +3 -2
- package/src/sap/fe/test/api/DialogAssertions.js +4 -3
- package/src/sap/fe/test/api/DialogCreateActions.js +6 -5
- package/src/sap/fe/test/api/DialogCreateAssertions.js +48 -49
- package/src/sap/fe/test/api/DialogHelper.js +4 -3
- package/src/sap/fe/test/api/DialogMassEditActions.js +3 -2
- package/src/sap/fe/test/api/DialogMassEditAssertions.js +3 -2
- package/src/sap/fe/test/api/DialogMessageActions.js +3 -2
- package/src/sap/fe/test/api/DialogMessageAssertions.js +82 -79
- package/src/sap/fe/test/api/DialogType.js +3 -2
- package/src/sap/fe/test/api/DialogValueHelpActions.js +3 -2
- package/src/sap/fe/test/api/DialogValueHelpAssertions.js +3 -2
- package/src/sap/fe/test/api/EditState.js +4 -3
- package/src/sap/fe/test/api/FilterBarAPI.js +3 -2
- package/src/sap/fe/test/api/FilterBarActions.js +3 -2
- package/src/sap/fe/test/api/FilterBarAssertions.js +3 -2
- package/src/sap/fe/test/api/FooterAPI.js +5 -4
- package/src/sap/fe/test/api/FooterActionsBase.js +3 -2
- package/src/sap/fe/test/api/FooterActionsOP.js +3 -2
- package/src/sap/fe/test/api/FooterAssertionsBase.js +3 -2
- package/src/sap/fe/test/api/FooterAssertionsOP.js +3 -2
- package/src/sap/fe/test/api/FormAPI.js +4 -2
- package/src/sap/fe/test/api/FormActions.js +3 -2
- package/src/sap/fe/test/api/FormAssertions.js +3 -2
- package/src/sap/fe/test/api/HeaderAPI.js +3 -2
- package/src/sap/fe/test/api/HeaderActions.js +3 -2
- package/src/sap/fe/test/api/HeaderActionsLR.js +3 -2
- package/src/sap/fe/test/api/HeaderAssertions.js +3 -2
- package/src/sap/fe/test/api/HeaderAssertionsLR.js +3 -2
- package/src/sap/fe/test/api/HeaderLR.js +3 -2
- package/src/sap/fe/test/api/KPICardAPI.js +5 -4
- package/src/sap/fe/test/api/KPICardActions.js +7 -11
- package/src/sap/fe/test/api/KPICardAssertions.js +3 -2
- package/src/sap/fe/test/api/TableAPI.js +5 -4
- package/src/sap/fe/test/api/TableActions.js +16 -9
- package/src/sap/fe/test/api/TableAssertions.js +8 -7
- package/src/sap/fe/test/builder/DialogBuilder.js +99 -102
- package/src/sap/fe/test/builder/FEBuilder.js +3 -2
- package/src/sap/fe/test/builder/KPIBuilder.js +16 -25
- package/src/sap/fe/test/builder/MacroFieldBuilder.js +4 -2
- package/src/sap/fe/test/builder/MdcFieldBuilder.js +3 -2
- package/src/sap/fe/test/builder/MdcFilterBarBuilder.js +3 -2
- package/src/sap/fe/test/builder/MdcFilterFieldBuilder.js +3 -2
- package/src/sap/fe/test/builder/MdcTableBuilder.js +196 -96
- package/src/sap/fe/test/builder/OverflowToolbarBuilder.js +3 -2
- package/src/sap/fe/test/builder/VMBuilder.js +3 -2
- package/src/sap/fe/test/internal/ConsoleErrorChecker.js +13 -48
- package/src/sap/fe/test/internal/FEArrangements.js +3 -2
- package/src/sap/fe/test/internal/FEJourneyRunner.js +5 -3
- package/src/sap/fe/test/library.js +3 -2
|
@@ -1,10 +1,14 @@
|
|
|
1
1
|
import type { AnyAnnotation, ConvertedMetadata, EntitySet, Property } from "@sap-ux/vocabularies-types";
|
|
2
2
|
import compiler from "@sap/cds-compiler";
|
|
3
3
|
import * as fs from "fs";
|
|
4
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
5
|
+
// @ts-ignore
|
|
6
|
+
import * as plugin from "@prettier/plugin-xml";
|
|
4
7
|
import * as path from "path";
|
|
5
|
-
import type {
|
|
8
|
+
import type { Options } from "prettier";
|
|
6
9
|
import { format } from "prettier";
|
|
7
10
|
import Log from "sap/base/Log";
|
|
11
|
+
import LoaderExtensions from "sap/base/util/LoaderExtensions";
|
|
8
12
|
import merge from "sap/base/util/merge";
|
|
9
13
|
import AppComponent from "sap/fe/core/AppComponent";
|
|
10
14
|
import { registerBuildingBlock } from "sap/fe/core/buildingBlocks/BuildingBlockRuntime";
|
|
@@ -17,6 +21,7 @@ import TemplateModel from "sap/fe/core/TemplateModel";
|
|
|
17
21
|
import type { DataModelObjectPath } from "sap/fe/core/templating/DataModelPathHelper";
|
|
18
22
|
import BindingParser from "sap/ui/base/BindingParser";
|
|
19
23
|
import ManagedObject from "sap/ui/base/ManagedObject";
|
|
24
|
+
import ManagedObjectMetadata from "sap/ui/base/ManagedObjectMetadata";
|
|
20
25
|
import Component from "sap/ui/core/Component";
|
|
21
26
|
import Control from "sap/ui/core/Control";
|
|
22
27
|
import InvisibleText from "sap/ui/core/InvisibleText";
|
|
@@ -32,6 +37,22 @@ import MetaModel from "sap/ui/model/MetaModel";
|
|
|
32
37
|
import _MetadataRequestor from "sap/ui/model/odata/v4/lib/_MetadataRequestor";
|
|
33
38
|
import ODataMetaModel from "sap/ui/model/odata/v4/ODataMetaModel";
|
|
34
39
|
import xpath from "xpath";
|
|
40
|
+
import { createFlexibilityChangesObject } from "./JestFlexibilityHelper";
|
|
41
|
+
|
|
42
|
+
type PreProcessorSettingsType = {
|
|
43
|
+
models: {
|
|
44
|
+
[name: string]: JSONModel | ODataMetaModel;
|
|
45
|
+
};
|
|
46
|
+
bindingContexts: {
|
|
47
|
+
[name: string]: Context | undefined;
|
|
48
|
+
};
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
type JestTemplatedView = {
|
|
52
|
+
asElement: Element | undefined;
|
|
53
|
+
asString: string;
|
|
54
|
+
};
|
|
55
|
+
|
|
35
56
|
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
36
57
|
const formatXml = require("xml-formatter");
|
|
37
58
|
|
|
@@ -39,28 +60,91 @@ Log.setLevel(1 as any, "sap.ui.core.util.XMLPreprocessor");
|
|
|
39
60
|
jest.setTimeout(40000);
|
|
40
61
|
|
|
41
62
|
const nameSpaceMap = {
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
63
|
+
macros: "sap.fe.macros",
|
|
64
|
+
macro: "sap.fe.macros",
|
|
65
|
+
macroField: "sap.fe.macros.field",
|
|
66
|
+
macrodata: "http://schemas.sap.com/sapui5/extension/sap.ui.core.CustomData/1",
|
|
67
|
+
log: "http://schemas.sap.com/sapui5/extension/sap.ui.core.CustomData/1",
|
|
68
|
+
unittest: "http://schemas.sap.com/sapui5/preprocessorextension/sap.fe.unittesting/1",
|
|
69
|
+
control: "sap.fe.core.controls",
|
|
70
|
+
core: "sap.ui.core",
|
|
71
|
+
dt: "sap.ui.dt",
|
|
72
|
+
m: "sap.m",
|
|
73
|
+
f: "sap.ui.layout.form",
|
|
74
|
+
fl: "sap.ui.fl",
|
|
75
|
+
internalMacro: "sap.fe.macros.internal",
|
|
76
|
+
mdc: "sap.ui.mdc",
|
|
77
|
+
mdcat: "sap.ui.mdc.actiontoolbar",
|
|
78
|
+
mdcField: "sap.ui.mdc.field",
|
|
79
|
+
mdcTable: "sap.ui.mdc.table",
|
|
80
|
+
u: "sap.ui.unified",
|
|
81
|
+
macroMicroChart: "sap.fe.macros.microchart",
|
|
82
|
+
microChart: "sap.suite.ui.microchart",
|
|
83
|
+
macroTable: "sap.fe.macros.table"
|
|
61
84
|
};
|
|
62
85
|
const select = xpath.useNamespaces(nameSpaceMap);
|
|
63
86
|
|
|
87
|
+
function _getTemplatedSelector(xmldom: Node, selector: string): string {
|
|
88
|
+
/**
|
|
89
|
+
* if a root element has been added during the templating by our Jest Templating methods
|
|
90
|
+
* the root element is added to the selector path.
|
|
91
|
+
*/
|
|
92
|
+
const rootPath = "/root";
|
|
93
|
+
return `${xmldom.nodeName === "root" && !selector.startsWith(rootPath) ? rootPath : ""}${selector}`;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
async function _buildPreProcessorSettings(
|
|
97
|
+
sMetadataUrl: string,
|
|
98
|
+
mBindingContexts: { [x: string]: string },
|
|
99
|
+
mModels: { [x: string]: any }
|
|
100
|
+
): Promise<PreProcessorSettingsType> {
|
|
101
|
+
const oMetaModel = await getMetaModel(sMetadataUrl);
|
|
102
|
+
|
|
103
|
+
// To ensure our macro can use #setBindingContext we ensure there is a pre existing JSONModel for converterContext
|
|
104
|
+
// if not already passed to teh templating
|
|
105
|
+
if (!mModels.hasOwnProperty("converterContext")) {
|
|
106
|
+
mModels = Object.assign(mModels, { converterContext: new TemplateModel({}, oMetaModel) });
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
Object.keys(mModels).forEach(function (sModelName) {
|
|
110
|
+
if (mModels[sModelName] && mModels[sModelName].isTemplateModel) {
|
|
111
|
+
mModels[sModelName] = new TemplateModel(mModels[sModelName].data, oMetaModel);
|
|
112
|
+
}
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
const settings: any = {
|
|
116
|
+
models: Object.assign(
|
|
117
|
+
{
|
|
118
|
+
metaModel: oMetaModel
|
|
119
|
+
},
|
|
120
|
+
mModels
|
|
121
|
+
),
|
|
122
|
+
bindingContexts: {}
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
//Inject models and bindingContexts
|
|
126
|
+
Object.keys(mBindingContexts).forEach(function (sKey) {
|
|
127
|
+
/* Assert to make sure the annotations are in the test metadata -> avoid misleading tests */
|
|
128
|
+
expect(typeof oMetaModel.getObject(mBindingContexts[sKey])).toBeDefined();
|
|
129
|
+
const oModel = mModels[sKey] || oMetaModel;
|
|
130
|
+
settings.bindingContexts[sKey] = oModel.createBindingContext(mBindingContexts[sKey]); //Value is sPath
|
|
131
|
+
settings.models[sKey] = oModel;
|
|
132
|
+
});
|
|
133
|
+
return settings;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
function _removeCommentFromXml(xml: string): string {
|
|
137
|
+
return formatXml(xml, {
|
|
138
|
+
filter: (node: any) => node.type !== "Comment"
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
function _loadResourceView(viewName: string): Element {
|
|
143
|
+
const name = viewName.replace(/\./g, "/") + ".view.xml";
|
|
144
|
+
const view = LoaderExtensions.loadResource(name);
|
|
145
|
+
return view.documentElement;
|
|
146
|
+
}
|
|
147
|
+
|
|
64
148
|
export const registerMacro = function (macroMetadata: any) {
|
|
65
149
|
registerBuildingBlock(macroMetadata);
|
|
66
150
|
};
|
|
@@ -76,7 +160,7 @@ export const runXPathQuery = function (selector: string, xmldom: Node | undefine
|
|
|
76
160
|
|
|
77
161
|
expect.extend({
|
|
78
162
|
toHaveControl(xmldom, selector) {
|
|
79
|
-
const nodes = runXPathQuery(
|
|
163
|
+
const nodes = runXPathQuery(_getTemplatedSelector(xmldom, selector), xmldom);
|
|
80
164
|
return {
|
|
81
165
|
message: () => {
|
|
82
166
|
const outputXml = serializeXML(xmldom);
|
|
@@ -86,7 +170,7 @@ expect.extend({
|
|
|
86
170
|
};
|
|
87
171
|
},
|
|
88
172
|
toNotHaveControl(xmldom, selector) {
|
|
89
|
-
const nodes = runXPathQuery(
|
|
173
|
+
const nodes = runXPathQuery(_getTemplatedSelector(xmldom, selector), xmldom);
|
|
90
174
|
return {
|
|
91
175
|
message: () => {
|
|
92
176
|
const outputXml = serializeXML(xmldom);
|
|
@@ -107,7 +191,7 @@ export const formatBuildingBlockXML = function (xmlString: string | string[]) {
|
|
|
107
191
|
};
|
|
108
192
|
|
|
109
193
|
export const getControlAttribute = function (controlSelector: string, attributeName: string, xmlDom: Node) {
|
|
110
|
-
const selector = `string(
|
|
194
|
+
const selector = `string(${_getTemplatedSelector(xmlDom, controlSelector)}/@${attributeName})`;
|
|
111
195
|
return runXPathQuery(selector, xmlDom);
|
|
112
196
|
};
|
|
113
197
|
|
|
@@ -118,10 +202,11 @@ export const serializeXML = function (xmlDom: Node) {
|
|
|
118
202
|
};
|
|
119
203
|
|
|
120
204
|
export const formatXML = function (xmlString: string) {
|
|
121
|
-
return format(
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
205
|
+
return format(xmlString, {
|
|
206
|
+
parser: "xml",
|
|
207
|
+
xmlWhitespaceSensitivity: "ignore",
|
|
208
|
+
plugins: [plugin]
|
|
209
|
+
} as Options & { xmlWhitespaceSensitivity: "ignore" | "strict" });
|
|
125
210
|
};
|
|
126
211
|
|
|
127
212
|
/**
|
|
@@ -305,13 +390,12 @@ export function evaluateBindingWithModel(
|
|
|
305
390
|
|
|
306
391
|
const TESTVIEWID = "testViewId";
|
|
307
392
|
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
)
|
|
314
|
-
const changes = createChangesObject(TESTVIEWID, aVariantDependentControlChanges);
|
|
393
|
+
const applyFlexChanges = async function (flexChanges: { [x: string]: object[] }, oMetaModel: MetaModel, resultXML: any) {
|
|
394
|
+
// prefix Ids
|
|
395
|
+
[...resultXML.querySelectorAll("[id]")].forEach((node) => {
|
|
396
|
+
node.id = `${TESTVIEWID}--${node.id}`;
|
|
397
|
+
});
|
|
398
|
+
const changes = createFlexibilityChangesObject(TESTVIEWID, flexChanges);
|
|
315
399
|
const appId = "someComponent";
|
|
316
400
|
const oManifest = {
|
|
317
401
|
"sap.app": {
|
|
@@ -332,7 +416,8 @@ export const applyFlexChanges = async function (
|
|
|
332
416
|
getEntry: function (name: string) {
|
|
333
417
|
return (oManifest as any)[name];
|
|
334
418
|
}
|
|
335
|
-
})
|
|
419
|
+
}),
|
|
420
|
+
getLocalId: jest.fn((sId) => sId)
|
|
336
421
|
} as unknown as AppComponent;
|
|
337
422
|
//fake changes
|
|
338
423
|
jest.spyOn(AppStorage, "loadFlexData").mockReturnValue(Promise.resolve(changes));
|
|
@@ -342,6 +427,10 @@ export const applyFlexChanges = async function (
|
|
|
342
427
|
componentId: appId
|
|
343
428
|
});
|
|
344
429
|
resultXML = await XmlPreprocessor.process(resultXML, { name: "Test Fragment", componentId: appId, id: TESTVIEWID });
|
|
430
|
+
|
|
431
|
+
//Assert that all changes have been applied
|
|
432
|
+
const changesApplied = getChangesFromXML(resultXML);
|
|
433
|
+
expect(changesApplied.length).toBe(flexChanges?.changes?.length ?? 0 + flexChanges?.variantDependentControlChanges?.length ?? 0);
|
|
345
434
|
return resultXML;
|
|
346
435
|
};
|
|
347
436
|
|
|
@@ -355,8 +444,7 @@ export const getTemplatingResult = async function (
|
|
|
355
444
|
sMetadataUrl: string,
|
|
356
445
|
mBindingContexts: { [x: string]: any; entitySet?: string },
|
|
357
446
|
mModels: { [x: string]: any },
|
|
358
|
-
|
|
359
|
-
createChangesObject?: Function
|
|
447
|
+
flexChanges?: { [x: string]: object[] }
|
|
360
448
|
) {
|
|
361
449
|
const templatedXml = `<root>${xmlInput}</root>`;
|
|
362
450
|
const parser = new window.DOMParser();
|
|
@@ -365,52 +453,18 @@ export const getTemplatingResult = async function (
|
|
|
365
453
|
// if not already passed to teh templating
|
|
366
454
|
|
|
367
455
|
const oMetaModel = await getMetaModel(sMetadataUrl);
|
|
368
|
-
|
|
369
|
-
mModels = Object.assign(mModels, { "converterContext": new TemplateModel({}, oMetaModel) });
|
|
370
|
-
}
|
|
371
|
-
|
|
372
|
-
Object.keys(mModels).forEach(function (sModelName) {
|
|
373
|
-
if (mModels[sModelName] && mModels[sModelName].isTemplateModel) {
|
|
374
|
-
mModels[sModelName] = new TemplateModel(mModels[sModelName].data, oMetaModel);
|
|
375
|
-
}
|
|
376
|
-
});
|
|
377
|
-
|
|
378
|
-
const oPreprocessorSettings: any = {
|
|
379
|
-
models: Object.assign(
|
|
380
|
-
{
|
|
381
|
-
metaModel: oMetaModel
|
|
382
|
-
},
|
|
383
|
-
mModels
|
|
384
|
-
),
|
|
385
|
-
bindingContexts: {}
|
|
386
|
-
};
|
|
387
|
-
|
|
388
|
-
//Inject models and bindingContexts
|
|
389
|
-
Object.keys(mBindingContexts).forEach(function (sKey) {
|
|
390
|
-
/* Assert to make sure the annotations are in the test metadata -> avoid misleading tests */
|
|
391
|
-
expect(typeof oMetaModel.getObject(mBindingContexts[sKey])).toBeDefined();
|
|
392
|
-
const oModel = mModels[sKey] || oMetaModel;
|
|
393
|
-
oPreprocessorSettings.bindingContexts[sKey] = oModel.createBindingContext(mBindingContexts[sKey]); //Value is sPath
|
|
394
|
-
oPreprocessorSettings.models[sKey] = oModel;
|
|
395
|
-
});
|
|
456
|
+
const oPreprocessorSettings = await _buildPreProcessorSettings(sMetadataUrl, mBindingContexts, mModels);
|
|
396
457
|
|
|
397
458
|
//This context for macro testing
|
|
398
459
|
if (oPreprocessorSettings.models["this"]) {
|
|
399
460
|
oPreprocessorSettings.bindingContexts["this"] = oPreprocessorSettings.models["this"].createBindingContext("/");
|
|
400
461
|
}
|
|
401
462
|
|
|
402
|
-
let resultXML = await XMLPreprocessor.process(xmlDoc.firstElementChild!, { name: "Test Fragment" }, oPreprocessorSettings);
|
|
463
|
+
let resultXML = (await XMLPreprocessor.process(xmlDoc.firstElementChild!, { name: "Test Fragment" }, oPreprocessorSettings)) as any;
|
|
403
464
|
|
|
404
|
-
if (
|
|
405
|
-
// prefix Ids
|
|
406
|
-
[...resultXML.querySelectorAll("[id]")].forEach((node) => {
|
|
407
|
-
node.id = `${TESTVIEWID}--${node.id}`;
|
|
408
|
-
});
|
|
465
|
+
if (flexChanges) {
|
|
409
466
|
// apply flex changes
|
|
410
|
-
resultXML = await applyFlexChanges(
|
|
411
|
-
//Assert that all changes have been applied
|
|
412
|
-
const changesApplied = getChangesFromXML(resultXML);
|
|
413
|
-
expect(changesApplied.length).toBe(aVariantDependentControlChanges.length);
|
|
467
|
+
resultXML = await applyFlexChanges(flexChanges, oMetaModel, resultXML);
|
|
414
468
|
}
|
|
415
469
|
return resultXML;
|
|
416
470
|
};
|
|
@@ -418,22 +472,44 @@ export const getTemplatingResult = async function (
|
|
|
418
472
|
export const getTemplatedXML = async function (
|
|
419
473
|
xmlInput: string,
|
|
420
474
|
sMetadataUrl: string,
|
|
421
|
-
mBindingContexts: { [x: string]:
|
|
475
|
+
mBindingContexts: { [x: string]: string },
|
|
422
476
|
mModels: { [x: string]: any },
|
|
423
|
-
|
|
424
|
-
createChangesObject?: Function
|
|
477
|
+
flexChanges?: { [x: string]: object[] }
|
|
425
478
|
) {
|
|
426
|
-
const templatedXML = await getTemplatingResult(
|
|
427
|
-
xmlInput,
|
|
428
|
-
sMetadataUrl,
|
|
429
|
-
mBindingContexts,
|
|
430
|
-
mModels,
|
|
431
|
-
aVariantDependentControlChanges,
|
|
432
|
-
createChangesObject
|
|
433
|
-
);
|
|
479
|
+
const templatedXML = await getTemplatingResult(xmlInput, sMetadataUrl, mBindingContexts, mModels, flexChanges);
|
|
434
480
|
return serializeXML(templatedXML);
|
|
435
481
|
};
|
|
436
482
|
|
|
483
|
+
/**
|
|
484
|
+
* Process the requested view with the provided data.
|
|
485
|
+
*
|
|
486
|
+
* @param name Fully qualified name of the view to be tested.
|
|
487
|
+
* @param sMetadataUrl Url of the metadata.
|
|
488
|
+
* @param mBindingContexts Map of the bindingContexts to set on the models.
|
|
489
|
+
* @param mModels Map of the models.
|
|
490
|
+
* @param flexChanges Object with UI changes like 'changes' or 'variantDependentControlChanges'
|
|
491
|
+
* @returns Templated view as string
|
|
492
|
+
*/
|
|
493
|
+
export async function processView(
|
|
494
|
+
name: string,
|
|
495
|
+
sMetadataUrl: string,
|
|
496
|
+
mBindingContexts: { [x: string]: string },
|
|
497
|
+
mModels: { [x: string]: any },
|
|
498
|
+
flexChanges?: { [x: string]: object[] }
|
|
499
|
+
): Promise<JestTemplatedView> {
|
|
500
|
+
const oMetaModel = await getMetaModel(sMetadataUrl);
|
|
501
|
+
const oViewDocument = _loadResourceView(name);
|
|
502
|
+
const oPreprocessorSettings = await _buildPreProcessorSettings(sMetadataUrl, mBindingContexts, mModels);
|
|
503
|
+
let oPreprocessedView = await XMLPreprocessor.process(oViewDocument, { name: name }, oPreprocessorSettings);
|
|
504
|
+
if (flexChanges) {
|
|
505
|
+
oPreprocessedView = await applyFlexChanges(flexChanges ?? [], oMetaModel, oPreprocessedView);
|
|
506
|
+
}
|
|
507
|
+
return {
|
|
508
|
+
asElement: oPreprocessedView,
|
|
509
|
+
asString: _removeCommentFromXml(oPreprocessedView?.outerHTML || "")
|
|
510
|
+
};
|
|
511
|
+
}
|
|
512
|
+
|
|
437
513
|
/**
|
|
438
514
|
* Process the requested XML fragment with the provided data.
|
|
439
515
|
*
|
|
@@ -462,7 +538,7 @@ export async function processFragment(name: string, testData: { [model: string]:
|
|
|
462
538
|
const resultDoc = await XMLPreprocessor.process(inputDoc.firstElementChild, { name }, settings);
|
|
463
539
|
|
|
464
540
|
// exclude nested fragments from test snapshots
|
|
465
|
-
const fragments = resultDoc.getElementsByTagName("core:Fragment");
|
|
541
|
+
const fragments = resultDoc.getElementsByTagName("core:Fragment") as any;
|
|
466
542
|
if (fragments?.length > 0) {
|
|
467
543
|
for (const fragment of fragments) {
|
|
468
544
|
fragment.innerHTML = "";
|
|
@@ -472,9 +548,7 @@ export async function processFragment(name: string, testData: { [model: string]:
|
|
|
472
548
|
// Keep the fragment result as child of root node when fragment generates multiple root controls
|
|
473
549
|
const xmlResult = resultDoc.children.length > 1 ? resultDoc.outerHTML : resultDoc.innerHTML;
|
|
474
550
|
|
|
475
|
-
return
|
|
476
|
-
filter: (node: any) => node.type !== "Comment"
|
|
477
|
-
});
|
|
551
|
+
return _removeCommentFromXml(xmlResult);
|
|
478
552
|
}
|
|
479
553
|
|
|
480
554
|
export function serializeControl(controlToSerialize: Control | Control[]) {
|
|
@@ -504,7 +578,8 @@ export function serializeControl(controlToSerialize: Control | Control[]) {
|
|
|
504
578
|
return "})";
|
|
505
579
|
},
|
|
506
580
|
middle: function (control: any) {
|
|
507
|
-
|
|
581
|
+
const id = control.getId();
|
|
582
|
+
let data = `{id: ${ManagedObjectMetadata.isGeneratedId(id) ? "__dynamicId" : id}`;
|
|
508
583
|
for (const oControlKey in control.mProperties) {
|
|
509
584
|
if (control.mProperties.hasOwnProperty(oControlKey)) {
|
|
510
585
|
data += `,\n${getTab()} ${oControlKey}: ${control.mProperties[oControlKey]}`;
|
|
@@ -520,6 +595,11 @@ export function serializeControl(controlToSerialize: Control | Control[]) {
|
|
|
520
595
|
data += `,\n${getTab()} ${oControlKey}: ${control.mAssociations[oControlKey][0]}`;
|
|
521
596
|
}
|
|
522
597
|
}
|
|
598
|
+
for (const oControlKey in control.mEventRegistry) {
|
|
599
|
+
if (control.mEventRegistry.hasOwnProperty(oControlKey)) {
|
|
600
|
+
data += `,\n${getTab()} ${oControlKey}: true}`;
|
|
601
|
+
}
|
|
602
|
+
}
|
|
523
603
|
data += ``;
|
|
524
604
|
return data;
|
|
525
605
|
},
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
/*
|
|
2
|
-
* !
|
|
2
|
+
* !
|
|
3
|
+
SAP UI development toolkit for HTML5 (SAPUI5)
|
|
3
4
|
(c) Copyright 2009-2021 SAP SE. All rights reserved
|
|
4
|
-
|
|
5
|
+
|
|
5
6
|
*/
|
|
6
7
|
sap.ui.define(["sap/ui/test/OpaBuilder", "sap/ui/test/Opa5", "sap/fe/test/Utils"], function (OpaBuilder, Opa5, Utils) {
|
|
7
8
|
"use strict";
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
/*
|
|
2
|
-
* !
|
|
2
|
+
* !
|
|
3
|
+
SAP UI development toolkit for HTML5 (SAPUI5)
|
|
3
4
|
(c) Copyright 2009-2021 SAP SE. All rights reserved
|
|
4
|
-
|
|
5
|
+
|
|
5
6
|
*/
|
|
6
7
|
sap.ui.define(
|
|
7
8
|
[
|
|
@@ -114,6 +115,36 @@ sap.ui.define(
|
|
|
114
115
|
.description(Utils.formatMessage("Executing action '{0}' from currently open action menu", vMenuAction));
|
|
115
116
|
}
|
|
116
117
|
|
|
118
|
+
function _getObjectPageSectionSubSectionBuilder(vOpaInstance, SectionSubSectionIdentifier, SectionSubSectionIdPrefix, ControlType) {
|
|
119
|
+
var SectionSubSectionBuilder = FEBuilder.create(vOpaInstance).hasType(ControlType);
|
|
120
|
+
if (Utils.isOfType(SectionSubSectionIdentifier, String)) {
|
|
121
|
+
SectionSubSectionBuilder.hasProperties({ title: SectionSubSectionIdentifier });
|
|
122
|
+
} else if (SectionSubSectionIdentifier.section) {
|
|
123
|
+
SectionSubSectionBuilder.hasId(SectionSubSectionIdPrefix + "::" + SectionSubSectionIdentifier.section);
|
|
124
|
+
} else {
|
|
125
|
+
throw new Error(
|
|
126
|
+
"section or sub-section parameters not supported for creating a control id: " + SectionSubSectionIdentifier
|
|
127
|
+
);
|
|
128
|
+
}
|
|
129
|
+
return SectionSubSectionBuilder;
|
|
130
|
+
}
|
|
131
|
+
function _getObjectPageSectionBuilder(vOpaInstance, ObjectPageSectionIdentifier, OPSectionIdPrefix) {
|
|
132
|
+
return _getObjectPageSectionSubSectionBuilder(
|
|
133
|
+
vOpaInstance,
|
|
134
|
+
ObjectPageSectionIdentifier,
|
|
135
|
+
OPSectionIdPrefix,
|
|
136
|
+
"sap.uxap.ObjectPageSection"
|
|
137
|
+
);
|
|
138
|
+
}
|
|
139
|
+
function _getObjectPageSubSectionBuilder(vOpaInstance, ObjectPageSubSectionIdentifier, OPSubSectionIdPrefix) {
|
|
140
|
+
return _getObjectPageSectionSubSectionBuilder(
|
|
141
|
+
vOpaInstance,
|
|
142
|
+
ObjectPageSubSectionIdentifier,
|
|
143
|
+
OPSubSectionIdPrefix,
|
|
144
|
+
"sap.uxap.ObjectPageSubSection"
|
|
145
|
+
);
|
|
146
|
+
}
|
|
147
|
+
|
|
117
148
|
/**
|
|
118
149
|
* Constructs a new ObjectPage instance.
|
|
119
150
|
*
|
|
@@ -204,7 +235,7 @@ sap.ui.define(
|
|
|
204
235
|
* @param {sap.fe.test.api.FilterBarIdentifier | string} vFilterBarIdentifier The identifier of the filterbar
|
|
205
236
|
* @returns {sap.fe.test.api.FilterBarActions} The available filter bar actions
|
|
206
237
|
* @function
|
|
207
|
-
* @alias sap.fe.test.
|
|
238
|
+
* @alias sap.fe.test.ObjectPage.actions#onFilterBar
|
|
208
239
|
* @public
|
|
209
240
|
*/
|
|
210
241
|
onFilterBar: function (vFilterBarIdentifier) {
|
|
@@ -260,12 +291,9 @@ sap.ui.define(
|
|
|
260
291
|
vFormIdentifier.fullSubSectionId = ViewId + "--" + vFormIdentifier.id;
|
|
261
292
|
}
|
|
262
293
|
if (vFormIdentifier.fieldGroup) {
|
|
263
|
-
|
|
264
|
-
vFormIdentifier.
|
|
265
|
-
|
|
266
|
-
} else {
|
|
267
|
-
vFormIdentifier.fieldGroupId = OPFormContainerIdPrefix + "::" + vFormIdentifier.fieldGroup;
|
|
268
|
-
}
|
|
294
|
+
vFormIdentifier.fieldGroupId = vFormIdentifier.isHeaderFacet
|
|
295
|
+
? OPFormContainerHeaderFacetsIdPrefix + "::" + vFormIdentifier.fieldGroup
|
|
296
|
+
: OPFormContainerIdPrefix + "::" + vFormIdentifier.fieldGroup;
|
|
269
297
|
}
|
|
270
298
|
}
|
|
271
299
|
return new FormActions(_getFormBuilder(this, vFormIdentifier), vFormIdentifier);
|
|
@@ -442,14 +470,14 @@ sap.ui.define(
|
|
|
442
470
|
iClickOnMessageButton: function () {
|
|
443
471
|
return OpaBuilder.create(this)
|
|
444
472
|
|
|
445
|
-
.hasType("sap.fe.
|
|
473
|
+
.hasType("sap.fe.macros.messages.MessageButton")
|
|
446
474
|
.doPress()
|
|
447
475
|
.description("Clicked on Message Button")
|
|
448
476
|
.execute();
|
|
449
477
|
},
|
|
450
478
|
iCheckMessageButtonTooltip: function (sText) {
|
|
451
479
|
return OpaBuilder.create(this)
|
|
452
|
-
.hasType("sap.fe.
|
|
480
|
+
.hasType("sap.fe.macros.messages.MessageButton")
|
|
453
481
|
.check(function (oControl) {
|
|
454
482
|
return oControl[0].getTooltip() === sText;
|
|
455
483
|
}, true)
|
|
@@ -528,7 +556,7 @@ sap.ui.define(
|
|
|
528
556
|
* @param {sap.fe.test.api.FilterBarIdentifier | string} vFilterBarIdentifier The identifier of the filterbar
|
|
529
557
|
* @returns {sap.fe.test.api.FilterBarAssertions} The available filter bar assertions
|
|
530
558
|
* @function
|
|
531
|
-
* @alias sap.fe.test.
|
|
559
|
+
* @alias sap.fe.test.ObjectPage.assertions#onFilterBar
|
|
532
560
|
* @public
|
|
533
561
|
*/
|
|
534
562
|
onFilterBar: function (vFilterBarIdentifier) {
|
|
@@ -582,7 +610,9 @@ sap.ui.define(
|
|
|
582
610
|
vFormIdentifier.fullSubSectionId = ViewId + "--" + vFormIdentifier.id;
|
|
583
611
|
}
|
|
584
612
|
if (vFormIdentifier.fieldGroup) {
|
|
585
|
-
vFormIdentifier.fieldGroupId =
|
|
613
|
+
vFormIdentifier.fieldGroupId = vFormIdentifier.isHeaderFacet
|
|
614
|
+
? OPFormContainerHeaderFacetsIdPrefix + "::" + vFormIdentifier.fieldGroup
|
|
615
|
+
: OPFormContainerIdPrefix + "::" + vFormIdentifier.fieldGroup;
|
|
586
616
|
}
|
|
587
617
|
}
|
|
588
618
|
return new FormAssertions(_getFormBuilder(this, vFormIdentifier), vFormIdentifier);
|
|
@@ -591,17 +621,17 @@ sap.ui.define(
|
|
|
591
621
|
iSeeMessageButton: function (messageType, messageButtonText) {
|
|
592
622
|
var message = {
|
|
593
623
|
Error: {
|
|
594
|
-
|
|
624
|
+
type: "Negative"
|
|
595
625
|
},
|
|
596
626
|
Warning: {
|
|
597
|
-
|
|
627
|
+
type: "Critical"
|
|
598
628
|
},
|
|
599
629
|
Information: {
|
|
600
|
-
|
|
630
|
+
type: "Neutral"
|
|
601
631
|
}
|
|
602
632
|
};
|
|
603
633
|
return OpaBuilder.create(this)
|
|
604
|
-
.hasType("sap.fe.
|
|
634
|
+
.hasType("sap.fe.macros.messages.MessageButton")
|
|
605
635
|
.hasProperties({
|
|
606
636
|
text: messageButtonText,
|
|
607
637
|
type: message[messageType]["type"]
|
|
@@ -685,20 +715,93 @@ sap.ui.define(
|
|
|
685
715
|
.description("Object Page is in mode '" + sMode + "'")
|
|
686
716
|
.execute();
|
|
687
717
|
},
|
|
718
|
+
/**
|
|
719
|
+
* Checks a section.
|
|
720
|
+
* It is checked if a section is already loaded and therefore the data is visible to the user. This function does not check properties of the anchor bar buttons for section selection.
|
|
721
|
+
*
|
|
722
|
+
* @param {string | sap.fe.test.api.SectionIdentifier} SectionIdentifier The identifier of the section.
|
|
723
|
+
* This can either be a string containing the label of the section or an object having the pattern
|
|
724
|
+
* <code><pre>
|
|
725
|
+
* {
|
|
726
|
+
* section: <section id>
|
|
727
|
+
* }
|
|
728
|
+
* </pre></code>
|
|
729
|
+
* @param {object} mState Defines the expected state of the section, e.g. its visibility
|
|
730
|
+
* @returns {object} The result of the {@link sap.ui.test.Opa5#waitFor} function, to be used for chained statements
|
|
731
|
+
* @public
|
|
732
|
+
*/
|
|
733
|
+
iCheckSection: function (SectionIdentifier, mState) {
|
|
734
|
+
var SectionBuilder = _getObjectPageSectionBuilder(this, SectionIdentifier, OPSectionIdPrefix);
|
|
735
|
+
return SectionBuilder.hasState(mState)
|
|
736
|
+
.description(
|
|
737
|
+
Utils.formatMessage("Checking section '{0}' having state='{1}'", SectionIdentifier, mState)
|
|
738
|
+
)
|
|
739
|
+
.execute();
|
|
740
|
+
},
|
|
741
|
+
/**
|
|
742
|
+
* TODO This function is only here for legacy reasons and therefore private. Use iCheckSection instead.
|
|
743
|
+
*
|
|
744
|
+
* @param {string} sTitle The name of the section
|
|
745
|
+
* @returns {object} The result of the {@link sap.ui.test.Opa5#waitFor} function, to be used for chained statements
|
|
746
|
+
* @private
|
|
747
|
+
*/
|
|
688
748
|
iSeeSectionWithTitle: function (sTitle) {
|
|
689
|
-
return
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
749
|
+
return this.iCheckSection(sTitle, { visible: true });
|
|
750
|
+
},
|
|
751
|
+
|
|
752
|
+
/**
|
|
753
|
+
* Checks the expected number of sections in an object page.
|
|
754
|
+
*
|
|
755
|
+
* @param {number} ExpectedNumberOfSections The number of expected sections within the object page.
|
|
756
|
+
* @returns {object} The result of the {@link sap.ui.test.Opa5#waitFor} function, to be used for chained statements
|
|
757
|
+
* @public
|
|
758
|
+
*/
|
|
759
|
+
iCheckNumberOfSections: function (ExpectedNumberOfSections) {
|
|
760
|
+
return FEBuilder.create(this)
|
|
761
|
+
.hasId(AnchorBarId)
|
|
762
|
+
.hasAggregationLength("content", ExpectedNumberOfSections)
|
|
763
|
+
.description(
|
|
764
|
+
Utils.formatMessage(
|
|
765
|
+
"Object Page contains the expected number of {0} sections",
|
|
766
|
+
ExpectedNumberOfSections
|
|
767
|
+
)
|
|
768
|
+
)
|
|
693
769
|
.execute();
|
|
694
770
|
},
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
771
|
+
|
|
772
|
+
/**
|
|
773
|
+
* Checks a sub-section.
|
|
774
|
+
*
|
|
775
|
+
* @param {string | sap.fe.test.api.SectionIdentifier} SubSectionIdentifier The identifier of the sub-section to be checked for visibility.
|
|
776
|
+
* This can either be a string containing the label of the sub-section or an object having the pattern
|
|
777
|
+
* <code><pre>
|
|
778
|
+
* {
|
|
779
|
+
* section: <sub-section id>
|
|
780
|
+
* }
|
|
781
|
+
* </pre></code>
|
|
782
|
+
* @param {object} mState Defines the expected state of the sub-section, e.g. its visibility
|
|
783
|
+
* @returns {object} The result of the {@link sap.ui.test.Opa5#waitFor} function, to be used for chained statements
|
|
784
|
+
* @public
|
|
785
|
+
*/
|
|
786
|
+
iCheckSubSection: function (SubSectionIdentifier, mState) {
|
|
787
|
+
var SubSectionBuilder = _getObjectPageSubSectionBuilder(this, SubSectionIdentifier, OPSubSectionIdPrefix);
|
|
788
|
+
return SubSectionBuilder.hasState(mState)
|
|
789
|
+
.description(
|
|
790
|
+
Utils.formatMessage("Checking sub-section '{0}' having state='{1}'", SubSectionIdentifier, mState)
|
|
791
|
+
)
|
|
700
792
|
.execute();
|
|
701
793
|
},
|
|
794
|
+
/**
|
|
795
|
+
* TODO This function is only here for legacy reasons and therefore private. Use iCheckSubSection instead.
|
|
796
|
+
*
|
|
797
|
+
* @param {string} sTitle The name of the sub-section
|
|
798
|
+
* @returns {object} The result of the {@link sap.ui.test.Opa5#waitFor} function, to be used for chained statements
|
|
799
|
+
* @private
|
|
800
|
+
*/
|
|
801
|
+
iSeeSubSectionWithTitle: function (sTitle) {
|
|
802
|
+
return this.iCheckSubSection(sTitle, { visible: true });
|
|
803
|
+
},
|
|
804
|
+
|
|
702
805
|
iSeeSectionButtonWithTitle: function (sTitle, mState) {
|
|
703
806
|
return FEBuilder.create(this)
|
|
704
807
|
.hasId(AnchorBarId)
|