diff --git a/lib/bootstrap.ts b/lib/bootstrap.ts index d1e532459f..9f4bb2094d 100644 --- a/lib/bootstrap.ts +++ b/lib/bootstrap.ts @@ -160,7 +160,7 @@ $injector.require("terminalSpinnerService", "./services/terminal-spinner-service $injector.require('playgroundService', './services/playground-service'); $injector.require("platformEnvironmentRequirements", "./services/platform-environment-requirements"); -$injector.require("nativescriptCloudExtensionService", "./services/nativescript-cloud-extension-service"); +$injector.require("nativeScriptCloudExtensionService", "./services/nativescript-cloud-extension-service"); $injector.requireCommand("resources|generate|icons", "./commands/generate-assets"); $injector.requireCommand("resources|generate|splashes", "./commands/generate-assets"); diff --git a/lib/commands/setup.ts b/lib/commands/setup.ts index 928582fb3e..cfddc687a7 100644 --- a/lib/commands/setup.ts +++ b/lib/commands/setup.ts @@ -12,10 +12,10 @@ $injector.registerCommand("setup|*", SetupCommand); export class CloudSetupCommand implements ICommand { public allowedParameters: ICommandParameter[] = []; - constructor(private $nativescriptCloudExtensionService: INativescriptCloudExtensionService) { } + constructor(private $nativeScriptCloudExtensionService: INativeScriptCloudExtensionService) { } public execute(args: string[]): Promise { - return this.$nativescriptCloudExtensionService.install(); + return this.$nativeScriptCloudExtensionService.install(); } } $injector.registerCommand(["setup|cloud", "cloud|setup"], CloudSetupCommand); diff --git a/lib/declarations.d.ts b/lib/declarations.d.ts index bb644633c1..ae5637ae1d 100644 --- a/lib/declarations.d.ts +++ b/lib/declarations.d.ts @@ -788,12 +788,17 @@ interface IBundleValidatorHelper { validate(): void; } -interface INativescriptCloudExtensionService { +interface INativeScriptCloudExtensionService { /** * Installs nativescript-cloud extension * @return {Promise} returns the extension data */ install(): Promise; + /** + * Checks if nativescript-cloud extension is installed + * @return {boolean} returns true in case when nativescript-cloud extension is installed, false otherwise + */ + isInstalled(): boolean } /** diff --git a/lib/services/nativescript-cloud-extension-service.ts b/lib/services/nativescript-cloud-extension-service.ts index 14eeacd46c..8b6a0f5889 100644 --- a/lib/services/nativescript-cloud-extension-service.ts +++ b/lib/services/nativescript-cloud-extension-service.ts @@ -1,17 +1,35 @@ import * as constants from "../constants"; +import * as semver from "semver"; -export class NativescriptCloudExtensionService implements INativescriptCloudExtensionService { - +export class NativeScriptCloudExtensionService implements INativeScriptCloudExtensionService { constructor(private $extensibilityService: IExtensibilityService, - private $logger: ILogger) { } + private $logger: ILogger, + private $npmInstallationManager: INpmInstallationManager) { } public install(): Promise { - const installedExtensions = this.$extensibilityService.getInstalledExtensions() || {}; - if (!installedExtensions[constants.NATIVESCRIPT_CLOUD_EXTENSION_NAME]) { + if (!this.isInstalled()) { return this.$extensibilityService.installExtension(constants.NATIVESCRIPT_CLOUD_EXTENSION_NAME); } this.$logger.out(`Extension ${constants.NATIVESCRIPT_CLOUD_EXTENSION_NAME} is already installed.`); } + + public isInstalled(): boolean { + return !!this.getExtensionData(); + } + + public async isLatestVersionInstalled(): Promise { + const extensionData = this.getExtensionData(); + if (extensionData) { + const latestVersion = await this.$npmInstallationManager.getLatestVersion(constants.NATIVESCRIPT_CLOUD_EXTENSION_NAME); + return semver.eq(latestVersion, extensionData.version); + } + + return false; + } + + private getExtensionData(): IExtensionData { + return _.find(this.$extensibilityService.getInstalledExtensionsData(), extensionData => extensionData.extensionName === constants.NATIVESCRIPT_CLOUD_EXTENSION_NAME); + } } -$injector.register("nativescriptCloudExtensionService", NativescriptCloudExtensionService); +$injector.register("nativeScriptCloudExtensionService", NativeScriptCloudExtensionService); diff --git a/lib/services/platform-environment-requirements.ts b/lib/services/platform-environment-requirements.ts index 0ae315c8dd..0eba53eb58 100644 --- a/lib/services/platform-environment-requirements.ts +++ b/lib/services/platform-environment-requirements.ts @@ -6,17 +6,20 @@ export class PlatformEnvironmentRequirements implements IPlatformEnvironmentRequ constructor(private $commandsService: ICommandsService, private $doctorService: IDoctorService, private $errors: IErrors, - private $nativescriptCloudExtensionService: INativescriptCloudExtensionService, private $logger: ILogger, + private $nativeScriptCloudExtensionService: INativeScriptCloudExtensionService, private $prompter: IPrompter, private $staticConfig: IStaticConfig) { } - public static CLOUD_BUILDS_OPTION_NAME = "Configure for Cloud Builds"; - public static SETUP_SCRIPT_OPTION_NAME = "Configure for Local Builds"; + public static CLOUD_SETUP_OPTION_NAME = "Configure for Cloud Builds"; + public static LOCAL_SETUP_OPTION_NAME = "Configure for Local Builds"; public static MANUALLY_SETUP_OPTION_NAME = "Skip Step and Configure Manually"; - private static BOTH_CLOUD_BUILDS_AND_SETUP_SCRIPT_OPTION_NAME = "Configure for Both Local and Cloud Builds"; - private static NOT_CONFIGURED_ENV_MESSAGE = "To continue, choose one of the following options: "; - private static NOT_CONFIGURED_ENV_AFTER_SETUP_SCRIPT_MESSAGE = "The setup script was not able to configure your environment for local builds. To execute local builds, you have to set up your environment manually. To continue, choose one of the following options:"; + private static BOTH_CLOUD_SETUP_AND_LOCAL_SETUP_OPTION_NAME = "Configure for Both Local and Cloud Builds"; + private static CHOOSE_OPTIONS_MESSAGE = "To continue, choose one of the following options: "; + private static NOT_CONFIGURED_ENV_AFTER_SETUP_SCRIPT_MESSAGE = `The setup script was not able to configure your environment for local builds. To execute local builds, you have to set up your environment manually. In case you have any questions, you can check our forum: 'http://forum.nativescript.org' and our public Slack channel: 'https://nativescriptcommunity.slack.com/'. ${PlatformEnvironmentRequirements.CHOOSE_OPTIONS_MESSAGE}`; + private static MISSING_LOCAL_SETUP_MESSAGE = "Your environment is not configured properly and you will not be able to execute local builds."; + private static MISSING_LOCAL_AND_CLOUD_SETUP_MESSAGE = `You are missing the ${constants.NATIVESCRIPT_CLOUD_EXTENSION_NAME} extension and you will not be able to execute cloud builds. ${PlatformEnvironmentRequirements.MISSING_LOCAL_SETUP_MESSAGE} ${PlatformEnvironmentRequirements.CHOOSE_OPTIONS_MESSAGE} `; + private static RUN_TNS_SETUP_MESSAGE = 'Run $ tns setup command to run the setup script to try to automatically configure your environment for local builds.'; private cliCommandToCloudCommandName: IStringDictionary = { "build": "tns cloud build", @@ -32,47 +35,38 @@ export class PlatformEnvironmentRequirements implements IPlatformEnvironmentRequ const canExecute = await this.$doctorService.canExecuteLocalBuild(platform); if (!canExecute) { if (!isInteractive()) { - this.fail(`You are missing the ${constants.NATIVESCRIPT_CLOUD_EXTENSION_NAME} extension and you will not be able to execute cloud builds. Your environment is not configured properly and you will not be able to execute local builds. To continue, choose one of the following options: ` + EOL - + "Run $ tns setup command to run the setup script to try to automatically configure your environment for local builds." + EOL - + `Run $ tns cloud setup command to install the ${constants.NATIVESCRIPT_CLOUD_EXTENSION_NAME} extension to configure your environment for cloud builds.` + EOL - + `Verify that your environment is configured according to the system requirements described at ${this.$staticConfig.SYS_REQUIREMENTS_LINK}`); + this.fail(this.getNonInteractiveConsoleMessage(platform)); } - this.$logger.info(`You are missing the ${constants.NATIVESCRIPT_CLOUD_EXTENSION_NAME} extension and you will not be able to execute cloud builds. Your environment is not configured properly and you will not be able to execute local builds. ` + EOL - + `Select "Configure for Cloud Builds" to install the ${constants.NATIVESCRIPT_CLOUD_EXTENSION_NAME} extension and automatically configure your environment for cloud builds.` + EOL - + `Select "Configure for Local Builds" to run the setup script and automatically configure your environment for local builds.` - + `Select "Configure for Both Local and Cloud Builds" to automatically configure your environment for both options.` - + `Select "Skip Step and Configure Manually" to disregard these options and install any required components manually.`); - - const selectedOption = await this.$prompter.promptForChoice(PlatformEnvironmentRequirements.NOT_CONFIGURED_ENV_MESSAGE, [ - PlatformEnvironmentRequirements.CLOUD_BUILDS_OPTION_NAME, - PlatformEnvironmentRequirements.SETUP_SCRIPT_OPTION_NAME, - PlatformEnvironmentRequirements.BOTH_CLOUD_BUILDS_AND_SETUP_SCRIPT_OPTION_NAME, - PlatformEnvironmentRequirements.MANUALLY_SETUP_OPTION_NAME, - ]); + this.$logger.info(this.getInteractiveConsoleMessage(platform)); - await this.processCloudBuildsIfNeeded(platform, selectedOption); + const selectedOption = await this.promptForChoice(); + await this.processCloudBuildsIfNeeded(platform, selectedOption); this.processManuallySetupIfNeeded(platform, selectedOption); - if (selectedOption === PlatformEnvironmentRequirements.SETUP_SCRIPT_OPTION_NAME) { + if (selectedOption === PlatformEnvironmentRequirements.LOCAL_SETUP_OPTION_NAME) { await this.$doctorService.runSetupScript(); if (await this.$doctorService.canExecuteLocalBuild(platform)) { return true; } - const option = await this.$prompter.promptForChoice(PlatformEnvironmentRequirements.NOT_CONFIGURED_ENV_AFTER_SETUP_SCRIPT_MESSAGE, [ - PlatformEnvironmentRequirements.CLOUD_BUILDS_OPTION_NAME, - PlatformEnvironmentRequirements.MANUALLY_SETUP_OPTION_NAME - ]); + if (this.$nativeScriptCloudExtensionService.isInstalled()) { + this.processManuallySetup(platform); + } else { + const option = await this.$prompter.promptForChoice(PlatformEnvironmentRequirements.NOT_CONFIGURED_ENV_AFTER_SETUP_SCRIPT_MESSAGE, [ + PlatformEnvironmentRequirements.CLOUD_SETUP_OPTION_NAME, + PlatformEnvironmentRequirements.MANUALLY_SETUP_OPTION_NAME + ]); - await this.processCloudBuildsIfNeeded(platform, option); + await this.processCloudBuildsIfNeeded(platform, option); - this.processManuallySetupIfNeeded(platform, option); + this.processManuallySetupIfNeeded(platform, option); + } } - if (selectedOption === PlatformEnvironmentRequirements.BOTH_CLOUD_BUILDS_AND_SETUP_SCRIPT_OPTION_NAME) { + if (selectedOption === PlatformEnvironmentRequirements.BOTH_CLOUD_SETUP_AND_LOCAL_SETUP_OPTION_NAME) { await this.processBothCloudBuildsAndSetupScript(platform); if (await this.$doctorService.canExecuteLocalBuild(platform)) { return true; @@ -86,7 +80,7 @@ export class PlatformEnvironmentRequirements implements IPlatformEnvironmentRequ } private async processCloudBuildsIfNeeded(platform: string, selectedOption: string): Promise { - if (selectedOption === PlatformEnvironmentRequirements.CLOUD_BUILDS_OPTION_NAME) { + if (selectedOption === PlatformEnvironmentRequirements.CLOUD_SETUP_OPTION_NAME) { await this.processCloudBuilds(platform); } } @@ -97,10 +91,10 @@ export class PlatformEnvironmentRequirements implements IPlatformEnvironmentRequ } private processCloudBuildsCore(platform: string): Promise { - return this.$nativescriptCloudExtensionService.install(); + return this.$nativeScriptCloudExtensionService.install(); } - private getCloudBuildsMessage(platform: string): string { + private getCloudBuildsMessage(platform: string): string { const cloudCommandName = this.cliCommandToCloudCommandName[this.$commandsService.currentCommandData.commandName]; if (!cloudCommandName) { return `In order to test your application use the $ tns login command to log in with your account and then $ tns cloud build command to build your app in the cloud.`; @@ -136,5 +130,62 @@ export class PlatformEnvironmentRequirements implements IPlatformEnvironmentRequ private fail(message: string): void { this.$errors.fail({ formatStr: message, suppressCommandHelp: true, printOnStdout: true }); } + + private getNonInteractiveConsoleMessage(platform: string) { + return this.$nativeScriptCloudExtensionService.isInstalled() ? + this.buildMultilineMessage([ + `${PlatformEnvironmentRequirements.MISSING_LOCAL_SETUP_MESSAGE} ${PlatformEnvironmentRequirements.CHOOSE_OPTIONS_MESSAGE}`, + PlatformEnvironmentRequirements.RUN_TNS_SETUP_MESSAGE, + this.getCloudBuildsMessage(platform), + this.getEnvVerificationMessage() + ]) : + this.buildMultilineMessage([ + PlatformEnvironmentRequirements.MISSING_LOCAL_AND_CLOUD_SETUP_MESSAGE, + PlatformEnvironmentRequirements.RUN_TNS_SETUP_MESSAGE, + `Run $ tns cloud setup command to install the ${constants.NATIVESCRIPT_CLOUD_EXTENSION_NAME} extension to configure your environment for cloud builds.`, + this.getEnvVerificationMessage() + ]); + } + + private getInteractiveConsoleMessage(platform: string) { + const message = `The ${constants.NATIVESCRIPT_CLOUD_EXTENSION_NAME} extension is installed and you can ${_.lowerFirst(this.getCloudBuildsMessage(platform))}`; + + return this.$nativeScriptCloudExtensionService.isInstalled() ? + this.buildMultilineMessage([ + `${message.bold}`, + `${PlatformEnvironmentRequirements.MISSING_LOCAL_SETUP_MESSAGE} ${PlatformEnvironmentRequirements.CHOOSE_OPTIONS_MESSAGE}`, + `Select "Configure for Local Builds" to run the setup script and automatically configure your environment for local builds.`, + `Select "Skip Step and Configure Manually" to disregard this option and install any required components manually.` + ]) : + this.buildMultilineMessage([ + PlatformEnvironmentRequirements.MISSING_LOCAL_AND_CLOUD_SETUP_MESSAGE, + `Select "Configure for Cloud Builds" to install the ${constants.NATIVESCRIPT_CLOUD_EXTENSION_NAME} extension and automatically configure your environment for cloud builds.`, + `Select "Configure for Local Builds" to run the setup script and automatically configure your environment for local builds.`, + `Select "Configure for Both Local and Cloud Builds" to automatically configure your environment for both options.`, + `Select "Configure for Both Local and Cloud Builds" to automatically configure your environment for both options.` + ]); + } + + private promptForChoice(): Promise { + const choices = this.$nativeScriptCloudExtensionService.isInstalled() ? [ + PlatformEnvironmentRequirements.LOCAL_SETUP_OPTION_NAME, + PlatformEnvironmentRequirements.MANUALLY_SETUP_OPTION_NAME, + ] : [ + PlatformEnvironmentRequirements.CLOUD_SETUP_OPTION_NAME, + PlatformEnvironmentRequirements.LOCAL_SETUP_OPTION_NAME, + PlatformEnvironmentRequirements.BOTH_CLOUD_SETUP_AND_LOCAL_SETUP_OPTION_NAME, + PlatformEnvironmentRequirements.MANUALLY_SETUP_OPTION_NAME, + ]; + + return this.$prompter.promptForChoice(PlatformEnvironmentRequirements.CHOOSE_OPTIONS_MESSAGE, choices); + } + + private getEnvVerificationMessage() { + return `Verify that your environment is configured according to the system requirements described at ${this.$staticConfig.SYS_REQUIREMENTS_LINK}.`; + } + + private buildMultilineMessage(parts: string[]): string { + return parts.join(EOL); + } } $injector.register("platformEnvironmentRequirements", PlatformEnvironmentRequirements); diff --git a/test/services/platform-environment-requirements.ts b/test/services/platform-environment-requirements.ts index 8c52e7635e..abb8bb76ee 100644 --- a/test/services/platform-environment-requirements.ts +++ b/test/services/platform-environment-requirements.ts @@ -2,11 +2,13 @@ import { Yok } from "../../lib/common/yok"; import { PlatformEnvironmentRequirements } from '../../lib/services/platform-environment-requirements'; import * as stubs from "../stubs"; import { assert } from "chai"; +import { EOL } from "os"; const platform = "android"; const cloudBuildsErrorMessage = `In order to test your application use the $ tns login command to log in with your account and then $ tns cloud build command to build your app in the cloud.`; const manuallySetupErrorMessage = `To be able to build for ${platform}, verify that your environment is configured according to the system requirements described at `; -const nonInteractiveConsoleErrorMessage = `You are missing the nativescript-cloud extension and you will not be able to execute cloud builds. Your environment is not configured properly and you will not be able to execute local builds. To continue, choose one of the following options: \nRun $ tns setup command to run the setup script to try to automatically configure your environment for local builds.\nRun $ tns cloud setup command to install the nativescript-cloud extension to configure your environment for cloud builds.\nVerify that your environment is configured according to the system requirements described at `; +const nonInteractiveConsoleMessageWhenExtensionIsNotInstalled = `You are missing the nativescript-cloud extension and you will not be able to execute cloud builds. Your environment is not configured properly and you will not be able to execute local builds. To continue, choose one of the following options: ${EOL}Run $ tns setup command to run the setup script to try to automatically configure your environment for local builds.${EOL}Run $ tns cloud setup command to install the nativescript-cloud extension to configure your environment for cloud builds.${EOL}Verify that your environment is configured according to the system requirements described at `; +const nonInteractiveConsoleMessageWhenExtensionIsInstalled = `Your environment is not configured properly and you will not be able to execute local builds. To continue, choose one of the following options: ${EOL}Run $ tns setup command to run the setup script to try to automatically configure your environment for local builds.${EOL}In order to test your application use the $ tns login command to log in with your account and then $ tns cloud build command to build your app in the cloud.${EOL}Verify that your environment is configured according to the system requirements described at .`; function createTestInjector() { const testInjector = new Yok(); @@ -22,7 +24,7 @@ function createTestInjector() { testInjector.register("prompter", {}); testInjector.register("platformEnvironmentRequirements", PlatformEnvironmentRequirements); testInjector.register("staticConfig", { SYS_REQUIREMENTS_LINK: "" }); - testInjector.register("nativescriptCloudExtensionService", {}); + testInjector.register("nativeScriptCloudExtensionService", {}); return testInjector; } @@ -31,6 +33,37 @@ describe("platformEnvironmentRequirements ", () => { describe("checkRequirements", () => { let testInjector: IInjector = null; let platformEnvironmentRequirements: IPlatformEnvironmentRequirements = null; + let promptForChoiceData: {message: string, choices: string[]}[] = []; + let isExtensionInstallCalled = false; + + function mockDoctorService(data: {canExecuteLocalBuild: boolean, mockSetupScript?: boolean}) { + const doctorService = testInjector.resolve("doctorService"); + doctorService.canExecuteLocalBuild = () => data.canExecuteLocalBuild; + if (data.mockSetupScript) { + doctorService.runSetupScript = () => Promise.resolve(); + } + } + + function mockPrompter(data: {firstCallOptionName: string, secondCallOptionName?: string}) { + const prompter = testInjector.resolve("prompter"); + prompter.promptForChoice = (message: string, choices: string[]) => { + promptForChoiceData.push({message: message, choices: choices}); + + if (promptForChoiceData.length === 1) { + return Promise.resolve(data.firstCallOptionName); + } + + if (data.secondCallOptionName) { + return Promise.resolve(data.secondCallOptionName); + } + }; + } + + function mockNativeScriptCloudExtensionService(data: {isInstalled: boolean}) { + const nativeScriptCloudExtensionService = testInjector.resolve("nativeScriptCloudExtensionService"); + nativeScriptCloudExtensionService.isInstalled = () => data.isInstalled; + nativeScriptCloudExtensionService.install = () => { isExtensionInstallCalled = true; }; + } beforeEach(() => { testInjector = createTestInjector(); @@ -39,156 +72,138 @@ describe("platformEnvironmentRequirements ", () => { process.stdin.isTTY = true; }); - it("should show prompt when environment is not configured", async () => { - const doctorService = testInjector.resolve("doctorService"); - doctorService.canExecuteLocalBuild = () => false; - - let isPromptForChoiceCalled = false; - const prompter = testInjector.resolve("prompter"); - prompter.promptForChoice = () => { - isPromptForChoiceCalled = true; - return PlatformEnvironmentRequirements.CLOUD_BUILDS_OPTION_NAME; - }; + afterEach(() => { + promptForChoiceData = []; + isExtensionInstallCalled = false; + delete process.env.NS_SKIP_ENV_CHECK; + }); - let isInstallExtensionCalled = false; - const nativescriptCloudExtensionService = testInjector.resolve("nativescriptCloudExtensionService"); - nativescriptCloudExtensionService.install = () => {isInstallExtensionCalled = true; }; + it("should return true when environment is configured", async () => { + mockDoctorService({ canExecuteLocalBuild: true }); + const result = await platformEnvironmentRequirements.checkEnvironmentRequirements(platform); + assert.isTrue(result); + assert.isTrue(promptForChoiceData.length === 0); + }); + it("should show prompt when environment is not configured and nativescript-cloud extension is not installed", async () => { + mockDoctorService({ canExecuteLocalBuild: false }); + mockPrompter({ firstCallOptionName: PlatformEnvironmentRequirements.CLOUD_SETUP_OPTION_NAME }); + mockNativeScriptCloudExtensionService({ isInstalled: false }); await assert.isRejected(platformEnvironmentRequirements.checkEnvironmentRequirements(platform)); - assert.isTrue(isPromptForChoiceCalled); - assert.isTrue(isInstallExtensionCalled); + assert.isTrue(promptForChoiceData.length === 1); + assert.isTrue(isExtensionInstallCalled); + assert.deepEqual("To continue, choose one of the following options: ", promptForChoiceData[0].message); + assert.deepEqual(['Configure for Cloud Builds', 'Configure for Local Builds', 'Configure for Both Local and Cloud Builds', 'Skip Step and Configure Manually'], promptForChoiceData[0].choices); }); + it("should show prompt when environment is not configured and nativescript-cloud extension is installed", async () => { + mockDoctorService({ canExecuteLocalBuild: false }); + mockPrompter({ firstCallOptionName: PlatformEnvironmentRequirements.CLOUD_SETUP_OPTION_NAME }); + mockNativeScriptCloudExtensionService({ isInstalled: true }); - it("should return true when environment is configured", async () => { - const doctorService = testInjector.resolve("doctorService"); - doctorService.canExecuteLocalBuild = () => true; + await assert.isRejected(platformEnvironmentRequirements.checkEnvironmentRequirements(platform)); + assert.isTrue(promptForChoiceData.length === 1); + assert.deepEqual("To continue, choose one of the following options: ", promptForChoiceData[0].message); + assert.deepEqual(['Configure for Local Builds', 'Skip Step and Configure Manually'], promptForChoiceData[0].choices); + }); + it("should skip env chech when NS_SKIP_ENV_CHECK environment variable is passed", async() => { + process.env.NS_SKIP_ENV_CHECK = true; - const result = await platformEnvironmentRequirements.checkEnvironmentRequirements(platform); - assert.isTrue(result); + assert.isTrue(await platformEnvironmentRequirements.checkEnvironmentRequirements(platform)); + assert.isFalse(isExtensionInstallCalled); + assert.isTrue(promptForChoiceData.length === 0); }); - describe("when setup script option is selected ", () => { + describe("when local setup option is selected", () => { + beforeEach(() => { + mockPrompter( {firstCallOptionName: PlatformEnvironmentRequirements.LOCAL_SETUP_OPTION_NAME}); + }); + it("should return true when env is configured after executing setup script", async () => { const doctorService = testInjector.resolve("doctorService"); doctorService.canExecuteLocalBuild = () => false; doctorService.runSetupScript = async () => { doctorService.canExecuteLocalBuild = () => true; }; - const prompter = testInjector.resolve("prompter"); - prompter.promptForChoice = () => Promise.resolve(PlatformEnvironmentRequirements.SETUP_SCRIPT_OPTION_NAME); + mockNativeScriptCloudExtensionService({ isInstalled: null }); - const result = await platformEnvironmentRequirements.checkEnvironmentRequirements(platform); - assert.isTrue(result); + assert.isTrue(await platformEnvironmentRequirements.checkEnvironmentRequirements(platform)); }); - it("should prompt for choice when env is not configured after executing setup script", async () => { - const doctorService = testInjector.resolve("doctorService"); - doctorService.canExecuteLocalBuild = () => false; - doctorService.runSetupScript = () => Promise.resolve(); - let isPromptForChoiceCalled = true; - const prompter = testInjector.resolve("prompter"); - prompter.promptForChoice = () => { - if (isPromptForChoiceCalled) { - isPromptForChoiceCalled = false; - return PlatformEnvironmentRequirements.SETUP_SCRIPT_OPTION_NAME; - } - - isPromptForChoiceCalled = true; - return PlatformEnvironmentRequirements.CLOUD_BUILDS_OPTION_NAME; - }; - - let isInstallExtensionCalled = false; - const nativescriptCloudExtensionService = testInjector.resolve("nativescriptCloudExtensionService"); - nativescriptCloudExtensionService.install = () => {isInstallExtensionCalled = true; }; - - await assert.isRejected(platformEnvironmentRequirements.checkEnvironmentRequirements(platform)); - assert.isTrue(isInstallExtensionCalled); - assert.isTrue(isPromptForChoiceCalled); - }); - - describe("and environment is not configured after executing setup script ", () => { - beforeEach(() => { - const doctorService = testInjector.resolve("doctorService"); - doctorService.canExecuteLocalBuild = () => false; - doctorService.runSetupScript = () => Promise.resolve(); - }); - it("should install nativescript-cloud extension when cloud builds option is selected", async () => { - let isPromptForChoiceCalled = true; - const prompter = testInjector.resolve("prompter"); - prompter.promptForChoice = () => { - if (isPromptForChoiceCalled) { - isPromptForChoiceCalled = false; - return PlatformEnvironmentRequirements.SETUP_SCRIPT_OPTION_NAME; - } - - isPromptForChoiceCalled = true; - return PlatformEnvironmentRequirements.CLOUD_BUILDS_OPTION_NAME; - }; - - let isInstallExtensionCalled = false; - const nativescriptCloudExtensionService = testInjector.resolve("nativescriptCloudExtensionService"); - nativescriptCloudExtensionService.install = () => {isInstallExtensionCalled = true; }; - - await assert.isRejected(platformEnvironmentRequirements.checkEnvironmentRequirements(platform), cloudBuildsErrorMessage); - assert.isTrue(isInstallExtensionCalled); - assert.isTrue(isPromptForChoiceCalled); - }); - it("should fail when manually setup option is selected", async () => { - let isPromptForChoiceCalled = true; - const prompter = testInjector.resolve("prompter"); - prompter.promptForChoice = () => { - if (isPromptForChoiceCalled) { - isPromptForChoiceCalled = false; - return PlatformEnvironmentRequirements.SETUP_SCRIPT_OPTION_NAME; - } - - isPromptForChoiceCalled = true; - return PlatformEnvironmentRequirements.MANUALLY_SETUP_OPTION_NAME; - }; + describe("and env is not configured after executing setup script", () => { + it("should setup manually when cloud extension is installed", async () => { + mockDoctorService( { canExecuteLocalBuild: false, mockSetupScript: true }); + mockPrompter({ firstCallOptionName: PlatformEnvironmentRequirements.LOCAL_SETUP_OPTION_NAME, secondCallOptionName: PlatformEnvironmentRequirements.MANUALLY_SETUP_OPTION_NAME }); + mockNativeScriptCloudExtensionService({ isInstalled: true }); await assert.isRejected(platformEnvironmentRequirements.checkEnvironmentRequirements(platform), manuallySetupErrorMessage); - assert.isTrue(isPromptForChoiceCalled); + }); + describe("and cloud extension is not installed", () => { + beforeEach(() => { + mockDoctorService({ canExecuteLocalBuild: false, mockSetupScript: true }); + mockNativeScriptCloudExtensionService({ isInstalled: false }); + }); + it("should list 2 posibile options to select", async () => { + mockPrompter({ firstCallOptionName: PlatformEnvironmentRequirements.LOCAL_SETUP_OPTION_NAME }); + + await platformEnvironmentRequirements.checkEnvironmentRequirements(platform); + assert.deepEqual(promptForChoiceData[1].choices, ['Configure for Cloud Builds', 'Skip Step and Configure Manually']); + }); + it("should install nativescript-cloud extension when 'Configure for Cloud Builds' option is selected", async () => { + mockPrompter({ firstCallOptionName: PlatformEnvironmentRequirements.LOCAL_SETUP_OPTION_NAME, secondCallOptionName: PlatformEnvironmentRequirements.CLOUD_SETUP_OPTION_NAME }); + + await assert.isRejected(platformEnvironmentRequirements.checkEnvironmentRequirements(platform), cloudBuildsErrorMessage); + assert.deepEqual(isExtensionInstallCalled, true); + }); + it("should setup manually when 'Skip Step and Configure Manually' option is selected", async () => { + mockPrompter({ firstCallOptionName: PlatformEnvironmentRequirements.LOCAL_SETUP_OPTION_NAME, secondCallOptionName: PlatformEnvironmentRequirements.MANUALLY_SETUP_OPTION_NAME }); + await assert.isRejected(platformEnvironmentRequirements.checkEnvironmentRequirements(platform), manuallySetupErrorMessage); + }); }); }); }); - describe("when cloud builds option is selected", () => { - it("should install nativescript-cloud extension when cloud builds option is selected", async () => { - const doctorService = testInjector.resolve("doctorService"); - doctorService.canExecuteLocalBuild = () => false; - - const prompter = testInjector.resolve("prompter"); - prompter.promptForChoice = () => Promise.resolve(PlatformEnvironmentRequirements.CLOUD_BUILDS_OPTION_NAME); - - let isInstallExtensionCalled = false; - const nativescriptCloudExtensionService = testInjector.resolve("nativescriptCloudExtensionService"); - nativescriptCloudExtensionService.install = () => {isInstallExtensionCalled = true; }; + describe("when cloud setup option is selected", () => { + beforeEach(() => { + mockDoctorService({ canExecuteLocalBuild: false }); + mockPrompter({ firstCallOptionName: PlatformEnvironmentRequirements.CLOUD_SETUP_OPTION_NAME }); + }); + it("should install nativescript-cloud extension when it is not installed", async () => { + mockNativeScriptCloudExtensionService({ isInstalled: false }); await assert.isRejected(platformEnvironmentRequirements.checkEnvironmentRequirements(platform), cloudBuildsErrorMessage); - assert.isTrue(isInstallExtensionCalled); + assert.isTrue(isExtensionInstallCalled); }); }); describe("when manually setup option is selected", () => { - it("should fail when manually setup option is selected", async () => { - const doctorService = testInjector.resolve("doctorService"); - doctorService.canExecuteLocalBuild = () => false; - - const prompter = testInjector.resolve("prompter"); - prompter.promptForChoice = () => PlatformEnvironmentRequirements.MANUALLY_SETUP_OPTION_NAME; + beforeEach(() => { + mockDoctorService({ canExecuteLocalBuild: false }); + mockPrompter({ firstCallOptionName: PlatformEnvironmentRequirements.MANUALLY_SETUP_OPTION_NAME }); + }); + it("should fail when nativescript-cloud extension is installed", async () => { + mockNativeScriptCloudExtensionService({ isInstalled: true }); + await assert.isRejected(platformEnvironmentRequirements.checkEnvironmentRequirements(platform), manuallySetupErrorMessage); + }); + it("should fail when nativescript-cloud extension is not installed", async () => { + mockNativeScriptCloudExtensionService({ isInstalled: false }); await assert.isRejected(platformEnvironmentRequirements.checkEnvironmentRequirements(platform), manuallySetupErrorMessage); }); }); - describe("when console is not interactive", () => { - it("should fail when console is not interactive", async () => { + describe("when console is non interactive", () => { + beforeEach(() => { process.stdout.isTTY = false; process.stdin.isTTY = false; + mockDoctorService({ canExecuteLocalBuild: false }); + }); - const doctorService = testInjector.resolve("doctorService"); - doctorService.canExecuteLocalBuild = () => false; - - await assert.isRejected(platformEnvironmentRequirements.checkEnvironmentRequirements(platform), nonInteractiveConsoleErrorMessage); + it("should fail when nativescript-cloud extension is installed", async () => { + mockNativeScriptCloudExtensionService({ isInstalled: true }); + await assert.isRejected(platformEnvironmentRequirements.checkEnvironmentRequirements(platform), nonInteractiveConsoleMessageWhenExtensionIsInstalled); + }); + it("should fail when nativescript-cloud extension is not installed", async () => { + mockNativeScriptCloudExtensionService({ isInstalled: false }); + await assert.isRejected(platformEnvironmentRequirements.checkEnvironmentRequirements(platform), nonInteractiveConsoleMessageWhenExtensionIsNotInstalled); }); }); });