appcenter-cli
Version:
Command line tool for Visual Studio App Center
232 lines (231 loc) • 11.7 kB
JavaScript
"use strict";
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.RunTestsCommand = void 0;
const commandline_1 = require("../../../util/commandline");
const test_cloud_uploader_1 = require("./test-cloud-uploader");
const state_checker_1 = require("./state-checker");
const interaction_1 = require("../../../util/interaction");
const profile_1 = require("../../../util/profile");
const parameters_parser_1 = require("./parameters-parser");
const included_files_parser_1 = require("./included-files-parser");
const interaction_2 = require("./interaction");
const help_messages_1 = require("./help-messages");
const _ = require("lodash");
const pfs = require("../../../util/misc/promisfied-fs");
const path = require("path");
const os = require("os");
const error_info_builder_1 = require("../lib/error-info-builder");
class RunTestsCommand extends commandline_1.AppCommand {
constructor(args) {
super(args);
this.isAppPathRequired = true;
this.streamingOutput = new interaction_1.StreamingArrayOutput();
this.testParameters = this.fixArrayParameter(this.testParameters);
this.include = this.fixArrayParameter(this.include);
if (this.timeoutSec && typeof this.timeoutSec === "string") {
this.timeoutSec = parseInt(this.timeoutSec, 10);
}
}
// Override this if you need to validate options
validateOptions() {
return __awaiter(this, void 0, void 0, function* () {
return;
});
}
// Override this if additional processing is needed need after test run completes
afterCompletion(client, testRun, streamingOutput) {
return __awaiter(this, void 0, void 0, function* () {
return;
});
}
// TODO: There is technical debt here.
// There is a lot of confusion and even duplicated code with respect to test params,
// included files and responsibility of prepare vs run.
run(client, portalBaseUrl) {
return __awaiter(this, void 0, void 0, function* () {
if (this.isAppPathRequired && !this.appPath) {
throw new Error("Argument --app-path is required");
}
yield this.validateOptions();
try {
const artifactsDir = yield this.getArtifactsDir();
this.streamingOutput.start();
try {
const manifestPath = yield interaction_2.progressWithResult("Preparing tests", this.prepareManifest(artifactsDir));
yield this.updateManifestAndCopyFilesToArtifactsDir(manifestPath);
const testRun = yield this.uploadAndStart(client, manifestPath, portalBaseUrl);
const vstsIdVariable = this.vstsIdVariable;
this.streamingOutput.text(function (testRun) {
let report = `Test run id: "${testRun.testRunId}"` + os.EOL;
if (vstsIdVariable) {
report = `##vso[task.setvariable variable=${vstsIdVariable}]${testRun.testRunId}` + os.EOL;
}
report += "Accepted devices: " + os.EOL;
testRun.acceptedDevices.map((item) => ` - ${item}`).forEach((text) => (report += text + os.EOL));
if (testRun.rejectedDevices && testRun.rejectedDevices.length > 0) {
report += "Rejected devices: " + os.EOL;
testRun.rejectedDevices.map((item) => ` - ${item}`).forEach((text) => (report += text + os.EOL));
}
return report;
}, testRun);
if (!this.async) {
const exitCode = yield this.waitForCompletion(client, testRun.testRunId);
yield this.afterCompletion(client, testRun, this.streamingOutput);
switch (exitCode) {
case 1:
return commandline_1.failure(exitCode, `There were Test Failures.${os.EOL}Test Report: ${testRun.testRunUrl}`);
case 2:
return commandline_1.failure(exitCode, `Cannot run tests. Returning exit code ${exitCode}.
${os.EOL}Test Report: ${testRun.testRunUrl}`);
}
}
if (!(this.async && this.format)) {
this.streamingOutput.text(function (testRun) {
const report = `Test Report: ${testRun.testRunUrl}` + os.EOL;
return report;
}, testRun);
}
return commandline_1.success();
}
finally {
yield this.cleanupArtifactsDir(artifactsDir);
this.streamingOutput.finish();
}
}
catch (err) {
const errInfo = error_info_builder_1.buildErrorInfo(err, profile_1.getUser(), this);
return commandline_1.failure(errInfo.exitCode, errInfo.message);
}
});
}
updateManifestAndCopyFilesToArtifactsDir(manifestPath) {
return __awaiter(this, void 0, void 0, function* () {
const manifestJson = yield pfs.readFile(manifestPath, "utf8");
const manifest = JSON.parse(manifestJson);
manifest.cliVersion = this.getVersion();
yield included_files_parser_1.processIncludedFiles(manifest, this.include, path.dirname(manifestPath), this.getSourceRootDir());
const modifiedManifest = JSON.stringify(manifest, null, 1);
yield pfs.writeFile(manifestPath, modifiedManifest);
});
}
prepareManifest(artifactsDir) {
throw new Error("This method must be overriden in derived classes");
}
getSourceRootDir() {
throw new Error("This method must be overriden in derived classes");
}
cleanupArtifactsDir(artifactsDir) {
return __awaiter(this, void 0, void 0, function* () {
yield pfs.rmDir(artifactsDir, true).catch(function (err) {
console.warn(`${err} while cleaning up artifacts directory ${artifactsDir}. This is often due to files being locked or in use. Please check your virus scan settings and any local security policies you might have in place for this directory. Continuing without cleanup.`);
});
});
}
getArtifactsDir() {
return __awaiter(this, void 0, void 0, function* () {
return this.artifactsDir || (this.artifactsDir = yield pfs.mkTempDir("appcenter-upload"));
});
}
uploadAndStart(client, manifestPath, portalBaseUrl) {
return __awaiter(this, void 0, void 0, function* () {
const uploader = new test_cloud_uploader_1.TestCloudUploader(client, this.app.ownerName, this.app.appName, manifestPath, this.devices, portalBaseUrl);
uploader.appPath = this.appPath;
uploader.language = this.language;
uploader.locale = this.locale;
uploader.testSeries = this.testSeries;
uploader.testParameters = this.combinedParameters();
if (this.dSymDir) {
console.warn("The option --dsym-dir is deprecated and ignored");
}
return yield uploader.uploadAndStart();
});
}
combinedParameters() {
const parameters = this.getParametersFromOptions();
if (this.testParameters) {
return _.merge(parameters, parameters_parser_1.parseTestParameters(this.testParameters));
}
else {
return parameters;
}
}
getParametersFromOptions() {
return {};
}
waitForCompletion(client, testRunId) {
const checker = new state_checker_1.StateChecker(client, testRunId, this.app.ownerName, this.app.appName, this.streamingOutput);
return checker.checkUntilCompleted(this.timeoutSec);
}
}
__decorate([
commandline_1.help(help_messages_1.Messages.TestCloud.Arguments.AppPath),
commandline_1.longName("app-path"),
commandline_1.hasArg
], RunTestsCommand.prototype, "appPath", void 0);
__decorate([
commandline_1.help(help_messages_1.Messages.TestCloud.Arguments.RunDevices),
commandline_1.longName("devices"),
commandline_1.hasArg,
commandline_1.required
], RunTestsCommand.prototype, "devices", void 0);
__decorate([
commandline_1.help(help_messages_1.Messages.TestCloud.Arguments.RunDSymDir),
commandline_1.longName("dsym-dir"),
commandline_1.hasArg
], RunTestsCommand.prototype, "dSymDir", void 0);
__decorate([
commandline_1.help(help_messages_1.Messages.TestCloud.Arguments.RunLocale),
commandline_1.longName("locale"),
commandline_1.hasArg
], RunTestsCommand.prototype, "locale", void 0);
__decorate([
commandline_1.help(help_messages_1.Messages.TestCloud.Arguments.RunLanguage),
commandline_1.longName("language"),
commandline_1.hasArg
], RunTestsCommand.prototype, "language", void 0);
__decorate([
commandline_1.help(help_messages_1.Messages.TestCloud.Arguments.RunTestSeries),
commandline_1.longName("test-series"),
commandline_1.hasArg
], RunTestsCommand.prototype, "testSeries", void 0);
__decorate([
commandline_1.help(help_messages_1.Messages.TestCloud.Arguments.Include),
commandline_1.longName("include"),
commandline_1.hasArg
], RunTestsCommand.prototype, "include", void 0);
__decorate([
commandline_1.help(help_messages_1.Messages.TestCloud.Arguments.TestParameter),
commandline_1.longName("test-parameter"),
commandline_1.shortName("p"),
commandline_1.hasArg
], RunTestsCommand.prototype, "testParameters", void 0);
__decorate([
commandline_1.help(help_messages_1.Messages.TestCloud.Arguments.RunAsync),
commandline_1.longName("async")
], RunTestsCommand.prototype, "async", void 0);
__decorate([
commandline_1.help(help_messages_1.Messages.TestCloud.Arguments.Timeout),
commandline_1.longName("timeout"),
commandline_1.hasArg
], RunTestsCommand.prototype, "timeoutSec", void 0);
__decorate([
commandline_1.help(help_messages_1.Messages.TestCloud.Arguments.VSTSIdVariable),
commandline_1.longName("vsts-id-variable"),
commandline_1.hasArg
], RunTestsCommand.prototype, "vstsIdVariable", void 0);
exports.RunTestsCommand = RunTestsCommand;