From 30ed97c05261bdea760c069f14fe1f79605a0957 Mon Sep 17 00:00:00 2001 From: Max Reichmann Date: Fri, 2 Oct 2026 14:47:16 +0200 Subject: [PATCH 01/22] 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 d006aa8cece52c698ba5c0f1b4ade427ea07617f Mon Sep 17 00:00:00 2001 From: Max Reichmann Date: Thu, 8 Oct 2026 15:08:45 +0200 Subject: [PATCH 02/22] 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 d39629a1491..57c4c233f92 100644 --- a/packages/project/lib/graph/Workspace.js +++ b/packages/project/lib/graph/Workspace.js @@ -144,7 +144,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 @@ -175,7 +175,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`); @@ -235,7 +235,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)) { @@ -248,7 +249,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 628498b0f9e9d6b685a9e7e7125c9bb6ceb0b815 Mon Sep 17 00:00:00 2001 From: Max Reichmann Date: Thu, 8 Oct 2026 16:08:25 +0200 Subject: [PATCH 03/22] 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 d7c329f87864b0589e6ab60cf1f446df6b848e5e Mon Sep 17 00:00:00 2001 From: Max Reichmann Date: Fri, 9 Oct 2026 12:02:58 +0200 Subject: [PATCH 04/22] 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 5130ef2acf0..c7b9b5c5e08 100644 --- a/packages/project/lib/graph/Module.js +++ b/packages/project/lib/graph/Module.js @@ -69,7 +69,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 57c4c233f92..8add41dcc3e 100644 --- a/packages/project/lib/graph/Workspace.js +++ b/packages/project/lib/graph/Workspace.js @@ -143,6 +143,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 553b6db5a52d9fff9e5082d69b46619007f6c727 Mon Sep 17 00:00:00 2001 From: Max Reichmann Date: Fri, 9 Oct 2026 18:20:51 +0200 Subject: [PATCH 05/22] 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 232f5965991..8279a0d93b3 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 5c857ed3631c5add9dc3e299765e15fa262751e3 Mon Sep 17 00:00:00 2001 From: Max Reichmann Date: Mon, 12 Oct 2026 15:27:59 +0200 Subject: [PATCH 06/22] 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 8add41dcc3e..83990fda630 100644 --- a/packages/project/lib/graph/Workspace.js +++ b/packages/project/lib/graph/Workspace.js @@ -145,13 +145,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 0dcf01a4df5c58011ee3df1ec7ba712036952d26 Mon Sep 17 00:00:00 2001 From: Max Reichmann Date: Tue, 13 Oct 2026 10:00:30 +0200 Subject: [PATCH 07/22] 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 4ebdb7311ecd7ddc5924956d0bc11dfc0d4b7cb6 Mon Sep 17 00:00:00 2001 From: Max Reichmann Date: Tue, 13 Oct 2026 15:25:49 +0200 Subject: [PATCH 08/22] 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 a1e309bf3f2aff650fb936de58b8c315d47babdc Mon Sep 17 00:00:00 2001 From: Max Reichmann Date: Tue, 13 Oct 2026 16:59:08 +0200 Subject: [PATCH 09/22] 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 92b7388ef6b5c3cf7afa928d38aa46e1fbad0ebc 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/22] 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 64f03a2ecddd62b1a841b05dcae0472f5bba4392 Mon Sep 17 00:00:00 2001 From: Max Reichmann Date: Thu, 15 Oct 2026 16:43:18 +0200 Subject: [PATCH 11/22] 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 c7f740b038ba5beb4652d056cde7b3a671d6cd4c Mon Sep 17 00:00:00 2001 From: Max Reichmann Date: Fri, 16 Oct 2026 13:32:09 +0200 Subject: [PATCH 12/22] 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 8279a0d93b3..60cd0a80343 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 09eb87e8b07151d3006299738ab3fd59829737d9 Mon Sep 17 00:00:00 2001 From: Max Reichmann Date: Fri, 16 Oct 2026 16:01:14 +0200 Subject: [PATCH 13/22] 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 c7b9b5c5e08..5bf5223a827 100644 --- a/packages/project/lib/graph/Module.js +++ b/packages/project/lib/graph/Module.js @@ -69,7 +69,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)) { @@ -308,8 +308,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 83990fda630..e47f9556cc1 100644 --- a/packages/project/lib/graph/Workspace.js +++ b/packages/project/lib/graph/Workspace.js @@ -260,7 +260,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 bbda6716c9f162d946f067d96c60c903fd76c081 Mon Sep 17 00:00:00 2001 From: Max Reichmann Date: Fri, 16 Oct 2026 17:01:38 +0200 Subject: [PATCH 14/22] 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 0a0d68da421771453d7036022480ca4a93eb533c Mon Sep 17 00:00:00 2001 From: Max Reichmann Date: Mon, 19 Oct 2026 16:03:31 +0200 Subject: [PATCH 15/22] 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 2e937eb6a2f48319ab9a87dd528ce7008822d743 Mon Sep 17 00:00:00 2001 From: Max Reichmann Date: Mon, 19 Oct 2026 22:52:48 +0200 Subject: [PATCH 16/22] 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 2a27fc60f8e52ec8ffdeaf9e99654e8a452a3c84 Mon Sep 17 00:00:00 2001 From: Max Reichmann Date: Tue, 20 Oct 2026 15:16:06 +0200 Subject: [PATCH 17/22] 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 a549823feb3e798e2492f3a90f61cba8c04a69cf Mon Sep 17 00:00:00 2001 From: Max Reichmann Date: Wed, 21 Oct 2026 13:53:05 +0200 Subject: [PATCH 18/22] 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 55b978028db1171bd4a5236ee6f020591c83f574 Mon Sep 17 00:00:00 2001 From: Max Reichmann Date: Wed, 21 Oct 2026 14:59:31 +0200 Subject: [PATCH 19/22] 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 e47f9556cc1..cd17ca3f61c 100644 --- a/packages/project/lib/graph/Workspace.js +++ b/packages/project/lib/graph/Workspace.js @@ -138,6 +138,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 0a19ab49ff96d7617c33551da9160ded39660eaa Mon Sep 17 00:00:00 2001 From: Max Reichmann Date: Thu, 22 Oct 2026 14:39:27 +0200 Subject: [PATCH 20/22] 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 b3a3a20226c6d203b96545b2e0059f347b0b6d73 Mon Sep 17 00:00:00 2001 From: Max Reichmann Date: Fri, 23 Oct 2026 10:51:06 +0200 Subject: [PATCH 21/22] 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 cd17ca3f61c..71a830e1c34 100644 --- a/packages/project/lib/graph/Workspace.js +++ b/packages/project/lib/graph/Workspace.js @@ -139,18 +139,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 85265b2e62335059ddad2119f94f3dac40827a4c Mon Sep 17 00:00:00 2001 From: Max Reichmann Date: Fri, 23 Oct 2026 16:09:09 +0200 Subject: [PATCH 22/22] 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")); } );