diff --git a/lib/bootstrap.ts b/lib/bootstrap.ts index 3e51ddaed5..43b41ed301 100644 --- a/lib/bootstrap.ts +++ b/lib/bootstrap.ts @@ -107,7 +107,6 @@ $injector.requireCommand("apple-login", "./commands/apple-login"); $injector.require("itmsTransporterService", "./services/itmstransporter-service"); $injector.requireCommand("setup|*", "./commands/setup"); -$injector.requireCommand(["setup|cloud", "cloud|setup"], "./commands/setup"); $injector.requirePublic("packageManager", "./package-manager"); $injector.requirePublic("npm", "./node-package-manager"); @@ -198,12 +197,10 @@ $injector.requireCommand("extension|uninstall", "./commands/extensibility/uninst $injector.requirePublicClass("extensibilityService", "./services/extensibility-service"); $injector.require("nodeModulesDependenciesBuilder", "./tools/node-modules/node-modules-dependencies-builder"); -$injector.require("subscriptionService", "./services/subscription-service"); $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.requireCommand("resources|generate|icons", "./commands/generate-assets"); $injector.requireCommand("resources|generate|splashes", "./commands/generate-assets"); diff --git a/lib/commands/debug.ts b/lib/commands/debug.ts index 16e23bc2e2..bca4b58863 100644 --- a/lib/commands/debug.ts +++ b/lib/commands/debug.ts @@ -66,7 +66,7 @@ export class DebugPlatformCommand extends ValidatePlatformCommandBase implements this.$errors.failWithHelp("--release flag is not applicable to this command."); } - const result = await super.canExecuteCommandBase(this.platform, { validateOptions: true, notConfiguredEnvOptions: { hideCloudBuildOption: true, hideSyncToPreviewAppOption: true } }); + const result = await super.canExecuteCommandBase(this.platform, { validateOptions: true, notConfiguredEnvOptions: { hideSyncToPreviewAppOption: true } }); return result; } } diff --git a/lib/commands/post-install.ts b/lib/commands/post-install.ts index 21432f241f..d2fa381ea8 100644 --- a/lib/commands/post-install.ts +++ b/lib/commands/post-install.ts @@ -2,7 +2,6 @@ import { doesCurrentNpmCommandMatch } from "../common/helpers"; export class PostInstallCliCommand implements ICommand { constructor(private $fs: IFileSystem, - private $subscriptionService: ISubscriptionService, private $commandsService: ICommandsService, private $helpService: IHelpService, private $settingsService: ISettingsService, @@ -36,10 +35,6 @@ export class PostInstallCliCommand implements ICommand { await this.$analyticsService.checkConsent(); await this.$commandsService.tryExecuteCommand("autocomplete", []); } - - if (canExecutePostInstallTask) { - await this.$subscriptionService.subscribeForNewsletter(); - } } public async postCommandAction(args: string[]): Promise { diff --git a/lib/commands/setup.ts b/lib/commands/setup.ts index cfddc687a7..66ce5f4136 100644 --- a/lib/commands/setup.ts +++ b/lib/commands/setup.ts @@ -8,14 +8,3 @@ export class SetupCommand implements ICommand { } } $injector.registerCommand("setup|*", SetupCommand); - -export class CloudSetupCommand implements ICommand { - public allowedParameters: ICommandParameter[] = []; - - constructor(private $nativeScriptCloudExtensionService: INativeScriptCloudExtensionService) { } - - public execute(args: string[]): Promise { - return this.$nativeScriptCloudExtensionService.install(); - } -} -$injector.registerCommand(["setup|cloud", "cloud|setup"], CloudSetupCommand); diff --git a/lib/commands/test.ts b/lib/commands/test.ts index f21a1fca27..921c4a6d35 100644 --- a/lib/commands/test.ts +++ b/lib/commands/test.ts @@ -76,8 +76,7 @@ abstract class TestCommandBase { projectDir: this.$projectData.projectDir, options: this.$options, notConfiguredEnvOptions: { - hideSyncToPreviewAppOption: true, - hideCloudBuildOption: true + hideSyncToPreviewAppOption: true } }); diff --git a/lib/common/yok.ts b/lib/common/yok.ts index 87e749103e..6fb58c665a 100644 --- a/lib/common/yok.ts +++ b/lib/common/yok.ts @@ -216,7 +216,7 @@ export class Yok implements IInjector { } else { commandName = defaultCommand ? this.getHierarchicalCommandName(name, defaultCommand) : "help"; // If we'll execute the default command, but it's full name had been written by the user - // for example "appbuilder cloud list", we have to remove the "list" option from the arguments that we'll pass to the command. + // for example "tns run ios", we have to remove the "ios" option from the arguments that we'll pass to the command. if (_.includes(this.hierarchicalCommands[name], CommandsDelimiters.DefaultCommandSymbol + args[0])) { commandArguments = _.tail(args); } else { diff --git a/lib/constants.ts b/lib/constants.ts index e9dcd6366d..ce26706ff5 100644 --- a/lib/constants.ts +++ b/lib/constants.ts @@ -212,8 +212,6 @@ export const enum BuildStates { Incremental = "Incremental" } -export const NATIVESCRIPT_CLOUD_EXTENSION_NAME = "nativescript-cloud"; - /** * Used in ProjectDataService to concatenate the names of the properties inside nativescript key of package.json. */ diff --git a/lib/services/nativescript-cloud-extension-service.ts b/lib/services/nativescript-cloud-extension-service.ts deleted file mode 100644 index 04c99c8bdc..0000000000 --- a/lib/services/nativescript-cloud-extension-service.ts +++ /dev/null @@ -1,35 +0,0 @@ -import * as constants from "../constants"; -import * as semver from "semver"; - -export class NativeScriptCloudExtensionService implements INativeScriptCloudExtensionService { - constructor(private $extensibilityService: IExtensibilityService, - private $logger: ILogger, - private $packageInstallationManager: IPackageInstallationManager) { } - - public install(): Promise { - if (!this.isInstalled()) { - return this.$extensibilityService.installExtension(constants.NATIVESCRIPT_CLOUD_EXTENSION_NAME); - } - - this.$logger.info(`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.$packageInstallationManager.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); diff --git a/lib/services/platform-environment-requirements.ts b/lib/services/platform-environment-requirements.ts index 2d30286da9..f1867bfe17 100644 --- a/lib/services/platform-environment-requirements.ts +++ b/lib/services/platform-environment-requirements.ts @@ -1,13 +1,11 @@ -import { NATIVESCRIPT_CLOUD_EXTENSION_NAME, TrackActionNames } from "../constants"; +import { TrackActionNames } from "../constants"; import { isInteractive, hook } from "../common/helpers"; import { EOL } from "os"; export class PlatformEnvironmentRequirements implements IPlatformEnvironmentRequirements { - constructor(private $commandsService: ICommandsService, - private $doctorService: IDoctorService, + constructor(private $doctorService: IDoctorService, private $errors: IErrors, private $logger: ILogger, - private $nativeScriptCloudExtensionService: INativeScriptCloudExtensionService, private $prompter: IPrompter, private $staticConfig: IStaticConfig, private $analyticsService: IAnalyticsService, @@ -18,27 +16,16 @@ export class PlatformEnvironmentRequirements implements IPlatformEnvironmentRequ return this.$injector.resolve("previewAppController"); } - public static CLOUD_SETUP_OPTION_NAME = "Configure for Cloud Builds"; public static LOCAL_SETUP_OPTION_NAME = "Configure for Local Builds"; - public static TRY_CLOUD_OPERATION_OPTION_NAME = "Try Cloud Operation"; public static SYNC_TO_PREVIEW_APP_OPTION_NAME = "Sync to Playground"; public static MANUALLY_SETUP_OPTION_NAME = "Skip Step and Configure Manually"; - 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. Please consult our setup instructions here 'https://docs.nativescript.org/start/quick-setup'.`; 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 ${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 MISSING_LOCAL_BUT_CLOUD_SETUP_MESSAGE = `You have ${NATIVESCRIPT_CLOUD_EXTENSION_NAME} extension installed, so you can execute cloud builds, but ${_.lowerFirst(PlatformEnvironmentRequirements.MISSING_LOCAL_SETUP_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 static SYNC_TO_PREVIEW_APP_MESSAGE = `Select "Sync to Playground" to enjoy NativeScript without any local setup. All you need is a couple of companion apps installed on your devices.`; private static RUN_PREVIEW_COMMAND_MESSAGE = `Run $ tns preview command to enjoy NativeScript without any local setup.`; - private cliCommandToCloudCommandName: IStringDictionary = { - "build": "tns cloud build", - "run": "tns cloud run", - "deploy": "tns cloud deploy" - }; - @hook("checkEnvironment") public async checkEnvironmentRequirements(input: ICheckEnvironmentRequirementsInput): Promise { const { platform, projectDir, runtimeVersion } = input; @@ -75,7 +62,6 @@ export class PlatformEnvironmentRequirements implements IPlatformEnvironmentRequ selectedOption = await this.promptForChoice({ infoMessage, choices }); - await this.processCloudBuildsIfNeeded(selectedOption, platform); this.processManuallySetupIfNeeded(selectedOption, platform); await this.processSyncToPreviewAppIfNeeded(selectedOption, projectDir, options); @@ -89,44 +75,8 @@ export class PlatformEnvironmentRequirements implements IPlatformEnvironmentRequ }; } - if (this.$nativeScriptCloudExtensionService.isInstalled()) { - const option = await this.promptForChoice({ - infoMessage: PlatformEnvironmentRequirements.NOT_CONFIGURED_ENV_AFTER_SETUP_SCRIPT_MESSAGE, - choices: [ - PlatformEnvironmentRequirements.TRY_CLOUD_OPERATION_OPTION_NAME, - PlatformEnvironmentRequirements.MANUALLY_SETUP_OPTION_NAME - ] - }); - - this.processTryCloudSetupIfNeeded(option, platform); - this.processManuallySetupIfNeeded(option, platform); - } else { - const option = await this.promptForChoice({ - infoMessage: PlatformEnvironmentRequirements.NOT_CONFIGURED_ENV_AFTER_SETUP_SCRIPT_MESSAGE, - choices: [ - PlatformEnvironmentRequirements.CLOUD_SETUP_OPTION_NAME, - PlatformEnvironmentRequirements.MANUALLY_SETUP_OPTION_NAME - ] - }); - - await this.processCloudBuildsIfNeeded(option, platform); - this.processManuallySetupIfNeeded(option, platform); - } + this.fail(PlatformEnvironmentRequirements.NOT_CONFIGURED_ENV_AFTER_SETUP_SCRIPT_MESSAGE); } - - if (selectedOption === PlatformEnvironmentRequirements.BOTH_CLOUD_SETUP_AND_LOCAL_SETUP_OPTION_NAME) { - await this.processBothCloudBuildsAndSetupScript(); - if (await this.$doctorService.canExecuteLocalBuild({ platform, projectDir, runtimeVersion, forceCheck: input.forceCheck })) { - return { - canExecute: true, - selectedOption - }; - } - - this.processManuallySetup(platform); - } - - this.processTryCloudSetupIfNeeded(selectedOption, platform); } return { @@ -135,40 +85,6 @@ export class PlatformEnvironmentRequirements implements IPlatformEnvironmentRequ }; } - private async processCloudBuildsIfNeeded(selectedOption: string, platform?: string): Promise { - if (selectedOption === PlatformEnvironmentRequirements.CLOUD_SETUP_OPTION_NAME) { - await this.processCloudBuilds(platform); - } - } - - private async processCloudBuilds(platform: string): Promise { - await this.processCloudBuildsCore(); - this.fail(this.getCloudBuildsMessage(platform)); - } - - private processCloudBuildsCore(): Promise { - return this.$nativeScriptCloudExtensionService.install(); - } - - 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.`; - } - - if (!platform) { - return `Use the $ tns login command to log in with your account and then $ ${cloudCommandName.toLowerCase()} command.`; - } - - return `Use the $ tns login command to log in with your account and then $ ${cloudCommandName.toLowerCase()} ${platform.toLowerCase()} command.`; - } - - private processTryCloudSetupIfNeeded(selectedOption: string, platform?: string) { - if (selectedOption === PlatformEnvironmentRequirements.TRY_CLOUD_OPERATION_OPTION_NAME) { - this.fail(this.getCloudBuildsMessage(platform)); - } - } - private processManuallySetupIfNeeded(selectedOption: string, platform?: string) { if (selectedOption === PlatformEnvironmentRequirements.MANUALLY_SETUP_OPTION_NAME) { this.processManuallySetup(platform); @@ -195,52 +111,25 @@ export class PlatformEnvironmentRequirements implements IPlatformEnvironmentRequ this.fail(`To be able to ${platform ? `build for ${platform}` : 'build'}, verify that your environment is configured according to the system requirements described at ${this.$staticConfig.SYS_REQUIREMENTS_LINK}. If you have any questions, check Stack Overflow: 'https://stackoverflow.com/questions/tagged/nativescript' and our public Slack channel: 'https://nativescriptcommunity.slack.com/'`); } - private async processBothCloudBuildsAndSetupScript(): Promise { - try { - await this.processCloudBuildsCore(); - } catch (e) { - this.$logger.trace(`Error while installing ${NATIVESCRIPT_CLOUD_EXTENSION_NAME} extension. ${e.message}.`); - } - - await this.$doctorService.runSetupScript(); - } - private fail(message: string): void { this.$errors.fail({ formatStr: message, printOnStdout: true }); } private getNonInteractiveConsoleMessage(platform: string) { - return this.$nativeScriptCloudExtensionService.isInstalled() ? - this.buildMultilineMessage([ - `${PlatformEnvironmentRequirements.MISSING_LOCAL_SETUP_MESSAGE} ${PlatformEnvironmentRequirements.CHOOSE_OPTIONS_MESSAGE}`, - PlatformEnvironmentRequirements.RUN_PREVIEW_COMMAND_MESSAGE, - PlatformEnvironmentRequirements.RUN_TNS_SETUP_MESSAGE, - this.getCloudBuildsMessage(platform), - this.getEnvVerificationMessage() - ]) : - this.buildMultilineMessage([ - PlatformEnvironmentRequirements.MISSING_LOCAL_AND_CLOUD_SETUP_MESSAGE, - PlatformEnvironmentRequirements.RUN_PREVIEW_COMMAND_MESSAGE, - PlatformEnvironmentRequirements.RUN_TNS_SETUP_MESSAGE, - `Run $ tns cloud setup command to install the ${NATIVESCRIPT_CLOUD_EXTENSION_NAME} extension to configure your environment for cloud builds.`, - this.getEnvVerificationMessage() - ]); + return this.buildMultilineMessage([ + `${PlatformEnvironmentRequirements.MISSING_LOCAL_SETUP_MESSAGE} ${PlatformEnvironmentRequirements.CHOOSE_OPTIONS_MESSAGE}`, + PlatformEnvironmentRequirements.RUN_PREVIEW_COMMAND_MESSAGE, + PlatformEnvironmentRequirements.RUN_TNS_SETUP_MESSAGE, + this.getEnvVerificationMessage() + ]); } private getInteractiveConsoleMessage(options: INotConfiguredEnvOptions) { - const isNativeScriptCloudExtensionInstalled = this.$nativeScriptCloudExtensionService.isInstalled(); - const message = isNativeScriptCloudExtensionInstalled ? - `${PlatformEnvironmentRequirements.MISSING_LOCAL_BUT_CLOUD_SETUP_MESSAGE} ${PlatformEnvironmentRequirements.CHOOSE_OPTIONS_MESSAGE}` : - PlatformEnvironmentRequirements.MISSING_LOCAL_AND_CLOUD_SETUP_MESSAGE; - const choices = isNativeScriptCloudExtensionInstalled ? [ + const message = PlatformEnvironmentRequirements.MISSING_LOCAL_SETUP_MESSAGE; + const choices = [ `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.` - ] : [ - `Select "Configure for Cloud Builds" to install the ${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.` - ]; + ]; if (!options.hideSyncToPreviewAppOption) { choices.unshift(PlatformEnvironmentRequirements.SYNC_TO_PREVIEW_APP_MESSAGE); @@ -279,21 +168,9 @@ export class PlatformEnvironmentRequirements implements IPlatformEnvironmentRequ private getChoices(options: INotConfiguredEnvOptions): string[] { const choices: string[] = []; - if (this.$nativeScriptCloudExtensionService.isInstalled()) { - choices.push(...[PlatformEnvironmentRequirements.LOCAL_SETUP_OPTION_NAME, - PlatformEnvironmentRequirements.MANUALLY_SETUP_OPTION_NAME]); - if (!options.hideCloudBuildOption) { - choices.unshift(PlatformEnvironmentRequirements.TRY_CLOUD_OPERATION_OPTION_NAME); - } - } else { - choices.push(...[ - PlatformEnvironmentRequirements.CLOUD_SETUP_OPTION_NAME, - PlatformEnvironmentRequirements.LOCAL_SETUP_OPTION_NAME, - PlatformEnvironmentRequirements.BOTH_CLOUD_SETUP_AND_LOCAL_SETUP_OPTION_NAME, - PlatformEnvironmentRequirements.MANUALLY_SETUP_OPTION_NAME, - ]); - } + choices.push(...[PlatformEnvironmentRequirements.LOCAL_SETUP_OPTION_NAME, + PlatformEnvironmentRequirements.MANUALLY_SETUP_OPTION_NAME]); if (!options.hideSyncToPreviewAppOption) { choices.unshift(PlatformEnvironmentRequirements.SYNC_TO_PREVIEW_APP_OPTION_NAME); diff --git a/lib/services/subscription-service.ts b/lib/services/subscription-service.ts deleted file mode 100644 index 6852cccef5..0000000000 --- a/lib/services/subscription-service.ts +++ /dev/null @@ -1,77 +0,0 @@ -import * as emailValidator from "email-validator"; -import * as queryString from "querystring"; -import * as helpers from "../common/helpers"; -import * as prompt from "inquirer"; -import { SubscribeForNewsletterMessages } from "../constants"; - -export class SubscriptionService implements ISubscriptionService { - constructor(private $httpClient: Server.IHttpClient, - private $prompter: IPrompter, - private $userSettingsService: IUserSettingsService, - private $logger: ILogger) { - } - - public async subscribeForNewsletter(): Promise { - if (await this.shouldAskForEmail()) { - this.$logger.printMarkdown(SubscribeForNewsletterMessages.ReviewPrivacyPolicyMsg); - this.$logger.printMarkdown(SubscribeForNewsletterMessages.AgreeToReceiveEmailMsg); - - const email = await this.getEmail(SubscribeForNewsletterMessages.PromptMsg); - await this.$userSettingsService.saveSetting("EMAIL_REGISTERED", true); - await this.sendEmail(email); - } - } - - /** - * Checks whether we should ask the current user if they want to subscribe to NativeScript newsletter. - * NOTE: This method is protected, not private, only because of our unit tests. - * @returns {Promise} - */ - protected async shouldAskForEmail(): Promise { - return helpers.isInteractive() && process.env.CLI_NOPROMPT !== "1" && !(await this.$userSettingsService.getSettingValue("EMAIL_REGISTERED")); - } - - private async getEmail(message: string, options?: IPrompterOptions): Promise { - const schema: prompt.Question = { - message, - type: "input", - name: "inputEmail", - validate: (value: any) => { - if (value === "" || emailValidator.validate(value)) { - return true; - } - - return "Please provide a valid e-mail or simply leave it blank."; - } - }; - - const result = await this.$prompter.get([schema]); - return result.inputEmail; - } - - private async sendEmail(email: string): Promise { - if (email) { - const postData = queryString.stringify({ - 'elqFormName': "NativeScript_IncludeinEmail", - 'elqSiteID': '1325', - 'emailAddress': email, - 'elqCookieWrite': '0' - }); - - const options = { - url: 'https://s1325.t.eloqua.com/e/f2', - method: 'POST', - headers: { - 'Content-Type': 'application/x-www-form-urlencoded', - 'Content-Length': postData.length - }, - body: postData - }; - - await this.$httpClient.httpRequest(options); - } - } - -} - -$injector.register("subscriptionService", SubscriptionService); diff --git a/test/commands/post-install.ts b/test/commands/post-install.ts index 39640112ea..15277f4600 100644 --- a/test/commands/post-install.ts +++ b/test/commands/post-install.ts @@ -9,10 +9,6 @@ const createTestInjector = (): IInjector => { setCurrentUserAsOwner: async (path: string, owner: string): Promise => undefined }); - testInjector.register("subscriptionService", { - subscribeForNewsletter: async (): Promise => undefined - }); - testInjector.register("staticConfig", {}); testInjector.register("commandsService", { @@ -57,26 +53,8 @@ describe("post-install command", () => { process.env.SUDO_USER = originalSudoUser; }); - it("calls subscriptionService.subscribeForNewsletter method", async () => { - const testInjector = createTestInjector(); - const subscriptionService = testInjector.resolve("subscriptionService"); - let isSubscribeForNewsletterCalled = false; - subscriptionService.subscribeForNewsletter = async (): Promise => { - isSubscribeForNewsletterCalled = true; - }; - const postInstallCommand = testInjector.resolveCommand("post-install-cli"); - - await postInstallCommand.execute([]); - assert.isTrue(isSubscribeForNewsletterCalled, "post-install-cli command must call subscriptionService.subscribeForNewsletter"); - }); - const verifyResult = async (opts: { shouldCallMethod: boolean }): Promise => { const testInjector = createTestInjector(); - const subscriptionService = testInjector.resolve("subscriptionService"); - let isSubscribeForNewsletterCalled = false; - subscriptionService.subscribeForNewsletter = async (): Promise => { - isSubscribeForNewsletterCalled = true; - }; const helpService = testInjector.resolve("helpService"); let isGenerateHtmlPagesCalled = false; @@ -104,7 +82,6 @@ describe("post-install command", () => { const hasNotInMsg = opts.shouldCallMethod ? "" : "NOT"; - assert.equal(isSubscribeForNewsletterCalled, opts.shouldCallMethod, `post-install-cli command must ${hasNotInMsg} call subscriptionService.subscribeForNewsletter`); assert.equal(isGenerateHtmlPagesCalled, opts.shouldCallMethod, `post-install-cli command must ${hasNotInMsg} call helpService.generateHtmlPages`); assert.equal(isCheckConsentCalled, opts.shouldCallMethod, `post-install-cli command must ${hasNotInMsg} call analyticsService.checkConsent`); assert.equal(isTryExecuteCommandCalled, opts.shouldCallMethod, `post-install-cli command must ${hasNotInMsg} call commandsService.tryExecuteCommand`); diff --git a/test/services/platform-environment-requirements.ts b/test/services/platform-environment-requirements.ts index cad355614f..d398b9a976 100644 --- a/test/services/platform-environment-requirements.ts +++ b/test/services/platform-environment-requirements.ts @@ -7,10 +7,8 @@ const helpers = require("../../lib/common/helpers"); const originalIsInteractive = helpers.isInteractive; 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 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 preview command to enjoy NativeScript without any local setup.${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 preview command to enjoy NativeScript without any local setup.${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 .`; +const nonInteractiveConsoleMessage = `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 preview command to enjoy NativeScript without any local setup.${EOL}Run $ tns setup command to run the setup script to try to automatically configure your environment for local builds.${EOL}Verify that your environment is configured according to the system requirements described at .`; function createTestInjector() { const testInjector = new Yok(); @@ -30,7 +28,6 @@ function createTestInjector() { testInjector.register("prompter", {}); testInjector.register("platformEnvironmentRequirements", PlatformEnvironmentRequirements); testInjector.register("staticConfig", { SYS_REQUIREMENTS_LINK: "" }); - testInjector.register("nativeScriptCloudExtensionService", {}); testInjector.register("previewQrCodeService", {}); return testInjector; @@ -49,7 +46,6 @@ describe("platformEnvironmentRequirements ", () => { 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"); @@ -74,12 +70,6 @@ describe("platformEnvironmentRequirements ", () => { }; } - function mockNativeScriptCloudExtensionService(data: { isInstalled: boolean }) { - const nativeScriptCloudExtensionService = testInjector.resolve("nativeScriptCloudExtensionService"); - nativeScriptCloudExtensionService.isInstalled = () => data.isInstalled; - nativeScriptCloudExtensionService.install = () => { isExtensionInstallCalled = true; }; - } - beforeEach(() => { testInjector = createTestInjector(); platformEnvironmentRequirements = testInjector.resolve("platformEnvironmentRequirements"); @@ -89,7 +79,6 @@ describe("platformEnvironmentRequirements ", () => { afterEach(() => { promptForChoiceData = []; - isExtensionInstallCalled = false; delete process.env.NS_SKIP_ENV_CHECK; }); @@ -99,48 +88,24 @@ describe("platformEnvironmentRequirements ", () => { assert.isTrue(result.canExecute); assert.isTrue(promptForChoiceData.length === 0); }); - it("should show prompt when environment is not configured and nativescript-cloud extension is not installed", async () => { + it("should show prompt when environment is not configured", async () => { mockDoctorService({ canExecuteLocalBuild: false }); - mockPrompter({ firstCallOptionName: PlatformEnvironmentRequirements.CLOUD_SETUP_OPTION_NAME }); - mockNativeScriptCloudExtensionService({ isInstalled: false }); + mockPrompter({ firstCallOptionName: PlatformEnvironmentRequirements.LOCAL_SETUP_OPTION_NAME }); await assert.isRejected(platformEnvironmentRequirements.checkEnvironmentRequirements({ platform })); assert.isTrue(promptForChoiceData.length === 1); - assert.isTrue(isExtensionInstallCalled); assert.deepEqual("To continue, choose one of the following options: ", promptForChoiceData[0].message); - assert.deepEqual(['Sync to Playground', 'Configure for Cloud Builds', 'Configure for Local Builds', 'Configure for Both Local and Cloud Builds', 'Skip Step and Configure Manually'], promptForChoiceData[0].choices); + assert.deepEqual(['Sync to Playground', 'Configure for Local 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 }); - 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(['Sync to Playground', 'Try Cloud Operation', 'Configure for Local Builds', 'Skip Step and Configure Manually'], promptForChoiceData[0].choices); - }); it("should not show 'Sync to Playground' option when hideSyncToPreviewAppOption is provided", async () => { mockDoctorService({ canExecuteLocalBuild: false }); - mockPrompter({ firstCallOptionName: PlatformEnvironmentRequirements.CLOUD_SETUP_OPTION_NAME }); - mockNativeScriptCloudExtensionService({ isInstalled: true }); + mockPrompter({ firstCallOptionName: PlatformEnvironmentRequirements.LOCAL_SETUP_OPTION_NAME }); await assert.isRejected(platformEnvironmentRequirements.checkEnvironmentRequirements({ platform, notConfiguredEnvOptions: { hideSyncToPreviewAppOption: true } })); assert.isTrue(promptForChoiceData.length === 1); - assert.isTrue(isExtensionInstallCalled); - assert.deepEqual("To continue, choose one of the following options: ", promptForChoiceData[0].message); - assert.deepEqual(['Try Cloud Operation', 'Configure for Local Builds', 'Skip Step and Configure Manually'], promptForChoiceData[0].choices); - }); - it("should not show 'Try Cloud Build' option when hideCloudBuildOption is provided", async () => { - mockDoctorService({ canExecuteLocalBuild: false }); - mockPrompter({ firstCallOptionName: PlatformEnvironmentRequirements.CLOUD_SETUP_OPTION_NAME }); - mockNativeScriptCloudExtensionService({ isInstalled: true }); - - await assert.isRejected(platformEnvironmentRequirements.checkEnvironmentRequirements({ platform, notConfiguredEnvOptions: { hideCloudBuildOption: true } })); - assert.isTrue(promptForChoiceData.length === 1); - assert.isTrue(isExtensionInstallCalled); assert.deepEqual("To continue, choose one of the following options: ", promptForChoiceData[0].message); - assert.deepEqual(['Sync to Playground', 'Configure for Local Builds', 'Skip Step and Configure Manually'], promptForChoiceData[0].choices); + assert.deepEqual(['Configure for Local Builds', 'Skip Step and Configure Manually'], promptForChoiceData[0].choices); }); it("should skip env check when NS_SKIP_ENV_CHECK environment variable is passed", async () => { (process.env).NS_SKIP_ENV_CHECK = true; @@ -148,7 +113,6 @@ describe("platformEnvironmentRequirements ", () => { const output = await platformEnvironmentRequirements.checkEnvironmentRequirements({ platform }); assert.isTrue(output.canExecute); - assert.isFalse(isExtensionInstallCalled); assert.isTrue(promptForChoiceData.length === 0); }); @@ -162,55 +126,22 @@ describe("platformEnvironmentRequirements ", () => { doctorService.canExecuteLocalBuild = () => false; doctorService.runSetupScript = async () => { doctorService.canExecuteLocalBuild = () => true; }; - mockNativeScriptCloudExtensionService({ isInstalled: null }); - const output = await platformEnvironmentRequirements.checkEnvironmentRequirements({ platform }); assert.isTrue(output.canExecute); }); describe("and env is not configured after executing setup script", () => { - it("should setup manually when cloud extension is installed", async () => { + beforeEach(() => { 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); - }); - 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 setup option is selected", () => { - beforeEach(() => { - mockDoctorService({ canExecuteLocalBuild: false }); - mockPrompter({ firstCallOptionName: PlatformEnvironmentRequirements.CLOUD_SETUP_OPTION_NAME }); - }); + it("should list 2 posibile options to select", async () => { + mockPrompter({ firstCallOptionName: PlatformEnvironmentRequirements.LOCAL_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(isExtensionInstallCalled); + await assert.isRejected(platformEnvironmentRequirements.checkEnvironmentRequirements({ platform }), + "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." + + " Please consult our setup instructions here 'https://docs.nativescript.org/start/quick-setup"); + }); }); }); @@ -220,12 +151,7 @@ describe("platformEnvironmentRequirements ", () => { 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 }); + it("should fail", async () => { await assert.isRejected(platformEnvironmentRequirements.checkEnvironmentRequirements({ platform }), manuallySetupErrorMessage); }); }); @@ -236,13 +162,8 @@ describe("platformEnvironmentRequirements ", () => { mockDoctorService({ canExecuteLocalBuild: false }); }); - 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); + it("should fail", async () => { + await assert.isRejected(platformEnvironmentRequirements.checkEnvironmentRequirements({ platform }), nonInteractiveConsoleMessage); }); }); }); diff --git a/test/services/subscription-service.ts b/test/services/subscription-service.ts deleted file mode 100644 index 612eabdba8..0000000000 --- a/test/services/subscription-service.ts +++ /dev/null @@ -1,290 +0,0 @@ -import { Yok } from "../../lib/common/yok"; -import { assert } from "chai"; -import { SubscriptionService } from "../../lib/services/subscription-service"; -import { LoggerStub } from "../stubs"; -import { stringify } from "querystring"; -import { SubscribeForNewsletterMessages } from "../../lib/constants"; -import * as prompt from "inquirer"; -const helpers = require("../../lib/common/helpers"); - -interface IValidateTestData { - name: string; - valuePassedToValidate: string; - expectedResult: boolean | string; -} - -const createTestInjector = (): IInjector => { - const testInjector = new Yok(); - testInjector.register("logger", LoggerStub); - - testInjector.register("userSettingsService", { - getSettingValue: async (value: string) => true, - saveSetting: async (key: string, value: any): Promise => undefined - }); - - testInjector.register("prompter", { - get: async (schemas: prompt.Question[]): Promise => ({ - inputEmail: "SomeEmail" - }) - }); - - testInjector.register("httpClient", { - httpRequest: async (options: any, proxySettings?: IProxySettings): Promise => undefined - }); - - return testInjector; -}; - -class SubscriptionServiceTester extends SubscriptionService { - public shouldAskForEmailResult: boolean = null; - - constructor($httpClient: Server.IHttpClient, - $prompter: IPrompter, - $userSettingsService: IUserSettingsService, - $logger: ILogger) { - super($httpClient, $prompter, $userSettingsService, $logger); - } - - public async shouldAskForEmail(): Promise { - if (this.shouldAskForEmailResult !== null) { - return this.shouldAskForEmailResult; - } - - return super.shouldAskForEmail(); - } -} - -describe("subscriptionService", () => { - describe("shouldAskForEmail", () => { - describe("returns false", () => { - it("when terminal is not interactive", async () => { - const originalIsInteractive = helpers.isInteractive; - helpers.isInteractive = () => false; - - const testInjector = createTestInjector(); - const subscriptionService = testInjector.resolve(SubscriptionServiceTester); - const shouldAskForEmailResult = await subscriptionService.shouldAskForEmail(); - - helpers.isInteractive = originalIsInteractive; - - assert.isFalse(shouldAskForEmailResult, "When console is not interactive, we should not ask for email."); - }); - - it("when environment variable CLI_NOPROMPT is set to 1", async () => { - const originalIsInteractive = helpers.isInteractive; - helpers.isInteractive = () => true; - - const originalCliNoPrompt = process.env.CLI_NOPROMPT; - process.env.CLI_NOPROMPT = "1"; - - const testInjector = createTestInjector(); - const subscriptionService = testInjector.resolve(SubscriptionServiceTester); - const shouldAskForEmailResult = await subscriptionService.shouldAskForEmail(); - - helpers.isInteractive = originalIsInteractive; - process.env.CLI_NOPROMPT = originalCliNoPrompt; - - assert.isFalse(shouldAskForEmailResult, "When the environment variable CLI_NOPROMPT is set to 1, we should not ask for email."); - }); - - it("when user had already been asked for mail", async () => { - const originalIsInteractive = helpers.isInteractive; - helpers.isInteractive = () => true; - - const originalCliNoPrompt = process.env.CLI_NOPROMPT; - process.env.CLI_NOPROMPT = "random_value"; - - const testInjector = createTestInjector(); - const subscriptionService = testInjector.resolve(SubscriptionServiceTester); - const shouldAskForEmailResult = await subscriptionService.shouldAskForEmail(); - - helpers.isInteractive = originalIsInteractive; - process.env.CLI_NOPROMPT = originalCliNoPrompt; - - assert.isFalse(shouldAskForEmailResult, "When the user had already been asked for mail, we should not ask for email."); - }); - }); - - describe("returns true", () => { - it("when console is interactive, CLI_NOPROMPT is not 1 and we have not asked user before that", async () => { - const originalIsInteractive = helpers.isInteractive; - helpers.isInteractive = () => true; - - const originalCliNoPrompt = process.env.CLI_NOPROMPT; - process.env.CLI_NOPROMPT = "random_value"; - - const testInjector = createTestInjector(); - const userSettingsService = testInjector.resolve("userSettingsService"); - userSettingsService.getSettingValue = async (settingName: string): Promise => false; - - const subscriptionService = testInjector.resolve(SubscriptionServiceTester); - const shouldAskForEmailResult = await subscriptionService.shouldAskForEmail(); - - helpers.isInteractive = originalIsInteractive; - process.env.CLI_NOPROMPT = originalCliNoPrompt; - - assert.isTrue(shouldAskForEmailResult, "We should ask the user for email when console is interactiv, CLI_NOPROMPT is not 1 and we have never asked the user before."); - }); - }); - }); - - describe("subscribeForNewsletter", () => { - it("does nothing when shouldAskForEmail returns false", async () => { - const testInjector = createTestInjector(); - const subscriptionService = testInjector.resolve(SubscriptionServiceTester); - subscriptionService.shouldAskForEmailResult = false; - const logger = testInjector.resolve("logger"); - let loggerOutput = ""; - logger.info = (...args: string[]): void => { - loggerOutput += args.join(" "); - }; - - await subscriptionService.subscribeForNewsletter(); - assert.deepEqual(loggerOutput, ""); - }); - - it("shows message that asks for e-mail address", async () => { - const testInjector = createTestInjector(); - const subscriptionService = testInjector.resolve(SubscriptionServiceTester); - subscriptionService.shouldAskForEmailResult = true; - - const logger = testInjector.resolve("logger"); - let loggerOutput = ""; - - logger.info = (...args: string[]): void => { - loggerOutput += args.join(" "); - }; - - logger.printMarkdown = (message: string): void => { - loggerOutput += message; - }; - - await subscriptionService.subscribeForNewsletter(); - - assert.equal(loggerOutput, `${SubscribeForNewsletterMessages.ReviewPrivacyPolicyMsg}${SubscribeForNewsletterMessages.AgreeToReceiveEmailMsg}`); - }); - - const expectedMessageInPrompter = SubscribeForNewsletterMessages.PromptMsg; - it(`calls prompter with specific message - ${expectedMessageInPrompter}`, async () => { - const testInjector = createTestInjector(); - const subscriptionService = testInjector.resolve(SubscriptionServiceTester); - subscriptionService.shouldAskForEmailResult = true; - const prompter = testInjector.resolve("prompter"); - let schemasPassedToPromter: prompt.Question[] = null; - prompter.get = async (schemas: prompt.Question[]): Promise => { - schemasPassedToPromter = schemas; - - return { inputEmail: "SomeEmail" }; - }; - - await subscriptionService.subscribeForNewsletter(); - - assert.isNotNull(schemasPassedToPromter, "Prompter should have been called."); - assert.equal(schemasPassedToPromter.length, 1, "A single schema should have been passed to schemas."); - - assert.equal(schemasPassedToPromter[0].message, expectedMessageInPrompter); - }); - - describe("calls prompter with validate method", () => { - const testData: IValidateTestData[] = [ - { - name: "returning true when empty string is passed", - valuePassedToValidate: "", - expectedResult: true - }, - { - name: "returning true when passing valid email", - valuePassedToValidate: "abc@def.gh", - expectedResult: true - }, - { - name: "returning specific message when invalid email is passed", - valuePassedToValidate: "abcdef.gh", - expectedResult: "Please provide a valid e-mail or simply leave it blank." - } - ]; - - _.each(testData, testCase => { - it(testCase.name, async () => { - const testInjector = createTestInjector(); - const subscriptionService = testInjector.resolve(SubscriptionServiceTester); - subscriptionService.shouldAskForEmailResult = true; - const prompter = testInjector.resolve("prompter"); - let schemasPassedToPromter: prompt.Question[] = null; - prompter.get = async (schemas: prompt.Question[]): Promise => { - schemasPassedToPromter = schemas; - return { inputEmail: "SomeEmail" }; - }; - - await subscriptionService.subscribeForNewsletter(); - - const schemaPassedToPromter = schemasPassedToPromter[0]; - const resultOfValidateMethod = schemaPassedToPromter.validate(testCase.valuePassedToValidate); - assert.equal(resultOfValidateMethod, testCase.expectedResult); - }); - }); - - }); - - const emailRegisteredKey = "EMAIL_REGISTERED"; - it(`persists ${emailRegisteredKey} setting with value true in user settings`, async () => { - const testInjector = createTestInjector(); - const subscriptionService = testInjector.resolve(SubscriptionServiceTester); - subscriptionService.shouldAskForEmailResult = true; - const userSettingsService = testInjector.resolve("userSettingsService"); - let keyPassedToSaveSetting: string = null; - let valuePassedToSaveSetting: boolean = null; - userSettingsService.saveSetting = async (key: string, value: any): Promise => { - keyPassedToSaveSetting = key; - valuePassedToSaveSetting = value; - }; - - await subscriptionService.subscribeForNewsletter(); - - assert.deepEqual(keyPassedToSaveSetting, emailRegisteredKey); - assert.deepEqual(valuePassedToSaveSetting, true); - }); - - it("calls httpRequest with concrete data", async () => { - const email = "abc@def.gh"; - - const postData = stringify({ - 'elqFormName': "NativeScript_IncludeinEmail", - 'elqSiteID': '1325', - 'emailAddress': email, - 'elqCookieWrite': '0' - }); - - const expectedOptions = { - url: 'https://s1325.t.eloqua.com/e/f2', - method: 'POST', - headers: { - 'Content-Type': 'application/x-www-form-urlencoded', - 'Content-Length': postData.length - }, - body: postData - }; - - const testInjector = createTestInjector(); - - const prompter = testInjector.resolve("prompter"); - prompter.get = async (schemas: prompt.Question[]): Promise => { - return { inputEmail: email }; - }; - - const httpClient = testInjector.resolve("httpClient"); - let optionsPassedToHttpRequest: any = null; - httpClient.httpRequest = async (options: any, proxySettings?: IProxySettings): Promise => { - optionsPassedToHttpRequest = options; - return null; - }; - - const subscriptionService = testInjector.resolve(SubscriptionServiceTester); - subscriptionService.shouldAskForEmailResult = true; - - await subscriptionService.subscribeForNewsletter(); - - assert.deepEqual(optionsPassedToHttpRequest, expectedOptions); - }); - }); -});