[6a3a178] | 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
|
---|