From f36e21180a3fcadb09ad54fd9087eb04a8dcdbd9 Mon Sep 17 00:00:00 2001 From: d3xter666 Date: Fri, 17 Jul 2026 09:44:34 +0300 Subject: [PATCH 01/17] 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/17] 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/17] 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/17] 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/17] 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/17] 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/17] 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/17] 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/17] 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/17] 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/17] 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/17] 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"); From 7c2ab89c87d43f20d680a7c5a31c78493e7fda35 Mon Sep 17 00:00:00 2001 From: d3xter666 Date: Mon, 20 Jul 2026 10:29:39 +0300 Subject: [PATCH 13/17] docs: Expose ui5DataDir util to JSDoc --- packages/project/lib/utils/dataDir.js | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/packages/project/lib/utils/dataDir.js b/packages/project/lib/utils/dataDir.js index 1c20725a038..fd0ae59a6b8 100644 --- a/packages/project/lib/utils/dataDir.js +++ b/packages/project/lib/utils/dataDir.js @@ -2,6 +2,13 @@ import path from "node:path"; import os from "node:os"; import Configuration from "../config/Configuration.js"; +/** + * Utilities for resolving the UI5 data directory. + * + * @public + * @module @ui5/project/utils/dataDir + */ + /** * Resolves the UI5 data directory using the standard precedence chain: *
      @@ -12,6 +19,7 @@ import Configuration from "../config/Configuration.js"; * * This function always returns an absolute path — never undefined. * + * @public * @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 From 9ac64b56e66e87656d0a43671fcc382ecb8fd211 Mon Sep 17 00:00:00 2001 From: d3xter666 Date: Mon, 20 Jul 2026 14:23:52 +0300 Subject: [PATCH 14/17] refactor: CacheManager falls back as AbstractResolver on ui5DataDir --- packages/project/lib/build/cache/CacheManager.js | 13 +++++-------- packages/project/lib/build/helpers/BuildContext.js | 6 ++++-- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/packages/project/lib/build/cache/CacheManager.js b/packages/project/lib/build/cache/CacheManager.js index 9a4bbdd324d..a4b5f12aad4 100644 --- a/packages/project/lib/build/cache/CacheManager.js +++ b/packages/project/lib/build/cache/CacheManager.js @@ -1,7 +1,7 @@ import path from "node:path"; -import {resolveUi5DataDir} from "../../utils/dataDir.js"; import {getLogger} from "@ui5/logger"; import BuildCacheStorage from "./BuildCacheStorage.js"; +import os from "node:os"; const log = getLogger("build:cache:CacheManager"); @@ -64,18 +64,15 @@ export default class CacheManager { * 3. Default: ~/.ui5/ * * @public - * @param {string} cwd Current working directory for resolving relative paths * @param {object} [options] * @param {string} [options.ui5DataDir] Explicit UI5 data directory. When provided, * environment variable, configuration file, and home-directory fallbacks are skipped. * @returns {Promise} Singleton CacheManager instance for the cache directory */ - static async create(cwd, {ui5DataDir} = {}) { - if (!ui5DataDir) { - ui5DataDir = await resolveUi5DataDir({projectRootPath: cwd}); - } else { - ui5DataDir = path.resolve(cwd, ui5DataDir); - } + static async create({ui5DataDir} = {}) { + ui5DataDir = path.resolve( + ui5DataDir || path.join(os.homedir(), ".ui5") + ); const cacheDir = path.join(ui5DataDir, "buildCache"); log.verbose(`Using build cache directory: ${cacheDir}`); diff --git a/packages/project/lib/build/helpers/BuildContext.js b/packages/project/lib/build/helpers/BuildContext.js index 50251ac9ddc..5a030cc728f 100644 --- a/packages/project/lib/build/helpers/BuildContext.js +++ b/packages/project/lib/build/helpers/BuildContext.js @@ -5,6 +5,7 @@ import {getBaseSignature} from "./getBuildSignature.js"; import {getLogger} from "@ui5/logger"; const log = getLogger("build:helpers:BuildContext"); import Cache from "../cache/Cache.js"; +import {resolveUi5DataDir} from "../../utils/dataDir.js"; /** * Context of a build process @@ -170,8 +171,9 @@ class BuildContext { if (this.#cacheManager) { return this.#cacheManager; } - this.#cacheManager = await CacheManager.create(this._graph.getRoot().getRootPath(), { - ui5DataDir: this._ui5DataDir, + this.#cacheManager = await CacheManager.create({ + ui5DataDir: this._ui5DataDir ?? + await resolveUi5DataDir({projectRootPath: this.getRootProject().getRootPath()}), }); return this.#cacheManager; } From bdae59f203b71158130e6b438f00c3a47ed55379 Mon Sep 17 00:00:00 2001 From: d3xter666 Date: Tue, 21 Jul 2026 14:16:56 +0300 Subject: [PATCH 15/17] refactor!: Require ui5DataDir for *Resolve files --- .../documentation/docs/updates/migrate-v5.md | 42 +++++++++++++++++++ .../lib/ui5Framework/AbstractResolver.js | 22 +++++----- .../lib/ui5Framework/Openui5Resolver.js | 4 +- .../Sapui5MavenSnapshotResolver.js | 4 +- .../lib/ui5Framework/Sapui5Resolver.js | 7 ++-- .../test/lib/ui5framework/AbstractResolver.js | 42 +++++++++---------- .../test/lib/ui5framework/Openui5Resolver.js | 6 ++- .../Sapui5MavenSnapshotResolver.js | 17 +++++--- .../test/lib/ui5framework/Sapui5Resolver.js | 9 ++-- 9 files changed, 103 insertions(+), 50 deletions(-) diff --git a/internal/documentation/docs/updates/migrate-v5.md b/internal/documentation/docs/updates/migrate-v5.md index c9db7b58dac..bbb66f25ac5 100644 --- a/internal/documentation/docs/updates/migrate-v5.md +++ b/internal/documentation/docs/updates/migrate-v5.md @@ -27,6 +27,8 @@ Or update your global install via: `npm i --global @ui5/cli@next` - **@ui5/cli: `ui5 serve` renders a status banner in interactive terminals** +- **@ui5/project: UI5 framework resolver constructors now require explicit `ui5DataDir`** + ## Node.js and npm Version Support @@ -107,6 +109,46 @@ If you previously passed any of these options to a command that did not use them The `ui5 init` command now generates projects with Specification Version 5.0 by default. +## Changes to @ui5/project (Node.js API) + +When consuming the Node.js API, UI5 framework resolver constructors now require the `ui5DataDir` option. +This affects `Openui5Resolver`, `Sapui5Resolver`, and `Sapui5MavenSnapshotResolver`. + +Previously, `ui5DataDir` was optional and resolver constructors implicitly resolved a fallback from +environment/configuration. In UI5 CLI v5, callers must resolve the UI5 data directory before constructing a +resolver and pass it explicitly. This change improves API clarity by making the dependency explicit. + +Use [`resolveUi5DataDir`](../api/module-@ui5_project_utils_dataDir.md) to resolve the path once at your async +entry boundary and forward the resolved value to all APIs that need it. + +::: code-group +```js [Before] +import Sapui5Resolver from "@ui5/project/ui5Framework/Sapui5Resolver"; + +const resolver = new Sapui5Resolver({ + cwd: process.cwd(), + version: "1.120.15" +}); +``` + +```js [After] +import Sapui5Resolver from "@ui5/project/ui5Framework/Sapui5Resolver"; +import {resolveUi5DataDir} from "@ui5/project/utils/dataDir"; + +async function createResolver() { + const ui5DataDir = await resolveUi5DataDir({projectRootPath: process.cwd()}); + + const resolver = new Sapui5Resolver({ + cwd: process.cwd(), + version: "1.120.15", + ui5DataDir + }); + + return resolver; +} +``` +::: + ## Component Type The `component` type feature aims to introduce a new project type within the UI5 CLI ecosystem to support the development of UI5 component-like applications intended to run in container apps such as the Fiori Launchpad (FLP) Sandbox or testsuite environments. diff --git a/packages/project/lib/ui5Framework/AbstractResolver.js b/packages/project/lib/ui5Framework/AbstractResolver.js index 266d4bcd1a6..fb2aaf0c46e 100644 --- a/packages/project/lib/ui5Framework/AbstractResolver.js +++ b/packages/project/lib/ui5Framework/AbstractResolver.js @@ -1,5 +1,4 @@ import path from "node:path"; -import os from "node:os"; import {getLogger} from "@ui5/logger"; const log = getLogger("ui5Framework:AbstractResolver"); import semver from "semver"; @@ -26,8 +25,8 @@ class AbstractResolver { * @param {boolean} [options.sources=false] Whether to install framework libraries as sources or * pre-built (with build manifest) * @param {string} [options.cwd=process.cwd()] Current working directory - * @param {string} [options.ui5DataDir="~/.ui5"] UI5 home directory location. This will be used to store packages, - * metadata and configuration used by the resolvers. Relative to `process.cwd()` + * @param {string} options.ui5DataDir Resolved UI5 home directory location. This is used to store + * metadata and packages used by the resolvers and must be resolved by the caller. * @param {object.} [options.providedLibraryMetadata] * Resolver skips installing listed libraries and uses the dependency information to resolve their dependencies. * version can be omitted in case all libraries can be resolved via the providedLibraryMetadata. @@ -38,12 +37,12 @@ class AbstractResolver { if (new.target === AbstractResolver) { throw new TypeError("Class 'AbstractResolver' is abstract"); } + if (!ui5DataDir) { + const resolverName = new.target?.name || "AbstractResolver"; + throw new Error(`${resolverName}: Missing parameter "ui5DataDir"`); + } - // In some CI environments, the homedir might be set explicitly to a relative - // path (e.g. "./"), but tooling requires an absolute path - this._ui5DataDir = path.resolve( - ui5DataDir || path.join(os.homedir(), ".ui5") - ); + this._ui5DataDir = path.resolve(ui5DataDir); this._cwd = cwd ? path.resolve(cwd) : process.cwd(); this._version = version; @@ -174,9 +173,12 @@ class AbstractResolver { * Installs the provided libraries and their dependencies * * ```js - * const resolver = new Sapui5Resolver({version: "1.76.0"}); + * const resolver = new Sapui5Resolver({ + * version: "1.76.0", + * ui5DataDir: "/path/to/.ui5" + * }); * // Or for OpenUI5: - * // const resolver = new Openui5Resolver({version: "1.76.0"}); + * // const resolver = new Openui5Resolver({version: "1.76.0", ui5DataDir: "/path/to/.ui5"}); * * resolver.install(["sap.ui.core", "sap.m"]).then(({libraryMetadata}) => { * // Installation done diff --git a/packages/project/lib/ui5Framework/Openui5Resolver.js b/packages/project/lib/ui5Framework/Openui5Resolver.js index a6a9c4fc02a..72eefe02a3e 100644 --- a/packages/project/lib/ui5Framework/Openui5Resolver.js +++ b/packages/project/lib/ui5Framework/Openui5Resolver.js @@ -18,8 +18,8 @@ class Openui5Resolver extends AbstractResolver { * @param {*} options options * @param {string} options.version OpenUI5 version to use * @param {string} [options.cwd=process.cwd()] Working directory to resolve configurations like .npmrc - * @param {string} [options.ui5DataDir="~/.ui5"] UI5 home directory location. This will be used to store packages, - * metadata and configuration used by the resolvers. Relative to `process.cwd()` + * @param {string} options.ui5DataDir Resolved UI5 home directory location. This is used to + * store metadata and packages used by the resolvers. * @param {string} [options.cacheDir] Where to store temp/cached packages. * @param {string} [options.packagesDir] Where to install packages * @param {string} [options.stagingDir] The staging directory for the packages diff --git a/packages/project/lib/ui5Framework/Sapui5MavenSnapshotResolver.js b/packages/project/lib/ui5Framework/Sapui5MavenSnapshotResolver.js index 01c4b843541..c56b4f7cba0 100644 --- a/packages/project/lib/ui5Framework/Sapui5MavenSnapshotResolver.js +++ b/packages/project/lib/ui5Framework/Sapui5MavenSnapshotResolver.js @@ -32,8 +32,8 @@ class Sapui5MavenSnapshotResolver extends AbstractResolver { * @param {boolean} [options.sources=false] Whether to install framework libraries as sources or * pre-built (with build manifest) * @param {string} [options.cwd=process.cwd()] Current working directory - * @param {string} [options.ui5DataDir="~/.ui5"] UI5 home directory location. This will be used to store packages, - * metadata and configuration used by the resolvers. Relative to `process.cwd()` + * @param {string} options.ui5DataDir Resolved UI5 home directory location. This is used to + * store metadata and packages used by the resolvers. * @param {module:@ui5/project/ui5Framework/maven/SnapshotCache} [options.snapshotCache=Default] * Snapshot cache mode to use */ diff --git a/packages/project/lib/ui5Framework/Sapui5Resolver.js b/packages/project/lib/ui5Framework/Sapui5Resolver.js index 300020dce25..0593cf734d7 100644 --- a/packages/project/lib/ui5Framework/Sapui5Resolver.js +++ b/packages/project/lib/ui5Framework/Sapui5Resolver.js @@ -21,8 +21,8 @@ class Sapui5Resolver extends AbstractResolver { * @param {*} options options * @param {string} options.version SAPUI5 version to use * @param {string} [options.cwd=process.cwd()] Working directory to resolve configurations like .npmrc - * @param {string} [options.ui5DataDir="~/.ui5"] UI5 home directory location. This will be used to store packages, - * metadata and configuration used by the resolvers. Relative to `process.cwd()` + * @param {string} options.ui5DataDir Resolved UI5 home directory location. This is used to + * store metadata and packages used by the resolvers. * @param {string} [options.cacheDir] Where to store temp/cached packages. * @param {string} [options.packagesDir] Where to install packages * @param {string} [options.stagingDir] The staging directory for packages @@ -75,7 +75,8 @@ class Sapui5Resolver extends AbstractResolver { const {default: Openui5Resolver} = await import("./Openui5Resolver.js"); const openui5Resolver = new Openui5Resolver({ cwd: this._cwd, - version: metadata.version + version: metadata.version, + ui5DataDir: this._ui5DataDir }); const openui5Metadata = await openui5Resolver.getLibraryMetadata(libraryName); return { diff --git a/packages/project/test/lib/ui5framework/AbstractResolver.js b/packages/project/test/lib/ui5framework/AbstractResolver.js index 7156df0a545..fe3065433d6 100644 --- a/packages/project/test/lib/ui5framework/AbstractResolver.js +++ b/packages/project/test/lib/ui5framework/AbstractResolver.js @@ -1,18 +1,19 @@ import test from "ava"; import sinon from "sinon"; import path from "node:path"; -import os from "node:os"; import esmock from "esmock"; test.beforeEach(async (t) => { - t.context.osHomeDirStub = sinon.stub().callsFake(() => os.homedir()); - t.context.AbstractResolver = await esmock.p("../../../lib/ui5Framework/AbstractResolver.js", { - "node:os": { - homedir: t.context.osHomeDirStub - } - }); + t.context.AbstractResolver = await esmock.p("../../../lib/ui5Framework/AbstractResolver.js", {}); class MyResolver extends t.context.AbstractResolver { + constructor(options = {}) { + super({ + ui5DataDir: "/ui5DataDir", + ...options + }); + } + static async fetchAllVersions() {} } @@ -118,24 +119,19 @@ test("AbstractResolver: Set relative 'ui5DataDir'", (t) => { t.is(resolver._ui5DataDir, path.resolve("./my-ui5DataDir"), "Should be resolved 'ui5DataDir'"); }); -test("AbstractResolver: 'ui5DataDir' overriden os.homedir()", (t) => { - const {MyResolver, osHomeDirStub} = t.context; - - osHomeDirStub.returns("./"); +test("AbstractResolver: constructor without 'ui5DataDir' should throw", (t) => { + const {AbstractResolver} = t.context; - const resolver = new MyResolver({ - version: "1.75.0" - }); - t.is(resolver._ui5DataDir, path.resolve("./.ui5"), "Should be resolved 'ui5DataDir'"); -}); + class MyResolverWithoutDefaults extends AbstractResolver { + static async fetchAllVersions() {} + } -test("AbstractResolver: Defaults 'ui5DataDir' to ~/.ui5", (t) => { - const {MyResolver} = t.context; - const resolver = new MyResolver({ - version: "1.75.0", - cwd: "/test-project/" - }); - t.is(resolver._ui5DataDir, path.join(os.homedir(), ".ui5"), "Should default to ~/.ui5"); + t.throws(() => { + new MyResolverWithoutDefaults({ + version: "1.75.0", + cwd: "/test-project/" + }); + }, {message: "MyResolverWithoutDefaults: Missing parameter \"ui5DataDir\""}); }); test("AbstractResolver: getLibraryMetadata should throw an Error when not implemented", async (t) => { diff --git a/packages/project/test/lib/ui5framework/Openui5Resolver.js b/packages/project/test/lib/ui5framework/Openui5Resolver.js index 34d756987da..278a4077d74 100644 --- a/packages/project/test/lib/ui5framework/Openui5Resolver.js +++ b/packages/project/test/lib/ui5framework/Openui5Resolver.js @@ -44,7 +44,8 @@ test.serial("Openui5Resolver: getLibraryMetadata", async (t) => { const resolver = new Openui5Resolver({ cwd: "/test-project/", - version: "1.75.0" + version: "1.75.0", + ui5DataDir: "/ui5DataDir" }); t.context.fetchPackageManifestStub @@ -104,7 +105,8 @@ test.serial("Openui5Resolver: handleLibrary", async (t) => { const resolver = new Openui5Resolver({ cwd: "/test-project/", - version: "1.75.0" + version: "1.75.0", + ui5DataDir: "/ui5DataDir" }); const getLibraryMetadataStub = sinon.stub(resolver, "getLibraryMetadata"); diff --git a/packages/project/test/lib/ui5framework/Sapui5MavenSnapshotResolver.js b/packages/project/test/lib/ui5framework/Sapui5MavenSnapshotResolver.js index ed9a4de6cd9..a88836ae2fd 100644 --- a/packages/project/test/lib/ui5framework/Sapui5MavenSnapshotResolver.js +++ b/packages/project/test/lib/ui5framework/Sapui5MavenSnapshotResolver.js @@ -63,7 +63,8 @@ test.serial( const resolver = new Sapui5MavenSnapshotResolver({ cwd: "/test-project/", - version: "1.75.0" + version: "1.75.0", + ui5DataDir: "/ui5DataDir" }); const expectedMetadata = { @@ -114,7 +115,8 @@ test.serial("Sapui5MavenSnapshotResolver: getLibraryMetadata throws", async (t) const resolver = new Sapui5MavenSnapshotResolver({ cwd: "/test-project/", - version: "1.75.0" + version: "1.75.0", + ui5DataDir: "/ui5DataDir" }); const loadDistMetadataStub = sinon.stub(resolver, "loadDistMetadata"); @@ -132,7 +134,8 @@ test.serial("Sapui5MavenSnapshotResolver: handleLibrary", async (t) => { const resolver = new Sapui5MavenSnapshotResolver({ cwd: "/test-project/", - version: "1.116.0-SNAPSHOT" + version: "1.116.0-SNAPSHOT", + ui5DataDir: "/ui5DataDir" }); const loadDistMetadataStub = sinon.stub(resolver, "loadDistMetadata"); @@ -186,7 +189,8 @@ test.serial("Sapui5MavenSnapshotResolver: handleLibrary - legacy version", async const resolver = new Sapui5MavenSnapshotResolver({ cwd: "/test-project/", - version: "1.75.0-SNAPSHOT" + version: "1.75.0-SNAPSHOT", + ui5DataDir: "/ui5DataDir" }); const loadDistMetadataStub = sinon.stub(resolver, "loadDistMetadata"); @@ -240,6 +244,7 @@ test.serial("Sapui5MavenSnapshotResolver: handleLibrary - sources requested", as const resolver = new Sapui5MavenSnapshotResolver({ cwd: "/test-project/", version: "1.116.0-SNAPSHOT", + ui5DataDir: "/ui5DataDir", sources: true }); @@ -294,6 +299,7 @@ test.serial("Sapui5MavenSnapshotResolver: handleLibrary - sources requested with const resolver = new Sapui5MavenSnapshotResolver({ cwd: "/test-project/", version: "1.75.0-SNAPSHOT", + ui5DataDir: "/ui5DataDir", sources: true }); @@ -347,7 +353,8 @@ test.serial("Sapui5MavenSnapshotResolver: handleLibrary throws", async (t) => { const resolver = new Sapui5MavenSnapshotResolver({ cwd: "/test-project/", - version: "1.75.0" + version: "1.75.0", + ui5DataDir: "/ui5DataDir" }); sinon.stub(resolver, "getLibraryMetadata").resolves({}); diff --git a/packages/project/test/lib/ui5framework/Sapui5Resolver.js b/packages/project/test/lib/ui5framework/Sapui5Resolver.js index 63288d356ed..99c5bad9c44 100644 --- a/packages/project/test/lib/ui5framework/Sapui5Resolver.js +++ b/packages/project/test/lib/ui5framework/Sapui5Resolver.js @@ -37,7 +37,8 @@ test.serial( const resolver = new Sapui5Resolver({ cwd: "/test-project/", - version: "1.75.0" + version: "1.75.0", + ui5DataDir: "/ui5DataDir" }); t.context.getTargetDirForPackageStub.callsFake(({pkgName, version}) => { @@ -88,7 +89,8 @@ test.serial("Sapui5Resolver: handleLibrary", async (t) => { const resolver = new Sapui5Resolver({ cwd: "/test-project/", - version: "1.75.0" + version: "1.75.0", + ui5DataDir: "/ui5DataDir" }); const loadDistMetadataStub = sinon.stub(resolver, "loadDistMetadata"); @@ -207,7 +209,8 @@ test.serial( const resolver = new Sapui5Resolver({ cwd: "/test-project/", - version: "1.77.7" + version: "1.77.7", + ui5DataDir: "/ui5DataDir" }); const openui5LibraryMetadata = { From 410bcc5b180c36784d779e14b9d234eb795b5cc7 Mon Sep 17 00:00:00 2001 From: d3xter666 Date: Thu, 23 Jul 2026 14:03:09 +0300 Subject: [PATCH 16/17] feat: Make dataDir a required param for public APIs --- .../documentation/docs/updates/migrate-v5.md | 27 ++ packages/builder/test/lib/builder/builder.js | 49 +++ .../builder/test/lib/builder/sourceMaps.js | 1 + .../generateLibraryPreload.integration.js | 5 + ...generateStandaloneAppBundle.integration.js | 3 + .../test/lib/tasks/generateCachebusterInfo.js | 2 + packages/cli/lib/cli/commands/build.js | 5 + packages/cli/lib/cli/commands/serve.js | 5 + packages/cli/lib/cli/commands/tree.js | 4 + packages/cli/lib/framework/utils.js | 13 +- packages/cli/test/lib/cli/commands/build.js | 15 + packages/cli/test/lib/cli/commands/serve.js | 19 +- packages/cli/test/lib/cli/commands/tree.js | 28 +- packages/cli/test/lib/framework/utils.js | 10 +- packages/project/lib/build/ProjectBuilder.js | 2 +- .../project/lib/build/cache/CacheManager.js | 19 +- .../project/lib/build/helpers/BuildContext.js | 11 +- packages/project/lib/graph/ProjectGraph.js | 12 +- packages/project/lib/graph/graph.js | 30 +- .../project/lib/graph/helpers/ui5Framework.js | 20 +- .../lib/ui5Framework/AbstractResolver.js | 19 +- .../lib/ui5Framework/Openui5Resolver.js | 18 +- .../Sapui5MavenSnapshotResolver.js | 9 +- .../lib/ui5Framework/Sapui5Resolver.js | 18 +- .../test/lib/build/BuildServer.integration.js | 1 + .../lib/build/ProjectBuilder.integration.js | 1 + .../test/lib/build/helpers/BuildContext.js | 4 +- .../lib/build/helpers/composeProjectList.js | 4 +- .../test/lib/graph/graph.integration.js | 17 +- packages/project/test/lib/graph/graph.js | 45 ++- .../project/test/lib/graph/graphFromObject.js | 61 ++- .../lib/graph/graphFromPackageDependencies.js | 10 +- .../test/lib/graph/graphFromStaticFile.js | 7 + .../graph/helpers/ui5Framework.integration.js | 18 +- .../test/lib/graph/helpers/ui5Framework.js | 58 +-- .../test/lib/ui5framework/AbstractResolver.js | 371 +++++++----------- .../Openui5Resolver.integration.js | 44 ++- .../test/lib/ui5framework/Openui5Resolver.js | 35 +- ...Sapui5MavenSnapshotResolver.integration.js | 42 +- .../Sapui5MavenSnapshotResolver.js | 28 +- .../Sapui5Resolver.integration.js | 44 ++- .../test/lib/ui5framework/Sapui5Resolver.js | 35 +- packages/server/lib/server.js | 7 +- .../lib/server/acceptRemoteConnections.js | 4 +- packages/server/test/lib/server/caching.js | 4 +- packages/server/test/lib/server/h2.js | 4 +- packages/server/test/lib/server/main.js | 20 +- packages/server/test/lib/server/ports.js | 18 +- packages/server/test/lib/server/server.js | 18 +- 49 files changed, 724 insertions(+), 520 deletions(-) diff --git a/internal/documentation/docs/updates/migrate-v5.md b/internal/documentation/docs/updates/migrate-v5.md index bbb66f25ac5..55ca44fc58c 100644 --- a/internal/documentation/docs/updates/migrate-v5.md +++ b/internal/documentation/docs/updates/migrate-v5.md @@ -29,6 +29,8 @@ Or update your global install via: `npm i --global @ui5/cli@next` - **@ui5/project: UI5 framework resolver constructors now require explicit `ui5DataDir`** +- **@ui5/project: UI5 framework resolver static APIs now require explicit `ui5DataDir`** + ## Node.js and npm Version Support @@ -114,6 +116,9 @@ The `ui5 init` command now generates projects with Specification Version 5.0 by When consuming the Node.js API, UI5 framework resolver constructors now require the `ui5DataDir` option. This affects `Openui5Resolver`, `Sapui5Resolver`, and `Sapui5MavenSnapshotResolver`. +The same requirement now applies to static resolver APIs (for example `resolveVersion`, `fetchAllVersions`, and +`fetchAllTags`). Calls without `ui5DataDir` now throw and no longer fall back to implicit default paths. + Previously, `ui5DataDir` was optional and resolver constructors implicitly resolved a fallback from environment/configuration. In UI5 CLI v5, callers must resolve the UI5 data directory before constructing a resolver and pass it explicitly. This change improves API clarity by making the dependency explicit. @@ -149,6 +154,28 @@ async function createResolver() { ``` ::: +For static APIs, pass the same resolved value as the second argument: + +::: code-group +```js [Before] +import Sapui5Resolver from "@ui5/project/ui5Framework/Sapui5Resolver"; + +const version = await Sapui5Resolver.resolveVersion("latest"); +``` + +```js [After] +import Sapui5Resolver from "@ui5/project/ui5Framework/Sapui5Resolver"; +import {resolveUi5DataDir} from "@ui5/project/utils/dataDir"; + +async function resolveFrameworkVersion(projectRootPath) { + const ui5DataDir = await resolveUi5DataDir({projectRootPath}); + return Sapui5Resolver.resolveVersion("latest", ui5DataDir, { + cwd: projectRootPath + }); +} +``` +::: + ## Component Type The `component` type feature aims to introduce a new project type within the UI5 CLI ecosystem to support the development of UI5 component-like applications intended to run in container apps such as the Fiori Launchpad (FLP) Sandbox or testsuite environments. diff --git a/packages/builder/test/lib/builder/builder.js b/packages/builder/test/lib/builder/builder.js index cf0f0c0c46b..69331bc7982 100644 --- a/packages/builder/test/lib/builder/builder.js +++ b/packages/builder/test/lib/builder/builder.js @@ -97,6 +97,7 @@ test.serial("Build application.a", async (t) => { const expectedPath = path.join("test", "expected", "build", "application.a", "dest"); const graph = await graphFromPackageDependencies({ + ui5DataDir: "/path/to/ui5-data-dir", cwd: applicationAPath }); graph.setTaskRepository(taskRepository); @@ -119,6 +120,7 @@ test.serial("Build application.a with dependencies", async (t) => { const expectedPath = path.join("test", "expected", "build", "application.a", "dest-deps"); const graph = await graphFromObject({ + ui5DataDir: "/path/to/ui5-data-dir", dependencyTree: applicationATree }); graph.setTaskRepository(taskRepository); @@ -145,6 +147,7 @@ test.serial("Build application.a with dependencies exclude", async (t) => { const expectedPath = path.join("test", "expected", "build", "application.a", "dest-deps-excl"); const graph = await graphFromObject({ + ui5DataDir: "/path/to/ui5-data-dir", dependencyTree: applicationATree }); graph.setTaskRepository(taskRepository); @@ -172,6 +175,7 @@ test.serial("Build application.a self-contained", async (t) => { const expectedPath = path.join("test", "expected", "build", "application.a", "dest-self"); const graph = await graphFromObject({ + ui5DataDir: "/path/to/ui5-data-dir", dependencyTree: applicationATree }); graph.setTaskRepository(taskRepository); @@ -195,6 +199,7 @@ test.serial("Build application.a with dependencies self-contained", async (t) => const expectedPath = path.join("test", "expected", "build", "application.a", "dest-depself"); const graph = await graphFromObject({ + ui5DataDir: "/path/to/ui5-data-dir", dependencyTree: applicationATree }); graph.setTaskRepository(taskRepository); @@ -221,21 +226,26 @@ test.serial("Build application.a and clean target path", async (t) => { const destPath = "./test/tmp/build/application.a/dest-clean"; const destPathRubbishSubFolder = destPath + "/rubbish-should-be-deleted"; const expectedPath = path.join("test", "expected", "build", "application.a", "dest-clean"); + const ui5DataDir = isolatedUi5DataDir(t); const graph1 = await graphFromObject({ + ui5DataDir: "/path/to/ui5-data-dir", dependencyTree: applicationATree }); const graph2 = await graphFromObject({ + ui5DataDir: "/path/to/ui5-data-dir", dependencyTree: applicationATree }); graph1.setTaskRepository(taskRepository); await graph1.build({ + ui5DataDir, graph: graph1, destPath: destPathRubbishSubFolder, excludedTasks: ["*"] }); graph2.setTaskRepository(taskRepository); await graph2.build({ + ui5DataDir, graph: graph2, destPath, cleanDest: true, @@ -255,6 +265,7 @@ test.serial("Build application.g", async (t) => { const expectedPath = path.join("test", "expected", "build", "application.g", "dest"); const graph = await graphFromObject({ + ui5DataDir: "/path/to/ui5-data-dir", dependencyTree: applicationGTree }); graph.setTaskRepository(taskRepository); @@ -277,6 +288,7 @@ test.serial("Build application.g with component preload paths", async (t) => { const expectedPath = path.join("test", "expected", "build", "application.g", "dest"); const graph = await graphFromObject({ + ui5DataDir: "/path/to/ui5-data-dir", dependencyTree: applicationGTreeComponentPreloadPaths }); graph.setTaskRepository(taskRepository); @@ -299,6 +311,7 @@ test.serial("Build application.g with excludes", async (t) => { const expectedPath = path.join("test", "expected", "build", "application.g", "excludes"); const graph = await graphFromObject({ + ui5DataDir: "/path/to/ui5-data-dir", dependencyTree: applicationGTreeWithExcludes }); graph.setTaskRepository(taskRepository); @@ -321,6 +334,7 @@ test.serial("Build application.h", async (t) => { const expectedPath = path.join("test", "expected", "build", "application.h", "dest"); const graph = await graphFromObject({ + ui5DataDir: "/path/to/ui5-data-dir", dependencyTree: applicationHTree }); graph.setTaskRepository(taskRepository); @@ -344,6 +358,7 @@ test.serial("Build application.h (no minify)", async (t) => { const expectedPath = path.join("test", "expected", "build", "application.h", "no-minify"); const graph = await graphFromObject({ + ui5DataDir: "/path/to/ui5-data-dir", dependencyTree: applicationHTree }); graph.setTaskRepository(taskRepository); @@ -367,6 +382,7 @@ test.serial("Build application.i", async (t) => { const expectedPath = path.join("test", "expected", "build", "application.i", "dest"); const graph = await graphFromObject({ + ui5DataDir: "/path/to/ui5-data-dir", dependencyTree: applicationITree }); graph.setTaskRepository(taskRepository); @@ -389,6 +405,7 @@ test.serial("Build application.j", async (t) => { const expectedPath = path.join("test", "expected", "build", "application.j", "dest"); const graph = await graphFromObject({ + ui5DataDir: "/path/to/ui5-data-dir", dependencyTree: applicationJTree }); graph.setTaskRepository(taskRepository); @@ -417,6 +434,7 @@ test.serial("Build application.j with resources.json and version info", async (t sinon.stub(Date.prototype, "getMinutes").returns(17); const graph = await graphFromObject({ + ui5DataDir: "/path/to/ui5-data-dir", dependencyTree: applicationJTree }); graph.setTaskRepository(taskRepository); @@ -439,6 +457,7 @@ test.serial("Build application.k (componentPreload excludes)", async (t) => { const expectedPath = path.join("test", "expected", "build", "application.k", "dest"); const graph = await graphFromObject({ + ui5DataDir: "/path/to/ui5-data-dir", dependencyTree: applicationKTree }); graph.setTaskRepository(taskRepository); @@ -462,6 +481,7 @@ test.serial("Build application.k (package sub-components / componentPreload excl const expectedPath = path.join("test", "expected", "build", "application.k", "dest-package-subcomponents"); const graph = await graphFromObject({ + ui5DataDir: "/path/to/ui5-data-dir", dependencyTree: applicationKPackageSubcomponentsTree }); graph.setTaskRepository(taskRepository); @@ -485,6 +505,7 @@ test.serial("Build application.l: minification excludes, w/ namespace", async (t const expectedPath = path.join("test", "expected", "build", "application.l", "dest"); const graph = await graphFromObject({ + ui5DataDir: "/path/to/ui5-data-dir", dependencyTree: applicationLTree }); graph.setTaskRepository(taskRepository); @@ -507,6 +528,7 @@ test.serial("Build application.m: bundle should not contain hashbang but an empt const expectedPath = path.join("test", "expected", "build", "application.m", "dest"); const graph = await graphFromPackageDependencies({ + ui5DataDir: "/path/to/ui5-data-dir", cwd: applicationMPath }); graph.setTaskRepository(taskRepository); @@ -528,6 +550,7 @@ test.serial("Build application.ø", async (t) => { const expectedPath = path.join("test", "expected", "build", "application.ø", "dest"); const graph = await graphFromObject({ + ui5DataDir: "/path/to/ui5-data-dir", dependencyTree: applicationØTree }); graph.setTaskRepository(taskRepository); @@ -550,6 +573,7 @@ test.serial("Build library.d with copyright from .library file", async (t) => { const expectedPath = path.join("test", "expected", "build", "library.d", "dest"); const graph = await graphFromObject({ + ui5DataDir: "/path/to/ui5-data-dir", dependencyTree: libraryDTree }); graph.setTaskRepository(taskRepository); @@ -572,6 +596,7 @@ test.serial("Build library.e with copyright from metadata configuration of tree" const expectedPath = path.join("test", "expected", "build", "library.e", "dest"); const graph = await graphFromObject({ + ui5DataDir: "/path/to/ui5-data-dir", dependencyTree: libraryETree }); graph.setTaskRepository(taskRepository); @@ -598,6 +623,7 @@ test.serial("Build library.e with build manifest", async (t) => { // Stub date because of timestamp in build-manifest.json const toISOStringStub = sinon.stub(Date.prototype, "toISOString").returns("2022-07-27T09:00:00.000Z"); const graph = await graphFromObject({ + ui5DataDir: "/path/to/ui5-data-dir", dependencyTree: libraryETree }); graph.setTaskRepository(taskRepository); @@ -674,6 +700,7 @@ test.serial("Build library.h with custom bundles and component-preloads", async const expectedPath = path.join("test", "expected", "build", "library.h", "dest"); const graph = await graphFromObject({ + ui5DataDir: "/path/to/ui5-data-dir", dependencyTree: libraryHTree }); graph.setTaskRepository(taskRepository); @@ -696,6 +723,7 @@ test.serial("Build library.h with custom bundles and component-preloads (no mini const expectedPath = path.join("test", "expected", "build", "library.h", "no-minify"); const graph = await graphFromObject({ + ui5DataDir: "/path/to/ui5-data-dir", dependencyTree: libraryHTree }); graph.setTaskRepository(taskRepository); @@ -722,6 +750,7 @@ test.serial("Build library.h w/ custom bundles, component-preloads, resources.js // Stub date because of timestamp in build-manifest.json const toISOStringStub = sinon.stub(Date.prototype, "toISOString").returns("2022-07-27T09:00:00.000Z"); const graph = await graphFromObject({ + ui5DataDir: "/path/to/ui5-data-dir", dependencyTree: libraryHTree }); graph.setTaskRepository(taskRepository); @@ -872,6 +901,7 @@ test.serial("Build library.i with manifest info taken from .library and library. const expectedPath = path.join("test", "expected", "build", "library.i", "dest"); const graph = await graphFromObject({ + ui5DataDir: "/path/to/ui5-data-dir", dependencyTree: libraryITree }); graph.setTaskRepository(taskRepository); @@ -894,6 +924,7 @@ test.serial("Build library.j with JSDoc build only", async (t) => { const expectedPath = path.join("test", "expected", "build", "library.j", "dest"); const graph = await graphFromObject({ + ui5DataDir: "/path/to/ui5-data-dir", dependencyTree: libraryJTree }); graph.setTaskRepository(taskRepository); @@ -921,6 +952,7 @@ test.serial("Build library.i, bundling library.h", async (t) => { const expectedPath = path.join("test", "expected", "build", "library.i", "bundle-library.h"); const graph = await graphFromObject({ + ui5DataDir: "/path/to/ui5-data-dir", dependencyTree: libraryIBundlingHTree }); graph.setTaskRepository(taskRepository); @@ -944,15 +976,18 @@ test.serial("Build library.i, bundling library.h with build manifest", async (t) const expectedPath = path.join("test", "expected", "build", "library.i", "bundle-library.h-build-manifest"); const resultBuildManifestPath = path.join(__dirname, "..", "..", "tmp", "build", "library.i", "bundle-library.h-build-manifest", ".ui5", "build-manifest.json"); + const ui5DataDir = isolatedUi5DataDir(t); setLogLevel("verbose"); const graph1 = await graphFromObject({ + ui5DataDir: "/path/to/ui5-data-dir", dependencyTree: libraryHTree }); graph1.setTaskRepository(taskRepository); await graph1.build({ + ui5DataDir, destPath: libraryHDestPath, createBuildManifest: true }); @@ -964,10 +999,12 @@ test.serial("Build library.i, bundling library.h with build manifest", async (t) // Stub date because of timestamp in build-manifest.json const toISOStringStub = sinon.stub(Date.prototype, "toISOString").returns("2022-07-27T09:00:00.000Z"); const graph2 = await graphFromObject({ + ui5DataDir: "/path/to/ui5-data-dir", dependencyTree: projectTree }); graph2.setTaskRepository(taskRepository); await graph2.build({ + ui5DataDir, destPath, createBuildManifest: true }); @@ -1030,6 +1067,7 @@ test.serial("Build library.l", async (t) => { const expectedPath = path.join("test", "expected", "build", "library.l", "dest"); const graph = await graphFromObject({ + ui5DataDir: "/path/to/ui5-data-dir", dependencyTree: libraryLTree }); graph.setTaskRepository(taskRepository); @@ -1052,6 +1090,7 @@ test.serial("Build theme.j even without an library", async (t) => { const expectedPath = "./test/expected/build/theme.j/dest"; const graph = await graphFromObject({ + ui5DataDir: "/path/to/ui5-data-dir", dependencyTree: themeJTree }); graph.setTaskRepository(taskRepository); @@ -1073,6 +1112,7 @@ test.serial("Build theme.j even without an library with resources.json", async ( const expectedPath = "./test/expected/build/theme.j/dest-resources-json"; const graph = await graphFromObject({ + ui5DataDir: "/path/to/ui5-data-dir", dependencyTree: themeJTree }); graph.setTaskRepository(taskRepository); @@ -1101,6 +1141,7 @@ test.serial("Build theme.j with build manifest", async (t) => { // Stub date because of timestamp in build-manifest.json const toISOStringStub = sinon.stub(Date.prototype, "toISOString").returns("2022-07-27T09:00:00.000Z"); const graph = await graphFromObject({ + ui5DataDir: "/path/to/ui5-data-dir", dependencyTree: themeJTree }); graph.setTaskRepository(taskRepository); @@ -1158,6 +1199,7 @@ test.serial("Build library.ø", async (t) => { const expectedPath = path.join("test", "expected", "build", "library.ø", "dest"); const graph = await graphFromObject({ + ui5DataDir: "/path/to/ui5-data-dir", dependencyTree: libraryØTree }); graph.setTaskRepository(taskRepository); @@ -1187,6 +1229,7 @@ test.serial("Build library.coreBuildtime: replaceBuildtime", async (t) => { ]; const graph = await graphFromObject({ + ui5DataDir: "/path/to/ui5-data-dir", dependencyTree: libraryCoreBuildtimeTree }); graph.setTaskRepository(taskRepository); @@ -1211,6 +1254,7 @@ test.serial("Build library with theme configured for CSS variables", async (t) = const expectedPath = "./test/expected/build/theme.j/dest-css-variables"; const graph = await graphFromObject({ + ui5DataDir: "/path/to/ui5-data-dir", dependencyTree: themeJTree }); graph.setTaskRepository(taskRepository); @@ -1233,6 +1277,7 @@ test.serial("Build library with theme configured for CSS variables and theme des const expectedPath = "./test/expected/build/theme.j/dest-css-variables-theme-designer-resources"; const graph = await graphFromObject({ + ui5DataDir: "/path/to/ui5-data-dir", dependencyTree: themeJTree }); graph.setTaskRepository(taskRepository); @@ -1256,6 +1301,7 @@ test.serial("Build theme-library with CSS variables", async (t) => { const expectedPath = "./test/expected/build/theme.library.e/dest-css-variables"; const graph = await graphFromObject({ + ui5DataDir: "/path/to/ui5-data-dir", dependencyTree: themeLibraryETree }); graph.setTaskRepository(taskRepository); @@ -1278,6 +1324,7 @@ test.serial("Build theme-library with CSS variables and theme designer resources const expectedPath = "./test/expected/build/theme.library.e/dest-css-variables-theme-designer-resources"; const graph = await graphFromObject({ + ui5DataDir: "/path/to/ui5-data-dir", dependencyTree: themeLibraryETree }); graph.setTaskRepository(taskRepository); @@ -1301,6 +1348,7 @@ test.serial("Build library.o with terminologies and supportedLocales", async (t) const expectedPath = path.join("test", "expected", "build", "library.o", "dest"); const graph = await graphFromPackageDependencies({ + ui5DataDir: "/path/to/ui5-data-dir", cwd: libraryOPath }); graph.setTaskRepository(taskRepository); @@ -1322,6 +1370,7 @@ test.serial("Build application.o with terminologies and supportedLocales", async const expectedPath = path.join("test", "expected", "build", "application.o", "dest"); const graph = await graphFromPackageDependencies({ + ui5DataDir: "/path/to/ui5-data-dir", cwd: applicationOPath }); graph.setTaskRepository(taskRepository); diff --git a/packages/builder/test/lib/builder/sourceMaps.js b/packages/builder/test/lib/builder/sourceMaps.js index 307ae5aee8f..979f3ba029f 100644 --- a/packages/builder/test/lib/builder/sourceMaps.js +++ b/packages/builder/test/lib/builder/sourceMaps.js @@ -60,6 +60,7 @@ test.serial("Verify source maps (test.application)", async (t) => { const destURL = t.context.destURL = new URL("./dest-standard-build/", applicationDestRootURL); const graph = await graphFromPackageDependencies({ + ui5DataDir: "/path/to/ui5-data-dir", cwd: fileURLToPath(applicationURL) }); graph.setTaskRepository(taskRepository); diff --git a/packages/builder/test/lib/tasks/bundlers/generateLibraryPreload.integration.js b/packages/builder/test/lib/tasks/bundlers/generateLibraryPreload.integration.js index c265f581cea..5f64c60c1a7 100644 --- a/packages/builder/test/lib/tasks/bundlers/generateLibraryPreload.integration.js +++ b/packages/builder/test/lib/tasks/bundlers/generateLibraryPreload.integration.js @@ -21,6 +21,7 @@ test.serial("integration: build library.d with library preload", async (t) => { const includedTasks = ["generateLibraryPreload"]; const graph = await graphFromObject({ + ui5DataDir: "/path/to/ui5-data-dir", dependencyTree: libraryDTree }); graph.setTaskRepository(taskRepository); @@ -75,6 +76,7 @@ test.serial("integration: build library.d-minified with library preload", async const includedTasks = ["generateLibraryPreload"]; const graph = await graphFromObject({ + ui5DataDir: "/path/to/ui5-data-dir", dependencyTree: libraryDMinifiedTree }); graph.setTaskRepository(taskRepository); @@ -129,6 +131,7 @@ test.serial("integration: build sap.ui.core with library preload", async (t) => const includedTasks = ["minify", "generateLibraryPreload"]; const graph = await graphFromObject({ + ui5DataDir: "/path/to/ui5-data-dir", dependencyTree: sapUiCoreTree }); graph.setTaskRepository(taskRepository); @@ -333,6 +336,7 @@ test.serial("integration: build library.n without enabled string bundling", asyn const includedTasks = ["generateLibraryPreload"]; const graph = await graphFromObject({ + ui5DataDir: "/path/to/ui5-data-dir", dependencyTree: libraryNTree }); graph.setTaskRepository(taskRepository); @@ -379,6 +383,7 @@ test.serial("integration: build library.n with enabled string bundling", async ( const includedTasks = ["generateLibraryPreload"]; const graph = await graphFromObject({ + ui5DataDir: "/path/to/ui5-data-dir", dependencyTree: libraryNTreeLegacy }); graph.setTaskRepository(taskRepository); diff --git a/packages/builder/test/lib/tasks/bundlers/generateStandaloneAppBundle.integration.js b/packages/builder/test/lib/tasks/bundlers/generateStandaloneAppBundle.integration.js index 2ab851a34c7..9aac122528b 100644 --- a/packages/builder/test/lib/tasks/bundlers/generateStandaloneAppBundle.integration.js +++ b/packages/builder/test/lib/tasks/bundlers/generateStandaloneAppBundle.integration.js @@ -22,6 +22,7 @@ test.serial("integration: build application.b standalone", async (t) => { const includedTasks = ["minify", "generateStandaloneAppBundle"]; const graph = await graphFromObject({ + ui5DataDir: "/path/to/ui5-data-dir", dependencyTree: applicationBTree }); @@ -186,6 +187,7 @@ test("integration: build application.n standalone without enabled string bundlin const includedTasks = ["minify", "generateStandaloneAppBundle"]; const graph = await graphFromObject({ + ui5DataDir: "/path/to/ui5-data-dir", dependencyTree: applicationNTree }); @@ -230,6 +232,7 @@ test("integration: build application.n standalone with enabled string bundling", const includedTasks = ["minify", "generateStandaloneAppBundle"]; const graph = await graphFromObject({ + ui5DataDir: "/path/to/ui5-data-dir", dependencyTree: applicationNTreeLegacy }); diff --git a/packages/builder/test/lib/tasks/generateCachebusterInfo.js b/packages/builder/test/lib/tasks/generateCachebusterInfo.js index 39f4a6d7380..1deaacf6f7b 100644 --- a/packages/builder/test/lib/tasks/generateCachebusterInfo.js +++ b/packages/builder/test/lib/tasks/generateCachebusterInfo.js @@ -19,6 +19,7 @@ test("integration: Build application.g", async (t) => { const cleanupCacheBusterInfo = (fileContent) => fileContent.replace(/(:\s+)(\d+)/g, ": 0"); const graph = await graphFromObject({ + ui5DataDir: "/path/to/ui5-data-dir", dependencyTree: applicationGTree }); @@ -58,6 +59,7 @@ test("integration: Build application.g with cachebuster using hashes", async (t) const cleanupCacheBusterInfo = (fileContent) => fileContent.replace(/(:\s+)("[^"]+")/g, ": \"\""); const graph = await graphFromObject({ + ui5DataDir: "/path/to/ui5-data-dir", dependencyTree: applicationGTreeWithCachebusterHash }); diff --git a/packages/cli/lib/cli/commands/build.js b/packages/cli/lib/cli/commands/build.js index af7d82890c9..af66141dde8 100644 --- a/packages/cli/lib/cli/commands/build.js +++ b/packages/cli/lib/cli/commands/build.js @@ -1,5 +1,6 @@ import baseMiddleware from "../middlewares/base.js"; import {applyProjectConfigOptions, applyWorkspaceOptions, applyBuildOptions, dedupeArray} from "../options.js"; +import {resolveUi5DataDir} from "@ui5/project/utils/dataDir"; import {getLogger} from "@ui5/logger"; const log = getLogger("cli:commands:build"); @@ -184,6 +185,7 @@ build.builder = function(cli) { async function handleBuild(argv) { const {graphFromStaticFile, graphFromPackageDependencies} = await import("@ui5/project/graph"); + const ui5DataDir = await resolveUi5DataDir({projectRootPath: process.cwd()}); const command = argv._[argv._.length - 1]; @@ -194,6 +196,7 @@ async function handleBuild(argv) { rootConfigPath: argv.config, versionOverride: argv.frameworkVersion, snapshotCache: argv.snapshotCache ?? argv.cacheMode ?? "Default", // Use cacheMode as fallback + ui5DataDir, }); } else { graph = await graphFromPackageDependencies({ @@ -202,6 +205,7 @@ async function handleBuild(argv) { snapshotCache: argv.snapshotCache ?? argv.cacheMode ?? "Default", // Use cacheMode as fallback workspaceConfigPath: argv.workspaceConfig, workspaceName: argv.workspace === false ? null : argv.workspace, + ui5DataDir, }); } const buildSettings = graph.getRoot().getBuilderSettings() || {}; @@ -229,6 +233,7 @@ async function handleBuild(argv) { cssVariables: argv["experimental-css-variables"], outputStyle: argv["output-style"], cache: argv["cache"], + ui5DataDir, }); } diff --git a/packages/cli/lib/cli/commands/serve.js b/packages/cli/lib/cli/commands/serve.js index e70924bc53f..fa6c13568fa 100644 --- a/packages/cli/lib/cli/commands/serve.js +++ b/packages/cli/lib/cli/commands/serve.js @@ -3,6 +3,7 @@ import os from "node:os"; import process from "node:process"; import baseMiddleware from "../middlewares/base.js"; import {applyProjectConfigOptions, applyWorkspaceOptions, applyBuildOptions, dedupeArray} from "../options.js"; +import {resolveUi5DataDir} from "@ui5/project/utils/dataDir"; import {getLogger} from "@ui5/logger"; const log = getLogger("cli:commands:serve"); @@ -146,6 +147,7 @@ serve.handler = async function(argv) { }); const {graphFromStaticFile, graphFromPackageDependencies} = await import("@ui5/project/graph"); + const ui5DataDir = await resolveUi5DataDir({projectRootPath: process.cwd()}); let graph; if (argv.dependencyDefinition) { @@ -154,6 +156,7 @@ serve.handler = async function(argv) { rootConfigPath: argv.config, versionOverride: argv.frameworkVersion, snapshotCache: argv.snapshotCache ?? argv.cacheMode ?? "Default", // Use cacheMode as fallback + ui5DataDir, }); } else { graph = await graphFromPackageDependencies({ @@ -162,6 +165,7 @@ serve.handler = async function(argv) { snapshotCache: argv.snapshotCache ?? argv.cacheMode ?? "Default", // Use cacheMode as fallback workspaceConfigPath: argv.workspaceConfig, workspaceName: argv.workspace === false ? null : argv.workspace, + ui5DataDir, }); } @@ -210,6 +214,7 @@ serve.handler = async function(argv) { cache: argv.cache, includedTasks: argv["include-task"], excludedTasks: argv["exclude-task"], + ui5DataDir, }; if (serverConfig.h2) { diff --git a/packages/cli/lib/cli/commands/tree.js b/packages/cli/lib/cli/commands/tree.js index 3c2ec93c3e6..b5f25471d90 100644 --- a/packages/cli/lib/cli/commands/tree.js +++ b/packages/cli/lib/cli/commands/tree.js @@ -1,6 +1,7 @@ // Tree import baseMiddleware from "../middlewares/base.js"; import {applyProjectConfigOptions, applyWorkspaceOptions, dedupeArray} from "../options.js"; +import {resolveUi5DataDir} from "@ui5/project/utils/dataDir"; import chalk from "chalk"; import {getLogger} from "@ui5/logger"; const log = getLogger("cli:commands:tree"); @@ -70,6 +71,7 @@ tree.handler = async function(argv) { startTime = process.hrtime(); } const {graphFromStaticFile, graphFromPackageDependencies} = await import("@ui5/project/graph"); + const ui5DataDir = await resolveUi5DataDir({projectRootPath: process.cwd()}); let graph; if (argv.dependencyDefinition) { graph = await graphFromStaticFile({ @@ -77,6 +79,7 @@ tree.handler = async function(argv) { rootConfigPath: argv.config, versionOverride: argv.frameworkVersion, snapshotCache: argv.snapshotCache ?? argv.cacheMode ?? "Default", // Use cacheMode as fallback + ui5DataDir, }); } else { graph = await graphFromPackageDependencies({ @@ -85,6 +88,7 @@ tree.handler = async function(argv) { snapshotCache: argv.snapshotCache ?? argv.cacheMode ?? "Default", // Use cacheMode as fallback workspaceConfigPath: argv.workspaceConfig, workspaceName: argv.workspace === false ? null : argv.workspace, + ui5DataDir, }); } diff --git a/packages/cli/lib/framework/utils.js b/packages/cli/lib/framework/utils.js index d5e272eda05..38527abbba5 100644 --- a/packages/cli/lib/framework/utils.js +++ b/packages/cli/lib/framework/utils.js @@ -2,14 +2,21 @@ import {graphFromStaticFile, graphFromPackageDependencies} from "@ui5/project/gr import {resolveUi5DataDir} from "@ui5/project/utils/dataDir"; export async function getRootProjectConfiguration(projectGraphOptions) { + const cwd = projectGraphOptions.cwd || process.cwd(); + const ui5DataDir = await resolveUi5DataDir({projectRootPath: cwd}); + let graph; if (projectGraphOptions.dependencyDefinition) { graph = await graphFromStaticFile({ + cwd, + ui5DataDir, filePath: projectGraphOptions.dependencyDefinition, resolveFrameworkDependencies: false }); } else { graph = await graphFromPackageDependencies({ + cwd, + ui5DataDir, rootConfigPath: projectGraphOptions.config, resolveFrameworkDependencies: false }); @@ -42,10 +49,8 @@ export async function createFrameworkResolverInstance({frameworkName, frameworkV export async function frameworkResolverResolveVersion({frameworkName, frameworkVersion}, {cwd}) { const Resolver = await utils.getFrameworkResolver(frameworkName, frameworkVersion); - return Resolver.resolveVersion(frameworkVersion, { - cwd, - ui5DataDir: await resolveUi5DataDir({projectRootPath: cwd}) - }); + const ui5DataDir = await resolveUi5DataDir({projectRootPath: cwd}); + return Resolver.resolveVersion(frameworkVersion, ui5DataDir, {cwd}); } const utils = { diff --git a/packages/cli/test/lib/cli/commands/build.js b/packages/cli/test/lib/cli/commands/build.js index 8234a6506bb..66d16ee5e55 100644 --- a/packages/cli/test/lib/cli/commands/build.js +++ b/packages/cli/test/lib/cli/commands/build.js @@ -64,6 +64,7 @@ function getDefaultBuilderArgs() { test.beforeEach(async (t) => { t.context.argv = getDefaultArgv(); t.context.expectedBuilderArgs = getDefaultBuilderArgs(); + t.context.ui5DataDir = "/resolved/ui5-data-dir"; t.context.builder = sinon.stub().resolves(); t.context.getBuilderSettings = sinon.stub().returns(undefined); @@ -75,15 +76,20 @@ test.beforeEach(async (t) => { build: t.context.builder }; t.context.expectedBuilderArgs.graph = fakeGraph; + t.context.expectedBuilderArgs.ui5DataDir = t.context.ui5DataDir; t.context.ProjectGraphStub = sinon.stub().resolves(fakeGraph); t.context.graphFromPackageDependenciesStub = sinon.stub().resolves(fakeGraph); t.context.graphFromStaticFileStub = sinon.stub().resolves(fakeGraph); + t.context.resolveUi5DataDirStub = sinon.stub().resolves(t.context.ui5DataDir); t.context.build = await esmock.p("../../../../lib/cli/commands/build.js", { "@ui5/project/graph": { graphFromPackageDependencies: t.context.graphFromPackageDependenciesStub, graphFromStaticFile: t.context.graphFromStaticFileStub }, + "@ui5/project/utils/dataDir": { + resolveUi5DataDir: t.context.resolveUi5DataDirStub + }, "@ui5/project/graph/ProjectGraph": t.context.ProjectGraphStub }); }); @@ -138,6 +144,7 @@ test.serial("ui5 build --framework-version", async (t) => { workspaceConfigPath: undefined, workspaceName: undefined, snapshotCache: "Default", + ui5DataDir: "/resolved/ui5-data-dir", }, "generateProjectGraph.graphFromPackageDependencies got called with expected arguments" ); }); @@ -157,6 +164,7 @@ test.serial("ui5 build --snapshot-cache", async (t) => { workspaceConfigPath: undefined, workspaceName: undefined, snapshotCache: "Off", + ui5DataDir: "/resolved/ui5-data-dir", }, "generateProjectGraph.graphFromPackageDependencies got called with expected arguments" ); }); @@ -176,6 +184,7 @@ test.serial("ui5 build --config", async (t) => { workspaceConfigPath: undefined, workspaceName: undefined, snapshotCache: "Default", + ui5DataDir: "/resolved/ui5-data-dir", }, "generateProjectGraph.graphFromPackageDependencies got called with expected arguments" ); }); @@ -195,6 +204,7 @@ test.serial("ui5 build --workspace", async (t) => { workspaceConfigPath: undefined, workspaceName: "dolphin", snapshotCache: "Default", + ui5DataDir: "/resolved/ui5-data-dir", }, "generateProjectGraph.graphFromPackageDependencies got called with expected arguments" ); }); @@ -214,6 +224,7 @@ test.serial("ui5 build --no-workspace", async (t) => { workspaceConfigPath: undefined, workspaceName: null, snapshotCache: "Default", + ui5DataDir: "/resolved/ui5-data-dir", }, "generateProjectGraph.graphFromPackageDependencies got called with expected arguments" ); }); @@ -234,6 +245,7 @@ test.serial("ui5 build --workspace-config", async (t) => { workspaceConfigPath: fakePath, workspaceName: undefined, snapshotCache: "Default", + ui5DataDir: "/resolved/ui5-data-dir", }, "generateProjectGraph.graphFromPackageDependencies got called with expected arguments" ); }); @@ -252,6 +264,7 @@ test.serial("ui5 build --dependency-definition", async (t) => { rootConfigPath: undefined, versionOverride: undefined, snapshotCache: "Default", + ui5DataDir: "/resolved/ui5-data-dir", }, "generateProjectGraph.graphFromStaticFile got called with expected arguments" ); }); @@ -271,6 +284,7 @@ test.serial("ui5 build --dependency-definition --config", async (t) => { rootConfigPath: "ui5-test.yaml", versionOverride: undefined, snapshotCache: "Default", + ui5DataDir: "/resolved/ui5-data-dir", }, "generateProjectGraph.graphFromStaticFile got called with expected arguments" ); }); @@ -291,6 +305,7 @@ test.serial("ui5 build --dependency-definition --config --framework-version", as rootConfigPath: "ui5-test.yaml", versionOverride: "1.99.0", snapshotCache: "Default", + ui5DataDir: "/resolved/ui5-data-dir", }, "generateProjectGraph.graphFromStaticFile got called with expected arguments" ); }); diff --git a/packages/cli/test/lib/cli/commands/serve.js b/packages/cli/test/lib/cli/commands/serve.js index ffab26787f0..75d4e41c753 100644 --- a/packages/cli/test/lib/cli/commands/serve.js +++ b/packages/cli/test/lib/cli/commands/serve.js @@ -34,6 +34,7 @@ function getDefaultArgv() { test.beforeEach(async (t) => { t.context.argv = getDefaultArgv(); + t.context.ui5DataDir = "/resolved/ui5-data-dir"; // server.serve is the CLI-facing synchronization point now that the handler // no longer writes "Server started" to stdout. Test cases await `serverServed` @@ -68,6 +69,7 @@ test.beforeEach(async (t) => { graphFromStaticFile: sinon.stub().resolves(t.context.fakeGraph), graphFromPackageDependencies: sinon.stub().resolves(t.context.fakeGraph) }; + t.context.resolveUi5DataDirStub = sinon.stub().resolves(t.context.ui5DataDir); // Capture stray writes to stderr/stdout so failing assertions surface the // actual output instead of ava's timeout diagnostics. @@ -85,6 +87,9 @@ test.beforeEach(async (t) => { "@ui5/server": t.context.server, "@ui5/server/internal/sslUtil": t.context.sslUtil, "@ui5/project/graph": t.context.graph, + "@ui5/project/utils/dataDir": { + resolveUi5DataDir: t.context.resolveUi5DataDirStub + }, "open": t.context.open }); }); @@ -106,6 +111,7 @@ test.serial("ui5 serve: default", async (t) => { rootConfigPath: undefined, versionOverride: undefined, workspaceConfigPath: undefined, workspaceName: undefined, snapshotCache: "Default", + ui5DataDir: "/resolved/ui5-data-dir", }]); t.is(server.serve.callCount, 1); @@ -125,6 +131,7 @@ test.serial("ui5 serve: default", async (t) => { liveReload: true, includedTasks: undefined, excludedTasks: undefined, + ui5DataDir: "/resolved/ui5-data-dir", } ]); t.is(typeof server.serve.getCall(0).args[2], "function"); @@ -169,6 +176,7 @@ test.serial("ui5 serve --h2", async (t) => { liveReload: true, includedTasks: undefined, excludedTasks: undefined, + ui5DataDir: "/resolved/ui5-data-dir", } ]); @@ -204,6 +212,7 @@ test.serial("ui5 serve --accept-remote-connections", async (t) => { liveReload: true, includedTasks: undefined, excludedTasks: undefined, + ui5DataDir: "/resolved/ui5-data-dir", } ]); }); @@ -257,6 +266,7 @@ test.serial("ui5 serve --config", async (t) => { rootConfigPath: fakePath, versionOverride: undefined, workspaceConfigPath: undefined, workspaceName: undefined, snapshotCache: "Default", + ui5DataDir: "/resolved/ui5-data-dir", }]); }); @@ -273,7 +283,7 @@ test.serial("ui5 serve --dependency-definition", async (t) => { t.is(graph.graphFromStaticFile.callCount, 1); t.deepEqual(graph.graphFromStaticFile.getCall(0).args, [{ filePath: fakePath, versionOverride: undefined, - snapshotCache: "Default", rootConfigPath: undefined + snapshotCache: "Default", ui5DataDir: "/resolved/ui5-data-dir", rootConfigPath: undefined }]); }); @@ -292,7 +302,7 @@ test.serial("ui5 serve --dependency-definition / --config", async (t) => { t.is(graph.graphFromStaticFile.callCount, 1); t.deepEqual(graph.graphFromStaticFile.getCall(0).args, [{ filePath: fakeDependenciesPath, versionOverride: undefined, - snapshotCache: "Default", rootConfigPath: fakeConfigPath + snapshotCache: "Default", ui5DataDir: "/resolved/ui5-data-dir", rootConfigPath: fakeConfigPath }]); }); @@ -308,6 +318,7 @@ test.serial("ui5 serve --framework-version", async (t) => { rootConfigPath: undefined, versionOverride: "1.234.5", workspaceConfigPath: undefined, workspaceName: undefined, snapshotCache: "Default", + ui5DataDir: "/resolved/ui5-data-dir", }]); }); @@ -323,6 +334,7 @@ test.serial("ui5 serve --snapshotCache", async (t) => { rootConfigPath: undefined, versionOverride: undefined, workspaceConfigPath: undefined, workspaceName: undefined, snapshotCache: "Force", + ui5DataDir: "/resolved/ui5-data-dir", }]); }); @@ -338,6 +350,7 @@ test.serial("ui5 serve --workspace", async (t) => { rootConfigPath: undefined, versionOverride: undefined, workspaceConfigPath: undefined, workspaceName: "dolphin", snapshotCache: "Default", + ui5DataDir: "/resolved/ui5-data-dir", }]); }); @@ -353,6 +366,7 @@ test.serial("ui5 serve --no-workspace", async (t) => { rootConfigPath: undefined, versionOverride: undefined, workspaceConfigPath: undefined, workspaceName: null, snapshotCache: "Default", + ui5DataDir: "/resolved/ui5-data-dir", }]); }); @@ -369,6 +383,7 @@ test.serial("ui5 serve --workspace-config", async (t) => { rootConfigPath: undefined, versionOverride: undefined, workspaceConfigPath: fakePath, workspaceName: undefined, snapshotCache: "Default", + ui5DataDir: "/resolved/ui5-data-dir", }]); }); diff --git a/packages/cli/test/lib/cli/commands/tree.js b/packages/cli/test/lib/cli/commands/tree.js index e08b338d236..3f3c19f8e4b 100644 --- a/packages/cli/test/lib/cli/commands/tree.js +++ b/packages/cli/test/lib/cli/commands/tree.js @@ -25,6 +25,7 @@ function getDefaultArgv() { test.beforeEach(async (t) => { t.context.argv = getDefaultArgv(); + t.context.ui5DataDir = path.join(path.sep, "resolved", "ui5-data-dir"); t.context.traverseBreadthFirst = sinon.stub(); t.context.getExtensionNames = sinon.stub().returns([]); @@ -38,6 +39,7 @@ test.beforeEach(async (t) => { graphFromStaticFile: sinon.stub().resolves(fakeGraph), graphFromPackageDependencies: sinon.stub().resolves(fakeGraph) }; + t.context.resolveUi5DataDirStub = sinon.stub().resolves(t.context.ui5DataDir); t.context.consoleOutput = ""; t.context.processStderrWrite = sinon.stub(process.stderr, "write").callsFake((message) => { @@ -48,7 +50,10 @@ test.beforeEach(async (t) => { }); t.context.tree = await esmock.p("../../../../lib/cli/commands/tree.js", { - "@ui5/project/graph": t.context.graph + "@ui5/project/graph": t.context.graph, + "@ui5/project/utils/dataDir": { + resolveUi5DataDir: t.context.resolveUi5DataDirStub + } }); }); test.afterEach.always((t) => { @@ -81,6 +86,7 @@ test.serial("ui5 tree (Without dependencies)", async (t) => { rootConfigPath: undefined, versionOverride: undefined, workspaceConfigPath: undefined, workspaceName: undefined, snapshotCache: "Default", + ui5DataDir: t.context.ui5DataDir, }]); t.is(t.context.consoleOutput, @@ -151,6 +157,7 @@ test.serial("ui5 tree", async (t) => { rootConfigPath: undefined, versionOverride: undefined, workspaceConfigPath: undefined, workspaceName: undefined, snapshotCache: "Default", + ui5DataDir: t.context.ui5DataDir, }]); t.is(t.context.consoleOutput, @@ -231,6 +238,7 @@ test.serial("ui5 tree --flat", async (t) => { rootConfigPath: undefined, versionOverride: undefined, workspaceConfigPath: undefined, workspaceName: undefined, snapshotCache: "Default", + ui5DataDir: t.context.ui5DataDir, }]); t.is(t.context.consoleOutput, @@ -308,6 +316,7 @@ test.serial("ui5 tree --level 1", async (t) => { rootConfigPath: undefined, versionOverride: undefined, workspaceConfigPath: undefined, workspaceName: undefined, snapshotCache: "Default", + ui5DataDir: t.context.ui5DataDir, }]); t.is(t.context.consoleOutput, @@ -398,6 +407,7 @@ test.serial("ui5 tree (With extensions)", async (t) => { rootConfigPath: undefined, versionOverride: undefined, workspaceConfigPath: undefined, workspaceName: undefined, snapshotCache: "Default", + ui5DataDir: t.context.ui5DataDir, }]); t.is(t.context.consoleOutput, @@ -444,6 +454,7 @@ test.serial("ui5 tree --perf", async (t) => { rootConfigPath: undefined, versionOverride: undefined, workspaceConfigPath: undefined, workspaceName: undefined, snapshotCache: "Default", + ui5DataDir: t.context.ui5DataDir, }]); t.is(t.context.consoleOutput, @@ -485,6 +496,7 @@ test.serial("ui5 tree --framework-version", async (t) => { rootConfigPath: undefined, versionOverride: "1.234.5", workspaceConfigPath: undefined, workspaceName: undefined, snapshotCache: "Default", + ui5DataDir: t.context.ui5DataDir, }]); t.is(t.context.consoleOutput, @@ -524,6 +536,7 @@ test.serial("ui5 tree --snapshot-cache", async (t) => { rootConfigPath: undefined, versionOverride: undefined, workspaceConfigPath: undefined, workspaceName: undefined, snapshotCache: "Force", + ui5DataDir: t.context.ui5DataDir, }]); t.is(t.context.consoleOutput, @@ -564,6 +577,7 @@ test.serial("ui5 tree --config", async (t) => { rootConfigPath: fakePath, versionOverride: undefined, workspaceConfigPath: undefined, workspaceName: undefined, snapshotCache: "Default", + ui5DataDir: t.context.ui5DataDir, }]); t.is(t.context.consoleOutput, @@ -603,6 +617,7 @@ test.serial("ui5 tree --workspace", async (t) => { rootConfigPath: undefined, versionOverride: undefined, workspaceConfigPath: undefined, workspaceName: "dolphin", snapshotCache: "Default", + ui5DataDir: t.context.ui5DataDir, }]); t.is(t.context.consoleOutput, @@ -642,6 +657,7 @@ test.serial("ui5 tree --no-workspace", async (t) => { rootConfigPath: undefined, versionOverride: undefined, workspaceConfigPath: undefined, workspaceName: null, snapshotCache: "Default", + ui5DataDir: t.context.ui5DataDir, }]); t.is(t.context.consoleOutput, @@ -682,6 +698,7 @@ test.serial("ui5 tree --workspace-config", async (t) => { rootConfigPath: undefined, versionOverride: undefined, workspaceConfigPath: fakePath, workspaceName: undefined, snapshotCache: "Default", + ui5DataDir: t.context.ui5DataDir, }]); t.is(t.context.consoleOutput, @@ -719,7 +736,11 @@ test.serial("ui5 tree --dependency-definition", async (t) => { t.is(graph.graphFromPackageDependencies.callCount, 0); t.is(graph.graphFromStaticFile.callCount, 1); t.deepEqual(graph.graphFromStaticFile.getCall(0).args, [{ - filePath: fakePath, rootConfigPath: undefined, versionOverride: undefined, snapshotCache: "Default" + filePath: fakePath, + rootConfigPath: undefined, + versionOverride: undefined, + snapshotCache: "Default", + ui5DataDir: t.context.ui5DataDir }]); t.is(t.context.consoleOutput, @@ -762,6 +783,7 @@ test.serial("ui5 tree --dependency-definition --config", async (t) => { filePath: fakeDepDefPath, rootConfigPath: fakeConfigPath, versionOverride: undefined, - snapshotCache: "Default" + snapshotCache: "Default", + ui5DataDir: t.context.ui5DataDir }], "graphFromStaticFile got called with --config forwarded as rootConfigPath"); }); diff --git a/packages/cli/test/lib/framework/utils.js b/packages/cli/test/lib/framework/utils.js index 48c7a4ba990..5f0d0856194 100644 --- a/packages/cli/test/lib/framework/utils.js +++ b/packages/cli/test/lib/framework/utils.js @@ -64,6 +64,8 @@ test.serial("getRootProjectConfiguration: dependencyDefinition", async (t) => { t.is(graphFromStaticFile.callCount, 1); t.deepEqual(graphFromStaticFile.getCall(0).args, [{ + cwd: process.cwd(), + ui5DataDir: "my-default-ui5-data-dir", filePath: "foo", resolveFrameworkDependencies: false }]); @@ -86,6 +88,8 @@ test.serial("getRootProjectConfiguration: config", async (t) => { t.is(graphFromPackageDependencies.callCount, 1); t.deepEqual(graphFromPackageDependencies.getCall(0).args, [{ + cwd: process.cwd(), + ui5DataDir: "my-default-ui5-data-dir", rootConfigPath: "foo", resolveFrameworkDependencies: false }]); @@ -230,9 +234,7 @@ test.serial("frameworkResolverResolveVersion", async (t) => { t.is(resolveVersionStub.callCount, 1); t.deepEqual(resolveVersionStub.getCall(0).args, [ "latest", - { - cwd: "my-project-path", - ui5DataDir: "my-default-ui5-data-dir" - } + "my-default-ui5-data-dir", + {cwd: "my-project-path"} ]); }); diff --git a/packages/project/lib/build/ProjectBuilder.js b/packages/project/lib/build/ProjectBuilder.js index 94a724b34fd..54ef4f6480c 100644 --- a/packages/project/lib/build/ProjectBuilder.js +++ b/packages/project/lib/build/ProjectBuilder.js @@ -123,7 +123,7 @@ class ProjectBuilder { } this._graph = graph; - this._buildContext = new BuildContext(graph, taskRepository, buildConfig, {ui5DataDir}); + this._buildContext = new BuildContext(graph, taskRepository, buildConfig, ui5DataDir); this.#log = new BuildLogger("ProjectBuilder"); } diff --git a/packages/project/lib/build/cache/CacheManager.js b/packages/project/lib/build/cache/CacheManager.js index a4b5f12aad4..dec1dcc5676 100644 --- a/packages/project/lib/build/cache/CacheManager.js +++ b/packages/project/lib/build/cache/CacheManager.js @@ -1,7 +1,6 @@ import path from "node:path"; import {getLogger} from "@ui5/logger"; import BuildCacheStorage from "./BuildCacheStorage.js"; -import os from "node:os"; const log = getLogger("build:cache:CacheManager"); @@ -57,22 +56,16 @@ export default class CacheManager { * Factory method to create or retrieve a CacheManager instance * * 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 or ui5DataDir from configuration file - * (resolved relative to cwd via {@link resolveUi5DataDir}) - * 3. Default: ~/.ui5/ * * @public - * @param {object} [options] - * @param {string} [options.ui5DataDir] Explicit UI5 data directory. When provided, - * environment variable, configuration file, and home-directory fallbacks are skipped. + * @param {string} ui5DataDir Resolved UI5 data directory. * @returns {Promise} Singleton CacheManager instance for the cache directory */ - static async create({ui5DataDir} = {}) { - ui5DataDir = path.resolve( - ui5DataDir || path.join(os.homedir(), ".ui5") - ); + static async create(ui5DataDir) { + if (!ui5DataDir) { + throw new Error("CacheManager: Missing parameter \"ui5DataDir\""); + } + ui5DataDir = path.resolve(ui5DataDir); const cacheDir = path.join(ui5DataDir, "buildCache"); log.verbose(`Using build cache directory: ${cacheDir}`); diff --git a/packages/project/lib/build/helpers/BuildContext.js b/packages/project/lib/build/helpers/BuildContext.js index 5a030cc728f..76b4192357b 100644 --- a/packages/project/lib/build/helpers/BuildContext.js +++ b/packages/project/lib/build/helpers/BuildContext.js @@ -5,7 +5,6 @@ import {getBaseSignature} from "./getBuildSignature.js"; import {getLogger} from "@ui5/logger"; const log = getLogger("build:helpers:BuildContext"); import Cache from "../cache/Cache.js"; -import {resolveUi5DataDir} from "../../utils/dataDir.js"; /** * Context of a build process @@ -24,7 +23,7 @@ class BuildContext { outputStyle = OutputStyleEnum.Default, includedTasks = [], excludedTasks = [], cache = Cache.Default, - } = {}, {ui5DataDir} = {}) { + } = {}, ui5DataDir) { if (!graph) { throw new Error(`Missing parameter 'graph'`); } @@ -171,10 +170,10 @@ class BuildContext { if (this.#cacheManager) { return this.#cacheManager; } - this.#cacheManager = await CacheManager.create({ - ui5DataDir: this._ui5DataDir ?? - await resolveUi5DataDir({projectRootPath: this.getRootProject().getRootPath()}), - }); + if (!this._ui5DataDir) { + throw new Error("BuildContext: Missing parameter \"ui5DataDir\" for cache-enabled builds"); + } + this.#cacheManager = await CacheManager.create(this._ui5DataDir); return this.#cacheManager; } diff --git a/packages/project/lib/graph/ProjectGraph.js b/packages/project/lib/graph/ProjectGraph.js index 653e6f7901b..ed2af07bf23 100644 --- a/packages/project/lib/graph/ProjectGraph.js +++ b/packages/project/lib/graph/ProjectGraph.js @@ -716,10 +716,8 @@ class ProjectGraph { * Processes build results into a specific directory structure. * @param {module:@ui5/project/build/cache/Cache} [parameters.cache=Default] * Cache mode to use for building UI5 projects - * @param {string} [parameters.ui5DataDir] - * Explicit UI5 data directory to use for the build cache. Overrides the - * UI5_DATA_DIR environment variable, the UI5 configuration file, - * and the default of ~/.ui5. + * @param {string} parameters.ui5DataDir + * Resolved UI5 data directory to use for the build cache. * @returns {Promise} Promise resolving to undefined once build has finished */ async build({ @@ -732,6 +730,9 @@ class ProjectGraph { cache = Cache.Default, ui5DataDir, }) { + if (!ui5DataDir) { + throw new Error("ProjectGraph#build: Missing parameter \"ui5DataDir\""); + } this.seal(); // Do not allow further changes to the graph if (this._builtOrServed) { throw new Error( @@ -768,6 +769,9 @@ class ProjectGraph { cache = Cache.Default, ui5DataDir, }) { + if (!ui5DataDir) { + throw new Error("ProjectGraph#serve: Missing parameter \"ui5DataDir\""); + } this.seal(); // Do not allow further changes to the graph if (this._builtOrServed) { throw new Error( diff --git a/packages/project/lib/graph/graph.js b/packages/project/lib/graph/graph.js index bf1bd5c3583..fa16498b18f 100644 --- a/packages/project/lib/graph/graph.js +++ b/packages/project/lib/graph/graph.js @@ -33,6 +33,8 @@ const log = getLogger("generateProjectGraph"); * Name of the workspace configuration that should be used. "default" if not provided. * @param {module:@ui5/project/ui5Framework/maven/SnapshotCache} [options.snapshotCache] * Snapshot cache mode to use when consuming SNAPSHOT versions of a framework + * @param {string} options.ui5DataDir + * Resolved UI5 data directory to use for framework metadata and package resolution. * @param {string} [options.workspaceConfigPath=ui5-workspace.yaml] * Workspace configuration file to use if no object has been provided * @param {@ui5/project/graph/Workspace~Configuration} [options.workspaceConfiguration] @@ -44,8 +46,12 @@ export async function graphFromPackageDependencies({ cwd, rootConfiguration, rootConfigPath, versionOverride, snapshotCache, resolveFrameworkDependencies = true, workspaceName="default", - workspaceConfiguration, workspaceConfigPath = "ui5-workspace.yaml" + workspaceConfiguration, workspaceConfigPath = "ui5-workspace.yaml", + ui5DataDir }) { + if (!ui5DataDir) { + throw new Error("graphFromPackageDependencies: Missing parameter \"ui5DataDir\""); + } log.verbose(`Creating project graph using npm provider...`); const { default: NpmProvider @@ -73,7 +79,7 @@ export async function graphFromPackageDependencies({ const projectGraph = await projectGraphBuilder(provider, workspace); if (resolveFrameworkDependencies) { - await ui5Framework.enrichProjectGraph(projectGraph, {versionOverride, snapshotCache, workspace}); + await ui5Framework.enrichProjectGraph(projectGraph, {versionOverride, snapshotCache, workspace, ui5DataDir}); } return projectGraph; @@ -100,6 +106,8 @@ export async function graphFromPackageDependencies({ * @param {string} [options.versionOverride] Framework version to use instead of the one defined in the root project * @param {module:@ui5/project/ui5Framework/maven/SnapshotCache} [options.snapshotCache] * Snapshot cache mode to use when consuming SNAPSHOT versions of a framework + * @param {string} options.ui5DataDir + * Resolved UI5 data directory to use for framework metadata and package resolution. * @param {string} [options.resolveFrameworkDependencies=true] * Whether framework dependencies should be added to the graph * @returns {Promise<@ui5/project/graph/ProjectGraph>} Promise resolving to a Project Graph instance @@ -107,8 +115,12 @@ export async function graphFromPackageDependencies({ export async function graphFromStaticFile({ filePath = "projectDependencies.yaml", cwd, rootConfiguration, rootConfigPath, - versionOverride, snapshotCache, resolveFrameworkDependencies = true + versionOverride, snapshotCache, resolveFrameworkDependencies = true, + ui5DataDir }) { + if (!ui5DataDir) { + throw new Error("graphFromStaticFile: Missing parameter \"ui5DataDir\""); + } log.verbose(`Creating project graph using static file...`); const { default: DependencyTreeProvider @@ -128,7 +140,7 @@ export async function graphFromStaticFile({ const projectGraph = await projectGraphBuilder(provider); if (resolveFrameworkDependencies) { - await ui5Framework.enrichProjectGraph(projectGraph, {versionOverride, snapshotCache}); + await ui5Framework.enrichProjectGraph(projectGraph, {versionOverride, snapshotCache, ui5DataDir}); } return projectGraph; @@ -152,6 +164,8 @@ export async function graphFromStaticFile({ * @param {string} [options.versionOverride] Framework version to use instead of the one defined in the root project * @param {module:@ui5/project/ui5Framework/maven/SnapshotCache} [options.snapshotCache] * Snapshot cache mode to use when consuming SNAPSHOT versions of a framework + * @param {string} options.ui5DataDir + * Resolved UI5 data directory to use for framework metadata and package resolution. * @param {string} [options.resolveFrameworkDependencies=true] * Whether framework dependencies should be added to the graph * @returns {Promise<@ui5/project/graph/ProjectGraph>} Promise resolving to a Project Graph instance @@ -159,8 +173,12 @@ export async function graphFromStaticFile({ export async function graphFromObject({ dependencyTree, cwd, rootConfiguration, rootConfigPath, - versionOverride, snapshotCache, resolveFrameworkDependencies = true + versionOverride, snapshotCache, resolveFrameworkDependencies = true, + ui5DataDir }) { + if (!ui5DataDir) { + throw new Error("graphFromObject: Missing parameter \"ui5DataDir\""); + } log.verbose(`Creating project graph using object...`); const { default: DependencyTreeProvider @@ -178,7 +196,7 @@ export async function graphFromObject({ const projectGraph = await projectGraphBuilder(dependencyTreeProvider); if (resolveFrameworkDependencies) { - await ui5Framework.enrichProjectGraph(projectGraph, {versionOverride, snapshotCache}); + await ui5Framework.enrichProjectGraph(projectGraph, {versionOverride, snapshotCache, ui5DataDir}); } return projectGraph; diff --git a/packages/project/lib/graph/helpers/ui5Framework.js b/packages/project/lib/graph/helpers/ui5Framework.js index 06921dc5b23..32466500816 100644 --- a/packages/project/lib/graph/helpers/ui5Framework.js +++ b/packages/project/lib/graph/helpers/ui5Framework.js @@ -2,7 +2,6 @@ import Module from "../Module.js"; import ProjectGraph from "../ProjectGraph.js"; import {getLogger} from "@ui5/logger"; const log = getLogger("graph:helpers:ui5Framework"); -import {resolveUi5DataDir} from "../../utils/dataDir.js"; class ProjectProcessor { constructor({libraryMetadata, graph, workspace}) { @@ -283,13 +282,17 @@ export default { * version * @param {module:@ui5/project/ui5Framework/maven/SnapshotCache} [options.snapshotCache] * Snapshot cache mode to use when consuming SNAPSHOT versions of a framework + * @param {string} options.ui5DataDir Resolved UI5 data directory to use for framework + * metadata and package resolution. * @param {@ui5/project/graph/Workspace} [options.workspace] * Optional workspace instance to use for overriding node resolutions + * @throws {Error} If framework libraries are referenced but no options.ui5DataDir + * is provided. * @returns {Promise<@ui5/project/graph/ProjectGraph>} * Promise resolving with the given graph instance to allow method chaining */ enrichProjectGraph: async function(projectGraph, options = {}) { - const {workspace, snapshotCache} = options; + const {workspace, snapshotCache, ui5DataDir} = options; const rootProject = projectGraph.getRoot(); const frameworkName = rootProject.getFrameworkName(); const frameworkVersion = rootProject.getFrameworkVersion(); @@ -338,6 +341,10 @@ export default { return projectGraph; } + if (!ui5DataDir) { + throw new Error("ui5Framework.enrichProjectGraph: Missing parameter \"ui5DataDir\""); + } + let Resolver; if (version && version.toLowerCase().endsWith("-snapshot")) { Resolver = (await import("../../ui5Framework/Sapui5MavenSnapshotResolver.js")).default; @@ -347,15 +354,8 @@ export default { Resolver = (await import("../../ui5Framework/Sapui5Resolver.js")).default; } - // Resolve the UI5 data directory using the shared utility. - // Returns ~/.ui5 by default when no env var or configuration is set. - const ui5DataDir = await resolveUi5DataDir({projectRootPath: cwd}); - if (options.versionOverride) { - version = await Resolver.resolveVersion(options.versionOverride, { - ui5DataDir, - cwd - }); + version = await Resolver.resolveVersion(options.versionOverride, ui5DataDir, {cwd}); log.info( `Overriding configured ${frameworkName} version ` + `${frameworkVersion} with version ${version}` diff --git a/packages/project/lib/ui5Framework/AbstractResolver.js b/packages/project/lib/ui5Framework/AbstractResolver.js index fb2aaf0c46e..750242ff0ce 100644 --- a/packages/project/lib/ui5Framework/AbstractResolver.js +++ b/packages/project/lib/ui5Framework/AbstractResolver.js @@ -210,21 +210,26 @@ class AbstractResolver { }; } - static async resolveVersion(version, {ui5DataDir, cwd} = {}) { + static async resolveVersion(version, ui5DataDir, {cwd} = {}) { // Don't allow nullish values // An empty string is a valid semver range that converts to "*", which should not be supported if (!version) { throw new Error(`Framework version specifier "${version}" is incorrect or not supported`); } - const spec = await this._getVersionSpec(version, {ui5DataDir, cwd}); + if (!ui5DataDir) { + throw new Error(`${this.name}: Missing parameter "ui5DataDir"`); + } + ui5DataDir = path.resolve(ui5DataDir); + + const spec = await this._getVersionSpec(version, ui5DataDir, {cwd}); // For all invalid cases which are not explicitly handled in _getVersionSpec if (!spec) { throw new Error(`Framework version specifier "${version}" is incorrect or not supported`); } - const versions = await this.fetchAllVersions({ui5DataDir, cwd}); + const versions = await this.fetchAllVersions(ui5DataDir, {cwd}); const resolvedVersion = semver.maxSatisfying(versions, spec, { // Allow ranges that end with -SNAPSHOT to match any -SNAPSHOT version // like a normal version in order to support ranges like 1.x.x-SNAPSHOT. @@ -251,7 +256,7 @@ class AbstractResolver { return resolvedVersion; } - static async _getVersionSpec(version, {ui5DataDir, cwd}) { + static async _getVersionSpec(version, ui5DataDir, {cwd} = {}) { if (this._isSnapshotVersionOrRange(version)) { const versionMatch = version.match(VERSION_RANGE_REGEXP); if (versionMatch) { @@ -273,7 +278,7 @@ class AbstractResolver { return null; } - const allTags = await this.fetchAllTags({ui5DataDir, cwd}); + const allTags = await this.fetchAllTags(ui5DataDir, {cwd}); if (!allTags) { // Resolver doesn't support tags (e.g. Sapui5MavenSnapshotResolver) @@ -309,10 +314,10 @@ class AbstractResolver { async handleLibrary(libraryName) { throw new Error("AbstractResolver: handleLibrary must be implemented!"); } - static fetchAllVersions(options) { + static fetchAllVersions(ui5DataDir, options) { throw new Error("AbstractResolver: static fetchAllVersions must be implemented!"); } - static fetchAllTags(options) { + static fetchAllTags(ui5DataDir, options) { return null; } } diff --git a/packages/project/lib/ui5Framework/Openui5Resolver.js b/packages/project/lib/ui5Framework/Openui5Resolver.js index 72eefe02a3e..bf51d1f2e50 100644 --- a/packages/project/lib/ui5Framework/Openui5Resolver.js +++ b/packages/project/lib/ui5Framework/Openui5Resolver.js @@ -1,5 +1,4 @@ import path from "node:path"; -import os from "node:os"; import AbstractResolver from "./AbstractResolver.js"; import Installer from "./npm/Installer.js"; @@ -84,22 +83,23 @@ class Openui5Resolver extends AbstractResolver { }) }; } - static async fetchAllVersions(options) { - const installer = this._getInstaller(options); + static async fetchAllVersions(ui5DataDir, {cwd} = {}) { + const installer = this._getInstaller(ui5DataDir, {cwd}); return await installer.fetchPackageVersions({pkgName: OPENUI5_CORE_PACKAGE}); } - static async fetchAllTags(options) { - const installer = this._getInstaller(options); + static async fetchAllTags(ui5DataDir, {cwd} = {}) { + const installer = this._getInstaller(ui5DataDir, {cwd}); return installer.fetchPackageDistTags({pkgName: OPENUI5_CORE_PACKAGE}); } - static _getInstaller({ui5DataDir, cwd} = {}) { + static _getInstaller(ui5DataDir, {cwd} = {}) { + if (!ui5DataDir) { + throw new Error(`${this.name}: Missing parameter "ui5DataDir"`); + } return new Installer({ cwd: cwd ? path.resolve(cwd) : process.cwd(), - ui5DataDir: - ui5DataDir ? path.resolve(ui5DataDir) : - path.join(os.homedir(), ".ui5") + ui5DataDir: path.resolve(ui5DataDir) }); } } diff --git a/packages/project/lib/ui5Framework/Sapui5MavenSnapshotResolver.js b/packages/project/lib/ui5Framework/Sapui5MavenSnapshotResolver.js index c56b4f7cba0..310f68a8882 100644 --- a/packages/project/lib/ui5Framework/Sapui5MavenSnapshotResolver.js +++ b/packages/project/lib/ui5Framework/Sapui5MavenSnapshotResolver.js @@ -144,12 +144,13 @@ class Sapui5MavenSnapshotResolver extends AbstractResolver { }; } - static async fetchAllVersions({ui5DataDir, cwd, snapshotEndpointUrl} = {}) { + static async fetchAllVersions(ui5DataDir, {cwd, snapshotEndpointUrl} = {}) { + if (!ui5DataDir) { + throw new Error(`${this.name}: Missing parameter "ui5DataDir"`); + } const installer = new Installer({ cwd: cwd ? path.resolve(cwd) : process.cwd(), - ui5DataDir: path.resolve( - ui5DataDir || path.join(os.homedir(), ".ui5") - ), + ui5DataDir: path.resolve(ui5DataDir), snapshotEndpointUrlCb: Sapui5MavenSnapshotResolver._createSnapshotEndpointUrlCallback(snapshotEndpointUrl), }); return await installer.fetchPackageVersions({ diff --git a/packages/project/lib/ui5Framework/Sapui5Resolver.js b/packages/project/lib/ui5Framework/Sapui5Resolver.js index 0593cf734d7..980e7d29100 100644 --- a/packages/project/lib/ui5Framework/Sapui5Resolver.js +++ b/packages/project/lib/ui5Framework/Sapui5Resolver.js @@ -1,5 +1,4 @@ import path from "node:path"; -import os from "node:os"; import semver from "semver"; import AbstractResolver from "./AbstractResolver.js"; import Installer from "./npm/Installer.js"; @@ -106,22 +105,23 @@ class Sapui5Resolver extends AbstractResolver { }) }; } - static async fetchAllVersions(options) { - const installer = this._getInstaller(options); + static async fetchAllVersions(ui5DataDir, {cwd} = {}) { + const installer = this._getInstaller(ui5DataDir, {cwd}); return await installer.fetchPackageVersions({pkgName: DIST_PKG_NAME}); } - static async fetchAllTags(options) { - const installer = this._getInstaller(options); + static async fetchAllTags(ui5DataDir, {cwd} = {}) { + const installer = this._getInstaller(ui5DataDir, {cwd}); return installer.fetchPackageDistTags({pkgName: DIST_PKG_NAME}); } - static _getInstaller({ui5DataDir, cwd} = {}) { + static _getInstaller(ui5DataDir, {cwd} = {}) { + if (!ui5DataDir) { + throw new Error(`${this.name}: Missing parameter "ui5DataDir"`); + } return new Installer({ cwd: cwd ? path.resolve(cwd) : process.cwd(), - ui5DataDir: - ui5DataDir ? path.resolve(ui5DataDir) : - path.join(os.homedir(), ".ui5") + ui5DataDir: path.resolve(ui5DataDir) }); } } diff --git a/packages/project/test/lib/build/BuildServer.integration.js b/packages/project/test/lib/build/BuildServer.integration.js index 0abcf7765a9..c6dbd7f4ac3 100644 --- a/packages/project/test/lib/build/BuildServer.integration.js +++ b/packages/project/test/lib/build/BuildServer.integration.js @@ -1078,6 +1078,7 @@ class FixtureTester { async serveProject({graphConfig = {}, config = {}, expectBuildErrors = false} = {}) { const graph = this.graph = await graphFromPackageDependencies({ + ui5DataDir: "/path/to/ui5-data-dir", ...graphConfig, cwd: this.fixturePath, }); diff --git a/packages/project/test/lib/build/ProjectBuilder.integration.js b/packages/project/test/lib/build/ProjectBuilder.integration.js index c754b7213f1..a7ee2cfa835 100644 --- a/packages/project/test/lib/build/ProjectBuilder.integration.js +++ b/packages/project/test/lib/build/ProjectBuilder.integration.js @@ -2719,6 +2719,7 @@ class FixtureTester { this._sinon.resetHistory(); const graph = await graphFromPackageDependencies({ + ui5DataDir: "/path/to/ui5-data-dir", ...graphConfig, cwd: this.fixturePath, }); diff --git a/packages/project/test/lib/build/helpers/BuildContext.js b/packages/project/test/lib/build/helpers/BuildContext.js index 16832b98679..ea1d04c6e6e 100644 --- a/packages/project/test/lib/build/helpers/BuildContext.js +++ b/packages/project/test/lib/build/helpers/BuildContext.js @@ -315,7 +315,7 @@ test("getProjectContext", async (t) => { .returns({getType: () => "library", getRootPath: () => ""}); const graph = {getRoot: rootProjectStub, getProject: () => "project"}; - const buildContext = new BuildContext(graph, "taskRepository"); + const buildContext = new BuildContext(graph, "taskRepository", undefined, "/ui5DataDir"); const projectBuildContext = await buildContext.getProjectContext("project"); t.is(t.context.ProjectBuildContextCreateStub.callCount, 1); @@ -350,7 +350,7 @@ test("getCacheManager: Creates and caches CacheManager for default cache mode", const graph = { getRoot: () => ({getType: () => "library", getRootPath: () => "/some/path"}), }; - const buildContext = new BuildContext(graph, "taskRepository"); + const buildContext = new BuildContext(graph, "taskRepository", undefined, "/ui5DataDir"); const cacheManager1 = await buildContext.getCacheManager(); t.is(cacheManager1, cacheManagerInstance, "Returned CacheManager instance"); diff --git a/packages/project/test/lib/build/helpers/composeProjectList.js b/packages/project/test/lib/build/helpers/composeProjectList.js index 8556efcdfbb..906133c6bc5 100644 --- a/packages/project/test/lib/build/helpers/composeProjectList.js +++ b/packages/project/test/lib/build/helpers/composeProjectList.js @@ -80,7 +80,7 @@ test.serial("_getFlattenedDependencyTree", async (t) => { }] }] }; - const graph = await graphFromObject({dependencyTree: tree}); + const graph = await graphFromObject({ui5DataDir: "/path/to/ui5-data-dir", dependencyTree: tree}); t.deepEqual(await _getFlattenedDependencyTree(graph), { "library.e": ["library.d", "library.a", "library.b", "library.c"], @@ -162,7 +162,7 @@ async function assertCreateDependencyLists(t, { }] }; - const graph = await graphFromObject({dependencyTree: tree}); + const graph = await graphFromObject({ui5DataDir: "/path/to/ui5-data-dir", dependencyTree: tree}); const {includedDependencies, excludedDependencies} = await t.context.composeProjectList(graph, { includeAllDependencies, diff --git a/packages/project/test/lib/graph/graph.integration.js b/packages/project/test/lib/graph/graph.integration.js index fcff4e57957..0424d82630c 100644 --- a/packages/project/test/lib/graph/graph.integration.js +++ b/packages/project/test/lib/graph/graph.integration.js @@ -45,6 +45,7 @@ test.serial("graphFromPackageDependencies with workspace object", async (t) => { const {graphFromPackageDependencies} = t.context.graph; const res = await graphFromPackageDependencies({ + ui5DataDir: "/path/to/ui5-data-dir", cwd: "cwd", rootConfiguration: "rootConfiguration", rootConfigPath: "/rootConfigPath", @@ -66,7 +67,7 @@ test.serial("graphFromPackageDependencies with workspace object", async (t) => { t.is(npmProviderConstructorStub.callCount, 1, "NPM provider constructor got called once"); t.deepEqual(npmProviderConstructorStub.getCall(0).args[0], { - cwd: path.join(__dirname, "..", "..", "..", "cwd"), + cwd: path.resolve("cwd"), rootConfiguration: "rootConfiguration", rootConfigPath: "/rootConfigPath", }, "Created NodePackageDependencies provider instance with correct parameters"); @@ -94,6 +95,7 @@ test.serial("graphFromPackageDependencies with workspace object and workspace na const {graphFromPackageDependencies} = t.context.graph; const res = await graphFromPackageDependencies({ + ui5DataDir: "/path/to/ui5-data-dir", cwd: "cwd", rootConfiguration: "rootConfiguration", rootConfigPath: "/rootConfigPath", @@ -116,7 +118,7 @@ test.serial("graphFromPackageDependencies with workspace object and workspace na t.is(npmProviderConstructorStub.callCount, 1, "NPM provider constructor got called once"); t.deepEqual(npmProviderConstructorStub.getCall(0).args[0], { - cwd: path.join(__dirname, "..", "..", "..", "cwd"), + cwd: path.resolve("cwd"), rootConfiguration: "rootConfiguration", rootConfigPath: "/rootConfigPath", }, "Created NodePackageDependencies provider instance with correct parameters"); @@ -140,6 +142,7 @@ test.serial("graphFromPackageDependencies with workspace object not matching wor const {graphFromPackageDependencies} = t.context.graph; await t.throwsAsync(graphFromPackageDependencies({ + ui5DataDir: "/path/to/ui5-data-dir", cwd: "cwd", rootConfiguration: "rootConfiguration", rootConfigPath: "/rootConfigPath", @@ -169,6 +172,7 @@ test.serial("graphFromPackageDependencies with workspace file", async (t) => { const {graphFromPackageDependencies} = t.context.graph; const res = await graphFromPackageDependencies({ + ui5DataDir: "/path/to/ui5-data-dir", cwd: libraryHPath, rootConfiguration: "rootConfiguration", rootConfigPath: "/rootConfigPath", @@ -208,6 +212,7 @@ test.serial("graphFromPackageDependencies with workspace file at custom path", a const {graphFromPackageDependencies} = t.context.graph; const res = await graphFromPackageDependencies({ + ui5DataDir: "/path/to/ui5-data-dir", cwd: "cwd", rootConfiguration: "rootConfiguration", rootConfigPath: "/rootConfigPath", @@ -220,7 +225,7 @@ test.serial("graphFromPackageDependencies with workspace file at custom path", a t.is(npmProviderConstructorStub.callCount, 1, "NPM provider constructor got called once"); t.deepEqual(npmProviderConstructorStub.getCall(0).args[0], { - cwd: path.join(__dirname, "..", "..", "..", "cwd"), + cwd: path.resolve("cwd"), rootConfiguration: "rootConfiguration", rootConfigPath: "/rootConfigPath" }, "Created NodePackageDependencies provider instance with correct parameters"); @@ -248,6 +253,7 @@ test.serial("graphFromPackageDependencies with inactive workspace file at custom const {graphFromPackageDependencies} = t.context.graph; const res = await graphFromPackageDependencies({ + ui5DataDir: "/path/to/ui5-data-dir", cwd: "cwd", rootConfiguration: "rootConfiguration", rootConfigPath: "/rootConfigPath", @@ -261,7 +267,7 @@ test.serial("graphFromPackageDependencies with inactive workspace file at custom t.is(npmProviderConstructorStub.callCount, 1, "NPM provider constructor got called once"); t.deepEqual(npmProviderConstructorStub.getCall(0).args[0], { - cwd: path.join(__dirname, "..", "..", "..", "cwd"), + cwd: path.resolve("cwd"), rootConfiguration: "rootConfiguration", rootConfigPath: "/rootConfigPath" }, "Created NodePackageDependencies provider instance with correct parameters"); @@ -278,6 +284,7 @@ test.serial("graphFromPackageDependencies with inactive workspace file at custom t.deepEqual(enrichProjectGraphStub.getCall(0).args[1], { versionOverride: "versionOverride", workspace: null, - snapshotCache: "Force" + snapshotCache: "Force", + ui5DataDir: "/path/to/ui5-data-dir" }, "enrichProjectGraph got called with correct options"); }); diff --git a/packages/project/test/lib/graph/graph.js b/packages/project/test/lib/graph/graph.js index 799033a59db..f901f88b98a 100644 --- a/packages/project/test/lib/graph/graph.js +++ b/packages/project/test/lib/graph/graph.js @@ -54,6 +54,7 @@ test.serial("graphFromPackageDependencies", async (t) => { const {graphFromPackageDependencies} = t.context.graph; const res = await graphFromPackageDependencies({ + ui5DataDir: "/path/to/ui5-data-dir", cwd: "cwd", rootConfiguration: "rootConfiguration", rootConfigPath: "/rootConfigPath", @@ -67,7 +68,7 @@ test.serial("graphFromPackageDependencies", async (t) => { t.is(createWorkspaceStub.callCount, 0, "createWorkspace did not get called"); t.is(npmProviderConstructorStub.callCount, 1, "NPM provider constructor got called once"); t.deepEqual(npmProviderConstructorStub.getCall(0).args[0], { - cwd: path.join(__dirname, "..", "..", "..", "cwd"), + cwd: path.resolve("cwd"), rootConfiguration: "rootConfiguration", rootConfigPath: "/rootConfigPath" }, "Created NodePackageDependencies provider instance with correct parameters"); @@ -84,7 +85,8 @@ test.serial("graphFromPackageDependencies", async (t) => { t.deepEqual(enrichProjectGraphStub.getCall(0).args[1], { versionOverride: "versionOverride", workspace: undefined, - snapshotCache: "Off" + snapshotCache: "Off", + ui5DataDir: "/path/to/ui5-data-dir" }, "enrichProjectGraph got called with correct options"); }); @@ -96,6 +98,7 @@ test.serial("graphFromPackageDependencies with workspace name", async (t) => { const {graphFromPackageDependencies} = t.context.graph; const res = await graphFromPackageDependencies({ + ui5DataDir: "/path/to/ui5-data-dir", cwd: "cwd", rootConfiguration: "rootConfiguration", rootConfigPath: "/rootConfigPath", @@ -108,7 +111,7 @@ test.serial("graphFromPackageDependencies with workspace name", async (t) => { t.is(createWorkspaceStub.callCount, 1, "createWorkspace got called once"); t.deepEqual(createWorkspaceStub.getCall(0).args[0], { - cwd: path.join(__dirname, "..", "..", "..", "cwd"), + cwd: path.resolve("cwd"), name: "dolphin", configPath: "ui5-workspace.yaml", configObject: undefined, @@ -116,7 +119,7 @@ test.serial("graphFromPackageDependencies with workspace name", async (t) => { t.is(npmProviderConstructorStub.callCount, 1, "NPM provider constructor got called once"); t.deepEqual(npmProviderConstructorStub.getCall(0).args[0], { - cwd: path.join(__dirname, "..", "..", "..", "cwd"), + cwd: path.resolve("cwd"), rootConfiguration: "rootConfiguration", rootConfigPath: "/rootConfigPath", }, "Created NodePackageDependencies provider instance with correct parameters"); @@ -133,7 +136,8 @@ test.serial("graphFromPackageDependencies with workspace name", async (t) => { t.deepEqual(enrichProjectGraphStub.getCall(0).args[1], { versionOverride: "versionOverride", workspace: "workspace", - snapshotCache: "Off" + snapshotCache: "Off", + ui5DataDir: "/path/to/ui5-data-dir" }, "enrichProjectGraph got called with correct options"); }); @@ -144,6 +148,7 @@ test.serial("graphFromPackageDependencies with workspace object", async (t) => { const {graphFromPackageDependencies} = t.context.graph; const res = await graphFromPackageDependencies({ + ui5DataDir: "/path/to/ui5-data-dir", cwd: "cwd", rootConfiguration: "rootConfiguration", rootConfigPath: "/rootConfigPath", @@ -156,7 +161,7 @@ test.serial("graphFromPackageDependencies with workspace object", async (t) => { t.is(createWorkspaceStub.callCount, 1, "createWorkspace got called once"); t.deepEqual(createWorkspaceStub.getCall(0).args[0], { - cwd: path.join(__dirname, "..", "..", "..", "cwd"), + cwd: path.resolve("cwd"), configPath: "ui5-workspace.yaml", name: null, configObject: "workspaceConfiguration" @@ -170,6 +175,7 @@ test.serial("graphFromPackageDependencies with workspace object and workspace na const {graphFromPackageDependencies} = t.context.graph; const res = await graphFromPackageDependencies({ + ui5DataDir: "/path/to/ui5-data-dir", cwd: "cwd", rootConfiguration: "rootConfiguration", rootConfigPath: "/rootConfigPath", @@ -182,7 +188,7 @@ test.serial("graphFromPackageDependencies with workspace object and workspace na t.is(createWorkspaceStub.callCount, 1, "createWorkspace got called once"); t.deepEqual(createWorkspaceStub.getCall(0).args[0], { - cwd: path.join(__dirname, "..", "..", "..", "cwd"), + cwd: path.resolve("cwd"), name: "dolphin", configPath: "ui5-workspace.yaml", configObject: "workspaceConfiguration" @@ -196,6 +202,7 @@ test.serial("graphFromPackageDependencies with workspace path and workspace name const {graphFromPackageDependencies} = t.context.graph; const res = await graphFromPackageDependencies({ + ui5DataDir: "/path/to/ui5-data-dir", cwd: "cwd", rootConfiguration: "rootConfiguration", rootConfigPath: "/rootConfigPath", @@ -208,7 +215,7 @@ test.serial("graphFromPackageDependencies with workspace path and workspace name t.is(createWorkspaceStub.callCount, 1, "createWorkspace got called once"); t.deepEqual(createWorkspaceStub.getCall(0).args[0], { - cwd: path.join(__dirname, "..", "..", "..", "cwd"), + cwd: path.resolve("cwd"), name: "dolphin", configPath: "workspaceConfigurationPath", configObject: undefined @@ -226,6 +233,7 @@ test.serial("graphFromPackageDependencies with empty workspace", async (t) => { createWorkspaceStub.resolves(null); const res = await graphFromPackageDependencies({ + ui5DataDir: "/path/to/ui5-data-dir", cwd: "cwd", rootConfiguration: "rootConfiguration", rootConfigPath: "/rootConfigPath", @@ -238,7 +246,7 @@ test.serial("graphFromPackageDependencies with empty workspace", async (t) => { t.is(createWorkspaceStub.callCount, 1, "createWorkspace got called once"); t.deepEqual(createWorkspaceStub.getCall(0).args[0], { - cwd: path.join(__dirname, "..", "..", "..", "cwd"), + cwd: path.resolve("cwd"), name: "dolphin", configPath: "ui5-workspace.yaml", configObject: undefined, @@ -246,7 +254,7 @@ test.serial("graphFromPackageDependencies with empty workspace", async (t) => { t.is(npmProviderConstructorStub.callCount, 1, "NPM provider constructor got called once"); t.deepEqual(npmProviderConstructorStub.getCall(0).args[0], { - cwd: path.join(__dirname, "..", "..", "..", "cwd"), + cwd: path.resolve("cwd"), rootConfiguration: "rootConfiguration", rootConfigPath: "/rootConfigPath", }, "Created NodePackageDependencies provider instance with correct parameters"); @@ -263,7 +271,8 @@ test.serial("graphFromPackageDependencies with empty workspace", async (t) => { t.deepEqual(enrichProjectGraphStub.getCall(0).args[1], { versionOverride: "versionOverride", workspace: null, - snapshotCache: "Off" + snapshotCache: "Off", + ui5DataDir: "/path/to/ui5-data-dir" }, "enrichProjectGraph got called with correct options"); }); @@ -272,6 +281,7 @@ test.serial("graphFromPackageDependencies: Do not resolve framework dependencies const {graphFromPackageDependencies} = t.context.graph; const res = await graphFromPackageDependencies({ + ui5DataDir: "/path/to/ui5-data-dir", cwd: "cwd", rootConfiguration: "rootConfiguration", rootConfigPath: "/rootConfigPath", @@ -288,6 +298,7 @@ test.serial("graphFromPackageDependencies: Default workspace name", async (t) => const {graphFromPackageDependencies} = t.context.graph; const res = await graphFromPackageDependencies({ + ui5DataDir: "/path/to/ui5-data-dir", cwd: "cwd", rootConfiguration: "rootConfiguration", rootConfigPath: "/rootConfigPath", @@ -312,6 +323,7 @@ test.serial("graphFromStaticFile", async (t) => { .resolves("dependencyTree"); const res = await graphFromStaticFile({ + ui5DataDir: "/path/to/ui5-data-dir", cwd: "cwd", filePath: "file/path", rootConfiguration: "rootConfiguration", @@ -323,7 +335,7 @@ test.serial("graphFromStaticFile", async (t) => { t.is(res, "graph"); t.is(readDependencyConfigFileStub.callCount, 1, "_readDependencyConfigFile got called once"); - t.is(readDependencyConfigFileStub.getCall(0).args[0], path.join(__dirname, "..", "..", "..", "cwd"), + t.is(readDependencyConfigFileStub.getCall(0).args[0], path.resolve("cwd"), "_readDependencyConfigFile got called with correct directory"); t.is(readDependencyConfigFileStub.getCall(0).args[1], "file/path", "_readDependencyConfigFile got called with correct file path"); @@ -344,7 +356,8 @@ test.serial("graphFromStaticFile", async (t) => { "enrichProjectGraph got called with graph"); t.deepEqual(enrichProjectGraphStub.getCall(0).args[1], { versionOverride: "versionOverride", - snapshotCache: "Off" + snapshotCache: "Off", + ui5DataDir: "/path/to/ui5-data-dir" }, "enrichProjectGraph got called with correct options"); }); @@ -356,6 +369,7 @@ test.serial("graphFromStaticFile: Do not resolve framework dependencies", async .resolves("dependencyTree"); const res = await graphFromStaticFile({ + ui5DataDir: "/path/to/ui5-data-dir", cwd: "cwd", filePath: "filePath", rootConfiguration: "rootConfiguration", @@ -376,6 +390,7 @@ test.serial("usingObject", async (t) => { const {graphFromObject} = t.context.graph; const res = await graphFromObject({ + ui5DataDir: "/path/to/ui5-data-dir", dependencyTree: "dependencyTree", rootConfiguration: "rootConfiguration", rootConfigPath: "/rootConfigPath", @@ -401,7 +416,8 @@ test.serial("usingObject", async (t) => { "enrichProjectGraph got called with graph"); t.deepEqual(enrichProjectGraphStub.getCall(0).args[1], { versionOverride: "versionOverride", - snapshotCache: "Off" + snapshotCache: "Off", + ui5DataDir: "/path/to/ui5-data-dir" }, "enrichProjectGraph got called with correct options"); }); @@ -409,6 +425,7 @@ test.serial("usingObject: Do not resolve framework dependencies", async (t) => { const {enrichProjectGraphStub} = t.context; const {graphFromObject} = t.context.graph; const res = await graphFromObject({ + ui5DataDir: "/path/to/ui5-data-dir", cwd: "cwd", filePath: "filePath", rootConfiguration: "rootConfiguration", diff --git a/packages/project/test/lib/graph/graphFromObject.js b/packages/project/test/lib/graph/graphFromObject.js index e331fdb4033..49733fe8fc9 100644 --- a/packages/project/test/lib/graph/graphFromObject.js +++ b/packages/project/test/lib/graph/graphFromObject.js @@ -48,14 +48,20 @@ test.afterEach.always((t) => { test("Application A", async (t) => { const {graphFromObject} = t.context; - const projectGraph = await graphFromObject({dependencyTree: getApplicationATree()}); + const projectGraph = await graphFromObject({ + ui5DataDir: "/path/to/ui5-data-dir", + dependencyTree: getApplicationATree() + }); const rootProject = projectGraph.getRoot(); t.is(rootProject.getName(), "application.a", "Returned correct root project"); }); test("Application A: Traverse project graph breadth first", async (t) => { const {graphFromObject} = t.context; - const projectGraph = await graphFromObject({dependencyTree: getApplicationATree()}); + const projectGraph = await graphFromObject({ + ui5DataDir: "/path/to/ui5-data-dir", + dependencyTree: getApplicationATree() + }); const callbackStub = t.context.sinon.stub().resolves(); await projectGraph.traverseBreadthFirst(callbackStub); @@ -74,7 +80,10 @@ test("Application A: Traverse project graph breadth first", async (t) => { test("Application Cycle A: Traverse project graph breadth first with cycles", async (t) => { const {graphFromObject, sinon} = t.context; - const projectGraph = await graphFromObject({dependencyTree: applicationCycleATreeIncDeduped}); + const projectGraph = await graphFromObject({ + ui5DataDir: "/path/to/ui5-data-dir", + dependencyTree: applicationCycleATreeIncDeduped + }); const callbackStub = sinon.stub().resolves(); const error = await t.throwsAsync(projectGraph.traverseBreadthFirst(callbackStub)); @@ -96,7 +105,10 @@ test("Application Cycle A: Traverse project graph breadth first with cycles", as test("Application Cycle B: Traverse project graph breadth first with cycles", async (t) => { const {graphFromObject, sinon} = t.context; - const projectGraph = await graphFromObject({dependencyTree: applicationCycleBTreeIncDeduped}); + const projectGraph = await graphFromObject({ + ui5DataDir: "/path/to/ui5-data-dir", + dependencyTree: applicationCycleBTreeIncDeduped + }); const callbackStub = sinon.stub().resolves(); await projectGraph.traverseBreadthFirst(callbackStub); @@ -115,7 +127,10 @@ test("Application Cycle B: Traverse project graph breadth first with cycles", as test("Application A: Traverse project graph depth first", async (t) => { const {graphFromObject, sinon} = t.context; - const projectGraph = await graphFromObject({dependencyTree: getApplicationATree()}); + const projectGraph = await graphFromObject({ + ui5DataDir: "/path/to/ui5-data-dir", + dependencyTree: getApplicationATree() + }); const callbackStub = sinon.stub().resolves(); await projectGraph.traverseDepthFirst(callbackStub); @@ -136,7 +151,10 @@ test("Application A: Traverse project graph depth first", async (t) => { test("Application Cycle A: Traverse project graph depth first with cycles", async (t) => { const {graphFromObject, sinon} = t.context; - const projectGraph = await graphFromObject({dependencyTree: applicationCycleATreeIncDeduped}); + const projectGraph = await graphFromObject({ + ui5DataDir: "/path/to/ui5-data-dir", + dependencyTree: applicationCycleATreeIncDeduped + }); const callbackStub = sinon.stub().resolves(); const error = await t.throwsAsync(projectGraph.traverseDepthFirst(callbackStub)); @@ -150,7 +168,10 @@ test("Application Cycle A: Traverse project graph depth first with cycles", asyn test("Application Cycle B: Traverse project graph depth first with cycles", async (t) => { const {graphFromObject, sinon} = t.context; - const projectGraph = await graphFromObject({dependencyTree: applicationCycleBTreeIncDeduped}); + const projectGraph = await graphFromObject({ + ui5DataDir: "/path/to/ui5-data-dir", + dependencyTree: applicationCycleBTreeIncDeduped + }); const callbackStub = sinon.stub().resolves(); const error = await t.throwsAsync(projectGraph.traverseDepthFirst(callbackStub)); @@ -179,7 +200,7 @@ async function _testBasicGraphCreation(t, tree, expectedOrder, bfs) { throw new Error("Test error: Parameter 'bfs' must be specified"); } const {graphFromObject, sinon} = t.context; - const projectGraph = await graphFromObject({dependencyTree: tree}); + const projectGraph = await graphFromObject({ui5DataDir: "/path/to/ui5-data-dir", dependencyTree: tree}); const callbackStub = sinon.stub().resolves(); if (bfs) { await projectGraph.traverseBreadthFirst(callbackStub); @@ -258,7 +279,7 @@ test("Project with inline configuration for two projects", async (t) => { }] }; - await t.throwsAsync(graphFromObject({dependencyTree: tree}), + await t.throwsAsync(graphFromObject({ui5DataDir: "/path/to/ui5-data-dir", dependencyTree: tree}), { message: `Found 2 configurations of kind 'project' for module application.a.id. ` + @@ -315,7 +336,7 @@ test("Missing configuration file for root project", async (t) => { path: "/non-existent", dependencies: [] }; - await t.throwsAsync(graphFromObject({dependencyTree: tree}), + await t.throwsAsync(graphFromObject({ui5DataDir: "/path/to/ui5-data-dir", dependencyTree: tree}), { message: "Failed to create a UI5 project from module application.a.id at /non-existent. " + @@ -330,7 +351,7 @@ test("Missing id for root project", async (t) => { path: path.join(__dirname, "fixtures/application.a"), dependencies: [] }; - await t.throwsAsync(graphFromObject({dependencyTree: tree}), + await t.throwsAsync(graphFromObject({ui5DataDir: "/path/to/ui5-data-dir", dependencyTree: tree}), {message: "Could not create Module: Missing or empty parameter 'id'"}, "Rejected with error"); }); @@ -349,7 +370,7 @@ test("No type configured for root project", async (t) => { } } }; - const error = await t.throwsAsync(graphFromObject({dependencyTree: tree})); + const error = await t.throwsAsync(graphFromObject({ui5DataDir: "/path/to/ui5-data-dir", dependencyTree: tree})); t.is(error.message, `Unable to create Specification instance: Unknown specification type 'undefined'`); }); @@ -361,7 +382,7 @@ test("Missing dependencies", async (t) => { version: "1.0.0", path: applicationAPath }); - await t.notThrowsAsync(graphFromObject({dependencyTree: tree}), + await t.notThrowsAsync(graphFromObject({ui5DataDir: "/path/to/ui5-data-dir", dependencyTree: tree}), "Gracefully accepted project with no dependencies attribute"); }); @@ -377,7 +398,7 @@ test("Missing second-level dependencies", async (t) => { path: path.join(applicationAPath, "node_modules", "library.d") }] }); - await t.notThrowsAsync(graphFromObject({dependencyTree: tree}), + await t.notThrowsAsync(graphFromObject({ui5DataDir: "/path/to/ui5-data-dir", dependencyTree: tree}), "Gracefully accepted project with no dependencies attribute"); }); @@ -1268,7 +1289,10 @@ test("Project with project-shim extension with invalid dependency configuration" dependencies: [] }] }; - const validationError = await t.throwsAsync(graphFromObject({dependencyTree: tree}), { + const validationError = await t.throwsAsync(graphFromObject({ + ui5DataDir: "/path/to/ui5-data-dir", + dependencyTree: tree + }), { instanceOf: ValidationError }); t.true(validationError.message.includes("Configuration must have required property 'metadata'"), @@ -1552,7 +1576,10 @@ test("Project with unknown extension dependency inline configuration", async (t) dependencies: [], }], }; - const validationError = await t.throwsAsync(graphFromObject({dependencyTree: tree})); + const validationError = await t.throwsAsync(graphFromObject({ + ui5DataDir: "/path/to/ui5-data-dir", + dependencyTree: tree + })); t.is(validationError.message, `Unable to create Specification instance: Unknown specification type 'phony-pony'`, "Should throw with expected error message"); @@ -1633,6 +1660,7 @@ test("Project with middleware extension dependency", async (t) => { test("rootConfiguration", async (t) => { const {graphFromObject} = t.context; const projectGraph = await graphFromObject({ + ui5DataDir: "/path/to/ui5-data-dir", dependencyTree: getApplicationATree(), rootConfiguration: { specVersion: "2.6", @@ -1654,6 +1682,7 @@ test("rootConfiguration", async (t) => { test("rootConfig", async (t) => { const {graphFromObject} = t.context; const projectGraph = await graphFromObject({ + ui5DataDir: "/path/to/ui5-data-dir", dependencyTree: getApplicationATree(), cwd: applicationAPath, rootConfigPath: "ui5-test-configPath.yaml", diff --git a/packages/project/test/lib/graph/graphFromPackageDependencies.js b/packages/project/test/lib/graph/graphFromPackageDependencies.js index 491cc442ed5..1de2a3fd7c0 100644 --- a/packages/project/test/lib/graph/graphFromPackageDependencies.js +++ b/packages/project/test/lib/graph/graphFromPackageDependencies.js @@ -15,13 +15,19 @@ test.afterEach.always((t) => { }); test("Application A", async (t) => { - const projectGraph = await graphFromPackageDependencies({cwd: applicationAPath}); + const projectGraph = await graphFromPackageDependencies({ + ui5DataDir: "/path/to/ui5-data-dir", + cwd: applicationAPath + }); const rootProject = projectGraph.getRoot(); t.is(rootProject.getName(), "application.a", "Returned correct root project"); }); test("Application A: Traverse project graph breadth first", async (t) => { - const projectGraph = await graphFromPackageDependencies({cwd: applicationAPath}); + const projectGraph = await graphFromPackageDependencies({ + ui5DataDir: "/path/to/ui5-data-dir", + cwd: applicationAPath + }); const callbackStub = t.context.sinon.stub().resolves(); await projectGraph.traverseBreadthFirst(callbackStub); diff --git a/packages/project/test/lib/graph/graphFromStaticFile.js b/packages/project/test/lib/graph/graphFromStaticFile.js index acff5761ab3..7eca21416c3 100644 --- a/packages/project/test/lib/graph/graphFromStaticFile.js +++ b/packages/project/test/lib/graph/graphFromStaticFile.js @@ -20,6 +20,7 @@ test.afterEach.always((t) => { test("Application H: Traverse project graph breadth first", async (t) => { const projectGraph = await graphFromStaticFile({ + ui5DataDir: "/path/to/ui5-data-dir", cwd: applicationHPath }); const callbackStub = t.context.sinon.stub().resolves(); @@ -37,6 +38,7 @@ test("Application H: Traverse project graph breadth first", async (t) => { test("Throws error if file not found", async (t) => { const err = await t.throwsAsync(graphFromStaticFile({ + ui5DataDir: "/path/to/ui5-data-dir", cwd: notExistingPath })); t.is(err.message, @@ -48,6 +50,7 @@ test("Throws error if file not found", async (t) => { test("Throws for missing id", async (t) => { const err = await t.throwsAsync(graphFromStaticFile({ + ui5DataDir: "/path/to/ui5-data-dir", cwd: applicationHPath, filePath: "projectDependencies-missing-id.yaml" })); @@ -60,6 +63,7 @@ test("Throws for missing id", async (t) => { test("Throws for missing version", async (t) => { const err = await t.throwsAsync(graphFromStaticFile({ + ui5DataDir: "/path/to/ui5-data-dir", cwd: applicationHPath, filePath: "projectDependencies-missing-version.yaml" })); @@ -72,6 +76,7 @@ test("Throws for missing version", async (t) => { test("Throws for missing path", async (t) => { const err = await t.throwsAsync(graphFromStaticFile({ + ui5DataDir: "/path/to/ui5-data-dir", cwd: applicationHPath, filePath: "projectDependencies-missing-path.yaml" })); @@ -84,6 +89,7 @@ test("Throws for missing path", async (t) => { test("rootConfiguration", async (t) => { const projectGraph = await graphFromStaticFile({ + ui5DataDir: "/path/to/ui5-data-dir", cwd: applicationHPath, rootConfiguration: { specVersion: "2.6", @@ -103,6 +109,7 @@ test("rootConfiguration", async (t) => { test("rootConfig", async (t) => { const projectGraph = await graphFromStaticFile({ + ui5DataDir: "/path/to/ui5-data-dir", cwd: applicationHPath, rootConfigPath: "../application.a/ui5-test-configPath.yaml" }); diff --git a/packages/project/test/lib/graph/helpers/ui5Framework.integration.js b/packages/project/test/lib/graph/helpers/ui5Framework.integration.js index 13d867c969b..e08b2bc58d7 100644 --- a/packages/project/test/lib/graph/helpers/ui5Framework.integration.js +++ b/packages/project/test/lib/graph/helpers/ui5Framework.integration.js @@ -11,6 +11,7 @@ const __dirname = import.meta.dirname; const fakeBaseDir = path.join(__dirname, "fake-tmp"); const ui5FrameworkBaseDir = path.join(fakeBaseDir, "homedir", ".ui5", "framework"); const ui5PackagesBaseDir = path.join(ui5FrameworkBaseDir, "packages"); +const defaultUi5DataDir = path.join(fakeBaseDir, "homedir", ".ui5"); test.beforeEach(async (t) => { const sinon = t.context.sinon = sinonGlobal.createSandbox(); @@ -136,9 +137,6 @@ test.beforeEach(async (t) => { "../../../../lib/ui5Framework/Openui5Resolver.js": t.context.Openui5Resolver, "../../../../lib/ui5Framework/Sapui5Resolver.js": t.context.Sapui5Resolver, "../../../../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", { @@ -496,9 +494,9 @@ function defineTest(testName, { getModuleByProjectName }; - await ui5Framework.enrichProjectGraph(projectGraph, {workspace}); + await ui5Framework.enrichProjectGraph(projectGraph, {workspace, ui5DataDir: defaultUi5DataDir}); } else { - await ui5Framework.enrichProjectGraph(projectGraph); + await ui5Framework.enrichProjectGraph(projectGraph, {ui5DataDir: defaultUi5DataDir}); } const callbackStub = sinon.stub().resolves(); @@ -707,7 +705,7 @@ function defineErrorTest(testName, { const provider = new DependencyTreeProvider({dependencyTree}); const projectGraph = await projectGraphBuilder(provider); await t.throwsAsync(async () => { - await ui5Framework.enrichProjectGraph(projectGraph); + await ui5Framework.enrichProjectGraph(projectGraph, {ui5DataDir: defaultUi5DataDir}); }, {message: expectedErrorMessage}); }); } @@ -785,7 +783,7 @@ test.serial("ui5Framework helper should not fail when no framework configuration }; const provider = new DependencyTreeProvider({dependencyTree}); const projectGraph = await projectGraphBuilder(provider); - await ui5Framework.enrichProjectGraph(projectGraph); + await ui5Framework.enrichProjectGraph(projectGraph, {ui5DataDir: defaultUi5DataDir}); t.is(projectGraph, projectGraph, "Returned same graph without error"); }); @@ -813,7 +811,7 @@ test.serial("ui5Framework translator should not try to install anything when no const provider = new DependencyTreeProvider({dependencyTree}); const projectGraph = await projectGraphBuilder(provider); - await ui5Framework.enrichProjectGraph(projectGraph); + await ui5Framework.enrichProjectGraph(projectGraph, {ui5DataDir: defaultUi5DataDir}); t.is(pacote.extract.callCount, 0, "No package should be extracted"); t.is(pacote.manifest.callCount, 0, "No manifest should be requested"); @@ -841,7 +839,7 @@ test.serial("ui5Framework helper shouldn't throw when framework version and libr const provider = new DependencyTreeProvider({dependencyTree}); const projectGraph = await projectGraphBuilder(provider); - await ui5Framework.enrichProjectGraph(projectGraph); + await ui5Framework.enrichProjectGraph(projectGraph, {ui5DataDir: defaultUi5DataDir}); t.is(logStub.verbose.callCount, 5); t.deepEqual(logStub.verbose.getCall(0).args, [ @@ -918,7 +916,7 @@ test.serial( }); await t.throwsAsync(async () => { - await ui5Framework.enrichProjectGraph(projectGraph); + await ui5Framework.enrichProjectGraph(projectGraph, {ui5DataDir: defaultUi5DataDir}); }, { message: `Failed to resolve library does.not.exist: Could not find library "does.not.exist"`}); }); diff --git a/packages/project/test/lib/graph/helpers/ui5Framework.js b/packages/project/test/lib/graph/helpers/ui5Framework.js index 3d9e91a7820..22176471ee9 100644 --- a/packages/project/test/lib/graph/helpers/ui5Framework.js +++ b/packages/project/test/lib/graph/helpers/ui5Framework.js @@ -13,6 +13,14 @@ const applicationAPath = path.join(__dirname, "..", "..", "..", "fixtures", "app const libraryDPath = path.join(__dirname, "..", "..", "..", "fixtures", "library.d"); const libraryEPath = path.join(__dirname, "..", "..", "..", "fixtures", "library.e"); const libraryFPath = path.join(__dirname, "..", "..", "..", "fixtures", "library.f"); +const defaultUi5DataDir = path.resolve("fake-ui5-data-dir"); + +function withUi5DataDir(options = {}) { + return { + ui5DataDir: defaultUi5DataDir, + ...options + }; +} test.beforeEach(async (t) => { // Tests either rely on not having UI5_DATA_DIR defined, or explicitly define it @@ -123,7 +131,7 @@ test.serial("enrichProjectGraph", async (t) => { const provider = new DependencyTreeProvider({dependencyTree}); const projectGraph = await projectGraphBuilder(provider); - await ui5Framework.enrichProjectGraph(projectGraph); + await ui5Framework.enrichProjectGraph(projectGraph, withUi5DataDir()); t.is(getFrameworkLibrariesFromGraphStub.callCount, 1, "getFrameworkLibrariesFromGraph should be called once"); @@ -191,7 +199,7 @@ test.serial("enrichProjectGraph: without framework configuration", async (t) => const provider = new DependencyTreeProvider({dependencyTree}); const projectGraph = await projectGraphBuilder(provider); - await ui5Framework.enrichProjectGraph(projectGraph); + await ui5Framework.enrichProjectGraph(projectGraph, withUi5DataDir()); t.is(projectGraph.getSize(), 1, "Project graph should remain unchanged"); t.is(log.verbose.callCount, 1); t.deepEqual(log.verbose.getCall(0).args, [ @@ -239,9 +247,9 @@ test.serial("enrichProjectGraph SNAPSHOT", async (t) => { const provider = new DependencyTreeProvider({dependencyTree}); const projectGraph = await projectGraphBuilder(provider); - await ui5Framework.enrichProjectGraph(projectGraph, { + await ui5Framework.enrichProjectGraph(projectGraph, withUi5DataDir({ snapshotCache: SnapshotCache.Force - }); + })); t.is(getFrameworkLibrariesFromGraphStub.callCount, 1, "getFrameworkLibrariesFromGraph should be called once"); @@ -332,7 +340,7 @@ test.serial("enrichProjectGraph: With versionOverride", async (t) => { const provider = new DependencyTreeProvider({dependencyTree}); const projectGraph = await projectGraphBuilder(provider); - await ui5Framework.enrichProjectGraph(projectGraph, {versionOverride: "1.99"}); + await ui5Framework.enrichProjectGraph(projectGraph, withUi5DataDir({versionOverride: "1.99"})); t.is(Sapui5ResolverResolveVersionStub.callCount, 1); t.deepEqual(Sapui5ResolverResolveVersionStub.getCall(0).args, ["1.99", { @@ -394,7 +402,7 @@ test.serial("enrichProjectGraph: With versionOverride containing snapshot versio const provider = new DependencyTreeProvider({dependencyTree}); const projectGraph = await projectGraphBuilder(provider); - await ui5Framework.enrichProjectGraph(projectGraph, {versionOverride: "1.99-SNAPSHOT"}); + await ui5Framework.enrichProjectGraph(projectGraph, withUi5DataDir({versionOverride: "1.99-SNAPSHOT"})); t.is(Sapui5MavenSnapshotResolverResolveVersionStub.callCount, 1); t.deepEqual(Sapui5MavenSnapshotResolverResolveVersionStub.getCall(0).args, ["1.99-SNAPSHOT", { @@ -457,7 +465,7 @@ test.serial("enrichProjectGraph: With versionOverride containing latest-snapshot const provider = new DependencyTreeProvider({dependencyTree}); const projectGraph = await projectGraphBuilder(provider); - await ui5Framework.enrichProjectGraph(projectGraph, {versionOverride: "latest-snapshot"}); + await ui5Framework.enrichProjectGraph(projectGraph, withUi5DataDir({versionOverride: "latest-snapshot"})); t.is(Sapui5MavenSnapshotResolverResolveVersionStub.callCount, 1); t.deepEqual(Sapui5MavenSnapshotResolverResolveVersionStub.getCall(0).args, ["latest-snapshot", { @@ -498,9 +506,9 @@ test.serial("enrichProjectGraph shouldn't throw when no framework version and no const projectGraph = await projectGraphBuilder(provider); // Framework override is fine, even if no framework version is configured - await ui5Framework.enrichProjectGraph(projectGraph, { + await ui5Framework.enrichProjectGraph(projectGraph, withUi5DataDir({ versionOverride: "1.75.0" - }); + })); t.is(Sapui5ResolverResolveVersionStub.callCount, 0, "resolveVersion should not be called when no libraries are provided"); @@ -534,7 +542,7 @@ test.serial("enrichProjectGraph should skip framework project without version", const provider = new DependencyTreeProvider({dependencyTree}); const projectGraph = await projectGraphBuilder(provider); - await ui5Framework.enrichProjectGraph(projectGraph); + await ui5Framework.enrichProjectGraph(projectGraph, withUi5DataDir()); t.is(projectGraph.getSize(), 1, "Project graph should remain unchanged"); }); @@ -622,7 +630,7 @@ test.serial("enrichProjectGraph should resolve framework project with version an const provider = new DependencyTreeProvider({dependencyTree}); const projectGraph = await projectGraphBuilder(provider); - await ui5Framework.enrichProjectGraph(projectGraph); + await ui5Framework.enrichProjectGraph(projectGraph, withUi5DataDir()); t.is(projectGraph.getSize(), 3, "Project graph should remain unchanged"); t.is(getFrameworkLibrariesFromGraphStub.callCount, 1, "getFrameworkLibrariesFromGrap should be called once"); @@ -721,7 +729,7 @@ test.serial("enrichProjectGraph should resolve framework project " + const provider = new DependencyTreeProvider({dependencyTree}); const projectGraph = await projectGraphBuilder(provider); - await ui5Framework.enrichProjectGraph(projectGraph, {versionOverride: "3.4.5"}); + await ui5Framework.enrichProjectGraph(projectGraph, withUi5DataDir({versionOverride: "3.4.5"})); t.is(projectGraph.getSize(), 3, "Project graph should remain unchanged"); t.is(Sapui5ResolverResolveVersionStub.callCount, 1); @@ -777,7 +785,7 @@ test.serial("enrichProjectGraph should skip framework project when all dependenc const provider = new DependencyTreeProvider({dependencyTree}); const projectGraph = await projectGraphBuilder(provider); - await ui5Framework.enrichProjectGraph(projectGraph); + await ui5Framework.enrichProjectGraph(projectGraph, withUi5DataDir()); t.is(projectGraph.getSize(), 2, "Project graph should remain unchanged"); }); @@ -811,7 +819,7 @@ test.serial("enrichProjectGraph should throw for framework project with dependen const provider = new DependencyTreeProvider({dependencyTree}); const projectGraph = await projectGraphBuilder(provider); - const err = await t.throwsAsync(ui5Framework.enrichProjectGraph(projectGraph)); + const err = await t.throwsAsync(ui5Framework.enrichProjectGraph(projectGraph, withUi5DataDir())); t.is(err.message, installError.message); }); @@ -844,7 +852,7 @@ test.serial("enrichProjectGraph should throw for incorrect framework name", asyn const projectGraph = await projectGraphBuilder(provider); sinon.stub(projectGraph.getRoot(), "getFrameworkName").returns("Pony5"); - const err = await t.throwsAsync(ui5Framework.enrichProjectGraph(projectGraph)); + const err = await t.throwsAsync(ui5Framework.enrichProjectGraph(projectGraph, withUi5DataDir())); t.is(err.message, `Unknown framework.name "Pony5" for project application.a. Must be "OpenUI5" or "SAPUI5"`, "Threw with expected error message"); }); @@ -866,7 +874,7 @@ test.serial("enrichProjectGraph should ignore root project without framework con const provider = new DependencyTreeProvider({dependencyTree}); const projectGraph = await projectGraphBuilder(provider); - await ui5Framework.enrichProjectGraph(projectGraph); + await ui5Framework.enrichProjectGraph(projectGraph, withUi5DataDir()); t.is(projectGraph.getSize(), 1, "Project graph should remain unchanged"); }); @@ -941,7 +949,7 @@ test.serial("enrichProjectGraph should throw error when projectGraph contains a const provider = new DependencyTreeProvider({dependencyTree}); const projectGraph = await projectGraphBuilder(provider); - await t.throwsAsync(ui5Framework.enrichProjectGraph(projectGraph), { + await t.throwsAsync(ui5Framework.enrichProjectGraph(projectGraph, withUi5DataDir()), { message: `Duplicate framework dependency definition(s) found for project application.a: sap.ui.core.\n` + `Framework libraries should only be referenced via ui5.yaml configuration. Neither the root project, ` + `nor any of its dependencies should include them as direct dependencies (e.g. via package.json).` @@ -994,7 +1002,7 @@ test.serial("enrichProjectGraph should use framework library metadata from works const provider = new DependencyTreeProvider({dependencyTree}); const projectGraph = await projectGraphBuilder(provider, {workspace}); - await ui5Framework.enrichProjectGraph(projectGraph, {workspace}); + await ui5Framework.enrichProjectGraph(projectGraph, withUi5DataDir({workspace})); t.is(Sapui5ResolverStub.callCount, 1, "Sapui5Resolver#constructor should be called once"); t.deepEqual(Sapui5ResolverStub.getCall(0).args, [{ @@ -1053,7 +1061,7 @@ test.serial("enrichProjectGraph should allow omitting framework version in case const provider = new DependencyTreeProvider({dependencyTree}); const projectGraph = await projectGraphBuilder(provider, {workspace}); - await ui5Framework.enrichProjectGraph(projectGraph, {workspace}); + await ui5Framework.enrichProjectGraph(projectGraph, withUi5DataDir({workspace})); t.is(Sapui5ResolverStub.callCount, 1, "Sapui5Resolver#constructor should be called once"); t.deepEqual(Sapui5ResolverStub.getCall(0).args, [{ @@ -1110,7 +1118,9 @@ test.serial("enrichProjectGraph should use UI5 data dir from env var", async (t) const expectedUi5DataDir = path.resolve(dependencyTree.path, "./ui5-data-dir-from-env-var"); - await ui5Framework.enrichProjectGraph(projectGraph); + await ui5Framework.enrichProjectGraph(projectGraph, { + ui5DataDir: expectedUi5DataDir + }); t.is(t.context.Sapui5ResolverStub.callCount, 1, "Sapui5Resolver#constructor should be called once"); t.deepEqual(t.context.Sapui5ResolverStub.getCall(0).args, [{ @@ -1166,7 +1176,9 @@ test.serial("enrichProjectGraph should use UI5 data dir from configuration", asy const expectedUi5DataDir = path.resolve(dependencyTree.path, "./ui5-data-dir-from-config"); - await ui5Framework.enrichProjectGraph(projectGraph); + await ui5Framework.enrichProjectGraph(projectGraph, { + ui5DataDir: expectedUi5DataDir + }); t.is(t.context.Sapui5ResolverStub.callCount, 1, "Sapui5Resolver#constructor should be called once"); t.deepEqual(t.context.Sapui5ResolverStub.getCall(0).args, [{ @@ -1222,7 +1234,9 @@ test.serial("enrichProjectGraph should use absolute UI5 data dir from configurat const expectedUi5DataDir = path.resolve("/absolute-ui5-data-dir-from-config"); - await ui5Framework.enrichProjectGraph(projectGraph); + await ui5Framework.enrichProjectGraph(projectGraph, { + ui5DataDir: expectedUi5DataDir + }); t.is(t.context.Sapui5ResolverStub.callCount, 1, "Sapui5Resolver#constructor should be called once"); t.deepEqual(t.context.Sapui5ResolverStub.getCall(0).args, [{ diff --git a/packages/project/test/lib/ui5framework/AbstractResolver.js b/packages/project/test/lib/ui5framework/AbstractResolver.js index fe3065433d6..9f5b1ebff1e 100644 --- a/packages/project/test/lib/ui5framework/AbstractResolver.js +++ b/packages/project/test/lib/ui5framework/AbstractResolver.js @@ -556,18 +556,15 @@ test.serial("AbstractResolver: Static resolveVersion resolves 'latest'", async ( const fetchAllVersionsStub = sinon.stub(MyResolver, "fetchAllVersions") .returns(["1.75.0", "1.75.1", "1.76.0"]); - const version = await MyResolver.resolveVersion("latest", { - cwd: "/cwd", - ui5DataDir: "/ui5DataDir" - }); + const version = await MyResolver.resolveVersion("latest", "/ui5DataDir", {cwd: "/cwd"}); t.is(version, "1.76.0", "Resolved version should be correct"); t.is(fetchAllVersionsStub.callCount, 1, "fetchAllVersions should be called once"); - t.deepEqual(fetchAllVersionsStub.getCall(0).args, [{ - cwd: "/cwd", - ui5DataDir: "/ui5DataDir" - }], "fetchAllVersions should be called with expected arguments"); + t.deepEqual(fetchAllVersionsStub.getCall(0).args, [ + "/ui5DataDir", + {cwd: "/cwd"} + ], "fetchAllVersions should be called with expected arguments"); }); test.serial("AbstractResolver: Static resolveVersion resolves 'MAJOR'", async (t) => { @@ -575,18 +572,15 @@ test.serial("AbstractResolver: Static resolveVersion resolves 'MAJOR'", async (t const fetchAllVersionsStub = sinon.stub(MyResolver, "fetchAllVersions") .returns(["1.75.0", "1.75.1", "1.76.0"]); - const version = await MyResolver.resolveVersion("1", { - cwd: "/cwd", - ui5DataDir: "/ui5DataDir" - }); + const version = await MyResolver.resolveVersion("1", "/ui5DataDir", {cwd: "/cwd"}); t.is(version, "1.76.0", "Resolved version should be correct"); t.is(fetchAllVersionsStub.callCount, 1, "fetchAllVersions should be called once"); - t.deepEqual(fetchAllVersionsStub.getCall(0).args, [{ - cwd: "/cwd", - ui5DataDir: "/ui5DataDir" - }], "fetchAllVersions should be called with expected arguments"); + t.deepEqual(fetchAllVersionsStub.getCall(0).args, [ + "/ui5DataDir", + {cwd: "/cwd"} + ], "fetchAllVersions should be called with expected arguments"); }); test.serial("AbstractResolver: Static resolveVersion resolves 'MAJOR-SNAPSHOT'", async (t) => { @@ -594,18 +588,15 @@ test.serial("AbstractResolver: Static resolveVersion resolves 'MAJOR-SNAPSHOT'", const fetchAllVersionsStub = sinon.stub(MyResolver, "fetchAllVersions") .returns(["1.76.0", "1.77.0", "1.77.0-SNAPSHOT", "1.78.0", "1.79.0-SNAPSHOT"]); - const version = await MyResolver.resolveVersion("1-SNAPSHOT", { - cwd: "/cwd", - ui5DataDir: "/ui5DataDir" - }); + const version = await MyResolver.resolveVersion("1-SNAPSHOT", "/ui5DataDir", {cwd: "/cwd"}); t.is(version, "1.79.0-SNAPSHOT", "Resolved version should be correct"); t.is(fetchAllVersionsStub.callCount, 1, "fetchAllVersions should be called once"); - t.deepEqual(fetchAllVersionsStub.getCall(0).args, [{ - cwd: "/cwd", - ui5DataDir: "/ui5DataDir" - }], "fetchAllVersions should be called with expected arguments"); + t.deepEqual(fetchAllVersionsStub.getCall(0).args, [ + "/ui5DataDir", + {cwd: "/cwd"} + ], "fetchAllVersions should be called with expected arguments"); }); test.serial("AbstractResolver: Static resolveVersion resolves 'MAJOR.MINOR'", async (t) => { @@ -613,18 +604,15 @@ test.serial("AbstractResolver: Static resolveVersion resolves 'MAJOR.MINOR'", as const fetchAllVersionsStub = sinon.stub(MyResolver, "fetchAllVersions") .returns(["1.75.0", "1.75.1", "1.76.0"]); - const version = await MyResolver.resolveVersion("1.75", { - cwd: "/cwd", - ui5DataDir: "/ui5DataDir" - }); + const version = await MyResolver.resolveVersion("1.75", "/ui5DataDir", {cwd: "/cwd"}); t.is(version, "1.75.1", "Resolved version should be correct"); t.is(fetchAllVersionsStub.callCount, 1, "fetchAllVersions should be called once"); - t.deepEqual(fetchAllVersionsStub.getCall(0).args, [{ - cwd: "/cwd", - ui5DataDir: "/ui5DataDir" - }], "fetchAllVersions should be called with expected arguments"); + t.deepEqual(fetchAllVersionsStub.getCall(0).args, [ + "/ui5DataDir", + {cwd: "/cwd"} + ], "fetchAllVersions should be called with expected arguments"); }); test.serial("AbstractResolver: Static resolveVersion resolves 'MAJOR.MINOR-SNAPSHOT'", async (t) => { @@ -632,18 +620,15 @@ test.serial("AbstractResolver: Static resolveVersion resolves 'MAJOR.MINOR-SNAPS const fetchAllVersionsStub = sinon.stub(MyResolver, "fetchAllVersions") .returns(["1.76.0", "1.77.0", "1.77.0-SNAPSHOT", "1.78.0", "1.79.0-SNAPSHOT"]); - const version = await MyResolver.resolveVersion("1.79-SNAPSHOT", { - cwd: "/cwd", - ui5DataDir: "/ui5DataDir" - }); + const version = await MyResolver.resolveVersion("1.79-SNAPSHOT", "/ui5DataDir", {cwd: "/cwd"}); t.is(version, "1.79.0-SNAPSHOT", "Resolved version should be correct"); t.is(fetchAllVersionsStub.callCount, 1, "fetchAllVersions should be called once"); - t.deepEqual(fetchAllVersionsStub.getCall(0).args, [{ - cwd: "/cwd", - ui5DataDir: "/ui5DataDir" - }], "fetchAllVersions should be called with expected arguments"); + t.deepEqual(fetchAllVersionsStub.getCall(0).args, [ + "/ui5DataDir", + {cwd: "/cwd"} + ], "fetchAllVersions should be called with expected arguments"); }); test.serial("AbstractResolver: Static resolveVersion resolves 'MAJOR.MINOR.PATCH'", async (t) => { @@ -651,18 +636,15 @@ test.serial("AbstractResolver: Static resolveVersion resolves 'MAJOR.MINOR.PATCH const fetchAllVersionsStub = sinon.stub(MyResolver, "fetchAllVersions") .returns(["1.75.0", "1.75.1", "1.76.0"]); - const version = await MyResolver.resolveVersion("1.75.0", { - cwd: "/cwd", - ui5DataDir: "/ui5DataDir" - }); + const version = await MyResolver.resolveVersion("1.75.0", "/ui5DataDir", {cwd: "/cwd"}); t.is(version, "1.75.0", "Resolved version should be correct"); t.is(fetchAllVersionsStub.callCount, 1, "fetchAllVersions should be called once"); - t.deepEqual(fetchAllVersionsStub.getCall(0).args, [{ - cwd: "/cwd", - ui5DataDir: "/ui5DataDir" - }], "fetchAllVersions should be called with expected arguments"); + t.deepEqual(fetchAllVersionsStub.getCall(0).args, [ + "/ui5DataDir", + {cwd: "/cwd"} + ], "fetchAllVersions should be called with expected arguments"); }); test.serial("AbstractResolver: Static resolveVersion resolves 'MAJOR.MINOR.PATCH-SNAPSHOT'", async (t) => { @@ -670,18 +652,15 @@ test.serial("AbstractResolver: Static resolveVersion resolves 'MAJOR.MINOR.PATCH const fetchAllVersionsStub = sinon.stub(MyResolver, "fetchAllVersions") .returns(["1.76.0", "1.77.0", "1.77.0-SNAPSHOT", "1.78.0", "1.79.0-SNAPSHOT"]); - const version = await MyResolver.resolveVersion("1.79.0-SNAPSHOT", { - cwd: "/cwd", - ui5DataDir: "/ui5DataDir" - }); + const version = await MyResolver.resolveVersion("1.79.0-SNAPSHOT", "/ui5DataDir", {cwd: "/cwd"}); t.is(version, "1.79.0-SNAPSHOT", "Resolved version should be correct"); t.is(fetchAllVersionsStub.callCount, 1, "fetchAllVersions should be called once"); - t.deepEqual(fetchAllVersionsStub.getCall(0).args, [{ - cwd: "/cwd", - ui5DataDir: "/ui5DataDir" - }], "fetchAllVersions should be called with expected arguments"); + t.deepEqual(fetchAllVersionsStub.getCall(0).args, [ + "/ui5DataDir", + {cwd: "/cwd"} + ], "fetchAllVersions should be called with expected arguments"); }); test.serial("AbstractResolver: Static resolveVersion does not include prereleases for 'latest' version", async (t) => { @@ -689,18 +668,15 @@ test.serial("AbstractResolver: Static resolveVersion does not include prerelease const fetchAllVersionsStub = sinon.stub(MyResolver, "fetchAllVersions") .returns(["1.76.0", "1.77.0", "1.78.0", "1.79.0-SNAPSHOT"]); - const version = await MyResolver.resolveVersion("latest", { - cwd: "/cwd", - ui5DataDir: "/ui5DataDir" - }); + const version = await MyResolver.resolveVersion("latest", "/ui5DataDir", {cwd: "/cwd"}); t.is(version, "1.78.0", "Resolved version should be correct"); t.is(fetchAllVersionsStub.callCount, 1, "fetchAllVersions should be called once"); - t.deepEqual(fetchAllVersionsStub.getCall(0).args, [{ - cwd: "/cwd", - ui5DataDir: "/ui5DataDir" - }], "fetchAllVersions should be called with expected arguments"); + t.deepEqual(fetchAllVersionsStub.getCall(0).args, [ + "/ui5DataDir", + {cwd: "/cwd"} + ], "fetchAllVersions should be called with expected arguments"); }); test.serial("AbstractResolver: Static resolveVersion resolves 'latest-snapshot'", async (t) => { @@ -708,18 +684,15 @@ test.serial("AbstractResolver: Static resolveVersion resolves 'latest-snapshot'" const fetchAllVersionsStub = sinon.stub(MyResolver, "fetchAllVersions") .returns(["1.75.0-SNAPSHOT", "1.75.1-SNAPSHOT", "1.76.0-SNAPSHOT", "1.76.1-SNAPSHOT"]); - const version = await MyResolver.resolveVersion("latest-snapshot", { - cwd: "/cwd", - ui5DataDir: "/ui5DataDir" - }); + const version = await MyResolver.resolveVersion("latest-snapshot", "/ui5DataDir", {cwd: "/cwd"}); t.is(version, "1.76.1-SNAPSHOT", "Resolved version should be correct"); t.is(fetchAllVersionsStub.callCount, 1, "fetchAllVersions should be called once"); - t.deepEqual(fetchAllVersionsStub.getCall(0).args, [{ - cwd: "/cwd", - ui5DataDir: "/ui5DataDir" - }], "fetchAllVersions should be called with expected arguments"); + t.deepEqual(fetchAllVersionsStub.getCall(0).args, [ + "/ui5DataDir", + {cwd: "/cwd"} + ], "fetchAllVersions should be called with expected arguments"); }); test.serial("AbstractResolver: Static resolveVersion includes non-prereleases for 'latest-snapshot'", async (t) => { @@ -729,42 +702,28 @@ test.serial("AbstractResolver: Static resolveVersion includes non-prereleases fo const fetchAllVersionsStub = sinon.stub(MyResolver, "fetchAllVersions") .returns(["1.76.0", "1.77.0", "1.78.0", "1.79.0-SNAPSHOT", "1.79.1"]); - const version = await MyResolver.resolveVersion("latest-snapshot", { - cwd: "/cwd", - ui5DataDir: "/ui5DataDir" - }); + const version = await MyResolver.resolveVersion("latest-snapshot", "/ui5DataDir", {cwd: "/cwd"}); t.is(version, "1.79.1", "Resolved version should be correct"); t.is(fetchAllVersionsStub.callCount, 1, "fetchAllVersions should be called once"); - t.deepEqual(fetchAllVersionsStub.getCall(0).args, [{ - cwd: "/cwd", - ui5DataDir: "/ui5DataDir" - }], "fetchAllVersions should be called with expected arguments"); + t.deepEqual(fetchAllVersionsStub.getCall(0).args, [ + "/ui5DataDir", + {cwd: "/cwd"} + ], "fetchAllVersions should be called with expected arguments"); }); test.serial("AbstractResolver: Static resolveVersion without options", async (t) => { const {MyResolver} = t.context; - const fetchAllVersionsStub = sinon.stub(MyResolver, "fetchAllVersions") - .returns(["1.75.0"]); - - await MyResolver.resolveVersion("1.75.0"); - - t.is(fetchAllVersionsStub.callCount, 1, "fetchAllVersions should be called once"); - t.deepEqual(fetchAllVersionsStub.getCall(0).args, [{ - cwd: undefined, - ui5DataDir: undefined - }], "fetchAllVersions should be called with expected arguments"); + const error = await t.throwsAsync(MyResolver.resolveVersion("1.75.0")); + t.is(error.message, "MyResolver: Missing parameter \"ui5DataDir\""); }); test.serial("AbstractResolver: Static resolveVersion throws error for 'lts'", async (t) => { const {MyResolver} = t.context; const fetchAllVersionsStub = sinon.stub(MyResolver, "fetchAllVersions"); - const error = await t.throwsAsync(MyResolver.resolveVersion("lts", { - cwd: "/cwd", - ui5DataDir: "/ui5DataDir" - })); + const error = await t.throwsAsync(MyResolver.resolveVersion("lts", "/ui5DataDir", {cwd: "/cwd"})); t.is(error.message, `Framework version specifier "lts" is incorrect or not supported`); @@ -776,18 +735,15 @@ test.serial("AbstractResolver: Static resolveVersion resolves '1.x'", async (t) const fetchAllVersionsStub = sinon.stub(MyResolver, "fetchAllVersions") .returns(["1.75.0", "1.75.1", "1.76.0", "2.0.0"]); - const version = await MyResolver.resolveVersion("1.x", { - cwd: "/cwd", - ui5DataDir: "/ui5DataDir" - }); + const version = await MyResolver.resolveVersion("1.x", "/ui5DataDir", {cwd: "/cwd"}); t.is(version, "1.76.0", "Resolved version should be correct"); t.is(fetchAllVersionsStub.callCount, 1, "fetchAllVersions should be called once"); - t.deepEqual(fetchAllVersionsStub.getCall(0).args, [{ - cwd: "/cwd", - ui5DataDir: "/ui5DataDir" - }], "fetchAllVersions should be called with expected arguments"); + t.deepEqual(fetchAllVersionsStub.getCall(0).args, [ + "/ui5DataDir", + {cwd: "/cwd"} + ], "fetchAllVersions should be called with expected arguments"); }); test.serial("AbstractResolver: Static resolveVersion resolves '1.75.x'", async (t) => { @@ -795,18 +751,15 @@ test.serial("AbstractResolver: Static resolveVersion resolves '1.75.x'", async ( const fetchAllVersionsStub = sinon.stub(MyResolver, "fetchAllVersions") .returns(["1.75.0", "1.75.1", "1.76.0", "2.0.0"]); - const version = await MyResolver.resolveVersion("1.75.x", { - cwd: "/cwd", - ui5DataDir: "/ui5DataDir" - }); + const version = await MyResolver.resolveVersion("1.75.x", "/ui5DataDir", {cwd: "/cwd"}); t.is(version, "1.75.1", "Resolved version should be correct"); t.is(fetchAllVersionsStub.callCount, 1, "fetchAllVersions should be called once"); - t.deepEqual(fetchAllVersionsStub.getCall(0).args, [{ - cwd: "/cwd", - ui5DataDir: "/ui5DataDir" - }], "fetchAllVersions should be called with expected arguments"); + t.deepEqual(fetchAllVersionsStub.getCall(0).args, [ + "/ui5DataDir", + {cwd: "/cwd"} + ], "fetchAllVersions should be called with expected arguments"); }); test.serial("AbstractResolver: Static resolveVersion resolves '^1.75.0'", async (t) => { @@ -814,18 +767,15 @@ test.serial("AbstractResolver: Static resolveVersion resolves '^1.75.0'", async const fetchAllVersionsStub = sinon.stub(MyResolver, "fetchAllVersions") .returns(["1.75.0", "1.75.1", "1.76.0", "2.0.0"]); - const version = await MyResolver.resolveVersion("^1.75.0", { - cwd: "/cwd", - ui5DataDir: "/ui5DataDir" - }); + const version = await MyResolver.resolveVersion("^1.75.0", "/ui5DataDir", {cwd: "/cwd"}); t.is(version, "1.76.0", "Resolved version should be correct"); t.is(fetchAllVersionsStub.callCount, 1, "fetchAllVersions should be called once"); - t.deepEqual(fetchAllVersionsStub.getCall(0).args, [{ - cwd: "/cwd", - ui5DataDir: "/ui5DataDir" - }], "fetchAllVersions should be called with expected arguments"); + t.deepEqual(fetchAllVersionsStub.getCall(0).args, [ + "/ui5DataDir", + {cwd: "/cwd"} + ], "fetchAllVersions should be called with expected arguments"); }); test.serial("AbstractResolver: Static resolveVersion resolves '~1.75.0'", async (t) => { @@ -833,18 +783,15 @@ test.serial("AbstractResolver: Static resolveVersion resolves '~1.75.0'", async const fetchAllVersionsStub = sinon.stub(MyResolver, "fetchAllVersions") .returns(["1.75.0", "1.75.1", "1.76.0", "2.0.0"]); - const version = await MyResolver.resolveVersion("~1.75.0", { - cwd: "/cwd", - ui5DataDir: "/ui5DataDir" - }); + const version = await MyResolver.resolveVersion("~1.75.0", "/ui5DataDir", {cwd: "/cwd"}); t.is(version, "1.75.1", "Resolved version should be correct"); t.is(fetchAllVersionsStub.callCount, 1, "fetchAllVersions should be called once"); - t.deepEqual(fetchAllVersionsStub.getCall(0).args, [{ - cwd: "/cwd", - ui5DataDir: "/ui5DataDir" - }], "fetchAllVersions should be called with expected arguments"); + t.deepEqual(fetchAllVersionsStub.getCall(0).args, [ + "/ui5DataDir", + {cwd: "/cwd"} + ], "fetchAllVersions should be called with expected arguments"); }); test.serial("AbstractResolver: Static resolveVersion resolves '> 1.75.0 < 1.75.3'", async (t) => { @@ -852,18 +799,15 @@ test.serial("AbstractResolver: Static resolveVersion resolves '> 1.75.0 < 1.75.3 const fetchAllVersionsStub = sinon.stub(MyResolver, "fetchAllVersions") .returns(["1.75.0", "1.75.1", "1.75.2", "1.75.3"]); - const version = await MyResolver.resolveVersion("> 1.75.0 < 1.75.3", { - cwd: "/cwd", - ui5DataDir: "/ui5DataDir" - }); + const version = await MyResolver.resolveVersion("> 1.75.0 < 1.75.3", "/ui5DataDir", {cwd: "/cwd"}); t.is(version, "1.75.2", "Resolved version should be correct"); t.is(fetchAllVersionsStub.callCount, 1, "fetchAllVersions should be called once"); - t.deepEqual(fetchAllVersionsStub.getCall(0).args, [{ - cwd: "/cwd", - ui5DataDir: "/ui5DataDir" - }], "fetchAllVersions should be called with expected arguments"); + t.deepEqual(fetchAllVersionsStub.getCall(0).args, [ + "/ui5DataDir", + {cwd: "/cwd"} + ], "fetchAllVersions should be called with expected arguments"); }); test.serial("AbstractResolver: Static resolveVersion resolves 'x.x.x-SNAPSHOT'", async (t) => { @@ -871,20 +815,17 @@ test.serial("AbstractResolver: Static resolveVersion resolves 'x.x.x-SNAPSHOT'", const fetchAllVersionsStub = sinon.stub(MyResolver, "fetchAllVersions") .returns(["1.75.0-SNAPSHOT", "1.76.0-SNAPSHOT", "1.77.0-SNAPSHOT"]); - const version = await MyResolver.resolveVersion("x.x.x-SNAPSHOT", { - cwd: "/cwd", - ui5DataDir: "/ui5DataDir" - }); + const version = await MyResolver.resolveVersion("x.x.x-SNAPSHOT", "/ui5DataDir", {cwd: "/cwd"}); // All ranges ending with -SNAPSHOT should use "includePrerelease" in order to // properly match prerelease (i.e. -SNAPSHOT) versions. t.is(version, "1.77.0-SNAPSHOT", "Resolved version should be correct"); t.is(fetchAllVersionsStub.callCount, 1, "fetchAllVersions should be called once"); - t.deepEqual(fetchAllVersionsStub.getCall(0).args, [{ - cwd: "/cwd", - ui5DataDir: "/ui5DataDir" - }], "fetchAllVersions should be called with expected arguments"); + t.deepEqual(fetchAllVersionsStub.getCall(0).args, [ + "/ui5DataDir", + {cwd: "/cwd"} + ], "fetchAllVersions should be called with expected arguments"); }); test.serial("AbstractResolver: Static resolveVersion resolves '^2.0.0-SNAPSHOT'", async (t) => { @@ -892,20 +833,17 @@ test.serial("AbstractResolver: Static resolveVersion resolves '^2.0.0-SNAPSHOT'" const fetchAllVersionsStub = sinon.stub(MyResolver, "fetchAllVersions") .returns(["2.0.0-SNAPSHOT", "2.0.1-SNAPSHOT", "2.1.0-SNAPSHOT"]); - const version = await MyResolver.resolveVersion("^2.0.0-SNAPSHOT", { - cwd: "/cwd", - ui5DataDir: "/ui5DataDir" - }); + const version = await MyResolver.resolveVersion("^2.0.0-SNAPSHOT", "/ui5DataDir", {cwd: "/cwd"}); // All ranges ending with -SNAPSHOT should use "includePrerelease" in order to // properly match prerelease (i.e. -SNAPSHOT) versions. t.is(version, "2.1.0-SNAPSHOT", "Resolved version should be correct"); t.is(fetchAllVersionsStub.callCount, 1, "fetchAllVersions should be called once"); - t.deepEqual(fetchAllVersionsStub.getCall(0).args, [{ - cwd: "/cwd", - ui5DataDir: "/ui5DataDir" - }], "fetchAllVersions should be called with expected arguments"); + t.deepEqual(fetchAllVersionsStub.getCall(0).args, [ + "/ui5DataDir", + {cwd: "/cwd"} + ], "fetchAllVersions should be called with expected arguments"); }); test.serial("AbstractResolver: Static resolveVersion resolves '2.x.x-alpha'", async (t) => { @@ -913,20 +851,17 @@ test.serial("AbstractResolver: Static resolveVersion resolves '2.x.x-alpha'", as const fetchAllVersionsStub = sinon.stub(MyResolver, "fetchAllVersions") .returns(["2.0.0-alpha", "2.0.1-alpha", "2.1.0-alpha"]); - const version = await MyResolver.resolveVersion("^2.0.0-alpha", { - cwd: "/cwd", - ui5DataDir: "/ui5DataDir" - }); + const version = await MyResolver.resolveVersion("^2.0.0-alpha", "/ui5DataDir", {cwd: "/cwd"}); // Prerelease ranges other than -SNAPSHOT should not use "includePrerelease" // and therefore not match pre-releases like normal versions t.is(version, "2.0.0-alpha", "Resolved version should be correct"); t.is(fetchAllVersionsStub.callCount, 1, "fetchAllVersions should be called once"); - t.deepEqual(fetchAllVersionsStub.getCall(0).args, [{ - cwd: "/cwd", - ui5DataDir: "/ui5DataDir" - }], "fetchAllVersions should be called with expected arguments"); + t.deepEqual(fetchAllVersionsStub.getCall(0).args, [ + "/ui5DataDir", + {cwd: "/cwd"} + ], "fetchAllVersions should be called with expected arguments"); }); test.serial("AbstractResolver: Static resolveVersion resolves 'next' using tags", async (t) => { @@ -939,23 +874,20 @@ test.serial("AbstractResolver: Static resolveVersion resolves 'next' using tags" "next": "2.0.0" }); - const version = await MyResolver.resolveVersion("next", { - cwd: "/cwd", - ui5DataDir: "/ui5DataDir" - }); + const version = await MyResolver.resolveVersion("next", "/ui5DataDir", {cwd: "/cwd"}); t.is(version, "2.0.0", "Resolved version should be correct"); t.is(fetchAllVersionsStub.callCount, 1, "fetchAllVersions should be called once"); - t.deepEqual(fetchAllVersionsStub.getCall(0).args, [{ - cwd: "/cwd", - ui5DataDir: "/ui5DataDir" - }], "fetchAllVersions should be called with expected arguments"); + t.deepEqual(fetchAllVersionsStub.getCall(0).args, [ + "/ui5DataDir", + {cwd: "/cwd"} + ], "fetchAllVersions should be called with expected arguments"); t.is(fetchAllTagsStub.callCount, 1, "fetchAllTagsStub should be called once"); - t.deepEqual(fetchAllVersionsStub.getCall(0).args, [{ - cwd: "/cwd", - ui5DataDir: "/ui5DataDir" - }], "fetchAllTags should be called with expected arguments"); + t.deepEqual(fetchAllVersionsStub.getCall(0).args, [ + "/ui5DataDir", + {cwd: "/cwd"} + ], "fetchAllTags should be called with expected arguments"); }); test.serial("AbstractResolver: Static resolveVersion resolves 'next' to a pre-release using tags", async (t) => { @@ -968,23 +900,20 @@ test.serial("AbstractResolver: Static resolveVersion resolves 'next' to a pre-re "next": "2.0.0-SNAPSHOT" }); - const version = await MyResolver.resolveVersion("next", { - cwd: "/cwd", - ui5DataDir: "/ui5DataDir" - }); + const version = await MyResolver.resolveVersion("next", "/ui5DataDir", {cwd: "/cwd"}); t.is(version, "2.0.0-SNAPSHOT", "Resolved version should be correct"); t.is(fetchAllVersionsStub.callCount, 1, "fetchAllVersions should be called once"); - t.deepEqual(fetchAllVersionsStub.getCall(0).args, [{ - cwd: "/cwd", - ui5DataDir: "/ui5DataDir" - }], "fetchAllVersions should be called with expected arguments"); + t.deepEqual(fetchAllVersionsStub.getCall(0).args, [ + "/ui5DataDir", + {cwd: "/cwd"} + ], "fetchAllVersions should be called with expected arguments"); t.is(fetchAllTagsStub.callCount, 1, "fetchAllTagsStub should be called once"); - t.deepEqual(fetchAllVersionsStub.getCall(0).args, [{ - cwd: "/cwd", - ui5DataDir: "/ui5DataDir" - }], "fetchAllTags should be called with expected arguments"); + t.deepEqual(fetchAllVersionsStub.getCall(0).args, [ + "/ui5DataDir", + {cwd: "/cwd"} + ], "fetchAllTags should be called with expected arguments"); }); @@ -998,34 +927,28 @@ test.serial("AbstractResolver: Static resolveVersion resolves 'latest' using tag // Resolver does not support tags (resolves with "null" instead of an object) // 'latest' should resolve to the highest version available - const version1 = await MyResolver.resolveVersion("latest", { - cwd: "/cwd", - ui5DataDir: "/ui5DataDir" - }); + const version1 = await MyResolver.resolveVersion("latest", "/ui5DataDir", {cwd: "/cwd"}); t.is(version1, "2.0.0", "Resolved version should be correct"); t.is(fetchAllTagsStub.callCount, 1, "fetchAllTagsStub should be called once"); - t.deepEqual(fetchAllVersionsStub.getCall(0).args, [{ - cwd: "/cwd", - ui5DataDir: "/ui5DataDir" - }], "fetchAllTags should be called with expected arguments"); + t.deepEqual(fetchAllVersionsStub.getCall(0).args, [ + "/ui5DataDir", + {cwd: "/cwd"} + ], "fetchAllTags should be called with expected arguments"); // Change behavior of Resolver to support tags, so that version should be used now // instead of the highest version fetchAllTagsStub.resolves({ "latest": "1.76.0" }); - const version2 = await MyResolver.resolveVersion("latest", { - cwd: "/cwd", - ui5DataDir: "/ui5DataDir" - }); + const version2 = await MyResolver.resolveVersion("latest", "/ui5DataDir", {cwd: "/cwd"}); t.is(version2, "1.76.0", "Resolved version should be correct"); t.is(fetchAllTagsStub.callCount, 2, "fetchAllTagsStub should be called twice"); - t.deepEqual(fetchAllVersionsStub.getCall(1).args, [{ - cwd: "/cwd", - ui5DataDir: "/ui5DataDir" - }], "fetchAllTags should be called with expected arguments"); + t.deepEqual(fetchAllVersionsStub.getCall(1).args, [ + "/ui5DataDir", + {cwd: "/cwd"} + ], "fetchAllTags should be called with expected arguments"); }); test.serial("AbstractResolver: Static resolveVersion throws error for empty string", async (t) => { @@ -1033,10 +956,7 @@ test.serial("AbstractResolver: Static resolveVersion throws error for empty stri sinon.stub(MyResolver, "fetchAllVersions") .returns(["1.75.0", "1.75.1", "1.76.0"]); - const error = await t.throwsAsync(MyResolver.resolveVersion("", { - cwd: "/cwd", - ui5DataDir: "/ui5DataDir" - })); + const error = await t.throwsAsync(MyResolver.resolveVersion("", "/ui5DataDir", {cwd: "/cwd"})); t.is(error.message, `Framework version specifier "" is incorrect or not supported`); }); @@ -1046,10 +966,7 @@ test.serial("AbstractResolver: Static resolveVersion throws error for invalid ta sinon.stub(MyResolver, "fetchAllVersions") .returns(["1.75.0", "1.75.1", "1.76.0"]); - const error = await t.throwsAsync(MyResolver.resolveVersion("%20", { - cwd: "/cwd", - ui5DataDir: "/ui5DataDir" - })); + const error = await t.throwsAsync(MyResolver.resolveVersion("%20", "/ui5DataDir", {cwd: "/cwd"})); t.is(error.message, `Framework version specifier "%20" is incorrect or not supported`); }); @@ -1061,10 +978,9 @@ test.serial("AbstractResolver: Static resolveVersion throws error for non-existi sinon.stub(MyResolver, "fetchAllTags") .resolves({"latest": "1.76.0"}); - const error = await t.throwsAsync(MyResolver.resolveVersion("this-tag-does-not-exist", { - cwd: "/cwd", - ui5DataDir: "/ui5DataDir" - })); + const error = await t.throwsAsync( + MyResolver.resolveVersion("this-tag-does-not-exist", "/ui5DataDir", {cwd: "/cwd"}) + ); t.is(error.message, `Could not resolve framework version via tag 'this-tag-does-not-exist'. ` + `Make sure the tag is available in the configured registry.` @@ -1076,10 +992,7 @@ test.serial("AbstractResolver: Static resolveVersion throws error for version no sinon.stub(MyResolver, "fetchAllVersions") .returns(["1.75.0", "1.75.1", "1.76.0"]); - const error = await t.throwsAsync(MyResolver.resolveVersion("1.74.0", { - cwd: "/cwd", - ui5DataDir: "/ui5DataDir" - })); + const error = await t.throwsAsync(MyResolver.resolveVersion("1.74.0", "/ui5DataDir", {cwd: "/cwd"})); t.is(error.message, `Could not resolve framework version 1.74.0. ` + `Make sure the version is valid and available in the configured registry.`); @@ -1095,10 +1008,7 @@ test.serial( sinon.stub(Openui5Resolver, "fetchAllVersions") .returns(["1.75.0", "1.75.1", "1.76.0"]); - const error = await t.throwsAsync(Openui5Resolver.resolveVersion("1.50.0", { - cwd: "/cwd", - ui5DataDir: "/ui5DataDir" - })); + const error = await t.throwsAsync(Openui5Resolver.resolveVersion("1.50.0", "/ui5DataDir", {cwd: "/cwd"})); t.is(error.message, `Could not resolve framework version 1.50.0. Note that OpenUI5 framework libraries can only be ` + @@ -1115,10 +1025,7 @@ test.serial( sinon.stub(Sapui5Resolver, "fetchAllVersions") .returns(["1.76.0", "1.76.1", "1.90.0"]); - const error = await t.throwsAsync(Sapui5Resolver.resolveVersion("1.75.0", { - cwd: "/cwd", - ui5DataDir: "/ui5DataDir" - })); + const error = await t.throwsAsync(Sapui5Resolver.resolveVersion("1.75.0", "/ui5DataDir", {cwd: "/cwd"})); t.is(error.message, `Could not resolve framework version 1.75.0. Note that SAPUI5 framework libraries can only be ` + @@ -1135,10 +1042,7 @@ test.serial( sinon.stub(Openui5Resolver, "fetchAllVersions") .returns([]); - const error = await t.throwsAsync(Openui5Resolver.resolveVersion("latest", { - cwd: "/cwd", - ui5DataDir: "/ui5DataDir" - })); + const error = await t.throwsAsync(Openui5Resolver.resolveVersion("latest", "/ui5DataDir", {cwd: "/cwd"})); t.is(error.message, `Could not resolve framework version latest. ` + `Make sure the version is valid and available in the configured registry.`); @@ -1154,10 +1058,7 @@ test.serial( sinon.stub(Sapui5Resolver, "fetchAllVersions") .returns([]); - const error = await t.throwsAsync(Sapui5Resolver.resolveVersion("latest", { - cwd: "/cwd", - ui5DataDir: "/ui5DataDir" - })); + const error = await t.throwsAsync(Sapui5Resolver.resolveVersion("latest", "/ui5DataDir", {cwd: "/cwd"})); t.is(error.message, `Could not resolve framework version latest. ` + `Make sure the version is valid and available in the configured registry.`); @@ -1173,10 +1074,7 @@ test.serial( sinon.stub(Openui5Resolver, "fetchAllVersions") .returns([]); - const error = await t.throwsAsync(Openui5Resolver.resolveVersion("1.99", { - cwd: "/cwd", - ui5DataDir: "/ui5DataDir" - })); + const error = await t.throwsAsync(Openui5Resolver.resolveVersion("1.99", "/ui5DataDir", {cwd: "/cwd"})); t.is(error.message, `Could not resolve framework version 1.99. ` + `Make sure the version is valid and available in the configured registry.`); @@ -1192,10 +1090,7 @@ test.serial( sinon.stub(Sapui5Resolver, "fetchAllVersions") .returns([]); - const error = await t.throwsAsync(Sapui5Resolver.resolveVersion("1.99", { - cwd: "/cwd", - ui5DataDir: "/ui5DataDir" - })); + const error = await t.throwsAsync(Sapui5Resolver.resolveVersion("1.99", "/ui5DataDir", {cwd: "/cwd"})); t.is(error.message, `Could not resolve framework version 1.99. ` + `Make sure the version is valid and available in the configured registry.`); diff --git a/packages/project/test/lib/ui5framework/Openui5Resolver.integration.js b/packages/project/test/lib/ui5framework/Openui5Resolver.integration.js index e8049f0a412..6728a944c59 100644 --- a/packages/project/test/lib/ui5framework/Openui5Resolver.integration.js +++ b/packages/project/test/lib/ui5framework/Openui5Resolver.integration.js @@ -126,7 +126,9 @@ test.serial("resolveVersion", async (t) => { // Generic testing without and with options argument const optionsArguments = [ - undefined, + { + ui5DataDir: defaultUi5DataDir + }, { cwd: path.join(fakeBaseDir, "custom-cwd"), ui5DataDir: path.join(fakeBaseDir, "custom-datadir", ".ui5") @@ -138,49 +140,49 @@ test.serial("resolveVersion", async (t) => { pacote.packument.resetHistory(); // Ranges - t.is(await Openui5Resolver.resolveVersion("1", options), "1.120.1"); - t.is(await Openui5Resolver.resolveVersion("1.120", options), "1.120.1"); - t.is(await Openui5Resolver.resolveVersion("1.x", options), "1.120.1"); - t.is(await Openui5Resolver.resolveVersion("1.x.x", options), "1.120.1"); - t.is(await Openui5Resolver.resolveVersion("^1", options), "1.120.1"); - t.is(await Openui5Resolver.resolveVersion("*", options), "1.120.1"); + t.is(await Openui5Resolver.resolveVersion("1", options.ui5DataDir, options), "1.120.1"); + t.is(await Openui5Resolver.resolveVersion("1.120", options.ui5DataDir, options), "1.120.1"); + t.is(await Openui5Resolver.resolveVersion("1.x", options.ui5DataDir, options), "1.120.1"); + t.is(await Openui5Resolver.resolveVersion("1.x.x", options.ui5DataDir, options), "1.120.1"); + t.is(await Openui5Resolver.resolveVersion("^1", options.ui5DataDir, options), "1.120.1"); + t.is(await Openui5Resolver.resolveVersion("*", options.ui5DataDir, options), "1.120.1"); // Tags - t.is(await Openui5Resolver.resolveVersion("latest", options), "1.120.0"); - t.is(await Openui5Resolver.resolveVersion("next", options), "2.0.0-rc.1"); - t.is(await Openui5Resolver.resolveVersion("not-a-snapshot", options), "1.118.0"); + t.is(await Openui5Resolver.resolveVersion("latest", options.ui5DataDir, options), "1.120.0"); + t.is(await Openui5Resolver.resolveVersion("next", options.ui5DataDir, options), "2.0.0-rc.1"); + t.is(await Openui5Resolver.resolveVersion("not-a-snapshot", options.ui5DataDir, options), "1.118.0"); // Exact versions - t.is(await Openui5Resolver.resolveVersion("1.118.0", options), "1.118.0"); - t.is(await Openui5Resolver.resolveVersion("2.0.0-rc.1", options), "2.0.0-rc.1"); - t.is(await Openui5Resolver.resolveVersion("1.123.4-SNAPSHOT", options), "1.123.4-SNAPSHOT"); + t.is(await Openui5Resolver.resolveVersion("1.118.0", options.ui5DataDir, options), "1.118.0"); + t.is(await Openui5Resolver.resolveVersion("2.0.0-rc.1", options.ui5DataDir, options), "2.0.0-rc.1"); + t.is(await Openui5Resolver.resolveVersion("1.123.4-SNAPSHOT", options.ui5DataDir, options), "1.123.4-SNAPSHOT"); // SNAPSHOT ranges - t.is(await Openui5Resolver.resolveVersion("1-SNAPSHOT", options), "1.123.4-SNAPSHOT"); - t.is(await Openui5Resolver.resolveVersion("1.123-SNAPSHOT", options), "1.123.4-SNAPSHOT"); + t.is(await Openui5Resolver.resolveVersion("1-SNAPSHOT", options.ui5DataDir, options), "1.123.4-SNAPSHOT"); + t.is(await Openui5Resolver.resolveVersion("1.123-SNAPSHOT", options.ui5DataDir, options), "1.123.4-SNAPSHOT"); // Error cases - await t.throwsAsync(Openui5Resolver.resolveVersion("", options), { + await t.throwsAsync(Openui5Resolver.resolveVersion("", options.ui5DataDir, options), { message: `Framework version specifier "" is incorrect or not supported` }); - await t.throwsAsync(Openui5Resolver.resolveVersion("tag-does-not-exist", options), { + await t.throwsAsync(Openui5Resolver.resolveVersion("tag-does-not-exist", options.ui5DataDir, options), { message: `Could not resolve framework version via tag 'tag-does-not-exist'. ` + `Make sure the tag is available in the configured registry.` }); - await t.throwsAsync(Openui5Resolver.resolveVersion("invalid-tag-%20", options), { + await t.throwsAsync(Openui5Resolver.resolveVersion("invalid-tag-%20", options.ui5DataDir, options), { message: `Framework version specifier "invalid-tag-%20" is incorrect or not supported` }); - await t.throwsAsync(Openui5Resolver.resolveVersion("1.999.9", options), { + await t.throwsAsync(Openui5Resolver.resolveVersion("1.999.9", options.ui5DataDir, options), { message: `Could not resolve framework version 1.999.9. ` + `Make sure the version is valid and available in the configured registry.` }); - await t.throwsAsync(Openui5Resolver.resolveVersion("1.0.0", options), { + await t.throwsAsync(Openui5Resolver.resolveVersion("1.0.0", options.ui5DataDir, options), { message: `Could not resolve framework version 1.0.0. ` + `Note that OpenUI5 framework libraries can only be consumed by the UI5 CLI ` + `starting with OpenUI5 v1.52.5` }); - await t.throwsAsync(Openui5Resolver.resolveVersion("^999", options), { + await t.throwsAsync(Openui5Resolver.resolveVersion("^999", options.ui5DataDir, options), { message: `Could not resolve framework version ^999. ` + `Make sure the version is valid and available in the configured registry.` }); diff --git a/packages/project/test/lib/ui5framework/Openui5Resolver.js b/packages/project/test/lib/ui5framework/Openui5Resolver.js index 278a4077d74..db3a4526ade 100644 --- a/packages/project/test/lib/ui5framework/Openui5Resolver.js +++ b/packages/project/test/lib/ui5framework/Openui5Resolver.js @@ -2,7 +2,6 @@ import test from "ava"; import sinon from "sinon"; import esmock from "esmock"; import path from "node:path"; -import os from "node:os"; test.beforeEach(async (t) => { t.context.InstallerStub = sinon.stub(); @@ -151,7 +150,7 @@ test.serial("Openui5Resolver: Static _getInstaller", (t) => { ui5DataDir: "/ui5DataDir" }; - const installer = Openui5Resolver._getInstaller(options); + const installer = Openui5Resolver._getInstaller(options.ui5DataDir, options); t.is(t.context.InstallerStub.callCount, 1, "Installer should be called once"); t.true(t.context.InstallerStub.calledWithNew(), "Installer should be called with new"); @@ -165,15 +164,11 @@ test.serial("Openui5Resolver: Static _getInstaller", (t) => { test.serial("Openui5Resolver: Static _getInstaller without options", (t) => { const {Openui5Resolver} = t.context; - const installer = Openui5Resolver._getInstaller(); - - t.is(t.context.InstallerStub.callCount, 1, "Installer should be called once"); - t.true(t.context.InstallerStub.calledWithNew(), "Installer should be called with new"); - t.is(installer, t.context.InstallerStub.getCall(0).returnValue, "Installer instance is returned"); - t.deepEqual(t.context.InstallerStub.getCall(0).args, [{ - cwd: process.cwd(), - ui5DataDir: path.join(os.homedir(), ".ui5") - }], "Installer should be called with expected arguments"); + const err = t.throws(() => { + Openui5Resolver._getInstaller(); + }); + t.is(err.message, "Openui5Resolver: Missing parameter \"ui5DataDir\""); + t.is(t.context.InstallerStub.callCount, 0, "Installer should not be called"); }); test.serial("Openui5Resolver: Static fetchAllVersions", async (t) => { @@ -185,7 +180,9 @@ test.serial("Openui5Resolver: Static fetchAllVersions", async (t) => { const getInstallerSpy = sinon.spy(Openui5Resolver, "_getInstaller"); - const versions = await Openui5Resolver.fetchAllVersions(); + const versions = await Openui5Resolver.fetchAllVersions("/ui5DataDir", { + cwd: "/cwd" + }); t.deepEqual(versions, expectedVersions, "Fetched versions should be correct"); @@ -194,7 +191,10 @@ test.serial("Openui5Resolver: Static fetchAllVersions", async (t) => { "fetchPackageVersions should be called with expected arguments"); t.is(getInstallerSpy.callCount, 1, "_getInstaller should be called once"); - t.is(getInstallerSpy.getCall(0).args[0], undefined, "_getInstaller should be called without any options"); + t.deepEqual(getInstallerSpy.getCall(0).args, [ + "/ui5DataDir", + {cwd: "/cwd"} + ], "_getInstaller should be called with expected arguments"); }); test.serial("Openui5Resolver: Static fetchAllTags", async (t) => { @@ -206,7 +206,9 @@ test.serial("Openui5Resolver: Static fetchAllTags", async (t) => { const getInstallerSpy = sinon.spy(Openui5Resolver, "_getInstaller"); - const tags = await Openui5Resolver.fetchAllTags(); + const tags = await Openui5Resolver.fetchAllTags("/ui5DataDir", { + cwd: "/cwd" + }); t.deepEqual(tags, expectedTags, "Fetched tags should be correct"); @@ -215,5 +217,8 @@ test.serial("Openui5Resolver: Static fetchAllTags", async (t) => { "fetchPackageVersions should be called with expected arguments"); t.is(getInstallerSpy.callCount, 1, "_getInstaller should be called once"); - t.is(getInstallerSpy.getCall(0).args[0], undefined, "_getInstaller should be called without any options"); + t.deepEqual(getInstallerSpy.getCall(0).args, [ + "/ui5DataDir", + {cwd: "/cwd"} + ], "_getInstaller should be called with expected arguments"); }); diff --git a/packages/project/test/lib/ui5framework/Sapui5MavenSnapshotResolver.integration.js b/packages/project/test/lib/ui5framework/Sapui5MavenSnapshotResolver.integration.js index c82fc42ab9a..56f9cf38b48 100644 --- a/packages/project/test/lib/ui5framework/Sapui5MavenSnapshotResolver.integration.js +++ b/packages/project/test/lib/ui5framework/Sapui5MavenSnapshotResolver.integration.js @@ -93,6 +93,9 @@ test.afterEach.always((t) => { test.serial("resolveVersion", async (t) => { const {Sapui5MavenSnapshotResolver, makeFetchHappen, logStub, sinon} = t.context; + const options = { + ui5DataDir: path.join(fakeBaseDir, "homedir", ".ui5") + }; makeFetchHappen.withArgs("_SNAPSHOT_URL_/com/sap/ui5/dist/sapui5-sdk-dist/maven-metadata.xml") .resolves({ @@ -117,37 +120,52 @@ test.serial("resolveVersion", async (t) => { // Exact SNAPSHOT versions - t.is(await Sapui5MavenSnapshotResolver.resolveVersion("1.123.4-SNAPSHOT"), "1.123.4-SNAPSHOT"); - t.is(await Sapui5MavenSnapshotResolver.resolveVersion("2.0.1-SNAPSHOT"), "2.0.1-SNAPSHOT"); + t.is( + await Sapui5MavenSnapshotResolver.resolveVersion("1.123.4-SNAPSHOT", options.ui5DataDir, options), + "1.123.4-SNAPSHOT" + ); + t.is( + await Sapui5MavenSnapshotResolver.resolveVersion("2.0.1-SNAPSHOT", options.ui5DataDir, options), + "2.0.1-SNAPSHOT" + ); // latest-snapshot - t.is(await Sapui5MavenSnapshotResolver.resolveVersion("latest-snapshot"), "2.1.2-SNAPSHOT"); + t.is( + await Sapui5MavenSnapshotResolver.resolveVersion("latest-snapshot", options.ui5DataDir, options), + "2.1.2-SNAPSHOT" + ); // SNAPSHOT ranges - t.is(await Sapui5MavenSnapshotResolver.resolveVersion("1-SNAPSHOT"), "1.123.4-SNAPSHOT"); - t.is(await Sapui5MavenSnapshotResolver.resolveVersion("2-SNAPSHOT"), "2.1.2-SNAPSHOT"); - t.is(await Sapui5MavenSnapshotResolver.resolveVersion("1.123-SNAPSHOT"), "1.123.4-SNAPSHOT"); + t.is( + await Sapui5MavenSnapshotResolver.resolveVersion("1-SNAPSHOT", options.ui5DataDir, options), + "1.123.4-SNAPSHOT" + ); + t.is(await Sapui5MavenSnapshotResolver.resolveVersion("2-SNAPSHOT", options.ui5DataDir, options), "2.1.2-SNAPSHOT"); + t.is( + await Sapui5MavenSnapshotResolver.resolveVersion("1.123-SNAPSHOT", options.ui5DataDir, options), + "1.123.4-SNAPSHOT" + ); // Error cases - await t.throwsAsync(Sapui5MavenSnapshotResolver.resolveVersion(""), { + await t.throwsAsync(Sapui5MavenSnapshotResolver.resolveVersion("", options.ui5DataDir, options), { message: `Framework version specifier "" is incorrect or not supported` }); - await t.throwsAsync(Sapui5MavenSnapshotResolver.resolveVersion("tag-does-not-exist"), { + await t.throwsAsync(Sapui5MavenSnapshotResolver.resolveVersion("tag-does-not-exist", options.ui5DataDir, options), { message: `Framework version specifier "tag-does-not-exist" is incorrect or not supported` }); - await t.throwsAsync(Sapui5MavenSnapshotResolver.resolveVersion("invalid-tag-%20"), { + await t.throwsAsync(Sapui5MavenSnapshotResolver.resolveVersion("invalid-tag-%20", options.ui5DataDir, options), { message: `Framework version specifier "invalid-tag-%20" is incorrect or not supported` }); - await t.throwsAsync(Sapui5MavenSnapshotResolver.resolveVersion("1.999.9"), { + await t.throwsAsync(Sapui5MavenSnapshotResolver.resolveVersion("1.999.9", options.ui5DataDir, options), { message: `Could not resolve framework version 1.999.9. ` + `Make sure the version is valid and available in the configured registry.` }); - await t.throwsAsync(Sapui5MavenSnapshotResolver.resolveVersion("1.0.0-SNAPSHOT"), { + await t.throwsAsync(Sapui5MavenSnapshotResolver.resolveVersion("1.0.0-SNAPSHOT", options.ui5DataDir, options), { message: `Could not resolve framework version 1.0.0-SNAPSHOT. ` + `Make sure the version is valid and available in the configured registry.` }); - await t.throwsAsync(Sapui5MavenSnapshotResolver.resolveVersion("3-SNAPSHOT"), { + await t.throwsAsync(Sapui5MavenSnapshotResolver.resolveVersion("3-SNAPSHOT", options.ui5DataDir, options), { message: `Could not resolve framework version 3-SNAPSHOT. ` + `Make sure the version is valid and available in the configured registry.` }); diff --git a/packages/project/test/lib/ui5framework/Sapui5MavenSnapshotResolver.js b/packages/project/test/lib/ui5framework/Sapui5MavenSnapshotResolver.js index a88836ae2fd..96ec4def9d1 100644 --- a/packages/project/test/lib/ui5framework/Sapui5MavenSnapshotResolver.js +++ b/packages/project/test/lib/ui5framework/Sapui5MavenSnapshotResolver.js @@ -2,7 +2,6 @@ import test from "ava"; import sinon from "sinon"; import esmock from "esmock"; import path from "node:path"; -import os from "node:os"; test.beforeEach(async (t) => { t.context.InstallerStub = sinon.stub(); @@ -379,7 +378,7 @@ test.serial("Sapui5MavenSnapshotResolver: Static fetchAllVersions", async (t) => sinon.stub(Sapui5MavenSnapshotResolver, "_createSnapshotEndpointUrlCallback") .returns("snapshotEndpointUrlCallback"); - const versions = await Sapui5MavenSnapshotResolver.fetchAllVersions(options); + const versions = await Sapui5MavenSnapshotResolver.fetchAllVersions(options.ui5DataDir, options); t.deepEqual(versions, expectedVersions, "Fetched versions should be correct"); @@ -402,28 +401,9 @@ test.serial("Sapui5MavenSnapshotResolver: Static fetchAllVersions", async (t) => test.serial("Sapui5MavenSnapshotResolver: Static fetchAllVersions without options", async (t) => { const {Sapui5MavenSnapshotResolver} = t.context; - const expectedVersions = ["1.75.0-SNAPSHOT", "1.75.1-SNAPSHOT", "1.76.0-SNAPSHOT"]; - - t.context.fetchPackageVersionsStub.returns(expectedVersions); - sinon.stub(Sapui5MavenSnapshotResolver, "_createSnapshotEndpointUrlCallback") - .returns("snapshotEndpointUrlCallback"); - - const versions = await Sapui5MavenSnapshotResolver.fetchAllVersions(); - - t.deepEqual(versions, expectedVersions, "Fetched versions should be correct"); - - t.is(t.context.fetchPackageVersionsStub.callCount, 1, "fetchPackageVersions should be called once"); - t.deepEqual(t.context.fetchPackageVersionsStub.getCall(0).args, - [{artifactId: "sapui5-sdk-dist", groupId: "com.sap.ui5.dist"}], - "fetchPackageVersions should be called with expected arguments"); - - t.is(t.context.InstallerStub.callCount, 1, "Installer should be called once"); - t.true(t.context.InstallerStub.calledWithNew(), "Installer should be called with new"); - t.deepEqual(t.context.InstallerStub.getCall(0).args, [{ - cwd: process.cwd(), - snapshotEndpointUrlCb: "snapshotEndpointUrlCallback", - ui5DataDir: path.join(os.homedir(), ".ui5") - }], "Installer should be called with expected arguments"); + const err = await t.throwsAsync(Sapui5MavenSnapshotResolver.fetchAllVersions()); + t.is(err.message, "Sapui5MavenSnapshotResolver: Missing parameter \"ui5DataDir\""); + t.is(t.context.InstallerStub.callCount, 0, "Installer should not be called"); }); test.serial("_createSnapshotEndpointUrlCallback: Environment variable", async (t) => { diff --git a/packages/project/test/lib/ui5framework/Sapui5Resolver.integration.js b/packages/project/test/lib/ui5framework/Sapui5Resolver.integration.js index 3b04805ad42..3dd59ba0b20 100644 --- a/packages/project/test/lib/ui5framework/Sapui5Resolver.integration.js +++ b/packages/project/test/lib/ui5framework/Sapui5Resolver.integration.js @@ -126,7 +126,9 @@ test.serial("resolveVersion", async (t) => { // Generic testing without and with options argument const optionsArguments = [ - undefined, + { + ui5DataDir: defaultUi5DataDir + }, { cwd: path.join(fakeBaseDir, "custom-cwd"), ui5DataDir: path.join(fakeBaseDir, "custom-datadir", ".ui5") @@ -138,49 +140,49 @@ test.serial("resolveVersion", async (t) => { pacote.packument.resetHistory(); // Ranges - t.is(await Sapui5Resolver.resolveVersion("1", options), "1.120.1"); - t.is(await Sapui5Resolver.resolveVersion("1.120", options), "1.120.1"); - t.is(await Sapui5Resolver.resolveVersion("1.x", options), "1.120.1"); - t.is(await Sapui5Resolver.resolveVersion("1.x.x", options), "1.120.1"); - t.is(await Sapui5Resolver.resolveVersion("^1", options), "1.120.1"); - t.is(await Sapui5Resolver.resolveVersion("*", options), "1.120.1"); + t.is(await Sapui5Resolver.resolveVersion("1", options.ui5DataDir, options), "1.120.1"); + t.is(await Sapui5Resolver.resolveVersion("1.120", options.ui5DataDir, options), "1.120.1"); + t.is(await Sapui5Resolver.resolveVersion("1.x", options.ui5DataDir, options), "1.120.1"); + t.is(await Sapui5Resolver.resolveVersion("1.x.x", options.ui5DataDir, options), "1.120.1"); + t.is(await Sapui5Resolver.resolveVersion("^1", options.ui5DataDir, options), "1.120.1"); + t.is(await Sapui5Resolver.resolveVersion("*", options.ui5DataDir, options), "1.120.1"); // Tags - t.is(await Sapui5Resolver.resolveVersion("latest", options), "1.120.0"); - t.is(await Sapui5Resolver.resolveVersion("next", options), "2.0.0-rc.1"); - t.is(await Sapui5Resolver.resolveVersion("not-a-snapshot", options), "1.118.0"); + t.is(await Sapui5Resolver.resolveVersion("latest", options.ui5DataDir, options), "1.120.0"); + t.is(await Sapui5Resolver.resolveVersion("next", options.ui5DataDir, options), "2.0.0-rc.1"); + t.is(await Sapui5Resolver.resolveVersion("not-a-snapshot", options.ui5DataDir, options), "1.118.0"); // Exact versions - t.is(await Sapui5Resolver.resolveVersion("1.118.0", options), "1.118.0"); - t.is(await Sapui5Resolver.resolveVersion("2.0.0-rc.1", options), "2.0.0-rc.1"); - t.is(await Sapui5Resolver.resolveVersion("1.123.4-SNAPSHOT", options), "1.123.4-SNAPSHOT"); + t.is(await Sapui5Resolver.resolveVersion("1.118.0", options.ui5DataDir, options), "1.118.0"); + t.is(await Sapui5Resolver.resolveVersion("2.0.0-rc.1", options.ui5DataDir, options), "2.0.0-rc.1"); + t.is(await Sapui5Resolver.resolveVersion("1.123.4-SNAPSHOT", options.ui5DataDir, options), "1.123.4-SNAPSHOT"); // SNAPSHOT ranges - t.is(await Sapui5Resolver.resolveVersion("1-SNAPSHOT", options), "1.123.4-SNAPSHOT"); - t.is(await Sapui5Resolver.resolveVersion("1.123-SNAPSHOT", options), "1.123.4-SNAPSHOT"); + t.is(await Sapui5Resolver.resolveVersion("1-SNAPSHOT", options.ui5DataDir, options), "1.123.4-SNAPSHOT"); + t.is(await Sapui5Resolver.resolveVersion("1.123-SNAPSHOT", options.ui5DataDir, options), "1.123.4-SNAPSHOT"); // Error cases - await t.throwsAsync(Sapui5Resolver.resolveVersion("", options), { + await t.throwsAsync(Sapui5Resolver.resolveVersion("", options.ui5DataDir, options), { message: `Framework version specifier "" is incorrect or not supported` }); - await t.throwsAsync(Sapui5Resolver.resolveVersion("tag-does-not-exist", options), { + await t.throwsAsync(Sapui5Resolver.resolveVersion("tag-does-not-exist", options.ui5DataDir, options), { message: `Could not resolve framework version via tag 'tag-does-not-exist'. ` + `Make sure the tag is available in the configured registry.` }); - await t.throwsAsync(Sapui5Resolver.resolveVersion("invalid-tag-%20", options), { + await t.throwsAsync(Sapui5Resolver.resolveVersion("invalid-tag-%20", options.ui5DataDir, options), { message: `Framework version specifier "invalid-tag-%20" is incorrect or not supported` }); - await t.throwsAsync(Sapui5Resolver.resolveVersion("1.999.9", options), { + await t.throwsAsync(Sapui5Resolver.resolveVersion("1.999.9", options.ui5DataDir, options), { message: `Could not resolve framework version 1.999.9. ` + `Make sure the version is valid and available in the configured registry.` }); - await t.throwsAsync(Sapui5Resolver.resolveVersion("1.0.0", options), { + await t.throwsAsync(Sapui5Resolver.resolveVersion("1.0.0", options.ui5DataDir, options), { message: `Could not resolve framework version 1.0.0. ` + `Note that SAPUI5 framework libraries can only be consumed by the UI5 CLI ` + `starting with SAPUI5 v1.76.0` }); - await t.throwsAsync(Sapui5Resolver.resolveVersion("^999", options), { + await t.throwsAsync(Sapui5Resolver.resolveVersion("^999", options.ui5DataDir, options), { message: `Could not resolve framework version ^999. ` + `Make sure the version is valid and available in the configured registry.` }); diff --git a/packages/project/test/lib/ui5framework/Sapui5Resolver.js b/packages/project/test/lib/ui5framework/Sapui5Resolver.js index 99c5bad9c44..d709ae7b810 100644 --- a/packages/project/test/lib/ui5framework/Sapui5Resolver.js +++ b/packages/project/test/lib/ui5framework/Sapui5Resolver.js @@ -2,7 +2,6 @@ import test from "ava"; import sinon from "sinon"; import esmock from "esmock"; import path from "node:path"; -import os from "node:os"; import Openui5Resolver from "../../../lib/ui5Framework/Openui5Resolver.js"; test.beforeEach(async (t) => { @@ -137,7 +136,7 @@ test.serial("Sapui5Resolver: Static _getInstaller", (t) => { ui5DataDir: "/ui5DataDir" }; - const installer = Sapui5Resolver._getInstaller(options); + const installer = Sapui5Resolver._getInstaller(options.ui5DataDir, options); t.is(t.context.InstallerStub.callCount, 1, "Installer should be called once"); t.true(t.context.InstallerStub.calledWithNew(), "Installer should be called with new"); @@ -151,15 +150,11 @@ test.serial("Sapui5Resolver: Static _getInstaller", (t) => { test.serial("Sapui5Resolver: Static _getInstaller without options", (t) => { const {Sapui5Resolver} = t.context; - const installer = Sapui5Resolver._getInstaller(); - - t.is(t.context.InstallerStub.callCount, 1, "Installer should be called once"); - t.true(t.context.InstallerStub.calledWithNew(), "Installer should be called with new"); - t.is(installer, t.context.InstallerStub.getCall(0).returnValue, "Installer instance is returned"); - t.deepEqual(t.context.InstallerStub.getCall(0).args, [{ - cwd: process.cwd(), - ui5DataDir: path.join(os.homedir(), ".ui5") - }], "Installer should be called with expected arguments"); + const err = t.throws(() => { + Sapui5Resolver._getInstaller(); + }); + t.is(err.message, "Sapui5Resolver: Missing parameter \"ui5DataDir\""); + t.is(t.context.InstallerStub.callCount, 0, "Installer should not be called"); }); test.serial("Sapui5Resolver: Static fetchAllVersions", async (t) => { @@ -171,7 +166,9 @@ test.serial("Sapui5Resolver: Static fetchAllVersions", async (t) => { const getInstallerSpy = sinon.spy(Sapui5Resolver, "_getInstaller"); - const versions = await Sapui5Resolver.fetchAllVersions(); + const versions = await Sapui5Resolver.fetchAllVersions("/ui5DataDir", { + cwd: "/cwd" + }); t.deepEqual(versions, expectedVersions, "Fetched versions should be correct"); @@ -179,7 +176,10 @@ test.serial("Sapui5Resolver: Static fetchAllVersions", async (t) => { t.deepEqual(t.context.fetchPackageVersionsStub.getCall(0).args, [{pkgName: "@sapui5/distribution-metadata"}], "fetchPackageVersions should be called with expected arguments"); t.is(getInstallerSpy.callCount, 1, "_getInstaller should be called once"); - t.is(getInstallerSpy.getCall(0).args[0], undefined, "_getInstaller should be called without any options"); + t.deepEqual(getInstallerSpy.getCall(0).args, [ + "/ui5DataDir", + {cwd: "/cwd"} + ], "_getInstaller should be called with expected arguments"); }); test.serial("Sapui5Resolver: Static fetchAllTags", async (t) => { @@ -191,7 +191,9 @@ test.serial("Sapui5Resolver: Static fetchAllTags", async (t) => { const getInstallerSpy = sinon.spy(Sapui5Resolver, "_getInstaller"); - const tags = await Sapui5Resolver.fetchAllTags(); + const tags = await Sapui5Resolver.fetchAllTags("/ui5DataDir", { + cwd: "/cwd" + }); t.deepEqual(tags, expectedTags, "Fetched tags should be correct"); @@ -200,7 +202,10 @@ test.serial("Sapui5Resolver: Static fetchAllTags", async (t) => { "fetchPackageVersions should be called with expected arguments"); t.is(getInstallerSpy.callCount, 1, "_getInstaller should be called once"); - t.is(getInstallerSpy.getCall(0).args[0], undefined, "_getInstaller should be called without any options"); + t.deepEqual(getInstallerSpy.getCall(0).args, [ + "/ui5DataDir", + {cwd: "/cwd"} + ], "_getInstaller should be called with expected arguments"); }); test.serial( diff --git a/packages/server/lib/server.js b/packages/server/lib/server.js index cee67f08326..0f47e239aa0 100644 --- a/packages/server/lib/server.js +++ b/packages/server/lib/server.js @@ -135,9 +135,7 @@ async function _addSsl({app, key, cert}) { * @param {boolean} [options.serveCSPReports=false] Enable CSP reports serving for request url * '/.ui5/csp/csp-reports.json' * @param {string} [options.cache="Default"] Cache mode to use for building UI5 projects. - * @param {string} [options.ui5DataDir] Explicit UI5 data directory to use for the build cache. - * Overrides the UI5_DATA_DIR environment variable, - * the UI5 configuration file, and the default of ~/.ui5. + * @param {string} options.ui5DataDir Resolved UI5 data directory to use for the build cache. * @param {string[]} [options.includedTasks] A list of tasks to be added to the default execution set. * Takes precedence over excludedTasks. * @param {string[]} [options.excludedTasks] A list of tasks to be excluded from the default task @@ -154,6 +152,9 @@ export async function serve(graph, { simpleIndex = false, liveReload = false, serveCSPReports = false, cache = Cache.Default, ui5DataDir, includedTasks, excludedTasks, }, error) { + if (!ui5DataDir) { + throw new Error("server.serve: Missing parameter \"ui5DataDir\""); + } const rootProject = graph.getRoot(); const readers = []; diff --git a/packages/server/test/lib/server/acceptRemoteConnections.js b/packages/server/test/lib/server/acceptRemoteConnections.js index 6bee8bb778c..45604578c36 100644 --- a/packages/server/test/lib/server/acceptRemoteConnections.js +++ b/packages/server/test/lib/server/acceptRemoteConnections.js @@ -9,14 +9,16 @@ let server; // Start server before running tests test.before(async (t) => { + const ui5DataDir = isolatedUi5DataDir(t); const graph = await graphFromPackageDependencies({ + ui5DataDir, cwd: "./test/fixtures/application.a" }); server = await serve(graph, { + ui5DataDir, port: 3334, acceptRemoteConnections: true, - ui5DataDir: isolatedUi5DataDir(t), }); request = supertest("http://127.0.0.1:3334"); diff --git a/packages/server/test/lib/server/caching.js b/packages/server/test/lib/server/caching.js index ae5931ed7b6..f5a6c7ef3a8 100644 --- a/packages/server/test/lib/server/caching.js +++ b/packages/server/test/lib/server/caching.js @@ -9,13 +9,15 @@ let server; // Start server before running tests test.before(async (t) => { + const ui5DataDir = isolatedUi5DataDir(t); const graph = await graphFromPackageDependencies({ + ui5DataDir, cwd: "./test/fixtures/application.a" }); server = await serve(graph, { + ui5DataDir, port: 3350, - ui5DataDir: isolatedUi5DataDir(t), }); request = supertest("http://localhost:3350"); }); diff --git a/packages/server/test/lib/server/h2.js b/packages/server/test/lib/server/h2.js index 6a7510e6616..bed5b324c01 100644 --- a/packages/server/test/lib/server/h2.js +++ b/packages/server/test/lib/server/h2.js @@ -17,8 +17,10 @@ if (nodeVersion < 24) { // Start server before running tests test.before(async (t) => { process.env["NODE_TLS_REJECT_UNAUTHORIZED"] = "0"; + const ui5DataDir = isolatedUi5DataDir(t); const graph = await graphFromPackageDependencies({ + ui5DataDir, cwd: "./test/fixtures/application.a" }); const sslPath = path.join(process.cwd(), "./test/fixtures/ssl/"); @@ -27,11 +29,11 @@ if (nodeVersion < 24) { path.join(sslPath, "server.crt"), ); server = await serve(graph, { + ui5DataDir, port: 3366, h2: true, key, cert, - ui5DataDir: isolatedUi5DataDir(t), }); request = supertest("https://localhost:3366"); }); diff --git a/packages/server/test/lib/server/main.js b/packages/server/test/lib/server/main.js index 8e2e0849f0c..d4b60208114 100644 --- a/packages/server/test/lib/server/main.js +++ b/packages/server/test/lib/server/main.js @@ -14,7 +14,9 @@ const SOURCE_MAPPING_URL = "//" + "# sourceMappingURL"; // Start server before running tests test.before(async (t) => { + const ui5DataDir = isolatedUi5DataDir(t); const graph = await graphFromPackageDependencies({ + ui5DataDir, cwd: "./test/fixtures/application.a" }); @@ -25,8 +27,8 @@ test.before(async (t) => { }; server = await serve(graph, { + ui5DataDir, port: 3333, - ui5DataDir: isolatedUi5DataDir(t), }); request = supertest("http://localhost:3333"); }); @@ -379,14 +381,16 @@ async (t) => { test("Stop server", async (t) => { const port = 3351; const request = supertest(`http://localhost:${port}`); + const ui5DataDir = isolatedUi5DataDir(t); const graph = await graphFromPackageDependencies({ + ui5DataDir, cwd: "./test/fixtures/application.a" }); const serveResult = await serve(graph, { + ui5DataDir, port: port, - ui5DataDir: isolatedUi5DataDir(t), }); const res = await request.get("/resources/library/a/.library"); @@ -510,16 +514,18 @@ test("CSP (defaults)", async (t) => { test("CSP (sap policies)", async (t) => { const port = 3400; const request = supertest(`http://localhost:${port}`); + const ui5DataDir = isolatedUi5DataDir(t); const graph = await graphFromPackageDependencies({ + ui5DataDir, cwd: "./test/fixtures/application.a" }); const serveResult = await serve(graph, { + ui5DataDir, port, sendSAPTargetCSP: true, simpleIndex: false, - ui5DataDir: isolatedUi5DataDir(t), }); const [ @@ -625,16 +631,18 @@ test("CSP (sap policies)", async (t) => { test("CSP serveCSPReports", async (t) => { const port = 3450; const request = supertest(`http://localhost:${port}`); + const ui5DataDir = isolatedUi5DataDir(t); const graph = await graphFromPackageDependencies({ + ui5DataDir, cwd: "./test/fixtures/application.a" }); const serveResult = await serve(graph, { + ui5DataDir, port, serveCSPReports: true, simpleIndex: false, - ui5DataDir: isolatedUi5DataDir(t), }); const cspReport = { @@ -680,17 +688,19 @@ test("CSP serveCSPReports", async (t) => { test("CSP with ignore paths", async (t) => { const port = 3500; const request = supertest(`http://localhost:${port}`); + const ui5DataDir = isolatedUi5DataDir(t); const graph = await graphFromPackageDependencies({ + ui5DataDir, cwd: "./test/fixtures/application.a" }); const serveResult = await serve(graph, { + ui5DataDir, port, serveCSPReports: true, sendSAPTargetCSP: true, simpleIndex: false, - ui5DataDir: isolatedUi5DataDir(t), }); const testrunnerRequest1 = request.get("/test-resources/sap/ui/qunit/testrunner.html"); const testrunnerRequest2 = request.get("/index.html") diff --git a/packages/server/test/lib/server/ports.js b/packages/server/test/lib/server/ports.js index 0dee288e112..2e4f30aa40e 100644 --- a/packages/server/test/lib/server/ports.js +++ b/packages/server/test/lib/server/ports.js @@ -18,6 +18,7 @@ test.afterEach.always((t) => { test.serial("Start server - Port is already taken and an error occurs", async (t) => { t.plan(6); const port = 3360; + const ui5DataDir = isolatedUi5DataDir(t); const nodeServer = http.createServer((req, res) => { res.end(); }); @@ -30,12 +31,13 @@ test.serial("Start server - Port is already taken and an error occurs", async (t }); const graph = await graphFromPackageDependencies({ + ui5DataDir, cwd: "./test/fixtures/application.a" }); const startServer = serve(graph, { + ui5DataDir, port, - ui5DataDir: isolatedUi5DataDir(t), }); const error = await t.throwsAsync(startServer); @@ -72,6 +74,7 @@ test.serial("Start server together with node server - Port is already taken and t.plan(2); const port = 3370; const nextFoundPort = 3371; + const ui5DataDir = isolatedUi5DataDir(t); const nodeServer = http.createServer((req, res) => { res.end(); }); @@ -83,12 +86,13 @@ test.serial("Start server together with node server - Port is already taken and }); const graph = await graphFromPackageDependencies({ + ui5DataDir, cwd: "./test/fixtures/application.a" }); const server = await serve(graph, { + ui5DataDir, port, changePortIfInUse: true, - ui5DataDir: isolatedUi5DataDir(t), }); t.deepEqual(server.port, nextFoundPort, "Resolves with correct port"); @@ -106,6 +110,7 @@ test.serial("Start server - Port can not be determined and an error occurs", asy const {sinon} = t.context; t.plan(2); + const ui5DataDir = isolatedUi5DataDir(t); const portscannerFake = function(port, portMax, host, callback) { return new Promise((resolve) => { callback(new Error("testError"), false); @@ -115,12 +120,13 @@ test.serial("Start server - Port can not be determined and an error occurs", asy const portScannerStub = sinon.stub(portscanner, "findAPortNotInUse").callsFake(portscannerFake); const graph = await graphFromPackageDependencies({ + ui5DataDir, cwd: "./test/fixtures/application.a" }); const startServer = serve(graph, { + ui5DataDir, port: 3990, changePortIfInUse: true, - ui5DataDir: isolatedUi5DataDir(t), }); const error = await t.throwsAsync(startServer); @@ -136,6 +142,7 @@ test.serial( t.plan(6); const portStart = 4000; const portRange = 31; + const ui5DataDir = isolatedUi5DataDir(t); const servers = []; const serversStart = []; let port; @@ -156,13 +163,14 @@ test.serial( await Promise.all(serversStart); const graph = await graphFromPackageDependencies({ + ui5DataDir, cwd: "./test/fixtures/application.a" }); const startServer = serve(graph, { + ui5DataDir, port: portStart, changePortIfInUse: true, - ui5DataDir: isolatedUi5DataDir(t), }); const error = await t.throwsAsync(startServer); @@ -202,6 +210,7 @@ test.serial("Start server twice - Port is already taken and the next one is used const nextFoundPort = 3381; const ui5DataDir = isolatedUi5DataDir(t); const graph1 = await graphFromPackageDependencies({ + ui5DataDir, cwd: "./test/fixtures/application.a" }); const serveResult1 = await serve(graph1, { @@ -212,6 +221,7 @@ test.serial("Start server twice - Port is already taken and the next one is used t.deepEqual(serveResult1.port, port, "Resolves with correct port"); const graph2 = await graphFromPackageDependencies({ + ui5DataDir, cwd: "./test/fixtures/application.a" }); const serveResult2 = await serve(graph2, { diff --git a/packages/server/test/lib/server/server.js b/packages/server/test/lib/server/server.js index fc85c81c5d2..b31766d28bd 100644 --- a/packages/server/test/lib/server/server.js +++ b/packages/server/test/lib/server/server.js @@ -6,7 +6,8 @@ import {EventEmitter} from "node:events"; function createMockGraph(mockBuildServer) { const mockProject = { getName: sinon.stub().returns("test.project"), - getSourceReader: sinon.stub().returns({}) + getSourceReader: sinon.stub().returns({}), + getRootPath: sinon.stub().returns("/test/project") }; return { getRoot: sinon.stub().returns(mockProject), @@ -99,7 +100,10 @@ test("server.on('error') rejects the serve promise", async (t) => { const {serve} = await esmock("../../../lib/server.js", mocks); const graph = createMockGraph(mockBuildServer); - const error = await t.throwsAsync(serve(graph, {port: 3000})); + const error = await t.throwsAsync(serve(graph, { + ui5DataDir: "/path/to/ui5-data-dir", + port: 3000 + })); t.is(error, testError); }); @@ -114,7 +118,10 @@ test("buildServer 'error' event is forwarded to error callback", async (t) => { const graph = createMockGraph(mockBuildServer); const errorReceived = new Promise((resolve) => { - serve(graph, {port: 3000}, resolve).then(() => { + serve(graph, { + ui5DataDir: "/path/to/ui5-data-dir", + port: 3000 + }, resolve).then(() => { mockBuildServer.emit("error", testError); }); }); @@ -132,7 +139,10 @@ test("close() still calls server.close when buildServer.destroy() rejects", asyn const {serve} = await esmock("../../../lib/server.js", mocks); const graph = createMockGraph(mockBuildServer); - const result = await serve(graph, {port: 3000}); + const result = await serve(graph, { + ui5DataDir: "/path/to/ui5-data-dir", + port: 3000 + }); await new Promise((resolve) => { result.close(resolve); From 62cdb97ed13da076681623ce24a2f26e7bd91963 Mon Sep 17 00:00:00 2001 From: d3xter666 Date: Thu, 23 Jul 2026 14:59:39 +0300 Subject: [PATCH 17/17] test: Fix failing scenarios for missed required arguments --- .../test/lib/graph/helpers/ui5Framework.js | 36 +++--- .../test/lib/ui5framework/AbstractResolver.js | 118 +++++++++--------- 2 files changed, 80 insertions(+), 74 deletions(-) diff --git a/packages/project/test/lib/graph/helpers/ui5Framework.js b/packages/project/test/lib/graph/helpers/ui5Framework.js index 22176471ee9..6e200d92635 100644 --- a/packages/project/test/lib/graph/helpers/ui5Framework.js +++ b/packages/project/test/lib/graph/helpers/ui5Framework.js @@ -343,10 +343,11 @@ test.serial("enrichProjectGraph: With versionOverride", async (t) => { await ui5Framework.enrichProjectGraph(projectGraph, withUi5DataDir({versionOverride: "1.99"})); t.is(Sapui5ResolverResolveVersionStub.callCount, 1); - t.deepEqual(Sapui5ResolverResolveVersionStub.getCall(0).args, ["1.99", { - cwd: dependencyTree.path, - ui5DataDir: path.resolve("fake-ui5-data-dir"), - }]); + t.deepEqual(Sapui5ResolverResolveVersionStub.getCall(0).args, [ + "1.99", + path.resolve("fake-ui5-data-dir"), + {cwd: dependencyTree.path}, + ]); t.is(Sapui5ResolverStub.callCount, 1, "Sapui5Resolver#constructor should be called once"); t.deepEqual(Sapui5ResolverStub.getCall(0).args, [{ @@ -405,10 +406,11 @@ test.serial("enrichProjectGraph: With versionOverride containing snapshot versio await ui5Framework.enrichProjectGraph(projectGraph, withUi5DataDir({versionOverride: "1.99-SNAPSHOT"})); t.is(Sapui5MavenSnapshotResolverResolveVersionStub.callCount, 1); - t.deepEqual(Sapui5MavenSnapshotResolverResolveVersionStub.getCall(0).args, ["1.99-SNAPSHOT", { - cwd: dependencyTree.path, - ui5DataDir: path.resolve("fake-ui5-data-dir"), - }]); + t.deepEqual(Sapui5MavenSnapshotResolverResolveVersionStub.getCall(0).args, [ + "1.99-SNAPSHOT", + path.resolve("fake-ui5-data-dir"), + {cwd: dependencyTree.path}, + ]); t.is(Sapui5MavenSnapshotResolverStub.callCount, 1, "Sapui5MavenSnapshotResolverStub#constructor should be called once"); @@ -468,10 +470,11 @@ test.serial("enrichProjectGraph: With versionOverride containing latest-snapshot await ui5Framework.enrichProjectGraph(projectGraph, withUi5DataDir({versionOverride: "latest-snapshot"})); t.is(Sapui5MavenSnapshotResolverResolveVersionStub.callCount, 1); - t.deepEqual(Sapui5MavenSnapshotResolverResolveVersionStub.getCall(0).args, ["latest-snapshot", { - cwd: dependencyTree.path, - ui5DataDir: path.resolve("fake-ui5-data-dir"), - }]); + t.deepEqual(Sapui5MavenSnapshotResolverResolveVersionStub.getCall(0).args, [ + "latest-snapshot", + path.resolve("fake-ui5-data-dir"), + {cwd: dependencyTree.path}, + ]); t.is(Sapui5MavenSnapshotResolverStub.callCount, 1, "Sapui5MavenSnapshotResolverStub#constructor should be called once"); @@ -733,10 +736,11 @@ test.serial("enrichProjectGraph should resolve framework project " + t.is(projectGraph.getSize(), 3, "Project graph should remain unchanged"); t.is(Sapui5ResolverResolveVersionStub.callCount, 1); - t.deepEqual(Sapui5ResolverResolveVersionStub.getCall(0).args, ["3.4.5", { - cwd: dependencyTree.path, - ui5DataDir: path.resolve("fake-ui5-data-dir"), - }]); + t.deepEqual(Sapui5ResolverResolveVersionStub.getCall(0).args, [ + "3.4.5", + path.resolve("fake-ui5-data-dir"), + {cwd: dependencyTree.path}, + ]); t.is(Sapui5ResolverStub.callCount, 1, "Sapui5Resolver#constructor should be called once"); t.is(getFrameworkLibrariesFromGraphStub.callCount, 1, "getFrameworkLibrariesFromGraph should be called once"); diff --git a/packages/project/test/lib/ui5framework/AbstractResolver.js b/packages/project/test/lib/ui5framework/AbstractResolver.js index 9f5b1ebff1e..6ccd0fd1fb2 100644 --- a/packages/project/test/lib/ui5framework/AbstractResolver.js +++ b/packages/project/test/lib/ui5framework/AbstractResolver.js @@ -3,13 +3,15 @@ import sinon from "sinon"; import path from "node:path"; import esmock from "esmock"; +const UI5_DATA_DIR = path.resolve("/ui5DataDir"); + test.beforeEach(async (t) => { t.context.AbstractResolver = await esmock.p("../../../lib/ui5Framework/AbstractResolver.js", {}); class MyResolver extends t.context.AbstractResolver { constructor(options = {}) { super({ - ui5DataDir: "/ui5DataDir", + ui5DataDir: UI5_DATA_DIR, ...options }); } @@ -556,13 +558,13 @@ test.serial("AbstractResolver: Static resolveVersion resolves 'latest'", async ( const fetchAllVersionsStub = sinon.stub(MyResolver, "fetchAllVersions") .returns(["1.75.0", "1.75.1", "1.76.0"]); - const version = await MyResolver.resolveVersion("latest", "/ui5DataDir", {cwd: "/cwd"}); + const version = await MyResolver.resolveVersion("latest", UI5_DATA_DIR, {cwd: "/cwd"}); t.is(version, "1.76.0", "Resolved version should be correct"); t.is(fetchAllVersionsStub.callCount, 1, "fetchAllVersions should be called once"); t.deepEqual(fetchAllVersionsStub.getCall(0).args, [ - "/ui5DataDir", + UI5_DATA_DIR, {cwd: "/cwd"} ], "fetchAllVersions should be called with expected arguments"); }); @@ -572,13 +574,13 @@ test.serial("AbstractResolver: Static resolveVersion resolves 'MAJOR'", async (t const fetchAllVersionsStub = sinon.stub(MyResolver, "fetchAllVersions") .returns(["1.75.0", "1.75.1", "1.76.0"]); - const version = await MyResolver.resolveVersion("1", "/ui5DataDir", {cwd: "/cwd"}); + const version = await MyResolver.resolveVersion("1", UI5_DATA_DIR, {cwd: "/cwd"}); t.is(version, "1.76.0", "Resolved version should be correct"); t.is(fetchAllVersionsStub.callCount, 1, "fetchAllVersions should be called once"); t.deepEqual(fetchAllVersionsStub.getCall(0).args, [ - "/ui5DataDir", + UI5_DATA_DIR, {cwd: "/cwd"} ], "fetchAllVersions should be called with expected arguments"); }); @@ -588,13 +590,13 @@ test.serial("AbstractResolver: Static resolveVersion resolves 'MAJOR-SNAPSHOT'", const fetchAllVersionsStub = sinon.stub(MyResolver, "fetchAllVersions") .returns(["1.76.0", "1.77.0", "1.77.0-SNAPSHOT", "1.78.0", "1.79.0-SNAPSHOT"]); - const version = await MyResolver.resolveVersion("1-SNAPSHOT", "/ui5DataDir", {cwd: "/cwd"}); + const version = await MyResolver.resolveVersion("1-SNAPSHOT", UI5_DATA_DIR, {cwd: "/cwd"}); t.is(version, "1.79.0-SNAPSHOT", "Resolved version should be correct"); t.is(fetchAllVersionsStub.callCount, 1, "fetchAllVersions should be called once"); t.deepEqual(fetchAllVersionsStub.getCall(0).args, [ - "/ui5DataDir", + UI5_DATA_DIR, {cwd: "/cwd"} ], "fetchAllVersions should be called with expected arguments"); }); @@ -604,13 +606,13 @@ test.serial("AbstractResolver: Static resolveVersion resolves 'MAJOR.MINOR'", as const fetchAllVersionsStub = sinon.stub(MyResolver, "fetchAllVersions") .returns(["1.75.0", "1.75.1", "1.76.0"]); - const version = await MyResolver.resolveVersion("1.75", "/ui5DataDir", {cwd: "/cwd"}); + const version = await MyResolver.resolveVersion("1.75", UI5_DATA_DIR, {cwd: "/cwd"}); t.is(version, "1.75.1", "Resolved version should be correct"); t.is(fetchAllVersionsStub.callCount, 1, "fetchAllVersions should be called once"); t.deepEqual(fetchAllVersionsStub.getCall(0).args, [ - "/ui5DataDir", + UI5_DATA_DIR, {cwd: "/cwd"} ], "fetchAllVersions should be called with expected arguments"); }); @@ -620,13 +622,13 @@ test.serial("AbstractResolver: Static resolveVersion resolves 'MAJOR.MINOR-SNAPS const fetchAllVersionsStub = sinon.stub(MyResolver, "fetchAllVersions") .returns(["1.76.0", "1.77.0", "1.77.0-SNAPSHOT", "1.78.0", "1.79.0-SNAPSHOT"]); - const version = await MyResolver.resolveVersion("1.79-SNAPSHOT", "/ui5DataDir", {cwd: "/cwd"}); + const version = await MyResolver.resolveVersion("1.79-SNAPSHOT", UI5_DATA_DIR, {cwd: "/cwd"}); t.is(version, "1.79.0-SNAPSHOT", "Resolved version should be correct"); t.is(fetchAllVersionsStub.callCount, 1, "fetchAllVersions should be called once"); t.deepEqual(fetchAllVersionsStub.getCall(0).args, [ - "/ui5DataDir", + UI5_DATA_DIR, {cwd: "/cwd"} ], "fetchAllVersions should be called with expected arguments"); }); @@ -636,13 +638,13 @@ test.serial("AbstractResolver: Static resolveVersion resolves 'MAJOR.MINOR.PATCH const fetchAllVersionsStub = sinon.stub(MyResolver, "fetchAllVersions") .returns(["1.75.0", "1.75.1", "1.76.0"]); - const version = await MyResolver.resolveVersion("1.75.0", "/ui5DataDir", {cwd: "/cwd"}); + const version = await MyResolver.resolveVersion("1.75.0", UI5_DATA_DIR, {cwd: "/cwd"}); t.is(version, "1.75.0", "Resolved version should be correct"); t.is(fetchAllVersionsStub.callCount, 1, "fetchAllVersions should be called once"); t.deepEqual(fetchAllVersionsStub.getCall(0).args, [ - "/ui5DataDir", + UI5_DATA_DIR, {cwd: "/cwd"} ], "fetchAllVersions should be called with expected arguments"); }); @@ -652,13 +654,13 @@ test.serial("AbstractResolver: Static resolveVersion resolves 'MAJOR.MINOR.PATCH const fetchAllVersionsStub = sinon.stub(MyResolver, "fetchAllVersions") .returns(["1.76.0", "1.77.0", "1.77.0-SNAPSHOT", "1.78.0", "1.79.0-SNAPSHOT"]); - const version = await MyResolver.resolveVersion("1.79.0-SNAPSHOT", "/ui5DataDir", {cwd: "/cwd"}); + const version = await MyResolver.resolveVersion("1.79.0-SNAPSHOT", UI5_DATA_DIR, {cwd: "/cwd"}); t.is(version, "1.79.0-SNAPSHOT", "Resolved version should be correct"); t.is(fetchAllVersionsStub.callCount, 1, "fetchAllVersions should be called once"); t.deepEqual(fetchAllVersionsStub.getCall(0).args, [ - "/ui5DataDir", + UI5_DATA_DIR, {cwd: "/cwd"} ], "fetchAllVersions should be called with expected arguments"); }); @@ -668,13 +670,13 @@ test.serial("AbstractResolver: Static resolveVersion does not include prerelease const fetchAllVersionsStub = sinon.stub(MyResolver, "fetchAllVersions") .returns(["1.76.0", "1.77.0", "1.78.0", "1.79.0-SNAPSHOT"]); - const version = await MyResolver.resolveVersion("latest", "/ui5DataDir", {cwd: "/cwd"}); + const version = await MyResolver.resolveVersion("latest", UI5_DATA_DIR, {cwd: "/cwd"}); t.is(version, "1.78.0", "Resolved version should be correct"); t.is(fetchAllVersionsStub.callCount, 1, "fetchAllVersions should be called once"); t.deepEqual(fetchAllVersionsStub.getCall(0).args, [ - "/ui5DataDir", + UI5_DATA_DIR, {cwd: "/cwd"} ], "fetchAllVersions should be called with expected arguments"); }); @@ -684,13 +686,13 @@ test.serial("AbstractResolver: Static resolveVersion resolves 'latest-snapshot'" const fetchAllVersionsStub = sinon.stub(MyResolver, "fetchAllVersions") .returns(["1.75.0-SNAPSHOT", "1.75.1-SNAPSHOT", "1.76.0-SNAPSHOT", "1.76.1-SNAPSHOT"]); - const version = await MyResolver.resolveVersion("latest-snapshot", "/ui5DataDir", {cwd: "/cwd"}); + const version = await MyResolver.resolveVersion("latest-snapshot", UI5_DATA_DIR, {cwd: "/cwd"}); t.is(version, "1.76.1-SNAPSHOT", "Resolved version should be correct"); t.is(fetchAllVersionsStub.callCount, 1, "fetchAllVersions should be called once"); t.deepEqual(fetchAllVersionsStub.getCall(0).args, [ - "/ui5DataDir", + UI5_DATA_DIR, {cwd: "/cwd"} ], "fetchAllVersions should be called with expected arguments"); }); @@ -702,13 +704,13 @@ test.serial("AbstractResolver: Static resolveVersion includes non-prereleases fo const fetchAllVersionsStub = sinon.stub(MyResolver, "fetchAllVersions") .returns(["1.76.0", "1.77.0", "1.78.0", "1.79.0-SNAPSHOT", "1.79.1"]); - const version = await MyResolver.resolveVersion("latest-snapshot", "/ui5DataDir", {cwd: "/cwd"}); + const version = await MyResolver.resolveVersion("latest-snapshot", UI5_DATA_DIR, {cwd: "/cwd"}); t.is(version, "1.79.1", "Resolved version should be correct"); t.is(fetchAllVersionsStub.callCount, 1, "fetchAllVersions should be called once"); t.deepEqual(fetchAllVersionsStub.getCall(0).args, [ - "/ui5DataDir", + UI5_DATA_DIR, {cwd: "/cwd"} ], "fetchAllVersions should be called with expected arguments"); }); @@ -723,7 +725,7 @@ test.serial("AbstractResolver: Static resolveVersion throws error for 'lts'", as const {MyResolver} = t.context; const fetchAllVersionsStub = sinon.stub(MyResolver, "fetchAllVersions"); - const error = await t.throwsAsync(MyResolver.resolveVersion("lts", "/ui5DataDir", {cwd: "/cwd"})); + const error = await t.throwsAsync(MyResolver.resolveVersion("lts", UI5_DATA_DIR, {cwd: "/cwd"})); t.is(error.message, `Framework version specifier "lts" is incorrect or not supported`); @@ -735,13 +737,13 @@ test.serial("AbstractResolver: Static resolveVersion resolves '1.x'", async (t) const fetchAllVersionsStub = sinon.stub(MyResolver, "fetchAllVersions") .returns(["1.75.0", "1.75.1", "1.76.0", "2.0.0"]); - const version = await MyResolver.resolveVersion("1.x", "/ui5DataDir", {cwd: "/cwd"}); + const version = await MyResolver.resolveVersion("1.x", UI5_DATA_DIR, {cwd: "/cwd"}); t.is(version, "1.76.0", "Resolved version should be correct"); t.is(fetchAllVersionsStub.callCount, 1, "fetchAllVersions should be called once"); t.deepEqual(fetchAllVersionsStub.getCall(0).args, [ - "/ui5DataDir", + UI5_DATA_DIR, {cwd: "/cwd"} ], "fetchAllVersions should be called with expected arguments"); }); @@ -751,13 +753,13 @@ test.serial("AbstractResolver: Static resolveVersion resolves '1.75.x'", async ( const fetchAllVersionsStub = sinon.stub(MyResolver, "fetchAllVersions") .returns(["1.75.0", "1.75.1", "1.76.0", "2.0.0"]); - const version = await MyResolver.resolveVersion("1.75.x", "/ui5DataDir", {cwd: "/cwd"}); + const version = await MyResolver.resolveVersion("1.75.x", UI5_DATA_DIR, {cwd: "/cwd"}); t.is(version, "1.75.1", "Resolved version should be correct"); t.is(fetchAllVersionsStub.callCount, 1, "fetchAllVersions should be called once"); t.deepEqual(fetchAllVersionsStub.getCall(0).args, [ - "/ui5DataDir", + UI5_DATA_DIR, {cwd: "/cwd"} ], "fetchAllVersions should be called with expected arguments"); }); @@ -767,13 +769,13 @@ test.serial("AbstractResolver: Static resolveVersion resolves '^1.75.0'", async const fetchAllVersionsStub = sinon.stub(MyResolver, "fetchAllVersions") .returns(["1.75.0", "1.75.1", "1.76.0", "2.0.0"]); - const version = await MyResolver.resolveVersion("^1.75.0", "/ui5DataDir", {cwd: "/cwd"}); + const version = await MyResolver.resolveVersion("^1.75.0", UI5_DATA_DIR, {cwd: "/cwd"}); t.is(version, "1.76.0", "Resolved version should be correct"); t.is(fetchAllVersionsStub.callCount, 1, "fetchAllVersions should be called once"); t.deepEqual(fetchAllVersionsStub.getCall(0).args, [ - "/ui5DataDir", + UI5_DATA_DIR, {cwd: "/cwd"} ], "fetchAllVersions should be called with expected arguments"); }); @@ -783,13 +785,13 @@ test.serial("AbstractResolver: Static resolveVersion resolves '~1.75.0'", async const fetchAllVersionsStub = sinon.stub(MyResolver, "fetchAllVersions") .returns(["1.75.0", "1.75.1", "1.76.0", "2.0.0"]); - const version = await MyResolver.resolveVersion("~1.75.0", "/ui5DataDir", {cwd: "/cwd"}); + const version = await MyResolver.resolveVersion("~1.75.0", UI5_DATA_DIR, {cwd: "/cwd"}); t.is(version, "1.75.1", "Resolved version should be correct"); t.is(fetchAllVersionsStub.callCount, 1, "fetchAllVersions should be called once"); t.deepEqual(fetchAllVersionsStub.getCall(0).args, [ - "/ui5DataDir", + UI5_DATA_DIR, {cwd: "/cwd"} ], "fetchAllVersions should be called with expected arguments"); }); @@ -799,13 +801,13 @@ test.serial("AbstractResolver: Static resolveVersion resolves '> 1.75.0 < 1.75.3 const fetchAllVersionsStub = sinon.stub(MyResolver, "fetchAllVersions") .returns(["1.75.0", "1.75.1", "1.75.2", "1.75.3"]); - const version = await MyResolver.resolveVersion("> 1.75.0 < 1.75.3", "/ui5DataDir", {cwd: "/cwd"}); + const version = await MyResolver.resolveVersion("> 1.75.0 < 1.75.3", UI5_DATA_DIR, {cwd: "/cwd"}); t.is(version, "1.75.2", "Resolved version should be correct"); t.is(fetchAllVersionsStub.callCount, 1, "fetchAllVersions should be called once"); t.deepEqual(fetchAllVersionsStub.getCall(0).args, [ - "/ui5DataDir", + UI5_DATA_DIR, {cwd: "/cwd"} ], "fetchAllVersions should be called with expected arguments"); }); @@ -815,7 +817,7 @@ test.serial("AbstractResolver: Static resolveVersion resolves 'x.x.x-SNAPSHOT'", const fetchAllVersionsStub = sinon.stub(MyResolver, "fetchAllVersions") .returns(["1.75.0-SNAPSHOT", "1.76.0-SNAPSHOT", "1.77.0-SNAPSHOT"]); - const version = await MyResolver.resolveVersion("x.x.x-SNAPSHOT", "/ui5DataDir", {cwd: "/cwd"}); + const version = await MyResolver.resolveVersion("x.x.x-SNAPSHOT", UI5_DATA_DIR, {cwd: "/cwd"}); // All ranges ending with -SNAPSHOT should use "includePrerelease" in order to // properly match prerelease (i.e. -SNAPSHOT) versions. @@ -823,7 +825,7 @@ test.serial("AbstractResolver: Static resolveVersion resolves 'x.x.x-SNAPSHOT'", t.is(fetchAllVersionsStub.callCount, 1, "fetchAllVersions should be called once"); t.deepEqual(fetchAllVersionsStub.getCall(0).args, [ - "/ui5DataDir", + UI5_DATA_DIR, {cwd: "/cwd"} ], "fetchAllVersions should be called with expected arguments"); }); @@ -833,7 +835,7 @@ test.serial("AbstractResolver: Static resolveVersion resolves '^2.0.0-SNAPSHOT'" const fetchAllVersionsStub = sinon.stub(MyResolver, "fetchAllVersions") .returns(["2.0.0-SNAPSHOT", "2.0.1-SNAPSHOT", "2.1.0-SNAPSHOT"]); - const version = await MyResolver.resolveVersion("^2.0.0-SNAPSHOT", "/ui5DataDir", {cwd: "/cwd"}); + const version = await MyResolver.resolveVersion("^2.0.0-SNAPSHOT", UI5_DATA_DIR, {cwd: "/cwd"}); // All ranges ending with -SNAPSHOT should use "includePrerelease" in order to // properly match prerelease (i.e. -SNAPSHOT) versions. @@ -841,7 +843,7 @@ test.serial("AbstractResolver: Static resolveVersion resolves '^2.0.0-SNAPSHOT'" t.is(fetchAllVersionsStub.callCount, 1, "fetchAllVersions should be called once"); t.deepEqual(fetchAllVersionsStub.getCall(0).args, [ - "/ui5DataDir", + UI5_DATA_DIR, {cwd: "/cwd"} ], "fetchAllVersions should be called with expected arguments"); }); @@ -851,7 +853,7 @@ test.serial("AbstractResolver: Static resolveVersion resolves '2.x.x-alpha'", as const fetchAllVersionsStub = sinon.stub(MyResolver, "fetchAllVersions") .returns(["2.0.0-alpha", "2.0.1-alpha", "2.1.0-alpha"]); - const version = await MyResolver.resolveVersion("^2.0.0-alpha", "/ui5DataDir", {cwd: "/cwd"}); + const version = await MyResolver.resolveVersion("^2.0.0-alpha", UI5_DATA_DIR, {cwd: "/cwd"}); // Prerelease ranges other than -SNAPSHOT should not use "includePrerelease" // and therefore not match pre-releases like normal versions @@ -859,7 +861,7 @@ test.serial("AbstractResolver: Static resolveVersion resolves '2.x.x-alpha'", as t.is(fetchAllVersionsStub.callCount, 1, "fetchAllVersions should be called once"); t.deepEqual(fetchAllVersionsStub.getCall(0).args, [ - "/ui5DataDir", + UI5_DATA_DIR, {cwd: "/cwd"} ], "fetchAllVersions should be called with expected arguments"); }); @@ -874,18 +876,18 @@ test.serial("AbstractResolver: Static resolveVersion resolves 'next' using tags" "next": "2.0.0" }); - const version = await MyResolver.resolveVersion("next", "/ui5DataDir", {cwd: "/cwd"}); + const version = await MyResolver.resolveVersion("next", UI5_DATA_DIR, {cwd: "/cwd"}); t.is(version, "2.0.0", "Resolved version should be correct"); t.is(fetchAllVersionsStub.callCount, 1, "fetchAllVersions should be called once"); t.deepEqual(fetchAllVersionsStub.getCall(0).args, [ - "/ui5DataDir", + UI5_DATA_DIR, {cwd: "/cwd"} ], "fetchAllVersions should be called with expected arguments"); t.is(fetchAllTagsStub.callCount, 1, "fetchAllTagsStub should be called once"); t.deepEqual(fetchAllVersionsStub.getCall(0).args, [ - "/ui5DataDir", + UI5_DATA_DIR, {cwd: "/cwd"} ], "fetchAllTags should be called with expected arguments"); }); @@ -900,18 +902,18 @@ test.serial("AbstractResolver: Static resolveVersion resolves 'next' to a pre-re "next": "2.0.0-SNAPSHOT" }); - const version = await MyResolver.resolveVersion("next", "/ui5DataDir", {cwd: "/cwd"}); + const version = await MyResolver.resolveVersion("next", UI5_DATA_DIR, {cwd: "/cwd"}); t.is(version, "2.0.0-SNAPSHOT", "Resolved version should be correct"); t.is(fetchAllVersionsStub.callCount, 1, "fetchAllVersions should be called once"); t.deepEqual(fetchAllVersionsStub.getCall(0).args, [ - "/ui5DataDir", + UI5_DATA_DIR, {cwd: "/cwd"} ], "fetchAllVersions should be called with expected arguments"); t.is(fetchAllTagsStub.callCount, 1, "fetchAllTagsStub should be called once"); t.deepEqual(fetchAllVersionsStub.getCall(0).args, [ - "/ui5DataDir", + UI5_DATA_DIR, {cwd: "/cwd"} ], "fetchAllTags should be called with expected arguments"); }); @@ -927,12 +929,12 @@ test.serial("AbstractResolver: Static resolveVersion resolves 'latest' using tag // Resolver does not support tags (resolves with "null" instead of an object) // 'latest' should resolve to the highest version available - const version1 = await MyResolver.resolveVersion("latest", "/ui5DataDir", {cwd: "/cwd"}); + const version1 = await MyResolver.resolveVersion("latest", UI5_DATA_DIR, {cwd: "/cwd"}); t.is(version1, "2.0.0", "Resolved version should be correct"); t.is(fetchAllTagsStub.callCount, 1, "fetchAllTagsStub should be called once"); t.deepEqual(fetchAllVersionsStub.getCall(0).args, [ - "/ui5DataDir", + UI5_DATA_DIR, {cwd: "/cwd"} ], "fetchAllTags should be called with expected arguments"); @@ -941,12 +943,12 @@ test.serial("AbstractResolver: Static resolveVersion resolves 'latest' using tag fetchAllTagsStub.resolves({ "latest": "1.76.0" }); - const version2 = await MyResolver.resolveVersion("latest", "/ui5DataDir", {cwd: "/cwd"}); + const version2 = await MyResolver.resolveVersion("latest", UI5_DATA_DIR, {cwd: "/cwd"}); t.is(version2, "1.76.0", "Resolved version should be correct"); t.is(fetchAllTagsStub.callCount, 2, "fetchAllTagsStub should be called twice"); t.deepEqual(fetchAllVersionsStub.getCall(1).args, [ - "/ui5DataDir", + UI5_DATA_DIR, {cwd: "/cwd"} ], "fetchAllTags should be called with expected arguments"); }); @@ -956,7 +958,7 @@ test.serial("AbstractResolver: Static resolveVersion throws error for empty stri sinon.stub(MyResolver, "fetchAllVersions") .returns(["1.75.0", "1.75.1", "1.76.0"]); - const error = await t.throwsAsync(MyResolver.resolveVersion("", "/ui5DataDir", {cwd: "/cwd"})); + const error = await t.throwsAsync(MyResolver.resolveVersion("", UI5_DATA_DIR, {cwd: "/cwd"})); t.is(error.message, `Framework version specifier "" is incorrect or not supported`); }); @@ -966,7 +968,7 @@ test.serial("AbstractResolver: Static resolveVersion throws error for invalid ta sinon.stub(MyResolver, "fetchAllVersions") .returns(["1.75.0", "1.75.1", "1.76.0"]); - const error = await t.throwsAsync(MyResolver.resolveVersion("%20", "/ui5DataDir", {cwd: "/cwd"})); + const error = await t.throwsAsync(MyResolver.resolveVersion("%20", UI5_DATA_DIR, {cwd: "/cwd"})); t.is(error.message, `Framework version specifier "%20" is incorrect or not supported`); }); @@ -979,7 +981,7 @@ test.serial("AbstractResolver: Static resolveVersion throws error for non-existi .resolves({"latest": "1.76.0"}); const error = await t.throwsAsync( - MyResolver.resolveVersion("this-tag-does-not-exist", "/ui5DataDir", {cwd: "/cwd"}) + MyResolver.resolveVersion("this-tag-does-not-exist", UI5_DATA_DIR, {cwd: "/cwd"}) ); t.is(error.message, `Could not resolve framework version via tag 'this-tag-does-not-exist'. ` + @@ -992,7 +994,7 @@ test.serial("AbstractResolver: Static resolveVersion throws error for version no sinon.stub(MyResolver, "fetchAllVersions") .returns(["1.75.0", "1.75.1", "1.76.0"]); - const error = await t.throwsAsync(MyResolver.resolveVersion("1.74.0", "/ui5DataDir", {cwd: "/cwd"})); + const error = await t.throwsAsync(MyResolver.resolveVersion("1.74.0", UI5_DATA_DIR, {cwd: "/cwd"})); t.is(error.message, `Could not resolve framework version 1.74.0. ` + `Make sure the version is valid and available in the configured registry.`); @@ -1008,7 +1010,7 @@ test.serial( sinon.stub(Openui5Resolver, "fetchAllVersions") .returns(["1.75.0", "1.75.1", "1.76.0"]); - const error = await t.throwsAsync(Openui5Resolver.resolveVersion("1.50.0", "/ui5DataDir", {cwd: "/cwd"})); + const error = await t.throwsAsync(Openui5Resolver.resolveVersion("1.50.0", UI5_DATA_DIR, {cwd: "/cwd"})); t.is(error.message, `Could not resolve framework version 1.50.0. Note that OpenUI5 framework libraries can only be ` + @@ -1025,7 +1027,7 @@ test.serial( sinon.stub(Sapui5Resolver, "fetchAllVersions") .returns(["1.76.0", "1.76.1", "1.90.0"]); - const error = await t.throwsAsync(Sapui5Resolver.resolveVersion("1.75.0", "/ui5DataDir", {cwd: "/cwd"})); + const error = await t.throwsAsync(Sapui5Resolver.resolveVersion("1.75.0", UI5_DATA_DIR, {cwd: "/cwd"})); t.is(error.message, `Could not resolve framework version 1.75.0. Note that SAPUI5 framework libraries can only be ` + @@ -1042,7 +1044,7 @@ test.serial( sinon.stub(Openui5Resolver, "fetchAllVersions") .returns([]); - const error = await t.throwsAsync(Openui5Resolver.resolveVersion("latest", "/ui5DataDir", {cwd: "/cwd"})); + const error = await t.throwsAsync(Openui5Resolver.resolveVersion("latest", UI5_DATA_DIR, {cwd: "/cwd"})); t.is(error.message, `Could not resolve framework version latest. ` + `Make sure the version is valid and available in the configured registry.`); @@ -1058,7 +1060,7 @@ test.serial( sinon.stub(Sapui5Resolver, "fetchAllVersions") .returns([]); - const error = await t.throwsAsync(Sapui5Resolver.resolveVersion("latest", "/ui5DataDir", {cwd: "/cwd"})); + const error = await t.throwsAsync(Sapui5Resolver.resolveVersion("latest", UI5_DATA_DIR, {cwd: "/cwd"})); t.is(error.message, `Could not resolve framework version latest. ` + `Make sure the version is valid and available in the configured registry.`); @@ -1074,7 +1076,7 @@ test.serial( sinon.stub(Openui5Resolver, "fetchAllVersions") .returns([]); - const error = await t.throwsAsync(Openui5Resolver.resolveVersion("1.99", "/ui5DataDir", {cwd: "/cwd"})); + const error = await t.throwsAsync(Openui5Resolver.resolveVersion("1.99", UI5_DATA_DIR, {cwd: "/cwd"})); t.is(error.message, `Could not resolve framework version 1.99. ` + `Make sure the version is valid and available in the configured registry.`); @@ -1090,7 +1092,7 @@ test.serial( sinon.stub(Sapui5Resolver, "fetchAllVersions") .returns([]); - const error = await t.throwsAsync(Sapui5Resolver.resolveVersion("1.99", "/ui5DataDir", {cwd: "/cwd"})); + const error = await t.throwsAsync(Sapui5Resolver.resolveVersion("1.99", UI5_DATA_DIR, {cwd: "/cwd"})); t.is(error.message, `Could not resolve framework version 1.99. ` + `Make sure the version is valid and available in the configured registry.`);