@sapui5/sap.fe.test 1.108.1 → 1.109.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.
@@ -5,6 +5,7 @@ import * as path from "path";
5
5
  import type { RequiredOptions } from "prettier";
6
6
  import { format } from "prettier";
7
7
  import Log from "sap/base/Log";
8
+ import LoaderExtensions from "sap/base/util/LoaderExtensions";
8
9
  import merge from "sap/base/util/merge";
9
10
  import AppComponent from "sap/fe/core/AppComponent";
10
11
  import { registerBuildingBlock } from "sap/fe/core/buildingBlocks/BuildingBlockRuntime";
@@ -32,6 +33,22 @@ import MetaModel from "sap/ui/model/MetaModel";
32
33
  import _MetadataRequestor from "sap/ui/model/odata/v4/lib/_MetadataRequestor";
33
34
  import ODataMetaModel from "sap/ui/model/odata/v4/ODataMetaModel";
34
35
  import xpath from "xpath";
36
+ import { createFlexibilityChangesObject } from "./JestFlexibilityHelper";
37
+
38
+ type PreProcessorSettingsType = {
39
+ models: {
40
+ [name: string]: JSONModel | ODataMetaModel;
41
+ };
42
+ bindingContexts: {
43
+ [name: string]: Context | undefined;
44
+ };
45
+ };
46
+
47
+ type JestTemplatedView = {
48
+ asElement: Element | undefined;
49
+ asString: string;
50
+ };
51
+
35
52
  // eslint-disable-next-line @typescript-eslint/no-var-requires
36
53
  const formatXml = require("xml-formatter");
37
54
 
@@ -61,6 +78,67 @@ const nameSpaceMap = {
61
78
  };
62
79
  const select = xpath.useNamespaces(nameSpaceMap);
63
80
 
81
+ function _getTemplatedSelector(xmldom: Node, selector: string): string {
82
+ /**
83
+ * if a root element has been added during the templating by our Jest Templating methods
84
+ * the root element is added to the selector path.
85
+ */
86
+ const rootPath = "/root";
87
+ return `${xmldom.nodeName === "root" && !selector.startsWith(rootPath) ? rootPath : ""}${selector}`;
88
+ }
89
+
90
+ async function _buildPreProcessorSettings(
91
+ sMetadataUrl: string,
92
+ mBindingContexts: { [x: string]: string },
93
+ mModels: { [x: string]: any }
94
+ ): Promise<PreProcessorSettingsType> {
95
+ const oMetaModel = await getMetaModel(sMetadataUrl);
96
+
97
+ // To ensure our macro can use #setBindingContext we ensure there is a pre existing JSONModel for converterContext
98
+ // if not already passed to teh templating
99
+ if (!mModels.hasOwnProperty("converterContext")) {
100
+ mModels = Object.assign(mModels, { "converterContext": new TemplateModel({}, oMetaModel) });
101
+ }
102
+
103
+ Object.keys(mModels).forEach(function (sModelName) {
104
+ if (mModels[sModelName] && mModels[sModelName].isTemplateModel) {
105
+ mModels[sModelName] = new TemplateModel(mModels[sModelName].data, oMetaModel);
106
+ }
107
+ });
108
+
109
+ const settings: any = {
110
+ models: Object.assign(
111
+ {
112
+ metaModel: oMetaModel
113
+ },
114
+ mModels
115
+ ),
116
+ bindingContexts: {}
117
+ };
118
+
119
+ //Inject models and bindingContexts
120
+ Object.keys(mBindingContexts).forEach(function (sKey) {
121
+ /* Assert to make sure the annotations are in the test metadata -> avoid misleading tests */
122
+ expect(typeof oMetaModel.getObject(mBindingContexts[sKey])).toBeDefined();
123
+ const oModel = mModels[sKey] || oMetaModel;
124
+ settings.bindingContexts[sKey] = oModel.createBindingContext(mBindingContexts[sKey]); //Value is sPath
125
+ settings.models[sKey] = oModel;
126
+ });
127
+ return settings;
128
+ }
129
+
130
+ function _removeCommentFromXml(xml: string): string {
131
+ return formatXml(xml, {
132
+ filter: (node: any) => node.type !== "Comment"
133
+ });
134
+ }
135
+
136
+ function _loadResourceView(viewName: string): Element {
137
+ const name = viewName.replace(/\./g, "/") + ".view.xml";
138
+ const view = LoaderExtensions.loadResource(name);
139
+ return view.documentElement;
140
+ }
141
+
64
142
  export const registerMacro = function (macroMetadata: any) {
65
143
  registerBuildingBlock(macroMetadata);
66
144
  };
