From 748a107880424c681128f7ebe7aee7682899f89d Mon Sep 17 00:00:00 2001 From: Max Reichmann Date: Fri, 2 Oct 2026 14:47:16 +0200 Subject: [PATCH 01/25] feat: Offer configPath property to workspace configs --- .../lib/validation/schema/ui5-workspace.json | 23 +++++++++++++++++-- packages/project/test/lib/graph/Workspace.js | 13 +++++++++++ 2 files changed, 34 insertions(+), 2 deletions(-) diff --git a/packages/project/lib/validation/schema/ui5-workspace.json b/packages/project/lib/validation/schema/ui5-workspace.json index d4cd46c1946..f7a4bf76826 100644 --- a/packages/project/lib/validation/schema/ui5-workspace.json +++ b/packages/project/lib/validation/schema/ui5-workspace.json @@ -9,7 +9,7 @@ "additionalProperties": false, "properties": { "specVersion": { - "enum": ["workspace/1.0"], + "enum": ["workspace/1.1", "workspace/1.0"], "errorMessage": "Unsupported \"specVersion\"\nYour UI5 CLI installation might be outdated.\nSupported specification versions: \"workspace/1.0\"\nFor details, see: https://ui5.github.io/cli/stable/pages/Workspace/#workspace-specification-versions" }, "metadata": { @@ -19,6 +19,23 @@ "$ref": "#/definitions/dependencyManagement" } }, + "if": { + "properties": {"specVersion": {"const": "workspace/1.0"}} + }, + "then": { + "properties": { + "dependencyManagement": { + "properties": { + "resolutions": { + "items": { + "not": {"required": ["configPath"]}, + "errorMessage": "\"configPath\" is only supported for specVersion \"workspace/1.1\" and higher" + } + } + } + } + } + }, "definitions": { "metadata": { "type": "object", @@ -42,13 +59,15 @@ "properties": { "resolutions": { "type": "array", - "items": { "type": "object", "additionalProperties": false, "properties": { "path": { "type": "string" + }, + "configPath": { + "type": "string" } } } diff --git a/packages/project/test/lib/graph/Workspace.js b/packages/project/test/lib/graph/Workspace.js index 6e5b586e138..d7cc2e9399b 100644 --- a/packages/project/test/lib/graph/Workspace.js +++ b/packages/project/test/lib/graph/Workspace.js @@ -473,3 +473,16 @@ test("Missing parameters", (t) => { message: "Could not create Workspace: Missing or empty parameter 'configuration'" }, "Threw with expected error message"); }); + +test("Basic validation of workspace with specified configPath for dependency", (t) => { + // TODO: add test which passes the following yaml example: + // specVersion: workspace/1.1 + // metadata: + // name: default + // dependencyManagement: + // resolutions: + // - path: ../my-reuse-library + // configPath: ui5-mock.yaml + + +}); From 7f536909a9c2d0ccaa5bacdc19fa9910a5e60acc Mon Sep 17 00:00:00 2001 From: Max Reichmann Date: Thu, 8 Oct 2026 15:08:45 +0200 Subject: [PATCH 02/25] feat(project): Add logic for configPath parameter + Add tests --- packages/project/lib/graph/Workspace.js | 10 ++- .../library.d/custom-config-path.yaml | 10 +++ packages/project/test/lib/graph/Workspace.js | 74 ++++++++++++++++--- 3 files changed, 79 insertions(+), 15 deletions(-) create mode 100644 packages/project/test/fixtures/library.d/custom-config-path.yaml diff --git a/packages/project/lib/graph/Workspace.js b/packages/project/lib/graph/Workspace.js index 54558ee8e26..cae5119aef9 100644 --- a/packages/project/lib/graph/Workspace.js +++ b/packages/project/lib/graph/Workspace.js @@ -145,7 +145,7 @@ class Workspace { `Missing property 'path' in dependency resolution configuration of workspace ${this.getName()}`); } return await this._getModulesFromPath( - this.#cwd, resolutionConfig.path); + this.#cwd, resolutionConfig.path, resolutionConfig.configPath); })); // Flatten array since package-workspaces might have resolved to multiple modules for a single resolution @@ -176,7 +176,7 @@ class Workspace { }; } - async _getModulesFromPath(cwd, relPath, failOnMissingFiles = true) { + async _getModulesFromPath(cwd, relPath, configPath, failOnMissingFiles = true) { const nodePath = path.join(cwd, relPath); if (this.#visitedNodePaths.has(nodePath)) { log.verbose(`Module located at ${nodePath} has already been visited`); @@ -236,7 +236,8 @@ class Workspace { const resolvedModules = new Map(); await Promise.all(searchPaths.map(async (pkgPath) => { - const modules = await this._getModulesFromPath(nodePath, pkgPath, staticPatterns.includes(pkgPath)); + const modules = + await this._getModulesFromPath(nodePath, pkgPath, configPath, staticPatterns.includes(pkgPath)); modules.forEach((module) => { const id = module.getId(); if (!resolvedModules.get(id)) { @@ -249,7 +250,8 @@ class Workspace { return [new Module({ id: pkg.name, version: pkg.version, - modulePath: nodePath + modulePath: nodePath, + configPath: configPath, })]; } } diff --git a/packages/project/test/fixtures/library.d/custom-config-path.yaml b/packages/project/test/fixtures/library.d/custom-config-path.yaml new file mode 100644 index 00000000000..ca7c0921253 --- /dev/null +++ b/packages/project/test/fixtures/library.d/custom-config-path.yaml @@ -0,0 +1,10 @@ +specVersion: "2.3" +type: library +metadata: + name: library.d + copyright: A custom config (called by configPath paramater) +resources: + configuration: + paths: + src: main/src + test: main/test diff --git a/packages/project/test/lib/graph/Workspace.js b/packages/project/test/lib/graph/Workspace.js index d7cc2e9399b..c78ac320a78 100644 --- a/packages/project/test/lib/graph/Workspace.js +++ b/packages/project/test/lib/graph/Workspace.js @@ -14,9 +14,9 @@ const collectionBLibraryA = path.join(__dirname, "..", "..", "fixtures", "collec const collectionBLibraryB = path.join(__dirname, "..", "..", "fixtures", "collection.b", "library.b"); const collectionBLibraryC = path.join(__dirname, "..", "..", "fixtures", "collection.b", "library.c"); -function createWorkspaceConfig({dependencyManagement}) { +function createWorkspaceConfig({dependencyManagement, specVersion = "workspace/1.0"}) { return { - specVersion: "workspace/1.0", + specVersion, metadata: { name: "workspace-name" }, @@ -474,15 +474,67 @@ test("Missing parameters", (t) => { }, "Threw with expected error message"); }); -test("Basic validation of workspace with specified configPath for dependency", (t) => { - // TODO: add test which passes the following yaml example: - // specVersion: workspace/1.1 - // metadata: - // name: default - // dependencyManagement: - // resolutions: - // - path: ../my-reuse-library - // configPath: ui5-mock.yaml +test("workspace/1.1: Basic validation with specified configPath for dependency", async (t) => { + const workspace = new t.context.Workspace({ + cwd: __dirname, + configuration: createWorkspaceConfig({ + specVersion: "workspace/1.1", + dependencyManagement: { + resolutions: [{ + path: "../../fixtures/library.d", + configPath: "custom-config-path.yaml" // Custom configPath + }, { + path: "../../fixtures/library.e", + // no custom yaml -> resorts to default: ui5.yaml + }] + } + }) + }); + + const {projectNameMap} = await workspace._getResolvedModules(); + t.deepEqual(Array.from(projectNameMap.keys()).sort(), ["library.d", "library.e"], "Correct project name keys"); + t.is(projectNameMap.get("library.d")._configPath, "custom-config-path.yaml"); + t.is(projectNameMap.get("library.e")._configPath, "ui5.yaml"); +}); + +test("workspace/1.1: Custom configPath for dependency but specVersion is workspace/1.0", async (t) => { + const workspace = new t.context.Workspace({ + cwd: __dirname, + configuration: createWorkspaceConfig({ + specVersion: "workspace/1.0", + dependencyManagement: { + resolutions: [{ + path: "../../fixtures/library.d", + configPath: "custom-config-path.yaml" // Custom configPath + }, { + path: "../../fixtures/library.e", + }] + } + }) + }); + + const err = await t.throwsAsync(async () => await workspace._getResolvedModules()); + t.is(err.message.includes("\"configPath\" is only supported for specVersion \"workspace/1.1\" and higher"), true); +}); +test("workspace/1.1: Custom configPath for dependency does not exist", async (t) => { + const workspace = new t.context.Workspace({ + cwd: __dirname, + configuration: createWorkspaceConfig({ + specVersion: "workspace/1.1", + dependencyManagement: { + resolutions: [{ + path: "../../fixtures/library.d", + configPath: "not-existing-config-path.yaml" // Custom configPath + }, { + path: "../../fixtures/library.e", + // no custom yaml -> resorts to default: ui5.yaml + }] + } + }) + }); + const err = await t.throwsAsync(async () => await workspace._getResolvedModules()); + t.is(err.message.includes("Failed to read configuration for module library.d: " + + "Could not find configuration file in module at path 'not-existing-config-path.yaml'"), true); }); From 2f9a8c3c4078cb18bb2c2bb912a0c9c8c73d7989 Mon Sep 17 00:00:00 2001 From: Max Reichmann Date: Thu, 8 Oct 2026 16:08:25 +0200 Subject: [PATCH 03/25] docs: Adjust Workspace.md page with configPath property --- .../documentation/docs/pages/Workspace.md | 34 +++++++++++++++---- 1 file changed, 27 insertions(+), 7 deletions(-) diff --git a/internal/documentation/docs/pages/Workspace.md b/internal/documentation/docs/pages/Workspace.md index 730e9420284..a588a33f721 100644 --- a/internal/documentation/docs/pages/Workspace.md +++ b/internal/documentation/docs/pages/Workspace.md @@ -5,7 +5,7 @@ ::: details ```yaml title="ui5-workspace.yaml" -specVersion: workspace/1.0 +specVersion: workspace/1.1 metadata: name: default dependencyManagement: @@ -23,14 +23,14 @@ Workspaces are typically configured in a `ui5-workspace.yaml` file, located next ::: details Example ```yaml title="ui5-workspace.yaml" -specVersion: workspace/1.0 +specVersion: workspace/1.1 metadata: name: default dependencyManagement: resolutions: - path: ../heavy.library --- -specVersion: workspace/1.0 +specVersion: workspace/1.1 metadata: name: extended dependencyManagement: @@ -52,14 +52,14 @@ This concept has been discussed in an RFC: [RFC 0006 Local Dependency Resolution A UI5 Workspace configuration must define a specification version (`specVersion`) compatible with its configuration. For more information, see [Workspace Specification Versions](#workspace-specification-versions). ```yaml -specVersion: "workspace/1.0" +specVersion: "workspace/1.1" ``` ### Metadata ::: details Example ```yaml -specVersion: workspace/1.0 +specVersion: workspace/1.1 metadata: name: dolphin ``` @@ -91,7 +91,7 @@ UI5 Workspace configurations allow to influence the dependency resolution when w ::: details ```yaml -specVersion: workspace/1.0 +specVersion: workspace/1.1 metadata: name: dolphin dependencyManagement: @@ -119,11 +119,24 @@ Paths must be written in POSIX (i.e. using only forward slashes `/` as path segm Note that this configuration only affects the resolution of dependencies which have already been found during the regular dependency resolution process of a project. For example, if a workspace resolution path resolves to a project that would otherwise not be part of the dependency tree of the current root project, it will not be added to the dependency tree. Also, transitive dependencies of resolved projects are not being followed. +#### configPath + +*Available since `workspace/1.1`* + +```yaml +dependencyManagement: + resolutions: + - path: ../test.library + configPath: custom-config.yaml +``` + +By default, UI5 CLI looks for a `ui5.yaml` at the root of the resolved module. The optional `configPath` property allows pointing to a different configuration file within the module directory. The path must be written in POSIX notation and must be **relative to the resolution `path`**. + ## Workspace Specification Versions A workspace configuration must define a Specification Version by setting the `specVersion` property. UI5 CLI uses this information to detect whether the currently installed version is compatible with a workspace's configuration. ```yaml -specVersion: "workspace/1.0" +specVersion: "workspace/1.1" [...] ``` @@ -142,8 +155,15 @@ Unless otherwise noted in the table below, UI5 CLI modules are backward-compatib Version | UI5 CLI Release --- | --- +**`workspace/1.1`** | v5.0.0+ and v4.1.0+ **`workspace/1.0`** | v3.0.0+ +### Specification Version `workspace/1.1` + +Added [`configPath`](#configpath) property to resolution entries. + +Specification Version `workspace/1.1` configurations are compatible with [UI5 CLI](https://github.com/SAP/ui5-cli) v5.0.0 and above, as well as v4.1.0 and above. + ### Specification Version `workspace/1.0` Initial version. From 9ab5e3348f9117985f0c2f09227418865e3e43bb Mon Sep 17 00:00:00 2001 From: Max Reichmann Date: Fri, 9 Oct 2026 12:02:58 +0200 Subject: [PATCH 04/25] refactor: Improve error handling when `path` is absolute + Forbid absolute paths for `configPath` property + Allow relative paths for `configPath` which are out-of-root (resolution dir) --- packages/project/lib/graph/Module.js | 2 +- packages/project/lib/graph/Workspace.js | 10 +++ .../library.e/ui5-library.d-config.yaml | 11 +++ packages/project/test/lib/graph/Workspace.js | 77 +++++++++++++++++-- 4 files changed, 92 insertions(+), 8 deletions(-) create mode 100644 packages/project/test/fixtures/library.e/ui5-library.d-config.yaml diff --git a/packages/project/lib/graph/Module.js b/packages/project/lib/graph/Module.js index 838158352bd..2e31e1ec6ae 100644 --- a/packages/project/lib/graph/Module.js +++ b/packages/project/lib/graph/Module.js @@ -70,7 +70,7 @@ class Module { this._id = id; this._version = version; this._modulePath = modulePath; - this._configPath = configPath || DEFAULT_CONFIG_PATH; + this._configPath = configPath ? path.resolve(modulePath, configPath) : DEFAULT_CONFIG_PATH; this._dependencies = Object.create(null); if (!Array.isArray(configuration)) { diff --git a/packages/project/lib/graph/Workspace.js b/packages/project/lib/graph/Workspace.js index cae5119aef9..2cb3c80b18b 100644 --- a/packages/project/lib/graph/Workspace.js +++ b/packages/project/lib/graph/Workspace.js @@ -144,6 +144,16 @@ class Workspace { throw new Error( `Missing property 'path' in dependency resolution configuration of workspace ${this.getName()}`); } + if (path.isAbsolute(resolutionConfig.path)) { + throw new Error( + `Absolute resolution path (${resolutionConfig.path}) is not supported in dependency resolution ` + + `configuration of workspace ${this.getName()}`); + } + if (resolutionConfig.configPath && path.isAbsolute(resolutionConfig.configPath)) { + throw new Error( + `Absolute configPath (${resolutionConfig.configPath}) is not supported in dependency resolution ` + + `configuration of workspace ${this.getName()}`); + } return await this._getModulesFromPath( this.#cwd, resolutionConfig.path, resolutionConfig.configPath); })); diff --git a/packages/project/test/fixtures/library.e/ui5-library.d-config.yaml b/packages/project/test/fixtures/library.e/ui5-library.d-config.yaml new file mode 100644 index 00000000000..a9f1cdca018 --- /dev/null +++ b/packages/project/test/fixtures/library.e/ui5-library.d-config.yaml @@ -0,0 +1,11 @@ +--- +specVersion: "2.3" +type: library +metadata: + name: library.d + copyright: This is a config for another library (outside of this dir) +resources: + configuration: + paths: + src: main/src + test: main/test diff --git a/packages/project/test/lib/graph/Workspace.js b/packages/project/test/lib/graph/Workspace.js index c78ac320a78..0d3dac931b6 100644 --- a/packages/project/test/lib/graph/Workspace.js +++ b/packages/project/test/lib/graph/Workspace.js @@ -474,6 +474,23 @@ test("Missing parameters", (t) => { }, "Threw with expected error message"); }); +test("Absolute resolution path", async (t) => { + const workspace = new t.context.Workspace({ + cwd: __dirname, + configuration: createWorkspaceConfig({ + dependencyManagement: { + resolutions: [{ + path: path.resolve(__dirname, "../../fixtures/library.d") + }] + } + }) + }); + + const err = await t.throwsAsync(async () => await workspace._getResolvedModules()); + t.is(err.message.includes(`Absolute resolution path (${path.resolve(__dirname, "../../fixtures/library.d")}) `+ + `is not supported in dependency resolution configuration of workspace ${workspace.getName()}`), true); +}); + test("workspace/1.1: Basic validation with specified configPath for dependency", async (t) => { const workspace = new t.context.Workspace({ cwd: __dirname, @@ -493,7 +510,10 @@ test("workspace/1.1: Basic validation with specified configPath for dependency", const {projectNameMap} = await workspace._getResolvedModules(); t.deepEqual(Array.from(projectNameMap.keys()).sort(), ["library.d", "library.e"], "Correct project name keys"); - t.is(projectNameMap.get("library.d")._configPath, "custom-config-path.yaml"); + // Custom config: + t.is(projectNameMap.get("library.d")._configPath, + path.resolve(__dirname, "../../fixtures/library.d/custom-config-path.yaml")); + // Default config: t.is(projectNameMap.get("library.e")._configPath, "ui5.yaml"); }); @@ -517,6 +537,25 @@ test("workspace/1.1: Custom configPath for dependency but specVersion is workspa t.is(err.message.includes("\"configPath\" is only supported for specVersion \"workspace/1.1\" and higher"), true); }); +test("workspace/1.1: Absolute custom configPath", async (t) => { + const workspace = new t.context.Workspace({ + cwd: __dirname, + configuration: createWorkspaceConfig({ + specVersion: "workspace/1.1", + dependencyManagement: { + resolutions: [{ + path: "../../fixtures/library.d", + configPath: path.resolve(__dirname, "ui5.yaml") // Absolute configPath + }] + } + }) + }); + + const err = await t.throwsAsync(async () => await workspace._getResolvedModules()); + t.is(err.message.includes(`Absolute configPath (${path.resolve(__dirname, "ui5.yaml")}) ` + + `is not supported in dependency resolution configuration of workspace ${workspace.getName()}`), true); +}); + test("workspace/1.1: Custom configPath for dependency does not exist", async (t) => { const workspace = new t.context.Workspace({ cwd: __dirname, @@ -525,16 +564,40 @@ test("workspace/1.1: Custom configPath for dependency does not exist", async (t) dependencyManagement: { resolutions: [{ path: "../../fixtures/library.d", - configPath: "not-existing-config-path.yaml" // Custom configPath - }, { - path: "../../fixtures/library.e", - // no custom yaml -> resorts to default: ui5.yaml + configPath: "not-existing-config-path.yaml" // Custom not-existing configPath }] } }) }); const err = await t.throwsAsync(async () => await workspace._getResolvedModules()); - t.is(err.message.includes("Failed to read configuration for module library.d: " + - "Could not find configuration file in module at path 'not-existing-config-path.yaml'"), true); + t.is(err.message.includes(`Failed to read configuration for module library.d at `+ + `'${path.resolve(__dirname, "../../fixtures/library.d/not-existing-config-path.yaml")}'. ` + + `Error: ENOENT: no such file or directory, `+ + `open '${path.resolve(__dirname, "../../fixtures/library.d/not-existing-config-path.yaml")}'`), true); +}); + +test("workspace/1.1: Custom relative configPath which is outside of the module dir", async (t) => { + // This test covers a scenario where the custom relative configPath + // points to a location outside of the module directory. + // Config for library.d is located in library.e: + + const workspace = new t.context.Workspace({ + cwd: __dirname, + configuration: createWorkspaceConfig({ + specVersion: "workspace/1.1", + dependencyManagement: { + resolutions: [{ + path: "../../fixtures/library.d", + configPath: "../library.e/ui5-library.d-config.yaml" // Out-of-root path + }] + } + }) + }); + + const {projectNameMap} = await workspace._getResolvedModules(); + t.deepEqual(Array.from(projectNameMap.keys()).sort(), ["library.d"], "Correct project name key"); + // Returns absolute path of other library config: + t.is(projectNameMap.get("library.d")._configPath, + path.resolve(__dirname, "../../fixtures/library.e/ui5-library.d-config.yaml")); }); From a6f5663214eb9b75610979a8ac751314e8a80483 Mon Sep 17 00:00:00 2001 From: Max Reichmann Date: Fri, 9 Oct 2026 18:20:51 +0200 Subject: [PATCH 05/25] refactor: Adjust workspace config to fix tests --- .../lib/validation/schema/ui5-workspace.json | 8 ++- .../lib/validation/schema/ui5-workspace.js | 57 ++++++++++++++++++- 2 files changed, 60 insertions(+), 5 deletions(-) diff --git a/packages/project/lib/validation/schema/ui5-workspace.json b/packages/project/lib/validation/schema/ui5-workspace.json index f7a4bf76826..7b2916daa3b 100644 --- a/packages/project/lib/validation/schema/ui5-workspace.json +++ b/packages/project/lib/validation/schema/ui5-workspace.json @@ -28,8 +28,12 @@ "properties": { "resolutions": { "items": { - "not": {"required": ["configPath"]}, - "errorMessage": "\"configPath\" is only supported for specVersion \"workspace/1.1\" and higher" + "if": {"type": "object"}, + "then": { + "not": {"required": ["configPath"]}, + "errorMessage": "\"configPath\" is only supported for specVersion \"workspace/1.1\" and higher" + }, + "else": {} } } } diff --git a/packages/project/test/lib/validation/schema/ui5-workspace.js b/packages/project/test/lib/validation/schema/ui5-workspace.js index d34182d359d..82a3e2d22f2 100644 --- a/packages/project/test/lib/validation/schema/ui5-workspace.js +++ b/packages/project/test/lib/validation/schema/ui5-workspace.js @@ -89,7 +89,7 @@ For details, see: https://ui5.github.io/cli/stable/pages/Workspace/#workspace-sp message: "must be equal to one of the allowed values", params: { - allowedValues: ["workspace/1.0"], + allowedValues: ["workspace/1.1", "workspace/1.0"], }, }, ], @@ -290,7 +290,7 @@ For details, see: https://ui5.github.io/cli/stable/pages/Workspace/#workspace-sp message: "must be equal to one of the allowed values", params: { - allowedValues: ["workspace/1.0"], + allowedValues: ["workspace/1.1", "workspace/1.0"], }, }, ], @@ -351,7 +351,7 @@ For details, see: https://ui5.github.io/cli/stable/pages/Workspace/#workspace-sp message: "must be equal to one of the allowed values", params: { - allowedValues: ["workspace/1.0"], + allowedValues: ["workspace/1.1", "workspace/1.0"], }, }, ], @@ -456,3 +456,54 @@ test("Invalid dependencyManagement", async (t) => { ] ); }); + +test("Invalid dependencyManagement: configPath not allowed for workspace/1.0", async (t) => { + await assertValidation( + t, + { + specVersion: "workspace/1.0", + metadata: { + name: "test-spec-name", + }, + dependencyManagement: { + resolutions: [ + { + path: "../my.library", + configPath: "config/ui5.yaml" + }], + }, + }, + [ + { + instancePath: "/dependencyManagement/resolutions/0", + keyword: "errorMessage", + message: "\"configPath\" is only supported for specVersion \"workspace/1.1\" and higher", + params: { + errors: [ + { + instancePath: "/dependencyManagement/resolutions/0", + keyword: "not", + message: "must NOT be valid", + params: {}, + }, + ], + }, + }, + ] + ); +}); + +test("Valid dependencyManagement: configPath allowed for workspace/1.1", async (t) => { + await assertValidation( + t, + { + specVersion: "workspace/1.1", + metadata: { + name: "test-spec-name", + }, + dependencyManagement: { + resolutions: [{path: "../my.library", configPath: "config/ui5.yaml"}], + }, + } + ); +}); From 4c46daa9f60643113d6391653e3677ae76fe32c9 Mon Sep 17 00:00:00 2001 From: Max Reichmann Date: Mon, 12 Oct 2026 15:27:59 +0200 Subject: [PATCH 06/25] test(project): Adjust resolutions path to absolute --- packages/project/lib/graph/Workspace.js | 8 +++--- packages/project/test/lib/graph/Module.js | 14 +++++++---- packages/project/test/lib/graph/Workspace.js | 26 ++++++++++++++------ 3 files changed, 31 insertions(+), 17 deletions(-) diff --git a/packages/project/lib/graph/Workspace.js b/packages/project/lib/graph/Workspace.js index 2cb3c80b18b..e9ef07899b8 100644 --- a/packages/project/lib/graph/Workspace.js +++ b/packages/project/lib/graph/Workspace.js @@ -146,13 +146,13 @@ class Workspace { } if (path.isAbsolute(resolutionConfig.path)) { throw new Error( - `Absolute resolution path (${resolutionConfig.path}) is not supported in dependency resolution ` + - `configuration of workspace ${this.getName()}`); + `Absolute paths are not allowed in workspace configurations of dependencies. ` + + `Found '${resolutionConfig.path}' in workspace '${this.getName()}'`); } if (resolutionConfig.configPath && path.isAbsolute(resolutionConfig.configPath)) { throw new Error( - `Absolute configPath (${resolutionConfig.configPath}) is not supported in dependency resolution ` + - `configuration of workspace ${this.getName()}`); + `Absolute config paths are not allowed in workspace configurations of dependencies. ` + + `Found '${resolutionConfig.configPath}' in workspace '${this.getName()}'`); } return await this._getModulesFromPath( this.#cwd, resolutionConfig.path, resolutionConfig.configPath); diff --git a/packages/project/test/lib/graph/Module.js b/packages/project/test/lib/graph/Module.js index abd57d90860..d94e6aafd4b 100644 --- a/packages/project/test/lib/graph/Module.js +++ b/packages/project/test/lib/graph/Module.js @@ -433,9 +433,11 @@ test("Corrupt configuration in file", async (t) => { }); const err = await t.throwsAsync(ui5Module.getSpecifications()); - t.regex(err.message, - new RegExp("^Failed to parse configuration for project application.a.id at 'ui5-test-corrupt.yaml'.*"), - "Threw with parsing error"); + t.log(err.message); + t.true(err.message.startsWith( + `Failed to parse configuration for project application.a.id at ` + + `'${path.resolve(__dirname, "../../fixtures/application.a/ui5-test-corrupt.yaml")}'`), + "Threw with parsing error"); }); test("Merge keys (<<:) in ui5.yaml are resolved and not left as literal keys", async (t) => { @@ -511,8 +513,10 @@ test("Incorrect config path", async (t) => { const err = await t.throwsAsync(ui5Module.getSpecifications()); t.is(err.message, - "Failed to read configuration for module application.a.id: " + - "Could not find configuration file in module at path 'ui5-does-not-exist.yaml'", + `Failed to read configuration for module application.a.id at `+ + `'${path.resolve(__dirname, "../../fixtures/application.a/ui5-does-not-exist.yaml")}'. `+ + `Error: ENOENT: no such file or directory, open `+ + `'${path.resolve(__dirname, "../../fixtures/application.a/ui5-does-not-exist.yaml")}'`, "Threw with expected error message"); }); diff --git a/packages/project/test/lib/graph/Workspace.js b/packages/project/test/lib/graph/Workspace.js index 0d3dac931b6..0bd426f3567 100644 --- a/packages/project/test/lib/graph/Workspace.js +++ b/packages/project/test/lib/graph/Workspace.js @@ -487,8 +487,10 @@ test("Absolute resolution path", async (t) => { }); const err = await t.throwsAsync(async () => await workspace._getResolvedModules()); - t.is(err.message.includes(`Absolute resolution path (${path.resolve(__dirname, "../../fixtures/library.d")}) `+ - `is not supported in dependency resolution configuration of workspace ${workspace.getName()}`), true); + t.log(err.message); + t.true(err.message.startsWith(`Absolute paths are not allowed in workspace configurations of dependencies. `+ + `Found '${path.resolve(__dirname, "../../fixtures/library.d")}' in workspace '${workspace.getName()}'`), + "Starts with expected error message"); }); test("workspace/1.1: Basic validation with specified configPath for dependency", async (t) => { @@ -534,7 +536,9 @@ test("workspace/1.1: Custom configPath for dependency but specVersion is workspa }); const err = await t.throwsAsync(async () => await workspace._getResolvedModules()); - t.is(err.message.includes("\"configPath\" is only supported for specVersion \"workspace/1.1\" and higher"), true); + t.log(err.message); + t.true(err.message.includes("\"configPath\" is only supported for specVersion \"workspace/1.1\" and higher"), + "Starts with expected error message"); }); test("workspace/1.1: Absolute custom configPath", async (t) => { @@ -545,15 +549,19 @@ test("workspace/1.1: Absolute custom configPath", async (t) => { dependencyManagement: { resolutions: [{ path: "../../fixtures/library.d", - configPath: path.resolve(__dirname, "ui5.yaml") // Absolute configPath + configPath: path.resolve(__dirname, "../../fixtures/library.d/ui5.yaml") // Absolute configPath }] } }) }); const err = await t.throwsAsync(async () => await workspace._getResolvedModules()); - t.is(err.message.includes(`Absolute configPath (${path.resolve(__dirname, "ui5.yaml")}) ` + - `is not supported in dependency resolution configuration of workspace ${workspace.getName()}`), true); + t.log(err.message); + t.true(err.message.startsWith(`Absolute config paths ` + + `are not allowed in workspace configurations of dependencies. ` + + `Found '${path.resolve(__dirname, "../../fixtures/library.d/ui5.yaml")}' `+ + `in workspace 'workspace-name'`), + "Starts with expected error message"); }); test("workspace/1.1: Custom configPath for dependency does not exist", async (t) => { @@ -571,10 +579,12 @@ test("workspace/1.1: Custom configPath for dependency does not exist", async (t) }); const err = await t.throwsAsync(async () => await workspace._getResolvedModules()); - t.is(err.message.includes(`Failed to read configuration for module library.d at `+ + t.log(err.message); + t.true(err.message.startsWith(`Failed to read configuration for module library.d at `+ `'${path.resolve(__dirname, "../../fixtures/library.d/not-existing-config-path.yaml")}'. ` + `Error: ENOENT: no such file or directory, `+ - `open '${path.resolve(__dirname, "../../fixtures/library.d/not-existing-config-path.yaml")}'`), true); + `open '${path.resolve(__dirname, "../../fixtures/library.d/not-existing-config-path.yaml")}'`), + "Starts with expected error message"); }); test("workspace/1.1: Custom relative configPath which is outside of the module dir", async (t) => { From e7f52af3029721aabe398650a5c538dd3246da18 Mon Sep 17 00:00:00 2001 From: Max Reichmann Date: Tue, 13 Oct 2026 10:00:30 +0200 Subject: [PATCH 07/25] refactor: Remove "else" block in schema + Adjust tests --- packages/project/lib/validation/schema/ui5-workspace.json | 3 +-- packages/project/test/lib/graph/Module.js | 7 +++---- packages/project/test/lib/graph/Workspace.js | 4 ---- 3 files changed, 4 insertions(+), 10 deletions(-) diff --git a/packages/project/lib/validation/schema/ui5-workspace.json b/packages/project/lib/validation/schema/ui5-workspace.json index 7b2916daa3b..7f75094dbf5 100644 --- a/packages/project/lib/validation/schema/ui5-workspace.json +++ b/packages/project/lib/validation/schema/ui5-workspace.json @@ -32,8 +32,7 @@ "then": { "not": {"required": ["configPath"]}, "errorMessage": "\"configPath\" is only supported for specVersion \"workspace/1.1\" and higher" - }, - "else": {} + } } } } diff --git a/packages/project/test/lib/graph/Module.js b/packages/project/test/lib/graph/Module.js index d94e6aafd4b..a70685342bc 100644 --- a/packages/project/test/lib/graph/Module.js +++ b/packages/project/test/lib/graph/Module.js @@ -433,11 +433,10 @@ test("Corrupt configuration in file", async (t) => { }); const err = await t.throwsAsync(ui5Module.getSpecifications()); - t.log(err.message); t.true(err.message.startsWith( `Failed to parse configuration for project application.a.id at ` + `'${path.resolve(__dirname, "../../fixtures/application.a/ui5-test-corrupt.yaml")}'`), - "Threw with parsing error"); + `Should throw with parsing error.`); }); test("Merge keys (<<:) in ui5.yaml are resolved and not left as literal keys", async (t) => { @@ -517,7 +516,7 @@ test("Incorrect config path", async (t) => { `'${path.resolve(__dirname, "../../fixtures/application.a/ui5-does-not-exist.yaml")}'. `+ `Error: ENOENT: no such file or directory, open `+ `'${path.resolve(__dirname, "../../fixtures/application.a/ui5-does-not-exist.yaml")}'`, - "Threw with expected error message"); + "Should throw with parsing error."); }); test("Incorrect absolute config path", async (t) => { @@ -532,7 +531,7 @@ test("Incorrect absolute config path", async (t) => { t.true(err.message.startsWith( `Failed to read configuration for module application.a.id at '${configPath}'. Error:`), - "Threw with expected error message"); + "Should throw with parsing error."); }); test("Module without ui5.yaml is ignored", async (t) => { diff --git a/packages/project/test/lib/graph/Workspace.js b/packages/project/test/lib/graph/Workspace.js index 0bd426f3567..18532786bbc 100644 --- a/packages/project/test/lib/graph/Workspace.js +++ b/packages/project/test/lib/graph/Workspace.js @@ -487,7 +487,6 @@ test("Absolute resolution path", async (t) => { }); const err = await t.throwsAsync(async () => await workspace._getResolvedModules()); - t.log(err.message); t.true(err.message.startsWith(`Absolute paths are not allowed in workspace configurations of dependencies. `+ `Found '${path.resolve(__dirname, "../../fixtures/library.d")}' in workspace '${workspace.getName()}'`), "Starts with expected error message"); @@ -536,7 +535,6 @@ test("workspace/1.1: Custom configPath for dependency but specVersion is workspa }); const err = await t.throwsAsync(async () => await workspace._getResolvedModules()); - t.log(err.message); t.true(err.message.includes("\"configPath\" is only supported for specVersion \"workspace/1.1\" and higher"), "Starts with expected error message"); }); @@ -556,7 +554,6 @@ test("workspace/1.1: Absolute custom configPath", async (t) => { }); const err = await t.throwsAsync(async () => await workspace._getResolvedModules()); - t.log(err.message); t.true(err.message.startsWith(`Absolute config paths ` + `are not allowed in workspace configurations of dependencies. ` + `Found '${path.resolve(__dirname, "../../fixtures/library.d/ui5.yaml")}' `+ @@ -579,7 +576,6 @@ test("workspace/1.1: Custom configPath for dependency does not exist", async (t) }); const err = await t.throwsAsync(async () => await workspace._getResolvedModules()); - t.log(err.message); t.true(err.message.startsWith(`Failed to read configuration for module library.d at `+ `'${path.resolve(__dirname, "../../fixtures/library.d/not-existing-config-path.yaml")}'. ` + `Error: ENOENT: no such file or directory, `+ From 451f07cae04fff076d2eaff9077bccf4ff6f5ef8 Mon Sep 17 00:00:00 2001 From: Max Reichmann Date: Tue, 13 Oct 2026 15:25:49 +0200 Subject: [PATCH 08/25] refactor: Fix AJV strict mode warnings --- packages/project/lib/validation/schema/ui5-workspace.json | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/packages/project/lib/validation/schema/ui5-workspace.json b/packages/project/lib/validation/schema/ui5-workspace.json index 7f75094dbf5..bfc61a3ba29 100644 --- a/packages/project/lib/validation/schema/ui5-workspace.json +++ b/packages/project/lib/validation/schema/ui5-workspace.json @@ -25,12 +25,17 @@ "then": { "properties": { "dependencyManagement": { + "type": "object", "properties": { "resolutions": { + "type": "array", "items": { "if": {"type": "object"}, "then": { - "not": {"required": ["configPath"]}, + "not": { + "type": "object", + "required": ["configPath"] + }, "errorMessage": "\"configPath\" is only supported for specVersion \"workspace/1.1\" and higher" } } From 6a0a620192ac626be21675867ed9ba660ce9ec7e Mon Sep 17 00:00:00 2001 From: Max Reichmann Date: Tue, 13 Oct 2026 16:59:08 +0200 Subject: [PATCH 09/25] docs: Remove "v5.0.0+" and only keep "v4.1.0+" --- internal/documentation/docs/pages/Workspace.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/internal/documentation/docs/pages/Workspace.md b/internal/documentation/docs/pages/Workspace.md index a588a33f721..441ea33e611 100644 --- a/internal/documentation/docs/pages/Workspace.md +++ b/internal/documentation/docs/pages/Workspace.md @@ -155,14 +155,14 @@ Unless otherwise noted in the table below, UI5 CLI modules are backward-compatib Version | UI5 CLI Release --- | --- -**`workspace/1.1`** | v5.0.0+ and v4.1.0+ +**`workspace/1.1`** | v4.1.0+ **`workspace/1.0`** | v3.0.0+ ### Specification Version `workspace/1.1` Added [`configPath`](#configpath) property to resolution entries. -Specification Version `workspace/1.1` configurations are compatible with [UI5 CLI](https://github.com/SAP/ui5-cli) v5.0.0 and above, as well as v4.1.0 and above. +Specification Version `workspace/1.1` configurations are compatible with [UI5 CLI](https://github.com/UI5/cli) v4.1.0 and above. ### Specification Version `workspace/1.0` From 20032ce0e7fab5cf40024c0d237096f1dc17961a Mon Sep 17 00:00:00 2001 From: Max Reichmann <91143102+maxreichmann@users.noreply.github.com> Date: Wed, 14 Oct 2026 13:16:37 +0200 Subject: [PATCH 10/25] docs: Apply @d3xter666 suggestion Co-authored-by: Yavor Ivanov --- packages/project/lib/validation/schema/ui5-workspace.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/project/lib/validation/schema/ui5-workspace.json b/packages/project/lib/validation/schema/ui5-workspace.json index bfc61a3ba29..ab2b5d5abea 100644 --- a/packages/project/lib/validation/schema/ui5-workspace.json +++ b/packages/project/lib/validation/schema/ui5-workspace.json @@ -10,7 +10,7 @@ "properties": { "specVersion": { "enum": ["workspace/1.1", "workspace/1.0"], - "errorMessage": "Unsupported \"specVersion\"\nYour UI5 CLI installation might be outdated.\nSupported specification versions: \"workspace/1.0\"\nFor details, see: https://ui5.github.io/cli/stable/pages/Workspace/#workspace-specification-versions" + "errorMessage": "Unsupported \"specVersion\"\nYour UI5 CLI installation might be outdated.\nSupported specification versions: \"workspace/1.0\", \"workspace/1.1\".\nFor details, see: https://ui5.github.io/cli/stable/pages/Workspace/#workspace-specification-versions" }, "metadata": { "$ref": "#/definitions/metadata" From 305e97a0f2a707b3043ebfa00cbd4cd07636e857 Mon Sep 17 00:00:00 2001 From: Max Reichmann Date: Thu, 15 Oct 2026 16:43:18 +0200 Subject: [PATCH 11/25] refactor: Add temp test matrix (DELETE LATER) --- configPath-test-matrix.md | 420 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 420 insertions(+) create mode 100644 configPath-test-matrix.md diff --git a/configPath-test-matrix.md b/configPath-test-matrix.md new file mode 100644 index 00000000000..e06f2d4f2d4 --- /dev/null +++ b/configPath-test-matrix.md @@ -0,0 +1,420 @@ +TODO: Delete this file before merging + +# `configPath` Test Matrix + +## Scenario 1 + +### GIVEN for all cases: + +``` +root.collection/ +├── library.a/ +│ ├── ui5.yaml +│ ├── ui5-custom.yaml +│ └── ui5-another-custom.yaml +├── library.b/ +│ ├── ui5.yaml +│ ├── ui5-custom.yaml +│ └── ui5-another-custom.yaml +└── collection.a/ + ├── library.c/ + │ ├── ui5.yaml + │ ├── ui5-custom.yaml + │ └── ui5-another-custom.yaml + └── library.d/ + ├── ui5.yaml + ├── ui5-custom.yaml + └── ui5-another-custom.yaml +``` + +--- + +### Case 1 + +**WHEN** + +```yaml +specVersion: "workspace/1.1" +metadata: + name: default +dependencyManagement: + resolutions: + - path: ../root.collection +``` + +**THEN** + +| Resolved library | Config file used | +|------------------|------------------| +| `library.a` | `ui5.yaml` +| `library.b` | `ui5.yaml` +| `collection.a/library.c` | `ui5.yaml` +| `collection.a/library.d` | `ui5.yaml` + + +### Case 2 + +**WHEN** + +```yaml +specVersion: "workspace/1.1" +metadata: + name: default +dependencyManagement: + resolutions: + - path: ../root.collection + configPath: ui5-custom.yaml +``` + +**THEN** + +| Resolved library | Config file used | +|------------------|------------------| +| `library.a` | `ui5-custom.yaml` +| `library.b` | `ui5-custom.yaml` +| `collection.a/library.c` | `ui5-custom.yaml` +| `collection.a/library.d` | `ui5-custom.yaml` + + +### Case 3 + +**WHEN** + +```yaml +specVersion: "workspace/1.1" +metadata: + name: default +dependencyManagement: + resolutions: + - path: ../root.collection + - path: ../root.collection/library.a + configPath: ui5-custom.yaml +``` + +**THEN** + +| Resolved library | Config file used | +|------------------|------------------| +| `library.a` | `ui5-custom.yaml` +| `library.b` | `ui5.yaml` +| `collection.a/library.c` | `ui5.yaml` +| `collection.a/library.d` | `ui5.yaml` + +### Case 4 + +**WHEN** + +```yaml +specVersion: "workspace/1.1" +metadata: + name: default +dependencyManagement: + resolutions: + - path: ../root.collection/library.a + configPath: ui5-custom.yaml + - path: ../root.collection # overwrites the prev. config entirely +``` + +**THEN** + +| Resolved library | Config file used | +|------------------|------------------| +| `library.a` | `ui5.yaml` +| `library.b` | `ui5.yaml` +| `collection.a/library.c` | `ui5.yaml` +| `collection.a/library.d` | `ui5.yaml` + +### Case 5 + +**WHEN** + +```yaml +specVersion: "workspace/1.1" +metadata: + name: default +dependencyManagement: + resolutions: + - path: ../root.collection + configPath: ui5-custom.yaml + - path: ../root.collection/library.a +``` + +**THEN** + +| Resolved library | Config file used | +|------------------|------------------| +| `library.a` | `ui5.yaml` +| `library.b` | `ui5-custom.yaml` +| `collection.a/library.c` | `ui5-custom.yaml` +| `collection.a/library.d` | `ui5-custom.yaml` + +### Case 6 + +**WHEN** + +```yaml +specVersion: "workspace/1.1" +metadata: + name: default +dependencyManagement: + resolutions: + - path: ../root.collection + - path: ../root.collection/library.a + configPath: ui5-custom.yaml + - path: ../root.collection/library.b + configPath: ui5-another-custom.yaml +``` + +**THEN** + +| Resolved library | Config file used | +|------------------|------------------| +| `library.a` | `ui5-custom.yaml` +| `library.b` | `ui5-another-custom.yaml` +| `collection.a/library.c` | `ui5.yaml` +| `collection.a/library.d` | `ui5.yaml` + +### Case 7 + +**WHEN** + +```yaml +specVersion: "workspace/1.1" +metadata: + name: default +dependencyManagement: + resolutions: + - path: ../root.collection + - path: ../root.collection/library.a + configPath: ui5-custom.yaml + - path: ../root.collection/library.a + configPath: ui5-another-custom.yaml +``` + +**THEN** + +| Resolved library | Config file used | +|------------------|------------------| +| `library.a` | `ui5-another-custom.yaml` +| `library.b` | `ui5.yaml` +| `collection.a/library.c` | `ui5.yaml` +| `collection.a/library.d` | `ui5.yaml` + +**Remark:** For the validation, maybe log a warning (because multiple resolutions for the same library exist). + +### Case 8 + +**WHEN** + +```yaml +specVersion: "workspace/1.1" +metadata: + name: default +dependencyManagement: + resolutions: + - path: ../root.collection + - path: ../root.collection/collection.a + configPath: ui5-custom.yaml +``` + +**THEN** + +| Resolved library | Config file used | +|------------------|------------------| +| `library.a` | `ui5.yaml` +| `library.b` | `ui5.yaml` +| `collection.a/library.c` | `ui5-custom.yaml` +| `collection.a/library.d` | `ui5-custom.yaml` + +### Case 9 + +**WHEN** + +```yaml +specVersion: "workspace/1.1" +metadata: + name: default +dependencyManagement: + resolutions: + - path: ../root.collection + - path: ../root.collection/collection.a + configPath: ui5-custom.yaml + - path: ../root.collection/collection.a/library.c + configPath: ui5-another-custom.yaml +``` + +**THEN** + +| Resolved library | Config file used | +|------------------|------------------| +| `library.a` | `ui5.yaml` +| `library.b` | `ui5.yaml` +| `collection.a/library.c` | `ui5-another-custom.yaml` +| `collection.a/library.d` | `ui5-custom.yaml` + +--- +--- + +## Scenario 2 + +### GIVEN for all cases: + +``` +root.collection/ +├── library.a/ +│ ├── ui5.yaml +│ │ # missing ui5-custom.yaml +│ └── ui5-another-custom.yaml +├── library.b/ +│ ├── ui5.yaml +│ ├── ui5-custom.yaml +│ └── ui5-another-custom.yaml +└── collection.a/ + ├── library.c/ + │ ├── ui5.yaml + │ ├── ui5-custom.yaml + │ └── ui5-another-custom.yaml + └── library.d/ + ├── ui5.yaml + ├── ui5-custom.yaml + └── ui5-another-custom.yaml +``` + +--- + +### Case 1 + +**WHEN** + +```yaml +specVersion: "workspace/1.1" +metadata: + name: default +dependencyManagement: + resolutions: + - path: ../root.collection +``` + +**THEN** + +| Resolved library | Config file used | +|------------------|------------------| +| `library.a` | `ui5.yaml` +| `library.b` | `ui5.yaml` +| `collection.a/library.c` | `ui5.yaml` +| `collection.a/library.d` | `ui5.yaml` + + +### Case 2 + +**WHEN** + +```yaml +specVersion: "workspace/1.1" +metadata: + name: default +dependencyManagement: + resolutions: + - path: ../root.collection + configPath: ui5-custom.yaml +``` + +**THEN** + +Error: `Cannot find config file 'ui5-custom.yaml' for path '../root.collection/library.a'` + + +### Case 3 + +**WHEN** + +```yaml +specVersion: "workspace/1.1" +metadata: + name: default +dependencyManagement: + resolutions: + - path: ../root.collection + - path: ../root.collection/library.a + configPath: ui5-custom.yaml +``` + +**THEN** + +Error: `Cannot find config file 'ui5-custom.yaml' for path '../root.collection/library.a'` + +### Case 4 + +**WHEN** + +```yaml +specVersion: "workspace/1.1" +metadata: + name: default +dependencyManagement: + resolutions: + - path: ../root.collection/library.a + configPath: ui5-custom.yaml + - path: ../root.collection # overwrites the prev. config entirely +``` + +**THEN** + +| Resolved library | Config file used | +|------------------|------------------| +| `library.a` | `ui5.yaml` +| `library.b` | `ui5.yaml` +| `collection.a/library.c` | `ui5.yaml` +| `collection.a/library.d` | `ui5.yaml` + +### Case 5 + +**WHEN** + +```yaml +specVersion: "workspace/1.1" +metadata: + name: default +dependencyManagement: + resolutions: + - path: ../root.collection + configPath: ui5-custom.yaml + - path: ../root.collection/library.a +``` + +**THEN** + +| Resolved library | Config file used | +|------------------|------------------| +| `library.a` | `ui5.yaml` +| `library.b` | `ui5-custom.yaml` +| `collection.a/library.c` | `ui5-custom.yaml` +| `collection.a/library.d` | `ui5-custom.yaml` + +### Case 6 + +**WHEN** + +```yaml +specVersion: "workspace/1.1" +metadata: + name: default +dependencyManagement: + resolutions: + - path: ../root.collection + - path: ../root.collection/library.a + configPath: ui5-custom.yaml + - path: ../root.collection/library.a + configPath: ui5-another-custom.yaml +``` + +**THEN** + +| Resolved library | Config file used | +|------------------|------------------| +| `library.a` | `ui5-another-custom.yaml` +| `library.b` | `ui5.yaml` +| `collection.a/library.c` | `ui5.yaml` +| `collection.a/library.d` | `ui5.yaml` + +**Remark:** For the validation, maybe log a warning (because multiple resolutions for the same library exist). From d1127e28b764402fe56ce46d08bb50a12401dea5 Mon Sep 17 00:00:00 2001 From: Max Reichmann Date: Fri, 16 Oct 2026 13:32:09 +0200 Subject: [PATCH 12/25] test(project): Fix schema tests --- .../project/test/lib/validation/schema/ui5-workspace.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/project/test/lib/validation/schema/ui5-workspace.js b/packages/project/test/lib/validation/schema/ui5-workspace.js index 82a3e2d22f2..c503be65d97 100644 --- a/packages/project/test/lib/validation/schema/ui5-workspace.js +++ b/packages/project/test/lib/validation/schema/ui5-workspace.js @@ -79,7 +79,7 @@ test("Empty config", async (t) => { keyword: "errorMessage", message: `Unsupported "specVersion" Your UI5 CLI installation might be outdated. -Supported specification versions: "workspace/1.0" +Supported specification versions: "workspace/1.0", "workspace/1.1". For details, see: https://ui5.github.io/cli/stable/pages/Workspace/#workspace-specification-versions`, params: { errors: [ @@ -280,7 +280,7 @@ test("Invalid fields", async (t) => { keyword: "errorMessage", message: `Unsupported "specVersion" Your UI5 CLI installation might be outdated. -Supported specification versions: "workspace/1.0" +Supported specification versions: "workspace/1.0", "workspace/1.1". For details, see: https://ui5.github.io/cli/stable/pages/Workspace/#workspace-specification-versions`, params: { errors: [ @@ -341,7 +341,7 @@ test("Invalid types", async (t) => { keyword: "errorMessage", message: `Unsupported "specVersion" Your UI5 CLI installation might be outdated. -Supported specification versions: "workspace/1.0" +Supported specification versions: "workspace/1.0", "workspace/1.1". For details, see: https://ui5.github.io/cli/stable/pages/Workspace/#workspace-specification-versions`, params: { errors: [ From e1226fa5f5a3c82a127d280ace3344ae0084356a Mon Sep 17 00:00:00 2001 From: Max Reichmann Date: Fri, 16 Oct 2026 16:01:14 +0200 Subject: [PATCH 13/25] test: Add scenario for configPath in collections --- packages/project/lib/graph/Module.js | 7 ++++--- packages/project/lib/graph/Workspace.js | 2 +- .../collection.a/library.c/package.json | 5 +++++ .../library.c/src/library/a/.library | 17 +++++++++++++++ .../library/a/themes/base/library.source.less | 6 ++++++ .../library.c/ui5-another-custom.yaml | 5 +++++ .../collection.a/library.c/ui5-custom.yaml | 5 +++++ .../collection.a/library.c/ui5.yaml | 5 +++++ .../collection.a/library.d/package.json | 5 +++++ .../library.d/src/library/b/.library | 17 +++++++++++++++ .../library.d/ui5-another-custom.yaml | 5 +++++ .../collection.a/library.d/ui5-custom.yaml | 5 +++++ .../collection.a/library.d/ui5.yaml | 5 +++++ .../collection.c/collection.a/package.json | 9 ++++++++ .../collection.c/library.a/package.json | 5 +++++ .../library.a/src/library/a/.library | 17 +++++++++++++++ .../library/a/themes/base/library.source.less | 6 ++++++ .../library.a/ui5-another-custom.yaml | 5 +++++ .../collection.c/library.a/ui5-custom.yaml | 5 +++++ .../fixtures/collection.c/library.a/ui5.yaml | 5 +++++ .../collection.c/library.b/package.json | 5 +++++ .../library.b/src/library/b/.library | 17 +++++++++++++++ .../library.b/ui5-another-custom.yaml | 5 +++++ .../collection.c/library.b/ui5-custom.yaml | 5 +++++ .../fixtures/collection.c/library.b/ui5.yaml | 5 +++++ .../test/fixtures/collection.c/package.json | 10 +++++++++ packages/project/test/lib/graph/Workspace.js | 21 +++++++++++++++++++ 27 files changed, 205 insertions(+), 4 deletions(-) create mode 100644 packages/project/test/fixtures/collection.c/collection.a/library.c/package.json create mode 100644 packages/project/test/fixtures/collection.c/collection.a/library.c/src/library/a/.library create mode 100644 packages/project/test/fixtures/collection.c/collection.a/library.c/src/library/a/themes/base/library.source.less create mode 100644 packages/project/test/fixtures/collection.c/collection.a/library.c/ui5-another-custom.yaml create mode 100644 packages/project/test/fixtures/collection.c/collection.a/library.c/ui5-custom.yaml create mode 100644 packages/project/test/fixtures/collection.c/collection.a/library.c/ui5.yaml create mode 100644 packages/project/test/fixtures/collection.c/collection.a/library.d/package.json create mode 100644 packages/project/test/fixtures/collection.c/collection.a/library.d/src/library/b/.library create mode 100644 packages/project/test/fixtures/collection.c/collection.a/library.d/ui5-another-custom.yaml create mode 100644 packages/project/test/fixtures/collection.c/collection.a/library.d/ui5-custom.yaml create mode 100644 packages/project/test/fixtures/collection.c/collection.a/library.d/ui5.yaml create mode 100644 packages/project/test/fixtures/collection.c/collection.a/package.json create mode 100644 packages/project/test/fixtures/collection.c/library.a/package.json create mode 100644 packages/project/test/fixtures/collection.c/library.a/src/library/a/.library create mode 100644 packages/project/test/fixtures/collection.c/library.a/src/library/a/themes/base/library.source.less create mode 100644 packages/project/test/fixtures/collection.c/library.a/ui5-another-custom.yaml create mode 100644 packages/project/test/fixtures/collection.c/library.a/ui5-custom.yaml create mode 100644 packages/project/test/fixtures/collection.c/library.a/ui5.yaml create mode 100644 packages/project/test/fixtures/collection.c/library.b/package.json create mode 100644 packages/project/test/fixtures/collection.c/library.b/src/library/b/.library create mode 100644 packages/project/test/fixtures/collection.c/library.b/ui5-another-custom.yaml create mode 100644 packages/project/test/fixtures/collection.c/library.b/ui5-custom.yaml create mode 100644 packages/project/test/fixtures/collection.c/library.b/ui5.yaml create mode 100644 packages/project/test/fixtures/collection.c/package.json diff --git a/packages/project/lib/graph/Module.js b/packages/project/lib/graph/Module.js index 2e31e1ec6ae..d64ec55d650 100644 --- a/packages/project/lib/graph/Module.js +++ b/packages/project/lib/graph/Module.js @@ -70,7 +70,7 @@ class Module { this._id = id; this._version = version; this._modulePath = modulePath; - this._configPath = configPath ? path.resolve(modulePath, configPath) : DEFAULT_CONFIG_PATH; + this._configPath = configPath || DEFAULT_CONFIG_PATH; this._dependencies = Object.create(null); if (!Array.isArray(configuration)) { @@ -309,8 +309,9 @@ class Module { } if (!configResource) { if (configPath !== DEFAULT_CONFIG_PATH) { - throw new Error("Failed to read configuration for module " + - `${this.getId()}: Could not find configuration file in module at path '${configPath}'`); + throw new Error(`Failed to read configuration for module ` + + `${this.getId()}: Could not find configuration file in module at path `+ + `'${this._configPath}'`); } return null; } diff --git a/packages/project/lib/graph/Workspace.js b/packages/project/lib/graph/Workspace.js index e9ef07899b8..f02d4bf3c0f 100644 --- a/packages/project/lib/graph/Workspace.js +++ b/packages/project/lib/graph/Workspace.js @@ -261,7 +261,7 @@ class Workspace { id: pkg.name, version: pkg.version, modulePath: nodePath, - configPath: configPath, + configPath: configPath ? path.resolve(nodePath, configPath) : undefined, })]; } } diff --git a/packages/project/test/fixtures/collection.c/collection.a/library.c/package.json b/packages/project/test/fixtures/collection.c/collection.a/library.c/package.json new file mode 100644 index 00000000000..efbb5e86409 --- /dev/null +++ b/packages/project/test/fixtures/collection.c/collection.a/library.c/package.json @@ -0,0 +1,5 @@ +{ + "name": "library.c", + "version": "1.0.0", + "description": "Simple SAPUI5 based library" +} diff --git a/packages/project/test/fixtures/collection.c/collection.a/library.c/src/library/a/.library b/packages/project/test/fixtures/collection.c/collection.a/library.c/src/library/a/.library new file mode 100644 index 00000000000..ef0ea1065bc --- /dev/null +++ b/packages/project/test/fixtures/collection.c/collection.a/library.c/src/library/a/.library @@ -0,0 +1,17 @@ + + + + library.a + SAP SE + Some fancy copyright ${currentYear} + ${version} + + Library A + + + + library.d + + + + diff --git a/packages/project/test/fixtures/collection.c/collection.a/library.c/src/library/a/themes/base/library.source.less b/packages/project/test/fixtures/collection.c/collection.a/library.c/src/library/a/themes/base/library.source.less new file mode 100644 index 00000000000..ff0f1d5e3df --- /dev/null +++ b/packages/project/test/fixtures/collection.c/collection.a/library.c/src/library/a/themes/base/library.source.less @@ -0,0 +1,6 @@ +@libraryAColor1: lightgoldenrodyellow; + +.library-a-foo { + color: @libraryAColor1; + padding: 1px 2px 3px 4px; +} diff --git a/packages/project/test/fixtures/collection.c/collection.a/library.c/ui5-another-custom.yaml b/packages/project/test/fixtures/collection.c/collection.a/library.c/ui5-another-custom.yaml new file mode 100644 index 00000000000..90edaf85c9f --- /dev/null +++ b/packages/project/test/fixtures/collection.c/collection.a/library.c/ui5-another-custom.yaml @@ -0,0 +1,5 @@ +--- +specVersion: "2.3" +type: library +metadata: + name: library.c.another.custom diff --git a/packages/project/test/fixtures/collection.c/collection.a/library.c/ui5-custom.yaml b/packages/project/test/fixtures/collection.c/collection.a/library.c/ui5-custom.yaml new file mode 100644 index 00000000000..c84f8a4e014 --- /dev/null +++ b/packages/project/test/fixtures/collection.c/collection.a/library.c/ui5-custom.yaml @@ -0,0 +1,5 @@ +--- +specVersion: "2.3" +type: library +metadata: + name: library.c.custom diff --git a/packages/project/test/fixtures/collection.c/collection.a/library.c/ui5.yaml b/packages/project/test/fixtures/collection.c/collection.a/library.c/ui5.yaml new file mode 100644 index 00000000000..7c5e38a7fc1 --- /dev/null +++ b/packages/project/test/fixtures/collection.c/collection.a/library.c/ui5.yaml @@ -0,0 +1,5 @@ +--- +specVersion: "2.3" +type: library +metadata: + name: library.c diff --git a/packages/project/test/fixtures/collection.c/collection.a/library.d/package.json b/packages/project/test/fixtures/collection.c/collection.a/library.d/package.json new file mode 100644 index 00000000000..fd53171e802 --- /dev/null +++ b/packages/project/test/fixtures/collection.c/collection.a/library.d/package.json @@ -0,0 +1,5 @@ +{ + "name": "library.d", + "version": "1.0.0", + "description": "Simple SAPUI5 based library" +} diff --git a/packages/project/test/fixtures/collection.c/collection.a/library.d/src/library/b/.library b/packages/project/test/fixtures/collection.c/collection.a/library.d/src/library/b/.library new file mode 100644 index 00000000000..7128151f3f4 --- /dev/null +++ b/packages/project/test/fixtures/collection.c/collection.a/library.d/src/library/b/.library @@ -0,0 +1,17 @@ + + + + library.b + SAP SE + Some fancy copyright ${currentYear} + ${version} + + Library B + + + + library.d + + + + diff --git a/packages/project/test/fixtures/collection.c/collection.a/library.d/ui5-another-custom.yaml b/packages/project/test/fixtures/collection.c/collection.a/library.d/ui5-another-custom.yaml new file mode 100644 index 00000000000..82c1f167712 --- /dev/null +++ b/packages/project/test/fixtures/collection.c/collection.a/library.d/ui5-another-custom.yaml @@ -0,0 +1,5 @@ +--- +specVersion: "2.3" +type: library +metadata: + name: library.d.another.custom diff --git a/packages/project/test/fixtures/collection.c/collection.a/library.d/ui5-custom.yaml b/packages/project/test/fixtures/collection.c/collection.a/library.d/ui5-custom.yaml new file mode 100644 index 00000000000..47d355df4f9 --- /dev/null +++ b/packages/project/test/fixtures/collection.c/collection.a/library.d/ui5-custom.yaml @@ -0,0 +1,5 @@ +--- +specVersion: "2.3" +type: library +metadata: + name: library.d.custom diff --git a/packages/project/test/fixtures/collection.c/collection.a/library.d/ui5.yaml b/packages/project/test/fixtures/collection.c/collection.a/library.d/ui5.yaml new file mode 100644 index 00000000000..e05b61880d8 --- /dev/null +++ b/packages/project/test/fixtures/collection.c/collection.a/library.d/ui5.yaml @@ -0,0 +1,5 @@ +--- +specVersion: "2.3" +type: library +metadata: + name: library.d diff --git a/packages/project/test/fixtures/collection.c/collection.a/package.json b/packages/project/test/fixtures/collection.c/collection.a/package.json new file mode 100644 index 00000000000..b4e3eb86aea --- /dev/null +++ b/packages/project/test/fixtures/collection.c/collection.a/package.json @@ -0,0 +1,9 @@ +{ + "name": "collection", + "version": "1.0.0", + "description": "Simple Collection", + "workspaces": [ + "library.c", + "library.d" + ] +} diff --git a/packages/project/test/fixtures/collection.c/library.a/package.json b/packages/project/test/fixtures/collection.c/library.a/package.json new file mode 100644 index 00000000000..3529634765e --- /dev/null +++ b/packages/project/test/fixtures/collection.c/library.a/package.json @@ -0,0 +1,5 @@ +{ + "name": "library.a", + "version": "1.0.0", + "description": "Simple SAPUI5 based library" +} diff --git a/packages/project/test/fixtures/collection.c/library.a/src/library/a/.library b/packages/project/test/fixtures/collection.c/library.a/src/library/a/.library new file mode 100644 index 00000000000..ef0ea1065bc --- /dev/null +++ b/packages/project/test/fixtures/collection.c/library.a/src/library/a/.library @@ -0,0 +1,17 @@ + + + + library.a + SAP SE + Some fancy copyright ${currentYear} + ${version} + + Library A + + + + library.d + + + + diff --git a/packages/project/test/fixtures/collection.c/library.a/src/library/a/themes/base/library.source.less b/packages/project/test/fixtures/collection.c/library.a/src/library/a/themes/base/library.source.less new file mode 100644 index 00000000000..ff0f1d5e3df --- /dev/null +++ b/packages/project/test/fixtures/collection.c/library.a/src/library/a/themes/base/library.source.less @@ -0,0 +1,6 @@ +@libraryAColor1: lightgoldenrodyellow; + +.library-a-foo { + color: @libraryAColor1; + padding: 1px 2px 3px 4px; +} diff --git a/packages/project/test/fixtures/collection.c/library.a/ui5-another-custom.yaml b/packages/project/test/fixtures/collection.c/library.a/ui5-another-custom.yaml new file mode 100644 index 00000000000..a1fa609975f --- /dev/null +++ b/packages/project/test/fixtures/collection.c/library.a/ui5-another-custom.yaml @@ -0,0 +1,5 @@ +--- +specVersion: "2.3" +type: library +metadata: + name: library.a.another.custom diff --git a/packages/project/test/fixtures/collection.c/library.a/ui5-custom.yaml b/packages/project/test/fixtures/collection.c/library.a/ui5-custom.yaml new file mode 100644 index 00000000000..62e46e14554 --- /dev/null +++ b/packages/project/test/fixtures/collection.c/library.a/ui5-custom.yaml @@ -0,0 +1,5 @@ +--- +specVersion: "2.3" +type: library +metadata: + name: library.a.custom diff --git a/packages/project/test/fixtures/collection.c/library.a/ui5.yaml b/packages/project/test/fixtures/collection.c/library.a/ui5.yaml new file mode 100644 index 00000000000..8d4784313c3 --- /dev/null +++ b/packages/project/test/fixtures/collection.c/library.a/ui5.yaml @@ -0,0 +1,5 @@ +--- +specVersion: "2.3" +type: library +metadata: + name: library.a diff --git a/packages/project/test/fixtures/collection.c/library.b/package.json b/packages/project/test/fixtures/collection.c/library.b/package.json new file mode 100644 index 00000000000..a1f0a5e1650 --- /dev/null +++ b/packages/project/test/fixtures/collection.c/library.b/package.json @@ -0,0 +1,5 @@ +{ + "name": "library.b", + "version": "1.0.0", + "description": "Simple SAPUI5 based library" +} diff --git a/packages/project/test/fixtures/collection.c/library.b/src/library/b/.library b/packages/project/test/fixtures/collection.c/library.b/src/library/b/.library new file mode 100644 index 00000000000..7128151f3f4 --- /dev/null +++ b/packages/project/test/fixtures/collection.c/library.b/src/library/b/.library @@ -0,0 +1,17 @@ + + + + library.b + SAP SE + Some fancy copyright ${currentYear} + ${version} + + Library B + + + + library.d + + + + diff --git a/packages/project/test/fixtures/collection.c/library.b/ui5-another-custom.yaml b/packages/project/test/fixtures/collection.c/library.b/ui5-another-custom.yaml new file mode 100644 index 00000000000..4c7d3de243b --- /dev/null +++ b/packages/project/test/fixtures/collection.c/library.b/ui5-another-custom.yaml @@ -0,0 +1,5 @@ +--- +specVersion: "2.3" +type: library +metadata: + name: library.b.another.custom diff --git a/packages/project/test/fixtures/collection.c/library.b/ui5-custom.yaml b/packages/project/test/fixtures/collection.c/library.b/ui5-custom.yaml new file mode 100644 index 00000000000..52a13d705f2 --- /dev/null +++ b/packages/project/test/fixtures/collection.c/library.b/ui5-custom.yaml @@ -0,0 +1,5 @@ +--- +specVersion: "2.3" +type: library +metadata: + name: library.b.custom diff --git a/packages/project/test/fixtures/collection.c/library.b/ui5.yaml b/packages/project/test/fixtures/collection.c/library.b/ui5.yaml new file mode 100644 index 00000000000..b2fe5be59ee --- /dev/null +++ b/packages/project/test/fixtures/collection.c/library.b/ui5.yaml @@ -0,0 +1,5 @@ +--- +specVersion: "2.3" +type: library +metadata: + name: library.b diff --git a/packages/project/test/fixtures/collection.c/package.json b/packages/project/test/fixtures/collection.c/package.json new file mode 100644 index 00000000000..edb7e2145da --- /dev/null +++ b/packages/project/test/fixtures/collection.c/package.json @@ -0,0 +1,10 @@ +{ + "name": "collection", + "version": "1.0.0", + "description": "Simple Collection", + "workspaces": [ + "library.a", + "library.b", + "collection.a" + ] +} diff --git a/packages/project/test/lib/graph/Workspace.js b/packages/project/test/lib/graph/Workspace.js index 18532786bbc..e12fb64fb36 100644 --- a/packages/project/test/lib/graph/Workspace.js +++ b/packages/project/test/lib/graph/Workspace.js @@ -607,3 +607,24 @@ test("workspace/1.1: Custom relative configPath which is outside of the module d t.is(projectNameMap.get("library.d")._configPath, path.resolve(__dirname, "../../fixtures/library.e/ui5-library.d-config.yaml")); }); + + +test("workspace/1.1: Nested library collection scenario", async (t) => { + const workspace = new t.context.Workspace({ + cwd: __dirname, + configuration: createWorkspaceConfig({ + specVersion: "workspace/1.1", + dependencyManagement: { + resolutions: [{ + path: "../../fixtures/collection.c", + }] + } + }) + }); + + const {projectNameMap} = await workspace._getResolvedModules(); + t.is(projectNameMap.get("library.a")._configPath, `ui5.yaml`); + t.is(projectNameMap.get("library.b")._configPath, `ui5.yaml`); + t.is(projectNameMap.get("library.c")._configPath, `ui5.yaml`); + t.is(projectNameMap.get("library.d")._configPath, `ui5.yaml`); +}); From 34ed51394c2709748579dd4cd1a6182f3854da5a Mon Sep 17 00:00:00 2001 From: Max Reichmann Date: Fri, 16 Oct 2026 17:01:38 +0200 Subject: [PATCH 14/25] test(project): Add first test case for collection scenarios --- packages/project/test/lib/graph/Workspace.js | 38 ++++++++++++++++---- 1 file changed, 32 insertions(+), 6 deletions(-) diff --git a/packages/project/test/lib/graph/Workspace.js b/packages/project/test/lib/graph/Workspace.js index e12fb64fb36..f8d1f4426ff 100644 --- a/packages/project/test/lib/graph/Workspace.js +++ b/packages/project/test/lib/graph/Workspace.js @@ -492,7 +492,7 @@ test("Absolute resolution path", async (t) => { "Starts with expected error message"); }); -test("workspace/1.1: Basic validation with specified configPath for dependency", async (t) => { +test("[workspace/1.1] Basic validation with specified configPath for dependency", async (t) => { const workspace = new t.context.Workspace({ cwd: __dirname, configuration: createWorkspaceConfig({ @@ -518,7 +518,7 @@ test("workspace/1.1: Basic validation with specified configPath for dependency", t.is(projectNameMap.get("library.e")._configPath, "ui5.yaml"); }); -test("workspace/1.1: Custom configPath for dependency but specVersion is workspace/1.0", async (t) => { +test("[workspace/1.1] Custom configPath for dependency but specVersion is workspace/1.0", async (t) => { const workspace = new t.context.Workspace({ cwd: __dirname, configuration: createWorkspaceConfig({ @@ -539,7 +539,7 @@ test("workspace/1.1: Custom configPath for dependency but specVersion is workspa "Starts with expected error message"); }); -test("workspace/1.1: Absolute custom configPath", async (t) => { +test("[workspace/1.1] Absolute custom configPath", async (t) => { const workspace = new t.context.Workspace({ cwd: __dirname, configuration: createWorkspaceConfig({ @@ -561,7 +561,7 @@ test("workspace/1.1: Absolute custom configPath", async (t) => { "Starts with expected error message"); }); -test("workspace/1.1: Custom configPath for dependency does not exist", async (t) => { +test("[workspace/1.1] Custom configPath for dependency does not exist", async (t) => { const workspace = new t.context.Workspace({ cwd: __dirname, configuration: createWorkspaceConfig({ @@ -583,7 +583,7 @@ test("workspace/1.1: Custom configPath for dependency does not exist", async (t) "Starts with expected error message"); }); -test("workspace/1.1: Custom relative configPath which is outside of the module dir", async (t) => { +test("[workspace/1.1] Custom relative configPath which is outside of the module dir", async (t) => { // This test covers a scenario where the custom relative configPath // points to a location outside of the module directory. // Config for library.d is located in library.e: @@ -609,7 +609,7 @@ test("workspace/1.1: Custom relative configPath which is outside of the module d }); -test("workspace/1.1: Nested library collection scenario", async (t) => { +test("[workspace/1.1] Nested library collection scenario", async (t) => { const workspace = new t.context.Workspace({ cwd: __dirname, configuration: createWorkspaceConfig({ @@ -628,3 +628,29 @@ test("workspace/1.1: Nested library collection scenario", async (t) => { t.is(projectNameMap.get("library.c")._configPath, `ui5.yaml`); t.is(projectNameMap.get("library.d")._configPath, `ui5.yaml`); }); + +test("[workspace/1.1] No configPath", async (t) => { + const workspace = new t.context.Workspace({ + cwd: __dirname, + configuration: createWorkspaceConfig({ + specVersion: "workspace/1.1", + dependencyManagement: { + resolutions: [{ + path: "../../fixtures/collection.c", + configPath: "ui5-custom.yaml" + }] + } + }) + }); + + const {projectNameMap} = await workspace._getResolvedModules(); + t.is(projectNameMap.get("library.a.custom")._configPath, + path.resolve(__dirname, `../../fixtures/collection.c/library.a/ui5-custom.yaml`)); + t.is(projectNameMap.get("library.b.custom")._configPath, + path.resolve(__dirname, `../../fixtures/collection.c/library.b/ui5-custom.yaml`)); + t.is(projectNameMap.get("library.c.custom")._configPath, + path.resolve(__dirname, `../../fixtures/collection.c/collection.a/library.c/ui5-custom.yaml`)); + t.is(projectNameMap.get("library.d.custom")._configPath, + path.resolve(__dirname, `../../fixtures/collection.c/collection.a/library.d/ui5-custom.yaml`)); +}); + From 2ad81ce98e2cf604b6519ca8bdfbd29c5a3df384 Mon Sep 17 00:00:00 2001 From: Max Reichmann Date: Mon, 19 Oct 2026 16:03:31 +0200 Subject: [PATCH 15/25] test(project): Add test cases --- configPath-test-matrix.md | 30 +- .../collection.a/library.c/package.json | 5 + .../library.c/src/library/a/.library | 17 + .../library.c/ui5-another-custom.yaml | 5 + .../collection.a/library.c/ui5-custom.yaml | 5 + .../collection.a/library.c/ui5.yaml | 5 + .../collection.a/library.d/package.json | 5 + .../library.d/src/library/b/.library | 17 + .../library.d/ui5-another-custom.yaml | 5 + .../collection.a/library.d/ui5-custom.yaml | 5 + .../collection.a/library.d/ui5.yaml | 5 + .../collection.a/package.json | 9 + .../library.a/package.json | 5 + .../library.a/src/library/a/.library | 17 + .../library.a/ui5-another-custom.yaml | 5 + .../library.a/ui5.yaml | 5 + .../library.b/package.json | 5 + .../library.b/src/library/b/.library | 17 + .../library.b/ui5-another-custom.yaml | 5 + .../library.b/ui5-custom.yaml | 5 + .../library.b/ui5.yaml | 5 + .../package.json | 10 + .../library/a/themes/base/library.source.less | 6 - .../library/a/themes/base/library.source.less | 6 - packages/project/test/lib/graph/Workspace.js | 380 +++++++++++++++++- 25 files changed, 550 insertions(+), 34 deletions(-) create mode 100644 packages/project/test/fixtures/collection.c.noCustomConfigForLibA/collection.a/library.c/package.json create mode 100644 packages/project/test/fixtures/collection.c.noCustomConfigForLibA/collection.a/library.c/src/library/a/.library create mode 100644 packages/project/test/fixtures/collection.c.noCustomConfigForLibA/collection.a/library.c/ui5-another-custom.yaml create mode 100644 packages/project/test/fixtures/collection.c.noCustomConfigForLibA/collection.a/library.c/ui5-custom.yaml create mode 100644 packages/project/test/fixtures/collection.c.noCustomConfigForLibA/collection.a/library.c/ui5.yaml create mode 100644 packages/project/test/fixtures/collection.c.noCustomConfigForLibA/collection.a/library.d/package.json create mode 100644 packages/project/test/fixtures/collection.c.noCustomConfigForLibA/collection.a/library.d/src/library/b/.library create mode 100644 packages/project/test/fixtures/collection.c.noCustomConfigForLibA/collection.a/library.d/ui5-another-custom.yaml create mode 100644 packages/project/test/fixtures/collection.c.noCustomConfigForLibA/collection.a/library.d/ui5-custom.yaml create mode 100644 packages/project/test/fixtures/collection.c.noCustomConfigForLibA/collection.a/library.d/ui5.yaml create mode 100644 packages/project/test/fixtures/collection.c.noCustomConfigForLibA/collection.a/package.json create mode 100644 packages/project/test/fixtures/collection.c.noCustomConfigForLibA/library.a/package.json create mode 100644 packages/project/test/fixtures/collection.c.noCustomConfigForLibA/library.a/src/library/a/.library create mode 100644 packages/project/test/fixtures/collection.c.noCustomConfigForLibA/library.a/ui5-another-custom.yaml create mode 100644 packages/project/test/fixtures/collection.c.noCustomConfigForLibA/library.a/ui5.yaml create mode 100644 packages/project/test/fixtures/collection.c.noCustomConfigForLibA/library.b/package.json create mode 100644 packages/project/test/fixtures/collection.c.noCustomConfigForLibA/library.b/src/library/b/.library create mode 100644 packages/project/test/fixtures/collection.c.noCustomConfigForLibA/library.b/ui5-another-custom.yaml create mode 100644 packages/project/test/fixtures/collection.c.noCustomConfigForLibA/library.b/ui5-custom.yaml create mode 100644 packages/project/test/fixtures/collection.c.noCustomConfigForLibA/library.b/ui5.yaml create mode 100644 packages/project/test/fixtures/collection.c.noCustomConfigForLibA/package.json delete mode 100644 packages/project/test/fixtures/collection.c/collection.a/library.c/src/library/a/themes/base/library.source.less delete mode 100644 packages/project/test/fixtures/collection.c/library.a/src/library/a/themes/base/library.source.less diff --git a/configPath-test-matrix.md b/configPath-test-matrix.md index e06f2d4f2d4..eac62f6c5de 100644 --- a/configPath-test-matrix.md +++ b/configPath-test-matrix.md @@ -29,7 +29,7 @@ root.collection/ --- -### Case 1 +### ✅ Case 1.1: [workspace/1.1] No configPath **WHEN** @@ -52,7 +52,7 @@ dependencyManagement: | `collection.a/library.d` | `ui5.yaml` -### Case 2 +### ✅ Case 1.2: [workspace/1.1] Custom configPath for all libs **WHEN** @@ -76,7 +76,7 @@ dependencyManagement: | `collection.a/library.d` | `ui5-custom.yaml` -### Case 3 +### ✅ Case 1.3: [workspace/1.1] No configPath + Custom configPath for single lib **WHEN** @@ -100,7 +100,7 @@ dependencyManagement: | `collection.a/library.c` | `ui5.yaml` | `collection.a/library.d` | `ui5.yaml` -### Case 4 +### ✅ Case 1.4: [workspace/1.1] Custom configPath for single lib + No configPath for all libs **WHEN** @@ -124,7 +124,7 @@ dependencyManagement: | `collection.a/library.c` | `ui5.yaml` | `collection.a/library.d` | `ui5.yaml` -### Case 5 +### ✅ Case 1.5: [workspace/1.1] Custom configPath for all libs + No configPath for single lib **WHEN** @@ -148,7 +148,7 @@ dependencyManagement: | `collection.a/library.c` | `ui5-custom.yaml` | `collection.a/library.d` | `ui5-custom.yaml` -### Case 6 +### ✅ Case 1.6: [workspace/1.1] No configPath for all libs + Custom configPaths for two libs **WHEN** @@ -174,7 +174,7 @@ dependencyManagement: | `collection.a/library.c` | `ui5.yaml` | `collection.a/library.d` | `ui5.yaml` -### Case 7 +### ✅ Case 1.7: [workspace/1.1] No configPath for all libs + Two custom configPaths for single lib **WHEN** @@ -202,7 +202,7 @@ dependencyManagement: **Remark:** For the validation, maybe log a warning (because multiple resolutions for the same library exist). -### Case 8 +### ✅ Case 1.8: [workspace/1.1] No configPath for all libs + Custom configPath for sub collection **WHEN** @@ -226,7 +226,7 @@ dependencyManagement: | `collection.a/library.c` | `ui5-custom.yaml` | `collection.a/library.d` | `ui5-custom.yaml` -### Case 9 +### ✅ Case 1.9: [workspace/1.1] No configPath for all libs + Custom configPaths for sub collection & single lib **WHEN** @@ -282,7 +282,7 @@ root.collection/ --- -### Case 1 +### Case 2.1: [workspace/1.1] No configPath -- **⚠️ NO AUTOMATED TEST** **WHEN** @@ -305,7 +305,7 @@ dependencyManagement: | `collection.a/library.d` | `ui5.yaml` -### Case 2 +### ✅ Case 2.2: [workspace/1.1] Custom configPath for all libs -> File not existing for single lib **WHEN** @@ -324,7 +324,7 @@ dependencyManagement: Error: `Cannot find config file 'ui5-custom.yaml' for path '../root.collection/library.a'` -### Case 3 +### ✅ Case 2.3: [workspace/1.1] Custom configPath for single lib -> File not existing for single lib **WHEN** @@ -343,7 +343,7 @@ dependencyManagement: Error: `Cannot find config file 'ui5-custom.yaml' for path '../root.collection/library.a'` -### Case 4 +### ✅ Case 2.4: [workspace/1.1] Not existing configPath for single lib overwritten by No configPath for all libs **WHEN** @@ -367,7 +367,7 @@ dependencyManagement: | `collection.a/library.c` | `ui5.yaml` | `collection.a/library.d` | `ui5.yaml` -### Case 5 +### ✅ Case 2.5: [workspace/1.1] Not existing configPath for single lib overwritten by No configPath for the same lib **WHEN** @@ -391,7 +391,7 @@ dependencyManagement: | `collection.a/library.c` | `ui5-custom.yaml` | `collection.a/library.d` | `ui5-custom.yaml` -### Case 6 +### ✅ Case 2.6: [workspace/1.1] Not existing configPath for single lib overwritten by Custom configPath for the same lib **WHEN** diff --git a/packages/project/test/fixtures/collection.c.noCustomConfigForLibA/collection.a/library.c/package.json b/packages/project/test/fixtures/collection.c.noCustomConfigForLibA/collection.a/library.c/package.json new file mode 100644 index 00000000000..efbb5e86409 --- /dev/null +++ b/packages/project/test/fixtures/collection.c.noCustomConfigForLibA/collection.a/library.c/package.json @@ -0,0 +1,5 @@ +{ + "name": "library.c", + "version": "1.0.0", + "description": "Simple SAPUI5 based library" +} diff --git a/packages/project/test/fixtures/collection.c.noCustomConfigForLibA/collection.a/library.c/src/library/a/.library b/packages/project/test/fixtures/collection.c.noCustomConfigForLibA/collection.a/library.c/src/library/a/.library new file mode 100644 index 00000000000..ef0ea1065bc --- /dev/null +++ b/packages/project/test/fixtures/collection.c.noCustomConfigForLibA/collection.a/library.c/src/library/a/.library @@ -0,0 +1,17 @@ + + + + library.a + SAP SE + Some fancy copyright ${currentYear} + ${version} + + Library A + + + + library.d + + + + diff --git a/packages/project/test/fixtures/collection.c.noCustomConfigForLibA/collection.a/library.c/ui5-another-custom.yaml b/packages/project/test/fixtures/collection.c.noCustomConfigForLibA/collection.a/library.c/ui5-another-custom.yaml new file mode 100644 index 00000000000..90edaf85c9f --- /dev/null +++ b/packages/project/test/fixtures/collection.c.noCustomConfigForLibA/collection.a/library.c/ui5-another-custom.yaml @@ -0,0 +1,5 @@ +--- +specVersion: "2.3" +type: library +metadata: + name: library.c.another.custom diff --git a/packages/project/test/fixtures/collection.c.noCustomConfigForLibA/collection.a/library.c/ui5-custom.yaml b/packages/project/test/fixtures/collection.c.noCustomConfigForLibA/collection.a/library.c/ui5-custom.yaml new file mode 100644 index 00000000000..c84f8a4e014 --- /dev/null +++ b/packages/project/test/fixtures/collection.c.noCustomConfigForLibA/collection.a/library.c/ui5-custom.yaml @@ -0,0 +1,5 @@ +--- +specVersion: "2.3" +type: library +metadata: + name: library.c.custom diff --git a/packages/project/test/fixtures/collection.c.noCustomConfigForLibA/collection.a/library.c/ui5.yaml b/packages/project/test/fixtures/collection.c.noCustomConfigForLibA/collection.a/library.c/ui5.yaml new file mode 100644 index 00000000000..7c5e38a7fc1 --- /dev/null +++ b/packages/project/test/fixtures/collection.c.noCustomConfigForLibA/collection.a/library.c/ui5.yaml @@ -0,0 +1,5 @@ +--- +specVersion: "2.3" +type: library +metadata: + name: library.c diff --git a/packages/project/test/fixtures/collection.c.noCustomConfigForLibA/collection.a/library.d/package.json b/packages/project/test/fixtures/collection.c.noCustomConfigForLibA/collection.a/library.d/package.json new file mode 100644 index 00000000000..fd53171e802 --- /dev/null +++ b/packages/project/test/fixtures/collection.c.noCustomConfigForLibA/collection.a/library.d/package.json @@ -0,0 +1,5 @@ +{ + "name": "library.d", + "version": "1.0.0", + "description": "Simple SAPUI5 based library" +} diff --git a/packages/project/test/fixtures/collection.c.noCustomConfigForLibA/collection.a/library.d/src/library/b/.library b/packages/project/test/fixtures/collection.c.noCustomConfigForLibA/collection.a/library.d/src/library/b/.library new file mode 100644 index 00000000000..7128151f3f4 --- /dev/null +++ b/packages/project/test/fixtures/collection.c.noCustomConfigForLibA/collection.a/library.d/src/library/b/.library @@ -0,0 +1,17 @@ + + + + library.b + SAP SE + Some fancy copyright ${currentYear} + ${version} + + Library B + + + + library.d + + + + diff --git a/packages/project/test/fixtures/collection.c.noCustomConfigForLibA/collection.a/library.d/ui5-another-custom.yaml b/packages/project/test/fixtures/collection.c.noCustomConfigForLibA/collection.a/library.d/ui5-another-custom.yaml new file mode 100644 index 00000000000..82c1f167712 --- /dev/null +++ b/packages/project/test/fixtures/collection.c.noCustomConfigForLibA/collection.a/library.d/ui5-another-custom.yaml @@ -0,0 +1,5 @@ +--- +specVersion: "2.3" +type: library +metadata: + name: library.d.another.custom diff --git a/packages/project/test/fixtures/collection.c.noCustomConfigForLibA/collection.a/library.d/ui5-custom.yaml b/packages/project/test/fixtures/collection.c.noCustomConfigForLibA/collection.a/library.d/ui5-custom.yaml new file mode 100644 index 00000000000..47d355df4f9 --- /dev/null +++ b/packages/project/test/fixtures/collection.c.noCustomConfigForLibA/collection.a/library.d/ui5-custom.yaml @@ -0,0 +1,5 @@ +--- +specVersion: "2.3" +type: library +metadata: + name: library.d.custom diff --git a/packages/project/test/fixtures/collection.c.noCustomConfigForLibA/collection.a/library.d/ui5.yaml b/packages/project/test/fixtures/collection.c.noCustomConfigForLibA/collection.a/library.d/ui5.yaml new file mode 100644 index 00000000000..e05b61880d8 --- /dev/null +++ b/packages/project/test/fixtures/collection.c.noCustomConfigForLibA/collection.a/library.d/ui5.yaml @@ -0,0 +1,5 @@ +--- +specVersion: "2.3" +type: library +metadata: + name: library.d diff --git a/packages/project/test/fixtures/collection.c.noCustomConfigForLibA/collection.a/package.json b/packages/project/test/fixtures/collection.c.noCustomConfigForLibA/collection.a/package.json new file mode 100644 index 00000000000..b4e3eb86aea --- /dev/null +++ b/packages/project/test/fixtures/collection.c.noCustomConfigForLibA/collection.a/package.json @@ -0,0 +1,9 @@ +{ + "name": "collection", + "version": "1.0.0", + "description": "Simple Collection", + "workspaces": [ + "library.c", + "library.d" + ] +} diff --git a/packages/project/test/fixtures/collection.c.noCustomConfigForLibA/library.a/package.json b/packages/project/test/fixtures/collection.c.noCustomConfigForLibA/library.a/package.json new file mode 100644 index 00000000000..3529634765e --- /dev/null +++ b/packages/project/test/fixtures/collection.c.noCustomConfigForLibA/library.a/package.json @@ -0,0 +1,5 @@ +{ + "name": "library.a", + "version": "1.0.0", + "description": "Simple SAPUI5 based library" +} diff --git a/packages/project/test/fixtures/collection.c.noCustomConfigForLibA/library.a/src/library/a/.library b/packages/project/test/fixtures/collection.c.noCustomConfigForLibA/library.a/src/library/a/.library new file mode 100644 index 00000000000..ef0ea1065bc --- /dev/null +++ b/packages/project/test/fixtures/collection.c.noCustomConfigForLibA/library.a/src/library/a/.library @@ -0,0 +1,17 @@ + + + + library.a + SAP SE + Some fancy copyright ${currentYear} + ${version} + + Library A + + + + library.d + + + + diff --git a/packages/project/test/fixtures/collection.c.noCustomConfigForLibA/library.a/ui5-another-custom.yaml b/packages/project/test/fixtures/collection.c.noCustomConfigForLibA/library.a/ui5-another-custom.yaml new file mode 100644 index 00000000000..a1fa609975f --- /dev/null +++ b/packages/project/test/fixtures/collection.c.noCustomConfigForLibA/library.a/ui5-another-custom.yaml @@ -0,0 +1,5 @@ +--- +specVersion: "2.3" +type: library +metadata: + name: library.a.another.custom diff --git a/packages/project/test/fixtures/collection.c.noCustomConfigForLibA/library.a/ui5.yaml b/packages/project/test/fixtures/collection.c.noCustomConfigForLibA/library.a/ui5.yaml new file mode 100644 index 00000000000..8d4784313c3 --- /dev/null +++ b/packages/project/test/fixtures/collection.c.noCustomConfigForLibA/library.a/ui5.yaml @@ -0,0 +1,5 @@ +--- +specVersion: "2.3" +type: library +metadata: + name: library.a diff --git a/packages/project/test/fixtures/collection.c.noCustomConfigForLibA/library.b/package.json b/packages/project/test/fixtures/collection.c.noCustomConfigForLibA/library.b/package.json new file mode 100644 index 00000000000..a1f0a5e1650 --- /dev/null +++ b/packages/project/test/fixtures/collection.c.noCustomConfigForLibA/library.b/package.json @@ -0,0 +1,5 @@ +{ + "name": "library.b", + "version": "1.0.0", + "description": "Simple SAPUI5 based library" +} diff --git a/packages/project/test/fixtures/collection.c.noCustomConfigForLibA/library.b/src/library/b/.library b/packages/project/test/fixtures/collection.c.noCustomConfigForLibA/library.b/src/library/b/.library new file mode 100644 index 00000000000..7128151f3f4 --- /dev/null +++ b/packages/project/test/fixtures/collection.c.noCustomConfigForLibA/library.b/src/library/b/.library @@ -0,0 +1,17 @@ + + + + library.b + SAP SE + Some fancy copyright ${currentYear} + ${version} + + Library B + + + + library.d + + + + diff --git a/packages/project/test/fixtures/collection.c.noCustomConfigForLibA/library.b/ui5-another-custom.yaml b/packages/project/test/fixtures/collection.c.noCustomConfigForLibA/library.b/ui5-another-custom.yaml new file mode 100644 index 00000000000..4c7d3de243b --- /dev/null +++ b/packages/project/test/fixtures/collection.c.noCustomConfigForLibA/library.b/ui5-another-custom.yaml @@ -0,0 +1,5 @@ +--- +specVersion: "2.3" +type: library +metadata: + name: library.b.another.custom diff --git a/packages/project/test/fixtures/collection.c.noCustomConfigForLibA/library.b/ui5-custom.yaml b/packages/project/test/fixtures/collection.c.noCustomConfigForLibA/library.b/ui5-custom.yaml new file mode 100644 index 00000000000..52a13d705f2 --- /dev/null +++ b/packages/project/test/fixtures/collection.c.noCustomConfigForLibA/library.b/ui5-custom.yaml @@ -0,0 +1,5 @@ +--- +specVersion: "2.3" +type: library +metadata: + name: library.b.custom diff --git a/packages/project/test/fixtures/collection.c.noCustomConfigForLibA/library.b/ui5.yaml b/packages/project/test/fixtures/collection.c.noCustomConfigForLibA/library.b/ui5.yaml new file mode 100644 index 00000000000..b2fe5be59ee --- /dev/null +++ b/packages/project/test/fixtures/collection.c.noCustomConfigForLibA/library.b/ui5.yaml @@ -0,0 +1,5 @@ +--- +specVersion: "2.3" +type: library +metadata: + name: library.b diff --git a/packages/project/test/fixtures/collection.c.noCustomConfigForLibA/package.json b/packages/project/test/fixtures/collection.c.noCustomConfigForLibA/package.json new file mode 100644 index 00000000000..edb7e2145da --- /dev/null +++ b/packages/project/test/fixtures/collection.c.noCustomConfigForLibA/package.json @@ -0,0 +1,10 @@ +{ + "name": "collection", + "version": "1.0.0", + "description": "Simple Collection", + "workspaces": [ + "library.a", + "library.b", + "collection.a" + ] +} diff --git a/packages/project/test/fixtures/collection.c/collection.a/library.c/src/library/a/themes/base/library.source.less b/packages/project/test/fixtures/collection.c/collection.a/library.c/src/library/a/themes/base/library.source.less deleted file mode 100644 index ff0f1d5e3df..00000000000 --- a/packages/project/test/fixtures/collection.c/collection.a/library.c/src/library/a/themes/base/library.source.less +++ /dev/null @@ -1,6 +0,0 @@ -@libraryAColor1: lightgoldenrodyellow; - -.library-a-foo { - color: @libraryAColor1; - padding: 1px 2px 3px 4px; -} diff --git a/packages/project/test/fixtures/collection.c/library.a/src/library/a/themes/base/library.source.less b/packages/project/test/fixtures/collection.c/library.a/src/library/a/themes/base/library.source.less deleted file mode 100644 index ff0f1d5e3df..00000000000 --- a/packages/project/test/fixtures/collection.c/library.a/src/library/a/themes/base/library.source.less +++ /dev/null @@ -1,6 +0,0 @@ -@libraryAColor1: lightgoldenrodyellow; - -.library-a-foo { - color: @libraryAColor1; - padding: 1px 2px 3px 4px; -} diff --git a/packages/project/test/lib/graph/Workspace.js b/packages/project/test/lib/graph/Workspace.js index f8d1f4426ff..475099db63d 100644 --- a/packages/project/test/lib/graph/Workspace.js +++ b/packages/project/test/lib/graph/Workspace.js @@ -608,8 +608,8 @@ test("[workspace/1.1] Custom relative configPath which is outside of the module path.resolve(__dirname, "../../fixtures/library.e/ui5-library.d-config.yaml")); }); - -test("[workspace/1.1] Nested library collection scenario", async (t) => { +// DEBUG: Case 1.1 +test("[workspace/1.1] No configPath", async (t) => { const workspace = new t.context.Workspace({ cwd: __dirname, configuration: createWorkspaceConfig({ @@ -623,13 +623,14 @@ test("[workspace/1.1] Nested library collection scenario", async (t) => { }); const {projectNameMap} = await workspace._getResolvedModules(); - t.is(projectNameMap.get("library.a")._configPath, `ui5.yaml`); - t.is(projectNameMap.get("library.b")._configPath, `ui5.yaml`); - t.is(projectNameMap.get("library.c")._configPath, `ui5.yaml`); - t.is(projectNameMap.get("library.d")._configPath, `ui5.yaml`); + t.is(projectNameMap.get("library.a")._configPath, "ui5.yaml"); + t.is(projectNameMap.get("library.b")._configPath, "ui5.yaml"); + t.is(projectNameMap.get("library.c")._configPath, "ui5.yaml"); + t.is(projectNameMap.get("library.d")._configPath, "ui5.yaml"); }); -test("[workspace/1.1] No configPath", async (t) => { +// DEBUG: Case 1.2 +test("[workspace/1.1] Custom configPath for all libs", async (t) => { const workspace = new t.context.Workspace({ cwd: __dirname, configuration: createWorkspaceConfig({ @@ -654,3 +655,368 @@ test("[workspace/1.1] No configPath", async (t) => { path.resolve(__dirname, `../../fixtures/collection.c/collection.a/library.d/ui5-custom.yaml`)); }); +// DEBUG: Case 1.3 +test("[workspace/1.1] No configPath + Custom configPath for single lib", async (t) => { + const workspace = new t.context.Workspace({ + cwd: __dirname, + configuration: createWorkspaceConfig({ + specVersion: "workspace/1.1", + dependencyManagement: { + resolutions: [{ + path: "../../fixtures/collection.c", + }, + { + path: "../../fixtures/collection.c/library.a", + configPath: "ui5-custom.yaml" + }] + } + }) + }); + + const {projectNameMap} = await workspace._getResolvedModules(); + t.is(projectNameMap.get("library.a.custom")._configPath, + path.resolve(__dirname, `../../fixtures/collection.c/library.a/ui5-custom.yaml`)); + t.is(projectNameMap.get("library.b")._configPath, "ui5.yaml"); + t.is(projectNameMap.get("library.c")._configPath, "ui5.yaml"); + t.is(projectNameMap.get("library.d")._configPath, "ui5.yaml"); +}); + +// DEBUG: Case 1.4 +test("[workspace/1.1] Custom configPath for single lib + No configPath for all libs", async (t) => { + const workspace = new t.context.Workspace({ + cwd: __dirname, + configuration: createWorkspaceConfig({ + specVersion: "workspace/1.1", + dependencyManagement: { + resolutions: [{ + path: "../../fixtures/collection.c/library.a", + configPath: "ui5-custom.yaml" + }, + { + path: "../../fixtures/collection.c", + }] + } + }) + }); + + const {projectNameMap} = await workspace._getResolvedModules(); // FIXME: Currently failing for library.a + t.is(projectNameMap.get("library.a")._configPath, "ui5.yaml"); + t.is(projectNameMap.get("library.b")._configPath, "ui5.yaml"); + t.is(projectNameMap.get("library.c")._configPath, "ui5.yaml"); + t.is(projectNameMap.get("library.d")._configPath, "ui5.yaml"); +}); + +// DEBUG: Case 1.5 +test("[workspace/1.1] Custom configPath for all libs + No configPath for single lib", async (t) => { + const workspace = new t.context.Workspace({ + cwd: __dirname, + configuration: createWorkspaceConfig({ + specVersion: "workspace/1.1", + dependencyManagement: { + resolutions: [{ + path: "../../fixtures/collection.c", + configPath: "ui5-custom.yaml" + }, + { + path: "../../fixtures/collection.c/library.a", + }] + } + }) + }); + + const collectionPath = path.resolve(__dirname, "../../fixtures/collection.c"); + + const {projectNameMap} = await workspace._getResolvedModules(); // FIXME: Works but we assumed a failure + t.is(projectNameMap.get("library.a")._configPath, "ui5.yaml"); + t.is(projectNameMap.get("library.b.custom")._configPath, + collectionPath + "/library.b/ui5-custom.yaml"); + t.is(projectNameMap.get("library.c.custom")._configPath, + collectionPath + "/collection.a/library.c/ui5-custom.yaml"); + t.is(projectNameMap.get("library.d.custom")._configPath, + collectionPath + "/collection.a/library.d/ui5-custom.yaml"); +}); + +// DEBUG: Case 1.6 +test("No configPath for all libs + Custom configPaths for two libs", async (t) => { + const workspace = new t.context.Workspace({ + cwd: __dirname, + configuration: createWorkspaceConfig({ + specVersion: "workspace/1.1", + dependencyManagement: { + resolutions: [{ + path: "../../fixtures/collection.c", + }, + { + path: "../../fixtures/collection.c/library.a", + configPath: "ui5-custom.yaml" + }, + { + path: "../../fixtures/collection.c/library.b", + configPath: "ui5-another-custom.yaml" + }] + } + }) + }); + + const collectionPath = path.resolve(__dirname, "../../fixtures/collection.c"); + + const {projectNameMap} = await workspace._getResolvedModules(); + t.is(projectNameMap.get("library.a.custom")._configPath, + collectionPath + "/library.a/ui5-custom.yaml"); + t.is(projectNameMap.get("library.b.another.custom")._configPath, + collectionPath + "/library.b/ui5-another-custom.yaml"); + t.is(projectNameMap.get("library.c")._configPath, + "ui5.yaml"); + t.is(projectNameMap.get("library.d")._configPath, + "ui5.yaml"); +}); + +// DEBUG: Case 1.7 +test("[workspace/1.1] No configPath for all libs + Two custom configPaths for single lib", async (t) => { + const workspace = new t.context.Workspace({ + cwd: __dirname, + configuration: createWorkspaceConfig({ + specVersion: "workspace/1.1", + dependencyManagement: { + resolutions: [{ + path: "../../fixtures/collection.c", + }, + { + path: "../../fixtures/collection.c/library.a", + configPath: "ui5-custom.yaml" + }, + { + path: "../../fixtures/collection.c/library.a", // double assignment + configPath: "ui5-another-custom.yaml" + }] + } + }) + }); + + const collectionPath = path.resolve(__dirname, "../../fixtures/collection.c"); + + const {projectNameMap} = await workspace._getResolvedModules(); + t.is(projectNameMap.get("library.a.another.custom")._configPath, + collectionPath + "/library.a/ui5-another-custom.yaml"); // FIXME: Currently failing (gets "library.a.custom") + t.is(projectNameMap.get("library.b")._configPath, + "ui5.yaml"); + t.is(projectNameMap.get("library.c")._configPath, + "ui5.yaml"); + t.is(projectNameMap.get("library.d")._configPath, + "ui5.yaml"); +}); + +// DEBUG: Case 1.8 +test("[workspace/1.1] No configPath for all libs + Custom configPath for sub collection", async (t) => { + const workspace = new t.context.Workspace({ + cwd: __dirname, + configuration: createWorkspaceConfig({ + specVersion: "workspace/1.1", + dependencyManagement: { + resolutions: [{ + path: "../../fixtures/collection.c", + }, + { + path: "../../fixtures/collection.c/collection.a", // sub collection + configPath: "ui5-custom.yaml" + }] + } + }) + }); + + const collectionPath = path.resolve(__dirname, "../../fixtures/collection.c"); + + const {projectNameMap} = await workspace._getResolvedModules(); + t.is(projectNameMap.get("library.a")._configPath, + "ui5.yaml"); + t.is(projectNameMap.get("library.b")._configPath, + "ui5.yaml"); + t.is(projectNameMap.get("library.c.custom")._configPath, + path.resolve(collectionPath, "collection.a/library.c/ui5-custom.yaml")); + t.is(projectNameMap.get("library.d.custom")._configPath, + path.resolve(collectionPath, "collection.a/library.d/ui5-custom.yaml")); +}); + +// DEBUG: Case 1.9 +test("[workspace/1.1] No configPath for all libs + Custom configPaths for sub collection & single lib", async (t) => { + const workspace = new t.context.Workspace({ + cwd: __dirname, + configuration: createWorkspaceConfig({ + specVersion: "workspace/1.1", + dependencyManagement: { + resolutions: [{ + path: "../../fixtures/collection.c", + }, + { + path: "../../fixtures/collection.c/collection.a", // sub collection + configPath: "ui5-custom.yaml" + }, + { + path: "../../fixtures/collection.c/collection.a/library.c", // lib in sub collection + configPath: "ui5-another-custom.yaml" + }] + } + }) + }); + + const collectionPath = path.resolve(__dirname, "../../fixtures/collection.c"); + + const {projectNameMap} = await workspace._getResolvedModules(); + t.is(projectNameMap.get("library.a")._configPath, + "ui5.yaml"); + t.is(projectNameMap.get("library.b")._configPath, + "ui5.yaml"); + t.is(projectNameMap.get("library.c.another.custom")._configPath, + path.resolve(collectionPath, "collection.a/library.c/ui5-another-custom.yaml")); + t.is(projectNameMap.get("library.d.custom")._configPath, + path.resolve(collectionPath, "collection.a/library.d/ui5-custom.yaml")); +}); + +// DEBUG: Case 2.2 +test("[workspace/1.1] Custom configPath for all libs -> File not existing for single lib", async (t) => { + const workspace = new t.context.Workspace({ + cwd: __dirname, + configuration: createWorkspaceConfig({ + specVersion: "workspace/1.1", + dependencyManagement: { + resolutions: [{ + path: "../../fixtures/collection.c.noCustomConfigForLibA", + configPath: "ui5-custom.yaml" // not existing + }] + } + }) + }); + + const collectionPath = path.resolve(__dirname, "../../fixtures/collection.c.noCustomConfigForLibA"); + const err = await t.throwsAsync(async () => await workspace._getResolvedModules()); + t.true(err.message.startsWith(`Failed to read configuration for module library.a at `+ + `'${collectionPath}/library.a/ui5-custom.yaml'. ` + + `Error: ENOENT: no such file or directory, `+ + `open '${collectionPath}/library.a/ui5-custom.yaml'`), + "Starts with expected error message"); +}); + +// DEBUG: Case 2.3 +test("[workspace/1.1] Custom configPath for single lib -> File not existing for single lib", async (t) => { + const workspace = new t.context.Workspace({ + cwd: __dirname, + configuration: createWorkspaceConfig({ + specVersion: "workspace/1.1", + dependencyManagement: { + resolutions: [{ + path: "../../fixtures/collection.c.noCustomConfigForLibA", + }, + { + path: "../../fixtures/collection.c.noCustomConfigForLibA/library.a", + configPath: "ui5-custom.yaml" // not existing + } + ] + } + }) + }); + + const collectionPath = path.resolve(__dirname, "../../fixtures/collection.c.noCustomConfigForLibA"); + const err = await t.throwsAsync(async () => await workspace._getResolvedModules()); + t.true(err.message.startsWith(`Failed to read configuration for module library.a at `+ + `'${collectionPath}/library.a/ui5-custom.yaml'. ` + + `Error: ENOENT: no such file or directory, `+ + `open '${collectionPath}/library.a/ui5-custom.yaml'`), + "Starts with expected error message"); +}); + +// DEBUG: Case 2.4 +test("[workspace/1.1] Not existing configPath for single lib overwritten by No configPath for all libs", + async (t) => { + const workspace = new t.context.Workspace({ + cwd: __dirname, + configuration: createWorkspaceConfig({ + specVersion: "workspace/1.1", + dependencyManagement: { + resolutions: [ + { + path: "../../fixtures/collection.c.noCustomConfigForLibA/library.a", + configPath: "ui5-custom.yaml" // not existing + }, + { + path: "../../fixtures/collection.c.noCustomConfigForLibA", + }, + ] + } + }) + }); + + const {projectNameMap} = await workspace._getResolvedModules(); // FIXME: Currently failing for library.a + t.is(projectNameMap.get("library.a")._configPath, "ui5.yaml"); + t.is(projectNameMap.get("library.b")._configPath, "ui5.yaml"); + t.is(projectNameMap.get("library.c")._configPath, "ui5.yaml"); + t.is(projectNameMap.get("library.d")._configPath, "ui5.yaml"); + } +); + +// DEBUG: Case 2.5 +test("[workspace/1.1] Not existing configPath for single lib overwritten by No configPath for the same lib", + async (t) => { + const workspace = new t.context.Workspace({ + cwd: __dirname, + configuration: createWorkspaceConfig({ + specVersion: "workspace/1.1", + dependencyManagement: { + resolutions: [{ + path: "../../fixtures/collection.c.noCustomConfigForLibA", + configPath: "ui5-custom.yaml" // not existing for library.a + }, + { + path: "../../fixtures/collection.c.noCustomConfigForLibA/library.a", + }] + } + }) + }); + + const collectionPath = path.resolve(__dirname, "../../fixtures/collection.c.noCustomConfigForLibA"); + + const {projectNameMap} = await workspace._getResolvedModules(); // FIXME: Works but we assumed a failure + t.is(projectNameMap.get("library.a")._configPath, "ui5.yaml"); + t.is(projectNameMap.get("library.b.custom")._configPath, + collectionPath + "/library.b/ui5-custom.yaml"); + t.is(projectNameMap.get("library.c.custom")._configPath, + collectionPath + "/collection.a/library.c/ui5-custom.yaml"); + t.is(projectNameMap.get("library.d.custom")._configPath, + collectionPath + "/collection.a/library.d/ui5-custom.yaml"); + } +); + +// DEBUG: Case 2.6 +test("[workspace/1.1] Not existing configPath for single lib overwritten by Custom configPath for the same lib", + async (t) => { + const workspace = new t.context.Workspace({ + cwd: __dirname, + configuration: createWorkspaceConfig({ + specVersion: "workspace/1.1", + dependencyManagement: { + resolutions: [{ + path: "../../fixtures/collection.c.noCustomConfigForLibA", + }, + { + path: "../../fixtures/collection.c.noCustomConfigForLibA/library.a", + configPath: "ui5-custom.yaml" // not existing + }, + { + path: "../../fixtures/collection.c.noCustomConfigForLibA/library.a", + configPath: "ui5-another-custom.yaml" // double assignment + }] + } + }) + }); + + const collectionPath = path.resolve(__dirname, "../../fixtures/collection.c.noCustomConfigForLibA"); + + const {projectNameMap} = await workspace._getResolvedModules(); // FIXME: Currently failing for library.a + t.is(projectNameMap.get("library.a.another.custom")._configPath, + collectionPath + "/library.a/ui5-another-custom.yaml"); + t.is(projectNameMap.get("library.b")._configPath, "ui5.yaml"); + t.is(projectNameMap.get("library.c")._configPath, "ui5.yaml"); + t.is(projectNameMap.get("library.d")._configPath, "ui5.yaml"); + } +); + + From a284769705f499dbc08f93a3a4b5afe31ab22518 Mon Sep 17 00:00:00 2001 From: Max Reichmann Date: Mon, 19 Oct 2026 22:52:48 +0200 Subject: [PATCH 16/25] test: Add important case isolating "last one wins" logic from current one --- configPath-test-matrix.md | 25 ++++++++++++++++ packages/project/test/lib/graph/Workspace.js | 30 ++++++++++++++++++-- 2 files changed, 53 insertions(+), 2 deletions(-) diff --git a/configPath-test-matrix.md b/configPath-test-matrix.md index eac62f6c5de..9d592484871 100644 --- a/configPath-test-matrix.md +++ b/configPath-test-matrix.md @@ -252,6 +252,31 @@ dependencyManagement: | `collection.a/library.c` | `ui5-another-custom.yaml` | `collection.a/library.d` | `ui5-custom.yaml` +### ✅ Case 1.10: [workspace/1.1] Two custom configPaths for the same single lib (ordering isolation) + +**WHEN** + +```yaml +specVersion: "workspace/1.1" +metadata: + name: default +dependencyManagement: + resolutions: + - path: ../root.collection/library.a + configPath: ui5-custom.yaml + - path: ../root.collection/library.a # same lib, later resolution + configPath: ui5-another-custom.yaml +``` + +**THEN** + +| Resolved library | Config file used | +|------------------|------------------| +| `library.a` | `ui5-another-custom.yaml` + +Only `library.a` is resolved (the two resolutions collapse into a single module). +This case isolates the concept **the last resolution wins** from the original logic "("specificity wins"). + --- --- diff --git a/packages/project/test/lib/graph/Workspace.js b/packages/project/test/lib/graph/Workspace.js index 475099db63d..cffc7f16007 100644 --- a/packages/project/test/lib/graph/Workspace.js +++ b/packages/project/test/lib/graph/Workspace.js @@ -726,7 +726,7 @@ test("[workspace/1.1] Custom configPath for all libs + No configPath for single const collectionPath = path.resolve(__dirname, "../../fixtures/collection.c"); - const {projectNameMap} = await workspace._getResolvedModules(); // FIXME: Works but we assumed a failure + const {projectNameMap} = await workspace._getResolvedModules(); t.is(projectNameMap.get("library.a")._configPath, "ui5.yaml"); t.is(projectNameMap.get("library.b.custom")._configPath, collectionPath + "/library.b/ui5-custom.yaml"); @@ -804,6 +804,7 @@ test("[workspace/1.1] No configPath for all libs + Two custom configPaths for si "ui5.yaml"); t.is(projectNameMap.get("library.d")._configPath, "ui5.yaml"); + t.is(projectNameMap.size, 4, "Exactly 4 libs resolved, library.a not duplicated"); }); // DEBUG: Case 1.8 @@ -872,6 +873,31 @@ test("[workspace/1.1] No configPath for all libs + Custom configPaths for sub co path.resolve(collectionPath, "collection.a/library.d/ui5-custom.yaml")); }); +// DEBUG: Case 1.10 (ordering isolation: two direct resolutions, same lib) +test("[workspace/1.1] Two custom configPaths for the same single lib -> last one wins", async (t) => { + const workspace = new t.context.Workspace({ + cwd: __dirname, + configuration: createWorkspaceConfig({ + specVersion: "workspace/1.1", + dependencyManagement: { + resolutions: [{ + path: "../../fixtures/collection.c/library.a", + configPath: "ui5-custom.yaml" + }, + { + path: "../../fixtures/collection.c/library.a", // same lib, later resolution + configPath: "ui5-another-custom.yaml" + }] + } + }) + }); + + const {projectNameMap} = await workspace._getResolvedModules(); + t.is(projectNameMap.get("library.a.another.custom")._configPath, + path.resolve(__dirname, "../../fixtures/collection.c/library.a/ui5-another-custom.yaml")); + t.is(projectNameMap.size, 1, "Exactly 1 lib resolved, library.a not duplicated"); +}); + // DEBUG: Case 2.2 test("[workspace/1.1] Custom configPath for all libs -> File not existing for single lib", async (t) => { const workspace = new t.context.Workspace({ @@ -974,7 +1000,7 @@ test("[workspace/1.1] Not existing configPath for single lib overwritten by No c const collectionPath = path.resolve(__dirname, "../../fixtures/collection.c.noCustomConfigForLibA"); - const {projectNameMap} = await workspace._getResolvedModules(); // FIXME: Works but we assumed a failure + const {projectNameMap} = await workspace._getResolvedModules(); t.is(projectNameMap.get("library.a")._configPath, "ui5.yaml"); t.is(projectNameMap.get("library.b.custom")._configPath, collectionPath + "/library.b/ui5-custom.yaml"); From 0d4d55c94b657833337490af09966d4e44c5390b Mon Sep 17 00:00:00 2001 From: Max Reichmann Date: Tue, 20 Oct 2026 15:16:06 +0200 Subject: [PATCH 17/25] test: Add test matrix for "specific one wins" logic --- ...d => configPath-test-matrix-lastOneWins.md | 2 +- configPath-test-matrix-specificOneWins.md | 563 ++++++++++++++++++ packages/project/test/lib/graph/Workspace.js | 190 +++++- 3 files changed, 747 insertions(+), 8 deletions(-) rename configPath-test-matrix.md => configPath-test-matrix-lastOneWins.md (99%) create mode 100644 configPath-test-matrix-specificOneWins.md diff --git a/configPath-test-matrix.md b/configPath-test-matrix-lastOneWins.md similarity index 99% rename from configPath-test-matrix.md rename to configPath-test-matrix-lastOneWins.md index 9d592484871..07504004bf1 100644 --- a/configPath-test-matrix.md +++ b/configPath-test-matrix-lastOneWins.md @@ -275,7 +275,7 @@ dependencyManagement: | `library.a` | `ui5-another-custom.yaml` Only `library.a` is resolved (the two resolutions collapse into a single module). -This case isolates the concept **the last resolution wins** from the original logic "("specificity wins"). +This case isolates the concept **the last resolution wins** from the original logic("specificity wins"). --- --- diff --git a/configPath-test-matrix-specificOneWins.md b/configPath-test-matrix-specificOneWins.md new file mode 100644 index 00000000000..584ccaf70bf --- /dev/null +++ b/configPath-test-matrix-specificOneWins.md @@ -0,0 +1,563 @@ +TODO: Delete this file before merging + +# `configPath` Test Matrix + +## Scenario 1 + +### GIVEN for all cases: + +``` +root.collection/ +├── library.a/ +│ ├── ui5.yaml +│ ├── ui5-custom.yaml +│ └── ui5-another-custom.yaml +├── library.b/ +│ ├── ui5.yaml +│ ├── ui5-custom.yaml +│ └── ui5-another-custom.yaml +└── collection.a/ + ├── library.c/ + │ ├── ui5.yaml + │ ├── ui5-custom.yaml + │ └── ui5-another-custom.yaml + └── library.d/ + ├── ui5.yaml + ├── ui5-custom.yaml + └── ui5-another-custom.yaml +``` + +--- + +### Case 1.1: [workspace/1.1] No configPath + +**WHEN** + +```yaml +specVersion: "workspace/1.1" +metadata: + name: default +dependencyManagement: + resolutions: + - path: ../root.collection +``` + +**THEN** + +| Resolved library | Config file used | +|------------------|------------------| +| `library.a` | `ui5.yaml` +| `library.b` | `ui5.yaml` +| `collection.a/library.c` | `ui5.yaml` +| `collection.a/library.d` | `ui5.yaml` + + +### Case 1.2: [workspace/1.1] Custom configPath for all libs + +**WHEN** + +```yaml +specVersion: "workspace/1.1" +metadata: + name: default +dependencyManagement: + resolutions: + - path: ../root.collection + configPath: ui5-custom.yaml +``` + +**THEN** + +| Resolved library | Config file used | +|------------------|------------------| +| `library.a` | `ui5-custom.yaml` +| `library.b` | `ui5-custom.yaml` +| `collection.a/library.c` | `ui5-custom.yaml` +| `collection.a/library.d` | `ui5-custom.yaml` + + +### Case 1.3: [workspace/1.1] No configPath + Custom configPath for single lib + +**WHEN** + +```yaml +specVersion: "workspace/1.1" +metadata: + name: default +dependencyManagement: + resolutions: + - path: ../root.collection + - path: ../root.collection/library.a + configPath: ui5-custom.yaml +``` + +**THEN** + +| Resolved library | Config file used | +|------------------|------------------| +| `library.a` | `ui5-custom.yaml` +| `library.b` | `ui5.yaml` +| `collection.a/library.c` | `ui5.yaml` +| `collection.a/library.d` | `ui5.yaml` + +### ⚠️ Case 1.4: [workspace/1.1] Custom configPath for single lib + No configPath for all libs + +**WHEN** + +```yaml +specVersion: "workspace/1.1" +metadata: + name: default +dependencyManagement: + resolutions: + - path: ../root.collection/library.a + configPath: ui5-custom.yaml + - path: ../root.collection +``` + +**THEN** + +| Resolved library | Config file used | +|------------------|------------------| +| `library.a` | ⚠️ `ui5-custom.yaml` +| `library.b` | `ui5.yaml` +| `collection.a/library.c` | `ui5.yaml` +| `collection.a/library.d` | `ui5.yaml` + +### Case 1.5: [workspace/1.1] Custom configPath for all libs + No configPath for single lib + +**WHEN** + +```yaml +specVersion: "workspace/1.1" +metadata: + name: default +dependencyManagement: + resolutions: + - path: ../root.collection + configPath: ui5-custom.yaml + - path: ../root.collection/library.a +``` + +**THEN** + +| Resolved library | Config file used | +|------------------|------------------| +| `library.a` | `ui5.yaml` +| `library.b` | `ui5-custom.yaml` +| `collection.a/library.c` | `ui5-custom.yaml` +| `collection.a/library.d` | `ui5-custom.yaml` + +### Case 1.5.1: [workspace/1.1] Custom configPath for all libs + No configPath for single lib (order changed) + +**WHEN** + +```yaml +specVersion: "workspace/1.1" +metadata: + name: default +dependencyManagement: + resolutions: + - path: ../root.collection/library.a + - path: ../root.collection + configPath: ui5-custom.yaml +``` + +**THEN** + +| Resolved library | Config file used | +|------------------|------------------| +| `library.a` | `ui5.yaml` +| `library.b` | `ui5-custom.yaml` +| `collection.a/library.c` | `ui5-custom.yaml` +| `collection.a/library.d` | `ui5-custom.yaml` + +### Case 1.6: [workspace/1.1] No configPath for all libs + Custom configPaths for two libs + +**WHEN** + +```yaml +specVersion: "workspace/1.1" +metadata: + name: default +dependencyManagement: + resolutions: + - path: ../root.collection + - path: ../root.collection/library.a + configPath: ui5-custom.yaml + - path: ../root.collection/library.b + configPath: ui5-another-custom.yaml +``` + +**THEN** + +| Resolved library | Config file used | +|------------------|------------------| +| `library.a` | `ui5-custom.yaml` +| `library.b` | `ui5-another-custom.yaml` +| `collection.a/library.c` | `ui5.yaml` +| `collection.a/library.d` | `ui5.yaml` + +### Case 1.7: [workspace/1.1] No configPath for all libs + Two custom configPaths for single lib + +**WHEN** + +```yaml +specVersion: "workspace/1.1" +metadata: + name: default +dependencyManagement: + resolutions: + - path: ../root.collection + - path: ../root.collection/library.a + configPath: ui5-custom.yaml + - path: ../root.collection/library.a + configPath: ui5-another-custom.yaml +``` + +**THEN** + +| Resolved library | Config file used | +|------------------|------------------| +| `library.a` | `ui5-another-custom.yaml` +| `library.b` | `ui5.yaml` +| `collection.a/library.c` | `ui5.yaml` +| `collection.a/library.d` | `ui5.yaml` + +### Case 1.7.1: [workspace/1.1] No configPath for all libs + Two custom configPaths for single lib (order changed) + +**WHEN** + +```yaml +specVersion: "workspace/1.1" +metadata: + name: default +dependencyManagement: + resolutions: + - path: ../root.collection/library.a + configPath: ui5-custom.yaml + - path: ../root.collection + - path: ../root.collection/library.a + configPath: ui5-another-custom.yaml +``` + +**THEN** + +| Resolved library | Config file used | +|------------------|------------------| +| `library.a` | `ui5-another-custom.yaml` +| `library.b` | `ui5.yaml` +| `collection.a/library.c` | `ui5.yaml` +| `collection.a/library.d` | `ui5.yaml` + +**Remark:** For the validation, maybe log a warning (because multiple resolutions for the same library exist). + +### Case 1.8: [workspace/1.1] No configPath for all libs + Custom configPath for sub collection + +**WHEN** + +```yaml +specVersion: "workspace/1.1" +metadata: + name: default +dependencyManagement: + resolutions: + - path: ../root.collection + - path: ../root.collection/collection.a + configPath: ui5-custom.yaml +``` + +**THEN** + +| Resolved library | Config file used | +|------------------|------------------| +| `library.a` | `ui5.yaml` +| `library.b` | `ui5.yaml` +| `collection.a/library.c` | `ui5-custom.yaml` +| `collection.a/library.d` | `ui5-custom.yaml` + +### Case 1.8.1: [workspace/1.1] Custom configPath for sub collection + No configPath for all libs + +**WHEN** + +```yaml +specVersion: "workspace/1.1" +metadata: + name: default +dependencyManagement: + resolutions: + - path: ../root.collection/collection.a + configPath: ui5-custom.yaml + - path: ../root.collection +``` + +**THEN** + +| Resolved library | Config file used | +|------------------|------------------| +| `library.a` | `ui5.yaml` +| `library.b` | `ui5.yaml` +| `collection.a/library.c` | `ui5-custom.yaml` +| `collection.a/library.d` | `ui5-custom.yaml` + +### Case 1.9: [workspace/1.1] No configPath for all libs + Custom configPaths for sub collection & single lib + +**WHEN** + +```yaml +specVersion: "workspace/1.1" +metadata: + name: default +dependencyManagement: + resolutions: + - path: ../root.collection + - path: ../root.collection/collection.a + configPath: ui5-custom.yaml + - path: ../root.collection/collection.a/library.c + configPath: ui5-another-custom.yaml +``` + +**THEN** + +| Resolved library | Config file used | +|------------------|------------------| +| `library.a` | `ui5.yaml` +| `library.b` | `ui5.yaml` +| `collection.a/library.c` | `ui5-another-custom.yaml` +| `collection.a/library.d` | `ui5-custom.yaml` + +### Case 1.9.1: [workspace/1.1] No configPath for all libs + Custom configPaths for sub collection & single lib (order changed) + +**WHEN** + +```yaml +specVersion: "workspace/1.1" +metadata: + name: default +dependencyManagement: + resolutions: + - path: ../root.collection/collection.a/library.c + configPath: ui5-another-custom.yaml + - path: ../root.collection + - path: ../root.collection/collection.a + configPath: ui5-custom.yaml +``` + +**THEN** + +| Resolved library | Config file used | +|------------------|------------------| +| `library.a` | `ui5.yaml` +| `library.b` | `ui5.yaml` +| `collection.a/library.c` | `ui5-another-custom.yaml` +| `collection.a/library.d` | `ui5-custom.yaml` + +### Case 1.10: [workspace/1.1] Two custom configPaths for the same single lib (ordering isolation) + +**WHEN** + +```yaml +specVersion: "workspace/1.1" +metadata: + name: default +dependencyManagement: + resolutions: + - path: ../root.collection/library.a + configPath: ui5-custom.yaml + - path: ../root.collection/library.a # same lib, later resolution + configPath: ui5-another-custom.yaml +``` + +**THEN** + +| Resolved library | Config file used | +|------------------|------------------| +| `library.a` | `ui5-another-custom.yaml` + +Only `library.a` is resolved (the two resolutions collapse into a single module). +This case isolates the concept **the last resolution wins** from the original logic ("specificity wins"). + +--- +--- + +## Scenario 2 + +### GIVEN for all cases: + +``` +root.collection/ +├── library.a/ +│ ├── ui5.yaml +│ │ # missing ui5-custom.yaml +│ └── ui5-another-custom.yaml +├── library.b/ +│ ├── ui5.yaml +│ ├── ui5-custom.yaml +│ └── ui5-another-custom.yaml +└── collection.a/ + ├── library.c/ + │ ├── ui5.yaml + │ ├── ui5-custom.yaml + │ └── ui5-another-custom.yaml + └── library.d/ + ├── ui5.yaml + ├── ui5-custom.yaml + └── ui5-another-custom.yaml +``` + +--- + +### Case 2.1: [workspace/1.1] No configPath -- **⚠️ NO AUTOMATED TEST** + +**WHEN** + +```yaml +specVersion: "workspace/1.1" +metadata: + name: default +dependencyManagement: + resolutions: + - path: ../root.collection +``` + +**THEN** + +| Resolved library | Config file used | +|------------------|------------------| +| `library.a` | `ui5.yaml` +| `library.b` | `ui5.yaml` +| `collection.a/library.c` | `ui5.yaml` +| `collection.a/library.d` | `ui5.yaml` + + +### Case 2.2: [workspace/1.1] Custom configPath for all libs -> File not existing for single lib + +**WHEN** + +```yaml +specVersion: "workspace/1.1" +metadata: + name: default +dependencyManagement: + resolutions: + - path: ../root.collection + configPath: ui5-custom.yaml +``` + +**THEN** + +Error: `Cannot find config file 'ui5-custom.yaml' for path '../root.collection/library.a'` + + +### Case 2.3: [workspace/1.1] Custom configPath for single lib -> File not existing for single lib + +**WHEN** + +```yaml +specVersion: "workspace/1.1" +metadata: + name: default +dependencyManagement: + resolutions: + - path: ../root.collection + - path: ../root.collection/library.a + configPath: ui5-custom.yaml +``` + +**THEN** + +Error: `Cannot find config file 'ui5-custom.yaml' for path '../root.collection/library.a'` + +### ⚠️ Case 2.4: [workspace/1.1] Not existing configPath for single lib overwritten by No configPath for all libs + +**WHEN** + +```yaml +specVersion: "workspace/1.1" +metadata: + name: default +dependencyManagement: + resolutions: + - path: ../root.collection/library.a + configPath: ui5-custom.yaml + - path: ../root.collection +``` + +**THEN** + +Error: `Cannot find config file 'ui5-custom.yaml' for path '../root.collection/library.a'` + +### Case 2.5: [workspace/1.1] Not existing configPath for single lib overwritten by No configPath for the same lib + +**WHEN** + +```yaml +specVersion: "workspace/1.1" +metadata: + name: default +dependencyManagement: + resolutions: + - path: ../root.collection + configPath: ui5-custom.yaml + - path: ../root.collection/library.a +``` + +**THEN** + +| Resolved library | Config file used | +|------------------|------------------| +| `library.a` | `ui5.yaml` +| `library.b` | `ui5-custom.yaml` +| `collection.a/library.c` | `ui5-custom.yaml` +| `collection.a/library.d` | `ui5-custom.yaml` + +### Case 2.6: [workspace/1.1] Not existing configPath for single lib overwritten by Custom configPath for the same lib + +**WHEN** + +```yaml +specVersion: "workspace/1.1" +metadata: + name: default +dependencyManagement: + resolutions: + - path: ../root.collection + - path: ../root.collection/library.a + configPath: ui5-custom.yaml + - path: ../root.collection/library.a + configPath: ui5-another-custom.yaml +``` + +**THEN** + +| Resolved library | Config file used | +|------------------|------------------| +| `library.a` | `ui5-another-custom.yaml` +| `library.b` | `ui5.yaml` +| `collection.a/library.c` | `ui5.yaml` +| `collection.a/library.d` | `ui5.yaml` + +**Remark:** For the validation, maybe log a warning (because multiple resolutions for the same library exist). + +### Case 2.6.1: [workspace/1.1] Not existing configPath for single lib overwritten by Custom configPath for the same lib (order changed) + +**WHEN** + +```yaml +specVersion: "workspace/1.1" +metadata: + name: default +dependencyManagement: + resolutions: + - path: ../root.collection + - path: ../root.collection/library.a + configPath: ui5-another-custom.yaml + - path: ../root.collection/library.a + configPath: ui5-custom.yaml +``` + +**THEN** + +Error: `Cannot find config file 'ui5-custom.yaml' for path '../root.collection/library.a'` + +**Remark:** For the validation, maybe log a warning (because multiple resolutions for the same library exist). diff --git a/packages/project/test/lib/graph/Workspace.js b/packages/project/test/lib/graph/Workspace.js index cffc7f16007..5840cbfbc3b 100644 --- a/packages/project/test/lib/graph/Workspace.js +++ b/packages/project/test/lib/graph/Workspace.js @@ -699,8 +699,11 @@ test("[workspace/1.1] Custom configPath for single lib + No configPath for all l }) }); - const {projectNameMap} = await workspace._getResolvedModules(); // FIXME: Currently failing for library.a - t.is(projectNameMap.get("library.a")._configPath, "ui5.yaml"); + const {projectNameMap} = await workspace._getResolvedModules(); + // "Specific one wins": the specific library.a resolution beats the broader + // collection resolution regardless of order. + t.is(projectNameMap.get("library.a.custom")._configPath, + path.resolve(__dirname, "../../fixtures/collection.c/library.a/ui5-custom.yaml")); t.is(projectNameMap.get("library.b")._configPath, "ui5.yaml"); t.is(projectNameMap.get("library.c")._configPath, "ui5.yaml"); t.is(projectNameMap.get("library.d")._configPath, "ui5.yaml"); @@ -736,6 +739,36 @@ test("[workspace/1.1] Custom configPath for all libs + No configPath for single collectionPath + "/collection.a/library.d/ui5-custom.yaml"); }); +// DEBUG: Case 1.5.1 +test("[workspace/1.1] No configPath for single lib + Custom configPath for all libs", async (t) => { + const workspace = new t.context.Workspace({ + cwd: __dirname, + configuration: createWorkspaceConfig({ + specVersion: "workspace/1.1", + dependencyManagement: { + resolutions: [{ + path: "../../fixtures/collection.c/library.a", + }, + { + path: "../../fixtures/collection.c", + configPath: "ui5-custom.yaml" + }] + } + }) + }); + + const collectionPath = path.resolve(__dirname, "../../fixtures/collection.c"); + + const {projectNameMap} = await workspace._getResolvedModules(); + t.is(projectNameMap.get("library.a")._configPath, "ui5.yaml"); + t.is(projectNameMap.get("library.b.custom")._configPath, + collectionPath + "/library.b/ui5-custom.yaml"); + t.is(projectNameMap.get("library.c.custom")._configPath, + collectionPath + "/collection.a/library.c/ui5-custom.yaml"); + t.is(projectNameMap.get("library.d.custom")._configPath, + collectionPath + "/collection.a/library.d/ui5-custom.yaml"); +}); + // DEBUG: Case 1.6 test("No configPath for all libs + Custom configPaths for two libs", async (t) => { const workspace = new t.context.Workspace({ @@ -807,6 +840,43 @@ test("[workspace/1.1] No configPath for all libs + Two custom configPaths for si t.is(projectNameMap.size, 4, "Exactly 4 libs resolved, library.a not duplicated"); }); +// DEBUG: Case 1.7.1 +test("[workspace/1.1] Two custom configPaths for single lib + No configPath for all libs (interleaved)", + async (t) => { + const workspace = new t.context.Workspace({ + cwd: __dirname, + configuration: createWorkspaceConfig({ + specVersion: "workspace/1.1", + dependencyManagement: { + resolutions: [{ + path: "../../fixtures/collection.c/library.a", + configPath: "ui5-custom.yaml" + }, + { + path: "../../fixtures/collection.c", + }, + { + path: "../../fixtures/collection.c/library.a", // double assignment + configPath: "ui5-another-custom.yaml" + }] + } + }) + }); + + const collectionPath = path.resolve(__dirname, "../../fixtures/collection.c"); + + const {projectNameMap} = await workspace._getResolvedModules(); + t.is(projectNameMap.get("library.a.another.custom")._configPath, + collectionPath + "/library.a/ui5-another-custom.yaml"); + t.is(projectNameMap.get("library.b")._configPath, + "ui5.yaml"); + t.is(projectNameMap.get("library.c")._configPath, + "ui5.yaml"); + t.is(projectNameMap.get("library.d")._configPath, + "ui5.yaml"); + } +); + // DEBUG: Case 1.8 test("[workspace/1.1] No configPath for all libs + Custom configPath for sub collection", async (t) => { const workspace = new t.context.Workspace({ @@ -838,6 +908,39 @@ test("[workspace/1.1] No configPath for all libs + Custom configPath for sub col path.resolve(collectionPath, "collection.a/library.d/ui5-custom.yaml")); }); +// DEBUG: Case 1.8.1 +test("[workspace/1.1] Custom configPath for sub collection + No configPath for all libs", async (t) => { + const workspace = new t.context.Workspace({ + cwd: __dirname, + configuration: createWorkspaceConfig({ + specVersion: "workspace/1.1", + dependencyManagement: { + resolutions: [{ + path: "../../fixtures/collection.c/collection.a", // sub collection + configPath: "ui5-custom.yaml" + }, + { + path: "../../fixtures/collection.c", + }] + } + }) + }); + + const collectionPath = path.resolve(__dirname, "../../fixtures/collection.c"); + + // "Specific one wins": the sub collection resolution beats the broader collection + // resolution regardless of order (mirror of Case 1.8 with reversed order). + const {projectNameMap} = await workspace._getResolvedModules(); + t.is(projectNameMap.get("library.a")._configPath, + "ui5.yaml"); + t.is(projectNameMap.get("library.b")._configPath, + "ui5.yaml"); + t.is(projectNameMap.get("library.c.custom")._configPath, + path.resolve(collectionPath, "collection.a/library.c/ui5-custom.yaml")); + t.is(projectNameMap.get("library.d.custom")._configPath, + path.resolve(collectionPath, "collection.a/library.d/ui5-custom.yaml")); +}); + // DEBUG: Case 1.9 test("[workspace/1.1] No configPath for all libs + Custom configPaths for sub collection & single lib", async (t) => { const workspace = new t.context.Workspace({ @@ -873,6 +976,42 @@ test("[workspace/1.1] No configPath for all libs + Custom configPaths for sub co path.resolve(collectionPath, "collection.a/library.d/ui5-custom.yaml")); }); +// DEBUG: Case 1.9.1 +test("[workspace/1.1] Custom configPaths for sub collection & single lib + No configPath for all libs " + + "(interleaved)", async (t) => { + const workspace = new t.context.Workspace({ + cwd: __dirname, + configuration: createWorkspaceConfig({ + specVersion: "workspace/1.1", + dependencyManagement: { + resolutions: [{ + path: "../../fixtures/collection.c/collection.a/library.c", // lib in sub collection + configPath: "ui5-another-custom.yaml" + }, + { + path: "../../fixtures/collection.c", + }, + { + path: "../../fixtures/collection.c/collection.a", // sub collection + configPath: "ui5-custom.yaml" + }] + } + }) + }); + + const collectionPath = path.resolve(__dirname, "../../fixtures/collection.c"); + + const {projectNameMap} = await workspace._getResolvedModules(); + t.is(projectNameMap.get("library.a")._configPath, + "ui5.yaml"); + t.is(projectNameMap.get("library.b")._configPath, + "ui5.yaml"); + t.is(projectNameMap.get("library.c.another.custom")._configPath, + path.resolve(collectionPath, "collection.a/library.c/ui5-another-custom.yaml")); + t.is(projectNameMap.get("library.d.custom")._configPath, + path.resolve(collectionPath, "collection.a/library.d/ui5-custom.yaml")); +}); + // DEBUG: Case 1.10 (ordering isolation: two direct resolutions, same lib) test("[workspace/1.1] Two custom configPaths for the same single lib -> last one wins", async (t) => { const workspace = new t.context.Workspace({ @@ -971,11 +1110,15 @@ test("[workspace/1.1] Not existing configPath for single lib overwritten by No c }) }); - const {projectNameMap} = await workspace._getResolvedModules(); // FIXME: Currently failing for library.a - t.is(projectNameMap.get("library.a")._configPath, "ui5.yaml"); - t.is(projectNameMap.get("library.b")._configPath, "ui5.yaml"); - t.is(projectNameMap.get("library.c")._configPath, "ui5.yaml"); - t.is(projectNameMap.get("library.d")._configPath, "ui5.yaml"); + const collectionPath = path.resolve(__dirname, "../../fixtures/collection.c.noCustomConfigForLibA"); + // "Specific one wins": the specific library.a + ui5-custom.yaml resolution beats the + // broader collection resolution regardless of order, so the missing file throws. + const err = await t.throwsAsync(async () => await workspace._getResolvedModules()); + t.true(err.message.startsWith(`Failed to read configuration for module library.a at `+ + `'${collectionPath}/library.a/ui5-custom.yaml'. ` + + `Error: ENOENT: no such file or directory, `+ + `open '${collectionPath}/library.a/ui5-custom.yaml'`), + "Starts with expected error message"); } ); @@ -1045,4 +1188,37 @@ test("[workspace/1.1] Not existing configPath for single lib overwritten by Cust } ); +// DEBUG: Case 2.6.1 +test("[workspace/1.1] Not existing configPath for single lib wins as last same-specificity resolution", + async (t) => { + const workspace = new t.context.Workspace({ + cwd: __dirname, + configuration: createWorkspaceConfig({ + specVersion: "workspace/1.1", + dependencyManagement: { + resolutions: [{ + path: "../../fixtures/collection.c.noCustomConfigForLibA", + }, + { + path: "../../fixtures/collection.c.noCustomConfigForLibA/library.a", + configPath: "ui5-another-custom.yaml" + }, + { + path: "../../fixtures/collection.c.noCustomConfigForLibA/library.a", + configPath: "ui5-custom.yaml" // not existing, wins as last resolution + }] + } + }) + }); + + const collectionPath = path.resolve(__dirname, "../../fixtures/collection.c.noCustomConfigForLibA"); + const err = await t.throwsAsync(async () => await workspace._getResolvedModules()); + t.true(err.message.startsWith(`Failed to read configuration for module library.a at `+ + `'${collectionPath}/library.a/ui5-custom.yaml'. ` + + `Error: ENOENT: no such file or directory, `+ + `open '${collectionPath}/library.a/ui5-custom.yaml'`), + "Starts with expected error message"); + } +); + From f8d3951e4879e294f74d6bc9357f73ac8f108490 Mon Sep 17 00:00:00 2001 From: Max Reichmann Date: Wed, 21 Oct 2026 13:53:05 +0200 Subject: [PATCH 18/25] test: Add concept markdown for logic "specific one wins + error on duplicat" --- ...matrix-specificOneWins+ErrorOnDuplicate.md | 596 ++++++++++++++++++ packages/project/test/lib/graph/Workspace.js | 159 +++-- 2 files changed, 704 insertions(+), 51 deletions(-) create mode 100644 configPath-test-matrix-specificOneWins+ErrorOnDuplicate.md diff --git a/configPath-test-matrix-specificOneWins+ErrorOnDuplicate.md b/configPath-test-matrix-specificOneWins+ErrorOnDuplicate.md new file mode 100644 index 00000000000..c9af24e7c1c --- /dev/null +++ b/configPath-test-matrix-specificOneWins+ErrorOnDuplicate.md @@ -0,0 +1,596 @@ +TODO: Delete this file before merging + +# `configPath` Test Matrix + +## Scenario 1 + +### GIVEN for all cases: + +``` +root.collection/ +├── library.a/ +│ ├── ui5.yaml +│ ├── ui5-custom.yaml +│ └── ui5-another-custom.yaml +├── library.b/ +│ ├── ui5.yaml +│ ├── ui5-custom.yaml +│ └── ui5-another-custom.yaml +└── collection.a/ + ├── library.c/ + │ ├── ui5.yaml + │ ├── ui5-custom.yaml + │ └── ui5-another-custom.yaml + └── library.d/ + ├── ui5.yaml + ├── ui5-custom.yaml + └── ui5-another-custom.yaml +``` + +--- + +### Case 1.1: [workspace/1.1] No configPath + +**WHEN** + +```yaml +specVersion: "workspace/1.1" +metadata: + name: default +dependencyManagement: + resolutions: + - path: ../root.collection +``` + +**THEN** + +| Resolved library | Config file used | +|------------------|------------------| +| `library.a` | `ui5.yaml` +| `library.b` | `ui5.yaml` +| `collection.a/library.c` | `ui5.yaml` +| `collection.a/library.d` | `ui5.yaml` + + +### Case 1.2: [workspace/1.1] Custom configPath for all libs + +**WHEN** + +```yaml +specVersion: "workspace/1.1" +metadata: + name: default +dependencyManagement: + resolutions: + - path: ../root.collection + configPath: ui5-custom.yaml +``` + +**THEN** + +| Resolved library | Config file used | +|------------------|------------------| +| `library.a` | `ui5-custom.yaml` +| `library.b` | `ui5-custom.yaml` +| `collection.a/library.c` | `ui5-custom.yaml` +| `collection.a/library.d` | `ui5-custom.yaml` + + +### Case 1.3: [workspace/1.1] No configPath + Custom configPath for single lib + +**WHEN** + +```yaml +specVersion: "workspace/1.1" +metadata: + name: default +dependencyManagement: + resolutions: + - path: ../root.collection + - path: ../root.collection/library.a + configPath: ui5-custom.yaml +``` + +**THEN** + +| Resolved library | Config file used | +|------------------|------------------| +| `library.a` | `ui5-custom.yaml` +| `library.b` | `ui5.yaml` +| `collection.a/library.c` | `ui5.yaml` +| `collection.a/library.d` | `ui5.yaml` + +### Case 1.4: [workspace/1.1] Custom configPath for single lib + No configPath for all libs + +**WHEN** + +```yaml +specVersion: "workspace/1.1" +metadata: + name: default +dependencyManagement: + resolutions: + - path: ../root.collection/library.a + configPath: ui5-custom.yaml + - path: ../root.collection +``` + +**THEN** + +| Resolved library | Config file used | +|------------------|------------------| +| `library.a` | `ui5-custom.yaml` +| `library.b` | `ui5.yaml` +| `collection.a/library.c` | `ui5.yaml` +| `collection.a/library.d` | `ui5.yaml` + +### Case 1.5: [workspace/1.1] Custom configPath for all libs + No configPath for single lib + +**WHEN** + +```yaml +specVersion: "workspace/1.1" +metadata: + name: default +dependencyManagement: + resolutions: + - path: ../root.collection + configPath: ui5-custom.yaml + - path: ../root.collection/library.a +``` + +**THEN** + +| Resolved library | Config file used | +|------------------|------------------| +| `library.a` | `ui5.yaml` +| `library.b` | `ui5-custom.yaml` +| `collection.a/library.c` | `ui5-custom.yaml` +| `collection.a/library.d` | `ui5-custom.yaml` + +### Case 1.5.1: [workspace/1.1] Custom configPath for all libs + No configPath for single lib (order changed) + +**WHEN** + +```yaml +specVersion: "workspace/1.1" +metadata: + name: default +dependencyManagement: + resolutions: + - path: ../root.collection/library.a + - path: ../root.collection + configPath: ui5-custom.yaml +``` + +**THEN** + +| Resolved library | Config file used | +|------------------|------------------| +| `library.a` | `ui5.yaml` +| `library.b` | `ui5-custom.yaml` +| `collection.a/library.c` | `ui5-custom.yaml` +| `collection.a/library.d` | `ui5-custom.yaml` + +### Case 1.6: [workspace/1.1] No configPath for all libs + Custom configPaths for two libs + +**WHEN** + +```yaml +specVersion: "workspace/1.1" +metadata: + name: default +dependencyManagement: + resolutions: + - path: ../root.collection + - path: ../root.collection/library.a + configPath: ui5-custom.yaml + - path: ../root.collection/library.b + configPath: ui5-another-custom.yaml +``` + +**THEN** + +| Resolved library | Config file used | +|------------------|------------------| +| `library.a` | `ui5-custom.yaml` +| `library.b` | `ui5-another-custom.yaml` +| `collection.a/library.c` | `ui5.yaml` +| `collection.a/library.d` | `ui5.yaml` + +### ⚠️ Case 1.7: [workspace/1.1] No configPath for all libs + Two custom configPaths for single lib + +**WHEN** + +```yaml +specVersion: "workspace/1.1" +metadata: + name: default +dependencyManagement: + resolutions: + - path: ../root.collection + - path: ../root.collection/library.a + configPath: ui5-custom.yaml + - path: ../root.collection/library.a + configPath: ui5-another-custom.yaml +``` + +**THEN** + +⚠️ Error: `Multiple resolutions for the same library 'library.a' in workspace config '' exist.` + +### ⚠️ Case 1.7.1: [workspace/1.1] No configPath for all libs + Two custom configPaths for single lib (order changed) + +**WHEN** + +```yaml +specVersion: "workspace/1.1" +metadata: + name: default +dependencyManagement: + resolutions: + - path: ../root.collection/library.a + configPath: ui5-custom.yaml + - path: ../root.collection + - path: ../root.collection/library.a + configPath: ui5-another-custom.yaml +``` + +**THEN** + +⚠️ Error: `Multiple resolutions for the same library 'library.a' in workspace config '' exist.` + +### Case 1.8: [workspace/1.1] No configPath for all libs + Custom configPath for sub collection + +**WHEN** + +```yaml +specVersion: "workspace/1.1" +metadata: + name: default +dependencyManagement: + resolutions: + - path: ../root.collection + - path: ../root.collection/collection.a + configPath: ui5-custom.yaml +``` + +**THEN** + +| Resolved library | Config file used | +|------------------|------------------| +| `library.a` | `ui5.yaml` +| `library.b` | `ui5.yaml` +| `collection.a/library.c` | `ui5-custom.yaml` +| `collection.a/library.d` | `ui5-custom.yaml` + +### Case 1.8.1: [workspace/1.1] Custom configPath for sub collection + No configPath for all libs + +**WHEN** + +```yaml +specVersion: "workspace/1.1" +metadata: + name: default +dependencyManagement: + resolutions: + - path: ../root.collection/collection.a + configPath: ui5-custom.yaml + - path: ../root.collection +``` + +**THEN** + +| Resolved library | Config file used | +|------------------|------------------| +| `library.a` | `ui5.yaml` +| `library.b` | `ui5.yaml` +| `collection.a/library.c` | `ui5-custom.yaml` +| `collection.a/library.d` | `ui5-custom.yaml` + +### Case 1.9: [workspace/1.1] No configPath for all libs + Custom configPaths for sub collection & single lib + +**WHEN** + +```yaml +specVersion: "workspace/1.1" +metadata: + name: default +dependencyManagement: + resolutions: + - path: ../root.collection + - path: ../root.collection/collection.a + configPath: ui5-custom.yaml + - path: ../root.collection/collection.a/library.c + configPath: ui5-another-custom.yaml +``` + +**THEN** + +| Resolved library | Config file used | +|------------------|------------------| +| `library.a` | `ui5.yaml` +| `library.b` | `ui5.yaml` +| `collection.a/library.c` | `ui5-another-custom.yaml` +| `collection.a/library.d` | `ui5-custom.yaml` + +### Case 1.9.1: [workspace/1.1] No configPath for all libs + Custom configPaths for sub collection & single lib (order changed) + +**WHEN** + +```yaml +specVersion: "workspace/1.1" +metadata: + name: default +dependencyManagement: + resolutions: + - path: ../root.collection/collection.a/library.c + configPath: ui5-another-custom.yaml + - path: ../root.collection + - path: ../root.collection/collection.a + configPath: ui5-custom.yaml +``` + +**THEN** + +| Resolved library | Config file used | +|------------------|------------------| +| `library.a` | `ui5.yaml` +| `library.b` | `ui5.yaml` +| `collection.a/library.c` | `ui5-another-custom.yaml` +| `collection.a/library.d` | `ui5-custom.yaml` + +### ⚠️ Case 1.10: [workspace/1.1] Two custom configPaths for the same single lib (ordering isolation) + +**WHEN** + +```yaml +specVersion: "workspace/1.1" +metadata: + name: default +dependencyManagement: + resolutions: + - path: ../root.collection/library.a + configPath: ui5-custom.yaml + - path: ../root.collection/library.a # same lib, later resolution + configPath: ui5-another-custom.yaml +``` + +**THEN** + +⚠️ Error: `Multiple resolutions for the same library 'library.a' in workspace config '' exist.` + +### ⚠️ Case 1.11: [workspace/1.1] Duplicate library via different path strings (no configPath) + +**WHEN** + +```yaml +specVersion: "workspace/1.1" +metadata: + name: default +dependencyManagement: + resolutions: + - path: ../root.collection/library.a + - path: ../root.collection/../root.collection/library.a +``` + +**THEN** + +⚠️ Error: `Multiple resolutions for the same library 'library.a' in workspace config '' exist.` + +### ⚠️ Case 1.11.1: [workspace/1.1] Duplicate library via different path strings (with configPath) + +**WHEN** + +```yaml +specVersion: "workspace/1.1" +metadata: + name: default +dependencyManagement: + resolutions: + - path: ../root.collection/library.a + configPath: ui5-custom.yaml + - path: ../root.collection/../root.collection/library.a + configPath: ui5-another-custom.yaml +``` + +**THEN** + +⚠️ Error: `Multiple resolutions for the same library 'library.a' in workspace config '' exist.` + +### ⚠️ Case 1.11.2: [workspace/1.1] Duplicate library name via different directories + +**WHEN** + +```yaml +specVersion: "workspace/1.1" +metadata: + name: default +dependencyManagement: + resolutions: + - path: ../root.collection/library.a + configPath: ui5-custom.yaml + - path: ../another.root.collection/library.a + configPath: ui5-another-custom.yaml +``` + +**THEN** + +⚠️ Error: `Multiple resolutions for the same library 'library.a' in workspace config '' exist.` + +--- +--- + +## Scenario 2 + +### GIVEN for all cases: + +``` +root.collection/ +├── library.a/ +│ ├── ui5.yaml +│ │ # missing ui5-custom.yaml +│ └── ui5-another-custom.yaml +├── library.b/ +│ ├── ui5.yaml +│ ├── ui5-custom.yaml +│ └── ui5-another-custom.yaml +└── collection.a/ + ├── library.c/ + │ ├── ui5.yaml + │ ├── ui5-custom.yaml + │ └── ui5-another-custom.yaml + └── library.d/ + ├── ui5.yaml + ├── ui5-custom.yaml + └── ui5-another-custom.yaml +``` + +--- + +### Case 2.1: [workspace/1.1] No configPath -- **⚠️ NO AUTOMATED TEST** + +**WHEN** + +```yaml +specVersion: "workspace/1.1" +metadata: + name: default +dependencyManagement: + resolutions: + - path: ../root.collection +``` + +**THEN** + +| Resolved library | Config file used | +|------------------|------------------| +| `library.a` | `ui5.yaml` +| `library.b` | `ui5.yaml` +| `collection.a/library.c` | `ui5.yaml` +| `collection.a/library.d` | `ui5.yaml` + + +### Case 2.2: [workspace/1.1] Custom configPath for all libs -> File not existing for single lib + +**WHEN** + +```yaml +specVersion: "workspace/1.1" +metadata: + name: default +dependencyManagement: + resolutions: + - path: ../root.collection + configPath: ui5-custom.yaml +``` + +**THEN** + +Error: `Cannot find config file 'ui5-custom.yaml' for path '../root.collection/library.a'` + + +### Case 2.3: [workspace/1.1] Custom configPath for single lib -> File not existing for single lib + +**WHEN** + +```yaml +specVersion: "workspace/1.1" +metadata: + name: default +dependencyManagement: + resolutions: + - path: ../root.collection + - path: ../root.collection/library.a + configPath: ui5-custom.yaml +``` + +**THEN** + +Error: `Cannot find config file 'ui5-custom.yaml' for path '../root.collection/library.a'` + +### Case 2.4: [workspace/1.1] Not existing configPath for single lib overwritten by No configPath for all libs + +**WHEN** + +```yaml +specVersion: "workspace/1.1" +metadata: + name: default +dependencyManagement: + resolutions: + - path: ../root.collection/library.a + configPath: ui5-custom.yaml + - path: ../root.collection +``` + +**THEN** + +Error: `Cannot find config file 'ui5-custom.yaml' for path '../root.collection/library.a'` + +### Case 2.5: [workspace/1.1] Not existing configPath for single lib overwritten by No configPath for the same lib + +**WHEN** + +```yaml +specVersion: "workspace/1.1" +metadata: + name: default +dependencyManagement: + resolutions: + - path: ../root.collection + configPath: ui5-custom.yaml + - path: ../root.collection/library.a +``` + +**THEN** + +| Resolved library | Config file used | +|------------------|------------------| +| `library.a` | `ui5.yaml` +| `library.b` | `ui5-custom.yaml` +| `collection.a/library.c` | `ui5-custom.yaml` +| `collection.a/library.d` | `ui5-custom.yaml` + +### ⚠️ Case 2.6: [workspace/1.1] Not existing configPath for single lib overwritten by Custom configPath for the same lib + +**WHEN** + +```yaml +specVersion: "workspace/1.1" +metadata: + name: default +dependencyManagement: + resolutions: + - path: ../root.collection + - path: ../root.collection/library.a + configPath: ui5-custom.yaml + - path: ../root.collection/library.a + configPath: ui5-another-custom.yaml +``` + +**THEN** + +⚠️ Error: `Multiple resolutions for the same library 'library.a' in workspace config '' exist.` + + +### ⚠️ Case 2.6.1: [workspace/1.1] Not existing configPath for single lib overwritten by Custom configPath for the same lib (order changed) + +**WHEN** + +```yaml +specVersion: "workspace/1.1" +metadata: + name: default +dependencyManagement: + resolutions: + - path: ../root.collection + - path: ../root.collection/library.a + configPath: ui5-another-custom.yaml + - path: ../root.collection/library.a + configPath: ui5-custom.yaml +``` + +**THEN** + +⚠️ Error: `Multiple resolutions for the same library 'library.a' in workspace config '' exist.` diff --git a/packages/project/test/lib/graph/Workspace.js b/packages/project/test/lib/graph/Workspace.js index 5840cbfbc3b..63935e09426 100644 --- a/packages/project/test/lib/graph/Workspace.js +++ b/packages/project/test/lib/graph/Workspace.js @@ -819,25 +819,17 @@ test("[workspace/1.1] No configPath for all libs + Two custom configPaths for si configPath: "ui5-custom.yaml" }, { - path: "../../fixtures/collection.c/library.a", // double assignment + path: "../../fixtures/collection.c/library.a", // duplicate resolution configPath: "ui5-another-custom.yaml" }] } }) }); - const collectionPath = path.resolve(__dirname, "../../fixtures/collection.c"); - - const {projectNameMap} = await workspace._getResolvedModules(); - t.is(projectNameMap.get("library.a.another.custom")._configPath, - collectionPath + "/library.a/ui5-another-custom.yaml"); // FIXME: Currently failing (gets "library.a.custom") - t.is(projectNameMap.get("library.b")._configPath, - "ui5.yaml"); - t.is(projectNameMap.get("library.c")._configPath, - "ui5.yaml"); - t.is(projectNameMap.get("library.d")._configPath, - "ui5.yaml"); - t.is(projectNameMap.size, 4, "Exactly 4 libs resolved, library.a not duplicated"); + const err = await t.throwsAsync(async () => await workspace._getResolvedModules()); + t.is(err.message, + `Multiple resolutions for the same library 'library.a' ` + + `in workspace '${workspace.getName()}' exist.`); }); // DEBUG: Case 1.7.1 @@ -856,24 +848,17 @@ test("[workspace/1.1] Two custom configPaths for single lib + No configPath for path: "../../fixtures/collection.c", }, { - path: "../../fixtures/collection.c/library.a", // double assignment + path: "../../fixtures/collection.c/library.a", // duplicate resolution configPath: "ui5-another-custom.yaml" }] } }) }); - const collectionPath = path.resolve(__dirname, "../../fixtures/collection.c"); - - const {projectNameMap} = await workspace._getResolvedModules(); - t.is(projectNameMap.get("library.a.another.custom")._configPath, - collectionPath + "/library.a/ui5-another-custom.yaml"); - t.is(projectNameMap.get("library.b")._configPath, - "ui5.yaml"); - t.is(projectNameMap.get("library.c")._configPath, - "ui5.yaml"); - t.is(projectNameMap.get("library.d")._configPath, - "ui5.yaml"); + const err = await t.throwsAsync(async () => await workspace._getResolvedModules()); + t.is(err.message, + `Multiple resolutions for the same library 'library.a' ` + + `in workspace '${workspace.getName()}' exist.`); } ); @@ -1013,7 +998,7 @@ test("[workspace/1.1] Custom configPaths for sub collection & single lib + No co }); // DEBUG: Case 1.10 (ordering isolation: two direct resolutions, same lib) -test("[workspace/1.1] Two custom configPaths for the same single lib -> last one wins", async (t) => { +test("[workspace/1.1] Two custom configPaths for the same single lib -> error on duplicate", async (t) => { const workspace = new t.context.Workspace({ cwd: __dirname, configuration: createWorkspaceConfig({ @@ -1024,17 +1009,92 @@ test("[workspace/1.1] Two custom configPaths for the same single lib -> last one configPath: "ui5-custom.yaml" }, { - path: "../../fixtures/collection.c/library.a", // same lib, later resolution + path: "../../fixtures/collection.c/library.a", // duplicate resolution configPath: "ui5-another-custom.yaml" }] } }) }); - const {projectNameMap} = await workspace._getResolvedModules(); - t.is(projectNameMap.get("library.a.another.custom")._configPath, - path.resolve(__dirname, "../../fixtures/collection.c/library.a/ui5-another-custom.yaml")); - t.is(projectNameMap.size, 1, "Exactly 1 lib resolved, library.a not duplicated"); + const err = await t.throwsAsync(async () => await workspace._getResolvedModules()); + t.is(err.message, + `Multiple resolutions for the same library 'library.a' ` + + `in workspace '${workspace.getName()}' exist.`); +}); + +// DEBUG: Case 1.11 +test("[workspace/1.1] Duplicate library via different path strings (no configPath)", async (t) => { + const workspace = new t.context.Workspace({ + cwd: __dirname, + configuration: createWorkspaceConfig({ + specVersion: "workspace/1.1", + dependencyManagement: { + resolutions: [{ + path: "../../fixtures/collection.c/library.a" + }, + { + // Different path string, same resolved directory + path: "../../fixtures/collection.c/../collection.c/library.a" + }] + } + }) + }); + + const err = await t.throwsAsync(async () => await workspace._getResolvedModules()); + t.is(err.message, + `Multiple resolutions for the same library 'library.a' ` + + `in workspace '${workspace.getName()}' exist.`); +}); + +// DEBUG: Case 1.11.1 +test("[workspace/1.1] Duplicate library via different path strings (with configPath)", async (t) => { + const workspace = new t.context.Workspace({ + cwd: __dirname, + configuration: createWorkspaceConfig({ + specVersion: "workspace/1.1", + dependencyManagement: { + resolutions: [{ + path: "../../fixtures/collection.c/library.a", + configPath: "ui5-custom.yaml" + }, + { + // Different path string, same resolved directory + path: "../../fixtures/collection.c/../collection.c/library.a", + configPath: "ui5-another-custom.yaml" + }] + } + }) + }); + + const err = await t.throwsAsync(async () => await workspace._getResolvedModules()); + t.is(err.message, + `Multiple resolutions for the same library 'library.a' ` + + `in workspace '${workspace.getName()}' exist.`); +}); + +// DEBUG: Case 1.11.2 +test("[workspace/1.1] Duplicate library name via different directories", async (t) => { + const workspace = new t.context.Workspace({ + cwd: __dirname, + configuration: createWorkspaceConfig({ + specVersion: "workspace/1.1", + dependencyManagement: { + resolutions: [{ + path: "../../fixtures/collection.c/library.a", + configPath: "ui5-custom.yaml" + }, + { + // Different directory, but same library (directory) name 'library.a' + path: "../../fixtures/collection/library.a" + }] + } + }) + }); + + const err = await t.throwsAsync(async () => await workspace._getResolvedModules()); + t.is(err.message, + `Multiple resolutions for the same library 'library.a' ` + + `in workspace '${workspace.getName()}' exist.`); }); // DEBUG: Case 2.2 @@ -1155,7 +1215,7 @@ test("[workspace/1.1] Not existing configPath for single lib overwritten by No c ); // DEBUG: Case 2.6 -test("[workspace/1.1] Not existing configPath for single lib overwritten by Custom configPath for the same lib", +test("[workspace/1.1] Duplicate resolutions for single lib (with missing config) -> error on duplicate", async (t) => { const workspace = new t.context.Workspace({ cwd: __dirname, @@ -1170,26 +1230,24 @@ test("[workspace/1.1] Not existing configPath for single lib overwritten by Cust configPath: "ui5-custom.yaml" // not existing }, { - path: "../../fixtures/collection.c.noCustomConfigForLibA/library.a", - configPath: "ui5-another-custom.yaml" // double assignment + path: "../../fixtures/collection.c.noCustomConfigForLibA/library.a", // duplicate resolution + configPath: "ui5-another-custom.yaml" }] } }) }); - const collectionPath = path.resolve(__dirname, "../../fixtures/collection.c.noCustomConfigForLibA"); - - const {projectNameMap} = await workspace._getResolvedModules(); // FIXME: Currently failing for library.a - t.is(projectNameMap.get("library.a.another.custom")._configPath, - collectionPath + "/library.a/ui5-another-custom.yaml"); - t.is(projectNameMap.get("library.b")._configPath, "ui5.yaml"); - t.is(projectNameMap.get("library.c")._configPath, "ui5.yaml"); - t.is(projectNameMap.get("library.d")._configPath, "ui5.yaml"); + // The duplicate is detected before any config file is read, so the missing + // 'ui5-custom.yaml' never causes an ENOENT; the duplicate error wins. + const err = await t.throwsAsync(async () => await workspace._getResolvedModules()); + t.is(err.message, + `Multiple resolutions for the same library 'library.a' ` + + `in workspace '${workspace.getName()}' exist.`); } ); // DEBUG: Case 2.6.1 -test("[workspace/1.1] Not existing configPath for single lib wins as last same-specificity resolution", +test("[workspace/1.1] Duplicate resolutions for single lib (missing config last) -> error on duplicate", async (t) => { const workspace = new t.context.Workspace({ cwd: __dirname, @@ -1204,20 +1262,19 @@ test("[workspace/1.1] Not existing configPath for single lib wins as last same-s configPath: "ui5-another-custom.yaml" }, { - path: "../../fixtures/collection.c.noCustomConfigForLibA/library.a", - configPath: "ui5-custom.yaml" // not existing, wins as last resolution + path: "../../fixtures/collection.c.noCustomConfigForLibA/library.a", // duplicate resolution + configPath: "ui5-custom.yaml" // not existing }] } }) }); - const collectionPath = path.resolve(__dirname, "../../fixtures/collection.c.noCustomConfigForLibA"); + // The duplicate is detected before any config file is read, so the missing + // 'ui5-custom.yaml' never causes an ENOENT; the duplicate error wins. const err = await t.throwsAsync(async () => await workspace._getResolvedModules()); - t.true(err.message.startsWith(`Failed to read configuration for module library.a at `+ - `'${collectionPath}/library.a/ui5-custom.yaml'. ` + - `Error: ENOENT: no such file or directory, `+ - `open '${collectionPath}/library.a/ui5-custom.yaml'`), - "Starts with expected error message"); + t.is(err.message, + `Multiple resolutions for the same library 'library.a' ` + + `in workspace '${workspace.getName()}' exist.`); } ); From 70f87d9ab844619396249dbf9e176f51d5a642c3 Mon Sep 17 00:00:00 2001 From: Max Reichmann Date: Wed, 21 Oct 2026 14:59:31 +0200 Subject: [PATCH 19/25] refactor: Add error handling for duplicate assignments Implements the previously created concept markdown for "specificity wins + Error on duplicate" --- packages/project/lib/graph/Workspace.js | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/packages/project/lib/graph/Workspace.js b/packages/project/lib/graph/Workspace.js index f02d4bf3c0f..b88a8bf27c9 100644 --- a/packages/project/lib/graph/Workspace.js +++ b/packages/project/lib/graph/Workspace.js @@ -139,6 +139,21 @@ class Workspace { }; } + // Detect duplicate resolutions targeting the same library + const seenLibraryNames = []; + for (const {path: resolutionPath} of resolutions) { + if (!resolutionPath) { + continue; + } + const libraryName = path.basename(resolutionPath); // "../collection/library.a" -> "library.a" + if (seenLibraryNames.includes(libraryName)) { + throw new Error( + `Multiple resolutions for the same library '${libraryName}' ` + + `in workspace '${this.getName()}' exist.`); + } + seenLibraryNames.push(libraryName); + } + let resolvedModules = await Promise.all(resolutions.map(async (resolutionConfig) => { if (!resolutionConfig.path) { throw new Error( From a61ed6d4c3fcd6acfada0dd200e6a7c5546f0088 Mon Sep 17 00:00:00 2001 From: Max Reichmann Date: Thu, 22 Oct 2026 14:39:27 +0200 Subject: [PATCH 20/25] test(project): Fix stale tests --- packages/project/test/lib/graph/Module.js | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/packages/project/test/lib/graph/Module.js b/packages/project/test/lib/graph/Module.js index a70685342bc..6460bbc2c9d 100644 --- a/packages/project/test/lib/graph/Module.js +++ b/packages/project/test/lib/graph/Module.js @@ -435,7 +435,7 @@ test("Corrupt configuration in file", async (t) => { t.true(err.message.startsWith( `Failed to parse configuration for project application.a.id at ` + - `'${path.resolve(__dirname, "../../fixtures/application.a/ui5-test-corrupt.yaml")}'`), + `'ui5-test-corrupt.yaml'`), `Should throw with parsing error.`); }); @@ -512,10 +512,8 @@ test("Incorrect config path", async (t) => { const err = await t.throwsAsync(ui5Module.getSpecifications()); t.is(err.message, - `Failed to read configuration for module application.a.id at `+ - `'${path.resolve(__dirname, "../../fixtures/application.a/ui5-does-not-exist.yaml")}'. `+ - `Error: ENOENT: no such file or directory, open `+ - `'${path.resolve(__dirname, "../../fixtures/application.a/ui5-does-not-exist.yaml")}'`, + `Failed to read configuration for module application.a.id: ` + + `Could not find configuration file in module at path 'ui5-does-not-exist.yaml'`, "Should throw with parsing error."); }); From 2497d7902acbabbdc47d94e0f35fd9ee65eaa40a Mon Sep 17 00:00:00 2001 From: Max Reichmann Date: Fri, 23 Oct 2026 10:51:06 +0200 Subject: [PATCH 21/25] refactor: Only throw double resolutions if same absolute path --- ...est-matrix-specificOneWins+ErrorOnDuplicate.md | 8 ++++++-- packages/project/lib/graph/Workspace.js | 13 +++++++------ packages/project/test/lib/graph/Workspace.js | 15 +++++++++++---- 3 files changed, 24 insertions(+), 12 deletions(-) diff --git a/configPath-test-matrix-specificOneWins+ErrorOnDuplicate.md b/configPath-test-matrix-specificOneWins+ErrorOnDuplicate.md index c9af24e7c1c..6f3f8b1f732 100644 --- a/configPath-test-matrix-specificOneWins+ErrorOnDuplicate.md +++ b/configPath-test-matrix-specificOneWins+ErrorOnDuplicate.md @@ -398,7 +398,7 @@ dependencyManagement: ⚠️ Error: `Multiple resolutions for the same library 'library.a' in workspace config '' exist.` -### ⚠️ Case 1.11.2: [workspace/1.1] Duplicate library name via different directories +### 🔎 Case 1.11.2: [workspace/1.1] Duplicate library name via different directories **WHEN** @@ -416,7 +416,11 @@ dependencyManagement: **THEN** -⚠️ Error: `Multiple resolutions for the same library 'library.a' in workspace config '' exist.` +| Resolved library | Config file used | +|------------------|------------------| +| `root.collection/library.a` | `ui5-custom.yaml` +| `another.root.collection/library.a` | `ui5-another-custom.yaml` + --- --- diff --git a/packages/project/lib/graph/Workspace.js b/packages/project/lib/graph/Workspace.js index b88a8bf27c9..d2c3f0470a5 100644 --- a/packages/project/lib/graph/Workspace.js +++ b/packages/project/lib/graph/Workspace.js @@ -140,18 +140,19 @@ class Workspace { } // Detect duplicate resolutions targeting the same library - const seenLibraryNames = []; + // (same exact absolute paths should throw): + const seenAbsolutePaths = []; for (const {path: resolutionPath} of resolutions) { if (!resolutionPath) { continue; } - const libraryName = path.basename(resolutionPath); // "../collection/library.a" -> "library.a" - if (seenLibraryNames.includes(libraryName)) { + const absolutePath = path.resolve(resolutionPath); + if (seenAbsolutePaths.includes(absolutePath)) { throw new Error( - `Multiple resolutions for the same library '${libraryName}' ` + - `in workspace '${this.getName()}' exist.`); + `Multiple resolutions for the same library '${path.basename(absolutePath)}' ` + + `(resolved to '${absolutePath}') in workspace '${this.getName()}' exist.`); } - seenLibraryNames.push(libraryName); + seenAbsolutePaths.push(absolutePath); } let resolvedModules = await Promise.all(resolutions.map(async (resolutionConfig) => { diff --git a/packages/project/test/lib/graph/Workspace.js b/packages/project/test/lib/graph/Workspace.js index 63935e09426..52b9f5841b5 100644 --- a/packages/project/test/lib/graph/Workspace.js +++ b/packages/project/test/lib/graph/Workspace.js @@ -829,6 +829,7 @@ test("[workspace/1.1] No configPath for all libs + Two custom configPaths for si const err = await t.throwsAsync(async () => await workspace._getResolvedModules()); t.is(err.message, `Multiple resolutions for the same library 'library.a' ` + + `(resolved to '${path.resolve("../../fixtures/collection.c/library.a")}') ` + `in workspace '${workspace.getName()}' exist.`); }); @@ -858,6 +859,7 @@ test("[workspace/1.1] Two custom configPaths for single lib + No configPath for const err = await t.throwsAsync(async () => await workspace._getResolvedModules()); t.is(err.message, `Multiple resolutions for the same library 'library.a' ` + + `(resolved to '${path.resolve("../../fixtures/collection.c/library.a")}') ` + `in workspace '${workspace.getName()}' exist.`); } ); @@ -1019,6 +1021,7 @@ test("[workspace/1.1] Two custom configPaths for the same single lib -> error on const err = await t.throwsAsync(async () => await workspace._getResolvedModules()); t.is(err.message, `Multiple resolutions for the same library 'library.a' ` + + `(resolved to '${path.resolve("../../fixtures/collection.c/library.a")}') ` + `in workspace '${workspace.getName()}' exist.`); }); @@ -1043,6 +1046,7 @@ test("[workspace/1.1] Duplicate library via different path strings (no configPat const err = await t.throwsAsync(async () => await workspace._getResolvedModules()); t.is(err.message, `Multiple resolutions for the same library 'library.a' ` + + `(resolved to '${path.resolve("../../fixtures/collection.c/library.a")}') ` + `in workspace '${workspace.getName()}' exist.`); }); @@ -1069,6 +1073,7 @@ test("[workspace/1.1] Duplicate library via different path strings (with configP const err = await t.throwsAsync(async () => await workspace._getResolvedModules()); t.is(err.message, `Multiple resolutions for the same library 'library.a' ` + + `(resolved to '${path.resolve("../../fixtures/collection.c/library.a")}') ` + `in workspace '${workspace.getName()}' exist.`); }); @@ -1091,10 +1096,10 @@ test("[workspace/1.1] Duplicate library name via different directories", async ( }) }); - const err = await t.throwsAsync(async () => await workspace._getResolvedModules()); - t.is(err.message, - `Multiple resolutions for the same library 'library.a' ` + - `in workspace '${workspace.getName()}' exist.`); + const {projectNameMap} = await workspace._getResolvedModules(); + t.is(projectNameMap.get("library.a.custom")._configPath, + path.resolve(__dirname, "../../fixtures/collection.c/library.a/ui5-custom.yaml")); + t.is(projectNameMap.get("library.a")._configPath, "ui5.yaml"); }); // DEBUG: Case 2.2 @@ -1242,6 +1247,7 @@ test("[workspace/1.1] Duplicate resolutions for single lib (with missing config) const err = await t.throwsAsync(async () => await workspace._getResolvedModules()); t.is(err.message, `Multiple resolutions for the same library 'library.a' ` + + `(resolved to '${path.resolve("../../fixtures/collection.c.noCustomConfigForLibA/library.a")}') ` + `in workspace '${workspace.getName()}' exist.`); } ); @@ -1274,6 +1280,7 @@ test("[workspace/1.1] Duplicate resolutions for single lib (missing config last) const err = await t.throwsAsync(async () => await workspace._getResolvedModules()); t.is(err.message, `Multiple resolutions for the same library 'library.a' ` + + `(resolved to '${path.resolve("../../fixtures/collection.c.noCustomConfigForLibA/library.a")}') ` + `in workspace '${workspace.getName()}' exist.`); } ); From 592a917c5cb2dda9ed8dccdb53518f13e4c42c9f Mon Sep 17 00:00:00 2001 From: Max Reichmann Date: Fri, 23 Oct 2026 16:09:09 +0200 Subject: [PATCH 22/25] test(project): Fix windows paths --- packages/project/test/lib/graph/Workspace.js | 37 +++++++++++--------- 1 file changed, 20 insertions(+), 17 deletions(-) diff --git a/packages/project/test/lib/graph/Workspace.js b/packages/project/test/lib/graph/Workspace.js index 52b9f5841b5..46c5ec13bdd 100644 --- a/packages/project/test/lib/graph/Workspace.js +++ b/packages/project/test/lib/graph/Workspace.js @@ -732,11 +732,11 @@ test("[workspace/1.1] Custom configPath for all libs + No configPath for single const {projectNameMap} = await workspace._getResolvedModules(); t.is(projectNameMap.get("library.a")._configPath, "ui5.yaml"); t.is(projectNameMap.get("library.b.custom")._configPath, - collectionPath + "/library.b/ui5-custom.yaml"); + path.join(collectionPath, "library.b", "ui5-custom.yaml")); t.is(projectNameMap.get("library.c.custom")._configPath, - collectionPath + "/collection.a/library.c/ui5-custom.yaml"); + path.join(collectionPath, "collection.a", "library.c", "ui5-custom.yaml")); t.is(projectNameMap.get("library.d.custom")._configPath, - collectionPath + "/collection.a/library.d/ui5-custom.yaml"); + path.join(collectionPath, "collection.a", "library.d", "ui5-custom.yaml")); }); // DEBUG: Case 1.5.1 @@ -762,11 +762,11 @@ test("[workspace/1.1] No configPath for single lib + Custom configPath for all l const {projectNameMap} = await workspace._getResolvedModules(); t.is(projectNameMap.get("library.a")._configPath, "ui5.yaml"); t.is(projectNameMap.get("library.b.custom")._configPath, - collectionPath + "/library.b/ui5-custom.yaml"); + path.join(collectionPath, "library.b", "ui5-custom.yaml")); t.is(projectNameMap.get("library.c.custom")._configPath, - collectionPath + "/collection.a/library.c/ui5-custom.yaml"); + path.join(collectionPath, "collection.a", "library.c", "ui5-custom.yaml")); t.is(projectNameMap.get("library.d.custom")._configPath, - collectionPath + "/collection.a/library.d/ui5-custom.yaml"); + path.join(collectionPath, "collection.a", "library.d", "ui5-custom.yaml")); }); // DEBUG: Case 1.6 @@ -795,9 +795,9 @@ test("No configPath for all libs + Custom configPaths for two libs", async (t) = const {projectNameMap} = await workspace._getResolvedModules(); t.is(projectNameMap.get("library.a.custom")._configPath, - collectionPath + "/library.a/ui5-custom.yaml"); + path.join(collectionPath, "library.a", "ui5-custom.yaml")); t.is(projectNameMap.get("library.b.another.custom")._configPath, - collectionPath + "/library.b/ui5-another-custom.yaml"); + path.join(collectionPath, "library.b", "ui5-another-custom.yaml")); t.is(projectNameMap.get("library.c")._configPath, "ui5.yaml"); t.is(projectNameMap.get("library.d")._configPath, @@ -1118,11 +1118,12 @@ test("[workspace/1.1] Custom configPath for all libs -> File not existing for si }); const collectionPath = path.resolve(__dirname, "../../fixtures/collection.c.noCustomConfigForLibA"); + const configPath = path.join(collectionPath, "library.a", "ui5-custom.yaml"); const err = await t.throwsAsync(async () => await workspace._getResolvedModules()); t.true(err.message.startsWith(`Failed to read configuration for module library.a at `+ - `'${collectionPath}/library.a/ui5-custom.yaml'. ` + + `'${configPath}'. ` + `Error: ENOENT: no such file or directory, `+ - `open '${collectionPath}/library.a/ui5-custom.yaml'`), + `open '${configPath}'`), "Starts with expected error message"); }); @@ -1146,11 +1147,12 @@ test("[workspace/1.1] Custom configPath for single lib -> File not existing for }); const collectionPath = path.resolve(__dirname, "../../fixtures/collection.c.noCustomConfigForLibA"); + const configPath = path.join(collectionPath, "library.a", "ui5-custom.yaml"); const err = await t.throwsAsync(async () => await workspace._getResolvedModules()); t.true(err.message.startsWith(`Failed to read configuration for module library.a at `+ - `'${collectionPath}/library.a/ui5-custom.yaml'. ` + + `'${configPath}'. ` + `Error: ENOENT: no such file or directory, `+ - `open '${collectionPath}/library.a/ui5-custom.yaml'`), + `open '${configPath}'`), "Starts with expected error message"); }); @@ -1176,13 +1178,14 @@ test("[workspace/1.1] Not existing configPath for single lib overwritten by No c }); const collectionPath = path.resolve(__dirname, "../../fixtures/collection.c.noCustomConfigForLibA"); + const configPath = path.join(collectionPath, "library.a", "ui5-custom.yaml"); // "Specific one wins": the specific library.a + ui5-custom.yaml resolution beats the // broader collection resolution regardless of order, so the missing file throws. const err = await t.throwsAsync(async () => await workspace._getResolvedModules()); t.true(err.message.startsWith(`Failed to read configuration for module library.a at `+ - `'${collectionPath}/library.a/ui5-custom.yaml'. ` + + `'${configPath}'. ` + `Error: ENOENT: no such file or directory, `+ - `open '${collectionPath}/library.a/ui5-custom.yaml'`), + `open '${configPath}'`), "Starts with expected error message"); } ); @@ -1211,11 +1214,11 @@ test("[workspace/1.1] Not existing configPath for single lib overwritten by No c const {projectNameMap} = await workspace._getResolvedModules(); t.is(projectNameMap.get("library.a")._configPath, "ui5.yaml"); t.is(projectNameMap.get("library.b.custom")._configPath, - collectionPath + "/library.b/ui5-custom.yaml"); + path.join(collectionPath, "library.b", "ui5-custom.yaml")); t.is(projectNameMap.get("library.c.custom")._configPath, - collectionPath + "/collection.a/library.c/ui5-custom.yaml"); + path.join(collectionPath, "collection.a", "library.c", "ui5-custom.yaml")); t.is(projectNameMap.get("library.d.custom")._configPath, - collectionPath + "/collection.a/library.d/ui5-custom.yaml"); + path.join(collectionPath, "collection.a", "library.d", "ui5-custom.yaml")); } ); From 2957791371daa390485a35a7a2dde22c130da6a1 Mon Sep 17 00:00:00 2001 From: Max Reichmann Date: Tue, 27 Oct 2026 14:09:21 +0100 Subject: [PATCH 23/25] test(project): Cleanup --- configPath-test-matrix-lastOneWins.md | 445 ------------- ...matrix-specificOneWins+ErrorOnDuplicate.md | 600 ------------------ configPath-test-matrix-specificOneWins.md | 563 ---------------- packages/project/test/lib/graph/Workspace.js | 40 +- 4 files changed, 3 insertions(+), 1645 deletions(-) delete mode 100644 configPath-test-matrix-lastOneWins.md delete mode 100644 configPath-test-matrix-specificOneWins+ErrorOnDuplicate.md delete mode 100644 configPath-test-matrix-specificOneWins.md diff --git a/configPath-test-matrix-lastOneWins.md b/configPath-test-matrix-lastOneWins.md deleted file mode 100644 index 07504004bf1..00000000000 --- a/configPath-test-matrix-lastOneWins.md +++ /dev/null @@ -1,445 +0,0 @@ -TODO: Delete this file before merging - -# `configPath` Test Matrix - -## Scenario 1 - -### GIVEN for all cases: - -``` -root.collection/ -├── library.a/ -│ ├── ui5.yaml -│ ├── ui5-custom.yaml -│ └── ui5-another-custom.yaml -├── library.b/ -│ ├── ui5.yaml -│ ├── ui5-custom.yaml -│ └── ui5-another-custom.yaml -└── collection.a/ - ├── library.c/ - │ ├── ui5.yaml - │ ├── ui5-custom.yaml - │ └── ui5-another-custom.yaml - └── library.d/ - ├── ui5.yaml - ├── ui5-custom.yaml - └── ui5-another-custom.yaml -``` - ---- - -### ✅ Case 1.1: [workspace/1.1] No configPath - -**WHEN** - -```yaml -specVersion: "workspace/1.1" -metadata: - name: default -dependencyManagement: - resolutions: - - path: ../root.collection -``` - -**THEN** - -| Resolved library | Config file used | -|------------------|------------------| -| `library.a` | `ui5.yaml` -| `library.b` | `ui5.yaml` -| `collection.a/library.c` | `ui5.yaml` -| `collection.a/library.d` | `ui5.yaml` - - -### ✅ Case 1.2: [workspace/1.1] Custom configPath for all libs - -**WHEN** - -```yaml -specVersion: "workspace/1.1" -metadata: - name: default -dependencyManagement: - resolutions: - - path: ../root.collection - configPath: ui5-custom.yaml -``` - -**THEN** - -| Resolved library | Config file used | -|------------------|------------------| -| `library.a` | `ui5-custom.yaml` -| `library.b` | `ui5-custom.yaml` -| `collection.a/library.c` | `ui5-custom.yaml` -| `collection.a/library.d` | `ui5-custom.yaml` - - -### ✅ Case 1.3: [workspace/1.1] No configPath + Custom configPath for single lib - -**WHEN** - -```yaml -specVersion: "workspace/1.1" -metadata: - name: default -dependencyManagement: - resolutions: - - path: ../root.collection - - path: ../root.collection/library.a - configPath: ui5-custom.yaml -``` - -**THEN** - -| Resolved library | Config file used | -|------------------|------------------| -| `library.a` | `ui5-custom.yaml` -| `library.b` | `ui5.yaml` -| `collection.a/library.c` | `ui5.yaml` -| `collection.a/library.d` | `ui5.yaml` - -### ✅ Case 1.4: [workspace/1.1] Custom configPath for single lib + No configPath for all libs - -**WHEN** - -```yaml -specVersion: "workspace/1.1" -metadata: - name: default -dependencyManagement: - resolutions: - - path: ../root.collection/library.a - configPath: ui5-custom.yaml - - path: ../root.collection # overwrites the prev. config entirely -``` - -**THEN** - -| Resolved library | Config file used | -|------------------|------------------| -| `library.a` | `ui5.yaml` -| `library.b` | `ui5.yaml` -| `collection.a/library.c` | `ui5.yaml` -| `collection.a/library.d` | `ui5.yaml` - -### ✅ Case 1.5: [workspace/1.1] Custom configPath for all libs + No configPath for single lib - -**WHEN** - -```yaml -specVersion: "workspace/1.1" -metadata: - name: default -dependencyManagement: - resolutions: - - path: ../root.collection - configPath: ui5-custom.yaml - - path: ../root.collection/library.a -``` - -**THEN** - -| Resolved library | Config file used | -|------------------|------------------| -| `library.a` | `ui5.yaml` -| `library.b` | `ui5-custom.yaml` -| `collection.a/library.c` | `ui5-custom.yaml` -| `collection.a/library.d` | `ui5-custom.yaml` - -### ✅ Case 1.6: [workspace/1.1] No configPath for all libs + Custom configPaths for two libs - -**WHEN** - -```yaml -specVersion: "workspace/1.1" -metadata: - name: default -dependencyManagement: - resolutions: - - path: ../root.collection - - path: ../root.collection/library.a - configPath: ui5-custom.yaml - - path: ../root.collection/library.b - configPath: ui5-another-custom.yaml -``` - -**THEN** - -| Resolved library | Config file used | -|------------------|------------------| -| `library.a` | `ui5-custom.yaml` -| `library.b` | `ui5-another-custom.yaml` -| `collection.a/library.c` | `ui5.yaml` -| `collection.a/library.d` | `ui5.yaml` - -### ✅ Case 1.7: [workspace/1.1] No configPath for all libs + Two custom configPaths for single lib - -**WHEN** - -```yaml -specVersion: "workspace/1.1" -metadata: - name: default -dependencyManagement: - resolutions: - - path: ../root.collection - - path: ../root.collection/library.a - configPath: ui5-custom.yaml - - path: ../root.collection/library.a - configPath: ui5-another-custom.yaml -``` - -**THEN** - -| Resolved library | Config file used | -|------------------|------------------| -| `library.a` | `ui5-another-custom.yaml` -| `library.b` | `ui5.yaml` -| `collection.a/library.c` | `ui5.yaml` -| `collection.a/library.d` | `ui5.yaml` - -**Remark:** For the validation, maybe log a warning (because multiple resolutions for the same library exist). - -### ✅ Case 1.8: [workspace/1.1] No configPath for all libs + Custom configPath for sub collection - -**WHEN** - -```yaml -specVersion: "workspace/1.1" -metadata: - name: default -dependencyManagement: - resolutions: - - path: ../root.collection - - path: ../root.collection/collection.a - configPath: ui5-custom.yaml -``` - -**THEN** - -| Resolved library | Config file used | -|------------------|------------------| -| `library.a` | `ui5.yaml` -| `library.b` | `ui5.yaml` -| `collection.a/library.c` | `ui5-custom.yaml` -| `collection.a/library.d` | `ui5-custom.yaml` - -### ✅ Case 1.9: [workspace/1.1] No configPath for all libs + Custom configPaths for sub collection & single lib - -**WHEN** - -```yaml -specVersion: "workspace/1.1" -metadata: - name: default -dependencyManagement: - resolutions: - - path: ../root.collection - - path: ../root.collection/collection.a - configPath: ui5-custom.yaml - - path: ../root.collection/collection.a/library.c - configPath: ui5-another-custom.yaml -``` - -**THEN** - -| Resolved library | Config file used | -|------------------|------------------| -| `library.a` | `ui5.yaml` -| `library.b` | `ui5.yaml` -| `collection.a/library.c` | `ui5-another-custom.yaml` -| `collection.a/library.d` | `ui5-custom.yaml` - -### ✅ Case 1.10: [workspace/1.1] Two custom configPaths for the same single lib (ordering isolation) - -**WHEN** - -```yaml -specVersion: "workspace/1.1" -metadata: - name: default -dependencyManagement: - resolutions: - - path: ../root.collection/library.a - configPath: ui5-custom.yaml - - path: ../root.collection/library.a # same lib, later resolution - configPath: ui5-another-custom.yaml -``` - -**THEN** - -| Resolved library | Config file used | -|------------------|------------------| -| `library.a` | `ui5-another-custom.yaml` - -Only `library.a` is resolved (the two resolutions collapse into a single module). -This case isolates the concept **the last resolution wins** from the original logic("specificity wins"). - ---- ---- - -## Scenario 2 - -### GIVEN for all cases: - -``` -root.collection/ -├── library.a/ -│ ├── ui5.yaml -│ │ # missing ui5-custom.yaml -│ └── ui5-another-custom.yaml -├── library.b/ -│ ├── ui5.yaml -│ ├── ui5-custom.yaml -│ └── ui5-another-custom.yaml -└── collection.a/ - ├── library.c/ - │ ├── ui5.yaml - │ ├── ui5-custom.yaml - │ └── ui5-another-custom.yaml - └── library.d/ - ├── ui5.yaml - ├── ui5-custom.yaml - └── ui5-another-custom.yaml -``` - ---- - -### Case 2.1: [workspace/1.1] No configPath -- **⚠️ NO AUTOMATED TEST** - -**WHEN** - -```yaml -specVersion: "workspace/1.1" -metadata: - name: default -dependencyManagement: - resolutions: - - path: ../root.collection -``` - -**THEN** - -| Resolved library | Config file used | -|------------------|------------------| -| `library.a` | `ui5.yaml` -| `library.b` | `ui5.yaml` -| `collection.a/library.c` | `ui5.yaml` -| `collection.a/library.d` | `ui5.yaml` - - -### ✅ Case 2.2: [workspace/1.1] Custom configPath for all libs -> File not existing for single lib - -**WHEN** - -```yaml -specVersion: "workspace/1.1" -metadata: - name: default -dependencyManagement: - resolutions: - - path: ../root.collection - configPath: ui5-custom.yaml -``` - -**THEN** - -Error: `Cannot find config file 'ui5-custom.yaml' for path '../root.collection/library.a'` - - -### ✅ Case 2.3: [workspace/1.1] Custom configPath for single lib -> File not existing for single lib - -**WHEN** - -```yaml -specVersion: "workspace/1.1" -metadata: - name: default -dependencyManagement: - resolutions: - - path: ../root.collection - - path: ../root.collection/library.a - configPath: ui5-custom.yaml -``` - -**THEN** - -Error: `Cannot find config file 'ui5-custom.yaml' for path '../root.collection/library.a'` - -### ✅ Case 2.4: [workspace/1.1] Not existing configPath for single lib overwritten by No configPath for all libs - -**WHEN** - -```yaml -specVersion: "workspace/1.1" -metadata: - name: default -dependencyManagement: - resolutions: - - path: ../root.collection/library.a - configPath: ui5-custom.yaml - - path: ../root.collection # overwrites the prev. config entirely -``` - -**THEN** - -| Resolved library | Config file used | -|------------------|------------------| -| `library.a` | `ui5.yaml` -| `library.b` | `ui5.yaml` -| `collection.a/library.c` | `ui5.yaml` -| `collection.a/library.d` | `ui5.yaml` - -### ✅ Case 2.5: [workspace/1.1] Not existing configPath for single lib overwritten by No configPath for the same lib - -**WHEN** - -```yaml -specVersion: "workspace/1.1" -metadata: - name: default -dependencyManagement: - resolutions: - - path: ../root.collection - configPath: ui5-custom.yaml - - path: ../root.collection/library.a -``` - -**THEN** - -| Resolved library | Config file used | -|------------------|------------------| -| `library.a` | `ui5.yaml` -| `library.b` | `ui5-custom.yaml` -| `collection.a/library.c` | `ui5-custom.yaml` -| `collection.a/library.d` | `ui5-custom.yaml` - -### ✅ Case 2.6: [workspace/1.1] Not existing configPath for single lib overwritten by Custom configPath for the same lib - -**WHEN** - -```yaml -specVersion: "workspace/1.1" -metadata: - name: default -dependencyManagement: - resolutions: - - path: ../root.collection - - path: ../root.collection/library.a - configPath: ui5-custom.yaml - - path: ../root.collection/library.a - configPath: ui5-another-custom.yaml -``` - -**THEN** - -| Resolved library | Config file used | -|------------------|------------------| -| `library.a` | `ui5-another-custom.yaml` -| `library.b` | `ui5.yaml` -| `collection.a/library.c` | `ui5.yaml` -| `collection.a/library.d` | `ui5.yaml` - -**Remark:** For the validation, maybe log a warning (because multiple resolutions for the same library exist). diff --git a/configPath-test-matrix-specificOneWins+ErrorOnDuplicate.md b/configPath-test-matrix-specificOneWins+ErrorOnDuplicate.md deleted file mode 100644 index 6f3f8b1f732..00000000000 --- a/configPath-test-matrix-specificOneWins+ErrorOnDuplicate.md +++ /dev/null @@ -1,600 +0,0 @@ -TODO: Delete this file before merging - -# `configPath` Test Matrix - -## Scenario 1 - -### GIVEN for all cases: - -``` -root.collection/ -├── library.a/ -│ ├── ui5.yaml -│ ├── ui5-custom.yaml -│ └── ui5-another-custom.yaml -├── library.b/ -│ ├── ui5.yaml -│ ├── ui5-custom.yaml -│ └── ui5-another-custom.yaml -└── collection.a/ - ├── library.c/ - │ ├── ui5.yaml - │ ├── ui5-custom.yaml - │ └── ui5-another-custom.yaml - └── library.d/ - ├── ui5.yaml - ├── ui5-custom.yaml - └── ui5-another-custom.yaml -``` - ---- - -### Case 1.1: [workspace/1.1] No configPath - -**WHEN** - -```yaml -specVersion: "workspace/1.1" -metadata: - name: default -dependencyManagement: - resolutions: - - path: ../root.collection -``` - -**THEN** - -| Resolved library | Config file used | -|------------------|------------------| -| `library.a` | `ui5.yaml` -| `library.b` | `ui5.yaml` -| `collection.a/library.c` | `ui5.yaml` -| `collection.a/library.d` | `ui5.yaml` - - -### Case 1.2: [workspace/1.1] Custom configPath for all libs - -**WHEN** - -```yaml -specVersion: "workspace/1.1" -metadata: - name: default -dependencyManagement: - resolutions: - - path: ../root.collection - configPath: ui5-custom.yaml -``` - -**THEN** - -| Resolved library | Config file used | -|------------------|------------------| -| `library.a` | `ui5-custom.yaml` -| `library.b` | `ui5-custom.yaml` -| `collection.a/library.c` | `ui5-custom.yaml` -| `collection.a/library.d` | `ui5-custom.yaml` - - -### Case 1.3: [workspace/1.1] No configPath + Custom configPath for single lib - -**WHEN** - -```yaml -specVersion: "workspace/1.1" -metadata: - name: default -dependencyManagement: - resolutions: - - path: ../root.collection - - path: ../root.collection/library.a - configPath: ui5-custom.yaml -``` - -**THEN** - -| Resolved library | Config file used | -|------------------|------------------| -| `library.a` | `ui5-custom.yaml` -| `library.b` | `ui5.yaml` -| `collection.a/library.c` | `ui5.yaml` -| `collection.a/library.d` | `ui5.yaml` - -### Case 1.4: [workspace/1.1] Custom configPath for single lib + No configPath for all libs - -**WHEN** - -```yaml -specVersion: "workspace/1.1" -metadata: - name: default -dependencyManagement: - resolutions: - - path: ../root.collection/library.a - configPath: ui5-custom.yaml - - path: ../root.collection -``` - -**THEN** - -| Resolved library | Config file used | -|------------------|------------------| -| `library.a` | `ui5-custom.yaml` -| `library.b` | `ui5.yaml` -| `collection.a/library.c` | `ui5.yaml` -| `collection.a/library.d` | `ui5.yaml` - -### Case 1.5: [workspace/1.1] Custom configPath for all libs + No configPath for single lib - -**WHEN** - -```yaml -specVersion: "workspace/1.1" -metadata: - name: default -dependencyManagement: - resolutions: - - path: ../root.collection - configPath: ui5-custom.yaml - - path: ../root.collection/library.a -``` - -**THEN** - -| Resolved library | Config file used | -|------------------|------------------| -| `library.a` | `ui5.yaml` -| `library.b` | `ui5-custom.yaml` -| `collection.a/library.c` | `ui5-custom.yaml` -| `collection.a/library.d` | `ui5-custom.yaml` - -### Case 1.5.1: [workspace/1.1] Custom configPath for all libs + No configPath for single lib (order changed) - -**WHEN** - -```yaml -specVersion: "workspace/1.1" -metadata: - name: default -dependencyManagement: - resolutions: - - path: ../root.collection/library.a - - path: ../root.collection - configPath: ui5-custom.yaml -``` - -**THEN** - -| Resolved library | Config file used | -|------------------|------------------| -| `library.a` | `ui5.yaml` -| `library.b` | `ui5-custom.yaml` -| `collection.a/library.c` | `ui5-custom.yaml` -| `collection.a/library.d` | `ui5-custom.yaml` - -### Case 1.6: [workspace/1.1] No configPath for all libs + Custom configPaths for two libs - -**WHEN** - -```yaml -specVersion: "workspace/1.1" -metadata: - name: default -dependencyManagement: - resolutions: - - path: ../root.collection - - path: ../root.collection/library.a - configPath: ui5-custom.yaml - - path: ../root.collection/library.b - configPath: ui5-another-custom.yaml -``` - -**THEN** - -| Resolved library | Config file used | -|------------------|------------------| -| `library.a` | `ui5-custom.yaml` -| `library.b` | `ui5-another-custom.yaml` -| `collection.a/library.c` | `ui5.yaml` -| `collection.a/library.d` | `ui5.yaml` - -### ⚠️ Case 1.7: [workspace/1.1] No configPath for all libs + Two custom configPaths for single lib - -**WHEN** - -```yaml -specVersion: "workspace/1.1" -metadata: - name: default -dependencyManagement: - resolutions: - - path: ../root.collection - - path: ../root.collection/library.a - configPath: ui5-custom.yaml - - path: ../root.collection/library.a - configPath: ui5-another-custom.yaml -``` - -**THEN** - -⚠️ Error: `Multiple resolutions for the same library 'library.a' in workspace config '' exist.` - -### ⚠️ Case 1.7.1: [workspace/1.1] No configPath for all libs + Two custom configPaths for single lib (order changed) - -**WHEN** - -```yaml -specVersion: "workspace/1.1" -metadata: - name: default -dependencyManagement: - resolutions: - - path: ../root.collection/library.a - configPath: ui5-custom.yaml - - path: ../root.collection - - path: ../root.collection/library.a - configPath: ui5-another-custom.yaml -``` - -**THEN** - -⚠️ Error: `Multiple resolutions for the same library 'library.a' in workspace config '' exist.` - -### Case 1.8: [workspace/1.1] No configPath for all libs + Custom configPath for sub collection - -**WHEN** - -```yaml -specVersion: "workspace/1.1" -metadata: - name: default -dependencyManagement: - resolutions: - - path: ../root.collection - - path: ../root.collection/collection.a - configPath: ui5-custom.yaml -``` - -**THEN** - -| Resolved library | Config file used | -|------------------|------------------| -| `library.a` | `ui5.yaml` -| `library.b` | `ui5.yaml` -| `collection.a/library.c` | `ui5-custom.yaml` -| `collection.a/library.d` | `ui5-custom.yaml` - -### Case 1.8.1: [workspace/1.1] Custom configPath for sub collection + No configPath for all libs - -**WHEN** - -```yaml -specVersion: "workspace/1.1" -metadata: - name: default -dependencyManagement: - resolutions: - - path: ../root.collection/collection.a - configPath: ui5-custom.yaml - - path: ../root.collection -``` - -**THEN** - -| Resolved library | Config file used | -|------------------|------------------| -| `library.a` | `ui5.yaml` -| `library.b` | `ui5.yaml` -| `collection.a/library.c` | `ui5-custom.yaml` -| `collection.a/library.d` | `ui5-custom.yaml` - -### Case 1.9: [workspace/1.1] No configPath for all libs + Custom configPaths for sub collection & single lib - -**WHEN** - -```yaml -specVersion: "workspace/1.1" -metadata: - name: default -dependencyManagement: - resolutions: - - path: ../root.collection - - path: ../root.collection/collection.a - configPath: ui5-custom.yaml - - path: ../root.collection/collection.a/library.c - configPath: ui5-another-custom.yaml -``` - -**THEN** - -| Resolved library | Config file used | -|------------------|------------------| -| `library.a` | `ui5.yaml` -| `library.b` | `ui5.yaml` -| `collection.a/library.c` | `ui5-another-custom.yaml` -| `collection.a/library.d` | `ui5-custom.yaml` - -### Case 1.9.1: [workspace/1.1] No configPath for all libs + Custom configPaths for sub collection & single lib (order changed) - -**WHEN** - -```yaml -specVersion: "workspace/1.1" -metadata: - name: default -dependencyManagement: - resolutions: - - path: ../root.collection/collection.a/library.c - configPath: ui5-another-custom.yaml - - path: ../root.collection - - path: ../root.collection/collection.a - configPath: ui5-custom.yaml -``` - -**THEN** - -| Resolved library | Config file used | -|------------------|------------------| -| `library.a` | `ui5.yaml` -| `library.b` | `ui5.yaml` -| `collection.a/library.c` | `ui5-another-custom.yaml` -| `collection.a/library.d` | `ui5-custom.yaml` - -### ⚠️ Case 1.10: [workspace/1.1] Two custom configPaths for the same single lib (ordering isolation) - -**WHEN** - -```yaml -specVersion: "workspace/1.1" -metadata: - name: default -dependencyManagement: - resolutions: - - path: ../root.collection/library.a - configPath: ui5-custom.yaml - - path: ../root.collection/library.a # same lib, later resolution - configPath: ui5-another-custom.yaml -``` - -**THEN** - -⚠️ Error: `Multiple resolutions for the same library 'library.a' in workspace config '' exist.` - -### ⚠️ Case 1.11: [workspace/1.1] Duplicate library via different path strings (no configPath) - -**WHEN** - -```yaml -specVersion: "workspace/1.1" -metadata: - name: default -dependencyManagement: - resolutions: - - path: ../root.collection/library.a - - path: ../root.collection/../root.collection/library.a -``` - -**THEN** - -⚠️ Error: `Multiple resolutions for the same library 'library.a' in workspace config '' exist.` - -### ⚠️ Case 1.11.1: [workspace/1.1] Duplicate library via different path strings (with configPath) - -**WHEN** - -```yaml -specVersion: "workspace/1.1" -metadata: - name: default -dependencyManagement: - resolutions: - - path: ../root.collection/library.a - configPath: ui5-custom.yaml - - path: ../root.collection/../root.collection/library.a - configPath: ui5-another-custom.yaml -``` - -**THEN** - -⚠️ Error: `Multiple resolutions for the same library 'library.a' in workspace config '' exist.` - -### 🔎 Case 1.11.2: [workspace/1.1] Duplicate library name via different directories - -**WHEN** - -```yaml -specVersion: "workspace/1.1" -metadata: - name: default -dependencyManagement: - resolutions: - - path: ../root.collection/library.a - configPath: ui5-custom.yaml - - path: ../another.root.collection/library.a - configPath: ui5-another-custom.yaml -``` - -**THEN** - -| Resolved library | Config file used | -|------------------|------------------| -| `root.collection/library.a` | `ui5-custom.yaml` -| `another.root.collection/library.a` | `ui5-another-custom.yaml` - - ---- ---- - -## Scenario 2 - -### GIVEN for all cases: - -``` -root.collection/ -├── library.a/ -│ ├── ui5.yaml -│ │ # missing ui5-custom.yaml -│ └── ui5-another-custom.yaml -├── library.b/ -│ ├── ui5.yaml -│ ├── ui5-custom.yaml -│ └── ui5-another-custom.yaml -└── collection.a/ - ├── library.c/ - │ ├── ui5.yaml - │ ├── ui5-custom.yaml - │ └── ui5-another-custom.yaml - └── library.d/ - ├── ui5.yaml - ├── ui5-custom.yaml - └── ui5-another-custom.yaml -``` - ---- - -### Case 2.1: [workspace/1.1] No configPath -- **⚠️ NO AUTOMATED TEST** - -**WHEN** - -```yaml -specVersion: "workspace/1.1" -metadata: - name: default -dependencyManagement: - resolutions: - - path: ../root.collection -``` - -**THEN** - -| Resolved library | Config file used | -|------------------|------------------| -| `library.a` | `ui5.yaml` -| `library.b` | `ui5.yaml` -| `collection.a/library.c` | `ui5.yaml` -| `collection.a/library.d` | `ui5.yaml` - - -### Case 2.2: [workspace/1.1] Custom configPath for all libs -> File not existing for single lib - -**WHEN** - -```yaml -specVersion: "workspace/1.1" -metadata: - name: default -dependencyManagement: - resolutions: - - path: ../root.collection - configPath: ui5-custom.yaml -``` - -**THEN** - -Error: `Cannot find config file 'ui5-custom.yaml' for path '../root.collection/library.a'` - - -### Case 2.3: [workspace/1.1] Custom configPath for single lib -> File not existing for single lib - -**WHEN** - -```yaml -specVersion: "workspace/1.1" -metadata: - name: default -dependencyManagement: - resolutions: - - path: ../root.collection - - path: ../root.collection/library.a - configPath: ui5-custom.yaml -``` - -**THEN** - -Error: `Cannot find config file 'ui5-custom.yaml' for path '../root.collection/library.a'` - -### Case 2.4: [workspace/1.1] Not existing configPath for single lib overwritten by No configPath for all libs - -**WHEN** - -```yaml -specVersion: "workspace/1.1" -metadata: - name: default -dependencyManagement: - resolutions: - - path: ../root.collection/library.a - configPath: ui5-custom.yaml - - path: ../root.collection -``` - -**THEN** - -Error: `Cannot find config file 'ui5-custom.yaml' for path '../root.collection/library.a'` - -### Case 2.5: [workspace/1.1] Not existing configPath for single lib overwritten by No configPath for the same lib - -**WHEN** - -```yaml -specVersion: "workspace/1.1" -metadata: - name: default -dependencyManagement: - resolutions: - - path: ../root.collection - configPath: ui5-custom.yaml - - path: ../root.collection/library.a -``` - -**THEN** - -| Resolved library | Config file used | -|------------------|------------------| -| `library.a` | `ui5.yaml` -| `library.b` | `ui5-custom.yaml` -| `collection.a/library.c` | `ui5-custom.yaml` -| `collection.a/library.d` | `ui5-custom.yaml` - -### ⚠️ Case 2.6: [workspace/1.1] Not existing configPath for single lib overwritten by Custom configPath for the same lib - -**WHEN** - -```yaml -specVersion: "workspace/1.1" -metadata: - name: default -dependencyManagement: - resolutions: - - path: ../root.collection - - path: ../root.collection/library.a - configPath: ui5-custom.yaml - - path: ../root.collection/library.a - configPath: ui5-another-custom.yaml -``` - -**THEN** - -⚠️ Error: `Multiple resolutions for the same library 'library.a' in workspace config '' exist.` - - -### ⚠️ Case 2.6.1: [workspace/1.1] Not existing configPath for single lib overwritten by Custom configPath for the same lib (order changed) - -**WHEN** - -```yaml -specVersion: "workspace/1.1" -metadata: - name: default -dependencyManagement: - resolutions: - - path: ../root.collection - - path: ../root.collection/library.a - configPath: ui5-another-custom.yaml - - path: ../root.collection/library.a - configPath: ui5-custom.yaml -``` - -**THEN** - -⚠️ Error: `Multiple resolutions for the same library 'library.a' in workspace config '' exist.` diff --git a/configPath-test-matrix-specificOneWins.md b/configPath-test-matrix-specificOneWins.md deleted file mode 100644 index 584ccaf70bf..00000000000 --- a/configPath-test-matrix-specificOneWins.md +++ /dev/null @@ -1,563 +0,0 @@ -TODO: Delete this file before merging - -# `configPath` Test Matrix - -## Scenario 1 - -### GIVEN for all cases: - -``` -root.collection/ -├── library.a/ -│ ├── ui5.yaml -│ ├── ui5-custom.yaml -│ └── ui5-another-custom.yaml -├── library.b/ -│ ├── ui5.yaml -│ ├── ui5-custom.yaml -│ └── ui5-another-custom.yaml -└── collection.a/ - ├── library.c/ - │ ├── ui5.yaml - │ ├── ui5-custom.yaml - │ └── ui5-another-custom.yaml - └── library.d/ - ├── ui5.yaml - ├── ui5-custom.yaml - └── ui5-another-custom.yaml -``` - ---- - -### Case 1.1: [workspace/1.1] No configPath - -**WHEN** - -```yaml -specVersion: "workspace/1.1" -metadata: - name: default -dependencyManagement: - resolutions: - - path: ../root.collection -``` - -**THEN** - -| Resolved library | Config file used | -|------------------|------------------| -| `library.a` | `ui5.yaml` -| `library.b` | `ui5.yaml` -| `collection.a/library.c` | `ui5.yaml` -| `collection.a/library.d` | `ui5.yaml` - - -### Case 1.2: [workspace/1.1] Custom configPath for all libs - -**WHEN** - -```yaml -specVersion: "workspace/1.1" -metadata: - name: default -dependencyManagement: - resolutions: - - path: ../root.collection - configPath: ui5-custom.yaml -``` - -**THEN** - -| Resolved library | Config file used | -|------------------|------------------| -| `library.a` | `ui5-custom.yaml` -| `library.b` | `ui5-custom.yaml` -| `collection.a/library.c` | `ui5-custom.yaml` -| `collection.a/library.d` | `ui5-custom.yaml` - - -### Case 1.3: [workspace/1.1] No configPath + Custom configPath for single lib - -**WHEN** - -```yaml -specVersion: "workspace/1.1" -metadata: - name: default -dependencyManagement: - resolutions: - - path: ../root.collection - - path: ../root.collection/library.a - configPath: ui5-custom.yaml -``` - -**THEN** - -| Resolved library | Config file used | -|------------------|------------------| -| `library.a` | `ui5-custom.yaml` -| `library.b` | `ui5.yaml` -| `collection.a/library.c` | `ui5.yaml` -| `collection.a/library.d` | `ui5.yaml` - -### ⚠️ Case 1.4: [workspace/1.1] Custom configPath for single lib + No configPath for all libs - -**WHEN** - -```yaml -specVersion: "workspace/1.1" -metadata: - name: default -dependencyManagement: - resolutions: - - path: ../root.collection/library.a - configPath: ui5-custom.yaml - - path: ../root.collection -``` - -**THEN** - -| Resolved library | Config file used | -|------------------|------------------| -| `library.a` | ⚠️ `ui5-custom.yaml` -| `library.b` | `ui5.yaml` -| `collection.a/library.c` | `ui5.yaml` -| `collection.a/library.d` | `ui5.yaml` - -### Case 1.5: [workspace/1.1] Custom configPath for all libs + No configPath for single lib - -**WHEN** - -```yaml -specVersion: "workspace/1.1" -metadata: - name: default -dependencyManagement: - resolutions: - - path: ../root.collection - configPath: ui5-custom.yaml - - path: ../root.collection/library.a -``` - -**THEN** - -| Resolved library | Config file used | -|------------------|------------------| -| `library.a` | `ui5.yaml` -| `library.b` | `ui5-custom.yaml` -| `collection.a/library.c` | `ui5-custom.yaml` -| `collection.a/library.d` | `ui5-custom.yaml` - -### Case 1.5.1: [workspace/1.1] Custom configPath for all libs + No configPath for single lib (order changed) - -**WHEN** - -```yaml -specVersion: "workspace/1.1" -metadata: - name: default -dependencyManagement: - resolutions: - - path: ../root.collection/library.a - - path: ../root.collection - configPath: ui5-custom.yaml -``` - -**THEN** - -| Resolved library | Config file used | -|------------------|------------------| -| `library.a` | `ui5.yaml` -| `library.b` | `ui5-custom.yaml` -| `collection.a/library.c` | `ui5-custom.yaml` -| `collection.a/library.d` | `ui5-custom.yaml` - -### Case 1.6: [workspace/1.1] No configPath for all libs + Custom configPaths for two libs - -**WHEN** - -```yaml -specVersion: "workspace/1.1" -metadata: - name: default -dependencyManagement: - resolutions: - - path: ../root.collection - - path: ../root.collection/library.a - configPath: ui5-custom.yaml - - path: ../root.collection/library.b - configPath: ui5-another-custom.yaml -``` - -**THEN** - -| Resolved library | Config file used | -|------------------|------------------| -| `library.a` | `ui5-custom.yaml` -| `library.b` | `ui5-another-custom.yaml` -| `collection.a/library.c` | `ui5.yaml` -| `collection.a/library.d` | `ui5.yaml` - -### Case 1.7: [workspace/1.1] No configPath for all libs + Two custom configPaths for single lib - -**WHEN** - -```yaml -specVersion: "workspace/1.1" -metadata: - name: default -dependencyManagement: - resolutions: - - path: ../root.collection - - path: ../root.collection/library.a - configPath: ui5-custom.yaml - - path: ../root.collection/library.a - configPath: ui5-another-custom.yaml -``` - -**THEN** - -| Resolved library | Config file used | -|------------------|------------------| -| `library.a` | `ui5-another-custom.yaml` -| `library.b` | `ui5.yaml` -| `collection.a/library.c` | `ui5.yaml` -| `collection.a/library.d` | `ui5.yaml` - -### Case 1.7.1: [workspace/1.1] No configPath for all libs + Two custom configPaths for single lib (order changed) - -**WHEN** - -```yaml -specVersion: "workspace/1.1" -metadata: - name: default -dependencyManagement: - resolutions: - - path: ../root.collection/library.a - configPath: ui5-custom.yaml - - path: ../root.collection - - path: ../root.collection/library.a - configPath: ui5-another-custom.yaml -``` - -**THEN** - -| Resolved library | Config file used | -|------------------|------------------| -| `library.a` | `ui5-another-custom.yaml` -| `library.b` | `ui5.yaml` -| `collection.a/library.c` | `ui5.yaml` -| `collection.a/library.d` | `ui5.yaml` - -**Remark:** For the validation, maybe log a warning (because multiple resolutions for the same library exist). - -### Case 1.8: [workspace/1.1] No configPath for all libs + Custom configPath for sub collection - -**WHEN** - -```yaml -specVersion: "workspace/1.1" -metadata: - name: default -dependencyManagement: - resolutions: - - path: ../root.collection - - path: ../root.collection/collection.a - configPath: ui5-custom.yaml -``` - -**THEN** - -| Resolved library | Config file used | -|------------------|------------------| -| `library.a` | `ui5.yaml` -| `library.b` | `ui5.yaml` -| `collection.a/library.c` | `ui5-custom.yaml` -| `collection.a/library.d` | `ui5-custom.yaml` - -### Case 1.8.1: [workspace/1.1] Custom configPath for sub collection + No configPath for all libs - -**WHEN** - -```yaml -specVersion: "workspace/1.1" -metadata: - name: default -dependencyManagement: - resolutions: - - path: ../root.collection/collection.a - configPath: ui5-custom.yaml - - path: ../root.collection -``` - -**THEN** - -| Resolved library | Config file used | -|------------------|------------------| -| `library.a` | `ui5.yaml` -| `library.b` | `ui5.yaml` -| `collection.a/library.c` | `ui5-custom.yaml` -| `collection.a/library.d` | `ui5-custom.yaml` - -### Case 1.9: [workspace/1.1] No configPath for all libs + Custom configPaths for sub collection & single lib - -**WHEN** - -```yaml -specVersion: "workspace/1.1" -metadata: - name: default -dependencyManagement: - resolutions: - - path: ../root.collection - - path: ../root.collection/collection.a - configPath: ui5-custom.yaml - - path: ../root.collection/collection.a/library.c - configPath: ui5-another-custom.yaml -``` - -**THEN** - -| Resolved library | Config file used | -|------------------|------------------| -| `library.a` | `ui5.yaml` -| `library.b` | `ui5.yaml` -| `collection.a/library.c` | `ui5-another-custom.yaml` -| `collection.a/library.d` | `ui5-custom.yaml` - -### Case 1.9.1: [workspace/1.1] No configPath for all libs + Custom configPaths for sub collection & single lib (order changed) - -**WHEN** - -```yaml -specVersion: "workspace/1.1" -metadata: - name: default -dependencyManagement: - resolutions: - - path: ../root.collection/collection.a/library.c - configPath: ui5-another-custom.yaml - - path: ../root.collection - - path: ../root.collection/collection.a - configPath: ui5-custom.yaml -``` - -**THEN** - -| Resolved library | Config file used | -|------------------|------------------| -| `library.a` | `ui5.yaml` -| `library.b` | `ui5.yaml` -| `collection.a/library.c` | `ui5-another-custom.yaml` -| `collection.a/library.d` | `ui5-custom.yaml` - -### Case 1.10: [workspace/1.1] Two custom configPaths for the same single lib (ordering isolation) - -**WHEN** - -```yaml -specVersion: "workspace/1.1" -metadata: - name: default -dependencyManagement: - resolutions: - - path: ../root.collection/library.a - configPath: ui5-custom.yaml - - path: ../root.collection/library.a # same lib, later resolution - configPath: ui5-another-custom.yaml -``` - -**THEN** - -| Resolved library | Config file used | -|------------------|------------------| -| `library.a` | `ui5-another-custom.yaml` - -Only `library.a` is resolved (the two resolutions collapse into a single module). -This case isolates the concept **the last resolution wins** from the original logic ("specificity wins"). - ---- ---- - -## Scenario 2 - -### GIVEN for all cases: - -``` -root.collection/ -├── library.a/ -│ ├── ui5.yaml -│ │ # missing ui5-custom.yaml -│ └── ui5-another-custom.yaml -├── library.b/ -│ ├── ui5.yaml -│ ├── ui5-custom.yaml -│ └── ui5-another-custom.yaml -└── collection.a/ - ├── library.c/ - │ ├── ui5.yaml - │ ├── ui5-custom.yaml - │ └── ui5-another-custom.yaml - └── library.d/ - ├── ui5.yaml - ├── ui5-custom.yaml - └── ui5-another-custom.yaml -``` - ---- - -### Case 2.1: [workspace/1.1] No configPath -- **⚠️ NO AUTOMATED TEST** - -**WHEN** - -```yaml -specVersion: "workspace/1.1" -metadata: - name: default -dependencyManagement: - resolutions: - - path: ../root.collection -``` - -**THEN** - -| Resolved library | Config file used | -|------------------|------------------| -| `library.a` | `ui5.yaml` -| `library.b` | `ui5.yaml` -| `collection.a/library.c` | `ui5.yaml` -| `collection.a/library.d` | `ui5.yaml` - - -### Case 2.2: [workspace/1.1] Custom configPath for all libs -> File not existing for single lib - -**WHEN** - -```yaml -specVersion: "workspace/1.1" -metadata: - name: default -dependencyManagement: - resolutions: - - path: ../root.collection - configPath: ui5-custom.yaml -``` - -**THEN** - -Error: `Cannot find config file 'ui5-custom.yaml' for path '../root.collection/library.a'` - - -### Case 2.3: [workspace/1.1] Custom configPath for single lib -> File not existing for single lib - -**WHEN** - -```yaml -specVersion: "workspace/1.1" -metadata: - name: default -dependencyManagement: - resolutions: - - path: ../root.collection - - path: ../root.collection/library.a - configPath: ui5-custom.yaml -``` - -**THEN** - -Error: `Cannot find config file 'ui5-custom.yaml' for path '../root.collection/library.a'` - -### ⚠️ Case 2.4: [workspace/1.1] Not existing configPath for single lib overwritten by No configPath for all libs - -**WHEN** - -```yaml -specVersion: "workspace/1.1" -metadata: - name: default -dependencyManagement: - resolutions: - - path: ../root.collection/library.a - configPath: ui5-custom.yaml - - path: ../root.collection -``` - -**THEN** - -Error: `Cannot find config file 'ui5-custom.yaml' for path '../root.collection/library.a'` - -### Case 2.5: [workspace/1.1] Not existing configPath for single lib overwritten by No configPath for the same lib - -**WHEN** - -```yaml -specVersion: "workspace/1.1" -metadata: - name: default -dependencyManagement: - resolutions: - - path: ../root.collection - configPath: ui5-custom.yaml - - path: ../root.collection/library.a -``` - -**THEN** - -| Resolved library | Config file used | -|------------------|------------------| -| `library.a` | `ui5.yaml` -| `library.b` | `ui5-custom.yaml` -| `collection.a/library.c` | `ui5-custom.yaml` -| `collection.a/library.d` | `ui5-custom.yaml` - -### Case 2.6: [workspace/1.1] Not existing configPath for single lib overwritten by Custom configPath for the same lib - -**WHEN** - -```yaml -specVersion: "workspace/1.1" -metadata: - name: default -dependencyManagement: - resolutions: - - path: ../root.collection - - path: ../root.collection/library.a - configPath: ui5-custom.yaml - - path: ../root.collection/library.a - configPath: ui5-another-custom.yaml -``` - -**THEN** - -| Resolved library | Config file used | -|------------------|------------------| -| `library.a` | `ui5-another-custom.yaml` -| `library.b` | `ui5.yaml` -| `collection.a/library.c` | `ui5.yaml` -| `collection.a/library.d` | `ui5.yaml` - -**Remark:** For the validation, maybe log a warning (because multiple resolutions for the same library exist). - -### Case 2.6.1: [workspace/1.1] Not existing configPath for single lib overwritten by Custom configPath for the same lib (order changed) - -**WHEN** - -```yaml -specVersion: "workspace/1.1" -metadata: - name: default -dependencyManagement: - resolutions: - - path: ../root.collection - - path: ../root.collection/library.a - configPath: ui5-another-custom.yaml - - path: ../root.collection/library.a - configPath: ui5-custom.yaml -``` - -**THEN** - -Error: `Cannot find config file 'ui5-custom.yaml' for path '../root.collection/library.a'` - -**Remark:** For the validation, maybe log a warning (because multiple resolutions for the same library exist). diff --git a/packages/project/test/lib/graph/Workspace.js b/packages/project/test/lib/graph/Workspace.js index 46c5ec13bdd..351726bf50b 100644 --- a/packages/project/test/lib/graph/Workspace.js +++ b/packages/project/test/lib/graph/Workspace.js @@ -595,7 +595,7 @@ test("[workspace/1.1] Custom relative configPath which is outside of the module dependencyManagement: { resolutions: [{ path: "../../fixtures/library.d", - configPath: "../library.e/ui5-library.d-config.yaml" // Out-of-root path + configPath: "../library.e/ui5-library.d-config.yaml" // Out-of-root path (is valid) }] } }) @@ -608,7 +608,6 @@ test("[workspace/1.1] Custom relative configPath which is outside of the module path.resolve(__dirname, "../../fixtures/library.e/ui5-library.d-config.yaml")); }); -// DEBUG: Case 1.1 test("[workspace/1.1] No configPath", async (t) => { const workspace = new t.context.Workspace({ cwd: __dirname, @@ -629,7 +628,6 @@ test("[workspace/1.1] No configPath", async (t) => { t.is(projectNameMap.get("library.d")._configPath, "ui5.yaml"); }); -// DEBUG: Case 1.2 test("[workspace/1.1] Custom configPath for all libs", async (t) => { const workspace = new t.context.Workspace({ cwd: __dirname, @@ -655,7 +653,6 @@ test("[workspace/1.1] Custom configPath for all libs", async (t) => { path.resolve(__dirname, `../../fixtures/collection.c/collection.a/library.d/ui5-custom.yaml`)); }); -// DEBUG: Case 1.3 test("[workspace/1.1] No configPath + Custom configPath for single lib", async (t) => { const workspace = new t.context.Workspace({ cwd: __dirname, @@ -681,7 +678,6 @@ test("[workspace/1.1] No configPath + Custom configPath for single lib", async ( t.is(projectNameMap.get("library.d")._configPath, "ui5.yaml"); }); -// DEBUG: Case 1.4 test("[workspace/1.1] Custom configPath for single lib + No configPath for all libs", async (t) => { const workspace = new t.context.Workspace({ cwd: __dirname, @@ -700,8 +696,6 @@ test("[workspace/1.1] Custom configPath for single lib + No configPath for all l }); const {projectNameMap} = await workspace._getResolvedModules(); - // "Specific one wins": the specific library.a resolution beats the broader - // collection resolution regardless of order. t.is(projectNameMap.get("library.a.custom")._configPath, path.resolve(__dirname, "../../fixtures/collection.c/library.a/ui5-custom.yaml")); t.is(projectNameMap.get("library.b")._configPath, "ui5.yaml"); @@ -709,7 +703,6 @@ test("[workspace/1.1] Custom configPath for single lib + No configPath for all l t.is(projectNameMap.get("library.d")._configPath, "ui5.yaml"); }); -// DEBUG: Case 1.5 test("[workspace/1.1] Custom configPath for all libs + No configPath for single lib", async (t) => { const workspace = new t.context.Workspace({ cwd: __dirname, @@ -739,7 +732,6 @@ test("[workspace/1.1] Custom configPath for all libs + No configPath for single path.join(collectionPath, "collection.a", "library.d", "ui5-custom.yaml")); }); -// DEBUG: Case 1.5.1 test("[workspace/1.1] No configPath for single lib + Custom configPath for all libs", async (t) => { const workspace = new t.context.Workspace({ cwd: __dirname, @@ -769,7 +761,6 @@ test("[workspace/1.1] No configPath for single lib + Custom configPath for all l path.join(collectionPath, "collection.a", "library.d", "ui5-custom.yaml")); }); -// DEBUG: Case 1.6 test("No configPath for all libs + Custom configPaths for two libs", async (t) => { const workspace = new t.context.Workspace({ cwd: __dirname, @@ -804,7 +795,6 @@ test("No configPath for all libs + Custom configPaths for two libs", async (t) = "ui5.yaml"); }); -// DEBUG: Case 1.7 test("[workspace/1.1] No configPath for all libs + Two custom configPaths for single lib", async (t) => { const workspace = new t.context.Workspace({ cwd: __dirname, @@ -833,7 +823,6 @@ test("[workspace/1.1] No configPath for all libs + Two custom configPaths for si `in workspace '${workspace.getName()}' exist.`); }); -// DEBUG: Case 1.7.1 test("[workspace/1.1] Two custom configPaths for single lib + No configPath for all libs (interleaved)", async (t) => { const workspace = new t.context.Workspace({ @@ -864,7 +853,6 @@ test("[workspace/1.1] Two custom configPaths for single lib + No configPath for } ); -// DEBUG: Case 1.8 test("[workspace/1.1] No configPath for all libs + Custom configPath for sub collection", async (t) => { const workspace = new t.context.Workspace({ cwd: __dirname, @@ -895,7 +883,6 @@ test("[workspace/1.1] No configPath for all libs + Custom configPath for sub col path.resolve(collectionPath, "collection.a/library.d/ui5-custom.yaml")); }); -// DEBUG: Case 1.8.1 test("[workspace/1.1] Custom configPath for sub collection + No configPath for all libs", async (t) => { const workspace = new t.context.Workspace({ cwd: __dirname, @@ -915,8 +902,6 @@ test("[workspace/1.1] Custom configPath for sub collection + No configPath for a const collectionPath = path.resolve(__dirname, "../../fixtures/collection.c"); - // "Specific one wins": the sub collection resolution beats the broader collection - // resolution regardless of order (mirror of Case 1.8 with reversed order). const {projectNameMap} = await workspace._getResolvedModules(); t.is(projectNameMap.get("library.a")._configPath, "ui5.yaml"); @@ -928,7 +913,6 @@ test("[workspace/1.1] Custom configPath for sub collection + No configPath for a path.resolve(collectionPath, "collection.a/library.d/ui5-custom.yaml")); }); -// DEBUG: Case 1.9 test("[workspace/1.1] No configPath for all libs + Custom configPaths for sub collection & single lib", async (t) => { const workspace = new t.context.Workspace({ cwd: __dirname, @@ -963,7 +947,6 @@ test("[workspace/1.1] No configPath for all libs + Custom configPaths for sub co path.resolve(collectionPath, "collection.a/library.d/ui5-custom.yaml")); }); -// DEBUG: Case 1.9.1 test("[workspace/1.1] Custom configPaths for sub collection & single lib + No configPath for all libs " + "(interleaved)", async (t) => { const workspace = new t.context.Workspace({ @@ -999,7 +982,6 @@ test("[workspace/1.1] Custom configPaths for sub collection & single lib + No co path.resolve(collectionPath, "collection.a/library.d/ui5-custom.yaml")); }); -// DEBUG: Case 1.10 (ordering isolation: two direct resolutions, same lib) test("[workspace/1.1] Two custom configPaths for the same single lib -> error on duplicate", async (t) => { const workspace = new t.context.Workspace({ cwd: __dirname, @@ -1025,7 +1007,6 @@ test("[workspace/1.1] Two custom configPaths for the same single lib -> error on `in workspace '${workspace.getName()}' exist.`); }); -// DEBUG: Case 1.11 test("[workspace/1.1] Duplicate library via different path strings (no configPath)", async (t) => { const workspace = new t.context.Workspace({ cwd: __dirname, @@ -1050,7 +1031,6 @@ test("[workspace/1.1] Duplicate library via different path strings (no configPat `in workspace '${workspace.getName()}' exist.`); }); -// DEBUG: Case 1.11.1 test("[workspace/1.1] Duplicate library via different path strings (with configPath)", async (t) => { const workspace = new t.context.Workspace({ cwd: __dirname, @@ -1077,7 +1057,6 @@ test("[workspace/1.1] Duplicate library via different path strings (with configP `in workspace '${workspace.getName()}' exist.`); }); -// DEBUG: Case 1.11.2 test("[workspace/1.1] Duplicate library name via different directories", async (t) => { const workspace = new t.context.Workspace({ cwd: __dirname, @@ -1090,6 +1069,7 @@ test("[workspace/1.1] Duplicate library name via different directories", async ( }, { // Different directory, but same library (directory) name 'library.a' + // --> is valid: should not conflict with the other library resolution path: "../../fixtures/collection/library.a" }] } @@ -1102,7 +1082,6 @@ test("[workspace/1.1] Duplicate library name via different directories", async ( t.is(projectNameMap.get("library.a")._configPath, "ui5.yaml"); }); -// DEBUG: Case 2.2 test("[workspace/1.1] Custom configPath for all libs -> File not existing for single lib", async (t) => { const workspace = new t.context.Workspace({ cwd: __dirname, @@ -1127,7 +1106,6 @@ test("[workspace/1.1] Custom configPath for all libs -> File not existing for si "Starts with expected error message"); }); -// DEBUG: Case 2.3 test("[workspace/1.1] Custom configPath for single lib -> File not existing for single lib", async (t) => { const workspace = new t.context.Workspace({ cwd: __dirname, @@ -1156,7 +1134,6 @@ test("[workspace/1.1] Custom configPath for single lib -> File not existing for "Starts with expected error message"); }); -// DEBUG: Case 2.4 test("[workspace/1.1] Not existing configPath for single lib overwritten by No configPath for all libs", async (t) => { const workspace = new t.context.Workspace({ @@ -1179,8 +1156,6 @@ test("[workspace/1.1] Not existing configPath for single lib overwritten by No c const collectionPath = path.resolve(__dirname, "../../fixtures/collection.c.noCustomConfigForLibA"); const configPath = path.join(collectionPath, "library.a", "ui5-custom.yaml"); - // "Specific one wins": the specific library.a + ui5-custom.yaml resolution beats the - // broader collection resolution regardless of order, so the missing file throws. const err = await t.throwsAsync(async () => await workspace._getResolvedModules()); t.true(err.message.startsWith(`Failed to read configuration for module library.a at `+ `'${configPath}'. ` + @@ -1190,7 +1165,6 @@ test("[workspace/1.1] Not existing configPath for single lib overwritten by No c } ); -// DEBUG: Case 2.5 test("[workspace/1.1] Not existing configPath for single lib overwritten by No configPath for the same lib", async (t) => { const workspace = new t.context.Workspace({ @@ -1200,7 +1174,7 @@ test("[workspace/1.1] Not existing configPath for single lib overwritten by No c dependencyManagement: { resolutions: [{ path: "../../fixtures/collection.c.noCustomConfigForLibA", - configPath: "ui5-custom.yaml" // not existing for library.a + configPath: "ui5-custom.yaml" // not existing }, { path: "../../fixtures/collection.c.noCustomConfigForLibA/library.a", @@ -1222,7 +1196,6 @@ test("[workspace/1.1] Not existing configPath for single lib overwritten by No c } ); -// DEBUG: Case 2.6 test("[workspace/1.1] Duplicate resolutions for single lib (with missing config) -> error on duplicate", async (t) => { const workspace = new t.context.Workspace({ @@ -1245,8 +1218,6 @@ test("[workspace/1.1] Duplicate resolutions for single lib (with missing config) }) }); - // The duplicate is detected before any config file is read, so the missing - // 'ui5-custom.yaml' never causes an ENOENT; the duplicate error wins. const err = await t.throwsAsync(async () => await workspace._getResolvedModules()); t.is(err.message, `Multiple resolutions for the same library 'library.a' ` + @@ -1255,7 +1226,6 @@ test("[workspace/1.1] Duplicate resolutions for single lib (with missing config) } ); -// DEBUG: Case 2.6.1 test("[workspace/1.1] Duplicate resolutions for single lib (missing config last) -> error on duplicate", async (t) => { const workspace = new t.context.Workspace({ @@ -1278,8 +1248,6 @@ test("[workspace/1.1] Duplicate resolutions for single lib (missing config last) }) }); - // The duplicate is detected before any config file is read, so the missing - // 'ui5-custom.yaml' never causes an ENOENT; the duplicate error wins. const err = await t.throwsAsync(async () => await workspace._getResolvedModules()); t.is(err.message, `Multiple resolutions for the same library 'library.a' ` + @@ -1287,5 +1255,3 @@ test("[workspace/1.1] Duplicate resolutions for single lib (missing config last) `in workspace '${workspace.getName()}' exist.`); } ); - - From 6e7dcd289185d677123bac88d97076c2a6f03693 Mon Sep 17 00:00:00 2001 From: Max Reichmann Date: Tue, 27 Oct 2026 17:40:40 +0100 Subject: [PATCH 24/25] docs: Fix path resolution --- internal/documentation/docs/pages/Workspace.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/documentation/docs/pages/Workspace.md b/internal/documentation/docs/pages/Workspace.md index 441ea33e611..bc67f932943 100644 --- a/internal/documentation/docs/pages/Workspace.md +++ b/internal/documentation/docs/pages/Workspace.md @@ -130,7 +130,7 @@ dependencyManagement: configPath: custom-config.yaml ``` -By default, UI5 CLI looks for a `ui5.yaml` at the root of the resolved module. The optional `configPath` property allows pointing to a different configuration file within the module directory. The path must be written in POSIX notation and must be **relative to the resolution `path`**. +By default, UI5 CLI looks for a `ui5.yaml` at the root of the resolved module. The optional `configPath` property allows pointing to a different configuration file within the module directory. The path must be written in POSIX notation and must be **relative to the root of the resolved module**. ## Workspace Specification Versions A workspace configuration must define a Specification Version by setting the `specVersion` property. UI5 CLI uses this information to detect whether the currently installed version is compatible with a workspace's configuration. From b17188792470860bb1de5b3b5397e4e60ce7b126 Mon Sep 17 00:00:00 2001 From: Max Reichmann Date: Tue, 27 Oct 2026 18:51:33 +0100 Subject: [PATCH 25/25] test: Fix path for error message --- packages/project/lib/graph/Workspace.js | 2 +- packages/project/test/lib/graph/Workspace.js | 16 +++++++++------- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/packages/project/lib/graph/Workspace.js b/packages/project/lib/graph/Workspace.js index d2c3f0470a5..8f143c3a442 100644 --- a/packages/project/lib/graph/Workspace.js +++ b/packages/project/lib/graph/Workspace.js @@ -146,7 +146,7 @@ class Workspace { if (!resolutionPath) { continue; } - const absolutePath = path.resolve(resolutionPath); + const absolutePath = path.resolve(this.#cwd, resolutionPath); if (seenAbsolutePaths.includes(absolutePath)) { throw new Error( `Multiple resolutions for the same library '${path.basename(absolutePath)}' ` + diff --git a/packages/project/test/lib/graph/Workspace.js b/packages/project/test/lib/graph/Workspace.js index 351726bf50b..301bc7aeec7 100644 --- a/packages/project/test/lib/graph/Workspace.js +++ b/packages/project/test/lib/graph/Workspace.js @@ -819,7 +819,7 @@ test("[workspace/1.1] No configPath for all libs + Two custom configPaths for si const err = await t.throwsAsync(async () => await workspace._getResolvedModules()); t.is(err.message, `Multiple resolutions for the same library 'library.a' ` + - `(resolved to '${path.resolve("../../fixtures/collection.c/library.a")}') ` + + `(resolved to '${path.resolve(__dirname, "../../fixtures/collection.c/library.a")}') ` + `in workspace '${workspace.getName()}' exist.`); }); @@ -848,7 +848,7 @@ test("[workspace/1.1] Two custom configPaths for single lib + No configPath for const err = await t.throwsAsync(async () => await workspace._getResolvedModules()); t.is(err.message, `Multiple resolutions for the same library 'library.a' ` + - `(resolved to '${path.resolve("../../fixtures/collection.c/library.a")}') ` + + `(resolved to '${path.resolve(__dirname, "../../fixtures/collection.c/library.a")}') ` + `in workspace '${workspace.getName()}' exist.`); } ); @@ -1003,7 +1003,7 @@ test("[workspace/1.1] Two custom configPaths for the same single lib -> error on const err = await t.throwsAsync(async () => await workspace._getResolvedModules()); t.is(err.message, `Multiple resolutions for the same library 'library.a' ` + - `(resolved to '${path.resolve("../../fixtures/collection.c/library.a")}') ` + + `(resolved to '${path.resolve(__dirname, "../../fixtures/collection.c/library.a")}') ` + `in workspace '${workspace.getName()}' exist.`); }); @@ -1027,7 +1027,7 @@ test("[workspace/1.1] Duplicate library via different path strings (no configPat const err = await t.throwsAsync(async () => await workspace._getResolvedModules()); t.is(err.message, `Multiple resolutions for the same library 'library.a' ` + - `(resolved to '${path.resolve("../../fixtures/collection.c/library.a")}') ` + + `(resolved to '${path.resolve(__dirname, "../../fixtures/collection.c/library.a")}') ` + `in workspace '${workspace.getName()}' exist.`); }); @@ -1053,7 +1053,7 @@ test("[workspace/1.1] Duplicate library via different path strings (with configP const err = await t.throwsAsync(async () => await workspace._getResolvedModules()); t.is(err.message, `Multiple resolutions for the same library 'library.a' ` + - `(resolved to '${path.resolve("../../fixtures/collection.c/library.a")}') ` + + `(resolved to '${path.resolve(__dirname, "../../fixtures/collection.c/library.a")}') ` + `in workspace '${workspace.getName()}' exist.`); }); @@ -1221,7 +1221,8 @@ test("[workspace/1.1] Duplicate resolutions for single lib (with missing config) const err = await t.throwsAsync(async () => await workspace._getResolvedModules()); t.is(err.message, `Multiple resolutions for the same library 'library.a' ` + - `(resolved to '${path.resolve("../../fixtures/collection.c.noCustomConfigForLibA/library.a")}') ` + + `(resolved to '${path.resolve(__dirname, + "../../fixtures/collection.c.noCustomConfigForLibA/library.a")}') ` + `in workspace '${workspace.getName()}' exist.`); } ); @@ -1251,7 +1252,8 @@ test("[workspace/1.1] Duplicate resolutions for single lib (missing config last) const err = await t.throwsAsync(async () => await workspace._getResolvedModules()); t.is(err.message, `Multiple resolutions for the same library 'library.a' ` + - `(resolved to '${path.resolve("../../fixtures/collection.c.noCustomConfigForLibA/library.a")}') ` + + `(resolved to '${path.resolve(__dirname, + "../../fixtures/collection.c.noCustomConfigForLibA/library.a")}') ` + `in workspace '${workspace.getName()}' exist.`); } );