@sap-ux/ui5-test-writer 0.9.8 → 0.9.11

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.
@@ -31,19 +31,4 @@ export declare function generateOPAFiles(basePath: string, opaConfig: {
31
31
  * @returns the manifest object. An exception is thrown if the manifest cannot be read.
32
32
  */
33
33
  export declare function readManifest(fs: Editor, basePath: string): Manifest;
34
- /**
35
- * Generate a page object file for a Fiori elements for OData V4 application.
36
- * Note: this doesn't modify other existing files in the webapp/test folder.
37
- *
38
- * @param basePath - the absolute target path where the application will be generated
39
- * @param pageObjectParameters - parameters for the page
40
- * @param pageObjectParameters.targetKey - the key of the target in the manifest file corresponding to the page
41
- * @param pageObjectParameters.appID - the appID. If not specified, will be read from the manifest in sap.app/id
42
- * @param fs - an optional reference to a mem-fs editor
43
- * @returns Reference to a mem-fs-editor
44
- */
45
- export declare function generatePageObjectFile(basePath: string, pageObjectParameters: {
46
- targetKey: string;
47
- appID?: string;
48
- }, fs?: Editor): Promise<Editor>;
49
34
  //# sourceMappingURL=fiori-elements-opa-writer.d.ts.map
@@ -2,7 +2,6 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.generateOPAFiles = generateOPAFiles;
4
4
  exports.readManifest = readManifest;
5
- exports.generatePageObjectFile = generatePageObjectFile;
6
5
  const node_path_1 = require("node:path");
7
6
  const node_fs_1 = require("node:fs");
8
7
  const mem_fs_1 = require("mem-fs");
@@ -443,32 +442,4 @@ function writePageObject(pageConfig, rootTemplateDirPath, testOutDirPath, fs) {
443
442
  globOptions: { dot: true }
444
443
  });
445
444
  }
446
- /**
447
- * Generate a page object file for a Fiori elements for OData V4 application.
448
- * Note: this doesn't modify other existing files in the webapp/test folder.
449
- *
450
- * @param basePath - the absolute target path where the application will be generated
451
- * @param pageObjectParameters - parameters for the page
452
- * @param pageObjectParameters.targetKey - the key of the target in the manifest file corresponding to the page
453
- * @param pageObjectParameters.appID - the appID. If not specified, will be read from the manifest in sap.app/id
454
- * @param fs - an optional reference to a mem-fs editor
455
- * @returns Reference to a mem-fs-editor
456
- */
457
- async function generatePageObjectFile(basePath, pageObjectParameters, fs) {
458
- const editor = fs ?? (0, mem_fs_editor_1.create)((0, mem_fs_1.create)());
459
- const manifest = readManifest(editor, basePath);
460
- const { applicationType } = getAppTypeAndHideFilterBarFromManifest(manifest);
461
- const pageConfig = createPageConfig(manifest, pageObjectParameters.targetKey, pageObjectParameters.appID);
462
- if (pageConfig) {
463
- const rootTemplateDirPath = (0, node_path_1.join)(__dirname, `../templates/${applicationType}`); // Only v4 is supported for the time being
464
- const testOutDirPath = (0, node_path_1.join)(await (0, project_access_1.getWebappPath)(basePath), 'test');
465
- writePageObject(pageConfig, rootTemplateDirPath, testOutDirPath, editor);
466
- }
467
- else {
468
- throw new types_1.ValidationError((0, i18n_1.t)('error.cannotGeneratePageFile', {
469
- targetKey: pageObjectParameters.targetKey
470
- }));
471
- }
472
- return editor;
473
- }
474
445
  //# sourceMappingURL=fiori-elements-opa-writer.js.map
package/dist/types.d.ts CHANGED
@@ -130,13 +130,17 @@ export type ListReportFeatures = {
130
130
  };
131
131
  deleteButton?: {
132
132
  enabled?: boolean | string;
133
- visible: boolean;
133
+ visible?: boolean;
134
134
  dynamicPath?: string;
135
135
  };
136
136
  filterBarItems?: string[];
137
137
  tableColumns?: Record<string, Record<string, string | number | boolean>>;
138
138
  toolBarActions?: ActionButtonState[];
139
139
  isALP?: boolean;
140
+ semanticKey?: {
141
+ semanticKeyProperties?: string[];
142
+ missingFromFilterBar?: string[];
143
+ };
140
144
  };
