diff --git a/packages/core/lib/session.js b/packages/core/lib/session.js index e9d5468ab..f7a42c276 100644 --- a/packages/core/lib/session.js +++ b/packages/core/lib/session.js @@ -16,7 +16,7 @@ normalize = require('./session/normalize'); utils = require('./utils'); session = function(action = {}) { - var base, base1, on_call, on_get, result; + var base, base1, on_call, on_get, result, schedulers; if (action.metadata == null) { action.metadata = {}; } @@ -129,7 +129,14 @@ session = function(action = {}) { } }); // Local scheduler to execute children and be notified on finish - action.scheduler = schedule(); + schedulers = { + in: schedule(), + out: schedule(null, { + pause: true + }) + }; + // Start with a paused scheduler to register actions out of the handler + action.scheduler = schedulers.out; if (action.context) { // Expose the action context action.config.context = action.context; @@ -156,6 +163,8 @@ session = function(action = {}) { action[k] = merge(action_from_registry[k], action[k]); } } + // Switch the scheduler to register actions inside the handler + action.scheduler = schedulers.in; // Hook attended to alter the execution of an action handler output = action.plugins.call({ name: 'nikita:action', @@ -168,6 +177,12 @@ session = function(action = {}) { }); // Ensure child actions are executed pump = function() { + var child; + while (child = schedulers.out.state.stack.shift()) { + // Now that the handler has been executed, + // import all the actions registered outside of it + action.scheduler.state.stack.push(child); + } return action.scheduler.pump(); }; output.then(pump, pump); diff --git a/packages/core/src/session.coffee b/packages/core/src/session.coffee index c25e58e2b..8f694cae9 100644 --- a/packages/core/src/session.coffee +++ b/packages/core/src/session.coffee @@ -67,7 +67,11 @@ session = (action={}) -> name: 'nikita:register' args: name: name, action: act # Local scheduler to execute children and be notified on finish - action.scheduler = schedule() + schedulers = + in: schedule() + out: schedule null, pause: true + # Start with a paused scheduler to register actions out of the handler + action.scheduler = schedulers.out # Expose the action context action.config.context = action.context if action.context action.context = new Proxy on_call, get: on_get @@ -85,6 +89,8 @@ session = (action={}) -> # Merge the registry action with the user action properties for k, v of action_from_registry action[k] = merge action_from_registry[k], action[k] + # Switch the scheduler to register actions inside the handler + action.scheduler = schedulers.in # Hook attended to alter the execution of an action handler output = action.plugins.call name: 'nikita:action' @@ -94,7 +100,11 @@ session = (action={}) -> # Execution of an action handler action.handler.call action.context, action # Ensure child actions are executed - pump = -> action.scheduler.pump() + pump = -> + # Now that the handler has been executed, + # import all the actions registered outside of it + action.scheduler.state.stack.push child while child = schedulers.out.state.stack.shift() + action.scheduler.pump() output.then pump, pump # Make sure the promise is resolved after the scheduler and its children Promise.all [output, action.scheduler] diff --git a/packages/core/test/actions/execute/index.coffee b/packages/core/test/actions/execute/index.coffee index c12cb501c..d6d39145d 100644 --- a/packages/core/test/actions/execute/index.coffee +++ b/packages/core/test/actions/execute/index.coffee @@ -64,7 +64,7 @@ describe 'actions.execute', -> false.should.be.true() await nikita ssh: ssh - , -> + , (->) .execute command: "cat #{__filename} | grep #{search1}" stdout: out diff --git a/packages/core/test/session/creation.coffee b/packages/core/test/session/creation.coffee index e9236c8b6..f50af2986 100644 --- a/packages/core/test/session/creation.coffee +++ b/packages/core/test/session/creation.coffee @@ -1,4 +1,5 @@ +nikita = require '../../src' session = require '../../src/session' describe 'session.creation', -> @@ -7,8 +8,14 @@ describe 'session.creation', -> it 'which succeed', -> result = await session [ - -> 1 - -> 2 + -> new Promise (resolve) -> + setTimeout -> + resolve 1 + , 100 + -> new Promise (resolve) -> + setTimeout -> + resolve 2 + , 10 ] result.should.eql [1, 2] @@ -18,3 +25,70 @@ describe 'session.creation', -> -> true ] .should.be.rejectedWith 'Catchme' + + describe 'flow with external action', -> + + it 'plugin and handler and no external action', -> + # No external action but we use it as a reference + stack = [] + await nikita + hooks: on_action: (action)-> + new Promise (resolve) -> + setTimeout -> + stack.push 'plugin' + resolve() + , 100 + , -> + @call ({metadata}) -> + stack.push metadata.position.join ':' + @call ({metadata}) -> + stack.push metadata.position.join ':' + stack.should.eql ['plugin', '0:0', '0:1'] + + it 'after no plugin and no handler but a property object', -> + stack = [] + await nikita + key: 'value' + .call ({metadata}) -> + stack.push metadata.position.join ':' + .call ({metadata}) -> + stack.push metadata.position.join ':' + stack.should.eql ['0:0', '0:1'] + + it 'after plugin', -> + stack = [] + await nikita + hooks: on_action: (action)-> + new Promise (resolve) -> + setTimeout -> + stack.push 'plugin' + resolve() + , 100 + .call ({metadata}) -> + stack.push metadata.position.join ':' + .call ({metadata}) -> + stack.push metadata.position.join ':' + stack.should.eql ['plugin', '0:0', '0:1'] + + it 'after plugin and handler', -> + stack = [] + await nikita + hooks: on_action: (action) -> + new Promise (resolve) -> + setTimeout -> + stack.push 'plugin' + resolve() + , 100 + , ({metadata}) -> + @call ({metadata}) -> + new Promise (resolve) -> + setTimeout -> + stack.push metadata.position.join ':' + resolve() + , 100 + .call ({metadata}) -> + stack.push metadata.position.join ':' + .call ({metadata}) -> + stack.push metadata.position.join ':' + stack.should.eql ['plugin', '0:0', '0:1', '0:2'] + diff --git a/packages/core/test/session/plugins/session.resolved.coffee b/packages/core/test/session/plugins/session.resolved.coffee index 624dc8951..99899a930 100644 --- a/packages/core/test/session/plugins/session.resolved.coffee +++ b/packages/core/test/session/plugins/session.resolved.coffee @@ -20,11 +20,11 @@ describe 'session.plugins.session.resolved', -> n.call -> stack.push '3' await n - # Not sure if we really expect 3 to be called before 1 and 2, what's - # important in the context of this test is how 'end' is called last + # what's important in the context of this test is how 'end' is called last + # however, we also indirectly test action in handler being called after external actions stack.should.eql [ - '3' '1' '2' + '3' 'end' ]