Skip to content

Commit c15c700

Browse files
Merge pull request #1552 from NativeScript/vladimirov/fix-versions-detection
Fix installation of latest compatible version
2 parents ebbabd8 + 39854a1 commit c15c700

File tree

2 files changed

+117
-65
lines changed

2 files changed

+117
-65
lines changed

lib/npm-installation-manager.ts

+26-19
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,9 @@ export class NpmInstallationManager implements INpmInstallationManager {
6565
public getLatestVersion(packageName: string): IFuture<string> {
6666
return (() => {
6767
let data = this.$npm.view(packageName, "dist-tags").wait();
68+
// data is something like :
69+
// { '1.0.1': { 'dist-tags': { latest: '1.0.1', next: '1.0.2-2016-02-25-181', next1: '1.0.2' } }
70+
// There's only one key and it's always the @latest tag.
6871
let latestVersion = _.first(_.keys(data));
6972
this.$logger.trace("Using version %s. ", latestVersion);
7073

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

7578
public getLatestCompatibleVersion(packageName: string): IFuture<string> {
7679
return (() => {
80+
let cliVersionRange = `~${this.$staticConfig.version}`;
7781
let latestVersion = this.getLatestVersion(packageName).wait();
78-
let data = this.$npm.view(packageName, "versions").wait();
79-
let versions: string[] = data[latestVersion].versions;
80-
81-
let versionData = this.getVersionData(this.$staticConfig.version);
82-
83-
let compatibleVersions = _(versions)
84-
.map(ver => this.getVersionData(ver))
85-
.filter(verData => versionData.major === verData.major && versionData.minor === verData.minor)
86-
.sortBy(verData => verData.patch)
87-
.value();
88-
89-
let result = _.last(compatibleVersions) || this.getVersionData(latestVersion);
82+
if(semver.satisfies(latestVersion, cliVersionRange)) {
83+
return latestVersion;
84+
}
9085

91-
let latestCompatibleVersion = `${result.major}.${result.minor}.${result.patch}`;
92-
return latestCompatibleVersion;
86+
let data: any = this.$npm.view(packageName, "versions").wait();
87+
/* data is something like:
88+
{
89+
"1.1.0":{
90+
"versions":[
91+
"1.0.0",
92+
"1.0.1-2016-02-25-181",
93+
"1.0.1",
94+
"1.0.2-2016-02-25-182",
95+
"1.0.2",
96+
"1.1.0-2016-02-25-183",
97+
"1.1.0",
98+
"1.2.0-2016-02-25-184"
99+
]
100+
}
101+
}
102+
*/
103+
let versions: string[] = data && data[latestVersion] && data[latestVersion].versions;
104+
return semver.maxSatisfying(versions, cliVersionRange) || latestVersion;
93105
}).future<string>()();
94106
}
95107

@@ -204,10 +216,5 @@ export class NpmInstallationManager implements INpmInstallationManager {
204216
return this.$fs.exists(directory).wait() && this.$fs.enumerateFilesInDirectorySync(directory).length > 0;
205217
}).future<boolean>()();
206218
}
207-
208-
private getVersionData(version: string): IVersionData {
209-
let [ major, minor, patch ] = version.split(".");
210-
return { major, minor, patch };
211-
}
212219
}
213220
$injector.register("npmInstallationManager", NpmInstallationManager);

test/npm-installation-manager.ts

+91-46
Original file line numberDiff line numberDiff line change
@@ -50,65 +50,110 @@ function mockNpm(testInjector: IInjector, versions: string[], latestVersion: str
5050
});
5151
}
5252

