@hkvstore/taco-cli
Version:
taco-cli is a command-line interface for rapid Apache Cordova development (forked from Microsoft taco-cli)
262 lines (260 loc) • 12.9 kB
JavaScript
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for details.
/// <reference path="../../typings/mocha.d.ts" />
/// <reference path="../../typings/node.d.ts" />
/// <reference path="../../typings/should.d.ts" />
/// <reference path="../../typings/del.d.ts" />
/// <reference path="../../typings/node.d.ts"/>
/// <reference path="../../typings/tacoTestsUtils.d.ts"/>
;
var fs = require("fs");
var os = require("os");
var path = require("path");
var Q = require("q");
var should = require("should");
var Settings = require("../cli/utils/settings");
var TacoUtility = require("taco-utils");
var CheckForNewerVersion = require("../cli/utils/checkForNewerVersion");
var tacoTestsUtils = require("taco-tests-utils");
var http = require("http");
var ServerMock = require("./utils/serverMock");
var utils = TacoUtility.UtilHelper;
var MemoryStream = tacoTestsUtils.MemoryStream;
var MessageExpectation;
(function (MessageExpectation) {
MessageExpectation[MessageExpectation["WillBeShown"] = 0] = "WillBeShown";
MessageExpectation[MessageExpectation["WontBeShown"] = 1] = "WontBeShown";
})(MessageExpectation || (MessageExpectation = {}));
describe("Check for newer version", function () {
var tacoHome = path.join(os.tmpdir(), "taco-cli", "check-for-new-version");
// Use a dummy home location so we don't trash any real configurations
process.env["TACO_HOME"] = tacoHome;
// because of function overloading assigning "(buffer: string, cb?: Function) => boolean" as the type for
// stdoutWrite just doesn't work
var stdoutWrite = process.stdout.write; // We save the original implementation, so we can restore it later
var memoryStdout;
var expectedRequestAndResponse;
var tacoCliLatestInformation;
var fakeServer = "http://localhost:8080";
var repositoryPath = "/taco-cli/latest";
var repositoryInFakeServerPath = fakeServer + repositoryPath;
var packageFilePath = path.join(utils.tacoHome, "package.json");
before(function () {
// Set up mocked out resources
process.env["TACO_UNIT_TEST"] = true;
process.listeners("beforeExit").should.be.empty; // We can't run the tests if we have unexpected beforeExit listeners
});
beforeEach(function () {
memoryStdout = new MemoryStream; // Each individual test gets a new and empty console
process.stdout.write = memoryStdout.writeAsFunction(); // We'll be printing into an "in-memory" console, so we can test the output
// These contents were copied from http://registry.npmjs.org/remotebuild/latest and then renamed to what should be a taco-cli response
tacoCliLatestInformation = {
name: "taco-cli",
description: "Front-end server that serves modules that implement remote build functionality, such as taco-remote.",
version: "1.0.0",
author: {
name: "Microsoft Corporation",
email: "vscordovatools-admin@microsoft.com"
},
homepage: "http://msdn.microsoft.com/en-us/vstudio/dn722381",
main: "./lib/server.js",
bin: {
remotebuild: "./bin/remotebuild"
},
keywords: [
"cordova",
"osx ",
"remote build"
],
preferGlobal: true,
dependencies: {
express: "4.12.2",
morgan: "1.5.1",
errorhandler: "1.3.4",
nconf: "0.6.9",
q: "1.0.1",
rimraf: "2.2.6",
"taco-utils": "1.0.0",
"taco-remote": "1.0.0"
},
optionalDependencies: {
"taco-remote": "1.0.0"
},
devDependencies: {
typescript: "1.3.0",
mocha: "2.0.1",
mkdirp: "0.3.5",
should: "4.3.0",
request: "2.36.0"
},
scripts: {
test: "mocha"
},
directories: {
lib: "lib",
doc: ".",
test: "test",
example: "examples"
},
license: "MIT",
_id: "remotebuild@1.0.0",
_shasum: "9b33d502b22f8ba8977e11c4a7db93bde5037e88",
_resolved: "file:remotebuild.tgz",
_from: "remotebuild.tgz",
_npmVersion: "2.7.4",
_nodeVersion: "0.12.2",
_npmUser: {
name: "multidevicehybridapp",
email: "vscordovatools-admin@microsoft.com"
},
dist: {
shasum: "9b33d502b22f8ba8977e11c4a7db93bde5037e88",
tarball: "http://registry.npmjs.org/remotebuild/-/remotebuild-1.0.0.tgz"
},
maintainers: [
{
name: "multidevicehybridapp",
email: "vscordovatools-admin@microsoft.com"
}
]
};
// Create the package.json
utils.createDirectoryIfNecessary(tacoHome);
fs.writeFileSync(packageFilePath, JSON.stringify(tacoCliLatestInformation));
// Default request answered by the fake NPM server
expectedRequestAndResponse = {
expectedUrl: repositoryPath,
head: { "Content-Type": "application/json" },
statusCode: 200,
response: JSON.stringify(tacoCliLatestInformation)
};
/* By default there is an update available. We do this after writing the package.json file
Because we want 1.0.0 to be the current version, and after the expectedRequestAndResponse because
it's required */
setLatestReleasedVersion("1.0.1");
});
afterEach(function () {
process.stdout.write = stdoutWrite;
process.removeAllListeners("beforeExit");
Settings.forgetSettings();
try {
// Not all tests create the file, so we ignore the exception
fs.unlinkSync(Settings.settingsFile);
}
catch (exception) {
utils.emptyMethod();
}
});
function simulateBeforeExit() {
var listeners = process.listeners("beforeExit");
listeners.length.should.eql(1, "There should be only a single listener for the beforeExit event");
listeners[0].call(process); // The listener expects "this" to be process
process.listeners("beforeExit").length.should.eql(0, "The beforeExit listener should clean itself up");
}
function launchFakeNPMServer(done) {
var serverIsListening = Q.defer();
// Port for the web server
var PORT = 8080;
// Create the server
var server = http.createServer(ServerMock.generateServerFunction(done, [expectedRequestAndResponse]));
server.listen(PORT);
// If there is any error, we reject the promise
server.on("error", function (error) {
serverIsListening.reject(error);
});
// Make the server start listening
server.listen(PORT, function () {
serverIsListening.resolve(server);
});
return serverIsListening.promise;
}
function testCheckForNewerVersion(messageExpectation, done) {
var timeBeforeTest = Date.now();
var fakeNPMServer;
return launchFakeNPMServer(done)
.then(function (server) {
fakeNPMServer = server;
should(fakeNPMServer).have.property("close");
})
.then(function () { return new CheckForNewerVersion(repositoryInFakeServerPath, packageFilePath)
.showOnExit()
.fail(function (error) { return TacoUtility.UtilHelper.emptyMethod(error); }); })
.then(function () {
// CheckForNewerVersion doesn't print anything synchronically. It prints it on the beforeExit event
var actual = memoryStdout.contentsAsText();
should(actual).be.empty;
if (messageExpectation === MessageExpectation.WillBeShown) {
simulateBeforeExit();
actual = memoryStdout.contentsAsText();
actual.should.be.equal("NewerTacoCLIVersionAvailable\n", "The output of the console should match what we expected");
return Settings.loadSettings().then(function (settings) {
var lastCheck = new Date(settings.lastCheckForNewerVersionTimestamp).getTime();
lastCheck.should.be.greaterThan(timeBeforeTest, "The last check for newer version timestamp: " + lastCheck + " should be updated after each attempt to check for a newer version and thus be greater than " + timeBeforeTest);
});
}
else {
process.listeners("beforeExit").should.be.empty; // We shouldn't have any listeners if no message is expected
}
})
.finally(function () {
fakeNPMServer.close();
});
}
function setCheckedTimestampToHoursAgo(howManyHoursAgo) {
var someHoursAgo = new Date();
someHoursAgo.setHours(someHoursAgo.getHours() - howManyHoursAgo);
var lastCheckForNewerVersionTimestamp = someHoursAgo.getTime();
return Settings.updateSettings(function (settings) { return settings.lastCheckForNewerVersionTimestamp = lastCheckForNewerVersionTimestamp; }).then(function () { return lastCheckForNewerVersionTimestamp; });
}
function setLatestReleasedVersion(version) {
tacoCliLatestInformation.version = version;
expectedRequestAndResponse.response = JSON.stringify(tacoCliLatestInformation);
}
it("shows message when there is an update available and it's the first time we've ever checked", function (done) {
testCheckForNewerVersion(MessageExpectation.WillBeShown, done).done(function () { return done(); }, done);
});
it("doesn't run the check if we've checked 3 hours ago", function (done) {
var lastCheckForNewerVersionTimestamp;
setCheckedTimestampToHoursAgo(3)
.then(function (storedNumber) { return lastCheckForNewerVersionTimestamp = storedNumber; })
.then(function () { return new CheckForNewerVersion(repositoryInFakeServerPath, packageFilePath).showOnExit().fail(utils.emptyMethod); })
.done(function () {
var listeners = process.listeners("beforeExit");
listeners.length.should.eql(0, "There should be no listeners for the beforeExit event");
var actual = memoryStdout.contentsAsText();
should(actual).be.empty;
return Settings.loadSettings().then(function (settings) {
settings.lastCheckForNewerVersionTimestamp.should.be.equal(lastCheckForNewerVersionTimestamp, "The last checked time shouldn't had changed expected: " + lastCheckForNewerVersionTimestamp + " actual: " + settings.lastCheckForNewerVersionTimestamp.should);
done();
});
});
});
it("does run the check if we've checked 5 hours ago", function (done) {
setCheckedTimestampToHoursAgo(5)
.then(function () { return testCheckForNewerVersion(MessageExpectation.WillBeShown, done); })
.done(function () { return done(); }, done);
});
it("doesn't show a message when there is not an update available", function (done) {
setLatestReleasedVersion("1.0.0");
testCheckForNewerVersion(MessageExpectation.WontBeShown, done).done(function () { return done(); }, done);
});
it("doesn't show any errors if the http request times out", function (done) {
expectedRequestAndResponse.responseDelay = 10 * 1000; // 10 seconds
testCheckForNewerVersion(MessageExpectation.WontBeShown, done).done(function () { return done(); }, done);
});
it("doesn't show any errors if the http request fails with 4xx", function (done) {
expectedRequestAndResponse.statusCode = 401;
testCheckForNewerVersion(MessageExpectation.WontBeShown, done).done(function () { return done(); }, done);
});
it("doesn't show any errors if the http request fails", function (done) {
expectedRequestAndResponse.statusCode = 500;
expectedRequestAndResponse.response = "There was a fake internal error"; // The body.version property doesn't exist with this response. It's also not JSON
testCheckForNewerVersion(MessageExpectation.WontBeShown, done).done(function () { return done(); }, done);
});
it("works if the settings file is empty", function (done) {
// Create an empty settings file
Settings.saveSettings({});
testCheckForNewerVersion(MessageExpectation.WillBeShown, done).done(function () { return done(); }, done);
});
});
//# sourceMappingURL=checkForNewerVersion.js.map