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/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 diff --git a/.gitignore b/.gitignore index c89fba3..13738df 100644 --- a/.gitignore +++ b/.gitignore @@ -9,7 +9,10 @@ # tests /coverage /mocks -/test/e2e/main/fixtures/files-watch +/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/CHANGELOG.md b/CHANGELOG.md index 4cf347e..6c5191e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,49 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ### Changed ### Fixed ### 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 +- chore(deps): Update mocks-server/core dependency. Adapt tests. + +## [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 + +### 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/README.md b/README.md index fcefc85..80dde4d 100644 --- a/README.md +++ b/README.md @@ -7,28 +7,28 @@ # [![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) +![Interactive CLI][animated-image-url] ## 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._ ## 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 -[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 faa67be..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/**/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 38be18d..01fade9 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", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -533,20 +533,29 @@ } }, "@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 }, + "@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", @@ -778,27 +787,76 @@ } }, "@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", + "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.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 + } + } + }, + "@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", @@ -1182,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", @@ -1818,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", @@ -2827,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", @@ -2994,6 +3076,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", @@ -4090,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", @@ -4711,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", @@ -4748,9 +4860,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 +6608,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": { @@ -6741,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", @@ -6828,6 +6953,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..68a73ed 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", + "version": "2.0.0", + "description": "Mocks server plugin providing an interactive CLI", "keywords": [ "mocks-server-plugin", "interactive", "cli", "inquirer", + "settings", "administration", - "testing", + "options", "development" ], "author": "Javier Brea", @@ -31,7 +32,7 @@ "test:unit": "npm run test" }, "peerDependencies": { - "@mocks-server/core": ">=1.6.0" + "@mocks-server/core": "2.x" }, "dependencies": { "chalk": "4.1.0", @@ -41,7 +42,9 @@ "node-emoji": "1.10.0" }, "devDependencies": { - "@mocks-server/core": "1.6.0", + "@mocks-server/core": "2.0.0", + "cross-fetch": "3.0.6", + "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 +59,8 @@ "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", + "wait-on": "5.2.1" }, "lint-staged": { "src/**/*.js": "eslint", diff --git a/sonar-project.properties b/sonar-project.properties index f860a1c..143289a 100644 --- a/sonar-project.properties +++ b/sonar-project.properties @@ -1,10 +1,10 @@ sonar.organization=mocks-server sonar.projectKey=mocks-server-plugin-inquirer-cli -sonar.projectVersion=1.4.1 +sonar.projectVersion=2.0.0 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/src/Cli.js b/src/Cli.js index 4959a40..a27a726 100644 --- a/src/Cli.js +++ b/src/Cli.js @@ -14,39 +14,59 @@ 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 questions = { +const MAIN_CHOICES = [ + { + name: "Change current mock", + value: "mock", + }, + { + name: "Change route variant", + value: "variant", + }, + { + name: "Restore routes variants", + value: "restoreVariants", + }, + { + 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: "watchLegacy", + 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", @@ -54,11 +74,21 @@ 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", message: "Please choose behavior", }, + variant: { + type: "autocomplete", + name: "value", + message: "Please choose route variant", + }, delay: { type: "input", name: "value", @@ -73,9 +103,20 @@ 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", + MOCK: "mock", DELAY: "delay", LOG_LEVEL: "log-level", LOGS: "logs", @@ -96,7 +137,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 +147,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(); @@ -127,13 +163,16 @@ 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(); - return this._displayMainMenu(); + this._displayMainMenu(); + return Promise.resolve(); } stop() { @@ -142,9 +181,9 @@ class Cli { } this._started = false; this._stopListeningChangeMocks(); + this._stopListeningChangeLegacyMocks(); this._stopListeningChangeAlerts(); this._settings.set("log", this._logLevel); - this._cli.removeListeners(); this._cli.logsMode(); this._cli.clearScreen({ header: false, @@ -154,7 +193,6 @@ class Cli { _refreshMainMenu() { if (this._currentScreen === SCREENS.MAIN) { - this._cli.removeListeners(); return this._displayMainMenu(); } return Promise.resolve(); @@ -180,11 +218,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(); } @@ -205,21 +246,55 @@ 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"); + + 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 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.customRoutesVariants.length + ? `${currentMock} (custom variants: ${this._core.mocks.customRoutesVariants.join(",")})` + : currentMock; + + const headers = [ renderHeader(`Mocks server listening at`, this._serverUrl), 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( + `Current mock`, + currentMockMessage, + getCurrentMockMessageLevel(this._core.mocks.customRoutesVariants, currentMock) + ), + 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), ]; - return header; + const legacyHeaders = legacyMode + ? [ + renderHeader(`Legacy: Watch enabled`, watchLegacyEnabled, !!watchLegacyEnabled ? 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,13 +302,18 @@ class Cli { } async _displayMainMenu() { + this._cli.questions = getQuestions(!!this._settings.get("pathLegacy")); this._cli.clearScreen(); this._cli.exitLogsMode(); this._currentScreen = SCREENS.MAIN; const action = await this._cli.inquire("main"); switch (action) { - case "behavior": - return this._changeCurrentBehavior(); + case "mock": + return this._changeCurrentMock(); + case "variant": + return this._changeRouteVariant(); + case "restoreVariants": + return this._restoreRoutesVariants(); case "delay": return this._changeDelay(); case "restart": @@ -244,9 +324,57 @@ 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 _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 _restoreRoutesVariants() { + this._core.mocks.restoreRoutesVariants(); + return this._displayMainMenu(); + } + async _changeCurrentBehavior() { this._currentScreen = SCREENS.BEHAVIOR; this._cli.clearScreen(); @@ -288,6 +416,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 ce64626..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 @@ -12,9 +13,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, @@ -34,34 +34,37 @@ const MAIN_MENU_ID = "main"; const DEFAULT_QUIT_NAME = "Exit"; const QUIT_ACTION_ID = "quit"; -// require("events").EventEmitter.defaultMaxListeners = 100; +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); - } - get displayName() { - return packageInfo.name; + this._exitLogsMode = this._exitLogsMode.bind(this); + this._currentInquirers = new Set(); } _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; @@ -100,20 +103,36 @@ 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._currentInquirers.delete(resolve); + this.removeListeners(); + if (questionKey === MAIN_MENU_ID && answers.value === QUIT_ACTION_ID) { + this.quit(); + } + resolve(answers.value); + }); + }); } quit() { - this._quit(); + exitProcess(); } clearScreen(opts) { @@ -136,7 +155,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); }); } diff --git a/src/helpers.js b/src/helpers.js index 86b47b1..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"); @@ -45,6 +55,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 +73,5 @@ module.exports = { renderLogsMode, formatError, clearScreen, + getCurrentMockMessageLevel, }; 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/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/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"; } 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 96% rename from test/e2e/support/CliRunner.js rename to test/e2e/inquirer/support/CliRunner.js index 055e0f1..dabeb5b 100644 --- a/test/e2e/support/CliRunner.js +++ b/test/e2e/inquirer/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; } 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/inquirer/support/utils.js b/test/e2e/inquirer/support/utils.js new file mode 100644 index 0000000..e83c8fb --- /dev/null +++ b/test/e2e/inquirer/support/utils.js @@ -0,0 +1,21 @@ +/* +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 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 63% rename from test/e2e/main/cli-no-behaviors.spec.js rename to test/e2e/main/v1/cli-no-behaviors.spec.js index 7b6b08d..db46f4d 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,17 +20,8 @@ describe("with no behaviors", () => { await cli.kill(); }); - it("should display alerts", async () => { - cli = new CliRunner([BINARY_PATH, "--behavior=foo", "--path=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, "--path=no-behaviors"], { + cli = new CliRunner([BINARY_PATH, "--pathLegacy=no-behaviors"], { cwd: cwdPath, }); await wait(); @@ -38,15 +29,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 90% rename from test/e2e/main/interactive-cli.spec.js rename to test/e2e/main/v1/interactive-cli.spec.js index 98bc3b9..862bf90 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,16 +65,13 @@ 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 () => { - 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([ @@ -103,7 +101,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 +112,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 +126,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/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/mocks-server b/test/e2e/main/v2/fixtures/files-error-mock/mocks.js old mode 100755 new mode 100644 similarity index 84% rename from test/e2e/mocks-server rename to test/e2e/main/v2/fixtures/files-error-mock/mocks.js index c52c9b5..fe754cc --- a/test/e2e/mocks-server +++ b/test/e2e/main/v2/fixtures/files-error-mock/mocks.js @@ -1,10 +1,5 @@ -#!/usr/bin/env node -"use strict"; - -require("./support/start").start(); - /* -Copyright 2019 XbyOrange +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 @@ -12,3 +7,5 @@ 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); + }); + }); +}); diff --git a/test/unit/Core.mocks.js b/test/unit/Core.mocks.js index 1296ede..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,11 +39,22 @@ 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(() => {}), + 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: { + 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..6f25fb2 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,8 +144,11 @@ 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); - process.stdin.on("keypress", () => {}); + const cli = new Inquirer(); + cli.questions = fooQuestions; + 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"); @@ -143,7 +158,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 +204,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 +222,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 +231,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 +244,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 +259,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 +273,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); });