@sap-ux/ui5-test-writer 0.7.101 → 0.7.103

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.
@@ -21,13 +21,14 @@ export declare function readManifest(fs: Editor, basePath: string): Manifest;
21
21
  * @param metadata - optional metadata for the OPA test generation
22
22
  * @param fs - an optional reference to a mem-fs editor
23
23
  * @param log - optional logger instance
24
+ * @param standalone - opa test generation run standalone, not during app generation
24
25
  * @returns Reference to a mem-fs-editor
25
26
  */
26
27
  export declare function generateOPAFiles(basePath: string, opaConfig: {
27
28
  scriptName?: string;
28
29
  appID?: string;
29
30
  htmlTarget?: string;
30
- }, metadata?: string, fs?: Editor, log?: Logger): Promise<Editor>;
31
+ }, metadata?: string, fs?: Editor, log?: Logger, standalone?: boolean): Promise<Editor>;
31
32
  /**
32
33
  * Generate a page object file for a Fiori elements for OData V4 application.
33
34
  * Note: this doesn't modify other existing files in the webapp/test folder.
@@ -42,5 +43,5 @@ export declare function generateOPAFiles(basePath: string, opaConfig: {
42
43
  export declare function generatePageObjectFile(basePath: string, pageObjectParameters: {
43
44
  targetKey: string;
44
45
  appID?: string;
45
- }, fs?: Editor): Editor;
46
+ }, fs?: Editor): Promise<Editor>;
46
47
  //# sourceMappingURL=fiori-elements-opa-writer.d.ts.map
@@ -4,12 +4,14 @@ exports.readManifest = readManifest;
4
4
  exports.generateOPAFiles = generateOPAFiles;
5
5
  exports.generatePageObjectFile = generatePageObjectFile;
6
6
  const node_path_1 = require("node:path");
7
+ const node_fs_1 = require("node:fs");
7
8
  const mem_fs_1 = require("mem-fs");
8
9
  const mem_fs_editor_1 = require("mem-fs-editor");
9
10
  const types_1 = require("./types");
10
11
  const i18n_1 = require("./i18n");
11
12
  const project_access_1 = require("@sap-ux/project-access");
12
13
  const modelUtils_1 = require("./utils/modelUtils");
14
+ const opaQUnitUtils_1 = require("./utils/opaQUnitUtils");
13
15
  /**
14
16
  * Reads the manifest for an app.
15
17
  *
@@ -192,6 +194,89 @@ function findLROP(pages, manifest) {
192
194
  }
193
195
  return { pageLR, pageOP };
194
196
  }
197
+ /**
198
+ * Writes common test files, page objects, and the first journey file.
199
+ *
200
+ * @param writeContext - shared write context (config, paths, editor, journey params)
201
+ * @param rootCommonTemplateDirPath - template root directory for common files
202
+ */
203
+ function writeCommonAndPageFiles(writeContext, rootCommonTemplateDirPath) {
204
+ const { config, rootV4TemplateDirPath, testOutDirPath, editor, journeyParams } = writeContext;
205
+ // Common test files
206
+ editor.copyTpl((0, node_path_1.join)(rootCommonTemplateDirPath), testOutDirPath,
207
+ // unit tests are not added for Fiori elements app
208
+ { appId: config.appID }, undefined, {
209
+ globOptions: { dot: true }
210
+ });
211
+ // Pages files (one for each page in the app)
212
+ config.pages.forEach((page) => {
213
+ writePageObject(page, rootV4TemplateDirPath, testOutDirPath, editor);
214
+ });
215
+ editor.copyTpl((0, node_path_1.join)(rootV4TemplateDirPath, 'integration', 'FirstJourney.js'), (0, node_path_1.join)(testOutDirPath, 'integration', `${config.opaJourneyFileName}.js`), journeyParams, undefined, {
216
+ globOptions: { dot: true }
217
+ });
218
+ // Journey Runner
219
+ editor.copyTpl((0, node_path_1.join)(rootV4TemplateDirPath, 'integration', 'pages', 'JourneyRunner.js'), (0, node_path_1.join)(testOutDirPath, 'integration', 'pages', 'JourneyRunner.js'), config, undefined, {
220
+ globOptions: { dot: true }
221
+ });
222
+ }
223
+ /**
224
+ * Writes journey files for list report, object pages and FPM pages.
225
+ *
226
+ * @param appFeatures - object containing feature data for list report, object pages, and FPM
227
+ * @param writeContext - shared write context (config, paths, editor, journey params)
228
+ * @param isStandalone - whether the generation is run in standalone mode (not during app generation)
229
+ * @param hasJourneyRunner - whether a JourneyRunner.js already exists (standalone upgrade path)
230
+ * @param virtualOPA5Configured - whether virtual OPA5 is configured
231
+ */
232
+ function writeJourneyFiles(appFeatures, writeContext, isStandalone, hasJourneyRunner = false, virtualOPA5Configured = false) {
233
+ const { config, rootV4TemplateDirPath, testOutDirPath, editor, journeyParams } = writeContext;
234
+ const generatedJourneyPages = [];
235
+ if (appFeatures.listReport?.name) {
236
+ editor.copyTpl((0, node_path_1.join)(rootV4TemplateDirPath, 'integration', 'ListReportJourney.js'), (0, node_path_1.join)(testOutDirPath, 'integration', `${appFeatures.listReport.name}Journey.js`), {
237
+ ...journeyParams,
238
+ ...appFeatures.listReport
239
+ }, undefined, {
240
+ globOptions: { dot: true }
241
+ });
242
+ generatedJourneyPages.push(appFeatures.listReport.name);
243
+ }
244
+ if (appFeatures.objectPages && appFeatures.objectPages.length > 0) {
245
+ appFeatures.objectPages.forEach((objectPage) => {
246
+ if (objectPage.name) {
247
+ editor.copyTpl((0, node_path_1.join)(rootV4TemplateDirPath, 'integration', 'ObjectPageJourney.js'), (0, node_path_1.join)(testOutDirPath, 'integration', `${objectPage.name}Journey.js`), {
248
+ ...journeyParams,
249
+ ...objectPage,
250
+ isStandalone
251
+ }, undefined, {
252
+ globOptions: { dot: true }
253
+ });
254
+ generatedJourneyPages.push(objectPage.name);
255
+ }
256
+ });
257
+ }
258
+ if (appFeatures.fpm?.name) {
259
+ editor.copyTpl((0, node_path_1.join)(rootV4TemplateDirPath, 'integration', 'FPMJourney.js'), (0, node_path_1.join)(testOutDirPath, 'integration', `${appFeatures.fpm.name}Journey.js`), {
260
+ ...journeyParams,
261
+ ...appFeatures.fpm
262
+ }, undefined, {
263
+ globOptions: { dot: true }
264
+ });
265
+ generatedJourneyPages.push(appFeatures.fpm.name);
266
+ }
267
+ if (!virtualOPA5Configured) {
268
+ if (hasJourneyRunner) {
269
+ (0, opaQUnitUtils_1.addPathsToQUnitJs)(generatedJourneyPages.map((page) => {
270
+ return `${config.appPath}/test/integration/${page}Journey`;
271
+ }), testOutDirPath, editor);
272
+ }
273
+ else {
274
+ editor.copyTpl((0, node_path_1.join)(rootV4TemplateDirPath, 'integration', 'opaTests.*.*'), (0, node_path_1.join)(testOutDirPath, 'integration'), { ...config, generatedJourneyPages }, undefined, {
275
+ globOptions: { dot: true }
276
+ });
277
+ }
278
+ }
279
+ }
195
280
  /**
196
281
  * Writes a page object in a mem-fs-editor.
197
282
  *
@@ -201,7 +286,7 @@ function findLROP(pages, manifest) {
201
286
  * @param fs - a reference to a mem-fs editor
202
287
  */
