@sap-ux/ui5-test-writer 1.1.13 → 1.2.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 (29) hide show
  1. package/dist/fiori-elements-opa-writer.js +360 -143
  2. package/dist/index.d.ts +1 -1
  3. package/dist/index.js +1 -1
  4. package/dist/translations/ui5-test-writer.i18n.json +10 -0
  5. package/dist/types.d.ts +10 -0
  6. package/dist/utils/fileWritingUtils.d.ts +55 -0
  7. package/dist/utils/fileWritingUtils.js +103 -0
  8. package/dist/utils/journeyRunnerUtils.d.ts +64 -0
  9. package/dist/utils/journeyRunnerUtils.js +251 -0
  10. package/dist/utils/opaJourneyTypesUtils.d.ts +27 -0
  11. package/dist/utils/opaJourneyTypesUtils.js +167 -0
  12. package/dist/utils/opaQUnitUtils.d.ts +6 -92
  13. package/dist/utils/opaQUnitUtils.js +20 -374
  14. package/dist/utils/virtualOpaUtils.d.ts +21 -0
  15. package/dist/utils/virtualOpaUtils.js +51 -0
  16. package/package.json +2 -1
  17. package/templates/v4/integration/FPMJourney.js +3 -3
  18. package/templates/v4/integration/FirstJourney.ts +5 -5
  19. package/templates/v4/integration/ListReportJourney.js +25 -25
  20. package/templates/v4/integration/ListReportJourney.ts +25 -25
  21. package/templates/v4/integration/ObjectPageJourney.js +37 -37
  22. package/templates/v4/integration/ObjectPageJourney.ts +38 -38
  23. package/templates/v4/integration/opaTests.qunit.js +5 -2
  24. package/templates/v4/integration/pages/FPM.js +17 -0
  25. package/templates/v4/integration/pages/JourneyRunner.js +6 -6
  26. package/templates/v4/integration/pages/JourneyRunner.ts +21 -12
  27. package/templates/v4/integration/pages/ListReport.js +17 -0
  28. package/templates/v4/integration/pages/ObjectPage.js +17 -0
  29. package/templates/v4/integration/types/OpaJourneyTypes.d.ts +5 -5
