diff --git a/.travis.yml b/.travis.yml index 5bc54516..a9ac0288 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,7 +5,7 @@ cache: notifications: email: true node_js: - - '8' + - '12' script: - npm test - npm run demo @@ -20,6 +20,7 @@ script: - START_SERVER_AND_TEST_INSECURE=1 npm run demo9 - npm run demo-cross-env - npm run demo-commands + - npm run demo-multiple after_success: - npm run travis-deploy-once "npm run semantic-release" branches: diff --git a/README.md b/README.md index 7a45d626..ff49ee40 100644 --- a/README.md +++ b/README.md @@ -216,7 +216,13 @@ This utility will wait for maximum of 5 minutes while checking for the server to ### Starting two servers -Sometimes you need to start one API server and one webserver in order to test the application. Just have two commands cascade. First command should wait on the webserver script, which in turn uses `start-server-and-test` to start the API server before running the webserver. Something like this +Sometimes you need to start one API server and one webserver in order to test the application. Use the syntax: + +``` +start-test +``` + +For example if API runs at port 3000 and server runs at port 8080: ```json { @@ -224,13 +230,12 @@ Sometimes you need to start one API server and one webserver in order to test th "test": "node src/test", "start:api": "node src/api", "start:server": "node src/server", - "start:server-and-api": "start-test start:api 7600 start:server", - "test:all": "start-test start:server-and-api 5000 test" + "test:all": "start-test start:api 3000 start:server 8080 test" } } ``` -In the above example you would run `npm run test:all` to start both servers and run the test. See repo [start-two-servers-example](https://github.com/bahmutov/start-two-servers-example) for full example +In the above example you would run `npm run test:all` to start the API first, then when it responds, start the server, and when the server is responding, it would run the tests. After the tests finish, it will shut down both servers. See the repo [start-two-servers-example](https://github.com/bahmutov/start-two-servers-example) for full example ### Small print diff --git a/__snapshots__/utils-spec.js b/__snapshots__/utils-spec.js index 84f69294..d0606be1 100644 --- a/__snapshots__/utils-spec.js +++ b/__snapshots__/utils-spec.js @@ -1,73 +1,170 @@ +exports['utils getArguments allows 5 arguments 1'] = { + "args": [ + "start", + "6000", + "start:web", + "6010", + "test" + ], + "parsed": { + "services": [ + { + "start": "npm run start", + "url": [ + "http://localhost:6000" + ] + }, + { + "start": "start:web", + "url": [ + "http://localhost:6010" + ] + } + ], + "test": "npm run test" + } +} + +exports['utils getArguments determines NPM script for each command 1'] = { + "args": [ + "startA", + "6000", + "startB", + "6010", + "testC" + ], + "parsed": { + "services": [ + { + "start": "npm run startA", + "url": [ + "http://localhost:6000" + ] + }, + { + "start": "npm run startB", + "url": [ + "http://localhost:6010" + ] + } + ], + "test": "npm run testC" + } +} + exports['utils getArguments handles 3 arguments with http-get url 1'] = { - "start": "npm run start", - "url": [ - "http-get://localhost:8080" + "services": [ + { + "start": "npm run start", + "url": [ + "http-get://localhost:8080" + ] + } ], "test": "npm run test" } exports['utils getArguments returns 3 arguments 1'] = { - "start": "npm run start", - "url": [ - "http://localhost:8080" + "args": [ + "start", + "8080", + "test" ], - "test": "npm run test" + "parsed": { + "services": [ + { + "start": "npm run start", + "url": [ + "http://localhost:8080" + ] + } + ], + "test": "npm run test" + } } exports['utils getArguments returns 3 arguments with url 1'] = { - "start": "npm run start", - "url": [ - "http://localhost:8080" + "services": [ + { + "start": "npm run start", + "url": [ + "http://localhost:8080" + ] + } ], "test": "npm run test" } exports['utils getArguments understands custom commands 1'] = { - "start": "custom-command --with argument", - "url": [ - "http://localhost:3000" + "services": [ + { + "start": "custom-command --with argument", + "url": [ + "http://localhost:3000" + ] + } ], "test": "test-command --x=1" } exports['utils getArguments understands several ports 1'] = { - "start": "npm run start", - "url": [ - "http://localhost:3000", - "http://localhost:4000", - "http://localhost:5000" + "services": [ + { + "start": "npm run start", + "url": [ + "http://localhost:3000", + "http://localhost:4000", + "http://localhost:5000" + ] + } ], "test": "npm run test" } exports['utils getArguments understands single :port 1'] = { - "start": "npm run start", - "url": [ - "http://localhost:3000" + "services": [ + { + "start": "npm run start", + "url": [ + "http://localhost:3000" + ] + } ], "test": "npm run test" } exports['utils getArguments understands single port 1'] = { - "start": "npm run start", - "url": [ - "http://localhost:3000" + "services": [ + { + "start": "npm run start", + "url": [ + "http://localhost:3000" + ] + } ], "test": "npm run test" } exports['utils getArguments understands start plus url 1'] = { - "start": "start-server", - "url": [ - "http://localhost:6000" + "services": [ + { + "start": "start-server", + "url": [ + "http://localhost:6000" + ] + } ], "test": "npm run test" } exports['utils getArguments understands url plus test 1'] = { - "start": "npm run start", - "url": [ - "http://localhost:6000" + "services": [ + { + "start": "npm run start", + "url": [ + "http://localhost:6000" + ] + } ], "test": "npm run test" } diff --git a/package-lock.json b/package-lock.json index 3688f913..ae79b411 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3028,6 +3028,51 @@ } } }, + "@sinonjs/commons": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.7.2.tgz", + "integrity": "sha512-+DUO6pnp3udV/v2VfUWgaY5BIE1IfT7lLfeDzPVeMT1XKkaAp9LgSI9x5RtrFQoZ9Oi0PgXQQHPaoKu7dCjVxw==", + "dev": true, + "requires": { + "type-detect": "4.0.8" + } + }, + "@sinonjs/fake-timers": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-6.0.1.tgz", + "integrity": "sha512-MZPUxrmFubI36XS1DI3qmI0YdN1gks62JtFZvxR67ljjSNCeK6U08Zx4msEWOXuofgqUt6zPHSi1H9fbjR/NRA==", + "dev": true, + "requires": { + "@sinonjs/commons": "^1.7.0" + } + }, + "@sinonjs/formatio": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@sinonjs/formatio/-/formatio-5.0.1.tgz", + "integrity": "sha512-KaiQ5pBf1MpS09MuA0kp6KBQt2JUOQycqVG1NZXvzeaXe5LGFqAKueIS0bw4w0P9r7KuBSVdUk5QjXsUdu2CxQ==", + "dev": true, + "requires": { + "@sinonjs/commons": "^1", + "@sinonjs/samsam": "^5.0.2" + } + }, + "@sinonjs/samsam": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-5.0.3.tgz", + "integrity": "sha512-QucHkc2uMJ0pFGjJUDP3F9dq5dx8QIaqISl9QgwLOh6P9yv877uONPGXh/OH/0zmM3tW1JjuJltAZV2l7zU+uQ==", + "dev": true, + "requires": { + "@sinonjs/commons": "^1.6.0", + "lodash.get": "^4.4.2", + "type-detect": "^4.0.8" + } + }, + "@sinonjs/text-encoding": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/@sinonjs/text-encoding/-/text-encoding-0.7.1.tgz", + "integrity": "sha512-+iTbntw2IZPb/anVDbypzfQa+ay64MW0Zo8aJ8gZPWMMK6/OubMVb6lUPMagqjOPnmtauXnFCACVl3O7ogjeqQ==", + "dev": true + }, "@szmarczak/http-timer": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-1.1.1.tgz", @@ -3426,6 +3471,12 @@ "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" }, + "assertion-error": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", + "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", + "dev": true + }, "assign": { "version": "0.1.7", "resolved": "http://registry.npmjs.org/assign/-/assign-0.1.7.tgz", @@ -4524,6 +4575,20 @@ "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=" }, + "chai": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/chai/-/chai-4.2.0.tgz", + "integrity": "sha512-XQU3bhBukrOsQCuwZndwGcCVQHyZi53fQ6Ys1Fym7E4olpIqqZZhhoFJoaKVvV17lWQoXYwgWN2nF5crA8J2jw==", + "dev": true, + "requires": { + "assertion-error": "^1.1.0", + "check-error": "^1.0.2", + "deep-eql": "^3.0.1", + "get-func-name": "^2.0.0", + "pathval": "^1.1.0", + "type-detect": "^4.0.5" + } + }, "chalk": { "version": "2.3.2", "resolved": "http://registry.npmjs.org/chalk/-/chalk-2.3.2.tgz", @@ -4564,6 +4629,12 @@ } } }, + "check-error": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz", + "integrity": "sha1-V00xLt2Iu13YkS6Sht1sCu1KrII=", + "dev": true + }, "check-more-types": { "version": "2.24.0", "resolved": "https://registry.npmjs.org/check-more-types/-/check-more-types-2.24.0.tgz", @@ -4604,9 +4675,9 @@ } }, "glob-parent": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.0.tgz", - "integrity": "sha512-qjtRgnIVmOfnKUE3NJAQEdk+lKrxfw8t5ke7SXtfMTHcjsBfOfWXCQfdb30zfDoZQ2IRSIiidmjtbHZPZ++Ihw==", + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.1.tgz", + "integrity": "sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ==", "dev": true, "requires": { "is-glob": "^4.0.1" @@ -5549,6 +5620,15 @@ "mimic-response": "^1.0.0" } }, + "deep-eql": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-3.0.1.tgz", + "integrity": "sha512-+QeIQyN5ZuO+3Uk5DYh6/1eKO0m0YmJFGNmFHGACpf1ClL1nmlV/p4gNgbl2pJGxgXb4faqo6UE+M5ACEMyVcw==", + "dev": true, + "requires": { + "type-detect": "^4.0.0" + } + }, "deep-extend": { "version": "0.4.2", "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.4.2.tgz", @@ -7563,6 +7643,12 @@ "integrity": "sha1-9wLmMSfn4jHBYKgMFVSstw1QR+U=", "dev": true }, + "get-func-name": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz", + "integrity": "sha1-6td0q+5y4gQJQzoGY2YCPdaIekE=", + "dev": true + }, "get-stdin": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-5.0.1.tgz", @@ -9001,6 +9087,12 @@ "object.assign": "^4.1.0" } }, + "just-extend": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/just-extend/-/just-extend-4.1.0.tgz", + "integrity": "sha512-ApcjaOdVTJ7y4r08xI5wIqpvwS48Q0PBG4DJROcEkH1f8MdAiNFyFxz3xoL0LWAVwjrwPYZdVHHxhRHcx/uGLA==", + "dev": true + }, "keyv": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/keyv/-/keyv-3.1.0.tgz", @@ -9951,9 +10043,9 @@ "dev": true }, "p-limit": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.2.tgz", - "integrity": "sha512-WGR+xHecKTr7EbUEhyLSh5Dube9JtdiG78ufaeLxTgpudf/20KqyMioIUZJAezlTIi6evxuoUs9YXc11cU+yzQ==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", "dev": true, "requires": { "p-try": "^2.0.0" @@ -10147,6 +10239,19 @@ "integrity": "sha1-2Tli9sUvLBVYwPvabVEoGfHv4cQ=", "dev": true }, + "nise": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/nise/-/nise-4.0.3.tgz", + "integrity": "sha512-EGlhjm7/4KvmmE6B/UFsKh7eHykRl9VH+au8dduHLCyWUO/hr7+N+WtTvDUwc9zHuM1IaIJs/0lQ6Ag1jDkQSg==", + "dev": true, + "requires": { + "@sinonjs/commons": "^1.7.0", + "@sinonjs/fake-timers": "^6.0.0", + "@sinonjs/text-encoding": "^0.7.1", + "just-extend": "^4.0.2", + "path-to-regexp": "^1.7.0" + } + }, "node-emoji": { "version": "1.10.0", "resolved": "https://registry.npmjs.org/node-emoji/-/node-emoji-1.10.0.tgz", @@ -14151,9 +14256,9 @@ } }, "es-abstract": { - "version": "1.17.4", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.4.tgz", - "integrity": "sha512-Ae3um/gb8F0mui/jPL+QiqmglkUsaQf7FwBEHYIFkztkneosu9imhqHpBzQ3h1vit8t5iQ74t6PEVvphBZiuiQ==", + "version": "1.17.5", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.5.tgz", + "integrity": "sha512-BR9auzDbySxOcfog0tLECW8l28eRGpDpU3Dm3Hp4q/N+VtLTmyj4EUN088XZWQDW/hzj6sYRDXeOFsaAODKvpg==", "dev": true, "requires": { "es-to-primitive": "^1.2.1", @@ -14681,6 +14786,23 @@ "integrity": "sha1-PBrfhx6pzWyUMbbqK9dKD/BVxME=", "dev": true }, + "path-to-regexp": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.8.0.tgz", + "integrity": "sha512-n43JRhlUKUAlibEJhPeir1ncUID16QnEjNpwzNdO3Lm4ywrBpBZ5oLD0I6br9evr1Y9JTqwRtAh7JLoOzAQdVA==", + "dev": true, + "requires": { + "isarray": "0.0.1" + }, + "dependencies": { + "isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", + "dev": true + } + } + }, "path-type": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/path-type/-/path-type-2.0.0.tgz", @@ -14698,6 +14820,12 @@ } } }, + "pathval": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.0.tgz", + "integrity": "sha1-uULm1L3mUwBe9rcTYd74cn0GReA=", + "dev": true + }, "pause-stream": { "version": "0.0.11", "resolved": "http://registry.npmjs.org/pause-stream/-/pause-stream-0.0.11.tgz", @@ -16473,6 +16601,50 @@ } } }, + "sinon": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/sinon/-/sinon-9.0.2.tgz", + "integrity": "sha512-0uF8Q/QHkizNUmbK3LRFqx5cpTttEVXudywY9Uwzy8bTfZUhljZ7ARzSxnRHWYWtVTeh4Cw+tTb3iU21FQVO9A==", + "dev": true, + "requires": { + "@sinonjs/commons": "^1.7.2", + "@sinonjs/fake-timers": "^6.0.1", + "@sinonjs/formatio": "^5.0.1", + "@sinonjs/samsam": "^5.0.3", + "diff": "^4.0.2", + "nise": "^4.0.1", + "supports-color": "^7.1.0" + }, + "dependencies": { + "diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "supports-color": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", + "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "sinon-chai": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/sinon-chai/-/sinon-chai-3.5.0.tgz", + "integrity": "sha512-IifbusYiQBpUxxFJkR3wTU68xzBN0+bxCScEaKMjBvAQERg6FnTTc1F17rseLb1tjmkJ23730AXpFI0c47FgAg==", + "dev": true + }, "slash": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/slash/-/slash-1.0.0.tgz", @@ -17559,14 +17731,111 @@ "strip-ansi": "^4.0.0" } }, + "string.prototype.trimend": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.1.tgz", + "integrity": "sha512-LRPxFUaTtpqYsTeNKaFOw3R4bxIzWOnbQ837QfBylo8jIxtcbK/A/sMV7Q+OAV/vWo+7s25pOE10KYSjaSO06g==", + "dev": true, + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.5" + }, + "dependencies": { + "define-properties": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", + "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", + "dev": true, + "requires": { + "object-keys": "^1.0.12" + } + }, + "es-abstract": { + "version": "1.17.5", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.5.tgz", + "integrity": "sha512-BR9auzDbySxOcfog0tLECW8l28eRGpDpU3Dm3Hp4q/N+VtLTmyj4EUN088XZWQDW/hzj6sYRDXeOFsaAODKvpg==", + "dev": true, + "requires": { + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1", + "is-callable": "^1.1.5", + "is-regex": "^1.0.5", + "object-inspect": "^1.7.0", + "object-keys": "^1.1.1", + "object.assign": "^4.1.0", + "string.prototype.trimleft": "^2.1.1", + "string.prototype.trimright": "^2.1.1" + } + }, + "es-to-primitive": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", + "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", + "dev": true, + "requires": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + } + }, + "has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dev": true, + "requires": { + "function-bind": "^1.1.1" + } + }, + "has-symbols": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz", + "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==", + "dev": true + }, + "is-callable": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.5.tgz", + "integrity": "sha512-ESKv5sMCJB2jnHTWZ3O5itG+O128Hsus4K4Qh1h2/cgn2vbgnLSVqfV46AeJA9D5EeeLa9w81KUXMtn34zhX+Q==", + "dev": true + }, + "is-regex": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.5.tgz", + "integrity": "sha512-vlKW17SNq44owv5AQR3Cq0bQPEb8+kF3UKZ2fiZNOWtztYE5i0CzCZxFDwO58qAOWtxdBRVO/V5Qin1wjCqFYQ==", + "dev": true, + "requires": { + "has": "^1.0.3" + } + }, + "is-symbol": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.3.tgz", + "integrity": "sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ==", + "dev": true, + "requires": { + "has-symbols": "^1.0.1" + } + }, + "object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "dev": true + } + } + }, "string.prototype.trimleft": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/string.prototype.trimleft/-/string.prototype.trimleft-2.1.1.tgz", - "integrity": "sha512-iu2AGd3PuP5Rp7x2kEZCrB2Nf41ehzh+goo8TV7z8/XDBbsvc6HQIlUl9RjkZ4oyrW1XM5UwlGl1oVEaDjg6Ag==", + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/string.prototype.trimleft/-/string.prototype.trimleft-2.1.2.tgz", + "integrity": "sha512-gCA0tza1JBvqr3bfAIFJGqfdRTyPae82+KTnm3coDXkZN9wnuW3HjGgN386D7hfv5CHQYCI022/rJPVlqXyHSw==", "dev": true, "requires": { "define-properties": "^1.1.3", - "function-bind": "^1.1.1" + "es-abstract": "^1.17.5", + "string.prototype.trimstart": "^1.0.0" }, "dependencies": { "define-properties": { @@ -17578,6 +17847,75 @@ "object-keys": "^1.0.12" } }, + "es-abstract": { + "version": "1.17.5", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.5.tgz", + "integrity": "sha512-BR9auzDbySxOcfog0tLECW8l28eRGpDpU3Dm3Hp4q/N+VtLTmyj4EUN088XZWQDW/hzj6sYRDXeOFsaAODKvpg==", + "dev": true, + "requires": { + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1", + "is-callable": "^1.1.5", + "is-regex": "^1.0.5", + "object-inspect": "^1.7.0", + "object-keys": "^1.1.1", + "object.assign": "^4.1.0", + "string.prototype.trimleft": "^2.1.1", + "string.prototype.trimright": "^2.1.1" + } + }, + "es-to-primitive": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", + "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", + "dev": true, + "requires": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + } + }, + "has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dev": true, + "requires": { + "function-bind": "^1.1.1" + } + }, + "has-symbols": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz", + "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==", + "dev": true + }, + "is-callable": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.5.tgz", + "integrity": "sha512-ESKv5sMCJB2jnHTWZ3O5itG+O128Hsus4K4Qh1h2/cgn2vbgnLSVqfV46AeJA9D5EeeLa9w81KUXMtn34zhX+Q==", + "dev": true + }, + "is-regex": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.5.tgz", + "integrity": "sha512-vlKW17SNq44owv5AQR3Cq0bQPEb8+kF3UKZ2fiZNOWtztYE5i0CzCZxFDwO58qAOWtxdBRVO/V5Qin1wjCqFYQ==", + "dev": true, + "requires": { + "has": "^1.0.3" + } + }, + "is-symbol": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.3.tgz", + "integrity": "sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ==", + "dev": true, + "requires": { + "has-symbols": "^1.0.1" + } + }, "object-keys": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", @@ -17587,13 +17925,110 @@ } }, "string.prototype.trimright": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/string.prototype.trimright/-/string.prototype.trimright-2.1.1.tgz", - "integrity": "sha512-qFvWL3/+QIgZXVmJBfpHmxLB7xsUXz6HsUmP8+5dRaC3Q7oKUv9Vo6aMCRZC1smrtyECFsIT30PqBJ1gTjAs+g==", + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/string.prototype.trimright/-/string.prototype.trimright-2.1.2.tgz", + "integrity": "sha512-ZNRQ7sY3KroTaYjRS6EbNiiHrOkjihL9aQE/8gfQ4DtAC/aEBRHFJa44OmoWxGGqXuJlfKkZW4WcXErGr+9ZFg==", + "dev": true, + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.5", + "string.prototype.trimend": "^1.0.0" + }, + "dependencies": { + "define-properties": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", + "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", + "dev": true, + "requires": { + "object-keys": "^1.0.12" + } + }, + "es-abstract": { + "version": "1.17.5", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.5.tgz", + "integrity": "sha512-BR9auzDbySxOcfog0tLECW8l28eRGpDpU3Dm3Hp4q/N+VtLTmyj4EUN088XZWQDW/hzj6sYRDXeOFsaAODKvpg==", + "dev": true, + "requires": { + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1", + "is-callable": "^1.1.5", + "is-regex": "^1.0.5", + "object-inspect": "^1.7.0", + "object-keys": "^1.1.1", + "object.assign": "^4.1.0", + "string.prototype.trimleft": "^2.1.1", + "string.prototype.trimright": "^2.1.1" + } + }, + "es-to-primitive": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", + "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", + "dev": true, + "requires": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + } + }, + "has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dev": true, + "requires": { + "function-bind": "^1.1.1" + } + }, + "has-symbols": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz", + "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==", + "dev": true + }, + "is-callable": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.5.tgz", + "integrity": "sha512-ESKv5sMCJB2jnHTWZ3O5itG+O128Hsus4K4Qh1h2/cgn2vbgnLSVqfV46AeJA9D5EeeLa9w81KUXMtn34zhX+Q==", + "dev": true + }, + "is-regex": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.5.tgz", + "integrity": "sha512-vlKW17SNq44owv5AQR3Cq0bQPEb8+kF3UKZ2fiZNOWtztYE5i0CzCZxFDwO58qAOWtxdBRVO/V5Qin1wjCqFYQ==", + "dev": true, + "requires": { + "has": "^1.0.3" + } + }, + "is-symbol": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.3.tgz", + "integrity": "sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ==", + "dev": true, + "requires": { + "has-symbols": "^1.0.1" + } + }, + "object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "dev": true + } + } + }, + "string.prototype.trimstart": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.1.tgz", + "integrity": "sha512-XxZn+QpvrBI1FOcg6dIpxUPgWCPuNXvMD72aaRaUQv1eD4e/Qy8i/hFTe0BUmD60p/QA6bh1avmuPTfNjqVWRw==", "dev": true, "requires": { "define-properties": "^1.1.3", - "function-bind": "^1.1.1" + "es-abstract": "^1.17.5" }, "dependencies": { "define-properties": { @@ -17605,6 +18040,75 @@ "object-keys": "^1.0.12" } }, + "es-abstract": { + "version": "1.17.5", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.5.tgz", + "integrity": "sha512-BR9auzDbySxOcfog0tLECW8l28eRGpDpU3Dm3Hp4q/N+VtLTmyj4EUN088XZWQDW/hzj6sYRDXeOFsaAODKvpg==", + "dev": true, + "requires": { + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1", + "is-callable": "^1.1.5", + "is-regex": "^1.0.5", + "object-inspect": "^1.7.0", + "object-keys": "^1.1.1", + "object.assign": "^4.1.0", + "string.prototype.trimleft": "^2.1.1", + "string.prototype.trimright": "^2.1.1" + } + }, + "es-to-primitive": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", + "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", + "dev": true, + "requires": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + } + }, + "has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dev": true, + "requires": { + "function-bind": "^1.1.1" + } + }, + "has-symbols": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz", + "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==", + "dev": true + }, + "is-callable": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.5.tgz", + "integrity": "sha512-ESKv5sMCJB2jnHTWZ3O5itG+O128Hsus4K4Qh1h2/cgn2vbgnLSVqfV46AeJA9D5EeeLa9w81KUXMtn34zhX+Q==", + "dev": true + }, + "is-regex": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.5.tgz", + "integrity": "sha512-vlKW17SNq44owv5AQR3Cq0bQPEb8+kF3UKZ2fiZNOWtztYE5i0CzCZxFDwO58qAOWtxdBRVO/V5Qin1wjCqFYQ==", + "dev": true, + "requires": { + "has": "^1.0.3" + } + }, + "is-symbol": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.3.tgz", + "integrity": "sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ==", + "dev": true, + "requires": { + "has-symbols": "^1.0.1" + } + }, "object-keys": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", @@ -18223,6 +18727,12 @@ "prelude-ls": "~1.1.2" } }, + "type-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "dev": true + }, "type-fest": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz", @@ -19221,9 +19731,9 @@ "dev": true }, "p-limit": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.2.tgz", - "integrity": "sha512-WGR+xHecKTr7EbUEhyLSh5Dube9JtdiG78ufaeLxTgpudf/20KqyMioIUZJAezlTIi6evxuoUs9YXc11cU+yzQ==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", "dev": true, "requires": { "p-try": "^2.0.0" diff --git a/package.json b/package.json index 430ad10f..745b0bd9 100644 --- a/package.json +++ b/package.json @@ -65,7 +65,7 @@ "pretty": "prettier-standard 'src/**/*.js'", "size": "t=\"$(npm pack .)\"; wc -c \"${t}\"; tar tvf \"${t}\"; rm \"${t}\";", "test": "npm run unit", - "unit": "mocha src/*-spec.js", + "unit": "mocha test/helper src/*-spec.js", "unused-deps": "dependency-check --unused --no-dev .", "semantic-release": "semantic-release", "start": "node test/server.js", @@ -77,7 +77,8 @@ "test2": "curl http://127.0.0.1:9000", "test3": "curl http://127.0.0.1:9000 && curl http://127.0.0.1:9001", "test4": "curl --insecure https://127.0.0.1:9000", - "demo": "node src/bin/start.js http://127.0.0.1:9000", + "message": "echo Hi there 👋", + "demo": "node src/bin/start.js http://127.0.0.1:9000 message", "demo2": "node src/bin/start.js start http://127.0.0.1:9000 test2", "demo3": "node src/bin/start.js start-with-child http://127.0.0.1:9000 test", "demo4": "node src/bin/start.js 9000", @@ -90,10 +91,12 @@ "demo11": "node src/bin/start.js http-get://127.0.0.1:9000", "demo-cross-env": "node src/bin/start.js start-cross-env 9000", "demo-commands": "node src/bin/start.js 'node test/server.js --port 8800' 8800 'node test/client --port 8800'", + "demo-multiple": "node src/bin/start.js 'node test/server --port 6000' 6000 'node test/server --port 6010' 6010 'curl http://127.0.0.1:6000 && curl http://127.0.0.1:6010'", "travis-deploy-once": "travis-deploy-once" }, "devDependencies": { "ban-sensitive-files": "1.9.7", + "chai": "4.2.0", "cross-env": "7.0.2", "dependency-check": "3.4.1", "deps-ok": "1.4.1", @@ -107,6 +110,8 @@ "prettier-standard": "8.0.1", "semantic-release": "15.13.24", "simple-commit-message": "4.0.13", + "sinon": "9.0.2", + "sinon-chai": "3.5.0", "snap-shot-it": "6.3.5", "standard": "13.1.0", "travis-deploy-once": "5.0.11" @@ -130,5 +135,8 @@ } ] } + }, + "standard": { + "globals": ["sandbox", "expect"] } } diff --git a/src/bin/start.js b/src/bin/start.js index 06fd8f48..22264336 100755 --- a/src/bin/start.js +++ b/src/bin/start.js @@ -3,19 +3,21 @@ const debug = require('debug')('start-server-and-test') const args = process.argv.slice(2) -const startAndTest = require('..') +const startAndTest = require('..').startAndTest const utils = require('../utils') debug('parsing CLI arguments: %o', args) const parsed = utils.getArguments(args) debug('parsed args: %o', parsed) -const { start, test, url } = parsed -console.log('starting server using command "%s"', start) -console.log('and when url "%s" is responding with HTTP status code 200', url) -console.log('running tests using command "%s"', test) +const { services, test } = parsed +if (!Array.isArray(services)) { + throw new Error(`Could not parse arguments %o, got %o`, args, parsed) +} -startAndTest({ start, url, test }).catch(e => { +utils.printArguments({ services, test }) + +startAndTest({ services, test }).catch(e => { console.error(e) process.exit(1) }) diff --git a/src/index.js b/src/index.js index b7a02fc4..71eb2e93 100644 --- a/src/index.js +++ b/src/index.js @@ -1,3 +1,4 @@ +// @ts-check 'use strict' const la = require('lazy-ass') @@ -21,9 +22,9 @@ const isDebug = () => const isInsecure = () => process.env.START_SERVER_AND_TEST_INSECURE -function startAndTest ({ start, url, test }) { +function waitAndRun ({ start, url, runFn }) { la(is.unemptyString(start), 'missing start script name', start) - la(is.unemptyString(test), 'missing test script name', test) + la(is.fn(runFn), 'missing test script name', runFn) la( is.unemptyString(url) || is.unemptyArray(url), 'missing url to wait on', @@ -94,15 +95,46 @@ function startAndTest ({ start, url, test }) { }) }) - function runTests () { - debug('running test script command: %s', test) - return execa(test, { shell: true, stdio: 'inherit' }) - } - return waited .tapCatch(stopServer) - .then(runTests) + .then(runFn) .finally(stopServer) } -module.exports = startAndTest +const runTheTests = testCommand => () => { + debug('running test script command: %s', testCommand) + return execa(testCommand, { shell: true, stdio: 'inherit' }) +} + +/** + * Starts a single service and runs tests or recursively + * runs a service, then goes to the next list, until it reaches 1 service and runs test. + */ +function startAndTest ({ services, test }) { + if (services.length === 0) { + throw new Error('Got zero services to start ...') + } + + if (services.length === 1) { + const runTests = runTheTests(test) + debug('single service "%s" to run and test', services[0].start) + return waitAndRun({ + start: services[0].start, + url: services[0].url, + runFn: runTests + }) + } + + return waitAndRun({ + start: services[0].start, + url: services[0].url, + runFn: () => { + debug('previous service started, now going to the next one') + return startAndTest({ services: services.slice(1), test }) + } + }) +} + +module.exports = { + startAndTest +} diff --git a/src/utils-spec.js b/src/utils-spec.js index 1afa679d..5f347cf1 100644 --- a/src/utils-spec.js +++ b/src/utils-spec.js @@ -3,6 +3,7 @@ /* eslint-env mocha */ const la = require('lazy-ass') const snapshot = require('snap-shot-it') +const debug = require('debug')('test') function arrayEq (a, b) { return a.length === b.length && a.every((el, index) => el === b[index]) @@ -28,8 +29,29 @@ describe('utils', () => { context('getArguments', () => { const getArguments = utils.getArguments + it('allows 5 arguments', () => { + const args = ['start', '6000', 'start:web', '6010', 'test'] + const parsed = getArguments(args) + debug('from %o', args) + debug('parsed %o', parsed) + debug('services %o', parsed.services) + snapshot({ args, parsed }) + }) + + it('determines NPM script for each command', () => { + sandbox.stub(utils, 'isPackageScriptName').returns(true) + const args = ['startA', '6000', 'startB', '6010', 'testC'] + const parsed = getArguments(args) + debug('from %o', args) + debug('parsed %o', parsed) + debug('services %o', parsed.services) + snapshot({ args, parsed }) + }) + it('returns 3 arguments', () => { - snapshot(getArguments(['start', '8080', 'test'])) + const args = ['start', '8080', 'test'] + const parsed = getArguments(args) + snapshot({ args, parsed }) }) it('returns 3 arguments with url', () => { @@ -62,6 +84,20 @@ describe('utils', () => { snapshot(getArguments(['3000|4000|5000'])) }) + it('asks if command is a script name', () => { + const args = ['custom-command-name', '3000', 'some-test-command'] + const isPackageScriptName = sandbox + .stub(utils, 'isPackageScriptName') + .returns(false) + const parsed = getArguments(args) + debug('from %o', args) + debug('parsed %o', parsed) + /* eslint-disable-next-line no-unused-expressions */ + expect(isPackageScriptName).to.have.been.calledTwice + expect(isPackageScriptName).to.have.been.calledWith('custom-command-name') + expect(isPackageScriptName).to.have.been.calledWith('some-test-command') + }) + it('understands custom commands', () => { // these commands are NOT script names in the package.json // thus they will be run as is diff --git a/src/utils.js b/src/utils.js index 1f342665..3891a00a 100644 --- a/src/utils.js +++ b/src/utils.js @@ -10,27 +10,42 @@ const { join } = require('path') const getArguments = cliArgs => { la(is.strings(cliArgs), 'expected list of strings', cliArgs) - let start = 'start' + const service = { + start: 'start', + url: undefined + } + const services = [service] + let test = 'test' - let url if (cliArgs.length === 1 && isUrlOrPort(cliArgs[0])) { // passed just single url or port number, for example // "start": "http://localhost:8080" - url = normalizeUrl(cliArgs[0]) + service.url = normalizeUrl(cliArgs[0]) } else if (cliArgs.length === 2) { if (isUrlOrPort(cliArgs[0])) { // passed port and custom test command // like ":8080 test-ci" - url = normalizeUrl(cliArgs[0]) + service.url = normalizeUrl(cliArgs[0]) test = cliArgs[1] } if (isUrlOrPort(cliArgs[1])) { // passed start command and url/port // like "start-server 8080" - start = cliArgs[0] - url = normalizeUrl(cliArgs[1]) + service.start = cliArgs[0] + service.url = normalizeUrl(cliArgs[1]) + } + } else if (cliArgs.length === 5) { + service.start = cliArgs[0] + service.url = normalizeUrl(cliArgs[1]) + + const secondService = { + start: cliArgs[2], + url: normalizeUrl(cliArgs[3]) } + services.push(secondService) + + test = cliArgs[4] } else { la( cliArgs.length === 3, @@ -38,26 +53,27 @@ const getArguments = cliArgs => { 'example: start-test start 8080 test\n', 'see https://github.com/bahmutov/start-server-and-test#use\n' ) - start = cliArgs[0] - url = normalizeUrl(cliArgs[1]) + service.start = cliArgs[0] + service.url = normalizeUrl(cliArgs[1]) test = cliArgs[2] } - if (isPackageScriptName(start)) { - start = `npm run ${start}` - } + services.forEach(service => { + service.start = normalizeCommand(service.start) + }) - if (isPackageScriptName(test)) { - test = `npm run ${test}` - } + test = normalizeCommand(test) return { - start, - url, + services, test } } +function normalizeCommand (command) { + return UTILS.isPackageScriptName(command) ? `npm run ${command}` : command +} + /** * Returns true if the given string is a name of a script in the package.json file * in the current working directory @@ -130,9 +146,27 @@ const normalizeUrl = input => { }) } -module.exports = { +function printArguments ({ services, test }) { + services.forEach((service, k) => { + console.log('%d: starting server using command "%s"', k + 1, service.start) + console.log( + 'and when url "%s" is responding with HTTP status code 200', + service.url + ) + }) + + console.log('running tests using command "%s"', test) + console.log('') +} + +// placing functions into a common object +// makes them methods for easy stubbing +const UTILS = { getArguments, isPackageScriptName, isUrlOrPort, - normalizeUrl + normalizeUrl, + printArguments } + +module.exports = UTILS diff --git a/test/helper.js b/test/helper.js new file mode 100644 index 00000000..0ba851ba --- /dev/null +++ b/test/helper.js @@ -0,0 +1,14 @@ +// https://glebbahmutov.com/blog/mocha-and-sinon/ +const chai = require('chai') +const sinonChai = require('sinon-chai') +const sinon = require('sinon') + +chai.use(sinonChai) +global.expect = chai.expect + +before(() => { + global.sandbox = sinon.createSandbox() +}) +beforeEach(() => { + global.sandbox.restore() +})