From 5a1272a4bf4ed2364bfbd0bce4b057527efc254d Mon Sep 17 00:00:00 2001 From: Bunyanuch Saengnet Date: Mon, 2 Sep 2019 17:43:14 +0000 Subject: [PATCH 01/12] added suspending update renderer for task --- lib/task.js | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/lib/task.js b/lib/task.js index 9413e7e..9194640 100644 --- a/lib/task.js +++ b/lib/task.js @@ -43,12 +43,21 @@ class Task extends Subject { this.title = task.title; this.skip = task.skip || defaultSkipFn; this.task = task.task; + this.taskOptions = task.options; } get subtasks() { return this._subtasks; } + shouldSuspendUpdateRenderer() { + try{ + return this.taskOptions.suspendUpdateRenderer; + }catch(TypeError){ + return false; + } + } + set state(state) { this._state = state; From 470a8b8294ef10b152e12598b33f2615e4d96f88 Mon Sep 17 00:00:00 2001 From: Bunyanuch Saengnet Date: Fri, 6 Sep 2019 14:36:31 +0000 Subject: [PATCH 02/12] fixed clinton && xo && nyc ava errors --- lib/task.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/lib/task.js b/lib/task.js index 9194640..87a7516 100644 --- a/lib/task.js +++ b/lib/task.js @@ -44,6 +44,7 @@ class Task extends Subject { this.skip = task.skip || defaultSkipFn; this.task = task.task; this.taskOptions = task.options; + } get subtasks() { @@ -51,12 +52,12 @@ class Task extends Subject { } shouldSuspendUpdateRenderer() { - try{ + try { return this.taskOptions.suspendUpdateRenderer; - }catch(TypeError){ + } catch (error) { return false; } - } + } set state(state) { this._state = state; From c0317c71704b277b65f44ee03134d8e0c452ca35 Mon Sep 17 00:00:00 2001 From: Bunyanuch Saengnet Date: Tue, 17 Sep 2019 19:10:18 +0000 Subject: [PATCH 03/12] unit-test for suspending update-rendering added --- lib/task.js | 2 +- package.json | 2 ++ test/concurrent.js | 2 +- test/enabled.js | 2 +- test/exit-on-error.js | 2 +- test/output.js | 2 +- test/renderer.js | 2 +- test/skip.js | 2 +- test/stream.js | 2 +- test/subtask.js | 2 +- test/suspend.js | 68 +++++++++++++++++++++++++++++++++++++++++++ test/task-wrapper.js | 2 +- test/tty.js | 2 +- test/utils.js | 2 +- 14 files changed, 82 insertions(+), 12 deletions(-) create mode 100644 test/suspend.js diff --git a/lib/task.js b/lib/task.js index 87a7516..9ba1f63 100644 --- a/lib/task.js +++ b/lib/task.js @@ -44,7 +44,6 @@ class Task extends Subject { this.skip = task.skip || defaultSkipFn; this.task = task.task; this.taskOptions = task.options; - } get subtasks() { @@ -170,6 +169,7 @@ class Task extends Subject { if (typeof skipped === 'string') { this.output = skipped; } + this.state = state.SKIPPED; return; } diff --git a/package.json b/package.json index 19129c8..277fed2 100644 --- a/package.json +++ b/package.json @@ -56,8 +56,10 @@ "hook-std": "^1.1.0", "lint-staged": "^8.0.5", "log-symbols": "^2.2.0", + "mockery": "^2.1.0", "nyc": "^13.1.0", "pre-commit": "^1.2.2", + "sinon": "^7.4.2", "split": "^1.0.1", "xo": "*", "zen-observable": "^0.8.11" diff --git a/test/concurrent.js b/test/concurrent.js index 8a1c095..808af65 100644 --- a/test/concurrent.js +++ b/test/concurrent.js @@ -1,8 +1,8 @@ import {serial as test} from 'ava'; import delay from 'delay'; -import Listr from '..'; import SimpleRenderer from './fixtures/simple-renderer'; import {testOutput} from './fixtures/utils'; +import Listr from '..'; const tasks = [ { diff --git a/test/enabled.js b/test/enabled.js index ac19cb1..850f2e9 100644 --- a/test/enabled.js +++ b/test/enabled.js @@ -1,7 +1,7 @@ import {serial as test} from 'ava'; -import Listr from '..'; import SimpleRenderer from './fixtures/simple-renderer'; import {testOutput} from './fixtures/utils'; +import Listr from '..'; test('do not run disabled tasks', async t => { const list = new Listr([ diff --git a/test/exit-on-error.js b/test/exit-on-error.js index 8196e9b..6d2fc15 100644 --- a/test/exit-on-error.js +++ b/test/exit-on-error.js @@ -1,7 +1,7 @@ import {serial as test} from 'ava'; -import Listr from '..'; import SimpleRenderer from './fixtures/simple-renderer'; import {testOutput} from './fixtures/utils'; +import Listr from '..'; const tasks = [ { diff --git a/test/output.js b/test/output.js index 30fc3dc..4bf1caa 100644 --- a/test/output.js +++ b/test/output.js @@ -1,8 +1,8 @@ import test from 'ava'; import {Observable} from 'rxjs'; -import Listr from '..'; import SimpleRenderer from './fixtures/simple-renderer'; import {testOutput} from './fixtures/utils'; +import Listr from '..'; test('output', async t => { const list = new Listr([ diff --git a/test/renderer.js b/test/renderer.js index 711172f..72c4c69 100644 --- a/test/renderer.js +++ b/test/renderer.js @@ -1,7 +1,7 @@ import test from 'ava'; -import Listr from '..'; import SimpleRenderer from './fixtures/simple-renderer'; import {testOutput} from './fixtures/utils'; +import Listr from '..'; test('renderer class', async t => { const list = new Listr([ diff --git a/test/skip.js b/test/skip.js index e62fc4c..1453cd5 100644 --- a/test/skip.js +++ b/test/skip.js @@ -1,7 +1,7 @@ import test from 'ava'; -import Listr from '..'; import SimpleRenderer from './fixtures/simple-renderer'; import {testOutput} from './fixtures/utils'; +import Listr from '..'; test('continue execution if skip() returns false or Promise for false', async t => { t.plan(5); // Verify the correct number of tasks were executed diff --git a/test/stream.js b/test/stream.js index 4241b19..022d614 100644 --- a/test/stream.js +++ b/test/stream.js @@ -2,9 +2,9 @@ import * as fs from 'fs'; import * as path from 'path'; import test from 'ava'; import split from 'split'; -import Listr from '..'; import SimpleRenderer from './fixtures/simple-renderer'; import {testOutput} from './fixtures/utils'; +import Listr from '..'; test('output', async t => { const list = new Listr([ diff --git a/test/subtask.js b/test/subtask.js index 5138f61..49fafa4 100644 --- a/test/subtask.js +++ b/test/subtask.js @@ -1,7 +1,7 @@ import test from 'ava'; -import Listr from '..'; import SimpleRenderer from './fixtures/simple-renderer'; import {testOutput} from './fixtures/utils'; +import Listr from '..'; test('renderer class', async t => { const list = new Listr([ diff --git a/test/suspend.js b/test/suspend.js new file mode 100644 index 0000000..2ca0f48 --- /dev/null +++ b/test/suspend.js @@ -0,0 +1,68 @@ +import {serial as test} from 'ava'; +import sinon from 'sinon'; + +const mockery = require('mockery'); + +const logUpdateApi = { + main(text) { + console.log(text); + } +}; +logUpdateApi.main.clear = function () { +}; + +logUpdateApi.main.done = function () { +}; + +const mock = sinon.mock(logUpdateApi); + +function getTasks() { + // Require is necessary at this position, otherwise mockery is not working correctly + const Listr = require('..'); + const tasks = new Listr([ + { + title: 'not suspending task', + task: () => { + return new Promise(resolve => { + setTimeout(() => { + resolve('predefined output'); + }, 200); + }); + } + }, + { + title: 'suspending task', + task: () => { + return new Promise(resolve => { + setTimeout(() => { + resolve('predefined output'); + }, 4000); + }); + }, + options: {suspendUpdateRenderer: true} + } + ]); + + return tasks; +} + +test.before(() => { + /* One time for the first task, + one time when both tasks are finished + */ + mock.expects('main').twice(); + mockery.registerAllowable('..'); + mockery.registerMock('log-update', logUpdateApi.main); + mockery.enable({useCleanCache: true, warnOnUnregistered: false}); +}); + +test('should suspend second task', async t => { + await getTasks().run(); + + t.true(mock.verify()); +}); + +test.after(() => { + mockery.disable(); + mockery.deregisterAll(); +}); diff --git a/test/task-wrapper.js b/test/task-wrapper.js index d457de3..14de3ad 100644 --- a/test/task-wrapper.js +++ b/test/task-wrapper.js @@ -1,7 +1,7 @@ import {serial as test} from 'ava'; -import Listr from '..'; import SimpleRenderer from './fixtures/simple-renderer'; import {testOutput} from './fixtures/utils'; +import Listr from '..'; test('changing the title during task execution', async t => { const list = new Listr([ diff --git a/test/tty.js b/test/tty.js index 9629178..5a666b5 100644 --- a/test/tty.js +++ b/test/tty.js @@ -1,9 +1,9 @@ import test from 'ava'; import {Observable} from 'rxjs'; -import Listr from '..'; import SimpleRenderer from './fixtures/simple-renderer'; import TTYRenderer from './fixtures/tty-renderer'; import {testOutput} from './fixtures/utils'; +import Listr from '..'; const ttyOutput = [ 'foo [started]', diff --git a/test/utils.js b/test/utils.js index 9766d01..6d1b57a 100644 --- a/test/utils.js +++ b/test/utils.js @@ -1,8 +1,8 @@ import test from 'ava'; import {Observable as RxObservable} from 'rxjs'; import ZenObservable from 'zen-observable'; -import Listr from '..'; import {isListr, isObservable} from '../lib/utils'; +import Listr from '..'; test('isListr', t => { t.false(isListr(null)); From e440c186731703b56b7851157ab5e4df7c244d64 Mon Sep 17 00:00:00 2001 From: Bunyanuch Saengnet Date: Tue, 17 Sep 2019 19:36:12 +0000 Subject: [PATCH 04/12] documentation for suspending-feature --- readme.md | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/readme.md b/readme.md index e9936fd..bb7fa06 100644 --- a/readme.md +++ b/readme.md @@ -296,6 +296,28 @@ tasks.run(); ``` +## Rendering options + +By default, [listr-update-renderer](https://github.com/SamVerschueren/listr-update-renderer) is active. This renderer refreshes the state of the tasks every 100 milliseconds. With the option ```suspendUpdateRenderer``` the refresh can be suspended for the time the task is running. After the task is completed, the listr-update-renderer starts again refreshing the state of the tasks. + +```js +const tasks = new Listr([ + { + title: 'Encrypt gpg-file', + task: () => execa('gpg', ['-e', 'file']), + options: {suspendUpdateRenderer: true} + } + { + title: 'Remove the unencrypted file', + task: () => execa('rm', ['file']) + } +]); + +tasks.run(); +``` + +This feature is usefull for tasks with user-input. The user-input is always gobbled up, when the listr-update-renderer is not in suspend-mode. + ## Custom renderers It's possible to write custom renderers for Listr. A renderer is an ES6 class that accepts the tasks that it should render, and the Listr options object. It has two methods, the `render` method which is called when it should start rendering, and the `end` method. The `end` method is called when all the tasks are completed or if a task failed. If a task failed, the error object is passed in via an argument. From 7a74f5d1665a6d69bd00a07d1ab3dbbe9bad8ce8 Mon Sep 17 00:00:00 2001 From: Bunyanuch Saengnet Date: Wed, 18 Sep 2019 16:21:42 +0000 Subject: [PATCH 05/12] Moved feature from listr-update-renderer to listr according to issue-comment: https://github.com/SamVerschueren/listr-update-renderer/pull/23/files/90210b0491209532723cbc6fe9f568c9d47fb357#r324416643 --- lib/task.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/lib/task.js b/lib/task.js index 9ba1f63..d189238 100644 --- a/lib/task.js +++ b/lib/task.js @@ -51,11 +51,10 @@ class Task extends Subject { } shouldSuspendUpdateRenderer() { - try { + if (this.isEnabled() && this.isPending() && !this.isCompleted() && this.taskOptions !== undefined) { return this.taskOptions.suspendUpdateRenderer; - } catch (error) { - return false; } + return false; } set state(state) { From 4b2c8012307aba7c13662e7a1937658434df569d Mon Sep 17 00:00:00 2001 From: Bunyanuch Saengnet Date: Wed, 18 Sep 2019 16:37:06 +0000 Subject: [PATCH 06/12] fixed issue in unit-test --- test/suspend.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/suspend.js b/test/suspend.js index 2ca0f48..5d5e0b9 100644 --- a/test/suspend.js +++ b/test/suspend.js @@ -26,7 +26,7 @@ function getTasks() { return new Promise(resolve => { setTimeout(() => { resolve('predefined output'); - }, 200); + }, 150); }); } }, From f2e2750f3a3460a841b92f6e5f603e080c7e03c6 Mon Sep 17 00:00:00 2001 From: Bunyanuch Saengnet Date: Wed, 18 Sep 2019 16:41:28 +0000 Subject: [PATCH 07/12] fixed xo-error --- lib/task.js | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/task.js b/lib/task.js index d189238..c5df930 100644 --- a/lib/task.js +++ b/lib/task.js @@ -54,6 +54,7 @@ class Task extends Subject { if (this.isEnabled() && this.isPending() && !this.isCompleted() && this.taskOptions !== undefined) { return this.taskOptions.suspendUpdateRenderer; } + return false; } From 8455f6317d3cedaf9813cf6fb167368839d2f014 Mon Sep 17 00:00:00 2001 From: Bunyanuch Saengnet Date: Sat, 4 Jan 2020 13:53:56 +0000 Subject: [PATCH 08/12] Added suspending update renderer for task: 1. Refactoring test-classes and readme 2. play back import order --- lib/task.js | 3 +-- package.json | 1 + readme.md | 6 +++--- test/concurrent.js | 2 +- test/enabled.js | 2 +- test/exit-on-error.js | 2 +- test/output.js | 2 +- test/renderer.js | 2 +- test/skip.js | 2 +- test/stream.js | 2 +- test/subtask.js | 2 +- test/suspend.js | 19 ++++++------------- test/task-wrapper.js | 2 +- test/tty.js | 2 +- test/utils.js | 2 +- 15 files changed, 22 insertions(+), 29 deletions(-) diff --git a/lib/task.js b/lib/task.js index c5df930..9355e7b 100644 --- a/lib/task.js +++ b/lib/task.js @@ -51,7 +51,7 @@ class Task extends Subject { } shouldSuspendUpdateRenderer() { - if (this.isEnabled() && this.isPending() && !this.isCompleted() && this.taskOptions !== undefined) { + if (this.isEnabled() && this.isPending() && !this.isCompleted() && this.taskOptions) { return this.taskOptions.suspendUpdateRenderer; } @@ -169,7 +169,6 @@ class Task extends Subject { if (typeof skipped === 'string') { this.output = skipped; } - this.state = state.SKIPPED; return; } diff --git a/package.json b/package.json index 277fed2..49c8689 100644 --- a/package.json +++ b/package.json @@ -56,6 +56,7 @@ "hook-std": "^1.1.0", "lint-staged": "^8.0.5", "log-symbols": "^2.2.0", + "log-update": "^2.3.0", "mockery": "^2.1.0", "nyc": "^13.1.0", "pre-commit": "^1.2.2", diff --git a/readme.md b/readme.md index bb7fa06..153ad03 100644 --- a/readme.md +++ b/readme.md @@ -298,7 +298,7 @@ tasks.run(); ## Rendering options -By default, [listr-update-renderer](https://github.com/SamVerschueren/listr-update-renderer) is active. This renderer refreshes the state of the tasks every 100 milliseconds. With the option ```suspendUpdateRenderer``` the refresh can be suspended for the time the task is running. After the task is completed, the listr-update-renderer starts again refreshing the state of the tasks. +By default, the [`listr-update-renderer`](https://github.com/SamVerschueren/listr-update-renderer) renderer is used. The renderer updates the state of the tasks every 100ms. When the `suspendUpdateRenderer` option is supplied, the state updates can be suspended while the task is running. After the task is completed, the renderer will resume updating the state of the tasks. ```js const tasks = new Listr([ @@ -306,7 +306,7 @@ const tasks = new Listr([ title: 'Encrypt gpg-file', task: () => execa('gpg', ['-e', 'file']), options: {suspendUpdateRenderer: true} - } + }, { title: 'Remove the unencrypted file', task: () => execa('rm', ['file']) @@ -316,7 +316,7 @@ const tasks = new Listr([ tasks.run(); ``` -This feature is usefull for tasks with user-input. The user-input is always gobbled up, when the listr-update-renderer is not in suspend-mode. +The `suspendUpdateRenderer` option is useful for tasks which receive user input while running, as without it the input will be ignored by the renderer. ## Custom renderers diff --git a/test/concurrent.js b/test/concurrent.js index 808af65..8a1c095 100644 --- a/test/concurrent.js +++ b/test/concurrent.js @@ -1,8 +1,8 @@ import {serial as test} from 'ava'; import delay from 'delay'; +import Listr from '..'; import SimpleRenderer from './fixtures/simple-renderer'; import {testOutput} from './fixtures/utils'; -import Listr from '..'; const tasks = [ { diff --git a/test/enabled.js b/test/enabled.js index 850f2e9..ac19cb1 100644 --- a/test/enabled.js +++ b/test/enabled.js @@ -1,7 +1,7 @@ import {serial as test} from 'ava'; +import Listr from '..'; import SimpleRenderer from './fixtures/simple-renderer'; import {testOutput} from './fixtures/utils'; -import Listr from '..'; test('do not run disabled tasks', async t => { const list = new Listr([ diff --git a/test/exit-on-error.js b/test/exit-on-error.js index 6d2fc15..8196e9b 100644 --- a/test/exit-on-error.js +++ b/test/exit-on-error.js @@ -1,7 +1,7 @@ import {serial as test} from 'ava'; +import Listr from '..'; import SimpleRenderer from './fixtures/simple-renderer'; import {testOutput} from './fixtures/utils'; -import Listr from '..'; const tasks = [ { diff --git a/test/output.js b/test/output.js index 4bf1caa..30fc3dc 100644 --- a/test/output.js +++ b/test/output.js @@ -1,8 +1,8 @@ import test from 'ava'; import {Observable} from 'rxjs'; +import Listr from '..'; import SimpleRenderer from './fixtures/simple-renderer'; import {testOutput} from './fixtures/utils'; -import Listr from '..'; test('output', async t => { const list = new Listr([ diff --git a/test/renderer.js b/test/renderer.js index 72c4c69..711172f 100644 --- a/test/renderer.js +++ b/test/renderer.js @@ -1,7 +1,7 @@ import test from 'ava'; +import Listr from '..'; import SimpleRenderer from './fixtures/simple-renderer'; import {testOutput} from './fixtures/utils'; -import Listr from '..'; test('renderer class', async t => { const list = new Listr([ diff --git a/test/skip.js b/test/skip.js index 1453cd5..e62fc4c 100644 --- a/test/skip.js +++ b/test/skip.js @@ -1,7 +1,7 @@ import test from 'ava'; +import Listr from '..'; import SimpleRenderer from './fixtures/simple-renderer'; import {testOutput} from './fixtures/utils'; -import Listr from '..'; test('continue execution if skip() returns false or Promise for false', async t => { t.plan(5); // Verify the correct number of tasks were executed diff --git a/test/stream.js b/test/stream.js index 022d614..4241b19 100644 --- a/test/stream.js +++ b/test/stream.js @@ -2,9 +2,9 @@ import * as fs from 'fs'; import * as path from 'path'; import test from 'ava'; import split from 'split'; +import Listr from '..'; import SimpleRenderer from './fixtures/simple-renderer'; import {testOutput} from './fixtures/utils'; -import Listr from '..'; test('output', async t => { const list = new Listr([ diff --git a/test/subtask.js b/test/subtask.js index 49fafa4..5138f61 100644 --- a/test/subtask.js +++ b/test/subtask.js @@ -1,7 +1,7 @@ import test from 'ava'; +import Listr from '..'; import SimpleRenderer from './fixtures/simple-renderer'; import {testOutput} from './fixtures/utils'; -import Listr from '..'; test('renderer class', async t => { const list = new Listr([ diff --git a/test/suspend.js b/test/suspend.js index 5d5e0b9..6c399e2 100644 --- a/test/suspend.js +++ b/test/suspend.js @@ -4,14 +4,7 @@ import sinon from 'sinon'; const mockery = require('mockery'); const logUpdateApi = { - main(text) { - console.log(text); - } -}; -logUpdateApi.main.clear = function () { -}; - -logUpdateApi.main.done = function () { + main: require('log-update') }; const mock = sinon.mock(logUpdateApi); @@ -56,13 +49,13 @@ test.before(() => { mockery.enable({useCleanCache: true, warnOnUnregistered: false}); }); +test.after(() => { + mockery.disable(); + mockery.deregisterAll(); +}); + test('should suspend second task', async t => { await getTasks().run(); t.true(mock.verify()); }); - -test.after(() => { - mockery.disable(); - mockery.deregisterAll(); -}); diff --git a/test/task-wrapper.js b/test/task-wrapper.js index 14de3ad..d457de3 100644 --- a/test/task-wrapper.js +++ b/test/task-wrapper.js @@ -1,7 +1,7 @@ import {serial as test} from 'ava'; +import Listr from '..'; import SimpleRenderer from './fixtures/simple-renderer'; import {testOutput} from './fixtures/utils'; -import Listr from '..'; test('changing the title during task execution', async t => { const list = new Listr([ diff --git a/test/tty.js b/test/tty.js index 5a666b5..9629178 100644 --- a/test/tty.js +++ b/test/tty.js @@ -1,9 +1,9 @@ import test from 'ava'; import {Observable} from 'rxjs'; +import Listr from '..'; import SimpleRenderer from './fixtures/simple-renderer'; import TTYRenderer from './fixtures/tty-renderer'; import {testOutput} from './fixtures/utils'; -import Listr from '..'; const ttyOutput = [ 'foo [started]', diff --git a/test/utils.js b/test/utils.js index 6d1b57a..9766d01 100644 --- a/test/utils.js +++ b/test/utils.js @@ -1,8 +1,8 @@ import test from 'ava'; import {Observable as RxObservable} from 'rxjs'; import ZenObservable from 'zen-observable'; -import {isListr, isObservable} from '../lib/utils'; import Listr from '..'; +import {isListr, isObservable} from '../lib/utils'; test('isListr', t => { t.false(isListr(null)); From 6eeb4c483f5a5fa20bf1b756616c196d18c63ea6 Mon Sep 17 00:00:00 2001 From: Bunyanuch Saengnet Date: Tue, 18 Feb 2020 18:17:57 +0000 Subject: [PATCH 09/12] Refactoring test-module --- test/suspend.js | 76 +++++++++++++++++++++++++++++++++---------------- 1 file changed, 52 insertions(+), 24 deletions(-) diff --git a/test/suspend.js b/test/suspend.js index 6c399e2..710711b 100644 --- a/test/suspend.js +++ b/test/suspend.js @@ -1,17 +1,41 @@ -import {serial as test} from 'ava'; +import test from 'ava'; import sinon from 'sinon'; - -const mockery = require('mockery'); +import Listr from '..'; const logUpdateApi = { main: require('log-update') }; -const mock = sinon.mock(logUpdateApi); +const render = tasks => { + const taskStatus = []; + for (const task of tasks) { + taskStatus.push({title: task.title, shouldSuspend: task.shouldSuspendUpdateRenderer(), completed: task.isCompleted()}); + } + + logUpdateApi.main(taskStatus); +}; + +class TaskRenderer { + constructor(tasks) { + this._tasks = tasks; + } + + static get nonTTY() { + return true; + } + + render() { + this._id = setInterval(() => { + render(this._tasks); + }, 100); + } + + end() { + clearInterval(this._id); + } +} function getTasks() { - // Require is necessary at this position, otherwise mockery is not working correctly - const Listr = require('..'); const tasks = new Listr([ { title: 'not suspending task', @@ -29,33 +53,37 @@ function getTasks() { return new Promise(resolve => { setTimeout(() => { resolve('predefined output'); - }, 4000); + }, 300); }); }, options: {suspendUpdateRenderer: true} + }, + { + title: 'last task', + task: () => { + return new Promise(resolve => { + setTimeout(() => { + resolve('predefined output'); + }, 100); + }); + } } - ]); + ], {renderer: TaskRenderer}); return tasks; } -test.before(() => { - /* One time for the first task, - one time when both tasks are finished - */ - mock.expects('main').twice(); - mockery.registerAllowable('..'); - mockery.registerMock('log-update', logUpdateApi.main); - mockery.enable({useCleanCache: true, warnOnUnregistered: false}); -}); - -test.after(() => { - mockery.disable(); - mockery.deregisterAll(); -}); - test('should suspend second task', async t => { + const mock = sinon.mock(logUpdateApi); + mock.expects('main').withExactArgs([{title: 'not suspending task', shouldSuspend: false, completed: false}, + {title: 'suspending task', shouldSuspend: false, completed: false}, + {title: 'last task', shouldSuspend: false, completed: false}]).once(); + mock.expects('main').withExactArgs([{title: 'not suspending task', shouldSuspend: false, completed: true}, + {title: 'suspending task', shouldSuspend: true, completed: false}, + {title: 'last task', shouldSuspend: false, completed: false}]).atLeast(1); + mock.expects('main').withExactArgs([{title: 'not suspending task', shouldSuspend: false, completed: true}, + {title: 'suspending task', shouldSuspend: false, completed: true}, + {title: 'last task', shouldSuspend: false, completed: false}]).once(); await getTasks().run(); - t.true(mock.verify()); }); From 47fb5a2991e68cf73bed0563af7ce522e06cf492 Mon Sep 17 00:00:00 2001 From: Bunyanuch Saengnet Date: Tue, 18 Feb 2020 18:34:27 +0000 Subject: [PATCH 10/12] Removed mockery dependency --- package.json | 1 - 1 file changed, 1 deletion(-) diff --git a/package.json b/package.json index 49c8689..909b32b 100644 --- a/package.json +++ b/package.json @@ -57,7 +57,6 @@ "lint-staged": "^8.0.5", "log-symbols": "^2.2.0", "log-update": "^2.3.0", - "mockery": "^2.1.0", "nyc": "^13.1.0", "pre-commit": "^1.2.2", "sinon": "^7.4.2", From 1cf0f100797e3ccf5cd822096b1bc74956cf4f63 Mon Sep 17 00:00:00 2001 From: Bunyanuch Saengnet Date: Fri, 6 Mar 2020 17:20:52 +0000 Subject: [PATCH 11/12] Refactoring suspend-testclass --- package.json | 4 +-- test/suspend.js | 83 +++++++++++++++++++------------------------------ 2 files changed, 34 insertions(+), 53 deletions(-) diff --git a/package.json b/package.json index 909b32b..d075ac6 100644 --- a/package.json +++ b/package.json @@ -53,13 +53,13 @@ "clinton": "*", "codecov": "^3.1.0", "delay": "^4.1.0", + "elegant-spinner": "^1.0.1", "hook-std": "^1.1.0", "lint-staged": "^8.0.5", "log-symbols": "^2.2.0", - "log-update": "^2.3.0", + "mockery": "^2.1.0", "nyc": "^13.1.0", "pre-commit": "^1.2.2", - "sinon": "^7.4.2", "split": "^1.0.1", "xo": "*", "zen-observable": "^0.8.11" diff --git a/test/suspend.js b/test/suspend.js index 710711b..067374c 100644 --- a/test/suspend.js +++ b/test/suspend.js @@ -1,44 +1,12 @@ import test from 'ava'; -import sinon from 'sinon'; -import Listr from '..'; - -const logUpdateApi = { - main: require('log-update') -}; - -const render = tasks => { - const taskStatus = []; - for (const task of tasks) { - taskStatus.push({title: task.title, shouldSuspend: task.shouldSuspendUpdateRenderer(), completed: task.isCompleted()}); - } - - logUpdateApi.main(taskStatus); -}; - -class TaskRenderer { - constructor(tasks) { - this._tasks = tasks; - } - - static get nonTTY() { - return true; - } - - render() { - this._id = setInterval(() => { - render(this._tasks); - }, 100); - } - - end() { - clearInterval(this._id); - } -} +import mockery from 'mockery'; +import elegantSpinner from 'elegant-spinner'; function getTasks() { + const Listr = require('..'); const tasks = new Listr([ { - title: 'not suspending task', + title: 'first', task: () => { return new Promise(resolve => { setTimeout(() => { @@ -48,7 +16,7 @@ function getTasks() { } }, { - title: 'suspending task', + title: 'second', task: () => { return new Promise(resolve => { setTimeout(() => { @@ -59,7 +27,7 @@ function getTasks() { options: {suspendUpdateRenderer: true} }, { - title: 'last task', + title: 'last', task: () => { return new Promise(resolve => { setTimeout(() => { @@ -68,22 +36,35 @@ function getTasks() { }); } } - ], {renderer: TaskRenderer}); + ]); return tasks; } -test('should suspend second task', async t => { - const mock = sinon.mock(logUpdateApi); - mock.expects('main').withExactArgs([{title: 'not suspending task', shouldSuspend: false, completed: false}, - {title: 'suspending task', shouldSuspend: false, completed: false}, - {title: 'last task', shouldSuspend: false, completed: false}]).once(); - mock.expects('main').withExactArgs([{title: 'not suspending task', shouldSuspend: false, completed: true}, - {title: 'suspending task', shouldSuspend: true, completed: false}, - {title: 'last task', shouldSuspend: false, completed: false}]).atLeast(1); - mock.expects('main').withExactArgs([{title: 'not suspending task', shouldSuspend: false, completed: true}, - {title: 'suspending task', shouldSuspend: false, completed: true}, - {title: 'last task', shouldSuspend: false, completed: false}]).once(); +test('should not erase output from suspended task, as long as this task is running', async t => { + const countOutputIsErasedWhenTaskRunning = {first: 0, second: 0, last: 0}; + + const logUpdate = output => { + const taskPendingPattern = new RegExp(elegantSpinner.frames.join('|')); + const tasks = output.split('\n'); + for (const task of tasks) { + if (task.search(taskPendingPattern) > 0) { + const title = task.match(/first|second|last/); + countOutputIsErasedWhenTaskRunning[title]++; + } + } + }; + + logUpdate.done = () => {}; + + mockery.registerAllowable('..'); + mockery.registerMock('log-update', logUpdate); + mockery.enable({useCleanCache: true, warnOnUnregistered: false}); + await getTasks().run(); - t.true(mock.verify()); + + mockery.deregisterAll(); + mockery.disable(); + + t.deepEqual(countOutputIsErasedWhenTaskRunning, {first: 1, second: 0, last: 1}); }); From 0327ff77f3e5121e6bec7b62273f26fc6e434da5 Mon Sep 17 00:00:00 2001 From: Bunyanuch Saengnet Date: Sat, 28 Mar 2020 16:47:03 +0000 Subject: [PATCH 12/12] Add testclass for user-input-verification --- test/fixtures/task-with-input.js | 37 ++++++++++++++++++++++++++++++++ test/task-with-input.js | 26 ++++++++++++++++++++++ 2 files changed, 63 insertions(+) create mode 100755 test/fixtures/task-with-input.js create mode 100644 test/task-with-input.js diff --git a/test/fixtures/task-with-input.js b/test/fixtures/task-with-input.js new file mode 100755 index 0000000..4d7ac18 --- /dev/null +++ b/test/fixtures/task-with-input.js @@ -0,0 +1,37 @@ +#!/usr/bin/env node +'use strict'; +const Listr = require('../..'); + +function getTasks(suspendUpdateRenderer) { + const task = () => { + return new Promise((resolve, reject) => { + console.log('Continue?'); + process.stdin.on('data', chunk => { + const received = chunk.toString().replace('\n', ''); + if (received === 'yes') { + resolve(received); + } else { + reject(new Error(`invalid input ${received}`)); + } + }); + }); + }; + + return new Listr([ + { + title: `receive user input from task with renderer is ${suspendUpdateRenderer ? 'suspended' : 'running'}`, + task, + options: {suspendUpdateRenderer} + } + ]); +} + +(async () => { + let suspendUpdateRenderer = false; + if (process.argv[2] === 'true') { + suspendUpdateRenderer = true; + } + + await getTasks(suspendUpdateRenderer).run(); + process.exit(0); +})(); diff --git a/test/task-with-input.js b/test/task-with-input.js new file mode 100644 index 0000000..d9132d1 --- /dev/null +++ b/test/task-with-input.js @@ -0,0 +1,26 @@ +'use strict'; +const {spawn} = require('child_process'); +const path = require('path'); +const test = require('ava'); + +const executeTask = async (t, suspendUpdateRenderer) => { + const promise = new Promise((resolve, reject) => { + const childProcess = spawn('./task-with-input.js', [suspendUpdateRenderer], {cwd: path.resolve('test', 'fixtures')}); + childProcess.stdout.pipe(process.stdout); + childProcess.on('close', code => { + if (code !== 0) { + reject(new Error(`exit code ${code}`)); + } + + resolve(); + }); + setTimeout(() => { + childProcess.stdin.write('yes'); + }, 50); + }); + await promise; + t.pass(); +}; + +test('should receive user input from task with running renderer', executeTask); +test('should receive user input from task with suspended renderer', executeTask, 'true');