Skip to content

Fix installation of latest compatible version #1552

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Mar 1, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 26 additions & 19 deletions lib/npm-installation-manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,9 @@ export class NpmInstallationManager implements INpmInstallationManager {
public getLatestVersion(packageName: string): IFuture<string> {
return (() => {
let data = this.$npm.view(packageName, "dist-tags").wait();
// data is something like :
// { '1.0.1': { 'dist-tags': { latest: '1.0.1', next: '1.0.2-2016-02-25-181', next1: '1.0.2' } }
// There's only one key and it's always the @latest tag.
let latestVersion = _.first(_.keys(data));
this.$logger.trace("Using version %s. ", latestVersion);

Expand All @@ -74,22 +77,31 @@ export class NpmInstallationManager implements INpmInstallationManager {

public getLatestCompatibleVersion(packageName: string): IFuture<string> {
return (() => {
let cliVersionRange = `~${this.$staticConfig.version}`;
let latestVersion = this.getLatestVersion(packageName).wait();
let data = this.$npm.view(packageName, "versions").wait();
let versions: string[] = data[latestVersion].versions;

let versionData = this.getVersionData(this.$staticConfig.version);

let compatibleVersions = _(versions)
.map(ver => this.getVersionData(ver))
.filter(verData => versionData.major === verData.major && versionData.minor === verData.minor)
.sortBy(verData => verData.patch)
.value();

let result = _.last(compatibleVersions) || this.getVersionData(latestVersion);
if(semver.satisfies(latestVersion, cliVersionRange)) {
return latestVersion;
}

let latestCompatibleVersion = `${result.major}.${result.minor}.${result.patch}`;
return latestCompatibleVersion;
let data: any = this.$npm.view(packageName, "versions").wait();
/* data is something like:
{
"1.1.0":{
"versions":[
"1.0.0",
"1.0.1-2016-02-25-181",
"1.0.1",
"1.0.2-2016-02-25-182",
"1.0.2",
"1.1.0-2016-02-25-183",
"1.1.0",
"1.2.0-2016-02-25-184"
]
}
}
*/
let versions: string[] = data && data[latestVersion] && data[latestVersion].versions;
return semver.maxSatisfying(versions, cliVersionRange) || latestVersion;
}).future<string>()();
}

Expand Down Expand Up @@ -204,10 +216,5 @@ export class NpmInstallationManager implements INpmInstallationManager {
return this.$fs.exists(directory).wait() && this.$fs.enumerateFilesInDirectorySync(directory).length > 0;
}).future<boolean>()();
}

private getVersionData(version: string): IVersionData {
let [ major, minor, patch ] = version.split(".");
return { major, minor, patch };
}
}
$injector.register("npmInstallationManager", NpmInstallationManager);
137 changes: 91 additions & 46 deletions test/npm-installation-manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,65 +50,110 @@ function mockNpm(testInjector: IInjector, versions: string[], latestVersion: str
});
}

