UNPKG

appcenter-cli

Version:

Command line tool for Visual Studio App Center

255 lines (254 loc) 12.9 kB
"use strict"; 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.UITestPreparer = void 0; const test_cloud_error_1 = require("./test-cloud-error"); const promisfied_glob_1 = require("../../../util/misc/promisfied-glob"); const promisfied_fs_1 = require("../../../util/misc/promisfied-fs"); const os = require("os"); const path = require("path"); const process = require("../../../util/misc/process-helper"); const interaction_1 = require("../../../util/interaction"); const debug = require("debug")("appcenter-cli:commands:test:lib:uitest-preparer"); const minimumVersion = [2, 2, 0]; class UITestPreparer { constructor(artifactsDir, buildDir, appPath) { if (!artifactsDir) { throw new Error("Argument --artifacts-dir is required"); } if (!buildDir) { throw new Error("Argument --build-dir is required"); } if (!appPath) { throw new Error("Argument --app-path is required"); } this.appPath = appPath; this.buildDir = buildDir; this.artifactsDir = artifactsDir; } prepare() { return __awaiter(this, void 0, void 0, function* () { this.validateArguments(); const command = yield this.getPrepareCommand(); debug(`Executing "test-cloud.exe prepare" command`); const exitCode = yield process.execWithArgsAndWait(command.file, command.args, this.outMessage, this.outMessage); if (exitCode !== 0) { const message = this.convertErrorCode(exitCode); throw new test_cloud_error_1.TestCloudError(`Failed to prepare UI Test artifacts using command "test-cloud.exe prepare" with error message:${os.EOL}${message}`, exitCode); } return path.join(this.artifactsDir, "manifest.json"); }); } convertErrorCode(exitCode) { const tryAgain = `, please try again. If you can't work out how to fix this issue, please contact support.`; switch (exitCode) { case 1: return `There was an unknown error preparing test artifacts${tryAgain}`; case 2: return `Invalid options were used to prepare the test artifacts${tryAgain}`; case 3: return `The app file was not found${tryAgain}`; case 4: return `The assembly directory was not found${tryAgain}`; case 5: return `The NUnit library was not found${tryAgain}`; case 6: return `The UITest.dll was not found${tryAgain}`; case 7: return `No test assemblies were found${tryAgain}`; case 8: return `The Sign Info File was not found${tryAgain}`; case 9: return `The KeyStore was not found${tryAgain}`; case 10: return `The dSym was not found or was not a directory${tryAgain}`; case 11: return `The dSym directory has the wrong extension${tryAgain}`; case 12: return `The dSym file contains more than one dwarf${tryAgain}`; case 13: return (`Test chunking failed.` + `Please try to delete dlls which are not necessary from the path provided in the --build-dir argument.` + `If this error happens again, please contact support.`); case 14: return `Upload negotiation failed${tryAgain}`; case 15: return `The upload failed${tryAgain}`; case 16: return `Job status retrival failed while preparing test artifacts${tryAgain}`; case 17: return `The NUnit report was not returned${tryAgain}`; case 18: return `The job failed while preparing test artifacts${tryAgain}`; case 19: return `There were test failures.`; case 20: return (`The version of UITest.dll and the tools are incompatible. ` + `Please make sure --uitest-tools-dir points to the same version of UITest as the dll in your assembly directory.`); case 21: return `No tests would run given the current parameters${tryAgain}`; case 22: return (`A specified data file was outside the assembly directory. ` + `Please make sure all referenced data files exist within your assembly directory and try again.`); case 23: return (`A specified data file was not found, please check your data files exist and try again. ` + `If this error happens again, please contact support.`); case 24: return `The test run did not pass validation${tryAgain}`; case 25: return `The test run was cancelled.`; case 26: return `The referenced version of NUnit was unsupported, please use NUnit 2.6.4 or below.`; case 27: return `The artifacts directory was invalid${tryAgain}`; } return `Returning exit code ${exitCode}.`; } validateArguments() { if (this.storeFile || this.storePassword || this.keyAlias || this.keyPassword) { if (!(this.storeFile && this.storePassword && this.keyAlias && this.keyPassword)) { throw new Error("If keystore is used, all of the following arguments must be set: --store-path, --store-password, --key-alias, --key-password"); } } } getPrepareCommand() { return __awaiter(this, void 0, void 0, function* () { let file = ""; const args = []; const executable = yield this.getTestCloudExecutablePath(); if (os.platform() === "win32") { file = executable; } else { file = "mono"; args.push(executable); } args.push("prepare", this.appPath); if (this.storeFile) { args.push("keystore", this.storeFile, this.storePassword, this.keyAlias, this.keyPassword); } args.push("--assembly-dir", this.buildDir, "--artifacts-dir", this.artifactsDir); if (this.signInfo) { args.push("--sign-info", this.signInfo); } if (this.fixture) { this.fixture.forEach((item) => { args.push("--fixture", item); }); } if (this.includeCategory) { this.includeCategory.forEach((category) => { args.push("--include", category); }); } if (this.excludeCategory) { this.excludeCategory.forEach((category) => { args.push("--exclude", category); }); } if (this.testChunk) { args.push("--test-chunk"); } if (this.fixtureChunk) { args.push("--fixture-chunk"); } return { file: file, args: args }; }); } getTestCloudExecutablePath() { return __awaiter(this, void 0, void 0, function* () { const toolsDir = this.uiTestToolsDir || (yield UITestPreparer.findXamarinUITestNugetDir(this.buildDir)); if (!(yield promisfied_fs_1.directoryExists(toolsDir))) { throw new Error(`Cannot find test-cloud.exe, the path specified by "--uitest-tools-dir" was not found.${os.EOL}` + `Please check that "${toolsDir}" is a valid directory and contains test-cloud.exe.${os.EOL}` + `Minimum required version is "${UITestPreparer.getMinimumVersionString()}".`); } let testCloudPath = path.join(toolsDir, "test-cloud.exe"); if (!(yield promisfied_fs_1.fileExists(testCloudPath))) { testCloudPath = path.join(toolsDir, "Xamarin.UITest.CLI.exe"); if (!(yield promisfied_fs_1.fileExists(testCloudPath))) { throw new Error(`Cannot find test-cloud.exe, the exe was not found in the path specified by "--uitest-tools-dir".${os.EOL}` + `Please check that ${testCloudPath} points to a test-cloud.exe.${os.EOL}` + `Minimum required version is "${UITestPreparer.getMinimumVersionString()}".`); } } if (testCloudPath.includes(" ")) { testCloudPath = `"${testCloudPath}"`; } debug(`Using test cloud tools path: ${testCloudPath}`); return testCloudPath; }); } static findXamarinUITestNugetDir(root, buildDir) { return __awaiter(this, void 0, void 0, function* () { const possibleNugetDirPattern = path.join(root, "packages", "Xamarin.UITest.*", "tools", "test-cloud.exe"); const files = (yield promisfied_glob_1.glob(possibleNugetDirPattern)).sort(); if (files.length === 0) { const parentDir = path.dirname(root); if (parentDir === root) { throw new Error(`Cannot find test-cloud.exe, which is required to prepare UI tests.${os.EOL}` + `We have searched for directory "packages${path.sep}Xamarin.UITest.*${path.sep}tools" inside ` + `"${buildDir || parentDir}" and all of its parent directories.${os.EOL}` + `Please use option "--uitest-tools-dir" to manually specify location of this tool.${os.EOL}` + `Minimum required version is "${this.getMinimumVersionString()}".`); } else { return yield UITestPreparer.findXamarinUITestNugetDir(parentDir, buildDir); } } else { const latestTestCloudPath = files[files.length - 1]; const match = latestTestCloudPath.match(/Xamarin\.UITest\.(\d+)\.(\d+)\.(\d+)/); if (!match) { throw new Error(`Found test-cloud.exe at "${path.dirname(latestTestCloudPath)}", but cannot recognize its version.${os.EOL}` + `Please use option "--uitest-tools-dir" to manually specify location of this tool.${os.EOL}` + `Minimum required version is "${this.getMinimumVersionString()}".`); } const [, major, minor, build] = match; if (!this.hasMinimumTestCloudVersion(parseInt(major, 10), parseInt(minor, 10), parseInt(build, 10))) { throw new Error(`The latest version of test-cloud.exe, found at "${path.dirname(latestTestCloudPath)}", ` + `is too old.${os.EOL}` + `Please upgrade the NuGet package to version ${this.getMinimumVersionString()} or higher.`); } else { return path.dirname(latestTestCloudPath); } } }); } static getMinimumVersionString() { return `${minimumVersion[0]}.${minimumVersion[1]}.${minimumVersion[2]}`; } static hasMinimumTestCloudVersion(major, minor, build) { const currentVersion = [major, minor, build]; const minLength = Math.min(currentVersion.length, minimumVersion.length); for (let i = 0; i < minLength; i++) { if (currentVersion[i] > minimumVersion[i]) { return true; } if (currentVersion[i] < minimumVersion[i]) { return false; } } return true; } /* the UITest preparer sometimes prints messages with it's own executable name, such as "Run 'test-cloud.exe help prepare' for more details". It's confusing for end users, so wer are removing lines that contain the executable name. */ outMessage(line) { if (line.indexOf("test-cloud.exe") === -1) { interaction_1.out.text(line); } } } exports.UITestPreparer = UITestPreparer;