mirror of https://github.com/thumbsup/thumbsup
refactor(components): bring ListrWorkQueue into this repo to simplify the build process
- This component was not published to the rpm registry anyway - We depended on the repo’s master branch which can break things for everyone - Its repo was not getting much attention which meant * no tests, no coverage report * no linting * no package linting (e.g. had 2 dependencies that weren’t actually used) It will be simpler to manage this waypull/118/head
parent
90cb3994eb
commit
bf60ae4677
@ -0,0 +1,56 @@
|
||||
const _ = require('lodash')
|
||||
const Listr = require('listr')
|
||||
|
||||
class ListrWorkQueue extends Listr {
|
||||
constructor (jobs, options) {
|
||||
options = _.defaults(options, {
|
||||
concurrent: 1,
|
||||
exitOnError: true
|
||||
})
|
||||
const threads = _.times(options.concurrent, i => {
|
||||
return {
|
||||
title: 'Waiting',
|
||||
task: (ctx, task) => {
|
||||
task.id = i
|
||||
return new Promise((resolve, reject) => {
|
||||
this.createWorker(task, jobs, err => {
|
||||
err ? reject(err) : resolve()
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
super(threads, options)
|
||||
this.options = options
|
||||
this.jobsTotal = jobs.length
|
||||
this.jobsDone = 0
|
||||
}
|
||||
|
||||
createWorker (task, jobs, done) {
|
||||
task.title = 'Finished'
|
||||
const job = jobs.shift()
|
||||
if (!job) {
|
||||
return done()
|
||||
} else {
|
||||
task.title = job.title
|
||||
}
|
||||
const nextJob = () => {
|
||||
setImmediate(() => this.createWorker(task, jobs, done))
|
||||
}
|
||||
job.task(null, task).then(() => {
|
||||
++this.jobsDone
|
||||
if (this.options.update) {
|
||||
this.options.update(this.jobsDone, this.jobsTotal)
|
||||
}
|
||||
nextJob()
|
||||
}).catch(err => {
|
||||
if (this.exitOnError) {
|
||||
done(new Error(`Error: ${err}`))
|
||||
} else {
|
||||
nextJob()
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = ListrWorkQueue
|
@ -0,0 +1,43 @@
|
||||
|
||||
/*
|
||||
Special Listr renderer that
|
||||
- on every change, renders the whole task list in memory
|
||||
- accumulates all rendered data into an array
|
||||
- has this array available as `listr._renderer.output`
|
||||
*/
|
||||
module.exports = class ListrTestRenderer {
|
||||
static get nonTTY () {
|
||||
return true
|
||||
}
|
||||
constructor (tasks) {
|
||||
this._tasks = tasks
|
||||
this.output = []
|
||||
}
|
||||
render () {
|
||||
for (let task of this._tasks) {
|
||||
this.subscribe(task)
|
||||
}
|
||||
}
|
||||
subscribe (task) {
|
||||
task.subscribe(
|
||||
event => {
|
||||
if (event.type === 'SUBTASKS') {
|
||||
// new subtasks: subscribe to them too
|
||||
task.subtasks.forEach(sub => this.subscribe(sub))
|
||||
} else {
|
||||
// something else happened, capture all titles
|
||||
const titles = this.allTitles(this._tasks, 0)
|
||||
this.output.push(titles)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
allTitles (tasks, indent) {
|
||||
return tasks.map(task => {
|
||||
const subTitles = this.allTitles(task.subtasks, indent + 1)
|
||||
return ' '.repeat(indent) + task.title + '\n' + subTitles
|
||||
}).join('')
|
||||
}
|
||||
end () {
|
||||
}
|
||||
}
|
@ -0,0 +1,90 @@
|
||||
const _ = require('lodash')
|
||||
const Listr = require('listr')
|
||||
const should = require('should/as-function')
|
||||
const ListrWorkQueue = require('../../../src/components/listr-work-queue/index')
|
||||
const ListrTestRenderer = require('./listr-test-renderer.js')
|
||||
|
||||
describe('Listr work queue', function () {
|
||||
this.slow(2000)
|
||||
this.timeout(2000)
|
||||
|
||||
it('processes all jobs in parallel', done => {
|
||||
const jobs = makeJobs(10)
|
||||
const listr = createWorkQueue(jobs, 3, null)
|
||||
listr.run().then(() => {
|
||||
const output = listr._renderer.output
|
||||
for (let i = 1; i <= 10; ++i) {
|
||||
hasItemMatching(output, `Job ${i}`)
|
||||
}
|
||||
done()
|
||||
})
|
||||
})
|
||||
|
||||
it('renders thread-like entries which keep changing title', done => {
|
||||
const jobs = makeJobs(10)
|
||||
const listr = createWorkQueue(jobs, 3, null)
|
||||
listr.run().then(() => {
|
||||
const output = listr._renderer.output
|
||||
// At some point a thread should have been waiting
|
||||
hasItemMatching(output, `Waiting`)
|
||||
// And a thread should have finished
|
||||
hasItemMatching(output, `Finished`)
|
||||
// And every single render should conform to a particular format
|
||||
const regex = /^Running jobs\n((\s\s(Waiting|Finished|Job \d+)\n){3})?$/
|
||||
for (let line of output) {
|
||||
if (!regex.test(line)) {
|
||||
should.fail(`Listr output does not match expected format: ${line}`)
|
||||
}
|
||||
}
|
||||
done()
|
||||
})
|
||||
})
|
||||
|
||||
it('reports progress', done => {
|
||||
const jobs = makeJobs(10)
|
||||
const listr = createWorkQueue(jobs, 3, (done, total) => {
|
||||
const progress = done === total ? '' : ` (${done}/${total})`
|
||||
return `Running jobs${progress}`
|
||||
})
|
||||
listr.run().then(() => {
|
||||
const output = listr._renderer.output
|
||||
for (let i = 1; i < 10; ++i) {
|
||||
hasItemMatching(output, `Running jobs (${i}/10)`)
|
||||
}
|
||||
done()
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
function createWorkQueue (jobs, concurrency, updater) {
|
||||
const task = {
|
||||
title: 'Running jobs',
|
||||
task: (ctx, task) => new ListrWorkQueue(jobs, {
|
||||
concurrent: concurrency,
|
||||
update: (done, total) => {
|
||||
if (updater) {
|
||||
task.title = updater(done, total)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
return new Listr([task], {
|
||||
renderer: ListrTestRenderer
|
||||
})
|
||||
}
|
||||
|
||||
function makeJobs (count) {
|
||||
return _.times(count, i => {
|
||||
return {
|
||||
title: `Job ${i + 1}`,
|
||||
task: () => new Promise(resolve => setTimeout(resolve, 100))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function hasItemMatching (list, substring) {
|
||||
const match = _.some(list, item => item.includes(substring))
|
||||
if (!match) {
|
||||
should.fail(`Excepted ${substring} to be present in ${list}`)
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue