1 | /**
|
---|
2 | * Runner middleware is responsible for communication with `karma run`.
|
---|
3 | *
|
---|
4 | * It basically triggers a test run and streams stdout back.
|
---|
5 | */
|
---|
6 |
|
---|
7 | const _ = require('lodash')
|
---|
8 | const path = require('path')
|
---|
9 | const helper = require('../helper')
|
---|
10 | const log = require('../logger').create()
|
---|
11 | const constant = require('../constants')
|
---|
12 | const json = require('body-parser').json()
|
---|
13 |
|
---|
14 | // TODO(vojta): disable when single-run mode
|
---|
15 | function createRunnerMiddleware (emitter, fileList, capturedBrowsers, reporter, executor,
|
---|
16 | /* config.protocol */ protocol, /* config.hostname */ hostname, /* config.port */
|
---|
17 | port, /* config.urlRoot */ urlRoot, config) {
|
---|
18 | helper.saveOriginalArgs(config)
|
---|
19 | return function (request, response, next) {
|
---|
20 | if (request.url !== '/__run__' && request.url !== urlRoot + 'run') {
|
---|
21 | return next()
|
---|
22 | }
|
---|
23 |
|
---|
24 | log.debug('Execution (fired by runner)')
|
---|
25 | response.writeHead(200)
|
---|
26 |
|
---|
27 | if (!capturedBrowsers.length) {
|
---|
28 | const url = `${protocol}//${hostname}:${port}${urlRoot}`
|
---|
29 | return response.end(`No captured browser, open ${url}\n`)
|
---|
30 | }
|
---|
31 |
|
---|
32 | json(request, response, function () {
|
---|
33 | if (!capturedBrowsers.areAllReady([])) {
|
---|
34 | response.write('Waiting for previous execution...\n')
|
---|
35 | }
|
---|
36 |
|
---|
37 | const data = request.body
|
---|
38 |
|
---|
39 | updateClientArgs(data)
|
---|
40 | handleRun(data)
|
---|
41 | refreshFileList(data).then(() => {
|
---|
42 | executor.schedule()
|
---|
43 | }).catch((error) => {
|
---|
44 | const errorMessage = `Error during refresh file list. ${error.stack || error}`
|
---|
45 | executor.scheduleError(errorMessage)
|
---|
46 | })
|
---|
47 | })
|
---|
48 |
|
---|
49 | function updateClientArgs (data) {
|
---|
50 | helper.restoreOriginalArgs(config)
|
---|
51 | if (_.isEmpty(data.args)) {
|
---|
52 | log.debug('Ignoring empty client.args from run command')
|
---|
53 | } else if ((_.isArray(data.args) && _.isArray(config.client.args)) ||
|
---|
54 | (_.isPlainObject(data.args) && _.isPlainObject(config.client.args))) {
|
---|
55 | log.debug('Merging client.args with ', data.args)
|
---|
56 | config.client.args = _.merge(config.client.args, data.args)
|
---|
57 | } else {
|
---|
58 | log.warn('Replacing client.args with ', data.args, ' as their types do not match.')
|
---|
59 | config.client.args = data.args
|
---|
60 | }
|
---|
61 | }
|
---|
62 |
|
---|
63 | async function refreshFileList (data) {
|
---|
64 | let fullRefresh = true
|
---|
65 |
|
---|
66 | if (helper.isArray(data.changedFiles)) {
|
---|
67 | await Promise.all(data.changedFiles.map(async function (filepath) {
|
---|
68 | await fileList.changeFile(path.resolve(config.basePath, filepath))
|
---|
69 | fullRefresh = false
|
---|
70 | }))
|
---|
71 | }
|
---|
72 |
|
---|
73 | if (helper.isArray(data.addedFiles)) {
|
---|
74 | await Promise.all(data.addedFiles.map(async function (filepath) {
|
---|
75 | await fileList.addFile(path.resolve(config.basePath, filepath))
|
---|
76 | fullRefresh = false
|
---|
77 | }))
|
---|
78 | }
|
---|
79 |
|
---|
80 | if (helper.isArray(data.removedFiles)) {
|
---|
81 | await Promise.all(data.removedFiles.map(async function (filepath) {
|
---|
82 | await fileList.removeFile(path.resolve(config.basePath, filepath))
|
---|
83 | fullRefresh = false
|
---|
84 | }))
|
---|
85 | }
|
---|
86 |
|
---|
87 | if (fullRefresh && data.refresh !== false) {
|
---|
88 | log.debug('Refreshing all the files / patterns')
|
---|
89 | await fileList.refresh()
|
---|
90 | }
|
---|
91 | }
|
---|
92 |
|
---|
93 | function handleRun (data) {
|
---|
94 | emitter.once('run_start', function () {
|
---|
95 | const responseWrite = response.write.bind(response)
|
---|
96 | responseWrite.colors = data.colors
|
---|
97 | reporter.addAdapter(responseWrite)
|
---|
98 |
|
---|
99 | // clean up, close runner response
|
---|
100 | emitter.once('run_complete', function (_browsers, results) {
|
---|
101 | reporter.removeAdapter(responseWrite)
|
---|
102 | const emptyTestSuite = (results.failed + results.success) === 0 ? 0 : 1
|
---|
103 | response.end(constant.EXIT_CODE + emptyTestSuite + results.exitCode)
|
---|
104 | })
|
---|
105 | })
|
---|
106 | }
|
---|
107 | }
|
---|
108 | }
|
---|
109 |
|
---|
110 | createRunnerMiddleware.$inject = ['emitter', 'fileList', 'capturedBrowsers', 'reporter', 'executor',
|
---|
111 | 'config.protocol', 'config.hostname', 'config.port', 'config.urlRoot', 'config']
|
---|
112 |
|
---|
113 | // PUBLIC API
|
---|
114 | exports.create = createRunnerMiddleware
|
---|