@@ -76,7 +154,7 @@ export const runXPathQuery = function (selector: string, xmldom: Node | undefine
76
154
 
77
155
  expect.extend({
78
156
  toHaveControl(xmldom, selector) {
79
- const nodes = runXPathQuery(`/root${selector}`, xmldom);
157
+ const nodes = runXPathQuery(_getTemplatedSelector(xmldom, selector), xmldom);
80
158
  return {
81
159
  message: () => {
82
160
  const outputXml = serializeXML(xmldom);
@@ -86,7 +164,7 @@ expect.extend({
86
164
  };
87
165
  },
88
166
  toNotHaveControl(xmldom, selector) {
89
- const nodes = runXPathQuery(`/root${selector}`, xmldom);
167
+ const nodes = runXPathQuery(_getTemplatedSelector(xmldom, selector), xmldom);
90
168
  return {
91
169
  message: () => {
92
170
  const outputXml = serializeXML(xmldom);
@@ -107,7 +185,7 @@ export const formatBuildingBlockXML = function (xmlString: string | string[]) {
107
185
  };
108
186
 
109
187
  export const getControlAttribute = function (controlSelector: string, attributeName: string, xmlDom: Node) {
110
- const selector = `string(/root${controlSelector}/@${attributeName})`;
188
+ const selector = `string(${_getTemplatedSelector(xmlDom, controlSelector)}/@${attributeName})`;
111
189
  return runXPathQuery(selector, xmlDom);
112
190
  };
113
191
 
@@ -305,13 +383,12 @@ export function evaluateBindingWithModel(
305
383
 
306
384
  const TESTVIEWID = "testViewId";
307
385
 
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);
386
+ const applyFlexChanges = async function (aVariantDependentControlChanges: any[], oMetaModel: MetaModel, resultXML: any) {
387
+ // prefix Ids
388
+ [...resultXML.querySelectorAll("[id]")].forEach((node) => {
389
+ node.id = `${TESTVIEWID}--${node.id}`;
390
+ });
391
+ const changes = createFlexibilityChangesObject(TESTVIEWID, aVariantDependentControlChanges);
315
392
  const appId = "someComponent";
316
393
  const oManifest = {
317
394
  "sap.app": {
@@ -342,6 +419,10 @@ export const applyFlexChanges = async function (
342
419
  componentId: appId
343
420
  });
344
421
  resultXML = await XmlPreprocessor.process(resultXML, { name: "Test Fragment", componentId: appId, id: TESTVIEWID });
422
+
423
+ //Assert that all changes have been applied
424
+ const changesApplied = getChangesFromXML(resultXML);
425
+ expect(changesApplied.length).toBe(aVariantDependentControlChanges.length);
345
426
  return resultXML;
346
427
  };
347
428
 
@@ -355,8 +436,7 @@ export const getTemplatingResult = async function (
355
436
  sMetadataUrl: string,
356
437
  mBindingContexts: { [x: string]: any; entitySet?: string },
357
438
  mModels: { [x: string]: any },
358
- aVariantDependentControlChanges?: any[],
359
- createChangesObject?: Function
439
+ aVariantDependentControlChanges?: any[]
360
440
  ) {
361
441
  const templatedXml = `<root>${xmlInput}</root>`;
362
442
  const parser = new window.DOMParser();
@@ -365,52 +445,18 @@ export const getTemplatingResult = async function (
365
445
  // if not already passed to teh templating
366
446
 
367
447
  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
- });
448
+ const oPreprocessorSettings = await _buildPreProcessorSettings(sMetadataUrl, mBindingContexts, mModels);
396
449
 
397
450
  //This context for macro testing
398
451
  if (oPreprocessorSettings.models["this"]) {
399
452
  oPreprocessorSettings.bindingContexts["this"] = oPreprocessorSettings.models["this"].createBindingContext("/");
400
453
  }
401
454
 
402
- let resultXML = await XMLPreprocessor.process(xmlDoc.firstElementChild!, { name: "Test Fragment" }, oPreprocessorSettings);
455
+ let resultXML = (await XMLPreprocessor.process(xmlDoc.firstElementChild!, { name: "Test Fragment" }, oPreprocessorSettings)) as any;
403
456
 
404
- if (aVariantDependentControlChanges && createChangesObject) {
405
- // prefix Ids
406
- [...resultXML.querySelectorAll("[id]")].forEach((node) => {
407
- node.id = `${TESTVIEWID}--${node.id}`;
408
- });
457
+ if (aVariantDependentControlChanges) {
409
458
  // 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);
459
+ resultXML = await applyFlexChanges(aVariantDependentControlChanges, oMetaModel, resultXML);
414
460
  }
415
461
  return resultXML;
416
462
  };
@@ -418,22 +464,44 @@ export const getTemplatingResult = async function (
418
464
  export const getTemplatedXML = async function (
419
465
  xmlInput: string,
420
466
  sMetadataUrl: string,
421
- mBindingContexts: { [x: string]: any; entitySet?: string },
467
+ mBindingContexts: { [x: string]: string },
422
468
  mModels: { [x: string]: any },
423
- aVariantDependentControlChanges?: any[],
424
- createChangesObject?: Function
469
+ aVariantDependentControlChanges?: any[]
425
470
  ) {
426
- const templatedXML = await getTemplatingResult(
427
- xmlInput,
428
- sMetadataUrl,
429
- mBindingContexts,
430
- mModels,
431
- aVariantDependentControlChanges,
432
- createChangesObject
433
- );
471
+ const templatedXML = await getTemplatingResult(xmlInput, sMetadataUrl, mBindingContexts, mModels, aVariantDependentControlChanges);
434
472
  return serializeXML(templatedXML);
435
473
  };
436
474
 
475
+ /**
476
+ * Process the requested view with the provided data.
477
+ *
478
+ * @param name Fully qualified name of the view to be tested.
479
+ * @param sMetadataUrl Url of the metadata.
480
+ * @param mBindingContexts Map of the bindingContexts to set on the models.
481
+ * @param mModels Map of the models.
482
+ * @param aVariantDependentControlChanges
483
+ * @returns Templated view as string
484
+ */
485
+ export async function processView(
486
+ name: string,
487
+ sMetadataUrl: string,
488
+ mBindingContexts: { [x: string]: string },
489
+ mModels: { [x: string]: any },
490
+ aVariantDependentControlChanges?: any[]
491
+ ): Promise<JestTemplatedView> {
492
+ const oMetaModel = await getMetaModel(sMetadataUrl);
493
+ const oViewDocument = _loadResourceView(name);
494
+ const oPreprocessorSettings = await _buildPreProcessorSettings(sMetadataUrl, mBindingContexts, mModels);
495
+ let oPreprocessedView = await XMLPreprocessor.process(oViewDocument, { name: name }, oPreprocessorSettings);
496
+ if (aVariantDependentControlChanges) {
497
+ oPreprocessedView = await applyFlexChanges(aVariantDependentControlChanges || [], oMetaModel, oPreprocessedView);
498
+ }
499
+ return {
500
+ asElement: oPreprocessedView,
501
+ asString: _removeCommentFromXml(oPreprocessedView?.outerHTML || "")
502
+ };
503
+ }
504
+
437
505
  /**
438
506
  * Process the requested XML fragment with the provided data.
439
507
  *
@@ -462,7 +530,7 @@ export async function processFragment(name: string, testData: { [model: string]:
462
530
  const resultDoc = await XMLPreprocessor.process(inputDoc.firstElementChild, { name }, settings);
463
531
 
464
532
  // exclude nested fragments from test snapshots
465
- const fragments = resultDoc.getElementsByTagName("core:Fragment");
533
+ const fragments = resultDoc.getElementsByTagName("core:Fragment") as any;
466
534
  if (fragments?.length > 0) {
467
535
  for (const fragment of fragments) {
468
536
  fragment.innerHTML = "";
@@ -472,9 +540,7 @@ export async function processFragment(name: string, testData: { [model: string]:
472
540
  // Keep the fragment result as child of root node when fragment generates multiple root controls
473
541
  const xmlResult = resultDoc.children.length > 1 ? resultDoc.outerHTML : resultDoc.innerHTML;
474
542
 
475
- return formatXml(xmlResult, {
476
- filter: (node: any) => node.type !== "Comment"
477
- });
543
+ return _removeCommentFromXml(xmlResult);
478
544
  }
479
545
 
480
546
  export function serializeControl(controlToSerialize: Control | Control[]) {
@@ -204,7 +204,7 @@ sap.ui.define(
204
204
  * @param {sap.fe.test.api.FilterBarIdentifier | string} vFilterBarIdentifier The identifier of the filterbar
205
205
  * @returns {sap.fe.test.api.FilterBarActions} The available filter bar actions
206
206
  * @function
207
- * @alias sap.fe.test.ListReport.actions#onFilterBar
207
+ * @alias sap.fe.test.ObjectPage.actions#onFilterBar
208
208
  * @public
209
209
  */
210
210
  onFilterBar: function (vFilterBarIdentifier) {
@@ -260,12 +260,9 @@ sap.ui.define(
260
260
  vFormIdentifier.fullSubSectionId = ViewId + "--" + vFormIdentifier.id;
261
261
  }
262
262
  if (vFormIdentifier.fieldGroup) {
263
- if (vFormIdentifier.isHeaderFacet) {
264
- vFormIdentifier.fieldGroupId =
265
- OPFormContainerHeaderFacetsIdPrefix + "::" + vFormIdentifier.fieldGroup;
266
- } else {
267
- vFormIdentifier.fieldGroupId = OPFormContainerIdPrefix + "::" + vFormIdentifier.fieldGroup;
268
- }
263
+ vFormIdentifier.fieldGroupId = vFormIdentifier.isHeaderFacet
264
+ ? OPFormContainerHeaderFacetsIdPrefix + "::" + vFormIdentifier.fieldGroup
265
+ : OPFormContainerIdPrefix + "::" + vFormIdentifier.fieldGroup;
269
266
  }
270
267
  }
271
268
  return new FormActions(_getFormBuilder(this, vFormIdentifier), vFormIdentifier);
@@ -528,7 +525,7 @@ sap.ui.define(
528
525
  * @param {sap.fe.test.api.FilterBarIdentifier | string} vFilterBarIdentifier The identifier of the filterbar
529
526
  * @returns {sap.fe.test.api.FilterBarAssertions} The available filter bar assertions
530
527
  * @function
531
- * @alias sap.fe.test.ListReport.assertions#onFilterBar
528
+ * @alias sap.fe.test.ObjectPage.assertions#onFilterBar
532
529
  * @public
533
530
  */
534
531
  onFilterBar: function (vFilterBarIdentifier) {
@@ -582,7 +579,9 @@ sap.ui.define(
582
579
  vFormIdentifier.fullSubSectionId = ViewId + "--" + vFormIdentifier.id;
583
580
  }
584
581
  if (vFormIdentifier.fieldGroup) {
585
- vFormIdentifier.fieldGroupId = OPFormContainerIdPrefix + "::" + vFormIdentifier.fieldGroup;
582
+ vFormIdentifier.fieldGroupId = vFormIdentifier.isHeaderFacet
583
+ ? OPFormContainerHeaderFacetsIdPrefix + "::" + vFormIdentifier.fieldGroup
584
+ : OPFormContainerIdPrefix + "::" + vFormIdentifier.fieldGroup;
586
585
  }
587
586
  }
588
587
  return new FormAssertions(_getFormBuilder(this, vFormIdentifier), vFormIdentifier);
@@ -21,6 +21,7 @@ sap.ui.define(
21
21
  * @typedef {object} FormIdentifier
22
22
  * @property {string} section The facet ID
23
23
  * @property {string} fieldGroup The fieldgroup ID
24
+ * @property {boolean} [isHeaderFacet] Is it about the editable header facet
24
25
  * @name sap.fe.test.api.FormIdentifier
25
26
  * @public
26
27
  */
@@ -57,7 +57,7 @@ sap.ui.define(
57
57
  };
58
58
 
59
59
  var TableBuilder = function () {
60
- return FEBuilder.apply(this, arguments).hasType("sap.ui.mdc.Table");
60
+ return FEBuilder.apply(this, arguments).hasType("sap.ui.mdc.Table").hasProperties({ busy: false });
61
61
  };
62
62
 
63
63
  function _isGridTable(oMdcTable) {
@@ -1078,6 +1078,15 @@ sap.ui.define(
1078
1078
  oCellControl = oCellControl.getCondition() ? oCellControl.getContentTrue() : oCellControl.getContentFalse();
1079
1079
  }
1080
1080
 
1081
+ // Collaboration case: the field is wrapped in a HBox with an avatar
1082
+ if (
1083
+ oCellControl.isA("sap.m.HBox") &&
1084
+ oCellControl.getItems().length === 2 &&
1085
+ oCellControl.getItems()[1].isA("sap.m.Avatar")
1086
+ ) {
1087
+ oCellControl = oCellControl.getItems()[0];
1088
+ }
1089
+
1081
1090
  if (vCellAction.executeOn) {
1082
1091
  vCellAction.executeOn(oCellControl);
1083
1092
  } else {
@@ -21,7 +21,7 @@ sap.ui.define(["sap/ui/core/Core", "sap/ui/core/library"], function (Core, _libr
21
21
  controls: [],
22
22
  elements: [],
23
23
  // eslint-disable-next-line no-template-curly-in-string
24
- version: "1.108.1",
24
+ version: "1.109.0",
25
25
  noLibraryCSS: true
26
26
  });
27
27
  return thisLib;