141
145
  export interface ActionButtonState {
142
146
  label: string;
@@ -95,6 +95,16 @@ export declare function analyzeDeleteRestrictions(restriction: DeleteRestriction
95
95
  * @returns ButtonState indicating visibility and enabled state based on the Updatable value
96
96
  */
97
97
  export declare function analyzeUpdateRestrictions(restriction: UpdateRestrictionsType | undefined): ButtonState;
98
+ /**
99
+ * Checks the visibility and enabled state of create and delete buttons for a given entity set
100
+ * by analyzing OData Capabilities annotations in the converted metadata.
101
+ *
102
+ * @param convertedMetadata The already-converted OData metadata
103
+ * @param entitySetName The name of the entity set to check
104
+ * @returns ButtonVisibilityResult containing the state of create and delete buttons
105
+ * @throws {Error} If entity set is not found
106
+ */
107
+ export declare function checkButtonVisibilityFromMetadata(convertedMetadata: ConvertedMetadata, entitySetName: string): ButtonVisibilityResult;
98
108
  /**
99
109
  * Checks the visibility and enabled state of create and delete buttons for a given entity set
100
110
  * by analyzing OData Capabilities annotations in the metadata.
@@ -115,6 +125,15 @@ export declare function checkButtonVisibility(metadataXml: string, entitySetName
115
125
  * @throws {Error} If metadata cannot be parsed or entity set is not found
116
126
  */
117
127
  export declare function checkEditVisibility(metadataXml: string, entitySetName: string): ButtonState;
128
+ /**
129
+ * Safely checks button visibility from already-converted metadata, with error handling.
130
+ *
131
+ * @param convertedMetadata The already-converted OData metadata
132
+ * @param entitySetName The name of the entity set
133
+ * @param log Optional logger instance
134
+ * @returns Button visibility result or undefined if error occurs
135
+ */
136
+ export declare function safeCheckButtonVisibilityFromMetadata(convertedMetadata: ConvertedMetadata, entitySetName: string, log?: Logger): ButtonVisibilityResult | undefined;
118
137
  /**
119
138
  * Safely checks button visibility with error handling.
120
139
  *
@@ -10,8 +10,10 @@ exports.analyzeRestrictionValue = analyzeRestrictionValue;
10
10
  exports.analyzeInsertRestrictions = analyzeInsertRestrictions;
11
11
  exports.analyzeDeleteRestrictions = analyzeDeleteRestrictions;
12
12
  exports.analyzeUpdateRestrictions = analyzeUpdateRestrictions;
13
+ exports.checkButtonVisibilityFromMetadata = checkButtonVisibilityFromMetadata;
13
14
  exports.checkButtonVisibility = checkButtonVisibility;
14
15
  exports.checkEditVisibility = checkEditVisibility;
16
+ exports.safeCheckButtonVisibilityFromMetadata = safeCheckButtonVisibilityFromMetadata;
15
17
  exports.safeCheckButtonVisibility = safeCheckButtonVisibility;
16
18
  exports.safeCheckEditVisibility = safeCheckEditVisibility;
17
19
  const edmx_parser_1 = require("@sap-ux/edmx-parser");
@@ -215,6 +217,27 @@ function analyzeUpdateRestrictions(restriction) {
215
217
  const value = restriction ? restriction['Updatable'] : undefined;
216
218
  return analyzeRestrictionValue(value);
217
219
  }
220
+ /**
221
+ * Checks the visibility and enabled state of create and delete buttons for a given entity set
222
+ * by analyzing OData Capabilities annotations in the converted metadata.
223
+ *
224
+ * @param convertedMetadata The already-converted OData metadata
225
+ * @param entitySetName The name of the entity set to check
226
+ * @returns ButtonVisibilityResult containing the state of create and delete buttons
227
+ * @throws {Error} If entity set is not found
228
+ */
229
+ function checkButtonVisibilityFromMetadata(convertedMetadata, entitySetName) {
230
+ const entitySet = convertedMetadata.entitySets.find((es) => es.name === entitySetName);
231
+ if (!entitySet) {
232
+ throw new Error(`Entity set '${entitySetName}' not found in metadata`);
233
+ }
234
+ const insertRestrictions = entitySet.annotations?.Capabilities?.InsertRestrictions;
235
+ const deleteRestrictions = entitySet.annotations?.Capabilities?.DeleteRestrictions;
236
+ return {
237
+ create: analyzeInsertRestrictions(insertRestrictions),
238
+ delete: analyzeDeleteRestrictions(deleteRestrictions)
239
+ };
240
+ }
218
241
  /**
219
242
  * Checks the visibility and enabled state of create and delete buttons for a given entity set
220
243
  * by analyzing OData Capabilities annotations in the metadata.
@@ -226,17 +249,7 @@ function analyzeUpdateRestrictions(restriction) {
226
249
  */
227
250
  function checkButtonVisibility(metadataXml, entitySetName) {
228
251
  try {
229
- const convertedMetadata = (0, annotation_converter_1.convert)((0, edmx_parser_1.parse)(metadataXml));
230
- const entitySet = convertedMetadata.entitySets.find((es) => es.name === entitySetName);
231
- if (!entitySet) {
232
- throw new Error(`Entity set '${entitySetName}' not found in metadata`);
233
- }
234
- const insertRestrictions = entitySet.annotations?.Capabilities?.InsertRestrictions;
235
- const deleteRestrictions = entitySet.annotations?.Capabilities?.DeleteRestrictions;
236
- return {
237
- create: analyzeInsertRestrictions(insertRestrictions),
238
- delete: analyzeDeleteRestrictions(deleteRestrictions)
239
- };
252
+ return checkButtonVisibilityFromMetadata((0, annotation_converter_1.convert)((0, edmx_parser_1.parse)(metadataXml)), entitySetName);
240
253
  }
241
254
  catch (error) {
242
255
  const errorMessage = error instanceof Error ? error.message : String(error);
@@ -267,6 +280,23 @@ function checkEditVisibility(metadataXml, entitySetName) {
267
280
  throw new Error(`Failed to analyze edit visibility: ${errorMessage}`);
268
281
  }
269
282
  }
283
+ /**
284
+ * Safely checks button visibility from already-converted metadata, with error handling.
285
+ *
286
+ * @param convertedMetadata The already-converted OData metadata
287
+ * @param entitySetName The name of the entity set
288
+ * @param log Optional logger instance
289
+ * @returns Button visibility result or undefined if error occurs
290
+ */
291
+ function safeCheckButtonVisibilityFromMetadata(convertedMetadata, entitySetName, log) {
292
+ try {
293
+ return checkButtonVisibilityFromMetadata(convertedMetadata, entitySetName);
294
+ }
295
+ catch (error) {
296
+ log?.debug(`Failed to check button visibility: ${error instanceof Error ? error.message : String(error)}`);
297
+ return undefined;
298
+ }
299
+ }
270
300
  /**
271
301
  * Safely checks button visibility with error handling.
272
302
  *
@@ -1,6 +1,7 @@
1
1
  import type { Logger } from '@sap-ux/logger';
2
2
  import type { TreeAggregations, TreeModel } from '@sap/ux-specification/dist/types/src/parser';
3
3
  import type { ActionButtonsResult, ActionButtonState, ButtonState, FEV4ManifestTarget, ListReportFeatures } from '../types';
4
+ import type { ConvertedMetadata } from '@sap-ux/vocabularies-types';
4
5
  import { safeCheckButtonVisibility } from './actionUtils';
5
6
  import type { PageWithModelV4 } from '@sap/ux-specification/dist/types/src/parser/application';
6
7
  import type { Manifest } from '@sap-ux/project-access';
@@ -20,13 +21,22 @@ export declare function buildButtonState(buttonState?: ButtonState): {
20
21
  /**
21
22
  * Safely checks action button states with error handling.
22
23
  *
23
- * @param metadata - The OData metadata XML content
24
+ * @param convertedMetadata - The already-converted OData metadata
24
25
  * @param entitySetName - The name of the entity set
25
26
  * @param actionNames - List of action names to check
26
27
  * @param log - Optional logger instance
27
28
  * @returns Array of action button states or empty array if error occurs
28
29
  */
29
- export declare function safeCheckActionButtonStates(metadata: string, entitySetName: string, actionNames: string[], log?: Logger): ActionButtonState[];
30
+ export declare function safeCheckActionButtonStates(convertedMetadata: ConvertedMetadata, entitySetName: string, actionNames: string[], log?: Logger): ActionButtonState[];
31
+ /**
32
+ * Safely gets semantic key properties with error handling.
33
+ *
34
+ * @param convertedMetadata - The already-converted OData metadata
35
+ * @param entitySetName - The name of the entity set
36
+ * @param log - Optional logger instance
37
+ * @returns Array of semantic key properties or undefined if error occurs
38
+ */
39
+ export declare function safeGetSemanticKeyProperties(convertedMetadata: ConvertedMetadata, entitySetName: string, log?: Logger): string[] | undefined;
30
40
  /**
31
41
  * Returns true when a ListReport manifest target is configured as an Analytical List Page.
32
42
  * ALP targets have a `views.paths` array where at least one entry contains a `primary` array,
@@ -69,6 +79,16 @@ export declare function getToolBarActions(pageModel: TreeModel): TreeAggregation
69
79
  * @returns - an array of filter field names
70
80
  */
71
81
  export declare function getFilterFieldNames(pageModel: TreeModel, log?: Logger): string[];
82
+ /**
83
+ * Checks the state of action buttons defined in UI.LineItem annotations for a given entity set.
84
+ *
85
+ * @param convertedMetadata The already-converted OData metadata
86
+ * @param entitySetName The name of the entity set to check
87
+ * @param actionNames Optional list of action names to filter (e.g., ['Check', 'deductDiscount']). If not provided, returns all actions.
88
+ * @returns ActionButtonsResult containing the list of action buttons and their states
89
+ * @throws {Error} If entity set is not found
90
+ */
91
+ export declare function checkActionButtonStatesFromMetadata(convertedMetadata: ConvertedMetadata, entitySetName: string, actionNames?: string[]): ActionButtonsResult;
72
92
  /**
73
93
  * Checks the state of action buttons defined in UI.LineItem annotations for a given entity set.
74
94
  *
@@ -94,4 +114,36 @@ export declare function getToolBarActionNames(pageModel: TreeModel, log?: Logger
94
114
  * @returns An array of toolbar action descriptions.
95
115
  */
96
116
  export declare function getToolBarActionItems(toolBarActionsAgg: TreeAggregations): string[];
117
+ /**
118
+ * Checks whether all SemanticKey properties for a given entity set appear as filter fields in the filter bar.
119
+ * Returns false if the semantic key is absent, empty, or no semantic key properties are present as filters.
120
+ *
121
+ * @param pageModel - The tree model containing filter bar definitions (from ux-specification)
122
+ * @param metadataXml - The OData metadata XML content as a string
123
+ * @param entitySetName - The name of the entity set to inspect
124
+ * @param log - optional logger instance
125
+ * @returns true if every SemanticKey property appears in the filter bar, false otherwise
126
+ */
127
+ export declare function isSemanticKeyInFilterBar(pageModel: TreeModel, metadataXml: string, entitySetName: string, log?: Logger): boolean;
128
+ /**
129
+ * Retrieves the SemanticKey PropertyPath values for a given entity set from already-converted metadata.
130
+ *
131
+ * @param convertedMetadata - The already-converted OData metadata
132
+ * @param entitySetName - The name of the entity set to inspect
133
+ * @param resolveLabels - when true, each property name is replaced with its Common.Label value (falls back to the property name when no label is defined). Use this when comparing against getFilterFieldNames(), which also returns labels.
134
+ * @returns An array of PropertyPath string values (or their labels) from the SemanticKey annotation, or an empty array if not found
135
+ * @throws {Error} If the entity set is not found
136
+ */
137
+ export declare function getSemanticKeyPropertiesFromMetadata(convertedMetadata: ConvertedMetadata, entitySetName: string, resolveLabels?: boolean): string[];
138
+ /**
139
+ * Retrieves the SemanticKey PropertyPath values for a given entity set from OData metadata XML.
140
+ * Returns the values of all PropertyPath entries in the SAP Common SemanticKey annotation on the entity type.
141
+ *
142
+ * @param metadataXml - The OData metadata XML content as a string
143
+ * @param entitySetName - The name of the entity set to inspect
144
+ * @param resolveLabels - when true, each property name is replaced with its Common.Label value (falls back to the property name when no label is defined). Use this when comparing against getFilterFieldNames(), which also returns labels.
145
+ * @returns An array of PropertyPath string values (or their labels) from the SemanticKey annotation, or an empty array if not found
146
+ * @throws {Error} If the metadata cannot be parsed or the entity set is not found
147
+ */
148
+ export declare function getSemanticKeyProperties(metadataXml: string, entitySetName: string, resolveLabels?: boolean): string[];
97
149
  //# sourceMappingURL=listReportUtils.d.ts.map
@@ -3,14 +3,19 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.safeCheckButtonVisibility = exports.safeCheckEditVisibility = exports.checkButtonVisibility = void 0;
4
4
  exports.buildButtonState = buildButtonState;
5
5
  exports.safeCheckActionButtonStates = safeCheckActionButtonStates;
6
+ exports.safeGetSemanticKeyProperties = safeGetSemanticKeyProperties;
6
7
  exports.isALPManifestTarget = isALPManifestTarget;
7
8
  exports.isALPFromManifest = isALPFromManifest;
8
9
  exports.getListReportFeatures = getListReportFeatures;
9
10
  exports.getToolBarActions = getToolBarActions;
10
11
  exports.getFilterFieldNames = getFilterFieldNames;
12
+ exports.checkActionButtonStatesFromMetadata = checkActionButtonStatesFromMetadata;
11
13
  exports.checkActionButtonStates = checkActionButtonStates;
12
14
  exports.getToolBarActionNames = getToolBarActionNames;
13
15
  exports.getToolBarActionItems = getToolBarActionItems;
16
+ exports.isSemanticKeyInFilterBar = isSemanticKeyInFilterBar;
17
+ exports.getSemanticKeyPropertiesFromMetadata = getSemanticKeyPropertiesFromMetadata;
18
+ exports.getSemanticKeyProperties = getSemanticKeyProperties;
14
19
  const modelUtils_1 = require("./modelUtils");
15
20
  const edmx_parser_1 = require("@sap-ux/edmx-parser");
16
21
  const annotation_converter_1 = require("@sap-ux/annotation-converter");
@@ -35,21 +40,38 @@ function buildButtonState(buttonState) {
35
40
  /**
36
41
  * Safely checks action button states with error handling.
37
42
  *
38
- * @param metadata - The OData metadata XML content
43
+ * @param convertedMetadata - The already-converted OData metadata
39
44
  * @param entitySetName - The name of the entity set
40
45
  * @param actionNames - List of action names to check
41
46
  * @param log - Optional logger instance
42
47
  * @returns Array of action button states or empty array if error occurs
43
48
  */
44
- function safeCheckActionButtonStates(metadata, entitySetName, actionNames, log) {
49
+ function safeCheckActionButtonStates(convertedMetadata, entitySetName, actionNames, log) {
45
50
  try {
46
- return checkActionButtonStates(metadata, entitySetName, actionNames).actions;
51
+ return checkActionButtonStatesFromMetadata(convertedMetadata, entitySetName, actionNames).actions;
47
52
  }
48
53
  catch (error) {
49
54
  log?.debug(`Failed to check action button states: ${error instanceof Error ? error.message : String(error)}`);
50
55
  return [];
51
56
  }
52
57
  }
58
+ /**
59
+ * Safely gets semantic key properties with error handling.
60
+ *
61
+ * @param convertedMetadata - The already-converted OData metadata
62
+ * @param entitySetName - The name of the entity set
63
+ * @param log - Optional logger instance
64
+ * @returns Array of semantic key properties or undefined if error occurs
65
+ */
66
+ function safeGetSemanticKeyProperties(convertedMetadata, entitySetName, log) {
67
+ try {
68
+ return getSemanticKeyPropertiesFromMetadata(convertedMetadata, entitySetName, true);
69
+ }
70
+ catch (error) {
71
+ log?.debug(`Failed to get semantic key properties: ${error instanceof Error ? error.message : String(error)}`);
72
+ return undefined;
73
+ }
74
+ }
53
75
  /**
54
76
  * Returns true when a ListReport manifest target is configured as an Analytical List Page.
55
77
  * ALP targets have a `views.paths` array where at least one entry contains a `primary` array,
@@ -89,20 +111,38 @@ function isALPFromManifest(manifest, targetKey) {
89
111
  * @returns feature data extracted from the List Report page model
90
112
  */
91
113
  function getListReportFeatures(listReportPage, log, metadata, manifest) {
92
- const buttonVisibility = metadata && listReportPage.entitySet
93
- ? (0, actionUtils_1.safeCheckButtonVisibility)(metadata, listReportPage.entitySet, log)
94
- : undefined;
95
114
  const toolbarActions = getToolBarActionNames(listReportPage.model, log);
115
+ const filterBarItems = getFilterFieldNames(listReportPage.model, log);
116
+ let buttonVisibility;
117
+ let semanticKeyProperties;
118
+ let toolBarActions = [];
119
+ if (metadata && listReportPage.entitySet) {
120
+ const entitySetName = listReportPage.entitySet;
121
+ try {
122
+ const convertedMetadata = (0, annotation_converter_1.convert)((0, edmx_parser_1.parse)(metadata));
123
+ buttonVisibility = (0, actionUtils_1.safeCheckButtonVisibilityFromMetadata)(convertedMetadata, entitySetName, log);
124
+ semanticKeyProperties = safeGetSemanticKeyProperties(convertedMetadata, entitySetName, log);
125
+ toolBarActions = safeCheckActionButtonStates(convertedMetadata, entitySetName, toolbarActions, log);
126
+ }
127
+ catch (error) {
128
+ log?.debug(`Failed to parse metadata: ${error instanceof Error ? error.message : String(error)}`);
129
+ }
130
+ }
131
+ const missingKeys = semanticKeyProperties?.length && filterBarItems.length
132
+ ? semanticKeyProperties.filter((key) => !filterBarItems.includes(key))
133
+ : undefined;
96
134
  return {
97
135
  name: listReportPage.name,
98
136
  createButton: buildButtonState(buttonVisibility?.create),
99
137
  deleteButton: buildButtonState(buttonVisibility?.delete),
100
- filterBarItems: getFilterFieldNames(listReportPage.model, log),
138
+ filterBarItems,
101
139
  tableColumns: (0, modelUtils_1.getTableColumnData)(listReportPage.model, log),
102
- toolBarActions: metadata && listReportPage.entitySet
103
- ? safeCheckActionButtonStates(metadata, listReportPage.entitySet, toolbarActions, log)
104
- : [],
105
- isALP: manifest ? isALPFromManifest(manifest, listReportPage.name) : false
140
+ toolBarActions,
141
+ isALP: manifest ? isALPFromManifest(manifest, listReportPage.name) : false,
142
+ semanticKey: {
143
+ semanticKeyProperties,
144
+ missingFromFilterBar: missingKeys?.length ? missingKeys : undefined
145
+ }
106
146
  };
107
147
  }
108
148
  /**
@@ -141,6 +181,34 @@ function getFilterFieldNames(pageModel, log) {
141
181
  }
142
182
  return filterBarItems;
143
183
  }
184
+ /**
185
+ * Checks the state of action buttons defined in UI.LineItem annotations for a given entity set.
186
+ *
187
+ * @param convertedMetadata The already-converted OData metadata
188
+ * @param entitySetName The name of the entity set to check
189
+ * @param actionNames Optional list of action names to filter (e.g., ['Check', 'deductDiscount']). If not provided, returns all actions.
190
+ * @returns ActionButtonsResult containing the list of action buttons and their states
191
+ * @throws {Error} If entity set is not found
192
+ */
193
+ function checkActionButtonStatesFromMetadata(convertedMetadata, entitySetName, actionNames) {
194
+ const entitySet = convertedMetadata.entitySets.find((es) => es.name === entitySetName);
195
+ if (!entitySet) {
196
+ throw new Error(`Entity set '${entitySetName}' not found in metadata`);
197
+ }
198
+ const entityType = entitySet.entityType;
199
+ if (!entityType) {
200
+ throw new Error(`Entity type not found for entity set '${entitySetName}'`);
201
+ }
202
+ const lineItemAnnotation = entityType.annotations?.UI?.LineItem;
203
+ if (!lineItemAnnotation || !Array.isArray(lineItemAnnotation)) {
204
+ return { actions: [], entityType: entityType.name };
205
+ }
206
+ const dataFieldForActions = lineItemAnnotation.filter((item) => item.$Type === 'com.sap.vocabularies.UI.v1.DataFieldForAction');
207
+ const actions = actionNames
208
+ ? findActionStates(dataFieldForActions, actionNames, convertedMetadata)
209
+ : extractAllActionStates(dataFieldForActions, convertedMetadata);
210
+ return { actions, entityType: entityType.name };
211
+ }
144
212
  /**
145
213
  * Checks the state of action buttons defined in UI.LineItem annotations for a given entity set.
146
214
  *
@@ -152,24 +220,7 @@ function getFilterFieldNames(pageModel, log) {
152
220
  */
153
221
  function checkActionButtonStates(metadataXml, entitySetName, actionNames) {
154
222
  try {
155
- const convertedMetadata = (0, annotation_converter_1.convert)((0, edmx_parser_1.parse)(metadataXml));
156
- const entitySet = convertedMetadata.entitySets.find((es) => es.name === entitySetName);
157
- if (!entitySet) {
158
- throw new Error(`Entity set '${entitySetName}' not found in metadata`);
159
- }
160
- const entityType = entitySet.entityType;
161
- if (!entityType) {
162
- throw new Error(`Entity type not found for entity set '${entitySetName}'`);
163
- }
164
- const lineItemAnnotation = entityType.annotations?.UI?.LineItem;
165
- if (!lineItemAnnotation || !Array.isArray(lineItemAnnotation)) {
166
- return { actions: [], entityType: entityType.name };
167
- }
168
- const dataFieldForActions = lineItemAnnotation.filter((item) => item.$Type === 'com.sap.vocabularies.UI.v1.DataFieldForAction');
169
- const actions = actionNames
170
- ? findActionStates(dataFieldForActions, actionNames, convertedMetadata)
171
- : extractAllActionStates(dataFieldForActions, convertedMetadata);
172
- return { actions, entityType: entityType.name };
223
+ return checkActionButtonStatesFromMetadata((0, annotation_converter_1.convert)((0, edmx_parser_1.parse)(metadataXml)), entitySetName, actionNames);
173
224
  }
174
225
  catch (error) {
175
226
  const errorMessage = error instanceof Error ? error.message : String(error);
@@ -249,4 +300,70 @@ function extractItemDescriptions(aggregations) {
249
300
  }
250
301
  return [];
251
302
  }
303
+ /**
304
+ * Checks whether all SemanticKey properties for a given entity set appear as filter fields in the filter bar.
305
+ * Returns false if the semantic key is absent, empty, or no semantic key properties are present as filters.
306
+ *
307
+ * @param pageModel - The tree model containing filter bar definitions (from ux-specification)
308
+ * @param metadataXml - The OData metadata XML content as a string
309
+ * @param entitySetName - The name of the entity set to inspect
310
+ * @param log - optional logger instance
311
+ * @returns true if every SemanticKey property appears in the filter bar, false otherwise
312
+ */
313
+ function isSemanticKeyInFilterBar(pageModel, metadataXml, entitySetName, log) {
314
+ try {
315
+ const semanticKeys = getSemanticKeyProperties(metadataXml, entitySetName, true);
316
+ if (!semanticKeys.length) {
317
+ return false;
318
+ }
319
+ const filterFields = getFilterFieldNames(pageModel, log);
320
+ return semanticKeys.every((key) => filterFields.includes(key));
321
+ }
322
+ catch (error) {
323
+ log?.debug(`Failed to check semantic key in filter bar: ${error instanceof Error ? error.message : String(error)}`);
324
+ return false;
325
+ }
326
+ }
327
+ /**
328
+ * Retrieves the SemanticKey PropertyPath values for a given entity set from already-converted metadata.
329
+ *
330
+ * @param convertedMetadata - The already-converted OData metadata
331
+ * @param entitySetName - The name of the entity set to inspect
332
+ * @param resolveLabels - when true, each property name is replaced with its Common.Label value (falls back to the property name when no label is defined). Use this when comparing against getFilterFieldNames(), which also returns labels.
333
+ * @returns An array of PropertyPath string values (or their labels) from the SemanticKey annotation, or an empty array if not found
334
+ * @throws {Error} If the entity set is not found
335
+ */
336
+ function getSemanticKeyPropertiesFromMetadata(convertedMetadata, entitySetName, resolveLabels = false) {
337
+ const entitySet = convertedMetadata.entitySets.find((es) => es.name === entitySetName);
338
+ if (!entitySet) {
339
+ throw new Error(`Entity set '${entitySetName}' not found in metadata`);
340
+ }
341
+ const semanticKey = entitySet.entityType.annotations?.Common?.SemanticKey;
342
+ if (!semanticKey) {
343
+ return [];
344
+ }
345
+ const propertyNames = semanticKey.map((entry) => entry.value).filter((v) => typeof v === 'string');
346
+ if (!resolveLabels) {
347
+ return propertyNames;
348
+ }
349
+ return propertyNames.map((propName) => {
350
+ const property = entitySet.entityType.entityProperties.find((p) => p.name === propName);
351
+ const label = property?.annotations?.Common?.Label;
352
+ const labelStr = label !== undefined && label !== null ? String(label) : '';
353
+ return labelStr || propName;
354
+ });
355
+ }
356
+ /**
357
+ * Retrieves the SemanticKey PropertyPath values for a given entity set from OData metadata XML.
358
+ * Returns the values of all PropertyPath entries in the SAP Common SemanticKey annotation on the entity type.
359
+ *
360
+ * @param metadataXml - The OData metadata XML content as a string
361
+ * @param entitySetName - The name of the entity set to inspect
362
+ * @param resolveLabels - when true, each property name is replaced with its Common.Label value (falls back to the property name when no label is defined). Use this when comparing against getFilterFieldNames(), which also returns labels.
363
+ * @returns An array of PropertyPath string values (or their labels) from the SemanticKey annotation, or an empty array if not found
364
+ * @throws {Error} If the metadata cannot be parsed or the entity set is not found
365
+ */
366
+ function getSemanticKeyProperties(metadataXml, entitySetName, resolveLabels = false) {
367
+ return getSemanticKeyPropertiesFromMetadata((0, annotation_converter_1.convert)((0, edmx_parser_1.parse)(metadataXml)), entitySetName, resolveLabels);
368
+ }
252
369
  //# sourceMappingURL=listReportUtils.js.map
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@sap-ux/ui5-test-writer",
3
3
  "description": "SAP UI5 tests writer",
4
- "version": "0.9.8",
4
+ "version": "0.9.11",
5
5
  "repository": {
6
6
  "type": "git",
7
7
  "url": "https://github.com/SAP/open-ux-tools.git",
@@ -27,11 +27,11 @@
27
27
  "@sap/ux-specification": "1.144.0",
28
28
  "@sap-ux/edmx-parser": "0.10.0",
29
29
  "@sap-ux/annotation-converter": "0.10.21",
30
- "@sap-ux/ui5-application-writer": "1.9.1",
31
- "@sap-ux/logger": "0.9.0",
32
- "@sap-ux/fiori-generator-shared": "0.15.3",
30
+ "@sap-ux/ui5-application-writer": "1.9.2",
33
31
  "@sap-ux/project-access": "1.38.1",
34
- "@sap-ux/preview-middleware": "0.26.7"
32
+ "@sap-ux/preview-middleware": "0.26.8",
33
+ "@sap-ux/fiori-generator-shared": "0.15.4",
34
+ "@sap-ux/logger": "0.9.0"
35
35
  },
36
36
  "devDependencies": {
37
37
  "@types/ejs": "3.1.5",
@@ -38,6 +38,32 @@ sap.ui.define([
38
38
  <%_ }); -%>
39
39
  });
40
40
  <%_ } -%>
41
+ <%_ if (semanticKey && semanticKey.missingFromFilterBar && semanticKey.missingFromFilterBar.length > 0) { %>
42
+ opaTest("Add semantic key properties to filter bar", function (Given, When, Then) {
43
+ Then.onThe<%- startLR%>.onFilterBar().iOpenFilterAdaptation();
44
+ <%_ semanticKey.missingFromFilterBar.forEach(function(property) { _%>
45
+ When.onThe<%- startLR%>.onFilterBar().iAddAdaptationFilterField("<%- property %>");
46
+ <%_ }); -%>
47
+ Then.onThe<%- startLR%>.onFilterBar().iConfirmFilterAdaptation();
48
+ <%_ semanticKey.missingFromFilterBar.forEach(function(property) { _%>
49
+ Then.onThe<%- startLR%>.onFilterBar().iCheckFilterField("<%- property %>");
50
+ <%_ }); -%>
51
+ <%_ semanticKey.missingFromFilterBar.forEach(function(property) { _%>
52
+ // Then.onThe<%- startLR%>.onFilterBar().iChangeFilterField("<%- property %>");
53
+ <%_ }); -%>
54
+ // Then.onThe<%- startLR%>.onFilterBar().iExecuteSearch();
55
+ // Then.onThe<%- startLR%>.onTable().iCheckRows();
56
+ // Then.onThe<%- startLR%>.onTable().iSelectRows(0);
57
+ // Then.onThe<%- startLR%>.onTable().iCheckAction("<Action Name>", { enabled: true });
58
+ });
59
+ <%_ } -%>
60
+
61
+ // Note: this test will only work if the ListReport page has a search field and shows data that matches the search term. Please ensure that the test data and search term are set up accordingly.
62
+ // opaTest("Perform a global search and check the result", function (Given, When, Then) {
63
+ // When.onThe<%- startLR%>.onFilterBar().iChangeSearchField("Search Term");
64
+ // When.onThe<%- startLR%>.onFilterBar().iExecuteSearch();
65
+ // Then.onThe<%- startLR%>.onTable().iCheckRows();
66
+ // });
41
67
 
42
68
  <%_ if ((toolBarActions && toolBarActions.length > 0 ) || (tableColumns && Object.keys(tableColumns).length > 0)) { -%>
43
69
  opaTest("Check table columns and actions", function (Given, When, Then) {