@@ -0,0 +1,167 @@
1
+ import { join } from 'node:path';
2
+ import { t } from '../i18n.js';
3
+ import { MAX_FILE_CONTENT_LENGTH, escapeRegex, findBracedBlock, insertAfterLastImport } from './fileWritingUtils.js';
4
+ /** Relative path from the test output directory to OpaJourneyTypes.d.ts */
5
+ const OPA_JOURNEY_TYPES_FILE = join('integration', 'types', 'OpaJourneyTypes.d.ts');
6
+ /** Frameworks the splicer knows how to render entries for. */
7
+ const SUPPORTED_FRAMEWORKS = new Set(['ListReport', 'ObjectPage']);
8
+ /**
9
+ * Filter `pages` down to those whose generator-owned custom-actions/assertions import
10
+ * is not yet present (matched against `../pages/<fileName>` with the `.gen` suffix).
11
+ *
12
+ * @param fileContent - the existing OpaJourneyTypes.d.ts content
13
+ * @param pages - candidate pages to splice in
14
+ * @returns the subset of pages that need to be added
15
+ */
16
+ function findPagesToAdd(fileContent, pages) {
17
+ return pages.filter((page) => {
18
+ const importPattern = new RegExp(String.raw `from\s+"\.\./pages/${escapeRegex(page.fileName)}"`);
19
+ return !importPattern.test(fileContent);
20
+ });
21
+ }
22
+ /**
23
+ * Build the framework-import lines (ListReport / ObjectPage / TemplatePage) that need to be
24
+ * added to the file, skipping any already imported.
25
+ *
26
+ * @param fileContent - the existing OpaJourneyTypes.d.ts content
27
+ * @param pages - the new pages being added
28
+ * @returns the list of framework-import lines to insert
29
+ */
30
+ function buildMissingFrameworkImports(fileContent, pages) {
31
+ const lines = [];
32
+ const templates = new Set(pages.map((page) => page.template).filter((template) => Boolean(template)));
33
+ if (templates.has('ListReport') && !fileContent.includes('from "sap/fe/test/ListReport"')) {
34
+ lines.push(`import type { actions as ListReportActions, assertions as ListReportAssertions } from "sap/fe/test/ListReport";`);
35
+ }
36
+ if (templates.has('ObjectPage') && !fileContent.includes('from "sap/fe/test/ObjectPage"')) {
37
+ lines.push(`import type { actions as ObjectPageActions, assertions as ObjectPageAssertions } from "sap/fe/test/ObjectPage";`);
38
+ }
39
+ if ((templates.has('ListReport') || templates.has('ObjectPage')) &&
40
+ !fileContent.includes('from "sap/fe/test/TemplatePage"')) {
41
+ lines.push(`import type { actions as TemplatePageActions, assertions as TemplatePageAssertions } from "sap/fe/test/TemplatePage";`);
42
+ }
43
+ return lines;
44
+ }
45
+ /**
46
+ * Build the per-page import line for a page's generator-owned custom actions/assertions.
47
+ *
48
+ * @param page - the page to render
49
+ * @returns the import line
50
+ */
51
+ function buildPageCustomImportLine(page) {
52
+ return (`import type { actions as ${page.targetKey}GeneratedCustomActions, ` +
53
+ `assertions as ${page.targetKey}GeneratedCustomAssertions } from "../pages/${page.fileName}";`);
54
+ }
55
+ /**
56
+ * Build the new entry to insert into the `When`/`Then` type union for a single page.
57
+ *
58
+ * @param page - the page to render
59
+ * @param mode - whether to render the entry for the `When` (actions) or `Then` (assertions) union
60
+ * @param indent - leading whitespace for the new line
61
+ * @returns the source line, or undefined if the page's framework is not supported
62
+ */
63
+ function buildPageUnionEntry(page, mode, indent) {
64
+ if (!page.template || !SUPPORTED_FRAMEWORKS.has(page.template)) {
65
+ return undefined;
66
+ }
67
+ const frameworkSuffix = mode === 'actions' ? 'Actions' : 'Assertions';
68
+ const customSuffix = mode === 'actions' ? 'CustomActions' : 'CustomAssertions';
69
+ return (`${indent}onThe${page.targetKey}Generated: Opa5 & ${page.template}${frameworkSuffix} & TemplatePage${frameworkSuffix} & ` +
70
+ `typeof ${page.targetKey}Generated${customSuffix};`);
71
+ }
72
+ /**
73
+ * Insert new `onThe<targetKey>Generated` member lines into the named exported type union,
74
+ * preserving the indentation of the first existing member.
75
+ *
76
+ * @param content - the file content
77
+ * @param exportName - the type union to update (`When` or `Then`)
78
+ * @param newEntries - the new union member lines to insert
79
+ * @returns the updated content (or unchanged if the type union or its closing brace can't be found)
80
+ */
81
+ function insertIntoTypeUnion(content, exportName, newEntries) {
82
+ if (newEntries.length === 0) {
83
+ return content;
84
+ }
85
+ const block = findBracedBlock(content, new RegExp(String.raw `export\s+type\s+${exportName}\b[^=]*=\s*[^{]*\{`));
86
+ if (!block) {
87
+ return content;
88
+ }
89
+ const { openBraceIdx, closeBraceIdx } = block;
90
+ const body = content.slice(openBraceIdx + 1, closeBraceIdx);
91
+ const indentMatch = /^([ \t]+)\S/m.exec(body);
92
+ const indent = indentMatch ? indentMatch[1] : ' ';
93
+ const insertion = newEntries.map((line) => line.replace(/^\s*/, indent)).join('\n');
94
+ // Insert before the closing brace, with a leading newline so the new lines sit on their own.
95
+ const head = content.slice(0, closeBraceIdx);
96
+ const tail = content.slice(closeBraceIdx);
97
+ return `${head.trimEnd()}\n${insertion}\n${tail}`;
98
+ }
99
+ /**
100
+ * Splice new journey entries into an existing `OpaJourneyTypes.d.ts`: framework imports,
101
+ * per-page custom actions/assertions imports, and `onThe<targetKey>Generated` members in
102
+ * the `When` and `Then` unions. Pages already imported are skipped; all other content is preserved.
103
+ *
104
+ * @param fileContent - the full content of the OpaJourneyTypes.d.ts file
105
+ * @param pages - pages to add
106
+ * @returns the updated file content, or the original content unchanged if nothing was added
107
+ */
108
+ export function spliceJourneysIntoOpaJourneyTypes(fileContent, pages) {
109
+ if (fileContent.length > MAX_FILE_CONTENT_LENGTH) {
110
+ return fileContent;
111
+ }
112
+ const toAdd = findPagesToAdd(fileContent, pages);
113
+ if (toAdd.length === 0) {
114
+ return fileContent;
115
+ }
116
+ const newImportLines = [
117
+ ...buildMissingFrameworkImports(fileContent, toAdd),
118
+ ...toAdd
119
+ .filter((page) => page.template && SUPPORTED_FRAMEWORKS.has(page.template))
120
+ .map((page) => buildPageCustomImportLine(page))
121
+ ];
122
+ const whenEntries = toAdd
123
+ .map((page) => buildPageUnionEntry(page, 'actions', ''))
124
+ .filter((entry) => entry !== undefined);
125
+ const thenEntries = toAdd
126
+ .map((page) => buildPageUnionEntry(page, 'assertions', ''))
127
+ .filter((entry) => entry !== undefined);
128
+ let result = insertAfterLastImport(fileContent, newImportLines);
129
+ result = insertIntoTypeUnion(result, 'When', whenEntries);
130
+ result = insertIntoTypeUnion(result, 'Then', thenEntries);
131
+ return result;
132
+ }
133
+ /**
134
+ * Read `OpaJourneyTypes.d.ts` from the project, splice entries for new pages, and write
135
+ * the result back. Pages already present are skipped. If the file does not exist or
136
+ * cannot be read, logs a warning and returns without writing.
137
+ *
138
+ * @param pages - pages whose journeys should be reflected in OpaJourneyTypes.d.ts
139
+ * @param testOutDirPath - path to the test output directory (`.../webapp/test`)
140
+ * @param fs - mem-fs-editor instance used to read and write the file
141
+ * @param log - optional logger instance used to surface warnings when the file
142
+ * cannot be read or updated
143
+ * @returns true if the file was written, false otherwise
144
+ */
145
+ export function addJourneysToOpaJourneyTypes(pages, testOutDirPath, fs, log) {
146
+ if (pages.length === 0) {
147
+ return false;
148
+ }
149
+ try {
150
+ const filePath = join(testOutDirPath, OPA_JOURNEY_TYPES_FILE);
151
+ const content = fs.read(filePath);
152
+ if (content.length > MAX_FILE_CONTENT_LENGTH) {
153
+ log?.warn(t('warn.cannotUpdateOpaJourneyTypes'));
154
+ return false;
155
+ }
156
+ const updated = spliceJourneysIntoOpaJourneyTypes(content, pages);
157
+ if (updated !== content) {
158
+ fs.write(filePath, updated);
159
+ return true;
160
+ }
161
+ }
162
+ catch {
163
+ log?.warn(t('warn.cannotUpdateOpaJourneyTypes'));
164
+ }
165
+ return false;
166
+ }
167
+ //# sourceMappingURL=opaJourneyTypesUtils.js.map
@@ -4,8 +4,7 @@
4
4
  * all other content (formatting, comments, whitespace) is preserved exactly.