describe("Npm installation manager tests", () => {
it("returns correct latest compatible version when only one exists", () => {
let testInjector = createTestInjector();

let versions = ["1.4.0"];
let latestVersion = "1.4.0";

mockNpm(testInjector, versions, latestVersion);
interface ITestData {
/**
* All versions of the package, including the ones from another tags.
*/
versions: string[];

/**
* The version under latest tag.
*/
packageLatestVersion: string;

/**
* Version of nativescript-cli, based on which the version of the package that will be installed is detected.
*/
cliVersion: string;

/**
* Expected result
*/
expectedResult: string;
}

// Mock staticConfig.version
let staticConfig = testInjector.resolve("staticConfig");
staticConfig.version = "1.4.0";
describe("Npm installation manager tests", () => {
let testData: IDictionary<ITestData> = {
"when there's only one available version and it matches CLI's version": {
versions: ["1.4.0"],
packageLatestVersion: "1.4.0",
cliVersion: "1.4.0",
expectedResult: "1.4.0"
},

// Mock npmInstallationManager.getLatestVersion
let npmInstallationManager = testInjector.resolve("npmInstallationManager");
npmInstallationManager.getLatestVersion = (packageName: string) => Future.fromResult(latestVersion);
"when there's only one available version and it is higher than match CLI's version": {
versions: ["1.4.0"],
packageLatestVersion: "1.4.0",
cliVersion: "1.2.0",
expectedResult: "1.4.0"
},

let actualLatestCompatibleVersion = npmInstallationManager.getLatestCompatibleVersion("").wait();
let expectedLatestCompatibleVersion = "1.4.0";
assert.equal(actualLatestCompatibleVersion, expectedLatestCompatibleVersion);
});
"when there's only one available version and it is lower than CLI's version": {
versions: ["1.4.0"],
packageLatestVersion: "1.4.0",
cliVersion: "1.6.0",
expectedResult: "1.4.0"
},

it("returns correct latest compatible version", () => {
let testInjector = createTestInjector();
"when there are multiple package versions and the latest one matches ~<cli-version>":{
versions: ["1.2.0", "1.3.0", "1.3.1", "1.3.2", "1.3.3", "1.4.0"],
packageLatestVersion: "1.3.3",
cliVersion: "1.3.0",
expectedResult: "1.3.3"
},

let versions = ["1.2.0", "1.3.0", "1.3.1", "1.3.2", "1.3.3", "1.4.0"];
let latestVersion = "1.3.3";
"when there are multiple package versions and the latest one matches ~<cli-version> when there are newer matching versions but they are not under latest tag":{
versions: ["1.2.0", "1.3.0", "1.3.1", "1.3.2", "1.3.3", "1.4.0"],
packageLatestVersion: "1.3.2",
cliVersion: "1.3.0",
expectedResult: "1.3.2"
},

mockNpm(testInjector, versions, latestVersion);
"when there are multiple package versions and the latest one is lower than ~<cli-version>": {
versions: ["1.2.0", "1.3.0", "1.3.1", "1.3.2", "1.3.3", "1.4.0"],
packageLatestVersion: "1.4.0",
cliVersion: "1.5.0",
expectedResult: "1.4.0"
},

// Mock staticConfig.version
let staticConfig = testInjector.resolve("staticConfig");
staticConfig.version = "1.3.0";
"when there are multiple package versions and there's beta version matching CLI's semver": {
versions: ["1.2.0", "1.3.0", "1.3.1", "1.4.0", "1.5.0-2016-02-25-182"],
packageLatestVersion: "1.4.0",
cliVersion: "1.5.0",
expectedResult: "1.4.0"
},

// Mock npmInstallationManager.getLatestVersion
let npmInstallationManager = testInjector.resolve("npmInstallationManager");
npmInstallationManager.getLatestVersion = (packageName: string) => Future.fromResult(latestVersion);
"when there are multiple package versions and package's latest version is greater than CLI's version": {
versions: ["1.2.0", "1.3.0", "1.3.1", "1.4.0", "1.5.0-2016-02-25-182", "1.5.0", "1.6.0"],
packageLatestVersion: "1.6.0",
cliVersion: "1.5.0",
expectedResult: "1.5.0"
},

let actualLatestCompatibleVersion = npmInstallationManager.getLatestCompatibleVersion("").wait();
let expectedLatestCompatibleVersion = "1.3.3";
assert.equal(actualLatestCompatibleVersion, expectedLatestCompatibleVersion);
});
"when there are multiple versions latest one does not match CLI's semver and other versions are not matching either": {
versions: ["1.0.0", "1.0.1", "1.2.0", "1.3.1", "1.4.0", "1.5.0-2016-02-25-182", "1.5.0"],
packageLatestVersion: "1.0.0",
cliVersion: "1.1.0",
expectedResult: "1.0.0"
}
};

it("returns correct latest compatible version", () => {
let testInjector = createTestInjector();
_.each(testData, (currentTestData: ITestData, testName: string) => {
it(`returns correct latest compatible version, ${testName}`, () => {
let testInjector = createTestInjector();

let versions = ["1.2.0", "1.3.0", "1.3.1", "1.3.2", "1.3.3", "1.4.0"];
let latestVersion = _.last(versions);
mockNpm(testInjector, versions, latestVersion);
mockNpm(testInjector, currentTestData.versions, currentTestData.packageLatestVersion);

// Mock staticConfig.version
let staticConfig = testInjector.resolve("staticConfig");
staticConfig.version = "1.5.0";
// Mock staticConfig.version
let staticConfig = testInjector.resolve("staticConfig");
staticConfig.version = currentTestData.cliVersion;

// Mock npmInstallationManager.getLatestVersion
let npmInstallationManager = testInjector.resolve("npmInstallationManager");
npmInstallationManager.getLatestVersion = (packageName: string) => Future.fromResult(latestVersion);
// Mock npmInstallationManager.getLatestVersion
let npmInstallationManager = testInjector.resolve("npmInstallationManager");
npmInstallationManager.getLatestVersion = (packageName: string) => Future.fromResult(currentTestData.packageLatestVersion);

let actualLatestCompatibleVersion = npmInstallationManager.getLatestCompatibleVersion("").wait();
assert.equal(actualLatestCompatibleVersion, latestVersion);
let actualLatestCompatibleVersion = npmInstallationManager.getLatestCompatibleVersion("").wait();
assert.equal(actualLatestCompatibleVersion, currentTestData.expectedResult);
});
});
});