203
288
  function writePageObject(pageConfig, rootTemplateDirPath, testOutDirPath, fs) {
204
- fs.copyTpl((0, node_path_1.join)(rootTemplateDirPath, `integration/pages/${pageConfig.template}.js`), (0, node_path_1.join)(testOutDirPath, `integration/pages/${pageConfig.targetKey}.js`), pageConfig, undefined, {
289
+ fs.copyTpl((0, node_path_1.join)(rootTemplateDirPath, 'integration', 'pages', `${pageConfig.template}.js`), (0, node_path_1.join)(testOutDirPath, 'integration', 'pages', `${pageConfig.targetKey}.js`), pageConfig, undefined, {
205
290
  globOptions: { dot: true }
206
291
  });
207
292
  }
@@ -217,78 +302,51 @@ function writePageObject(pageConfig, rootTemplateDirPath, testOutDirPath, fs) {
217
302
  * @param metadata - optional metadata for the OPA test generation
218
303
  * @param fs - an optional reference to a mem-fs editor
219
304
  * @param log - optional logger instance
305
+ * @param standalone - opa test generation run standalone, not during app generation
220
306
  * @returns Reference to a mem-fs-editor
221
307
  */
222
- async function generateOPAFiles(basePath, opaConfig, metadata, fs, log) {
308
+ async function generateOPAFiles(basePath, opaConfig, metadata, fs, log, standalone = false) {
223
309
  const editor = fs ?? (0, mem_fs_editor_1.create)((0, mem_fs_1.create)());
224
310
  const manifest = readManifest(editor, basePath);
225
311
  const { applicationType, hideFilterBar } = getAppTypeAndHideFilterBarFromManifest(manifest);
226
312
  const config = createConfig(manifest, opaConfig, hideFilterBar);
227
313
  const rootCommonTemplateDirPath = (0, node_path_1.join)(__dirname, '../templates/common');
228
314
  const rootV4TemplateDirPath = (0, node_path_1.join)(__dirname, `../templates/${applicationType}`); // Only v4 is supported for the time being
229
- const testOutDirPath = (0, node_path_1.join)(basePath, 'webapp/test');
230
- // Common test files
231
- editor.copyTpl((0, node_path_1.join)(rootCommonTemplateDirPath), testOutDirPath,
232
- // unit tests are not added for Fiori elements app
233
- { appId: config.appID }, undefined, {
234
- globOptions: { dot: true }
235
- });
236
- // Pages files (one for each page in the app)
237
- config.pages.forEach((page) => {
238
- writePageObject(page, rootV4TemplateDirPath, testOutDirPath, editor);
239
- });
315
+ const testOutDirPath = (0, node_path_1.join)(await (0, project_access_1.getWebappPath)(basePath), 'test');
316
+ // Access ux-specification to get feature data for OPA test generation
317
+ const appFeatures = await (0, modelUtils_1.getAppFeatures)(basePath, editor, log, metadata);
240
318
  // OPA Journey file
241
319
  const startPages = config.pages.filter((page) => page.isStartup).map((page) => page.targetKey);
242
320
  const LROP = findLROP(config.pages, manifest);
243
- // Access ux-specification to get feature data for OPA test generation
244
- const { listReport, objectPages, fpm } = await (0, modelUtils_1.getAppFeatures)(basePath, editor, log, metadata);
245
321
  const journeyParams = {
246
322
  startPages,
247
323
  startLR: LROP.pageLR?.targetKey,
248
324
  navigatedOP: LROP.pageOP?.targetKey,
249
325
  hideFilterBar: config.hideFilterBar
250
326
  };
251
- const generatedJourneyPages = [];
252
- editor.copyTpl((0, node_path_1.join)(rootV4TemplateDirPath, 'integration/FirstJourney.js'), (0, node_path_1.join)(testOutDirPath, `integration/${config.opaJourneyFileName}.js`), journeyParams, undefined, {
253
- globOptions: { dot: true }
254
- });
255
- if (listReport) {
256
- editor.copyTpl((0, node_path_1.join)(rootV4TemplateDirPath, 'integration/ListReportJourney.js'), (0, node_path_1.join)(testOutDirPath, `integration/${listReport.name}Journey.js`), {
257
- ...journeyParams,
258
- ...listReport
259
- }, undefined, {
260
- globOptions: { dot: true }
261
- });
262
- generatedJourneyPages.push(listReport.name);
263
- }
264
- if (objectPages && objectPages.length > 0) {
265
- objectPages.forEach((objectPage) => {
266
- editor.copyTpl((0, node_path_1.join)(rootV4TemplateDirPath, 'integration/ObjectPageJourney.js'), (0, node_path_1.join)(testOutDirPath, `integration/${objectPage.name}Journey.js`), {
267
- ...journeyParams,
268
- ...objectPage
269
- }, undefined, {
270
- globOptions: { dot: true }
271
- });
272
- generatedJourneyPages.push(objectPage.name);
273
- });
327
+ const writeContext = { config, rootV4TemplateDirPath, testOutDirPath, editor, journeyParams };
328
+ if (standalone) {
329
+ const hasJourneyRunner = (0, node_fs_1.existsSync)((0, node_path_1.join)(testOutDirPath, 'integration', 'pages', 'JourneyRunner.js'));
330
+ const virtualOPA5Configured = await (0, opaQUnitUtils_1.hasVirtualOPA5)(basePath);
331
+ if (hasJourneyRunner) {
332
+ writeJourneyFiles(appFeatures, writeContext, true, true, virtualOPA5Configured);
333
+ }
334
+ else {
335
+ editor.move((0, node_path_1.join)(testOutDirPath, 'integration', '**'), (0, node_path_1.join)(testOutDirPath, 'integration_old'));
336
+ await (0, opaQUnitUtils_1.addIntegrationOldToGitignore)(basePath, editor);
337
+ const htmlTarget = (0, opaQUnitUtils_1.readHtmlTargetFromQUnitJs)(testOutDirPath, editor) ?? config.htmlTarget;
338
+ const standaloneConfig = { ...config, htmlTarget };
339
+ const standaloneWriteContext = { ...writeContext, config: standaloneConfig };
340
+ if (!virtualOPA5Configured) {
341
+ writeCommonAndPageFiles(standaloneWriteContext, rootCommonTemplateDirPath);
342
+ }
343
+ writeJourneyFiles(appFeatures, standaloneWriteContext, true, true, virtualOPA5Configured);
344
+ }
274
345
  }
275
- if (fpm) {
276
- editor.copyTpl((0, node_path_1.join)(rootV4TemplateDirPath, 'integration/FPMJourney.js'), (0, node_path_1.join)(testOutDirPath, `integration/${fpm.name}Journey.js`), {
277
- ...journeyParams,
278
- ...fpm
279
- }, undefined, {
280
- globOptions: { dot: true }
281
- });
282
- generatedJourneyPages.push(fpm.name);
346
+ else {
347
+ writeCommonAndPageFiles(writeContext, rootCommonTemplateDirPath);
348
+ writeJourneyFiles(appFeatures, writeContext, false);
283
349
  }
284
- // Integration (OPA) test files - version-specific
285
- editor.copyTpl((0, node_path_1.join)(rootV4TemplateDirPath, 'integration', 'opaTests.*.*'), (0, node_path_1.join)(testOutDirPath, 'integration'), { ...config, generatedJourneyPages }, undefined, {
286
- globOptions: { dot: true }
287
- });
288
- // Journey Runner
289
- editor.copyTpl((0, node_path_1.join)(rootV4TemplateDirPath, 'integration', 'pages', 'JourneyRunner.js'), (0, node_path_1.join)(testOutDirPath, 'integration', 'pages', 'JourneyRunner.js'), config, undefined, {
290
- globOptions: { dot: true }
291
- });
292
350
  return editor;
293
351
  }
294
352
  /**
@@ -302,14 +360,14 @@ async function generateOPAFiles(basePath, opaConfig, metadata, fs, log) {
302
360
  * @param fs - an optional reference to a mem-fs editor
303
361
  * @returns Reference to a mem-fs-editor
304
362
  */
305
- function generatePageObjectFile(basePath, pageObjectParameters, fs) {
363
+ async function generatePageObjectFile(basePath, pageObjectParameters, fs) {
306
364
  const editor = fs || (0, mem_fs_editor_1.create)((0, mem_fs_1.create)());
307
365
  const manifest = readManifest(editor, basePath);
308
366
  const { applicationType } = getAppTypeAndHideFilterBarFromManifest(manifest);
309
367
  const pageConfig = createPageConfig(manifest, pageObjectParameters.targetKey, pageObjectParameters.appID);
310
368
  if (pageConfig) {
311
369
  const rootTemplateDirPath = (0, node_path_1.join)(__dirname, `../templates/${applicationType}`); // Only v4 is supported for the time being
312
- const testOutDirPath = (0, node_path_1.join)(basePath, 'webapp/test');
370
+ const testOutDirPath = (0, node_path_1.join)(await (0, project_access_1.getWebappPath)(basePath), 'test');
313
371
  writePageObject(pageConfig, rootTemplateDirPath, testOutDirPath, editor);
314
372
  }
315
373
  else {
package/dist/types.d.ts CHANGED
@@ -1,3 +1,4 @@
1
+ import type { Editor } from 'mem-fs-editor';
1
2
  export declare const SupportedPageTypes: {
2
3
  [id: string]: string;
3
4
  };
@@ -20,6 +21,12 @@ export type FEV4OPAConfig = {
20
21
  hideFilterBar: boolean;
21
22
  filterBarItems?: string[];
22
23
  };
24
+ export type JourneyParams = {
25
+ startPages: string[];
26
+ startLR: string | undefined;
27
+ navigatedOP: string | undefined;
28
+ hideFilterBar: boolean;
29
+ };
23
30
  export type FEV4ManifestTarget = {
24
31
  type?: string;
25
32
  name?: string;
@@ -135,6 +142,13 @@ export type AppFeatures = {
135
142
  objectPages?: ObjectPageFeatures[];
136
143
  fpm?: FPMFeatures;
137
144
  };
145
+ export type WriteContext = {
146
+ config: FEV4OPAConfig;
147
+ rootV4TemplateDirPath: string;
148
+ testOutDirPath: string;
149
+ editor: Editor;
150
+ journeyParams: JourneyParams;
151
+ };
138
152
  export type FormField = {
139
153
  fieldGroupQualifier?: string;
140
154
  field?: string;
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Utility for reading the FLP sandbox HTML file and extracting the
3
+ * application hash (intent) from the sap-ushell-config applications object.
4
+ */
5
+ import type { Editor } from 'mem-fs-editor';
6
+ /**
7
+ * Reads an FLP sandbox HTML file and extracts the first application key
8
+ * from the `sap-ushell-config` `applications` object.
9
+ *
10
+ * @param htmlRelativePath - path to the HTML file relative to `webapp/`
11
+ * (e.g. `test/flpSandbox.html`)
12
+ * @param webappPath - path to the webapp directory
13
+ * @param fs - mem-fs-editor instance used to read the file
14
+ * @returns the application key (e.g. `fincashbankmanage-tile`), or undefined
15
+ */
16
+ export declare function readHashFromFlpSandbox(htmlRelativePath: string, webappPath: string, fs: Editor): string | undefined;
17
+ //# sourceMappingURL=flpSandboxUtils.d.ts.map
@@ -0,0 +1,40 @@
1
+ "use strict";
2
+ /**
3
+ * Utility for reading the FLP sandbox HTML file and extracting the
4
+ * application hash (intent) from the sap-ushell-config applications object.
5
+ */
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.readHashFromFlpSandbox = readHashFromFlpSandbox;
8
+ const node_path_1 = require("node:path");
9
+ /**
10
+ * Regex to extract the first application key from the sap-ushell-config
11
+ * `applications` block. Matches patterns like:
12
+ *
13
+ * applications: {
14
+ * "fincashbankmanage-tile": {
15
+ *
16
+ * Captures the quoted key (e.g. `fincashbankmanage-tile`).
17
+ */
18
+ const APPLICATIONS_KEY_REGEX = /applications\s*:\s*\{[^"]*"([^"]+)"\s*:/;
19
+ /**
20
+ * Reads an FLP sandbox HTML file and extracts the first application key
21
+ * from the `sap-ushell-config` `applications` object.
22
+ *
23
+ * @param htmlRelativePath - path to the HTML file relative to `webapp/`
24
+ * (e.g. `test/flpSandbox.html`)
25
+ * @param webappPath - path to the webapp directory
26
+ * @param fs - mem-fs-editor instance used to read the file
27
+ * @returns the application key (e.g. `fincashbankmanage-tile`), or undefined
28
+ */
29
+ function readHashFromFlpSandbox(htmlRelativePath, webappPath, fs) {
30
+ try {
31
+ const filePath = (0, node_path_1.join)(webappPath, htmlRelativePath);
32
+ const content = fs.read(filePath);
33
+ const match = APPLICATIONS_KEY_REGEX.exec(content);
34
+ return match?.[1];
35
+ }
36
+ catch {
37
+ return undefined;
38
+ }
39
+ }
40
+ //# sourceMappingURL=flpSandboxUtils.js.map
@@ -0,0 +1,54 @@
1
+ /**
2
+ * Utility for reading and updating a generated opaTests.qunit.js file.
3
+ * The file is modified in-place: only the sap.ui.require array is changed;
4
+ * all other content (formatting, comments, whitespace) is preserved exactly.
5
+ */
6
+ import type { Editor } from 'mem-fs-editor';
7
+ /**
8
+ * Splices new module paths into the sap.ui.require array of the content string.
9
+ * Entries that are already present are skipped. All other content is preserved exactly.
10
+ *
11
+ * @param fileContent - the full content of the opaTests.qunit.js file
12
+ * @param moduleNames - module paths to add (e.g. ["myApp/test/integration/SomeJourney"])
13
+ * @returns the updated file content, or the original content unchanged if nothing was added
14
+ */
15
+ export declare function spliceModulesIntoQUnitContent(fileContent: string, moduleNames: string[]): string;
16
+ /**
17
+ * Reads opaTests.qunit.js from webapp/test/integration_old and extracts the html
18
+ * launch target (path, query parameters, and hash fragment) from the launchUrl
19
+ * line, e.g. `test/flpSandbox.html?sap-ui-xx-viewCache=false#myApp-tile`.
20
+ * Returns undefined if the file cannot be read or the pattern is not found.
21
+ *
22
+ * @param testPath - path to the test output directory (`.../webapp/test`)
23
+ * @param fs - mem-fs-editor instance used to read the file
24
+ * @returns the html target string, or undefined if not found
25
+ */
26
+ export declare function readHtmlTargetFromQUnitJs(testPath: string, fs: Editor): string | undefined;
27
+ /**
28
+ * Appends `/webapp/test/integration_old` to the project's `.gitignore`.
29
+ * Creates the file if it does not exist. Skips if the entry is already present.
30
+ *
31
+ * @param basePath - project root (contains .gitignore)
32
+ * @param fs - mem-fs-editor instance used to read and write the file
33
+ */
34
+ export declare function addIntegrationOldToGitignore(basePath: string, fs: Editor): Promise<void>;
35
+ /**
36
+ * Reads opaTests.qunit.js from the project, adds module paths to the
37
+ * sap.ui.require array, and writes the updated content back.
38
+ * Entries that are already present are skipped.
39
+ * All other file content is preserved exactly.
40
+ *
41
+ * @param filePaths - module paths to add (e.g. ["myApp/test/integration/SomeJourney"])
42
+ * @param projectPath - path to the test output directory (`.../webapp/test`)
43
+ * @param fs - mem-fs-editor instance used to read and write the file
44
+ */
45
+ export declare function addPathsToQUnitJs(filePaths: string[], projectPath: string, fs: Editor): void;
46
+ /**
47
+ * Returns true if any UI5 yaml file in the project contains a `fiori-tools-preview`
48
+ * middleware whose `test` array includes an entry with `framework: OPA5`.
49
+ *
50
+ * @param basePath - project root directory
51
+ * @returns true when OPA5 is configured in a preview middleware, false otherwise
52
+ */
53
+ export declare function hasVirtualOPA5(basePath: string): Promise<boolean>;
54
+ //# sourceMappingURL=opaQUnitUtils.d.ts.map
@@ -0,0 +1,184 @@
1
+ "use strict";
2
+ /**
3
+ * Utility for reading and updating a generated opaTests.qunit.js file.
4
+ * The file is modified in-place: only the sap.ui.require array is changed;
5
+ * all other content (formatting, comments, whitespace) is preserved exactly.
6
+ */
7
+ Object.defineProperty(exports, "__esModule", { value: true });
8
+ exports.spliceModulesIntoQUnitContent = spliceModulesIntoQUnitContent;
9
+ exports.readHtmlTargetFromQUnitJs = readHtmlTargetFromQUnitJs;
10
+ exports.addIntegrationOldToGitignore = addIntegrationOldToGitignore;
11
+ exports.addPathsToQUnitJs = addPathsToQUnitJs;
12
+ exports.hasVirtualOPA5 = hasVirtualOPA5;
13
+ const node_path_1 = require("node:path");
14
+ const flpSandboxUtils_1 = require("./flpSandboxUtils");
15
+ const project_access_1 = require("@sap-ux/project-access");
16
+ /** Relative path from the test output directory to opaTests.qunit.js */
17
+ const OPA_QUNIT_FILE = (0, node_path_1.join)('integration', 'opaTests.qunit.js');
18
+ /**
19
+ * The regex matches the opening bracket of the sap.ui.require array and
20
+ * captures everything up to (but not including) the closing bracket followed
21
+ * by `], function`. This lets us splice new entries in without disturbing
22
+ * any other part of the file.
23
+ *
24
+ * Matches:
25
+ * sap.ui.require(\n [\n "a",\n "b",\n ], function
26
+ * ^^^^^^^^^^^^^^^^^
27
+ * captured as group 1
28
+ *
29
+ * The `d` flag enables `match.indices` so we can read the capture group's
30
+ * exact start/end positions without fragile string searching.
31
+ */
32
+ const SAP_UI_REQUIRE_ARRAY_REGEX = /sap\.ui\.require\s*\(\s*\[([\s\S]*?)\]\s*,\s*function/d;
33
+ /**
34
+ * Splices new module paths into the sap.ui.require array of the content string.
35
+ * Entries that are already present are skipped. All other content is preserved exactly.
36
+ *
37
+ * @param fileContent - the full content of the opaTests.qunit.js file
38
+ * @param moduleNames - module paths to add (e.g. ["myApp/test/integration/SomeJourney"])
39
+ * @returns the updated file content, or the original content unchanged if nothing was added
40
+ */
41
+ function spliceModulesIntoQUnitContent(fileContent, moduleNames) {
42
+ const match = SAP_UI_REQUIRE_ARRAY_REGEX.exec(fileContent);
43
+ if (!match) {
44
+ return fileContent;
45
+ }
46
+ const arrayBody = match[1]; // everything between `[` and `]`
47
+ // Collect existing quoted entries so we don't add duplicates
48
+ const existingEntries = new Set();
49
+ const entryRegex = /"([^"]+)"/g;
50
+ let entryMatch;
51
+ while ((entryMatch = entryRegex.exec(arrayBody)) !== null) {
52
+ existingEntries.add(entryMatch[1]);
53
+ }
54
+ const toAdd = moduleNames.filter((name) => !existingEntries.has(name));
55
+ if (toAdd.length === 0) {
56
+ return fileContent;
57
+ }
58
+ // Detect the indentation used by the existing entries (e.g. four spaces)
59
+ const indentMatch = /^([ \t]+)"/m.exec(arrayBody);
60
+ const indent = indentMatch ? indentMatch[1] : ' ';
61
+ // Build the lines to insert, each terminated with a trailing comma
62
+ const newLines = toAdd.map((name) => `${indent}"${name}",`).join('\n');
63
+ // Insert just before the closing `]` using the capture group's end index.
64
+ const insertPosition = match.indices?.[1]?.[1];
65
+ if (insertPosition === undefined) {
66
+ return fileContent;
67
+ }
68
+ const before = fileContent.slice(0, insertPosition);
69
+ const after = fileContent.slice(insertPosition);
70
+ const leadingNewline = arrayBody.endsWith('\n') ? '' : '\n';
71
+ return `${before}${leadingNewline}${newLines}\n${after}`;
72
+ }
73
+ /**
74
+ * Regex to extract the html launch target from a `launchUrl` line of the form:
75
+ * sap.ui.require.toUrl('...') + '/some/path.html?params#hash'
76
+ * Captures the path/query/hash portion after the closing `') + '`.
77
+ */
78
+ const LAUNCH_URL_REGEX = /\.toUrl\s*\([^)]+\)\s*\+\s*'([^']+)'/;
79
+ /**
80
+ * Reads opaTests.qunit.js from webapp/test/integration_old and extracts the html
81
+ * launch target (path, query parameters, and hash fragment) from the launchUrl
82
+ * line, e.g. `test/flpSandbox.html?sap-ui-xx-viewCache=false#myApp-tile`.
83
+ * Returns undefined if the file cannot be read or the pattern is not found.
84
+ *
85
+ * @param testPath - path to the test output directory (`.../webapp/test`)
86
+ * @param fs - mem-fs-editor instance used to read the file
87
+ * @returns the html target string, or undefined if not found
88
+ */
89
+ function readHtmlTargetFromQUnitJs(testPath, fs) {
90
+ try {
91
+ const integrationOldDir = (0, node_path_1.join)(testPath, 'integration_old');
92
+ let filePath = (0, node_path_1.join)(integrationOldDir, 'opaTests.qunit.js');
93
+ if (!fs.exists(filePath)) {
94
+ filePath = (0, node_path_1.join)(integrationOldDir, 'Opa.qunit.js');
95
+ }
96
+ const content = fs.read(filePath);
97
+ const match = LAUNCH_URL_REGEX.exec(content);
98
+ const launchUrl = match?.[1].replace(/^\//, '');
99
+ if (!launchUrl) {
100
+ return undefined;
101
+ }
102
+ // If the launch URL already contains a hash fragment, use it as-is
103
+ if (launchUrl.includes('#')) {
104
+ return launchUrl;
105
+ }
106
+ // No hash fragment — read the referenced HTML file to extract the
107
+ // application key from the sap-ushell-config applications object
108
+ const htmlPath = launchUrl.split('?')[0];
109
+ const hash = (0, flpSandboxUtils_1.readHashFromFlpSandbox)(htmlPath, (0, node_path_1.join)(testPath, '..'), fs);
110
+ return hash ? `${launchUrl}#${hash}` : launchUrl;
111
+ }
112
+ catch {
113
+ return undefined;
114
+ }
115
+ }
116
+ /** The gitignore entry added for the moved integration test folder */
117
+ const INTEGRATION_OLD_GITIGNORE_ENTRY = '/webapp/test/integration_old';
118
+ /**
119
+ * Appends `/webapp/test/integration_old` to the project's `.gitignore`.
120
+ * Creates the file if it does not exist. Skips if the entry is already present.
121
+ *
122
+ * @param basePath - project root (contains .gitignore)
123
+ * @param fs - mem-fs-editor instance used to read and write the file
124
+ */
125
+ async function addIntegrationOldToGitignore(basePath, fs) {
126
+ const filePath = (0, node_path_1.join)(basePath, '.gitignore');
127
+ const existing = fs.exists(filePath) ? fs.read(filePath) : '';
128
+ const lines = existing.split('\n');
129
+ if (lines.some((line) => line.trim() === INTEGRATION_OLD_GITIGNORE_ENTRY)) {
130
+ return;
131
+ }
132
+ const updated = existing.endsWith('\n') || existing === ''
133
+ ? `${existing}${INTEGRATION_OLD_GITIGNORE_ENTRY}\n`
134
+ : `${existing}\n${INTEGRATION_OLD_GITIGNORE_ENTRY}\n`;
135
+ fs.write(filePath, updated);
136
+ }
137
+ /**
138
+ * Reads opaTests.qunit.js from the project, adds module paths to the
139
+ * sap.ui.require array, and writes the updated content back.
140
+ * Entries that are already present are skipped.
141
+ * All other file content is preserved exactly.
142
+ *
143
+ * @param filePaths - module paths to add (e.g. ["myApp/test/integration/SomeJourney"])
144
+ * @param projectPath - path to the test output directory (`.../webapp/test`)
145
+ * @param fs - mem-fs-editor instance used to read and write the file
146
+ */
147
+ function addPathsToQUnitJs(filePaths, projectPath, fs) {
148
+ try {
149
+ const filePath = (0, node_path_1.join)(projectPath, OPA_QUNIT_FILE);
150
+ const content = fs.read(filePath);
151
+ const updated = spliceModulesIntoQUnitContent(content, filePaths);
152
+ if (updated !== content) {
153
+ fs.write(filePath, updated);
154
+ }
155
+ }
156
+ catch {
157
+ // If the file doesn't exist or can't be read, do nothing
158
+ }
159
+ }
160
+ /**
161
+ * Returns true if any UI5 yaml file in the project contains a `fiori-tools-preview`
162
+ * middleware whose `test` array includes an entry with `framework: OPA5`.
163
+ *
164
+ * @param basePath - project root directory
165
+ * @returns true when OPA5 is configured in a preview middleware, false otherwise
166
+ */
167
+ async function hasVirtualOPA5(basePath) {
168
+ const yamlFileNames = await (0, project_access_1.getAllUi5YamlFileNames)(basePath);
169
+ for (const fileName of yamlFileNames) {
170
+ try {
171
+ const ui5Config = await (0, project_access_1.readUi5Yaml)(basePath, fileName);
172
+ const previewMiddleware = ui5Config.findCustomMiddleware('fiori-tools-preview');
173
+ const testEntries = previewMiddleware?.configuration?.test;
174
+ if (testEntries?.some((entry) => entry.framework === 'OPA5')) {
175
+ return true;
176
+ }
177
+ }
178
+ catch {
179
+ // Skip yaml files that cannot be read
180
+ }
181
+ }
182
+ return false;
183
+ }
184
+ //# sourceMappingURL=opaQUnitUtils.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.7.101",
4
+ "version": "0.7.103",
5
5
  "repository": {
6
6
  "type": "git",
7
7
  "url": "https://github.com/SAP/open-ux-tools.git",
@@ -27,9 +27,9 @@
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.8.4",
30
+ "@sap-ux/ui5-application-writer": "1.8.5",
31
31
  "@sap-ux/logger": "0.8.5",
32
- "@sap-ux/project-access": "1.35.19"
32
+ "@sap-ux/project-access": "1.35.20"
33
33
  },
34
34
  "devDependencies": {
35
35
  "@types/ejs": "3.1.5",
@@ -66,8 +66,12 @@ sap.ui.define([
66
66
  Then.onThe<%- name%>.iCheckNumberOfSections(<%- bodySections.length %>);
67
67
  <% } -%>
68
68
  <% bodySections.forEach(function(section) { -%>
69
+ <% if (!isStandalone) { -%>
70
+ <% if (bodySections.length > 1) { -%>
69
71
  When.onThe<%- name%>.iPressSectionIconTabFilterButton("<%- section.id %>");
72
+ <% } -%>
70
73
  Then.onThe<%- name%>.iCheckSection({ section: "<%- section.id %>" });
74
+ <% } -%>
71
75
  <% if (section?.subSections?.length > 0) { -%>
72
76
  <% section.subSections.forEach(function(subSection) { -%>
73
77
  //When.onThe<%- name%>.iGoToSection({ section: "<%- section.id %>", subSection: "<%- subSection.id %>" });