UNPKG

@hkvstore/taco-cli

Version:

taco-cli is a command-line interface for rapid Apache Cordova development (forked from Microsoft taco-cli)

397 lines (395 loc) 21.9 kB
// 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/cordovaExtensions.d.ts" /> /// <reference path="../../typings/del.d.ts" /> "use strict"; /* tslint:disable:no-var-requires */ // var require needed for should module to work correctly // Note not import: We don't want to refer to shouldModule, but we need the require to occur since it modifies the prototype of Object. var shouldModule = require("should"); /* tslint:enable:no-var-requires */ var AdmZip = require("adm-zip"); var fs = require("fs"); var http = require("http"); var os = require("os"); var path = require("path"); var Q = require("q"); var querystring = require("querystring"); var util = require("util"); var ServerMock = require("./utils/serverMock"); var Settings = require("../cli/utils/settings"); var TacoUtility = require("taco-utils"); var TacoTestUtils = require("taco-tests-utils"); var BuildInfo = TacoUtility.BuildInfo; var utils = TacoUtility.UtilHelper; var CommandHelper = require("./utils/commandHelper"); var MockCordova = TacoTestUtils.MockCordova; var build = CommandHelper.getCommand("build"); var create = CommandHelper.getCommand("create"); /** * Build/Run telemetry test plan * * Concerns to test: * options: local, remote, clean, debug, release, device, emulator, target * platforms: android, ios, windows, wp8 * + multiple platforms in a single command * + no platforms specified in the command line, so we choose what to build * + specifying contradictory options in the command line (e.g.: --debug --release) --> The command fails, so we don't need to test this * remote build: project"s size, gziped project"s size, changed files" count, was incremental build, secure HTTPs server? * Unexpected behavior: --uknown_option and unknown_platform * Run specific options: nobuild, debuginfo * * Test cases: * 1. android local clean release emulator * 2. ios remote debug target secure_server incremental --> We are not testing secure_server currently * 3. android ios unsecure_server not_incremental * 4. no command line platforms, implicit windows wp8 device * 5. --uknown_option unknown_platform * 6. nobuild debuginfo (Only for Run) * * TODO: We currently aren't testing a secure server, because we need to find a good way of either * installing the client certificate in both Windows and Mac, or mocking the certificate path */ var BuildAndRunTelemetryTests; (function (BuildAndRunTelemetryTests) { (function (Command) { Command[Command["Build"] = 0] = "Build"; Command[Command["Run"] = 1] = "Run"; Command[Command["Emulate"] = 2] = "Emulate"; })(BuildAndRunTelemetryTests.Command || (BuildAndRunTelemetryTests.Command = {})); var Command = BuildAndRunTelemetryTests.Command; function createBuildAndRunTelemetryTests(runCommand, command) { var tacoHome = path.join(os.tmpdir(), "taco-cli", commandSwitch("build", "run", "emulate")); var projectPath = path.join(tacoHome, "example"); var testIosHttpServer; var testAndroidHttpServer; var iosPort = 3001; var androidPort = 3002; var cordova = MockCordova.MockCordova510.getDefault(); var vcordova = "4.0.0"; var buildNumber = 12341; var isNotEmulate = command !== Command.Emulate; var customLoader = { lazyRequire: function (packageName, packageId, logLevel) { return Q(cordova); }, lazyRun: function (packageName, packageId, commandName) { return Q("cordova"); } }; before(function () { testIosHttpServer = http.createServer(); testIosHttpServer.listen(iosPort); testAndroidHttpServer = http.createServer(); testAndroidHttpServer.listen(androidPort); }); after(function () { testIosHttpServer.close(); testAndroidHttpServer.close(); }); // We mock cordova build cordova.raw.build = function (options) { return Q({}); }; // We mock cordova run cordova.raw.run = function (options) { return Q({}); }; // We mock cordova emulate cordova.raw.emulate = function (options) { return Q({}); }; function generateCompleteBuildSequence(platform, port, isIncrementalTest) { var configuration = "debug"; // Mock out the server on the other side var queryOptions = { command: "build", vcordova: vcordova, vcli: require(path.join(__dirname, "..", "package.json")).version, cfg: configuration, platform: platform }; var zip = new AdmZip(); zip.addFile("test.txt", new Buffer("test file"), "comment"); var zippedAppBuffer = zip.toBuffer(); var nonIncrementalBuildStart = [{ expectedUrl: "/cordova/build/tasks?" + querystring.stringify(queryOptions), head: { "Content-Type": "application/json", "Content-Location": "http://localhost:" + port + "/cordova/build/tasks/" + buildNumber }, statusCode: 202, response: JSON.stringify(new BuildInfo({ status: BuildInfo.UPLOADING, buildNumber: buildNumber, buildLang: "en" })), waitForPayload: true }]; queryOptions["buildNumber"] = "" + buildNumber; var incrementalBuildStart = [{ expectedUrl: "/cordova/build/" + buildNumber, head: { "Content-Type": "application/json" }, statusCode: 200, response: JSON.stringify(new BuildInfo({ status: BuildInfo.COMPLETE, buildNumber: buildNumber, buildLang: "en" })), waitForPayload: false }, { expectedUrl: "/cordova/build/tasks?" + querystring.stringify(queryOptions), head: { "Content-Type": "application/json", "Content-Location": "http://localhost:" + port + "/cordova/build/tasks/" + buildNumber }, statusCode: 202, response: JSON.stringify(new BuildInfo({ status: BuildInfo.UPLOADING, buildNumber: buildNumber, buildLang: "en" })), waitForPayload: true }]; var remainingBuildSequence = [{ expectedUrl: "/cordova/build/tasks/" + buildNumber, head: { "Content-Type": "application/json" }, statusCode: 200, response: JSON.stringify(new BuildInfo({ status: BuildInfo.UPLOADED, buildNumber: buildNumber, buildLang: "en" })), waitForPayload: false }, { expectedUrl: "/cordova/build/tasks/" + buildNumber + "/log?offset=0", head: { "Content-Type": "application/json" }, statusCode: 200, response: "1", waitForPayload: false }, { expectedUrl: "/cordova/build/tasks", head: { "Content-Type": "application/json" }, statusCode: 200, response: JSON.stringify({ queued: 0, queuedBuilds: [] }), waitForPayload: false }, { expectedUrl: "/cordova/build/tasks/" + buildNumber, head: { "Content-Type": "application/json" }, statusCode: 200, response: JSON.stringify(new BuildInfo({ status: BuildInfo.COMPLETE, buildNumber: buildNumber, buildLang: "en" })), waitForPayload: false }, { expectedUrl: "/cordova/build/tasks/" + buildNumber + "/log?offset=1", head: { "Content-Type": "application/json" }, statusCode: 200, response: "2", waitForPayload: false }, { expectedUrl: util.format("/cordova/files/%d/cordovaApp/plugins/%s.json", buildNumber, platform), head: { "Content-Type": "application/json" }, statusCode: 200, response: JSON.stringify({}), waitForPayload: false } ]; var buildSequence = (isIncrementalTest ? incrementalBuildStart : nonIncrementalBuildStart).concat(remainingBuildSequence); if (command !== Command.Build) { var target = isIncrementalTest ? "ipad 2" : ""; var runSequence = [{ expectedUrl: "/cordova/build/" + buildNumber + "/emulate?" + querystring.stringify({ target: target }), head: { "Content-Type": "application/json" }, statusCode: 200, response: JSON.stringify(new BuildInfo({ status: BuildInfo.EMULATED, buildNumber: buildNumber })), waitForPayload: false }]; buildSequence = buildSequence.concat(runSequence); } return buildSequence; } function configureRemoteServer(done, isIncrementalTest) { var iosSequence = generateCompleteBuildSequence("ios", iosPort, isIncrementalTest); var androidSequence = generateCompleteBuildSequence("android", androidPort, isIncrementalTest); var iosServerFunction = ServerMock.generateServerFunction(done, iosSequence); var androidServerFunction = ServerMock.generateServerFunction(done, androidSequence); testIosHttpServer.on("request", iosServerFunction); if (!isIncrementalTest) { testAndroidHttpServer.on("request", androidServerFunction); } var platforms = { ios: { host: "localhost", port: iosPort, secure: false, mountPoint: "cordova" } }; if (!isIncrementalTest) { platforms["android"] = { host: "localhost", port: androidPort, secure: false, mountPoint: "cordova" }; } return Settings.saveSettings({ remotePlatforms: platforms }); } var expectedGzipedSizeAbsoluteError = 60; /* This is how much the gzip size changes because of the different compression rate of different file modification dates, etc... */ // We use this function to validate that the gzip size is near the expected ratio (non-deterministic changes in dates or other // numbers might change the compression ratio, so it's difficult to predict the exact size), and then replace the number with // the expected size, so we can compare it by eql with the expected full telemetry properties function validateGzipedSize(telemetryProperties, platform, expectedGzippedSize) { var keyName = "remotebuild." + platform + ".gzipedProjectSizeInBytes"; if (expectedGzippedSize !== -1) { var value = telemetryProperties[keyName].value; value.should.be.above(expectedGzipedSizeAbsoluteError - expectedGzippedSize); value.should.be.below(expectedGzipedSizeAbsoluteError + expectedGzippedSize); telemetryProperties[keyName].value = String(expectedGzippedSize); } else { (typeof telemetryProperties[keyName] === "undefined").should.be.equal(true); } } function telemetryShouldEqual(telemetryProperties, expected, iosExpectedGzipedSize, androidGzipSize) { if (iosExpectedGzipedSize === void 0) { iosExpectedGzipedSize = -1; } if (androidGzipSize === void 0) { androidGzipSize = -1; } (typeof telemetryProperties === "undefined").should.be.equal(false); validateGzipedSize(telemetryProperties, "ios", iosExpectedGzipedSize); validateGzipedSize(telemetryProperties, "android", androidGzipSize); telemetryProperties.should.containEql(expected); // We are comparing the objects, after overriding the sizes with the expected values } beforeEach(function (done) { // Warning: After this line, all cordova CLI commands will have to be mocked TacoUtility.TacoPackageLoader.mockForTests = customLoader; Settings.saveSettings({ remotePlatforms: {} }) .done(function () { return done(); }, done); }); afterEach(function () { TacoUtility.TacoPackageLoader.mockForTests = null; }); function commandSwitch(buildResult, runResult, emulateResult) { switch (command) { case Command.Build: return buildResult; case Command.Run: return runResult; case Command.Emulate: return emulateResult; default: throw new Error("Unknown command"); } } it("1. android local clean release emulator", function (done) { var args = ["--local", "--release", "android"]; var expected = { "options.local": { isPii: false, value: "true" }, "options.release": { isPii: false, value: "true" }, "platforms.requestedViaCommandLine.local1": { isPii: false, value: "android" } }; if ((command === Command.Build)) { args.unshift("--clean"); // Only build supports clean expected["options.clean"] = { isPii: false, value: "true" }; } else if (command !== Command.Emulate) { args.unshift("--emulator"); // Emulator doesn't support emulator expected["options.emulator"] = { isPii: false, value: "true" }; } if (command !== Command.Run) { expected["platforms.actuallyBuilt.local1"] = { isPii: false, value: "android" }; } runCommand(args).then(function (telemetryProperties) { telemetryShouldEqual(telemetryProperties, expected); }).done(function () { return done(); }, done); }); function mockProjectWithIncrementalBuild() { // We write an empty changes file, and a build info file so we'll get an incremental build var changeTimeFileDirectory = path.join(projectPath, "remote", "ios", "debug"); utils.createDirectoryIfNecessary(changeTimeFileDirectory); var changeTimeFile = path.join(changeTimeFileDirectory, "lastChangeTime.json"); var buildInfoFile = path.join(changeTimeFileDirectory, "buildInfo.json"); fs.writeFileSync(changeTimeFile, "{}"); fs.writeFileSync(buildInfoFile, "{\"buildNumber\": " + buildNumber + "}"); } it("2. TestCordovaExempt ios remote debug target non_secure_server incremental", function (done) { var args = ["--remote", "--debug", "--target=ipad 2", "ios"]; var expected = { "options.remote": { isPii: false, value: "true" }, "options.debug": { isPii: false, value: "true" }, "options.target": { isPii: false, value: "ipad 2" }, "platforms.actuallyBuilt.remote1": { isPii: false, value: "ios" }, "platforms.requestedViaCommandLine.remote1": { isPii: false, value: "ios" }, "platforms.remote.ios.is_secure": { isPii: false, value: "false" }, "remoteBuild.ios.filesChangedCount": { isPii: false, value: 8 }, "remoteBuild.ios.wasIncremental": { isPii: false, value: "true" }, "remotebuild.ios.gzipedProjectSizeInBytes": { isPii: false, value: "28382" }, "remotebuild.ios.projectSizeInBytes": { isPii: false, value: "48128" } }; mockProjectWithIncrementalBuild(); configureRemoteServer(done, /* Incremental test*/ true) .then(function () { return runCommand(args); }) .finally(function () { testIosHttpServer.removeAllListeners("request"); testAndroidHttpServer.removeAllListeners("request"); }) .then(function (telemetryProperties) { telemetryShouldEqual(telemetryProperties, expected, 28382); }).done(function () { return done(); }, done); }); it("3. TestCordovaExempt android ios unsecure_server not_incremental", function (done) { var args = ["android", "ios"]; var expected = { "platforms.actuallyBuilt.remote1": { isPii: false, value: "android" }, "platforms.actuallyBuilt.remote2": { isPii: false, value: "ios" }, "platforms.requestedViaCommandLine.remote1": { isPii: false, value: "android" }, "platforms.requestedViaCommandLine.remote2": { isPii: false, value: "ios" }, "platforms.remote.android.is_secure": { isPii: false, value: "false" }, "platforms.remote.ios.is_secure": { isPii: false, value: "false" }, "remoteBuild.android.filesChangedCount": { isPii: false, value: 8 }, "remoteBuild.android.wasIncremental": { isPii: false, value: "false" }, "remotebuild.android.gzipedProjectSizeInBytes": { isPii: false, value: "28379" }, "remotebuild.android.projectSizeInBytes": { isPii: false, value: "48128" }, "remoteBuild.ios.filesChangedCount": { isPii: false, value: 8 }, "remoteBuild.ios.wasIncremental": { isPii: false, value: "false" }, "remotebuild.ios.gzipedProjectSizeInBytes": { isPii: false, value: "28379" }, "remotebuild.ios.projectSizeInBytes": { isPii: false, value: "48128" } }; configureRemoteServer(done, /* Not incremental test*/ false) .then(function () { return runCommand(args); }) .finally(function () { testIosHttpServer.removeAllListeners("request"); testAndroidHttpServer.removeAllListeners("request"); }) .then(function (telemetryProperties) { return telemetryShouldEqual(telemetryProperties, expected, 28379, 28379); }) .done(function () { return done(); }, done); }); it("4. no command line platforms, implicit windows wp8 device", function (done) { // taco platform add windows wp8: We mock adding the platform utils.createDirectoryIfNecessary(path.join(projectPath, "platforms", "windows")); utils.createDirectoryIfNecessary(path.join(projectPath, "platforms", "wp8")); var expected = { "platforms.actuallyBuilt.local1": { isPii: false, value: "windows" }, "platforms.actuallyBuilt.local2": { isPii: false, value: "wp8" } }; var args = []; if (isNotEmulate) { args.unshift("--device"); expected["options.device"] = { isPii: false, value: "true" }; } runCommand(args) .then(function (telemetryProperties) { return telemetryShouldEqual(telemetryProperties, expected); }) .then(function () { return done(); }, done); }); it("5. --uknown_option unknown_platform", function (done) { var args = ["--uknown_option=unknown_value", "unknown_platform"]; var expected = { "platforms.requestedViaCommandLine.local1": { isPii: true, value: "unknown_platform" }, "platforms.actuallyBuilt.local1": { isPii: true, value: "unknown_platform" }, "unknownOption1.name": { isPii: true, value: "uknown_option" }, "unknownOption1.value": { isPii: true, value: "unknown_value" } }; runCommand(args).then(function (telemetryProperties) { telemetryShouldEqual(telemetryProperties, expected); }).done(function () { return done(); }, done); }); if ((command !== Command.Build)) { it("6. nobuild debuginfo", function (done) { utils.createDirectoryIfNecessary(path.join(projectPath, "platforms", "android")); var args = ["--nobuild", "--debuginfo", "android"]; var expected = { "options.nobuild": { isPii: false, value: "true" }, "options.debuginfo": { isPii: false, value: "true" }, "platforms.actuallyBuilt.local1": { isPii: false, value: "android" }, "platforms.requestedViaCommandLine.local1": { isPii: false, value: "android" } }; runCommand(args) .then(function (telemetryProperties) { return telemetryShouldEqual(telemetryProperties, expected); }) .then(function () { return done(); }, done); }); } } BuildAndRunTelemetryTests.createBuildAndRunTelemetryTests = createBuildAndRunTelemetryTests; })(BuildAndRunTelemetryTests || (BuildAndRunTelemetryTests = {})); module.exports = BuildAndRunTelemetryTests; //# sourceMappingURL=buildAndRunTelemetry.js.map