Skip to content

Commit

Permalink
add enabled function to a task - fixes #42
Browse files Browse the repository at this point in the history
  • Loading branch information
SamVerschueren committed Jan 27, 2017
1 parent 7f44533 commit 477964d
Show file tree
Hide file tree
Showing 5 changed files with 143 additions and 6 deletions.
4 changes: 2 additions & 2 deletions example.js
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,8 @@ const tasks = new Listr([
},
{
title: 'Install dependencies with npm',
skip: ctx => ctx.yarn !== false && 'Already installed with Yarn',
task: () => delay(300)
enabled: ctx => ctx.yarn === false,
task: () => delay(3000)
},
{
title: 'Run tests',
Expand Down
22 changes: 20 additions & 2 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,13 @@ const Task = require('./lib/task');
const TaskWrapper = require('./lib/task-wrapper');
const renderer = require('./lib/renderer');

const runTask = (task, context) => new TaskWrapper(task).run(context);
const runTask = (task, context) => {
if (!task.isEnabled()) {
return Promise.resolve();
}

return new TaskWrapper(task).run(context);
};

class Listr {

Expand Down Expand Up @@ -31,6 +37,12 @@ class Listr {
this.add(tasks || []);
}

_checkAll(context) {
for (const task of this._tasks) {
task.check(context);
}
}

get tasks() {
return this._tasks;
}
Expand Down Expand Up @@ -62,11 +74,17 @@ class Listr {

context = context || Object.create(null);

this._checkAll(context);

let tasks;
if (this._options.concurrent === true) {
tasks = Promise.all(this._tasks.map(task => runTask(task, context)));
} else {
tasks = this._tasks.reduce((promise, task) => promise.then(() => runTask(task, context)), Promise.resolve());
tasks = this._tasks.reduce((promise, task) => promise.then(() => {
this._checkAll(context);

return runTask(task, context);
}), Promise.resolve());
}

return tasks
Expand Down
26 changes: 26 additions & 0 deletions lib/task.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,16 @@ class Task extends Subject {
throw new TypeError(`Expected property \`skip\` of type \`function\` but got \`${typeof task.skip}\``);
}

if (task.enabled && typeof task.enabled !== 'function') {
throw new TypeError(`Expected property \`enabled\` of type \`function\` but got \`${typeof task.enabled}\``);
}

this._listr = listr;
this._options = options || {};
this._subtasks = [];
this._output = undefined;
this._enabledFn = task.enabled;
this._isEnabled = true;

this.title = task.title;
this.skip = task.skip || defaultSkipFn;
Expand All @@ -60,6 +66,22 @@ class Task extends Subject {
return state.toString(this._state);
}

check(ctx) {
// Check if a task is enabled or disabled
if (this._state === undefined && this._enabledFn) {
const isEnabled = this._enabledFn(ctx);

if (this._isEnabled !== isEnabled) {
this._isEnabled = isEnabled;

this.next({
type: 'ENABLED',
data: isEnabled
});
}
}
}

hasSubtasks() {
return this._subtasks.length > 0;
}
Expand All @@ -76,6 +98,10 @@ class Task extends Subject {
return this._state === state.COMPLETED;
}

isEnabled() {
return this._isEnabled;
}

This comment has been minimized.

Copy link
@sindresorhus

sindresorhus Jan 28, 2017

Wouldn't it make more sense to make all these getter properties?


hasFailed() {
return this._state === state.FAILED;
}
Expand Down
37 changes: 35 additions & 2 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,11 +47,16 @@ const tasks = new Listr([
title: 'Install package dependencies with Yarn',
task: (ctx, task) => execa('yarn')
.catch(() => {
task.title = 'Install package dependencies with npm (Yarn not available)';
ctx.yarn === false;

return execa('npm', ['install']);
task.skip('Yarn not available, install it via `npm install -g yarn`');

This comment has been minimized.

Copy link
@sindresorhus

sindresorhus Jan 28, 2017

Maybe is after Yarn?

})
},
{
title: 'Install package dependencies with npm',
enabled: ctx => ctx.yarn === false,
task: () => execa('npm', ['install'])
},
{
title: 'Run tests',
task: () => execa('npm', ['test'])
Expand Down Expand Up @@ -179,6 +184,33 @@ const tasks = new Listr([

> Tip: You can still skip a task while already executing the `task` function with the [task object](#task-object).
## Enabling tasks

By default, every task is enabled which means that every task will be executed. However, it's also possible to provide an `enabled` function that returns wheter the task should be executed or not.

This comment has been minimized.

Copy link
@sindresorhus

sindresorhus Jan 28, 2017

wheter => whether

```js
const tasks = new Listr([
{
title: 'Install package dependencies with Yarn',
task: (ctx, task) => execa('yarn')
.catch(() => {
ctx.yarn === false;

task.skip('Yarn not available, install it via `npm install -g yarn`');
})
},
{
title: 'Install package dependencies with npm',
enabled: ctx => ctx.yarn === false,
task: () => execa('npm', ['install'])
}
]);
```

In the above example, we try to run `yarn` first, if that fails we will fall back to `npm`. However, at first only the Yarn task will be visible. Because we set the `yarn` flag of the [context](https://github.com/SamVerschueren/listr#context) object to `false`, the second task will automatically be enabled and will be executed.

> Note: This does not work in combination with [concurrent](https://github.com/SamVerschueren/listr#concurrent) tasks.

## Context

Expand Down Expand Up @@ -277,6 +309,7 @@ Every task is an observable. The task emits three different events and every eve
2. The task outputted data (`DATA`).
3. The task returns a subtask list (`SUBTASKS`).
4. The task's title changed (`TITLE`).
5. The task became enabled or disabled (`ENABLED`).

This allows you to flexibly build up your UI. Let's render every task that starts executing.

Expand Down
60 changes: 60 additions & 0 deletions test/enabled.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import {serial as test} from 'ava';
import Listr from '../';

This comment has been minimized.

Copy link
@sindresorhus

sindresorhus Jan 28, 2017

Btw, you don't need the / anymore with Node.js 4 and up. Same with ./ => ..

import SimpleRenderer from './fixtures/simple-renderer';
import {testOutput} from './fixtures/utils';

test('do not run disabled tasks', async t => {
const list = new Listr([
{
title: 'foo',
task: () => Promise.resolve()
},
{
title: 'bar',
enabled: ctx => ctx.run === true,
task: () => Promise.resolve()
}
], {
renderer: SimpleRenderer
});

testOutput(t, [
'foo [started]',
'foo [completed]',
'done'
]);

await list.run();
});

test('run enabled task', async t => {
const list = new Listr([
{
title: 'foo',
task: (ctx, task) => Promise.reject(new Error('Something went wrong'))
.catch(() => {
task.skip('It failed');

ctx.failed = true;
})
},
{
title: 'bar',
enabled: ctx => ctx.failed === true,
task: () => Promise.resolve()
}
], {
renderer: SimpleRenderer
});

testOutput(t, [
'foo [started]',
'foo [skipped]',
'> It failed',
'bar [started]',
'bar [completed]',
'done'
]);

await list.run();
});

0 comments on commit 477964d

Please sign in to comment.