Skip to content

Commit 63cf457

Browse files
Fix parsing of CLI's output
The sysInfo spawns NativeScript CLI's commands to get CLI's version and the version of `nativescript-cloud`. However, in some cases, CLI's output contains warnings that Node.js version is not supported. These warnings contain version of Node.js and current code decides that this is the version of CLI (and nativescript-cloud). Fix the parsing by using different regular expression - in CLI's output the version is always on a new line, so rely on this. Add unit tests for this scenario and for getting the version of nativescript-cloud.
1 parent 8b2cfb6 commit 63cf457

File tree

2 files changed

+91
-38
lines changed

2 files changed

+91
-38
lines changed

lib/sys-info.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ export class SysInfo implements NativeScriptDoctor.ISysInfo {
1717
private static JAVA_COMPILER_VERSION_REGEXP = /^javac (.*)/im;
1818
private static XCODE_VERSION_REGEXP = /Xcode (.*)/;
1919
private static VERSION_REGEXP = /(\d{1,})\.(\d{1,})\.*([\w-]{0,})/m;
20+
private static CLI_OUTPUT_VERSION_REGEXP = /^(?:\d+\.){2}\d+.*?$/m;
2021
private static GIT_VERSION_REGEXP = /^git version (.*)/;
2122
private static GRADLE_VERSION_REGEXP = /Gradle (.*)/i;
2223

@@ -298,14 +299,14 @@ export class SysInfo implements NativeScriptDoctor.ISysInfo {
298299
public getNativeScriptCliVersion(): Promise<string> {
299300
return this.getValueForProperty(() => this.nativeScriptCliVersionCache, async (): Promise<string> => {
300301
const output = await this.execCommand("tns --version");
301-
return output ? this.getVersionFromString(output.trim()) : output;
302+
return output ? this.getVersionFromCLIOutput(output.trim()) : output;
302303
});
303304
}
304305

305306
public getNativeScriptCloudVersion(): Promise<string> {
306307
return this.getValueForProperty(() => this.nativeScriptCloudVersionCache, async (): Promise<string> => {
307308
const output = await this.execCommand("tns cloud lib version");
308-
return output ? this.getVersionFromString(output.trim()) : output;
309+
return output ? this.getVersionFromCLIOutput(output.trim()) : output;
309310
});
310311
}
311312

@@ -442,6 +443,11 @@ export class SysInfo implements NativeScriptDoctor.ISysInfo {
442443
return null;
443444
}
444445

446+
private getVersionFromCLIOutput(commandOutput: string): string {
447+
const matches = commandOutput.match(SysInfo.CLI_OUTPUT_VERSION_REGEXP);
448+
return matches && matches[0];
449+
}
450+
445451
private async winVer(): Promise<string> {
446452
let productName: string;
447453
let currentVersion: string;

test/sys-info.ts

Lines changed: 83 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import * as assert from "assert";
22
import * as path from "path";
3+
import { EOL } from "os";
34
import { SysInfo } from "../lib/sys-info";
45
import { Helpers } from "../lib/helpers";
56
import { ChildProcess } from "../lib/wrappers/child-process";
@@ -14,7 +15,13 @@ interface IChildProcessResultDescription {
1415
shouldThrowError?: boolean;
1516
}
1617

18+
interface ICLIOutputVersionTestCase {
19+
testedProperty: string;
20+
method: (sysInfo: SysInfo) => Promise<string>;
21+
}
22+
1723
interface IChildProcessResults {
24+
[property: string]: IChildProcessResultDescription;
1825
uname: IChildProcessResultDescription;
1926
npmV: IChildProcessResultDescription;
2027
nodeV: IChildProcessResultDescription;
@@ -30,6 +37,7 @@ interface IChildProcessResults {
3037
podVersion: IChildProcessResultDescription;
3138
pod: IChildProcessResultDescription;
3239
nativeScriptCliVersion: IChildProcessResultDescription;
40+
nativeScriptCloudVersion: IChildProcessResultDescription;
3341
git: IChildProcessResultDescription;
3442
}
3543

@@ -86,6 +94,7 @@ function createChildProcessResults(childProcessResult: IChildProcessResults): ID
8694
'"C:\\Program Files/Git/cmd/git.exe" --version': childProcessResult.gitVersion, // When running Windows test on the Non-Windows platform
8795
"gradle -v": childProcessResult.gradleVersion,
8896
"tns --version": childProcessResult.nativeScriptCliVersion,
97+
"tns cloud lib version": childProcessResult.nativeScriptCloudVersion,
8998
"emulator": { shouldThrowError: false },
9099
"which git": childProcessResult.git
91100
};
@@ -150,6 +159,7 @@ function setStdErr(value: string): { stderr: string } {
150159

151160
describe("SysInfo unit tests", () => {
152161
let sysInfo: SysInfo;
162+
const dotNetVersion = "4.5.1";
153163

154164
beforeEach(() => {
155165
// We need to mock this because on Mac the tests in which the platform is mocked to Windows in the process there will be no CommonProgramFiles.
@@ -230,6 +240,7 @@ describe("SysInfo unit tests", () => {
230240
podVersion: { result: setStdOut("0.38.2") },
231241
pod: { result: setStdOut("success") },
232242
nativeScriptCliVersion: { result: setStdOut("2.5.0") },
243+
nativeScriptCloudVersion: { result: setStdOut("0.1.0") },
233244
git: { result: setStdOut("git") }
234245
};
235246

@@ -264,25 +275,25 @@ describe("SysInfo unit tests", () => {
264275
it("on Windows", async () => {
265276
const originalProgramFiles = process.env[PROGRAM_FILES];
266277
process.env[PROGRAM_FILES] = PROGRAM_FILES_ENV_PATH;
267-
sysInfo = mockSysInfo(childProcessResult, { isWindows: true, isDarwin: false, dotNetVersion: "4.5.1" });
268-
let result = await sysInfo.getSysInfo();
278+
sysInfo = mockSysInfo(childProcessResult, { isWindows: true, isDarwin: false, dotNetVersion });
279+
const result = await sysInfo.getSysInfo();
269280
process.env[PROGRAM_FILES] = originalProgramFiles;
270281
assertCommonValues(result);
271282
assert.deepEqual(result.xcodeVer, null);
272283
assert.deepEqual(result.cocoaPodsVer, null);
273284
});
274285

275286
it("on Mac", async () => {
276-
sysInfo = mockSysInfo(childProcessResult, { isWindows: false, isDarwin: true, dotNetVersion: "4.5.1" });
277-
let result = await sysInfo.getSysInfo();
287+
sysInfo = mockSysInfo(childProcessResult, { isWindows: false, isDarwin: true, dotNetVersion });
288+
const result = await sysInfo.getSysInfo();
278289
assertCommonValues(result);
279290
assert.deepEqual(result.xcodeVer, "6.4.0");
280291
assert.deepEqual(result.cocoaPodsVer, childProcessResult.podVersion.result.stdout);
281292
});
282293

283294
it("on Linux", async () => {
284-
sysInfo = mockSysInfo(childProcessResult, { isWindows: false, isDarwin: false, dotNetVersion: "4.5.1" });
285-
let result = await sysInfo.getSysInfo();
295+
sysInfo = mockSysInfo(childProcessResult, { isWindows: false, isDarwin: false, dotNetVersion });
296+
const result = await sysInfo.getSysInfo();
286297
assertCommonValues(result);
287298
assert.deepEqual(result.xcodeVer, null);
288299
assert.deepEqual(result.cocoaPodsVer, null);
@@ -293,55 +304,90 @@ describe("SysInfo unit tests", () => {
293304
it("is null when cocoapods are not installed", async () => {
294305
// simulate error when pod --version command is executed
295306
childProcessResult.podVersion = { shouldThrowError: true };
296-
sysInfo = mockSysInfo(childProcessResult, { isWindows: false, isDarwin: true, dotNetVersion: "4.5.1" });
297-
let result = await sysInfo.getSysInfo();
307+
sysInfo = mockSysInfo(childProcessResult, { isWindows: false, isDarwin: true, dotNetVersion });
308+
const result = await sysInfo.getSysInfo();
298309
assert.deepEqual(result.cocoaPodsVer, null);
299310
});
300311

301312
it("is null when OS is not Mac", async () => {
302313
const originalProgramFiles = process.env[PROGRAM_FILES];
303314
process.env[PROGRAM_FILES] = PROGRAM_FILES_ENV_PATH;
304-
sysInfo = mockSysInfo(childProcessResult, { isWindows: true, isDarwin: false, dotNetVersion: "4.5.1" });
305-
let result = await sysInfo.getSysInfo();
315+
sysInfo = mockSysInfo(childProcessResult, { isWindows: true, isDarwin: false, dotNetVersion });
316+
const result = await sysInfo.getSysInfo();
306317
process.env[PROGRAM_FILES] = originalProgramFiles;
307318
assert.deepEqual(result.cocoaPodsVer, null);
308319
});
309320

310321
it("is correct when cocoapods output has warning after version output", async () => {
311322
childProcessResult.podVersion = { result: setStdOut("0.38.2\nWARNING:\n") };
312-
sysInfo = mockSysInfo(childProcessResult, { isWindows: false, isDarwin: true, dotNetVersion: "4.5.1" });
313-
let result = await sysInfo.getSysInfo();
323+
sysInfo = mockSysInfo(childProcessResult, { isWindows: false, isDarwin: true, dotNetVersion });
324+
const result = await sysInfo.getSysInfo();
314325
assert.deepEqual(result.cocoaPodsVer, "0.38.2");
315326
});
316327

317328
it("is correct when cocoapods output has warnings before version output", async () => {
318329
childProcessResult.podVersion = { result: setStdOut("WARNING\nWARNING2\n0.38.2") };
319-
sysInfo = mockSysInfo(childProcessResult, { isWindows: false, isDarwin: true, dotNetVersion: "4.5.1" });
320-
let result = await sysInfo.getSysInfo();
330+
sysInfo = mockSysInfo(childProcessResult, { isWindows: false, isDarwin: true, dotNetVersion });
331+
const result = await sysInfo.getSysInfo();
321332
assert.deepEqual(result.cocoaPodsVer, "0.38.2");
322333
});
323334
});
324335

325-
describe("nativeScriptCliVersion", () => {
326-
it("is null when tns is not installed", async () => {
327-
childProcessResult.nativeScriptCliVersion = { shouldThrowError: true };
328-
sysInfo = mockSysInfo(childProcessResult, { isWindows: false, isDarwin: true, dotNetVersion: "4.5.1" });
329-
let result = await sysInfo.getSysInfo();
330-
assert.deepEqual(result.nativeScriptCliVersion, null);
331-
});
336+
const testData: ICLIOutputVersionTestCase[] = [
337+
{
338+
testedProperty: "nativeScriptCliVersion",
339+
method: (currentSysInfo: SysInfo) => currentSysInfo.getNativeScriptCliVersion()
340+
},
341+
{
342+
testedProperty: "nativeScriptCloudVersion",
343+
method: (currentSysInfo: SysInfo) => currentSysInfo.getNativeScriptCloudVersion()
344+
}];
345+
346+
testData.forEach((testCase) => {
347+
describe(testCase.testedProperty, () => {
348+
it("is null when tns is not installed", async () => {
349+
childProcessResult[testCase.testedProperty] = { shouldThrowError: true };
350+
sysInfo = mockSysInfo(childProcessResult, { isWindows: false, isDarwin: true, dotNetVersion });
351+
const result = await testCase.method(sysInfo);
352+
assert.deepEqual(result, null);
353+
});
332354

333-
it("is correct when the version is the only row in `tns --version` output", async () => {
334-
childProcessResult.nativeScriptCliVersion = { result: setStdOut("3.0.0") };
335-
sysInfo = mockSysInfo(childProcessResult, { isWindows: false, isDarwin: true, dotNetVersion: "4.5.1" });
336-
let result = await sysInfo.getSysInfo();
337-
assert.deepEqual(result.nativeScriptCliVersion, "3.0.0");
338-
});
355+
it("is correct when the version is the only row in command output", async () => {
356+
childProcessResult[testCase.testedProperty] = { result: setStdOut("3.0.0") };
357+
sysInfo = mockSysInfo(childProcessResult, { isWindows: false, isDarwin: true, dotNetVersion });
358+
const result = await testCase.method(sysInfo);
359+
assert.deepEqual(result, "3.0.0");
360+
});
339361

340-
it("is correct when there are warnings in the `tns --version` output", async () => {
341-
childProcessResult.nativeScriptCliVersion = { result: setStdOut("Some warning due to invalid extensions\\n3.0.0") };
342-
sysInfo = mockSysInfo(childProcessResult, { isWindows: false, isDarwin: true, dotNetVersion: "4.5.1" });
343-
let result = await sysInfo.getSysInfo();
344-
assert.deepEqual(result.nativeScriptCliVersion, "3.0.0");
362+
it("is correct when there are warnings in the command's output", async () => {
363+
childProcessResult[testCase.testedProperty] = { result: setStdOut(`Some warning due to invalid extensions${EOL}3.0.0`) };
364+
sysInfo = mockSysInfo(childProcessResult, { isWindows: false, isDarwin: true, dotNetVersion });
365+
const result = await testCase.method(sysInfo);
366+
assert.deepEqual(result, "3.0.0");
367+
});
368+
369+
it("is correct when there are warnings with version in them in the command's output", async () => {
370+
const cliOutput = `
371+
Support for Node.js 7.6.0 is not verified. This CLI might not install or run properly.
372+
373+
3.0.0`;
374+
childProcessResult[testCase.testedProperty] = { result: setStdOut(cliOutput) };
375+
sysInfo = mockSysInfo(childProcessResult, { isWindows: false, isDarwin: true, dotNetVersion });
376+
const result = await testCase.method(sysInfo);
377+
assert.deepEqual(result, "3.0.0");
378+
});
379+
380+
it("is correct when there are warnings in the command's output and searched version is a prerelease", async () => {
381+
const expectedCliVersion = "3.2.0-2017-07-21-9480";
382+
const cliOutput = `
383+
Support for Node.js 7.6.0 is not verified. This CLI might not install or run properly.
384+
385+
${expectedCliVersion}`;
386+
childProcessResult[testCase.testedProperty] = { result: setStdOut(cliOutput) };
387+
sysInfo = mockSysInfo(childProcessResult, { isWindows: false, isDarwin: true, dotNetVersion });
388+
const result = await testCase.method(sysInfo);
389+
assert.deepEqual(result, expectedCliVersion);
390+
});
345391
});
346392
});
347393

@@ -363,14 +409,15 @@ describe("SysInfo unit tests", () => {
363409
podVersion: { shouldThrowError: true },
364410
pod: { shouldThrowError: true },
365411
nativeScriptCliVersion: { shouldThrowError: true },
412+
nativeScriptCloudVersion: { shouldThrowError: true },
366413
git: { shouldThrowError: false }
367414
};
368415
androidToolsInfo.validateAndroidHomeEnvVariable = (): any[] => [1];
369416
});
370417

371418
describe("when all of calls throw", () => {
372419
let assertAllValuesAreNull = async () => {
373-
let result = await sysInfo.getSysInfo();
420+
const result = await sysInfo.getSysInfo();
374421
assert.deepEqual(result.npmVer, null);
375422
assert.deepEqual(result.javaVer, null);
376423
assert.deepEqual(result.javacVersion, null);
@@ -387,18 +434,18 @@ describe("SysInfo unit tests", () => {
387434
it("on Windows", async () => {
388435
const originalProgramFiles = process.env[PROGRAM_FILES];
389436
process.env[PROGRAM_FILES] = PROGRAM_FILES_ENV_PATH;
390-
sysInfo = mockSysInfo(childProcessResult, { isWindows: true, isDarwin: false, dotNetVersion: "4.5.1" });
437+
sysInfo = mockSysInfo(childProcessResult, { isWindows: true, isDarwin: false, dotNetVersion });
391438
process.env[PROGRAM_FILES] = originalProgramFiles;
392439
await assertAllValuesAreNull();
393440
});
394441

395442
it("on Mac", async () => {
396-
sysInfo = mockSysInfo(childProcessResult, { isWindows: false, isDarwin: true, dotNetVersion: "4.5.1" });
443+
sysInfo = mockSysInfo(childProcessResult, { isWindows: false, isDarwin: true, dotNetVersion });
397444
await assertAllValuesAreNull();
398445
});
399446

400447
it("on Linux", async () => {
401-
sysInfo = mockSysInfo(childProcessResult, { isWindows: false, isDarwin: false, dotNetVersion: "4.5.1" });
448+
sysInfo = mockSysInfo(childProcessResult, { isWindows: false, isDarwin: false, dotNetVersion });
402449
await assertAllValuesAreNull();
403450
});
404451
});

0 commit comments

Comments
 (0)