1 |
|
2 | 'use strict'
|
3 |
|
4 | const la = require('lazy-ass')
|
5 | const is = require('check-more-types')
|
6 | const execa = require('execa')
|
7 | const waitOn = require('wait-on')
|
8 | const Promise = require('bluebird')
|
9 | const psTree = require('ps-tree')
|
10 | const debug = require('debug')('start-server-and-test')
|
11 |
|
12 |
|
13 |
|
14 |
|
15 | const fiveMinutes = 5 * 60 * 1000
|
16 | const waitOnTimeout = process.env.WAIT_ON_TIMEOUT
|
17 | ? Number(process.env.WAIT_ON_TIMEOUT)
|
18 | : fiveMinutes
|
19 |
|
20 | const isDebug = () =>
|
21 | process.env.DEBUG && process.env.DEBUG.indexOf('start-server-and-test') !== -1
|
22 |
|
23 | const isInsecure = () => process.env.START_SERVER_AND_TEST_INSECURE
|
24 |
|
25 | function waitAndRun ({ start, url, runFn }) {
|
26 | la(is.unemptyString(start), 'missing start script name', start)
|
27 | la(is.fn(runFn), 'missing test script name', runFn)
|
28 | la(
|
29 | is.unemptyString(url) || is.unemptyArray(url),
|
30 | 'missing url to wait on',
|
31 | url
|
32 | )
|
33 |
|
34 | debug('starting server with command "%s", verbose mode?', start, isDebug())
|
35 |
|
36 | const server = execa(start, { shell: true, stdio: 'inherit' })
|
37 | let serverStopped
|
38 |
|
39 | function stopServer () {
|
40 | debug('getting child processes')
|
41 | if (!serverStopped) {
|
42 | serverStopped = true
|
43 | return Promise.fromNode(cb => psTree(server.pid, cb))
|
44 | .then(children => {
|
45 | debug('stopping child processes')
|
46 | children.forEach(child => {
|
47 | try {
|
48 | process.kill(child.PID, 'SIGINT')
|
49 | } catch (e) {
|
50 | if (e.code === 'ESRCH') {
|
51 | console.log(
|
52 | `Child process ${child.PID} exited before trying to stop it`
|
53 | )
|
54 | } else {
|
55 | throw e
|
56 | }
|
57 | }
|
58 | })
|
59 | })
|
60 | .then(() => {
|
61 | debug('stopping server')
|
62 | server.kill()
|
63 | })
|
64 | }
|
65 | }
|
66 |
|
67 | const waited = new Promise((resolve, reject) => {
|
68 | const onClose = () => {
|
69 | reject(new Error('server closed unexpectedly'))
|
70 | }
|
71 |
|
72 | server.on('close', onClose)
|
73 |
|
74 | debug('starting waitOn %s', url)
|
75 | const options = {
|
76 | resources: Array.isArray(url) ? url : [url],
|
77 | interval: 2000,
|
78 | window: 1000,
|
79 | timeout: waitOnTimeout,
|
80 | verbose: isDebug(),
|
81 | strictSSL: !isInsecure(),
|
82 | log: isDebug(),
|
83 | headers: {
|
84 | Accept: 'text/html, application/json, text/plain, */*'
|
85 | }
|
86 | }
|
87 | debug('wait-on options %o', options)
|
88 |
|
89 | waitOn(options, err => {
|
90 | if (err) {
|
91 | debug('error waiting for url', url)
|
92 | debug(err.message)
|
93 | return reject(err)
|
94 | }
|
95 | debug('waitOn finished successfully')
|
96 | server.removeListener('close', onClose)
|
97 | resolve()
|
98 | })
|
99 | })
|
100 |
|
101 | return waited
|
102 | .tapCatch(stopServer)
|
103 | .then(runFn)
|
104 | .finally(stopServer)
|
105 | }
|
106 |
|
107 | const runTheTests = testCommand => () => {
|
108 | debug('running test script command: %s', testCommand)
|
109 | return execa(testCommand, { shell: true, stdio: 'inherit' })
|
110 | }
|
111 |
|
112 |
|
113 |
|
114 |
|
115 |
|
116 | function startAndTest ({ services, test }) {
|
117 | if (services.length === 0) {
|
118 | throw new Error('Got zero services to start ...')
|
119 | }
|
120 |
|
121 | if (services.length === 1) {
|
122 | const runTests = runTheTests(test)
|
123 | debug('single service "%s" to run and test', services[0].start)
|
124 | return waitAndRun({
|
125 | start: services[0].start,
|
126 | url: services[0].url,
|
127 | runFn: runTests
|
128 | })
|
129 | }
|
130 |
|
131 | return waitAndRun({
|
132 | start: services[0].start,
|
133 | url: services[0].url,
|
134 | runFn: () => {
|
135 | debug('previous service started, now going to the next one')
|
136 | return startAndTest({ services: services.slice(1), test })
|
137 | }
|
138 | })
|
139 | }
|
140 |
|
141 | module.exports = {
|
142 | startAndTest
|
143 | }
|