From f36e21180a3fcadb09ad54fd9087eb04a8dcdbd9 Mon Sep 17 00:00:00 2001 From: d3xter666 Date: Fri, 17 Jul 2026 09:44:34 +0300 Subject: [PATCH 01/12] refactor: Consolidate UI5 data directory resolution into resolveUi5DataDir MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Introduces packages/project/lib/utils/dataDir.js with a single resolveUi5DataDir() utility that encapsulates the full resolution chain: 1. UI5_DATA_DIR environment variable 2. ui5DataDir from configuration file (~/.ui5rc) 3. Default: ~/.ui5 Removes the duplicate getUi5DataDir() function from packages/cli/lib/framework/utils.js and migrates its callers (createFrameworkResolverInstance, frameworkResolverResolveVersion) to use resolveUi5DataDir() directly. The defensive fallbacks in the public Resolver/Installer APIs (AbstractResolver, Openui5Resolver, Sapui5Resolver, Sapui5MavenSnapshotResolver) are intentionally kept — those are synchronous constructors that cannot call the async utility, and they are publicly exported so external callers may pass undefined. --- packages/cli/lib/framework/utils.js | 18 +---- packages/cli/test/lib/framework/utils.js | 83 ++++------------------ packages/project/lib/utils/dataDir.js | 28 ++++++++ packages/project/package.json | 1 + packages/project/test/lib/utils/dataDir.js | 80 +++++++++++++++++++++ 5 files changed, 124 insertions(+), 86 deletions(-) create mode 100644 packages/project/lib/utils/dataDir.js create mode 100644 packages/project/test/lib/utils/dataDir.js diff --git a/packages/cli/lib/framework/utils.js b/packages/cli/lib/framework/utils.js index 799c8a35253..0599b824e47 100644 --- a/packages/cli/lib/framework/utils.js +++ b/packages/cli/lib/framework/utils.js @@ -1,6 +1,5 @@ -import path from "node:path"; import {graphFromStaticFile, graphFromPackageDependencies} from "@ui5/project/graph"; -import Configuration from "@ui5/project/config/Configuration"; +import {resolveUi5DataDir} from "@ui5/project/utils/dataDir"; export async function getRootProjectConfiguration(projectGraphOptions) { let graph; @@ -37,7 +36,7 @@ export async function createFrameworkResolverInstance({frameworkName, frameworkV return new Resolver({ cwd, version: frameworkVersion, - ui5DataDir: await utils.getUi5DataDir({cwd}) + ui5DataDir: await resolveUi5DataDir() }); } @@ -45,26 +44,15 @@ export async function frameworkResolverResolveVersion({frameworkName, frameworkV const Resolver = await utils.getFrameworkResolver(frameworkName, frameworkVersion); return Resolver.resolveVersion(frameworkVersion, { cwd, - ui5DataDir: await utils.getUi5DataDir({cwd}) + ui5DataDir: await resolveUi5DataDir() }); } -async function getUi5DataDir({cwd}) { - // ENV var should take precedence over the dataDir from the configuration. - let ui5DataDir = process.env.UI5_DATA_DIR; - if (!ui5DataDir) { - const config = await Configuration.fromFile(); - ui5DataDir = config.getUi5DataDir(); - } - return ui5DataDir ? path.resolve(cwd, ui5DataDir) : undefined; -} - const utils = { getRootProjectConfiguration, getFrameworkResolver, createFrameworkResolverInstance, frameworkResolverResolveVersion, - getUi5DataDir }; let _utils; // For mocking of functions in unit tests and testing internal functions diff --git a/packages/cli/test/lib/framework/utils.js b/packages/cli/test/lib/framework/utils.js index 0ac6f4aaf37..1094bce26d9 100644 --- a/packages/cli/test/lib/framework/utils.js +++ b/packages/cli/test/lib/framework/utils.js @@ -1,7 +1,6 @@ import test from "ava"; import sinonGlobal from "sinon"; import esmock from "esmock"; -import path from "node:path"; test.beforeEach(async (t) => { // Tests either rely on not having UI5_DATA_DIR defined, or explicitly define it @@ -21,12 +20,7 @@ test.beforeEach(async (t) => { t.context.Openui5Resolver = sinon.stub(); t.context.Sapui5MavenSnapshotResolver = sinon.stub(); - t.context.ConfigurationGetUi5DataDirStub = sinon.stub().returns(undefined); - t.context.ConfigurationStub = { - fromFile: sinon.stub().resolves({ - getUi5DataDir: t.context.ConfigurationGetUi5DataDirStub - }) - }; + t.context.resolveUi5DataDirStub = sinon.stub().resolves(undefined); t.context.utils = await esmock.p("../../../lib/framework/utils.js", { "@ui5/project/graph": { @@ -42,7 +36,9 @@ test.beforeEach(async (t) => { "@ui5/project/ui5Framework/Sapui5MavenSnapshotResolver": { default: t.context.Sapui5MavenSnapshotResolver }, - "@ui5/project/config/Configuration": t.context.ConfigurationStub + "@ui5/project/utils/dataDir": { + resolveUi5DataDir: t.context.resolveUi5DataDirStub + } }); t.context._utils = t.context.utils._utils; }); @@ -144,11 +140,11 @@ test.serial("getFrameworkResolver: Invalid framework.name", async (t) => { test.serial("createFrameworkResolverInstance: Without ui5DataDir", async (t) => { const {createFrameworkResolverInstance} = t.context.utils; - const {sinon} = t.context; + const {sinon, resolveUi5DataDirStub} = t.context; const ResolverStub = sinon.stub().returns({}); sinon.stub(t.context._utils, "getFrameworkResolver").resolves(ResolverStub); - sinon.stub(t.context._utils, "getUi5DataDir").resolves(undefined); + resolveUi5DataDirStub.resolves(undefined); const result = await createFrameworkResolverInstance({ frameworkName: "", @@ -163,12 +159,7 @@ test.serial("createFrameworkResolverInstance: Without ui5DataDir", async (t) => "" ]); - t.is(t.context._utils.getUi5DataDir.callCount, 1); - t.deepEqual(t.context._utils.getUi5DataDir.getCall(0).args, [ - { - cwd: "my-project-path" - } - ]); + t.is(resolveUi5DataDirStub.callCount, 1); t.is(ResolverStub.callCount, 1); t.is(result, ResolverStub.getCall(0).returnValue); @@ -184,11 +175,11 @@ test.serial("createFrameworkResolverInstance: Without ui5DataDir", async (t) => test.serial("createFrameworkResolverInstance: With ui5DataDir", async (t) => { const {createFrameworkResolverInstance} = t.context.utils; - const {sinon} = t.context; + const {sinon, resolveUi5DataDirStub} = t.context; const ResolverStub = sinon.stub().returns({}); sinon.stub(t.context._utils, "getFrameworkResolver").resolves(ResolverStub); - sinon.stub(t.context._utils, "getUi5DataDir").resolves("my-ui5-data-dir"); + resolveUi5DataDirStub.resolves("my-ui5-data-dir"); const result = await createFrameworkResolverInstance({ frameworkName: "", @@ -203,12 +194,7 @@ test.serial("createFrameworkResolverInstance: With ui5DataDir", async (t) => { "" ]); - t.is(t.context._utils.getUi5DataDir.callCount, 1); - t.deepEqual(t.context._utils.getUi5DataDir.getCall(0).args, [ - { - cwd: "my-project-path" - } - ]); + t.is(resolveUi5DataDirStub.callCount, 1); t.is(ResolverStub.callCount, 1); t.is(result, ResolverStub.getCall(0).returnValue); @@ -224,13 +210,13 @@ test.serial("createFrameworkResolverInstance: With ui5DataDir", async (t) => { test.serial("frameworkResolverResolveVersion", async (t) => { const {frameworkResolverResolveVersion} = t.context.utils; - const {sinon} = t.context; + const {sinon, resolveUi5DataDirStub} = t.context; const resolveVersionStub = sinon.stub().resolves("1.111.1"); sinon.stub(t.context._utils, "getFrameworkResolver").resolves({ resolveVersion: resolveVersionStub }); - sinon.stub(t.context._utils, "getUi5DataDir").resolves(undefined); + resolveUi5DataDirStub.resolves(undefined); const result = await frameworkResolverResolveVersion({ frameworkName: "SAPUI5", @@ -250,48 +236,3 @@ test.serial("frameworkResolverResolveVersion", async (t) => { } ]); }); - -test.serial("getUi5DataDir: no value defined", async (t) => { - const {ConfigurationGetUi5DataDirStub} = t.context; - const {getUi5DataDir} = t.context._utils; - - const result = await getUi5DataDir({ - cwd: path.resolve("foo") - }); - - t.is(result, undefined); - - t.is(ConfigurationGetUi5DataDirStub.callCount, 1); -}); - -test.serial("getUi5DataDir: from environment variable", async (t) => { - const {ConfigurationGetUi5DataDirStub} = t.context; - const {getUi5DataDir} = t.context._utils; - - // Environment variable must be preferred over configuration value - ConfigurationGetUi5DataDirStub.returns(".ui5-data-dir-from-configuration"); - process.env.UI5_DATA_DIR = ".ui5-data-dir-from-env-variable"; - - const result = await getUi5DataDir({ - cwd: path.resolve("foo") - }); - - t.is(result, path.join(path.resolve("foo"), ".ui5-data-dir-from-env-variable")); - - t.is(ConfigurationGetUi5DataDirStub.callCount, 0); -}); - -test.serial("getUi5DataDir: from Configuration", async (t) => { - const {ConfigurationGetUi5DataDirStub} = t.context; - const {getUi5DataDir} = t.context._utils; - - ConfigurationGetUi5DataDirStub.returns(".ui5-data-dir-from-configuration"); - - const result = await getUi5DataDir({ - cwd: path.resolve("foo") - }); - - t.is(result, path.join(path.resolve("foo"), ".ui5-data-dir-from-configuration")); - - t.is(ConfigurationGetUi5DataDirStub.callCount, 1); -}); diff --git a/packages/project/lib/utils/dataDir.js b/packages/project/lib/utils/dataDir.js new file mode 100644 index 00000000000..484b7bfda1b --- /dev/null +++ b/packages/project/lib/utils/dataDir.js @@ -0,0 +1,28 @@ +import path from "node:path"; +import os from "node:os"; +import Configuration from "../config/Configuration.js"; + +/** + * Resolves the UI5 data directory using the standard precedence chain: + *
    + *
  1. UI5_DATA_DIR environment variable
  2. + *
  3. ui5DataDir option from the configuration file (~/.ui5rc)
  4. + *
  5. Default: ~/.ui5
  6. + *
+ * + * Relative paths are resolved against cwd. + * This function always returns an absolute path — never undefined. + * + * @returns {Promise} Resolved absolute path to the UI5 data directory + */ +export async function resolveUi5DataDir() { + let ui5DataDir = process.env.UI5_DATA_DIR; + if (!ui5DataDir) { + const config = await Configuration.fromFile(); + ui5DataDir = config.getUi5DataDir(); + } + if (ui5DataDir) { + return path.resolve(process.cwd(), ui5DataDir); + } + return path.join(os.homedir(), ".ui5"); +} diff --git a/packages/project/package.json b/packages/project/package.json index 313bc5db619..394b6a269aa 100644 --- a/packages/project/package.json +++ b/packages/project/package.json @@ -27,6 +27,7 @@ "./ui5Framework/Sapui5Resolver": "./lib/ui5Framework/Sapui5Resolver.js", "./ui5Framework/maven/SnapshotCache": "./lib/ui5Framework/maven/SnapshotCache.js", "./validation/validator": "./lib/validation/validator.js", + "./utils/dataDir": "./lib/utils/dataDir.js", "./validation/ValidationError": "./lib/validation/ValidationError.js", "./graph/ProjectGraph": "./lib/graph/ProjectGraph.js", "./graph/projectGraphBuilder": "./lib/graph/projectGraphBuilder.js", diff --git a/packages/project/test/lib/utils/dataDir.js b/packages/project/test/lib/utils/dataDir.js new file mode 100644 index 00000000000..7ccf6937ed6 --- /dev/null +++ b/packages/project/test/lib/utils/dataDir.js @@ -0,0 +1,80 @@ +import test from "ava"; +import path from "node:path"; +import os from "node:os"; +import sinon from "sinon"; +import esmock from "esmock"; +test.beforeEach(async (t) => { + t.context.originalUi5DataDirEnv = process.env.UI5_DATA_DIR; + delete process.env.UI5_DATA_DIR; + + t.context.configGetUi5DataDirStub = sinon.stub().returns(undefined); + t.context.ConfigurationStub = { + fromFile: sinon.stub().resolves({ + getUi5DataDir: t.context.configGetUi5DataDirStub + }) + }; + + const {resolveUi5DataDir} = await esmock("../../../lib/utils/dataDir.js", { + "../../../lib/config/Configuration.js": t.context.ConfigurationStub + }); + t.context.resolveUi5DataDir = resolveUi5DataDir; +}); + +test.afterEach.always((t) => { + if (typeof t.context.originalUi5DataDirEnv === "undefined") { + delete process.env.UI5_DATA_DIR; + } else { + process.env.UI5_DATA_DIR = t.context.originalUi5DataDirEnv; + } + sinon.restore(); +}); + +test.serial("resolveUi5DataDir: returns ~/.ui5 when nothing is configured", async (t) => { + const {resolveUi5DataDir} = t.context; + const result = await resolveUi5DataDir(); + t.is(result, path.join(os.homedir(), ".ui5")); +}); + +test.serial("resolveUi5DataDir: returns value from UI5_DATA_DIR env var (absolute)", async (t) => { + const {resolveUi5DataDir} = t.context; + process.env.UI5_DATA_DIR = "/custom/data/dir"; + const result = await resolveUi5DataDir(); + t.is(result, path.resolve("/custom/data/dir")); + t.is(t.context.ConfigurationStub.fromFile.callCount, 0, "Configuration not read when env var is set"); +}); + +test.serial("resolveUi5DataDir: resolves relative UI5_DATA_DIR env var against cwd", async (t) => { + const {resolveUi5DataDir} = t.context; + process.env.UI5_DATA_DIR = "relative/data"; + const result = await resolveUi5DataDir(); + t.is(result, path.resolve("relative/data")); +}); + +test.serial("resolveUi5DataDir: returns value from Configuration (absolute)", async (t) => { + const {resolveUi5DataDir} = t.context; + t.context.configGetUi5DataDirStub.returns("/config/data/dir"); + const result = await resolveUi5DataDir(); + t.is(result, path.resolve("/config/data/dir")); +}); + +test.serial("resolveUi5DataDir: resolves relative Configuration value against cwd", async (t) => { + const {resolveUi5DataDir} = t.context; + t.context.configGetUi5DataDirStub.returns("my-data"); + const result = await resolveUi5DataDir(); + t.is(result, path.resolve("my-data")); +}); + +test.serial("resolveUi5DataDir: env var takes precedence over Configuration", async (t) => { + const {resolveUi5DataDir} = t.context; + process.env.UI5_DATA_DIR = "/env/data"; + t.context.configGetUi5DataDirStub.returns("/config/data"); + const result = await resolveUi5DataDir(); + t.is(result, path.resolve("/env/data")); +}); + +test.serial("resolveUi5DataDir: uses process.cwd() when cwd is not provided", async (t) => { + const {resolveUi5DataDir} = t.context; + t.context.configGetUi5DataDirStub.returns("relative/data"); + const result = await resolveUi5DataDir(); + t.is(result, path.resolve(process.cwd(), "relative/data")); +}); From 5fafbca60265c2554dbc8235a717f8abc093a264 Mon Sep 17 00:00:00 2001 From: d3xter666 Date: Fri, 17 Jul 2026 10:07:07 +0300 Subject: [PATCH 02/12] refactor: Use projectRootPath in resolveUi5DataDir for correct relative path resolution Renames the cwd option to projectRootPath to make its purpose explicit: this is the root of the specific project being processed (where package.json lives), not necessarily the shell's current working directory. When omitted, falls back to process.cwd(). Callers in createFrameworkResolverInstance and frameworkResolverResolveVersion now thread the project root through so relative ui5DataDir values in config are resolved against the correct base directory. --- packages/cli/lib/framework/utils.js | 4 ++-- packages/project/lib/utils/dataDir.js | 12 +++++++++--- packages/project/test/lib/utils/dataDir.js | 20 +++++++++++++++++--- 3 files changed, 28 insertions(+), 8 deletions(-) diff --git a/packages/cli/lib/framework/utils.js b/packages/cli/lib/framework/utils.js index 0599b824e47..d5e272eda05 100644 --- a/packages/cli/lib/framework/utils.js +++ b/packages/cli/lib/framework/utils.js @@ -36,7 +36,7 @@ export async function createFrameworkResolverInstance({frameworkName, frameworkV return new Resolver({ cwd, version: frameworkVersion, - ui5DataDir: await resolveUi5DataDir() + ui5DataDir: await resolveUi5DataDir({projectRootPath: cwd}) }); } @@ -44,7 +44,7 @@ export async function frameworkResolverResolveVersion({frameworkName, frameworkV const Resolver = await utils.getFrameworkResolver(frameworkName, frameworkVersion); return Resolver.resolveVersion(frameworkVersion, { cwd, - ui5DataDir: await resolveUi5DataDir() + ui5DataDir: await resolveUi5DataDir({projectRootPath: cwd}) }); } diff --git a/packages/project/lib/utils/dataDir.js b/packages/project/lib/utils/dataDir.js index 484b7bfda1b..d13c16801e9 100644 --- a/packages/project/lib/utils/dataDir.js +++ b/packages/project/lib/utils/dataDir.js @@ -10,19 +10,25 @@ import Configuration from "../config/Configuration.js"; *
  • Default: ~/.ui5
  • * * - * Relative paths are resolved against cwd. * This function always returns an absolute path — never undefined. * + * @param {object} [options] + * @param {string} [options.projectRootPath] The root directory of the project being processed. + * Used to resolve a relative ui5DataDir value from the environment variable or + * configuration file against the correct base. This is NOT necessarily the shell's current + * working directory — when processing a project in a sub-directory or a workspace member, + * this should be the root of that specific project (where its package.json lives). + * Defaults to process.cwd() when not provided. * @returns {Promise} Resolved absolute path to the UI5 data directory */ -export async function resolveUi5DataDir() { +export async function resolveUi5DataDir({projectRootPath} = {}) { let ui5DataDir = process.env.UI5_DATA_DIR; if (!ui5DataDir) { const config = await Configuration.fromFile(); ui5DataDir = config.getUi5DataDir(); } if (ui5DataDir) { - return path.resolve(process.cwd(), ui5DataDir); + return path.resolve(projectRootPath ?? process.cwd(), ui5DataDir); } return path.join(os.homedir(), ".ui5"); } diff --git a/packages/project/test/lib/utils/dataDir.js b/packages/project/test/lib/utils/dataDir.js index 7ccf6937ed6..485a4bab118 100644 --- a/packages/project/test/lib/utils/dataDir.js +++ b/packages/project/test/lib/utils/dataDir.js @@ -43,7 +43,14 @@ test.serial("resolveUi5DataDir: returns value from UI5_DATA_DIR env var (absolut t.is(t.context.ConfigurationStub.fromFile.callCount, 0, "Configuration not read when env var is set"); }); -test.serial("resolveUi5DataDir: resolves relative UI5_DATA_DIR env var against cwd", async (t) => { +test.serial("resolveUi5DataDir: resolves relative UI5_DATA_DIR env var against projectRootPath", async (t) => { + const {resolveUi5DataDir} = t.context; + process.env.UI5_DATA_DIR = "relative/data"; + const result = await resolveUi5DataDir({projectRootPath: "/my/project"}); + t.is(result, path.join("/my/project", "relative/data")); +}); + +test.serial("resolveUi5DataDir: resolves relative UI5_DATA_DIR env var against process.cwd() when no projectRootPath", async (t) => { const {resolveUi5DataDir} = t.context; process.env.UI5_DATA_DIR = "relative/data"; const result = await resolveUi5DataDir(); @@ -57,7 +64,14 @@ test.serial("resolveUi5DataDir: returns value from Configuration (absolute)", as t.is(result, path.resolve("/config/data/dir")); }); -test.serial("resolveUi5DataDir: resolves relative Configuration value against cwd", async (t) => { +test.serial("resolveUi5DataDir: resolves relative Configuration value against projectRootPath", async (t) => { + const {resolveUi5DataDir} = t.context; + t.context.configGetUi5DataDirStub.returns("my-data"); + const result = await resolveUi5DataDir({projectRootPath: "/my/project"}); + t.is(result, path.join("/my/project", "my-data")); +}); + +test.serial("resolveUi5DataDir: resolves relative Configuration value against process.cwd() when no projectRootPath", async (t) => { const {resolveUi5DataDir} = t.context; t.context.configGetUi5DataDirStub.returns("my-data"); const result = await resolveUi5DataDir(); @@ -72,7 +86,7 @@ test.serial("resolveUi5DataDir: env var takes precedence over Configuration", as t.is(result, path.resolve("/env/data")); }); -test.serial("resolveUi5DataDir: uses process.cwd() when cwd is not provided", async (t) => { +test.serial("resolveUi5DataDir: uses process.cwd() when projectRootPath is not provided", async (t) => { const {resolveUi5DataDir} = t.context; t.context.configGetUi5DataDirStub.returns("relative/data"); const result = await resolveUi5DataDir(); From 0119147a13ed99b18d94465c8cb82b50615cabd5 Mon Sep 17 00:00:00 2001 From: d3xter666 Date: Fri, 17 Jul 2026 10:59:08 +0300 Subject: [PATCH 03/12] refactor: Migrate remaining inline ui5DataDir resolutions to resolveUi5DataDir MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two call sites that duplicated the env→config→default resolution logic are migrated to resolveUi5DataDir({projectRootPath}): - packages/project/lib/graph/helpers/ui5Framework.js: enrichProjectGraph resolves ui5DataDir via resolveUi5DataDir using rootProject.getRootPath() as projectRootPath, then passes it to the framework Resolver. Configuration and os imports removed. - packages/project/lib/build/cache/CacheManager.create(): The no-ui5DataDir path now delegates to resolveUi5DataDir instead of inlining env/config/default logic. The explicit ui5DataDir option still resolves against cwd for backward compatibility. os and Configuration imports removed. Tests updated: resolveUi5DataDir stub added to ui5Framework.js esmocks so the stub mirrors the real resolution logic (including env and config stubs), and package-exports count updated. --- .../project/lib/build/cache/CacheManager.js | 15 ++----- .../project/lib/graph/helpers/ui5Framework.js | 16 +++---- .../graph/helpers/ui5Framework.integration.js | 5 ++- .../test/lib/graph/helpers/ui5Framework.js | 42 +++++++++++++------ packages/project/test/lib/package-exports.js | 2 +- 5 files changed, 43 insertions(+), 37 deletions(-) diff --git a/packages/project/lib/build/cache/CacheManager.js b/packages/project/lib/build/cache/CacheManager.js index e1e37a6f521..d2621ced02f 100644 --- a/packages/project/lib/build/cache/CacheManager.js +++ b/packages/project/lib/build/cache/CacheManager.js @@ -1,6 +1,5 @@ import path from "node:path"; -import os from "node:os"; -import Configuration from "../../config/Configuration.js"; +import {resolveUi5DataDir} from "../../utils/dataDir.js"; import {getLogger} from "@ui5/logger"; import BuildCacheStorage from "./BuildCacheStorage.js"; @@ -73,17 +72,9 @@ export default class CacheManager { */ static async create(cwd, {ui5DataDir} = {}) { if (!ui5DataDir) { - // ENV var should take precedence over the dataDir from the configuration. - ui5DataDir = process.env.UI5_DATA_DIR; - if (!ui5DataDir) { - const config = await Configuration.fromFile(); - ui5DataDir = config.getUi5DataDir(); - } - } - if (ui5DataDir) { - ui5DataDir = path.resolve(cwd, ui5DataDir); + ui5DataDir = await resolveUi5DataDir({projectRootPath: cwd}); } else { - ui5DataDir = path.join(os.homedir(), ".ui5"); + ui5DataDir = path.resolve(cwd, ui5DataDir); } const cacheDir = path.join(ui5DataDir, "buildCache"); log.verbose(`Using build cache directory: ${cacheDir}`); diff --git a/packages/project/lib/graph/helpers/ui5Framework.js b/packages/project/lib/graph/helpers/ui5Framework.js index 660cc78427e..9bf90e21546 100644 --- a/packages/project/lib/graph/helpers/ui5Framework.js +++ b/packages/project/lib/graph/helpers/ui5Framework.js @@ -2,8 +2,7 @@ import Module from "../Module.js"; import ProjectGraph from "../ProjectGraph.js"; import {getLogger} from "@ui5/logger"; const log = getLogger("graph:helpers:ui5Framework"); -import Configuration from "../../config/Configuration.js"; -import path from "node:path"; +import {resolveUi5DataDir} from "../../utils/dataDir.js"; class ProjectProcessor { constructor({libraryMetadata, graph, workspace}) { @@ -348,15 +347,10 @@ export default { Resolver = (await import("../../ui5Framework/Sapui5Resolver.js")).default; } - // ENV var should take precedence over the dataDir from the configuration. - let ui5DataDir = process.env.UI5_DATA_DIR; - if (!ui5DataDir) { - const config = await Configuration.fromFile(); - ui5DataDir = config.getUi5DataDir(); - } - if (ui5DataDir) { - ui5DataDir = path.resolve(cwd, ui5DataDir); - } + // Resolve the UI5 data directory using the shared utility. + // Only env var and configuration are considered — if neither is set the + // Resolver uses its own default (~/.ui5) via its fallback. + const ui5DataDir = await resolveUi5DataDir({projectRootPath: cwd}); if (options.versionOverride) { version = await Resolver.resolveVersion(options.versionOverride, { diff --git a/packages/project/test/lib/graph/helpers/ui5Framework.integration.js b/packages/project/test/lib/graph/helpers/ui5Framework.integration.js index 93096d50109..13d867c969b 100644 --- a/packages/project/test/lib/graph/helpers/ui5Framework.integration.js +++ b/packages/project/test/lib/graph/helpers/ui5Framework.integration.js @@ -135,7 +135,10 @@ test.beforeEach(async (t) => { "../../../../lib/graph/Module.js": t.context.Module, "../../../../lib/ui5Framework/Openui5Resolver.js": t.context.Openui5Resolver, "../../../../lib/ui5Framework/Sapui5Resolver.js": t.context.Sapui5Resolver, - "../../../../lib/config/Configuration.js": t.context.Configuration + "../../../../lib/config/Configuration.js": t.context.Configuration, + "../../../../lib/utils/dataDir.js": { + resolveUi5DataDir: sinon.stub().resolves(path.join(fakeBaseDir, "homedir", ".ui5")) + } }); t.context.projectGraphBuilder = await esmock.p("../../../../lib/graph/projectGraphBuilder.js", { diff --git a/packages/project/test/lib/graph/helpers/ui5Framework.js b/packages/project/test/lib/graph/helpers/ui5Framework.js index b134ac187ac..0f974a82997 100644 --- a/packages/project/test/lib/graph/helpers/ui5Framework.js +++ b/packages/project/test/lib/graph/helpers/ui5Framework.js @@ -1,4 +1,5 @@ import path from "node:path"; +import os from "node:os"; import test from "ava"; import sinonGlobal from "sinon"; import esmock from "esmock"; @@ -62,11 +63,28 @@ test.beforeEach(async (t) => { }) }; + t.context.resolveUi5DataDirStub = sinon.stub().callsFake(async ({projectRootPath} = {}) => { + // Mirror resolveUi5DataDir logic using the test's own Configuration stub + // so tests that set getUi5DataDirStub or UI5_DATA_DIR get the expected path. + let ui5DataDir = process.env.UI5_DATA_DIR; + if (!ui5DataDir) { + const config = await t.context.ConfigurationStub.fromFile(); + ui5DataDir = config.getUi5DataDir(); + } + if (ui5DataDir) { + return path.resolve(projectRootPath ?? process.cwd(), ui5DataDir); + } + return path.join(os.homedir(), ".ui5"); + }); + t.context.ui5Framework = await esmock.p("../../../../lib/graph/helpers/ui5Framework.js", { "@ui5/logger": ui5Logger, "../../../../lib/ui5Framework/Sapui5Resolver.js": t.context.Sapui5ResolverStub, "../../../../lib/ui5Framework/Sapui5MavenSnapshotResolver.js": t.context.Sapui5MavenSnapshotResolverStub, "../../../../lib/config/Configuration.js": t.context.ConfigurationStub, + "../../../../lib/utils/dataDir.js": { + resolveUi5DataDir: t.context.resolveUi5DataDirStub, + }, }); t.context.utils = t.context.ui5Framework._utils; }); @@ -131,7 +149,7 @@ test.serial("enrichProjectGraph", async (t) => { snapshotCache: undefined, cwd: dependencyTree.path, version: dependencyTree.configuration.framework.version, - ui5DataDir: undefined, + ui5DataDir: path.join(os.homedir(), ".ui5"), providedLibraryMetadata: undefined }], "Sapui5Resolver#constructor should be called with expected args"); @@ -336,7 +354,7 @@ test.serial("enrichProjectGraph: With versionOverride", async (t) => { t.is(Sapui5ResolverResolveVersionStub.callCount, 1); t.deepEqual(Sapui5ResolverResolveVersionStub.getCall(0).args, ["1.99", { cwd: dependencyTree.path, - ui5DataDir: undefined, + ui5DataDir: path.join(os.homedir(), ".ui5"), }]); t.is(Sapui5ResolverStub.callCount, 1, "Sapui5Resolver#constructor should be called once"); @@ -344,7 +362,7 @@ test.serial("enrichProjectGraph: With versionOverride", async (t) => { snapshotCache: undefined, cwd: dependencyTree.path, version: "1.99.9", - ui5DataDir: undefined, + ui5DataDir: path.join(os.homedir(), ".ui5"), providedLibraryMetadata: undefined }], "Sapui5Resolver#constructor should be called with expected args"); }); @@ -398,7 +416,7 @@ test.serial("enrichProjectGraph: With versionOverride containing snapshot versio t.is(Sapui5MavenSnapshotResolverResolveVersionStub.callCount, 1); t.deepEqual(Sapui5MavenSnapshotResolverResolveVersionStub.getCall(0).args, ["1.99-SNAPSHOT", { cwd: dependencyTree.path, - ui5DataDir: undefined, + ui5DataDir: path.join(os.homedir(), ".ui5"), }]); t.is(Sapui5MavenSnapshotResolverStub.callCount, 1, @@ -407,7 +425,7 @@ test.serial("enrichProjectGraph: With versionOverride containing snapshot versio snapshotCache: undefined, cwd: dependencyTree.path, version: "1.99.9-SNAPSHOT", - ui5DataDir: undefined, + ui5DataDir: path.join(os.homedir(), ".ui5"), providedLibraryMetadata: undefined }], "Sapui5Resolver#constructor should be called with expected args"); }); @@ -461,7 +479,7 @@ test.serial("enrichProjectGraph: With versionOverride containing latest-snapshot t.is(Sapui5MavenSnapshotResolverResolveVersionStub.callCount, 1); t.deepEqual(Sapui5MavenSnapshotResolverResolveVersionStub.getCall(0).args, ["latest-snapshot", { cwd: dependencyTree.path, - ui5DataDir: undefined, + ui5DataDir: path.join(os.homedir(), ".ui5"), }]); t.is(Sapui5MavenSnapshotResolverStub.callCount, 1, @@ -470,7 +488,7 @@ test.serial("enrichProjectGraph: With versionOverride containing latest-snapshot snapshotCache: undefined, cwd: dependencyTree.path, version: "1.99.9-SNAPSHOT", - ui5DataDir: undefined, + ui5DataDir: path.join(os.homedir(), ".ui5"), providedLibraryMetadata: undefined }], "Sapui5Resolver#constructor should be called with expected args"); }); @@ -630,7 +648,7 @@ test.serial("enrichProjectGraph should resolve framework project with version an snapshotCache: undefined, cwd: dependencyTree.path, version: "1.2.3", - ui5DataDir: undefined, + ui5DataDir: path.join(os.homedir(), ".ui5"), providedLibraryMetadata: undefined }], "Sapui5Resolver#constructor should be called with expected args"); }); @@ -726,7 +744,7 @@ test.serial("enrichProjectGraph should resolve framework project " + t.is(Sapui5ResolverResolveVersionStub.callCount, 1); t.deepEqual(Sapui5ResolverResolveVersionStub.getCall(0).args, ["3.4.5", { cwd: dependencyTree.path, - ui5DataDir: undefined, + ui5DataDir: path.join(os.homedir(), ".ui5"), }]); t.is(Sapui5ResolverStub.callCount, 1, "Sapui5Resolver#constructor should be called once"); @@ -735,7 +753,7 @@ test.serial("enrichProjectGraph should resolve framework project " + snapshotCache: undefined, cwd: dependencyTree.path, version: "1.99.9", - ui5DataDir: undefined, + ui5DataDir: path.join(os.homedir(), ".ui5"), providedLibraryMetadata: undefined }], "Sapui5Resolver#constructor should be called with expected args"); }); @@ -1000,7 +1018,7 @@ test.serial("enrichProjectGraph should use framework library metadata from works snapshotCache: undefined, cwd: dependencyTree.path, version: "1.111.1", - ui5DataDir: undefined, + ui5DataDir: path.join(os.homedir(), ".ui5"), providedLibraryMetadata: workspaceFrameworkLibraryMetadata }], "Sapui5Resolver#constructor should be called with expected args"); t.is(Sapui5ResolverStub.getCall(0).args[0].providedLibraryMetadata, workspaceFrameworkLibraryMetadata); @@ -1058,7 +1076,7 @@ test.serial("enrichProjectGraph should allow omitting framework version in case t.deepEqual(Sapui5ResolverStub.getCall(0).args, [{ snapshotCache: undefined, cwd: dependencyTree.path, - ui5DataDir: undefined, + ui5DataDir: path.join(os.homedir(), ".ui5"), version: undefined, providedLibraryMetadata: workspaceFrameworkLibraryMetadata }], "Sapui5Resolver#constructor should be called with expected args"); diff --git a/packages/project/test/lib/package-exports.js b/packages/project/test/lib/package-exports.js index 684e8634a84..ec16c6e22bc 100644 --- a/packages/project/test/lib/package-exports.js +++ b/packages/project/test/lib/package-exports.js @@ -13,7 +13,7 @@ test("export of package.json", (t) => { // Check number of definied exports test("check number of exports", (t) => { const packageJson = require("@ui5/project/package.json"); - t.is(Object.keys(packageJson.exports).length, 14); + t.is(Object.keys(packageJson.exports).length, 15); }); // Public API contract (exported modules) From 62561856646c0e4dec3796013c7ab2dded8220ed Mon Sep 17 00:00:00 2001 From: d3xter666 Date: Fri, 17 Jul 2026 11:57:42 +0300 Subject: [PATCH 04/12] refactor: Migrate remaining inline ui5DataDir resolutions to resolveUi5DataDir MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two call sites that duplicated the env→config→default resolution logic are migrated to resolveUi5DataDir({projectRootPath}): - packages/project/lib/graph/helpers/ui5Framework.js: enrichProjectGraph resolves ui5DataDir via resolveUi5DataDir using rootProject.getRootPath() as projectRootPath, then passes it to the framework Resolver. Configuration and os imports removed. - packages/project/lib/build/cache/CacheManager.create(): The no-ui5DataDir path now delegates to resolveUi5DataDir instead of inlining env/config/default logic. The explicit ui5DataDir option still resolves against cwd for backward compatibility. os and Configuration imports removed. Tests updated: resolveUi5DataDir stub added to ui5Framework.js esmocks so the stub mirrors the real resolution logic (including env and config stubs), and package-exports count updated. --- packages/project/test/lib/utils/dataDir.js | 46 ++++++++++++++++------ 1 file changed, 34 insertions(+), 12 deletions(-) diff --git a/packages/project/test/lib/utils/dataDir.js b/packages/project/test/lib/utils/dataDir.js index 485a4bab118..710715336e0 100644 --- a/packages/project/test/lib/utils/dataDir.js +++ b/packages/project/test/lib/utils/dataDir.js @@ -43,6 +43,13 @@ test.serial("resolveUi5DataDir: returns value from UI5_DATA_DIR env var (absolut t.is(t.context.ConfigurationStub.fromFile.callCount, 0, "Configuration not read when env var is set"); }); +test.serial("resolveUi5DataDir: absolute UI5_DATA_DIR ignores projectRootPath", async (t) => { + const {resolveUi5DataDir} = t.context; + process.env.UI5_DATA_DIR = "/custom/data/dir"; + const result = await resolveUi5DataDir({projectRootPath: "/some/project"}); + t.is(result, "/custom/data/dir", "absolute path is returned as-is regardless of projectRootPath"); +}); + test.serial("resolveUi5DataDir: resolves relative UI5_DATA_DIR env var against projectRootPath", async (t) => { const {resolveUi5DataDir} = t.context; process.env.UI5_DATA_DIR = "relative/data"; @@ -50,12 +57,16 @@ test.serial("resolveUi5DataDir: resolves relative UI5_DATA_DIR env var against p t.is(result, path.join("/my/project", "relative/data")); }); -test.serial("resolveUi5DataDir: resolves relative UI5_DATA_DIR env var against process.cwd() when no projectRootPath", async (t) => { - const {resolveUi5DataDir} = t.context; - process.env.UI5_DATA_DIR = "relative/data"; - const result = await resolveUi5DataDir(); - t.is(result, path.resolve("relative/data")); -}); +test.serial( + "resolveUi5DataDir: resolves relative UI5_DATA_DIR " + + "env var against process.cwd() when no projectRootPath", + async (t) => { + const {resolveUi5DataDir} = t.context; + process.env.UI5_DATA_DIR = "relative/data"; + const result = await resolveUi5DataDir(); + t.is(result, path.resolve("relative/data")); + }, +); test.serial("resolveUi5DataDir: returns value from Configuration (absolute)", async (t) => { const {resolveUi5DataDir} = t.context; @@ -64,6 +75,13 @@ test.serial("resolveUi5DataDir: returns value from Configuration (absolute)", as t.is(result, path.resolve("/config/data/dir")); }); +test.serial("resolveUi5DataDir: absolute Configuration value ignores projectRootPath", async (t) => { + const {resolveUi5DataDir} = t.context; + t.context.configGetUi5DataDirStub.returns("/config/data/dir"); + const result = await resolveUi5DataDir({projectRootPath: "/some/project"}); + t.is(result, "/config/data/dir", "absolute path is returned as-is regardless of projectRootPath"); +}); + test.serial("resolveUi5DataDir: resolves relative Configuration value against projectRootPath", async (t) => { const {resolveUi5DataDir} = t.context; t.context.configGetUi5DataDirStub.returns("my-data"); @@ -71,12 +89,16 @@ test.serial("resolveUi5DataDir: resolves relative Configuration value against pr t.is(result, path.join("/my/project", "my-data")); }); -test.serial("resolveUi5DataDir: resolves relative Configuration value against process.cwd() when no projectRootPath", async (t) => { - const {resolveUi5DataDir} = t.context; - t.context.configGetUi5DataDirStub.returns("my-data"); - const result = await resolveUi5DataDir(); - t.is(result, path.resolve("my-data")); -}); +test.serial( + "resolveUi5DataDir: resolves relative Configuration value against" + + " process.cwd() when no projectRootPath", + async (t) => { + const {resolveUi5DataDir} = t.context; + t.context.configGetUi5DataDirStub.returns("my-data"); + const result = await resolveUi5DataDir(); + t.is(result, path.resolve("my-data")); + }, +); test.serial("resolveUi5DataDir: env var takes precedence over Configuration", async (t) => { const {resolveUi5DataDir} = t.context; From 077feb0d051de9e147ea34b0bb8e6af67cd9c0e2 Mon Sep 17 00:00:00 2001 From: d3xter666 Date: Fri, 17 Jul 2026 14:14:43 +0300 Subject: [PATCH 05/12] docs: Fix stale comments after resolveUi5DataDir migration --- packages/project/lib/build/cache/CacheManager.js | 6 +++--- packages/project/lib/graph/helpers/ui5Framework.js | 3 +-- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/packages/project/lib/build/cache/CacheManager.js b/packages/project/lib/build/cache/CacheManager.js index d2621ced02f..9a4bbdd324d 100644 --- a/packages/project/lib/build/cache/CacheManager.js +++ b/packages/project/lib/build/cache/CacheManager.js @@ -59,9 +59,9 @@ export default class CacheManager { * Returns a singleton CacheManager for the determined cache directory. * The cache directory is resolved in this order: * 1. Explicit ui5DataDir option (resolved relative to cwd) - * 2. UI5_DATA_DIR environment variable (resolved relative to cwd) - * 3. ui5DataDir from UI5 configuration file - * 4. Default: ~/.ui5/ + * 2. UI5_DATA_DIR environment variable or ui5DataDir from configuration file + * (resolved relative to cwd via {@link resolveUi5DataDir}) + * 3. Default: ~/.ui5/ * * @public * @param {string} cwd Current working directory for resolving relative paths diff --git a/packages/project/lib/graph/helpers/ui5Framework.js b/packages/project/lib/graph/helpers/ui5Framework.js index 9bf90e21546..06921dc5b23 100644 --- a/packages/project/lib/graph/helpers/ui5Framework.js +++ b/packages/project/lib/graph/helpers/ui5Framework.js @@ -348,8 +348,7 @@ export default { } // Resolve the UI5 data directory using the shared utility. - // Only env var and configuration are considered — if neither is set the - // Resolver uses its own default (~/.ui5) via its fallback. + // Returns ~/.ui5 by default when no env var or configuration is set. const ui5DataDir = await resolveUi5DataDir({projectRootPath: cwd}); if (options.versionOverride) { From b091c774bdee480ff6ead2f14437a87c7fcb6b92 Mon Sep 17 00:00:00 2001 From: d3xter666 Date: Fri, 17 Jul 2026 14:25:55 +0300 Subject: [PATCH 06/12] test: Fix Windows path failures in dataDir tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace hardcoded POSIX absolute paths (/my/project, /custom/data/dir, etc.) with path.resolve(os.tmpdir(), ...) so assertions hold on all platforms. On Windows, /foo is not an absolute path — it is relative to the current drive root — causing four tests to fail. --- packages/project/test/lib/utils/dataDir.js | 37 ++++++++++++---------- 1 file changed, 20 insertions(+), 17 deletions(-) diff --git a/packages/project/test/lib/utils/dataDir.js b/packages/project/test/lib/utils/dataDir.js index 710715336e0..66675186510 100644 --- a/packages/project/test/lib/utils/dataDir.js +++ b/packages/project/test/lib/utils/dataDir.js @@ -3,6 +3,7 @@ import path from "node:path"; import os from "node:os"; import sinon from "sinon"; import esmock from "esmock"; + test.beforeEach(async (t) => { t.context.originalUi5DataDirEnv = process.env.UI5_DATA_DIR; delete process.env.UI5_DATA_DIR; @@ -37,24 +38,25 @@ test.serial("resolveUi5DataDir: returns ~/.ui5 when nothing is configured", asyn test.serial("resolveUi5DataDir: returns value from UI5_DATA_DIR env var (absolute)", async (t) => { const {resolveUi5DataDir} = t.context; - process.env.UI5_DATA_DIR = "/custom/data/dir"; + process.env.UI5_DATA_DIR = path.resolve("custom", "data", "dir"); const result = await resolveUi5DataDir(); - t.is(result, path.resolve("/custom/data/dir")); + t.is(result, path.resolve("custom", "data", "dir")); t.is(t.context.ConfigurationStub.fromFile.callCount, 0, "Configuration not read when env var is set"); }); test.serial("resolveUi5DataDir: absolute UI5_DATA_DIR ignores projectRootPath", async (t) => { const {resolveUi5DataDir} = t.context; - process.env.UI5_DATA_DIR = "/custom/data/dir"; - const result = await resolveUi5DataDir({projectRootPath: "/some/project"}); - t.is(result, "/custom/data/dir", "absolute path is returned as-is regardless of projectRootPath"); + process.env.UI5_DATA_DIR = path.resolve("custom", "data", "dir"); + const result = await resolveUi5DataDir({projectRootPath: path.resolve("some", "project")}); + t.is(result, path.resolve("custom", "data", "dir"), + "absolute path is returned as-is regardless of projectRootPath"); }); test.serial("resolveUi5DataDir: resolves relative UI5_DATA_DIR env var against projectRootPath", async (t) => { const {resolveUi5DataDir} = t.context; process.env.UI5_DATA_DIR = "relative/data"; - const result = await resolveUi5DataDir({projectRootPath: "/my/project"}); - t.is(result, path.join("/my/project", "relative/data")); + const result = await resolveUi5DataDir({projectRootPath: path.resolve("my", "project")}); + t.is(result, path.join(path.resolve("my", "project"), "relative/data")); }); test.serial( @@ -70,23 +72,24 @@ test.serial( test.serial("resolveUi5DataDir: returns value from Configuration (absolute)", async (t) => { const {resolveUi5DataDir} = t.context; - t.context.configGetUi5DataDirStub.returns("/config/data/dir"); + t.context.configGetUi5DataDirStub.returns(path.resolve("config", "data", "dir")); const result = await resolveUi5DataDir(); - t.is(result, path.resolve("/config/data/dir")); + t.is(result, path.resolve("config", "data", "dir")); }); test.serial("resolveUi5DataDir: absolute Configuration value ignores projectRootPath", async (t) => { const {resolveUi5DataDir} = t.context; - t.context.configGetUi5DataDirStub.returns("/config/data/dir"); - const result = await resolveUi5DataDir({projectRootPath: "/some/project"}); - t.is(result, "/config/data/dir", "absolute path is returned as-is regardless of projectRootPath"); + t.context.configGetUi5DataDirStub.returns(path.resolve("config", "data", "dir")); + const result = await resolveUi5DataDir({projectRootPath: path.resolve("some", "project")}); + t.is(result, path.resolve("config", "data", "dir"), + "absolute path is returned as-is regardless of projectRootPath"); }); test.serial("resolveUi5DataDir: resolves relative Configuration value against projectRootPath", async (t) => { const {resolveUi5DataDir} = t.context; t.context.configGetUi5DataDirStub.returns("my-data"); - const result = await resolveUi5DataDir({projectRootPath: "/my/project"}); - t.is(result, path.join("/my/project", "my-data")); + const result = await resolveUi5DataDir({projectRootPath: path.resolve("my", "project")}); + t.is(result, path.join(path.resolve("my", "project"), "my-data")); }); test.serial( @@ -102,10 +105,10 @@ test.serial( test.serial("resolveUi5DataDir: env var takes precedence over Configuration", async (t) => { const {resolveUi5DataDir} = t.context; - process.env.UI5_DATA_DIR = "/env/data"; - t.context.configGetUi5DataDirStub.returns("/config/data"); + process.env.UI5_DATA_DIR = path.resolve("custom", "data", "dir"); + t.context.configGetUi5DataDirStub.returns(path.resolve("config", "data", "dir")); const result = await resolveUi5DataDir(); - t.is(result, path.resolve("/env/data")); + t.is(result, path.resolve("custom", "data", "dir")); }); test.serial("resolveUi5DataDir: uses process.cwd() when projectRootPath is not provided", async (t) => { From 5f8ee9f98dd13bef0e0a1e1d8110565687262cb6 Mon Sep 17 00:00:00 2001 From: d3xter666 Date: Fri, 17 Jul 2026 14:55:29 +0300 Subject: [PATCH 07/12] fix: Ensure resolveUi5DataDir always returns an absolute path - Use path.resolve instead of path.join for the default ~/.ui5 fallback so the result is absolute even when os.homedir() returns a relative path (e.g. "./" in some CI/container environments). - Add edge-case test that asserts absoluteness when HOME is relative. - Fix CLI framework/utils tests: stubs resolving undefined reflected old behavior where no ui5DataDir meant undefined; add clarifying comments that these tests validate stub wiring, not real resolution. - Add utils/dataDir to the explicit API parity list in package-exports test so a path/mapping regression would be caught by a specific test. --- packages/cli/test/lib/framework/utils.js | 8 ++++---- packages/project/lib/utils/dataDir.js | 2 +- packages/project/test/lib/package-exports.js | 1 + packages/project/test/lib/utils/dataDir.js | 13 ++++++++++++- 4 files changed, 18 insertions(+), 6 deletions(-) diff --git a/packages/cli/test/lib/framework/utils.js b/packages/cli/test/lib/framework/utils.js index 1094bce26d9..eae789dbe04 100644 --- a/packages/cli/test/lib/framework/utils.js +++ b/packages/cli/test/lib/framework/utils.js @@ -138,7 +138,7 @@ test.serial("getFrameworkResolver: Invalid framework.name", async (t) => { }); }); -test.serial("createFrameworkResolverInstance: Without ui5DataDir", async (t) => { +test.serial("createFrameworkResolverInstance: Without explicit ui5DataDir (uses resolved default)", async (t) => { const {createFrameworkResolverInstance} = t.context.utils; const {sinon, resolveUi5DataDirStub} = t.context; @@ -168,7 +168,7 @@ test.serial("createFrameworkResolverInstance: Without ui5DataDir", async (t) => { cwd: "my-project-path", version: "", - ui5DataDir: undefined + ui5DataDir: undefined // stub resolves undefined — tests stub contract, not real behavior } ]); }); @@ -216,7 +216,7 @@ test.serial("frameworkResolverResolveVersion", async (t) => { sinon.stub(t.context._utils, "getFrameworkResolver").resolves({ resolveVersion: resolveVersionStub }); - resolveUi5DataDirStub.resolves(undefined); + resolveUi5DataDirStub.resolves(undefined); // stub contract test — real resolver always returns a path const result = await frameworkResolverResolveVersion({ frameworkName: "SAPUI5", @@ -232,7 +232,7 @@ test.serial("frameworkResolverResolveVersion", async (t) => { "latest", { cwd: "my-project-path", - ui5DataDir: undefined + ui5DataDir: undefined // stub resolves undefined — tests stub contract, not real behavior } ]); }); diff --git a/packages/project/lib/utils/dataDir.js b/packages/project/lib/utils/dataDir.js index d13c16801e9..1c20725a038 100644 --- a/packages/project/lib/utils/dataDir.js +++ b/packages/project/lib/utils/dataDir.js @@ -30,5 +30,5 @@ export async function resolveUi5DataDir({projectRootPath} = {}) { if (ui5DataDir) { return path.resolve(projectRootPath ?? process.cwd(), ui5DataDir); } - return path.join(os.homedir(), ".ui5"); + return path.resolve(os.homedir(), ".ui5"); } diff --git a/packages/project/test/lib/package-exports.js b/packages/project/test/lib/package-exports.js index ec16c6e22bc..10472adbf86 100644 --- a/packages/project/test/lib/package-exports.js +++ b/packages/project/test/lib/package-exports.js @@ -26,6 +26,7 @@ test("check number of exports", (t) => { "ui5Framework/Sapui5Resolver", "ui5Framework/Sapui5MavenSnapshotResolver", "ui5Framework/maven/SnapshotCache", + "utils/dataDir", "validation/validator", "validation/ValidationError", "graph/ProjectGraph", diff --git a/packages/project/test/lib/utils/dataDir.js b/packages/project/test/lib/utils/dataDir.js index 66675186510..6ecc3324d9c 100644 --- a/packages/project/test/lib/utils/dataDir.js +++ b/packages/project/test/lib/utils/dataDir.js @@ -33,7 +33,18 @@ test.afterEach.always((t) => { test.serial("resolveUi5DataDir: returns ~/.ui5 when nothing is configured", async (t) => { const {resolveUi5DataDir} = t.context; const result = await resolveUi5DataDir(); - t.is(result, path.join(os.homedir(), ".ui5")); + t.is(result, path.resolve(os.homedir(), ".ui5")); + t.true(path.isAbsolute(result), "default path must always be absolute"); +}); + +test.serial("resolveUi5DataDir: default is absolute even when HOME is relative", async (t) => { + const {resolveUi5DataDir: resolveWithRelativeHome} = await esmock("../../../lib/utils/dataDir.js", { + "../../../lib/config/Configuration.js": t.context.ConfigurationStub, + "node:os": {...os, homedir: () => "./relative-home"} + }); + const result = await resolveWithRelativeHome(); + t.true(path.isAbsolute(result), "result must be absolute even when os.homedir() is relative"); + esmock.purge(resolveWithRelativeHome); }); test.serial("resolveUi5DataDir: returns value from UI5_DATA_DIR env var (absolute)", async (t) => { From d922c91dfaef44147e9508551dea50eeca30eb90 Mon Sep 17 00:00:00 2001 From: d3xter666 Date: Fri, 17 Jul 2026 15:41:16 +0300 Subject: [PATCH 08/12] test: Fix ESLint findings --- packages/cli/test/lib/framework/utils.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/cli/test/lib/framework/utils.js b/packages/cli/test/lib/framework/utils.js index eae789dbe04..e9455d89d83 100644 --- a/packages/cli/test/lib/framework/utils.js +++ b/packages/cli/test/lib/framework/utils.js @@ -168,7 +168,7 @@ test.serial("createFrameworkResolverInstance: Without explicit ui5DataDir (uses { cwd: "my-project-path", version: "", - ui5DataDir: undefined // stub resolves undefined — tests stub contract, not real behavior + ui5DataDir: undefined // stub resolves undefined — tests stub contract, not real behavior } ]); }); @@ -232,7 +232,7 @@ test.serial("frameworkResolverResolveVersion", async (t) => { "latest", { cwd: "my-project-path", - ui5DataDir: undefined // stub resolves undefined — tests stub contract, not real behavior + ui5DataDir: undefined // stub resolves undefined — tests stub contract, not real behavior } ]); }); From 52b93a58cb15d42afbf471027ee35b848a435f7b Mon Sep 17 00:00:00 2001 From: d3xter666 Date: Fri, 17 Jul 2026 15:57:45 +0300 Subject: [PATCH 09/12] test: Fix ESLint findings - cli/framework/utils.js: stub now resolves the real default path (path.join(os.homedir(), ".ui5")) instead of undefined, matching the production contract of resolveUi5DataDir. - graph/helpers/ui5Framework.js: stub and assertions use path.resolve instead of path.join for the default ~/.ui5 path, matching the stricter absolute-path guarantee of the implementation. --- packages/cli/test/lib/framework/utils.js | 12 +++++---- .../test/lib/graph/helpers/ui5Framework.js | 26 +++++++++---------- 2 files changed, 20 insertions(+), 18 deletions(-) diff --git a/packages/cli/test/lib/framework/utils.js b/packages/cli/test/lib/framework/utils.js index e9455d89d83..ef278e2443e 100644 --- a/packages/cli/test/lib/framework/utils.js +++ b/packages/cli/test/lib/framework/utils.js @@ -1,4 +1,6 @@ import test from "ava"; +import path from "node:path"; +import os from "node:os"; import sinonGlobal from "sinon"; import esmock from "esmock"; @@ -20,7 +22,7 @@ test.beforeEach(async (t) => { t.context.Openui5Resolver = sinon.stub(); t.context.Sapui5MavenSnapshotResolver = sinon.stub(); - t.context.resolveUi5DataDirStub = sinon.stub().resolves(undefined); + t.context.resolveUi5DataDirStub = sinon.stub().resolves(path.join(os.homedir(), ".ui5")); t.context.utils = await esmock.p("../../../lib/framework/utils.js", { "@ui5/project/graph": { @@ -144,7 +146,7 @@ test.serial("createFrameworkResolverInstance: Without explicit ui5DataDir (uses const ResolverStub = sinon.stub().returns({}); sinon.stub(t.context._utils, "getFrameworkResolver").resolves(ResolverStub); - resolveUi5DataDirStub.resolves(undefined); + resolveUi5DataDirStub.resolves(path.join(os.homedir(), ".ui5")); const result = await createFrameworkResolverInstance({ frameworkName: "", @@ -168,7 +170,7 @@ test.serial("createFrameworkResolverInstance: Without explicit ui5DataDir (uses { cwd: "my-project-path", version: "", - ui5DataDir: undefined // stub resolves undefined — tests stub contract, not real behavior + ui5DataDir: path.join(os.homedir(), ".ui5") } ]); }); @@ -216,7 +218,7 @@ test.serial("frameworkResolverResolveVersion", async (t) => { sinon.stub(t.context._utils, "getFrameworkResolver").resolves({ resolveVersion: resolveVersionStub }); - resolveUi5DataDirStub.resolves(undefined); // stub contract test — real resolver always returns a path + resolveUi5DataDirStub.resolves(path.join(os.homedir(), ".ui5")); const result = await frameworkResolverResolveVersion({ frameworkName: "SAPUI5", @@ -232,7 +234,7 @@ test.serial("frameworkResolverResolveVersion", async (t) => { "latest", { cwd: "my-project-path", - ui5DataDir: undefined // stub resolves undefined — tests stub contract, not real behavior + ui5DataDir: path.join(os.homedir(), ".ui5") } ]); }); diff --git a/packages/project/test/lib/graph/helpers/ui5Framework.js b/packages/project/test/lib/graph/helpers/ui5Framework.js index 0f974a82997..c1e3bfe8e70 100644 --- a/packages/project/test/lib/graph/helpers/ui5Framework.js +++ b/packages/project/test/lib/graph/helpers/ui5Framework.js @@ -74,7 +74,7 @@ test.beforeEach(async (t) => { if (ui5DataDir) { return path.resolve(projectRootPath ?? process.cwd(), ui5DataDir); } - return path.join(os.homedir(), ".ui5"); + return path.resolve(os.homedir(), ".ui5"); }); t.context.ui5Framework = await esmock.p("../../../../lib/graph/helpers/ui5Framework.js", { @@ -149,7 +149,7 @@ test.serial("enrichProjectGraph", async (t) => { snapshotCache: undefined, cwd: dependencyTree.path, version: dependencyTree.configuration.framework.version, - ui5DataDir: path.join(os.homedir(), ".ui5"), + ui5DataDir: path.resolve(os.homedir(), ".ui5"), providedLibraryMetadata: undefined }], "Sapui5Resolver#constructor should be called with expected args"); @@ -354,7 +354,7 @@ test.serial("enrichProjectGraph: With versionOverride", async (t) => { t.is(Sapui5ResolverResolveVersionStub.callCount, 1); t.deepEqual(Sapui5ResolverResolveVersionStub.getCall(0).args, ["1.99", { cwd: dependencyTree.path, - ui5DataDir: path.join(os.homedir(), ".ui5"), + ui5DataDir: path.resolve(os.homedir(), ".ui5"), }]); t.is(Sapui5ResolverStub.callCount, 1, "Sapui5Resolver#constructor should be called once"); @@ -362,7 +362,7 @@ test.serial("enrichProjectGraph: With versionOverride", async (t) => { snapshotCache: undefined, cwd: dependencyTree.path, version: "1.99.9", - ui5DataDir: path.join(os.homedir(), ".ui5"), + ui5DataDir: path.resolve(os.homedir(), ".ui5"), providedLibraryMetadata: undefined }], "Sapui5Resolver#constructor should be called with expected args"); }); @@ -416,7 +416,7 @@ test.serial("enrichProjectGraph: With versionOverride containing snapshot versio t.is(Sapui5MavenSnapshotResolverResolveVersionStub.callCount, 1); t.deepEqual(Sapui5MavenSnapshotResolverResolveVersionStub.getCall(0).args, ["1.99-SNAPSHOT", { cwd: dependencyTree.path, - ui5DataDir: path.join(os.homedir(), ".ui5"), + ui5DataDir: path.resolve(os.homedir(), ".ui5"), }]); t.is(Sapui5MavenSnapshotResolverStub.callCount, 1, @@ -425,7 +425,7 @@ test.serial("enrichProjectGraph: With versionOverride containing snapshot versio snapshotCache: undefined, cwd: dependencyTree.path, version: "1.99.9-SNAPSHOT", - ui5DataDir: path.join(os.homedir(), ".ui5"), + ui5DataDir: path.resolve(os.homedir(), ".ui5"), providedLibraryMetadata: undefined }], "Sapui5Resolver#constructor should be called with expected args"); }); @@ -479,7 +479,7 @@ test.serial("enrichProjectGraph: With versionOverride containing latest-snapshot t.is(Sapui5MavenSnapshotResolverResolveVersionStub.callCount, 1); t.deepEqual(Sapui5MavenSnapshotResolverResolveVersionStub.getCall(0).args, ["latest-snapshot", { cwd: dependencyTree.path, - ui5DataDir: path.join(os.homedir(), ".ui5"), + ui5DataDir: path.resolve(os.homedir(), ".ui5"), }]); t.is(Sapui5MavenSnapshotResolverStub.callCount, 1, @@ -488,7 +488,7 @@ test.serial("enrichProjectGraph: With versionOverride containing latest-snapshot snapshotCache: undefined, cwd: dependencyTree.path, version: "1.99.9-SNAPSHOT", - ui5DataDir: path.join(os.homedir(), ".ui5"), + ui5DataDir: path.resolve(os.homedir(), ".ui5"), providedLibraryMetadata: undefined }], "Sapui5Resolver#constructor should be called with expected args"); }); @@ -648,7 +648,7 @@ test.serial("enrichProjectGraph should resolve framework project with version an snapshotCache: undefined, cwd: dependencyTree.path, version: "1.2.3", - ui5DataDir: path.join(os.homedir(), ".ui5"), + ui5DataDir: path.resolve(os.homedir(), ".ui5"), providedLibraryMetadata: undefined }], "Sapui5Resolver#constructor should be called with expected args"); }); @@ -744,7 +744,7 @@ test.serial("enrichProjectGraph should resolve framework project " + t.is(Sapui5ResolverResolveVersionStub.callCount, 1); t.deepEqual(Sapui5ResolverResolveVersionStub.getCall(0).args, ["3.4.5", { cwd: dependencyTree.path, - ui5DataDir: path.join(os.homedir(), ".ui5"), + ui5DataDir: path.resolve(os.homedir(), ".ui5"), }]); t.is(Sapui5ResolverStub.callCount, 1, "Sapui5Resolver#constructor should be called once"); @@ -753,7 +753,7 @@ test.serial("enrichProjectGraph should resolve framework project " + snapshotCache: undefined, cwd: dependencyTree.path, version: "1.99.9", - ui5DataDir: path.join(os.homedir(), ".ui5"), + ui5DataDir: path.resolve(os.homedir(), ".ui5"), providedLibraryMetadata: undefined }], "Sapui5Resolver#constructor should be called with expected args"); }); @@ -1018,7 +1018,7 @@ test.serial("enrichProjectGraph should use framework library metadata from works snapshotCache: undefined, cwd: dependencyTree.path, version: "1.111.1", - ui5DataDir: path.join(os.homedir(), ".ui5"), + ui5DataDir: path.resolve(os.homedir(), ".ui5"), providedLibraryMetadata: workspaceFrameworkLibraryMetadata }], "Sapui5Resolver#constructor should be called with expected args"); t.is(Sapui5ResolverStub.getCall(0).args[0].providedLibraryMetadata, workspaceFrameworkLibraryMetadata); @@ -1076,7 +1076,7 @@ test.serial("enrichProjectGraph should allow omitting framework version in case t.deepEqual(Sapui5ResolverStub.getCall(0).args, [{ snapshotCache: undefined, cwd: dependencyTree.path, - ui5DataDir: path.join(os.homedir(), ".ui5"), + ui5DataDir: path.resolve(os.homedir(), ".ui5"), version: undefined, providedLibraryMetadata: workspaceFrameworkLibraryMetadata }], "Sapui5Resolver#constructor should be called with expected args"); From 51799f6c53db1008627fe1e89bcedfdd59d4ffc1 Mon Sep 17 00:00:00 2001 From: d3xter666 Date: Fri, 17 Jul 2026 17:10:56 +0300 Subject: [PATCH 10/12] test: Remove os/path imports from framework utils test MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The stub for resolveUi5DataDir does not need to compute a real filesystem path — it just needs an opaque value that flows through correctly. Replace path.join(os.homedir(), '.ui5') with the string literal 'my-default-ui5-data-dir' and remove the unused os and path imports. --- packages/cli/test/lib/framework/utils.js | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/packages/cli/test/lib/framework/utils.js b/packages/cli/test/lib/framework/utils.js index ef278e2443e..48c7a4ba990 100644 --- a/packages/cli/test/lib/framework/utils.js +++ b/packages/cli/test/lib/framework/utils.js @@ -1,6 +1,4 @@ import test from "ava"; -import path from "node:path"; -import os from "node:os"; import sinonGlobal from "sinon"; import esmock from "esmock"; @@ -22,7 +20,7 @@ test.beforeEach(async (t) => { t.context.Openui5Resolver = sinon.stub(); t.context.Sapui5MavenSnapshotResolver = sinon.stub(); - t.context.resolveUi5DataDirStub = sinon.stub().resolves(path.join(os.homedir(), ".ui5")); + t.context.resolveUi5DataDirStub = sinon.stub().resolves("my-default-ui5-data-dir"); t.context.utils = await esmock.p("../../../lib/framework/utils.js", { "@ui5/project/graph": { @@ -146,7 +144,7 @@ test.serial("createFrameworkResolverInstance: Without explicit ui5DataDir (uses const ResolverStub = sinon.stub().returns({}); sinon.stub(t.context._utils, "getFrameworkResolver").resolves(ResolverStub); - resolveUi5DataDirStub.resolves(path.join(os.homedir(), ".ui5")); + resolveUi5DataDirStub.resolves("my-default-ui5-data-dir"); const result = await createFrameworkResolverInstance({ frameworkName: "", @@ -170,7 +168,7 @@ test.serial("createFrameworkResolverInstance: Without explicit ui5DataDir (uses { cwd: "my-project-path", version: "", - ui5DataDir: path.join(os.homedir(), ".ui5") + ui5DataDir: "my-default-ui5-data-dir" } ]); }); @@ -218,7 +216,7 @@ test.serial("frameworkResolverResolveVersion", async (t) => { sinon.stub(t.context._utils, "getFrameworkResolver").resolves({ resolveVersion: resolveVersionStub }); - resolveUi5DataDirStub.resolves(path.join(os.homedir(), ".ui5")); + resolveUi5DataDirStub.resolves("my-default-ui5-data-dir"); const result = await frameworkResolverResolveVersion({ frameworkName: "SAPUI5", @@ -234,7 +232,7 @@ test.serial("frameworkResolverResolveVersion", async (t) => { "latest", { cwd: "my-project-path", - ui5DataDir: path.join(os.homedir(), ".ui5") + ui5DataDir: "my-default-ui5-data-dir" } ]); }); From 2c4bd1a161bf7d72ce79fa96c66edefd144f6d88 Mon Sep 17 00:00:00 2001 From: d3xter666 Date: Fri, 17 Jul 2026 17:40:49 +0300 Subject: [PATCH 11/12] test: Refactor tests to mock correctly Configuration object --- .../test/lib/graph/helpers/ui5Framework.js | 45 ++++++------------- 1 file changed, 14 insertions(+), 31 deletions(-) diff --git a/packages/project/test/lib/graph/helpers/ui5Framework.js b/packages/project/test/lib/graph/helpers/ui5Framework.js index c1e3bfe8e70..8fcbcb78500 100644 --- a/packages/project/test/lib/graph/helpers/ui5Framework.js +++ b/packages/project/test/lib/graph/helpers/ui5Framework.js @@ -1,5 +1,4 @@ import path from "node:path"; -import os from "node:os"; import test from "ava"; import sinonGlobal from "sinon"; import esmock from "esmock"; @@ -55,7 +54,7 @@ test.beforeEach(async (t) => { t.context.Sapui5MavenSnapshotResolverResolveVersionStub = sinon.stub(); t.context.Sapui5MavenSnapshotResolverStub.resolveVersion = t.context.Sapui5MavenSnapshotResolverResolveVersionStub; - t.context.getUi5DataDirStub = sinon.stub().returns(undefined); + t.context.getUi5DataDirStub = sinon.stub().returns("/fake-ui5-data-dir"); t.context.ConfigurationStub = { fromFile: sinon.stub().resolves({ @@ -63,28 +62,12 @@ test.beforeEach(async (t) => { }) }; - t.context.resolveUi5DataDirStub = sinon.stub().callsFake(async ({projectRootPath} = {}) => { - // Mirror resolveUi5DataDir logic using the test's own Configuration stub - // so tests that set getUi5DataDirStub or UI5_DATA_DIR get the expected path. - let ui5DataDir = process.env.UI5_DATA_DIR; - if (!ui5DataDir) { - const config = await t.context.ConfigurationStub.fromFile(); - ui5DataDir = config.getUi5DataDir(); - } - if (ui5DataDir) { - return path.resolve(projectRootPath ?? process.cwd(), ui5DataDir); - } - return path.resolve(os.homedir(), ".ui5"); - }); - t.context.ui5Framework = await esmock.p("../../../../lib/graph/helpers/ui5Framework.js", { "@ui5/logger": ui5Logger, "../../../../lib/ui5Framework/Sapui5Resolver.js": t.context.Sapui5ResolverStub, "../../../../lib/ui5Framework/Sapui5MavenSnapshotResolver.js": t.context.Sapui5MavenSnapshotResolverStub, + }, { "../../../../lib/config/Configuration.js": t.context.ConfigurationStub, - "../../../../lib/utils/dataDir.js": { - resolveUi5DataDir: t.context.resolveUi5DataDirStub, - }, }); t.context.utils = t.context.ui5Framework._utils; }); @@ -149,7 +132,7 @@ test.serial("enrichProjectGraph", async (t) => { snapshotCache: undefined, cwd: dependencyTree.path, version: dependencyTree.configuration.framework.version, - ui5DataDir: path.resolve(os.homedir(), ".ui5"), + ui5DataDir: "/fake-ui5-data-dir", providedLibraryMetadata: undefined }], "Sapui5Resolver#constructor should be called with expected args"); @@ -354,7 +337,7 @@ test.serial("enrichProjectGraph: With versionOverride", async (t) => { t.is(Sapui5ResolverResolveVersionStub.callCount, 1); t.deepEqual(Sapui5ResolverResolveVersionStub.getCall(0).args, ["1.99", { cwd: dependencyTree.path, - ui5DataDir: path.resolve(os.homedir(), ".ui5"), + ui5DataDir: "/fake-ui5-data-dir", }]); t.is(Sapui5ResolverStub.callCount, 1, "Sapui5Resolver#constructor should be called once"); @@ -362,7 +345,7 @@ test.serial("enrichProjectGraph: With versionOverride", async (t) => { snapshotCache: undefined, cwd: dependencyTree.path, version: "1.99.9", - ui5DataDir: path.resolve(os.homedir(), ".ui5"), + ui5DataDir: "/fake-ui5-data-dir", providedLibraryMetadata: undefined }], "Sapui5Resolver#constructor should be called with expected args"); }); @@ -416,7 +399,7 @@ test.serial("enrichProjectGraph: With versionOverride containing snapshot versio t.is(Sapui5MavenSnapshotResolverResolveVersionStub.callCount, 1); t.deepEqual(Sapui5MavenSnapshotResolverResolveVersionStub.getCall(0).args, ["1.99-SNAPSHOT", { cwd: dependencyTree.path, - ui5DataDir: path.resolve(os.homedir(), ".ui5"), + ui5DataDir: "/fake-ui5-data-dir", }]); t.is(Sapui5MavenSnapshotResolverStub.callCount, 1, @@ -425,7 +408,7 @@ test.serial("enrichProjectGraph: With versionOverride containing snapshot versio snapshotCache: undefined, cwd: dependencyTree.path, version: "1.99.9-SNAPSHOT", - ui5DataDir: path.resolve(os.homedir(), ".ui5"), + ui5DataDir: "/fake-ui5-data-dir", providedLibraryMetadata: undefined }], "Sapui5Resolver#constructor should be called with expected args"); }); @@ -479,7 +462,7 @@ test.serial("enrichProjectGraph: With versionOverride containing latest-snapshot t.is(Sapui5MavenSnapshotResolverResolveVersionStub.callCount, 1); t.deepEqual(Sapui5MavenSnapshotResolverResolveVersionStub.getCall(0).args, ["latest-snapshot", { cwd: dependencyTree.path, - ui5DataDir: path.resolve(os.homedir(), ".ui5"), + ui5DataDir: "/fake-ui5-data-dir", }]); t.is(Sapui5MavenSnapshotResolverStub.callCount, 1, @@ -488,7 +471,7 @@ test.serial("enrichProjectGraph: With versionOverride containing latest-snapshot snapshotCache: undefined, cwd: dependencyTree.path, version: "1.99.9-SNAPSHOT", - ui5DataDir: path.resolve(os.homedir(), ".ui5"), + ui5DataDir: "/fake-ui5-data-dir", providedLibraryMetadata: undefined }], "Sapui5Resolver#constructor should be called with expected args"); }); @@ -648,7 +631,7 @@ test.serial("enrichProjectGraph should resolve framework project with version an snapshotCache: undefined, cwd: dependencyTree.path, version: "1.2.3", - ui5DataDir: path.resolve(os.homedir(), ".ui5"), + ui5DataDir: "/fake-ui5-data-dir", providedLibraryMetadata: undefined }], "Sapui5Resolver#constructor should be called with expected args"); }); @@ -744,7 +727,7 @@ test.serial("enrichProjectGraph should resolve framework project " + t.is(Sapui5ResolverResolveVersionStub.callCount, 1); t.deepEqual(Sapui5ResolverResolveVersionStub.getCall(0).args, ["3.4.5", { cwd: dependencyTree.path, - ui5DataDir: path.resolve(os.homedir(), ".ui5"), + ui5DataDir: "/fake-ui5-data-dir", }]); t.is(Sapui5ResolverStub.callCount, 1, "Sapui5Resolver#constructor should be called once"); @@ -753,7 +736,7 @@ test.serial("enrichProjectGraph should resolve framework project " + snapshotCache: undefined, cwd: dependencyTree.path, version: "1.99.9", - ui5DataDir: path.resolve(os.homedir(), ".ui5"), + ui5DataDir: "/fake-ui5-data-dir", providedLibraryMetadata: undefined }], "Sapui5Resolver#constructor should be called with expected args"); }); @@ -1018,7 +1001,7 @@ test.serial("enrichProjectGraph should use framework library metadata from works snapshotCache: undefined, cwd: dependencyTree.path, version: "1.111.1", - ui5DataDir: path.resolve(os.homedir(), ".ui5"), + ui5DataDir: "/fake-ui5-data-dir", providedLibraryMetadata: workspaceFrameworkLibraryMetadata }], "Sapui5Resolver#constructor should be called with expected args"); t.is(Sapui5ResolverStub.getCall(0).args[0].providedLibraryMetadata, workspaceFrameworkLibraryMetadata); @@ -1076,7 +1059,7 @@ test.serial("enrichProjectGraph should allow omitting framework version in case t.deepEqual(Sapui5ResolverStub.getCall(0).args, [{ snapshotCache: undefined, cwd: dependencyTree.path, - ui5DataDir: path.resolve(os.homedir(), ".ui5"), + ui5DataDir: "/fake-ui5-data-dir", version: undefined, providedLibraryMetadata: workspaceFrameworkLibraryMetadata }], "Sapui5Resolver#constructor should be called with expected args"); From 6f34876993a452db24a6860b7188b018a86567c8 Mon Sep 17 00:00:00 2001 From: d3xter666 Date: Fri, 17 Jul 2026 17:48:46 +0300 Subject: [PATCH 12/12] test: Fix tests for Windows --- .../test/lib/graph/helpers/ui5Framework.js | 26 +++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/packages/project/test/lib/graph/helpers/ui5Framework.js b/packages/project/test/lib/graph/helpers/ui5Framework.js index 8fcbcb78500..3d9e91a7820 100644 --- a/packages/project/test/lib/graph/helpers/ui5Framework.js +++ b/packages/project/test/lib/graph/helpers/ui5Framework.js @@ -54,7 +54,7 @@ test.beforeEach(async (t) => { t.context.Sapui5MavenSnapshotResolverResolveVersionStub = sinon.stub(); t.context.Sapui5MavenSnapshotResolverStub.resolveVersion = t.context.Sapui5MavenSnapshotResolverResolveVersionStub; - t.context.getUi5DataDirStub = sinon.stub().returns("/fake-ui5-data-dir"); + t.context.getUi5DataDirStub = sinon.stub().returns(path.resolve("fake-ui5-data-dir")); t.context.ConfigurationStub = { fromFile: sinon.stub().resolves({ @@ -132,7 +132,7 @@ test.serial("enrichProjectGraph", async (t) => { snapshotCache: undefined, cwd: dependencyTree.path, version: dependencyTree.configuration.framework.version, - ui5DataDir: "/fake-ui5-data-dir", + ui5DataDir: path.resolve("fake-ui5-data-dir"), providedLibraryMetadata: undefined }], "Sapui5Resolver#constructor should be called with expected args"); @@ -337,7 +337,7 @@ test.serial("enrichProjectGraph: With versionOverride", async (t) => { t.is(Sapui5ResolverResolveVersionStub.callCount, 1); t.deepEqual(Sapui5ResolverResolveVersionStub.getCall(0).args, ["1.99", { cwd: dependencyTree.path, - ui5DataDir: "/fake-ui5-data-dir", + ui5DataDir: path.resolve("fake-ui5-data-dir"), }]); t.is(Sapui5ResolverStub.callCount, 1, "Sapui5Resolver#constructor should be called once"); @@ -345,7 +345,7 @@ test.serial("enrichProjectGraph: With versionOverride", async (t) => { snapshotCache: undefined, cwd: dependencyTree.path, version: "1.99.9", - ui5DataDir: "/fake-ui5-data-dir", + ui5DataDir: path.resolve("fake-ui5-data-dir"), providedLibraryMetadata: undefined }], "Sapui5Resolver#constructor should be called with expected args"); }); @@ -399,7 +399,7 @@ test.serial("enrichProjectGraph: With versionOverride containing snapshot versio t.is(Sapui5MavenSnapshotResolverResolveVersionStub.callCount, 1); t.deepEqual(Sapui5MavenSnapshotResolverResolveVersionStub.getCall(0).args, ["1.99-SNAPSHOT", { cwd: dependencyTree.path, - ui5DataDir: "/fake-ui5-data-dir", + ui5DataDir: path.resolve("fake-ui5-data-dir"), }]); t.is(Sapui5MavenSnapshotResolverStub.callCount, 1, @@ -408,7 +408,7 @@ test.serial("enrichProjectGraph: With versionOverride containing snapshot versio snapshotCache: undefined, cwd: dependencyTree.path, version: "1.99.9-SNAPSHOT", - ui5DataDir: "/fake-ui5-data-dir", + ui5DataDir: path.resolve("fake-ui5-data-dir"), providedLibraryMetadata: undefined }], "Sapui5Resolver#constructor should be called with expected args"); }); @@ -462,7 +462,7 @@ test.serial("enrichProjectGraph: With versionOverride containing latest-snapshot t.is(Sapui5MavenSnapshotResolverResolveVersionStub.callCount, 1); t.deepEqual(Sapui5MavenSnapshotResolverResolveVersionStub.getCall(0).args, ["latest-snapshot", { cwd: dependencyTree.path, - ui5DataDir: "/fake-ui5-data-dir", + ui5DataDir: path.resolve("fake-ui5-data-dir"), }]); t.is(Sapui5MavenSnapshotResolverStub.callCount, 1, @@ -471,7 +471,7 @@ test.serial("enrichProjectGraph: With versionOverride containing latest-snapshot snapshotCache: undefined, cwd: dependencyTree.path, version: "1.99.9-SNAPSHOT", - ui5DataDir: "/fake-ui5-data-dir", + ui5DataDir: path.resolve("fake-ui5-data-dir"), providedLibraryMetadata: undefined }], "Sapui5Resolver#constructor should be called with expected args"); }); @@ -631,7 +631,7 @@ test.serial("enrichProjectGraph should resolve framework project with version an snapshotCache: undefined, cwd: dependencyTree.path, version: "1.2.3", - ui5DataDir: "/fake-ui5-data-dir", + ui5DataDir: path.resolve("fake-ui5-data-dir"), providedLibraryMetadata: undefined }], "Sapui5Resolver#constructor should be called with expected args"); }); @@ -727,7 +727,7 @@ test.serial("enrichProjectGraph should resolve framework project " + t.is(Sapui5ResolverResolveVersionStub.callCount, 1); t.deepEqual(Sapui5ResolverResolveVersionStub.getCall(0).args, ["3.4.5", { cwd: dependencyTree.path, - ui5DataDir: "/fake-ui5-data-dir", + ui5DataDir: path.resolve("fake-ui5-data-dir"), }]); t.is(Sapui5ResolverStub.callCount, 1, "Sapui5Resolver#constructor should be called once"); @@ -736,7 +736,7 @@ test.serial("enrichProjectGraph should resolve framework project " + snapshotCache: undefined, cwd: dependencyTree.path, version: "1.99.9", - ui5DataDir: "/fake-ui5-data-dir", + ui5DataDir: path.resolve("fake-ui5-data-dir"), providedLibraryMetadata: undefined }], "Sapui5Resolver#constructor should be called with expected args"); }); @@ -1001,7 +1001,7 @@ test.serial("enrichProjectGraph should use framework library metadata from works snapshotCache: undefined, cwd: dependencyTree.path, version: "1.111.1", - ui5DataDir: "/fake-ui5-data-dir", + ui5DataDir: path.resolve("fake-ui5-data-dir"), providedLibraryMetadata: workspaceFrameworkLibraryMetadata }], "Sapui5Resolver#constructor should be called with expected args"); t.is(Sapui5ResolverStub.getCall(0).args[0].providedLibraryMetadata, workspaceFrameworkLibraryMetadata); @@ -1059,7 +1059,7 @@ test.serial("enrichProjectGraph should allow omitting framework version in case t.deepEqual(Sapui5ResolverStub.getCall(0).args, [{ snapshotCache: undefined, cwd: dependencyTree.path, - ui5DataDir: "/fake-ui5-data-dir", + ui5DataDir: path.resolve("fake-ui5-data-dir"), version: undefined, providedLibraryMetadata: workspaceFrameworkLibraryMetadata }], "Sapui5Resolver#constructor should be called with expected args");