blob: 914d7417b27be142be0a73311d3057bfeee2b204 [file] [log] [blame]
Yang Guo4fd355c2019-09-19 08:59:031/**
2 * Runner middleware is responsible for communication with `karma run`.
3 *
4 * It basically triggers a test run and streams stdout back.
5 */
6
7const _ = require('lodash')
8const path = require('path')
9const helper = require('../helper')
10const log = require('../logger').create()
11const constant = require('../constants')
12const json = require('body-parser').json()
13
14// TODO(vojta): disable when single-run mode
15function 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 emitter.once('run_start', function () {
39 const responseWrite = response.write.bind(response)
40 responseWrite.colors = data.colors
41 reporter.addAdapter(responseWrite)
42
43 // clean up, close runner response
44 emitter.once('run_complete', function (browsers, results) {
45 reporter.removeAdapter(responseWrite)
46 const emptyTestSuite = (results.failed + results.success) === 0 ? 0 : 1
47 response.end(constant.EXIT_CODE + emptyTestSuite + results.exitCode)
48 })
49 })
50
51 helper.restoreOriginalArgs(config)
52 if (_.isEmpty(data.args)) {
53 log.debug('Ignoring empty client.args from run command')
54 } else if ((_.isArray(data.args) && _.isArray(config.client.args)) ||
55 (_.isPlainObject(data.args) && _.isPlainObject(config.client.args))) {
56 log.debug('Merging client.args with ', data.args)
57 config.client.args = _.merge(config.client.args, data.args)
58 } else {
59 log.warn('Replacing client.args with ', data.args, ' as their types do not match.')
60 config.client.args = data.args
61 }
62
63 let fullRefresh = true
64
65 if (helper.isArray(data.changedFiles)) {
66 data.changedFiles.forEach(function (filepath) {
67 fileList.changeFile(path.resolve(config.basePath, filepath))
68 fullRefresh = false
69 })
70 }
71
72 if (helper.isArray(data.addedFiles)) {
73 data.addedFiles.forEach(function (filepath) {
74 fileList.addFile(path.resolve(config.basePath, filepath))
75 fullRefresh = false
76 })
77 }
78
79 if (helper.isArray(data.removedFiles)) {
80 data.removedFiles.forEach(function (filepath) {
81 fileList.removeFile(path.resolve(config.basePath, filepath))
82 fullRefresh = false
83 })
84 }
85
86 if (fullRefresh && data.refresh !== false) {
87 log.debug('Refreshing all the files / patterns')
88 fileList.refresh().then(function () {
89 // Wait for the file list refresh to complete before starting test run,
90 // otherwise the context.html generation might not see new/updated files.
91 if (!config.autoWatch) {
92 executor.schedule()
93 }
94 })
95 } else {
96 executor.schedule()
97 }
98 })
99 }
100}
101
102createRunnerMiddleware.$inject = ['emitter', 'fileList', 'capturedBrowsers', 'reporter', 'executor',
103 'config.protocol', 'config.hostname', 'config.port', 'config.urlRoot', 'config']
104
105// PUBLIC API
106exports.create = createRunnerMiddleware