-
Notifications
You must be signed in to change notification settings - Fork 14
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
5 changed files
with
567 additions
and
6 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,201 @@ | ||
import test from "ava"; | ||
import sinonGlobal from "sinon"; | ||
import esmock from "esmock"; | ||
import path from "node:path"; | ||
import {fileURLToPath} from "node:url"; | ||
|
||
const __dirname = path.dirname(fileURLToPath(import.meta.url)); | ||
|
||
// Use path within project as mocking base directory to reduce chance of side effects | ||
// in case mocks/stubs do not work and real fs is used | ||
const fakeBaseDir = path.join(__dirname, "fake-tmp"); | ||
|
||
test.beforeEach(async (t) => { | ||
const sinon = t.context.sinon = sinonGlobal.createSandbox(); | ||
|
||
t.context.logStub = { | ||
info: sinon.stub(), | ||
verbose: sinon.stub(), | ||
silly: sinon.stub(), | ||
warn: sinon.stub(), | ||
error: sinon.stub(), | ||
isLevelEnabled: sinon.stub().returns(false), | ||
_getLogger: sinon.stub() | ||
}; | ||
const ui5Logger = { | ||
getLogger: sinon.stub().returns(t.context.logStub) | ||
}; | ||
|
||
t.context.pacote = { | ||
packument: sinon.stub().callsFake(async (...args) => { | ||
throw new Error(`pacote.packument stub called with unknown args: ${args}`); | ||
}) | ||
}; | ||
|
||
t.context.NpmcliConfig = sinon.stub().returns({ | ||
load: sinon.stub().resolves(), | ||
flat: { | ||
registry: "https://registry.fake" | ||
} | ||
}); | ||
|
||
t.context.Registry = await esmock.p("../../../lib/ui5Framework/npm/Registry.js", { | ||
"@ui5/logger": ui5Logger, | ||
"pacote": t.context.pacote, | ||
"@npmcli/config": { | ||
"default": t.context.NpmcliConfig | ||
} | ||
}); | ||
|
||
const AbstractInstaller = await esmock.p("../../../lib/ui5Framework/AbstractInstaller.js", { | ||
"@ui5/logger": ui5Logger, | ||
"../../../lib/utils/fs.js": { | ||
mkdirp: sinon.stub().resolves() | ||
}, | ||
"lockfile": { | ||
lock: sinon.stub().yieldsAsync(), | ||
unlock: sinon.stub().yieldsAsync() | ||
} | ||
}); | ||
|
||
t.context.Installer = await esmock.p("../../../lib/ui5Framework/npm/Installer.js", { | ||
"@ui5/logger": ui5Logger, | ||
"graceful-fs": { | ||
rename: sinon.stub().yieldsAsync(), | ||
}, | ||
"../../../lib/utils/fs.js": { | ||
mkdirp: sinon.stub().resolves() | ||
}, | ||
"../../../lib/ui5Framework/npm/Registry.js": t.context.Registry, | ||
"../../../lib/ui5Framework/AbstractInstaller.js": AbstractInstaller | ||
}); | ||
|
||
t.context.AbstractResolver = await esmock.p("../../../lib/ui5Framework/AbstractResolver.js", { | ||
"@ui5/logger": ui5Logger, | ||
"node:os": { | ||
homedir: sinon.stub().returns(path.join(fakeBaseDir, "homedir")) | ||
}, | ||
}); | ||
|
||
t.context.Openui5Resolver = await esmock.p("../../../lib/ui5Framework/Openui5Resolver.js", { | ||
"@ui5/logger": ui5Logger, | ||
"node:os": { | ||
homedir: sinon.stub().returns(path.join(fakeBaseDir, "homedir")) | ||
}, | ||
"../../../lib/ui5Framework/AbstractResolver.js": t.context.AbstractResolver, | ||
"../../../lib/ui5Framework/npm/Installer.js": t.context.Installer | ||
}); | ||
}); | ||
|
||
test.afterEach.always((t) => { | ||
t.context.sinon.restore(); | ||
esmock.purge(t.context.Registry); | ||
esmock.purge(t.context.Installer); | ||
esmock.purge(t.context.AbstractResolver); | ||
esmock.purge(t.context.Openui5Resolver); | ||
}); | ||
|
||
test.serial("resolveVersion", async (t) => { | ||
const {Openui5Resolver, pacote, logStub, NpmcliConfig} = t.context; | ||
|
||
pacote.packument | ||
.withArgs("@openui5/sap.ui.core") | ||
.resolves({ | ||
"versions": { | ||
"1.120.1": "", | ||
"1.120.0": "", | ||
"1.119.0": "", | ||
"1.118.0": "", | ||
"2.0.0-rc.1": "", | ||
"1.123.4-SNAPSHOT": "" | ||
}, | ||
"dist-tags": { | ||
// NOTE: latest does not correspond to highest version in order to verify | ||
// that this tag is used instead of picking the highest version | ||
"latest": "1.120.0", | ||
|
||
"next": "2.0.0-rc.1", | ||
|
||
// NOTE: Tag ends with "-snapshot" in order to verify that the special handling | ||
// of that | ||
"not-a-snapshot": "1.118.0" | ||
} | ||
}); | ||
|
||
const defaultCwd = process.cwd(); | ||
const defaultUi5HomeDir = path.join(fakeBaseDir, "homedir", ".ui5"); | ||
|
||
// Generic testing without and with options argument | ||
const optionsArguments = [ | ||
undefined, | ||
{ | ||
cwd: path.join(fakeBaseDir, "custom-cwd"), | ||
ui5HomeDir: path.join(fakeBaseDir, "custom-homedir", ".ui5") | ||
} | ||
]; | ||
for (const options of optionsArguments) { | ||
// Reset calls to be able to check them per for-loop run | ||
NpmcliConfig.resetHistory(); | ||
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"); | ||
|
||
// 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"); | ||
|
||
// 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"); | ||
|
||
// 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"); | ||
|
||
// Error cases | ||
await t.throwsAsync(Openui5Resolver.resolveVersion("", options), { | ||
message: `Framework version specifier "" is incorrect or not supported` | ||
}); | ||
await t.throwsAsync(Openui5Resolver.resolveVersion("tag-does-not-exist", 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), { | ||
message: `Framework version specifier "invalid-tag-%20" is incorrect or not supported` | ||
}); | ||
|
||
await t.throwsAsync(Openui5Resolver.resolveVersion("1.999.9", 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), { | ||
message: `Could not resolve framework version 1.0.0. ` + | ||
`Note that OpenUI5 framework libraries can only be consumed by the UI5 Tooling ` + | ||
`starting with OpenUI5 v1.52.5` | ||
}); | ||
await t.throwsAsync(Openui5Resolver.resolveVersion("^999", options), { | ||
message: `Could not resolve framework version ^999. ` + | ||
`Make sure the version is valid and available in the configured registry.` | ||
}); | ||
|
||
// Check whether options have been passed as expected | ||
t.true(NpmcliConfig.alwaysCalledWithNew()); | ||
t.true(NpmcliConfig.alwaysCalledWithMatch(sinonGlobal.match({ | ||
cwd: options?.cwd ?? defaultCwd | ||
}))); | ||
t.true(pacote.packument.alwaysCalledWithMatch("@openui5/sap.ui.core", { | ||
cache: path.join(options?.ui5HomeDir ?? defaultUi5HomeDir, "framework", "cacache") | ||
})); | ||
} | ||
|
||
t.is(logStub.warn.callCount, 0); | ||
t.is(logStub.error.callCount, 0); | ||
}); |
158 changes: 158 additions & 0 deletions
158
test/lib/ui5framework/Sapui5MavenSnapshotResolver.integration.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,158 @@ | ||
import test from "ava"; | ||
import sinonGlobal from "sinon"; | ||
import esmock from "esmock"; | ||
import path from "node:path"; | ||
import {fileURLToPath} from "node:url"; | ||
|
||
const __dirname = path.dirname(fileURLToPath(import.meta.url)); | ||
|
||
// Use path within project as mocking base directory to reduce chance of side effects | ||
// in case mocks/stubs do not work and real fs is used | ||
const fakeBaseDir = path.join(__dirname, "fake-tmp"); | ||
|
||
test.beforeEach(async (t) => { | ||
const sinon = t.context.sinon = sinonGlobal.createSandbox(); | ||
|
||
process.env.UI5_MAVEN_SNAPSHOT_ENDPOINT_URL = "_SNAPSHOT_URL_"; | ||
|
||
t.context.logStub = { | ||
info: sinon.stub(), | ||
verbose: sinon.stub(), | ||
silly: sinon.stub(), | ||
warn: sinon.stub(), | ||
error: sinon.stub(), | ||
isLevelEnabled: sinon.stub().returns(false), | ||
_getLogger: sinon.stub() | ||
}; | ||
const ui5Logger = { | ||
getLogger: sinon.stub().returns(t.context.logStub) | ||
}; | ||
|
||
t.context.makeFetchHappen = sinon.stub(); | ||
|
||
t.context.gracefulFs = { | ||
stat: sinon.stub().yieldsAsync(), | ||
readFile: sinon.stub().yieldsAsync(), | ||
writeFile: sinon.stub().yieldsAsync(), | ||
rename: sinon.stub().yieldsAsync(), | ||
rm: sinon.stub().yieldsAsync(), | ||
createWriteStream: sinon.stub() | ||
}; | ||
|
||
t.context.Registry = await esmock.p("../../../lib/ui5Framework/maven/Registry.js", { | ||
"@ui5/logger": ui5Logger, | ||
"graceful-fs": t.context.gracefulFs, | ||
"make-fetch-happen": t.context.makeFetchHappen, | ||
}); | ||
|
||
const AbstractInstaller = await esmock.p("../../../lib/ui5Framework/AbstractInstaller.js", { | ||
"@ui5/logger": ui5Logger, | ||
"../../../lib/utils/fs.js": { | ||
mkdirp: sinon.stub().resolves() | ||
}, | ||
"lockfile": { | ||
lock: sinon.stub().yieldsAsync(), | ||
unlock: sinon.stub().yieldsAsync() | ||
} | ||
}); | ||
|
||
t.context.Installer = await esmock.p("../../../lib/ui5Framework/maven/Installer.js", { | ||
"@ui5/logger": ui5Logger, | ||
"graceful-fs": t.context.gracefulFs, | ||
"../../../lib/utils/fs.js": { | ||
mkdirp: sinon.stub().resolves() | ||
}, | ||
"../../../lib/ui5Framework/maven/Registry.js": t.context.Registry, | ||
"../../../lib/ui5Framework/AbstractInstaller.js": AbstractInstaller | ||
}); | ||
|
||
t.context.AbstractResolver = await esmock.p("../../../lib/ui5Framework/AbstractResolver.js", { | ||
"@ui5/logger": ui5Logger, | ||
"node:os": { | ||
homedir: sinon.stub().returns(path.join(fakeBaseDir, "homedir")) | ||
}, | ||
}); | ||
|
||
t.context.Sapui5MavenSnapshotResolver = await esmock.p("../../../lib/ui5Framework/Sapui5MavenSnapshotResolver.js", { | ||
"@ui5/logger": ui5Logger, | ||
"node:os": { | ||
homedir: sinon.stub().returns(path.join(fakeBaseDir, "homedir")) | ||
}, | ||
"../../../lib/ui5Framework/AbstractResolver.js": t.context.AbstractResolver, | ||
"../../../lib/ui5Framework/maven/Installer.js": t.context.Installer | ||
}); | ||
}); | ||
|
||
test.afterEach.always((t) => { | ||
t.context.sinon.restore(); | ||
esmock.purge(t.context.Registry); | ||
esmock.purge(t.context.Installer); | ||
esmock.purge(t.context.AbstractResolver); | ||
esmock.purge(t.context.Sapui5MavenSnapshotResolver); | ||
delete process.env.UI5_MAVEN_SNAPSHOT_ENDPOINT_URL; | ||
}); | ||
|
||
test.serial("resolveVersion", async (t) => { | ||
const {Sapui5MavenSnapshotResolver, makeFetchHappen, logStub, sinon} = t.context; | ||
|
||
makeFetchHappen.withArgs("_SNAPSHOT_URL_/com/sap/ui5/dist/sapui5-sdk-dist/maven-metadata.xml") | ||
.resolves({ | ||
ok: true, | ||
buffer: sinon.stub().resolves(` | ||
<?xml version="1.0" encoding="UTF-8"?> | ||
<metadata> | ||
<versioning> | ||
<versions> | ||
<version>1.120.1</version> | ||
<version>2.0.0-rc.1</version> | ||
<version>1.120.1-SNAPSHOT</version> | ||
<version>1.123.4-SNAPSHOT</version> | ||
<version>2.0.0-SNAPSHOT</version> | ||
<version>2.0.1-SNAPSHOT</version> | ||
<version>2.1.2-SNAPSHOT</version> | ||
</versions> | ||
</versioning> | ||
</metadata> | ||
`) | ||
}); | ||
|
||
|
||
// 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"); | ||
|
||
// latest-snapshot | ||
t.is(await Sapui5MavenSnapshotResolver.resolveVersion("latest-snapshot"), "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"); | ||
|
||
// Error cases | ||
await t.throwsAsync(Sapui5MavenSnapshotResolver.resolveVersion(""), { | ||
message: `Framework version specifier "" is incorrect or not supported` | ||
}); | ||
await t.throwsAsync(Sapui5MavenSnapshotResolver.resolveVersion("tag-does-not-exist"), { | ||
message: `Framework version specifier "tag-does-not-exist" is incorrect or not supported` | ||
}); | ||
await t.throwsAsync(Sapui5MavenSnapshotResolver.resolveVersion("invalid-tag-%20"), { | ||
message: `Framework version specifier "invalid-tag-%20" is incorrect or not supported` | ||
}); | ||
|
||
await t.throwsAsync(Sapui5MavenSnapshotResolver.resolveVersion("1.999.9"), { | ||
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"), { | ||
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"), { | ||
message: `Could not resolve framework version 3-SNAPSHOT. ` + | ||
`Make sure the version is valid and available in the configured registry.` | ||
}); | ||
|
||
t.is(logStub.warn.callCount, 0); | ||
t.is(logStub.error.callCount, 0); | ||
}); |
Oops, something went wrong.