From 973f1d9725fa50f1619811b580c4dc5539e672a9 Mon Sep 17 00:00:00 2001 From: Renovate Bot Date: Sat, 16 Jan 2021 17:21:41 +0000 Subject: [PATCH 01/22] chore(deps): update endbug/version-check action to v2 --- .github/workflows/check-package-version.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/check-package-version.yml b/.github/workflows/check-package-version.yml index b203ee4..005efff 100644 --- a/.github/workflows/check-package-version.yml +++ b/.github/workflows/check-package-version.yml @@ -11,7 +11,7 @@ jobs: uses: actions/checkout@v2 - name: Get NPM version is new id: check - uses: EndBug/version-check@v1.6.0 + uses: EndBug/version-check@v2.0.1 with: diff-search: true file-name: ./package.json From 18cd2b93500e9ce2e0fa81c5138f615c7c164cac Mon Sep 17 00:00:00 2001 From: "javier.brea" Date: Fri, 29 Jan 2021 19:11:46 +0100 Subject: [PATCH 02/22] feat: Display legacy options and menus only when core setting has value. --- CHANGELOG.md | 5 +++ src/Cli.js | 110 ++++++++++++++++++++++++++++++------------------ src/Inquirer.js | 26 +++++++----- 3 files changed, 90 insertions(+), 51 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4cf347e..f2721f6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,9 +6,14 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ## [unreleased] ### Added +- feat: Display current mock. ### Changed +- feat: Display legacy options and menus only when core `pathLegacy` setting has value. +- refactor: Refresh inquirer main options every time main menu is displayed. ### Fixed ### Removed +### BREAKING CHANGE +- Changed format of `cli` option to boolean (now `--no-cli` has to be used to disable it) ## [1.4.1] - 2020-12-25 diff --git a/src/Cli.js b/src/Cli.js index 4959a40..4a7289f 100644 --- a/src/Cli.js +++ b/src/Cli.js @@ -16,37 +16,45 @@ const { isNumber } = require("lodash"); const inquirer = require("./Inquirer"); const { renderHeader, renderAlert } = require("./helpers"); -const questions = { +const MAIN_CHOICES = [ + { + name: "Change delay", + value: "delay", + }, + { + name: "Restart server", + value: "restart", + }, + { + name: "Change log level", + value: "logLevel", + }, + { + name: "Switch watch", + value: "watch", + }, + { + name: "Display server logs", + value: "logs", + }, + { + name: "Legacy: Change behavior", + value: "behavior", + isLegacy: true, + }, + { + name: "Legacy: Switch watch", + value: "watch", + isLegacy: true, + }, +]; + +const QUESTIONS = { main: { type: "list", message: "Select action:", name: "value", - choices: [ - { - name: "Change current behavior", - value: "behavior", - }, - { - name: "Change delay", - value: "delay", - }, - { - name: "Restart server", - value: "restart", - }, - { - name: "Change log level", - value: "logLevel", - }, - { - name: "Switch watch", - value: "watch", - }, - { - name: "Display server logs", - value: "logs", - }, - ], + choices: MAIN_CHOICES, }, logLevel: { type: "list", @@ -73,6 +81,16 @@ const questions = { }, }; +const mainChoices = (legacyMode) => { + return MAIN_CHOICES.filter((choice) => !(choice.isLegacy && !legacyMode)); +}; + +const getQuestions = (legacyMode) => { + const questions = QUESTIONS; + questions.main.choices = mainChoices(legacyMode); + return questions; +}; + const SCREENS = { MAIN: "main", BEHAVIOR: "behavior", @@ -96,7 +114,7 @@ class Cli { this._core.addSetting({ name: "cli", - type: "booleanString", // Workaround to maintain backward compaitbility with --cli=false + type: "boolean", description: "Start interactive CLI plugin", default: true, }); @@ -106,12 +124,7 @@ class Cli { if (!this._settings.get("cli")) { return Promise.resolve(); } - this._questions = questions; - this._cli = new inquirer.Inquirer( - this._questions, - this._header.bind(this), - this._alertsHeader.bind(this) - ); + this._cli = new inquirer.Inquirer(this._header.bind(this), this._alertsHeader.bind(this)); this._stopListeningChangeSettings = this._core.onChangeSettings(this._onChangeSettings); this._inited = true; return Promise.resolve(); @@ -205,21 +218,35 @@ class Cli { _header() { const delay = this._settings.get("delay"); + const watchEnabled = this._settings.get("watch"); + const legacyMode = !!this._settings.get("pathLegacy"); + + const currentMock = this._core.mocks.current || "-"; const behaviorsCount = this._core.behaviors.count; const currentBehavior = this._core.behaviors.currentId || "-"; const currentFixtures = this._core.fixtures.count; - const watchEnabled = this._settings.get("watch"); - const header = [ + + const headers = [ renderHeader(`Mocks server listening at`, this._serverUrl), + renderHeader(`Current mock`, currentMock, currentMock === "-" ? 2 : 0), renderHeader(`Delay`, delay, delay > 0 ? 1 : 0), - renderHeader(`Behaviors`, behaviorsCount, behaviorsCount < 1 ? 2 : 0), - renderHeader(`Current behavior`, currentBehavior, currentBehavior === "-" ? 2 : 0), - renderHeader(`Current fixtures`, currentFixtures, currentFixtures < 1 ? 2 : 0), renderHeader(`Log level`, this._logLevel), - renderHeader(`Watch enabled`, watchEnabled, !!watchEnabled ? 0 : 1), ]; - return header; + const legacyHeaders = legacyMode + ? [ + renderHeader(`Legacy: Watch enabled`, watchEnabled, !!watchEnabled ? 0 : 1), + renderHeader(`Legacy: behaviors`, behaviorsCount, behaviorsCount < 1 ? 2 : 0), + renderHeader( + `Legacy: Current behavior`, + currentBehavior, + currentBehavior === "-" ? 2 : 0 + ), + renderHeader(`Legacy: Current fixtures`, currentFixtures, currentFixtures < 1 ? 2 : 0), + ] + : []; + + return [...headers, ...legacyHeaders]; } _alertsHeader() { @@ -227,6 +254,7 @@ class Cli { } async _displayMainMenu() { + this._cli.questions = getQuestions(!!this._settings.get("pathLegacy")); this._cli.clearScreen(); this._cli.exitLogsMode(); this._currentScreen = SCREENS.MAIN; diff --git a/src/Inquirer.js b/src/Inquirer.js index ce64626..3a2104e 100644 --- a/src/Inquirer.js +++ b/src/Inquirer.js @@ -34,13 +34,20 @@ const MAIN_MENU_ID = "main"; const DEFAULT_QUIT_NAME = "Exit"; const QUIT_ACTION_ID = "quit"; +const QUIT_QUESTION = { + name: DEFAULT_QUIT_NAME, + value: QUIT_ACTION_ID, +}; + +const exitProcess = () => process.exit(); + // require("events").EventEmitter.defaultMaxListeners = 100; const Inquirer = class Inquirer { - constructor(questions, header, alerts) { + constructor(header, alerts) { this._alertsHeader = alerts; this._header = header; - this._questions = this._initQuestions(questions); + this._exitLogsMode = this._exitLogsMode.bind(this); } @@ -50,18 +57,17 @@ const Inquirer = class Inquirer { _initQuestions(questions) { const clonedQuestions = cloneDeep(questions); - const quitQuestion = { - name: DEFAULT_QUIT_NAME, - value: QUIT_ACTION_ID, - }; if (clonedQuestions[MAIN_MENU_ID] && clonedQuestions[MAIN_MENU_ID].choices) { clonedQuestions[MAIN_MENU_ID].choices.push(new inquirer.Separator()); - clonedQuestions[MAIN_MENU_ID].choices.push(quitQuestion); - this._quit = () => process.exit(); + clonedQuestions[MAIN_MENU_ID].choices.push(QUIT_QUESTION); } return clonedQuestions; } + set questions(questions) { + this._questions = this._initQuestions(questions); + } + exitLogsMode() { if (this._logModeExit) { const stdin = process.stdin; @@ -107,13 +113,13 @@ const Inquirer = class Inquirer { }); this.removeListeners(); if (questionKey === MAIN_MENU_ID && answers.value === QUIT_ACTION_ID) { - return this._quit(); + return this.quit(); } return answers.value; } quit() { - this._quit(); + exitProcess(); } clearScreen(opts) { From 0d3ff970e589c62afe106a4c3888907307ca5fbb Mon Sep 17 00:00:00 2001 From: "javier.brea" Date: Sat, 30 Jan 2021 20:45:21 +0100 Subject: [PATCH 03/22] feat: display current mock --- CHANGELOG.md | 4 +++- src/Cli.js | 58 ++++++++++++++++++++++++++++++++++++++++++------- src/Inquirer.js | 41 +++++++++++++++++++++------------- 3 files changed, 79 insertions(+), 24 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f2721f6..2bb8243 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,10 +7,12 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ## [unreleased] ### Added - feat: Display current mock. +- feat: Display menus for changing current mock. ### Changed -- feat: Display legacy options and menus only when core `pathLegacy` setting has value. +- feat: Display legacy options and menus only when core `pathLegacy` setting has value. Add toggle legacy watch menu. - refactor: Refresh inquirer main options every time main menu is displayed. ### Fixed +- fix: Resolve previous inquirers before displaying a new one ### Removed ### BREAKING CHANGE - Changed format of `cli` option to boolean (now `--no-cli` has to be used to disable it) diff --git a/src/Cli.js b/src/Cli.js index 4a7289f..f92756b 100644 --- a/src/Cli.js +++ b/src/Cli.js @@ -17,6 +17,10 @@ const inquirer = require("./Inquirer"); const { renderHeader, renderAlert } = require("./helpers"); const MAIN_CHOICES = [ + { + name: "Change current mock", + value: "mock", + }, { name: "Change delay", value: "delay", @@ -44,7 +48,7 @@ const MAIN_CHOICES = [ }, { name: "Legacy: Switch watch", - value: "watch", + value: "watchLegacy", isLegacy: true, }, ]; @@ -62,6 +66,11 @@ const QUESTIONS = { name: "value", choices: ["silly", "debug", "verbose", "info", "warn", "error"], }, + mock: { + type: "autocomplete", + name: "value", + message: "Please choose mock", + }, behavior: { type: "autocomplete", name: "value", @@ -94,6 +103,7 @@ const getQuestions = (legacyMode) => { const SCREENS = { MAIN: "main", BEHAVIOR: "behavior", + MOCK: "mock", DELAY: "delay", LOG_LEVEL: "log-level", LOGS: "logs", @@ -157,7 +167,6 @@ class Cli { this._stopListeningChangeMocks(); this._stopListeningChangeAlerts(); this._settings.set("log", this._logLevel); - this._cli.removeListeners(); this._cli.logsMode(); this._cli.clearScreen({ header: false, @@ -167,7 +176,6 @@ class Cli { _refreshMainMenu() { if (this._currentScreen === SCREENS.MAIN) { - this._cli.removeListeners(); return this._displayMainMenu(); } return Promise.resolve(); @@ -193,11 +201,14 @@ class Cli { } } if ( - newSettings.hasOwnProperty("behavior") || + newSettings.hasOwnProperty("mock") || newSettings.hasOwnProperty("delay") || newSettings.hasOwnProperty("host") || newSettings.hasOwnProperty("log") || - newSettings.hasOwnProperty("watch") + newSettings.hasOwnProperty("watch") || + // To be deprecated + newSettings.hasOwnProperty("watchLegacy") || + newSettings.hasOwnProperty("behavior") ) { return this._refreshMainMenu(); } @@ -218,6 +229,7 @@ class Cli { _header() { const delay = this._settings.get("delay"); + const watchLegacyEnabled = this._settings.get("watchLegacy"); const watchEnabled = this._settings.get("watch"); const legacyMode = !!this._settings.get("pathLegacy"); @@ -231,11 +243,12 @@ class Cli { renderHeader(`Current mock`, currentMock, currentMock === "-" ? 2 : 0), renderHeader(`Delay`, delay, delay > 0 ? 1 : 0), renderHeader(`Log level`, this._logLevel), + renderHeader(`Watch enabled`, watchEnabled, !!watchEnabled ? 0 : 1), ]; const legacyHeaders = legacyMode ? [ - renderHeader(`Legacy: Watch enabled`, watchEnabled, !!watchEnabled ? 0 : 1), + renderHeader(`Legacy: Watch enabled`, watchLegacyEnabled, !!watchLegacyEnabled ? 0 : 1), renderHeader(`Legacy: behaviors`, behaviorsCount, behaviorsCount < 1 ? 2 : 0), renderHeader( `Legacy: Current behavior`, @@ -260,8 +273,8 @@ class Cli { this._currentScreen = SCREENS.MAIN; const action = await this._cli.inquire("main"); switch (action) { - case "behavior": - return this._changeCurrentBehavior(); + case "mock": + return this._changeCurrentMock(); case "delay": return this._changeDelay(); case "restart": @@ -272,7 +285,31 @@ class Cli { return this._switchWatch(); case "logs": return this._displayLogs(); + // Legacy, to be removed + case "behavior": + return this._changeCurrentBehavior(); + case "watchLegacy": + return this._switchWatchLegacy(); + } + } + + async _changeCurrentMock() { + this._currentScreen = SCREENS.MOCK; + this._cli.clearScreen(); + const mocksIds = this._core.mocks.ids; + if (!mocksIds.length) { + return this._displayMainMenu(); } + const mockId = await this._cli.inquire("mock", { + source: (answers, input) => { + if (!input || !input.length) { + return Promise.resolve(mocksIds); + } + return Promise.resolve(mocksIds.filter((currentMock) => currentMock.includes(input))); + }, + }); + this._settings.set("mock", mockId); + return this._displayMainMenu(); } async _changeCurrentBehavior() { @@ -316,6 +353,11 @@ class Cli { return this._displayMainMenu(); } + async _switchWatchLegacy() { + this._settings.set("watchLegacy", !this._settings.get("watchLegacy")); + return this._displayMainMenu(); + } + async _changeLogLevel() { this._currentScreen = SCREENS.LOG_LEVEL; this._cli.clearScreen(); diff --git a/src/Inquirer.js b/src/Inquirer.js index 3a2104e..da48df9 100644 --- a/src/Inquirer.js +++ b/src/Inquirer.js @@ -12,9 +12,8 @@ Unless required by applicable law or agreed to in writing, software distributed const inquirer = require("inquirer"); const autocomplete = require("inquirer-autocomplete-prompt"); -const { cloneDeep, map } = require("lodash"); +const { cloneDeep } = require("lodash"); -const packageInfo = require("../package.json"); const { renderSectionHeader, renderSectionFooter, @@ -49,10 +48,7 @@ const Inquirer = class Inquirer { this._header = header; this._exitLogsMode = this._exitLogsMode.bind(this); - } - - get displayName() { - return packageInfo.name; + this._currentInquirers = new Set(); } _initQuestions(questions) { @@ -106,16 +102,31 @@ const Inquirer = class Inquirer { }); } - async inquire(questionKey, extendProperties) { - const answers = await inquirer.prompt({ - ...this._questions[questionKey], - ...extendProperties, + _resolvePreviousInquirers() { + this._currentInquirers.forEach((inquirerPromise) => { + inquirerPromise(null); + this._currentInquirers.delete(inquirerPromise); }); + } + + async inquire(questionKey, extendProperties) { + this._resolvePreviousInquirers(); this.removeListeners(); - if (questionKey === MAIN_MENU_ID && answers.value === QUIT_ACTION_ID) { - return this.quit(); - } - return answers.value; + return new Promise((resolve) => { + this._currentInquirers.add(resolve); + inquirer + .prompt({ + ...this._questions[questionKey], + ...extendProperties, + }) + .then((answers) => { + this.removeListeners(); + if (questionKey === MAIN_MENU_ID && answers.value === QUIT_ACTION_ID) { + this.quit(); + } + resolve(answers.value); + }); + }); } quit() { @@ -142,7 +153,7 @@ const Inquirer = class Inquirer { removeListeners() { const listeners = process.stdin.listeners(EVENT_LISTENER); - map(listeners, (listener) => { + listeners.forEach((listener) => { process.stdin.removeListener(EVENT_LISTENER, listener); }); } From 9a22b122d2f312b27204bff61ff8ba857e26ae94 Mon Sep 17 00:00:00 2001 From: "javier.brea" Date: Mon, 1 Feb 2021 19:45:29 +0100 Subject: [PATCH 04/22] feat: Display current mocks and routes. Support changing route variants --- src/Cli.js | 61 +++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 60 insertions(+), 1 deletion(-) diff --git a/src/Cli.js b/src/Cli.js index f92756b..a9e8975 100644 --- a/src/Cli.js +++ b/src/Cli.js @@ -21,6 +21,14 @@ const MAIN_CHOICES = [ name: "Change current mock", value: "mock", }, + { + name: "Change route variant", + value: "variant", + }, + { + name: "Restore route variants", + value: "restoreVariants", + }, { name: "Change delay", value: "delay", @@ -76,6 +84,11 @@ const QUESTIONS = { name: "value", message: "Please choose behavior", }, + variant: { + type: "autocomplete", + name: "value", + message: "Please choose route variant", + }, delay: { type: "input", name: "value", @@ -237,11 +250,29 @@ class Cli { const behaviorsCount = this._core.behaviors.count; const currentBehavior = this._core.behaviors.currentId || "-"; const currentFixtures = this._core.fixtures.count; + const availableMocks = this._core.mocks.plainMocks.length; + const availableRoutes = this._core.mocks.plainRoutes.length; + const availableRoutesVariants = this._core.mocks.plainRoutesVariants.length; + + const currentMockMessage = this._core.mocks.customRouteVariants.length + ? `${currentMock} (custom variants: ${this._core.mocks.customRouteVariants.join(",")})` + : currentMock; const headers = [ renderHeader(`Mocks server listening at`, this._serverUrl), - renderHeader(`Current mock`, currentMock, currentMock === "-" ? 2 : 0), renderHeader(`Delay`, delay, delay > 0 ? 1 : 0), + renderHeader( + `Current mock`, + currentMockMessage, + this._core.mocks.customRouteVariants.length ? 1 : currentMock === "-" ? 2 : 0 + ), + renderHeader(`Mocks`, availableMocks, availableMocks < 1 ? 2 : 0), + renderHeader(`Routes`, availableRoutes, availableRoutes < 1 ? 2 : 0), + renderHeader( + `Routes variants`, + availableRoutesVariants, + availableRoutesVariants < 1 ? 2 : 0 + ), renderHeader(`Log level`, this._logLevel), renderHeader(`Watch enabled`, watchEnabled, !!watchEnabled ? 0 : 1), ]; @@ -275,6 +306,10 @@ class Cli { switch (action) { case "mock": return this._changeCurrentMock(); + case "variant": + return this._changeRouteVariant(); + case "restoreVariants": + return this._restoreRouteVariants(); case "delay": return this._changeDelay(); case "restart": @@ -312,6 +347,30 @@ class Cli { return this._displayMainMenu(); } + async _changeRouteVariant() { + this._currentScreen = SCREENS.MOCK; + this._cli.clearScreen(); + const routeVariantsIds = this._core.mocks.plainRoutesVariants.map((variant) => variant.id); + if (!routeVariantsIds.length) { + return this._displayMainMenu(); + } + const variantId = await this._cli.inquire("variant", { + source: (answers, input) => { + if (!input || !input.length) { + return Promise.resolve(routeVariantsIds); + } + return Promise.resolve(routeVariantsIds.filter((variant) => variant.includes(input))); + }, + }); + this._core.mocks.useRouteVariant(variantId); + return this._displayMainMenu(); + } + + async _restoreRouteVariants() { + this._core.mocks.restoreRouteVariants(); + return this._displayMainMenu(); + } + async _changeCurrentBehavior() { this._currentScreen = SCREENS.BEHAVIOR; this._cli.clearScreen(); From 496c3f304cb6c48cd7f042f06c0d5d1b40a74146 Mon Sep 17 00:00:00 2001 From: "javier.brea" Date: Tue, 2 Feb 2021 19:38:46 +0100 Subject: [PATCH 05/22] fix: start promise was never resolved --- CHANGELOG.md | 2 ++ src/Cli.js | 3 ++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2bb8243..47627f9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,11 +8,13 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ### Added - feat: Display current mock. - feat: Display menus for changing current mock. +- feat: Add menus for changing route variant and restore variants ### Changed - feat: Display legacy options and menus only when core `pathLegacy` setting has value. Add toggle legacy watch menu. - refactor: Refresh inquirer main options every time main menu is displayed. ### Fixed - fix: Resolve previous inquirers before displaying a new one +- fix: Start promise was never resolved ### Removed ### BREAKING CHANGE - Changed format of `cli` option to boolean (now `--no-cli` has to be used to disable it) diff --git a/src/Cli.js b/src/Cli.js index a9e8975..37f96c6 100644 --- a/src/Cli.js +++ b/src/Cli.js @@ -169,7 +169,8 @@ class Cli { this._stopListeningChangeMocks = this._core.onChangeMocks(this._onChangeMocks); this._logLevel = this._settings.get("log"); this._silentTraces(); - return this._displayMainMenu(); + this._displayMainMenu(); + return Promise.resolve(); } stop() { From 2e9ed4df5df0f228f091dc3426bc5843ddd83c81 Mon Sep 17 00:00:00 2001 From: "javier.brea" Date: Sun, 14 Feb 2021 11:24:05 +0100 Subject: [PATCH 06/22] feat: use core new method name --- src/Cli.js | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/Cli.js b/src/Cli.js index 37f96c6..98a1a82 100644 --- a/src/Cli.js +++ b/src/Cli.js @@ -26,7 +26,7 @@ const MAIN_CHOICES = [ value: "variant", }, { - name: "Restore route variants", + name: "Restore routes variants", value: "restoreVariants", }, { @@ -255,8 +255,8 @@ class Cli { const availableRoutes = this._core.mocks.plainRoutes.length; const availableRoutesVariants = this._core.mocks.plainRoutesVariants.length; - const currentMockMessage = this._core.mocks.customRouteVariants.length - ? `${currentMock} (custom variants: ${this._core.mocks.customRouteVariants.join(",")})` + const currentMockMessage = this._core.mocks.customRoutesVariants.length + ? `${currentMock} (custom variants: ${this._core.mocks.customRoutesVariants.join(",")})` : currentMock; const headers = [ @@ -265,7 +265,7 @@ class Cli { renderHeader( `Current mock`, currentMockMessage, - this._core.mocks.customRouteVariants.length ? 1 : currentMock === "-" ? 2 : 0 + this._core.mocks.customRoutesVariants.length ? 1 : currentMock === "-" ? 2 : 0 ), renderHeader(`Mocks`, availableMocks, availableMocks < 1 ? 2 : 0), renderHeader(`Routes`, availableRoutes, availableRoutes < 1 ? 2 : 0), @@ -310,7 +310,7 @@ class Cli { case "variant": return this._changeRouteVariant(); case "restoreVariants": - return this._restoreRouteVariants(); + return this._restoreRoutesVariants(); case "delay": return this._changeDelay(); case "restart": @@ -367,8 +367,8 @@ class Cli { return this._displayMainMenu(); } - async _restoreRouteVariants() { - this._core.mocks.restoreRouteVariants(); + async _restoreRoutesVariants() { + this._core.mocks.restoreRoutesVariants(); return this._displayMainMenu(); } From 2b4366623525265ae485359cd3a2a05d51e35022 Mon Sep 17 00:00:00 2001 From: "javier.brea" Date: Sun, 14 Feb 2021 11:25:15 +0100 Subject: [PATCH 07/22] chore(ci): build pre-release branch. publish to npm with beta tag --- .github/workflows/build.yml | 1 + .github/workflows/publish-to-github.yml | 2 +- .github/workflows/publish-to-npm.yml | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 8b5192a..fe6f5d0 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -4,6 +4,7 @@ on: branches: - master - release + - pre-release pull_request: jobs: test: diff --git a/.github/workflows/publish-to-github.yml b/.github/workflows/publish-to-github.yml index ba50e4f..7d6964e 100644 --- a/.github/workflows/publish-to-github.yml +++ b/.github/workflows/publish-to-github.yml @@ -13,6 +13,6 @@ jobs: registry-url: 'https://npm.pkg.github.com' # Defaults to the user or organization that owns the workflow file scope: '@mocks-server' - - run: npm publish + - run: npm publish --tag beta env: NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/publish-to-npm.yml b/.github/workflows/publish-to-npm.yml index 2444d4b..fd47750 100644 --- a/.github/workflows/publish-to-npm.yml +++ b/.github/workflows/publish-to-npm.yml @@ -11,6 +11,6 @@ jobs: with: node-version: '12.x' registry-url: 'https://registry.npmjs.org/' - - run: npm publish + - run: npm publish --tag beta env: NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} From f64c822095bd006ce7bb16d7b56ed99e0cec22d9 Mon Sep 17 00:00:00 2001 From: "javier.brea" Date: Sun, 14 Feb 2021 12:46:59 +0100 Subject: [PATCH 08/22] test: add unit tests --- src/Inquirer.js | 1 + test/unit/Core.mocks.js | 10 ++ test/unit/src/Cli.spec.js | 292 ++++++++++++++++++++++++++++++--- test/unit/src/Inquirer.spec.js | 73 ++++++--- 4 files changed, 329 insertions(+), 47 deletions(-) diff --git a/src/Inquirer.js b/src/Inquirer.js index da48df9..ff18def 100644 --- a/src/Inquirer.js +++ b/src/Inquirer.js @@ -120,6 +120,7 @@ const Inquirer = class Inquirer { ...extendProperties, }) .then((answers) => { + this._currentInquirers.delete(resolve); this.removeListeners(); if (questionKey === MAIN_MENU_ID && answers.value === QUIT_ACTION_ID) { this.quit(); diff --git a/test/unit/Core.mocks.js b/test/unit/Core.mocks.js index 1296ede..4af5139 100644 --- a/test/unit/Core.mocks.js +++ b/test/unit/Core.mocks.js @@ -40,6 +40,16 @@ class CoreMock { onChangeMocks: this._sandbox.stub().returns(() => {}), addRouter: this._sandbox.stub(), addSetting: this._sandbox.stub(), + mocks: { + current: "foo-current", + plainMocks: [], + plainRoutes: [], + plainRoutesVariants: [], + customRoutesVariants: [], + ids: [], + useRouteVariant: this._sandbox.stub(), + restoreRoutesVariants: this._sandbox.stub(), + }, behaviors: { count: 0, currentId: "foo-current", diff --git a/test/unit/src/Cli.spec.js b/test/unit/src/Cli.spec.js index 5174a95..b53f6d4 100644 --- a/test/unit/src/Cli.spec.js +++ b/test/unit/src/Cli.spec.js @@ -170,12 +170,10 @@ describe("Cli", () => { it("should stop cli if it was started and cli option is false", async () => { inquirerMocks.reset(); - expect.assertions(2); coreInstance.settings.get.withArgs("cli").returns(false); coreInstance.onChangeSettings.getCall(0).args[0]({ cli: false, }); - expect(inquirerMocks.stubs.inquirer.removeListeners.callCount).toEqual(1); expect(inquirerMocks.stubs.inquirer.clearScreen.getCall(0).args[0]).toEqual({ header: false, }); @@ -278,9 +276,7 @@ describe("Cli", () => { it("should clear screen", async () => { inquirerMocks.reset(); - expect.assertions(2); await cli.stop(); - expect(inquirerMocks.stubs.inquirer.removeListeners.callCount).toEqual(1); expect(inquirerMocks.stubs.inquirer.clearScreen.getCall(0).args[0]).toEqual({ header: false, }); @@ -299,7 +295,9 @@ describe("Cli", () => { it("should not stop if it was already stopped", async () => { await cli.stop(); await cli.stop(); - expect(inquirerMocks.stubs.inquirer.removeListeners.callCount).toEqual(1); + await cli.stop(); + await cli.stop(); + expect(inquirerMocks.stubs.inquirer.clearScreen.callCount).toEqual(2); }); }); @@ -376,6 +374,152 @@ describe("Cli", () => { }); }); + describe('when user selects "Change current mock"', () => { + const fooSelectedMock = "foo mock"; + let originalIds; + + beforeEach(() => { + originalIds = coreInstance.mocks.ids; + coreInstance.mocks.ids = ["foo-mock"]; + inquirerMocks.stubs.inquirer.inquire.onCall(0).resolves("mock"); + inquirerMocks.stubs.inquirer.inquire.onCall(1).resolves(fooSelectedMock); + }); + + afterEach(() => { + coreInstance.mocks.ids = originalIds; + }); + + it("should call to clear screen", async () => { + await cli.start(); + expect(inquirerMocks.stubs.inquirer.clearScreen.callCount).toEqual(3); + }); + + it("should display main menu if there are no mocks", async () => { + coreInstance.mocks.ids = []; + await cli.start(); + expect(inquirerMocks.stubs.inquirer.inquire.getCall(1).args[0]).toEqual("main"); + }); + + it("should call to display mock menu", async () => { + await cli.start(); + expect(inquirerMocks.stubs.inquirer.inquire.getCall(1).args[0]).toEqual("mock"); + }); + + it("should set current selected mock", async () => { + await cli.start(); + expect(coreInstance.settings.set.getCall(1).args).toEqual(["mock", fooSelectedMock]); + }); + + it("should not filter current mocks if there is no input", async () => { + const fooMocks = ["foo1", "foo2"]; + inquirerMocks.stubs.inquirer.inquireFake.executeCb(true); + inquirerMocks.stubs.inquirer.inquireFake.returns(null); + inquirerMocks.stubs.inquirer.inquire + .onCall(0) + .callsFake(inquirerMocks.stubs.inquirer.inquireFake.runner); + coreInstance.mocks.ids = fooMocks; + await cli._changeCurrentMock(); + expect(coreInstance.settings.set.getCall(0).args).toEqual(["mock", ["foo1", "foo2"]]); + }); + + it("should not filter current mocks if current input is empty", async () => { + const fooMocks = ["foo1", "foo2"]; + inquirerMocks.stubs.inquirer.inquireFake.executeCb(true); + inquirerMocks.stubs.inquirer.inquireFake.returns([]); + inquirerMocks.stubs.inquirer.inquire + .onCall(0) + .callsFake(inquirerMocks.stubs.inquirer.inquireFake.runner); + coreInstance.mocks.ids = fooMocks; + await cli._changeCurrentMock(); + expect(coreInstance.settings.set.getCall(0).args).toEqual(["mock", ["foo1", "foo2"]]); + }); + + it("should filter current mocks and returns all that includes current input", async () => { + const fooMocks = ["foo1", "foo2", "not-included"]; + inquirerMocks.stubs.inquirer.inquireFake.executeCb(true); + inquirerMocks.stubs.inquirer.inquireFake.returns("foo"); + inquirerMocks.stubs.inquirer.inquire + .onCall(0) + .callsFake(inquirerMocks.stubs.inquirer.inquireFake.runner); + coreInstance.mocks.ids = fooMocks; + await cli._changeCurrentMock(); + expect(coreInstance.settings.set.getCall(0).args).toEqual(["mock", ["foo1", "foo2"]]); + }); + }); + + describe('when user selects "Change route variant"', () => { + const fooSelectedVariant = "foo variant"; + let originalVariants; + + beforeEach(() => { + originalVariants = coreInstance.mocks.plainRoutesVariants; + coreInstance.mocks.plainRoutesVariants = [{ id: "foo-variant" }]; + inquirerMocks.stubs.inquirer.inquire.onCall(0).resolves("variant"); + inquirerMocks.stubs.inquirer.inquire.onCall(1).resolves(fooSelectedVariant); + }); + + afterEach(() => { + coreInstance.mocks.plainRoutesVariants = originalVariants; + }); + + it("should call to clear screen", async () => { + await cli.start(); + expect(inquirerMocks.stubs.inquirer.clearScreen.callCount).toEqual(3); + }); + + it("should display main menu if there are no routes variants", async () => { + coreInstance.mocks.plainRoutesVariants = []; + await cli.start(); + expect(inquirerMocks.stubs.inquirer.inquire.getCall(1).args[0]).toEqual("main"); + }); + + it("should call to display routes variants menu", async () => { + await cli.start(); + expect(inquirerMocks.stubs.inquirer.inquire.getCall(1).args[0]).toEqual("variant"); + }); + + it("should set current selected route variant", async () => { + await cli.start(); + expect(coreInstance.mocks.useRouteVariant.getCall(0).args).toEqual([fooSelectedVariant]); + }); + + it("should not filter current routes variants if there is no input", async () => { + const fooVariants = [{ id: "foo1" }, { id: "foo2" }]; + inquirerMocks.stubs.inquirer.inquireFake.executeCb(true); + inquirerMocks.stubs.inquirer.inquireFake.returns(null); + inquirerMocks.stubs.inquirer.inquire + .onCall(0) + .callsFake(inquirerMocks.stubs.inquirer.inquireFake.runner); + coreInstance.mocks.plainRoutesVariants = fooVariants; + await cli._changeRouteVariant(); + expect(coreInstance.mocks.useRouteVariant.getCall(0).args).toEqual([["foo1", "foo2"]]); + }); + + it("should not filter current routes variants if current input is empty", async () => { + const fooVariants = [{ id: "foo1" }, { id: "foo2" }]; + inquirerMocks.stubs.inquirer.inquireFake.executeCb(true); + inquirerMocks.stubs.inquirer.inquireFake.returns([]); + inquirerMocks.stubs.inquirer.inquire + .onCall(0) + .callsFake(inquirerMocks.stubs.inquirer.inquireFake.runner); + coreInstance.mocks.plainRoutesVariants = fooVariants; + await cli._changeRouteVariant(); + expect(coreInstance.mocks.useRouteVariant.getCall(0).args).toEqual([["foo1", "foo2"]]); + }); + + it("should filter current variants and returns all that includes current input", async () => { + const fooVariants = [{ id: "foo1" }, { id: "foo2" }, { id: "not-included" }]; + inquirerMocks.stubs.inquirer.inquireFake.executeCb(true); + inquirerMocks.stubs.inquirer.inquireFake.returns("foo"); + inquirerMocks.stubs.inquirer.inquire + .onCall(0) + .callsFake(inquirerMocks.stubs.inquirer.inquireFake.runner); + coreInstance.mocks.plainRoutesVariants = fooVariants; + await cli._changeRouteVariant(); + expect(coreInstance.mocks.useRouteVariant.getCall(0).args).toEqual([["foo1", "foo2"]]); + }); + }); + describe('when user selects "Change Delay"', () => { const fooDelay = 2000; beforeEach(() => { @@ -399,11 +543,17 @@ describe("Cli", () => { }); it("should not pass delay validation if user introduce non numeric characters", async () => { - expect(cli._questions.delay.validate(cli._questions.delay.filter("asdads"))).toEqual(false); + await cli.start(); + expect(cli._cli.questions.delay.validate(cli._cli.questions.delay.filter("asdads"))).toEqual( + false + ); }); it("should pass delay validation if user introduce numeric characters", async () => { - expect(cli._questions.delay.validate(cli._questions.delay.filter("123230"))).toEqual(true); + await cli.start(); + expect(cli._cli.questions.delay.validate(cli._cli.questions.delay.filter("123230"))).toEqual( + true + ); }); }); @@ -418,6 +568,17 @@ describe("Cli", () => { }); }); + describe('when user selects "restore route variants"', () => { + beforeEach(() => { + inquirerMocks.stubs.inquirer.inquire.onCall(0).resolves("restoreVariants"); + }); + + it("should call to restore variants", async () => { + await cli.start(); + expect(coreInstance.mocks.restoreRoutesVariants.callCount).toEqual(1); + }); + }); + describe('when user selects "Change log level"', () => { const fooLogLevel = "foo-level"; @@ -455,6 +616,24 @@ describe("Cli", () => { }); }); + describe('when user selects "Switch watch legacy"', () => { + beforeEach(() => { + inquirerMocks.stubs.inquirer.inquire.onCall(0).resolves("watchLegacy"); + }); + + it("should call to switchWatchLegacy server method, passing true if it was disabled", async () => { + coreInstance.settings.get.withArgs("watchLegacy").returns(false); + await cli.start(); + expect(coreInstance.settings.set.getCall(1).args).toEqual(["watchLegacy", true]); + }); + + it("should call to switchWatchLegacy server method, passing false if it was enabled", async () => { + coreInstance.settings.get.withArgs("watchLegacy").returns(true); + await cli.start(); + expect(coreInstance.settings.set.getCall(1).args).toEqual(["watchLegacy", false]); + }); + }); + describe('when user selects "Display server logs"', () => { beforeEach(() => { inquirerMocks.stubs.inquirer.inquire.onCall(0).resolves("logs"); @@ -508,52 +687,127 @@ describe("Cli", () => { expect(cli._header()[1]).toEqual(expect.stringContaining(chalk.green("0"))); }); + it("should print mocks in red if are equal to 0", async () => { + coreInstance.mocks.plainMocks = []; + await cli.start(); + expect(cli._header()[3]).toEqual(expect.stringContaining(chalk.red("0"))); + }); + + it("should print mocks in green if are greater than 0", async () => { + coreInstance.mocks.plainMocks = [{}, {}, {}, {}]; + await cli.start(); + expect(cli._header()[3]).toEqual(expect.stringContaining(chalk.green("4"))); + }); + + it("should print current mock in red if it is null", async () => { + coreInstance.mocks.current = null; + await cli.start(); + expect(cli._header()[2]).toEqual(expect.stringContaining(chalk.red("-"))); + }); + + it("should print current mock in green if it is defined", async () => { + coreInstance.mocks.current = "foo"; + await cli.start(); + expect(cli._header()[2]).toEqual(expect.stringContaining(chalk.green("foo"))); + }); + + it("should print current mock in yellow if there are custom routes variants", async () => { + coreInstance.mocks.current = "foo"; + coreInstance.mocks.customRoutesVariants = ["foo-variant", "foo-variant-2"]; + await cli.start(); + expect(cli._header()[2]).toEqual( + expect.stringContaining(chalk.yellow("foo (custom variants: foo-variant,foo-variant-2)")) + ); + }); + + it("should print current routes in red if there are less than 1", async () => { + coreInstance.mocks.plainRoutes = []; + await cli.start(); + expect(cli._header()[4]).toEqual(expect.stringContaining(chalk.red("0"))); + }); + + it("should print current routes in green if there are less than 1", async () => { + coreInstance.mocks.plainRoutes = [{}, {}]; + await cli.start(); + expect(cli._header()[4]).toEqual(expect.stringContaining(chalk.green("2"))); + }); + + it("should print current routes variants in red if there are less than 1", async () => { + coreInstance.mocks.plainRoutesVariants = []; + await cli.start(); + expect(cli._header()[5]).toEqual(expect.stringContaining(chalk.red("0"))); + }); + + it("should print current routes in green if there are less than 1", async () => { + coreInstance.mocks.plainRoutesVariants = [{}, {}]; + await cli.start(); + expect(cli._header()[5]).toEqual(expect.stringContaining(chalk.green("2"))); + }); + + it("should print watch in yellow if it is disabled", async () => { + coreInstance.settings.get.withArgs("watch").returns(false); + await cli.start(); + expect(cli._header()[7]).toEqual(expect.stringContaining(chalk.yellow("false"))); + }); + + it("should print watch in yellow if it is enabled", async () => { + coreInstance.settings.get.withArgs("watch").returns(true); + await cli.start(); + expect(cli._header()[7]).toEqual(expect.stringContaining(chalk.green("true"))); + }); + }); + + describe("when printing header with legacy mode enabled", () => { + beforeEach(() => { + coreInstance.settings.get.withArgs("pathLegacy").returns(true); + }); + it("should print behaviors in red if are equal to 0", async () => { coreInstance.behaviors.count = 0; await cli.start(); - expect(cli._header()[2]).toEqual(expect.stringContaining(chalk.red("0"))); + expect(cli._header()[9]).toEqual(expect.stringContaining(chalk.red("0"))); }); it("should print behaviors in green if are greater than 0", async () => { coreInstance.behaviors.count = 10; await cli.start(); - expect(cli._header()[2]).toEqual(expect.stringContaining(chalk.green("10"))); + expect(cli._header()[9]).toEqual(expect.stringContaining(chalk.green("10"))); }); it("should print current behavior in red if it is null", async () => { coreInstance.behaviors.currentId = null; await cli.start(); - expect(cli._header()[3]).toEqual(expect.stringContaining(chalk.red("-"))); + expect(cli._header()[10]).toEqual(expect.stringContaining(chalk.red("-"))); }); it("should print current behavior in green if it is defined", async () => { coreInstance.behaviors.currentId = "foo"; await cli.start(); - expect(cli._header()[3]).toEqual(expect.stringContaining(chalk.green("foo"))); + expect(cli._header()[10]).toEqual(expect.stringContaining(chalk.green("foo"))); }); it("should print current fixtures in red if there are less than 1", async () => { coreInstance.fixtures.count = 0; await cli.start(); - expect(cli._header()[4]).toEqual(expect.stringContaining(chalk.red("0"))); + expect(cli._header()[11]).toEqual(expect.stringContaining(chalk.red("0"))); }); it("should print current fixtures in green if there are less than 1", async () => { coreInstance.fixtures.count = 10; await cli.start(); - expect(cli._header()[4]).toEqual(expect.stringContaining(chalk.green("10"))); + expect(cli._header()[11]).toEqual(expect.stringContaining(chalk.green("10"))); }); it("should print watch in yellow if it is disabled", async () => { - coreInstance.settings.get.withArgs("watch").returns(false); + coreInstance.settings.get.withArgs("watchLegacy").returns(false); await cli.start(); - expect(cli._header()[6]).toEqual(expect.stringContaining(chalk.yellow("false"))); + expect(cli._header()[8]).toEqual(expect.stringContaining(chalk.yellow("false"))); }); it("should print watch in yellow if it is enabled", async () => { - coreInstance.settings.get.withArgs("watch").returns(true); + coreInstance.settings.get.withArgs("watchLegacy").returns(true); await cli.start(); - expect(cli._header()[6]).toEqual(expect.stringContaining(chalk.green("true"))); + expect(cli._header()[8]).toEqual(expect.stringContaining(chalk.green("true"))); }); }); @@ -605,10 +859,6 @@ describe("Cli", () => { coreInstance.onChangeMocks.getCall(0).args[0](); }); - it("should remove all base-cli listeners", async () => { - expect(inquirerMocks.stubs.inquirer.removeListeners.callCount).toEqual(1); - }); - it("should exit logs mode", async () => { expect(inquirerMocks.stubs.inquirer.exitLogsMode.callCount).toEqual(2); }); diff --git a/test/unit/src/Inquirer.spec.js b/test/unit/src/Inquirer.spec.js index 3d61e3f..ef92814 100644 --- a/test/unit/src/Inquirer.spec.js +++ b/test/unit/src/Inquirer.spec.js @@ -41,23 +41,18 @@ describe("Inquirer", () => { sandbox.restore(); }); - describe("display name", () => { - it("should return package name", async () => { - const cli = new Inquirer(fooQuestions); - expect(cli.displayName).toEqual("@mocks-server/plugin-inquirer-cli"); - }); - }); - - describe("when initing questions", () => { + describe("when setting questions", () => { it("should add an extra exit option to main menu", () => { - const cli = new Inquirer(fooQuestions); + const cli = new Inquirer(); + cli.questions = fooQuestions; expect(cli._questions.main.choices[2].name).toEqual("Exit"); }); it("should not add an extra option if main question does not exists", () => { const questionsWithoutMain = { notMain: fooQuestions.main }; - const cli = new Inquirer(questionsWithoutMain); + const cli = new Inquirer(); + cli.questions = questionsWithoutMain; expect(cli._questions.notMain.choices.length).toEqual(1); }); }); @@ -65,7 +60,7 @@ describe("Inquirer", () => { describe("quit method", () => { it("should call to exit process", () => { sandbox.stub(process, "exit"); - const cli = new Inquirer(fooQuestions); + const cli = new Inquirer(); cli.quit(); expect(process.exit.callCount).toEqual(1); }); @@ -74,7 +69,7 @@ describe("Inquirer", () => { describe("clearScreen method", () => { it("should write Clear screen characters in process stdout", () => { sandbox.stub(process.stdout, "write"); - const cli = new Inquirer(fooQuestions); + const cli = new Inquirer(); cli.clearScreen(); expect(process.stdout.write.calledWith("\x1Bc")).toEqual(true); }); @@ -82,7 +77,7 @@ describe("Inquirer", () => { it("should print all strings returned by header method provided to constructor", () => { const fooHeader = "foo header"; sandbox.stub(process.stdout, "write"); - const cli = new Inquirer(fooQuestions, () => [fooHeader]); + const cli = new Inquirer(() => [fooHeader]); cli.clearScreen(); // Get console call 3 (From 0 to 2 are rendering section header) expect(console.log.getCall(3).args[0]).toEqual(expect.stringContaining(fooHeader)); @@ -92,7 +87,6 @@ describe("Inquirer", () => { const fooAlert = "foo header"; sandbox.stub(process.stdout, "write"); const cli = new Inquirer( - fooQuestions, () => [], () => [fooAlert] ); @@ -104,7 +98,7 @@ describe("Inquirer", () => { it("should not print header if header option is set to false", () => { const fooHeader = "foo header"; sandbox.stub(process.stdout, "write"); - const cli = new Inquirer(fooQuestions, () => [fooHeader]); + const cli = new Inquirer(() => [fooHeader]); cli.clearScreen({ header: false, }); @@ -117,14 +111,32 @@ describe("Inquirer", () => { expect.assertions(1); const fooValue = "foo-value"; sandbox.stub(inquirer, "prompt").usingPromise().resolves({ value: fooValue }); - const cli = new Inquirer(fooQuestions); + const cli = new Inquirer(); + cli.questions = fooQuestions; + expect(await cli.inquire("main")).toEqual(fooValue); + }); + + it("should resolve previous questions", async () => { + expect.assertions(4); + const fooValue = "foo-value"; + sandbox.stub(inquirer, "prompt").usingPromise().resolves({ value: fooValue }); + const cli = new Inquirer(); + cli.questions = fooQuestions; + const resolvePreviousQuestion1 = sandbox.spy(); + const resolvePreviousQuestion2 = sandbox.spy(); + cli._currentInquirers.add(resolvePreviousQuestion1); + cli._currentInquirers.add(resolvePreviousQuestion2); expect(await cli.inquire("main")).toEqual(fooValue); + expect(resolvePreviousQuestion1.callCount).toEqual(1); + expect(resolvePreviousQuestion2.callCount).toEqual(1); + expect(cli._currentInquirers.size).toEqual(0); }); it("should call to inquire prompt method, passing the correspondant question", async () => { expect.assertions(1); sandbox.stub(inquirer, "prompt").usingPromise().resolves({}); - const cli = new Inquirer(fooQuestions); + const cli = new Inquirer(); + cli.questions = fooQuestions; await cli.inquire("main"); expect(inquirer.prompt.getCall(0).args[0].message).toEqual("Select action:"); }); @@ -132,7 +144,8 @@ describe("Inquirer", () => { it("should call to remove keypress listener after inquire has finished", async () => { expect.assertions(1); sandbox.stub(inquirer, "prompt").usingPromise().resolves({}); - const cli = new Inquirer(fooQuestions); + const cli = new Inquirer(); + cli.questions = fooQuestions; process.stdin.on("keypress", () => {}); sandbox.stub(process.stdin, "removeListener"); await cli.inquire("main"); @@ -143,7 +156,8 @@ describe("Inquirer", () => { expect.assertions(1); sandbox.stub(process, "exit"); sandbox.stub(inquirer, "prompt").usingPromise().resolves({ value: "quit" }); - const cli = new Inquirer(fooQuestions); + const cli = new Inquirer(); + cli.questions = fooQuestions; await cli.inquire("main"); expect(process.exit.callCount).toEqual(1); }); @@ -188,13 +202,15 @@ describe("Inquirer", () => { }); it("should call to clear Screen", async () => { - const cli = new Inquirer(fooQuestions); + const cli = new Inquirer(); + cli.questions = fooQuestions; await cli.logsMode(); expect(process.stdout.write.calledWith("\x1Bc")).toEqual(true); }); it("should print that logs mode started", async () => { - const cli = new Inquirer(fooQuestions); + const cli = new Inquirer(); + cli.questions = fooQuestions; await cli.logsMode(); expect( console.log.calledWith( @@ -204,7 +220,8 @@ describe("Inquirer", () => { }); it("should call to provided callback", async () => { - const cli = new Inquirer(fooQuestions); + const cli = new Inquirer(); + cli.questions = fooQuestions; const fooCallBack = sinon.spy(); await cli.logsMode(fooCallBack); expect(fooCallBack.callCount).toEqual(1); @@ -212,7 +229,8 @@ describe("Inquirer", () => { it("should call to exit process if pressed key is equal to CTRL+C", async () => { fooKeyPresser.key = "\u0003"; - const cli = new Inquirer(fooQuestions); + const cli = new Inquirer(); + cli.questions = fooQuestions; await cli.logsMode(); expect(process.exit.callCount).toEqual(1); }); @@ -224,7 +242,8 @@ describe("Inquirer", () => { fakeRawMode = true; process.stdin.setRawMode = sandbox.stub(); } - const cli = new Inquirer(fooQuestions); + const cli = new Inquirer(); + cli.questions = fooQuestions; await cli.logsMode(); expect(process.stdin.setRawMode.callCount).toEqual(2); }); @@ -238,7 +257,8 @@ describe("Inquirer", () => { } const originalStdin = process.stdin; process.stdin.setRawMode = false; - const cli = new Inquirer(fooQuestions); + const cli = new Inquirer(); + cli.questions = fooQuestions; await cli.logsMode(); process.stdin = originalStdin; expect(process.stdin.setRawMode.callCount).toEqual(undefined); @@ -251,7 +271,8 @@ describe("Inquirer", () => { }); it("should do nothing if logs mode is not currently enabled", async () => { - const cli = new Inquirer(fooQuestions); + const cli = new Inquirer(); + cli.questions = fooQuestions; cli.exitLogsMode(); expect(process.stdin.removeListener.callCount).toEqual(0); }); From c318697dd130cca73901ff27a934a9602e060b85 Mon Sep 17 00:00:00 2001 From: "javier.brea" Date: Sun, 14 Feb 2021 12:47:57 +0100 Subject: [PATCH 09/22] chore(ci): skip e2e tests temporarily --- .github/workflows/build.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index fe6f5d0..bdcabc8 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -41,9 +41,9 @@ jobs: run: npm run lint - name: Test unit run: npm run test:unit - - name: Test E2E - run: npm run test:e2e - id: test-e2e + #- name: Test E2E + #run: npm run test:e2e + #id: test-e2e - name: Upload test results uses: actions/upload-artifact@v2 with: From 49b684f4dd3fe683668ce05825e0cd2000dfb409 Mon Sep 17 00:00:00 2001 From: "javier.brea" Date: Sun, 14 Feb 2021 17:44:42 +0100 Subject: [PATCH 10/22] test(e2e): start e2e tests migration --- jest.e2e.config.js | 2 +- package-lock.json | 90 +++++++++++++++++----- package.json | 7 +- test/e2e/inquirer/fixtures/autocomplete.js | 3 +- test/e2e/support/CliRunner.js | 8 +- 5 files changed, 80 insertions(+), 30 deletions(-) diff --git a/jest.e2e.config.js b/jest.e2e.config.js index faa67be..9cb1f9a 100644 --- a/jest.e2e.config.js +++ b/jest.e2e.config.js @@ -6,7 +6,7 @@ module.exports = { clearMocks: true, testMatch: ["/test/e2e/**/*.spec.js"], - // testMatch: ["/test/e2e/**/web-tutorial-files-watch.spec.js"], + testMatch: ["/test/e2e/inquirer/autocomplete.spec.js"], // Indicates whether the coverage information should be collected while executing the test collectCoverage: false, diff --git a/package-lock.json b/package-lock.json index 38be18d..1042caf 100644 --- a/package-lock.json +++ b/package-lock.json @@ -533,18 +533,18 @@ } }, "@hapi/boom": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/@hapi/boom/-/boom-9.1.0.tgz", - "integrity": "sha512-4nZmpp4tXbm162LaZT45P7F7sgiem8dwAh2vHWT6XX24dozNjGMg6BvKCRvtCUcmcXqeMIUqWN8Rc5X8yKuROQ==", + "version": "9.1.1", + "resolved": "https://registry.npmjs.org/@hapi/boom/-/boom-9.1.1.tgz", + "integrity": "sha512-VNR8eDbBrOxBgbkddRYIe7+8DZ+vSbV6qlmaN2x7eWjsUjy2VmQgChkOKcVZIeupEZYj+I0dqNg430OhwzagjA==", "dev": true, "requires": { "@hapi/hoek": "9.x.x" } }, "@hapi/hoek": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.1.0.tgz", - "integrity": "sha512-i9YbZPN3QgfighY/1X1Pu118VUz2Fmmhd6b2n0/O8YVgGGfw0FbUYoA97k7FkpGJ+pLCFEDLUmAPPV4D1kpeFw==", + "version": "9.1.1", + "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.1.1.tgz", + "integrity": "sha512-CAEbWH7OIur6jEOzaai83jq3FmKmv4PmX1JYfs9IrYcGEVI/lyL1EXJGCj7eFVJ0bg5QR8LMxBlEtA+xKiLpFw==", "dev": true }, "@istanbuljs/load-nyc-config": { @@ -778,25 +778,53 @@ } }, "@mocks-server/core": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/@mocks-server/core/-/core-1.6.0.tgz", - "integrity": "sha512-Gh2C7RGXvs09x7qDArby2XN1G11iOyLY5AcZzSaJXHM59F59NV9E17rNKf5i8S2XJVv1aNA4WO/AqsIfMFDcXQ==", + "version": "2.0.0-beta.1", + "resolved": "https://registry.npmjs.org/@mocks-server/core/-/core-2.0.0-beta.1.tgz", + "integrity": "sha512-K2q4dcUfVxCMrsvPJHpSFLTtlIi5eu7jJsANVjQZnpjbXgZceO5AjBc70F9y/PW+wh5n9AdTa4rjV73KKHb0DQ==", "dev": true, "requires": { - "@hapi/boom": "9.1.0", + "@hapi/boom": "9.1.1", "body-parser": "1.19.0", - "commander": "6.2.0", + "commander": "6.2.1", "cors": "2.8.5", "express": "4.17.1", "express-request-id": "1.4.1", - "fs-extra": "9.0.1", + "fs-extra": "9.1.0", + "globule": "1.3.2", "is-promise": "4.0.0", "lodash": "4.17.20", "md5": "2.3.0", - "node-watch": "0.7.0", + "node-watch": "0.7.1", "require-all": "3.0.0", "route-parser": "0.0.5", - "winston": "3.3.3" + "winston": "3.3.3", + "winston-array-transport": "1.0.1" + }, + "dependencies": { + "commander": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-6.2.1.tgz", + "integrity": "sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA==", + "dev": true + }, + "fs-extra": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", + "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", + "dev": true, + "requires": { + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + } + }, + "universalify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", + "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", + "dev": true + } } }, "@sinonjs/commons": { @@ -2994,6 +3022,17 @@ "type-fest": "^0.8.1" } }, + "globule": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/globule/-/globule-1.3.2.tgz", + "integrity": "sha512-7IDTQTIu2xzXkT+6mlluidnWo+BypnbSoEVVQCGfzqnl5Ik8d3e1d4wycb8Rj9tWW+Z39uPWsdlquqiqPCd/pA==", + "dev": true, + "requires": { + "glob": "~7.1.1", + "lodash": "~4.17.10", + "minimatch": "~3.0.2" + } + }, "graceful-fs": { "version": "4.2.4", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", @@ -4748,9 +4787,9 @@ } }, "node-watch": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/node-watch/-/node-watch-0.7.0.tgz", - "integrity": "sha512-OOBiglke5SlRQT5WYfwXTmYqTfXjcTNBHpalyHLtLxDpQYVpVRkJqabcch1kmwJsjV/J4OZuzEafeb4soqtFZA==", + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/node-watch/-/node-watch-0.7.1.tgz", + "integrity": "sha512-UWblPYuZYrkCQCW5PxAwYSxaELNBLUckrTBBk8xr1/bUgyOkYYTsUcV4e3ytcazFEOyiRyiUrsG37pu6I0I05g==", "dev": true }, "normalize-package-data": { @@ -6496,10 +6535,10 @@ "punycode": "^2.1.1" } }, - "tree-kill-sync": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/tree-kill-sync/-/tree-kill-sync-1.0.0.tgz", - "integrity": "sha1-1LrZklYR55KXmjrcFetgBMHVqa0=", + "tree-kill": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", + "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==", "dev": true }, "triple-beam": { @@ -6828,6 +6867,15 @@ } } }, + "winston-array-transport": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/winston-array-transport/-/winston-array-transport-1.0.1.tgz", + "integrity": "sha512-LfDPpViBtbO8N54YigOTTvPjr9ZESianNv5h2WT9Tr5U1O7bqEr8qhLZhHUhK5XJVWGINpQq/FubY1HlnZRjjQ==", + "dev": true, + "requires": { + "winston-transport": "^4.3.0" + } + }, "winston-transport": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/winston-transport/-/winston-transport-4.4.0.tgz", diff --git a/package.json b/package.json index 0b1a22a..575e547 100644 --- a/package.json +++ b/package.json @@ -31,7 +31,7 @@ "test:unit": "npm run test" }, "peerDependencies": { - "@mocks-server/core": ">=1.6.0" + "@mocks-server/core": ">=2.0.0" }, "dependencies": { "chalk": "4.1.0", @@ -41,7 +41,8 @@ "node-emoji": "1.10.0" }, "devDependencies": { - "@mocks-server/core": "1.6.0", + "@mocks-server/core": "2.0.0-beta.1", + "cross-spawn": "7.0.3", "eslint": "7.15.0", "eslint-plugin-no-only-tests": "2.4.0", "eslint-config-prettier": "6.15.0", @@ -56,7 +57,7 @@ "request-promise": "4.2.6", "sinon": "9.2.2", "strip-ansi": "6.0.0", - "tree-kill-sync": "1.0.0" + "tree-kill": "1.2.2" }, "lint-staged": { "src/**/*.js": "eslint", diff --git a/test/e2e/inquirer/fixtures/autocomplete.js b/test/e2e/inquirer/fixtures/autocomplete.js index 45f1deb..7f94b20 100644 --- a/test/e2e/inquirer/fixtures/autocomplete.js +++ b/test/e2e/inquirer/fixtures/autocomplete.js @@ -32,7 +32,8 @@ const questions = { const MyCli = class MyCli { constructor() { - this._cli = new Inquirer(questions, this.header.bind(this)); + this._cli = new Inquirer(this.header.bind(this)); + this._cli.questions = questions; this._selectedOption = "None"; } diff --git a/test/e2e/support/CliRunner.js b/test/e2e/support/CliRunner.js index 055e0f1..dabeb5b 100644 --- a/test/e2e/support/CliRunner.js +++ b/test/e2e/support/CliRunner.js @@ -12,9 +12,9 @@ Unless required by applicable law or agreed to in writing, software distributed "use strict"; const EventEmitter = require("events"); -const childProcess = require("child_process"); +const crossSpawn = require("cross-spawn"); -const treeKillSync = require("tree-kill-sync"); +const treeKill = require("tree-kill"); const stripAnsi = require("strip-ansi"); const { isArray, isNumber } = require("lodash"); @@ -73,7 +73,7 @@ module.exports = class CliRunner { if (this._cliProcess) { throw new Error("Cli is already running"); } else { - this._cliProcess = childProcess.spawn(this._command.name, this._command.params, { + this._cliProcess = crossSpawn(this._command.name, this._command.params, { cwd: this._cwd, }); this._cliProcess.stdin.setEncoding(ENCODING_TYPE); @@ -112,7 +112,7 @@ module.exports = class CliRunner { } async kill() { - treeKillSync(this._cliProcess.pid); + treeKill(this._cliProcess.pid); return this._exitPromise; } From 0e15f9736f62da1ffacbbbab910d1106baaf280a Mon Sep 17 00:00:00 2001 From: "javier.brea" Date: Sun, 14 Feb 2021 17:53:19 +0100 Subject: [PATCH 11/22] test(e2e): adapt all inquirer e2e tests --- README.md | 11 ++++++----- jest.e2e.config.js | 2 +- test/e2e/inquirer/fixtures/custom-quit-method.js | 3 ++- test/e2e/inquirer/fixtures/exit-logs-mode.js | 3 ++- test/e2e/inquirer/fixtures/logs-mode.js | 3 ++- test/e2e/inquirer/fixtures/readme-example.js | 3 ++- test/e2e/inquirer/fixtures/remove-listeners.js | 3 ++- 7 files changed, 17 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index fcefc85..808d308 100644 --- a/README.md +++ b/README.md @@ -7,18 +7,18 @@ # [![Mocks Server][logo-url]][website-url] Mocks Server Plugin Inquirer CLI -Plugin for [Mocks Server][website-url] that provides an interactive CLI that allows to change the server settings while it is running and displays logs and alerts. +[Mocks Server][website-url] plugin that provides an interactive CLI that allows to change the server settings while it is running and displays logs and alerts. ## Usage -This plugin is preinstalled in the [main distribution of the Mocks Server project][main-distribution-url]. _If you want ot install it by yourself, you can refer to the [plugins documentation website][plugins-url]._ +This plugin is preinstalled in the [main distribution of the Mocks Server project][main-distribution-url]. _If you want ot install it by yourself, you can refer to the [plugins documentation][plugins-url]._ ![Interactive CLI](assets/cli_animation.gif) ## Main features -* __Displays current [settings][settings-url].__ _Settings will be refreshed automatically even if they are changed using other plugins, as the REST API, etc._ -* __Allows changing [settings][settings-url].__ +* __Displays current [settings][settings-url].__ _Settings will be refreshed automatically even when changed using other plugins, as the REST API, etc._ +* __Allows to change [settings][settings-url].__ * __Displays current alerts.__ _Alerts include errors when loading mock files, wrong options, etc._ * __Displays logs.__ _Mocks-server logs are displayed in real time._ @@ -28,7 +28,7 @@ This plugin is preinstalled in the [main distribution of the Mocks Server projec ## Support -[Inquirer][inquirer-url] is used for displaying the interactive CLI. You can [consult his OS Terminals support here][inquirer-support]. +[Inquirer][inquirer-url] is used for displaying the interactive CLI. You can [consult its OS Terminals support here][inquirer-support]. ## Contributing @@ -40,6 +40,7 @@ Please read the [contributing guidelines](.github/CONTRIBUTING.md) and [code of [website-url]: https://www.mocks-server.org [plugins-url]: https://www.mocks-server.org/docs/plugins-adding-plugins [settings-url]: https://www.mocks-server.org/docs/configuration-options +[animated-image-url]: https://www.mocks-server.org/img/inquirer-cli.gif [main-distribution-url]: https://www.npmjs.com/package/@mocks-server/main [options-url]: https://www.mocks-server.org/docs/configuration-options [logo-url]: https://www.mocks-server.org/img/logo_120.png diff --git a/jest.e2e.config.js b/jest.e2e.config.js index 9cb1f9a..722f099 100644 --- a/jest.e2e.config.js +++ b/jest.e2e.config.js @@ -6,7 +6,7 @@ module.exports = { clearMocks: true, testMatch: ["/test/e2e/**/*.spec.js"], - testMatch: ["/test/e2e/inquirer/autocomplete.spec.js"], + testMatch: ["/test/e2e/inquirer/*.spec.js"], // Indicates whether the coverage information should be collected while executing the test collectCoverage: false, diff --git a/test/e2e/inquirer/fixtures/custom-quit-method.js b/test/e2e/inquirer/fixtures/custom-quit-method.js index 6170571..92a51b0 100644 --- a/test/e2e/inquirer/fixtures/custom-quit-method.js +++ b/test/e2e/inquirer/fixtures/custom-quit-method.js @@ -27,10 +27,11 @@ const questions = { const MyCli = class MyCli { constructor() { - this._cli = new Inquirer(questions, this.header.bind(this), { + this._cli = new Inquirer(this.header.bind(this), { name: "Custom Quit", action: this.customExitSelected.bind(this), }); + this._cli.questions = questions; this._selectedOption = "None"; } diff --git a/test/e2e/inquirer/fixtures/exit-logs-mode.js b/test/e2e/inquirer/fixtures/exit-logs-mode.js index 859c90f..0b1d344 100644 --- a/test/e2e/inquirer/fixtures/exit-logs-mode.js +++ b/test/e2e/inquirer/fixtures/exit-logs-mode.js @@ -31,7 +31,8 @@ const questions = { const MyCli = class MyCli { constructor() { - this._cli = new Inquirer(questions, this.header.bind(this)); + this._cli = new Inquirer(this.header.bind(this)); + this._cli.questions = questions; this._selectedOption = "None"; } diff --git a/test/e2e/inquirer/fixtures/logs-mode.js b/test/e2e/inquirer/fixtures/logs-mode.js index 3daf053..c8a25d9 100644 --- a/test/e2e/inquirer/fixtures/logs-mode.js +++ b/test/e2e/inquirer/fixtures/logs-mode.js @@ -31,7 +31,8 @@ const questions = { const MyCli = class MyCli { constructor() { - this._cli = new Inquirer(questions, this.header.bind(this)); + this._cli = new Inquirer(this.header.bind(this)); + this._cli.questions = questions; this._selectedOption = "None"; } diff --git a/test/e2e/inquirer/fixtures/readme-example.js b/test/e2e/inquirer/fixtures/readme-example.js index 3f2c897..6cec1d0 100644 --- a/test/e2e/inquirer/fixtures/readme-example.js +++ b/test/e2e/inquirer/fixtures/readme-example.js @@ -31,7 +31,8 @@ const questions = { const MyCli = class MyCli { constructor() { - this._cli = new Inquirer(questions, this.header.bind(this)); + this._cli = new Inquirer(this.header.bind(this)); + this._cli.questions = questions; this._selectedOption = "None"; } diff --git a/test/e2e/inquirer/fixtures/remove-listeners.js b/test/e2e/inquirer/fixtures/remove-listeners.js index a872387..4ed5042 100644 --- a/test/e2e/inquirer/fixtures/remove-listeners.js +++ b/test/e2e/inquirer/fixtures/remove-listeners.js @@ -31,7 +31,8 @@ const questions = { const MyCli = class MyCli { constructor() { - this._cli = new Inquirer(questions, this.header.bind(this)); + this._cli = new Inquirer(this.header.bind(this)); + this._cli.questions = questions; this._selectedOption = "None"; } From 6843b7dadd0c06fa64057a515ea06d6faeba111f Mon Sep 17 00:00:00 2001 From: "javier.brea" Date: Sun, 14 Feb 2021 19:08:37 +0100 Subject: [PATCH 12/22] test(e2e): Adapt all e2e tests --- .gitignore | 3 +- README.md | 4 +- jest.e2e.config.js | 2 +- src/Cli.js | 3 + test/e2e/inquirer/autocomplete.spec.js | 2 +- test/e2e/inquirer/exit-logs-mode.spec.js | 2 +- test/e2e/inquirer/logs-mode.spec.js | 2 +- test/e2e/inquirer/readme-example.spec.js | 2 +- test/e2e/inquirer/remove-listeners.spec.js | 2 +- test/e2e/{ => inquirer}/support/CliRunner.js | 0 .../support/InteractiveCliRunner.js | 0 .../support/utils.js} | 19 +- test/e2e/main/cli-disabled-arguments.spec.js | 182 ------------------ test/e2e/main/{ => v1}/cli-arguments.spec.js | 22 +-- .../cli-disabled-arguments.spec.js} | 83 ++++---- .../main/{ => v1}/cli-no-behaviors.spec.js | 16 +- .../fixtures/files-error/fixtures/users.js | 0 .../{ => v1}/fixtures/files-error/standard.js | 0 .../files-modification/fixtures/users.js | 0 .../files-modification/new-fixtures/users.js | 0 .../fixtures/files-modification/standard.js | 0 test/e2e/main/v1/fixtures/mocks/.gitkeep | 0 .../{ => v1}/fixtures/no-behaviors/base.js | 0 test/e2e/main/v1/fixtures/starter | 24 +++ .../fixtures/web-tutorial/fixtures/users.js | 0 .../fixtures/web-tutorial/standard.js | 0 .../e2e/main/{ => v1}/interactive-cli.spec.js | 18 +- test/e2e/{ => main/v1}/support/start.js | 2 +- test/e2e/{ => main/v1}/support/utils.js | 4 +- .../main/{ => v1}/web-tutorial-cli.spec.js | 11 +- .../{ => v1}/web-tutorial-files-watch.spec.js | 49 +++-- test/unit/Core.mocks.js | 1 + 32 files changed, 169 insertions(+), 284 deletions(-) rename test/e2e/{ => inquirer}/support/CliRunner.js (100%) rename test/e2e/{ => inquirer}/support/InteractiveCliRunner.js (100%) rename test/e2e/{mocks-server => inquirer/support/utils.js} (73%) mode change 100755 => 100644 delete mode 100644 test/e2e/main/cli-disabled-arguments.spec.js rename test/e2e/main/{ => v1}/cli-arguments.spec.js (81%) rename test/e2e/main/{deprecated.cli-arguments.spec.js => v1/cli-disabled-arguments.spec.js} (58%) rename test/e2e/main/{ => v1}/cli-no-behaviors.spec.js (73%) rename test/e2e/main/{ => v1}/fixtures/files-error/fixtures/users.js (100%) rename test/e2e/main/{ => v1}/fixtures/files-error/standard.js (100%) rename test/e2e/main/{ => v1}/fixtures/files-modification/fixtures/users.js (100%) rename test/e2e/main/{ => v1}/fixtures/files-modification/new-fixtures/users.js (100%) rename test/e2e/main/{ => v1}/fixtures/files-modification/standard.js (100%) create mode 100644 test/e2e/main/v1/fixtures/mocks/.gitkeep rename test/e2e/main/{ => v1}/fixtures/no-behaviors/base.js (100%) create mode 100755 test/e2e/main/v1/fixtures/starter rename test/e2e/main/{ => v1}/fixtures/web-tutorial/fixtures/users.js (100%) rename test/e2e/main/{ => v1}/fixtures/web-tutorial/standard.js (100%) rename test/e2e/main/{ => v1}/interactive-cli.spec.js (91%) rename test/e2e/{ => main/v1}/support/start.js (94%) rename test/e2e/{ => main/v1}/support/utils.js (93%) rename test/e2e/main/{ => v1}/web-tutorial-cli.spec.js (90%) rename test/e2e/main/{ => v1}/web-tutorial-files-watch.spec.js (86%) diff --git a/.gitignore b/.gitignore index c89fba3..51b8af7 100644 --- a/.gitignore +++ b/.gitignore @@ -9,7 +9,8 @@ # tests /coverage /mocks -/test/e2e/main/fixtures/files-watch +/test/e2e/main/v1/fixtures/files-watch +/test/e2e/main/v1/fixtures/mocks.config.js # misc .DS_Store diff --git a/README.md b/README.md index 808d308..80dde4d 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ This plugin is preinstalled in the [main distribution of the Mocks Server project][main-distribution-url]. _If you want ot install it by yourself, you can refer to the [plugins documentation][plugins-url]._ -![Interactive CLI](assets/cli_animation.gif) +![Interactive CLI][animated-image-url] ## Main features @@ -24,7 +24,7 @@ This plugin is preinstalled in the [main distribution of the Mocks Server projec ## Options -* `cli`: `` Start interactive CLI or not. Default is `true`. Use `false` to disable it. +* `cli`: `` Start interactive CLI or not. Default is `true`. Use `false` to disable it _(`--no-cli`) when using command line arguments)_ ## Support diff --git a/jest.e2e.config.js b/jest.e2e.config.js index 722f099..35aae60 100644 --- a/jest.e2e.config.js +++ b/jest.e2e.config.js @@ -6,7 +6,7 @@ module.exports = { clearMocks: true, testMatch: ["/test/e2e/**/*.spec.js"], - testMatch: ["/test/e2e/inquirer/*.spec.js"], + // testMatch: ["/test/e2e/main/v1/web-tutorial-files-watch.spec.js"], // Indicates whether the coverage information should be collected while executing the test collectCoverage: false, diff --git a/src/Cli.js b/src/Cli.js index 98a1a82..565660d 100644 --- a/src/Cli.js +++ b/src/Cli.js @@ -163,10 +163,12 @@ class Cli { this._started = true; if (this._stopListeningChangeMocks) { this._stopListeningChangeMocks(); + this._stopListeningChangeLegacyMocks(); this._stopListeningChangeAlerts(); } this._stopListeningChangeAlerts = this._core.onChangeAlerts(this._onChangeAlerts); this._stopListeningChangeMocks = this._core.onChangeMocks(this._onChangeMocks); + this._stopListeningChangeLegacyMocks = this._core.onChangeLegacyMocks(this._onChangeMocks); this._logLevel = this._settings.get("log"); this._silentTraces(); this._displayMainMenu(); @@ -179,6 +181,7 @@ class Cli { } this._started = false; this._stopListeningChangeMocks(); + this._stopListeningChangeLegacyMocks(); this._stopListeningChangeAlerts(); this._settings.set("log", this._logLevel); this._cli.logsMode(); diff --git a/test/e2e/inquirer/autocomplete.spec.js b/test/e2e/inquirer/autocomplete.spec.js index c86e5df..e7c3a24 100644 --- a/test/e2e/inquirer/autocomplete.spec.js +++ b/test/e2e/inquirer/autocomplete.spec.js @@ -11,7 +11,7 @@ Unless required by applicable law or agreed to in writing, software distributed const path = require("path"); -const CliRunner = require("../support/CliRunner"); +const CliRunner = require("./support/CliRunner"); const END_SCREEN = "Exit"; diff --git a/test/e2e/inquirer/exit-logs-mode.spec.js b/test/e2e/inquirer/exit-logs-mode.spec.js index abe5983..53a727b 100644 --- a/test/e2e/inquirer/exit-logs-mode.spec.js +++ b/test/e2e/inquirer/exit-logs-mode.spec.js @@ -11,7 +11,7 @@ Unless required by applicable law or agreed to in writing, software distributed const path = require("path"); -const CliRunner = require("../support/CliRunner"); +const CliRunner = require("./support/CliRunner"); const END_SCREEN = "Exit"; diff --git a/test/e2e/inquirer/logs-mode.spec.js b/test/e2e/inquirer/logs-mode.spec.js index daf2463..e97689d 100644 --- a/test/e2e/inquirer/logs-mode.spec.js +++ b/test/e2e/inquirer/logs-mode.spec.js @@ -11,7 +11,7 @@ Unless required by applicable law or agreed to in writing, software distributed const path = require("path"); -const CliRunner = require("../support/CliRunner"); +const CliRunner = require("./support/CliRunner"); const END_SCREEN = "Exit"; diff --git a/test/e2e/inquirer/readme-example.spec.js b/test/e2e/inquirer/readme-example.spec.js index d6e4364..25a048a 100644 --- a/test/e2e/inquirer/readme-example.spec.js +++ b/test/e2e/inquirer/readme-example.spec.js @@ -11,7 +11,7 @@ Unless required by applicable law or agreed to in writing, software distributed const path = require("path"); -const CliRunner = require("../support/CliRunner"); +const CliRunner = require("./support/CliRunner"); const END_SCREEN = "Exit"; diff --git a/test/e2e/inquirer/remove-listeners.spec.js b/test/e2e/inquirer/remove-listeners.spec.js index bb5a187..c4b7066 100644 --- a/test/e2e/inquirer/remove-listeners.spec.js +++ b/test/e2e/inquirer/remove-listeners.spec.js @@ -11,7 +11,7 @@ Unless required by applicable law or agreed to in writing, software distributed const path = require("path"); -const CliRunner = require("../support/CliRunner"); +const CliRunner = require("./support/CliRunner"); const END_SCREEN = "Exit"; diff --git a/test/e2e/support/CliRunner.js b/test/e2e/inquirer/support/CliRunner.js similarity index 100% rename from test/e2e/support/CliRunner.js rename to test/e2e/inquirer/support/CliRunner.js diff --git a/test/e2e/support/InteractiveCliRunner.js b/test/e2e/inquirer/support/InteractiveCliRunner.js similarity index 100% rename from test/e2e/support/InteractiveCliRunner.js rename to test/e2e/inquirer/support/InteractiveCliRunner.js diff --git a/test/e2e/mocks-server b/test/e2e/inquirer/support/utils.js old mode 100755 new mode 100644 similarity index 73% rename from test/e2e/mocks-server rename to test/e2e/inquirer/support/utils.js index c52c9b5..e83c8fb --- a/test/e2e/mocks-server +++ b/test/e2e/inquirer/support/utils.js @@ -1,10 +1,5 @@ -#!/usr/bin/env node -"use strict"; - -require("./support/start").start(); - /* -Copyright 2019 XbyOrange +Copyright 2019 Javier Brea Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at @@ -12,3 +7,15 @@ http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ + +const wait = (time = 1000) => { + return new Promise((resolve) => { + setTimeout(() => { + resolve(); + }, time); + }); +}; + +module.exports = { + wait, +}; diff --git a/test/e2e/main/cli-disabled-arguments.spec.js b/test/e2e/main/cli-disabled-arguments.spec.js deleted file mode 100644 index 052b89c..0000000 --- a/test/e2e/main/cli-disabled-arguments.spec.js +++ /dev/null @@ -1,182 +0,0 @@ -/* -Copyright 2019 Javier Brea - -Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at - -http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. -*/ - -const path = require("path"); -const { request, wait, TimeCounter, BINARY_PATH } = require("../support/utils"); -const CliRunner = require("../support/CliRunner"); - -describe("command line arguments with cli disabled", () => { - const cwdPath = path.resolve(__dirname, "fixtures"); - let cli; - - afterEach(async () => { - await cli.kill(); - }); - - describe("interactive cli", () => { - it("should not be started", async () => { - cli = new CliRunner([BINARY_PATH, "--path=web-tutorial", "--cli=false"], { - cwd: cwdPath, - }); - await wait(3000); - expect(cli.logs).toEqual(expect.not.stringContaining("Select action")); - }); - }); - - describe("path option", () => { - it("should set mocks folder", async () => { - cli = new CliRunner([BINARY_PATH, "--path=web-tutorial", "--cli=false"], { - cwd: cwdPath, - }); - await wait(); - const users = await request("/api/users"); - expect(users).toEqual([ - { id: 1, name: "John Doe" }, - { id: 2, name: "Jane Doe" }, - ]); - }); - }); - - describe("behavior option", () => { - describe("when not provided", () => { - it("should set as current behavior the first one found", async () => { - cli = new CliRunner([BINARY_PATH, "--path=web-tutorial", "--cli=false"], { - cwd: cwdPath, - }); - await wait(); - const users = await request("/api/users/2"); - expect(users).toEqual({ id: 1, name: "John Doe" }); - }); - }); - - describe("when provided and exists", () => { - it("should set current behavior", async () => { - cli = new CliRunner( - [BINARY_PATH, "--path=web-tutorial", "--behavior=dynamic", "--cli=false"], - { - cwd: cwdPath, - } - ); - await wait(); - const users = await request("/api/users/2"); - expect(users).toEqual({ id: 2, name: "Jane Doe" }); - }); - }); - - describe("when provided and does not exist", () => { - it("should print a warning", async () => { - cli = new CliRunner( - [BINARY_PATH, "--path=web-tutorial", "--behavior=foo", "--cli=false"], - { - cwd: cwdPath, - } - ); - await wait(); - expect(cli.logs).toEqual(expect.stringContaining('Defined behavior "foo" was not found')); - }); - - it("should set as current behavior the first one found", async () => { - cli = new CliRunner( - [BINARY_PATH, "--path=web-tutorial", "--behavior=foo", "--cli=false"], - { - cwd: cwdPath, - } - ); - await wait(); - const users = await request("/api/users/2"); - expect(users).toEqual({ id: 1, name: "John Doe" }); - }); - }); - }); - - describe("features option", () => { - it("should set mocks folder", async () => { - cli = new CliRunner([BINARY_PATH, "--features=web-tutorial", "--cli=false"], { - cwd: cwdPath, - }); - await wait(); - const users = await request("/api/users"); - expect(users).toEqual([ - { id: 1, name: "John Doe" }, - { id: 2, name: "Jane Doe" }, - ]); - }); - }); - - describe("feature option", () => { - describe("when not provided", () => { - it("should set as current behavior the first one found", async () => { - cli = new CliRunner([BINARY_PATH, "--features=web-tutorial", "--cli=false"], { - cwd: cwdPath, - }); - await wait(); - const users = await request("/api/users/2"); - expect(users).toEqual({ id: 1, name: "John Doe" }); - }); - }); - - describe("when provided and exists", () => { - it("should set current behavior", async () => { - cli = new CliRunner( - [BINARY_PATH, "--features=web-tutorial", "--feature=dynamic", "--cli=false"], - { - cwd: cwdPath, - } - ); - await wait(); - const users = await request("/api/users/2"); - expect(users).toEqual({ id: 2, name: "Jane Doe" }); - }); - }); - - describe("when provided and does not exist", () => { - it("should print a warning", async () => { - cli = new CliRunner( - [BINARY_PATH, "--features=web-tutorial", "--feature=foo", "--cli=false"], - { - cwd: cwdPath, - } - ); - await wait(); - expect(cli.logs).toEqual(expect.stringContaining('Defined behavior "foo" was not found')); - }); - - it("should set as current behavior the first one found", async () => { - cli = new CliRunner( - [BINARY_PATH, "--features=web-tutorial", "--feature=foo", "--cli=false"], - { - cwd: cwdPath, - } - ); - await wait(); - const users = await request("/api/users/2"); - expect(users).toEqual({ id: 1, name: "John Doe" }); - }); - }); - }); - - describe("delay option", () => { - it("should set delay", async () => { - expect.assertions(2); - cli = new CliRunner([BINARY_PATH, "--path=web-tutorial", "--delay=2000", "--cli=false"], { - cwd: cwdPath, - }); - await wait(); - const timeCounter = new TimeCounter(); - const users = await request("/api/users"); - timeCounter.stop(); - expect(users).toEqual([ - { id: 1, name: "John Doe" }, - { id: 2, name: "Jane Doe" }, - ]); - expect(timeCounter.total).toBeGreaterThan(1999); - }); - }); -}); diff --git a/test/e2e/main/cli-arguments.spec.js b/test/e2e/main/v1/cli-arguments.spec.js similarity index 81% rename from test/e2e/main/cli-arguments.spec.js rename to test/e2e/main/v1/cli-arguments.spec.js index e16dfa1..55051d7 100644 --- a/test/e2e/main/cli-arguments.spec.js +++ b/test/e2e/main/v1/cli-arguments.spec.js @@ -9,8 +9,8 @@ Unless required by applicable law or agreed to in writing, software distributed */ const path = require("path"); -const { request, wait, TimeCounter, BINARY_PATH } = require("../support/utils"); -const CliRunner = require("../support/CliRunner"); +const { request, wait, TimeCounter, BINARY_PATH } = require("./support/utils"); +const CliRunner = require("../../inquirer/support/CliRunner"); describe("command line arguments", () => { const cwdPath = path.resolve(__dirname, "fixtures"); @@ -20,10 +20,10 @@ describe("command line arguments", () => { await cli.kill(); }); - describe("path option", () => { + describe("pathLegacy option", () => { it("should set mocks folder", async () => { expect.assertions(2); - cli = new CliRunner([BINARY_PATH, "--path=web-tutorial"], { + cli = new CliRunner([BINARY_PATH, "--pathLegacy=web-tutorial"], { cwd: cwdPath, }); await wait(); @@ -32,7 +32,7 @@ describe("command line arguments", () => { { id: 1, name: "John Doe" }, { id: 2, name: "Jane Doe" }, ]); - expect(cli.logs).toEqual(expect.stringContaining("Behaviors: 3")); + expect(cli.logs).toEqual(expect.stringContaining("behaviors: 3")); }); }); @@ -40,7 +40,7 @@ describe("command line arguments", () => { describe("when not provided", () => { it("should set as current behavior the first one found", async () => { expect.assertions(2); - cli = new CliRunner([BINARY_PATH, "--path=web-tutorial"], { + cli = new CliRunner([BINARY_PATH, "--pathLegacy=web-tutorial"], { cwd: cwdPath, }); await wait(); @@ -53,7 +53,7 @@ describe("command line arguments", () => { describe("when provided and exists", () => { it("should set current behavior", async () => { expect.assertions(2); - cli = new CliRunner([BINARY_PATH, "--path=web-tutorial", "--behavior=dynamic"], { + cli = new CliRunner([BINARY_PATH, "--pathLegacy=web-tutorial", "--behavior=dynamic"], { cwd: cwdPath, }); await wait(); @@ -65,7 +65,7 @@ describe("command line arguments", () => { describe("when provided and does not exist", () => { it("should display an alert", async () => { - cli = new CliRunner([BINARY_PATH, "--path=web-tutorial", "--behavior=foo"], { + cli = new CliRunner([BINARY_PATH, "--pathLegacy=web-tutorial", "--behavior=foo"], { cwd: cwdPath, }); await wait(); @@ -77,7 +77,7 @@ describe("command line arguments", () => { it("should set as current behavior the first one found", async () => { expect.assertions(3); - cli = new CliRunner([BINARY_PATH, "--path=web-tutorial", "--behavior=foo"], { + cli = new CliRunner([BINARY_PATH, "--pathLegacy=web-tutorial", "--behavior=foo"], { cwd: cwdPath, }); await wait(); @@ -94,7 +94,7 @@ describe("command line arguments", () => { describe("delay option", () => { it("should set delay", async () => { expect.assertions(3); - cli = new CliRunner([BINARY_PATH, "--path=web-tutorial", "--delay=2000"], { + cli = new CliRunner([BINARY_PATH, "--pathLegacy=web-tutorial", "--delay=2000"], { cwd: cwdPath, }); await wait(); @@ -112,7 +112,7 @@ describe("command line arguments", () => { describe("log option", () => { it("should set log level", async () => { - cli = new CliRunner([BINARY_PATH, "--path=web-tutorial", "--log=debug"], { + cli = new CliRunner([BINARY_PATH, "--pathLegacy=web-tutorial", "--log=debug"], { cwd: cwdPath, }); await wait(); diff --git a/test/e2e/main/deprecated.cli-arguments.spec.js b/test/e2e/main/v1/cli-disabled-arguments.spec.js similarity index 58% rename from test/e2e/main/deprecated.cli-arguments.spec.js rename to test/e2e/main/v1/cli-disabled-arguments.spec.js index 076953c..3a02b29 100644 --- a/test/e2e/main/deprecated.cli-arguments.spec.js +++ b/test/e2e/main/v1/cli-disabled-arguments.spec.js @@ -9,10 +9,10 @@ Unless required by applicable law or agreed to in writing, software distributed */ const path = require("path"); -const { request, wait, BINARY_PATH } = require("../support/utils"); -const CliRunner = require("../support/CliRunner"); +const { request, wait, TimeCounter, BINARY_PATH } = require("./support/utils"); +const CliRunner = require("../../inquirer/support/CliRunner"); -describe("deprecated command line arguments", () => { +describe("command line arguments with cli disabled", () => { const cwdPath = path.resolve(__dirname, "fixtures"); let cli; @@ -20,26 +20,19 @@ describe("deprecated command line arguments", () => { await cli.kill(); }); - describe("behaviors option", () => { - it("should set mocks folder", async () => { - expect.assertions(2); - cli = new CliRunner([BINARY_PATH, "--behaviors=web-tutorial"], { + describe("interactive cli", () => { + it("should not be started", async () => { + cli = new CliRunner([BINARY_PATH, "--pathLegacy=web-tutorial", "--no-cli"], { cwd: cwdPath, }); - await wait(); - const users = await request("/api/users"); - expect(users).toEqual([ - { id: 1, name: "John Doe" }, - { id: 2, name: "Jane Doe" }, - ]); - expect(cli.logs).toEqual(expect.stringContaining("Behaviors: 3")); + await wait(3000); + expect(cli.logs).toEqual(expect.not.stringContaining("Select action")); }); }); - describe("features option", () => { + describe("path option", () => { it("should set mocks folder", async () => { - expect.assertions(2); - cli = new CliRunner([BINARY_PATH, "--features=web-tutorial"], { + cli = new CliRunner([BINARY_PATH, "--pathLegacy=web-tutorial", "--no-cli"], { cwd: cwdPath, }); await wait(); @@ -48,56 +41,76 @@ describe("deprecated command line arguments", () => { { id: 1, name: "John Doe" }, { id: 2, name: "Jane Doe" }, ]); - expect(cli.logs).toEqual(expect.stringContaining("Behaviors: 3")); }); }); - describe("feature option", () => { + describe("behavior option", () => { describe("when not provided", () => { it("should set as current behavior the first one found", async () => { - expect.assertions(2); - cli = new CliRunner([BINARY_PATH, "--features=web-tutorial"], { + cli = new CliRunner([BINARY_PATH, "--pathLegacy=web-tutorial", "--no-cli"], { cwd: cwdPath, }); await wait(); const users = await request("/api/users/2"); expect(users).toEqual({ id: 1, name: "John Doe" }); - expect(cli.logs).toEqual(expect.stringContaining("Current behavior: standard")); }); }); describe("when provided and exists", () => { it("should set current behavior", async () => { - expect.assertions(2); - cli = new CliRunner([BINARY_PATH, "--features=web-tutorial", "--feature=dynamic"], { - cwd: cwdPath, - }); + cli = new CliRunner( + [BINARY_PATH, "--pathLegacy=web-tutorial", "--behavior=dynamic", "--no-cli"], + { + cwd: cwdPath, + } + ); await wait(); const users = await request("/api/users/2"); expect(users).toEqual({ id: 2, name: "Jane Doe" }); - expect(cli.logs).toEqual(expect.stringContaining("Current behavior: dynamic")); }); }); describe("when provided and does not exist", () => { it("should print a warning", async () => { - cli = new CliRunner([BINARY_PATH, "--features=web-tutorial", "--feature=foo"], { - cwd: cwdPath, - }); + cli = new CliRunner( + [BINARY_PATH, "--pathLegacy=web-tutorial", "--behavior=foo", "--no-cli"], + { + cwd: cwdPath, + } + ); await wait(); expect(cli.logs).toEqual(expect.stringContaining('Defined behavior "foo" was not found')); }); it("should set as current behavior the first one found", async () => { - expect.assertions(2); - cli = new CliRunner([BINARY_PATH, "--features=web-tutorial", "--feature=foo"], { - cwd: cwdPath, - }); + cli = new CliRunner( + [BINARY_PATH, "--pathLegacy=web-tutorial", "--behavior=foo", "--no-cli"], + { + cwd: cwdPath, + } + ); await wait(); const users = await request("/api/users/2"); expect(users).toEqual({ id: 1, name: "John Doe" }); - expect(cli.logs).toEqual(expect.stringContaining("Current behavior: standard")); }); }); }); + + describe("delay option", () => { + it("should set delay", async () => { + expect.assertions(2); + cli = new CliRunner([BINARY_PATH, "--pathLegacy=web-tutorial", "--delay=2000", "--no-cli"], { + cwd: cwdPath, + }); + await wait(); + const timeCounter = new TimeCounter(); + const users = await request("/api/users"); + timeCounter.stop(); + expect(users).toEqual([ + { id: 1, name: "John Doe" }, + { id: 2, name: "Jane Doe" }, + ]); + expect(timeCounter.total).toBeGreaterThan(1999); + }); + }); }); diff --git a/test/e2e/main/cli-no-behaviors.spec.js b/test/e2e/main/v1/cli-no-behaviors.spec.js similarity index 73% rename from test/e2e/main/cli-no-behaviors.spec.js rename to test/e2e/main/v1/cli-no-behaviors.spec.js index 7b6b08d..8eb21e0 100644 --- a/test/e2e/main/cli-no-behaviors.spec.js +++ b/test/e2e/main/v1/cli-no-behaviors.spec.js @@ -9,8 +9,8 @@ Unless required by applicable law or agreed to in writing, software distributed */ const path = require("path"); -const { wait, BINARY_PATH } = require("../support/utils"); -const CliRunner = require("../support/CliRunner"); +const { wait, BINARY_PATH } = require("./support/utils"); +const CliRunner = require("../../inquirer/support/CliRunner"); describe("with no behaviors", () => { const cwdPath = path.resolve(__dirname, "fixtures"); @@ -20,8 +20,8 @@ describe("with no behaviors", () => { await cli.kill(); }); - it("should display alerts", async () => { - cli = new CliRunner([BINARY_PATH, "--behavior=foo", "--path=no-behaviors"], { + it.skip("should display alerts", async () => { + cli = new CliRunner([BINARY_PATH, "--behavior=foo", "--pathLegacy=no-behaviors"], { cwd: cwdPath, }); await wait(); @@ -30,7 +30,7 @@ describe("with no behaviors", () => { }); it("should print a dash as current behavior", async () => { - cli = new CliRunner([BINARY_PATH, "--path=no-behaviors"], { + cli = new CliRunner([BINARY_PATH, "--pathLegacy=no-behaviors"], { cwd: cwdPath, }); await wait(); @@ -38,15 +38,15 @@ describe("with no behaviors", () => { }); it("should print behaviors as 0", async () => { - cli = new CliRunner([BINARY_PATH, "--path=no-behaviors"], { + cli = new CliRunner([BINARY_PATH, "--pathLegacy=no-behaviors"], { cwd: cwdPath, }); await wait(); - expect(cli.logs).toEqual(expect.stringContaining("Behaviors: 0")); + expect(cli.logs).toEqual(expect.stringContaining("behaviors: 0")); }); it("should print current fixtures as 0", async () => { - cli = new CliRunner([BINARY_PATH, "--path=no-behaviors"], { + cli = new CliRunner([BINARY_PATH, "--pathLegacy=no-behaviors"], { cwd: cwdPath, }); await wait(); diff --git a/test/e2e/main/fixtures/files-error/fixtures/users.js b/test/e2e/main/v1/fixtures/files-error/fixtures/users.js similarity index 100% rename from test/e2e/main/fixtures/files-error/fixtures/users.js rename to test/e2e/main/v1/fixtures/files-error/fixtures/users.js diff --git a/test/e2e/main/fixtures/files-error/standard.js b/test/e2e/main/v1/fixtures/files-error/standard.js similarity index 100% rename from test/e2e/main/fixtures/files-error/standard.js rename to test/e2e/main/v1/fixtures/files-error/standard.js diff --git a/test/e2e/main/fixtures/files-modification/fixtures/users.js b/test/e2e/main/v1/fixtures/files-modification/fixtures/users.js similarity index 100% rename from test/e2e/main/fixtures/files-modification/fixtures/users.js rename to test/e2e/main/v1/fixtures/files-modification/fixtures/users.js diff --git a/test/e2e/main/fixtures/files-modification/new-fixtures/users.js b/test/e2e/main/v1/fixtures/files-modification/new-fixtures/users.js similarity index 100% rename from test/e2e/main/fixtures/files-modification/new-fixtures/users.js rename to test/e2e/main/v1/fixtures/files-modification/new-fixtures/users.js diff --git a/test/e2e/main/fixtures/files-modification/standard.js b/test/e2e/main/v1/fixtures/files-modification/standard.js similarity index 100% rename from test/e2e/main/fixtures/files-modification/standard.js rename to test/e2e/main/v1/fixtures/files-modification/standard.js diff --git a/test/e2e/main/v1/fixtures/mocks/.gitkeep b/test/e2e/main/v1/fixtures/mocks/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/test/e2e/main/fixtures/no-behaviors/base.js b/test/e2e/main/v1/fixtures/no-behaviors/base.js similarity index 100% rename from test/e2e/main/fixtures/no-behaviors/base.js rename to test/e2e/main/v1/fixtures/no-behaviors/base.js diff --git a/test/e2e/main/v1/fixtures/starter b/test/e2e/main/v1/fixtures/starter new file mode 100755 index 0000000..f57fadd --- /dev/null +++ b/test/e2e/main/v1/fixtures/starter @@ -0,0 +1,24 @@ +#!/usr/bin/env node +"use strict"; + +const { Core } = require("@mocks-server/core"); +const InquirerCli = require("../../../../../index"); + +const handleError = (error) => { + console.error(`Error: ${error.message}`); + process.exitCode = 1; +}; + +const start = () => { + try { + const mocksServer = new Core({ + plugins: [InquirerCli], + }); + + return mocksServer.start().catch(handleError); + } catch (error) { + return handleError(error); + } +}; + +start(); diff --git a/test/e2e/main/fixtures/web-tutorial/fixtures/users.js b/test/e2e/main/v1/fixtures/web-tutorial/fixtures/users.js similarity index 100% rename from test/e2e/main/fixtures/web-tutorial/fixtures/users.js rename to test/e2e/main/v1/fixtures/web-tutorial/fixtures/users.js diff --git a/test/e2e/main/fixtures/web-tutorial/standard.js b/test/e2e/main/v1/fixtures/web-tutorial/standard.js similarity index 100% rename from test/e2e/main/fixtures/web-tutorial/standard.js rename to test/e2e/main/v1/fixtures/web-tutorial/standard.js diff --git a/test/e2e/main/interactive-cli.spec.js b/test/e2e/main/v1/interactive-cli.spec.js similarity index 91% rename from test/e2e/main/interactive-cli.spec.js rename to test/e2e/main/v1/interactive-cli.spec.js index 98bc3b9..8ad6f97 100644 --- a/test/e2e/main/interactive-cli.spec.js +++ b/test/e2e/main/v1/interactive-cli.spec.js @@ -9,15 +9,16 @@ Unless required by applicable law or agreed to in writing, software distributed */ const path = require("path"); -const { request, wait, TimeCounter, BINARY_PATH } = require("../support/utils"); -const InteractiveCliRunner = require("../support/InteractiveCliRunner"); +const { request, wait, TimeCounter, BINARY_PATH } = require("./support/utils"); +const InteractiveCliRunner = require("../../inquirer/support/InteractiveCliRunner"); describe("interactive CLI", () => { + jest.setTimeout(15000); let cli; const cwdPath = path.resolve(__dirname, "fixtures"); beforeAll(async () => { - cli = new InteractiveCliRunner([BINARY_PATH, "--path=web-tutorial", "--behavior=foo"], { + cli = new InteractiveCliRunner([BINARY_PATH, "--pathLegacy=web-tutorial", "--behavior=foo"], { cwd: cwdPath, }); await wait(); @@ -40,7 +41,7 @@ describe("interactive CLI", () => { }); it("should have 3 behaviors available", async () => { - expect(cli.logs).toEqual(expect.stringContaining("Behaviors: 3")); + expect(cli.logs).toEqual(expect.stringContaining("behaviors: 3")); }); it("should serve users collection mock under the /api/users path", async () => { @@ -64,13 +65,14 @@ describe("interactive CLI", () => { describe('When changing current behavior to "dynamic"', () => { it("should display new selected behavior", async () => { + await cli.cursorDown(8); await cli.pressEnter(); await cli.cursorDown(2); const newScreen = await cli.pressEnter(); expect(newScreen).toEqual(expect.stringContaining("Current behavior: dynamic")); }); - it("should have removed alert", async () => { + it.skip("should have removed alert", async () => { expect(cli.currentScreen).toEqual(expect.not.stringContaining("ALERTS")); }); @@ -103,7 +105,7 @@ describe("interactive CLI", () => { describe("When changing logs level", () => { it("should display new selected log level", async () => { - await cli.cursorDown(3); + await cli.cursorDown(5); await cli.pressEnter(); await cli.cursorDown(2); const newScreen = await cli.pressEnter(); @@ -114,7 +116,7 @@ describe("interactive CLI", () => { describe("When displaying logs", () => { it("should log requests", async () => { expect.assertions(2); - await cli.cursorDown(5); + await cli.cursorDown(7); await cli.pressEnter(); await request("/api/users"); await wait(1000); @@ -128,7 +130,7 @@ describe("interactive CLI", () => { describe("When changing delay time", () => { it("should display new selected delay time", async () => { - await cli.cursorDown(); + await cli.cursorDown(3); await cli.pressEnter(); await cli.write(2000); const newScreen = await cli.pressEnter(); diff --git a/test/e2e/support/start.js b/test/e2e/main/v1/support/start.js similarity index 94% rename from test/e2e/support/start.js rename to test/e2e/main/v1/support/start.js index b211dcb..7755bb7 100644 --- a/test/e2e/support/start.js +++ b/test/e2e/main/v1/support/start.js @@ -12,7 +12,7 @@ Unless required by applicable law or agreed to in writing, software distributed "use strict"; const { Core } = require("@mocks-server/core"); -const InquirerCli = require("../../../index"); +const InquirerCli = require("../../../../../index"); const handleError = (error) => { console.error(`Error: ${error.message}`); diff --git a/test/e2e/support/utils.js b/test/e2e/main/v1/support/utils.js similarity index 93% rename from test/e2e/support/utils.js rename to test/e2e/main/v1/support/utils.js index df99403..6115c1a 100644 --- a/test/e2e/support/utils.js +++ b/test/e2e/main/v1/support/utils.js @@ -14,14 +14,14 @@ const requestPromise = require("request-promise"); const SERVER_PORT = 3100; -const BINARY_PATH = "../../mocks-server"; +const BINARY_PATH = "./starter"; const defaultRequestOptions = { method: "GET", }; const fixturesFolder = (folderName) => { - return path.resolve(__dirname, "..", "main", "fixtures", folderName); + return path.resolve(__dirname, "..", "fixtures", folderName); }; const request = (uri, options = {}) => { diff --git a/test/e2e/main/web-tutorial-cli.spec.js b/test/e2e/main/v1/web-tutorial-cli.spec.js similarity index 90% rename from test/e2e/main/web-tutorial-cli.spec.js rename to test/e2e/main/v1/web-tutorial-cli.spec.js index fffafaa..7c4755c 100644 --- a/test/e2e/main/web-tutorial-cli.spec.js +++ b/test/e2e/main/v1/web-tutorial-cli.spec.js @@ -9,15 +9,16 @@ Unless required by applicable law or agreed to in writing, software distributed */ const path = require("path"); -const { request, wait, BINARY_PATH } = require("../support/utils"); -const InteractiveCliRunner = require("../support/InteractiveCliRunner"); +const { request, wait, BINARY_PATH } = require("./support/utils"); +const InteractiveCliRunner = require("../../inquirer/support/InteractiveCliRunner"); describe("web tutorial", () => { + jest.setTimeout(15000); let cli; const cwdPath = path.resolve(__dirname, "fixtures"); beforeAll(async () => { - cli = new InteractiveCliRunner([BINARY_PATH, "--path=web-tutorial"], { + cli = new InteractiveCliRunner([BINARY_PATH, "--pathLegacy=web-tutorial"], { cwd: cwdPath, }); await wait(); @@ -29,7 +30,7 @@ describe("web tutorial", () => { describe("When started", () => { it("should have 3 behaviors available", async () => { - expect(cli.logs).toEqual(expect.stringContaining("Behaviors: 3")); + expect(cli.logs).toEqual(expect.stringContaining("behaviors: 3")); }); it("should serve users collection mock under the /api/users path", async () => { @@ -53,6 +54,7 @@ describe("web tutorial", () => { describe('When changing current behavior to "user2"', () => { it("should display new selected behavior", async () => { + await cli.cursorDown(8); await cli.pressEnter(); await cli.cursorDown(); const newScreen = await cli.pressEnter(); @@ -80,6 +82,7 @@ describe("web tutorial", () => { describe('When changing current behavior to "dynamic"', () => { it("should display new selected behavior", async () => { + await cli.cursorDown(8); await cli.pressEnter(); await cli.cursorDown(2); const newScreen = await cli.pressEnter(); diff --git a/test/e2e/main/web-tutorial-files-watch.spec.js b/test/e2e/main/v1/web-tutorial-files-watch.spec.js similarity index 86% rename from test/e2e/main/web-tutorial-files-watch.spec.js rename to test/e2e/main/v1/web-tutorial-files-watch.spec.js index 747f129..ca77ed4 100644 --- a/test/e2e/main/web-tutorial-files-watch.spec.js +++ b/test/e2e/main/v1/web-tutorial-files-watch.spec.js @@ -10,17 +10,18 @@ Unless required by applicable law or agreed to in writing, software distributed const path = require("path"); const fsExtra = require("fs-extra"); -const { request, fixturesFolder, wait, BINARY_PATH } = require("../support/utils"); -const InteractiveCliRunner = require("../support/InteractiveCliRunner"); +const { request, fixturesFolder, wait, BINARY_PATH } = require("./support/utils"); +const InteractiveCliRunner = require("../../inquirer/support/InteractiveCliRunner"); describe("files watcher", () => { + jest.setTimeout(15000); const cwdPath = path.resolve(__dirname, "fixtures"); let interactiveCli; beforeAll(async () => { fsExtra.removeSync(fixturesFolder("files-watch")); fsExtra.copySync(fixturesFolder("web-tutorial"), fixturesFolder("files-watch")); - interactiveCli = new InteractiveCliRunner([BINARY_PATH, "--path=files-watch"], { + interactiveCli = new InteractiveCliRunner([BINARY_PATH, "--pathLegacy=files-watch"], { cwd: cwdPath, }); await wait(); @@ -33,7 +34,7 @@ describe("files watcher", () => { describe("When started", () => { it("should display available behaviors in CLI", async () => { await wait(500); - expect(interactiveCli.logs).toEqual(expect.stringContaining("Behaviors: 3")); + expect(interactiveCli.logs).toEqual(expect.stringContaining("behaviors: 3")); }); it("should display current behavior in CLI", async () => { @@ -62,12 +63,12 @@ describe("files watcher", () => { describe("When files are modified", () => { beforeAll(async () => { fsExtra.copySync(fixturesFolder("files-modification"), fixturesFolder("files-watch")); - await wait(2000); + await wait(5000); }); describe("without changing current behavior", () => { it("should display available behaviors in CLI", async () => { - expect(interactiveCli.logs).toEqual(expect.stringContaining("Behaviors: 4")); + expect(interactiveCli.currentScreen).toEqual(expect.stringContaining("behaviors: 4")); }); it("should serve users collection mock under the /api/users path", async () => { @@ -91,6 +92,7 @@ describe("files watcher", () => { describe('When changing current behavior to "user2"', () => { beforeAll(async () => { + await interactiveCli.cursorDown(8); await interactiveCli.pressEnter(); await interactiveCli.cursorDown(); await interactiveCli.pressEnter(); @@ -122,6 +124,7 @@ describe("files watcher", () => { describe('When changing current behavior to "dynamic"', () => { beforeAll(async () => { + await interactiveCli.cursorDown(8); await interactiveCli.pressEnter(); await interactiveCli.cursorDown(2); await interactiveCli.pressEnter(); @@ -153,6 +156,7 @@ describe("files watcher", () => { describe('When changing current behavior to "newOne"', () => { beforeAll(async () => { + await interactiveCli.cursorDown(8); await interactiveCli.pressEnter(); await interactiveCli.cursorDown(3); await interactiveCli.pressEnter(); @@ -190,9 +194,11 @@ describe("files watcher", () => { }); it("should display an error", async () => { - expect(interactiveCli.currentScreen).toEqual(expect.stringContaining("ALERTS")); expect(interactiveCli.currentScreen).toEqual( - expect.stringContaining("main/fixtures/files-watch: FOO is not defined") + expect.stringContaining("Error: Error loading files from legacy folder") + ); + expect(interactiveCli.currentScreen).toEqual( + expect.stringContaining("main/v1/fixtures/files-watch: FOO is not defined") ); expect(interactiveCli.currentScreen).toEqual( expect.stringContaining("files-watch/fixtures/users.js:2:18") @@ -200,7 +206,7 @@ describe("files watcher", () => { }); it("should have same behaviors than before available", async () => { - expect(interactiveCli.currentScreen).toEqual(expect.stringContaining("Behaviors: 4")); + expect(interactiveCli.currentScreen).toEqual(expect.stringContaining("behaviors: 4")); }); it("should still serve users collection mock under the /api/users path", async () => { @@ -214,13 +220,15 @@ describe("files watcher", () => { it("should remove alerts when error is fixed", async () => { fsExtra.copySync(fixturesFolder("files-modification"), fixturesFolder("files-watch")); await wait(2000); - expect(interactiveCli.currentScreen).toEqual(expect.not.stringContaining("ALERTS")); + expect(interactiveCli.currentScreen).toEqual( + expect.not.stringContaining("main/v1/fixtures/files-watch: FOO is not defined") + ); }); }); describe("When files are modified while displaying logs", () => { it("should display logs", async () => { - await interactiveCli.cursorDown(5); + await interactiveCli.cursorDown(7); await interactiveCli.pressEnter(); await wait(1000); expect(interactiveCli.currentScreen).toEqual(expect.stringContaining("Displaying logs")); @@ -231,25 +239,28 @@ describe("files watcher", () => { fsExtra.copySync(fixturesFolder("files-error"), fixturesFolder("files-watch")); await wait(2000); expect(interactiveCli.currentScreen).toEqual(expect.stringContaining("Displaying logs")); - expect(interactiveCli.currentScreen).toEqual(expect.not.stringContaining("ALERTS")); + expect(interactiveCli.currentScreen).toEqual( + expect.not.stringContaining("Error: Error loading files from legacy folder") + ); }); it("should have displayed error in logs", async () => { expect.assertions(2); - expect(interactiveCli.currentScreen).toEqual(expect.not.stringContaining("ALERTS")); expect(interactiveCli.currentScreen).toEqual( - expect.stringContaining("Error loading files from folder") + expect.not.stringContaining("Error: Error loading files from legacy folder") + ); + expect(interactiveCli.currentScreen).toEqual( + expect.stringContaining("Error loading files from legacy folder") ); }); it("should display alerts when exit logs mode", async () => { - expect.assertions(4); + expect.assertions(3); await interactiveCli.pressEnter(); await wait(2000); expect(interactiveCli.currentScreen).toEqual(expect.not.stringContaining("Displaying logs")); - expect(interactiveCli.currentScreen).toEqual(expect.stringContaining("ALERTS")); expect(interactiveCli.currentScreen).toEqual( - expect.stringContaining("main/fixtures/files-watch: FOO is not defined") + expect.stringContaining("Error: Error loading files from legacy folder") ); expect(interactiveCli.currentScreen).toEqual( expect.stringContaining("files-watch/fixtures/users.js:2:18") @@ -260,7 +271,9 @@ describe("files watcher", () => { expect.assertions(2); fsExtra.copySync(fixturesFolder("files-modification"), fixturesFolder("files-watch")); await wait(2000); - expect(interactiveCli.currentScreen).toEqual(expect.not.stringContaining("ALERTS")); + expect(interactiveCli.currentScreen).toEqual( + expect.not.stringContaining("Error: Error loading files from legacy folder") + ); expect(interactiveCli.currentScreen).toEqual(expect.stringContaining("CURRENT SETTINGS")); }); }); diff --git a/test/unit/Core.mocks.js b/test/unit/Core.mocks.js index 4af5139..f9b3094 100644 --- a/test/unit/Core.mocks.js +++ b/test/unit/Core.mocks.js @@ -38,6 +38,7 @@ class CoreMock { onChangeSettings: this._sandbox.stub().returns(() => {}), onChangeAlerts: this._sandbox.stub().returns(() => {}), onChangeMocks: this._sandbox.stub().returns(() => {}), + onChangeLegacyMocks: this._sandbox.stub().returns(() => {}), addRouter: this._sandbox.stub(), addSetting: this._sandbox.stub(), mocks: { From 4efc891dfe9545d06c4db32316eb6633caa0bbca Mon Sep 17 00:00:00 2001 From: "javier.brea" Date: Sun, 14 Feb 2021 20:29:00 +0100 Subject: [PATCH 13/22] test(e2e): Add v2 e2e tests --- .github/workflows/build.yml | 6 +- .gitignore | 2 + jest.e2e.config.js | 2 +- package-lock.json | 86 +++++++ package.json | 9 +- sonar-project.properties | 2 +- test/e2e/main/v1/cli-no-behaviors.spec.js | 9 - test/e2e/main/v1/interactive-cli.spec.js | 4 - test/e2e/main/v2/arguments.spec.js | 101 ++++++++ test/e2e/main/v2/disabled.spec.js | 75 ++++++ test/e2e/main/v2/files-watch.spec.js | 233 ++++++++++++++++++ .../v2/fixtures/files-error-mock/db/users.js | 24 ++ .../v2/fixtures/files-error-mock/mocks.js | 11 + .../fixtures/files-error-mock/routes/user.js | 51 ++++ .../fixtures/files-error-mock/routes/users.js | 37 +++ test/e2e/main/v2/fixtures/no-mocks/.gitkeep | 0 test/e2e/main/v2/fixtures/starter | 24 ++ .../web-tutorial-modified/db/users.js | 24 ++ .../fixtures/web-tutorial-modified/mocks.js | 31 +++ .../web-tutorial-modified/routes/user.js | 51 ++++ .../web-tutorial-modified/routes/users.js | 61 +++++ .../main/v2/fixtures/web-tutorial/db/users.js | 24 ++ .../main/v2/fixtures/web-tutorial/mocks.js | 26 ++ .../v2/fixtures/web-tutorial/routes/user.js | 51 ++++ .../v2/fixtures/web-tutorial/routes/users.js | 37 +++ test/e2e/main/v2/interactive-cli.spec.js | 147 +++++++++++ test/e2e/main/v2/no-mocks.spec.js | 44 ++++ test/e2e/main/v2/support/helpers.js | 114 +++++++++ test/e2e/main/v2/web-tutorial.spec.js | 179 ++++++++++++++ 29 files changed, 1444 insertions(+), 21 deletions(-) create mode 100644 test/e2e/main/v2/arguments.spec.js create mode 100644 test/e2e/main/v2/disabled.spec.js create mode 100644 test/e2e/main/v2/files-watch.spec.js create mode 100644 test/e2e/main/v2/fixtures/files-error-mock/db/users.js create mode 100644 test/e2e/main/v2/fixtures/files-error-mock/mocks.js create mode 100644 test/e2e/main/v2/fixtures/files-error-mock/routes/user.js create mode 100644 test/e2e/main/v2/fixtures/files-error-mock/routes/users.js create mode 100644 test/e2e/main/v2/fixtures/no-mocks/.gitkeep create mode 100755 test/e2e/main/v2/fixtures/starter create mode 100644 test/e2e/main/v2/fixtures/web-tutorial-modified/db/users.js create mode 100644 test/e2e/main/v2/fixtures/web-tutorial-modified/mocks.js create mode 100644 test/e2e/main/v2/fixtures/web-tutorial-modified/routes/user.js create mode 100644 test/e2e/main/v2/fixtures/web-tutorial-modified/routes/users.js create mode 100644 test/e2e/main/v2/fixtures/web-tutorial/db/users.js create mode 100644 test/e2e/main/v2/fixtures/web-tutorial/mocks.js create mode 100644 test/e2e/main/v2/fixtures/web-tutorial/routes/user.js create mode 100644 test/e2e/main/v2/fixtures/web-tutorial/routes/users.js create mode 100644 test/e2e/main/v2/interactive-cli.spec.js create mode 100644 test/e2e/main/v2/no-mocks.spec.js create mode 100644 test/e2e/main/v2/support/helpers.js create mode 100644 test/e2e/main/v2/web-tutorial.spec.js diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index bdcabc8..fe6f5d0 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -41,9 +41,9 @@ jobs: run: npm run lint - name: Test unit run: npm run test:unit - #- name: Test E2E - #run: npm run test:e2e - #id: test-e2e + - name: Test E2E + run: npm run test:e2e + id: test-e2e - name: Upload test results uses: actions/upload-artifact@v2 with: diff --git a/.gitignore b/.gitignore index 51b8af7..13738df 100644 --- a/.gitignore +++ b/.gitignore @@ -11,6 +11,8 @@ /mocks /test/e2e/main/v1/fixtures/files-watch /test/e2e/main/v1/fixtures/mocks.config.js +/test/e2e/main/v2/fixtures/temp +/test/e2e/main/v2/fixtures/mocks.config.js # misc .DS_Store diff --git a/jest.e2e.config.js b/jest.e2e.config.js index 35aae60..e029628 100644 --- a/jest.e2e.config.js +++ b/jest.e2e.config.js @@ -6,7 +6,7 @@ module.exports = { clearMocks: true, testMatch: ["/test/e2e/**/*.spec.js"], - // testMatch: ["/test/e2e/main/v1/web-tutorial-files-watch.spec.js"], + // testMatch: ["/test/e2e/main/v2/files-watch.spec.js"], // Indicates whether the coverage information should be collected while executing the test collectCoverage: false, diff --git a/package-lock.json b/package-lock.json index 1042caf..c8920d3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -547,6 +547,15 @@ "integrity": "sha512-CAEbWH7OIur6jEOzaai83jq3FmKmv4PmX1JYfs9IrYcGEVI/lyL1EXJGCj7eFVJ0bg5QR8LMxBlEtA+xKiLpFw==", "dev": true }, + "@hapi/topo": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@hapi/topo/-/topo-5.0.0.tgz", + "integrity": "sha512-tFJlT47db0kMqVm3H4nQYgn6Pwg10GTZHb1pwmSiv1K4ks6drQOtfEF5ZnPjkvC+y4/bUPHK+bc87QvLcL+WMw==", + "dev": true, + "requires": { + "@hapi/hoek": "^9.0.0" + } + }, "@istanbuljs/load-nyc-config": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", @@ -827,6 +836,27 @@ } } }, + "@sideway/address": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/@sideway/address/-/address-4.1.1.tgz", + "integrity": "sha512-+I5aaQr3m0OAmMr7RQ3fR9zx55sejEYR2BFJaxL+zT3VM2611X0SHvPWIbAUBZVTn/YzYKbV8gJ2oT/QELknfQ==", + "dev": true, + "requires": { + "@hapi/hoek": "^9.0.0" + } + }, + "@sideway/formula": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@sideway/formula/-/formula-3.0.0.tgz", + "integrity": "sha512-vHe7wZ4NOXVfkoRb8T5otiENVlT7a3IAiw7H5M2+GO+9CDgcVUUsX1zalAztCmwyOr2RUTGJdgB+ZvSVqmdHmg==", + "dev": true + }, + "@sideway/pinpoint": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@sideway/pinpoint/-/pinpoint-2.0.0.tgz", + "integrity": "sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ==", + "dev": true + }, "@sinonjs/commons": { "version": "1.7.2", "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.7.2.tgz", @@ -1210,6 +1240,15 @@ "integrity": "sha512-wMHVg2EOHaMRxbzgFJ9gtjOOCrI80OHLG14rxi28XwOW8ux6IiEbRCGGGqCtdAIg4FQCbW20k9RsT4y3gJlFug==", "dev": true }, + "axios": { + "version": "0.21.1", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.21.1.tgz", + "integrity": "sha512-dKQiRHxGD9PPRIUNIWvZhPTPpl1rf/OxTYKsqKUDjBwYylTvV7SjSHJb9ratfyzM6wCdLCOYLzs73qpg5c4iGA==", + "dev": true, + "requires": { + "follow-redirects": "^1.10.0" + } + }, "babel-jest": { "version": "26.6.3", "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-26.6.3.tgz", @@ -1846,6 +1885,15 @@ "yaml": "^1.10.0" } }, + "cross-fetch": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.0.6.tgz", + "integrity": "sha512-KBPUbqgFjzWlVcURG+Svp9TlhA5uliYtiNx/0r8nv0pdypeQCRJ9IaSIc3q/x3q8t3F75cHuwxVql1HFGHCNJQ==", + "dev": true, + "requires": { + "node-fetch": "2.6.1" + } + }, "cross-spawn": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", @@ -2855,6 +2903,12 @@ "integrity": "sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==", "dev": true }, + "follow-redirects": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.13.2.tgz", + "integrity": "sha512-6mPTgLxYm3r6Bkkg0vNM0HTjfGrOEtsfbhagQvbxDEsEkpNhw582upBaoRZylzen6krEmxXJgt9Ju6HiI4O7BA==", + "dev": true + }, "for-in": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", @@ -4129,6 +4183,19 @@ } } }, + "joi": { + "version": "17.4.0", + "resolved": "https://registry.npmjs.org/joi/-/joi-17.4.0.tgz", + "integrity": "sha512-F4WiW2xaV6wc1jxete70Rw4V/VuMd6IN+a5ilZsxG4uYtUXWu2kq9W5P2dz30e7Gmw8RCbY/u/uk+dMPma9tAg==", + "dev": true, + "requires": { + "@hapi/hoek": "^9.0.0", + "@hapi/topo": "^5.0.0", + "@sideway/address": "^4.1.0", + "@sideway/formula": "^3.0.0", + "@sideway/pinpoint": "^2.0.0" + } + }, "js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -4750,6 +4817,12 @@ "lodash.toarray": "^4.4.0" } }, + "node-fetch": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz", + "integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==", + "dev": true + }, "node-int64": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", @@ -6780,6 +6853,19 @@ "xml-name-validator": "^3.0.0" } }, + "wait-on": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/wait-on/-/wait-on-5.2.1.tgz", + "integrity": "sha512-H2F986kNWMU9hKlI9l/ppO6tN8ZSJd35yBljMLa1/vjzWP++Qh6aXyt77/u7ySJFZQqBtQxnvm/xgG48AObXcw==", + "dev": true, + "requires": { + "axios": "^0.21.1", + "joi": "^17.3.0", + "lodash": "^4.17.20", + "minimist": "^1.2.5", + "rxjs": "^6.6.3" + } + }, "walker": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.7.tgz", diff --git a/package.json b/package.json index 575e547..8bc7a99 100644 --- a/package.json +++ b/package.json @@ -1,14 +1,15 @@ { "name": "@mocks-server/plugin-inquirer-cli", "version": "1.4.1", - "description": "Plugin for Mocks server. Displays an interactive CLI", + "description": "Mocks server plugin providing an interactive CLI", "keywords": [ "mocks-server-plugin", "interactive", "cli", "inquirer", + "settings", "administration", - "testing", + "options", "development" ], "author": "Javier Brea", @@ -42,6 +43,7 @@ }, "devDependencies": { "@mocks-server/core": "2.0.0-beta.1", + "cross-fetch": "3.0.6", "cross-spawn": "7.0.3", "eslint": "7.15.0", "eslint-plugin-no-only-tests": "2.4.0", @@ -57,7 +59,8 @@ "request-promise": "4.2.6", "sinon": "9.2.2", "strip-ansi": "6.0.0", - "tree-kill": "1.2.2" + "tree-kill": "1.2.2", + "wait-on": "5.2.1" }, "lint-staged": { "src/**/*.js": "eslint", diff --git a/sonar-project.properties b/sonar-project.properties index f860a1c..207da59 100644 --- a/sonar-project.properties +++ b/sonar-project.properties @@ -4,7 +4,7 @@ sonar.projectVersion=1.4.1 sonar.javascript.file.suffixes=.js sonar.sourceEncoding=UTF-8 -sonar.exclusions=node_modules/**,*.config.js +sonar.exclusions=node_modules/**,*.config.js,test/**/fixtures/** sonar.test.exclusions=test/**/* sonar.coverage.exclusions=test/**/* sonar.cpd.exclusions=test/** diff --git a/test/e2e/main/v1/cli-no-behaviors.spec.js b/test/e2e/main/v1/cli-no-behaviors.spec.js index 8eb21e0..db46f4d 100644 --- a/test/e2e/main/v1/cli-no-behaviors.spec.js +++ b/test/e2e/main/v1/cli-no-behaviors.spec.js @@ -20,15 +20,6 @@ describe("with no behaviors", () => { await cli.kill(); }); - it.skip("should display alerts", async () => { - cli = new CliRunner([BINARY_PATH, "--behavior=foo", "--pathLegacy=no-behaviors"], { - cwd: cwdPath, - }); - await wait(); - expect(cli.currentScreen).toEqual(expect.stringContaining("ALERTS")); - expect(cli.currentScreen).toEqual(expect.stringContaining("Warning: No behaviors found")); - }); - it("should print a dash as current behavior", async () => { cli = new CliRunner([BINARY_PATH, "--pathLegacy=no-behaviors"], { cwd: cwdPath, diff --git a/test/e2e/main/v1/interactive-cli.spec.js b/test/e2e/main/v1/interactive-cli.spec.js index 8ad6f97..862bf90 100644 --- a/test/e2e/main/v1/interactive-cli.spec.js +++ b/test/e2e/main/v1/interactive-cli.spec.js @@ -72,10 +72,6 @@ describe("interactive CLI", () => { expect(newScreen).toEqual(expect.stringContaining("Current behavior: dynamic")); }); - it.skip("should have removed alert", async () => { - expect(cli.currentScreen).toEqual(expect.not.stringContaining("ALERTS")); - }); - it("should serve users collection mock under the /api/users path", async () => { const users = await request("/api/users"); expect(users).toEqual([ diff --git a/test/e2e/main/v2/arguments.spec.js b/test/e2e/main/v2/arguments.spec.js new file mode 100644 index 0000000..3b5de40 --- /dev/null +++ b/test/e2e/main/v2/arguments.spec.js @@ -0,0 +1,101 @@ +/* +Copyright 2019 Javier Brea + +Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. +*/ + +const { mocksRunner, fetch, waitForServerAndCli, TimeCounter } = require("./support/helpers"); + +describe("command line arguments", () => { + let mocks; + + afterEach(async () => { + await mocks.kill(); + }); + + describe("path option", () => { + it("should set mocks folder", async () => { + expect.assertions(2); + mocks = mocksRunner(["--path=web-tutorial"]); + await waitForServerAndCli(); + const users = await fetch("/api/users"); + expect(users.body).toEqual([ + { id: 1, name: "John Doe" }, + { id: 2, name: "Jane Doe" }, + ]); + expect(mocks.currentScreen).toEqual(expect.stringContaining("Mocks: 3")); + }); + }); + + describe("mock option", () => { + describe("when not provided", () => { + it("should set as current mock the first one found", async () => { + expect.assertions(2); + mocks = mocksRunner(["--path=web-tutorial"]); + await waitForServerAndCli(); + const users = await fetch("/api/users/2"); + expect(users.body).toEqual({ id: 1, name: "John Doe" }); + expect(mocks.currentScreen).toEqual(expect.stringContaining("Current mock: base")); + }); + }); + + describe("when provided and exists", () => { + it("should set current mock", async () => { + expect.assertions(2); + mocks = mocksRunner(["--path=web-tutorial", "--mock=user-2"]); + await waitForServerAndCli(); + const users = await fetch("/api/users/2"); + expect(users.body).toEqual({ id: 2, name: "Jane Doe" }); + expect(mocks.currentScreen).toEqual(expect.stringContaining("Current mock: user-2")); + }); + }); + + describe("when provided and does not exist", () => { + it("should display an alert", async () => { + mocks = mocksRunner(["--path=web-tutorial", "--mock=foo"]); + await waitForServerAndCli(); + expect(mocks.currentScreen).toEqual(expect.stringContaining("ALERTS")); + expect(mocks.currentScreen).toEqual(expect.stringContaining('Mock "foo" was not found')); + }); + + it("should set as current behavior the first one found", async () => { + expect.assertions(3); + mocks = mocksRunner(["--path=web-tutorial", "--mock=foo"]); + await waitForServerAndCli(); + const users = await fetch("/api/users/2"); + expect(users.body).toEqual({ id: 1, name: "John Doe" }); + expect(mocks.currentScreen).toEqual(expect.stringContaining("Using the first one found")); + expect(mocks.currentScreen).toEqual(expect.stringContaining("Current mock: base")); + }); + }); + }); + + describe("delay option", () => { + it("should set delay", async () => { + expect.assertions(3); + mocks = mocksRunner(["--path=web-tutorial", "--delay=2000"]); + await waitForServerAndCli(); + const timeCounter = new TimeCounter(); + const users = await fetch("/api/users"); + timeCounter.stop(); + expect(users.body).toEqual([ + { id: 1, name: "John Doe" }, + { id: 2, name: "Jane Doe" }, + ]); + expect(mocks.currentScreen).toEqual(expect.stringContaining("Delay: 2000")); + expect(timeCounter.total).toBeGreaterThan(1999); + }); + }); + + describe("log option", () => { + it("should set log level", async () => { + mocks = mocksRunner(["--path=web-tutorial", "--log=debug"]); + await waitForServerAndCli(); + expect(mocks.currentScreen).toEqual(expect.stringContaining("Log level: debug")); + }); + }); +}); diff --git a/test/e2e/main/v2/disabled.spec.js b/test/e2e/main/v2/disabled.spec.js new file mode 100644 index 0000000..fa09ec1 --- /dev/null +++ b/test/e2e/main/v2/disabled.spec.js @@ -0,0 +1,75 @@ +/* +Copyright 2019 Javier Brea + +Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. +*/ + +const { mocksRunner, fetch, waitForServer, wait, TimeCounter } = require("./support/helpers"); + +describe("command line arguments with cli disabled", () => { + let mocks; + + afterEach(async () => { + await mocks.kill(); + }); + + describe("interactive cli", () => { + it("should not be started", async () => { + mocks = mocksRunner(["--path=web-tutorial", "--no-cli"]); + await wait(3000); + expect(mocks.logs).toEqual(expect.not.stringContaining("Select action")); + }); + }); + + describe("path option", () => { + it("should set mocks folder", async () => { + mocks = mocksRunner(["--path=web-tutorial", "--no-cli"]); + await wait(); + const users = await fetch("/api/users"); + expect(users.body).toEqual([ + { id: 1, name: "John Doe" }, + { id: 2, name: "Jane Doe" }, + ]); + }); + }); + + describe("behavior option", () => { + describe("when not provided", () => { + it("should set as current behavior the first one found", async () => { + mocks = mocksRunner(["--path=web-tutorial", "--no-cli"]); + await wait(); + const users = await fetch("/api/users/2"); + expect(users.body).toEqual({ id: 1, name: "John Doe" }); + }); + }); + + describe("when provided and exists", () => { + it("should set current behavior", async () => { + mocks = mocksRunner(["--path=web-tutorial", "--no-cli", "--mock=user-2"]); + await wait(); + const users = await fetch("/api/users/2"); + expect(users.body).toEqual({ id: 2, name: "Jane Doe" }); + }); + }); + }); + + describe("delay option", () => { + it("should set delay", async () => { + expect.assertions(2); + mocks = mocksRunner(["--path=web-tutorial", "--no-cli", "--delay=2000"]); + await waitForServer(); + const timeCounter = new TimeCounter(); + const users = await fetch("/api/users"); + timeCounter.stop(); + expect(users.body).toEqual([ + { id: 1, name: "John Doe" }, + { id: 2, name: "Jane Doe" }, + ]); + expect(timeCounter.total).toBeGreaterThan(1999); + }); + }); +}); diff --git a/test/e2e/main/v2/files-watch.spec.js b/test/e2e/main/v2/files-watch.spec.js new file mode 100644 index 0000000..d24202f --- /dev/null +++ b/test/e2e/main/v2/files-watch.spec.js @@ -0,0 +1,233 @@ +/* +Copyright 2019 Javier Brea + +Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. +*/ + +const fsExtra = require("fs-extra"); +const { + mocksRunner, + fetch, + waitForServerAndCli, + wait, + fixturesFolder, +} = require("./support/helpers"); + +describe("files watcher", () => { + jest.setTimeout(15000); + let mocks; + + beforeAll(async () => { + fsExtra.removeSync(fixturesFolder("temp")); + fsExtra.copySync(fixturesFolder("web-tutorial"), fixturesFolder("temp")); + mocks = mocksRunner(["--path=temp"]); + await waitForServerAndCli(); + }); + + afterAll(async () => { + await mocks.kill(); + }); + + describe("When started", () => { + it("should display available mocks in CLI", async () => { + expect(mocks.currentScreen).toEqual(expect.stringContaining("Mocks: 3")); + }); + + it("should display current mock in CLI", async () => { + expect(mocks.logs).toEqual(expect.stringContaining("Current mock: base")); + }); + + it("should serve users collection mock under the /api/users path", async () => { + const users = await fetch("/api/users"); + expect(users.body).toEqual([ + { id: 1, name: "John Doe" }, + { id: 2, name: "Jane Doe" }, + ]); + }); + + it("should serve user 1 under the /api/users/1 path", async () => { + const users = await fetch("/api/users/1"); + expect(users.body).toEqual({ id: 1, name: "John Doe" }); + }); + + it("should serve user 1 under the /api/users/2 path", async () => { + const users = await fetch("/api/users/2"); + expect(users.body).toEqual({ id: 1, name: "John Doe" }); + }); + }); + + describe("When files are modified", () => { + beforeAll(async () => { + fsExtra.copySync(fixturesFolder("web-tutorial-modified"), fixturesFolder("temp")); + await wait(5000); + }); + + describe("without changing current mock", () => { + it("should display available mocks in CLI", async () => { + expect(mocks.currentScreen).toEqual(expect.stringContaining("Mocks: 4")); + }); + + it("should serve users collection mock under the /api/users path", async () => { + const users = await fetch("/api/users"); + expect(users.body).toEqual([ + { id: 1, name: "John Doe modified" }, + { id: 2, name: "Jane Doe modified" }, + ]); + }); + + it("should serve user 1 under the /api/users/1 path", async () => { + const users = await fetch("/api/users/1"); + expect(users.body).toEqual({ id: 1, name: "John Doe modified" }); + }); + + it("should serve user 1 under the /api/users/2 path", async () => { + const users = await fetch("/api/users/2"); + expect(users.body).toEqual({ id: 1, name: "John Doe modified" }); + }); + }); + + describe('When changing current mock to "user-2"', () => { + beforeAll(async () => { + await mocks.pressEnter(); + await mocks.cursorDown(); + await mocks.pressEnter(); + }); + + it("should display current mock in CLI", async () => { + await wait(500); + expect(mocks.logs).toEqual(expect.stringContaining("Current mock: user-2")); + }); + + it("should serve users collection mock under the /api/users path", async () => { + const users = await fetch("/api/users"); + expect(users.body).toEqual([ + { id: 1, name: "John Doe modified" }, + { id: 2, name: "Jane Doe modified" }, + ]); + }); + + it("should serve user 2 under the /api/users/1 path", async () => { + const users = await fetch("/api/users/1"); + expect(users.body).toEqual({ id: 2, name: "Jane Doe modified" }); + }); + + it("should serve user 2 under the /api/users/2 path", async () => { + const users = await fetch("/api/users/2"); + expect(users.body).toEqual({ id: 2, name: "Jane Doe modified" }); + }); + }); + + describe('When changing current mock to "user-real"', () => { + beforeAll(async () => { + await mocks.pressEnter(); + await mocks.cursorDown(2); + await mocks.pressEnter(); + }); + + it("should display current behavior in CLI", async () => { + await wait(500); + expect(mocks.logs).toEqual(expect.stringContaining("Current mock: user-real")); + }); + + it("should serve users collection mock under the /api/users path", async () => { + const users = await fetch("/api/users"); + expect(users.body).toEqual([ + { id: 1, name: "John Doe modified" }, + { id: 2, name: "Jane Doe modified" }, + ]); + }); + + it("should serve user 1 under the /api/users/1 path", async () => { + const users = await fetch("/api/users/1"); + expect(users.body).toEqual({ id: 1, name: "John Doe modified" }); + }); + + it("should serve user 2 under the /api/users/2 path", async () => { + const users = await fetch("/api/users/2"); + expect(users.body).toEqual({ id: 2, name: "Jane Doe modified" }); + }); + }); + }); + + describe("When files are modified and contain an error", () => { + beforeAll(async () => { + fsExtra.copySync(fixturesFolder("files-error-mock"), fixturesFolder("temp")); + await wait(2000); + }); + + it("should display an error", async () => { + expect(mocks.currentScreen).toEqual( + expect.stringContaining("Error: Error loading mocks from file") + ); + expect(mocks.currentScreen).toEqual( + expect.stringContaining("main/v2/fixtures/temp/mocks.js: foo is not defined") + ); + expect(mocks.currentScreen).toEqual( + expect.stringContaining("main/v2/fixtures/temp/mocks.js:11:18") + ); + }); + + it("should have no mocks available", async () => { + expect(mocks.currentScreen).toEqual(expect.stringContaining("Mocks: 0")); + }); + + it("should not serve users collection mock under the /api/users path", async () => { + const users = await fetch("/api/users"); + expect(users.status).toEqual(404); + }); + + it("should remove alerts when error is fixed", async () => { + fsExtra.copySync(fixturesFolder("web-tutorial-modified"), fixturesFolder("temp")); + await wait(2000); + expect(mocks.currentScreen).toEqual(expect.not.stringContaining("ALERTS")); + }); + }); + + describe("When files are modified while displaying logs", () => { + it("should display logs", async () => { + await mocks.cursorDown(7); + await mocks.pressEnter(); + await wait(1000); + expect(mocks.currentScreen).toEqual(expect.stringContaining("Displaying logs")); + }); + + it("should not display alerts when files are modified and contain an error", async () => { + expect.assertions(2); + fsExtra.copySync(fixturesFolder("files-error-mock"), fixturesFolder("temp")); + await wait(3000); + expect(mocks.currentScreen).toEqual(expect.stringContaining("Displaying logs")); + expect(mocks.currentScreen).toEqual(expect.not.stringContaining("ALERTS")); + }); + + it("should have displayed error in logs", async () => { + expect.assertions(2); + expect(mocks.currentScreen).toEqual(expect.not.stringContaining("ALERTS")); + expect(mocks.currentScreen).toEqual( + expect.stringContaining("Error loading mocks from file") + ); + }); + + it("should display alerts when exit logs mode", async () => { + expect.assertions(3); + await mocks.pressEnter(); + await wait(2000); + expect(mocks.currentScreen).toEqual(expect.not.stringContaining("Displaying logs")); + expect(mocks.currentScreen).toEqual( + expect.stringContaining("Error: Error loading mocks from file") + ); + expect(mocks.currentScreen).toEqual(expect.stringContaining("ALERTS")); + }); + + it("should remove alerts when error is fixed", async () => { + expect.assertions(2); + fsExtra.copySync(fixturesFolder("web-tutorial-modified"), fixturesFolder("temp")); + await wait(2000); + expect(mocks.currentScreen).toEqual(expect.not.stringContaining("ALERTS")); + expect(mocks.currentScreen).toEqual(expect.stringContaining("CURRENT SETTINGS")); + }); + }); +}); diff --git a/test/e2e/main/v2/fixtures/files-error-mock/db/users.js b/test/e2e/main/v2/fixtures/files-error-mock/db/users.js new file mode 100644 index 0000000..f05a501 --- /dev/null +++ b/test/e2e/main/v2/fixtures/files-error-mock/db/users.js @@ -0,0 +1,24 @@ +/* +Copyright 2021 Javier Brea + +Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. +*/ + +const USERS = [ + { + id: 1, + name: "John Doe", + }, + { + id: 2, + name: "Jane Doe", + }, +]; + +module.exports = { + USERS, +}; diff --git a/test/e2e/main/v2/fixtures/files-error-mock/mocks.js b/test/e2e/main/v2/fixtures/files-error-mock/mocks.js new file mode 100644 index 0000000..fe754cc --- /dev/null +++ b/test/e2e/main/v2/fixtures/files-error-mock/mocks.js @@ -0,0 +1,11 @@ +/* +Copyright 2021 Javier Brea + +Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. +*/ +/* eslint-disable */ +module.exports = foo; diff --git a/test/e2e/main/v2/fixtures/files-error-mock/routes/user.js b/test/e2e/main/v2/fixtures/files-error-mock/routes/user.js new file mode 100644 index 0000000..ac18756 --- /dev/null +++ b/test/e2e/main/v2/fixtures/files-error-mock/routes/user.js @@ -0,0 +1,51 @@ +/* +Copyright 2021 Javier Brea + +Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. +*/ + +const { USERS } = require("../db/users"); + +module.exports = [ + { + id: "get-user", + url: "/api/users/:id", + method: "GET", + variants: [ + { + id: "1", + response: { + status: 200, + body: USERS[0], + }, + }, + { + id: "2", + response: { + status: 200, + body: USERS[1], + }, + }, + { + id: "real", + response: (req, res) => { + const userId = req.params.id; + const user = USERS.find((userData) => userData.id === Number(userId)); + if (user) { + res.status(200); + res.send(user); + } else { + res.status(404); + res.send({ + message: "User not found", + }); + } + }, + }, + ], + }, +]; diff --git a/test/e2e/main/v2/fixtures/files-error-mock/routes/users.js b/test/e2e/main/v2/fixtures/files-error-mock/routes/users.js new file mode 100644 index 0000000..6c1d2ae --- /dev/null +++ b/test/e2e/main/v2/fixtures/files-error-mock/routes/users.js @@ -0,0 +1,37 @@ +/* +Copyright 2021 Javier Brea + +Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. +*/ + +const { USERS } = require("../db/users"); + +module.exports = [ + { + id: "get-users", + url: "/api/users", + method: "GET", + variants: [ + { + id: "success", + response: { + status: 200, + body: USERS, + }, + }, + { + id: "error", + response: { + status: 403, + body: { + message: "Bad data", + }, + }, + }, + ], + }, +]; diff --git a/test/e2e/main/v2/fixtures/no-mocks/.gitkeep b/test/e2e/main/v2/fixtures/no-mocks/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/test/e2e/main/v2/fixtures/starter b/test/e2e/main/v2/fixtures/starter new file mode 100755 index 0000000..f57fadd --- /dev/null +++ b/test/e2e/main/v2/fixtures/starter @@ -0,0 +1,24 @@ +#!/usr/bin/env node +"use strict"; + +const { Core } = require("@mocks-server/core"); +const InquirerCli = require("../../../../../index"); + +const handleError = (error) => { + console.error(`Error: ${error.message}`); + process.exitCode = 1; +}; + +const start = () => { + try { + const mocksServer = new Core({ + plugins: [InquirerCli], + }); + + return mocksServer.start().catch(handleError); + } catch (error) { + return handleError(error); + } +}; + +start(); diff --git a/test/e2e/main/v2/fixtures/web-tutorial-modified/db/users.js b/test/e2e/main/v2/fixtures/web-tutorial-modified/db/users.js new file mode 100644 index 0000000..627c6a7 --- /dev/null +++ b/test/e2e/main/v2/fixtures/web-tutorial-modified/db/users.js @@ -0,0 +1,24 @@ +/* +Copyright 2021 Javier Brea + +Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. +*/ + +const USERS = [ + { + id: 1, + name: "John Doe modified", + }, + { + id: 2, + name: "Jane Doe modified", + }, +]; + +module.exports = { + USERS, +}; diff --git a/test/e2e/main/v2/fixtures/web-tutorial-modified/mocks.js b/test/e2e/main/v2/fixtures/web-tutorial-modified/mocks.js new file mode 100644 index 0000000..0141b77 --- /dev/null +++ b/test/e2e/main/v2/fixtures/web-tutorial-modified/mocks.js @@ -0,0 +1,31 @@ +/* +Copyright 2021 Javier Brea + +Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. +*/ + +module.exports = [ + { + id: "base", + routesVariants: ["get-users:success", "get-user:1", "get-users-new:success"], + }, + { + id: "user-2", + from: "base", + routesVariants: ["get-user:2"], + }, + { + id: "user-real", + from: "base", + routesVariants: ["get-user:real"], + }, + { + id: "users-error", + from: "base", + routesVariants: ["get-users:error"], + }, +]; diff --git a/test/e2e/main/v2/fixtures/web-tutorial-modified/routes/user.js b/test/e2e/main/v2/fixtures/web-tutorial-modified/routes/user.js new file mode 100644 index 0000000..ac18756 --- /dev/null +++ b/test/e2e/main/v2/fixtures/web-tutorial-modified/routes/user.js @@ -0,0 +1,51 @@ +/* +Copyright 2021 Javier Brea + +Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. +*/ + +const { USERS } = require("../db/users"); + +module.exports = [ + { + id: "get-user", + url: "/api/users/:id", + method: "GET", + variants: [ + { + id: "1", + response: { + status: 200, + body: USERS[0], + }, + }, + { + id: "2", + response: { + status: 200, + body: USERS[1], + }, + }, + { + id: "real", + response: (req, res) => { + const userId = req.params.id; + const user = USERS.find((userData) => userData.id === Number(userId)); + if (user) { + res.status(200); + res.send(user); + } else { + res.status(404); + res.send({ + message: "User not found", + }); + } + }, + }, + ], + }, +]; diff --git a/test/e2e/main/v2/fixtures/web-tutorial-modified/routes/users.js b/test/e2e/main/v2/fixtures/web-tutorial-modified/routes/users.js new file mode 100644 index 0000000..afedb5f --- /dev/null +++ b/test/e2e/main/v2/fixtures/web-tutorial-modified/routes/users.js @@ -0,0 +1,61 @@ +/* +Copyright 2021 Javier Brea + +Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. +*/ + +const { USERS } = require("../db/users"); + +module.exports = [ + { + id: "get-users", + url: "/api/users", + method: "GET", + variants: [ + { + id: "success", + response: { + headers: { + "x-custom-header": "foo-header", + "x-another-header": "another-header", + }, + status: 200, + body: USERS, + }, + }, + { + id: "error", + response: { + status: 403, + body: { + message: "Bad data", + }, + }, + }, + ], + }, + { + id: "get-users-new", + url: "/api/new-users", + method: "GET", + variants: [ + { + id: "success", + response: { + status: 200, + body: [ + ...USERS, + { + id: 3, + name: "Brand new user", + }, + ], + }, + }, + ], + }, +]; diff --git a/test/e2e/main/v2/fixtures/web-tutorial/db/users.js b/test/e2e/main/v2/fixtures/web-tutorial/db/users.js new file mode 100644 index 0000000..f05a501 --- /dev/null +++ b/test/e2e/main/v2/fixtures/web-tutorial/db/users.js @@ -0,0 +1,24 @@ +/* +Copyright 2021 Javier Brea + +Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. +*/ + +const USERS = [ + { + id: 1, + name: "John Doe", + }, + { + id: 2, + name: "Jane Doe", + }, +]; + +module.exports = { + USERS, +}; diff --git a/test/e2e/main/v2/fixtures/web-tutorial/mocks.js b/test/e2e/main/v2/fixtures/web-tutorial/mocks.js new file mode 100644 index 0000000..82511e6 --- /dev/null +++ b/test/e2e/main/v2/fixtures/web-tutorial/mocks.js @@ -0,0 +1,26 @@ +/* +Copyright 2021 Javier Brea + +Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. +*/ + +module.exports = [ + { + id: "base", + routesVariants: ["get-users:success", "get-user:1"], + }, + { + id: "user-2", + from: "base", + routesVariants: ["get-user:2"], + }, + { + id: "user-real", + from: "base", + routesVariants: ["get-user:real"], + }, +]; diff --git a/test/e2e/main/v2/fixtures/web-tutorial/routes/user.js b/test/e2e/main/v2/fixtures/web-tutorial/routes/user.js new file mode 100644 index 0000000..ac18756 --- /dev/null +++ b/test/e2e/main/v2/fixtures/web-tutorial/routes/user.js @@ -0,0 +1,51 @@ +/* +Copyright 2021 Javier Brea + +Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. +*/ + +const { USERS } = require("../db/users"); + +module.exports = [ + { + id: "get-user", + url: "/api/users/:id", + method: "GET", + variants: [ + { + id: "1", + response: { + status: 200, + body: USERS[0], + }, + }, + { + id: "2", + response: { + status: 200, + body: USERS[1], + }, + }, + { + id: "real", + response: (req, res) => { + const userId = req.params.id; + const user = USERS.find((userData) => userData.id === Number(userId)); + if (user) { + res.status(200); + res.send(user); + } else { + res.status(404); + res.send({ + message: "User not found", + }); + } + }, + }, + ], + }, +]; diff --git a/test/e2e/main/v2/fixtures/web-tutorial/routes/users.js b/test/e2e/main/v2/fixtures/web-tutorial/routes/users.js new file mode 100644 index 0000000..6c1d2ae --- /dev/null +++ b/test/e2e/main/v2/fixtures/web-tutorial/routes/users.js @@ -0,0 +1,37 @@ +/* +Copyright 2021 Javier Brea + +Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. +*/ + +const { USERS } = require("../db/users"); + +module.exports = [ + { + id: "get-users", + url: "/api/users", + method: "GET", + variants: [ + { + id: "success", + response: { + status: 200, + body: USERS, + }, + }, + { + id: "error", + response: { + status: 403, + body: { + message: "Bad data", + }, + }, + }, + ], + }, +]; diff --git a/test/e2e/main/v2/interactive-cli.spec.js b/test/e2e/main/v2/interactive-cli.spec.js new file mode 100644 index 0000000..29ce9d6 --- /dev/null +++ b/test/e2e/main/v2/interactive-cli.spec.js @@ -0,0 +1,147 @@ +/* +Copyright 2019 Javier Brea + +Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. +*/ + +const { + mocksRunner, + fetch, + waitForServerAndCli, + wait, + TimeCounter, +} = require("./support/helpers"); + +describe("interactive CLI", () => { + jest.setTimeout(15000); + let mocks; + + beforeAll(async () => { + mocks = mocksRunner(["--path=web-tutorial", "--mock=foo"]); + await waitForServerAndCli(); + }); + + afterAll(async () => { + await mocks.kill(); + }); + + describe("When started", () => { + it("should display an alert because chosen mock does not exist", async () => { + expect(mocks.currentScreen).toEqual(expect.stringContaining('Mock "foo" was not found.')); + expect(mocks.currentScreen).toEqual(expect.stringContaining("ALERTS")); + }); + + it("should have loaded first mock", async () => { + expect(mocks.currentScreen).toEqual(expect.stringContaining("Current mock: base")); + }); + + it("should have 3 mocks available", async () => { + expect(mocks.currentScreen).toEqual(expect.stringContaining("Mocks: 3")); + }); + + it("should serve users collection mock under the /api/users path", async () => { + const users = await fetch("/api/users"); + expect(users.body).toEqual([ + { id: 1, name: "John Doe" }, + { id: 2, name: "Jane Doe" }, + ]); + }); + + it("should serve user 1 under the /api/users/1 path", async () => { + const users = await fetch("/api/users/1"); + expect(users.body).toEqual({ id: 1, name: "John Doe" }); + }); + + it("should serve user 1 under the /api/users/2 path", async () => { + const users = await fetch("/api/users/2"); + expect(users.body).toEqual({ id: 1, name: "John Doe" }); + }); + }); + + describe('When changing current mock to "user-real"', () => { + it("should display new selected mock", async () => { + await mocks.pressEnter(); + await mocks.cursorDown(2); + const newScreen = await mocks.pressEnter(); + expect(newScreen).toEqual(expect.stringContaining("Current mock: user-real")); + }); + + it("should have removed alert", async () => { + expect(mocks.currentScreen).toEqual(expect.not.stringContaining("ALERTS")); + }); + + it("should serve users collection mock under the /api/users path", async () => { + const users = await fetch("/api/users"); + expect(users.body).toEqual([ + { id: 1, name: "John Doe" }, + { id: 2, name: "Jane Doe" }, + ]); + }); + + it("should serve user 1 under the /api/users/1 path", async () => { + const users = await fetch("/api/users/1"); + expect(users.body).toEqual({ id: 1, name: "John Doe" }); + }); + + it("should serve user 2 under the /api/users/2 path", async () => { + const users = await fetch("/api/users/2"); + expect(users.body).toEqual({ id: 2, name: "Jane Doe" }); + }); + + it("should return not found for /api/users/3 path", async () => { + const usersResponse = await fetch("/api/users/3"); + expect(usersResponse.status).toEqual(404); + }); + }); + + describe("When changing logs level", () => { + it("should display new selected log level", async () => { + await mocks.cursorDown(5); + await mocks.pressEnter(); + await mocks.cursorDown(2); + const newScreen = await mocks.pressEnter(); + expect(newScreen).toEqual(expect.stringContaining("Log level: verbose")); + }); + }); + + describe("When displaying logs", () => { + it("should log requests", async () => { + expect.assertions(2); + await mocks.cursorDown(7); + await mocks.pressEnter(); + await fetch("/api/users"); + await wait(1000); + expect(mocks.currentScreen).toEqual(expect.stringContaining("Displaying logs")); + expect(mocks.currentScreen).toEqual( + expect.stringContaining("[Mocks verbose] Request received") + ); + await mocks.pressEnter(); + }); + }); + + describe("When changing delay time", () => { + it("should display new selected delay time", async () => { + await mocks.cursorDown(3); + await mocks.pressEnter(); + await mocks.write(2000); + const newScreen = await mocks.pressEnter(); + expect(newScreen).toEqual(expect.stringContaining("Delay: 2000")); + }); + + it("should respond after defined delay", async () => { + expect.assertions(2); + const timeCounter = new TimeCounter(); + const users = await fetch("/api/users"); + timeCounter.stop(); + expect(timeCounter.total).toBeGreaterThan(1999); + expect(users.body).toEqual([ + { id: 1, name: "John Doe" }, + { id: 2, name: "Jane Doe" }, + ]); + }); + }); +}); diff --git a/test/e2e/main/v2/no-mocks.spec.js b/test/e2e/main/v2/no-mocks.spec.js new file mode 100644 index 0000000..24c67af --- /dev/null +++ b/test/e2e/main/v2/no-mocks.spec.js @@ -0,0 +1,44 @@ +/* +Copyright 2019 Javier Brea + +Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. +*/ + +const { mocksRunner, waitForServerAndCli } = require("./support/helpers"); + +describe("with no behaviors", () => { + let mocks; + + afterEach(async () => { + await mocks.kill(); + }); + + it("should display alerts", async () => { + mocks = mocksRunner(["--path=no-mocks", "mock=foo"]); + await waitForServerAndCli(); + expect(mocks.currentScreen).toEqual(expect.stringContaining("ALERTS")); + expect(mocks.currentScreen).toEqual(expect.stringContaining("Warning: No mocks found")); + }); + + it("should print a dash as current behavior", async () => { + mocks = mocksRunner(["--path=no-mocks"]); + await waitForServerAndCli(); + expect(mocks.currentScreen).toEqual(expect.stringContaining("Current mock: -")); + }); + + it("should print mocks as 0", async () => { + mocks = mocksRunner(["--path=no-mocks"]); + await waitForServerAndCli(); + expect(mocks.currentScreen).toEqual(expect.stringContaining("Mocks: 0")); + }); + + it("should print current routes as 0", async () => { + mocks = mocksRunner(["--path=no-mocks"]); + await waitForServerAndCli(); + expect(mocks.logs).toEqual(expect.stringContaining("Routes: 0")); + }); +}); diff --git a/test/e2e/main/v2/support/helpers.js b/test/e2e/main/v2/support/helpers.js new file mode 100644 index 0000000..affec37 --- /dev/null +++ b/test/e2e/main/v2/support/helpers.js @@ -0,0 +1,114 @@ +/* +Copyright 2019 Javier Brea + +Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. +*/ + +const path = require("path"); + +const crossFetch = require("cross-fetch"); +const waitOn = require("wait-on"); + +const InteractiveCliRunner = require("../../../inquirer/support/InteractiveCliRunner"); + +const DEFAULT_BINARY_PATH = "./starter"; + +const SERVER_PORT = 3100; + +const defaultRequestOptions = { + method: "GET", + headers: { + "Content-Type": "application/json", + }, +}; + +const defaultMocksRunnerOptions = { + cwd: path.resolve(__dirname, "..", "fixtures"), +}; + +const fixturesFolder = (folderName) => { + return path.resolve(__dirname, "..", "fixtures", folderName); +}; + +const serverUrl = (port) => { + return `http://localhost:${port || SERVER_PORT}`; +}; + +const fetch = (uri, options = {}) => { + const requestOptions = { + ...defaultRequestOptions, + ...options, + }; + + return crossFetch(`${serverUrl(options.port)}${uri}`, { + ...requestOptions, + }).then((res) => { + return res + .json() + .then((processedRes) => ({ body: processedRes, status: res.status, headers: res.headers })); + }); +}; + +class TimeCounter { + constructor() { + this._startTime = new Date(); + } + + _getMiliseconds() { + this._miliseconds = this._endTime - this._startTime; + } + + get total() { + return this._miliseconds; + } + + stop() { + this._endTime = new Date(); + this._getMiliseconds(); + } +} + +const wait = (time = 1000) => { + return new Promise((resolve) => { + setTimeout(() => { + resolve(); + }, time); + }); +}; + +const waitForServer = (port) => { + return waitOn({ resources: [`tcp:localhost:${port || SERVER_PORT}`] }); +}; + +const waitForServerUrl = (url) => { + return waitOn({ resources: [`${serverUrl()}${url}`] }); +}; + +const waitForServerAndCli = async (port) => { + await waitForServer(port); + await wait(); +}; + +const mocksRunner = (args = [], options = {}) => { + const argsToSend = [...args]; + argsToSend.unshift(DEFAULT_BINARY_PATH); + return new InteractiveCliRunner(argsToSend, { + ...defaultMocksRunnerOptions, + ...options, + }); +}; + +module.exports = { + fetch, + TimeCounter, + mocksRunner, + wait, + waitForServer, + waitForServerUrl, + waitForServerAndCli, + fixturesFolder, +}; diff --git a/test/e2e/main/v2/web-tutorial.spec.js b/test/e2e/main/v2/web-tutorial.spec.js new file mode 100644 index 0000000..5178cf4 --- /dev/null +++ b/test/e2e/main/v2/web-tutorial.spec.js @@ -0,0 +1,179 @@ +/* +Copyright 2019 Javier Brea + +Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. +*/ + +const { mocksRunner, fetch, waitForServerAndCli } = require("./support/helpers"); + +describe("web tutorial", () => { + jest.setTimeout(15000); + let mocks; + + beforeAll(async () => { + mocks = mocksRunner(["--path=web-tutorial"]); + await waitForServerAndCli(); + }); + + afterAll(async () => { + await mocks.kill(); + }); + + describe("When started", () => { + it("should have 3 mocks available", async () => { + expect(mocks.currentScreen).toEqual(expect.stringContaining("Mocks: 3")); + }); + + it("should not display behaviors", async () => { + expect(mocks.currentScreen).toEqual(expect.not.stringContaining("behaviors")); + }); + + it("should serve users collection mock under the /api/users path", async () => { + const users = await fetch("/api/users"); + expect(users.body).toEqual([ + { id: 1, name: "John Doe" }, + { id: 2, name: "Jane Doe" }, + ]); + }); + + it("should serve user 1 under the /api/users/1 path", async () => { + const users = await fetch("/api/users/1"); + expect(users.body).toEqual({ id: 1, name: "John Doe" }); + }); + + it("should serve user 1 under the /api/users/2 path", async () => { + const users = await fetch("/api/users/2"); + expect(users.body).toEqual({ id: 1, name: "John Doe" }); + }); + }); + + describe('When changing current mock to "user-2"', () => { + it("should display new selected mock", async () => { + await mocks.pressEnter(); + await mocks.cursorDown(); + const newScreen = await mocks.pressEnter(); + expect(newScreen).toEqual(expect.stringContaining("Current mock: user-2")); + }); + + it("should serve users collection mock under the /api/users path", async () => { + const users = await fetch("/api/users"); + expect(users.body).toEqual([ + { id: 1, name: "John Doe" }, + { id: 2, name: "Jane Doe" }, + ]); + }); + + it("should serve user 2 under the /api/users/1 path", async () => { + const users = await fetch("/api/users/1"); + expect(users.body).toEqual({ id: 2, name: "Jane Doe" }); + }); + + it("should serve user 2 under the /api/users/2 path", async () => { + const users = await fetch("/api/users/2"); + expect(users.body).toEqual({ id: 2, name: "Jane Doe" }); + }); + }); + + describe('When changing current mock to "user-real"', () => { + it("should display new selected mock", async () => { + await mocks.pressEnter(); + await mocks.cursorDown(2); + const newScreen = await mocks.pressEnter(); + expect(newScreen).toEqual(expect.stringContaining("Current mock: user-real")); + }); + + it("should serve users collection mock under the /api/users path", async () => { + const users = await fetch("/api/users"); + expect(users.body).toEqual([ + { id: 1, name: "John Doe" }, + { id: 2, name: "Jane Doe" }, + ]); + }); + + it("should serve user 1 under the /api/users/1 path", async () => { + const users = await fetch("/api/users/1"); + expect(users.body).toEqual({ id: 1, name: "John Doe" }); + }); + + it("should serve user 2 under the /api/users/2 path", async () => { + const users = await fetch("/api/users/2"); + expect(users.body).toEqual({ id: 2, name: "Jane Doe" }); + }); + + it("should return not found for /api/users/3 path", async () => { + const usersResponse = await fetch("/api/users/3"); + expect(usersResponse.status).toEqual(404); + }); + }); + + describe("When setting custom route variant", () => { + it("should display custom route variant", async () => { + await mocks.cursorDown(); + await mocks.pressEnter(); + await mocks.cursorDown(); + const newScreen = await mocks.pressEnter(); + expect(newScreen).toEqual( + expect.stringContaining("Current mock: user-real (custom variants: get-user:2)") + ); + }); + + it("should serve users collection mock under the /api/users path", async () => { + const users = await fetch("/api/users"); + expect(users.body).toEqual([ + { id: 1, name: "John Doe" }, + { id: 2, name: "Jane Doe" }, + ]); + }); + + it("should serve user 2 under the /api/users/1 path", async () => { + const users = await fetch("/api/users/1"); + expect(users.body).toEqual({ id: 2, name: "Jane Doe" }); + }); + + it("should serve user 2 under the /api/users/2 path", async () => { + const users = await fetch("/api/users/2"); + expect(users.body).toEqual({ id: 2, name: "Jane Doe" }); + }); + + it("should return user 2 for /api/users/3 path", async () => { + const users = await fetch("/api/users/3"); + expect(users.body).toEqual({ id: 2, name: "Jane Doe" }); + }); + }); + + describe("When restoring route variants", () => { + it("should display mock", async () => { + await mocks.cursorDown(2); + await mocks.pressEnter(); + const newScreen = await mocks.pressEnter(); + expect(newScreen).toEqual(expect.stringContaining("Current mock: user-real")); + }); + + it("should serve users collection mock under the /api/users path", async () => { + const users = await fetch("/api/users"); + expect(users.body).toEqual([ + { id: 1, name: "John Doe" }, + { id: 2, name: "Jane Doe" }, + ]); + }); + + it("should serve user 1 under the /api/users/1 path", async () => { + const users = await fetch("/api/users/1"); + expect(users.body).toEqual({ id: 1, name: "John Doe" }); + }); + + it("should serve user 2 under the /api/users/2 path", async () => { + const users = await fetch("/api/users/2"); + expect(users.body).toEqual({ id: 2, name: "Jane Doe" }); + }); + + it("should return not found for /api/users/3 path", async () => { + const usersResponse = await fetch("/api/users/3"); + expect(usersResponse.status).toEqual(404); + }); + }); +}); From 429b234c935b8e19e0cb1204e8456c9be285ace6 Mon Sep 17 00:00:00 2001 From: "javier.brea" Date: Sun, 14 Feb 2021 20:30:53 +0100 Subject: [PATCH 14/22] chore(release): upgrade version --- CHANGELOG.md | 12 +++++++++++- package-lock.json | 2 +- package.json | 2 +- sonar-project.properties | 2 +- 4 files changed, 14 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 47627f9..6e9bdae 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,17 +5,27 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/) and this project adheres to [Semantic Versioning](http://semver.org/). ## [unreleased] +### Added +### Changed +### Fixed +### Removed +### BREAKING CHANGE + +## [2.0.0-beta.1] - 2021-02-14 + ### Added - feat: Display current mock. - feat: Display menus for changing current mock. - feat: Add menus for changing route variant and restore variants + ### Changed - feat: Display legacy options and menus only when core `pathLegacy` setting has value. Add toggle legacy watch menu. - refactor: Refresh inquirer main options every time main menu is displayed. + ### Fixed - fix: Resolve previous inquirers before displaying a new one - fix: Start promise was never resolved -### Removed + ### BREAKING CHANGE - Changed format of `cli` option to boolean (now `--no-cli` has to be used to disable it) diff --git a/package-lock.json b/package-lock.json index c8920d3..9564f23 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "@mocks-server/plugin-inquirer-cli", - "version": "1.4.1", + "version": "2.0.0-beta.1", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index 8bc7a99..dd77f8c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@mocks-server/plugin-inquirer-cli", - "version": "1.4.1", + "version": "2.0.0-beta.1", "description": "Mocks server plugin providing an interactive CLI", "keywords": [ "mocks-server-plugin", diff --git a/sonar-project.properties b/sonar-project.properties index 207da59..dc5ec68 100644 --- a/sonar-project.properties +++ b/sonar-project.properties @@ -1,6 +1,6 @@ sonar.organization=mocks-server sonar.projectKey=mocks-server-plugin-inquirer-cli -sonar.projectVersion=1.4.1 +sonar.projectVersion=2.0.0-beta.1 sonar.javascript.file.suffixes=.js sonar.sourceEncoding=UTF-8 From ffcae5340e0b44fe6ff6999331c1bce8de245358 Mon Sep 17 00:00:00 2001 From: "javier.brea" Date: Tue, 16 Feb 2021 19:41:46 +0100 Subject: [PATCH 15/22] chore(deps): Update core dependency --- CHANGELOG.md | 5 +++++ package-lock.json | 8 ++++---- package.json | 4 ++-- sonar-project.properties | 2 +- 4 files changed, 12 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6e9bdae..b9b784d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,11 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ### Removed ### BREAKING CHANGE +## [2.0.0-beta.2] - 2021-02-16 + +### Changed +- chore(deps): Update mocks-server/core dependency. Adapt tests. + ## [2.0.0-beta.1] - 2021-02-14 ### Added diff --git a/package-lock.json b/package-lock.json index 9564f23..1ac46af 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "@mocks-server/plugin-inquirer-cli", - "version": "2.0.0-beta.1", + "version": "2.0.0-beta.2", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -787,9 +787,9 @@ } }, "@mocks-server/core": { - "version": "2.0.0-beta.1", - "resolved": "https://registry.npmjs.org/@mocks-server/core/-/core-2.0.0-beta.1.tgz", - "integrity": "sha512-K2q4dcUfVxCMrsvPJHpSFLTtlIi5eu7jJsANVjQZnpjbXgZceO5AjBc70F9y/PW+wh5n9AdTa4rjV73KKHb0DQ==", + "version": "2.0.0-beta.2", + "resolved": "https://registry.npmjs.org/@mocks-server/core/-/core-2.0.0-beta.2.tgz", + "integrity": "sha512-8LzIH2wcz4ofn2Hnf55zQHfoXiq1TZ4l9aenjB7frfYBD6iBlpk8z6zdQGQ3Eo/iNLpgiAGaRMokrBwWkbbkuw==", "dev": true, "requires": { "@hapi/boom": "9.1.1", diff --git a/package.json b/package.json index dd77f8c..585be88 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@mocks-server/plugin-inquirer-cli", - "version": "2.0.0-beta.1", + "version": "2.0.0-beta.2", "description": "Mocks server plugin providing an interactive CLI", "keywords": [ "mocks-server-plugin", @@ -42,7 +42,7 @@ "node-emoji": "1.10.0" }, "devDependencies": { - "@mocks-server/core": "2.0.0-beta.1", + "@mocks-server/core": "2.0.0-beta.2", "cross-fetch": "3.0.6", "cross-spawn": "7.0.3", "eslint": "7.15.0", diff --git a/sonar-project.properties b/sonar-project.properties index dc5ec68..5ffe83a 100644 --- a/sonar-project.properties +++ b/sonar-project.properties @@ -1,6 +1,6 @@ sonar.organization=mocks-server sonar.projectKey=mocks-server-plugin-inquirer-cli -sonar.projectVersion=2.0.0-beta.1 +sonar.projectVersion=2.0.0-beta.2 sonar.javascript.file.suffixes=.js sonar.sourceEncoding=UTF-8 From 145286e5f777630e0b44c4305a0811f7e8d1013c Mon Sep 17 00:00:00 2001 From: "javier.brea" Date: Wed, 17 Feb 2021 07:37:51 +0100 Subject: [PATCH 16/22] refactor: fix Sonar smells --- src/Cli.js | 4 ++-- src/Inquirer.js | 2 +- src/helpers.js | 11 +++++++++++ test/unit/Core.mocks.js | 12 ++++++++---- 4 files changed, 22 insertions(+), 7 deletions(-) diff --git a/src/Cli.js b/src/Cli.js index 565660d..a27a726 100644 --- a/src/Cli.js +++ b/src/Cli.js @@ -14,7 +14,7 @@ Unless required by applicable law or agreed to in writing, software distributed const { isNumber } = require("lodash"); const inquirer = require("./Inquirer"); -const { renderHeader, renderAlert } = require("./helpers"); +const { renderHeader, renderAlert, getCurrentMockMessageLevel } = require("./helpers"); const MAIN_CHOICES = [ { @@ -268,7 +268,7 @@ class Cli { renderHeader( `Current mock`, currentMockMessage, - this._core.mocks.customRoutesVariants.length ? 1 : currentMock === "-" ? 2 : 0 + getCurrentMockMessageLevel(this._core.mocks.customRoutesVariants, currentMock) ), renderHeader(`Mocks`, availableMocks, availableMocks < 1 ? 2 : 0), renderHeader(`Routes`, availableRoutes, availableRoutes < 1 ? 2 : 0), diff --git a/src/Inquirer.js b/src/Inquirer.js index ff18def..54ab8ed 100644 --- a/src/Inquirer.js +++ b/src/Inquirer.js @@ -40,7 +40,7 @@ const QUIT_QUESTION = { const exitProcess = () => process.exit(); -// require("events").EventEmitter.defaultMaxListeners = 100; +require("events").EventEmitter.defaultMaxListeners = 100; const Inquirer = class Inquirer { constructor(header, alerts) { diff --git a/src/helpers.js b/src/helpers.js index 86b47b1..0cbfa3d 100644 --- a/src/helpers.js +++ b/src/helpers.js @@ -45,6 +45,16 @@ function clearScreen() { process.stdout.write(CLRS); } +function getCurrentMockMessageLevel(customRoutesVariants, currentMock) { + if (customRoutesVariants.length) { + return 1; + } + if (currentMock === "-") { + return 2; + } + return 0; +} + module.exports = { renderSectionHeader, renderSectionFooter, @@ -53,4 +63,5 @@ module.exports = { renderLogsMode, formatError, clearScreen, + getCurrentMockMessageLevel, }; diff --git a/test/unit/Core.mocks.js b/test/unit/Core.mocks.js index f9b3094..0b33c9b 100644 --- a/test/unit/Core.mocks.js +++ b/test/unit/Core.mocks.js @@ -14,6 +14,10 @@ jest.mock("@mocks-server/core"); const { Core } = require("@mocks-server/core"); +const doNothing = () => { + // do nothing +}; + class CoreMock { constructor() { this._sandbox = sinon.createSandbox(); @@ -35,10 +39,10 @@ class CoreMock { warn: this._sandbox.stub(), error: this._sandbox.stub(), }, - onChangeSettings: this._sandbox.stub().returns(() => {}), - onChangeAlerts: this._sandbox.stub().returns(() => {}), - onChangeMocks: this._sandbox.stub().returns(() => {}), - onChangeLegacyMocks: this._sandbox.stub().returns(() => {}), + onChangeSettings: this._sandbox.stub().returns(doNothing), + onChangeAlerts: this._sandbox.stub().returns(doNothing), + onChangeMocks: this._sandbox.stub().returns(doNothing), + onChangeLegacyMocks: this._sandbox.stub().returns(doNothing), addRouter: this._sandbox.stub(), addSetting: this._sandbox.stub(), mocks: { From cd660c2b89d16d9bcccddaddf33106e43195221d Mon Sep 17 00:00:00 2001 From: "javier.brea" Date: Wed, 17 Feb 2021 07:46:48 +0100 Subject: [PATCH 17/22] test: fix Sonar smell --- test/unit/src/Inquirer.spec.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/test/unit/src/Inquirer.spec.js b/test/unit/src/Inquirer.spec.js index ef92814..6f25fb2 100644 --- a/test/unit/src/Inquirer.spec.js +++ b/test/unit/src/Inquirer.spec.js @@ -146,7 +146,9 @@ describe("Inquirer", () => { sandbox.stub(inquirer, "prompt").usingPromise().resolves({}); const cli = new Inquirer(); cli.questions = fooQuestions; - process.stdin.on("keypress", () => {}); + process.stdin.on("keypress", () => { + // do nothing + }); sandbox.stub(process.stdin, "removeListener"); await cli.inquire("main"); expect(process.stdin.removeListener.getCall(0).args[0]).toEqual("keypress"); From 5b6dae949d86477164c5695909330c51d7869a87 Mon Sep 17 00:00:00 2001 From: "javier.brea" Date: Wed, 17 Feb 2021 18:34:40 +0100 Subject: [PATCH 18/22] chore(deps): Update dependencies --- package-lock.json | 6 +++--- package.json | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package-lock.json b/package-lock.json index 1ac46af..e0659c0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -787,9 +787,9 @@ } }, "@mocks-server/core": { - "version": "2.0.0-beta.2", - "resolved": "https://registry.npmjs.org/@mocks-server/core/-/core-2.0.0-beta.2.tgz", - "integrity": "sha512-8LzIH2wcz4ofn2Hnf55zQHfoXiq1TZ4l9aenjB7frfYBD6iBlpk8z6zdQGQ3Eo/iNLpgiAGaRMokrBwWkbbkuw==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@mocks-server/core/-/core-2.0.0.tgz", + "integrity": "sha512-qvMLu2DEIWB4GPphUJTqwtGpnB5nKwm5l9l31LAni4biw9Om0GHBF2Ahvw5+5Hz4QlkxLCju4mkHVwX4+rtldA==", "dev": true, "requires": { "@hapi/boom": "9.1.1", diff --git a/package.json b/package.json index 585be88..0a40acd 100644 --- a/package.json +++ b/package.json @@ -32,7 +32,7 @@ "test:unit": "npm run test" }, "peerDependencies": { - "@mocks-server/core": ">=2.0.0" + "@mocks-server/core": "2.x" }, "dependencies": { "chalk": "4.1.0", @@ -42,7 +42,7 @@ "node-emoji": "1.10.0" }, "devDependencies": { - "@mocks-server/core": "2.0.0-beta.2", + "@mocks-server/core": "2.0.0", "cross-fetch": "3.0.6", "cross-spawn": "7.0.3", "eslint": "7.15.0", From 90adede99b00671bc6a7bbe8073bcd835d5f6638 Mon Sep 17 00:00:00 2001 From: "javier.brea" Date: Wed, 17 Feb 2021 18:36:46 +0100 Subject: [PATCH 19/22] docs: add license --- src/helpers.js | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/helpers.js b/src/helpers.js index 0cbfa3d..f1c9e9b 100644 --- a/src/helpers.js +++ b/src/helpers.js @@ -1,3 +1,13 @@ +/* +Copyright 2021 Javier Brea + +Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. +*/ + const { trim } = require("lodash"); const emoji = require("node-emoji"); const chalk = require("chalk"); From 3ecad21071bbdf9bf82aede57bc3d78e4555720d Mon Sep 17 00:00:00 2001 From: "javier.brea" Date: Wed, 17 Feb 2021 18:37:20 +0100 Subject: [PATCH 20/22] docs: add license --- src/Inquirer.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Inquirer.js b/src/Inquirer.js index 54ab8ed..e4ec1c4 100644 --- a/src/Inquirer.js +++ b/src/Inquirer.js @@ -1,4 +1,5 @@ /* +Copyright 2021 Javier Brea Copyright 2019 XbyOrange Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at From 51aa42b50e88b7ec4bb8945d21676c7d2da1f6c3 Mon Sep 17 00:00:00 2001 From: "javier.brea" Date: Wed, 17 Feb 2021 18:55:42 +0100 Subject: [PATCH 21/22] chore: remove publish tags --- .github/workflows/publish-to-github.yml | 2 +- .github/workflows/publish-to-npm.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/publish-to-github.yml b/.github/workflows/publish-to-github.yml index 7d6964e..ba50e4f 100644 --- a/.github/workflows/publish-to-github.yml +++ b/.github/workflows/publish-to-github.yml @@ -13,6 +13,6 @@ jobs: registry-url: 'https://npm.pkg.github.com' # Defaults to the user or organization that owns the workflow file scope: '@mocks-server' - - run: npm publish --tag beta + - run: npm publish env: NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/publish-to-npm.yml b/.github/workflows/publish-to-npm.yml index fd47750..2444d4b 100644 --- a/.github/workflows/publish-to-npm.yml +++ b/.github/workflows/publish-to-npm.yml @@ -11,6 +11,6 @@ jobs: with: node-version: '12.x' registry-url: 'https://registry.npmjs.org/' - - run: npm publish --tag beta + - run: npm publish env: NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} From adf402041c650f08f2fbd58895c3c099fe8178c7 Mon Sep 17 00:00:00 2001 From: "javier.brea" Date: Wed, 17 Feb 2021 18:57:09 +0100 Subject: [PATCH 22/22] chore(release): Upgrade version --- CHANGELOG.md | 19 +++++++++++++++++++ package-lock.json | 2 +- package.json | 2 +- sonar-project.properties | 2 +- 4 files changed, 22 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b9b784d..6c5191e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,25 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ### Removed ### BREAKING CHANGE +## [2.0.0] - 2021-02-17 + +### Added +- feat: Display current mock. +- feat: Display menus for changing current mock. +- feat: Add menus for changing route variant and restore variants + +### Changed +- feat: Display legacy options and menus only when core `pathLegacy` setting has value. Add toggle legacy watch menu. +- refactor: Refresh inquirer main options every time main menu is displayed. +- chore(deps): Update dependencies + +### Fixed +- fix: Resolve previous inquirers before displaying a new one +- fix: Start promise was never resolved + +### BREAKING CHANGE +- Changed format of `cli` option to boolean (now `--no-cli` has to be used to disable it) + ## [2.0.0-beta.2] - 2021-02-16 ### Changed diff --git a/package-lock.json b/package-lock.json index e0659c0..01fade9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "@mocks-server/plugin-inquirer-cli", - "version": "2.0.0-beta.2", + "version": "2.0.0", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index 0a40acd..68a73ed 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@mocks-server/plugin-inquirer-cli", - "version": "2.0.0-beta.2", + "version": "2.0.0", "description": "Mocks server plugin providing an interactive CLI", "keywords": [ "mocks-server-plugin", diff --git a/sonar-project.properties b/sonar-project.properties index 5ffe83a..143289a 100644 --- a/sonar-project.properties +++ b/sonar-project.properties @@ -1,6 +1,6 @@ sonar.organization=mocks-server sonar.projectKey=mocks-server-plugin-inquirer-cli -sonar.projectVersion=2.0.0-beta.2 +sonar.projectVersion=2.0.0 sonar.javascript.file.suffixes=.js sonar.sourceEncoding=UTF-8