53-
describe("Npm installation manager tests", () => {
54-
it("returns correct latest compatible version when only one exists", () => {
55-
let testInjector = createTestInjector();
56-
57-
let versions = ["1.4.0"];
58-
let latestVersion = "1.4.0";
59-
60-
mockNpm(testInjector, versions, latestVersion);
53+
interface ITestData {
54+
/**
55+
* All versions of the package, including the ones from another tags.
56+
*/
57+
versions: string[];
58+
59+
/**
60+
* The version under latest tag.
61+
*/
62+
packageLatestVersion: string;
63+
64+
/**
65+
* Version of nativescript-cli, based on which the version of the package that will be installed is detected.
66+
*/
67+
cliVersion: string;
68+
69+
/**
70+
* Expected result
71+
*/
72+
expectedResult: string;
73+
}
6174

62-
// Mock staticConfig.version
63-
let staticConfig = testInjector.resolve("staticConfig");
64-
staticConfig.version = "1.4.0";
75+
describe("Npm installation manager tests", () => {
76+
let testData: IDictionary<ITestData> = {
77+
"when there's only one available version and it matches CLI's version": {
78+
versions: ["1.4.0"],
79+
packageLatestVersion: "1.4.0",
80+
cliVersion: "1.4.0",
81+
expectedResult: "1.4.0"
82+
},
6583

66-
// Mock npmInstallationManager.getLatestVersion
67-
let npmInstallationManager = testInjector.resolve("npmInstallationManager");
68-
npmInstallationManager.getLatestVersion = (packageName: string) => Future.fromResult(latestVersion);
84+
"when there's only one available version and it is higher than match CLI's version": {
85+
versions: ["1.4.0"],
86+
packageLatestVersion: "1.4.0",
87+
cliVersion: "1.2.0",
88+
expectedResult: "1.4.0"
89+
},
6990

70-
let actualLatestCompatibleVersion = npmInstallationManager.getLatestCompatibleVersion("").wait();
71-
let expectedLatestCompatibleVersion = "1.4.0";
72-
assert.equal(actualLatestCompatibleVersion, expectedLatestCompatibleVersion);
73-
});
91+
"when there's only one available version and it is lower than CLI's version": {
92+
versions: ["1.4.0"],
93+
packageLatestVersion: "1.4.0",
94+
cliVersion: "1.6.0",
95+
expectedResult: "1.4.0"
96+
},
7497

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

78-
let versions = ["1.2.0", "1.3.0", "1.3.1", "1.3.2", "1.3.3", "1.4.0"];
79-
let latestVersion = "1.3.3";
105+
"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":{
106+
versions: ["1.2.0", "1.3.0", "1.3.1", "1.3.2", "1.3.3", "1.4.0"],
107+
packageLatestVersion: "1.3.2",
108+
cliVersion: "1.3.0",
109+
expectedResult: "1.3.2"
110+
},
80111

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

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

87-
// Mock npmInstallationManager.getLatestVersion
88-
let npmInstallationManager = testInjector.resolve("npmInstallationManager");
89-
npmInstallationManager.getLatestVersion = (packageName: string) => Future.fromResult(latestVersion);
126+
"when there are multiple package versions and package's latest version is greater than CLI's version": {
127+
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"],
128+
packageLatestVersion: "1.6.0",
129+
cliVersion: "1.5.0",
130+
expectedResult: "1.5.0"
131+
},
90132

91-
let actualLatestCompatibleVersion = npmInstallationManager.getLatestCompatibleVersion("").wait();
92-
let expectedLatestCompatibleVersion = "1.3.3";
93-
assert.equal(actualLatestCompatibleVersion, expectedLatestCompatibleVersion);
94-
});
133+
"when there are multiple versions latest one does not match CLI's semver and other versions are not matching either": {
134+
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"],
135+
packageLatestVersion: "1.0.0",
136+
cliVersion: "1.1.0",
137+
expectedResult: "1.0.0"
138+
}
139+
};
95140

96-
it("returns correct latest compatible version", () => {
97-
let testInjector = createTestInjector();
141+
_.each(testData, (currentTestData: ITestData, testName: string) => {
142+
it(`returns correct latest compatible version, ${testName}`, () => {
143+
let testInjector = createTestInjector();
98144

99-
let versions = ["1.2.0", "1.3.0", "1.3.1", "1.3.2", "1.3.3", "1.4.0"];
100-
let latestVersion = _.last(versions);
101-
mockNpm(testInjector, versions, latestVersion);
145+
mockNpm(testInjector, currentTestData.versions, currentTestData.packageLatestVersion);
102146

103-
// Mock staticConfig.version
104-
let staticConfig = testInjector.resolve("staticConfig");
105-
staticConfig.version = "1.5.0";
147+
// Mock staticConfig.version
148+
let staticConfig = testInjector.resolve("staticConfig");
149+
staticConfig.version = currentTestData.cliVersion;
106150

107-
// Mock npmInstallationManager.getLatestVersion
108-
let npmInstallationManager = testInjector.resolve("npmInstallationManager");
109-
npmInstallationManager.getLatestVersion = (packageName: string) => Future.fromResult(latestVersion);
151+
// Mock npmInstallationManager.getLatestVersion
152+
let npmInstallationManager = testInjector.resolve("npmInstallationManager");
153+
npmInstallationManager.getLatestVersion = (packageName: string) => Future.fromResult(currentTestData.packageLatestVersion);
110154

111-
let actualLatestCompatibleVersion = npmInstallationManager.getLatestCompatibleVersion("").wait();
112-
assert.equal(actualLatestCompatibleVersion, latestVersion);
155+
let actualLatestCompatibleVersion = npmInstallationManager.getLatestCompatibleVersion("").wait();
156+
assert.equal(actualLatestCompatibleVersion, currentTestData.expectedResult);
157+
});
113158
});
114159
});

0 commit comments

Comments
 (0)