5
5
  */
6
6
  import type { Editor } from 'mem-fs-editor';
7
- import { DotFileExtension } from '../types.js';
8
- import type { TestConfig as PreviewMiddlewareTestConfig } from '@sap-ux/preview-middleware';
7
+ import type { Logger } from '@sap-ux/logger';
9
8
  /**
10
9
  * Splices new module paths into the sap.ui.require array of the content string.
11
10
  * Entries that are already present are skipped. All other content is preserved exactly.
@@ -19,7 +18,7 @@ import type { TestConfig as PreviewMiddlewareTestConfig } from '@sap-ux/preview-
19
18
  */
20
19
  export declare function spliceModulesIntoQUnitContent(fileContent: string, moduleNames: string[]): string;
21
20
  /**
22
- * Reads opaTests.qunit.js from webapp/test/integration_old and extracts the html
21
+ * Reads opaTests.qunit.js from webapp/test/integration and extracts the html
23
22
  * launch target (path, query parameters, and hash fragment) from the launchUrl
24
23
  * line, e.g. `test/flpSandbox.html?sap-ui-xx-viewCache=false#myApp-tile`.
25
24
  * Returns undefined if the file cannot be read or the pattern is not found.
@@ -29,14 +28,6 @@ export declare function spliceModulesIntoQUnitContent(fileContent: string, modul
29
28
  * @returns the html target string, or undefined if not found
30
29
  */
31
30
  export declare function readHtmlTargetFromQUnitJs(testPath: string, fs: Editor): string | undefined;
32
- /**
33
- * Appends `/webapp/test/integration_old` to the project's `.gitignore`.
34
- * Creates the file if it does not exist. Skips if the entry is already present.
35
- *
36
- * @param basePath - project root (contains .gitignore)
37
- * @param fs - mem-fs-editor instance used to read and write the file
38
- */
39
- export declare function addIntegrationOldToGitignore(basePath: string, fs: Editor): Promise<void>;
40
31
  /**
41
32
  * Reads opaTests.qunit.js from the project, adds module paths to the
42
33
  * sap.ui.require array, and writes the updated content back.
@@ -46,86 +37,9 @@ export declare function addIntegrationOldToGitignore(basePath: string, fs: Edito
46
37
  * @param filePaths - module paths to add (e.g. ["myApp/test/integration/SomeJourney"])
47
38
  * @param projectPath - path to the test output directory (`.../webapp/test`)
48
39
  * @param fs - mem-fs-editor instance used to read and write the file
40
+ * @param log - optional logger instance used to surface warnings when the file
41
+ * cannot be read or updated
42
+ * @returns true if the file was written, false otherwise
49
43
  */
