@sap-ux/preview-middleware 1.0.49 → 1.1.1

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.
package/README.md CHANGED
@@ -66,12 +66,13 @@ Array of additional application configurations:
66
66
  | `rta.endpoints.developerMode` | `boolean` | optional | `false` | Flag to enable the runtime adaptation developer mode (only supported for adaptation projects) |
67
67
 
68
68
  ### [`test`](#configuration-option-test)
69
- | Option | Value Type | Requirement Type | Default Value | Description |
70
- | --------------- |------------|------------------|----------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
71
- | `framework` | `string` | mandatory | `undefined` | Currently `OPA5`, `QUnit` (only QUnit 2.3.2 provided as third-party module using [OpenUI5](https://github.com/SAP/openui5/blob/master/THIRDPARTY.txt)/SAPUI5) and `Testsuite` are supported. `Testsuite` will generate a testsuite for all configured frameworks that can be be used with a test runner (such as karma) |
72
- | `path` | `string` | optional | `(calculated)` | The mount point to be used for test suite. By default, `/test/opaTests.qunit.html` is used for `OPA5`, `/test/unitTests.qunit.html` is used for `QUnit`, and `/test/testsuite.qunit.html` is used for `Testsuite` |
73
- | `init` | `string` | optional | `undefined` | The mount point to be used for custom test runner script |
74
- | `pattern` | `string` | optional | `(calculated)` | Optional glob pattern to find the tests. By default, `/test/**/*Journey{,.gen}.{js,ts}` is used for `OPA5` and `/test/**/*Test.{js,ts}` is used for `QUnit` (not applicable for `Testsuite`) |
69
+ | Option | Value Type | Requirement Type | Default Value | Description |
70
+ |-------------------|------------|------------------|----------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
71
+ | `framework` | `string` | mandatory | `undefined` | Currently `OPA5`, `QUnit` (only QUnit 2.3.2 provided as third-party module using [OpenUI5](https://github.com/SAP/openui5/blob/master/THIRDPARTY.txt)/SAPUI5) and `Testsuite` are supported. `Testsuite` will generate a testsuite for all configured frameworks that can be be used with a test runner (such as karma) |
72
+ | `path` | `string` | optional | `(calculated)` | The mount point to be used for test suite. By default, `/test/opaTests.qunit.html` is used for `OPA5`, `/test/unitTests.qunit.html` is used for `QUnit`, and `/test/testsuite.qunit.html` is used for `Testsuite` |
73
+ | `init` | `string` | optional | `undefined` | The mount point to be used for custom test runner script |
74
+ | `pattern` | `string` | optional | `(calculated)` | Optional glob pattern to find the tests. By default, `/test/**/*Journey{,.gen}.{js,ts}` is used for `OPA5` and `/test/**/*Test.{js,ts}` is used for `QUnit` (not applicable for `Testsuite`) |
75
+ | `isolateJourneys` | `boolean` | optional | `false` | Only applicable for `OPA5`. When `true` and a `Testsuite` is configured, each journey is added as a separate test page with a `?journey=` URL parameter. The OPA5 init script respects this parameter and loads only the specified journey, enabling isolated test runs per journey. |
75
76
 
76
77
 
77
78
  ## [Usage](#usage)
@@ -246,6 +246,8 @@ export declare class FlpSandbox {
246
246
  * @param config the test configuration
247
247
  * @param initTemplate the test runner template
248
248
  * @param testPaths the paths to the test files
249
+ * @param opa5Path optional relative path from testsuite to OPA5 HTML page
250
+ * @param journeyNamesForIsolation optional list of journey module paths for isolated runs
249
251
  * @private
250
252
  */
251
253
  private testSuiteJsGetHandler;
@@ -253,6 +255,7 @@ export declare class FlpSandbox {
253
255
  * If it is part of TestConfig, create a test suite for the test configurations.
254
256
  *
255
257
  * @param configs test configurations
258
+ * @param id application id from manifest
256
259
  * @private
257
260
  */
258
261
  private createTestSuite;
package/dist/base/flp.js CHANGED
@@ -20,6 +20,15 @@ import { getIntegrationCard } from './utils/cards.js';
20
20
  import { createPropertiesI18nEntries } from '@sap-ux/i18n';
21
21
  import { AdaptationProjectType } from '@sap-ux/axios-extension';
22
22
  const DEFAULT_LIVERELOAD_PORT = 35729;
23
+ /**
24
+ * Convert an application id to its namespace by replacing dots with slashes.
25
+ *
26
+ * @param id application id from manifest (e.g. 'test.fe.v2.app')
27
+ * @returns namespace string (e.g. 'test/fe/v2/app')
28
+ */
29
+ function toNamespace(id) {
30
+ return id.replaceAll('.', '/');
31
+ }
23
32
  /**
24
33
  * Class handling preview of a sandbox FLP.
25
34
  */
@@ -106,7 +115,7 @@ export class FlpSandbox {
106
115
  }
107
116
  if (this.test) {
108
117
  this.addTestRoutes(this.test.filter((config) => config.framework !== 'Testsuite'), id);
109
- this.createTestSuite(this.test);
118
+ await this.createTestSuite(this.test, id);
110
119
  }
111
120
  if (this.flpConfig.enhancedHomePage) {
112
121
  this.addCDMRoute();
@@ -648,9 +657,11 @@ export class FlpSandbox {
648
657
  * @param config the test configuration
649
658
  * @param initTemplate the test runner template
650
659
  * @param testPaths the paths to the test files
660
+ * @param opa5Path optional relative path from testsuite to OPA5 HTML page
661
+ * @param journeyNamesForIsolation optional list of journey module paths for isolated runs
651
662
  * @private
652
663
  */
653
- async testSuiteJsGetHandler(res, next, config, initTemplate, testPaths) {
664
+ async testSuiteJsGetHandler(res, next, config, initTemplate, testPaths, opa5Path, journeyNamesForIsolation) {
654
665
  const files = await this.project.byGlob(config.init.replace('.js', '.[jt]s'));
655
666
  if (files?.length > 0) {
656
667
  this.logger.warn(`Script returned at ${config.path} is loaded from the file system.`);
@@ -658,9 +669,9 @@ export class FlpSandbox {
658
669
  }
659
670
  else {
660
671
  this.logger.debug(`Serving test route: ${config.init}`);
661
- const templateConfig = {
662
- testPaths: testPaths
663
- };
672
+ const templateConfig = opa5Path && journeyNamesForIsolation?.length
673
+ ? { testPaths, opa5Path, journeyNames: journeyNamesForIsolation }
674
+ : { testPaths };
664
675
  const js = render(initTemplate, templateConfig);
665
676
  this.sendResponse(res, 'application/javascript', 200, js);
666
677
  }
@@ -669,9 +680,10 @@ export class FlpSandbox {
669
680
  * If it is part of TestConfig, create a test suite for the test configurations.
670
681
  *
671
682
  * @param configs test configurations
683
+ * @param id application id from manifest
672
684
  * @private
673
685
  */
674
- createTestSuite(configs) {
686
+ async createTestSuite(configs, id) {
675
687
  const testsuiteConfig = configs.find((config) => config.framework === 'Testsuite');
676
688
  if (!testsuiteConfig) {
677
689
  //silent skip: create a testsuite only if it is explicitly part of the test configuration
@@ -691,18 +703,36 @@ export class FlpSandbox {
691
703
  this.logger.debug(`Skip serving testsuite init script in favor of provided script: ${testsuiteConfig.init}`);
692
704
  return;
693
705
  }
706
+ const ns = toNamespace(id);
707
+ const qunitConfig = configs.find((c) => c.framework === 'QUnit');
708
+ const opa5Config = configs.find((c) => c.framework === 'OPA5');
709
+ const mergedOpa5Config = opa5Config ? mergeTestConfigDefaults(opa5Config) : undefined;
694
710
  const testPaths = [];
695
- for (const testConfig of configs) {
696
- if (testConfig.framework === 'Testsuite') {
697
- continue;
711
+ let opa5Path;
712
+ let journeyNamesForIsolation;
713
+ if (qunitConfig) {
714
+ testPaths.push(posix.relative(posix.dirname(config.path), mergeTestConfigDefaults(qunitConfig).path));
715
+ }
716
+ if (mergedOpa5Config) {
717
+ if (mergedOpa5Config.isolateJourneys) {
718
+ try {
719
+ const journeyFiles = await this.project.byGlob(mergedOpa5Config.pattern);
720
+ journeyNamesForIsolation = generateImportList(ns, journeyFiles);
721
+ opa5Path = posix.relative(posix.dirname(config.path), mergedOpa5Config.path);
722
+ }
723
+ catch (e) {
724
+ this.logger.error(`Failed to discover journey files: ${e.message}. OPA5 page will be added to testsuite without isolated journeys.`);
725
+ testPaths.push(posix.relative(posix.dirname(config.path), mergedOpa5Config.path));
726
+ }
727
+ }
728
+ else {
729
+ testPaths.push(posix.relative(posix.dirname(config.path), mergedOpa5Config.path));
698
730
  }
699
- const mergedConfig = mergeTestConfigDefaults(testConfig);
700
- testPaths.push(posix.relative(posix.dirname(config.path), mergedConfig.path));
701
731
  }
702
- const initTemplate = readFileSync(join(__dirname, '../../templates/test/testsuite.qunit.js'), 'utf-8');
732
+ const initTemplate = readFileSync(join(__dirname, '../../templates/test/testsuite.qunit-init.ejs'), 'utf-8');
703
733
  this.logger.debug(`Add route for ${config.init}`);
704
734
  this.router.get(config.init, async (_req, res, next) => {
705
- await this.testSuiteJsGetHandler(res, next, config, initTemplate, testPaths);
735
+ await this.testSuiteJsGetHandler(res, next, config, initTemplate, testPaths, opa5Path, journeyNamesForIsolation);
706
736
  });
707
737
  }
708
738
  /**
@@ -763,7 +793,13 @@ export class FlpSandbox {
763
793
  }
764
794
  else {
765
795
  const testFiles = await this.project.byGlob(config.pattern);
766
- const templateConfig = { tests: generateImportList(ns, testFiles) };
796
+ if (testFiles.length === 0) {
797
+ this.logger.warn(`No test files found for pattern '${config.pattern}'.`);
798
+ }
799
+ const templateConfig = {
800
+ tests: generateImportList(ns, testFiles),
801
+ isolateJourneys: config.isolateJourneys === true
802
+ };
767
803
  const js = render(initTemplate, templateConfig);
768
804
  this.sendResponse(res, 'application/javascript', 200, js);
769
805
  }
@@ -775,7 +811,7 @@ export class FlpSandbox {
775
811
  * @param id application id from manifest
776
812
  */
777
813
  addTestRoutes(configs, id) {
778
- const ns = id.replace(/\./g, '/');
814
+ const ns = toNamespace(id);
779
815
  const htmlTemplate = readFileSync(join(__dirname, '../../templates/test/qunit.ejs'), 'utf-8');
780
816
  for (const testConfig of configs) {
781
817
  const config = mergeTestConfigDefaults(testConfig);
@@ -789,7 +825,7 @@ export class FlpSandbox {
789
825
  continue;
790
826
  }
791
827
  // add route for the init file
792
- const initTemplate = readFileSync(join(__dirname, '../../templates/test/qunit.js'), 'utf-8');
828
+ const initTemplate = readFileSync(join(__dirname, '../../templates/test/qunit-init.ejs'), 'utf-8');
793
829
  this.logger.debug(`Add route for ${config.init}`);
794
830
  this.router.get(config.init, async (_req, res, next) => {
795
831
  await this.testRunnerJsGetHandler(res, next, config, initTemplate, ns);
package/dist/base/test.js CHANGED
@@ -3,19 +3,22 @@ const DEFAULTS = {
3
3
  path: '/test/unitTests.qunit.html',
4
4
  init: '/test/unitTests.qunit.js',
5
5
  pattern: '/test/**/*Test.{js,ts}',
6
- framework: 'QUnit'
6
+ framework: 'QUnit',
7
+ isolateJourneys: false
7
8
  },
8
9
  opa5: {
9
10
  path: '/test/opaTests.qunit.html',
10
11
  init: '/test/opaTests.qunit.js',
11
12
  pattern: '/test/**/*Journey{,.gen}.{js,ts}',
12
- framework: 'OPA5'
13
+ framework: 'OPA5',
14
+ isolateJourneys: false
13
15
  },
14
16
  testsuite: {
15
17
  path: '/test/testsuite.qunit.html',
16
18
  init: '/test/testsuite.qunit.js',
17
19
  pattern: '',
18
- framework: 'Testsuite'
20
+ framework: 'Testsuite',
21
+ isolateJourneys: false
19
22
  }
20
23
  };
21
24
  /**
@@ -80,6 +80,11 @@ export interface TestConfig {
80
80
  * Pattern to match the test files
81
81
  */
82
82
  pattern?: string;
83
+ /**
84
+ * Optional: when true, each OPA5 journey runs in its own isolated test page.
85
+ * Only meaningful when framework is 'OPA5'.
86
+ */
87
+ isolateJourneys?: boolean;
83
88
  }
84
89
  /**
85
90
  * Test configuration that has been enriched with the defaults in case of missing values.
@@ -94,18 +99,21 @@ export type TestConfigDefaults = {
94
99
  init: '/test/unitTests.qunit.js';
95
100
  pattern: '/test/**/*Test.{js,ts}';
96
101
  framework: 'QUnit';
102
+ isolateJourneys: false;
97
103
  };
98
104
  opa5: {
99
105
  path: '/test/opaTests.qunit.html';
100
106
  init: '/test/opaTests.qunit.js';
101
107
  pattern: '/test/**/*Journey{,.gen}.{js,ts}';
102
108
  framework: 'OPA5';
109
+ isolateJourneys: false;
103
110
  };
104
111
  testsuite: {
105
112
  path: '/test/testsuite.qunit.html';
106
113
  init: '/test/testsuite.qunit.js';
107
114
  pattern: '';
108
115
  framework: 'Testsuite';
116
+ isolateJourneys: false;
109
117
  };
110
118
  };
111
119
  export type CardGeneratorConfig = {
package/package.json CHANGED
@@ -10,7 +10,7 @@
10
10
  "bugs": {
11
11
  "url": "https://github.com/SAP/open-ux-tools/issues?q=is%3Aopen+is%3Aissue+label%3Abug+label%3Apreview-middleware"
12
12
  },
13
- "version": "1.0.49",
13
+ "version": "1.1.1",
14
14
  "license": "Apache-2.0",
15
15
  "author": "@SAP/ux-tools-team",
16
16
  "main": "dist/index.js",
@@ -28,12 +28,12 @@
28
28
  "mem-fs-editor": "9.4.0",
29
29
  "qrcode": "1.5.4",
30
30
  "@sap/bas-sdk": "3.13.10",
31
- "@sap-ux/adp-tooling": "1.0.39",
31
+ "@sap-ux/adp-tooling": "1.0.40",
32
32
  "@sap-ux/btp-utils": "2.0.6",
33
- "@sap-ux/control-property-editor-sources": "npm:@sap-ux/control-property-editor@1.0.10",
34
33
  "@sap-ux/feature-toggle": "1.0.5",
35
34
  "@sap-ux/logger": "1.0.3",
36
- "@sap-ux/project-access": "2.1.9",
35
+ "@sap-ux/control-property-editor-sources": "npm:@sap-ux/control-property-editor@1.0.10",
36
+ "@sap-ux/project-access": "2.1.10",
37
37
  "@sap-ux/system-access": "1.0.10",
38
38
  "@sap-ux/i18n": "1.0.2"
39
39
  },
@@ -54,7 +54,7 @@
54
54
  "nock": "14.0.16",
55
55
  "npm-run-all2": "9.0.2",
56
56
  "supertest": "7.2.2",
57
- "@private/preview-middleware-client": "npm:@sap-ux-private/preview-middleware-client@1.0.49",
57
+ "@private/preview-middleware-client": "npm:@sap-ux-private/preview-middleware-client@1.1.1",
58
58
  "@sap-ux-private/playwright": "1.0.6",
59
59
  "@sap-ux/axios-extension": "2.0.8",
60
60
  "@sap-ux/store": "2.0.6",
@@ -17,13 +17,20 @@ sap.ui.loader.config({
17
17
 
18
18
  window.QUnit = Object.assign({}, window.QUnit, { config: { autostart: false } });
19
19
 
20
+ <% if (locals.isolateJourneys) { -%>
21
+ const knownJourneys = <%- JSON.stringify(tests) %>;
22
+ const journeyParam = new URLSearchParams(window.location.search).get("journey");
23
+ const modulesToRequire = (journeyParam && knownJourneys.includes(journeyParam)) ? [journeyParam] : knownJourneys;
24
+ <% } else { -%>
25
+ const modulesToRequire = <%- JSON.stringify(tests) %>;
26
+ <% } -%>
20
27
  sap.ui.require([
21
28
  "sap/ui/thirdparty/qunit-2",
22
29
  "sap/ui/qunit/qunit-junit",
23
30
  "sap/ui/qunit/qunit-coverage"
24
31
  ], function (QUnit) {
25
32
  'use strict';
26
- sap.ui.require(<%- JSON.stringify(tests) %>, function() {
33
+ sap.ui.require(modulesToRequire, function() {
27
34
  QUnit.start();
28
35
  });
29
- });
36
+ });
@@ -0,0 +1,13 @@
1
+ window.suite = function () {
2
+ const oSuite = new parent.jsUnitTestSuite();
3
+ var sContextPath = location.pathname.substring(0, location.pathname.lastIndexOf("/") + 1);
4
+ <% for (const path of locals.testPaths) { -%>
5
+ oSuite.addTestPage(sContextPath + "<%- path %>");
6
+ <% } -%>
7
+ <% if (locals.opa5Path && locals.journeyNames && locals.journeyNames.length > 0) { -%>
8
+ <% for (const name of locals.journeyNames) { -%>
9
+ oSuite.addTestPage(sContextPath + "<%- locals.opa5Path %>?journey=<%- name %>");
10
+ <% } -%>
11
+ <% } -%>
12
+ return oSuite;
13
+ }
@@ -1,8 +0,0 @@
1
- window.suite = function () {
2
- const oSuite = new parent.jsUnitTestSuite();
3
- var sContextPath = location.pathname.substring(0, location.pathname.lastIndexOf("/") + 1);
4
- <% for (const path of locals.testPaths) { %>
5
- oSuite.addTestPage(sContextPath + "<%- path %>");
6
- <% } %>
7
- return oSuite;
8
- }