@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.
Files changed (84) hide show
  1. package/README.md +5 -1
  2. package/package.json +9 -7
  3. package/src/sap/fe/test/.library +17 -18
  4. package/src/sap/fe/test/BaseActions.js +3 -2
  5. package/src/sap/fe/test/BaseArrangements.js +3 -2
  6. package/src/sap/fe/test/BaseAssertions.js +26 -2
  7. package/src/sap/fe/test/CollaborationClient.js +21 -5
  8. package/src/sap/fe/test/CollaborationClient.ts +29 -1
  9. package/src/sap/fe/test/ConfirmDialog.js +12 -11
  10. package/src/sap/fe/test/FCLView.js +16 -15
  11. package/src/sap/fe/test/FeMocks.js +1 -11
  12. package/src/sap/fe/test/Flexibility.js +12 -7
  13. package/src/sap/fe/test/FlexibleColumnLayout.js +12 -11
  14. package/src/sap/fe/test/JestFlexibilityHelper.js +141 -0
  15. package/src/sap/fe/test/JestFlexibilityHelper.ts +135 -0
  16. package/src/sap/fe/test/JestTemplatingHelper.js +191 -207
  17. package/src/sap/fe/test/JestTemplatingHelper.ts +171 -91
  18. package/src/sap/fe/test/JourneyRunner.js +3 -2
  19. package/src/sap/fe/test/ListReport.js +3 -2
  20. package/src/sap/fe/test/LocationUtil.js +3 -2
  21. package/src/sap/fe/test/ObjectPage.js +129 -26
  22. package/src/sap/fe/test/Shell.js +9 -4
  23. package/src/sap/fe/test/Stubs.js +18 -17
  24. package/src/sap/fe/test/TemplatePage.js +3 -2
  25. package/src/sap/fe/test/TemplatingTestUtils.js +3 -2
  26. package/src/sap/fe/test/UI5MockHelper.js +39 -72
  27. package/src/sap/fe/test/UI5MockHelper.ts +3 -0
  28. package/src/sap/fe/test/Utils.js +4 -3
  29. package/src/sap/fe/test/api/APIHelper.js +3 -2
  30. package/src/sap/fe/test/api/BaseAPI.js +3 -2
  31. package/src/sap/fe/test/api/ChartActions.js +3 -2
  32. package/src/sap/fe/test/api/ChartAssertions.js +3 -2
  33. package/src/sap/fe/test/api/CollaborationAPI.js +2 -30
  34. package/src/sap/fe/test/api/DialogAPI.js +3 -2
  35. package/src/sap/fe/test/api/DialogActions.js +3 -2
  36. package/src/sap/fe/test/api/DialogAssertions.js +4 -3
  37. package/src/sap/fe/test/api/DialogCreateActions.js +6 -5
  38. package/src/sap/fe/test/api/DialogCreateAssertions.js +48 -49
  39. package/src/sap/fe/test/api/DialogHelper.js +4 -3
  40. package/src/sap/fe/test/api/DialogMassEditActions.js +3 -2
  41. package/src/sap/fe/test/api/DialogMassEditAssertions.js +3 -2
  42. package/src/sap/fe/test/api/DialogMessageActions.js +3 -2
  43. package/src/sap/fe/test/api/DialogMessageAssertions.js +82 -79
  44. package/src/sap/fe/test/api/DialogType.js +3 -2
  45. package/src/sap/fe/test/api/DialogValueHelpActions.js +3 -2
  46. package/src/sap/fe/test/api/DialogValueHelpAssertions.js +3 -2
  47. package/src/sap/fe/test/api/EditState.js +4 -3
  48. package/src/sap/fe/test/api/FilterBarAPI.js +3 -2
  49. package/src/sap/fe/test/api/FilterBarActions.js +3 -2
  50. package/src/sap/fe/test/api/FilterBarAssertions.js +3 -2
  51. package/src/sap/fe/test/api/FooterAPI.js +5 -4
  52. package/src/sap/fe/test/api/FooterActionsBase.js +3 -2
  53. package/src/sap/fe/test/api/FooterActionsOP.js +3 -2
  54. package/src/sap/fe/test/api/FooterAssertionsBase.js +3 -2
  55. package/src/sap/fe/test/api/FooterAssertionsOP.js +3 -2
  56. package/src/sap/fe/test/api/FormAPI.js +4 -2
  57. package/src/sap/fe/test/api/FormActions.js +3 -2
  58. package/src/sap/fe/test/api/FormAssertions.js +3 -2
  59. package/src/sap/fe/test/api/HeaderAPI.js +3 -2
  60. package/src/sap/fe/test/api/HeaderActions.js +3 -2
  61. package/src/sap/fe/test/api/HeaderActionsLR.js +3 -2
  62. package/src/sap/fe/test/api/HeaderAssertions.js +3 -2
  63. package/src/sap/fe/test/api/HeaderAssertionsLR.js +3 -2
  64. package/src/sap/fe/test/api/HeaderLR.js +3 -2
  65. package/src/sap/fe/test/api/KPICardAPI.js +5 -4
  66. package/src/sap/fe/test/api/KPICardActions.js +7 -11
  67. package/src/sap/fe/test/api/KPICardAssertions.js +3 -2
  68. package/src/sap/fe/test/api/TableAPI.js +5 -4
  69. package/src/sap/fe/test/api/TableActions.js +16 -9
  70. package/src/sap/fe/test/api/TableAssertions.js +8 -7
  71. package/src/sap/fe/test/builder/DialogBuilder.js +99 -102
  72. package/src/sap/fe/test/builder/FEBuilder.js +3 -2
  73. package/src/sap/fe/test/builder/KPIBuilder.js +16 -25
  74. package/src/sap/fe/test/builder/MacroFieldBuilder.js +4 -2
  75. package/src/sap/fe/test/builder/MdcFieldBuilder.js +3 -2
  76. package/src/sap/fe/test/builder/MdcFilterBarBuilder.js +3 -2
  77. package/src/sap/fe/test/builder/MdcFilterFieldBuilder.js +3 -2
  78. package/src/sap/fe/test/builder/MdcTableBuilder.js +196 -96
  79. package/src/sap/fe/test/builder/OverflowToolbarBuilder.js +3 -2
  80. package/src/sap/fe/test/builder/VMBuilder.js +3 -2
  81. package/src/sap/fe/test/internal/ConsoleErrorChecker.js +13 -48
  82. package/src/sap/fe/test/internal/FEArrangements.js +3 -2
  83. package/src/sap/fe/test/internal/FEJourneyRunner.js +5 -3
  84. 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 { RequiredOptions } from "prettier";
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
- "macros": "sap.fe.macros",
43
- "macro": "sap.fe.macros",
44
- "macroField": "sap.fe.macros.field",
45
- "macrodata": "http://schemas.sap.com/sapui5/extension/sap.ui.core.CustomData/1",
46
- "log": "http://schemas.sap.com/sapui5/extension/sap.ui.core.CustomData/1",
47
- "unittest": "http://schemas.sap.com/sapui5/preprocessorextension/sap.fe.unittesting/1",
48
- "control": "sap.fe.core.controls",
49
- "core": "sap.ui.core",
50
- "m": "sap.m",
51
- "f": "sap.ui.layout.form",
52
- "internalMacro": "sap.fe.macros.internal",
53
- "mdc": "sap.ui.mdc",
54
- "mdcat": "sap.ui.mdc.actiontoolbar",
55
- "mdcField": "sap.ui.mdc.field",
56
- "mdcTable": "sap.ui.mdc.table",
57
- "u": "sap.ui.unified",
58
- "macroMicroChart": "sap.fe.macros.microchart",
59
- "microChart": "sap.suite.ui.microchart",
60
- "macroTable": "sap.fe.macros.table"
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(`/root${selector}`, xmldom);
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(`/root${selector}`, xmldom);
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(/root${controlSelector}/@${attributeName})`;
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
- xmlString,
123
- { parser: "xml", xmlWhitespaceSensitivity: "ignore" } as Partial<RequiredOptions> /* options by the Prettier XML plugin */
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
- export const applyFlexChanges = async function (
309
- aVariantDependentControlChanges: any[],
310
- oMetaModel: MetaModel,
311
- resultXML: any,
312
- createChangesObject: Function
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
- aVariantDependentControlChanges?: any[],
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
- if (!mModels.hasOwnProperty("converterContext")) {
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 (aVariantDependentControlChanges && createChangesObject) {
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(aVariantDependentControlChanges, oMetaModel, resultXML, createChangesObject);
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]: any; entitySet?: string },
475
+ mBindingContexts: { [x: string]: string },
422
476
  mModels: { [x: string]: any },
423
- aVariantDependentControlChanges?: any[],
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 formatXml(xmlResult, {
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
- let data = `{id: ${control.getId()}`;
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
- * ! SAP UI development toolkit for HTML5 (SAPUI5)
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
  /* global QUnit */
7
8
  sap.ui.define(
@@ -1,7 +1,8 @@
1
1
  /*
2
- * ! SAP UI development toolkit for HTML5 (SAPUI5)
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
  [
@@ -1,7 +1,8 @@
1
1
  /*
2
- * ! SAP UI development toolkit for HTML5 (SAPUI5)
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
- * ! SAP UI development toolkit for HTML5 (SAPUI5)
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.ListReport.actions#onFilterBar
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
- if (vFormIdentifier.isHeaderFacet) {
264
- vFormIdentifier.fieldGroupId =
265
- OPFormContainerHeaderFacetsIdPrefix + "::" + vFormIdentifier.fieldGroup;
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.common.MessageButton")
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.common.MessageButton")
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.ListReport.assertions#onFilterBar
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 = OPFormContainerIdPrefix + "::" + vFormIdentifier.fieldGroup;
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
- "type": "Negative"
624
+ type: "Negative"
595
625
  },
596
626
  Warning: {
597
- "type": "Critical"
627
+ type: "Critical"
598
628
  },
599
629
  Information: {
600
- "type": "Neutral"
630
+ type: "Neutral"
601
631
  }
602
632
  };
603
633
  return OpaBuilder.create(this)
604
- .hasType("sap.fe.common.MessageButton")
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 OpaBuilder.create(this)
690
- .hasType("sap.uxap.ObjectPageSection")
691
- .hasProperties({ title: sTitle })
692
- .description("Seeing section with title '" + sTitle + "'")
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
- iSeeSubSectionWithTitle: function (sTitle) {
696
- return OpaBuilder.create(this)
697
- .hasType("sap.uxap.ObjectPageSubSection")
698
- .hasProperties({ title: sTitle })
699
- .description("Seeing sub-section with title '" + sTitle + "'")
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)