diff --git a/.gitignore b/.gitignore index e58d414..23ebca0 100644 --- a/.gitignore +++ b/.gitignore @@ -9,7 +9,7 @@ # tests /coverage /mocks -/test/acceptance/main/fixtures/files-watch +/test/acceptance/fixtures/files-watch # misc .DS_Store diff --git a/.travis.yml b/.travis.yml index b434a85..6633dfd 100644 --- a/.travis.yml +++ b/.travis.yml @@ -19,7 +19,7 @@ script: - npm run lint - npm run test-ci - npm run coveralls - - sonar-scanner -Dsonar.login=${SONAR_TOKEN} + - 'if [ -n "$SONAR_TOKEN" ]; then sonar-scanner -Dsonar.login=${SONAR_TOKEN}; fi' deploy: provider: npm diff --git a/CHANGELOG.md b/CHANGELOG.md index 26e392e..5f762c6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,10 +5,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/) and this project adheres to [Semantic Versioning](http://semver.org/). ## [To be deprecated] -- Deprecate options "features" and "feature". - Remove "/features" api path. -- Remove "features" getter from Server -- Remove Server and Cli constructors. Use @mocks-server/core instead ## [unreleased] ### Added @@ -16,50 +13,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ### Fixed ### Removed -## [1.3.0] - 2019-11-17 -### Added -- Add programmatic Classes end-to-end tests -- Add files watcher end-to-end tests - -### Changed -- Full refactor for making it pluggable. -- Split code into core, cli and api main Classes, which are intended to be published separately. - -## [1.2.0] - 2019-11-13 -### Added -- Add api acceptance tests - -### Changed -- Upgrade dependencies - -### Fixed -- Catch server.listen error and reject start method promise with it when occurs. - -## [1.1.1] - 2019-11-12 -### Changed -- Change readme. Add links to docs website. - -## [1.1.0] - 2019-11-08 -### Changed -- Change "feature" concept by "behavior". Maintain old "feature" options and urls as aliases for maintaining compatibility. -- Upgrade dependencies - -## [1.0.3] - 2019-11-08 -### Fixed -- Fix examples and badges in readme. - -## [1.0.2] - 2019-11-08 -### Changed -- Project forked from xbyorange/mocks-server. Fixed license. Read NOTICE for further details - -### Fixed -- Fix some minor Sonar bugs and code smells. - -## [1.0.1] - 2019-06-04 -### Fixed -- Upgrade dependencies to fix potential security vulnerability -- Bind winston tracer methods to winston tracer instance to fix an issue produced by new Winston version as described in: https://github.com/winstonjs/winston/issues/1577 - -## [1.0.0] - 2019-06-04 -### Changed -- Forked from xbyorange mocks-server gitlab private repository. +## [1.0.0] - 2019-12-06 +- Migrate administration api from [@mocks-server/main, v1.3.0](https://github.com/mocks-server/main/releases/tag/v1.3.0). For further info read the [previous repository CHANGELOG.md](https://github.com/mocks-server/main/blob/v1.3.0/CHANGELOG.md#130---2019-11-17) +- Export Plugin, which can be used only programmatically. (Binary is still distributed in the [@mocks-server/main](https://github.com/mocks-server/main) package). +- Remove core and inquirer-cli. Both have now their own repositories. diff --git a/LICENSE b/LICENSE index 58a0db8..b43ac62 100644 --- a/LICENSE +++ b/LICENSE @@ -175,5 +175,5 @@ END OF TERMS AND CONDITIONS - Copyright 2019 Javier Brea and contributors + Copyright 2019 Javier Brea Copyright 2019 XbyOrange diff --git a/NOTICE b/NOTICE index 69d10be..e318eb6 100644 --- a/NOTICE +++ b/NOTICE @@ -1,4 +1,4 @@ -mocks-server. Main distributable +mocks-server admin-api plugin. Copyright 2019 Javier Brea Portions of this software were developed at XbyOrange company. diff --git a/README.md b/README.md index 8161e5a..d91c946 100644 --- a/README.md +++ b/README.md @@ -5,25 +5,27 @@ [![NPM downloads][npm-downloads-image]][npm-downloads-url] [![License][license-image]][license-url] -# [![Mocks Server][logo-url]][website-url] Mocks Server +# [![Mocks Server][logo-url]][website-url] Mocks Server Plugin Admin Api -This package provides a server that simulates API behaviors. It can be added as a dependency of your project, and started simply running an NPM command. +Plugin for [Mocks Server][website-url] that provides an API REST that allows to change dinamically the current behavior, change delay time, and another [Mocks Server options][options-url]. -## Documentation +This is __very useful when running acceptance tests, as you can change the behavior of the api__ simply making an HTTP request in your tests `before` method, for example. -Please refer to the [project documentation website][website-url]: +## Usage -* [Get started](https://www.mocks-server.org/docs/get-started-intro) -* [Tutorials](https://www.mocks-server.org/docs/tutorials-static) -* [Configuration](https://www.mocks-server.org/docs/configuration-command-line-arguments) +This plugin is included in the [main distribution of the Mocks Server project][main-distribution-url], so you can refer to the [official documentation website][website-url]. -## Why a mocks server? +## API Resources -Controlling the responses of the api will improve the front-end development workflow, avoiding early dependencies with back-end. It also improves the testing and development of error cases or another cases that are commonly hard to reproduce in the real api. +Available api resources are: -Defining the api responses during the earliest phases of development will improve the communication among team members and align their expectations. - -Working with Node.js, it integrates better in front-end projects as any other NPM dependency, and it will be easier for front-end developers to maintain the mocks. +* `GET` `/mocks/behaviors/current` Returns current behavior. +* `PUT` `/mocks/behaviors/current` Set current behavior. + * Request body example: `{ "name": "behavior-name" }` +* `GET` `/mocks/settings` Returns current server settings. + * Response body example: `{ "delay": 0 }` +* `PUT` `/mocks/settings` Changes current server settings. + * Request body example: `{ "delay": 3000 }` ## Contributing @@ -31,23 +33,23 @@ Contributors are welcome. Please read the [contributing guidelines](.github/CONTRIBUTING.md) and [code of conduct](.github/CODE_OF_CONDUCT.md). [website-url]: https://www.mocks-server.org +[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 -[inquirer-url]: https://www.npmjs.com/package/inquirer#support-os-terminals -[inquirer-support]: https://www.npmjs.com/package/inquirer#support-os-terminals - -[coveralls-image]: https://coveralls.io/repos/github/mocks-server/main/badge.svg -[coveralls-url]: https://coveralls.io/github/mocks-server/main -[travisci-image]: https://travis-ci.com/mocks-server/main.svg?branch=master -[travisci-url]: https://travis-ci.com/mocks-server/main -[last-commit-image]: https://img.shields.io/github/last-commit/mocks-server/main.svg -[last-commit-url]: https://github.com/mocks-server/main/commits -[license-image]: https://img.shields.io/npm/l/@mocks-server/main.svg -[license-url]: https://github.com/mocks-server/main/blob/master/LICENSE -[npm-downloads-image]: https://img.shields.io/npm/dm/@mocks-server/main.svg -[npm-downloads-url]: https://www.npmjs.com/package/@mocks-server/main -[npm-dependencies-image]: https://img.shields.io/david/mocks-server/main.svg -[npm-dependencies-url]: https://david-dm.org/mocks-server/main -[quality-gate-image]: https://sonarcloud.io/api/project_badges/measure?project=mocks-server-main&metric=alert_status -[quality-gate-url]: https://sonarcloud.io/dashboard?id=mocks-server-main -[release-image]: https://img.shields.io/github/release-date/mocks-server/main.svg -[release-url]: https://github.com/mocks-server/main/releases + +[coveralls-image]: https://coveralls.io/repos/github/mocks-server/plugin-admin-api/badge.svg +[coveralls-url]: https://coveralls.io/github/mocks-server/plugin-admin-api +[travisci-image]: https://travis-ci.com/mocks-server/plugin-admin-api.svg?branch=master +[travisci-url]: https://travis-ci.com/mocks-server/plugin-admin-api +[last-commit-image]: https://img.shields.io/github/last-commit/mocks-server/plugin-admin-api.svg +[last-commit-url]: https://github.com/mocks-server/plugin-admin-api/commits +[license-image]: https://img.shields.io/npm/l/@mocks-server/plugin-admin-api.svg +[license-url]: https://github.com/mocks-server/plugin-admin-api/blob/master/LICENSE +[npm-downloads-image]: https://img.shields.io/npm/dm/@mocks-server/plugin-admin-api.svg +[npm-downloads-url]: https://www.npmjs.com/package/@mocks-server/plugin-admin-api +[npm-dependencies-image]: https://img.shields.io/david/mocks-server/plugin-admin-api.svg +[npm-dependencies-url]: https://david-dm.org/mocks-server/plugin-admin-api +[quality-gate-image]: https://sonarcloud.io/api/project_badges/measure?project=mocks-server-plugin-admin-api&metric=alert_status +[quality-gate-url]: https://sonarcloud.io/dashboard?id=mocks-server-plugin-admin-api +[release-image]: https://img.shields.io/github/release-date/mocks-server/plugin-admin-api.svg +[release-url]: https://github.com/mocks-server/plugin-admin-api/releases diff --git a/assets/cli_animation.gif b/assets/cli_animation.gif deleted file mode 100644 index 9f45b50..0000000 Binary files a/assets/cli_animation.gif and /dev/null differ diff --git a/bin/mocks-server b/bin/mocks-server deleted file mode 100755 index 95c063c..0000000 --- a/bin/mocks-server +++ /dev/null @@ -1,14 +0,0 @@ -#!/usr/bin/env node -"use strict"; - -require("../lib/start").start(); - -/* -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 - -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. -*/ diff --git a/docs/cli.md b/docs/cli.md deleted file mode 100644 index fd42eb9..0000000 --- a/docs/cli.md +++ /dev/null @@ -1,30 +0,0 @@ -#### CLI - -The interactive CLI can be instantiated and started programmatically: - -```js -const { Cli } = require("@mocks-server/main"); - -const startMyCli = () => { - const cli = new Cli({ - port: 3200, - log: "debug", - watch: false - }); - - return cli.start(); -}; - -startMyCli().catch(err => { - console.log("Error starting CLI", err); -}); -``` - -##### `Cli` (\[options\]\[,customQuitMethod\]) -For first argument options, please read the [options](#options) chapter of this documentation. Available methods of an instance are: -- `start` () -Inits the server in case it was stopped, adds the watch listeners, and renders main menu. -- `initServer` () -Inits the server in case it was stopped, adds the watch listeners. -- `stopListeningServerWatch` () -When server watch is active, the main menu will be displayed on file changes. This behavior can be deactivated using this method. This is useful when this CLI is loaded as a submenu of another CLI, for example. diff --git a/index.js b/index.js index 9c35100..875fbc2 100644 --- a/index.js +++ b/index.js @@ -11,13 +11,6 @@ Unless required by applicable law or agreed to in writing, software distributed "use strict"; -const ProgrammaticCli = require("./lib/ProgrammaticCli"); -const ProgrammaticServer = require("./lib/ProgrammaticServer"); -const Behavior = require("./lib/core/mocks/Behavior"); +const PluginAdminApi = require("./src/Api"); -module.exports = { - Cli: ProgrammaticCli, - Server: ProgrammaticServer, - Feature: Behavior, - Behavior -}; +module.exports = PluginAdminApi; diff --git a/jest.acceptance.config.js b/jest.acceptance.config.js index 3f31d80..c1029d2 100644 --- a/jest.acceptance.config.js +++ b/jest.acceptance.config.js @@ -6,7 +6,6 @@ module.exports = { clearMocks: true, testMatch: ["**/test/acceptance/**/?(*.)+(spec|test).js?(x)"], - //testMatch: ["**/test/acceptance/main/programmatic-cli.spec.js"], // Indicates whether the coverage information should be collected while executing the test collectCoverage: false, diff --git a/jest.config.js b/jest.config.js index e71e637..077d9f7 100644 --- a/jest.config.js +++ b/jest.config.js @@ -9,7 +9,7 @@ module.exports = { collectCoverage: true, // An array of glob patterns indicating a set of files for which coverage information should be collected - collectCoverageFrom: ["index.js", "lib/**"], + collectCoverageFrom: ["index.js", "src/**"], // The directory where Jest should output its coverage files coverageDirectory: "coverage", diff --git a/lib/ProgrammaticCli.js b/lib/ProgrammaticCli.js deleted file mode 100644 index 63eba70..0000000 --- a/lib/ProgrammaticCli.js +++ /dev/null @@ -1,67 +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. -*/ - -"use strict"; - -// TODO, deprecate this programmatic initialization. Use Core instead. - -const Core = require("./core/Core"); -const AdminApi = require("./api/Api"); -const InquirerCli = require("./cli/Cli"); - -class ProgrammaticCli { - constructor(options = {}) { - const createInquirerCli = core => { - this._inquirerCli = new InquirerCli(core); - return this._inquirerCli; - }; - - this._core = new Core({ - onlyProgrammaticOptions: true, - plugins: [AdminApi, createInquirerCli] - }); - this._options = { ...options }; - this._cliStarted = false; - this._coreStarted = false; - this._core.tracer.warn( - "Deprecation warning: Cli constructor will be deprecated. Use @mocks-server/core instead" - ); - } - - async _startCore(cliEnabled) { - if (!this._coreStarted) { - this._coreStarted = true; - await this._core.init(this._options); - this._core.settings.set("cli", cliEnabled); - return this._core.start(); - } - return Promise.resolve(); - } - - async start() { - await this._startCore(true); - if (!this._cliStarted && !this._core.settings.get("cli")) { - this._core.settings.set("cli", true); - this._cliStarted = true; - return this._inquirerCli.start(); - } - return Promise.resolve(); - } - - async initServer() { - return this._startCore(false); - } - - stopListeningServerWatch() { - return this._inquirerCli.stopListeningServerWatch(); - } -} - -module.exports = ProgrammaticCli; diff --git a/lib/ProgrammaticServer.js b/lib/ProgrammaticServer.js deleted file mode 100644 index 33f11d0..0000000 --- a/lib/ProgrammaticServer.js +++ /dev/null @@ -1,77 +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. -*/ - -"use strict"; - -// TODO, deprecate this programmatic initialization. Use Core instead. - -const Core = require("./core/Core"); -const AdminApi = require("./api/Api"); - -class ProgrammaticServer { - constructor(mocksFolder, options = {}) { - this._core = new Core({ - onlyProgrammaticOptions: true, - plugins: [AdminApi] - }); - this._options = { ...options, behaviors: mocksFolder }; - this._initPromise = null; - this._onLoadMocks = this._onLoadMocks.bind(this); - this._core.tracer.warn( - "Deprecation warning: Server constructor will be deprecated. Use @mocks-server/core instead" - ); - } - - async _init() { - if (!this._initPromise) { - this._core.onChangeSettings(this._onLoadMocks); - this._initPromise = this._core.init(this._options); - } - return this._initPromise; - } - - async start() { - await this._init(); - return this._core.start().then(() => { - return Promise.resolve(this); - }); - } - - _onLoadMocks() { - // TODO, deprecate Retro compability - this._core._eventEmitter.emit("watch-reload"); - } - - stop() { - return this._core.stop(); - } - - async switchWatch(state) { - return this._core.settings.set("watch", state); - } - - get behaviors() { - return this._core.behaviors; - } - - get watchEnabled() { - return this._core.settings.get("watch"); - } - - get error() { - return this._core.serverError; - } - - get events() { - return this._core._eventEmitter; - } -} - -module.exports = ProgrammaticServer; diff --git a/lib/cli/Cli.js b/lib/cli/Cli.js deleted file mode 100644 index 1914de5..0000000 --- a/lib/cli/Cli.js +++ /dev/null @@ -1,250 +0,0 @@ -/* -Copyright 2019 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 - -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. -*/ - -"use strict"; - -const chalk = require("chalk"); -const { isNumber } = require("lodash"); - -const inquirer = require("./Inquirer"); - -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" - } - ] - }, - logLevel: { - type: "list", - message: "Select log level:", - name: "value", - choices: ["silly", "debug", "verbose", "info", "warn", "error"] - }, - behavior: { - type: "autocomplete", - name: "value", - message: "Please choose behavior" - }, - delay: { - type: "input", - name: "value", - message: "Enter delay time in ms:", - validate: value => isNumber(value), - filter: value => { - if (/^\d*$/.test(value)) { - return parseInt(value, 10); - } - return false; - } - } -}; - -const SCREENS = { - MAIN: "main", - BEHAVIOR: "behavior", - DELAY: "delay", - LOG_LEVEL: "log-level", - LOGS: "logs" -}; - -class Cli { - constructor(core) { - this._core = core; - this._tracer = core.tracer; - this._settings = core.settings; - this._inited = false; - this._currentScreen = null; - - this._onLoadMocks = this._onLoadMocks.bind(this); - this._onChangeSettings = this._onChangeSettings.bind(this); - - this._core.addCustomSetting({ - name: "cli", - type: "booleanString", // Workaround to maintain retrocompatibility with --cli=false - description: "Start interactive CLI plugin", - default: true - }); - } - - init() { - if (!this._settings.get("cli")) { - return Promise.resolve(); - } - this._questions = questions; - this._cli = new inquirer.Inquirer( - this._questions, - this._header.bind(this) // TODO, deprecate quit method - ); - this._logLevel = this._settings.get("log"); - this._inited = true; - this._core.onChangeSettings(this._onChangeSettings); - return Promise.resolve(); - } - - start() { - if (!this._inited || !this._settings.get("cli")) { - return Promise.resolve(); - } - this._stopListeningFilesLoad = this._core.onLoadMocks(this._onLoadMocks); - this._silentTraces(); - return this._displayMainMenu(); - } - - _onLoadMocks() { - this._cli.removeListeners(); - this._cli.exitLogsMode(); - return this._displayMainMenu(); - } - - _onChangeSettings() { - if (this._currentScreen === SCREENS.MAIN) { - this._cli.removeListeners(); - return this._displayMainMenu(); - } - } - - get _serverUrl() { - return `http://${this._settings.get("host")}:${this._settings.get("port")}`; - } - - _header() { - const header = [ - `Mocks server listening at: ${chalk.cyan(this._serverUrl)}`, - `Delay: ${chalk.cyan(this._settings.get("delay"))}`, - `Behaviors: ${chalk.cyan(this._core.behaviors.totalBehaviors)}`, - `Current behavior: ${chalk.cyan(this._core.behaviors.currentName || "-")}`, - `Current fixtures: ${chalk.cyan(this._core.behaviors.currentTotalFixtures || 0)}`, - `Log level: ${chalk.cyan(this._logLevel)}`, - `Watch enabled: ${chalk.cyan(this._settings.get("watch"))}` - ]; - - if (this._core.serverError) { - header.unshift( - chalk.red.bold(`There was an error restarting server: ${this._core.serverError.message}`) - ); - } - - return header; - } - - async _displayMainMenu() { - this._cli.clearScreen(); - this._currentScreen = SCREENS.MAIN; - const action = await this._cli.inquire("main"); - switch (action) { - case "behavior": - return this._changeCurrentBehavior(); - case "delay": - return this._changeDelay(); - case "restart": - return this._restartServer(); - case "logLevel": - return this._changeLogLevel(); - case "watch": - return this._switchWatch(); - case "logs": - return this._displayLogs(); - } - } - - async _changeCurrentBehavior() { - this._currentScreen = SCREENS.BEHAVIOR; - this._cli.clearScreen(); - const behaviorsNames = this._core.behaviors.names; - const behavior = await this._cli.inquire("behavior", { - source: (answers, input) => { - if (!input || !input.length) { - return Promise.resolve(behaviorsNames); - } - return Promise.resolve( - behaviorsNames.filter(currentBehavior => currentBehavior.includes(input)) - ); - } - }); - this._settings.set("behavior", behavior); - return this._displayMainMenu(); - } - - async _changeDelay() { - this._currentScreen = SCREENS.DELAY; - this._cli.clearScreen(); - const delay = await this._cli.inquire("delay"); - this._settings.set("delay", delay); - return this._displayMainMenu(); - } - - async _restartServer() { - try { - await this._core.restart(); - } catch (err) {} - return this._displayMainMenu(); - } - - async _switchWatch() { - this._settings.set("watch", !this._settings.get("watch")); - return this._displayMainMenu(); - } - - async _changeLogLevel() { - this._currentScreen = SCREENS.LOG_LEVEL; - this._cli.clearScreen(); - this._logLevel = await this._cli.inquire("logLevel"); - return this._displayMainMenu(); - } - - async _displayLogs() { - this._currentScreen = SCREENS.LOGS; - this._cli.clearScreen(); - await this._cli.logsMode(() => { - this._settings.set("log", this._logLevel); - }); - this._silentTraces(); - return this._displayMainMenu(); - } - - _silentTraces() { - this._settings.set("log", "silent"); - } - - stopListeningServerWatch() { - if (this._stopListeningFilesLoad) { - this._stopListeningFilesLoad(); - } - } -} - -module.exports = Cli; diff --git a/lib/cli/Inquirer.js b/lib/cli/Inquirer.js deleted file mode 100644 index 9977556..0000000 --- a/lib/cli/Inquirer.js +++ /dev/null @@ -1,136 +0,0 @@ -/* -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 - -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. -*/ - -"use strict"; - -const chalk = require("chalk"); -const inquirer = require("inquirer"); -const autocomplete = require("inquirer-autocomplete-prompt"); -const { cloneDeep, map } = require("lodash"); - -inquirer.registerPrompt("autocomplete", autocomplete); - -const STDIN_ENCODING = "utf8"; -const CTRL_C = "\u0003"; -const CLRS = "\x1Bc"; -const EVENT_LISTENER = "keypress"; -const STDIN_EVENT = "data"; - -const HEADER_ITEM = chalk.yellow(">>"); -const HEADER_FOOTER = "------------------------------------"; -const MAIN_MENU_ID = "main"; -const DEFAULT_QUIT_NAME = "Exit"; -const QUIT_ACTION_ID = "quit"; -const LOGS_MODE_MESSAGE = "Displaying logs. Press any key to display main menu again"; - -const Inquirer = class Inquirer { - constructor(questions, header, quitMethod) { - this._header = header; - this._questions = this._initQuestions(questions, quitMethod); - this._exitLogsMode = this._exitLogsMode.bind(this); - } - - _initQuestions(questions, quitMethod) { - 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()); - if (quitMethod) { - clonedQuestions[MAIN_MENU_ID].choices.push({ - ...quitQuestion, - name: quitMethod.name - }); - this._quit = quitMethod.action; - } else { - clonedQuestions[MAIN_MENU_ID].choices.push(quitQuestion); - this._quit = () => process.exit(); - } - } - return clonedQuestions; - } - - exitLogsMode() { - if (this._logModeExit) { - const stdin = process.stdin; - if (stdin.setRawMode) { - stdin.setRawMode(false); - } - stdin.pause(); - stdin.removeListener(STDIN_EVENT, this._exitLogsMode); - this._logModeExit(); - delete this._logModeExit; - } - } - - _exitLogsMode(key) { - if (key === CTRL_C) { - process.exit(); - } - this.exitLogsMode(); - } - - async logsMode(startLogs) { - this.clearScreen(); - console.log(chalk.blue(LOGS_MODE_MESSAGE)); - const stdin = process.stdin; - if (stdin.setRawMode) { - stdin.setRawMode(true); - } - stdin.resume(); - stdin.setEncoding(STDIN_ENCODING); - stdin.on(STDIN_EVENT, this._exitLogsMode); - if (startLogs) { - startLogs(); - } - return new Promise(resolve => { - this._logModeExit = resolve; - }); - } - - async inquire(questionKey, extendProperties) { - const answers = await inquirer.prompt({ - ...this._questions[questionKey], - ...extendProperties - }); - this.removeListeners(); - if (questionKey === MAIN_MENU_ID && answers.value === QUIT_ACTION_ID) { - return this._quit(); - } - return answers.value; - } - - quit() { - this._quit(); - } - - clearScreen(opts) { - const options = opts || {}; - process.stdout.write(CLRS); - if (options.header !== false) { - const headers = (this._header && this._header()) || []; - headers.map(header => console.log(`${HEADER_ITEM} ${header}`)); - console.log(HEADER_FOOTER); - } - } - - removeListeners() { - const listeners = process.stdin.listeners(EVENT_LISTENER); - map(listeners, listener => { - process.stdin.removeListener(EVENT_LISTENER, listener); - }); - } -}; - -module.exports = { - Inquirer -}; diff --git a/lib/core/Core.js b/lib/core/Core.js deleted file mode 100644 index 15b95f4..0000000 --- a/lib/core/Core.js +++ /dev/null @@ -1,125 +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 EventEmitter = require("events"); - -const { LOAD_MOCKS, CHANGE_SETTINGS } = require("./eventNames"); -const Server = require("./server/Server"); -const tracer = require("./tracer"); -const Mocks = require("./mocks/Mocks"); -const Settings = require("./settings/Settings"); -const Plugins = require("./Plugins"); - -class Core { - constructor(coreOptions = {}) { - this._eventEmitter = new EventEmitter(); - this._settings = new Settings( - { - onlyProgrammaticOptions: coreOptions.onlyProgrammaticOptions - }, - this._eventEmitter - ); - this._mocks = new Mocks(this._settings, this._eventEmitter); - this._server = new Server(this._mocks, this._settings, this._eventEmitter); - this._plugins = new Plugins(coreOptions.plugins, this); - this._inited = false; - this._startPluginsPromise = null; - } - - async init(options) { - if (this._inited) { - return Promise.resolve(); - } - this._inited = true; - // Register plugins, let them add their custom settings - await this._plugins.register(); - // Init settings, read command line arguments, etc. - await this._settings.init(options); - // Settings are ready, init all - await this._mocks.init(); - await this._server.init(); - return this._plugins.init(); - } - - async start() { - await this.init(); // in case it has not been initializated manually before - await this._mocks.start(); - await this._server.start(); - return this._startPlugins(); - } - - async _startPlugins() { - if (!this._startPluginsPromise) { - this._startPluginsPromise = this._plugins.start(); - } - return this._startPluginsPromise; - } - - addCustomRouter(path, router) { - return this._server.addCustomRouter(path, router); - } - - addCustomSetting(option) { - return this._settings.addCustom(option); - } - - // Listeners - - onLoadMocks(cb) { - const removeCallback = () => { - this._eventEmitter.removeListener(LOAD_MOCKS, cb); - }; - this._eventEmitter.on(LOAD_MOCKS, cb); - return removeCallback; - } - - onChangeSettings(cb) { - const removeCallback = () => { - this._eventEmitter.removeListener(CHANGE_SETTINGS, cb); - }; - this._eventEmitter.on(CHANGE_SETTINGS, cb); - return removeCallback; - } - - // Expose Server methods and getters - - stop() { - return this._server.stop(); - } - - restart() { - return this._server.restart(); - } - - get serverError() { - return this._server.error; - } - - // Expose child objects needed - - get settings() { - return this._settings; - } - - get behaviors() { - return this._mocks.behaviors; - } - - // TODO, deprecate getter - get features() { - return this._mocks.behaviors; - } - - get tracer() { - return tracer; - } -} - -module.exports = Core; diff --git a/lib/core/Plugins.js b/lib/core/Plugins.js deleted file mode 100644 index 044142e..0000000 --- a/lib/core/Plugins.js +++ /dev/null @@ -1,161 +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 isPromise = require("is-promise"); -const { isObject, isFunction } = require("lodash"); - -const tracer = require("./tracer"); - -class Plugins { - constructor(plugins, core) { - this._core = core; - this._plugins = plugins || []; - this._pluginsInstances = []; - this._pluginsRegistered = 0; - this._pluginsInitialized = 0; - this._pluginsStarted = 0; - } - - register() { - return this._registerPlugins().then(() => { - tracer.verbose(`Registered ${this._pluginsRegistered} plugins`); - return Promise.resolve(); - }); - } - - init() { - return this._initPlugins().then(() => { - tracer.verbose(`Initializated ${this._pluginsInitialized} plugins`); - return Promise.resolve(); - }); - } - - start() { - return this._startPlugins().then(() => { - tracer.verbose(`Started ${this._pluginsStarted} plugins`); - return Promise.resolve(); - }); - } - - _catchRegisterError(error) { - console.log("Error registering plugin"); - console.log(error); - return {}; - } - - _catchInitError(error) { - this._pluginsInitialized = this._pluginsInitialized - 1; - tracer.error("Error initializating plugin"); - tracer.debug(error); - return Promise.resolve(); - } - - _catchStartError(error) { - this._pluginsStarted = this._pluginsStarted - 1; - tracer.error("Error starting plugin"); - tracer.debug(error); - return Promise.resolve(); - } - - _registerPluginFunction(Plugin) { - let pluginInstance; - try { - pluginInstance = Plugin(this._core); - this._pluginsRegistered++; - } catch (error) { - return this._catchRegisterError(error); - } - return pluginInstance; - } - - _registerPlugin(Plugin) { - if (isObject(Plugin) && !isFunction(Plugin)) { - this._pluginsRegistered++; - return Plugin; - } - let pluginInstance; - try { - pluginInstance = new Plugin(this._core); - this._pluginsRegistered++; - } catch (error) { - if (error.message.includes("is not a constructor")) { - return this._registerPluginFunction(Plugin); - } else { - return this._catchRegisterError(error); - } - } - return pluginInstance; - } - - _registerPlugins(pluginIndex = 0) { - if (pluginIndex === this._plugins.length) { - return Promise.resolve(); - } - const plugin = this._registerPlugin(this._plugins[pluginIndex]); - this._pluginsInstances.push(plugin); - return this._registerPlugins(pluginIndex + 1); - } - - _initPlugins(pluginIndex = 0) { - if (pluginIndex === this._pluginsInstances.length) { - return Promise.resolve(); - } - this._pluginsInitialized++; - tracer.debug(`Initializing plugin ${pluginIndex}`); - const initNextPlugin = () => { - return this._initPlugins(pluginIndex + 1); - }; - - if (!this._pluginsInstances[pluginIndex].init) { - this._pluginsInitialized = this._pluginsInitialized - 1; - return initNextPlugin(); - } - let pluginInit; - try { - pluginInit = this._pluginsInstances[pluginIndex].init(); - } catch (error) { - return this._catchInitError(error).then(initNextPlugin); - } - - if (!isPromise(pluginInit)) { - return initNextPlugin(); - } - return pluginInit.catch(this._catchInitError).then(initNextPlugin); - } - - _startPlugins(pluginIndex = 0) { - if (pluginIndex === this._pluginsInstances.length) { - return Promise.resolve(); - } - this._pluginsStarted++; - tracer.debug(`Starting plugin ${pluginIndex}`); - const startNextPlugin = () => { - return this._startPlugins(pluginIndex + 1); - }; - - if (!this._pluginsInstances[pluginIndex].start) { - this._pluginsStarted = this._pluginsStarted - 1; - return startNextPlugin(); - } - let pluginStart; - try { - pluginStart = this._pluginsInstances[pluginIndex].start(); - } catch (error) { - return this._catchStartError(error).then(startNextPlugin); - } - - if (!isPromise(pluginStart)) { - return startNextPlugin(); - } - return pluginStart.catch(this._catchStartError).then(startNextPlugin); - } -} - -module.exports = Plugins; diff --git a/lib/core/eventNames.js b/lib/core/eventNames.js deleted file mode 100644 index 11e05e6..0000000 --- a/lib/core/eventNames.js +++ /dev/null @@ -1,14 +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. -*/ - -module.exports = { - LOAD_MOCKS: "load:mocks", - CHANGE_SETTINGS: "change:settings" -}; diff --git a/lib/core/mocks/Behavior.js b/lib/core/mocks/Behavior.js deleted file mode 100644 index 3c65120..0000000 --- a/lib/core/mocks/Behavior.js +++ /dev/null @@ -1,66 +0,0 @@ -/* -Copyright 2019 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 - -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. -*/ - -"use strict"; - -const { cloneDeep, map, sum } = require("lodash"); -const routeParser = require("route-parser"); - -const FUNCTION_TYPE = "function"; - -class Behavior { - constructor(fixtures) { - this._fixtures = fixtures; - this._methods = this.fixturesToMethods(fixtures); - this._fixturesCollection = this.fixturesToCollection(fixtures); - } - - fixturesToCollection(fixtures) { - return cloneDeep(fixtures).map(fixture => { - if (typeof fixture.response === FUNCTION_TYPE) { - // TODO, add a fixture property indicating type - fixture.response = FUNCTION_TYPE; - } - delete fixture.route; - return fixture; - }); - } - - fixturesToMethods(fixtures, baseFixtures = {}) { - const fixturesObject = cloneDeep(baseFixtures); - fixtures.forEach(fixtureData => { - fixturesObject[fixtureData.method] = fixturesObject[fixtureData.method] || {}; - fixturesObject[fixtureData.method][fixtureData.url] = { - route: routeParser(fixtureData.url), - response: fixtureData.response - }; - }); - return fixturesObject; - } - - extend(fixtures) { - return new Behavior(this._fixtures.concat(fixtures)); - } - - get methods() { - return this._methods; - } - - get fixtures() { - return this._fixturesCollection; - } - - get totalFixtures() { - return sum(map(this._methods, urls => Object.keys(urls).length)); - } -} - -module.exports = Behavior; diff --git a/lib/core/mocks/Behaviors.js b/lib/core/mocks/Behaviors.js deleted file mode 100644 index f670b33..0000000 --- a/lib/core/mocks/Behaviors.js +++ /dev/null @@ -1,168 +0,0 @@ -/* -Copyright 2019 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 - -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. -*/ - -"use strict"; - -const Boom = require("boom"); - -const { flatten, map, each, compact } = require("lodash"); - -const tracer = require("../tracer"); - -const { LOAD_MOCKS, CHANGE_SETTINGS } = require("../eventNames"); - -class Behaviors { - constructor(filesHandler, settings, eventEmitter) { - this._filesHandler = filesHandler; - this._settings = settings; - this._eventEmitter = eventEmitter; - this._onLoadMocks = this._onLoadMocks.bind(this); - this._onChangeSettings = this._onChangeSettings.bind(this); - } - - init() { - this._loadBehaviors(); - this._eventEmitter.on(LOAD_MOCKS, this._onLoadMocks); - this._eventEmitter.on(CHANGE_SETTINGS, this._onChangeSettings); - return Promise.resolve(); - } - - _loadBehaviors() { - tracer.debug("Processing mocks"); - this._behaviors = this._getBehaviors(this._filesHandler.files); - this._totalFixtures = this._getTotalFixtures(this._filesHandler.files); - this._collection = this._getCollection(this._filesHandler.files); - this._names = this._getNames(this._collection); - this._current = this._settings.get("behavior") || this._names[0]; - tracer.silly( - `Mocks details: ${JSON.stringify( - { - totalFixtures: this._totalFixtures, - current: this._current - }, - null, - 2 - )}` - ); - - try { - this._checkCurrent(this._current); - } catch (error) { - tracer.warn( - `Defined behavior "${this._current}" was not found. Inititializing with first found behavior` - ); - this._current = this._names[0]; - } - } - - _onLoadMocks() { - this._loadBehaviors(); - } - - _onChangeSettings(changeDetails) { - if (changeDetails.hasOwnProperty("behavior")) { - this.current = changeDetails.behavior; - } - } - - _getCollection(mocksFolderFiles) { - return compact( - flatten( - map(mocksFolderFiles, mocksFolderFile => - map(mocksFolderFile, (behavior, behaviorName) => { - if (behavior.fixtures) { - return { - name: behaviorName, - fixtures: behavior.fixtures - }; - } - return null; - }) - ) - ) - ); - } - - _getBehaviors(mocksFolderFiles) { - const behaviors = {}; - each(mocksFolderFiles, mocksFolderFile => { - each(mocksFolderFile, (behavior, behaviorName) => { - // TODO, check if current object is a behavior with common method - if (behavior.methods) { - behaviors[behaviorName] = behavior.methods; - } - }); - }); - return behaviors; - } - - _getTotalFixtures(mocksFolderFiles) { - const totalFixtures = {}; - each(mocksFolderFiles, mocksFolderFile => { - each(mocksFolderFile, (behavior, behaviorName) => { - // TODO, check if current object is a behavior with common method - if (behavior.totalFixtures) { - totalFixtures[behaviorName] = behavior.totalFixtures; - } - }); - }); - return totalFixtures; - } - - _getNames(collection) { - return collection.map(item => item.name); - } - - _checkCurrent(behaviorName) { - if (!this._names.includes(behaviorName)) { - throw Boom.badData(`Behavior not found: ${behaviorName}`); - } - } - - set current(behaviorName) { - this._checkCurrent(behaviorName); - this._current = behaviorName; - } - - get current() { - return this._behaviors[this._current]; - } - - get currentTotalFixtures() { - return this._totalFixtures[this._current]; - } - - get currentFromCollection() { - return this._collection.find(item => item.name === this._current); - } - - get all() { - return this._behaviors; - } - - get names() { - return this._names; - } - - get totalBehaviors() { - return this._names.length; - } - - get currentName() { - return this._current; - } - - get collection() { - return this._collection; - } -} - -module.exports = Behaviors; diff --git a/lib/core/mocks/FilesHandler.js b/lib/core/mocks/FilesHandler.js deleted file mode 100644 index 173f6e7..0000000 --- a/lib/core/mocks/FilesHandler.js +++ /dev/null @@ -1,125 +0,0 @@ -/* -Copyright 2019 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 - -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 Boom = require("boom"); -const requireAll = require("require-all"); -const watch = require("node-watch"); - -const { map, debounce } = require("lodash"); - -const tracer = require("../tracer"); -const { LOAD_MOCKS, CHANGE_SETTINGS } = require("../eventNames"); - -class FilesHandler { - constructor(settings, eventEmitter, extraOptions = {}) { - this._customRequireCache = extraOptions.requireCache; - this._settings = settings; - this._eventEmitter = eventEmitter; - this._onChangeSettings = this._onChangeSettings.bind(this); - } - - init() { - this._eventEmitter.on(CHANGE_SETTINGS, this._onChangeSettings); - try { - this._loadFiles(); - return Promise.resolve(); - } catch (err) { - return Promise.reject(err); - } - } - - start() { - this._switchWatch(); - } - - _cleanRequireCacheFolder() { - map(this._cache(), (cacheData, filePath) => { - if (filePath.indexOf(this._path) === 0) { - this._cleanRequireCache(this._cache()[filePath]); - } - }); - } - - _cleanRequireCache(requireModule) { - if (requireModule) { - map(requireModule.children, moduleData => { - if (moduleData.id.indexOf(this._path) === 0) { - this._cleanRequireCache(this._cache()[moduleData.id]); - } - }); - this._cache()[requireModule.id] = undefined; - } - } - - _resolveFolder(folder) { - if (!folder) { - tracer.error( - 'Please provide a path to a folder containing behaviors using the "behaviors" option' - ); - throw Boom.badData("Invalid mocks folder"); - } - if (path.isAbsolute(folder)) { - return folder; - } - return path.resolve(process.cwd(), folder); - } - - _loadFiles() { - this._path = this._resolveFolder(this._settings.get("behaviors")); - tracer.info(`Loading mocks from folder ${this._path}`); - this._cleanRequireCacheFolder(); - this._files = requireAll({ - dirname: this._path, - recursive: true - }); - this._eventEmitter.emit(LOAD_MOCKS); - } - - _switchWatch() { - const enabled = this._settings.get("watch"); - if (this._watcher) { - tracer.debug("Stopping files watch"); - this._watcher.close(); - } - if (enabled) { - tracer.debug("Starting files watcher"); - this._watcher = watch( - this._path, - { recursive: true }, - debounce(() => { - tracer.info("Files changed detected"); - this._loadFiles(); - }), - 1000 - ); - } - } - - _onChangeSettings(changeDetails) { - if (changeDetails.hasOwnProperty("behaviors")) { - this._loadFiles(); - this._switchWatch(); - } else if (changeDetails.hasOwnProperty("watch")) { - this._switchWatch(); - } - } - - _cache() { - return this._customRequireCache || require.cache; - } - - get files() { - return this._files; - } -} - -module.exports = FilesHandler; diff --git a/lib/core/mocks/Mocks.js b/lib/core/mocks/Mocks.js deleted file mode 100644 index f051ea7..0000000 --- a/lib/core/mocks/Mocks.js +++ /dev/null @@ -1,37 +0,0 @@ -/* -Copyright 2019 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 - -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 Behaviors = require("./Behaviors"); -const FilesHandler = require("./FilesHandler"); - -class Mocks { - constructor(settings, eventEmitter) { - this._settings = settings; - this._eventEmitter = eventEmitter; - this._filesHandler = new FilesHandler(this._settings, this._eventEmitter); - this._behaviors = new Behaviors(this._filesHandler, this._settings, this._eventEmitter); - } - - async init() { - await this._filesHandler.init(); - return this._behaviors.init(); - } - - async start() { - await this._filesHandler.start(); - } - - get behaviors() { - return this._behaviors; - } -} - -module.exports = Mocks; diff --git a/lib/core/server/Server.js b/lib/core/server/Server.js deleted file mode 100644 index 6e06ba8..0000000 --- a/lib/core/server/Server.js +++ /dev/null @@ -1,166 +0,0 @@ -/* -Copyright 2019 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 - -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. -*/ - -"use strict"; - -const http = require("http"); - -const express = require("express"); -const { delay, find } = require("lodash"); -const tracer = require("../tracer"); -const middlewares = require("./middlewares"); - -class Server { - constructor(mocks, settings, eventEmitter) { - this._mocks = mocks; - this._eventEmitter = eventEmitter; - this._customRouters = []; - this._settings = settings; - this._error = null; - - this._startServer = this._startServer.bind(this); - } - - init() { - process.on("SIGINT", () => { - this.stop().then(() => { - tracer.info("Server closed"); - }); - process.exit(); - }); - return Promise.resolve(); - } - - _initServer() { - if (this._serverInitted) { - return; - } - this._serverInitted = true; - this._express = express(); - - // Add middlewares - this._express.use(middlewares.addRequestId); - this._express.use(middlewares.enableCors); - this._express.use(middlewares.addCommonHeaders); - this._express.options("*", middlewares.enableCors); - this._express.use(middlewares.jsonBodyParser); - this._express.use(middlewares.traceRequest); - this._registerCustomRouters(); - this._express.use(this._fixturesMiddleware.bind(this)); - this._express.use(middlewares.notFound); - this._express.use(middlewares.errorHandler); - - // Create server - this._server = http.createServer(this._express); - - this._server.on("error", error => { - tracer.error(`Server error: ${error.message}`); - this._error = error; - throw error; - }); - } - - _startServer(resolve, reject) { - try { - this._server.listen( - { - port: this._settings.get("port"), - host: this._settings.get("host") - }, - error => { - if (error) { - tracer.error(`Error starting server: ${error.message}`); - this._serverStarting = false; - this._serverStarted = false; - this._error = error; - reject(error); - } else { - tracer.info( - `Server started and listening at http://localhost:${this._settings.get("port")}` - ); - this._error = null; - this._serverStarting = false; - this._serverStarted = true; - resolve(this); - } - } - ); - } catch (error) { - reject(error); - } - } - - _registerCustomRouters() { - this._customRouters.forEach(customRouter => { - this._express.use(customRouter.path, customRouter.router); - }); - } - - _getFixtureMatching(method, url) { - return find(this._mocks.behaviors.current[method], fixture => fixture.route.match(url)); - } - - _fixturesMiddleware(req, res, next) { - const fixture = this._getFixtureMatching(req.method, req.url); - if (fixture) { - delay(() => { - // TODO, add property to fixtures indicating type - if (typeof fixture.response === "function") { - tracer.debug(`Fixture response is a function, executing response | ${req.id}`); - req.params = fixture.route.match(req.url); - fixture.response(req, res, next); - } else { - tracer.debug(`Sending fixture | ${req.id}`); - res.status(fixture.response.status); - res.send(fixture.response.body); - } - }, this._settings.get("delay")); - } else { - next(); - } - } - - stop() { - if (this._server) { - return new Promise(resolve => { - tracer.verbose("Stopping server"); - this._server.close(() => { - tracer.info("Server stopped"); - resolve(); - }); - }); - } - return Promise.resolve(); - } - - async start() { - this._initServer(); - return new Promise(this._startServer); - } - - addCustomRouter(path, router) { - this._customRouters.push({ - path, - router - }); - } - - async restart() { - await this.stop(); - return this.start(); - } - - get error() { - return this._error; - } -} - -module.exports = Server; diff --git a/lib/core/server/middlewares.js b/lib/core/server/middlewares.js deleted file mode 100644 index 76133ea..0000000 --- a/lib/core/server/middlewares.js +++ /dev/null @@ -1,65 +0,0 @@ -/* -Copyright 2019 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 - -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. -*/ - -"use strict"; - -const bodyParser = require("body-parser"); -const Boom = require("boom"); -const cors = require("cors"); -const expressRequestId = require("express-request-id"); - -const tracer = require("../tracer"); - -const addRequestId = expressRequestId(); -const jsonBodyParser = bodyParser.json(); -const enableCors = cors(); - -const addCommonHeaders = (req, res, next) => { - res.header("Accept-Encoding", "gzip, deflate, br"); - res.header("Accept-Language", "es-ES,es;q=0.9,en;q=0.8,la;q=0.7,fr;q=0.6"); // TODO, remove harcoded language headers - next(); -}; - -const traceRequest = (req, res, next) => { - tracer.verbose(`Request received | ${req.method} => ${req.url} | Assigned id: ${req.id}`); - next(); -}; - -const notFound = (req, res, next) => { - tracer.debug(`Sending Not found response | ${req.id}`); - next(Boom.notFound()); -}; - -const errorHandler = (err, req, res, next) => { - const isBoom = Boom.isBoom(err); - const stack = isBoom ? null : err && err.stack; - const error = isBoom ? err : err && Boom.badImplementation(err); - if (error) { - tracer.error(`Sending Error "${error.message}" | ${req.id}`); - if (stack) { - tracer.silly(stack.toString()); - } - res.status(error.output.statusCode); - res.send(error.output.payload); - } else { - next(); - } -}; - -module.exports = { - addRequestId, - jsonBodyParser, - traceRequest, - enableCors, - addCommonHeaders, - notFound, - errorHandler -}; diff --git a/lib/core/settings/CommandLineArguments.js b/lib/core/settings/CommandLineArguments.js deleted file mode 100644 index 1563043..0000000 --- a/lib/core/settings/CommandLineArguments.js +++ /dev/null @@ -1,76 +0,0 @@ -/* -Copyright 2019 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 - -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. -*/ - -"use strict"; - -const { isUndefined } = require("lodash"); - -const commander = require("commander"); - -class CommandLineArguments { - constructor(defaultOptions) { - this._options = {}; - this._optionsNames = Object.keys(defaultOptions); - // TODO, generate initial options dynamically from Options object using "addCustomOption" method - this._commander = commander - .option("--behavior ", "Define current behavior") - .option("--behaviors ", "Define folder from which load behaviors") - .option("--delay ", "Define delay time") - .option("--host ", "Host for server") - .option("--log ", "Log level") - .option("--port ", "Port for server", parseInt) - .option("--watch ", "Watch or not", this._stringToBoolean) // TODO, change by --no-watch option - // TODO, remove deprecated options - .option("--feature ", "Define current behavior") - .option("--features ", "Define folder from which load behaviors"); - } - - init() { - this._options = this._commander.parse(process.argv); - return Promise.resolve(); - } - - // TODO, deprecate "stringBoolean" options. Use --no- commander feature - _stringToBoolean(val) { - if (isUndefined(val) || val === "true") { - return true; - } else if (val === "false") { - return false; - } - throw new Error("Invalid boolean value"); - } - - addCustom(optionDetails) { - const optionPrefix = - optionDetails.type === "boolean" && optionDetails.default === true ? "--no-" : "--"; - const optionValueGetter = optionDetails.type === "boolean" ? "" : ` <${optionDetails.name}>`; - const optionParser = optionDetails.parse - ? optionDetails.parse - : optionDetails.type === "number" - ? parseInt - : optionDetails.type === "booleanString" - ? this._stringToBoolean - : undefined; - - this._commander.option( - `${optionPrefix}${optionDetails.name}${optionValueGetter}`, - optionDetails.description, - optionParser - ); - this._optionsNames.push(optionDetails.name); - } - - get options() { - return this._options; - } -} - -module.exports = CommandLineArguments; diff --git a/lib/core/settings/Options.js b/lib/core/settings/Options.js deleted file mode 100644 index 3d7eec4..0000000 --- a/lib/core/settings/Options.js +++ /dev/null @@ -1,156 +0,0 @@ -/* -Copyright 2019 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 - -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 { isUndefined } = require("lodash"); - -const tracer = require("../tracer"); - -const CommandLineArguments = require("./CommandLineArguments"); - -const DEFAULT_OPTIONS = { - behavior: null, - behaviors: null, - delay: 0, - host: "0.0.0.0", - port: 3100, - watch: true, - log: "info", - // TODO, remove deprecated options - feature: null, - features: null -}; - -const DEPRECATED_OPTIONS = { - feature: "behavior", - features: "behaviors" -}; - -class Options { - constructor(coreOptions = {}) { - this._onlyProgrammaticOptions = coreOptions.onlyProgrammaticOptions; - this._options = {}; - this._optionsNames = Object.keys(DEFAULT_OPTIONS); - this._customDefaults = {}; - this._initialized = false; - this._removeDeprecatedOption = this._removeDeprecatedOption.bind(this); - this._commandLineArguments = new CommandLineArguments(DEFAULT_OPTIONS); - } - - async init(options) { - if (!this._initialized) { - this._initialized = true; - const baseOptions = { - ...DEFAULT_OPTIONS, - ...this._customDefaults, - ...options - }; - if (!this._onlyProgrammaticOptions) { - await this._commandLineArguments.init(); - this._options = this._getValidOptions( - this._removeDeprecatedOptions({ - ...baseOptions, - ...this._commandLineArguments.options - }) - ); - } else { - this._options = this._getValidOptions(this._removeDeprecatedOptions(baseOptions)); - } - } - return Promise.resolve(); - } - - get options() { - return this._options; - } - - _rejectCustomOption(errorMessage) { - tracer.error(errorMessage); - throw new Error(errorMessage); - } - - addCustom(optionDetails) { - if (this._initialized) { - this._rejectCustomOption("Options are already initializated. No more options can be added"); - } - if (!optionDetails) { - this._rejectCustomOption("Please provide option details when adding a new option"); - } - if (!optionDetails.name) { - this._rejectCustomOption("Please provide option name when adding a new option"); - } - if (this._optionsNames.includes(optionDetails.name)) { - this._rejectCustomOption(`Option with name ${optionDetails.name} is already registered`); - } - if ( - !optionDetails.type || - !["string", "number", "boolean", "booleanString"].includes(optionDetails.type) - ) { - this._rejectCustomOption( - "Please provide a valid option type between: string, number, boolean" - ); - } - if (!optionDetails.description) { - tracer.warn("Please provide option description when adding a new option"); - optionDetails.description = ""; - } - - this._optionsNames.push(optionDetails.name); - this._customDefaults[optionDetails.name] = optionDetails.default; - this._commandLineArguments.addCustom(optionDetails); - } - - getValidOptionName(optionName) { - if (this._optionsNames.includes(optionName) && !DEPRECATED_OPTIONS[optionName]) { - return optionName; - } - if (DEPRECATED_OPTIONS[optionName]) { - tracer.warn( - `Deprecation warning: ${optionName} option will be deprecated. Use ${DEPRECATED_OPTIONS[optionName]} instead` - ); - return DEPRECATED_OPTIONS[optionName]; - } - throw new Error("Not valid option"); - } - - _getValidOptions(options) { - return this._optionsNames.reduce((cleanObject, currentKey) => { - if (!isUndefined(options[currentKey])) { - cleanObject[currentKey] = options[currentKey]; - } - return cleanObject; - }, {}); - } - - _removeDeprecatedOption(options, optionName) { - if (options[optionName] !== DEFAULT_OPTIONS[optionName]) { - const newOption = DEPRECATED_OPTIONS[optionName]; - if (!options[newOption]) { - options[newOption] = options[optionName]; - } - tracer.warn( - `Deprecation warning: --${optionName} option will be deprecated. Use --${DEPRECATED_OPTIONS[optionName]} instead` - ); - } - delete options[optionName]; - return options; - } - - _removeDeprecatedOptions(options) { - let newOptions = options; - Object.keys(DEPRECATED_OPTIONS).forEach(optionName => { - newOptions = this._removeDeprecatedOption(newOptions, optionName); - }); - - return newOptions; - } -} - -module.exports = Options; diff --git a/lib/core/settings/Settings.js b/lib/core/settings/Settings.js deleted file mode 100644 index a35af5a..0000000 --- a/lib/core/settings/Settings.js +++ /dev/null @@ -1,64 +0,0 @@ -/* -Copyright 2019 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 - -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 Options = require("./Options"); - -const tracer = require("../tracer"); -const { CHANGE_SETTINGS } = require("../eventNames"); - -class Settings { - constructor(coreOptions, eventEmitter) { - this._eventEmitter = eventEmitter; - this._optionsHandler = new Options(coreOptions); - this._emitChange = this._emitChange.bind(this); //Add debounce here to group change events - this._newSettings = {}; - } - - async init(options) { - await this._optionsHandler.init(options); - this._settings = { ...this._optionsHandler.options }; - this._setTracerLevel(); - tracer.info("Settings ready"); - tracer.verbose(`Current settings: ${JSON.stringify(this._settings, null, 2)}`); - return Promise.resolve(); - } - - _setTracerLevel() { - tracer.set("console", this._settings.log); - } - - _emitChange() { - this._eventEmitter.emit(CHANGE_SETTINGS, { ...this._newSettings }); - this._newSettings = {}; - } - - set(option, value) { - const optionName = this._optionsHandler.getValidOptionName(option); - if (this._settings[optionName] !== value) { - this._settings[optionName] = value; - this._newSettings[optionName] = value; - this._emitChange(); - if (optionName === "log") { - this._setTracerLevel(); - } - } - } - - get(option) { - return this._settings[this._optionsHandler.getValidOptionName(option)]; - } - - addCustom(option) { - return this._optionsHandler.addCustom(option); - } -} - -module.exports = Settings; diff --git a/lib/core/tracer.js b/lib/core/tracer.js deleted file mode 100644 index c604c78..0000000 --- a/lib/core/tracer.js +++ /dev/null @@ -1,59 +0,0 @@ -/* -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 - -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. -*/ - -"use strict"; - -const winston = require("winston"); - -const format = winston.format.printf(info => { - return `${info.timestamp} [Mocks ${info.level}] ${info.message}`; -}); - -const transports = { - console: new winston.transports.Console({ - format: winston.format.combine( - winston.format.colorize(), - winston.format.timestamp({ - format: "HH:mm:ss:SS" - }), - format - ) - }) -}; - -const logger = winston.createLogger({ - transports: [transports.console] -}); - -logger.silly = logger.silly.bind(logger); -logger.debug = logger.debug.bind(logger); -logger.verbose = logger.verbose.bind(logger); -logger.info = logger.info.bind(logger); -logger.warn = logger.warn.bind(logger); -logger.error = logger.error.bind(logger); - -const set = (transport, level) => { - if (level === "silent") { - transports[transport].silent = true; - } else { - transports[transport].silent = false; - transports[transport].level = level; - } -}; - -module.exports = { - silly: logger.silly, - debug: logger.debug, - verbose: logger.verbose, - info: logger.info, - warn: logger.warn, - error: logger.error, - set: set -}; diff --git a/lib/start.js b/lib/start.js deleted file mode 100644 index afeeda1..0000000 --- a/lib/start.js +++ /dev/null @@ -1,37 +0,0 @@ -/* -Copyright 2019 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 - -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. -*/ - -"use strict"; - -const Core = require("./core/Core"); -const AdminApi = require("./api/Api"); -const InquirerCli = require("./cli/Cli"); - -const handleError = error => { - console.error(`Error: ${error.message}`); - process.exitCode = 1; -}; - -const start = () => { - try { - const mocksServer = new Core({ - plugins: [AdminApi, InquirerCli] - }); - - return mocksServer.start().catch(handleError); - } catch (error) { - return handleError(error); - } -}; - -module.exports = { - start -}; diff --git a/package-lock.json b/package-lock.json index a2cc090..6dd9ba4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { - "name": "@mocks-server/main", - "version": "1.3.0", + "name": "@mocks-server/plugin-admin-api", + "version": "1.0.0", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -464,6 +464,25 @@ "@types/yargs": "^13.0.0" } }, + "@mocks-server/core": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@mocks-server/core/-/core-1.0.0.tgz", + "integrity": "sha512-Up3gDTUBCyC81H+tivxwoJorVaxEmVeQFe4PdaqajydyfxZEfZI3hBtjEgj2JjiqjZSQTkILrbIdo2CkVe7T1g==", + "dev": true, + "requires": { + "body-parser": "1.19.0", + "boom": "7.3.0", + "commander": "4.0.1", + "cors": "2.8.5", + "express": "4.17.1", + "express-request-id": "1.4.1", + "lodash": "4.17.15", + "node-watch": "0.6.3", + "require-all": "3.0.0", + "route-parser": "0.0.5", + "winston": "3.2.1" + } + }, "@nodelib/fs.scandir": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.3.tgz", @@ -576,11 +595,6 @@ "@babel/types": "^7.3.0" } }, - "@types/color-name": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz", - "integrity": "sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==" - }, "@types/events": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/@types/events/-/events-3.0.0.tgz", @@ -753,17 +767,20 @@ "ansi-escapes": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.1.0.tgz", - "integrity": "sha512-UgAb8H9D41AQnu/PbWlCofQVcnV4Gs2bBJi9eZPxfU/hgglFh3SMDMENRIqdr7H6XFnXdoknctFByVsCOotTVw==" + "integrity": "sha512-UgAb8H9D41AQnu/PbWlCofQVcnV4Gs2bBJi9eZPxfU/hgglFh3SMDMENRIqdr7H6XFnXdoknctFByVsCOotTVw==", + "dev": true }, "ansi-regex": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==" + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "dev": true }, "ansi-styles": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, "requires": { "color-convert": "^1.9.0" } @@ -871,6 +888,7 @@ "version": "2.6.3", "resolved": "https://registry.npmjs.org/async/-/async-2.6.3.tgz", "integrity": "sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg==", + "dev": true, "requires": { "lodash": "^4.17.14" } @@ -1123,6 +1141,7 @@ "version": "7.3.0", "resolved": "https://registry.npmjs.org/boom/-/boom-7.3.0.tgz", "integrity": "sha512-Swpoyi2t5+GhOEGw8rEsKvTxFLIDiiKoUc2gsoV6Lyr43LHBIzch3k2MvYUs8RTROrIkVJ3Al0TkaOGjnb+B6A==", + "dev": true, "requires": { "hoek": "6.x.x" } @@ -1279,56 +1298,11 @@ "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=", "dev": true }, - "chalk": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", - "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "dependencies": { - "ansi-styles": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.0.tgz", - "integrity": "sha512-7kFQgnEaMdRtwf6uSfUnVr9gSGC7faurn+J/Mv90/W+iTtN0405/nLdopfMWwchyxhbGYl6TC4Sccn9TUkGAgg==", - "requires": { - "@types/color-name": "^1.1.1", - "color-convert": "^2.0.1" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" - }, - "supports-color": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", - "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, "chardet": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", - "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==" + "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", + "dev": true }, "ci-info": { "version": "2.0.0", @@ -1369,6 +1343,7 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", + "dev": true, "requires": { "restore-cursor": "^3.1.0" } @@ -1429,7 +1404,8 @@ "cli-width": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.2.0.tgz", - "integrity": "sha1-/xnt6Kml5XkyQUewwR8PvLq+1jk=" + "integrity": "sha1-/xnt6Kml5XkyQUewwR8PvLq+1jk=", + "dev": true }, "cliui": { "version": "5.0.0", @@ -1496,6 +1472,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/color/-/color-3.0.0.tgz", "integrity": "sha512-jCpd5+s0s0t7p3pHQKpnJ0TpQKKdleP71LWcA0aqiljpiuAkOSUFN/dyH8ZwF0hRmFlrIuRhufds1QyEP9EB+w==", + "dev": true, "requires": { "color-convert": "^1.9.1", "color-string": "^1.5.2" @@ -1505,6 +1482,7 @@ "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, "requires": { "color-name": "1.1.3" } @@ -1512,12 +1490,14 @@ "color-name": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true }, "color-string": { "version": "1.5.3", "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.5.3.tgz", "integrity": "sha512-dC2C5qeWoYkxki5UAXapdjqO672AM4vZuPGRQfO8b5HKuKGBbKWpITyDYN7TOFKvRW7kOgAn3746clDBMDJyQw==", + "dev": true, "requires": { "color-name": "^1.0.0", "simple-swizzle": "^0.2.2" @@ -1526,17 +1506,20 @@ "colornames": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/colornames/-/colornames-1.1.1.tgz", - "integrity": "sha1-+IiQMGhcfE/54qVZ9Qd+t2qBb5Y=" + "integrity": "sha1-+IiQMGhcfE/54qVZ9Qd+t2qBb5Y=", + "dev": true }, "colors": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz", - "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==" + "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==", + "dev": true }, "colorspace": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/colorspace/-/colorspace-1.1.2.tgz", "integrity": "sha512-vt+OoIP2d76xLhjwbBaucYlNSpPsrJWPlBTtwCpQKIu6/CSMutyzX93O/Do0qzpH3YoHEes8YEFXyZ797rEhzQ==", + "dev": true, "requires": { "color": "3.0.x", "text-hex": "1.0.x" @@ -1554,7 +1537,8 @@ "commander": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/commander/-/commander-4.0.1.tgz", - "integrity": "sha512-IPF4ouhCP+qdlcmCedhxX4xiGBPyigb8v5NeUp+0LyhwLgxMqyp3S0vl7TAPfS/hiP7FC3caI/PB9lTmP8r1NA==" + "integrity": "sha512-IPF4ouhCP+qdlcmCedhxX4xiGBPyigb8v5NeUp+0LyhwLgxMqyp3S0vl7TAPfS/hiP7FC3caI/PB9lTmP8r1NA==", + "dev": true }, "component-emitter": { "version": "1.3.0", @@ -1609,12 +1593,14 @@ "core-util-is": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", + "dev": true }, "cors": { "version": "2.8.5", "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", + "dev": true, "requires": { "object-assign": "^4", "vary": "^1" @@ -1883,6 +1869,7 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/diagnostics/-/diagnostics-1.1.1.tgz", "integrity": "sha512-8wn1PmdunLJ9Tqbx+Fx/ZEuHfJf4NKSN2ZBj7SJC/OWRWha843+WsTjqMe1B5E3p28jqBlp+mJ2fPVxPyNgYKQ==", + "dev": true, "requires": { "colorspace": "1.1.x", "enabled": "1.0.x", @@ -1967,6 +1954,7 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/enabled/-/enabled-1.0.2.tgz", "integrity": "sha1-ll9lE9LC0cX0ZStkouM5ZGf8L5M=", + "dev": true, "requires": { "env-variable": "0.0.x" } @@ -1988,7 +1976,8 @@ "env-variable": { "version": "0.0.5", "resolved": "https://registry.npmjs.org/env-variable/-/env-variable-0.0.5.tgz", - "integrity": "sha512-zoB603vQReOFvTg5xMl9I1P2PnHsHQQKTEowsKKD7nseUfJq6UWzK+4YtlWUO1nhiQUxe6XMkk+JleSZD1NZFA==" + "integrity": "sha512-zoB603vQReOFvTg5xMl9I1P2PnHsHQQKTEowsKKD7nseUfJq6UWzK+4YtlWUO1nhiQUxe6XMkk+JleSZD1NZFA==", + "dev": true }, "error-ex": { "version": "1.3.2", @@ -2036,7 +2025,8 @@ "escape-string-regexp": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "dev": true }, "escodegen": { "version": "1.12.0", @@ -2378,6 +2368,7 @@ "version": "1.4.1", "resolved": "https://registry.npmjs.org/express-request-id/-/express-request-id-1.4.1.tgz", "integrity": "sha512-qpxK6XhDYtdx9FvxwCHkUeZVWtkGbWR87hBAzGECfwYF/QQCPXEwwB2/9NGkOR1tT7/aLs9mma3CT0vjSzuZVw==", + "dev": true, "requires": { "uuid": "^3.3.2" } @@ -2413,6 +2404,7 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", + "dev": true, "requires": { "chardet": "^0.7.0", "iconv-lite": "^0.4.24", @@ -2575,7 +2567,8 @@ "fast-safe-stringify": { "version": "2.0.7", "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.0.7.tgz", - "integrity": "sha512-Utm6CdzT+6xsDk2m8S6uL8VHxNwI6Jub+e9NYTcAms28T84pTa25GJQV9j0CY0N1rM8hK4x6grpF2BQf+2qwVA==" + "integrity": "sha512-Utm6CdzT+6xsDk2m8S6uL8VHxNwI6Jub+e9NYTcAms28T84pTa25GJQV9j0CY0N1rM8hK4x6grpF2BQf+2qwVA==", + "dev": true }, "fastq": { "version": "1.6.0", @@ -2598,12 +2591,14 @@ "fecha": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/fecha/-/fecha-2.3.3.tgz", - "integrity": "sha512-lUGBnIamTAwk4znq5BcqsDaxSmZ9nDVJaij6NvRt/Tg4R69gERA+otPKbS86ROw9nxVMw2/mp1fnaiWqbs6Sdg==" + "integrity": "sha512-lUGBnIamTAwk4znq5BcqsDaxSmZ9nDVJaij6NvRt/Tg4R69gERA+otPKbS86ROw9nxVMw2/mp1fnaiWqbs6Sdg==", + "dev": true }, "figures": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz", "integrity": "sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI=", + "dev": true, "requires": { "escape-string-regexp": "^1.0.5" } @@ -2775,8 +2770,7 @@ "ansi-regex": { "version": "2.1.1", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "aproba": { "version": "1.2.0", @@ -2797,14 +2791,12 @@ "balanced-match": { "version": "1.0.0", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "brace-expansion": { "version": "1.1.11", "bundled": true, "dev": true, - "optional": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -2819,20 +2811,17 @@ "code-point-at": { "version": "1.1.0", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "concat-map": { "version": "0.0.1", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "console-control-strings": { "version": "1.1.0", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "core-util-is": { "version": "1.0.2", @@ -2949,8 +2938,7 @@ "inherits": { "version": "2.0.3", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "ini": { "version": "1.3.5", @@ -2962,7 +2950,6 @@ "version": "1.0.0", "bundled": true, "dev": true, - "optional": true, "requires": { "number-is-nan": "^1.0.0" } @@ -2977,7 +2964,6 @@ "version": "3.0.4", "bundled": true, "dev": true, - "optional": true, "requires": { "brace-expansion": "^1.1.7" } @@ -2985,14 +2971,12 @@ "minimist": { "version": "0.0.8", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "minipass": { "version": "2.3.5", "bundled": true, "dev": true, - "optional": true, "requires": { "safe-buffer": "^5.1.2", "yallist": "^3.0.0" @@ -3011,7 +2995,6 @@ "version": "0.5.1", "bundled": true, "dev": true, - "optional": true, "requires": { "minimist": "0.0.8" } @@ -3092,8 +3075,7 @@ "number-is-nan": { "version": "1.0.1", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "object-assign": { "version": "4.1.1", @@ -3105,7 +3087,6 @@ "version": "1.4.0", "bundled": true, "dev": true, - "optional": true, "requires": { "wrappy": "1" } @@ -3191,8 +3172,7 @@ "safe-buffer": { "version": "5.1.2", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "safer-buffer": { "version": "2.1.2", @@ -3228,7 +3208,6 @@ "version": "1.0.2", "bundled": true, "dev": true, - "optional": true, "requires": { "code-point-at": "^1.0.0", "is-fullwidth-code-point": "^1.0.0", @@ -3248,7 +3227,6 @@ "version": "3.0.1", "bundled": true, "dev": true, - "optional": true, "requires": { "ansi-regex": "^2.0.0" } @@ -3292,14 +3270,12 @@ "wrappy": { "version": "1.0.2", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "yallist": { "version": "3.0.3", "bundled": true, - "dev": true, - "optional": true + "dev": true } } }, @@ -3485,7 +3461,8 @@ "has-flag": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=" + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true }, "has-symbols": { "version": "1.0.0", @@ -3528,7 +3505,8 @@ "hoek": { "version": "6.1.3", "resolved": "https://registry.npmjs.org/hoek/-/hoek-6.1.3.tgz", - "integrity": "sha512-YXXAAhmF9zpQbC7LEcREFtXfGq5K1fmd+4PHkBq8NUqmzW3G+Dq10bI/i0KucLRwss3YYFQ0fSfoxBZYiGUqtQ==" + "integrity": "sha512-YXXAAhmF9zpQbC7LEcREFtXfGq5K1fmd+4PHkBq8NUqmzW3G+Dq10bI/i0KucLRwss3YYFQ0fSfoxBZYiGUqtQ==", + "dev": true }, "hosted-git-info": { "version": "2.8.5", @@ -3716,6 +3694,7 @@ "version": "7.0.0", "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-7.0.0.tgz", "integrity": "sha512-rSdC7zelHdRQFkWnhsMu2+2SO41mpv2oF2zy4tMhmiLWkcKbOAs87fWAJhVXttKVwhdZvymvnuM95EyEXg2/tQ==", + "dev": true, "requires": { "ansi-escapes": "^4.2.1", "chalk": "^2.4.2", @@ -3736,6 +3715,7 @@ "version": "4.2.1", "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.2.1.tgz", "integrity": "sha512-Cg3ymMAdN10wOk/VYfLV7KCQyv7EDirJ64500sU7n9UlmioEtDuU5Gd+hj73hXSU/ex7tHJSssmyftDdkMLO8Q==", + "dev": true, "requires": { "type-fest": "^0.5.2" } @@ -3744,6 +3724,7 @@ "version": "2.4.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, "requires": { "ansi-styles": "^3.2.1", "escape-string-regexp": "^1.0.5", @@ -3754,6 +3735,7 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/figures/-/figures-3.1.0.tgz", "integrity": "sha512-ravh8VRXqHuMvZt/d8GblBeqDMkdJMBdv/2KntFH+ra5MXkO7nxNKpzQ3n6QD/2da1kH0aWmNISdvhM7gl2gVg==", + "dev": true, "requires": { "escape-string-regexp": "^1.0.5" } @@ -3762,6 +3744,7 @@ "version": "5.2.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, "requires": { "ansi-regex": "^4.1.0" } @@ -3769,30 +3752,8 @@ "type-fest": { "version": "0.5.2", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.5.2.tgz", - "integrity": "sha512-DWkS49EQKVX//Tbupb9TFa19c7+MK1XmzkrZUR8TAktmE/DizXoaoJV6TZ/tSIPXipqNiRI6CyAe7x69Jb6RSw==" - } - } - }, - "inquirer-autocomplete-prompt": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/inquirer-autocomplete-prompt/-/inquirer-autocomplete-prompt-1.0.1.tgz", - "integrity": "sha512-Y4V6ifAu9LNrNjcEtYq8YUKhrgmmufUn5fsDQqeWgHY8rEO6ZAQkNUiZtBm2kw2uUQlC9HdgrRCHDhTPPguH5A==", - "requires": { - "ansi-escapes": "^3.0.0", - "chalk": "^2.0.0", - "figures": "^2.0.0", - "run-async": "^2.3.0" - }, - "dependencies": { - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } + "integrity": "sha512-DWkS49EQKVX//Tbupb9TFa19c7+MK1XmzkrZUR8TAktmE/DizXoaoJV6TZ/tSIPXipqNiRI6CyAe7x69Jb6RSw==", + "dev": true } } }, @@ -3923,7 +3884,8 @@ "is-fullwidth-code-point": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==" + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true }, "is-generator-fn": { "version": "2.1.0", @@ -3999,7 +3961,8 @@ "is-promise": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.1.0.tgz", - "integrity": "sha1-eaKp7OfwlugPNtKy87wWwf9L8/o=" + "integrity": "sha1-eaKp7OfwlugPNtKy87wWwf9L8/o=", + "dev": true }, "is-regex": { "version": "1.0.4", @@ -4019,7 +3982,8 @@ "is-stream": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", - "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=" + "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=", + "dev": true }, "is-symbol": { "version": "1.0.2", @@ -4051,7 +4015,8 @@ "isarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true }, "isexe": { "version": "2.0.0", @@ -4914,6 +4879,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/kuler/-/kuler-1.0.1.tgz", "integrity": "sha512-J9nVUucG1p/skKul6DU3PUZrhs0LPulNaeUOox0IyXDi8S4CztTHs1gQphhuZmzXG7VOQSf6NJfKuzteQLv9gQ==", + "dev": true, "requires": { "colornames": "^1.1.1" } @@ -5327,7 +5293,8 @@ "lodash": { "version": "4.17.15", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", - "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==" + "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==", + "dev": true }, "lodash.sortby": { "version": "4.7.0", @@ -5455,6 +5422,7 @@ "version": "2.1.2", "resolved": "https://registry.npmjs.org/logform/-/logform-2.1.2.tgz", "integrity": "sha512-+lZh4OpERDBLqjiwDLpAWNQu6KMjnlXH2ByZwCuSqVPJletw0kTWJf5CgSNAUKn1KUkv3m2cUz/LK8zyEy7wzQ==", + "dev": true, "requires": { "colors": "^1.2.1", "fast-safe-stringify": "^2.0.4", @@ -5597,7 +5565,8 @@ "mimic-fn": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==" + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true }, "minimatch": { "version": "3.0.4", @@ -5652,7 +5621,8 @@ "mute-stream": { "version": "0.0.8", "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", - "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==" + "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==", + "dev": true }, "nan": { "version": "2.14.0", @@ -5752,7 +5722,8 @@ "node-watch": { "version": "0.6.3", "resolved": "https://registry.npmjs.org/node-watch/-/node-watch-0.6.3.tgz", - "integrity": "sha512-InVPsg51EemnMVH3fvrrSVgqVBMlksZ/mK7ZDWx/NuWdNQi28wcVJX1/BP38alraPFXbRi9jZ35OfK4Ra7l8Bg==" + "integrity": "sha512-InVPsg51EemnMVH3fvrrSVgqVBMlksZ/mK7ZDWx/NuWdNQi28wcVJX1/BP38alraPFXbRi9jZ35OfK4Ra7l8Bg==", + "dev": true }, "normalize-package-data": { "version": "2.5.0", @@ -5813,7 +5784,8 @@ "object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", + "dev": true }, "object-copy": { "version": "0.1.0", @@ -5906,12 +5878,14 @@ "one-time": { "version": "0.0.4", "resolved": "https://registry.npmjs.org/one-time/-/one-time-0.0.4.tgz", - "integrity": "sha1-+M33eISCb+Tf+T46nMN7HkSAdC4=" + "integrity": "sha1-+M33eISCb+Tf+T46nMN7HkSAdC4=", + "dev": true }, "onetime": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.0.tgz", "integrity": "sha512-5NcSkPHhwTVFIQN+TUqXoS5+dlElHXdpAWu9I0HP20YOtIi+aZ0Ct82jdlILDxjLEAWwvm+qj1m6aEtsDVmm6Q==", + "dev": true, "requires": { "mimic-fn": "^2.1.0" } @@ -5949,7 +5923,8 @@ "os-tmpdir": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", - "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=" + "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", + "dev": true }, "p-each-series": { "version": "1.0.0", @@ -6184,7 +6159,8 @@ "process-nextick-args": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", - "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "dev": true }, "progress": { "version": "2.0.3", @@ -6348,6 +6324,7 @@ "version": "3.4.0", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.4.0.tgz", "integrity": "sha512-jItXPLmrSR8jmTRmRWJXCnGJsfy85mB3Wd/uINMXA65yrnFo0cPClFIUWzo2najVNSl+mx7/4W8ttlLWJe99pQ==", + "dev": true, "requires": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", @@ -6460,7 +6437,8 @@ "require-all": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/require-all/-/require-all-3.0.0.tgz", - "integrity": "sha1-Rz1JcEvjEBFc4ST3c4Ox69hnExI=" + "integrity": "sha1-Rz1JcEvjEBFc4ST3c4Ox69hnExI=", + "dev": true }, "require-directory": { "version": "2.1.1", @@ -6516,6 +6494,7 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", + "dev": true, "requires": { "onetime": "^5.1.0", "signal-exit": "^3.0.2" @@ -6545,7 +6524,8 @@ "route-parser": { "version": "0.0.5", "resolved": "https://registry.npmjs.org/route-parser/-/route-parser-0.0.5.tgz", - "integrity": "sha1-fR0J0zXkkJQDHqFpkaSnmwG74fQ=" + "integrity": "sha1-fR0J0zXkkJQDHqFpkaSnmwG74fQ=", + "dev": true }, "rsvp": { "version": "4.8.5", @@ -6557,6 +6537,7 @@ "version": "2.3.0", "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.3.0.tgz", "integrity": "sha1-A3GrSuC91yDUFm19/aZP96RFpsA=", + "dev": true, "requires": { "is-promise": "^2.1.0" } @@ -6577,6 +6558,7 @@ "version": "6.5.2", "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.5.2.tgz", "integrity": "sha512-HUb7j3kvb7p7eCUHE3FqjoDsC1xfZQ4AHFWfTKSpZ+sAhhz5X1WX0ZuUqWbzB2QhSLp3DoLUG+hMdEDKqWo2Zg==", + "dev": true, "requires": { "tslib": "^1.9.0" } @@ -6749,12 +6731,14 @@ "signal-exit": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", - "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=" + "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", + "dev": true }, "simple-swizzle": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", "integrity": "sha1-pNprY1/8zMoz9w0Xy5JZLeleVXo=", + "dev": true, "requires": { "is-arrayish": "^0.3.1" }, @@ -6762,7 +6746,8 @@ "is-arrayish": { "version": "0.3.2", "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", - "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==" + "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==", + "dev": true } } }, @@ -7042,7 +7027,8 @@ "stack-trace": { "version": "0.0.10", "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", - "integrity": "sha1-VHxws0fo0ytOEI6hoqFZ5f3eGcA=" + "integrity": "sha1-VHxws0fo0ytOEI6hoqFZ5f3eGcA=", + "dev": true }, "stack-utils": { "version": "1.0.2", @@ -7119,6 +7105,7 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.1.0.tgz", "integrity": "sha512-NrX+1dVVh+6Y9dnQ19pR0pP4FiEIlUvdTGn8pw6CKTNq5sgib2nIhmUNT5TAmhWmvKr3WcxBcP3E8nWezuipuQ==", + "dev": true, "requires": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", @@ -7128,12 +7115,14 @@ "emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true }, "strip-ansi": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, "requires": { "ansi-regex": "^4.1.0" } @@ -7164,6 +7153,7 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dev": true, "requires": { "safe-buffer": "~5.2.0" }, @@ -7171,7 +7161,8 @@ "safe-buffer": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.0.tgz", - "integrity": "sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg==" + "integrity": "sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg==", + "dev": true } } }, @@ -7190,6 +7181,7 @@ "version": "6.0.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", + "dev": true, "requires": { "ansi-regex": "^5.0.0" }, @@ -7197,7 +7189,8 @@ "ansi-regex": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", - "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==" + "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", + "dev": true } } }, @@ -7229,6 +7222,7 @@ "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, "requires": { "has-flag": "^3.0.0" } @@ -7302,7 +7296,8 @@ "text-hex": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/text-hex/-/text-hex-1.0.0.tgz", - "integrity": "sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==" + "integrity": "sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==", + "dev": true }, "text-table": { "version": "0.2.0", @@ -7319,12 +7314,14 @@ "through": { "version": "2.3.8", "resolved": "http://registry.npmjs.org/through/-/through-2.3.8.tgz", - "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=" + "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", + "dev": true }, "tmp": { "version": "0.0.33", "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", + "dev": true, "requires": { "os-tmpdir": "~1.0.2" } @@ -7418,17 +7415,20 @@ "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=" + "integrity": "sha1-1LrZklYR55KXmjrcFetgBMHVqa0=", + "dev": true }, "triple-beam": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/triple-beam/-/triple-beam-1.3.0.tgz", - "integrity": "sha512-XrHUvV5HpdLmIj4uVMxHggLbFSZYIn7HEWsqePZcI50pco+MPqJ50wMGY794X7AOOhxOBAjbkqfAbEe/QMp2Lw==" + "integrity": "sha512-XrHUvV5HpdLmIj4uVMxHggLbFSZYIn7HEWsqePZcI50pco+MPqJ50wMGY794X7AOOhxOBAjbkqfAbEe/QMp2Lw==", + "dev": true }, "tslib": { "version": "1.10.0", "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.10.0.tgz", - "integrity": "sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ==" + "integrity": "sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ==", + "dev": true }, "tunnel-agent": { "version": "0.6.0", @@ -7597,7 +7597,8 @@ "util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", + "dev": true }, "util.promisify": { "version": "1.0.0", @@ -7617,7 +7618,8 @@ "uuid": { "version": "3.3.2", "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz", - "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==" + "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==", + "dev": true }, "v8-compile-cache": { "version": "2.1.0", @@ -7720,6 +7722,7 @@ "version": "3.2.1", "resolved": "https://registry.npmjs.org/winston/-/winston-3.2.1.tgz", "integrity": "sha512-zU6vgnS9dAWCEKg/QYigd6cgMVVNwyTzKs81XZtTFuRwJOcDdBg7AU0mXVyNbs7O5RH2zdv+BdNZUlx7mXPuOw==", + "dev": true, "requires": { "async": "^2.6.1", "diagnostics": "^1.1.1", @@ -7736,6 +7739,7 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/winston-transport/-/winston-transport-4.3.0.tgz", "integrity": "sha512-B2wPuwUi3vhzn/51Uukcao4dIduEiPOcOt9HJ3QeaXgkJ5Z7UwpBzxS4ZGNHtrxrUvTwemsQiSys0ihOf8Mp1A==", + "dev": true, "requires": { "readable-stream": "^2.3.6", "triple-beam": "^1.2.0" @@ -7745,6 +7749,7 @@ "version": "2.3.6", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", + "dev": true, "requires": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", @@ -7759,6 +7764,7 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, "requires": { "safe-buffer": "~5.1.0" } diff --git a/package.json b/package.json index 160b996..4f42438 100644 --- a/package.json +++ b/package.json @@ -1,63 +1,47 @@ { - "name": "@mocks-server/main", - "version": "1.3.0", - "description": "Mocks server supporting multiple api behaviors", + "name": "@mocks-server/plugin-admin-api", + "version": "1.0.0", + "description": "Plugin for Mocks Server. Provides a REST API for administrating settings, fixtures and behaviors", "keywords": [ "mocks", "server", "mock", - "fixtures", - "behaviors", + "plugin", + "administration", + "admin", "api", "rest", - "cli", - "express", "testing", "development" ], "author": "Javier Brea", "license": "Apache-2.0", - "repository": "https://github.com/mocks-server/main", + "repository": "https://github.com/mocks-server/plugin-admin-api", "homepage": "https://www.mocks-server.org", "publishConfig": { "access": "public" }, "files": [ - "bin", - "lib", + "src", "index.js" ], "main": "index.js", - "bin": { - "mocks-server": "./bin/mocks-server" - }, "scripts": { - "lint": "eslint index.js lib test jest.config.js jest.acceptance.config.js", + "lint": "eslint index.js src test jest.config.js jest.acceptance.config.js", "lint-staged": "lint-staged", "test": "jest", "test-acceptance": "jest --config=jest.acceptance.config.js --runInBand", "test-ci": "npm run test && npm run test-acceptance", "coveralls": "cat ./coverage/lcov.info | coveralls" }, + "peerDependencies": { + "@mocks-server/core": "1.0.0" + }, "dependencies": { - "body-parser": "1.19.0", - "boom": "7.3.0", - "chalk": "^3.0.0", - "commander": "4.0.1", - "cors": "2.8.5", - "express": "4.17.1", - "express-request-id": "1.4.1", - "inquirer": "7.0.0", - "inquirer-autocomplete-prompt": "^1.0.1", - "lodash": "4.17.15", - "node-watch": "0.6.3", - "require-all": "3.0.0", - "route-parser": "0.0.5", - "strip-ansi": "6.0.0", - "tree-kill-sync": "^1.0.0", - "winston": "3.2.1" + "express": "4.17.1" }, "devDependencies": { + "@mocks-server/core": "1.0.0", "coveralls": "^3.0.7", "eslint": "6.6.0", "eslint-config-prettier": "6.5.0", @@ -70,10 +54,12 @@ "prettier": "^1.19.1", "request": "^2.88.0", "request-promise": "^4.2.5", - "sinon": "^7.5.0" + "sinon": "^7.5.0", + "strip-ansi": "6.0.0", + "tree-kill-sync": "^1.0.0" }, "lint-staged": { - "lib/**/*.js": "eslint", + "src/**/*.js": "eslint", "test/**/*.js": "eslint", "index.js": "eslint", "jest.config.js": "eslint", diff --git a/sonar-project.properties b/sonar-project.properties index 9a9acec..aeacfbc 100644 --- a/sonar-project.properties +++ b/sonar-project.properties @@ -1,6 +1,6 @@ sonar.organization=mocks-server -sonar.projectKey=mocks-server-main -sonar.projectVersion=1.3.0 +sonar.projectKey=mocks-server-plugin-admin-api +sonar.projectVersion=1.0.0 sonar.javascript.file.suffixes=.js sonar.sourceEncoding=UTF-8 diff --git a/lib/api/Api.js b/src/Api.js similarity index 100% rename from lib/api/Api.js rename to src/Api.js diff --git a/lib/api/Behaviors.js b/src/Behaviors.js similarity index 100% rename from lib/api/Behaviors.js rename to src/Behaviors.js diff --git a/lib/api/Settings.js b/src/Settings.js similarity index 100% rename from lib/api/Settings.js rename to src/Settings.js diff --git a/test/acceptance/CliRunner.js b/test/acceptance/CliRunner.js new file mode 100644 index 0000000..0c1e5a7 --- /dev/null +++ b/test/acceptance/CliRunner.js @@ -0,0 +1,92 @@ +/* +Copyright 2019 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 + +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. +*/ + +"use strict"; + +const childProcess = require("child_process"); + +const treeKillSync = require("tree-kill-sync"); +const stripAnsi = require("strip-ansi"); + +const ENCODING_TYPE = "utf8"; + +module.exports = class CliRunner { + constructor(commandToExecute, options = {}) { + this._command = this.getCommandToExecute(commandToExecute); + this._cwd = options.cwd || process.cwd(); + this._logs = []; + + this._exitPromise = new Promise(resolve => { + this._resolveExitPromise = resolve; + }); + + this.logData = this.logData.bind(this); + + this.run(); + } + + getCommandToExecute(command) { + return { + name: command[0], + params: command.splice(1, command.length - 1) + }; + } + + logData(data) { + const cleanData = stripAnsi(data.replace(/\x1Bc/, "")); + if (cleanData.length) { + this._logs.push(cleanData); + } + } + + run() { + if (this._cliProcess) { + throw new Error("Cli is already running"); + } else { + this._cliProcess = childProcess.spawn(this._command.name, this._command.params, { + cwd: this._cwd + }); + this._cliProcess.stdin.setEncoding(ENCODING_TYPE); + + this._cliProcess.stdout.setEncoding(ENCODING_TYPE); + this._cliProcess.stderr.setEncoding(ENCODING_TYPE); + + this._cliProcess.stdout.on("data", this.logData); + this._cliProcess.stderr.on("data", this.logData); + + this._cliProcess.on("close", code => { + this._exitCode = code; + this._resolveExitPromise(true); + }); + } + } + + async kill() { + treeKillSync(this._cliProcess.pid); + return this._exitPromise; + } + + async hasExit() { + return this._exitPromise; + } + + flush() { + this._logs = []; + } + + get logs() { + return this._logs.join(""); + } + + get exitCode() { + return this._exitCode; + } +}; diff --git a/test/acceptance/main/web-tutorial-admin-api.spec.js b/test/acceptance/change-behavior.spec.js similarity index 98% rename from test/acceptance/main/web-tutorial-admin-api.spec.js rename to test/acceptance/change-behavior.spec.js index d3c6597..e3c1324 100644 --- a/test/acceptance/main/web-tutorial-admin-api.spec.js +++ b/test/acceptance/change-behavior.spec.js @@ -10,7 +10,7 @@ Unless required by applicable law or agreed to in writing, software distributed const { startServer, stopServer, request, changeBehavior, getBehaviors } = require("./utils"); -describe("web tutorial", () => { +describe("API for changing current behavior", () => { let server; beforeAll(async () => { diff --git a/test/acceptance/cli/CliRunner.js b/test/acceptance/cli/CliRunner.js deleted file mode 100644 index 8240bf9..0000000 --- a/test/acceptance/cli/CliRunner.js +++ /dev/null @@ -1,246 +0,0 @@ -/* -Copyright 2019 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 - -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. -*/ - -"use strict"; - -const EventEmitter = require("events"); -const childProcess = require("child_process"); - -const treeKillSync = require("tree-kill-sync"); -const stripAnsi = require("strip-ansi"); -const { isArray, isNumber } = require("lodash"); - -const ENCODING_TYPE = "utf8"; -const CLRS = "\x1Bc"; -const CTRL_C = "\u0003"; -const LOG_EVENT_NAME = "logged"; -const CLEAR_SCREEN_EVENT_NAME = "clearScreen"; -const CLI_DEBUG = "[CLI-Runner] ------ "; - -module.exports = class CliRunner { - constructor(commandToExecute, options = {}) { - this._eventEmitter = new EventEmitter(); - this._command = this.getCommandToExecute(commandToExecute); - this._cwd = options.cwd || process.cwd(); - this._debug = options.debug === true; - this._logs = []; - this._allLogs = []; - this.logData = this.logData.bind(this); - this.write = this.write.bind(this); - this.pressEnter = this.pressEnter.bind(this); - this.cursorUp = this.cursorUp.bind(this); - this.cursorDown = this.cursorDown.bind(this); - this.pressCtrlC = this.pressCtrlC.bind(this); - this._exitPromise = new Promise(resolve => { - this._resolveExitPromise = resolve; - }); - this.run(); - } - - getCommandToExecute(commandToExecute) { - const commandIsArray = isArray(commandToExecute); - const command = commandIsArray ? [...commandToExecute] : commandToExecute; - return { - name: commandIsArray ? command[0] : "node", - params: commandIsArray ? command.splice(1, command.length - 1) : [command] - }; - } - - logData(data) { - if (data.includes(CLRS)) { - this._eventEmitter.emit(CLEAR_SCREEN_EVENT_NAME, stripAnsi(data.split(CLRS)[1] || "")); - } - const cleanData = stripAnsi(data.replace(/\x1Bc/, "")); - if (cleanData.length) { - this._logs.push(cleanData); - this._allLogs.push(data); - this._eventEmitter.emit(LOG_EVENT_NAME, cleanData); - } - } - - run() { - if (this._cliProcess) { - throw new Error("Cli is already running"); - } else { - this._cliProcess = childProcess.spawn(this._command.name, this._command.params, { - cwd: this._cwd - }); - this._cliProcess.stdin.setEncoding(ENCODING_TYPE); - - this._cliProcess.stdout.setEncoding(ENCODING_TYPE); - this._cliProcess.stderr.setEncoding(ENCODING_TYPE); - - this._cliProcess.stdout.on("data", this.logData); - this._cliProcess.stderr.on("data", this.logData); - - this._cliProcess.on("close", code => { - this._exitCode = code; - this._resolveExitPromise(true); - }); - } - } - - write(data) { - this._cliProcess.stdin.write(`${data}`); - } - - pressEnter() { - this.write("\n"); - } - - cursorUp() { - this.write("\u001b[A"); - } - - cursorDown() { - this.write("\u001b[B"); - } - - pressCtrlC() { - this.write(CTRL_C); - } - - async kill() { - treeKillSync(this._cliProcess.pid); - return this._exitPromise; - } - - async hasExit() { - return this._exitPromise; - } - - async hasPrinted(data, inputAction, inputTimeOut = 1000) { - let timeOut = inputTimeOut; - let action = inputAction; - if (isNumber(action)) { - timeOut = action; - action = null; - } - let resolver; - let rejecter; - - const timeout = setTimeout(() => { - const errorMessage = `${data} was not printed after ${timeOut}ms`; - console.log(errorMessage); - rejecter(new Error(errorMessage)); - }, timeOut); - - const listener = logData => { - if (logData.includes(data)) { - this._eventEmitter.removeListener(LOG_EVENT_NAME, listener); - clearTimeout(timeout); - resolver(); - } - }; - - return new Promise((resolve, reject) => { - resolver = resolve; - rejecter = reject; - this._eventEmitter.on(LOG_EVENT_NAME, listener); - if (action) { - action(); - } - }); - } - - async newScreenAfter(action, timeOut = 1000) { - let resolver; - let rejecter; - let screenLogs = []; - let noLogsTimeout; - let continuosLogsTriggered; - let continuosLogsTimeout; - - const getScreenLogs = () => { - const logs = screenLogs.join(""); - if (this._debug) { - console.log(`${CLI_DEBUG} Screen:`); - console.log(logs); - } - return logs; - }; - - const clearScreenTimeout = setTimeout(() => { - const errorMessage = `No new screen was rendered after ${timeOut}ms`; - console.log(errorMessage); - this._eventEmitter.removeListener(CLEAR_SCREEN_EVENT_NAME, clearScreenListener); - rejecter(new Error(errorMessage)); - }, timeOut); - - const startContinuosLogsTimeout = () => { - continuosLogsTimeout = setTimeout(() => { - continuosLogsTriggered = true; - clearTimeout(noLogsTimeout); - this._eventEmitter.removeListener(LOG_EVENT_NAME, logsListener); - console.log( - `Still receiving logs after 3000ms. Resolving promise with received data until now` - ); - resolver(getScreenLogs()); - }, 3000); - }; - - const waitForNewLogs = () => { - if (noLogsTimeout) { - clearTimeout(noLogsTimeout); - } - noLogsTimeout = setTimeout(() => { - this._eventEmitter.removeListener(LOG_EVENT_NAME, logsListener); - clearTimeout(continuosLogsTimeout); - resolver(getScreenLogs()); - }, 200); - }; - - const logsListener = logData => { - screenLogs.push(logData); - if (!continuosLogsTriggered) { - waitForNewLogs(); - } - }; - - const clearScreenListener = logData => { - if (this._debug) { - console.log(`${CLI_DEBUG} New screen`); - } - clearTimeout(clearScreenTimeout); - this._eventEmitter.removeListener(CLEAR_SCREEN_EVENT_NAME, clearScreenListener); - this._eventEmitter.on(LOG_EVENT_NAME, logsListener); - screenLogs.push(logData); - startContinuosLogsTimeout(); - waitForNewLogs(); - }; - - return new Promise((resolve, reject) => { - resolver = resolve; - rejecter = reject; - this._eventEmitter.on(CLEAR_SCREEN_EVENT_NAME, clearScreenListener); - action(); - }); - } - - flush() { - this._logs = []; - } - - get logs() { - return this._logs.join(""); - } - - get allLogs() { - return this._allLogs - .join("") - .split(CLRS) - .map(stripAnsi); - } - - get exitCode() { - return this._exitCode; - } -}; diff --git a/test/acceptance/cli/autocomplete.spec.js b/test/acceptance/cli/autocomplete.spec.js deleted file mode 100644 index 6611a50..0000000 --- a/test/acceptance/cli/autocomplete.spec.js +++ /dev/null @@ -1,173 +0,0 @@ -/* -Copyright 2019 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 - -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 CliRunner = require("./CliRunner"); - -const END_SCREEN = "Exit"; - -const RENDER_TIME_OUT = 10000; -const INSERT_TAGS = "Insert tags"; - -describe("when autocomplete fixture is executed", () => { - jest.setTimeout(15000); - const cliFile = path.resolve(__dirname, "fixtures", "autocomplete.js"); - let cliRunner; - - beforeEach(() => { - expect.assertions(1); - cliRunner = new CliRunner(cliFile); - }); - - afterEach(async () => { - await cliRunner.kill(); - }); - - it('should print a menu with "Select tags"', async () => { - await cliRunner.hasPrinted(END_SCREEN, RENDER_TIME_OUT); - expect(cliRunner.logs).toEqual(expect.stringContaining("Select tags")); - }); - - it('should print a menu with "Exit" option', async () => { - await cliRunner.hasPrinted(END_SCREEN, RENDER_TIME_OUT); - expect(cliRunner.logs).toEqual(expect.stringContaining("Exit")); - }); - - it('should print selected option as "none" in header when inited', async () => { - await cliRunner.hasPrinted(END_SCREEN, RENDER_TIME_OUT); - expect(cliRunner.logs).toEqual(expect.stringContaining("Selected option: None")); - }); - - it('should allow to choose all available tags when "Select tags" is selected', async () => { - expect.assertions(3); - await cliRunner.hasPrinted(END_SCREEN, RENDER_TIME_OUT); - const newScreen = await cliRunner.newScreenAfter(cliRunner.pressEnter, RENDER_TIME_OUT); - expect(newScreen).toEqual(expect.stringContaining("a-tag")); - expect(newScreen).toEqual(expect.stringContaining("b-tag")); - expect(newScreen).toEqual(expect.stringContaining("c-tag")); - }); - - it('should allow to choose only "a-tag" tag when "Select tags" is selected, and "a" is pressed', async () => { - expect.assertions(3); - await cliRunner.hasPrinted(END_SCREEN, RENDER_TIME_OUT); - await cliRunner.newScreenAfter(cliRunner.pressEnter, RENDER_TIME_OUT); - await cliRunner.hasPrinted( - INSERT_TAGS, - () => { - cliRunner.write("a"); - }, - RENDER_TIME_OUT - ); - await cliRunner.hasPrinted(INSERT_TAGS, RENDER_TIME_OUT); - cliRunner.flush(); - await cliRunner.hasPrinted(INSERT_TAGS, RENDER_TIME_OUT); - - expect(cliRunner.logs).toEqual(expect.stringContaining("a-tag")); - expect(cliRunner.logs).not.toEqual(expect.stringContaining("b-tag")); - expect(cliRunner.logs).not.toEqual(expect.stringContaining("c-tag")); - }); - - it('should print selected option as "a-tag" when a-tag is selected in autocomplete', async () => { - await cliRunner.hasPrinted(END_SCREEN, RENDER_TIME_OUT); - await cliRunner.newScreenAfter(cliRunner.pressEnter, RENDER_TIME_OUT); - await cliRunner.hasPrinted( - INSERT_TAGS, - () => { - cliRunner.write("a"); - }, - RENDER_TIME_OUT - ); - await cliRunner.hasPrinted(INSERT_TAGS, RENDER_TIME_OUT); - await cliRunner.hasPrinted(INSERT_TAGS, RENDER_TIME_OUT); - const newScreen = await cliRunner.newScreenAfter(cliRunner.pressEnter, RENDER_TIME_OUT); - - expect(newScreen).toEqual(expect.stringContaining("Selected option: a-tag")); - }); - - it('should allow to choose only "b-tag" tag when "Select tags" is selected, and "b" is pressed', async () => { - expect.assertions(4); - await cliRunner.hasPrinted(END_SCREEN, RENDER_TIME_OUT); - await cliRunner.newScreenAfter(cliRunner.pressEnter, RENDER_TIME_OUT); - await cliRunner.hasPrinted( - INSERT_TAGS, - () => { - cliRunner.write("b"); - }, - RENDER_TIME_OUT - ); - cliRunner.flush(); - await cliRunner.hasPrinted(INSERT_TAGS, RENDER_TIME_OUT); - expect(cliRunner.logs).toEqual(expect.stringContaining("Searching...")); - cliRunner.flush(); - await cliRunner.hasPrinted(INSERT_TAGS, RENDER_TIME_OUT); - - expect(cliRunner.logs).toEqual(expect.stringContaining("b-tag")); - expect(cliRunner.logs).not.toEqual(expect.stringContaining("a-tag")); - expect(cliRunner.logs).not.toEqual(expect.stringContaining("c-tag")); - }); - - it('should print selected option as "b-tag" when b-tag is selected in autocomplete', async () => { - await cliRunner.hasPrinted(END_SCREEN, RENDER_TIME_OUT); - await cliRunner.newScreenAfter(cliRunner.pressEnter, RENDER_TIME_OUT); - await cliRunner.hasPrinted( - INSERT_TAGS, - () => { - cliRunner.write("b"); - }, - RENDER_TIME_OUT - ); - await cliRunner.hasPrinted(INSERT_TAGS, RENDER_TIME_OUT); - await cliRunner.hasPrinted(INSERT_TAGS, RENDER_TIME_OUT); - const newScreen = await cliRunner.newScreenAfter(cliRunner.pressEnter, RENDER_TIME_OUT); - - expect(newScreen).toEqual(expect.stringContaining("Selected option: b-tag")); - }); - - it('should allow to choose only "c-tag" tag when "Select tags" is selected, and "c" is pressed', async () => { - expect.assertions(4); - await cliRunner.hasPrinted(END_SCREEN, RENDER_TIME_OUT); - await cliRunner.newScreenAfter(cliRunner.pressEnter, RENDER_TIME_OUT); - await cliRunner.hasPrinted( - INSERT_TAGS, - () => { - cliRunner.write("c"); - }, - RENDER_TIME_OUT - ); - cliRunner.flush(); - await cliRunner.hasPrinted(INSERT_TAGS, RENDER_TIME_OUT); - expect(cliRunner.logs).toEqual(expect.stringContaining("Searching...")); - cliRunner.flush(); - await cliRunner.hasPrinted(INSERT_TAGS, RENDER_TIME_OUT); - - expect(cliRunner.logs).toEqual(expect.stringContaining("c-tag")); - expect(cliRunner.logs).not.toEqual(expect.stringContaining("a-tag")); - expect(cliRunner.logs).not.toEqual(expect.stringContaining("b-tag")); - }); - - it('should print selected option as "c-tag" when c-tag is selected in autocomplete', async () => { - await cliRunner.hasPrinted(END_SCREEN, RENDER_TIME_OUT); - await cliRunner.newScreenAfter(cliRunner.pressEnter, RENDER_TIME_OUT); - await cliRunner.hasPrinted( - INSERT_TAGS, - () => { - cliRunner.write("c"); - }, - RENDER_TIME_OUT - ); - await cliRunner.hasPrinted(INSERT_TAGS, RENDER_TIME_OUT); - await cliRunner.hasPrinted(INSERT_TAGS, RENDER_TIME_OUT); - const newScreen = await cliRunner.newScreenAfter(cliRunner.pressEnter, RENDER_TIME_OUT); - - expect(newScreen).toEqual(expect.stringContaining("Selected option: c-tag")); - }); -}); diff --git a/test/acceptance/cli/custom-quit.spec.js b/test/acceptance/cli/custom-quit.spec.js deleted file mode 100644 index 0a35248..0000000 --- a/test/acceptance/cli/custom-quit.spec.js +++ /dev/null @@ -1,68 +0,0 @@ -/* -Copyright 2019 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 - -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 CliRunner = require("./CliRunner"); - -const END_SCREEN = "Quit"; - -jest.setTimeout(20000); - -describe("when a customQuit fixture (which has a custom quitMethod) is executed", () => { - const cliFile = path.resolve(__dirname, "fixtures", "custom-quit-method.js"); - let cliRunner; - - beforeEach(() => { - expect.assertions(1); - cliRunner = new CliRunner(cliFile); - }); - - afterEach(async () => { - await cliRunner.kill(); - }); - - it('should print a menu with "Option 1"', async () => { - await cliRunner.hasPrinted(END_SCREEN); - expect(cliRunner.logs).toEqual(expect.stringContaining("Option 1")); - }); - - it('should print a menu with "Quit" option', async () => { - await cliRunner.hasPrinted(END_SCREEN); - expect(cliRunner.logs).toEqual(expect.stringContaining("Quit")); - }); - - it('should print selected option as "none" in header when inited', async () => { - await cliRunner.hasPrinted(END_SCREEN); - expect(cliRunner.logs).toEqual(expect.stringContaining("Selected option: None")); - }); - - it('should print selected option as "option1" when user selects "Option 1"', async () => { - await cliRunner.hasPrinted(END_SCREEN); - const newScreen = await cliRunner.newScreenAfter(cliRunner.pressEnter); - expect(newScreen).toEqual(expect.stringContaining("Selected option: option1")); - }); - - it('should print selected option as "Quit" when user selects "Quit"', async () => { - await cliRunner.hasPrinted(END_SCREEN); - cliRunner.cursorDown(); - const newScreen = await cliRunner.newScreenAfter(cliRunner.pressEnter); - - expect(newScreen).toEqual(expect.stringContaining("Selected option: Quit")); - }); - - it("should exit process if user press CTRL+C", async () => { - await cliRunner.hasPrinted(END_SCREEN); - cliRunner.pressCtrlC(); - - expect(await cliRunner.hasExit()).toEqual(true); - }); -}); diff --git a/test/acceptance/cli/exit-logs-mode.spec.js b/test/acceptance/cli/exit-logs-mode.spec.js deleted file mode 100644 index 38e072a..0000000 --- a/test/acceptance/cli/exit-logs-mode.spec.js +++ /dev/null @@ -1,63 +0,0 @@ -/* -Copyright 2019 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 - -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 CliRunner = require("./CliRunner"); - -const END_SCREEN = "Exit"; - -describe("when exitLogsMode is executed", () => { - const cliFile = path.resolve(__dirname, "fixtures", "exit-logs-mode.js"); - let cliRunner; - - beforeEach(() => { - expect.assertions(1); - cliRunner = new CliRunner(cliFile); - }); - - afterEach(async () => { - await cliRunner.kill(); - }); - - it('should print a menu with "Option 1"', async () => { - await cliRunner.hasPrinted(END_SCREEN); - expect(cliRunner.logs).toEqual(expect.stringContaining("Option 1")); - }); - - it('should print a menu with "Exit" option', async () => { - await cliRunner.hasPrinted(END_SCREEN); - expect(cliRunner.logs).toEqual(expect.stringContaining("Exit")); - }); - - it('should print selected option as "none" in header when inited', async () => { - await cliRunner.hasPrinted(END_SCREEN); - expect(cliRunner.logs).toEqual(expect.stringContaining("Selected option: None")); - }); - - it('should print selected option as "option1" when user selects "Option 1"', async () => { - await cliRunner.hasPrinted(END_SCREEN); - const newScreen = await cliRunner.newScreenAfter(cliRunner.pressEnter); - expect(newScreen).toEqual(expect.stringContaining("Selected option: option1")); - }); - - it('should enter in logs mode, exit it after 500ms when user select "Display logs", and continue working normally', async () => { - expect.assertions(3); - await cliRunner.hasPrinted(END_SCREEN); - cliRunner.cursorDown(); - let newScreen = await cliRunner.newScreenAfter(cliRunner.pressEnter); - expect(newScreen).toEqual(expect.stringContaining("Displaying logs")); - await cliRunner.hasPrinted(END_SCREEN); - expect(cliRunner.logs).toEqual(expect.stringContaining("Option 1")); - newScreen = await cliRunner.newScreenAfter(cliRunner.pressEnter); - expect(newScreen).toEqual(expect.stringContaining("Selected option: option1")); - }); -}); diff --git a/test/acceptance/cli/fixtures/autocomplete.js b/test/acceptance/cli/fixtures/autocomplete.js deleted file mode 100644 index 0356431..0000000 --- a/test/acceptance/cli/fixtures/autocomplete.js +++ /dev/null @@ -1,67 +0,0 @@ -/* -Copyright 2019 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 - -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 { Inquirer } = require("../../../../lib/cli/Inquirer"); - -const questions = { - main: { - type: "list", - message: "Select action:", - name: "value", - choices: [ - { - name: "Select tags", - value: "tags" - } - ] - }, - tags: { - type: "autocomplete", - name: "value", - message: "Insert tags:" - } -}; - -const MyCli = class MyCli { - constructor() { - this._cli = new Inquirer(questions, this.header.bind(this)); - this._selectedOption = "None"; - } - - header() { - return [`Selected option: ${this._selectedOption}`]; - } - - async changeTags() { - this._cli.clearScreen(); - const tags = ["a-tag", "b-tag", "c-tag"]; - this._selectedOption = await this._cli.inquire("tags", { - source: (answers, input) => { - const currentInput = input ? input.split(" ").pop() : null; - if (!currentInput) { - return Promise.resolve(tags); - } - return Promise.resolve(tags.filter(tag => tag.indexOf(currentInput) === 0)); - } - }); - return this.displayMainMenu(); - } - - async displayMainMenu() { - this._cli.clearScreen(); - this._selectedOption = await this._cli.inquire("main"); - if (this._selectedOption === "tags") { - return this.changeTags(); - } - } -}; - -new MyCli().displayMainMenu(); diff --git a/test/acceptance/cli/fixtures/custom-quit-method.js b/test/acceptance/cli/fixtures/custom-quit-method.js deleted file mode 100644 index 6fee694..0000000 --- a/test/acceptance/cli/fixtures/custom-quit-method.js +++ /dev/null @@ -1,53 +0,0 @@ -/* -Copyright 2019 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 - -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 { Inquirer } = require("../../../../lib/cli/Inquirer"); - -const questions = { - main: { - type: "list", - message: "Select action:", - name: "value", - choices: [ - { - name: "Option 1", - value: "option1" - } - ] - } -}; - -const MyCli = class MyCli { - constructor() { - this._cli = new Inquirer(questions, this.header.bind(this), { - name: "Custom Quit", - action: this.customExitSelected.bind(this) - }); - this._selectedOption = "None"; - } - - header() { - return [`Selected option: ${this._selectedOption}`]; - } - - async customExitSelected() { - this._selectedOption = "Quit"; - await this.displayMainMenu(); - } - - async displayMainMenu() { - this._cli.clearScreen(); - this._selectedOption = await this._cli.inquire("main"); - await this.displayMainMenu(); - } -}; - -new MyCli().displayMainMenu(); diff --git a/test/acceptance/cli/fixtures/exit-logs-mode.js b/test/acceptance/cli/fixtures/exit-logs-mode.js deleted file mode 100644 index f22c630..0000000 --- a/test/acceptance/cli/fixtures/exit-logs-mode.js +++ /dev/null @@ -1,61 +0,0 @@ -/* -Copyright 2019 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 - -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 { Inquirer } = require("../../../../lib/cli/Inquirer"); - -const questions = { - main: { - type: "list", - message: "Select action:", - name: "value", - choices: [ - { - name: "Option 1", - value: "option1" - }, - { - name: "Display logs", - value: "logs" - } - ] - } -}; - -const MyCli = class MyCli { - constructor() { - this._cli = new Inquirer(questions, this.header.bind(this)); - this._selectedOption = "None"; - } - - header() { - return [`Selected option: ${this._selectedOption}`]; - } - - async displayLogs() { - await this._cli.logsMode(() => { - console.log("This is a foo log"); - setTimeout(() => { - this._cli.exitLogsMode(); - }, 500); - }); - } - - async displayMainMenu() { - this._cli.clearScreen(); - this._selectedOption = await this._cli.inquire("main"); - if (this._selectedOption === "logs") { - await this.displayLogs(); - } - await this.displayMainMenu(); - } -}; - -new MyCli().displayMainMenu(); diff --git a/test/acceptance/cli/fixtures/logs-mode.js b/test/acceptance/cli/fixtures/logs-mode.js deleted file mode 100644 index 2b55158..0000000 --- a/test/acceptance/cli/fixtures/logs-mode.js +++ /dev/null @@ -1,58 +0,0 @@ -/* -Copyright 2019 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 - -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 { Inquirer } = require("../../../../lib/cli/Inquirer"); - -const questions = { - main: { - type: "list", - message: "Select action:", - name: "value", - choices: [ - { - name: "Option 1", - value: "option1" - }, - { - name: "Display logs", - value: "logs" - } - ] - } -}; - -const MyCli = class MyCli { - constructor() { - this._cli = new Inquirer(questions, this.header.bind(this)); - this._selectedOption = "None"; - } - - header() { - return [`Selected option: ${this._selectedOption}`]; - } - - async displayLogs() { - await this._cli.logsMode(() => { - console.log("This is a foo log"); - }); - } - - async displayMainMenu() { - this._cli.clearScreen(); - this._selectedOption = await this._cli.inquire("main"); - if (this._selectedOption === "logs") { - await this.displayLogs(); - } - await this.displayMainMenu(); - } -}; - -new MyCli().displayMainMenu(); diff --git a/test/acceptance/cli/fixtures/readme-example.js b/test/acceptance/cli/fixtures/readme-example.js deleted file mode 100644 index 8a2f716..0000000 --- a/test/acceptance/cli/fixtures/readme-example.js +++ /dev/null @@ -1,49 +0,0 @@ -/* -Copyright 2019 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 - -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 { Inquirer } = require("../../../../lib/cli/Inquirer"); - -const questions = { - main: { - type: "list", - message: "Select action:", - name: "value", - choices: [ - { - name: "Main option 1", - value: "option1" - }, - { - name: "Main option 2", - value: "option2" - } - ] - } -}; - -const MyCli = class MyCli { - constructor() { - this._cli = new Inquirer(questions, this.header.bind(this)); - this._selectedOption = "None"; - } - - header() { - return [`Selected option: ${this._selectedOption}`]; - } - - async displayMainMenu() { - this._cli.clearScreen(); - this._selectedOption = await this._cli.inquire("main"); - await this.displayMainMenu(); - } -}; - -new MyCli().displayMainMenu(); diff --git a/test/acceptance/cli/fixtures/remove-listeners.js b/test/acceptance/cli/fixtures/remove-listeners.js deleted file mode 100644 index 8226685..0000000 --- a/test/acceptance/cli/fixtures/remove-listeners.js +++ /dev/null @@ -1,58 +0,0 @@ -/* -Copyright 2019 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 - -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 { Inquirer } = require("../../../../lib/cli/Inquirer"); - -const questions = { - main: { - type: "list", - message: "Select action:", - name: "value", - choices: [ - { - name: "Option 1", - value: "option1" - }, - { - name: "Display logs", - value: "logs" - } - ] - } -}; - -const MyCli = class MyCli { - constructor() { - this._cli = new Inquirer(questions, this.header.bind(this)); - this._selectedOption = "None"; - } - - header() { - return [`Selected option: ${this._selectedOption}`]; - } - - async displayMainMenu() { - this._cli.clearScreen(); - - setTimeout(() => { - this._cli.removeListeners(); - }, 100); // Remove listeners will do promise never resolving. This is for testing purposes only - - this._selectedOption = await this._cli.inquire("main"); - - if (this._selectedOption === "logs") { - await this.displayLogs(); - } - await this.displayMainMenu(); - } -}; - -new MyCli().displayMainMenu(); diff --git a/test/acceptance/cli/logs-mode.spec.js b/test/acceptance/cli/logs-mode.spec.js deleted file mode 100644 index 9e54225..0000000 --- a/test/acceptance/cli/logs-mode.spec.js +++ /dev/null @@ -1,77 +0,0 @@ -/* -Copyright 2019 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 - -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 CliRunner = require("./CliRunner"); - -const END_SCREEN = "Exit"; - -describe("when logs-mode fixture is executed", () => { - const cliFile = path.resolve(__dirname, "fixtures", "logs-mode.js"); - let cliRunner; - - beforeEach(() => { - expect.assertions(1); - cliRunner = new CliRunner(cliFile); - }); - - afterEach(async () => { - await cliRunner.kill(); - }); - - it('should print a menu with "Option 1"', async () => { - await cliRunner.hasPrinted(END_SCREEN); - expect(cliRunner.logs).toEqual(expect.stringContaining("Option 1")); - }); - - it('should print a menu with "Exit" option', async () => { - await cliRunner.hasPrinted(END_SCREEN); - expect(cliRunner.logs).toEqual(expect.stringContaining("Exit")); - }); - - it('should print selected option as "none" in header when inited', async () => { - await cliRunner.hasPrinted(END_SCREEN); - expect(cliRunner.logs).toEqual(expect.stringContaining("Selected option: None")); - }); - - it('should print selected option as "option1" when user selects "Option 1"', async () => { - await cliRunner.hasPrinted(END_SCREEN); - const newScreen = await cliRunner.newScreenAfter(cliRunner.pressEnter); - expect(newScreen).toEqual(expect.stringContaining("Selected option: option1")); - }); - - it('should enter in logs mode when user select "Display logs"', async () => { - await cliRunner.hasPrinted(END_SCREEN); - cliRunner.cursorDown(); - const newScreen = await cliRunner.newScreenAfter(cliRunner.pressEnter); - - expect(newScreen).toEqual(expect.stringContaining("Displaying logs")); - }); - - it("should exit logs mode when user press a key", async () => { - await cliRunner.hasPrinted(END_SCREEN); - cliRunner.cursorDown(); - await cliRunner.newScreenAfter(cliRunner.pressEnter); - const newScreen = await cliRunner.newScreenAfter(cliRunner.pressEnter); - - expect(newScreen).toEqual(expect.stringContaining("Selected option: logs")); - }); - - it("should exit process when displaying logs if user press CTRL+C", async () => { - await cliRunner.hasPrinted(END_SCREEN); - cliRunner.cursorDown(); - await cliRunner.newScreenAfter(cliRunner.pressEnter); - cliRunner.pressCtrlC(); - - expect(await cliRunner.hasExit()).toEqual(true); - }); -}); diff --git a/test/acceptance/cli/mocks-server-bin.spec.js b/test/acceptance/cli/mocks-server-bin.spec.js deleted file mode 100644 index 7e9bd48..0000000 --- a/test/acceptance/cli/mocks-server-bin.spec.js +++ /dev/null @@ -1,27 +0,0 @@ -/* -Copyright 2019 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 - -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 CliRunner = require("./CliRunner"); - -describe("when mocks-server binary is executed", () => { - const binFile = path.resolve(__dirname, "..", "..", "..", "bin", "mocks-server"); - let cliRunner; - - it("should throw a controlled error if no behaviors folder is provided", async () => { - cliRunner = new CliRunner([binFile]); - await cliRunner.hasExit(); - expect(await cliRunner.logs).toEqual( - expect.stringContaining("Please provide a path to a folder containing behaviors") - ); - }); -}); diff --git a/test/acceptance/cli/readme-example.spec.js b/test/acceptance/cli/readme-example.spec.js deleted file mode 100644 index 6599486..0000000 --- a/test/acceptance/cli/readme-example.spec.js +++ /dev/null @@ -1,76 +0,0 @@ -/* -Copyright 2019 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 - -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 CliRunner = require("./CliRunner"); - -const END_SCREEN = "Exit"; - -describe("when readme example is executed", () => { - const cliFile = path.resolve(__dirname, "fixtures", "readme-example.js"); - let cliRunner; - - beforeEach(() => { - expect.assertions(1); - cliRunner = new CliRunner(cliFile); - }); - - afterEach(async () => { - await cliRunner.kill(); - }); - - it('should print a menu with "Main option 1" and "Main option 2"', async () => { - expect.assertions(2); - await cliRunner.hasPrinted(END_SCREEN); - expect(cliRunner.logs).toEqual(expect.stringContaining("Main option 1")); - expect(cliRunner.logs).toEqual(expect.stringContaining("Main option 2")); - }); - - it('should print a menu with "Exit" option', async () => { - await cliRunner.hasPrinted(END_SCREEN); - expect(cliRunner.logs).toEqual(expect.stringContaining("Exit")); - }); - - it('should print selected option as "none" in header when inited', async () => { - await cliRunner.hasPrinted(END_SCREEN); - expect(cliRunner.logs).toEqual(expect.stringContaining("Selected option: None")); - }); - - it('should print selected option as "option1" when user selects "Main option 1"', async () => { - await cliRunner.hasPrinted(END_SCREEN); - const newScreen = await cliRunner.newScreenAfter(cliRunner.pressEnter); - expect(newScreen).toEqual(expect.stringContaining("Selected option: option1")); - }); - - it('should print selected option as "option2" when user selects "Main option 2"', async () => { - await cliRunner.hasPrinted(END_SCREEN); - cliRunner.cursorDown(); - const newScreen = await cliRunner.newScreenAfter(cliRunner.pressEnter); - expect(newScreen).toEqual(expect.stringContaining("Selected option: option2")); - }); - - it('should exit process when user selects "Exit"', async () => { - await cliRunner.hasPrinted(END_SCREEN); - cliRunner.cursorDown(); - cliRunner.cursorDown(); - cliRunner.pressEnter(); - - expect(await cliRunner.hasExit()).toEqual(true); - }); - - it("should exit process if user press CTRL+C", async () => { - await cliRunner.hasPrinted(END_SCREEN); - cliRunner.pressCtrlC(); - - expect(await cliRunner.hasExit()).toEqual(true); - }); -}); diff --git a/test/acceptance/cli/remove-listeners.spec.js b/test/acceptance/cli/remove-listeners.spec.js deleted file mode 100644 index dfee055..0000000 --- a/test/acceptance/cli/remove-listeners.spec.js +++ /dev/null @@ -1,57 +0,0 @@ -/* -Copyright 2019 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 - -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 CliRunner = require("./CliRunner"); - -const END_SCREEN = "Exit"; - -describe("when removeListeners is executed", () => { - const cliFile = path.resolve(__dirname, "fixtures", "remove-listeners.js"); - let cliRunner; - - beforeEach(() => { - expect.assertions(1); - cliRunner = new CliRunner(cliFile); - }); - - afterEach(async () => { - await cliRunner.kill(); - }); - - it('should print a menu with "Option 1"', async () => { - await cliRunner.hasPrinted(END_SCREEN); - expect(cliRunner.logs).toEqual(expect.stringContaining("Option 1")); - }); - - it('should print a menu with "Exit" option', async () => { - await cliRunner.hasPrinted(END_SCREEN); - expect(cliRunner.logs).toEqual(expect.stringContaining("Exit")); - }); - - it('should print selected option as "none" in header when inited', async () => { - await cliRunner.hasPrinted(END_SCREEN); - expect(cliRunner.logs).toEqual(expect.stringContaining("Selected option: None")); - }); - - it("should do nothing after removing Listeners even when user tries to select an option", async done => { - await cliRunner.hasPrinted(END_SCREEN); - setTimeout(async () => { - try { - await cliRunner.newScreenAfter(cliRunner.pressEnter); - } catch (error) { - expect(error.message).toEqual(expect.stringContaining("No new screen was rendered")); - done(); - } - }, 600); - }); -}); diff --git a/test/acceptance/main/web-tutorial-files-watch.spec.js b/test/acceptance/core-events.spec.js similarity index 69% rename from test/acceptance/main/web-tutorial-files-watch.spec.js rename to test/acceptance/core-events.spec.js index f7588ff..b900ccf 100644 --- a/test/acceptance/main/web-tutorial-files-watch.spec.js +++ b/test/acceptance/core-events.spec.js @@ -11,34 +11,36 @@ Unless required by applicable law or agreed to in writing, software distributed const path = require("path"); const fsExtra = require("fs-extra"); const { - CliRunner, request, changeBehavior, getBehaviors, fixturesFolder, - wait + wait, + CliRunner } = require("./utils"); -const InteractiveCliRunner = require("./InteractiveCliRunner"); -const runTests = interactiveCli => { - describe("When started", () => { +describe("Plugin listening to core events", () => { + let cli; + + beforeAll(async () => { + fsExtra.removeSync(fixturesFolder("files-watch")); + fsExtra.copySync(fixturesFolder("web-tutorial"), fixturesFolder("files-watch")); + cli = new CliRunner(["node", "start-files-watch.js"], { + cwd: path.resolve(__dirname, "fixtures") + }); + await wait(1000); + }); + + afterAll(async () => { + await cli.kill(); + }); + + describe("When server is started", () => { it("should have 3 behaviors available", async () => { const behaviors = await getBehaviors(); expect(behaviors.length).toEqual(3); }); - if (interactiveCli) { - it("should display available behaviors in CLI", async () => { - await wait(500); - expect(interactiveCli.cli.logs).toEqual(expect.stringContaining("Behaviors: 3")); - }); - it("should display current behavior in CLI", async () => { - expect(interactiveCli.cli.logs).toEqual( - expect.stringContaining("Current behavior: standard") - ); - }); - } - it("should serve users collection mock under the /api/users path", async () => { const users = await request("/api/users"); expect(users).toEqual([ @@ -70,12 +72,6 @@ const runTests = interactiveCli => { expect(behaviors.length).toEqual(4); }); - if (interactiveCli) { - it("should display available behaviors in CLI", async () => { - expect(interactiveCli.cli.logs).toEqual(expect.stringContaining("Behaviors: 4")); - }); - } - it("should serve users collection mock under the /api/users path", async () => { const users = await request("/api/users"); expect(users).toEqual([ @@ -100,15 +96,6 @@ const runTests = interactiveCli => { await changeBehavior("user2"); }); - if (interactiveCli) { - it("should display current behavior in CLI", async () => { - await wait(500); - expect(interactiveCli.cli.logs).toEqual( - expect.stringContaining("Current behavior: user2") - ); - }); - } - it("should serve users collection mock under the /api/users path", async () => { const users = await request("/api/users"); expect(users).toEqual([ @@ -133,15 +120,6 @@ const runTests = interactiveCli => { await changeBehavior("dynamic"); }); - if (interactiveCli) { - it("should display current behavior in CLI", async () => { - await wait(500); - expect(interactiveCli.cli.logs).toEqual( - expect.stringContaining("Current behavior: dynamic") - ); - }); - } - it("should serve users collection mock under the /api/users path", async () => { const users = await request("/api/users"); expect(users).toEqual([ @@ -166,15 +144,6 @@ const runTests = interactiveCli => { await changeBehavior("newOne"); }); - if (interactiveCli) { - it("should display current behavior in CLI", async () => { - await wait(500); - expect(interactiveCli.cli.logs).toEqual( - expect.stringContaining("Current behavior: newOne") - ); - }); - } - it("should serve users collection mock under the /api/users path", async () => { const users = await request("/api/new-users"); expect(users).toEqual([ @@ -194,47 +163,4 @@ const runTests = interactiveCli => { }); }); }); -}; - -describe("files watcher", () => { - const binaryPath = "../../../../bin/mocks-server"; - const cwdPath = path.resolve(__dirname, "fixtures"); - const interactiveCli = { - cli: null - }; - - beforeAll(async () => { - fsExtra.removeSync(fixturesFolder("files-watch")); - fsExtra.copySync(fixturesFolder("web-tutorial"), fixturesFolder("files-watch")); - interactiveCli.cli = new InteractiveCliRunner([binaryPath, "--behaviors=files-watch"], { - cwd: cwdPath - }); - await wait(); - }); - - afterAll(async () => { - await interactiveCli.cli.kill(); - }); - - runTests(interactiveCli); -}); - -describe("files watcher started using Server", () => { - const cwdPath = fixturesFolder("programmatic-server"); - let cli; - - beforeAll(async () => { - fsExtra.removeSync(fixturesFolder("files-watch")); - fsExtra.copySync(fixturesFolder("web-tutorial"), fixturesFolder("files-watch")); - cli = new CliRunner("start-watch.js", { - cwd: cwdPath - }); - await wait(); - }); - - afterAll(async () => { - await cli.kill(); - }); - - runTests(); }); diff --git a/test/acceptance/main/fixtures/files-modification/fixtures/users.js b/test/acceptance/fixtures/files-modification/fixtures/users.js similarity index 100% rename from test/acceptance/main/fixtures/files-modification/fixtures/users.js rename to test/acceptance/fixtures/files-modification/fixtures/users.js diff --git a/test/acceptance/main/fixtures/files-modification/new-fixtures/users.js b/test/acceptance/fixtures/files-modification/new-fixtures/users.js similarity index 100% rename from test/acceptance/main/fixtures/files-modification/new-fixtures/users.js rename to test/acceptance/fixtures/files-modification/new-fixtures/users.js diff --git a/test/acceptance/main/fixtures/files-modification/standard.js b/test/acceptance/fixtures/files-modification/standard.js similarity index 95% rename from test/acceptance/main/fixtures/files-modification/standard.js rename to test/acceptance/fixtures/files-modification/standard.js index fd98237..383ecc2 100644 --- a/test/acceptance/main/fixtures/files-modification/standard.js +++ b/test/acceptance/fixtures/files-modification/standard.js @@ -10,7 +10,7 @@ Unless required by applicable law or agreed to in writing, software distributed // /mocks/behaviors.js -const { Behavior } = require("../../../../../index"); +const { Behavior } = require("@mocks-server/core"); const { getUsers, getUser, getUser2, getRealUser } = require("./fixtures/users"); const { getNewUsers, getNewUser } = require("./new-fixtures/users"); diff --git a/test/acceptance/fixtures/start-files-watch.js b/test/acceptance/fixtures/start-files-watch.js new file mode 100644 index 0000000..2b7faa4 --- /dev/null +++ b/test/acceptance/fixtures/start-files-watch.js @@ -0,0 +1,6 @@ +const path = require("path"); +const { startServer } = require("../utils"); + +startServer(path.resolve(__dirname, "files-watch"), { + watch: true +}); diff --git a/test/acceptance/main/fixtures/web-tutorial/fixtures/users.js b/test/acceptance/fixtures/web-tutorial/fixtures/users.js similarity index 100% rename from test/acceptance/main/fixtures/web-tutorial/fixtures/users.js rename to test/acceptance/fixtures/web-tutorial/fixtures/users.js diff --git a/test/acceptance/main/fixtures/web-tutorial/standard.js b/test/acceptance/fixtures/web-tutorial/standard.js similarity index 94% rename from test/acceptance/main/fixtures/web-tutorial/standard.js rename to test/acceptance/fixtures/web-tutorial/standard.js index c2703a0..9035692 100644 --- a/test/acceptance/main/fixtures/web-tutorial/standard.js +++ b/test/acceptance/fixtures/web-tutorial/standard.js @@ -10,7 +10,7 @@ Unless required by applicable law or agreed to in writing, software distributed // /mocks/behaviors.js -const { Behavior } = require("../../../../../index"); +const { Behavior } = require("@mocks-server/core"); const { getUsers, getUser, getUser2, getRealUser } = require("./fixtures/users"); diff --git a/test/acceptance/main/InteractiveCliRunner.js b/test/acceptance/main/InteractiveCliRunner.js deleted file mode 100644 index b71c841..0000000 --- a/test/acceptance/main/InteractiveCliRunner.js +++ /dev/null @@ -1,78 +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 { CliRunner, wait } = require("./utils"); - -const LOG = "[CLI]: "; -const SCREEN_SEPARATOR = ">> Mocks server"; - -module.exports = class InteractiveCliRunner { - constructor(cliArguments, cliOptions) { - this._cli = new CliRunner(cliArguments, cliOptions); - } - - _log(log) { - console.log(`${LOG}${log}`); - } - - async getCurrentScreen() { - await wait(500); - const allLogs = this._cli.allLogs; - const lastLog = allLogs[allLogs.length - 1]; - return lastLog.includes(SCREEN_SEPARATOR) - ? `${SCREEN_SEPARATOR}${lastLog.split(SCREEN_SEPARATOR).pop()}` - : lastLog; - } - - async logCurrentScreen() { - this._log(`Current screen:\n${await this.getCurrentScreen()}`); - this._log("----------------"); - } - - async getCurrentSelection() { - const screen = await this.getCurrentScreen(); - const splitted = screen.split("❯"); - const lastAction = splitted[splitted.length - 1]; - return lastAction.split("\n")[0]; - } - - async logCurrentSelection() { - this._log(`Current selection: ❯ ${await this.getCurrentSelection()}`); - } - - kill() { - return this._cli.kill(); - } - - get logs() { - return this._cli.logs; - } - - async cursorDown(number) { - this._log("Moving cursor down"); - this._cli.cursorDown(); - await this.logCurrentSelection(); - if (number > 1) { - await this.cursorDown(number - 1); - } - } - - async pressEnter() { - this._log("Pressing Enter"); - const newScreen = await this._cli.newScreenAfter(this._cli.pressEnter); - await this.logCurrentSelection(); - return newScreen; - } - - write(data) { - this._log(`Writing: "${data}"`); - this._cli.write(data); - } -}; diff --git a/test/acceptance/main/cli-arguments.spec.js b/test/acceptance/main/cli-arguments.spec.js deleted file mode 100644 index 2d6887c..0000000 --- a/test/acceptance/main/cli-arguments.spec.js +++ /dev/null @@ -1,170 +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 { CliRunner, request, wait, TimeCounter } = require("./utils"); - -describe("command line arguments", () => { - const binaryPath = "../../../../bin/mocks-server"; - const cwdPath = path.resolve(__dirname, "fixtures"); - let cli; - - afterEach(async () => { - await cli.kill(); - }); - - describe("behaviors option", () => { - it("should set mocks folder", async () => { - expect.assertions(2); - cli = new CliRunner([binaryPath, "--behaviors=web-tutorial"], { - 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")); - }); - }); - - describe("behavior option", () => { - describe("when not provided", () => { - it("should set as current behavior the first one found", async () => { - expect.assertions(2); - cli = new CliRunner([binaryPath, "--behaviors=web-tutorial"], { - 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([binaryPath, "--behaviors=web-tutorial", "--behavior=dynamic"], { - 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([binaryPath, "--behaviors=web-tutorial", "--behavior=foo"], { - 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([binaryPath, "--behaviors=web-tutorial", "--behavior=foo"], { - 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("features option", () => { - it("should set mocks folder", async () => { - expect.assertions(2); - cli = new CliRunner([binaryPath, "--features=web-tutorial"], { - 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")); - }); - }); - - describe("feature option", () => { - describe("when not provided", () => { - it("should set as current behavior the first one found", async () => { - expect.assertions(2); - cli = new CliRunner([binaryPath, "--features=web-tutorial"], { - 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([binaryPath, "--features=web-tutorial", "--feature=dynamic"], { - 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([binaryPath, "--features=web-tutorial", "--feature=foo"], { - 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([binaryPath, "--features=web-tutorial", "--feature=foo"], { - 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([binaryPath, "--behaviors=web-tutorial", "--delay=2000"], { - 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/acceptance/main/cli-disabled-arguments.spec.js b/test/acceptance/main/cli-disabled-arguments.spec.js deleted file mode 100644 index f33237a..0000000 --- a/test/acceptance/main/cli-disabled-arguments.spec.js +++ /dev/null @@ -1,185 +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 { CliRunner, request, wait, TimeCounter } = require("./utils"); - -describe("command line arguments with cli disabled", () => { - const binaryPath = "../../../../bin/mocks-server"; - 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([binaryPath, "--behaviors=web-tutorial", "--cli=false"], { - cwd: cwdPath - }); - await wait(3000); - expect(cli.logs).toEqual(expect.not.stringContaining("Select action")); - }); - }); - - describe("behaviors option", () => { - it("should set mocks folder", async () => { - cli = new CliRunner([binaryPath, "--behaviors=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([binaryPath, "--behaviors=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( - [binaryPath, "--behaviors=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( - [binaryPath, "--behaviors=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( - [binaryPath, "--behaviors=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([binaryPath, "--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([binaryPath, "--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( - [binaryPath, "--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( - [binaryPath, "--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( - [binaryPath, "--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( - [binaryPath, "--behaviors=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/acceptance/main/cli-no-behaviors.spec.js b/test/acceptance/main/cli-no-behaviors.spec.js deleted file mode 100644 index abdb09c..0000000 --- a/test/acceptance/main/cli-no-behaviors.spec.js +++ /dev/null @@ -1,46 +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 { CliRunner, wait } = require("./utils"); - -describe("with no behaviors", () => { - const binaryPath = "../../../../bin/mocks-server"; - const cwdPath = path.resolve(__dirname, "fixtures"); - let cli; - - afterEach(async () => { - await cli.kill(); - }); - - it("should print a dash as current behavior", async () => { - cli = new CliRunner([binaryPath, "--behaviors=no-behaviors"], { - cwd: cwdPath - }); - await wait(); - expect(cli.logs).toEqual(expect.stringContaining("Current behavior: -")); - }); - - it("should print behaviors as 0", async () => { - cli = new CliRunner([binaryPath, "--behaviors=no-behaviors"], { - cwd: cwdPath - }); - await wait(); - expect(cli.logs).toEqual(expect.stringContaining("Behaviors: 0")); - }); - - it("should print current fixtures as 0", async () => { - cli = new CliRunner([binaryPath, "--behaviors=no-behaviors"], { - cwd: cwdPath - }); - await wait(); - expect(cli.logs).toEqual(expect.stringContaining("Current fixtures: 0")); - }); -}); diff --git a/test/acceptance/main/delay-setting.spec.js b/test/acceptance/main/delay-setting.spec.js deleted file mode 100644 index 6d7982f..0000000 --- a/test/acceptance/main/delay-setting.spec.js +++ /dev/null @@ -1,42 +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 { startServer, stopServer, request, changeDelay, TimeCounter } = require("./utils"); - -describe("delay setting", () => { - let server; - - beforeAll(async () => { - server = await startServer(); - }); - - afterAll(() => { - stopServer(server); - }); - - describe("When started", () => { - it("should respond with no delay", async () => { - const timeCounter = new TimeCounter(); - await request("/api/users"); - timeCounter.stop(); - expect(timeCounter.total).toBeLessThan(200); - }); - }); - - describe("When delay is changed through admin-api", () => { - it("should respond after defined delay", async () => { - await changeDelay(1000); - const timeCounter = new TimeCounter(); - await request("/api/users"); - timeCounter.stop(); - expect(timeCounter.total).toBeGreaterThan(999); - }); - }); -}); diff --git a/test/acceptance/main/fixtures/no-behaviors/base.js b/test/acceptance/main/fixtures/no-behaviors/base.js deleted file mode 100644 index f053ebf..0000000 --- a/test/acceptance/main/fixtures/no-behaviors/base.js +++ /dev/null @@ -1 +0,0 @@ -module.exports = {}; diff --git a/test/acceptance/main/fixtures/programmatic-cli/init-server.js b/test/acceptance/main/fixtures/programmatic-cli/init-server.js deleted file mode 100644 index 79b190f..0000000 --- a/test/acceptance/main/fixtures/programmatic-cli/init-server.js +++ /dev/null @@ -1,29 +0,0 @@ -const path = require("path"); -const { Cli } = require("../../../../../index"); - -const wait = (time = 3000) => { - return new Promise(resolve => { - setTimeout(() => { - resolve(); - }, time); - }); -}; - -const cli = new Cli({ - behaviors: path.resolve(__dirname, "..", "web-tutorial"), - port: 3100, - log: "debug", - watch: false -}); - -cli - .initServer() - .catch(err => { - console.log("Error initializing CLI", err); - }) - .then(() => { - console.log("Cli initialized"); - return wait().then(() => { - return cli.start(); - }); - }); diff --git a/test/acceptance/main/fixtures/programmatic-cli/start-delay.js b/test/acceptance/main/fixtures/programmatic-cli/start-delay.js deleted file mode 100644 index 9d5b298..0000000 --- a/test/acceptance/main/fixtures/programmatic-cli/start-delay.js +++ /dev/null @@ -1,13 +0,0 @@ -const path = require("path"); -const { Cli } = require("../../../../../index"); - -const cli = new Cli({ - behaviors: path.resolve(__dirname, "..", "web-tutorial"), - port: 3100, - log: "debug", - delay: 2000 -}); - -cli.start().catch(err => { - console.log("Error starting CLI", err); -}); diff --git a/test/acceptance/main/fixtures/programmatic-cli/start-dynamic-behavior.js b/test/acceptance/main/fixtures/programmatic-cli/start-dynamic-behavior.js deleted file mode 100644 index 4312e85..0000000 --- a/test/acceptance/main/fixtures/programmatic-cli/start-dynamic-behavior.js +++ /dev/null @@ -1,14 +0,0 @@ -const path = require("path"); -const { Cli } = require("../../../../../index"); - -const cli = new Cli({ - behaviors: path.resolve(__dirname, "..", "web-tutorial"), - behavior: "dynamic", - port: 3100, - log: "debug", - watch: false -}); - -cli.start().catch(err => { - console.log("Error starting CLI", err); -}); diff --git a/test/acceptance/main/fixtures/programmatic-cli/start-dynamic-feature.js b/test/acceptance/main/fixtures/programmatic-cli/start-dynamic-feature.js deleted file mode 100644 index 86266d4..0000000 --- a/test/acceptance/main/fixtures/programmatic-cli/start-dynamic-feature.js +++ /dev/null @@ -1,14 +0,0 @@ -const path = require("path"); -const { Cli } = require("../../../../../index"); - -const cli = new Cli({ - behaviors: path.resolve(__dirname, "..", "web-tutorial"), - feature: "dynamic", - port: 3100, - log: "debug", - watch: false -}); - -cli.start().catch(err => { - console.log("Error starting CLI", err); -}); diff --git a/test/acceptance/main/fixtures/programmatic-cli/start-features.js b/test/acceptance/main/fixtures/programmatic-cli/start-features.js deleted file mode 100644 index 0e8052c..0000000 --- a/test/acceptance/main/fixtures/programmatic-cli/start-features.js +++ /dev/null @@ -1,13 +0,0 @@ -const path = require("path"); -const { Cli } = require("../../../../../index"); - -const cli = new Cli({ - features: path.resolve(__dirname, "..", "web-tutorial"), - port: 3100, - log: "debug", - watch: false -}); - -cli.start().catch(err => { - console.log("Error starting CLI", err); -}); diff --git a/test/acceptance/main/fixtures/programmatic-cli/start-unexistant-behavior.js b/test/acceptance/main/fixtures/programmatic-cli/start-unexistant-behavior.js deleted file mode 100644 index c8ca70c..0000000 --- a/test/acceptance/main/fixtures/programmatic-cli/start-unexistant-behavior.js +++ /dev/null @@ -1,14 +0,0 @@ -const path = require("path"); -const { Cli } = require("../../../../../index"); - -const cli = new Cli({ - behaviors: path.resolve(__dirname, "..", "web-tutorial"), - behavior: "foo", - port: 3100, - log: "debug", - watch: false -}); - -cli.start().catch(err => { - console.log("Error starting CLI", err); -}); diff --git a/test/acceptance/main/fixtures/programmatic-cli/start-unexistant-feature.js b/test/acceptance/main/fixtures/programmatic-cli/start-unexistant-feature.js deleted file mode 100644 index 1075ba3..0000000 --- a/test/acceptance/main/fixtures/programmatic-cli/start-unexistant-feature.js +++ /dev/null @@ -1,14 +0,0 @@ -const path = require("path"); -const { Cli } = require("../../../../../index"); - -const cli = new Cli({ - behaviors: path.resolve(__dirname, "..", "web-tutorial"), - feature: "foo", - port: 3100, - log: "debug", - watch: false -}); - -cli.start().catch(err => { - console.log("Error starting CLI", err); -}); diff --git a/test/acceptance/main/fixtures/programmatic-cli/start.js b/test/acceptance/main/fixtures/programmatic-cli/start.js deleted file mode 100644 index 378ec40..0000000 --- a/test/acceptance/main/fixtures/programmatic-cli/start.js +++ /dev/null @@ -1,13 +0,0 @@ -const path = require("path"); -const { Cli } = require("../../../../../index"); - -const cli = new Cli({ - behaviors: path.resolve(__dirname, "..", "web-tutorial"), - port: 3100, - log: "debug", - watch: false -}); - -cli.start().catch(err => { - console.log("Error starting CLI", err); -}); diff --git a/test/acceptance/main/fixtures/programmatic-server/start-and-stop.js b/test/acceptance/main/fixtures/programmatic-server/start-and-stop.js deleted file mode 100644 index 930e457..0000000 --- a/test/acceptance/main/fixtures/programmatic-server/start-and-stop.js +++ /dev/null @@ -1,24 +0,0 @@ -const path = require("path"); -const { Server } = require("../../../../../index"); - -const wait = (time = 2000) => { - return new Promise(resolve => { - setTimeout(() => { - resolve(); - }, time); - }); -}; - -const server = new Server(path.resolve(__dirname, "..", "web-tutorial"), { - port: 3100, - log: "debug", - watch: false -}); - -server.start().then(serverInstance => { - console.log("Server started"); - return wait().then(() => { - serverInstance.stop(); - console.log("Server stopped"); - }); -}); diff --git a/test/acceptance/main/fixtures/programmatic-server/start-delay.js b/test/acceptance/main/fixtures/programmatic-server/start-delay.js deleted file mode 100644 index aec77d7..0000000 --- a/test/acceptance/main/fixtures/programmatic-server/start-delay.js +++ /dev/null @@ -1,12 +0,0 @@ -const path = require("path"); -const { Server } = require("../../../../../index"); - -const server = new Server(path.resolve(__dirname, "..", "web-tutorial"), { - port: 3100, - log: "debug", - delay: 2000 -}); - -server.start().then(() => { - console.log("Server started"); -}); diff --git a/test/acceptance/main/fixtures/programmatic-server/start-dynamic-behavior.js b/test/acceptance/main/fixtures/programmatic-server/start-dynamic-behavior.js deleted file mode 100644 index 64458b2..0000000 --- a/test/acceptance/main/fixtures/programmatic-server/start-dynamic-behavior.js +++ /dev/null @@ -1,13 +0,0 @@ -const path = require("path"); -const { Server } = require("../../../../../index"); - -const server = new Server(path.resolve(__dirname, "..", "web-tutorial"), { - port: 3100, - log: "debug", - watch: false, - behavior: "dynamic" -}); - -server.start().then(() => { - console.log("Server started"); -}); diff --git a/test/acceptance/main/fixtures/programmatic-server/start-dynamic-feature.js b/test/acceptance/main/fixtures/programmatic-server/start-dynamic-feature.js deleted file mode 100644 index 5ffd8e5..0000000 --- a/test/acceptance/main/fixtures/programmatic-server/start-dynamic-feature.js +++ /dev/null @@ -1,13 +0,0 @@ -const path = require("path"); -const { Server } = require("../../../../../index"); - -const server = new Server(path.resolve(__dirname, "..", "web-tutorial"), { - port: 3100, - log: "debug", - watch: false, - feature: "dynamic" -}); - -server.start().then(() => { - console.log("Server started"); -}); diff --git a/test/acceptance/main/fixtures/programmatic-server/start-unexistant-behavior.js b/test/acceptance/main/fixtures/programmatic-server/start-unexistant-behavior.js deleted file mode 100644 index 7af1d2e..0000000 --- a/test/acceptance/main/fixtures/programmatic-server/start-unexistant-behavior.js +++ /dev/null @@ -1,13 +0,0 @@ -const path = require("path"); -const { Server } = require("../../../../../index"); - -const server = new Server(path.resolve(__dirname, "..", "web-tutorial"), { - port: 3100, - log: "debug", - watch: false, - behavior: "foo" -}); - -server.start().then(() => { - console.log("Server started"); -}); diff --git a/test/acceptance/main/fixtures/programmatic-server/start-unexistant-feature.js b/test/acceptance/main/fixtures/programmatic-server/start-unexistant-feature.js deleted file mode 100644 index 7928b02..0000000 --- a/test/acceptance/main/fixtures/programmatic-server/start-unexistant-feature.js +++ /dev/null @@ -1,13 +0,0 @@ -const path = require("path"); -const { Server } = require("../../../../../index"); - -const server = new Server(path.resolve(__dirname, "..", "web-tutorial"), { - port: 3100, - log: "debug", - watch: false, - feature: "foo" -}); - -server.start().then(() => { - console.log("Server started"); -}); diff --git a/test/acceptance/main/fixtures/programmatic-server/start-watch.js b/test/acceptance/main/fixtures/programmatic-server/start-watch.js deleted file mode 100644 index c31b8da..0000000 --- a/test/acceptance/main/fixtures/programmatic-server/start-watch.js +++ /dev/null @@ -1,12 +0,0 @@ -const path = require("path"); -const { Server } = require("../../../../../index"); - -const server = new Server(path.resolve(__dirname, "..", "files-watch"), { - port: 3100, - log: "debug", - watch: true -}); - -server.start().then(() => { - console.log("Server started"); -}); diff --git a/test/acceptance/main/fixtures/programmatic-server/start.js b/test/acceptance/main/fixtures/programmatic-server/start.js deleted file mode 100644 index 2b879de..0000000 --- a/test/acceptance/main/fixtures/programmatic-server/start.js +++ /dev/null @@ -1,12 +0,0 @@ -const path = require("path"); -const { Server } = require("../../../../../index"); - -const server = new Server(path.resolve(__dirname, "..", "web-tutorial"), { - port: 3100, - log: "debug", - watch: false -}); - -server.start().then(() => { - console.log("Server started"); -}); diff --git a/test/acceptance/main/interactive-cli.spec.js b/test/acceptance/main/interactive-cli.spec.js deleted file mode 100644 index 9756390..0000000 --- a/test/acceptance/main/interactive-cli.spec.js +++ /dev/null @@ -1,134 +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 } = require("./utils"); -const InteractiveCliRunner = require("./InteractiveCliRunner"); - -describe("interactive CLI", () => { - let cli; - const binaryPath = "../../../../bin/mocks-server"; - const cwdPath = path.resolve(__dirname, "fixtures"); - - beforeAll(async () => { - cli = new InteractiveCliRunner([binaryPath, "--behaviors=web-tutorial"], { - cwd: cwdPath - }); - await wait(); - }); - - afterAll(async () => { - await cli.kill(); - }); - - describe("When started", () => { - it("should have 3 behaviors available", async () => { - expect(cli.logs).toEqual(expect.stringContaining("Behaviors: 3")); - }); - - it("should serve users collection mock under the /api/users path", async () => { - const users = await request("/api/users"); - expect(users).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 request("/api/users/1"); - expect(users).toEqual({ id: 1, name: "John Doe" }); - }); - - it("should serve user 1 under the /api/users/2 path", async () => { - const users = await request("/api/users/2"); - expect(users).toEqual({ id: 1, name: "John Doe" }); - }); - }); - - describe('When changing current behavior to "dynamic"', () => { - it("should display new selected behavior", async () => { - await cli.pressEnter(); - await cli.cursorDown(2); - const newScreen = await cli.pressEnter(); - expect(newScreen).toEqual(expect.stringContaining("Current behavior: dynamic")); - }); - - it("should serve users collection mock under the /api/users path", async () => { - const users = await request("/api/users"); - expect(users).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 request("/api/users/1"); - expect(users).toEqual({ id: 1, name: "John Doe" }); - }); - - it("should serve user 2 under the /api/users/2 path", async () => { - const users = await request("/api/users/2"); - expect(users).toEqual({ id: 2, name: "Jane Doe" }); - }); - - it("should return not found for /api/users/3 path", async () => { - const usersResponse = await request("/api/users/3", { - resolveWithFullResponse: true, - simple: false - }); - expect(usersResponse.statusCode).toEqual(404); - }); - }); - - describe("When changing logs level", () => { - it("should display new selected log level", async () => { - await cli.cursorDown(3); - await cli.pressEnter(); - await cli.cursorDown(2); - const newScreen = await cli.pressEnter(); - expect(newScreen).toEqual(expect.stringContaining("Log level: verbose")); - }); - }); - - describe("When displaying logs", () => { - it("should log requests", async () => { - expect.assertions(2); - await cli.cursorDown(5); - await cli.pressEnter(); - await request("/api/users"); - const newScreen = await cli.getCurrentScreen(); - expect(newScreen).toEqual(expect.stringContaining("Displaying logs")); - expect(newScreen).toEqual(expect.stringContaining("[Mocks verbose] Request received")); - await cli.pressEnter(); - }); - }); - - describe("When changing delay time", () => { - it("should display new selected delay time", async () => { - await cli.cursorDown(); - await cli.pressEnter(); - await cli.write(2000); - const newScreen = await cli.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 request("/api/users"); - timeCounter.stop(); - expect(timeCounter.total).toBeGreaterThan(1999); - expect(users).toEqual([ - { id: 1, name: "John Doe" }, - { id: 2, name: "Jane Doe" } - ]); - }); - }); -}); diff --git a/test/acceptance/main/programmatic-cli.spec.js b/test/acceptance/main/programmatic-cli.spec.js deleted file mode 100644 index 60621ac..0000000 --- a/test/acceptance/main/programmatic-cli.spec.js +++ /dev/null @@ -1,193 +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 { CliRunner, request, wait, TimeCounter, fixturesFolder } = require("./utils"); - -describe("programmatic Cli", () => { - const cwdPath = fixturesFolder("programmatic-cli"); - let cli; - - afterEach(async () => { - await cli.kill(); - }); - - describe("When started", () => { - it("should set mocks folder", async () => { - expect.assertions(2); - cli = new CliRunner("start.js", { - 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")); - }); - - it("should set mocks folder even when deprecated features option is received", async () => { - expect.assertions(3); - cli = new CliRunner("start-features.js", { - 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( - "Deprecation warning: --features option will be deprecated. Use --behaviors instead" - ) - ); - expect(cli.logs).toEqual(expect.stringContaining("Behaviors: 3")); - }); - - it("should print a log when started", async () => { - cli = new CliRunner("start.js", { - cwd: cwdPath - }); - await wait(); - expect(cli.logs).toEqual(expect.stringContaining("Server started")); - }); - }); - - describe("behavior option", () => { - describe("when not provided", () => { - it("should set as current behavior the first one found", async () => { - expect.assertions(2); - cli = new CliRunner("start.js", { - 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("start-dynamic-behavior.js", { - 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("start-unexistant-behavior.js", { - 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("start-unexistant-behavior.js", { - 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("feature option", () => { - describe("when provided and exists", () => { - it("should set current behavior", async () => { - expect.assertions(2); - cli = new CliRunner("start-dynamic-feature.js", { - 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")); - }); - - it("should print a deprecation warning", async () => { - cli = new CliRunner("start-dynamic-feature.js", { - cwd: cwdPath - }); - await wait(); - expect(cli.logs).toEqual( - expect.stringContaining( - "Deprecation warning: --feature option will be deprecated. Use --behavior instead" - ) - ); - }); - }); - - describe("when provided and does not exist", () => { - it("should print a warning", async () => { - cli = new CliRunner("start-unexistant-feature.js", { - 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("start-unexistant-feature.js", { - 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("start-delay.js", { - 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); - }); - }); - - describe("when initializing server manually and start after", () => { - it("should start server without cli, then start", async () => { - expect.assertions(3); - cli = new CliRunner("init-server.js", { - cwd: cwdPath - }); - await wait(); - const users = await request("/api/users/2"); - expect(users).toEqual({ id: 1, name: "John Doe" }); - expect(cli.logs).toEqual(expect.not.stringContaining("Select action")); - await wait(3000); - expect(cli.logs).toEqual(expect.stringContaining("Select action")); - }); - }); -}); diff --git a/test/acceptance/main/programmatic-server.spec.js b/test/acceptance/main/programmatic-server.spec.js deleted file mode 100644 index 027100e..0000000 --- a/test/acceptance/main/programmatic-server.spec.js +++ /dev/null @@ -1,168 +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 { CliRunner, request, wait, TimeCounter, fixturesFolder } = require("./utils"); - -describe("programmatic Server", () => { - const cwdPath = fixturesFolder("programmatic-server"); - let cli; - - afterEach(async () => { - await cli.kill(); - }); - - describe("When started", () => { - it("should set mocks folder", async () => { - cli = new CliRunner("start.js", { - cwd: cwdPath - }); - await wait(); - const users = await request("/api/users"); - expect(users).toEqual([ - { id: 1, name: "John Doe" }, - { id: 2, name: "Jane Doe" } - ]); - }); - - it("should print a log when started", async () => { - cli = new CliRunner("start.js", { - cwd: cwdPath - }); - await wait(); - expect(cli.logs).toEqual(expect.stringContaining("Server started")); - }); - }); - - describe("behavior option", () => { - describe("when not provided", () => { - it("should set as current behavior the first one found", async () => { - cli = new CliRunner("start.js", { - 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("start-dynamic-behavior.js", { - 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("start-unexistant-behavior.js", { - 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("start-unexistant-behavior.js", { - cwd: cwdPath - }); - await wait(); - const users = await request("/api/users/2"); - expect(users).toEqual({ id: 1, name: "John Doe" }); - }); - }); - }); - - describe("feature option", () => { - describe("when provided and exists", () => { - it("should set current behavior", async () => { - cli = new CliRunner("start-dynamic-feature.js", { - cwd: cwdPath - }); - await wait(); - const users = await request("/api/users/2"); - expect(users).toEqual({ id: 2, name: "Jane Doe" }); - }); - - it("should print a deprecation warning", async () => { - cli = new CliRunner("start-dynamic-feature.js", { - cwd: cwdPath - }); - await wait(); - expect(cli.logs).toEqual( - expect.stringContaining( - "Deprecation warning: --feature option will be deprecated. Use --behavior instead" - ) - ); - }); - }); - - describe("when provided and does not exist", () => { - it("should print a warning", async () => { - cli = new CliRunner("start-unexistant-feature.js", { - 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("start-unexistant-feature.js", { - 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("start-delay.js", { - 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); - }); - }); - - describe("stop method", () => { - it("should stop the server", async () => { - expect.assertions(2); - cli = new CliRunner("start-and-stop.js", { - cwd: cwdPath - }); - await wait(); - const users = await request("/api/users"); - expect(users).toEqual([ - { id: 1, name: "John Doe" }, - { id: 2, name: "Jane Doe" } - ]); - await wait(2000); - try { - await request("/api/users"); - } catch (error) { - expect(error.message).toEqual(expect.stringContaining("ECONNREFUSED")); - } - }); - }); -}); diff --git a/test/acceptance/main/web-tutorial-cli.spec.js b/test/acceptance/main/web-tutorial-cli.spec.js deleted file mode 100644 index d86c83c..0000000 --- a/test/acceptance/main/web-tutorial-cli.spec.js +++ /dev/null @@ -1,116 +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 } = require("./utils"); -const InteractiveCliRunner = require("./InteractiveCliRunner"); - -describe("web tutorial", () => { - let cli; - const binaryPath = "../../../../bin/mocks-server"; - const cwdPath = path.resolve(__dirname, "fixtures"); - - beforeAll(async () => { - cli = new InteractiveCliRunner([binaryPath, "--behaviors=web-tutorial"], { - cwd: cwdPath - }); - await wait(); - }); - - afterAll(async () => { - await cli.kill(); - }); - - describe("When started", () => { - it("should have 3 behaviors available", async () => { - expect(cli.logs).toEqual(expect.stringContaining("Behaviors: 3")); - }); - - it("should serve users collection mock under the /api/users path", async () => { - const users = await request("/api/users"); - expect(users).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 request("/api/users/1"); - expect(users).toEqual({ id: 1, name: "John Doe" }); - }); - - it("should serve user 1 under the /api/users/2 path", async () => { - const users = await request("/api/users/2"); - expect(users).toEqual({ id: 1, name: "John Doe" }); - }); - }); - - describe('When changing current behavior to "user2"', () => { - it("should display new selected behavior", async () => { - await cli.pressEnter(); - await cli.cursorDown(); - const newScreen = await cli.pressEnter(); - expect(newScreen).toEqual(expect.stringContaining("Current behavior: user2")); - }); - - it("should serve users collection mock under the /api/users path", async () => { - const users = await request("/api/users"); - expect(users).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 request("/api/users/1"); - expect(users).toEqual({ id: 2, name: "Jane Doe" }); - }); - - it("should serve user 2 under the /api/users/2 path", async () => { - const users = await request("/api/users/2"); - expect(users).toEqual({ id: 2, name: "Jane Doe" }); - }); - }); - - describe('When changing current behavior to "dynamic"', () => { - it("should display new selected behavior", async () => { - await cli.pressEnter(); - await cli.cursorDown(2); - const newScreen = await cli.pressEnter(); - expect(newScreen).toEqual(expect.stringContaining("Current behavior: dynamic")); - }); - - it("should serve users collection mock under the /api/users path", async () => { - const users = await request("/api/users"); - expect(users).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 request("/api/users/1"); - expect(users).toEqual({ id: 1, name: "John Doe" }); - }); - - it("should serve user 2 under the /api/users/2 path", async () => { - const users = await request("/api/users/2"); - expect(users).toEqual({ id: 2, name: "Jane Doe" }); - }); - - it("should return not found for /api/users/3 path", async () => { - const usersResponse = await request("/api/users/3", { - resolveWithFullResponse: true, - simple: false - }); - expect(usersResponse.statusCode).toEqual(404); - }); - }); -}); diff --git a/test/acceptance/main/utils.js b/test/acceptance/utils.js similarity index 79% rename from test/acceptance/main/utils.js rename to test/acceptance/utils.js index 3046686..7b66800 100644 --- a/test/acceptance/main/utils.js +++ b/test/acceptance/utils.js @@ -9,16 +9,18 @@ Unless required by applicable law or agreed to in writing, software distributed */ const path = require("path"); +const { Core } = require("@mocks-server/core"); const requestPromise = require("request-promise"); -const CliRunner = require("../cli/CliRunner"); // TODO, export in CLI package for testing purposes? -const { Server } = require("../../../index"); +const CliRunner = require("./CliRunner"); + +const PluginAdminApi = require("../../index"); const SERVER_PORT = 3100; const defaultOptions = { port: SERVER_PORT, - log: "debug", + log: "silly", watch: false }; @@ -30,20 +32,26 @@ const fixturesFolder = folderName => { return path.resolve(__dirname, "fixtures", folderName); }; -const startServer = (mocksPath, options = {}) => { +const startServer = (mocksPath, opts = {}) => { const mocks = mocksPath || "web-tutorial"; - const server = new Server(fixturesFolder(mocks), { + const options = { + behaviors: fixturesFolder(mocks), ...defaultOptions, - ...options + ...opts + }; + const server = new Core({ + onlyProgrammaticOptions: true, + plugins: [PluginAdminApi] }); - return server.start().then(() => { - return Promise.resolve(server); + return server.init(options).then(() => { + return server.start().then(() => { + return Promise.resolve(server); + }); }); }; const stopServer = server => { - server.stop(); - server.switchWatch(false); + return server.stop(); }; const request = (uri, options = {}) => { @@ -116,7 +124,7 @@ module.exports = { getBehaviors, changeDelay, TimeCounter, - CliRunner, wait, - fixturesFolder + fixturesFolder, + CliRunner }; diff --git a/test/unit/core/Core.mocks.js b/test/unit/Core.mocks.js similarity index 95% rename from test/unit/core/Core.mocks.js rename to test/unit/Core.mocks.js index 972791c..8e88ddb 100644 --- a/test/unit/core/Core.mocks.js +++ b/test/unit/Core.mocks.js @@ -10,9 +10,9 @@ Unless required by applicable law or agreed to in writing, software distributed const sinon = require("sinon"); -jest.mock("../../../lib/core/Core"); +jest.mock("@mocks-server/core"); -const Core = require("../../../lib/core/Core"); +const { Core } = require("@mocks-server/core"); class CoreMock { constructor() { diff --git a/test/unit/Libs.mocks.js b/test/unit/Libs.mocks.js index 6f09240..7185221 100644 --- a/test/unit/Libs.mocks.js +++ b/test/unit/Libs.mocks.js @@ -1,4 +1,5 @@ /* +Copyright 2019 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 @@ -13,10 +14,8 @@ const sinon = require("sinon"); const http = require("http"); jest.mock("express"); -jest.mock("node-watch"); const express = require("express"); -const watch = require("node-watch"); class CallBackRunner { constructor() { @@ -34,43 +33,14 @@ class CallBackRunner { } } -class WatchRunner { - constructor(closeStub) { - this.runner = this.runner.bind(this); - this.triggerChange = this.triggerChange.bind(this); - this._closeStub = closeStub; - this._change = false; - } - - runner(eventName, options, cb) { - if (this._change) { - return cb(); - } - return { - close: this._closeStub - }; - } - - triggerChange(change) { - this._change = change; - } -} - class Mock { constructor() { this._sandbox = sinon.createSandbox(); const httpCreateServerOnError = new CallBackRunner(); const httpCreateServerOnListen = new CallBackRunner(); - const watchClose = this._sandbox.stub(); - const watchRunner = new WatchRunner(watchClose); - const watchStub = this._sandbox.stub().callsFake(watchRunner.runner); - watchStub.triggerChange = watchRunner.triggerChange; this._stubs = { - watch: watchStub, - watchTriggerChange: watchRunner.triggerChange, - watchClose, express: { use: this._sandbox.stub(), options: this._sandbox.stub() @@ -87,7 +57,6 @@ class Mock { }; express.mockImplementation(() => this._stubs.express); - watch.mockImplementation(this._stubs.watch); this._sandbox.stub(http, "createServer").returns(this._stubs.http.createServer); } diff --git a/test/unit/ProgrammaticCli.spec.js b/test/unit/ProgrammaticCli.spec.js deleted file mode 100644 index 5c81974..0000000 --- a/test/unit/ProgrammaticCli.spec.js +++ /dev/null @@ -1,137 +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 sinon = require("sinon"); - -const CoreMocks = require("./core/Core.mocks.js"); -const CliMocks = require("./cli/Cli.mocks.js"); - -const AdminApi = require("../../lib/api/Api"); - -const ProgrammaticCli = require("../../lib/ProgrammaticCli"); - -describe("start method", () => { - let sandbox; - let coreMocks; - let cliMocks; - - beforeEach(() => { - sandbox = sinon.createSandbox(); - coreMocks = new CoreMocks(); - cliMocks = new CliMocks(); - }); - - afterEach(() => { - sandbox.restore(); - coreMocks.restore(); - }); - - describe("when created", () => { - it("should pass admin Api plugin to Core", async () => { - new ProgrammaticCli(); - expect(coreMocks.stubs.Constructor.mock.calls[0][0].plugins[0]).toEqual(AdminApi); - }); - - it("should pass a callback to Core returning a CLI plugin, which reference is saved", async () => { - const cli = new ProgrammaticCli(); - coreMocks.stubs.Constructor.mock.calls[0][0].plugins[1](); - expect(cli._inquirerCli).toEqual(cliMocks.stubs.instance); - }); - }); - - describe("start method", () => { - let cli; - const fooOptions = { - foo: "foo", - foo2: "foo2" - }; - - beforeEach(() => { - cli = new ProgrammaticCli(fooOptions); - coreMocks.stubs.Constructor.mock.calls[0][0].plugins[1](); - }); - - it("should init Core wit provided options", async () => { - await cli.start(); - expect(coreMocks.stubs.instance.init.getCall(0).args[0]).toEqual(fooOptions); - }); - - it("should start core", async () => { - await cli.start(); - expect(coreMocks.stubs.instance.start.callCount).toEqual(1); - }); - - it("should start CLI", async () => { - await cli.start(); - expect(cliMocks.stubs.instance.start.callCount).toEqual(1); - }); - - it("should start CLI only when CLI was disabled", async () => { - coreMocks.stubs.instance.settings.get.returns(true); - await cli.start(); - expect(cliMocks.stubs.instance.start.callCount).toEqual(0); - }); - - it("should start core and CLI only once when called multiple times", async () => { - expect.assertions(2); - await cli.start(); - await cli.start(); - expect(cliMocks.stubs.instance.start.callCount).toEqual(1); - expect(coreMocks.stubs.instance.start.callCount).toEqual(1); - }); - }); - - describe("init method", () => { - let cli; - const fooOptions = { - foo: "foo", - foo2: "foo2" - }; - - beforeEach(() => { - cli = new ProgrammaticCli(fooOptions); - coreMocks.stubs.Constructor.mock.calls[0][0].plugins[1](); - }); - - it("should init Core wit provided options", async () => { - await cli.initServer(); - expect(coreMocks.stubs.instance.init.getCall(0).args[0]).toEqual(fooOptions); - }); - - it("should disable cli", async () => { - await cli.initServer(); - expect(coreMocks.stubs.instance.settings.set.getCall(0).args).toEqual(["cli", false]); - }); - - it("should start core", async () => { - await cli.initServer(); - expect(coreMocks.stubs.instance.start.callCount).toEqual(1); - }); - - it("should start core only once after calling start", async () => { - await cli.initServer(); - await cli.start(); - expect(coreMocks.stubs.instance.start.callCount).toEqual(1); - }); - }); - - describe("stopListeningServerWatch method", () => { - let cli; - beforeEach(() => { - cli = new ProgrammaticCli(); - coreMocks.stubs.Constructor.mock.calls[0][0].plugins[1](); - }); - - it("should call to stopListeningServerWatch method of cli plugin", async () => { - await cli.stopListeningServerWatch(); - expect(cliMocks.stubs.instance.stopListeningServerWatch.callCount).toEqual(1); - }); - }); -}); diff --git a/test/unit/ProgrammaticServer.spec.js b/test/unit/ProgrammaticServer.spec.js deleted file mode 100644 index 660fad2..0000000 --- a/test/unit/ProgrammaticServer.spec.js +++ /dev/null @@ -1,125 +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 sinon = require("sinon"); - -const CoreMocks = require("./core/Core.mocks.js"); - -const AdminApi = require("../../lib/api/Api"); - -const ProgrammaticServer = require("../../lib/ProgrammaticServer"); - -describe("start method", () => { - const fooMocksPath = "foo-mocks-path"; - const fooOptions = { - foo: "foo", - foo2: "foo2" - }; - let sandbox; - let coreMocks; - let cli; - - beforeEach(() => { - sandbox = sinon.createSandbox(); - coreMocks = new CoreMocks(); - cli = new ProgrammaticServer(fooMocksPath, fooOptions); - }); - - afterEach(() => { - sandbox.restore(); - coreMocks.restore(); - }); - - describe("when created", () => { - it("should pass admin Api plugin to Core", async () => { - new ProgrammaticServer(); - expect(coreMocks.stubs.Constructor.mock.calls[0][0].plugins[0]).toEqual(AdminApi); - }); - }); - - describe("start method", () => { - it("should init Core with provided options", async () => { - await cli.start(); - expect(coreMocks.stubs.instance.init.getCall(0).args[0]).toEqual({ - ...fooOptions, - behaviors: fooMocksPath - }); - }); - - it("should not init core if it was done before", async () => { - await cli.start(); - await cli.start(); - await cli.start(); - expect(coreMocks.stubs.instance.init.callCount).toEqual(1); - }); - - it("should start core", async () => { - await cli.start(); - expect(coreMocks.stubs.instance.start.callCount).toEqual(1); - }); - - it("should resolve with ProgrammaticServer instance", async () => { - const instance = await cli.start(); - expect(instance).toEqual(cli); - }); - - it("should emit a watch-reload event when core emits a load:mocks event", async () => { - await cli.start(); - coreMocks.stubs.instance.onChangeSettings.getCall(0).args[0](); - expect(coreMocks.stubs.instance._eventEmitter.emit.getCall(0).args[0]).toEqual( - "watch-reload" - ); - }); - }); - - describe("stop method", () => { - it("should stop core", async () => { - await cli.stop(); - expect(coreMocks.stubs.instance.stop.callCount).toEqual(1); - }); - }); - - describe("switchWatch method", () => { - it("should set watch state as false if called with false", async () => { - await cli.switchWatch(false); - expect(coreMocks.stubs.instance.settings.set.getCall(0).args).toEqual(["watch", false]); - }); - - it("should set watch state as true if called with true", async () => { - await cli.switchWatch(true); - expect(coreMocks.stubs.instance.settings.set.getCall(0).args).toEqual(["watch", true]); - }); - }); - - describe("behaviors getter", () => { - it("should return core behaviors", async () => { - expect(cli.behaviors).toEqual(coreMocks.stubs.instance.behaviors); - }); - }); - - describe("watchEnabled getter", () => { - it("should return watch setting state", async () => { - coreMocks.stubs.instance.settings.get.returns(false); - expect(cli.watchEnabled).toEqual(false); - }); - }); - - describe("error getter", () => { - it("should return core serverError", async () => { - expect(cli.error).toEqual(coreMocks.stubs.instance.serverError); - }); - }); - - describe("events getter", () => { - it("should return core _eventEmitter", async () => { - expect(cli.events).toEqual(coreMocks.stubs.instance._eventEmitter); - }); - }); -}); diff --git a/test/unit/api/Api.mocks.js b/test/unit/api/Api.mocks.js deleted file mode 100644 index eea29cf..0000000 --- a/test/unit/api/Api.mocks.js +++ /dev/null @@ -1,40 +0,0 @@ -/* -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 - -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 sinon = require("sinon"); - -jest.mock("../../../lib/api/Api"); - -const Api = require("../../../lib/api/Api"); - -const Mock = class Mock { - constructor() { - this._sandbox = sinon.createSandbox(); - - this._stubs = { - router: this._sandbox.stub() - }; - - Api.mockImplementation(() => this._stubs); - } - - get stubs() { - return { - Constructor: Api, - instance: this._stubs - }; - } - - restore() { - this._sandbox.restore(); - } -}; - -module.exports = Mock; diff --git a/test/unit/cli/Cli.mocks.js b/test/unit/cli/Cli.mocks.js deleted file mode 100644 index d60c615..0000000 --- a/test/unit/cli/Cli.mocks.js +++ /dev/null @@ -1,42 +0,0 @@ -/* -Copyright 2019 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 - -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 sinon = require("sinon"); - -jest.mock("../../../lib/cli/Cli"); - -const Cli = require("../../../lib/cli/Cli"); - -class Mock { - constructor() { - this._sandbox = sinon.createSandbox(); - - this._stubs = { - start: this._sandbox.stub().resolves(), - stopListeningServerWatch: this._sandbox.stub() - }; - - Cli.mockImplementation(() => this._stubs); - } - - get stubs() { - return { - Constructor: Cli, - instance: this._stubs - }; - } - - restore() { - this._sandbox.restore(); - } -} - -module.exports = Mock; diff --git a/test/unit/cli/Cli.spec.js b/test/unit/cli/Cli.spec.js deleted file mode 100644 index 650cc99..0000000 --- a/test/unit/cli/Cli.spec.js +++ /dev/null @@ -1,326 +0,0 @@ -/* -Copyright 2019 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 - -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 sinon = require("sinon"); - -const CoreMocks = require("../core/Core.mocks.js"); -const InquirerMocks = require("./Inquirer.mocks.js"); - -const Cli = require("../../../lib/cli/Cli"); - -describe("Cli", () => { - let sandbox; - let inquirerMocks; - let coreMocks; - let coreInstance; - let cli; - - beforeEach(async () => { - sandbox = sinon.createSandbox(); - inquirerMocks = new InquirerMocks(); - coreMocks = new CoreMocks(); - coreInstance = coreMocks.stubs.instance; - cli = new Cli(coreInstance); - expect.assertions(1); - coreInstance.settings.get.withArgs("cli").returns(true); - coreInstance.settings.get.withArgs("log").returns("info"); - await cli.init(); - }); - - afterEach(() => { - sandbox.restore(); - inquirerMocks.restore(); - coreMocks.restore(); - }); - - describe("when created", () => { - it("should have added cli custom setting to core", () => { - coreMocks.reset(); - cli = new Cli(coreInstance); - expect(coreInstance.addCustomSetting.getCall(0).args[0].name).toEqual("cli"); - }); - }); - - describe("when initializated", () => { - it("should call to create an inquirer", () => { - expect(inquirerMocks.stubs.Inquirer.callCount).toEqual(1); - }); - - it("not be initializated if cli setting is disabled", async () => { - inquirerMocks.reset(); - coreInstance.settings.get.withArgs("cli").returns(false); - cli = new Cli(coreInstance); - await cli.init(); - expect(inquirerMocks.stubs.Inquirer.callCount).toEqual(0); - }); - - it("should save current log log level", () => { - expect(cli._logLevel).toEqual("info"); - }); - - it("should display main menu when core settings are changed and current screen is main menu", async () => { - await cli.start(); - expect.assertions(2); - coreInstance.onChangeSettings.getCall(0).args[0](); - expect(inquirerMocks.stubs.inquirer.inquire.callCount).toEqual(2); - expect(inquirerMocks.stubs.inquirer.inquire.getCall(1).args[0]).toEqual("main"); - }); - - it("should not display main menu when core settings are changed and current screen is not main menu", async () => { - await cli.start(); - expect.assertions(2); - cli._changeCurrentBehavior(); - coreInstance.onChangeSettings.getCall(0).args[0](); - expect(inquirerMocks.stubs.inquirer.inquire.callCount).toEqual(2); - expect(inquirerMocks.stubs.inquirer.inquire.getCall(1).args[0]).toEqual("behavior"); - }); - }); - - describe("when started", () => { - beforeEach(async () => { - await cli.start(); - }); - - it("should do nothing if it cli has not been inited", async () => { - inquirerMocks.reset(); - cli = new Cli(coreInstance); - coreInstance.settings.get.withArgs("cli").returns(true); - await cli.start(); - expect(inquirerMocks.stubs.inquirer.inquire.callCount).toEqual(0); - }); - - it("should do nothing if it cli is disabled", async () => { - inquirerMocks.reset(); - cli = new Cli(coreInstance); - coreInstance.settings.get.withArgs("cli").returns(false); - await cli.init(); - await cli.start(); - expect(inquirerMocks.stubs.inquirer.inquire.callCount).toEqual(0); - }); - - it("should silent core tracer", () => { - expect(coreInstance.settings.set.getCall(0).args).toEqual(["log", "silent"]); - }); - - it("should display inquirer", () => { - expect(inquirerMocks.stubs.inquirer.inquire.callCount).toEqual(1); - }); - }); - - describe('when user selects "Change current behavior"', () => { - const fooSelectedBehavior = "foo behavior"; - beforeEach(() => { - inquirerMocks.stubs.inquirer.inquire.onCall(0).resolves("behavior"); - inquirerMocks.stubs.inquirer.inquire.onCall(1).resolves(fooSelectedBehavior); - }); - - it("should call to clear screen", async () => { - await cli.start(); - expect(inquirerMocks.stubs.inquirer.clearScreen.callCount).toEqual(3); - }); - - it("should call to display behavior menu", async () => { - await cli.start(); - expect(inquirerMocks.stubs.inquirer.inquire.getCall(1).args[0]).toEqual("behavior"); - }); - - it("should set current selected behavior", async () => { - await cli.start(); - expect(coreInstance.settings.set.getCall(1).args).toEqual(["behavior", fooSelectedBehavior]); - }); - - it("should not filter current behaviors if there is no input", async () => { - const fooBehaviorsNames = ["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.behaviors.names = fooBehaviorsNames; - await cli._changeCurrentBehavior(); - expect(coreInstance.settings.set.getCall(0).args).toEqual(["behavior", fooBehaviorsNames]); - }); - - it("should not filter current features if current input is empty", async () => { - const fooBehaviorsNames = ["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.behaviors.names = fooBehaviorsNames; - await cli._changeCurrentBehavior(); - expect(coreInstance.settings.set.getCall(0).args).toEqual(["behavior", fooBehaviorsNames]); - }); - - it("should filter current behaviors and returns all that includes current input", async () => { - const fooBehaviorsNames = ["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.behaviors.names = fooBehaviorsNames; - await cli._changeCurrentBehavior(); - expect(coreInstance.settings.set.getCall(0).args).toEqual(["behavior", ["foo1", "foo2"]]); - }); - }); - - describe('when user selects "Change Delay"', () => { - const fooDelay = 2000; - beforeEach(() => { - inquirerMocks.stubs.inquirer.inquire.onCall(0).resolves("delay"); - inquirerMocks.stubs.inquirer.inquire.onCall(1).resolves(fooDelay); - }); - - it("should call to clear screen", async () => { - await cli.start(); - expect(inquirerMocks.stubs.inquirer.clearScreen.callCount).toEqual(3); - }); - - it("should call to display delay menu", async () => { - await cli.start(); - expect(inquirerMocks.stubs.inquirer.inquire.getCall(1).args[0]).toEqual("delay"); - }); - - it("should set current selected feature", async () => { - await cli.start(); - expect(coreInstance.settings.set.getCall(1).args).toEqual(["delay", fooDelay]); - }); - - 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); - }); - - it("should pass delay validation if user introduce numeric characters", async () => { - expect(cli._questions.delay.validate(cli._questions.delay.filter("123230"))).toEqual(true); - }); - }); - - describe('when user selects "Restart server"', () => { - beforeEach(() => { - inquirerMocks.stubs.inquirer.inquire.onCall(0).resolves("restart"); - }); - - it("should call to restart server", async () => { - await cli.start(); - expect(coreInstance.restart.callCount).toEqual(1); - }); - }); - - describe('when user selects "Change log level"', () => { - const fooLogLevel = "foo-level"; - - beforeEach(() => { - inquirerMocks.stubs.inquirer.inquire.onCall(0).resolves("logLevel"); - inquirerMocks.stubs.inquirer.inquire.onCall(1).resolves(fooLogLevel); - }); - - it("should call to display log level menu", async () => { - await cli.start(); - expect(inquirerMocks.stubs.inquirer.inquire.getCall(1).args[0]).toEqual("logLevel"); - }); - - it("should set current log level with the result of log level question", async () => { - await cli.start(); - expect(cli._logLevel).toEqual(fooLogLevel); - }); - }); - - describe('when user selects "Switch watch"', () => { - beforeEach(() => { - inquirerMocks.stubs.inquirer.inquire.onCall(0).resolves("watch"); - }); - - it("should call to switchWatch server method, passing true if it was disabled", async () => { - coreInstance.settings.get.withArgs("watch").returns(false); - await cli.start(); - expect(coreInstance.settings.set.getCall(1).args).toEqual(["watch", true]); - }); - - it("should call to switchWatch server method, passing false if it was enabled", async () => { - coreInstance.settings.get.withArgs("watch").returns(true); - await cli.start(); - expect(coreInstance.settings.set.getCall(1).args).toEqual(["watch", false]); - }); - }); - - describe('when user selects "Display server logs"', () => { - beforeEach(() => { - inquirerMocks.stubs.inquirer.inquire.onCall(0).resolves("logs"); - }); - - it("should call to logsMode CLI method", async () => { - await cli.start(); - expect(inquirerMocks.stubs.inquirer.logsMode.callCount).toEqual(1); - }); - - it("should call to set current log level after logs mode is enabled", async () => { - const fooLogLevel = "foo-log-level"; - coreMocks.reset(); - cli = new Cli(coreInstance); - coreInstance.settings.get.withArgs("cli").returns(true); - coreInstance.settings.get.withArgs("log").returns(fooLogLevel); - await cli.init(); - inquirerMocks.stubs.inquirer.logsMode.executeCb(true); - await cli.start(); - expect(coreInstance.settings.set.getCall(1).args).toEqual(["log", fooLogLevel]); - }); - }); - - describe("when printing header", () => { - it("should print it as first element if server has an error", async () => { - const fooServerErrorMessage = "foo server error"; - const fooServerError = new Error(fooServerErrorMessage); - coreInstance.serverError = fooServerError; - await cli.start(); - expect(cli._header()[0]).toEqual(expect.stringContaining(fooServerErrorMessage)); - }); - - it("should print server url as first element if server has not an error", async () => { - coreInstance.serverError = null; - await cli.start(); - expect(cli._header()[0]).toEqual(expect.stringContaining("Mocks server listening")); - }); - }); - - describe("when server emits load:mocks event watch has reloaded the features", () => { - beforeEach(async () => { - await cli.start(); - coreInstance.onLoadMocks.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(1); - }); - }); - - describe("stopListeningServerWatch method", () => { - it("should remove load:mocks listener if cli has been started", async () => { - const spy = sandbox.spy(); - coreInstance.onLoadMocks.returns(spy); - await cli.start(); - cli.stopListeningServerWatch(); - expect(spy.callCount).toEqual(1); - }); - - it("should not remove load:mocks listener if cli has not been started", () => { - const spy = sandbox.spy(); - coreInstance.onLoadMocks.returns(spy); - cli.stopListeningServerWatch(); - expect(spy.callCount).toEqual(0); - }); - }); -}); diff --git a/test/unit/cli/Inquirer.mocks.js b/test/unit/cli/Inquirer.mocks.js deleted file mode 100644 index 17c010a..0000000 --- a/test/unit/cli/Inquirer.mocks.js +++ /dev/null @@ -1,82 +0,0 @@ -/* -Copyright 2019 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 - -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 sinon = require("sinon"); - -const inquirer = require("../../../lib/cli/Inquirer"); - -class CallBackRunner { - constructor() { - this.runner = this.runner.bind(this); - this.executeCb = this.executeCb.bind(this); - this._execute = false; - this._returns = null; - } - - runner(eventName, cb) { - if (this._execute) { - if (cb) { - if (cb.source) { - return cb.source(null, this._returns); - } - return cb(); - } - return eventName(); - } - } - - executeCb(execute) { - this._execute = execute; - } - - returns(data) { - this._returns = data; - } -} - -class Mock { - constructor() { - this._sandbox = sinon.createSandbox(); - - const logsModeFake = new CallBackRunner(); - const logsModeStub = this._sandbox.stub().callsFake(logsModeFake.runner); - logsModeStub.executeCb = logsModeFake.executeCb; - - const inquireFake = new CallBackRunner(); - - this._stubs = { - inquirer: { - removeListeners: this._sandbox.stub(), - exitLogsMode: this._sandbox.stub(), - clearScreen: this._sandbox.stub(), - inquire: this._sandbox.stub().resolves(), - inquireFake: inquireFake, - logsMode: logsModeStub - } - }; - - this._stubs.Inquirer = this._sandbox.stub(inquirer, "Inquirer").returns(this._stubs.inquirer); - } - - get stubs() { - return this._stubs; - } - - restore() { - this._sandbox.restore(); - } - - reset() { - this._sandbox.reset(); - } -} - -module.exports = Mock; diff --git a/test/unit/cli/Inquirer.spec.js b/test/unit/cli/Inquirer.spec.js deleted file mode 100644 index 0ef205d..0000000 --- a/test/unit/cli/Inquirer.spec.js +++ /dev/null @@ -1,275 +0,0 @@ -/* -Copyright 2019 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 - -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 inquirer = require("inquirer"); -const sinon = require("sinon"); -const chalk = require("chalk"); - -const Inquirer = require("../../../lib/cli/Inquirer").Inquirer; - -const fooQuestions = { - main: { - type: "list", - message: "Select action:", - name: "value", - choices: [ - { - name: "Option 1", - value: "option1" - } - ] - } -}; - -describe("Inquirer", () => { - let sandbox; - - beforeEach(() => { - sandbox = sinon.createSandbox(); - }); - - afterEach(() => { - sandbox.restore(); - }); - - describe("when initing questions", () => { - it("should add an extra exit option to main menu", () => { - const cli = new Inquirer(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); - expect(cli._questions.notMain.choices.length).toEqual(1); - }); - - describe("when quitMethod option is provided", () => { - it("should add it as an extra option to main menu choices", () => { - const fooQuitMethodName = "foo name"; - const cli = new Inquirer(fooQuestions, null, { - name: fooQuitMethodName, - action: () => {} - }); - expect(cli._questions.main.choices[2].name).toEqual(fooQuitMethodName); - }); - }); - }); - - describe("quit method", () => { - it("should call to exit process", () => { - sandbox.stub(process, "exit"); - const cli = new Inquirer(fooQuestions); - cli.quit(); - expect(process.exit.callCount).toEqual(1); - }); - - describe("when quitMethod option is provided", () => { - it("should call to provided quitMethod action", () => { - const fooQuitMethodAction = sandbox.spy(); - const cli = new Inquirer(fooQuestions, null, { - name: "foo name", - action: fooQuitMethodAction - }); - cli.quit(); - expect(fooQuitMethodAction.callCount).toEqual(1); - }); - }); - }); - - describe("clearScreen method", () => { - it("should write Clear screen characters in process stdout", () => { - sandbox.stub(process.stdout, "write"); - const cli = new Inquirer(fooQuestions); - cli.clearScreen(); - expect(process.stdout.write.calledWith("\x1Bc")).toEqual(true); - }); - - it("should print all strings returned by header method provided to constructor", () => { - const fooHeader = "foo header"; - sandbox.stub(process.stdout, "write"); - sandbox.stub(console, "log"); - const cli = new Inquirer(fooQuestions, () => [fooHeader]); - cli.clearScreen(); - expect(console.log.getCall(0).args[0]).toEqual(expect.stringContaining(fooHeader)); - }); - - it("should not print header if header option is set to false", () => { - const fooHeader = "foo header"; - sandbox.stub(process.stdout, "write"); - sandbox.stub(console, "log"); - const cli = new Inquirer(fooQuestions, () => [fooHeader]); - cli.clearScreen({ - header: false - }); - expect(console.log.getCalls().length).toEqual(0); - }); - }); - - describe("inquire method", () => { - it("should return the inquirer returned value", async () => { - expect.assertions(1); - const fooValue = "foo-value"; - sandbox - .stub(inquirer, "prompt") - .usingPromise() - .resolves({ value: fooValue }); - const cli = new Inquirer(fooQuestions); - expect(await cli.inquire("main")).toEqual(fooValue); - }); - - 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); - await cli.inquire("main"); - expect(inquirer.prompt.getCall(0).args[0].message).toEqual("Select action:"); - }); - - 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", () => {}); - sandbox.stub(process.stdin, "removeListener"); - await cli.inquire("main"); - expect(process.stdin.removeListener.getCall(0).args[0]).toEqual("keypress"); - }); - - it('should call to quit method if inquired question is "main" and answer value is "quit"', async () => { - expect.assertions(1); - sandbox.stub(process, "exit"); - sandbox - .stub(inquirer, "prompt") - .usingPromise() - .resolves({ value: "quit" }); - const cli = new Inquirer(fooQuestions); - await cli.inquire("main"); - expect(process.exit.callCount).toEqual(1); - }); - }); - - describe("logs mode method", () => { - let fooKeyPresser; - let fakeRawMode = false; - - const FooKeyPresser = class FooKeyPresser { - constructor(key = "a") { - this._key = key; - this.handler = this.handler.bind(this); - } - - handler(eventName, callBack) { - this._callBack = callBack; - setTimeout(() => { - callBack(this._key); - }, 200); - } - - set key(keyToPress) { - this._key = keyToPress; - } - }; - - beforeEach(() => { - fooKeyPresser = new FooKeyPresser(); - sandbox.stub(process, "exit"); - sandbox.stub(process.stdin, "resume"); - sandbox.stub(process.stdin, "removeListener"); - sandbox.stub(process.stdin, "setEncoding"); - sandbox.stub(process.stdout, "write"); - sandbox.stub(process.stdin, "on").callsFake(fooKeyPresser.handler); - sandbox.spy(console, "log"); - }); - - afterEach(() => { - if (fakeRawMode) { - delete process.stdin.setRawMode; - } - }); - - it("should call to clear Screen", async () => { - const cli = new Inquirer(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); - await cli.logsMode(); - expect( - console.log.calledWith( - chalk.blue("Displaying logs. Press any key to display main menu again") - ) - ).toEqual(true); - }); - - it("should call to provided callback", async () => { - const cli = new Inquirer(fooQuestions); - const fooCallBack = sinon.spy(); - await cli.logsMode(fooCallBack); - expect(fooCallBack.callCount).toEqual(1); - }); - - it("should call to exit process if pressed key is equal to CTRL+C", async () => { - fooKeyPresser.key = "\u0003"; - const cli = new Inquirer(fooQuestions); - await cli.logsMode(); - expect(process.exit.callCount).toEqual(1); - }); - - it("should not call to setRawMode if process has it available", async () => { - if (process.stdin.setRawMode) { - sandbox.stub(process.stdin, "setRawMode"); - } else { - fakeRawMode = true; - process.stdin.setRawMode = sandbox.stub(); - } - const cli = new Inquirer(fooQuestions); - await cli.logsMode(); - expect(process.stdin.setRawMode.callCount).toEqual(2); - }); - - it("should not call to setRawMode if process has not it available", async () => { - if (process.stdin.setRawMode) { - sandbox.stub(process.stdin, "setRawMode"); - } else { - fakeRawMode = true; - process.stdin.setRawMode = sandbox.stub(); - } - const originalStdin = process.stdin; - process.stdin.setRawMode = false; - const cli = new Inquirer(fooQuestions); - await cli.logsMode(); - process.stdin = originalStdin; - expect(process.stdin.setRawMode.callCount).toEqual(undefined); - }); - }); - - describe("exitLogsMode method", () => { - beforeEach(() => { - sandbox.stub(process.stdin, "removeListener"); - }); - - it("should do nothing if logs mode is not currently enabled", async () => { - const cli = new Inquirer(fooQuestions); - await cli.exitLogsMode(); - expect(process.stdin.removeListener.callCount).toEqual(0); - }); - }); -}); diff --git a/test/unit/core/Core.spec.js b/test/unit/core/Core.spec.js deleted file mode 100644 index b26d6cd..0000000 --- a/test/unit/core/Core.spec.js +++ /dev/null @@ -1,223 +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 sinon = require("sinon"); - -const SettingsMocks = require("./settings/Settings.mocks.js"); -const MocksMocks = require("./mocks/Mocks.mocks.js"); -const ServerMocks = require("./server/Server.mocks.js"); -const PluginsMocks = require("./Plugins.mocks.js"); - -const Core = require("../../../lib/core/Core"); -const tracer = require("../../../lib/core/tracer"); - -describe("Settings", () => { - let sandbox; - let settingsMocks; - let settingsInstance; - let mocksMocks; - let mocksInstance; - let serverMocks; - let serverInstance; - let pluginsMocks; - let pluginsInstance; - let core; - - beforeEach(async () => { - sandbox = sinon.createSandbox(); - settingsMocks = new SettingsMocks(); - settingsInstance = settingsMocks.stubs.instance; - mocksMocks = new MocksMocks(); - mocksInstance = mocksMocks.stubs.instance; - serverMocks = new ServerMocks(); - serverInstance = serverMocks.stubs.instance; - pluginsMocks = new PluginsMocks(); - pluginsInstance = pluginsMocks.stubs.instance; - - core = new Core(); - await core.init(); - }); - - afterEach(() => { - sandbox.restore(); - settingsMocks.restore(); - mocksMocks.restore(); - serverMocks.restore(); - pluginsMocks.restore(); - }); - - describe("init method", () => { - it("should init only once", async () => { - await core.init(); - await core.init(); - expect(pluginsInstance.register.callCount).toEqual(1); - }); - - it("should register plugins", () => { - expect(pluginsInstance.register.callCount).toEqual(1); - }); - - it("should init settings with received options", async () => { - const fooOptions = { - foo: "foo" - }; - core = new Core(); - await core.init(fooOptions); - expect(settingsInstance.init.calledWith(fooOptions)).toEqual(true); - }); - - it("should init mocks", () => { - expect(mocksInstance.init.callCount).toEqual(1); - }); - - it("should init server", () => { - expect(serverInstance.init.callCount).toEqual(1); - }); - - it("should init plugins", () => { - expect(pluginsInstance.init.callCount).toEqual(1); - }); - }); - - describe("start method", () => { - it("should init if it has not been done before", async () => { - pluginsMocks.reset(); - core = new Core(); - await core.start(); - expect(pluginsInstance.register.callCount).toEqual(1); - }); - - it("should not init if it has been done before", async () => { - await core.start(); - expect(pluginsInstance.register.callCount).toEqual(1); - }); - - it("should start mocks", async () => { - await core.start(); - expect(mocksInstance.start.callCount).toEqual(1); - }); - - it("should start server", async () => { - await core.start(); - expect(serverInstance.start.callCount).toEqual(1); - }); - - it("should start plugins", async () => { - await core.start(); - expect(pluginsInstance.start.callCount).toEqual(1); - }); - - it("should start plugins only once", async () => { - core.start(); - core.start(); - core.start(); - await core.start(); - expect(pluginsInstance.start.callCount).toEqual(1); - }); - }); - - describe("addCustomRouter method", () => { - it("should add custom router to server", () => { - core.addCustomRouter(); - expect(serverInstance.addCustomRouter.callCount).toEqual(1); - }); - }); - - describe("addCustomSetting method", () => { - it("should add custom setting to settings", () => { - core.addCustomSetting(); - expect(settingsInstance.addCustom.callCount).toEqual(1); - }); - }); - - describe("onLoadMocks method", () => { - it("should add listener to eventEmitter", () => { - const spy = sandbox.spy(); - core.onLoadMocks(spy); - core._eventEmitter.emit("load:mocks"); - expect(spy.callCount).toEqual(1); - }); - - it("should return a function to remove listener", () => { - expect.assertions(2); - const spy = sandbox.spy(); - const removeCallback = core.onLoadMocks(spy); - core._eventEmitter.emit("load:mocks"); - expect(spy.callCount).toEqual(1); - removeCallback(); - core._eventEmitter.emit("load:mocks"); - expect(spy.callCount).toEqual(1); - }); - }); - - describe("onChangeSettings method", () => { - it("should add listener to eventEmitter", () => { - const spy = sandbox.spy(); - core.onChangeSettings(spy); - core._eventEmitter.emit("change:settings"); - expect(spy.callCount).toEqual(1); - }); - - it("should return a function to remove listener", () => { - expect.assertions(2); - const spy = sandbox.spy(); - const removeCallback = core.onChangeSettings(spy); - core._eventEmitter.emit("change:settings"); - expect(spy.callCount).toEqual(1); - removeCallback(); - core._eventEmitter.emit("change:settings"); - expect(spy.callCount).toEqual(1); - }); - }); - - describe("stop method", () => { - it("should stop server", async () => { - await core.stop(); - expect(serverInstance.stop.callCount).toEqual(1); - }); - }); - - describe("restart method", () => { - it("should restart server", async () => { - await core.restart(); - expect(serverInstance.restart.callCount).toEqual(1); - }); - }); - - describe("tracer getter", () => { - it("should return tracer instance", () => { - expect(core.tracer).toEqual(tracer); - }); - }); - - describe("serverError getter", () => { - it("should return server error", () => { - expect(core.serverError).toEqual(serverInstance.error); - }); - }); - - describe("settings getter", () => { - it("should return settings", () => { - expect(core.settings).toEqual(settingsInstance); - }); - }); - - describe("behaviors getter", () => { - it("should return mocks behaviors", () => { - expect(core.behaviors).toEqual(mocksInstance.behaviors); - }); - }); - - describe("features getter", () => { - it("should return mocks behaviors", () => { - expect(core.features).toEqual(mocksInstance.behaviors); - }); - }); -}); diff --git a/test/unit/core/Plugins.mocks.js b/test/unit/core/Plugins.mocks.js deleted file mode 100644 index 2ec9255..0000000 --- a/test/unit/core/Plugins.mocks.js +++ /dev/null @@ -1,47 +0,0 @@ -/* -Copyright 2019 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 - -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 sinon = require("sinon"); - -jest.mock("../../../lib/core/Plugins"); - -const Plugins = require("../../../lib/core/Plugins"); - -class Mock { - constructor() { - this._sandbox = sinon.createSandbox(); - - this._stubs = { - init: this._sandbox.stub(), - register: this._sandbox.stub(), - start: this._sandbox.stub().resolves() - }; - - Plugins.mockImplementation(() => this._stubs); - } - - get stubs() { - return { - Constructor: Plugins, - instance: this._stubs - }; - } - - restore() { - this._sandbox.restore(); - } - - reset() { - this._sandbox.reset(); - } -} - -module.exports = Mock; diff --git a/test/unit/core/Plugins.spec.js b/test/unit/core/Plugins.spec.js deleted file mode 100644 index fdf9d1a..0000000 --- a/test/unit/core/Plugins.spec.js +++ /dev/null @@ -1,265 +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 sinon = require("sinon"); - -const CoreMocks = require("./Core.mocks.js"); - -const Plugins = require("../../../lib/core/Plugins"); -const tracer = require("../../../lib/core/tracer"); - -describe("Settings", () => { - let sandbox; - let coreMocks; - let coreInstance; - let plugins; - - beforeEach(async () => { - sandbox = sinon.createSandbox(); - sandbox.stub(tracer, "verbose"); - sandbox.stub(tracer, "debug"); - sandbox.stub(tracer, "error"); - sandbox.spy(console, "log"); - coreMocks = new CoreMocks(); - coreInstance = coreMocks.stubs.instance; - }); - - afterEach(() => { - sandbox.restore(); - coreMocks.restore(); - }); - - describe("register method", () => { - it("should do nothing if there are no plugins to register", async () => { - plugins = new Plugins(null, coreInstance); - await plugins.register(); - expect(tracer.verbose.calledWith("Registered 0 plugins")).toEqual(true); - }); - - it("should register object plugins", async () => { - const fooPlugin = {}; - plugins = new Plugins([fooPlugin], coreInstance); - await plugins.register(); - expect(tracer.verbose.calledWith("Registered 1 plugins")).toEqual(true); - }); - - it("should not register strings as plugins", async () => { - expect.assertions(2); - plugins = new Plugins(["foo"], coreInstance); - await plugins.register(); - expect(console.log.calledWith("Error registering plugin")).toEqual(true); - expect(tracer.verbose.calledWith("Registered 0 plugins")).toEqual(true); - }); - - it("should not register booleans as plugins", async () => { - expect.assertions(2); - plugins = new Plugins([true], coreInstance); - await plugins.register(); - expect(console.log.calledWith("Error registering plugin")).toEqual(true); - expect(tracer.verbose.calledWith("Registered 0 plugins")).toEqual(true); - }); - - it("should register function plugins executing them passing the core", async () => { - expect.assertions(3); - const fooPlugin = sinon.spy(); - plugins = new Plugins([fooPlugin], coreInstance); - await plugins.register(); - expect(fooPlugin.calledWith(coreInstance)).toEqual(true); - expect(fooPlugin.callCount).toEqual(1); - expect(tracer.verbose.calledWith("Registered 1 plugins")).toEqual(true); - }); - - it("should register class plugins, instantiating them passing the core", async () => { - expect.assertions(3); - let instantiated = false; - let receivedCore; - class FooPlugin { - constructor(core) { - console.log("Created class"); - receivedCore = core; - instantiated = true; - } - } - plugins = new Plugins([FooPlugin], coreInstance); - await plugins.register(); - expect(receivedCore).toEqual(coreInstance); - expect(instantiated).toEqual(true); - expect(tracer.verbose.calledWith("Registered 1 plugins")).toEqual(true); - }); - - it("should not register class plugins if class throw an error when being created", async () => { - expect.assertions(2); - class FooPlugin { - constructor() { - throw new Error(); - } - } - plugins = new Plugins([FooPlugin], coreInstance); - await plugins.register(); - expect(console.log.calledWith("Error registering plugin")).toEqual(true); - expect(tracer.verbose.calledWith("Registered 0 plugins")).toEqual(true); - }); - - it("should trace the total number of registered plugins", async () => { - expect.assertions(2); - class FooPlugin { - constructor() { - throw new Error(); - } - } - class FooPlugin2 {} - plugins = new Plugins( - [FooPlugin, FooPlugin2, () => {}, true, false, "foo", { foo: "foo" }], - coreInstance - ); - await plugins.register(); - expect(console.log.calledWith("Error registering plugin")).toEqual(true); - expect(tracer.verbose.calledWith("Registered 3 plugins")).toEqual(true); - }); - }); - - describe("init method", () => { - it("should do nothing if there are no plugins to register", async () => { - plugins = new Plugins(null, coreInstance); - await plugins.register(); - await plugins.init(); - expect(tracer.verbose.calledWith("Initializated 0 plugins")).toEqual(true); - }); - - it("should init object plugins with an init property", async () => { - expect.assertions(2); - const fooPlugin = { - init: sinon.spy() - }; - plugins = new Plugins([fooPlugin], coreInstance); - await plugins.register(); - await plugins.init(); - expect(fooPlugin.init.callCount).toEqual(1); - expect(tracer.verbose.calledWith("Initializated 1 plugins")).toEqual(true); - }); - - it("should accept init methods non returning a Promise", async () => { - expect.assertions(1); - const fooPlugin = { - init: () => true - }; - const fooPlugin2 = { - init: () => Promise.resolve() - }; - plugins = new Plugins([fooPlugin, fooPlugin2], coreInstance); - await plugins.register(); - await plugins.init(); - expect(tracer.verbose.calledWith("Initializated 2 plugins")).toEqual(true); - }); - - it("should catch init method errors", async () => { - expect.assertions(1); - const fooPlugin = { - init: () => { - throw new Error(); - } - }; - const fooPlugin2 = { - init: () => Promise.resolve() - }; - const fooPlugin3 = { - init: () => Promise.resolve() - }; - plugins = new Plugins([fooPlugin, fooPlugin2, fooPlugin3], coreInstance); - await plugins.register(); - await plugins.init(); - expect(tracer.verbose.calledWith("Initializated 2 plugins")).toEqual(true); - }); - - it("should accept plugins with no init method", async () => { - expect.assertions(1); - const fooPlugin = {}; - const fooPlugin2 = { - init: () => Promise.resolve() - }; - const fooPlugin3 = { - init: () => Promise.resolve() - }; - plugins = new Plugins([fooPlugin, fooPlugin2, fooPlugin3], coreInstance); - await plugins.register(); - await plugins.init(); - expect(tracer.verbose.calledWith("Initializated 2 plugins")).toEqual(true); - }); - }); - - describe("start method", () => { - it("should do nothing if there are no plugins to register", async () => { - plugins = new Plugins(null, coreInstance); - await plugins.register(); - await plugins.start(); - expect(tracer.verbose.calledWith("Started 0 plugins")).toEqual(true); - }); - - it("should start object plugins with an init property", async () => { - expect.assertions(2); - const fooPlugin = { - start: sinon.spy() - }; - plugins = new Plugins([fooPlugin], coreInstance); - await plugins.register(); - await plugins.start(); - expect(fooPlugin.start.callCount).toEqual(1); - expect(tracer.verbose.calledWith("Started 1 plugins")).toEqual(true); - }); - - it("should accept start methods non returning a Promise", async () => { - expect.assertions(1); - const fooPlugin = { - start: () => true - }; - const fooPlugin2 = { - start: () => Promise.resolve() - }; - plugins = new Plugins([fooPlugin, fooPlugin2], coreInstance); - await plugins.register(); - await plugins.start(); - expect(tracer.verbose.calledWith("Started 2 plugins")).toEqual(true); - }); - - it("should catch start method errors", async () => { - expect.assertions(1); - const fooPlugin = { - start: () => { - throw new Error(); - } - }; - const fooPlugin2 = { - start: () => Promise.resolve() - }; - const fooPlugin3 = { - start: () => Promise.resolve() - }; - plugins = new Plugins([fooPlugin, fooPlugin2, fooPlugin3], coreInstance); - await plugins.register(); - await plugins.start(); - expect(tracer.verbose.calledWith("Started 2 plugins")).toEqual(true); - }); - - it("should accept plugins with no start method", async () => { - expect.assertions(1); - const fooPlugin = {}; - const fooPlugin2 = { - start: () => Promise.resolve() - }; - const fooPlugin3 = { - start: () => Promise.resolve() - }; - plugins = new Plugins([fooPlugin, fooPlugin2, fooPlugin3], coreInstance); - await plugins.register(); - await plugins.start(); - expect(tracer.verbose.calledWith("Started 2 plugins")).toEqual(true); - }); - }); -}); diff --git a/test/unit/core/mocks/Behavior.spec.js b/test/unit/core/mocks/Behavior.spec.js deleted file mode 100644 index 47a52d2..0000000 --- a/test/unit/core/mocks/Behavior.spec.js +++ /dev/null @@ -1,304 +0,0 @@ -/* -Copyright 2019 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 - -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. -*/ - -jest.mock("route-parser"); - -const routeParser = require("route-parser"); - -const Behavior = require("../../../../lib/core/mocks/Behavior"); - -describe("Behavior", () => { - const fooFunctionResponse = () => {}; - const fooBehaviorData = [ - { - url: "/api/foo/foo-uri", - method: "GET", - response: { - status: 200, - body: { - fooProperty: "foo" - } - } - }, - { - url: "/api/foo/foo-uri-2", - method: "POST", - response: { - status: 422, - body: { - fooProperty2: "foo2" - } - } - }, - { - url: "/api/foo/foo-uri-3", - method: "POST", - response: fooFunctionResponse - } - ]; - - const fooExtendedData = [ - { - url: "/api/foo/foo-uri-2", - method: "POST", - response: { - status: 500, - body: { - fooPropertyExtended: "foo-extended" - } - } - }, - { - url: "/api/foo/foo-uri-4", - method: "POST", - response: { - status: 200, - body: {} - } - } - ]; - - beforeEach(() => { - routeParser.mockImplementation(() => "foo-route-parser"); - expect.assertions(1); - }); - - describe("methods method", () => { - it("should return an object containing provided fixtures grouped by methods", () => { - const behavior = new Behavior(fooBehaviorData); - expect(behavior.methods).toEqual({ - GET: { - "/api/foo/foo-uri": { - route: "foo-route-parser", - response: { - status: 200, - body: { - fooProperty: "foo" - } - } - } - }, - POST: { - "/api/foo/foo-uri-2": { - route: "foo-route-parser", - response: { - status: 422, - body: { - fooProperty2: "foo2" - } - } - }, - "/api/foo/foo-uri-3": { - route: "foo-route-parser", - response: fooFunctionResponse - } - } - }); - }); - }); - - describe("fixtures method", () => { - it("should return an array containing provided fixtures", () => { - const behavior = new Behavior(fooBehaviorData); - expect(behavior.fixtures).toEqual([ - { - url: "/api/foo/foo-uri", - method: "GET", - response: { - status: 200, - body: { - fooProperty: "foo" - } - } - }, - { - url: "/api/foo/foo-uri-2", - method: "POST", - response: { - status: 422, - body: { - fooProperty2: "foo2" - } - } - }, - { - url: "/api/foo/foo-uri-3", - method: "POST", - response: "function" - } - ]); - }); - }); - - describe("totalFixtures method", () => { - it("should return the total number of fixtures", () => { - const behavior = new Behavior(fooBehaviorData); - expect(behavior.totalFixtures).toEqual(3); - }); - }); - - describe("extend method", () => { - it("should leave original Behavior methods without modification", () => { - const behavior = new Behavior(fooBehaviorData); - behavior.extend(fooExtendedData); - expect(behavior.methods).toEqual({ - GET: { - "/api/foo/foo-uri": { - route: "foo-route-parser", - response: { - status: 200, - body: { - fooProperty: "foo" - } - } - } - }, - POST: { - "/api/foo/foo-uri-2": { - route: "foo-route-parser", - response: { - status: 422, - body: { - fooProperty2: "foo2" - } - } - }, - "/api/foo/foo-uri-3": { - route: "foo-route-parser", - response: fooFunctionResponse - } - } - }); - }); - - it("should leave original Behavior fixtures without modification", () => { - const behavior = new Behavior(fooBehaviorData); - behavior.extend(fooExtendedData); - expect(behavior.fixtures).toEqual([ - { - url: "/api/foo/foo-uri", - method: "GET", - response: { - status: 200, - body: { - fooProperty: "foo" - } - } - }, - { - url: "/api/foo/foo-uri-2", - method: "POST", - response: { - status: 422, - body: { - fooProperty2: "foo2" - } - } - }, - { - url: "/api/foo/foo-uri-3", - method: "POST", - response: "function" - } - ]); - }); - - it("should return a new Behavior which methods will be an extension from current", () => { - const behavior = new Behavior(fooBehaviorData); - const extendedBehavior = behavior.extend(fooExtendedData); - expect(extendedBehavior.methods).toEqual({ - GET: { - "/api/foo/foo-uri": { - route: "foo-route-parser", - response: { - status: 200, - body: { - fooProperty: "foo" - } - } - } - }, - POST: { - "/api/foo/foo-uri-2": { - route: "foo-route-parser", - response: { - status: 500, - body: { - fooPropertyExtended: "foo-extended" - } - } - }, - "/api/foo/foo-uri-3": { - route: "foo-route-parser", - response: fooFunctionResponse - }, - "/api/foo/foo-uri-4": { - route: "foo-route-parser", - response: { - status: 200, - body: {} - } - } - } - }); - }); - - it("should return a new Behavior which fixtures will be an extension from current", () => { - const behavior = new Behavior(fooBehaviorData); - const extendedBehavior = behavior.extend(fooExtendedData); - expect(extendedBehavior.fixtures).toEqual([ - { - url: "/api/foo/foo-uri", - method: "GET", - response: { - status: 200, - body: { - fooProperty: "foo" - } - } - }, - { - url: "/api/foo/foo-uri-2", - method: "POST", - response: { - status: 422, - body: { - fooProperty2: "foo2" - } - } - }, - { - url: "/api/foo/foo-uri-3", - method: "POST", - response: "function" - }, - { - url: "/api/foo/foo-uri-2", - method: "POST", - response: { - status: 500, - body: { - fooPropertyExtended: "foo-extended" - } - } - }, - { - url: "/api/foo/foo-uri-4", - method: "POST", - response: { - status: 200, - body: {} - } - } - ]); - }); - }); -}); diff --git a/test/unit/core/mocks/Behaviors.mocks.js b/test/unit/core/mocks/Behaviors.mocks.js deleted file mode 100644 index db83e41..0000000 --- a/test/unit/core/mocks/Behaviors.mocks.js +++ /dev/null @@ -1,44 +0,0 @@ -/* -Copyright 2019 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 - -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 sinon = require("sinon"); - -jest.mock("../../../../lib/core/mocks/Behaviors"); - -const Behaviors = require("../../../../lib/core/mocks/Behaviors"); - -class Mock { - constructor() { - this._sandbox = sinon.createSandbox(); - - this._stubs = { - currentName: "foo-current-name", - current: {}, - init: this._sandbox.stub().resolves(), - start: this._sandbox.stub().resolves() - }; - - Behaviors.mockImplementation(() => this._stubs); - } - - get stubs() { - return { - Constructor: Behaviors, - instance: this._stubs - }; - } - - restore() { - this._sandbox.restore(); - } -} - -module.exports = Mock; diff --git a/test/unit/core/mocks/Behaviors.spec.js b/test/unit/core/mocks/Behaviors.spec.js deleted file mode 100644 index 876a696..0000000 --- a/test/unit/core/mocks/Behaviors.spec.js +++ /dev/null @@ -1,254 +0,0 @@ -/* -Copyright 2019 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 - -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 sinon = require("sinon"); -const Boom = require("boom"); - -const FilesHandlerMocks = require("./FilesHandler.mocks.js"); -const CoreMocks = require("../Core.mocks.js"); - -const Behaviors = require("../../../../lib/core/mocks/Behaviors"); -const tracer = require("../../../../lib/core/tracer"); - -describe("Behaviors", () => { - const fooBoomError = new Error("foo boom error"); - let sandbox; - let coreMocks; - let coreInstance; - let filesHandlerMock; - let filesHandlerInstance; - let behaviors; - - beforeEach(() => { - sandbox = sinon.createSandbox(); - sandbox.stub(Boom, "badData").returns(fooBoomError); - filesHandlerMock = new FilesHandlerMocks(); - filesHandlerInstance = filesHandlerMock.stubs.instance; - coreMocks = new CoreMocks(); - coreInstance = coreMocks.stubs.instance; - coreInstance.settings.get.withArgs("behavior").returns("behavior1"); - sandbox.stub(tracer, "warn"); - sandbox.stub(tracer, "silly"); - behaviors = new Behaviors( - filesHandlerInstance, - coreInstance.settings, - coreInstance._eventEmitter - ); - }); - - afterEach(() => { - sandbox.restore(); - filesHandlerMock.restore(); - coreMocks.restore(); - }); - - describe("when initializated", () => { - it("should set as current the first one behavior found if no behavior is defined", async () => { - coreInstance.settings.get.withArgs("behavior").returns(undefined); - await behaviors.init(); - expect(behaviors.currentName).toEqual("behavior1"); - }); - - it("should trace an error if selected behavior is not found in behaviors", async () => { - coreInstance.settings.get.withArgs("behavior").returns("foo"); - await behaviors.init(); - expect(tracer.warn.getCall(0).args[0]).toEqual( - expect.stringContaining('Defined behavior "foo" was not found') - ); - }); - }); - - describe("when core emits load:mocks", () => { - it("should process mocks again", async () => { - await behaviors.init(); - coreInstance._eventEmitter.on.getCall(0).args[1](); - expect(tracer.silly.callCount).toEqual(2); - }); - }); - - describe("when core emits a change:settings event", () => { - it("should set new behavior as current one if behavior has changed", async () => { - await behaviors.init(); - coreInstance._eventEmitter.on.getCall(1).args[1]({ - behavior: "behavior2" - }); - expect(behaviors.currentName).toEqual("behavior2"); - }); - - it("should do nothing if behavior has not changed", async () => { - await behaviors.init(); - coreInstance._eventEmitter.on.getCall(1).args[1]({}); - expect(behaviors.currentName).toEqual("behavior1"); - }); - }); - - describe("current setter", () => { - it("should throw an error if behavior to set is not found in behaviors", async () => { - await behaviors.init(); - try { - behaviors.current = "foo"; - } catch (err) { - expect(err).toEqual(fooBoomError); - } - }); - - it("should change the current selected behavior", async () => { - await behaviors.init(); - behaviors.current = "behavior2"; - expect(behaviors.current).toEqual({ - POST: { - "/api/foo/foo-uri-2": { - route: "foo-route-parser", - response: { - status: 422, - body: { - fooProperty2: "foo2" - } - } - } - } - }); - }); - }); - - describe("current getter", () => { - it("should return the current selected behavior", async () => { - await behaviors.init(); - expect(behaviors.current).toEqual({ - POST: { - "/api/foo/foo-uri": { - route: "foo-route-parser", - response: { - status: 200, - body: { - fooProperty: "foo" - } - } - } - } - }); - }); - - it("should return the first behavior if current was not set", async () => { - await behaviors.init(); - expect(behaviors.current).toEqual({ - POST: { - "/api/foo/foo-uri": { - route: "foo-route-parser", - response: { - status: 200, - body: { - fooProperty: "foo" - } - } - } - } - }); - }); - }); - - describe("totalBehaviors getter", () => { - it("should return the number of behaviors", async () => { - await behaviors.init(); - expect(behaviors.totalBehaviors).toEqual(2); - }); - }); - - describe("currentTotalFixtures getter", () => { - it("should return the total number of fixtures of currently selected behavior", async () => { - await behaviors.init(); - expect(behaviors.currentTotalFixtures).toEqual(1); - }); - }); - - describe("currentFromCollection getter", () => { - it("should return the current selected behavior in collection format", async () => { - await behaviors.init(); - expect(behaviors.currentFromCollection).toEqual({ - fixtures: [ - { - method: "GET", - response: { body: { fooProperty: "foo" }, status: 200 }, - url: "/api/foo/foo-uri" - } - ], - name: "behavior1" - }); - }); - }); - - describe("all getter", () => { - it("should return all behaviors", async () => { - await behaviors.init(); - expect(behaviors.all).toEqual({ - behavior1: { - POST: { - "/api/foo/foo-uri": { - response: { body: { fooProperty: "foo" }, status: 200 }, - route: "foo-route-parser" - } - } - }, - behavior2: { - POST: { - "/api/foo/foo-uri-2": { - response: { body: { fooProperty2: "foo2" }, status: 422 }, - route: "foo-route-parser" - } - } - } - }); - }); - }); - - describe("names getter", () => { - it("should return all behaviors names", async () => { - await behaviors.init(); - expect(behaviors.names).toEqual(["behavior1", "behavior2"]); - }); - }); - - describe("currentName getter", () => { - it("should return current behavior name", async () => { - coreInstance.settings.get.withArgs("behavior").returns("behavior2"); - await behaviors.init(); - expect(behaviors.currentName).toEqual("behavior2"); - }); - }); - - describe("collection getter", () => { - it("should return all behaviors in collection format", async () => { - await behaviors.init(); - expect(behaviors.collection).toEqual([ - { - fixtures: [ - { - method: "GET", - response: { body: { fooProperty: "foo" }, status: 200 }, - url: "/api/foo/foo-uri" - } - ], - name: "behavior1" - }, - { - fixtures: [ - { - method: "POST", - response: { body: { fooProperty2: "foo2" }, status: 422 }, - url: "/api/foo/foo-uri-2" - } - ], - name: "behavior2" - } - ]); - }); - }); -}); diff --git a/test/unit/core/mocks/FilesHandler.mocks.js b/test/unit/core/mocks/FilesHandler.mocks.js deleted file mode 100644 index cf906a1..0000000 --- a/test/unit/core/mocks/FilesHandler.mocks.js +++ /dev/null @@ -1,117 +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 sinon = require("sinon"); - -jest.mock("../../../../lib/core/mocks/FilesHandler"); - -const FilesHandler = require("../../../../lib/core/mocks/FilesHandler"); - -const INITIAL_FILES = { - file1: { - behavior1: { - fixtures: [ - { - url: "/api/foo/foo-uri", - method: "GET", - response: { - status: 200, - body: { - fooProperty: "foo" - } - } - } - ], - totalFixtures: 1, - methods: { - POST: { - "/api/foo/foo-uri": { - route: "foo-route-parser", - response: { - status: 200, - body: { - fooProperty: "foo" - } - } - } - } - } - } - }, - file2: { - behavior2: { - fixtures: [ - { - url: "/api/foo/foo-uri-2", - method: "POST", - response: { - status: 422, - body: { - fooProperty2: "foo2" - } - } - } - ], - totalFixtures: 1, - methods: { - POST: { - "/api/foo/foo-uri-2": { - route: "foo-route-parser", - response: { - status: 422, - body: { - fooProperty2: "foo2" - } - } - } - } - } - } - }, - folder: { - folder2: { - file: { - fooProperty: "" - } - } - } -}; - -class Mock { - static get files() { - return INITIAL_FILES; - } - - constructor() { - this._sandbox = sinon.createSandbox(); - - this._stubs = { - files: INITIAL_FILES, - init: this._sandbox.stub().resolves(), - start: this._sandbox.stub().resolves() - }; - - FilesHandler.mockImplementation(() => this._stubs); - } - - get stubs() { - return { - Constructor: FilesHandler, - instance: this._stubs - }; - } - - restore() { - this._stubs.files = INITIAL_FILES; - this._sandbox.restore(); - } -} - -module.exports = Mock; diff --git a/test/unit/core/mocks/FilesHandler.spec.js b/test/unit/core/mocks/FilesHandler.spec.js deleted file mode 100644 index e3182cf..0000000 --- a/test/unit/core/mocks/FilesHandler.spec.js +++ /dev/null @@ -1,311 +0,0 @@ -/* -Copyright 2019 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 - -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 sinon = require("sinon"); -const Boom = require("boom"); -const { cloneDeep } = require("lodash"); - -jest.mock("require-all"); - -const requireAll = require("require-all"); - -const LibsMocks = require("../../Libs.mocks.js"); -const CoreMocks = require("../Core.mocks.js"); - -const FilesHandler = require("../../../../lib/core/mocks/FilesHandler"); -const tracer = require("../../../../lib/core/tracer"); - -const wait = () => { - return new Promise(resolve => { - setTimeout(() => { - resolve(); - }, 1000); - }); -}; - -describe("Behaviors", () => { - const fooRequireCache = { - "foo-path": { - id: "foo-path", - children: { - "foo-path/foo-children": { - id: "foo-path/foo-children" - } - } - }, - "foo-path/foo-children": { - id: "foo-path/foo-children", - children: { - "foo-path/foo-children-2": { - id: "foo-path/foo-children-2" - } - } - }, - "foo-path/foo-children-2": { - id: "foo-path/foo-children-2", - children: { - "foo-children-3": { - id: "foo-children-3" - } - } - }, - "foo-not-children": { - id: "foo-not-children" - } - }; - - const fooFiles = { - file1: { - behavior1: { - fixtures: [ - { - url: "/api/foo/foo-uri", - method: "GET", - response: { - status: 200, - body: { - fooProperty: "foo" - } - } - } - ], - totalFixtures: 1, - methods: { - POST: { - "/api/foo/foo-uri": { - route: "foo-route-parser", - response: { - status: 200, - body: { - fooProperty: "foo" - } - } - } - } - } - } - }, - file2: { - behavior2: { - fixtures: [ - { - url: "/api/foo/foo-uri-2", - method: "POST", - response: { - status: 422, - body: { - fooProperty2: "foo2" - } - } - } - ], - totalFixtures: 1, - methods: { - POST: { - "/api/foo/foo-uri-2": { - route: "foo-route-parser", - response: { - status: 422, - body: { - fooProperty2: "foo2" - } - } - } - } - } - } - }, - folder: { - folder2: { - file: { - fooProperty: "" - } - } - } - }; - - const fooBoomError = new Error("foo boom error"); - let sandbox; - let coreMocks; - let coreInstance; - let requireCache; - let filesHandler; - let libsMocks; - - beforeEach(async () => { - requireCache = cloneDeep(fooRequireCache); - sandbox = sinon.createSandbox(); - sandbox.stub(Boom, "badData").returns(fooBoomError); - coreMocks = new CoreMocks(); - libsMocks = new LibsMocks(); - coreInstance = coreMocks.stubs.instance; - sandbox.stub(tracer, "error"); - sandbox.stub(tracer, "info"); - sandbox.stub(tracer, "debug"); - requireAll.mockImplementation(() => fooFiles); - filesHandler = new FilesHandler(coreInstance.settings, coreInstance._eventEmitter, { - requireCache - }); - sandbox.stub(path, "isAbsolute").returns(true); - coreInstance.settings.get.withArgs("behaviors").returns("foo-path"); - }); - - afterEach(() => { - libsMocks.restore(); - sandbox.restore(); - coreMocks.restore(); - }); - - describe("when initialized", () => { - it("should require all files from mocks folders calculating it from cwd", async () => { - path.isAbsolute.returns(false); - await filesHandler.init(); - expect(requireAll).toHaveBeenCalledWith({ - dirname: path.resolve(process.cwd(), "foo-path"), - recursive: true - }); - }); - - it("should require all files from exactly mocks folder if it is absolute", async () => { - await filesHandler.init(); - expect(requireAll).toHaveBeenCalledWith({ - dirname: "foo-path", - recursive: true - }); - }); - - it("should throw an error if mocks folder is not defined", async () => { - expect.assertions(1); - try { - coreInstance.settings.get.withArgs("behaviors").returns(undefined); - await filesHandler.init(); - } catch (error) { - expect(error).toEqual(fooBoomError); - } - }); - - it("should clean require cache for behaviors folder", async () => { - const fooCachePath = "foo-path"; - - expect(requireCache[fooCachePath]).toBeDefined(); - await filesHandler.init(); - expect(requireCache[fooCachePath]).not.toBeDefined(); - }); - - it("should require cache in order to found the behaviors folder", async () => { - filesHandler = new FilesHandler(coreInstance.settings, coreInstance._eventEmitter); - sandbox.spy(filesHandler, "_cleanRequireCache"); - await filesHandler.init(); - // it seems like require cache is empty in jest environment - expect(filesHandler._cleanRequireCache.callCount).toEqual(0); - }); - - it("should clean require cache for behaviors folder childs", async () => { - const fooCachePath = "foo-path/foo-children"; - - expect(requireCache[fooCachePath]).toBeDefined(); - await filesHandler.init(); - expect(requireCache[fooCachePath]).not.toBeDefined(); - }); - - it("should clean require cache for behaviors folder childs recursively", async () => { - const fooCachePath = "foo-path/foo-children-2"; - - expect(requireCache[fooCachePath]).toBeDefined(); - await filesHandler.init(); - expect(requireCache[fooCachePath]).not.toBeDefined(); - }); - }); - - describe("files getter", () => { - it("should return current files", async () => { - await filesHandler.init(); - expect(filesHandler.files).toEqual(fooFiles); - }); - }); - - describe("start method", () => { - describe("when starting files watch", () => { - it("should do nothing if watch was not enabled", async () => { - coreInstance.settings.get.withArgs("watch").returns(false); - await filesHandler.init(); - await filesHandler.start(); - expect(libsMocks.stubs.watch.callCount).toEqual(0); - }); - - it("should call to close watcher if watch was enabled previously", async () => { - coreInstance.settings.get.withArgs("watch").returns(true); - await filesHandler.init(); - await filesHandler.start(); - await filesHandler.start(); - expect(libsMocks.stubs.watchClose.callCount).toEqual(1); - }); - }); - }); - - describe("when a file is changed", () => { - it("should load files again", async () => { - coreInstance.settings.get.withArgs("watch").returns(true); - await filesHandler.init(); - await filesHandler.start(); - libsMocks.stubs.watch.getCall(0).args[2](); - await wait(); - expect(requireAll.mock.calls.length).toEqual(2); - }); - }); - - describe("when core settings change", () => { - it("should load files again if behaviors setting is changed", async () => { - await filesHandler.init(); - coreInstance._eventEmitter.on.getCall(0).args[1]({ - behaviors: "foo-path" - }); - await wait(); - expect(requireAll.mock.calls.length).toEqual(2); - }); - - it("should enable watch again if behaviors setting is changed", async () => { - coreInstance.settings.get.withArgs("watch").returns(true); - await filesHandler.init(); - await filesHandler.start(); - coreInstance._eventEmitter.on.getCall(0).args[1]({ - behaviors: "foo-path" - }); - await wait(); - expect(libsMocks.stubs.watch.callCount).toEqual(2); - }); - - it("should disable watch if watch is changed", async () => { - coreInstance.settings.get.withArgs("watch").returns(true); - await filesHandler.init(); - await filesHandler.start(); - coreInstance.settings.get.withArgs("watch").returns(false); - coreInstance._eventEmitter.on.getCall(0).args[1]({ - watch: false - }); - await wait(); - expect(libsMocks.stubs.watchClose.callCount).toEqual(1); - }); - - it("should do nothing if no behaviors or watch settings are changed", async () => { - expect.assertions(3); - coreInstance.settings.get.withArgs("watch").returns(true); - await filesHandler.init(); - await filesHandler.start(); - coreInstance._eventEmitter.on.getCall(0).args[1]({}); - await wait(); - expect(requireAll.mock.calls.length).toEqual(1); - expect(libsMocks.stubs.watch.callCount).toEqual(1); - expect(libsMocks.stubs.watchClose.callCount).toEqual(0); - }); - }); -}); diff --git a/test/unit/core/mocks/Mocks.mocks.js b/test/unit/core/mocks/Mocks.mocks.js deleted file mode 100644 index 1cf2dc1..0000000 --- a/test/unit/core/mocks/Mocks.mocks.js +++ /dev/null @@ -1,44 +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 sinon = require("sinon"); - -jest.mock("../../../../lib/core/mocks/Mocks"); - -const Mocks = require("../../../../lib/core/mocks/Mocks"); - -class Mock { - constructor() { - this._sandbox = sinon.createSandbox(); - - this._stubs = { - init: this._sandbox.stub().resolves(), - start: this._sandbox.stub().resolves(), - behaviors: { - current: {} - } - }; - - Mocks.mockImplementation(() => this._stubs); - } - - get stubs() { - return { - Constructor: Mocks, - instance: this._stubs - }; - } - - restore() { - this._sandbox.restore(); - } -} - -module.exports = Mock; diff --git a/test/unit/core/mocks/Mocks.spec.js b/test/unit/core/mocks/Mocks.spec.js deleted file mode 100644 index 70ad12c..0000000 --- a/test/unit/core/mocks/Mocks.spec.js +++ /dev/null @@ -1,67 +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 sinon = require("sinon"); -const Boom = require("boom"); - -const BehaviorsMocks = require("./Behaviors.mocks.js"); -const FilesHandlerMocks = require("./FilesHandler.mocks.js"); -const CoreMocks = require("../Core.mocks.js"); - -const Mocks = require("../../../../lib/core/mocks/Mocks"); - -describe("Behaviors", () => { - const fooBoomError = new Error("foo boom error"); - let sandbox; - let coreMocks; - let filesHandlerMocks; - let behaviorsMocks; - let coreInstance; - let mocks; - - beforeEach(() => { - sandbox = sinon.createSandbox(); - sandbox.stub(Boom, "badData").returns(fooBoomError); - coreMocks = new CoreMocks(); - coreInstance = coreMocks.stubs.instance; - filesHandlerMocks = new FilesHandlerMocks(); - behaviorsMocks = new BehaviorsMocks(); - mocks = new Mocks(coreInstance.settings, coreInstance._eventEmitter); - }); - - afterEach(() => { - sandbox.restore(); - filesHandlerMocks.restore(); - behaviorsMocks.restore(); - coreMocks.restore(); - }); - - describe("init method", () => { - it("should init filesHandler and behaviors", async () => { - expect.assertions(2); - await mocks.init(); - expect(filesHandlerMocks.stubs.instance.init.callCount).toEqual(1); - expect(behaviorsMocks.stubs.instance.init.callCount).toEqual(1); - }); - }); - - describe("start method", () => { - it("should start filesHandler", async () => { - await mocks.start(); - expect(filesHandlerMocks.stubs.instance.start.callCount).toEqual(1); - }); - }); - - describe("behaviors getter", () => { - it("should return behaviors", async () => { - expect(mocks.behaviors).toEqual(behaviorsMocks.stubs.instance); - }); - }); -}); diff --git a/test/unit/core/server/Server.mocks.js b/test/unit/core/server/Server.mocks.js deleted file mode 100644 index b0a27a4..0000000 --- a/test/unit/core/server/Server.mocks.js +++ /dev/null @@ -1,46 +0,0 @@ -/* -Copyright 2019 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 - -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 sinon = require("sinon"); - -jest.mock("../../../../lib/core/server/Server"); - -const Server = require("../../../../lib/core/server/Server"); - -class Mock { - constructor() { - this._sandbox = sinon.createSandbox(); - - this._stubs = { - error: null, - start: this._sandbox.stub().resolves(), - restart: this._sandbox.stub().resolves(), - stop: this._sandbox.stub().resolves(), - init: this._sandbox.stub().resolves(), - addCustomRouter: this._sandbox.stub().resolves() - }; - - Server.mockImplementation(() => this._stubs); - } - - get stubs() { - return { - Constructor: Server, - instance: this._stubs - }; - } - - restore() { - this._sandbox.restore(); - } -} - -module.exports = Mock; diff --git a/test/unit/core/server/Server.spec.js b/test/unit/core/server/Server.spec.js deleted file mode 100644 index 06f52e5..0000000 --- a/test/unit/core/server/Server.spec.js +++ /dev/null @@ -1,292 +0,0 @@ -/* -Copyright 2019 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 - -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 sinon = require("sinon"); - -const LibsMocks = require("../../Libs.mocks.js"); -const MocksMocks = require("../mocks/Mocks.mocks.js"); -const CoreMocks = require("../Core.mocks.js"); - -const Server = require("../../../../lib/core/server/Server"); -const tracer = require("../../../../lib/core/tracer"); - -const wait = (time = 1000) => { - return new Promise(resolve => { - setTimeout(() => { - resolve(); - }, time); - }); -}; - -describe("Server", () => { - let sandbox; - let libsMocks; - let mocksMocks; - let coreMocks; - let coreInstance; - let processOnStub; - let server; - - beforeEach(() => { - sandbox = sinon.createSandbox(); - - processOnStub = sandbox.stub(process, "on"); - sandbox.stub(process, "exit"); - sandbox.stub(tracer, "error"); - sandbox.stub(tracer, "info"); - sandbox.stub(tracer, "debug"); - libsMocks = new LibsMocks(); - mocksMocks = new MocksMocks(); - coreMocks = new CoreMocks(); - coreInstance = coreMocks.stubs.instance; - server = new Server( - mocksMocks.stubs.instance, - coreInstance.settings, - coreInstance._eventEmitter - ); - expect.assertions(1); - }); - - afterEach(() => { - libsMocks.restore(); - sandbox.restore(); - coreMocks.restore(); - mocksMocks.restore(); - }); - - describe("when initialized", () => { - it("should be listening to process exit signals and stop the server if occurs", async () => { - processOnStub.callsFake((event, cb) => { - wait().then(() => { - cb(); - }); - }); - libsMocks.stubs.http.createServer.onListen.returns(null); - await server.init(); - await server.start(); - await wait(); - expect(libsMocks.stubs.http.createServer.close.callCount).toEqual(1); - }); - }); - - describe("custom routers", () => { - it("should be registered when initializating http server", async () => { - const fooRouter = sandbox.spy(); - server.addCustomRouter("fooPath", fooRouter); - libsMocks.stubs.http.createServer.onListen.returns(null); - await server.start(); - expect(libsMocks.stubs.express.use.calledWith("fooPath", fooRouter)).toEqual(true); - }); - }); - - describe("when started", () => { - it("should reject the promise if an error occurs when calling to server listen method", async () => { - const error = new Error("Foo error"); - libsMocks.stubs.http.createServer.listen.throws(error); - - await server.init(); - - try { - await server.start(); - } catch (err) { - expect(err).toEqual(error); - } - }); - - it("should be listening to server errors and throw an error if occurs", async () => { - const error = new Error(); - libsMocks.stubs.http.createServer.onError.returns(error); - - try { - await server.init(); - await server.start(); - } catch (err) { - expect(err).toEqual(error); - } - }); - - it("should not init httpServer more than once", async () => { - libsMocks.stubs.http.createServer.onListen.returns(null); - await server.start(); - await server.start(); - await server.start(); - expect(libsMocks.stubs.http.createServer.on.callCount).toEqual(1); - }); - - it("should call to server listen, and resolve the promise when started", async () => { - libsMocks.stubs.http.createServer.onListen.returns(null); - - await server.init(); - - expect(await server.start()).toEqual(server); - }); - - it("should call to server listen, and rejects the promise when starts throw an error", async () => { - const error = new Error(); - libsMocks.stubs.http.createServer.onListen.returns(new Error()); - await server.init(); - - try { - await server.start(); - } catch (err) { - expect(err).toEqual(error); - } - }); - }); - - describe("stop method", () => { - beforeEach(() => { - libsMocks.stubs.http.createServer.onListen.returns(null); - }); - - it("should call to stop the server", async () => { - await server.init(); - await server.start(); - await server.stop(); - expect(libsMocks.stubs.http.createServer.close.callCount).toEqual(1); - }); - - it("should not call to stop server if it has not been initialized", async () => { - await server.init(); - await server.stop(); - expect(libsMocks.stubs.http.createServer.close.callCount).toEqual(0); - }); - }); - - describe("restart method", () => { - beforeEach(() => { - libsMocks.stubs.http.createServer.onListen.returns(null); - }); - - it("should call to stop the server", async () => { - await server.init(); - await server.start(); - await server.restart(); - expect(libsMocks.stubs.http.createServer.close.callCount).toEqual(1); - }); - - it("should call to start server again", async () => { - await server.init(); - await server.start(); - await server.restart(); - expect(libsMocks.stubs.http.createServer.listen.callCount).toEqual(2); - }); - }); - - describe("error getter", () => { - it("should return null if there is no error", async () => { - await server.init(); - expect(server.error).toEqual(null); - }); - - it("should return current error if there was an error", async () => { - const error = new Error(); - libsMocks.stubs.http.createServer.onListen.returns(new Error()); - await server.init(); - - try { - await server.start(); - } catch (err) { - expect(server.error).toEqual(error); - } - }); - }); - - describe("behaviors middleware", () => { - const fooRequest = { - method: "get", - url: "foo-route" - }; - let statusSpy; - let sendSpy; - let resMock; - let nextSpy; - - beforeEach(async () => { - statusSpy = sandbox.spy(); - sendSpy = sandbox.spy(); - resMock = { - status: statusSpy, - send: sendSpy - }; - nextSpy = sandbox.spy(); - libsMocks.stubs.http.createServer.onListen.returns(null); - coreInstance.settings.get.withArgs("delay").returns(0); - }); - - it("should call next if does not found a fixture in current feature matching the request url", async () => { - await server.start(); - - server._fixturesMiddleware(fooRequest, resMock, nextSpy); - expect(nextSpy.callCount).toEqual(1); - }); - - it("should call to response status method to set the matching fixture status code", async () => { - await server.start(); - mocksMocks.stubs.instance.behaviors.current = { - get: { - "foo-route": { - route: { - match: () => true - }, - response: { - status: 200 - } - } - } - }; - server._fixturesMiddleware(fooRequest, resMock, nextSpy); - await wait(200); - expect(resMock.status.getCall(0).args[0]).toEqual(200); - }); - - it("should call to response send method passing the matching fixture body", async () => { - const fooBody = { - foo: "foo-data" - }; - await server.start(); - mocksMocks.stubs.instance.behaviors.current = { - get: { - "foo-route": { - route: { - match: () => true - }, - response: { - status: 200, - body: fooBody - } - } - } - }; - server._fixturesMiddleware(fooRequest, resMock, nextSpy); - await wait(200); - expect(resMock.send.getCall(0).args[0]).toEqual(fooBody); - }); - - it("should call to fixture response method passing all request data if it is a function", async () => { - const responseSpy = sandbox.spy(); - await server.start(); - mocksMocks.stubs.instance.behaviors.current = { - get: { - "foo-route": { - route: { - match: () => true - }, - response: responseSpy - } - } - }; - server._fixturesMiddleware(fooRequest, resMock, nextSpy); - await wait(200); - expect(responseSpy.calledWith(fooRequest, resMock, nextSpy)).toEqual(true); - }); - }); -}); diff --git a/test/unit/core/server/middlewares.spec.js b/test/unit/core/server/middlewares.spec.js deleted file mode 100644 index ddafe95..0000000 --- a/test/unit/core/server/middlewares.spec.js +++ /dev/null @@ -1,189 +0,0 @@ -/* -Copyright 2019 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 - -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 sinon = require("sinon"); -const Boom = require("boom"); - -const tracer = require("../../../../lib/core/tracer"); -const middlewares = require("../../../../lib/core/server/middlewares"); - -describe("middlewares", () => { - let sandbox; - let statusSpy; - let sendSpy; - let resMock; - let nextSpy; - let headerSpy; - - beforeEach(() => { - sandbox = sinon.createSandbox(); - statusSpy = sandbox.spy(); - sendSpy = sandbox.spy(); - headerSpy = sandbox.spy(); - resMock = { - status: statusSpy, - header: headerSpy, - send: sendSpy - }; - nextSpy = sandbox.spy(); - }); - - afterEach(() => { - sandbox.restore(); - }); - - describe("addCommonHeaders", () => { - it("should call to set encoding header", () => { - middlewares.addCommonHeaders({}, resMock, nextSpy); - expect(headerSpy.getCall(0).args[0]).toEqual("Accept-Encoding"); - }); - - it("should call to set language header", () => { - middlewares.addCommonHeaders({}, resMock, nextSpy); - expect(headerSpy.getCall(1).args[0]).toEqual("Accept-Language"); - }); - - it("should call to next callback", () => { - middlewares.addCommonHeaders({}, resMock, nextSpy); - expect(nextSpy.callCount).toEqual(1); - }); - }); - - describe("traceRequest", () => { - const fooRequest = { - method: "foo-method", - url: "foo-url", - id: "foo-id" - }; - let tracerStub; - - beforeEach(() => { - tracerStub = sandbox.stub(tracer, "verbose"); - }); - - it("should call to tracer verbose method, printing the request method", () => { - middlewares.traceRequest(fooRequest, resMock, nextSpy); - expect(tracerStub.getCall(0).args[0]).toEqual(expect.stringContaining("foo-method")); - }); - - it("should call to tracer verbose method, printing the request url", () => { - middlewares.traceRequest(fooRequest, resMock, nextSpy); - expect(tracerStub.getCall(0).args[0]).toEqual(expect.stringContaining("foo-url")); - }); - - it("should call to tracer verbose method, printing the request id", () => { - middlewares.traceRequest(fooRequest, resMock, nextSpy); - expect(tracerStub.getCall(0).args[0]).toEqual(expect.stringContaining("foo-id")); - }); - - it("should call to next callback", () => { - middlewares.traceRequest(fooRequest, resMock, nextSpy); - expect(nextSpy.callCount).toEqual(1); - }); - }); - - describe("notFound", () => { - const fooNotFoundError = new Error("foo"); - const fooRequest = { - id: "foo-id" - }; - let tracerStub; - - beforeEach(() => { - tracerStub = sandbox.stub(tracer, "debug"); - sandbox.stub(Boom, "notFound").returns(fooNotFoundError); - }); - - it("should call to tracer debug method, printing the request id", () => { - middlewares.notFound(fooRequest, resMock, nextSpy); - expect(tracerStub.getCall(0).args[0]).toEqual(expect.stringContaining("foo-id")); - }); - - it("should call to next callback, passing a not found error", () => { - middlewares.notFound(fooRequest, resMock, nextSpy); - expect(nextSpy.getCall(0).args[0]).toEqual(fooNotFoundError); - }); - }); - - describe("errorHandler", () => { - const fooError = new Error("foo error message"); - const fooBadImplementationError = new Error("foo bad implementation error message"); - const fooRequest = { - id: "foo-id" - }; - - beforeEach(() => { - fooError.output = { - statusCode: "foo-status-code", - payload: "foo-payload" - }; - fooBadImplementationError.output = { - statusCode: "foo-bad-implementation-status-code", - payload: "foo-bad-implementation-payload" - }; - sandbox.stub(tracer, "error"); - sandbox.stub(tracer, "silly"); - sandbox.stub(Boom, "isBoom").returns(false); - sandbox.stub(Boom, "badImplementation").returns(fooBadImplementationError); - }); - - it("should call to next callback if no error is received", () => { - middlewares.errorHandler(null, fooRequest, resMock, nextSpy); - expect(nextSpy.callCount).toEqual(1); - }); - - it("should trace the received error with the request id", () => { - middlewares.errorHandler(fooError, fooRequest, resMock, nextSpy); - expect(tracer.error.getCall(0).args[0]).toEqual(expect.stringContaining("foo-id")); - }); - - it("should convert the received error to a bad implementation error if it is not a Boom error", () => { - middlewares.errorHandler(fooError, fooRequest, resMock, nextSpy); - expect(Boom.badImplementation.getCall(0).args[0]).toEqual(fooError); - }); - - it("should trace the received error message", () => { - middlewares.errorHandler(fooError, fooRequest, resMock, nextSpy); - expect(tracer.error.getCall(0).args[0]).toEqual( - expect.stringContaining("foo bad implementation error message") - ); - }); - - it("should trace the received error stack", () => { - middlewares.errorHandler(fooError, fooRequest, resMock, nextSpy); - expect(tracer.silly.getCall(0).args[0]).toEqual(fooError.stack.toString()); - }); - - it("should call to set response status as error statusCode", () => { - fooError.stack = null; - middlewares.errorHandler(fooError, fooRequest, resMock, nextSpy); - expect(resMock.status.getCall(0).args[0]).toEqual("foo-bad-implementation-status-code"); - }); - - it("should call to send response with error payload", () => { - fooError.stack = null; - middlewares.errorHandler(fooError, fooRequest, resMock, nextSpy); - expect(resMock.send.getCall(0).args[0]).toEqual("foo-bad-implementation-payload"); - }); - - it("should not trace error stack if error is controlled", () => { - Boom.isBoom.returns(true); - middlewares.errorHandler(fooError, fooRequest, resMock, nextSpy); - expect(tracer.silly.callCount).toEqual(0); - }); - - it("should not convert the received error if it is controlled", () => { - Boom.isBoom.returns(true); - middlewares.errorHandler(fooError, fooRequest, resMock, nextSpy); - expect(Boom.badImplementation.callCount).toEqual(0); - }); - }); -}); diff --git a/test/unit/core/settings/CommandLineArguments.mocks.js b/test/unit/core/settings/CommandLineArguments.mocks.js deleted file mode 100644 index edb4675..0000000 --- a/test/unit/core/settings/CommandLineArguments.mocks.js +++ /dev/null @@ -1,43 +0,0 @@ -/* -Copyright 2019 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 - -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 sinon = require("sinon"); - -jest.mock("../../../../lib/core/settings/CommandLineArguments"); - -const CommandLineArguments = require("../../../../lib/core/settings/CommandLineArguments"); - -class Mock { - constructor() { - this._sandbox = sinon.createSandbox(); - - this._stubs = { - init: this._sandbox.stub(), - addCustom: this._sandbox.stub(), - options: {} - }; - - CommandLineArguments.mockImplementation(() => this._stubs); - } - - get stubs() { - return { - Constructor: CommandLineArguments, - instance: this._stubs - }; - } - - restore() { - this._sandbox.restore(); - } -} - -module.exports = Mock; diff --git a/test/unit/core/settings/CommandLineArguments.spec.js b/test/unit/core/settings/CommandLineArguments.spec.js deleted file mode 100644 index caf4e36..0000000 --- a/test/unit/core/settings/CommandLineArguments.spec.js +++ /dev/null @@ -1,378 +0,0 @@ -/* -Copyright 2019 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 - -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 sinon = require("sinon"); -const commander = require("commander"); - -const CommandLineArguments = require("../../../../lib/core/settings/CommandLineArguments"); - -describe("options", () => { - const DEFAULT_OPTIONS = { - behavior: null, - behaviors: null, - delay: 0, - host: "0.0.0.0", - port: 3100, - watch: true, - log: "info", - // TODO, remove deprecated options - feature: null, - features: null - }; - let sandbox; - let optionStub; - let parseStub; - let commandLineArguments; - - beforeEach(() => { - sandbox = sinon.createSandbox(); - optionStub = sandbox.stub(); - parseStub = sandbox.stub().returns({}); - sandbox.spy(console, "warn"); - - optionStub.returns({ - option: optionStub, - parse: parseStub - }); - - sandbox.stub(commander, "option").returns({ - option: optionStub - }); - - commandLineArguments = new CommandLineArguments(DEFAULT_OPTIONS); - }); - - afterEach(() => { - sandbox.restore(); - }); - - describe("init method", () => { - it("should call to commander to get user options from command line", async () => { - await commandLineArguments.init(); - expect(optionStub.callCount).toEqual(8); - }); - - it("should call to convert to number received value in --port option", async () => { - expect.assertions(1); - optionStub.callsFake((commandName, description, parser) => { - if (commandName.includes("--port")) { - expect(parser("5")).toEqual(5); - } - return { - option: optionStub, - parse: parseStub - }; - }); - commandLineArguments = new CommandLineArguments({}); - await commandLineArguments.init(); - }); - - it("should omit undefined values", async () => { - const options = { - behavior: "foo-behavior", - behaviors: undefined - }; - parseStub.returns(options); - await commandLineArguments.init(); - expect(commandLineArguments.options).toEqual({ - behavior: "foo-behavior" - }); - }); - }); - - describe("stringBoolean options", () => { - it("should return true if value is 'true'", async () => { - expect.assertions(1); - optionStub.callsFake((commandName, description, parser) => { - if (commandName.includes("--watch")) { - expect(parser("true")).toEqual(true); - } - return { - option: optionStub, - parse: parseStub - }; - }); - commandLineArguments = new CommandLineArguments({}); - await commandLineArguments.init(); - }); - - it("should return true if value is 'undefined'", async () => { - expect.assertions(1); - optionStub.callsFake((commandName, description, parser) => { - if (commandName.includes("--watch")) { - expect(parser()).toEqual(true); - } - return { - option: optionStub, - parse: parseStub - }; - }); - commandLineArguments = new CommandLineArguments({}); - await commandLineArguments.init(); - }); - - it("should return false if value is 'false'", async () => { - expect.assertions(1); - optionStub.callsFake((commandName, description, parser) => { - if (commandName.includes("--watch")) { - expect(parser("false")).toEqual(false); - } - return { - option: optionStub, - parse: parseStub - }; - }); - commandLineArguments = new CommandLineArguments({}); - await commandLineArguments.init(); - }); - - it("should throw an error if value is not true nor false", async () => { - expect.assertions(1); - optionStub.callsFake((commandName, description, parser) => { - if (commandName.includes("--watch")) { - try { - parser("foo"); - } catch (error) { - expect(error.message).toEqual(expect.stringContaining("Invalid boolean value")); - } - } - return { - option: optionStub, - parse: parseStub - }; - }); - commandLineArguments = new CommandLineArguments({}); - await commandLineArguments.init(); - }); - }); - - describe("when adding custom option", () => { - describe("when it is string type", () => { - it("should add commander option with mandatory value", async () => { - expect.assertions(2); - const option = { - name: "foo", - description: "foo description", - type: "string" - }; - optionStub.callsFake((commandName, description) => { - if (commandName.includes("--foo")) { - expect(commandName).toEqual("--foo "); - expect(description).toEqual(option.description); - } - return { - option: optionStub, - parse: parseStub - }; - }); - commandLineArguments.addCustom(option); - await commandLineArguments.init(); - }); - - it("should use provided custom parser", async () => { - expect.assertions(1); - const option = { - name: "foo", - type: "string", - parse: () => {} - }; - optionStub.callsFake((commandName, description, parser) => { - if (commandName.includes("--foo")) { - expect(parser).toEqual(option.parse); - } - return { - option: optionStub, - parse: parseStub - }; - }); - commandLineArguments.addCustom(option); - await commandLineArguments.init(); - }); - }); - - describe("when it is booleanString type", () => { - it("should add commander option with mandatory value", async () => { - expect.assertions(2); - const option = { - name: "foo", - description: "foo description", - type: "booleanString" - }; - optionStub.callsFake((commandName, description) => { - if (commandName.includes("--foo")) { - expect(commandName).toEqual("--foo "); - expect(description).toEqual(option.description); - } - return { - option: optionStub, - parse: parseStub - }; - }); - commandLineArguments.addCustom(option); - await commandLineArguments.init(); - }); - - it("should convert string to boolean when parsed", async () => { - expect.assertions(1); - const option = { - name: "foo", - description: "foo description", - type: "booleanString" - }; - optionStub.callsFake((commandName, description, parser) => { - if (commandName.includes("--foo")) { - expect(parser("false")).toEqual(false); - } - return { - option: optionStub, - parse: parseStub - }; - }); - commandLineArguments.addCustom(option); - await commandLineArguments.init(); - }); - - it("should use custom parser if provided to get value", async () => { - expect.assertions(2); - const option = { - name: "foo", - description: "foo description", - type: "booleanString", - parse: val => val - }; - sandbox.spy(option, "parse"); - optionStub.callsFake((commandName, description, parser) => { - if (commandName.includes("--foo")) { - expect(parser("false")).toEqual("false"); - expect(option.parse.callCount).toEqual(1); - } - return { - option: optionStub, - parse: parseStub - }; - }); - commandLineArguments.addCustom(option); - await commandLineArguments.init(); - }); - }); - - describe("when type is boolean", () => { - it("should not add commander option with mandatory value", async () => { - expect.assertions(2); - const option = { - name: "foo", - description: "foo description", - type: "boolean" - }; - optionStub.callsFake((commandName, description) => { - if (commandName.includes("--foo")) { - expect(commandName).toEqual("--foo"); - expect(description).toEqual(option.description); - } - return { - option: optionStub, - parse: parseStub - }; - }); - commandLineArguments.addCustom(option); - await commandLineArguments.init(); - }); - - it("should add commander --no prefix if default value is true", async () => { - expect.assertions(2); - const option = { - name: "foo", - description: "foo description", - type: "boolean", - default: true - }; - optionStub.callsFake((commandName, description) => { - if (commandName.includes("-foo")) { - expect(commandName).toEqual("--no-foo"); - expect(description).toEqual(option.description); - } - return { - option: optionStub, - parse: parseStub - }; - }); - commandLineArguments.addCustom(option); - await commandLineArguments.init(); - }); - }); - - describe("when type is number", () => { - it("should add commander option with mandatory value", async () => { - expect.assertions(2); - const option = { - name: "foo", - description: "foo description", - type: "number" - }; - optionStub.callsFake((commandName, description) => { - if (commandName.includes("--foo")) { - expect(commandName).toEqual("--foo "); - expect(description).toEqual(option.description); - } - return { - option: optionStub, - parse: parseStub - }; - }); - commandLineArguments.addCustom(option); - await commandLineArguments.init(); - }); - - it("should convert string to number when parsed", async () => { - expect.assertions(1); - const option = { - name: "foo", - description: "foo description", - type: "number" - }; - optionStub.callsFake((commandName, description, parser) => { - if (commandName.includes("--foo")) { - expect(parser("4")).toEqual(4); - } - return { - option: optionStub, - parse: parseStub - }; - }); - commandLineArguments.addCustom(option); - await commandLineArguments.init(); - }); - - it("should use custom parser if provided to get value", async () => { - expect.assertions(2); - const option = { - name: "foo", - description: "foo description", - type: "number", - parse: val => val - }; - sandbox.spy(option, "parse"); - optionStub.callsFake((commandName, description, parser) => { - if (commandName.includes("--foo")) { - expect(parser("5")).toEqual("5"); - expect(option.parse.callCount).toEqual(1); - } - return { - option: optionStub, - parse: parseStub - }; - }); - commandLineArguments.addCustom(option); - await commandLineArguments.init(); - }); - }); - }); -}); diff --git a/test/unit/core/settings/Options.mocks.js b/test/unit/core/settings/Options.mocks.js deleted file mode 100644 index 35844dc..0000000 --- a/test/unit/core/settings/Options.mocks.js +++ /dev/null @@ -1,47 +0,0 @@ -/* -Copyright 2019 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 - -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 sinon = require("sinon"); - -jest.mock("../../../../lib/core/settings/Options"); - -const Options = require("../../../../lib/core/settings/Options"); - -class Mock { - constructor() { - this._sandbox = sinon.createSandbox(); - - this._stubs = { - init: this._sandbox.stub(), - addCustom: this._sandbox.stub(), - getValidOptionName: this._sandbox.stub(), - options: { - log: "foo-log-level", - behavior: "foo-behavior" - } - }; - - Options.mockImplementation(() => this._stubs); - } - - get stubs() { - return { - Constructor: Options, - instance: this._stubs - }; - } - - restore() { - this._sandbox.restore(); - } -} - -module.exports = Mock; diff --git a/test/unit/core/settings/Options.spec.js b/test/unit/core/settings/Options.spec.js deleted file mode 100644 index 9b9ad0c..0000000 --- a/test/unit/core/settings/Options.spec.js +++ /dev/null @@ -1,476 +0,0 @@ -/* -Copyright 2019 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 - -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 sinon = require("sinon"); - -const CommandLineArgumentsMocks = require("./CommandLineArguments.mocks.js"); - -const Options = require("../../../../lib/core/settings/Options"); -const tracer = require("../../../../lib/core/tracer"); - -describe("options", () => { - let sandbox; - let options; - let commandLineArgumentsMocks; - - beforeEach(() => { - sandbox = sinon.createSandbox(); - sandbox.spy(tracer, "warn"); - sandbox.stub(tracer, "error"); - commandLineArgumentsMocks = new CommandLineArgumentsMocks(); - options = new Options(); - }); - - afterEach(() => { - sandbox.restore(); - commandLineArgumentsMocks.restore(); - }); - - describe("init method", () => { - it("should call to get command line arguments", async () => { - await options.init(); - expect(commandLineArgumentsMocks.stubs.instance.init.callCount).toEqual(1); - }); - - it("should call only once to get command line arguments", async () => { - await options.init(); - await options.init(); - await options.init(); - expect(commandLineArgumentsMocks.stubs.instance.init.callCount).toEqual(1); - }); - - it("should print a warning if --feature option is received", async () => { - commandLineArgumentsMocks.stubs.instance.options = { - feature: "foo-feature", - cli: true, - behaviors: "foo/features/path" - }; - await options.init(); - expect(tracer.warn.getCall(0).args[0]).toEqual( - expect.stringContaining("Deprecation warning: --feature") - ); - }); - - it("should print a warning if --features option is received", async () => { - commandLineArgumentsMocks.stubs.instance.options = { - cli: true, - features: "foo/features/path" - }; - await options.init(); - expect(tracer.warn.getCall(0).args[0]).toEqual( - expect.stringContaining("Deprecation warning: --features") - ); - }); - }); - - describe("init method when using programmatic options", () => { - it("should not call to get command line arguments", async () => { - options = new Options({ - onlyProgrammaticOptions: true - }); - await options.init(); - expect(commandLineArgumentsMocks.stubs.instance.init.callCount).toEqual(0); - }); - }); - - describe("when adding custom option", () => { - it("should trow an error if options have been already initialized", async () => { - expect.assertions(2); - await options.init(); - try { - options.addCustom(); - } catch (error) { - const errorMessageContains = "already initializated"; - expect(tracer.error.getCall(0).args[0]).toEqual( - expect.stringContaining(errorMessageContains) - ); - expect(error.message).toEqual(expect.stringContaining(errorMessageContains)); - } - }); - - it("should trow an error if no option is provided", () => { - expect.assertions(2); - try { - options.addCustom(); - } catch (error) { - const errorMessageContains = "provide option details"; - expect(tracer.error.getCall(0).args[0]).toEqual( - expect.stringContaining(errorMessageContains) - ); - expect(error.message).toEqual(expect.stringContaining(errorMessageContains)); - } - }); - - it("should trow an error if option name is not provided", () => { - expect.assertions(2); - try { - options.addCustom({ - description: "foo" - }); - } catch (error) { - const errorMessageContains = "provide option name"; - expect(tracer.error.getCall(0).args[0]).toEqual( - expect.stringContaining(errorMessageContains) - ); - expect(error.message).toEqual(expect.stringContaining(errorMessageContains)); - } - }); - - it("should trow an error if option was already declared", () => { - expect.assertions(2); - try { - options.addCustom({ - name: "behaviors" - }); - } catch (error) { - const errorMessageContains = "already registered"; - expect(tracer.error.getCall(0).args[0]).toEqual( - expect.stringContaining(errorMessageContains) - ); - expect(error.message).toEqual(expect.stringContaining(errorMessageContains)); - } - }); - - it("should trow an error if option type is unknown", () => { - expect.assertions(2); - try { - options.addCustom({ - name: "foo", - type: "foo" - }); - } catch (error) { - const errorMessageContains = "provide a valid option type"; - expect(tracer.error.getCall(0).args[0]).toEqual( - expect.stringContaining(errorMessageContains) - ); - expect(error.message).toEqual(expect.stringContaining(errorMessageContains)); - } - }); - - it("should print a warning if option description is not provided", () => { - expect.assertions(1); - options.addCustom({ - name: "foo", - type: "string" - }); - const errorMessageContains = "provide option description"; - expect(tracer.warn.getCall(0).args[0]).toEqual( - expect.stringContaining(errorMessageContains) - ); - }); - - it("should not print a warning if option description is not provided", () => { - expect.assertions(1); - options.addCustom({ - name: "foo", - type: "string", - description: "foo-description" - }); - expect(tracer.warn.callCount).toEqual(0); - }); - }); - - describe("options getter", () => { - it("should only get values from keys defined in default values", async () => { - commandLineArgumentsMocks.stubs.instance.options = { - behavior: "foo-behavior", - cli: true, - behaviors: "foo/behaviors/path", - foo: undefined, - foo2: "foooo" - }; - await options.init(); - expect(options.options).toEqual({ - port: 3100, - host: "0.0.0.0", - log: "info", - delay: 0, - watch: true, - behavior: "foo-behavior", - behaviors: "foo/behaviors/path" - }); - }); - - it("should remove deprecated options", async () => { - commandLineArgumentsMocks.stubs.instance.options = { - behavior: "foo-behavior", - cli: true, - behaviors: "foo/behaviors/path", - foo: undefined, - foo2: "foooo", - recursive: false - }; - await options.init(); - expect(options.options).toEqual({ - port: 3100, - host: "0.0.0.0", - log: "info", - delay: 0, - watch: true, - behavior: "foo-behavior", - behaviors: "foo/behaviors/path" - }); - }); - - it("should get values from keys defined in new options", async () => { - commandLineArgumentsMocks.stubs.instance.options = { - behavior: "foo-behavior", - cli: true, - behaviors: "foo/behaviors/path", - foo: "foo" - }; - options.addCustom({ - name: "cli", - type: "boolean" - }); - options.addCustom({ - name: "foo", - type: "string" - }); - await options.init(); - expect(options.options).toEqual({ - port: 3100, - host: "0.0.0.0", - log: "info", - cli: true, - foo: "foo", - delay: 0, - watch: true, - behavior: "foo-behavior", - behaviors: "foo/behaviors/path" - }); - }); - - it("should extend default options with user options, ommiting undefined values", async () => { - commandLineArgumentsMocks.stubs.instance.options = { - behavior: "foo-behavior", - cli: true, - behaviors: "foo/behaviors/path", - foo: undefined - }; - await options.init(); - expect(options.options).toEqual({ - port: 3100, - host: "0.0.0.0", - log: "info", - delay: 0, - watch: true, - behavior: "foo-behavior", - behaviors: "foo/behaviors/path" - }); - }); - - it("should convert feature and features options to behavior and behaviors", async () => { - commandLineArgumentsMocks.stubs.instance.options = { - feature: "foo-feature", - cli: true, - features: "foo/features/path" - }; - await options.init(); - expect(options.options).toEqual({ - port: 3100, - host: "0.0.0.0", - log: "info", - delay: 0, - watch: true, - behavior: "foo-feature", - behaviors: "foo/features/path" - }); - }); - - it("should apply behavior and behavior options if feature and features options are received too", async () => { - commandLineArgumentsMocks.stubs.instance.options = { - behavior: "foo-behavior", - feature: "foo-feature", - cli: true, - behaviors: "foo/behaviors/path", - features: "foo-feature" - }; - await options.init(); - expect(options.options).toEqual({ - port: 3100, - host: "0.0.0.0", - log: "info", - delay: 0, - watch: true, - behavior: "foo-behavior", - behaviors: "foo/behaviors/path" - }); - }); - }); - - describe("options getter when using programmatic options", () => { - beforeEach(() => { - options = new Options({ - onlyProgrammaticOptions: true - }); - }); - - it("should only get values from keys defined in default values", async () => { - await options.init({ - behavior: "foo-behavior", - cli: true, - behaviors: "foo/behaviors/path", - foo: undefined, - foo2: "foooo" - }); - expect(options.options).toEqual({ - port: 3100, - host: "0.0.0.0", - log: "info", - delay: 0, - watch: true, - behavior: "foo-behavior", - behaviors: "foo/behaviors/path" - }); - }); - - it("should remove deprecated options", async () => { - await options.init({ - behavior: "foo-behavior", - cli: true, - behaviors: "foo/behaviors/path", - foo: undefined, - foo2: "foooo", - recursive: false - }); - expect(options.options).toEqual({ - port: 3100, - host: "0.0.0.0", - log: "info", - delay: 0, - watch: true, - behavior: "foo-behavior", - behaviors: "foo/behaviors/path" - }); - }); - - it("should get values from keys defined in new options", async () => { - options.addCustom({ - name: "cli", - type: "boolean" - }); - options.addCustom({ - name: "foo", - type: "string" - }); - await options.init({ - behavior: "foo-behavior", - cli: true, - behaviors: "foo/behaviors/path", - foo: "foo" - }); - expect(options.options).toEqual({ - port: 3100, - host: "0.0.0.0", - log: "info", - cli: true, - foo: "foo", - delay: 0, - watch: true, - behavior: "foo-behavior", - behaviors: "foo/behaviors/path" - }); - }); - - it("should extend default options with user options, ommiting undefined values", async () => { - await options.init({ - behavior: "foo-behavior", - cli: true, - behaviors: "foo/behaviors/path", - foo: undefined - }); - expect(options.options).toEqual({ - port: 3100, - host: "0.0.0.0", - log: "info", - delay: 0, - watch: true, - behavior: "foo-behavior", - behaviors: "foo/behaviors/path" - }); - }); - - it("should convert feature and features options to behavior and behaviors", async () => { - await options.init({ - feature: "foo-feature", - cli: true, - features: "foo/features/path" - }); - expect(options.options).toEqual({ - port: 3100, - host: "0.0.0.0", - log: "info", - delay: 0, - watch: true, - behavior: "foo-feature", - behaviors: "foo/features/path" - }); - }); - - it("should apply behavior and behavior options if feature and features options are received too", async () => { - await options.init({ - behavior: "foo-behavior", - feature: "foo-feature", - cli: true, - behaviors: "foo/behaviors/path", - features: "foo-feature" - }); - expect(options.options).toEqual({ - port: 3100, - host: "0.0.0.0", - log: "info", - delay: 0, - watch: true, - behavior: "foo-behavior", - behaviors: "foo/behaviors/path" - }); - }); - }); - - describe("getValidOptionName method", () => { - it("should throw an error if option is not valid", async () => { - expect.assertions(1); - await options.init(); - try { - options.getValidOptionName("foo"); - } catch (error) { - expect(error.message).toEqual(expect.stringContaining("Not valid option")); - } - }); - - it("should return option name if option is valid", async () => { - await options.init(); - expect(options.getValidOptionName("behavior")).toEqual("behavior"); - }); - - it("should return new option name if option is deprecated", async () => { - expect.assertions(2); - await options.init(); - const option = options.getValidOptionName("feature"); - expect( - tracer.warn.calledWith( - "Deprecation warning: feature option will be deprecated. Use behavior instead" - ) - ).toEqual(true); - expect(option).toEqual("behavior"); - }); - - it("should return true if option is custom option", async () => { - options.addCustom({ - name: "foo", - type: "boolean" - }); - await options.init(); - expect(options.getValidOptionName("foo")).toEqual("foo"); - }); - }); -}); diff --git a/test/unit/core/settings/Settings.mocks.js b/test/unit/core/settings/Settings.mocks.js deleted file mode 100644 index 93c7649..0000000 --- a/test/unit/core/settings/Settings.mocks.js +++ /dev/null @@ -1,44 +0,0 @@ -/* -Copyright 2019 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 - -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 sinon = require("sinon"); - -jest.mock("../../../../lib/core/settings/Settings"); - -const Settings = require("../../../../lib/core/settings/Settings"); - -class Mock { - constructor() { - this._sandbox = sinon.createSandbox(); - - this._stubs = { - get: this._sandbox.stub(), - set: this._sandbox.stub(), - init: this._sandbox.stub().resolves(), - addCustom: this._sandbox.stub() - }; - - Settings.mockImplementation(() => this._stubs); - } - - get stubs() { - return { - Constructor: Settings, - instance: this._stubs - }; - } - - restore() { - this._sandbox.restore(); - } -} - -module.exports = Mock; diff --git a/test/unit/core/settings/Settings.spec.js b/test/unit/core/settings/Settings.spec.js deleted file mode 100644 index 487b963..0000000 --- a/test/unit/core/settings/Settings.spec.js +++ /dev/null @@ -1,114 +0,0 @@ -/* -Copyright 2019 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 - -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 sinon = require("sinon"); - -const OptionsMocks = require("./Options.mocks.js"); -const CoreMocks = require("../Core.mocks.js"); - -const Settings = require("../../../../lib/core/settings/Settings"); -const tracer = require("../../../../lib/core/tracer"); - -describe("Settings", () => { - let optionsMocks; - let optionsInstance; - let coreMocks; - let coreInstance; - let sandbox; - let settings; - - beforeEach(async () => { - sandbox = sinon.createSandbox(); - optionsMocks = new OptionsMocks(); - optionsInstance = optionsMocks.stubs.instance; - optionsInstance.getValidOptionName.callsFake(name => name); - coreMocks = new CoreMocks(); - coreInstance = coreMocks.stubs.instance; - sandbox.stub(tracer, "set"); - sandbox.stub(tracer, "info"); - settings = new Settings({}, coreInstance._eventEmitter); - await settings.init(); - }); - - afterEach(() => { - sandbox.restore(); - optionsMocks.restore(); - coreMocks.restore(); - }); - - describe("when initializated", () => { - it("should init options", () => { - expect(optionsInstance.init.callCount).toEqual(1); - }); - - it("should set current tracer level", () => { - expect(tracer.set.calledWith("console", "foo-log-level")).toEqual(true); - }); - }); - - describe("get method", () => { - it("should return current option value", () => { - expect(settings.get("log")).toEqual("foo-log-level"); - }); - - it("should return current deprecated option value", () => { - optionsInstance.getValidOptionName.returns("behavior"); - expect(settings.get("log")).toEqual("foo-behavior"); - }); - - it("should return new value if set is called", () => { - settings.set("log", "foo-new-value"); - expect(settings.get("log")).toEqual("foo-new-value"); - }); - }); - - describe("set method", () => { - it("should set log level if setting is log", () => { - settings.set("log", "foo-new-value"); - expect(tracer.set.calledWith("console", "foo-new-value")).toEqual(true); - }); - - it("should set new option if provided one is deprecated", () => { - optionsInstance.getValidOptionName.returns("behavior"); - settings.set("log", "foo"); - expect(settings.get("behavior")).toEqual("foo"); - }); - - it("should emit change if setting has changed value", () => { - settings.set("foo", "foo-new-value"); - expect( - coreInstance._eventEmitter.emit.calledWith("change:settings", { - foo: "foo-new-value" - }) - ).toEqual(true); - }); - - it("should not emit change if setting maintains value", () => { - const setFoo = () => { - settings.set("foo", "foo-new-value"); - }; - setFoo(); - setFoo(); - setFoo(); - expect(coreInstance._eventEmitter.emit.callCount).toEqual(1); - }); - }); - - describe("addCustom method", () => { - it("should pass custom option to options Class", () => { - const fooOption = { - foo: "foo" - }; - settings.addCustom(fooOption); - expect(optionsInstance.addCustom.calledWith(fooOption)).toEqual(true); - }); - }); -}); diff --git a/test/unit/core/tracer.spec.js b/test/unit/core/tracer.spec.js deleted file mode 100644 index a1f332f..0000000 --- a/test/unit/core/tracer.spec.js +++ /dev/null @@ -1,42 +0,0 @@ -/* -Copyright 2019 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 - -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 sinon = require("sinon"); - -const tracer = require("../../../lib/core/tracer"); - -describe("tracer", () => { - let sandbox; - - beforeEach(() => { - sandbox = sinon.createSandbox(); - sandbox.stub(tracer, "debug"); - sandbox.spy(console, "log"); - }); - - afterEach(() => { - sandbox.restore(); - }); - - describe("set method", () => { - it("should call to set log level", () => { - tracer.set("console", "error"); - tracer.debug("foo debug"); - expect(console.log.callCount).toEqual(0); - }); - - it("should deactivate logs if called with level silent", () => { - tracer.set("console", "silent"); - tracer.error("foo error"); - expect(console.log.callCount).toEqual(0); - }); - }); -}); diff --git a/test/unit/index.spec.js b/test/unit/index.spec.js index 7142acc..dc84679 100644 --- a/test/unit/index.spec.js +++ b/test/unit/index.spec.js @@ -10,21 +10,10 @@ Unless required by applicable law or agreed to in writing, software distributed */ const index = require("../../index"); +const Api = require("../../src/Api"); describe("index", () => { - it("should export a Cli constructor", () => { - expect(index.Cli).toBeDefined(); - }); - - it("should export a Server constructor", () => { - expect(typeof index.Server).toEqual("function"); - }); - - it("should export a Behavior constructor", () => { - expect(typeof index.Behavior).toEqual("function"); - }); - - it("should export a deprecated Feature constructor", () => { - expect(index.Feature).toBe(index.Behavior); + it("should export the Api constructor", () => { + expect(index).toEqual(Api); }); }); diff --git a/test/unit/api/Api.spec.js b/test/unit/src/Api.spec.js similarity index 97% rename from test/unit/api/Api.spec.js rename to test/unit/src/Api.spec.js index 8553884..6651f7f 100644 --- a/test/unit/api/Api.spec.js +++ b/test/unit/src/Api.spec.js @@ -12,11 +12,11 @@ Unless required by applicable law or agreed to in writing, software distributed const express = require("express"); const sinon = require("sinon"); -const CoreMocks = require("../core/Core.mocks.js"); +const CoreMocks = require("../Core.mocks.js"); const BehaviorsMocks = require("./Behaviors.mocks.js"); const SettingsMocks = require("./Settings.mocks.js"); -const Api = require("../../../lib/api/Api"); +const Api = require("../../../src/Api"); describe("Api", () => { let sandbox; diff --git a/test/unit/api/Behaviors.mocks.js b/test/unit/src/Behaviors.mocks.js similarity index 91% rename from test/unit/api/Behaviors.mocks.js rename to test/unit/src/Behaviors.mocks.js index 16d463f..9cfeda2 100644 --- a/test/unit/api/Behaviors.mocks.js +++ b/test/unit/src/Behaviors.mocks.js @@ -11,9 +11,9 @@ Unless required by applicable law or agreed to in writing, software distributed const sinon = require("sinon"); -jest.mock("../../../lib/api/Behaviors"); +jest.mock("../../../src/Behaviors"); -const Behaviors = require("../../../lib/api/Behaviors"); +const Behaviors = require("../../../src/Behaviors"); const Mock = class Mock { constructor() { diff --git a/test/unit/api/Behaviors.spec.js b/test/unit/src/Behaviors.spec.js similarity index 96% rename from test/unit/api/Behaviors.spec.js rename to test/unit/src/Behaviors.spec.js index 02a8933..5a71ec9 100644 --- a/test/unit/api/Behaviors.spec.js +++ b/test/unit/src/Behaviors.spec.js @@ -12,9 +12,9 @@ Unless required by applicable law or agreed to in writing, software distributed const express = require("express"); const sinon = require("sinon"); -const CoreMocks = require("../core/Core.mocks.js"); +const CoreMocks = require("../Core.mocks.js"); -const Behaviors = require("../../../lib/api/Behaviors"); +const Behaviors = require("../../../src/Behaviors"); describe("Behaviors Api", () => { let sandbox; diff --git a/test/unit/api/Settings.mocks.js b/test/unit/src/Settings.mocks.js similarity index 91% rename from test/unit/api/Settings.mocks.js rename to test/unit/src/Settings.mocks.js index 5d36914..6dc81bc 100644 --- a/test/unit/api/Settings.mocks.js +++ b/test/unit/src/Settings.mocks.js @@ -11,9 +11,9 @@ Unless required by applicable law or agreed to in writing, software distributed const sinon = require("sinon"); -jest.mock("../../../lib/api/Settings"); +jest.mock("../../../src/Settings"); -const Settings = require("../../../lib/api/Settings"); +const Settings = require("../../../src/Settings"); const Mock = class Mock { constructor() { diff --git a/test/unit/api/Settings.spec.js b/test/unit/src/Settings.spec.js similarity index 96% rename from test/unit/api/Settings.spec.js rename to test/unit/src/Settings.spec.js index 4336a4a..8c47ba9 100644 --- a/test/unit/api/Settings.spec.js +++ b/test/unit/src/Settings.spec.js @@ -12,9 +12,9 @@ Unless required by applicable law or agreed to in writing, software distributed const express = require("express"); const sinon = require("sinon"); -const CoreMocks = require("../core/Core.mocks.js"); +const CoreMocks = require("../Core.mocks.js"); -const Settings = require("../../../lib/api/Settings"); +const Settings = require("../../../src/Settings"); describe("Settings Api", () => { let sandbox; diff --git a/test/unit/start.spec.js b/test/unit/start.spec.js deleted file mode 100644 index e5e4e22..0000000 --- a/test/unit/start.spec.js +++ /dev/null @@ -1,59 +0,0 @@ -/* -Copyright 2019 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 - -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 sinon = require("sinon"); - -const CoreMocks = require("./core/Core.mocks.js"); -const AdminApi = require("../../lib/api/Api"); -const InquirerCli = require("../../lib/cli/Cli"); - -const { start } = require("../../lib/start"); - -describe("start method", () => { - let sandbox; - let coreMocks; - - beforeEach(() => { - sandbox = sinon.createSandbox(); - coreMocks = new CoreMocks(); - expect.assertions(1); - }); - - afterEach(() => { - sandbox.restore(); - coreMocks.restore(); - }); - - it("should create a new Core, passing to it AdminApi and CLI plugins", async () => { - await start(); - expect(coreMocks.stubs.Constructor.mock.calls[0][0]).toEqual({ - plugins: [AdminApi, InquirerCli] - }); - }); - - it("should catch and trace errors while creating Core", async () => { - const errorMessage = "Foo error"; - sandbox.stub(console, "error"); - coreMocks.stubs.Constructor.mockImplementationOnce(() => { - throw new Error(errorMessage); - }); - await start(); - expect(console.error.getCall(0).args[0]).toEqual(`Error: ${errorMessage}`); - }); - - it("should catch and trace start method errors", async () => { - const errorMessage = "Foo error"; - sandbox.stub(console, "error"); - coreMocks.stubs.instance.start.rejects(new Error(errorMessage)); - await start(); - expect(console.error.getCall(0).args[0]).toEqual(`Error: ${errorMessage}`); - }); -});