50
- export declare function addPathsToQUnitJs(filePaths: string[], projectPath: string, fs: Editor): void;
51
- /**
52
- * Page entry to splice into an existing JourneyRunner.js / JourneyRunner.ts.
53
- */
54
- export interface JourneyRunnerPage {
55
- /** The page's targetKey, used as both the variable name and `onThe<targetKey>` key */
56
- targetKey: string;
57
- /** The app module path prefix (e.g. "project1/test/integration/pages") */
58
- appPath: string;
59
- /** The framework page template (`'ListReport'` or `'ObjectPage'`); used by the TS splicer to construct `new <FW>(definition, Custom<Page>)`. */
60
- template?: string;
61
- /** The app id (sap.app.id from the manifest); only needed by the TS splicer. */
62
- appID?: string;
63
- /** The component id (defined in the target section); only needed by the TS splicer. */
64
- componentID?: string;
65
- /** The entity set name (if the page uses an entitySet rather than a contextPath); only needed by the TS splicer. */
66
- entitySet?: string;
67
- /** The context path (if the page uses a contextPath rather than an entitySet); only needed by the TS splicer. */
68
- contextPath?: string;
69
- }
70
- /**
71
- * Splices new page entries into the three locations of an existing JourneyRunner.js:
72
- * - the sap.ui.define dependency array
73
- * - the function parameter list
74
- * - the pages object literal
75
- *
76
- * Pages already present (detected by their module path in the define array) are skipped.
77
- * All other content — formatting, comments, whitespace — is preserved exactly.
78
- *
79
- * Note: files exceeding MAX_FILE_CONTENT_LENGTH characters are returned unchanged to prevent
80
- * ReDoS on crafted inputs. Valid generated files are well within this limit.
81
- *
82
- * @param fileContent - the full content of the JourneyRunner.js file
83
- * @param pages - pages to add
84
- * @returns the updated file content, or the original content unchanged if nothing was added
85
- */
86
- export declare function splicePageIntoJourneyRunner(fileContent: string, pages: JourneyRunnerPage[]): string;
87
- /**
88
- * Splices new page entries into an existing TypeScript JourneyRunner.ts:
89
- * - adds a default-import line after the last existing page import
90
- * - adds an entry inside the `pages: { ... }` object literal
91
- *
92
- * Pages already present (detected by their import line) are skipped.
93
- * All other content — formatting, comments, whitespace — is preserved exactly.
94
- *
95
- * Note: files exceeding MAX_FILE_CONTENT_LENGTH characters are returned unchanged to prevent
96
- * ReDoS on crafted inputs. Valid generated files are well within this limit.
97
- *
98
- * @param fileContent - the full content of the JourneyRunner.ts file
99
- * @param pages - pages to add
100
- * @returns the updated file content, or the original content unchanged if nothing was added
101
- */
102
- export declare function splicePageIntoJourneyRunnerTs(fileContent: string, pages: JourneyRunnerPage[]): string;
103
- /**
104
- * Reads JourneyRunner from the project, adds new page entries, and writes the updated
105
- * content back. Pages already present are skipped. Both AMD (`.js`) and ES module
106
- * (`.ts`) variants are supported and dispatched on `dotFileExtension`.
107
- *
108
- * @param pages - pages to add
109
- * @param testOutDirPath - path to the test output directory (`.../webapp/test`)
110
- * @param fs - mem-fs-editor instance used to read and write the file
111
- * @param dotFileExtension - file extension of the JourneyRunner ('.ts' or '.js'); defaults to '.js'
112
- */
113
- export declare function addPagesToJourneyRunner(pages: JourneyRunnerPage[], testOutDirPath: string, fs: Editor, dotFileExtension?: DotFileExtension): void;
114
- /**
115
- * Returns true if any UI5 yaml file in the project contains a `fiori-tools-preview`
116
- * middleware whose `test` array includes an entry with `framework: OPA5`.
117
- *
118
- * @param basePath - project root directory
119
- * @returns true when OPA5 is configured in a preview middleware, false otherwise
120
- */
121
- export declare function hasVirtualOPA5(basePath: string): Promise<boolean>;
122
- /**
123
- * Updates the fiori-tools-preview middleware in ui5-mock.yaml to support virtual OPA test endpoints.
124
- * Adds test framework entries to ui5-mock.yaml.
125
- *
126
- * @param basePath - the absolute target path of the application
127
- * @param testFrameworks - the test framework entries to add to ui5-mock.yaml
128
- * @param fs - the memfs editor instance
129
- */
130
- export declare function addVirtualTestConfig(basePath: string, testFrameworks: PreviewMiddlewareTestConfig[], fs: Editor): Promise<void>;
44
+ export declare function addPathsToQUnitJs(filePaths: string[], projectPath: string, fs: Editor, log?: Logger): boolean;
131
45
  //# sourceMappingURL=opaQUnitUtils.d.ts.map