├── .gitignore ├── frameworks └── foundation │ ├── core.js │ ├── tests │ ├── state_transitioning │ │ ├── history_state │ │ │ ├── initial_substate │ │ │ │ ├── core.js │ │ │ │ └── without_concurrent_states.js │ │ │ └── standard │ │ │ │ ├── with_concurrent_states.js │ │ │ │ └── without_concurrent_states │ │ │ │ ├── context.js │ │ │ │ └── core.js │ │ ├── async │ │ │ ├── core.js │ │ │ ├── with_concurrent_states.js │ │ │ └── without_concurrent_states.js │ │ ├── transient │ │ │ └── without_concurrent_states.js │ │ └── standard │ │ │ ├── without_concurrent_states │ │ │ ├── context.js │ │ │ └── core.js │ │ │ └── with_concurrent_states │ │ │ ├── intermediate.js │ │ │ ├── basic.js │ │ │ └── advanced.js │ ├── state │ │ ├── is_current_state.js │ │ ├── plugin │ │ │ ├── mixin.js │ │ │ └── nesting.js │ │ ├── initial_substate.js │ │ ├── namespacing.js │ │ └── state_observes.js │ ├── event_handling │ │ ├── responder │ │ │ ├── root_responder.js │ │ │ ├── pane.js │ │ │ └── responder_chain.js │ │ ├── basic │ │ │ ├── without_concurrent_states.js │ │ │ └── with_concurrent_states.js │ │ └── advanced │ │ │ └── without_concurrent_states.js │ └── statechart │ │ ├── create │ │ ├── assigned_root_state.js │ │ └── unassigned_root_state.js │ │ ├── destroy.js │ │ ├── owner.js │ │ └── invoke_state_method.js │ └── debug │ └── monitor.js ├── Buildfile ├── license.js ├── History.md └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | tmp 2 | .sass-cache -------------------------------------------------------------------------------- /frameworks/foundation/core.js: -------------------------------------------------------------------------------- 1 | // ========================================================================== 2 | // Project: Ki - A Statechart Framework for SproutCore 3 | // License: Licensed under MIT license (see license.js) 4 | // ========================================================================== 5 | 6 | window.Ki = window.Ki || SC.Object.create(); 7 | window.KI = window.KI || window.Ki ; -------------------------------------------------------------------------------- /Buildfile: -------------------------------------------------------------------------------- 1 | # ========================================================================== 2 | # Ki - Buildfile 3 | # Copyright: (c) 2010 - Michael Cohen, and contributors 4 | # License: Licensed under MIT license (see license.js) 5 | # ========================================================================== 6 | 7 | 8 | config :all, 9 | :required => ['sproutcore/runtime'], 10 | :test_required => ['sproutcore/runtime', 'sproutcore/foundation', 'sproutcore/desktop'], 11 | :debug_required => ['sproutcore/runtime', 'sproutcore/foundation', 'sproutcore/desktop'] 12 | 13 | mode :debug do 14 | config :all, 15 | :combine_javascript => true, 16 | :combine_stylesheet => true 17 | end 18 | 19 | # CORE FRAMEWORKS 20 | config :foundation, :required => [] 21 | 22 | # WRAPPER FRAMEWORKS 23 | config :ki, :required => [:foundation] 24 | 25 | # SPECIAL THEMES 26 | # These do not require any of the built-in SproutCore frameworks 27 | %w(standard_theme empty_theme).each do |target_name| 28 | config target_name, 29 | :required => [], :test_required => [], :debug_required => [] 30 | end 31 | 32 | # CONFIGURE THEMES 33 | config :empty_theme, 34 | :theme_name => 'empty-theme', 35 | :test_required => ['sproutcore/testing'], 36 | :debug_required => ['sproutcore/debug'] 37 | 38 | config :standard_theme, 39 | :required => :empty_theme, 40 | :theme_name => 'sc-theme', 41 | :test_required => ['sproutcore/testing'], 42 | :debug_required => ['sproutcore/debug'] -------------------------------------------------------------------------------- /frameworks/foundation/tests/state_transitioning/history_state/initial_substate/core.js: -------------------------------------------------------------------------------- 1 | // ========================================================================== 2 | // Ki.Statechart Unit Test 3 | // ========================================================================== 4 | /*globals Ki */ 5 | 6 | var statechart, stateA, stateB, stateC; 7 | 8 | module("Ki.HistoryState Tests", { 9 | setup: function() { 10 | statechart = Ki.Statechart.create({initialState: 'a', a: Ki.State.design()}); 11 | stateA = Ki.State.create({ name: 'stateA' }); 12 | stateB = Ki.State.create({ name: 'stateB' }); 13 | stateC = Ki.State.create({ name: 'stateC' }); 14 | }, 15 | 16 | teardown: function() { 17 | statechart = stateA = stateB = stateC = null; 18 | } 19 | }); 20 | 21 | test("Check default history state", function() { 22 | var historyState = Ki.HistoryState.create(); 23 | 24 | equals(historyState.get('isRecursive'), false); 25 | }); 26 | 27 | test("Check assigned history state", function() { 28 | var historyState = Ki.HistoryState.create({ 29 | isRecursive: YES, 30 | statechart: statechart, 31 | parentState: stateA, 32 | defaultState: stateB 33 | }); 34 | 35 | equals(historyState.get('statechart'), statechart); 36 | equals(historyState.get('parentState'), stateA); 37 | equals(historyState.get('defaultState'), stateB); 38 | equals(historyState.get('isRecursive'), true); 39 | equals(historyState.get('state'), stateB); 40 | 41 | stateA.set('historyState', stateC); 42 | 43 | equals(historyState.get('state'), stateC); 44 | 45 | stateA.set('historyState', null); 46 | 47 | equals(historyState.get('state'), stateB); 48 | }); -------------------------------------------------------------------------------- /frameworks/foundation/tests/state/is_current_state.js: -------------------------------------------------------------------------------- 1 | // ========================================================================== 2 | // Ki.Statechart Unit Test 3 | // ========================================================================== 4 | /*globals Ki */ 5 | 6 | var statechart = null; 7 | 8 | module("Ki.Statechart: State - isCurrentState Property Tests", { 9 | setup: function() { 10 | 11 | statechart = Ki.Statechart.create({ 12 | 13 | monitorIsActive: YES, 14 | 15 | rootState: Ki.State.design({ 16 | 17 | initialSubstate: 'a', 18 | 19 | a: Ki.State.design(), 20 | 21 | b: Ki.State.design() 22 | 23 | }) 24 | 25 | }); 26 | 27 | statechart.initStatechart(); 28 | }, 29 | 30 | teardown: function() { 31 | statechart.destroy(); 32 | statechart = null; 33 | } 34 | }); 35 | 36 | test("check binding to isCurrentState", function() { 37 | var a = statechart.getState('a'); 38 | 39 | var o = SC.Object.create({ 40 | value: null, 41 | valueBinding: SC.Binding.oneWay().from('isCurrentState', a) 42 | }); 43 | 44 | SC.run(); 45 | equals(a.get('isCurrentState'), true); 46 | equals(o.get('value'), true); 47 | 48 | SC.run(function() { statechart.gotoState('b'); }); 49 | equals(a.get('isCurrentState'), false); 50 | equals(o.get('value'), false); 51 | 52 | SC.run(function() { statechart.gotoState('a'); }); 53 | equals(a.get('isCurrentState'), true); 54 | equals(o.get('value'), true); 55 | 56 | SC.run(function() { statechart.gotoState('b'); }); 57 | equals(a.get('isCurrentState'), false); 58 | equals(o.get('value'), false); 59 | 60 | }); -------------------------------------------------------------------------------- /license.js: -------------------------------------------------------------------------------- 1 | /*! @license 2 | ========================================================================== 3 | Ki -- A Statechart Framework for Sproutcore 4 | copyright 2010, 2011 Michael Cohen, and contributors. All rights reserved. 5 | 6 | Some portions of the Ki framework were derived from the statechart 7 | framework in SproutCore whose original authors consist of Michael Ball, 8 | Evin Grano and Michael Cohen. 9 | 10 | Permission is hereby granted, free of charge, to any person obtaining a 11 | copy of this software and associated documentation files (the "Software"), 12 | to deal in the Software without restriction, including without limitation 13 | the rights to use, copy, modify, merge, publish, distribute, sublicense, 14 | and/or sell copies of the Software, and to permit persons to whom the 15 | Software is furnished to do so, subject to the following conditions: 16 | 17 | The above copyright notice and this permission notice shall be included in 18 | all copies or substantial portions of the Software. 19 | 20 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 23 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 24 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 25 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 26 | DEALINGS IN THE SOFTWARE. 27 | 28 | For more information about SproutCore, visit http://github.com/FrozenCanuck/Ki 29 | 30 | ========================================================================== 31 | @license */ -------------------------------------------------------------------------------- /frameworks/foundation/tests/state/plugin/mixin.js: -------------------------------------------------------------------------------- 1 | // ========================================================================== 2 | // Ki.Statechart Unit Test 3 | // ========================================================================== 4 | /*globals Ki TestState */ 5 | 6 | TestState = null; 7 | var obj, MixinA, MixinB, stateA, stateB, stateC; 8 | 9 | module("Ki.State.plugin: Mixin Tests", { 10 | setup: function() { 11 | 12 | MixinA = { 13 | isMixinA: YES 14 | }; 15 | 16 | MixinB = { 17 | isMixinB: YES 18 | }; 19 | 20 | TestState = Ki.State.extend({ 21 | isTestState: YES 22 | }); 23 | 24 | obj = SC.Object.create(Ki.StatechartManager, { 25 | 26 | initialState: 'stateA', 27 | 28 | stateA: Ki.State.plugin('TestState'), 29 | 30 | stateB: Ki.State.plugin('TestState', MixinA), 31 | 32 | stateC: Ki.State.plugin('TestState', MixinA, MixinB) 33 | 34 | }); 35 | 36 | stateA = obj.getState('stateA'); 37 | stateB = obj.getState('stateB'); 38 | stateC = obj.getState('stateC'); 39 | 40 | }, 41 | 42 | teardown: function() { 43 | obj = TestState = MixinA = MixinB = null; 44 | stateA = stateB = stateC = null; 45 | } 46 | 47 | }); 48 | 49 | test("check plugin state A", function() { 50 | ok(SC.kindOf(stateA, TestState)); 51 | ok(stateA.get('isTestState')); 52 | ok(!stateA.get('isMixinA')); 53 | ok(!stateA.get('isMixinB')); 54 | }); 55 | 56 | test("check plugin state B", function() { 57 | ok(SC.kindOf(stateB, TestState)); 58 | ok(stateB.get('isTestState')); 59 | ok(stateB.get('isMixinA')); 60 | ok(!stateB.get('isMixinB')); 61 | }); 62 | 63 | test("check plugin state C", function() { 64 | ok(SC.kindOf(stateC, TestState)); 65 | ok(stateC.get('isTestState')); 66 | ok(stateC.get('isMixinA')); 67 | ok(stateC.get('isMixinB')); 68 | }); -------------------------------------------------------------------------------- /frameworks/foundation/tests/event_handling/responder/root_responder.js: -------------------------------------------------------------------------------- 1 | // ========================================================================== 2 | // Ki.Statechart Unit Test 3 | // ========================================================================== 4 | /*globals Ki statechart */ 5 | 6 | window.statechart = null; 7 | var responder, fooInvokedCount; 8 | 9 | // .......................................................... 10 | // CONTENT CHANGING 11 | // 12 | 13 | module("Ki.Statechart: No Concurrent States - Root Responder Default Responder Tests", { 14 | setup: function() { 15 | fooInvokedCount = 0; 16 | 17 | window.statechart = Ki.Statechart.create({ 18 | 19 | rootState: Ki.State.design({ 20 | 21 | initialSubstate: 'a', 22 | 23 | a: Ki.State.design({ 24 | foo: function() { 25 | fooInvokedCount++; 26 | this.gotoState('b'); 27 | } 28 | }), 29 | 30 | b: Ki.State.design({ 31 | foo: function() { 32 | fooInvokedCount++; 33 | this.gotoState('a'); 34 | } 35 | }) 36 | 37 | }) 38 | 39 | }); 40 | 41 | window.statechart.initStatechart(); 42 | 43 | responder = SC.RootResponder.responder; 44 | 45 | SC.RunLoop.begin(); 46 | responder.set('defaultResponder', 'statechart'); 47 | SC.RunLoop.end(); 48 | }, 49 | 50 | teardown: function() { 51 | window.statechart = null; 52 | responder = null; 53 | SC.RootResponder.responder.set('defaultResponder', null); 54 | } 55 | }); 56 | 57 | test("click button", function() { 58 | equals(fooInvokedCount, 0, 'foo should not have been invoked'); 59 | equals(statechart.stateIsCurrentState('a'), true, 'state a should be a current state'); 60 | equals(statechart.stateIsCurrentState('b'), false, 'state b should not be a current state'); 61 | 62 | responder.sendAction('foo'); 63 | 64 | equals(fooInvokedCount, 1, 'foo should have been invoked once'); 65 | equals(statechart.stateIsCurrentState('a'), false, 'state a should not be a current state'); 66 | equals(statechart.stateIsCurrentState('b'), true, 'state b should be a current state'); 67 | 68 | responder.sendAction('foo'); 69 | 70 | equals(fooInvokedCount, 2, 'foo should have been invoked twice'); 71 | equals(statechart.stateIsCurrentState('a'), true, 'state a should be a current state'); 72 | equals(statechart.stateIsCurrentState('b'), false, 'state b should not be a current state'); 73 | 74 | }); -------------------------------------------------------------------------------- /frameworks/foundation/tests/statechart/create/assigned_root_state.js: -------------------------------------------------------------------------------- 1 | // ========================================================================== 2 | // Ki.Statechart Unit Test 3 | // ========================================================================== 4 | /*globals Ki statechart */ 5 | 6 | var obj1, rootState1, stateA, stateB; 7 | var obj2; 8 | 9 | module("Ki.Statechart: Create Statechart with Assigned Root State Tests", { 10 | setup: function() { 11 | obj1 = SC.Object.extend(Ki.StatechartManager, { 12 | rootState: Ki.State.design({ 13 | 14 | initialSubstate: 'a', 15 | 16 | a: Ki.State.design({ 17 | foo: function() { 18 | this.gotoState('b'); 19 | } 20 | }), 21 | 22 | b: Ki.State.design({ 23 | bar: function() { 24 | this.gotoState('a'); 25 | } 26 | }) 27 | 28 | }) 29 | }); 30 | 31 | obj1 = obj1.create(); 32 | rootState1 = obj1.get('rootState'); 33 | stateA = obj1.getState('a'); 34 | stateB = obj1.getState('b'); 35 | 36 | obj2 = SC.Object.extend(Ki.StatechartManager, { 37 | autoInitStatechart: NO, 38 | rootState: Ki.State.design() 39 | }); 40 | 41 | obj2 = obj2.create(); 42 | }, 43 | 44 | teardown: function() { 45 | obj1 = rootState1 = stateA = stateB = null; 46 | obj2 = null; 47 | } 48 | }); 49 | 50 | test("check obj1", function() { 51 | ok(obj1.get('isStatechart'), "obj should be statechart"); 52 | ok(obj1.get('statechartIsInitialized'), "obj should be an initialized statechart"); 53 | ok(SC.kindOf(rootState1, Ki.State), "root state should be kind of Ki.State"); 54 | equals(obj1.get('initialState'), null, "obj initialState should be null"); 55 | 56 | ok(stateA.get('isCurrentState'), "state A should be current state"); 57 | ok(!stateB.get('isCurrentState'), "state B should not be current state"); 58 | 59 | equals(rootState1.get('owner'), obj1, "root state's owner should be obj"); 60 | equals(stateA.get('owner'), obj1, "state A's owner should be obj"); 61 | equals(stateB.get('owner'), obj1, "state B's owner should be obj"); 62 | 63 | obj1.sendEvent('foo'); 64 | 65 | ok(!stateA.get('isCurrentState'), "state A should not be current state"); 66 | ok(stateB.get('isCurrentState'), "state B should be current state"); 67 | }); 68 | 69 | test("check obj2", function() { 70 | ok(obj2.get('isStatechart'), "obj should be statechart"); 71 | ok(!obj2.get('statechartIsInitialized'), "obj not should be an initialized statechart"); 72 | 73 | obj2.initStatechart(); 74 | 75 | ok(obj2.get('statechartIsInitialized'), "obj should be an initialized statechart"); 76 | }); -------------------------------------------------------------------------------- /frameworks/foundation/tests/state/initial_substate.js: -------------------------------------------------------------------------------- 1 | // ========================================================================== 2 | // Ki.State Unit Test 3 | // ========================================================================== 4 | /*globals Ki externalState1 externalState2 */ 5 | 6 | var statechart, root, monitor, stateA, stateB, stateC, stateD, stateE, stateF; 7 | 8 | module("Ki.Statechart: State Initial Substate Tests", { 9 | setup: function() { 10 | 11 | statechart = Ki.Statechart.create({ 12 | 13 | monitorIsActive: YES, 14 | 15 | rootState: Ki.State.design({ 16 | 17 | initialSubstate: 'a', 18 | 19 | a: Ki.State.design({ 20 | initialSubstate: 'c', 21 | c: Ki.State.design(), 22 | d: Ki.State.design() 23 | }), 24 | 25 | b: Ki.State.design({ 26 | e: Ki.State.design(), 27 | f: Ki.State.design() 28 | }) 29 | 30 | }) 31 | 32 | }); 33 | 34 | statechart.initStatechart(); 35 | 36 | root = statechart.get('rootState'); 37 | monitor = statechart.get('monitor'); 38 | stateA = statechart.getState('a'); 39 | stateB = statechart.getState('b'); 40 | stateC = statechart.getState('c'); 41 | stateD = statechart.getState('d'); 42 | stateE = statechart.getState('e'); 43 | stateF = statechart.getState('f'); 44 | }, 45 | 46 | teardown: function() { 47 | statechart = root = stateA = stateB = stateC = stateD = stateE = stateF = null; 48 | } 49 | }); 50 | 51 | test("check initial substates", function() { 52 | equals(root.get('initialSubstate'), stateA, "root state's initial substate should be state A"); 53 | equals(stateA.get('initialSubstate'), stateC, "state a's initial substate should be state c"); 54 | equals(stateC.get('initialSubstate'), null, "state c's initial substate should be null"); 55 | equals(stateD.get('initialSubstate'), null, "state d's initial substate should be null"); 56 | equals(SC.kindOf(stateB.get('initialSubstate'), Ki.EmptyState), true, "state b's initial substate should be an empty state"); 57 | equals(stateE.get('initialSubstate'), null, "state e's initial substate should be null"); 58 | equals(stateF.get('initialSubstate'), null, "state f's initial substate should be null"); 59 | }); 60 | 61 | test("go to state b and confirm current state is an empty state", function() { 62 | equals(stateC.get('isCurrentState'), true); 63 | monitor.reset(); 64 | statechart.gotoState(stateB); 65 | ok(monitor.matchSequence().begin().exited(stateC, stateA).entered(stateB, stateB.get('initialSubstate')).end()); 66 | equals(stateB.getPath('initialSubstate.isCurrentState'), true, "state b\'s initial substate should be the current state"); 67 | }); -------------------------------------------------------------------------------- /frameworks/foundation/tests/event_handling/responder/pane.js: -------------------------------------------------------------------------------- 1 | // ========================================================================== 2 | // Ki.Statechart Unit Test 3 | // ========================================================================== 4 | /*globals Ki statechart */ 5 | 6 | window.startchart = null; 7 | var pane, button, fooInvokedCount; 8 | 9 | // .......................................................... 10 | // CONTENT CHANGING 11 | // 12 | 13 | module("Ki.Statechart: No Concurrent States - Pane Default Responder Tests", { 14 | setup: function() { 15 | fooInvokedCount = 0; 16 | 17 | window.statechart = Ki.Statechart.create({ 18 | 19 | rootState: Ki.State.design({ 20 | 21 | initialSubstate: 'a', 22 | 23 | a: Ki.State.design({ 24 | foo: function() { 25 | fooInvokedCount++; 26 | this.gotoState('b'); 27 | } 28 | }), 29 | 30 | b: Ki.State.design({ 31 | foo: function() { 32 | fooInvokedCount++; 33 | this.gotoState('a'); 34 | } 35 | }) 36 | 37 | }) 38 | 39 | }); 40 | 41 | statechart.initStatechart(); 42 | 43 | SC.RunLoop.begin(); 44 | pane = SC.MainPane.create({ 45 | defaultResponder: 'statechart', 46 | childViews: [ 47 | SC.ButtonView.extend({ 48 | action: 'foo' 49 | }) 50 | ] 51 | }); 52 | pane.append(); 53 | SC.RunLoop.end(); 54 | 55 | button = pane.childViews[0]; 56 | }, 57 | 58 | teardown: function() { 59 | pane.remove(); 60 | pane = button = fooInvokedCount = null; 61 | window.statechart = null; 62 | } 63 | }); 64 | 65 | test("click button", function() { 66 | var target; 67 | 68 | equals(fooInvokedCount, 0, 'foo should not have been invoked'); 69 | equals(statechart.stateIsCurrentState('a'), true, 'state a should be a current state'); 70 | equals(statechart.stateIsCurrentState('b'), false, 'state b should not be a current state'); 71 | 72 | target = button.$().get(0); 73 | SC.Event.trigger(target, "mousedown"); 74 | target = button.$().get(0); 75 | SC.Event.trigger(target, "mouseup"); 76 | 77 | equals(fooInvokedCount, 1, 'foo should have been invoked once'); 78 | equals(statechart.stateIsCurrentState('a'), false, 'state a should not be a current state'); 79 | equals(statechart.stateIsCurrentState('b'), true, 'state b should be a current state'); 80 | 81 | target = button.$().get(0); 82 | SC.Event.trigger(target, "mousedown"); 83 | target = button.$().get(0); 84 | SC.Event.trigger(target, "mouseup"); 85 | 86 | equals(fooInvokedCount, 2, 'foo should have been invoked twice'); 87 | equals(statechart.stateIsCurrentState('a'), true, 'state a should be a current state'); 88 | equals(statechart.stateIsCurrentState('b'), false, 'state b should not be a current state'); 89 | 90 | }); -------------------------------------------------------------------------------- /frameworks/foundation/tests/state_transitioning/async/core.js: -------------------------------------------------------------------------------- 1 | // ========================================================================== 2 | // Ki.Statechart Unit Test 3 | // ========================================================================== 4 | /*globals Ki */ 5 | 6 | var Obj, obj, async, func; 7 | 8 | // .......................................................... 9 | // CONTENT CHANGING 10 | // 11 | 12 | module("Ki.Async Tests", { 13 | setup: function() { 14 | Obj = SC.Object.extend({ 15 | fooInvoked: NO, 16 | arg1: null, 17 | arg2: null, 18 | 19 | foo: function(arg1, arg2) { 20 | this.set('fooInvoked', YES); 21 | this.set('arg1', arg1); 22 | this.set('arg2', arg2); 23 | } 24 | }); 25 | }, 26 | 27 | teardown: function() { 28 | Obj = obj = async = func = null; 29 | } 30 | }); 31 | 32 | test("test async - Ki.Async.perform('foo')", function() { 33 | async = Ki.Async.perform('foo'); 34 | equals(SC.kindOf(async, Ki.Async), YES); 35 | equals(async.get('func'), 'foo'); 36 | equals(async.get('arg1'), null); 37 | equals(async.get('arg2'), null); 38 | 39 | obj = Obj.create(); 40 | async.tryToPerform(obj); 41 | equals(obj.get('fooInvoked'), YES); 42 | equals(obj.get('arg1'), null); 43 | equals(obj.get('arg2'), null); 44 | }); 45 | 46 | test("test async - Ki.Async.perform('foo', 'hello', 'world')", function() { 47 | async = Ki.Async.perform('foo', 'hello', 'world'); 48 | equals(async.get('func'), 'foo'); 49 | equals(async.get('arg1'), 'hello'); 50 | equals(async.get('arg2'), 'world'); 51 | 52 | obj = Obj.create(); 53 | async.tryToPerform(obj); 54 | equals(obj.get('fooInvoked'), YES); 55 | equals(obj.get('arg1'), 'hello'); 56 | equals(obj.get('arg2'), 'world'); 57 | }); 58 | 59 | test("test async - Ki.Async.perform(function() { ... })", function() { 60 | func = function() { this.foo(); }; 61 | async = Ki.Async.perform(func); 62 | equals(async.get('func'), func); 63 | equals(async.get('arg1'), null); 64 | equals(async.get('arg2'), null); 65 | 66 | obj = Obj.create(); 67 | async.tryToPerform(obj); 68 | equals(obj.get('fooInvoked'), YES); 69 | equals(obj.get('arg1'), null); 70 | equals(obj.get('arg2'), null); 71 | }); 72 | 73 | test("test async - Ki.Async.perform(function() { ... }, 'aaa', 'bbb')", function() { 74 | func = function(arg1, arg2) { this.foo(arg1, arg2); }; 75 | async = Ki.Async.perform(func, 'aaa', 'bbb'); 76 | equals(async.get('func'), func); 77 | equals(async.get('arg1'), 'aaa'); 78 | equals(async.get('arg2'), 'bbb'); 79 | 80 | obj = Obj.create(); 81 | async.tryToPerform(obj); 82 | equals(obj.get('fooInvoked'), YES); 83 | equals(obj.get('arg1'), 'aaa'); 84 | equals(obj.get('arg2'), 'bbb'); 85 | }); 86 | 87 | test("test async - Ki.Async.perform('bar')", function() { 88 | async = Ki.Async.perform('bar'); 89 | equals(async.get('func'), 'bar'); 90 | 91 | obj = Obj.create(); 92 | async.tryToPerform(obj); 93 | equals(obj.get('fooInvoked'), NO); 94 | }); -------------------------------------------------------------------------------- /frameworks/foundation/tests/state_transitioning/history_state/standard/with_concurrent_states.js: -------------------------------------------------------------------------------- 1 | // ========================================================================== 2 | // Ki Unit Test 3 | // ========================================================================== 4 | /*globals Ki */ 5 | 6 | var statechart = null; 7 | 8 | // .......................................................... 9 | // CONTENT CHANGING 10 | // 11 | 12 | module("Ki.Statechart: With Concurrent States - Goto History State Tests", { 13 | setup: function() { 14 | 15 | statechart = Ki.Statechart.create({ 16 | 17 | monitorIsActive: YES, 18 | 19 | rootState: Ki.State.design({ 20 | 21 | initialSubstate: 'x', 22 | 23 | x: Ki.State.design({ 24 | 25 | substatesAreConcurrent: YES, 26 | 27 | a: Ki.State.design({ 28 | initialSubstate: 'c', 29 | c: Ki.State.design(), 30 | d: Ki.State.design() 31 | }), 32 | 33 | b: Ki.State.design({ 34 | initialSubstate: 'e', 35 | e: Ki.State.design(), 36 | f: Ki.State.design() 37 | }) 38 | 39 | }), 40 | 41 | z: Ki.State.design() 42 | 43 | }) 44 | 45 | }); 46 | 47 | statechart.initStatechart(); 48 | }, 49 | 50 | teardown: function() { 51 | statechart.destroy(); 52 | statechart = null; 53 | } 54 | }); 55 | 56 | test("send event eventA", function() { 57 | var monitor = statechart.get('monitor'), 58 | stateA = statechart.getState('a'), 59 | stateB = statechart.getState('b'), 60 | stateC = statechart.getState('c'), 61 | stateD = statechart.getState('d'), 62 | stateE = statechart.getState('e'), 63 | stateF = statechart.getState('f'), 64 | stateZ = statechart.getState('z'); 65 | 66 | stateC.gotoState('d'); 67 | stateE.gotoState('f'); 68 | 69 | equals(stateA.get('historyState'), stateD, 'state a should have state d as its history state'); 70 | equals(stateB.get('historyState'), stateF, 'state b should have state f as its history state'); 71 | equals(stateD.get('isCurrentState'), true, 'state d should be current state'); 72 | equals(stateF.get('isCurrentState'), true, 'state f should be current state'); 73 | equals(stateE.get('isCurrentState'), false, 'state e should not be current state'); 74 | 75 | monitor.reset(); 76 | 77 | stateD.gotoState('z'); 78 | equals(stateZ.get('isCurrentState'), true, 'state z should be current state'); 79 | 80 | stateZ.gotoHistoryState('a'); 81 | 82 | equals(stateA.get('historyState'), stateD, 'state a should have state d as its history state'); 83 | equals(stateB.get('historyState'), stateE, 'state b should have state e as its history state'); 84 | equals(stateD.get('isCurrentState'), true, 'state d should be current state'); 85 | equals(stateF.get('isCurrentState'), false, 'state f should not be current state'); 86 | equals(stateE.get('isCurrentState'), true, 'state e should be current state'); 87 | 88 | }); -------------------------------------------------------------------------------- /frameworks/foundation/tests/state/plugin/nesting.js: -------------------------------------------------------------------------------- 1 | // ========================================================================== 2 | // Ki.State Unit Test 3 | // ========================================================================== 4 | /*globals Ki externalState1 externalState2 */ 5 | 6 | var statechart = null; 7 | externalState1 = null; 8 | externalState2 = null; 9 | 10 | module("Ki.State.plugin: Nest States Tests", { 11 | setup: function() { 12 | 13 | externalState1 = Ki.State.extend({ 14 | 15 | message: 'external state 1' 16 | 17 | }); 18 | 19 | externalState2 = Ki.State.extend({ 20 | 21 | initialSubstate: 'd', 22 | 23 | message: 'external state 2', 24 | 25 | d: Ki.State.design(), 26 | 27 | e: Ki.State.plugin('externalState1') 28 | 29 | }); 30 | 31 | statechart = Ki.Statechart.create({ 32 | 33 | monitorIsActive: YES, 34 | 35 | rootState: Ki.State.design({ 36 | 37 | initialSubstate: 'a', 38 | 39 | a: Ki.State.plugin('externalState1'), 40 | 41 | b: Ki.State.plugin('externalState2'), 42 | 43 | c: Ki.State.design() 44 | 45 | }) 46 | 47 | }); 48 | 49 | statechart.initStatechart(); 50 | }, 51 | 52 | teardown: function() { 53 | statechart.destroy(); 54 | externalState1 = null; 55 | externalState2 = null; 56 | } 57 | }); 58 | 59 | test("check statechart states", function() { 60 | var stateA = statechart.getState('a'), 61 | stateB = statechart.getState('b'), 62 | stateC = statechart.getState('c'), 63 | stateD = statechart.getState('d'), 64 | stateE = statechart.getState('e'); 65 | 66 | equals(SC.kindOf(stateA, externalState1), true, 'state a should be kind of externalState1'); 67 | equals(SC.kindOf(stateB, externalState2), true, 'state b should be kind of externalState2'); 68 | equals(SC.kindOf(stateE, externalState1), true, 'state e should be kind of externalState1'); 69 | equals(SC.kindOf(stateC, externalState1), false, 'state c should not be kind of externalState1'); 70 | equals(SC.kindOf(stateD, externalState1), false, 'state d should not be kind of externalState1'); 71 | 72 | equals(stateA !== stateE, true, 'state a should not be equal to state e'); 73 | }); 74 | 75 | test("check statechart initialization", function() { 76 | var monitor = statechart.get('monitor'); 77 | var root = statechart.get('rootState'); 78 | 79 | equals(monitor.get('length'), 2, 'initial state sequence should be of length 2'); 80 | equals(monitor.matchSequence().begin().entered(root, 'a').end(), true, 'initial sequence should be entered[ROOT, a]'); 81 | equals(statechart.get('currentStateCount'), 1, 'current state count should be 1'); 82 | equals(statechart.stateIsCurrentState('a'), true, 'current state should be a'); 83 | }); 84 | 85 | test("go to state e", function() { 86 | var monitor = statechart.get('monitor'); 87 | 88 | monitor.reset(); 89 | statechart.gotoState('e'); 90 | 91 | equals(monitor.get('length'), 3, 'initial state sequence should be of length 3'); 92 | equals(monitor.matchSequence().begin().exited('a').entered('b', 'e').end(), true, 'initial sequence should be exited[a], entered[b, e]'); 93 | equals(statechart.get('currentStateCount'), 1, 'current state count should be 1'); 94 | equals(statechart.stateIsCurrentState('e'), true, 'current state should be e'); 95 | }); -------------------------------------------------------------------------------- /History.md: -------------------------------------------------------------------------------- 1 | # Ki 0.5.0 March 20, 2011 2 | 3 | * Added stateObserves() feature to framework. Thanks to Geoffrey Donaldson (@geoffreyd) for the 4 | original suggestion. 5 | * Changed allowTracing to allowStatechartTracing in Ki.StatechartManager 6 | * Enhanced framework to check if a state is currently entered 7 | 8 | # Ki 0.4.0 March 6, 2011 9 | 10 | * Updated the statechart and state class to now include logic that checks if they can respond to 11 | and event 12 | * tryToPerform has been added to the statechart mixin 13 | * The state's tryToHandleEvent method now checks if an event handler returned the value NO 14 | * statechart's sendEvent arguments have been made to be more generic. Now arg1 and arg2 15 | * Updated framework's tracing functionality to improve debugging statecharts 16 | * removed the try-catch block in statechart's sendEvent method. Was an old hold-over that is no 17 | longer useful 18 | * Updated framework to simplify the construction of a statechart 19 | * refactored logging logic for tracing, errors and warning messages 20 | * added invokeStateMethod to the Ki.StatechartManager mixin 21 | * Added firstCurrentState computed property to Ki.StatechartManager mixin 22 | * Updated the gotoState error message when attempting to transition from one state to another state 23 | where the pivot state's substates are concurrent 24 | * Added destory logic to both Ki.StatechartManager and Ki.State 25 | * Improved the trace and owner feature so that the properties representing them are no longer 26 | fixed. Can now use special key properties to change what owner and trace properties to use 27 | * Added feature to statechart to suppress warning messages produced by the statechart 28 | * Enhanced Ki.State.plugin feature so that it can optionally be provided hash objects that 29 | get added to a plugged-in state upon creation. 30 | 31 | # Ki 0.3.0 January 9, 2011 32 | 33 | * Refactored Ki.StatechartManager mixin logic. initMixin is now part of the initStatechart method 34 | * Added Ki.HistoryState that can be set as the initial substate 35 | * Updated Ki.State toString method. Now provides a better string representation for debugging 36 | * Added fullPath computed property to Ki.State 37 | * Updated Ki.StatechartMonitor's toString method 38 | * Updated statechart logic so that you can now supply an optional context value to both gotoState 39 | and gotoHistoryState that will be supplied to all states that are exited and entered during 40 | a state transition process 41 | * Updated statechart logic so that if a state's initialSubstate is *not* a value then the default 42 | state used will be an empty state (Ki.EmptyState). A root state must *always* have its 43 | initialSubstate property assigned an explicit value. 44 | 45 | # Ki 0.2.0 October 6, 2010 46 | 47 | * Added advanced event handling functionality to framework 48 | * Fixed issue with enterState being called before the state is 49 | considered entered [Colin Campbell] 50 | * Updated statechart so that you can plugin the root state just like any 51 | other state using Ki.State.plugin 52 | * Updated framework to notify when the statechart's current states have 53 | changed and when a state's isCurrentState property has changed. 54 | * Removed statechart alias methods that are no longer useful. 55 | 56 | # Ki 0.1.0 September 7, 2010 57 | 58 | * First beta release of Ki 59 | * Features: 60 | * Nested states 61 | * Parallel states 62 | * State history 63 | * Asynchronous state transitioning 64 | * State Plug-in 65 | * State Namespacing -------------------------------------------------------------------------------- /frameworks/foundation/tests/event_handling/responder/responder_chain.js: -------------------------------------------------------------------------------- 1 | // ========================================================================== 2 | // Ki.Statechart Unit Test 3 | // ========================================================================== 4 | /*globals Ki statechart */ 5 | 6 | var pane, viewA, viewB, stateA, stateB, responder = null; 7 | 8 | module("Ki.Statechart: Responder Chain Tests", { 9 | setup: function() { 10 | pane = SC.MainPane.create({ 11 | 12 | childViews: 'viewA'.w(), 13 | 14 | viewA: SC.View.extend({ 15 | 16 | childViews: 'viewB'.w(), 17 | 18 | viewB: SC.View.extend(Ki.StatechartManager, { 19 | 20 | initialState: 'a', 21 | 22 | a: Ki.State.design({ 23 | mouseDown: function(evt) { 24 | responder = this; 25 | this.gotoState('b'); 26 | }, 27 | 28 | click: function(evt) { 29 | responder = this; 30 | return NO; 31 | } 32 | }), 33 | 34 | b: Ki.State.design({ 35 | mouseUp: function(evt) { 36 | responder = this; 37 | this.gotoState('a'); 38 | } 39 | 40 | }), 41 | 42 | keyUp: function(evt) { 43 | responder = this; 44 | } 45 | 46 | }), 47 | 48 | keyUp: function(evt) { 49 | responder = this; 50 | }, 51 | 52 | keyDown: function(evt) { 53 | responder = this; 54 | }, 55 | 56 | click: function(evt) { 57 | responder = this; 58 | } 59 | 60 | }) 61 | 62 | }); 63 | 64 | viewA = pane.get('viewA'); 65 | viewB = viewA.get('viewB'); 66 | stateA = viewB.getState('a'); 67 | stateB = viewB.getState('b'); 68 | }, 69 | 70 | teardown: function() { 71 | pane = viewA = viewB = stateA = stateB = responder = null; 72 | } 73 | }); 74 | 75 | test("check state A and B are responders -- mouseDown, mouseUp", function() { 76 | equals(responder, null, "responder should be null"); 77 | ok(stateA.get('isCurrentState'), "state A should be current state"); 78 | ok(!stateB.get('isCurrentState'), "state B should not be current state"); 79 | 80 | pane.sendEvent('mouseDown', {}, viewB); 81 | 82 | ok(!stateA.get('isCurrentState'), "state A should not be current state"); 83 | ok(stateB.get('isCurrentState'), "state B shold be current state"); 84 | equals(responder, stateA, "state A should be responder"); 85 | 86 | pane.sendEvent('mouseUp', {}, viewB); 87 | 88 | ok(stateA.get('isCurrentState'), "state A should be current state"); 89 | ok(!stateB.get('isCurrentState'), "state B shold not be current state"); 90 | equals(responder, stateB, "state B should be responder"); 91 | }); 92 | 93 | test("check view B is responder -- keyUp", function() { 94 | equals(responder, null, "responder should be null"); 95 | pane.sendEvent('keyUp', {}, viewB); 96 | equals(responder, viewB, "view A should be responder"); 97 | }); 98 | 99 | test("check view A is responder -- keyDown", function() { 100 | equals(responder, null, "responder should be null"); 101 | pane.sendEvent('keyDown', {}, viewB); 102 | equals(responder, viewA, "view A should be responder"); 103 | }); 104 | 105 | test("check view A is responder -- click", function() { 106 | equals(responder, null, "responder should be null"); 107 | pane.sendEvent('click', {}, viewB); 108 | equals(responder, viewA, "view A should be responder"); 109 | }); -------------------------------------------------------------------------------- /frameworks/foundation/tests/statechart/destroy.js: -------------------------------------------------------------------------------- 1 | // ========================================================================== 2 | // Ki.Statechart Unit Test 3 | // ========================================================================== 4 | /*globals Ki statechart State */ 5 | 6 | var obj, rootState, stateA, stateB; 7 | 8 | module("Ki.Statechart: Destroy Statechart Tests", { 9 | setup: function() { 10 | 11 | obj = SC.Object.create(Ki.StatechartManager, { 12 | 13 | initialState: 'stateA', 14 | 15 | stateA: Ki.State.design(), 16 | 17 | stateB: Ki.State.design() 18 | 19 | }); 20 | 21 | obj.initStatechart(); 22 | rootState = obj.get('rootState'); 23 | stateA = obj.getState('stateA'); 24 | stateB = obj.getState('stateB'); 25 | }, 26 | 27 | teardown: function() { 28 | obj = rootState = stateA = stateB = null; 29 | } 30 | }); 31 | 32 | test("check obj before and after destroy", function() { 33 | ok(!obj.get('isDestroyed'), "obj should not be destroyed"); 34 | ok(obj.hasObserverFor('owner'), "obj should have observers for property owner"); 35 | ok(obj.hasObserverFor('trace'), "obj should have observers for property trace"); 36 | equals(obj.get('rootState'), rootState, "object should have a root state"); 37 | 38 | ok(!rootState.get('isDestroyed'), "root state should not be destoryed"); 39 | equals(rootState.getPath('substates.length'), 2, "root state should have two substates"); 40 | equals(rootState.getPath('currentSubstates.length'), 1, "root state should one current substate"); 41 | equals(rootState.get('historyState'), stateA, "root state should have history state of A"); 42 | equals(rootState.get('initialSubstate'), stateA, "root state should have initial substate of A"); 43 | equals(rootState.get('statechart'), obj, "root state's statechart should be obj"); 44 | equals(rootState.get('owner'), obj, "root state's owner should be obj"); 45 | 46 | ok(!stateA.get('isDestroyed'), "state A should not be destoryed"); 47 | equals(stateA.get('parentState'), rootState, "state A should have a parent state of root state"); 48 | 49 | ok(!stateB.get('isDestroyed'), "state B should not be destroyed"); 50 | equals(stateB.get('parentState'), rootState, "state B should have a parent state of root state"); 51 | 52 | obj.destroy(); 53 | 54 | ok(obj.get('isDestroyed'), "obj should be destroyed"); 55 | ok(!obj.hasObserverFor('owner'), "obj should not have observers for property owner"); 56 | ok(!obj.hasObserverFor('trace'), "obj should not have observers for property trace"); 57 | equals(obj.get('rootState'), null, "obj should not have a root state"); 58 | 59 | ok(rootState.get('isDestroyed'), "root state should be destroyed"); 60 | equals(rootState.get('substates'), null, "root state should not have substates"); 61 | equals(rootState.get('currentSubstates'), null, "root state should not have current substates"); 62 | equals(rootState.get('parentState'), null, "root state should not have a parent state"); 63 | equals(rootState.get('historyState'), null, "root state should not have a history state"); 64 | equals(rootState.get('initialSubstate'), null, "root state should not have an initial substate"); 65 | equals(rootState.get('statechart'), null, "root state's statechart should be null"); 66 | equals(rootState.get('owner'), null, "root state's owner should be null"); 67 | 68 | ok(stateA.get('isDestroyed'), "state A should be destroyed"); 69 | equals(stateA.get('parentState'), null, "state A should not have a parent state"); 70 | 71 | ok(stateB.get('isDestroyed'), "state B should be destroyed"); 72 | equals(stateB.get('parentState'), null, "state B should not have a parent state"); 73 | }); -------------------------------------------------------------------------------- /frameworks/foundation/debug/monitor.js: -------------------------------------------------------------------------------- 1 | // ========================================================================== 2 | // Project: Ki - A Statechart Framework for SproutCore 3 | // License: Licensed under MIT license (see license.js) 4 | // ========================================================================== 5 | 6 | /*globals Ki */ 7 | 8 | Ki.StatechartMonitor = SC.Object.extend({ 9 | 10 | statechart: null, 11 | 12 | sequence: null, 13 | 14 | init: function() { 15 | sc_super(); 16 | this.reset(); 17 | }, 18 | 19 | reset: function() { 20 | this.propertyWillChange('length'); 21 | this.sequence = []; 22 | this.propertyDidChange('length'); 23 | }, 24 | 25 | length: function() { 26 | return this.sequence.length; 27 | }.property(), 28 | 29 | pushEnteredState: function(state) { 30 | this.propertyWillChange('length'); 31 | this.sequence.push({ entered: state }); 32 | this.propertyDidChange('length'); 33 | }, 34 | 35 | pushExitedState: function(state) { 36 | this.propertyWillChange('length'); 37 | this.sequence.push({ exited: state }); 38 | this.propertyDidChange('length'); 39 | }, 40 | 41 | matchSequence: function() { 42 | return Ki.StatechartSequenceMatcher.create({ 43 | statechartMonitor: this 44 | }); 45 | }, 46 | 47 | matchEnteredStates: function() { 48 | var expected = SC.A(arguments.length === 1 ? arguments[0] : arguments), 49 | actual = this.getPath('statechart.enteredStates'), 50 | matched = 0, 51 | statechart = this.get('statechart'); 52 | 53 | if (expected.length !== actual.length) return NO; 54 | 55 | expected.forEach(function(item) { 56 | if (SC.typeOf(item) === SC.T_STRING) item = statechart.getState(item); 57 | if (!item) return; 58 | if (statechart.stateIsEntered(item) && item.get('isEnteredState')) matched += 1; 59 | }); 60 | 61 | return matched === actual.length; 62 | }, 63 | 64 | toString: function() { 65 | var seq = "", 66 | i = 0, 67 | len = 0, 68 | item = null; 69 | 70 | seq += "["; 71 | 72 | len = this.sequence.length; 73 | for (i = 0; i < len; i += 1) { 74 | item = this.sequence[i]; 75 | if (item.exited) { 76 | seq += "exited %@".fmt(item.exited.get('fullPath')); 77 | } 78 | else if (item.entered) { 79 | seq += "entered %@".fmt(item.entered.get('fullPath')); 80 | } 81 | if (i < len - 1) seq += ", "; 82 | } 83 | 84 | seq += "]"; 85 | 86 | return seq; 87 | } 88 | 89 | }); 90 | 91 | Ki.StatechartSequenceMatcher = SC.Object.extend({ 92 | 93 | statechartMonitor: null, 94 | 95 | position: 0, 96 | 97 | match: YES, 98 | 99 | begin: function() { 100 | this.position = -1; 101 | this.match = YES; 102 | return this; 103 | }, 104 | 105 | end: function() { 106 | return this.match; 107 | }, 108 | 109 | entered: function() { 110 | return this._doCheck('entered', arguments); 111 | }, 112 | 113 | exited: function() { 114 | return this._doCheck('exited', arguments); 115 | }, 116 | 117 | _doCheck: function(event, args) { 118 | var i = 0, 119 | len = args.length, 120 | seqItem = null, 121 | arg = null, 122 | seq = this.statechartMonitor.sequence; 123 | 124 | for (; i < len; i += 1) { 125 | this.position += 1; 126 | 127 | if (this.position >= seq.length) { 128 | this.match = NO; 129 | return this; 130 | } 131 | 132 | seqItem = seq[this.position]; 133 | if (!seqItem[event]) { 134 | this.match = NO; 135 | return this; 136 | } 137 | 138 | arg = args[i]; 139 | if (SC.typeOf(arg) === SC.T_OBJECT) { 140 | if (seqItem[event] !== arg) { 141 | this.match = NO; 142 | return this; 143 | } 144 | } 145 | else if (seqItem[event].get('name') !== arg) { 146 | this.match = NO; 147 | return this; 148 | } 149 | } 150 | 151 | return this; 152 | } 153 | 154 | }); -------------------------------------------------------------------------------- /frameworks/foundation/tests/event_handling/basic/without_concurrent_states.js: -------------------------------------------------------------------------------- 1 | // ========================================================================== 2 | // Ki.Statechart Unit Test 3 | // ========================================================================== 4 | /*globals Ki */ 5 | 6 | var statechart = null; 7 | 8 | // .......................................................... 9 | // CONTENT CHANGING 10 | // 11 | 12 | module("Ki.Statechart: No Concurrent States - Send Event Tests", { 13 | setup: function() { 14 | 15 | statechart = Ki.Statechart.create({ 16 | 17 | monitorIsActive: YES, 18 | 19 | rootState: Ki.State.design({ 20 | 21 | initialSubstate: 'a', 22 | 23 | a: Ki.State.design({ 24 | 25 | initialSubstate: 'c', 26 | 27 | eventB: function() { 28 | this.gotoState('b'); 29 | }, 30 | 31 | c: Ki.State.design({ 32 | eventA: function() { this.gotoState('d'); } 33 | }), 34 | 35 | d: Ki.State.design({ 36 | sender: null, 37 | context: null, 38 | eventC: function(sender, context) { 39 | this.set('sender', sender); 40 | this.set('context', context); 41 | this.gotoState('f'); 42 | } 43 | }) 44 | 45 | }), 46 | 47 | b: Ki.State.design({ 48 | 49 | initialSubstate: 'e', 50 | 51 | e: Ki.State.design(), 52 | 53 | f: Ki.State.design() 54 | 55 | }) 56 | 57 | }) 58 | 59 | }); 60 | 61 | statechart.initStatechart(); 62 | }, 63 | 64 | teardown: function() { 65 | statechart.destroy(); 66 | } 67 | }); 68 | 69 | test("send event eventA while in state C", function() { 70 | var monitor = statechart.get('monitor'); 71 | monitor.reset(); 72 | statechart.sendEvent('eventA'); 73 | 74 | equals(monitor.get('length'), 2, 'state sequence should be of length 2'); 75 | equals(monitor.matchSequence().begin().exited('c').entered('d').end(), true, 'sequence should be exited[c], entered[d]'); 76 | equals(statechart.stateIsCurrentState('d'), true, 'current state should be d'); 77 | }); 78 | 79 | test("send event eventB while in parent state A", function() { 80 | var monitor = statechart.get('monitor'); 81 | monitor.reset(); 82 | statechart.sendEvent('eventB'); 83 | 84 | equals(monitor.get('length'), 4, 'state sequence should be of length 4'); 85 | equals(monitor.matchSequence().begin().exited('c', 'a').entered('b', 'e').end(), true, 'sequence should be exited[c, a], entered[b, e]'); 86 | equals(statechart.stateIsCurrentState('e'), true, 'current state should be e'); 87 | }); 88 | 89 | test("send event eventC while in state D", function() { 90 | var monitor = statechart.get('monitor'), 91 | stateD = statechart.getState('d'); 92 | 93 | statechart.gotoState('d'); 94 | 95 | monitor.reset(); 96 | 97 | statechart.sendEvent('eventC', statechart, 'foobar'); 98 | 99 | equals(monitor.get('length'), 4, 'state sequence should be of length 4'); 100 | equals(monitor.matchSequence().begin().exited('d', 'a').entered('b', 'f').end(), true, 'sequence should be exited[d, a], entered[b, f]'); 101 | equals(statechart.stateIsCurrentState('f'), true, 'current state should be f'); 102 | equals(stateD.get('sender'), statechart); 103 | equals(stateD.get('context'), 'foobar'); 104 | }); 105 | 106 | test("send event eventC while in state C", function() { 107 | var monitor = statechart.get('monitor'); 108 | monitor.reset(); 109 | statechart.sendEvent('eventC'); 110 | 111 | equals(monitor.get('length'), 0, 'state sequence should be of length 0'); 112 | equals(statechart.stateIsCurrentState('c'), true, 'current state should be c'); 113 | }); 114 | 115 | test("send event eventD while in state C", function() { 116 | var monitor = statechart.get('monitor'); 117 | monitor.reset(); 118 | statechart.sendEvent('eventD'); 119 | 120 | equals(monitor.get('length'), 0, 'state sequence should be of length 0'); 121 | equals(statechart.stateIsCurrentState('c'), true, 'current state should be c'); 122 | }); -------------------------------------------------------------------------------- /frameworks/foundation/tests/state_transitioning/async/with_concurrent_states.js: -------------------------------------------------------------------------------- 1 | // ========================================================================== 2 | // Ki.Statechart Unit Test 3 | // ========================================================================== 4 | /*globals Ki */ 5 | 6 | var statechart = null; 7 | 8 | // .......................................................... 9 | // CONTENT CHANGING 10 | // 11 | 12 | module("Ki.Statechart: With Concurrent States - Goto State Asynchronous Tests", { 13 | setup: function() { 14 | 15 | var StateMixin = { 16 | 17 | counter: 0, 18 | 19 | foo: function() { 20 | this.set('counter', this.get('counter') + 1); 21 | this.resumeGotoState(); 22 | }, 23 | 24 | enterState: function() { 25 | return this.performAsync('foo'); 26 | }, 27 | 28 | exitState: function() { 29 | return this.performAsync(function() { this.foo(); }); 30 | } 31 | }; 32 | 33 | statechart = Ki.Statechart.create({ 34 | 35 | monitorIsActive: YES, 36 | 37 | rootState: Ki.State.design({ 38 | 39 | initialSubstate: 'a', 40 | 41 | a: Ki.State.design(), 42 | 43 | b: Ki.State.design({ 44 | 45 | substatesAreConcurrent: YES, 46 | 47 | c: Ki.State.design(StateMixin), 48 | 49 | d: Ki.State.design(StateMixin) 50 | 51 | }) 52 | 53 | }) 54 | 55 | }); 56 | 57 | statechart.initStatechart(); 58 | }, 59 | 60 | teardown: function() { 61 | statechart.destroy(); 62 | statechart = null; 63 | } 64 | }); 65 | 66 | test("go to state b", function() { 67 | var monitor = statechart.get('monitor'), 68 | stateA = statechart.getState('a'), 69 | stateC = statechart.getState('c'), 70 | stateD = statechart.getState('d'); 71 | 72 | monitor.reset(); 73 | 74 | equals(statechart.get('gotoStateActive'), NO, "statechart should not have active gotoState"); 75 | equals(statechart.get('gotoStateSuspended'), NO, "statechart should not have active gotoState suspended"); 76 | 77 | stateA.gotoState('b'); 78 | 79 | equals(statechart.get('gotoStateActive'), NO, "statechart should not have active gotoState"); 80 | equals(statechart.get('gotoStateSuspended'), NO, "statechart should not have active gotoState suspended"); 81 | 82 | equals(monitor.matchSequence() 83 | .begin() 84 | .exited('a') 85 | .entered('b', 'c', 'd') 86 | .end(), 87 | true, 'sequence should be exited[a], entered[b, c, d]'); 88 | equals(statechart.get('currentStateCount'), 2, 'current state count should be 2'); 89 | equals(stateC.get('isCurrentState'), true, 'current state should be c'); 90 | equals(stateD.get('isCurrentState'), true, 'current state should be d'); 91 | equals(stateC.get('counter'), 1, "state c should have counter equal to 1"); 92 | equals(stateD.get('counter'), 1, "state d should have counter equal to 1"); 93 | }); 94 | 95 | test("go to state b, then back to state a", function() { 96 | var monitor = statechart.get('monitor'), 97 | stateA = statechart.getState('a'), 98 | stateB = statechart.getState('b'), 99 | stateC = statechart.getState('c'), 100 | stateD = statechart.getState('d'); 101 | 102 | stateA.gotoState('b'); 103 | 104 | monitor.reset(); 105 | 106 | stateC.gotoState('a'); 107 | 108 | equals(statechart.get('gotoStateActive'), NO, "statechart should not have active gotoState"); 109 | equals(statechart.get('gotoStateSuspended'), NO, "statechart should not have active gotoState suspended"); 110 | 111 | equals(monitor.matchSequence() 112 | .begin() 113 | .exited('c', 'd', 'b') 114 | .entered('a') 115 | .end(), 116 | true, 'sequence should be exited[c, d, b], entered[a]'); 117 | equals(statechart.get('currentStateCount'), 1, 'current state count should be 1'); 118 | equals(stateA.get('isCurrentState'), true, 'current state should not be a'); 119 | equals(stateC.get('isCurrentState'), false, 'current state should not be c'); 120 | equals(stateD.get('isCurrentState'), false, 'current state should not be d'); 121 | equals(stateC.get('counter'), 2, "state c should have counter equal to 2"); 122 | equals(stateD.get('counter'), 2, "state d should have counter equal to 2"); 123 | }); -------------------------------------------------------------------------------- /frameworks/foundation/tests/state_transitioning/transient/without_concurrent_states.js: -------------------------------------------------------------------------------- 1 | // ========================================================================== 2 | // Ki.Statechart Unit Test 3 | // ========================================================================== 4 | /*globals Ki */ 5 | 6 | var statechart; 7 | 8 | // .......................................................... 9 | // CONTENT CHANGING 10 | // 11 | 12 | module("Ki.Statechart: No Concurrent States - Transient State Transition Tests", { 13 | setup: function() { 14 | 15 | statechart = Ki.Statechart.create({ 16 | 17 | monitorIsActive: YES, 18 | 19 | rootState: Ki.State.design({ 20 | 21 | initialSubstate: 'a', 22 | 23 | a: Ki.State.design({ 24 | 25 | initialSubstate: 'b', 26 | 27 | b: Ki.State.design({ 28 | eventC: function() { this.gotoState('c'); }, 29 | eventD: function() { this.gotoState('d'); }, 30 | eventE: function() { this.gotoState('e'); }, 31 | eventX: function() { this.gotoState('x'); } 32 | }), 33 | 34 | c: Ki.State.design({ 35 | enterState: function() { this.gotoState('z'); } 36 | }), 37 | 38 | d: Ki.State.design({ 39 | enterState: function() { this.gotoState('c'); } 40 | }), 41 | 42 | e: Ki.State.design({ 43 | enterState: function() { this.gotoState('f'); } 44 | }), 45 | 46 | f: Ki.State.design(), 47 | 48 | g: Ki.State.design({ 49 | 50 | initialSubstate: 'x', 51 | 52 | foo: function() { /* no-op */ }, 53 | 54 | enterState: function() { 55 | return this.performAsync('foo'); 56 | }, 57 | 58 | x: Ki.State.design({ 59 | enterState: function() { this.gotoState('h'); } 60 | }) 61 | 62 | }), 63 | 64 | h: Ki.State.design() 65 | 66 | }), 67 | 68 | z: Ki.State.design() 69 | 70 | }) 71 | 72 | }); 73 | 74 | statechart.initStatechart(); 75 | }, 76 | 77 | teardown: function() { 78 | statechart.destroy(); 79 | statechart = null; 80 | } 81 | }); 82 | 83 | test("enter transient state C", function() { 84 | var monitor = statechart.get('monitor'), 85 | stateA = statechart.getState('a'), 86 | stateC = statechart.getState('c'); 87 | 88 | monitor.reset(); 89 | statechart.sendEvent('eventC'); 90 | 91 | equals(monitor.get('length'), 5, 'state sequence should be of length 5'); 92 | equals(monitor.matchSequence() 93 | .begin() 94 | .exited('b') 95 | .entered('c') 96 | .exited('c', 'a') 97 | .entered('z') 98 | .end(), true, 99 | 'sequence should be exited[b], entered[c], exited[c, a], entered[z]'); 100 | equals(statechart.stateIsCurrentState('z'), true, 'current state should be z'); 101 | 102 | equals(stateA.get('historyState'), stateC); 103 | }); 104 | 105 | test("enter transient state D", function() { 106 | var monitor = statechart.get('monitor'), 107 | stateA = statechart.getState('a'), 108 | stateC = statechart.getState('c'); 109 | 110 | monitor.reset(); 111 | statechart.sendEvent('eventD'); 112 | 113 | equals(monitor.get('length'), 7, 'state sequence should be of length 7'); 114 | equals(monitor.matchSequence() 115 | .begin() 116 | .exited('b') 117 | .entered('d') 118 | .exited('d') 119 | .entered('c') 120 | .exited('c', 'a') 121 | .entered('z') 122 | .end(), true, 123 | 'sequence should be exited[b], entered[d], exited[d], entered[c], exited[c, a], entered[z]'); 124 | equals(statechart.stateIsCurrentState('z'), true, 'current state should be z'); 125 | 126 | equals(stateA.get('historyState'), stateC); 127 | }); 128 | 129 | test("enter transient state X", function() { 130 | var monitor = statechart.get('monitor'), 131 | stateA = statechart.getState('a'), 132 | stateH = statechart.getState('h'); 133 | 134 | monitor.reset(); 135 | statechart.sendEvent('eventX'); 136 | 137 | equals(monitor.get('length'), 2, 'state sequence should be of length 2'); 138 | equals(monitor.matchSequence() 139 | .begin() 140 | .exited('b') 141 | .entered('g') 142 | .end(), true, 143 | 'sequence should be exited[b], entered[g]'); 144 | equals(statechart.get('gotoStateActive'), true, 'statechart should be in active goto state'); 145 | equals(statechart.get('gotoStateSuspended'), true, 'statechart should have a suspended, active goto state'); 146 | 147 | statechart.resumeGotoState(); 148 | 149 | equals(monitor.get('length'), 6, 'state sequence should be of length 6'); 150 | equals(monitor.matchSequence() 151 | .begin() 152 | .exited('b') 153 | .entered('g', 'x') 154 | .exited('x', 'g') 155 | .entered('h') 156 | .end(), true, 157 | 'sequence should be exited[b], entered[g, x], exited[x, g], entered[h]'); 158 | equals(statechart.get('gotoStateActive'), false, 'statechart should not be in active goto state'); 159 | equals(statechart.get('gotoStateSuspended'), false, 'statechart should not have a suspended, active goto state'); 160 | 161 | equals(stateA.get('historyState'), stateH); 162 | }); -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Ki Framework 2 | 3 | -- A Statechart Framework for Sproutcore 4 | 5 | #Overview 6 | 7 | Ki is a statechart framework that supports: 8 | 9 | * __State Hierarchy__ - nesting of states 10 | * __State Orthogonality__ - state that are independent (concurrent) of each other 11 | * __State Clustering__ - grouping states together within a state 12 | * __State History__ - keeping track of states that were entered, both shallow and deep history 13 | * __Event Handling__ - states reacting to incoming events 14 | * __Asynchronous State Transitioning__ - allow for asynchronous actions during a state transition process 15 | * __Module Design__ - building states independently that can then be connected and reused within a statechart 16 | 17 | The framework's design was closely based on David Harel's original paper ["Statecharts: A Visual Formalism For Complex Systems"](http://www.wisdom.weizmann.ac.il/~harel/papers/Statecharts.pdf). 18 | 19 | #Why Use Statecharts? 20 | 21 | In most applications that react to external and internal asynchronous events, some form of states are used to 22 | help manage when an action can respond to those incoming events. Usually these states are represented as kinds of 23 | variables whose value are checked and updated throughout various objects and modules that make up the application. 24 | The problem with states that are built as such are the following: 25 | 26 | * The actions that rely on the state variables are spread throughout the system and not 27 | centralized with a single entity, such as the state or states they use 28 | * States represented as simple variables are difficult to test as independent entities 29 | * States that are independent of or dependent on other states are not represented explicitly, but, 30 | rather, buried within statements of code 31 | * Transitioning between states is often not done in a centralized and consistent manner 32 | * Modularizing and being able to reuse state logic is often error prone and not done in a consistent manner 33 | 34 | Ki resolves these issues and thereby helps make your state logic more maintainable, testable, and easier to analyze. 35 | 36 | #Some Background on Statecharts 37 | 38 | David Harel originally developed the idea of statecharts back in the early 80s while working on a complex application for the 39 | Israel Aircraft Industries. Given the complexity of the system and the vast number of events the system had 40 | to react to, Harel attempted to make use of traditional state transition diagrams to explicitly design how the 41 | system would use states to react to events and transition to other states within the system. However, trying to use a tradition 42 | approach to representing finite states during the design process led to a few problems. 43 | 44 | First, a small change to the system would often represent a large change to the state transition diagram, and, ultimately, 45 | would lead to exponential growth. This made the state transition diagrams difficult if not 46 | impossible to maintain and analyze. Next, there was no method of being able to abstract away details. No abstraction 47 | meant there was no method of being able to zoom in and out of the state transition diagrams. State transition diagrams were 48 | also not well suited to depict how a group of states were dependent on a common state or states or how states were independent 49 | (orthogonal) of one another. Finally, given that state transition diagrams were drawn on paper, there was no way to economically represent 50 | states and state transitions in order to conserve space. 51 | 52 | Through various experimentation while working on the application, Harel and a team of others worked on new approaches 53 | to address the problems with traditional state transition diagrams. This eventually led to a new way of depicting, analyzing, 54 | and maintaining an application's states: Statecharts. 55 | 56 | Statechart diagrams are able to address the shortcomings of traditional state transition diagrams. They allowed states to be grouped 57 | together and placed with a common parent state. Grouping of states into a common parent state also allowed for abstraction. 58 | In addition, grouping states into a common parent state meant that the number of state transitions could be reduced. 59 | States that were independent of each other could be depicted in statecharts with minimal effort. 60 | 61 | This new approach of depicting states meant that diagrams could now be properly managed for complex systems and would not 62 | grow or change exponentially in size based on small changes to the system. Another benefit of statecharts was its 63 | formal and precise way of describing states, but also allowing for relative ease of analyzing the diagrams even by people who 64 | were not experts of statecharts. 65 | 66 | For more information about the history of statecharts, please refer to Harel's paper ["Statecharts in the Making: 67 | A Personal Account"](http://www.wisdom.weizmann.ac.il/~harel/papers/Statecharts.History.pdf). 68 | 69 | #Adding Ki to Your SproutCore Project 70 | 71 | To add Ki to your SproutCore project, start by acquiring the framework from github: 72 | 73 | $ cd 74 | $ mkdir frameworks # if you don't already have a frameworks folder 75 | $ cd frameworks 76 | $ git clone git://github.com/FrozenCanuck/Ki.git ki 77 | 78 | Once acquired, you then need to update your project's `Buildfile` file. This can be done like so: 79 | 80 | config :all, :required => [:sproutcore, :ki] 81 | 82 | Congrats! You're now on your way to using Ki. 83 | 84 | #Getting Started 85 | 86 | If you're new to Ki or just want information to help you apply statecharts to your app, then refer to the 87 | [Ki wiki](http://github.com/FrozenCanuck/Ki/wiki). -------------------------------------------------------------------------------- /frameworks/foundation/tests/state_transitioning/standard/without_concurrent_states/context.js: -------------------------------------------------------------------------------- 1 | // ========================================================================== 2 | // Ki Unit Test 3 | // ========================================================================== 4 | /*globals Ki */ 5 | 6 | var statechart, 7 | TestState, 8 | context, 9 | monitor, 10 | root, 11 | stateA, 12 | stateB, 13 | stateC, 14 | stateD, 15 | stateE, 16 | stateF; 17 | 18 | module("Ki.Statechart: Supply Context Parameter to gotoState - Without Concurrent States", { 19 | setup: function() { 20 | 21 | TestState = Ki.State.extend({ 22 | enterStateContext: null, 23 | exitStateContext: null, 24 | 25 | enterState: function(context) { 26 | this.set('enterStateContext', context); 27 | }, 28 | 29 | exitState: function(context) { 30 | this.set('exitStateContext', context); 31 | } 32 | }); 33 | 34 | statechart = Ki.Statechart.create({ 35 | 36 | monitorIsActive: YES, 37 | 38 | rootState: TestState.design({ 39 | 40 | initialSubstate: 'a', 41 | 42 | a: TestState.design({ 43 | initialSubstate: 'c', 44 | c: TestState.design(), 45 | d: TestState.design() 46 | }), 47 | 48 | b: TestState.design({ 49 | initialSubstate: 'e', 50 | e: TestState.design(), 51 | f: TestState.design() 52 | }) 53 | }) 54 | 55 | }); 56 | 57 | statechart.initStatechart(); 58 | 59 | context = { foo: 100 }; 60 | 61 | monitor = statechart.get('monitor'); 62 | root = statechart.get('rootState'); 63 | stateA = statechart.getState('a'); 64 | stateB = statechart.getState('b'); 65 | stateC = statechart.getState('c'); 66 | stateD = statechart.getState('d'); 67 | stateE = statechart.getState('e'); 68 | stateF = statechart.getState('f'); 69 | }, 70 | 71 | teardown: function() { 72 | statechart = TestState = monitor = context = null; 73 | root = stateA = stateB = stateC = stateD = stateE = stateF; 74 | } 75 | }); 76 | 77 | test("check statechart initialization", function() { 78 | equals(root.get('enterStateContext'), null); 79 | equals(stateA.get('enterStateContext'), null); 80 | equals(stateC.get('enterStateContext'), null); 81 | }); 82 | 83 | test("pass no context when going to state f using statechart", function() { 84 | statechart.gotoState('f'); 85 | equals(stateF.get('isCurrentState'), true); 86 | equals(stateC.get('exitStateContext'), null); 87 | equals(stateA.get('exitStateContext'), null); 88 | equals(stateB.get('enterStateContext'), null); 89 | equals(stateF.get('enterStateContext'), null); 90 | }); 91 | 92 | test("pass no context when going to state f using state", function() { 93 | stateC.gotoState('f'); 94 | equals(stateF.get('isCurrentState'), true); 95 | equals(stateC.get('exitStateContext'), null); 96 | equals(stateA.get('exitStateContext'), null); 97 | equals(stateB.get('enterStateContext'), null); 98 | equals(stateF.get('enterStateContext'), null); 99 | }); 100 | 101 | test("pass context when going to state f using statechart - gotoState('f', context)", function() { 102 | statechart.gotoState('f', context); 103 | equals(stateF.get('isCurrentState'), true); 104 | equals(stateC.get('exitStateContext'), context, 'state c should have context upon exiting'); 105 | equals(stateA.get('exitStateContext'), context, 'state a should have context upon exiting'); 106 | equals(stateB.get('enterStateContext'), context, 'state b should have context upon entering'); 107 | equals(stateF.get('enterStateContext'), context, 'state f should have context upon entering'); 108 | }); 109 | 110 | test("pass context when going to state f using state - gotoState('f', context)", function() { 111 | stateC.gotoState('f', context); 112 | equals(stateF.get('isCurrentState'), true); 113 | equals(stateC.get('exitStateContext'), context, 'state c should have context upon exiting'); 114 | equals(stateA.get('exitStateContext'), context, 'state a should have context upon exiting'); 115 | equals(stateB.get('enterStateContext'), context, 'state b should have context upon entering'); 116 | equals(stateF.get('enterStateContext'), context, 'state f should have context upon entering'); 117 | }); 118 | 119 | test("pass context when going to state f using statechart - gotoState('f', stateC, context) ", function() { 120 | statechart.gotoState('f', stateC, context); 121 | equals(stateF.get('isCurrentState'), true); 122 | equals(stateC.get('exitStateContext'), context, 'state c should have context upon exiting'); 123 | equals(stateA.get('exitStateContext'), context, 'state a should have context upon exiting'); 124 | equals(stateB.get('enterStateContext'), context, 'state b should have context upon entering'); 125 | equals(stateF.get('enterStateContext'), context, 'state f should have context upon entering'); 126 | }); 127 | 128 | test("pass context when going to state f using statechart - gotoState('f', false, context) ", function() { 129 | statechart.gotoState('f', false, context); 130 | equals(stateF.get('isCurrentState'), true); 131 | equals(stateC.get('exitStateContext'), context, 'state c should have context upon exiting'); 132 | equals(stateA.get('exitStateContext'), context, 'state a should have context upon exiting'); 133 | equals(stateB.get('enterStateContext'), context, 'state b should have context upon entering'); 134 | equals(stateF.get('enterStateContext'), context, 'state f should have context upon entering'); 135 | }); 136 | 137 | test("pass context when going to state f using statechart - gotoState('f', stateC, false, context) ", function() { 138 | statechart.gotoState('f', stateC, false, context); 139 | equals(stateF.get('isCurrentState'), true); 140 | equals(stateC.get('exitStateContext'), context, 'state c should have context upon exiting'); 141 | equals(stateA.get('exitStateContext'), context, 'state a should have context upon exiting'); 142 | equals(stateB.get('enterStateContext'), context, 'state b should have context upon entering'); 143 | equals(stateF.get('enterStateContext'), context, 'state f should have context upon entering'); 144 | }); -------------------------------------------------------------------------------- /frameworks/foundation/tests/state_transitioning/standard/with_concurrent_states/intermediate.js: -------------------------------------------------------------------------------- 1 | // ========================================================================== 2 | // Ki.State Unit Test 3 | // ========================================================================== 4 | /*globals Ki */ 5 | 6 | var statechart = null; 7 | var monitor, root, stateA, stateB, stateC, stateD, stateE, stateF, stateG, stateZ; 8 | 9 | module("Ki.Statechart: With Concurrent States - Goto State Intermediate Tests", { 10 | setup: function() { 11 | 12 | statechart = Ki.Statechart.create({ 13 | 14 | monitorIsActive: YES, 15 | 16 | rootState: Ki.State.design({ 17 | 18 | initialSubstate: 'a', 19 | 20 | a: Ki.State.design({ 21 | substatesAreConcurrent: YES, 22 | 23 | b: Ki.State.design({ 24 | initialSubstate: 'd', 25 | d: Ki.State.design(), 26 | e: Ki.State.design() 27 | }), 28 | 29 | c: Ki.State.design({ 30 | initialSubstate: 'f', 31 | f: Ki.State.design(), 32 | g: Ki.State.design() 33 | }) 34 | }), 35 | 36 | z: Ki.State.design() 37 | }) 38 | 39 | }); 40 | 41 | statechart.initStatechart(); 42 | 43 | monitor = statechart.get('monitor'); 44 | root = statechart.get('rootState'); 45 | stateA = statechart.getState('a'); 46 | stateB = statechart.getState('b'); 47 | stateC = statechart.getState('c'); 48 | stateD = statechart.getState('d'); 49 | stateE = statechart.getState('e'); 50 | stateF = statechart.getState('f'); 51 | stateZ = statechart.getState('z'); 52 | }, 53 | 54 | teardown: function() { 55 | statechart.destroy(); 56 | statechart = monitor = root = null; 57 | stateA = stateB = stateC = stateD = stateE = stateF = stateG = stateZ = null; 58 | } 59 | }); 60 | 61 | test("check statechart initialization", function() { 62 | equals(monitor.get('length'), 6, 'initial state sequence should be of length 5'); 63 | equals(monitor.matchSequence().begin().entered(root, 'a', 'b', 'd', 'c', 'f').end(), true, 'initial sequence should be entered[ROOT, a, b, d, c, f]'); 64 | equals(statechart.get('currentStateCount'), 2, 'current state count should be 2'); 65 | equals(statechart.stateIsCurrentState('d'), true, 'current state should be d'); 66 | equals(statechart.stateIsCurrentState('f'), true, 'current state should be f'); 67 | equals(stateA.stateIsCurrentSubstate('d'), true, 'state a\'s current substate should be state d'); 68 | equals(stateA.stateIsCurrentSubstate('f'), true, 'state a\'s current substate should be state f'); 69 | equals(stateA.stateIsCurrentSubstate('e'), false, 'state a\'s current substate should not be state e'); 70 | equals(stateA.stateIsCurrentSubstate('g'), false, 'state a\'s current substate should not be state g'); 71 | equals(stateB.stateIsCurrentSubstate('d'), true, 'state b\'s current substate should be state d'); 72 | equals(stateB.stateIsCurrentSubstate('e'), false, 'state b\'s current substate should not be state e'); 73 | equals(stateC.stateIsCurrentSubstate('f'), true, 'state c\'s current substate should be state f'); 74 | equals(stateC.stateIsCurrentSubstate('g'), false, 'state c\'s current substate should not be state g'); 75 | 76 | ok(monitor.matchEnteredStates(root, 'a', 'b', 'c', 'd', 'f'), 'states root, A, B, C, D and F should all be entered'); 77 | }); 78 | 79 | test("from state d, go to state z", function() { 80 | monitor.reset(); 81 | stateD.gotoState('z'); 82 | 83 | equals(monitor.get('length'), 6, 'initial state sequence should be of length 6'); 84 | equals(monitor.matchSequence().begin().exited('d', 'b', 'f', 'c', 'a').entered('z').end(), true, 'sequence should be exited[d, b, f, c, a], entered[z]'); 85 | equals(statechart.get('currentStateCount'), 1, 'current state count should be 1'); 86 | equals(statechart.stateIsCurrentState('z'), true, 'current state should be z'); 87 | equals(stateA.getPath('currentSubstates.length'), 0, 'state a should have 0 current substates'); 88 | equals(stateA.stateIsCurrentSubstate('d'), false, 'state a\'s current substate should not be state d'); 89 | equals(stateA.stateIsCurrentSubstate('f'), false, 'state a\'s current substate should not be state f'); 90 | equals(stateA.stateIsCurrentSubstate('e'), false, 'state a\'s current substate should not be state e'); 91 | equals(stateA.stateIsCurrentSubstate('g'), false, 'state a\'s current substate should not be state g'); 92 | 93 | ok(monitor.matchEnteredStates(root, 'z'), 'states root and Z should all be entered'); 94 | }); 95 | 96 | test("from state a, go to state z and then back to state a", function() { 97 | monitor.reset(); 98 | stateA.gotoState('z'); 99 | 100 | equals(monitor.get('length'), 6, 'initial state sequence should be of length 6'); 101 | equals(monitor.matchSequence().begin().exited('d', 'b', 'f', 'c', 'a').entered('z').end(), true, 'sequence should be exited[d, b, f, c, a], entered[z]'); 102 | equals(statechart.get('currentStateCount'), 1, 'current state count should be 1'); 103 | equals(statechart.stateIsCurrentState('z'), true, 'current state should be z'); 104 | 105 | monitor.reset(); 106 | stateZ.gotoState('a'); 107 | 108 | equals(monitor.get('length'), 6, 'initial state sequence should be of length 6'); 109 | equals(monitor.matchSequence().begin().exited('z').entered('a', 'b', 'd', 'c', 'f').end(), true, 'sequence should be exited[z], entered[a, b, d, c, f]'); 110 | equals(statechart.get('currentStateCount'), 2, 'current state count should be 1'); 111 | equals(statechart.stateIsCurrentState('d'), true, 'current state should be d'); 112 | equals(statechart.stateIsCurrentState('f'), true, 'current state should be f'); 113 | equals(stateA.getPath('currentSubstates.length'), 2, 'state a should have 2 current substates'); 114 | equals(stateA.stateIsCurrentSubstate('d'), true, 'state a\'s current substate should be state d'); 115 | equals(stateA.stateIsCurrentSubstate('e'), false, 'state a\'s current substate should not be state e'); 116 | equals(stateA.stateIsCurrentSubstate('f'), true, 'state a\'s current substate should be state f'); 117 | equals(stateA.stateIsCurrentSubstate('g'), false, 'state a\'s current substate should not be state g'); 118 | 119 | ok(monitor.matchEnteredStates(root, 'a', 'b', 'c', 'd', 'f'), 'states root, A, B, C, D and F should all be entered'); 120 | }); -------------------------------------------------------------------------------- /frameworks/foundation/tests/event_handling/basic/with_concurrent_states.js: -------------------------------------------------------------------------------- 1 | // ========================================================================== 2 | // Ki.Statechart Unit Test 3 | // ========================================================================== 4 | /*globals Ki */ 5 | 6 | var statechart = null; 7 | 8 | // .......................................................... 9 | // CONTENT CHANGING 10 | // 11 | 12 | module("Ki.Statechart: With Concurrent States - Send Event Tests", { 13 | setup: function() { 14 | 15 | statechart = Ki.Statechart.create({ 16 | 17 | monitorIsActive: YES, 18 | 19 | rootState: Ki.State.design({ 20 | 21 | initialSubstate: 'x', 22 | 23 | x: Ki.State.design({ 24 | 25 | substatesAreConcurrent: YES, 26 | 27 | a: Ki.State.design({ 28 | 29 | initialSubstate: 'c', 30 | 31 | eventAInvoked: NO, 32 | 33 | eventA: function() { this.set('eventAInvoked', YES); }, 34 | 35 | c: Ki.State.design({ 36 | eventB: function() { this.gotoState('d'); }, 37 | eventD: function() { this.gotoState('y'); } 38 | }), 39 | 40 | d: Ki.State.design({ 41 | eventC: function() { this.gotoState('c'); } 42 | }) 43 | 44 | }), 45 | 46 | b: Ki.State.design({ 47 | 48 | initialSubstate: 'e', 49 | 50 | eventAInvoked: NO, 51 | 52 | eventA: function() { this.set('eventAInvoked', YES); }, 53 | 54 | e: Ki.State.design({ 55 | eventB: function() { this.gotoState('f'); }, 56 | eventD: function() { this.gotoState('y'); } 57 | }), 58 | 59 | f: Ki.State.design({ 60 | eventC: function() { this.gotoState('e'); } 61 | }) 62 | 63 | }) 64 | 65 | }), 66 | 67 | y: Ki.State.design() 68 | 69 | }) 70 | 71 | }); 72 | 73 | statechart.initStatechart(); 74 | }, 75 | 76 | teardown: function() { 77 | statechart.destroy(); 78 | statechart = null; 79 | } 80 | }); 81 | 82 | test("send event eventA", function() { 83 | var monitor = statechart.get('monitor'), 84 | stateA = statechart.getState('a'), 85 | stateB = statechart.getState('b'); 86 | 87 | monitor.reset(); 88 | 89 | equals(stateA.get('eventAInvoked'), false); 90 | equals(stateB.get('eventAInvoked'), false); 91 | 92 | statechart.sendEvent('eventA'); 93 | 94 | equals(monitor.get('length'), 0, 'state sequence should be of length 0'); 95 | equals(statechart.stateIsCurrentState('c'), true, 'current state should be c'); 96 | equals(statechart.stateIsCurrentState('e'), true, 'current state should be e'); 97 | equals(stateA.get('eventAInvoked'), true); 98 | equals(stateB.get('eventAInvoked'), true); 99 | }); 100 | 101 | test("send event eventB", function() { 102 | var monitor = statechart.get('monitor'); 103 | 104 | monitor.reset(); 105 | 106 | equals(statechart.stateIsCurrentState('c'), true, 'current state should be c'); 107 | equals(statechart.stateIsCurrentState('e'), true, 'current state should be e'); 108 | 109 | statechart.sendEvent('eventB'); 110 | 111 | equals(statechart.get('currentStateCount'), 2, 'current state count should be 2'); 112 | equals(statechart.stateIsCurrentState('d'), true, 'current state should be d'); 113 | equals(statechart.stateIsCurrentState('f'), true, 'current state should be f'); 114 | 115 | equals(monitor.get('length'), 4, 'state sequence should be of length 4'); 116 | equals(monitor.matchSequence() 117 | .begin() 118 | .exited('c').entered('d') 119 | .exited('e').entered('f') 120 | .end(), 121 | true, 'sequence should be exited[c], entered[d], exited[e], entered[f]'); 122 | }); 123 | 124 | test("send event eventB then eventC", function() { 125 | var monitor = statechart.get('monitor'); 126 | 127 | statechart.sendEvent('eventB'); 128 | 129 | equals(statechart.stateIsCurrentState('d'), true, 'current state should be d'); 130 | equals(statechart.stateIsCurrentState('f'), true, 'current state should be f'); 131 | 132 | monitor.reset(); 133 | 134 | statechart.sendEvent('eventC'); 135 | 136 | equals(statechart.stateIsCurrentState('c'), true, 'current state should be c'); 137 | equals(statechart.stateIsCurrentState('e'), true, 'current state should be e'); 138 | 139 | equals(monitor.get('length'), 4, 'state sequence should be of length 4'); 140 | equals(monitor.matchSequence() 141 | .begin() 142 | .exited('d').entered('c') 143 | .exited('f').entered('e') 144 | .end(), 145 | true, 'sequence should be exited[d], entered[c], exited[f], entered[e]'); 146 | }); 147 | 148 | test("send event eventD", function() { 149 | var monitor = statechart.get('monitor'); 150 | 151 | monitor.reset(); 152 | 153 | equals(statechart.stateIsCurrentState('c'), true, 'current state should be c'); 154 | equals(statechart.stateIsCurrentState('e'), true, 'current state should be e'); 155 | 156 | statechart.sendEvent('eventD'); 157 | 158 | equals(monitor.get('length'), 6, 'state sequence should be of length 6'); 159 | equals(monitor.matchSequence() 160 | .begin() 161 | .exited('c', 'a', 'e', 'b', 'x').entered('y') 162 | .end(), 163 | true, 'sequence should be exited[c, a, e, b, x], entered[y]'); 164 | 165 | equals(statechart.currentStateCount(), 1, 'statechart should only have 1 current state'); 166 | equals(statechart.stateIsCurrentState('c'), false, 'current state not should be c'); 167 | equals(statechart.stateIsCurrentState('e'), false, 'current state not should be e'); 168 | equals(statechart.stateIsCurrentState('y'), true, 'current state should be y'); 169 | }); 170 | 171 | test("send event eventZ", function() { 172 | var monitor = statechart.get('monitor'); 173 | 174 | monitor.reset(); 175 | 176 | equals(statechart.stateIsCurrentState('c'), true, 'current state should be c'); 177 | equals(statechart.stateIsCurrentState('e'), true, 'current state should be e'); 178 | 179 | equals(monitor.get('length'), 0, 'state sequence should be of length 0'); 180 | 181 | equals(statechart.stateIsCurrentState('c'), true, 'current state should be c'); 182 | equals(statechart.stateIsCurrentState('e'), true, 'current state should be e'); 183 | }); 184 | -------------------------------------------------------------------------------- /frameworks/foundation/tests/state_transitioning/standard/with_concurrent_states/basic.js: -------------------------------------------------------------------------------- 1 | // ========================================================================== 2 | // Ki Unit Test 3 | // ========================================================================== 4 | /*globals Ki */ 5 | 6 | var statechart = null; 7 | var monitor, root, stateA, stateB, stateC, stateD, stateE, stateF; 8 | 9 | module("Ki.Statechart: With Concurrent States - Goto State Basic Tests", { 10 | setup: function() { 11 | 12 | statechart = Ki.Statechart.create({ 13 | 14 | monitorIsActive: YES, 15 | 16 | rootState: Ki.State.design({ 17 | 18 | substatesAreConcurrent: YES, 19 | 20 | a: Ki.State.design({ 21 | initialSubstate: 'c', 22 | c: Ki.State.design(), 23 | d: Ki.State.design() 24 | }), 25 | 26 | b: Ki.State.design({ 27 | initialSubstate: 'e', 28 | e: Ki.State.design(), 29 | f: Ki.State.design() 30 | }) 31 | }) 32 | 33 | }); 34 | 35 | statechart.initStatechart(); 36 | 37 | monitor = statechart.get('monitor'); 38 | root = statechart.get('rootState'); 39 | stateA = statechart.getState('a'); 40 | stateB = statechart.getState('b'); 41 | stateC = statechart.getState('c'); 42 | stateD = statechart.getState('d'); 43 | stateE = statechart.getState('e'); 44 | stateF = statechart.getState('f'); 45 | }, 46 | 47 | teardown: function() { 48 | statechart.destroy(); 49 | statechart = monitor = root = stateA = stateB = stateC = stateD = stateE = stateF = null; 50 | } 51 | }); 52 | 53 | test("check statechart initialization", function() { 54 | equals(monitor.get('length'), 5, 'initial state sequence should be of length 5'); 55 | equals(monitor.matchSequence().begin().entered(root, 'a', 'c', 'b', 'e').end(), true, 'initial sequence should be entered[ROOT, a, c, b, e]'); 56 | equals(monitor.matchSequence().begin().entered(root, 'a', 'b', 'c', 'e').end(), false, 'initial sequence should not be entered[ROOT, a, b, c, e]'); 57 | 58 | equals(statechart.get('currentStateCount'), 2, 'current state count should be 2'); 59 | 60 | equals(statechart.stateIsCurrentState('c'), true, 'current state should be c'); 61 | equals(statechart.stateIsCurrentState('e'), true, 'current state should be e'); 62 | equals(statechart.stateIsCurrentState('d'), false, 'current state should not be d'); 63 | equals(statechart.stateIsCurrentState('f'), false, 'current state should not be f'); 64 | 65 | equals(stateA.stateIsCurrentSubstate('c'), true, 'state a\'s current substate should be state c'); 66 | equals(stateA.stateIsCurrentSubstate('d'), false, 'state a\'s current substate should not be state d'); 67 | equals(stateB.stateIsCurrentSubstate('e'), true, 'state a\'s current substate should be state e'); 68 | equals(stateB.stateIsCurrentSubstate('f'), false, 'state a\'s current substate should not be state f'); 69 | 70 | equals(stateA.get('isCurrentState'), false, 'state a should not be current state'); 71 | equals(stateB.get('isCurrentState'), false, 'state b should not be current state'); 72 | equals(stateC.get('isCurrentState'), true, 'state c should be current state'); 73 | equals(stateD.get('isCurrentState'), false, 'state d should not be current state'); 74 | equals(stateE.get('isCurrentState'), true, 'state e should be current state'); 75 | equals(stateF.get('isCurrentState'), false, 'state f should not be current state'); 76 | 77 | ok(monitor.matchEnteredStates(root, 'a', 'c', 'b', 'e'), 'states root, A, C, B and E should all be entered'); 78 | }); 79 | 80 | test("from state c, go to state d, and from state e, go to state f", function() { 81 | monitor.reset(); 82 | 83 | stateC.gotoState('d'); 84 | equals(monitor.get('length'), 2, 'state sequence should be of length 2'); 85 | equals(monitor.matchSequence().begin().exited('c').entered('d').end(), true, 'sequence should be exited[c], enterd[d]'); 86 | 87 | monitor.reset(); 88 | 89 | stateE.gotoState('f'); 90 | equals(monitor.get('length'), 2, 'state sequence should be of length 2'); 91 | equals(monitor.matchSequence().begin().exited('e').entered('f').end(), true, 'sequence should be exited[e], enterd[f]'); 92 | 93 | equals(statechart.get('currentStateCount'), 2, 'current state count should be 2'); 94 | 95 | equals(statechart.stateIsCurrentState('d'), true, 'current state should be d'); 96 | equals(statechart.stateIsCurrentState('f'), true, 'current state should be f'); 97 | 98 | equals(stateA.stateIsCurrentSubstate('c'), false, 'state a\'s current substate should not be state c'); 99 | equals(stateA.stateIsCurrentSubstate('d'), true, 'state a\'s current substate should be state d'); 100 | equals(stateB.stateIsCurrentSubstate('e'), false, 'state b\'s current substate should not be state e'); 101 | equals(stateB.stateIsCurrentSubstate('f'), true, 'state b\'s current substate should be state f'); 102 | 103 | equals(stateA.get('isCurrentState'), false, 'state a should not be current state'); 104 | equals(stateB.get('isCurrentState'), false, 'state b should not be current state'); 105 | equals(stateC.get('isCurrentState'), false, 'state c should not be current state'); 106 | equals(stateD.get('isCurrentState'), true, 'state d should be current state'); 107 | equals(stateE.get('isCurrentState'), false, 'state e should not be current state'); 108 | equals(stateF.get('isCurrentState'), true, 'state f should be current state'); 109 | 110 | ok(monitor.matchEnteredStates(root, 'a', 'd', 'b', 'f'), 'states root, A, D, B and F should all be entered'); 111 | }); 112 | 113 | test("from state a, go to sibling concurrent state b", function() { 114 | monitor.reset(); 115 | 116 | // Expect to get an error to be outputted in the JS console, which is what we want since 117 | // the pivot state is the root state and it's substates are concurrent 118 | console.log('expecting to get an error...'); 119 | stateA.gotoState('b'); 120 | 121 | equals(monitor.get('length'), 0, 'state sequence should be of length 0'); 122 | equals(statechart.get('currentStateCount'), 2, 'current state count should be 2'); 123 | equals(statechart.stateIsCurrentState('c'), true, 'current state should be c'); 124 | equals(statechart.stateIsCurrentState('e'), true, 'current state should be e'); 125 | equals(stateA.stateIsCurrentSubstate('c'), true, 'state a\'s current substate should be state c'); 126 | equals(stateA.stateIsCurrentSubstate('d'), false, 'state a\'s current substate should not be state d'); 127 | equals(stateB.stateIsCurrentSubstate('e'), true, 'state a\'s current substate should be state e'); 128 | equals(stateB.stateIsCurrentSubstate('f'), false, 'state a\'s current substate should not be state f'); 129 | 130 | ok(monitor.matchEnteredStates(root, 'a', 'c', 'b', 'e'), 'states root, A, C, B and E should all be entered'); 131 | }); -------------------------------------------------------------------------------- /frameworks/foundation/tests/statechart/create/unassigned_root_state.js: -------------------------------------------------------------------------------- 1 | // ========================================================================== 2 | // Ki.Statechart Unit Test 3 | // ========================================================================== 4 | /*globals Ki statechart State */ 5 | 6 | var obj1, rootState1, stateA, stateB; 7 | var obj2, rootState2, stateC, stateD; 8 | var obj3, rootState3, stateE, rootStateExample; 9 | var obj4; 10 | var owner; 11 | 12 | module("Ki.Statechart: Create Statechart with Unassigned Root State Tests", { 13 | setup: function() { 14 | owner = SC.Object.create(); 15 | 16 | obj1 = SC.Object.extend(Ki.StatechartManager, { 17 | 18 | initialState: 'stateA', 19 | 20 | stateA: Ki.State.design({ 21 | foo: function() { 22 | this.gotoState('stateB'); 23 | } 24 | }), 25 | 26 | stateB: Ki.State.design({ 27 | bar: function() { 28 | this.gotoState('stateA'); 29 | } 30 | }) 31 | 32 | }); 33 | 34 | obj1 = obj1.create(); 35 | rootState1 = obj1.get('rootState'); 36 | stateA = obj1.getState('stateA'); 37 | stateB = obj1.getState('stateB'); 38 | 39 | obj2 = SC.Object.extend(Ki.StatechartManager, { 40 | 41 | statesAreConcurrent: YES, 42 | 43 | stateC: Ki.State.design({ 44 | foo: function() { 45 | this.gotoState('stateD'); 46 | } 47 | }), 48 | 49 | stateD: Ki.State.design({ 50 | bar: function() { 51 | this.gotoState('stateC'); 52 | } 53 | }) 54 | 55 | }); 56 | 57 | obj2 = obj2.create(); 58 | rootState2 = obj2.get('rootState'); 59 | stateC = obj2.getState('stateC'); 60 | stateD = obj2.getState('stateD'); 61 | 62 | rootStateExample = Ki.State.extend({ test: 'foo' }); 63 | 64 | obj3 = SC.Object.extend(Ki.StatechartManager, { 65 | owner: owner, 66 | initialState: 'stateE', 67 | rootStateExample: rootStateExample, 68 | stateE: Ki.State.design() 69 | }); 70 | 71 | obj3 = obj3.create(); 72 | rootState3 = obj3.get('rootState'); 73 | stateE = obj3.getState('stateE'); 74 | 75 | obj4 = SC.Object.extend(Ki.StatechartManager, { 76 | autoInitStatechart: NO, 77 | initialState: 'stateF', 78 | rootStateExample: rootStateExample, 79 | stateF: Ki.State.design() 80 | }); 81 | 82 | obj4 = obj4.create(); 83 | }, 84 | 85 | teardown: function() { 86 | obj1 = rootState1 = stateA = stateB = null; 87 | obj2 = rootState2 = stateC = stateD = null; 88 | obj3 = rootState3 = stateE = rootStateExample = null; 89 | obj4 = null; 90 | } 91 | }); 92 | 93 | test("check obj1 statechart", function() { 94 | ok(obj1.get('isStatechart'), "obj should be a statechart"); 95 | ok(obj1.get('statechartIsInitialized'), "obj should be an initialized statechart"); 96 | ok(SC.kindOf(rootState1, Ki.State), "root state should be kind of Ki.State"); 97 | ok(!rootState1.get('substateAreConcurrent'), "root state's substates should not be concurrent"); 98 | 99 | equals(obj1.get('initialState'), stateA, "obj's initialState should be state A"); 100 | equals(rootState1.get('initialSubstate'), stateA, "root state's initialState should be state A"); 101 | equals(stateA, rootState1.getSubstate('stateA'), "obj.stateA and rootState.stateA should be equal"); 102 | equals(stateB, rootState1.getSubstate('stateB'), "obj.stateB and rootState.stateB should be equal"); 103 | 104 | equals(rootState1.get('owner'), obj1, "root state's owner should be obj"); 105 | equals(stateA.get('owner'), obj1, "state A's owner should be obj"); 106 | equals(stateB.get('owner'), obj1, "state B's owner should be obj"); 107 | 108 | ok(stateA.get('isCurrentState'), "state A should be current state"); 109 | ok(!stateB.get('isCurrentState'), "state B should not be current state"); 110 | 111 | obj1.sendEvent('foo'); 112 | 113 | ok(!stateA.get('isCurrentState'), "state A should not be current state"); 114 | ok(stateB.get('isCurrentState'), "state B should be current state"); 115 | }); 116 | 117 | test("check obj2 statechart", function() { 118 | ok(obj2.get('isStatechart'), "obj should be a statechart"); 119 | ok(obj2.get('statechartIsInitialized'), "obj should be an initialized statechart"); 120 | ok(SC.kindOf(rootState2, Ki.State), "root state should be kind of Ki.State"); 121 | ok(rootState2.get('substatesAreConcurrent'), "root state's substates should be concurrent"); 122 | 123 | equals(obj2.get('initialState'), null, "obj's initialState should be null"); 124 | equals(rootState2.get('initialSubstate'), null, "root state's initialState should be null"); 125 | equals(stateC, rootState2.getSubstate('stateC'), "obj.stateC and rootState.stateC should be equal"); 126 | equals(stateD, rootState2.getSubstate('stateD'), "obj.stateD and rootState.stateD should be equal"); 127 | 128 | equals(rootState2.get('owner'), obj2, "root state's owner should be obj"); 129 | equals(stateC.get('owner'), obj2, "state C's owner should be obj"); 130 | equals(stateD.get('owner'), obj2, "state D's owner should be obj"); 131 | 132 | ok(stateC.get('isCurrentState'), "state C should be current state"); 133 | ok(stateD.get('isCurrentState'), "state D should not be current state"); 134 | }); 135 | 136 | test("check obj3 statechart", function() { 137 | ok(obj3.get('isStatechart'), "obj should be a statechart"); 138 | ok(obj3.get('statechartIsInitialized'), "obj should be an initialized statechart"); 139 | ok(SC.kindOf(rootState3, rootStateExample), "root state should be kind of rootStateExample"); 140 | ok(!rootState3.get('substatesAreConcurrent'), "root state's substates should be concurrent"); 141 | 142 | equals(rootState3.get('owner'), owner, "root state's owner should be owner"); 143 | equals(stateE.get('owner'), owner, "state C's owner should be owner"); 144 | 145 | equals(obj3.get('initialState'), stateE, "obj's initialState should be stateE"); 146 | equals(rootState3.get('initialSubstate'), stateE, "root state's initialState should be stateE"); 147 | equals(stateE, rootState3.getSubstate('stateE'), "obj.stateE and rootState.stateE should be equal"); 148 | 149 | ok(stateE.get('isCurrentState'), "state E should be current state"); 150 | }); 151 | 152 | test("check obj4 statechart", function() { 153 | ok(obj4.get('isStatechart'), "obj should be a statechart"); 154 | ok(!obj4.get('statechartIsInitialized'), "obj should not be an initialized statechart"); 155 | equals(obj4.get('rootState'), null, "obj's root state should be null"); 156 | 157 | obj4.initStatechart(); 158 | 159 | ok(obj4.get('statechartIsInitialized'), "obj should be an initialized statechart"); 160 | ok(obj4.get('rootState'), "obj's root state should not be null"); 161 | equals(obj4.get('rootState').getSubstate('stateF'), obj4.getState('stateF'), "obj.stateF should be equal to rootState.stateF"); 162 | }); -------------------------------------------------------------------------------- /frameworks/foundation/tests/state_transitioning/async/without_concurrent_states.js: -------------------------------------------------------------------------------- 1 | // ========================================================================== 2 | // Ki.Statechart Unit Test 3 | // ========================================================================== 4 | /*globals Ki */ 5 | 6 | var statechart = null; 7 | 8 | // .......................................................... 9 | // CONTENT CHANGING 10 | // 11 | 12 | module("Ki.Statechart: No Concurrent States - Goto State Asynchronous Tests", { 13 | setup: function() { 14 | 15 | var StateMixin = { 16 | 17 | counter: 0, 18 | 19 | foo: function() { 20 | this.set('counter', this.get('counter') + 1); 21 | this.resumeGotoState(); 22 | }, 23 | 24 | enterState: function() { 25 | return this.performAsync('foo'); 26 | }, 27 | 28 | exitState: function() { 29 | return this.performAsync(function() { this.foo(); }); 30 | } 31 | }; 32 | 33 | statechart = Ki.Statechart.create({ 34 | 35 | monitorIsActive: YES, 36 | 37 | rootState: Ki.State.design({ 38 | 39 | initialSubstate: 'a', 40 | 41 | a: Ki.State.design(), 42 | 43 | b: Ki.State.design({ 44 | 45 | methodInvoked: null, 46 | 47 | enterState: function() { 48 | return this.performAsync('foo'); 49 | }, 50 | 51 | exitState: function() { 52 | return this.performAsync('bar'); 53 | }, 54 | 55 | foo: function(arg1, arg2) { 56 | this.set('methodInvoked', 'foo'); 57 | }, 58 | 59 | bar: function(arg1, arg2) { 60 | this.set('methodInvoked', 'bar'); 61 | } 62 | 63 | }), 64 | 65 | c: Ki.State.design(StateMixin, { 66 | 67 | initialSubstate: 'd', 68 | 69 | d: Ki.State.design(StateMixin, { 70 | 71 | initialSubstate: 'e', 72 | 73 | e: Ki.State.design(StateMixin) 74 | 75 | }) 76 | 77 | }) 78 | 79 | }) 80 | 81 | }); 82 | 83 | statechart.initStatechart(); 84 | }, 85 | 86 | teardown: function() { 87 | statechart.destroy(); 88 | } 89 | }); 90 | 91 | test("go to state b", function() { 92 | var stateB = statechart.getState('b'), 93 | monitor = statechart.get('monitor'); 94 | 95 | monitor.reset(); 96 | 97 | equals(statechart.get('gotoStateActive'), NO, "statechart should not have active gotoState"); 98 | equals(statechart.get('gotoStateSuspended'), NO, "statechart should not have active gotoState suspended"); 99 | 100 | statechart.gotoState(stateB); 101 | 102 | equals(statechart.get('gotoStateActive'), YES, "statechart should have active gotoState"); 103 | equals(statechart.get('gotoStateSuspended'), YES, "statechart should have active gotoState suspended"); 104 | 105 | statechart.resumeGotoState(); 106 | 107 | equals(statechart.get('gotoStateActive'), NO, "statechart should not have active gotoState"); 108 | equals(statechart.get('gotoStateSuspended'), NO, "statechart should not have active gotoState suspended"); 109 | 110 | equals(monitor.matchSequence().begin().exited('a').entered('b').end(), true, 'sequence should be exited[a], entered[b]'); 111 | equals(statechart.get('currentStateCount'), 1, 'current state count should be 1'); 112 | equals(statechart.stateIsCurrentState('a'), false, 'current state should not be a'); 113 | equals(statechart.stateIsCurrentState('b'), true, 'current state should be b'); 114 | equals(stateB.get('methodInvoked'), 'foo', "state b should have invoked method foo"); 115 | }); 116 | 117 | test("go to state b and then back to state a", function() { 118 | var stateA = statechart.getState('a'), 119 | stateB = statechart.getState('b'), 120 | monitor = statechart.get('monitor'); 121 | 122 | statechart.gotoState(stateB); 123 | statechart.resumeGotoState(); 124 | 125 | monitor.reset(); 126 | 127 | statechart.gotoState(stateA); 128 | 129 | equals(statechart.get('gotoStateActive'), YES, "statechart should have active gotoState"); 130 | equals(statechart.get('gotoStateSuspended'), YES, "statechart should have active gotoState suspended"); 131 | 132 | statechart.resumeGotoState(); 133 | 134 | equals(statechart.get('gotoStateActive'), NO, "statechart should not have active gotoState"); 135 | equals(statechart.get('gotoStateSuspended'), NO, "statechart should not have active gotoState suspended"); 136 | 137 | equals(monitor.matchSequence().begin().exited('b').entered('a').end(), true, 'sequence should be exited[b], entered[a]'); 138 | equals(statechart.get('currentStateCount'), 1, 'current state count should be 1'); 139 | equals(statechart.stateIsCurrentState('a'), true, 'current state should be a'); 140 | equals(statechart.stateIsCurrentState('b'), false, 'current state should not be b'); 141 | equals(stateB.get('methodInvoked'), 'bar', "state b should have invoked method bar"); 142 | }); 143 | 144 | test("go to state c", function() { 145 | var stateC = statechart.getState('c'), 146 | stateD = statechart.getState('d'), 147 | stateE = statechart.getState('e'), 148 | monitor = statechart.get('monitor'); 149 | 150 | monitor.reset(); 151 | 152 | statechart.gotoState(stateC); 153 | 154 | equals(statechart.get('gotoStateActive'), NO, "statechart should not have active gotoState"); 155 | equals(statechart.get('gotoStateSuspended'), NO, "statechart should not have active gotoState suspended"); 156 | 157 | equals(monitor.matchSequence().begin().exited('a').entered('c', 'd', 'e').end(), true, 158 | 'sequence should be exited[a], entered[c, d, e]'); 159 | equals(statechart.get('currentStateCount'), 1, 'current state count should be 1'); 160 | equals(statechart.stateIsCurrentState('a'), false, 'current state should not be a'); 161 | equals(statechart.stateIsCurrentState('e'), true, 'current state should be e'); 162 | equals(stateC.get('counter'), 1, 'state c counter should be 1'); 163 | equals(stateD.get('counter'), 1, 'state d counter should be 1'); 164 | equals(stateE.get('counter'), 1, 'state e counter should be 1'); 165 | }); 166 | 167 | test("go to state c and then back to state a", function() { 168 | var stateA = statechart.getState('a'), 169 | stateC = statechart.getState('c'), 170 | stateD = statechart.getState('d'), 171 | stateE = statechart.getState('e'), 172 | monitor = statechart.get('monitor'); 173 | 174 | statechart.gotoState(stateC); 175 | 176 | monitor.reset(); 177 | 178 | statechart.gotoState(stateA); 179 | 180 | equals(statechart.get('gotoStateActive'), NO, "statechart should not have active gotoState"); 181 | equals(statechart.get('gotoStateSuspended'), NO, "statechart should not have active gotoState suspended"); 182 | 183 | equals(monitor.matchSequence().begin().exited('e', 'd', 'c').entered('a').end(), true, 184 | 'sequence should be exited[e, d, c], entered[a]'); 185 | equals(statechart.get('currentStateCount'), 1, 'current state count should be 1'); 186 | equals(statechart.stateIsCurrentState('a'), true, 'current state should be a'); 187 | equals(statechart.stateIsCurrentState('e'), false, 'current state should not be e'); 188 | equals(stateC.get('counter'), 2, 'state c counter should be 2'); 189 | equals(stateD.get('counter'), 2, 'state d counter should be 2'); 190 | equals(stateE.get('counter'), 2, 'state e counter should be 2'); 191 | }); -------------------------------------------------------------------------------- /frameworks/foundation/tests/statechart/owner.js: -------------------------------------------------------------------------------- 1 | // ========================================================================== 2 | // Ki.Statechart Unit Test 3 | // ========================================================================== 4 | /*globals Ki statechart State */ 5 | 6 | var obj1, rootState1, stateA, stateB, stateX, stateY, stateZ; 7 | var obj2, rootState2, stateC, stateD; 8 | var obj3, rootState3, stateE, stateF; 9 | var owner, owner2; 10 | var TestObject, TestState; 11 | 12 | module("Ki.Statechart: Change Statechart Owner Property Tests", { 13 | setup: function() { 14 | owner = SC.Object.create({ 15 | toString: function() { return "owner"; } 16 | }); 17 | 18 | owner2 = SC.Object.create({ 19 | toString: function() { return "owner2"; } 20 | }); 21 | 22 | TestState = Ki.State.extend({ 23 | accessedOwner: null, 24 | 25 | reset: function() { 26 | this.set('accessedOwner', null); 27 | }, 28 | 29 | render: function() { 30 | this.set('accessedOwner', this.get('owner')); 31 | } 32 | }); 33 | 34 | TestObject = SC.Object.extend(Ki.StatechartManager, { 35 | render: function() { 36 | this.invokeStateMethod('render'); 37 | } 38 | }); 39 | 40 | obj1 = TestObject.extend({ 41 | 42 | initialState: 'stateA', 43 | 44 | stateA: TestState.design({ 45 | foo: function() { 46 | this.gotoState('stateB'); 47 | } 48 | }), 49 | 50 | stateB: TestState.design({ 51 | bar: function() { 52 | this.gotoState('stateA'); 53 | } 54 | }), 55 | 56 | stateX: TestState.design({ 57 | initialSubstate: 'stateY', 58 | 59 | stateY: TestState.design({ 60 | initialSubstate: 'stateZ', 61 | 62 | stateZ: TestState.design() 63 | }) 64 | }) 65 | 66 | }); 67 | 68 | obj1 = obj1.create(); 69 | rootState1 = obj1.get('rootState'); 70 | stateA = obj1.getState('stateA'); 71 | stateB = obj1.getState('stateB'); 72 | stateX = obj1.getState('stateX'); 73 | stateY = obj1.getState('stateY'); 74 | stateZ = obj1.getState('stateZ'); 75 | 76 | obj2 = TestObject.extend({ 77 | 78 | owner: owner, 79 | 80 | initialState: 'stateC', 81 | 82 | stateC: TestState.design({ 83 | foo: function() { 84 | this.gotoState('stateD'); 85 | } 86 | }), 87 | 88 | stateD: TestState.design({ 89 | bar: function() { 90 | this.gotoState('stateC'); 91 | } 92 | }) 93 | 94 | }); 95 | 96 | obj2 = obj2.create(); 97 | rootState2 = obj2.get('rootState'); 98 | stateC = obj2.getState('stateC'); 99 | stateD = obj2.getState('stateD'); 100 | 101 | obj3 = TestObject.extend({ 102 | 103 | statechartOwnerKey: 'fooOwner', 104 | 105 | fooOwner: owner, 106 | 107 | initialState: 'stateE', 108 | 109 | stateE: TestState.design({ 110 | foo: function() { 111 | this.gotoState('stateF'); 112 | } 113 | }), 114 | 115 | stateF: TestState.design({ 116 | bar: function() { 117 | this.gotoState('stateE'); 118 | } 119 | }) 120 | 121 | }); 122 | 123 | obj3 = obj3.create(); 124 | rootState3 = obj3.get('rootState'); 125 | stateE = obj3.getState('stateE'); 126 | stateF = obj3.getState('stateF'); 127 | }, 128 | 129 | teardown: function() { 130 | obj1 = rootState1 = stateA = stateB = stateX = stateY = stateZ = null; 131 | obj2 = rootState2 = stateC = stateD = null; 132 | obj3 = rootState3 = stateE = stateF = null; 133 | owner = owner2 = null; 134 | TestObject = TestState = null; 135 | } 136 | }); 137 | 138 | test("check obj1 -- basic owner get and set", function() { 139 | equals(rootState1.get('owner'), obj1, "root state's owner should be obj"); 140 | equals(stateA.get('owner'), obj1, "state A's owner should be obj"); 141 | equals(stateB.get('owner'), obj1, "state B's owner should be obj"); 142 | equals(stateX.get('owner'), obj1, "state X's owner should be obj"); 143 | equals(stateY.get('owner'), obj1, "state Y's owner should be obj"); 144 | equals(stateZ.get('owner'), obj1, "state Z's owner should be obj"); 145 | 146 | obj1.set('owner', owner); 147 | 148 | equals(rootState1.get('owner'), owner, "root state's owner should be owner"); 149 | equals(stateA.get('owner'), owner, "state A's owner should be owner"); 150 | equals(stateB.get('owner'), owner, "state B's owner should be owner"); 151 | equals(stateX.get('owner'), owner, "state X's owner should be owner"); 152 | equals(stateY.get('owner'), owner, "state Y's owner should be owner"); 153 | equals(stateZ.get('owner'), owner, "state Z's owner should be owner"); 154 | 155 | obj1.set('owner', null); 156 | 157 | equals(rootState1.get('owner'), obj1, "root state's owner should be obj"); 158 | equals(stateA.get('owner'), obj1, "state A's owner should be obj"); 159 | equals(stateB.get('owner'), obj1, "state B's owner should be obj"); 160 | equals(stateX.get('owner'), obj1, "state X's owner should be obj"); 161 | equals(stateY.get('owner'), obj1, "state Y's owner should be obj"); 162 | equals(stateZ.get('owner'), obj1, "state Z's owner should be obj"); 163 | }); 164 | 165 | test("check stateA -- access owner via invokeStateMethod", function() { 166 | ok(stateA.get('isCurrentState')); 167 | equals(stateA.get('accessedOwner'), null); 168 | 169 | obj1.render(); 170 | 171 | equals(stateA.get('accessedOwner'), obj1); 172 | 173 | stateA.reset(); 174 | obj1.set('owner', owner); 175 | obj1.render(); 176 | 177 | equals(stateA.get('accessedOwner'), owner); 178 | }); 179 | 180 | test("check stateZ -- access owner via invokeStateMethod", function() { 181 | obj1.gotoState(stateZ); 182 | ok(stateZ.get('isCurrentState')); 183 | 184 | equals(stateZ.get('accessedOwner'), null); 185 | 186 | obj1.render(); 187 | 188 | equals(stateZ.get('accessedOwner'), obj1); 189 | 190 | stateA.reset(); 191 | obj1.set('owner', owner); 192 | obj1.render(); 193 | 194 | equals(stateZ.get('accessedOwner'), owner); 195 | }); 196 | 197 | test("check obj2 -- statechartOwnerKey", function() { 198 | equals(rootState2.get('owner'), owner, "root state's owner should be owner"); 199 | equals(stateC.get('owner'), owner, "state C's owner should be owner"); 200 | equals(stateD.get('owner'), owner, "state D's owner should be owner"); 201 | 202 | obj2.set('owner', null); 203 | 204 | equals(rootState2.get('owner'), obj2, "root state's owner should be obj"); 205 | equals(stateC.get('owner'), obj2, "state C's owner should be obj"); 206 | equals(stateD.get('owner'), obj2, "state D's owner should be obj"); 207 | }); 208 | 209 | test("check obj3 -- basic owner get and set", function() { 210 | equals(obj3.get('statechartOwnerKey'), 'fooOwner', "obj's statechartOwnerKey should be fooOwner"); 211 | equals(obj3.get('fooOwner'), owner, "obj's fooOwner should be owner"); 212 | 213 | equals(rootState3.get('owner'), owner, "root state's owner should be owner"); 214 | equals(stateE.get('owner'), owner, "state E's owner should be owner"); 215 | equals(stateF.get('owner'), owner, "state F's owner should be owner"); 216 | 217 | obj3.set('fooOwner', null); 218 | 219 | equals(rootState3.get('owner'), obj3, "root state's owner should be obj"); 220 | equals(stateE.get('owner'), obj3, "state E's owner should be obj"); 221 | equals(stateF.get('owner'), obj3, "state F's owner should be obj"); 222 | 223 | obj3.set('fooOwner', owner2); 224 | 225 | equals(rootState3.get('owner'), owner2, "root state's owner2 should be owner2"); 226 | equals(stateE.get('owner'), owner2, "state E's owner2 should be owner2"); 227 | equals(stateF.get('owner'), owner2, "state F's owner2 should be owner2"); 228 | 229 | ok(obj3.hasObserverFor('fooOwner')); 230 | equals(rootState3.get('owner'), owner2); 231 | 232 | obj3.destroy(); 233 | 234 | ok(!obj3.hasObserverFor('fooOwner')); 235 | equals(rootState3.get('owner'), null); 236 | }); -------------------------------------------------------------------------------- /frameworks/foundation/tests/state_transitioning/history_state/initial_substate/without_concurrent_states.js: -------------------------------------------------------------------------------- 1 | // ========================================================================== 2 | // Ki.Statechart Unit Test 3 | // ========================================================================== 4 | /*globals Ki */ 5 | 6 | window.statechart = null; 7 | 8 | // .......................................................... 9 | // CONTENT CHANGING 10 | // 11 | 12 | module("Ki.HistoryState - Without Concurrent States Tests", { 13 | setup: function() { 14 | 15 | statechart = Ki.Statechart.create({ 16 | 17 | monitorIsActive: YES, 18 | 19 | rootState: Ki.State.design({ 20 | 21 | initialSubstate: 'a', 22 | 23 | a: Ki.State.design({ 24 | 25 | initialSubstate: Ki.HistoryState.design({ 26 | defaultState: 'c' 27 | }), 28 | 29 | c: Ki.State.design({ 30 | initialSubstate: 'g', 31 | 32 | g: Ki.State.design(), 33 | h: Ki.State.design() 34 | }), 35 | 36 | d: Ki.State.design({ 37 | initialSubstate: 'i', 38 | 39 | i: Ki.State.design(), 40 | j: Ki.State.design() 41 | }) 42 | 43 | }), 44 | 45 | b: Ki.State.design({ 46 | 47 | initialSubstate: Ki.HistoryState.design({ 48 | isRecursive: YES, 49 | defaultState: 'e' 50 | }), 51 | 52 | e: Ki.State.design({ 53 | initialSubstate: 'k', 54 | 55 | k: Ki.State.design(), 56 | l: Ki.State.design() 57 | }), 58 | 59 | f: Ki.State.design({ 60 | initialSubstate: 'm', 61 | 62 | m: Ki.State.design(), 63 | n: Ki.State.design() 64 | }) 65 | 66 | }) 67 | 68 | }) 69 | 70 | }); 71 | 72 | statechart.initStatechart(); 73 | 74 | }, 75 | 76 | teardown: function() { 77 | //statechart = null; 78 | } 79 | }); 80 | 81 | test("check initial substate after statechart init", function() { 82 | var monitor = statechart.get('monitor'), 83 | root = statechart.get('rootState'), 84 | a = statechart.getState('a'), 85 | b = statechart.getState('b'), 86 | c = statechart.getState('c'), 87 | d = statechart.getState('d'), 88 | e = statechart.getState('e'), 89 | f = statechart.getState('f'), 90 | g = statechart.getState('g'), 91 | h = statechart.getState('h'), 92 | i = statechart.getState('i'), 93 | j = statechart.getState('j'), 94 | k = statechart.getState('k'), 95 | l = statechart.getState('l'), 96 | m = statechart.getState('m'), 97 | n = statechart.getState('n'), 98 | aInitSubstate = a.get('initialSubstate'), 99 | bInitSubstate = b.get('initialSubstate'); 100 | 101 | equals(monitor.get('length'), 4, 'initial state sequence should be of length 3'); 102 | equals(monitor.matchSequence().begin().entered(root, a, c, g).end(), true, 'initial sequence should be entered[root, a, c, g]'); 103 | 104 | equals(root.get('initialSubstate'), a, "root state's initial substate should be state a"); 105 | equals(c.get('initialSubstate'), g, "c state's initial substate should be state g"); 106 | equals(d.get('initialSubstate'), i, "d state's initial substate should be state i"); 107 | equals(e.get('initialSubstate'), k, "e state's initial substate should be state k"); 108 | equals(f.get('initialSubstate'), m, "f state's initial substate should be state m"); 109 | 110 | equals(SC.kindOf(aInitSubstate, Ki.HistoryState), true, "a state's initial substate should be of type Ki.HistoryState"); 111 | equals(aInitSubstate.get('isRecursive'), false, "a's initial substate should not be recursive"); 112 | equals(aInitSubstate.get('defaultState'), c, "a's initial substate should have default state c"); 113 | equals(aInitSubstate.get('statechart'), statechart, "a's initial substate should have an assigned statechart"); 114 | equals(aInitSubstate.get('parentState'), a, "a's initial substate should have parent state a"); 115 | equals(aInitSubstate.get('state'), c, "a's initial substate state should be state c"); 116 | 117 | equals(SC.kindOf(bInitSubstate, Ki.HistoryState), true, "b state's initial substate should be of type Ki.HistoryState"); 118 | equals(bInitSubstate.get('isRecursive'), true, "b's initial substate should be recursive"); 119 | equals(bInitSubstate.get('defaultState'), e, "b's initial substate should have default state e"); 120 | equals(bInitSubstate.get('statechart'), statechart, "b's initial substate should have an assigned statechart"); 121 | equals(bInitSubstate.get('parentState'), b, "b's initial substate should have parent state b"); 122 | equals(bInitSubstate.get('state'), e, "b's initial substate state should be state e"); 123 | 124 | equals(a.get('historyState'), c); 125 | equals(b.get('historyState'), null); 126 | }); 127 | 128 | test("check state sequence after going to state b", function() { 129 | var monitor = statechart.get('monitor'), 130 | root = statechart.get('rootState'), 131 | b = statechart.getState('b'), 132 | e = statechart.getState('e'); 133 | 134 | monitor.reset(); 135 | 136 | statechart.gotoState('b'); 137 | 138 | equals(b.get('historyState'), e); 139 | equals(b.getPath('initialSubstate.state'), e); 140 | 141 | equals(monitor.get('length'), 6, 'initial state sequence should be of length 6'); 142 | equals(monitor.matchSequence() 143 | .begin() 144 | .exited('g', 'c', 'a') 145 | .entered('b', 'e', 'k') 146 | .end(), true, 147 | 'sequence should be exited[g, c, a], entered[b, e, k]'); 148 | }); 149 | 150 | test("check state sequence with state a's historyState assigned", function() { 151 | var monitor = statechart.get('monitor'), 152 | root = statechart.get('rootState'), 153 | a = statechart.getState('a'), 154 | b = statechart.getState('b'), 155 | c = statechart.getState('c'), 156 | d = statechart.getState('d'), 157 | e = statechart.getState('e'), 158 | f = statechart.getState('f'), 159 | g = statechart.getState('g'), 160 | h = statechart.getState('h'), 161 | i = statechart.getState('i'), 162 | j = statechart.getState('j'), 163 | k = statechart.getState('k'), 164 | l = statechart.getState('l'), 165 | m = statechart.getState('m'), 166 | n = statechart.getState('n'); 167 | 168 | statechart.gotoState('j'); 169 | 170 | equals(a.get('historyState'), d); 171 | equals(d.get('historyState'), j); 172 | 173 | equals(a.getPath('initialSubstate.state'), d); 174 | 175 | statechart.gotoState('b'); 176 | 177 | monitor.reset(); 178 | 179 | statechart.gotoState('a'); 180 | 181 | equals(monitor.get('length'), 6, 'initial state sequence should be of length 6'); 182 | equals(monitor.matchSequence() 183 | .begin() 184 | .exited(k, e, b) 185 | .entered(a, d, i) 186 | .end(), true, 187 | 'sequence should be exited[k, e, b], entered[a, d, i]'); 188 | 189 | }); 190 | 191 | test("check state sequence with state b's historyState assigned", function() { 192 | var monitor = statechart.get('monitor'), 193 | root = statechart.get('rootState'), 194 | b = statechart.getState('b'), 195 | f = statechart.getState('f'), 196 | n = statechart.getState('n'); 197 | 198 | statechart.gotoState('n'); 199 | 200 | equals(b.get('historyState'), f); 201 | equals(f.get('historyState'), n); 202 | 203 | equals(b.getPath('initialSubstate.state'), f); 204 | 205 | statechart.gotoState('a'); 206 | 207 | monitor.reset(); 208 | 209 | statechart.gotoState('b'); 210 | 211 | equals(monitor.get('length'), 6, 'initial state sequence should be of length 6'); 212 | equals(monitor.matchSequence() 213 | .begin() 214 | .exited('g', 'c', 'a') 215 | .entered('b', 'f', 'n') 216 | .end(), true, 217 | 'sequence should be exited[g, c, a], entered[b, f, n]'); 218 | }); -------------------------------------------------------------------------------- /frameworks/foundation/tests/state_transitioning/history_state/standard/without_concurrent_states/context.js: -------------------------------------------------------------------------------- 1 | // ========================================================================== 2 | // Ki Unit Test 3 | // ========================================================================== 4 | /*globals Ki */ 5 | 6 | var statechart, 7 | TestState, 8 | context, 9 | monitor, 10 | root, 11 | stateA, 12 | stateB, 13 | stateC, 14 | stateD, 15 | stateE, 16 | stateF; 17 | 18 | module("Ki.Statechart: Supply Context Parameter gotoHistoryState - Without Concurrent States", { 19 | setup: function() { 20 | 21 | TestState = Ki.State.extend({ 22 | enterStateContext: null, 23 | exitStateContext: null, 24 | 25 | enterState: function(context) { 26 | this.set('enterStateContext', context); 27 | }, 28 | 29 | exitState: function(context) { 30 | this.set('exitStateContext', context); 31 | } 32 | }); 33 | 34 | statechart = Ki.Statechart.create({ 35 | 36 | monitorIsActive: YES, 37 | 38 | rootState: TestState.design({ 39 | 40 | initialSubstate: 'a', 41 | 42 | a: TestState.design({ 43 | initialSubstate: 'c', 44 | c: TestState.design(), 45 | d: TestState.design() 46 | }), 47 | 48 | b: TestState.design({ 49 | initialSubstate: 'e', 50 | e: TestState.design(), 51 | f: TestState.design() 52 | }) 53 | }) 54 | 55 | }); 56 | 57 | statechart.initStatechart(); 58 | 59 | statechart.gotoState('d'); 60 | 61 | context = { foo: 100 }; 62 | 63 | monitor = statechart.get('monitor'); 64 | root = statechart.get('rootState'); 65 | stateA = statechart.getState('a'); 66 | stateB = statechart.getState('b'); 67 | stateC = statechart.getState('c'); 68 | stateD = statechart.getState('d'); 69 | stateE = statechart.getState('e'); 70 | stateF = statechart.getState('f'); 71 | }, 72 | 73 | teardown: function() { 74 | statechart = TestState = monitor = context = null; 75 | root = stateA = stateB = stateC = stateD = stateE = stateF; 76 | } 77 | }); 78 | 79 | test("check statechart initialization", function() { 80 | equals(root.get('enterStateContext'), null); 81 | equals(stateA.get('enterStateContext'), null); 82 | equals(stateD.get('enterStateContext'), null); 83 | }); 84 | 85 | test("pass no context when going to state a's history state using statechart", function() { 86 | statechart.gotoState('f'); 87 | statechart.gotoHistoryState('a'); 88 | equals(stateD.get('isCurrentState'), true); 89 | equals(stateD.get('enterStateContext'), null); 90 | equals(stateA.get('enterStateContext'), null); 91 | equals(stateB.get('exitStateContext'), null); 92 | equals(stateF.get('exitStateContext'), null); 93 | }); 94 | 95 | test("pass no context when going to state a's history state using state", function() { 96 | stateD.gotoState('f'); 97 | stateF.gotoHistoryState('a'); 98 | equals(stateD.get('isCurrentState'), true); 99 | equals(stateD.get('enterStateContext'), null); 100 | equals(stateA.get('enterStateContext'), null); 101 | equals(stateB.get('exitStateContext'), null); 102 | equals(stateF.get('exitStateContext'), null); 103 | }); 104 | 105 | test("pass context when going to state a's history state using statechart - gotoHistoryState('f', context)", function() { 106 | statechart.gotoState('f'); 107 | statechart.gotoHistoryState('a', context); 108 | equals(stateD.get('isCurrentState'), true); 109 | equals(stateD.get('enterStateContext'), context, 'state d should have context upon entering'); 110 | equals(stateA.get('enterStateContext'), context, 'state a should have context upon entering'); 111 | equals(stateB.get('exitStateContext'), context, 'state b should have context upon exiting'); 112 | equals(stateF.get('exitStateContext'), context, 'state f should have context upon exiting'); 113 | }); 114 | 115 | test("pass context when going to state a's history state using state - gotoHistoryState('f', context)", function() { 116 | statechart.gotoState('f'); 117 | stateF.gotoHistoryState('a', context); 118 | equals(stateD.get('isCurrentState'), true); 119 | equals(stateD.get('enterStateContext'), context, 'state d should have context upon entering'); 120 | equals(stateA.get('enterStateContext'), context, 'state a should have context upon entering'); 121 | equals(stateB.get('exitStateContext'), context, 'state b should have context upon exiting'); 122 | equals(stateF.get('exitStateContext'), context, 'state f should have context upon exiting'); 123 | }); 124 | 125 | test("pass context when going to state a's history state using statechart - gotoHistoryState('f', stateF, context)", function() { 126 | statechart.gotoState('f'); 127 | statechart.gotoHistoryState('a', stateF, context); 128 | equals(stateD.get('isCurrentState'), true); 129 | equals(stateD.get('enterStateContext'), context, 'state d should have context upon entering'); 130 | equals(stateA.get('enterStateContext'), context, 'state a should have context upon entering'); 131 | equals(stateB.get('exitStateContext'), context, 'state b should have context upon exiting'); 132 | equals(stateF.get('exitStateContext'), context, 'state f should have context upon exiting'); 133 | }); 134 | 135 | test("pass context when going to state a's history state using statechart - gotoHistoryState('f', true, context)", function() { 136 | statechart.gotoState('f'); 137 | statechart.gotoHistoryState('a', true, context); 138 | equals(stateD.get('isCurrentState'), true); 139 | equals(stateD.get('enterStateContext'), context, 'state d should have context upon entering'); 140 | equals(stateA.get('enterStateContext'), context, 'state a should have context upon entering'); 141 | equals(stateB.get('exitStateContext'), context, 'state b should have context upon exiting'); 142 | equals(stateF.get('exitStateContext'), context, 'state f should have context upon exiting'); 143 | }); 144 | 145 | test("pass context when going to state a's history state using statechart - gotoHistoryState('f', stateF, true, context)", function() { 146 | statechart.gotoState('f'); 147 | statechart.gotoHistoryState('a', stateF, true, context); 148 | equals(stateD.get('isCurrentState'), true); 149 | equals(stateD.get('enterStateContext'), context, 'state d should have context upon entering'); 150 | equals(stateA.get('enterStateContext'), context, 'state a should have context upon entering'); 151 | equals(stateB.get('exitStateContext'), context, 'state b should have context upon exiting'); 152 | equals(stateF.get('exitStateContext'), context, 'state f should have context upon exiting'); 153 | }); 154 | 155 | test("pass context when going to state a's history state using state - gotoHistoryState('f', true, context)", function() { 156 | statechart.gotoState('f'); 157 | stateF.gotoHistoryState('a', true, context); 158 | equals(stateD.get('isCurrentState'), true); 159 | equals(stateD.get('enterStateContext'), context, 'state d should have context upon entering'); 160 | equals(stateA.get('enterStateContext'), context, 'state a should have context upon entering'); 161 | equals(stateB.get('exitStateContext'), context, 'state b should have context upon exiting'); 162 | equals(stateF.get('exitStateContext'), context, 'state f should have context upon exiting'); 163 | }); 164 | 165 | // 166 | // test("pass context when going to state f using state - gotoState('f', context)", function() { 167 | // stateC.gotoState('f', context); 168 | // equals(stateF.get('isCurrentState'), true); 169 | // equals(stateC.get('exitStateContext'), context, 'state c should have context upon exiting'); 170 | // equals(stateA.get('exitStateContext'), context, 'state a should have context upon exiting'); 171 | // equals(stateB.get('enterStateContext'), context, 'state b should have context upon entering'); 172 | // equals(stateF.get('enterStateContext'), context, 'state f should have context upon entering'); 173 | // }); 174 | // 175 | // test("pass context when going to state f using statechart - gotoState('f', stateC, context) ", function() { 176 | // statechart.gotoState('f', stateC, context); 177 | // equals(stateF.get('isCurrentState'), true); 178 | // equals(stateC.get('exitStateContext'), context, 'state c should have context upon exiting'); 179 | // equals(stateA.get('exitStateContext'), context, 'state a should have context upon exiting'); 180 | // equals(stateB.get('enterStateContext'), context, 'state b should have context upon entering'); 181 | // equals(stateF.get('enterStateContext'), context, 'state f should have context upon entering'); 182 | // }); 183 | // 184 | // test("pass context when going to state f using statechart - gotoState('f', stateC, false, context) ", function() { 185 | // statechart.gotoState('f', stateC, false, context); 186 | // equals(stateF.get('isCurrentState'), true); 187 | // equals(stateC.get('exitStateContext'), context, 'state c should have context upon exiting'); 188 | // equals(stateA.get('exitStateContext'), context, 'state a should have context upon exiting'); 189 | // equals(stateB.get('enterStateContext'), context, 'state b should have context upon entering'); 190 | // equals(stateF.get('enterStateContext'), context, 'state f should have context upon entering'); 191 | // }); -------------------------------------------------------------------------------- /frameworks/foundation/tests/state/namespacing.js: -------------------------------------------------------------------------------- 1 | // ========================================================================== 2 | // Ki Unit Test 3 | // ========================================================================== 4 | /*globals Ki */ 5 | 6 | var statechart1, statechart2, statechart3, statechart4; 7 | 8 | module("Ki.Statechart: Namespace - Access Substate Tests", { 9 | setup: function() { 10 | 11 | statechart1 = Ki.Statechart.create({ 12 | 13 | rootState: Ki.State.design({ 14 | initialSubstate: 'a', 15 | a: Ki.State.design({ value: 'state A' }), 16 | b: Ki.State.design({ value: 'state B' }) 17 | }) 18 | 19 | }); 20 | 21 | statechart2 = Ki.Statechart.create({ 22 | 23 | rootState: Ki.State.design({ 24 | initialSubstate: 'a', 25 | a: Ki.State.design({ 26 | value: 'state A', 27 | initialSubstate: 'c', 28 | c: Ki.State.design({ value: 'state C' }), 29 | d: Ki.State.design({ value: 'state D' }) 30 | }), 31 | 32 | b: Ki.State.design({ 33 | value: 'state B', 34 | initialSubstate: 'e', 35 | e: Ki.State.design({ value: 'state E' }), 36 | f: Ki.State.design({ value: 'state F' }) 37 | }) 38 | }) 39 | 40 | }); 41 | 42 | statechart3 = Ki.Statechart.create({ 43 | 44 | rootState: Ki.State.design({ 45 | initialSubstate: 'a', 46 | a: Ki.State.design({ value: 'state A' }), 47 | b: Ki.State.design({ 48 | value: 'state B', 49 | initialSubstate: 'a', 50 | a: Ki.State.design({ value: 'state B.A' }), 51 | c: Ki.State.design({ 52 | value: 'state C', 53 | initialSubstate: 'a', 54 | a: Ki.State.design({ value: 'state B.C.A' }), 55 | d: Ki.State.design({ value: 'state D' }) 56 | }) 57 | }) 58 | }) 59 | 60 | }); 61 | 62 | statechart4 = Ki.Statechart.create({ 63 | 64 | rootState: Ki.State.design({ 65 | initialSubstate: 'a', 66 | a: Ki.State.design({ 67 | value: 'state A', 68 | initialSubstate: 'x', 69 | x: Ki.State.design({ value: 'state A.X' }), 70 | y: Ki.State.design({ value: 'state A.Y' }) 71 | }), 72 | 73 | b: Ki.State.design({ 74 | value: 'state B', 75 | initialSubstate: 'x', 76 | x: Ki.State.design({ value: 'state B.X' }), 77 | y: Ki.State.design({ value: 'state B.Y' }) 78 | }) 79 | }) 80 | 81 | }); 82 | 83 | statechart1.initStatechart(); 84 | statechart2.initStatechart(); 85 | statechart3.initStatechart(); 86 | statechart4.initStatechart(); 87 | }, 88 | 89 | teardown: function() { 90 | statechart1.destroy(); 91 | statechart2.destroy(); 92 | statechart3.destroy(); 93 | statechart4.destroy(); 94 | statechart1 = statechart2 = statechart3 = statechart4 = null; 95 | } 96 | }); 97 | 98 | test("access statechart1 states", function() { 99 | var state; 100 | 101 | state = statechart1.getState('a'); 102 | equals(SC.none(state), false, 'state a should not be null'); 103 | equals(state.get('value'), 'state A', 'state a should have value "state A"'); 104 | 105 | state = statechart1.getState('b'); 106 | equals(SC.none(state), false, 'state b should not be null'); 107 | equals(state.get('value'), 'state B', 'state a should have value "state B"'); 108 | }); 109 | 110 | test("access statechart2 states", function() { 111 | var state; 112 | 113 | state = statechart2.getState('a'); 114 | equals(SC.none(state), false, 'state a should not be null'); 115 | equals(state.get('value'), 'state A', 'state a should have value "state A"'); 116 | 117 | state = statechart2.getState('b'); 118 | equals(SC.none(state), false, 'state b should not be null'); 119 | equals(state.get('value'), 'state B', 'state b should have value "state B"'); 120 | 121 | state = statechart2.getState('c'); 122 | equals(SC.none(state), false, 'state c should not be null'); 123 | equals(state.get('value'), 'state C', 'state c should have value "state C"'); 124 | 125 | state = statechart2.getState('d'); 126 | equals(SC.none(state), false, 'state d should not be null'); 127 | equals(state.get('value'), 'state D', 'state d should have value "state D"'); 128 | 129 | state = statechart2.getState('e'); 130 | equals(SC.none(state), false, 'state e should not be null'); 131 | equals(state.get('value'), 'state E', 'state d should have value "state E"'); 132 | 133 | state = statechart2.getState('f'); 134 | equals(SC.none(state), false, 'state f should not be null'); 135 | equals(state.get('value'), 'state F', 'state d should have value "state F"'); 136 | 137 | state = statechart2.getState('a.c'); 138 | equals(SC.none(state), false, 'state a.c should not be null'); 139 | equals(state, statechart2.getState('c'), 'state a.c should be equal to state c'); 140 | equals(state.get('value'), 'state C', 'state a.c should have value "state C"'); 141 | 142 | state = statechart2.getState('a.d'); 143 | equals(SC.none(state), false, 'state a.d should not be null'); 144 | equals(state, statechart2.getState('d'), 'state a.d should be equal to state d'); 145 | equals(state.get('value'), 'state D', 'state a.d should have value "state D"'); 146 | 147 | state = statechart2.getState('b.e'); 148 | equals(SC.none(state), false, 'state b.e should not be null'); 149 | equals(state, statechart2.getState('e'), 'state b.e should be equal to state e'); 150 | equals(state.get('value'), 'state E', 'state b.e should have value "state E"'); 151 | 152 | state = statechart2.getState('b.f'); 153 | equals(SC.none(state), false, 'state b.f should not be null'); 154 | equals(state, statechart2.getState('f'), 'state b.f should be equal to state f'); 155 | equals(state.get('value'), 'state F', 'state b.f should have value "state F"'); 156 | }); 157 | 158 | test("access all A states in statechart3", function() { 159 | var state; 160 | 161 | state = statechart3.getState('a'); 162 | equals(SC.none(state), false, 'state a should not be null'); 163 | equals(state.get('value'), 'state A', 'state a should have value "state A"'); 164 | 165 | state = statechart3.getState('b.a'); 166 | equals(SC.none(state), false, 'state b.a should not be null'); 167 | equals(state.get('value'), 'state B.A', 'state a should have value "state B.A"'); 168 | 169 | state = statechart3.getState('b.c.a'); 170 | equals(SC.none(state), false, 'state b.c.a should not be null'); 171 | equals(state.get('value'), 'state B.C.A', 'state a should have value "state B.C.A"'); 172 | }); 173 | 174 | test("access all A states relative to state B in statechart3", function() { 175 | var state, 176 | stateB = statechart3.getState('b'); 177 | 178 | state = stateB.getSubstate('a'); 179 | equals(SC.none(state), false, 'state a should not be null'); 180 | equals(state.get('value'), 'state B.A', 'state a should have value "state B.A"'); 181 | 182 | state = stateB.getSubstate('c.a'); 183 | equals(SC.none(state), false, 'state c.a should not be null'); 184 | equals(state.get('value'), 'state B.C.A', 'state a should have value "state B.C.A"'); 185 | }); 186 | 187 | test("access all A states relative to state C in statechart3", function() { 188 | var state, 189 | stateC = statechart3.getState('c'); 190 | 191 | state = stateC.getSubstate('a'); 192 | equals(SC.none(state), false, 'state a should not be null'); 193 | equals(state.get('value'), 'state B.C.A', 'state a should have value "state B.C.A"'); 194 | }); 195 | 196 | test("access all states in statechart4", function() { 197 | var state, 198 | stateA = statechart4.getState('a'), 199 | stateB = statechart4.getState('b'); 200 | 201 | state = statechart4.getState('a'); 202 | equals(SC.none(state), false, 'state a should not be null'); 203 | equals(state.get('value'), 'state A', 'state a should have value "state A"'); 204 | 205 | state = statechart4.getState('a.x'); 206 | equals(SC.none(state), false, 'state a.x should not be null'); 207 | equals(state.get('value'), 'state A.X', 'state a should have value "state A.X"'); 208 | 209 | state = statechart4.getState('a.y'); 210 | equals(SC.none(state), false, 'state a.y should not be null'); 211 | equals(state.get('value'), 'state A.Y', 'state a should have value "state A.Y"'); 212 | 213 | state = statechart4.getState('b'); 214 | equals(SC.none(state), false, 'state a should not be null'); 215 | equals(state.get('value'), 'state B', 'state b should have value "state B"'); 216 | 217 | state = statechart4.getState('b.x'); 218 | equals(SC.none(state), false, 'state b.x should not be null'); 219 | equals(state.get('value'), 'state B.X', 'state b should have value "state B.X"'); 220 | 221 | state = statechart4.getState('b.y'); 222 | equals(SC.none(state), false, 'state b.y should not be null'); 223 | equals(state.get('value'), 'state B.Y', 'state a should have value "state B.Y"'); 224 | 225 | console.log('expecting to get an error message...'); 226 | state = statechart4.getState('x'); 227 | equals(SC.none(state), true, 'state x should be null'); 228 | 229 | console.log('expecting to get an error message...'); 230 | state = statechart4.getState('y'); 231 | equals(SC.none(state), true, 'state y should be null'); 232 | 233 | state = stateA.getSubstate('x'); 234 | equals(SC.none(state), false, 'state a.x should not be null'); 235 | equals(state.get('value'), 'state A.X', 'state a should have value "state A.X"'); 236 | 237 | state = stateA.getSubstate('y'); 238 | equals(SC.none(state), false, 'state a.y should not be null'); 239 | equals(state.get('value'), 'state A.Y', 'state a should have value "state A.Y"'); 240 | 241 | state = stateB.getSubstate('x'); 242 | equals(SC.none(state), false, 'state b.x should not be null'); 243 | equals(state.get('value'), 'state B.X', 'state a should have value "state B.X"'); 244 | 245 | state = stateB.getSubstate('y'); 246 | equals(SC.none(state), false, 'state b.y should not be null'); 247 | equals(state.get('value'), 'state B.Y', 'state a should have value "state B.Y"'); 248 | }); -------------------------------------------------------------------------------- /frameworks/foundation/tests/state/state_observes.js: -------------------------------------------------------------------------------- 1 | // ========================================================================== 2 | // Ki.State Unit Test 3 | // ========================================================================== 4 | /*globals Ki obj1 obj2 obj3 */ 5 | 6 | window.obj1 = null; 7 | window.obj2 = null; 8 | window.obj3 = null; 9 | var statechart1, statechart2, TestState; 10 | var stateA, stateB, stateC, stateD; 11 | 12 | module("Ki.Statechart: stateObserves Tests", { 13 | 14 | setup: function() { 15 | 16 | obj1 = SC.Object.create({ 17 | foo: 'abc' 18 | }); 19 | 20 | obj2 = SC.Object.create({ 21 | bar: 'xyz' 22 | }); 23 | 24 | obj3 = SC.Object.create({ 25 | mah: 123 26 | }); 27 | 28 | TestState = Ki.State.extend({ 29 | 30 | notifyStateObserveHandlerInvoked: function(handler, target, key) { 31 | this['%@Invoked'.fmt(handler)] = { 32 | target: target, 33 | key: key 34 | }; 35 | } 36 | 37 | }); 38 | 39 | statechart1 = Ki.Statechart.create({ 40 | 41 | autoInitStatechart: YES, 42 | 43 | initialState: 'stateA', 44 | 45 | stateA: TestState.design({ 46 | 47 | testProp: null, 48 | 49 | testProp2: obj3, 50 | 51 | testPropChanged: function(target, key) { 52 | this.notifyStateObserveHandlerInvoked('testPropChanged', target, key); 53 | }.stateObserves('testProp'), 54 | 55 | testProp2Changed: function(target, key) { 56 | this.notifyStateObserveHandlerInvoked('testProp2Changed', target, key); 57 | }.stateObserves('.testProp2.mah'), 58 | 59 | fooChanged: function(target, key) { 60 | this.notifyStateObserveHandlerInvoked('fooChanged', target, key); 61 | }.stateObserves('obj1.foo'), 62 | 63 | barChanged: function(target, key) { 64 | this.notifyStateObserveHandlerInvoked('barChanged', target, key); 65 | }.stateObserves('obj2.bar') 66 | 67 | }), 68 | 69 | stateB: TestState.design() 70 | 71 | }); 72 | 73 | stateA = statechart1.getState('stateA'); 74 | stateB = statechart1.getState('stateB'); 75 | 76 | statechart2 = Ki.Statechart.create({ 77 | 78 | autoInitStatechart: YES, 79 | 80 | initialState: 'stateC', 81 | 82 | stateC: TestState.design({ 83 | 84 | mahChanged: function(target, key) { 85 | this.notifyStateObserveHandlerInvoked('mahChanged', target, key); 86 | }.stateObserves('obj1.foo', 'obj2.bar') 87 | 88 | }), 89 | 90 | stateD: TestState.design() 91 | 92 | }); 93 | 94 | stateC = statechart2.getState('stateC'); 95 | stateD = statechart2.getState('stateD'); 96 | 97 | }, 98 | 99 | teardown: function() { 100 | window.obj1 = undefined; 101 | window.obj2 = undefined; 102 | window.obj3 = undefined; 103 | statechart1 = statechart2 = null; 104 | stateA = stateB = stateC = stateD = null; 105 | TestState = null; 106 | } 107 | 108 | }); 109 | 110 | test("check state observe handlers when obj1's foo is changed", function() { 111 | ok(!stateA.fooChangedInvoked, "state A's fooChanged should not be invoked"); 112 | ok(!stateA.barChangedInvoked, "state A's barChanged should not be invoked"); 113 | ok(!stateC.mahChangedInvoked, "state C's mahChanged should not be invoked"); 114 | ok(obj1.hasObserverFor('foo'), "obj1 should have observers for property foo"); 115 | 116 | obj1.set('foo', 100); 117 | 118 | ok(stateA.fooChangedInvoked, "state A's fooChanged should be invoked"); 119 | equals(stateA.fooChangedInvoked.target, obj1, "target should be obj1"); 120 | equals(stateA.fooChangedInvoked.key, 'foo', "key should be foo"); 121 | 122 | ok(!stateA.barChangedInvoked, "state A's barChanged should not be invoked"); 123 | 124 | ok(stateC.mahChangedInvoked, "state C's mahChanged should be invoked"); 125 | equals(stateC.mahChangedInvoked.target, obj1, "target should be obj1"); 126 | equals(stateC.mahChangedInvoked.key, 'foo', "key should be foo"); 127 | }); 128 | 129 | test("check state observe handlers when obj2's bar is changed", function() { 130 | ok(!stateA.fooChangedInvoked, "state A's fooChanged should not be invoked"); 131 | ok(!stateA.barChangedInvoked, "state A's barChanged should not be invoked"); 132 | ok(!stateC.mahChangedInvoked, "state C's mahChanged should not be invoked"); 133 | ok(obj2.hasObserverFor('bar'), "obj2 should have observers for property bar"); 134 | 135 | obj2.notifyPropertyChange('bar'); 136 | 137 | ok(!stateA.fooChangedInvoked, "state A's fooChanged should not be invoked"); 138 | 139 | ok(stateA.barChangedInvoked, "state A's barChanged should be invoked"); 140 | equals(stateA.barChangedInvoked.target, obj2, "target should be obj2"); 141 | equals(stateA.barChangedInvoked.key, 'bar', "key should be bar"); 142 | 143 | ok(stateC.mahChangedInvoked, "state C's mahChanged should be invoked"); 144 | equals(stateC.mahChangedInvoked.target, obj2, "target should be obj1"); 145 | equals(stateC.mahChangedInvoked.key, 'bar', "key should be bar"); 146 | }); 147 | 148 | test("check state observe handlers when state A's testProp is changed", function() { 149 | ok(!stateA.testPropChangedInvoked, "state A's testPropChanged should not be invoked"); 150 | ok(stateA.hasObserverFor('testProp'), "state A should have observers for property testProp"); 151 | 152 | stateA.notifyPropertyChange('testProp'); 153 | 154 | ok(stateA.testPropChangedInvoked, "state A's testPropChanged should be invoked"); 155 | equals(stateA.testPropChangedInvoked.target, stateA, "target should be stateA"); 156 | equals(stateA.testPropChangedInvoked.key, 'testProp', "key should be testProp"); 157 | }); 158 | 159 | test("check state observe handlers when state A's testProp2.mah is changed", function() { 160 | ok(!stateA.testProp2ChangedInvoked, "state A's testProp2Changed should not be invoked"); 161 | ok(!stateA.hasObserverFor('testProp2'), "state A should have observers for property testProp2"); 162 | ok(stateA.get('testProp2').hasObserverFor('mah'), "state A's testProp2 should have observers for property mah"); 163 | 164 | stateA.notifyPropertyChange('testProp2'); 165 | 166 | ok(!stateA.testProp2ChangedInvoked, "state A's testProp2Changed should not be invoked"); 167 | 168 | stateA.get('testProp2').notifyPropertyChange('mah'); 169 | 170 | ok(stateA.testProp2ChangedInvoked, "state A's testProp2Changed should be invoked"); 171 | equals(stateA.testProp2ChangedInvoked.target, obj3, "target should be obj3"); 172 | equals(stateA.testProp2ChangedInvoked.key, 'mah', "key should be mah"); 173 | }); 174 | 175 | test("change current states and check state observe handlers when objs' property change", function() { 176 | ok(!stateA.fooChangedInvoked, "state A's fooChanged should not be invoked"); 177 | ok(!stateA.barChangedInvoked, "state A's barChanged should not be invoked"); 178 | ok(!stateA.testPropChangedInvoked, "state A's testPropChanged should not be invoked"); 179 | ok(!stateA.testProp2ChangedInvoked, "state A's testProp2Changed should not be invoked"); 180 | ok(!stateC.mahChangedInvoked, "state C's mahChanged should not be invoked"); 181 | 182 | statechart1.gotoState('stateB'); 183 | statechart2.gotoState('stateD'); 184 | 185 | ok(!obj1.hasObserverFor('foo'), "obj1 should not have observers for property foo"); 186 | ok(!obj2.hasObserverFor('bar'), "obj2 should not have observers for property bar"); 187 | ok(!stateA.hasObserverFor('testProp'), "state A should not have observers for property testProp"); 188 | ok(!stateA.get('testProp2').hasObserverFor('mah'), "state A's testProp2 should not have observers for property mah"); 189 | 190 | obj1.notifyPropertyChange('foo'); 191 | obj2.notifyPropertyChange('bar'); 192 | stateA.notifyPropertyChange('testProp'); 193 | stateA.get('testProp2').notifyPropertyChange('mah'); 194 | 195 | ok(!stateA.fooChangedInvoked, "state A's fooChanged should not be invoked"); 196 | ok(!stateA.barChangedInvoked, "state A's barChanged should not be invoked"); 197 | ok(!stateA.testPropChangedInvoked, "state A's testPropChanged should not be invoked"); 198 | ok(!stateA.testProp2ChangedInvoked, "state A's testProp2Changed should not be invoked"); 199 | ok(!stateC.mahChangedInvoked, "state C's mahChanged should not be invoked"); 200 | 201 | statechart1.gotoState('stateA'); 202 | statechart2.gotoState('stateC'); 203 | 204 | ok(obj1.hasObserverFor('foo'), "obj1 should have observers for property foo"); 205 | ok(obj2.hasObserverFor('bar'), "obj2 should have observers for property bar"); 206 | ok(stateA.hasObserverFor('testProp'), "state A should have observers for property testProp"); 207 | ok(stateA.get('testProp2').hasObserverFor('mah'), "state A's testProp2 should not have observers for property mah"); 208 | 209 | obj1.notifyPropertyChange('foo'); 210 | obj2.notifyPropertyChange('bar'); 211 | stateA.notifyPropertyChange('testProp'); 212 | stateA.get('testProp2').notifyPropertyChange('mah'); 213 | 214 | ok(stateA.fooChangedInvoked, "state A's fooChanged should be invoked"); 215 | ok(stateA.barChangedInvoked, "state A's barChanged should be invoked"); 216 | ok(stateA.testPropChangedInvoked, "state A's testPropChanged should be invoked"); 217 | ok(stateA.testProp2ChangedInvoked, "state A's testProp2Changed should be invoked"); 218 | ok(stateC.mahChangedInvoked, "state C's mahChanged should be invoked"); 219 | }); 220 | 221 | test("destroy statecharts and check that objs have not observers", function() { 222 | ok(obj1.hasObserverFor('foo'), "obj1 should have observers for property foo"); 223 | ok(obj2.hasObserverFor('bar'), "obj2 should have observers for property bar"); 224 | ok(stateA.hasObserverFor('testProp'), "state A should have observers for property testProp"); 225 | ok(stateA.get('testProp2').hasObserverFor('mah'), "state A's testProp2 should have observers for property mah"); 226 | 227 | statechart1.destroy(); 228 | statechart2.destroy(); 229 | 230 | ok(!obj1.hasObserverFor('foo'), "obj1 should not have observers for property foo"); 231 | ok(!obj2.hasObserverFor('bar'), "obj2 should not have observers for property bar"); 232 | ok(!stateA.hasObserverFor('testProp'), "state A should not have observers for property testProp"); 233 | ok(!stateA.get('testProp2').hasObserverFor('mah'), "state A's testProp2 should not have observers for property mah"); 234 | }); 235 | 236 | -------------------------------------------------------------------------------- /frameworks/foundation/tests/state_transitioning/history_state/standard/without_concurrent_states/core.js: -------------------------------------------------------------------------------- 1 | // ========================================================================== 2 | // Ki.Statechart Unit Test 3 | // ========================================================================== 4 | /*globals Ki */ 5 | 6 | var statechart = null; 7 | 8 | // .......................................................... 9 | // CONTENT CHANGING 10 | // 11 | 12 | module("Ki.Statechart: No Concurrent States - Goto History State Tests", { 13 | setup: function() { 14 | 15 | statechart = Ki.Statechart.create({ 16 | 17 | monitorIsActive: YES, 18 | 19 | rootState: Ki.State.design({ 20 | 21 | initialSubstate: 'a', 22 | 23 | a: Ki.State.design({ 24 | 25 | initialSubstate: 'c', 26 | 27 | c: Ki.State.design({ 28 | initialSubstate: 'g', 29 | g: Ki.State.design(), 30 | h: Ki.State.design() 31 | }), 32 | 33 | d: Ki.State.design({ 34 | initialSubstate: 'i', 35 | i: Ki.State.design(), 36 | j: Ki.State.design() 37 | }) 38 | 39 | }), 40 | 41 | b: Ki.State.design({ 42 | 43 | initialSubstate: 'e', 44 | 45 | e: Ki.State.design({ 46 | initialSubstate: 'k', 47 | k: Ki.State.design(), 48 | l: Ki.State.design() 49 | }), 50 | 51 | f: Ki.State.design({ 52 | initialSubstate: 'm', 53 | m: Ki.State.design(), 54 | n: Ki.State.design() 55 | }) 56 | 57 | }) 58 | 59 | }) 60 | 61 | }); 62 | 63 | statechart.initStatechart(); 64 | }, 65 | 66 | teardown: function() { 67 | statechart.destroy(); 68 | } 69 | }); 70 | 71 | test("check initial statechart history states", function() { 72 | equals(statechart.get('rootState').get('historyState'), statechart.getState('a'), 'root state\'s history state should be state a'); 73 | 74 | equals(statechart.getState('a').get('historyState'), statechart.getState('c'), 'state a\'s history state should be state c'); 75 | equals(statechart.getState('c').get('historyState'), statechart.getState('g'), 'state c\'s history state should be state g'); 76 | equals(statechart.getState('g').get('historyState'), null, 'state g\'s history state should be null'); 77 | 78 | equals(statechart.getState('h').get('historyState'), null, 'state h\'s history state should be null'); 79 | equals(statechart.getState('d').get('historyState'), null, 'state d\'s history state should be null'); 80 | 81 | equals(statechart.getState('b').get('historyState'), null, 'state b\'s history state should be null'); 82 | equals(statechart.getState('e').get('historyState'), null, 'state e\'s history state should be null'); 83 | equals(statechart.getState('f').get('historyState'), null, 'state f\'s history state should be null'); 84 | }); 85 | 86 | test("go to state h and check history states", function() { 87 | var monitor = statechart.get('monitor'); 88 | monitor.reset(); 89 | 90 | statechart.gotoState('h'); 91 | equals(monitor.matchSequence().begin().exited('g').entered('h').end(), true, 'sequence should be exited[f], entered[h]'); 92 | 93 | equals(statechart.getState('a').get('historyState'), statechart.getState('c'), 'state a\'s history state should be state c'); 94 | equals(statechart.getState('c').get('historyState'), statechart.getState('h'), 'state c\'s history state should be state h'); 95 | equals(statechart.getState('h').get('historyState'), null, 'state h\'s history state should be null'); 96 | equals(statechart.getState('g').get('historyState'), null, 'state g\'s history state should be null'); 97 | 98 | equals(statechart.getState('d').get('historyState'), null, 'state d\'s history state should be null'); 99 | equals(statechart.getState('b').get('historyState'), null, 'state b\'s history state should be null'); 100 | }); 101 | 102 | test("go to state d and check history states", function() { 103 | var monitor = statechart.get('monitor'); 104 | monitor.reset(); 105 | 106 | statechart.gotoState('d'); 107 | equals(monitor.matchSequence().begin().exited('g', 'c').entered('d', 'i').end(), true, 'sequence should be exited[g, c], entered[d, i]'); 108 | 109 | equals(statechart.getState('a').get('historyState'), statechart.getState('d'), 'state a\'s history state should be state d'); 110 | equals(statechart.getState('d').get('historyState'), statechart.getState('i'), 'state d\'s history state should be state i'); 111 | equals(statechart.getState('c').get('historyState'), statechart.getState('g'), 'state c\'s history state should be state g'); 112 | equals(statechart.getState('h').get('historyState'), null, 'state h\'s history state should be null'); 113 | equals(statechart.getState('g').get('historyState'), null, 'state g\'s history state should be null'); 114 | equals(statechart.getState('i').get('historyState'), null, 'state i\'s history state should be null'); 115 | equals(statechart.getState('j').get('historyState'), null, 'state j\'s history state should be null'); 116 | 117 | equals(statechart.getState('b').get('historyState'), null, 'state b\'s history state should be null'); 118 | }); 119 | 120 | test("go to state b and check history states", function() { 121 | var monitor = statechart.get('monitor'); 122 | monitor.reset(); 123 | 124 | statechart.gotoState('b'); 125 | equals(monitor.matchSequence().begin().exited('g', 'c', 'a').entered('b', 'e', 'k').end(), true, 'sequence should be exited[g, c, a], entered[b, e, k]'); 126 | 127 | equals(statechart.get('rootState').get('historyState'), statechart.getState('b'), 'root state\'s history state should be state b'); 128 | equals(statechart.getState('b').get('historyState'), statechart.getState('e'), 'state b\'s history state should be e'); 129 | equals(statechart.getState('e').get('historyState'), statechart.getState('k'), 'state e\'s history state should be k'); 130 | equals(statechart.getState('a').get('historyState'), statechart.getState('c'), 'state a\'s history state should be state c'); 131 | equals(statechart.getState('c').get('historyState'), statechart.getState('g'), 'state c\'s history state should be state g'); 132 | }); 133 | 134 | test("go to state j, then state m, then go to state a's history state (non-recursive)", function() { 135 | var monitor = statechart.get('monitor'); 136 | 137 | statechart.gotoState('j'); 138 | statechart.gotoState('m'); 139 | 140 | monitor.reset(); 141 | statechart.gotoHistoryState('a'); 142 | 143 | equals(monitor.get('length'), 6, 'initial state sequence should be of length 6'); 144 | equals(monitor.matchSequence().begin().exited('m', 'f', 'b').entered('a', 'd', 'i').end(), true, 'sequence should be exited[m, f, b], entered[a, d, i]'); 145 | equals(statechart.get('currentStateCount'), 1, 'current state count should be 1'); 146 | equals(statechart.stateIsCurrentState('i'), true, 'current state should be i'); 147 | equals(statechart.get('rootState').get('historyState'), statechart.getState('a'), 'root state\'s history state should be state a'); 148 | equals(statechart.getState('a').get('historyState'), statechart.getState('d'), 'state a\'s history state should be state d'); 149 | equals(statechart.getState('d').get('historyState'), statechart.getState('i'), 'state d\'s history state should be state i'); 150 | 151 | }); 152 | 153 | test("go to state j, then state m, then go to state a's history state (recursive)", function() { 154 | var monitor = statechart.get('monitor'); 155 | 156 | statechart.gotoState('j'); 157 | statechart.gotoState('m'); 158 | 159 | monitor.reset(); 160 | statechart.gotoHistoryState('a', null, YES); 161 | 162 | equals(monitor.get('length'), 6, 'initial state sequence should be of length 6'); 163 | equals(monitor.matchSequence().begin().exited('m', 'f', 'b').entered('a', 'd', 'j').end(), true, 'sequence should be exited[m, f, b], entered[a, d, j]'); 164 | equals(statechart.get('currentStateCount'), 1, 'current state count should be 1'); 165 | equals(statechart.stateIsCurrentState('j'), true, 'current state should be j'); 166 | equals(statechart.get('rootState').get('historyState'), statechart.getState('a'), 'root state\'s history state should be state a'); 167 | equals(statechart.getState('a').get('historyState'), statechart.getState('d'), 'state a\'s history state should be state d'); 168 | equals(statechart.getState('d').get('historyState'), statechart.getState('j'), 'state d\'s history state should be state j'); 169 | }); 170 | 171 | 172 | test("go to state b's history state (non-recursive)", function() { 173 | var monitor = statechart.get('monitor'); 174 | monitor.reset(); 175 | 176 | statechart.gotoHistoryState('b'); 177 | 178 | equals(monitor.get('length'), 6, 'initial state sequence should be of length 6'); 179 | equals(monitor.matchSequence().begin().exited('g', 'c', 'a').entered('b', 'e', 'k').end(), true, 'sequence should be exited[g, c, a], entered[b, e, k]'); 180 | equals(statechart.get('currentStateCount'), 1, 'current state count should be 1'); 181 | equals(statechart.stateIsCurrentState('k'), true, 'current state should be k'); 182 | equals(statechart.get('rootState').get('historyState'), statechart.getState('b'), 'root state\'s history state should be state b'); 183 | equals(statechart.getState('b').get('historyState'), statechart.getState('e'), 'state b\'s history state should be state e'); 184 | equals(statechart.getState('e').get('historyState'), statechart.getState('k'), 'state e\'s history state should be state k'); 185 | }); 186 | 187 | test("go to state b's history state (recursive)", function() { 188 | var monitor = statechart.get('monitor'); 189 | monitor.reset(); 190 | 191 | statechart.gotoHistoryState('b', null, YES); 192 | 193 | equals(monitor.get('length'), 6, 'initial state sequence should be of length 6'); 194 | equals(monitor.matchSequence().begin().exited('g', 'c', 'a').entered('b', 'e', 'k').end(), true, 'sequence should be exited[g, c, a], entered[b, e, k]'); 195 | equals(statechart.get('currentStateCount'), 1, 'current state count should be 1'); 196 | equals(statechart.stateIsCurrentState('k'), true, 'current state should be k'); 197 | equals(statechart.get('rootState').get('historyState'), statechart.getState('b'), 'root state\'s history state should be state b'); 198 | equals(statechart.getState('b').get('historyState'), statechart.getState('e'), 'state b\'s history state should be state e'); 199 | equals(statechart.getState('e').get('historyState'), statechart.getState('k'), 'state e\'s history state should be state k'); 200 | }); -------------------------------------------------------------------------------- /frameworks/foundation/tests/state_transitioning/standard/with_concurrent_states/advanced.js: -------------------------------------------------------------------------------- 1 | // ========================================================================== 2 | // Ki.State Unit Test 3 | // ========================================================================== 4 | /*globals Ki */ 5 | 6 | var statechart = null; 7 | var monitor, root, stateA, stateB, stateC, stateD, stateE, stateF, stateG; 8 | var stateH, stateI, stateJ, stateK, stateL, stateM, stateN, stateO, stateP; 9 | var stateQ, stateR, stateS, stateZ; 10 | 11 | // .......................................................... 12 | // CONTENT CHANGING 13 | // 14 | 15 | module("Ki.Statechart: With Concurrent States - Goto State Advanced Tests", { 16 | setup: function() { 17 | 18 | statechart = Ki.Statechart.create({ 19 | 20 | monitorIsActive: YES, 21 | 22 | rootState: Ki.State.design({ 23 | 24 | initialSubstate: 'a', 25 | 26 | a: Ki.State.design({ 27 | substatesAreConcurrent: YES, 28 | 29 | b: Ki.State.design({ 30 | initialSubstate: 'd', 31 | d: Ki.State.design(), 32 | e: Ki.State.design() 33 | }), 34 | 35 | c: Ki.State.design({ 36 | 37 | initialSubstate: 'f', 38 | 39 | f: Ki.State.design({ 40 | substatesAreConcurrent: YES, 41 | 42 | h: Ki.State.design({ 43 | initialSubstate: 'l', 44 | l: Ki.State.design(), 45 | m: Ki.State.design() 46 | }), 47 | 48 | i: Ki.State.design({ 49 | initialSubstate: 'n', 50 | n: Ki.State.design(), 51 | o: Ki.State.design() 52 | }) 53 | }), 54 | 55 | g: Ki.State.design({ 56 | substatesAreConcurrent: YES, 57 | 58 | j: Ki.State.design({ 59 | initialSubstate: 'p', 60 | p: Ki.State.design(), 61 | q: Ki.State.design() 62 | }), 63 | 64 | k: Ki.State.design({ 65 | initialSubstate: 'r', 66 | r: Ki.State.design(), 67 | s: Ki.State.design() 68 | }) 69 | }) 70 | 71 | }) 72 | }), 73 | 74 | z: Ki.State.design() 75 | }) 76 | 77 | }); 78 | 79 | statechart.initStatechart(); 80 | 81 | monitor = statechart.get('monitor'); 82 | root = statechart.get('rootState'); 83 | stateA = statechart.getState('a'); 84 | stateB = statechart.getState('b'); 85 | stateC = statechart.getState('c'); 86 | stateD = statechart.getState('d'); 87 | stateE = statechart.getState('e'); 88 | stateF = statechart.getState('f'); 89 | stateG = statechart.getState('g'); 90 | stateH = statechart.getState('h'); 91 | stateI = statechart.getState('i'); 92 | stateJ = statechart.getState('j'); 93 | stateK = statechart.getState('k'); 94 | stateL = statechart.getState('l'); 95 | stateM = statechart.getState('m'); 96 | stateN = statechart.getState('n'); 97 | stateO = statechart.getState('o'); 98 | stateP = statechart.getState('p'); 99 | stateQ = statechart.getState('q'); 100 | stateR = statechart.getState('r'); 101 | stateS = statechart.getState('s'); 102 | stateZ = statechart.getState('z'); 103 | }, 104 | 105 | teardown: function() { 106 | statechart.destroy(); 107 | monitor = root = stateA = stateB = stateC = stateD = stateE = stateF = stateG = null; 108 | stateH = stateI = stateJ = stateK = stateL = stateM = stateN = stateO = stateP = null; 109 | stateQ = stateR = stateS = stateZ = null; 110 | } 111 | }); 112 | 113 | test("check statechart initialization", function() { 114 | equals(monitor.get('length'), 10, 'initial state sequence should be of length 10'); 115 | equals(monitor.matchSequence().begin().entered(root, 'a', 'b', 'd', 'c', 'f', 'h', 'l', 'i', 'n').end(), true, 116 | 'initial sequence should be entered[ROOT, a, b, d, c, f, h, l, i, n]'); 117 | 118 | equals(statechart.get('currentStateCount'), 3, 'current state count should be 3'); 119 | equals(statechart.stateIsCurrentState('d'), true, 'current state should be d'); 120 | equals(statechart.stateIsCurrentState('l'), true, 'current state should be l'); 121 | equals(statechart.stateIsCurrentState('n'), true, 'current state should be n'); 122 | 123 | equals(statechart.stateIsCurrentState('h'), false, 'current state should not be h'); 124 | equals(statechart.stateIsCurrentState('i'), false, 'current state should not be i'); 125 | equals(statechart.stateIsCurrentState('p'), false, 'current state should not be p'); 126 | equals(statechart.stateIsCurrentState('q'), false, 'current state should not be q'); 127 | equals(statechart.stateIsCurrentState('r'), false, 'current state should not be r'); 128 | equals(statechart.stateIsCurrentState('s'), false, 'current state should not be s'); 129 | 130 | equals(stateA.getPath('currentSubstates.length'), 3, 'state a should have 3 current substates'); 131 | equals(stateA.stateIsCurrentSubstate('d'), true, 'state a\'s current substate should be state d'); 132 | equals(stateA.stateIsCurrentSubstate('l'), true, 'state a\'s current substate should be state l'); 133 | equals(stateA.stateIsCurrentSubstate('n'), true, 'state a\'s current substate should be state n'); 134 | 135 | equals(stateC.getPath('currentSubstates.length'), 2, 'state a should have 2 current substates'); 136 | equals(stateC.stateIsCurrentSubstate('l'), true, 'state c\'s current substate should be state l'); 137 | equals(stateC.stateIsCurrentSubstate('n'), true, 'state c\'s current substate should be state n'); 138 | 139 | equals(stateF.getPath('currentSubstates.length'), 2, 'state f should have 2 current substates'); 140 | equals(stateF.stateIsCurrentSubstate('l'), true, 'state f\'s current substate should be state l'); 141 | equals(stateF.stateIsCurrentSubstate('n'), true, 'state f\'s current substate should be state n'); 142 | 143 | equals(stateG.getPath('currentSubstates.length'), 0, 'state g should have no current substates'); 144 | 145 | ok(monitor.matchEnteredStates(root, 'a', 'b', 'd', 'c', 'f', 'h', 'i', 'l', 'n'), 'states root, A, B, C, D, F, H, I, L and N should all be entered'); 146 | }); 147 | 148 | test("from state l, go to state g", function() { 149 | monitor.reset(); 150 | stateL.gotoState('g'); 151 | 152 | equals(monitor.get('length'), 10, 'initial state sequence should be of length 10'); 153 | equals(monitor.matchSequence() 154 | .begin() 155 | .exited('l', 'h', 'n', 'i', 'f') 156 | .entered('g', 'j', 'p', 'k', 'r') 157 | .end(), 158 | true, 'initial sequence should be exited[l, h, n, i, f], entered[g, j, p, k, r]'); 159 | 160 | equals(statechart.get('currentStateCount'), 3, 'current state count should be 3'); 161 | equals(statechart.stateIsCurrentState('d'), true, 'current state should be d'); 162 | equals(statechart.stateIsCurrentState('l'), false, 'current state should not be l'); 163 | equals(statechart.stateIsCurrentState('n'), false, 'current state should not be n'); 164 | equals(statechart.stateIsCurrentState('p'), true, 'current state should be p'); 165 | equals(statechart.stateIsCurrentState('r'), true, 'current state should be r'); 166 | 167 | equals(stateA.getPath('currentSubstates.length'), 3, 'state a should have 3 current substates'); 168 | equals(stateA.stateIsCurrentSubstate('d'), true, 'state a\'s current substate should be state d'); 169 | equals(stateA.stateIsCurrentSubstate('p'), true, 'state a\'s current substate should be state p'); 170 | equals(stateA.stateIsCurrentSubstate('r'), true, 'state a\'s current substate should be state r'); 171 | 172 | equals(stateC.getPath('currentSubstates.length'), 2, 'state a should have 2 current substates'); 173 | equals(stateC.stateIsCurrentSubstate('p'), true, 'state c\'s current substate should be state p'); 174 | equals(stateC.stateIsCurrentSubstate('r'), true, 'state c\'s current substate should be state r'); 175 | 176 | equals(stateF.getPath('currentSubstates.length'), 0, 'state f should have no current substates'); 177 | 178 | equals(stateG.getPath('currentSubstates.length'), 2, 'state g should have 2 current substates'); 179 | equals(stateG.stateIsCurrentSubstate('p'), true, 'state g\'s current substate should be state p'); 180 | equals(stateG.stateIsCurrentSubstate('r'), true, 'state g\'s current substate should be state r'); 181 | 182 | ok(monitor.matchEnteredStates(root, 'a', 'b', 'd', 'c', 'g', 'j', 'k', 'p', 'r'), 'states root, A, B, C, D, G, J, K, P and R should all be entered'); 183 | }); 184 | 185 | test('from state l, go to state z', function() { 186 | monitor.reset(); 187 | stateL.gotoState('z'); 188 | 189 | equals(monitor.get('length'), 10, 'initial state sequence should be of length 10'); 190 | equals(monitor.matchSequence() 191 | .begin() 192 | .exited('l', 'h', 'n', 'i', 'f', 'c', 'd', 'b', 'a') 193 | .entered('z') 194 | .end(), 195 | true, 'sequence should be exited[l, h, n, i, f, c, d, b, a], entered[z]'); 196 | 197 | equals(statechart.get('currentStateCount'), 1, 'current state count should be 1'); 198 | equals(statechart.stateIsCurrentState('z'), true, 'current state should be z'); 199 | equals(statechart.stateIsCurrentState('l'), false, 'current state should not be l'); 200 | equals(statechart.stateIsCurrentState('n'), false, 'current state should not be n'); 201 | equals(statechart.stateIsCurrentState('d'), false, 'current state should not be d'); 202 | 203 | equals(stateA.getPath('currentSubstates.length'), 0, 'state a should have no current substates'); 204 | equals(stateB.getPath('currentSubstates.length'), 0, 'state b should have no current substates'); 205 | equals(stateC.getPath('currentSubstates.length'), 0, 'state c should have no current substates'); 206 | equals(stateF.getPath('currentSubstates.length'), 0, 'state f should have no current substates'); 207 | equals(stateG.getPath('currentSubstates.length'), 0, 'state g should have no current substates'); 208 | 209 | ok(monitor.matchEnteredStates(root, 'z'), 'states root and Z should all be entered'); 210 | }); 211 | 212 | test('from state l, go to state z, and then go to state s', function() { 213 | stateL.gotoState('z'); 214 | 215 | monitor.reset(); 216 | stateZ.gotoState('s'); 217 | 218 | equals(monitor.get('length'), 10, 'initial state sequence should be of length 10'); 219 | equals(monitor.matchSequence() 220 | .begin() 221 | .exited('z') 222 | .entered('a', 'c', 'g', 'k', 's', 'j', 'p', 'b', 'd') 223 | .end(), 224 | true, 'sequence should be exited[z], entered[a, c, g, k, s, j, p, b, d]'); 225 | 226 | equals(statechart.get('currentStateCount'), 3, 'current state count should be 1'); 227 | equals(statechart.stateIsCurrentState('z'), false, 'current state should not be z'); 228 | equals(statechart.stateIsCurrentState('s'), true, 'current state should be s'); 229 | equals(statechart.stateIsCurrentState('p'), true, 'current state should be p'); 230 | equals(statechart.stateIsCurrentState('d'), true, 'current state should be d'); 231 | 232 | equals(stateA.getPath('currentSubstates.length'), 3, 'state a should have 3 current substates'); 233 | equals(stateB.getPath('currentSubstates.length'), 1, 'state b should have 1 current substates'); 234 | equals(stateC.getPath('currentSubstates.length'), 2, 'state c should have 2 current substates'); 235 | equals(stateF.getPath('currentSubstates.length'), 0, 'state f should have no current substates'); 236 | equals(stateG.getPath('currentSubstates.length'), 2, 'state g should have 2 current substates'); 237 | 238 | ok(monitor.matchEnteredStates(root, 'a', 'b', 'd', 'c', 'g', 'j', 'k', 'p', 's'), 'states root, A, B, C, D, G, J, K, P and S should all be entered'); 239 | }); -------------------------------------------------------------------------------- /frameworks/foundation/tests/statechart/invoke_state_method.js: -------------------------------------------------------------------------------- 1 | // ========================================================================== 2 | // Ki.Statechart Unit Test 3 | // ========================================================================== 4 | /*globals Ki statechart State */ 5 | 6 | var TestState; 7 | var obj1, rootState1, stateA, stateB; 8 | var obj2, rootState2, stateC, stateD; 9 | 10 | module("Ki.Statechart: invokeStateMethod Tests", { 11 | setup: function() { 12 | TestState = Ki.State.extend({ 13 | testInvokedCount: 0, 14 | arg1: undefined, 15 | arg2: undefined, 16 | returnValue: undefined, 17 | 18 | testInvoked: function() { 19 | return this.get('testInvokedCount') > 0; 20 | }.property('testInvokedCount'), 21 | 22 | test: function(arg1, arg2) { 23 | this.set('testInvokedCount', this.get('testInvokedCount') + 1); 24 | this.set('arg1', arg1); 25 | this.set('arg2', arg2); 26 | if (this.get('returnValue') !== undefined) { 27 | return this.get('returnValue'); 28 | } 29 | } 30 | }); 31 | 32 | obj1 = SC.Object.extend(Ki.StatechartManager, { 33 | 34 | initialState: 'stateA', 35 | 36 | rootStateExample: TestState.design({ 37 | testX: function(arg1, arg2) { 38 | this.set('testInvokedCount', this.get('testInvokedCount') + 1); 39 | this.set('arg1', arg1); 40 | this.set('arg2', arg2); 41 | if (this.get('returnValue') !== undefined) { 42 | return this.get('returnValue'); 43 | } 44 | } 45 | }), 46 | 47 | stateA: TestState.design(), 48 | 49 | stateB: TestState.design() 50 | 51 | }); 52 | 53 | obj1 = obj1.create(); 54 | rootState1 = obj1.get('rootState'); 55 | stateA = obj1.getState('stateA'); 56 | stateB = obj1.getState('stateB'); 57 | 58 | obj2 = SC.Object.extend(Ki.StatechartManager, { 59 | 60 | statesAreConcurrent: YES, 61 | 62 | rootStateExample: TestState.design({ 63 | testX: function(arg1, arg2) { 64 | this.set('testInvokedCount', this.get('testInvokedCount') + 1); 65 | this.set('arg1', arg1); 66 | this.set('arg2', arg2); 67 | if (this.get('returnValue') !== undefined) { 68 | return this.get('returnValue'); 69 | } 70 | } 71 | }), 72 | 73 | stateC: TestState.design(), 74 | 75 | stateD: TestState.design() 76 | 77 | }); 78 | 79 | obj2 = obj2.create(); 80 | rootState2 = obj2.get('rootState'); 81 | stateC = obj2.getState('stateC'); 82 | stateD = obj2.getState('stateD'); 83 | }, 84 | 85 | teardown: function() { 86 | TestState = obj1 = rootState1 = stateA = stateB = null; 87 | obj2 = rootState2 = stateC = stateD = null; 88 | } 89 | }); 90 | 91 | test("check obj1 - invoke method test1", function() { 92 | var result = obj1.invokeStateMethod('test1'); 93 | ok(!rootState1.get('testInvoked'), "root state test method should not have been invoked"); 94 | ok(!stateA.get('testInvoked'), "state A's test method should not have been invoked"); 95 | ok(!stateB.get('testInvoked'), "state B's test method should not have been invoked"); 96 | }); 97 | 98 | test("check obj1 - invoke method test, current state A, no args, no return value", function() { 99 | var result = obj1.invokeStateMethod('test'); 100 | equals(stateA.get('testInvokedCount'), 1, "state A's test method should have been invoked once"); 101 | equals(stateA.get('arg1'), undefined, "state A's arg1 method should be undefined"); 102 | equals(stateA.get('arg2'), undefined, "state A's arg2 method should be undefined"); 103 | equals(result, undefined, "returned result should be undefined"); 104 | ok(!rootState1.get('testInvoked'), "root state's test method should not have been invoked"); 105 | ok(!stateB.get('testInvoked'), "state B's test method should not have been invoked"); 106 | }); 107 | 108 | test("check obj1 - invoke method test, current state A, one args, no return value", function() { 109 | var result = obj1.invokeStateMethod('test', "frozen"); 110 | ok(stateA.get('testInvoked'), "state A's test method should have been invoked"); 111 | equals(stateA.get('arg1'), "frozen", "state A's arg1 method should be 'frozen'"); 112 | equals(stateA.get('arg2'), undefined, "state A's arg2 method should be undefined"); 113 | ok(!rootState1.get('testInvoked'), "root state's test method should not have been invoked"); 114 | ok(!stateB.get('testInvoked'), "state B's test method should not have been invoked"); 115 | }); 116 | 117 | test("check obj1 - invoke method test, current state A, two args, no return value", function() { 118 | var result = obj1.invokeStateMethod('test', 'frozen', 'canuck'); 119 | ok(stateA.get('testInvoked'), "state A's test method should have been invoked"); 120 | equals(stateA.get('arg1'), "frozen", "state A's arg1 method should be 'frozen'"); 121 | equals(stateA.get('arg2'), "canuck", "state A's arg2 method should be undefined"); 122 | ok(!rootState1.get('testInvoked'), "root state's test method should not have been invoked"); 123 | ok(!stateB.get('testInvoked'), "state B's test method should not have been invoked"); 124 | }); 125 | 126 | test("check obj1 - invoke method test, current state A, no args, return value", function() { 127 | stateA.set('returnValue', 100); 128 | var result = obj1.invokeStateMethod('test'); 129 | ok(stateA.get('testInvoked'), "state A's test method should have been invoked"); 130 | equals(result, 100, "returned result should be 100"); 131 | ok(!rootState1.get('testInvoked'), "root state's test method should not have been invoked"); 132 | ok(!stateB.get('testInvoked'), "state B's test method should not have been invoked"); 133 | }); 134 | 135 | test("check obj1 - invoke method test, current state B, two args, return value", function() { 136 | stateB.set('returnValue', 100); 137 | obj1.gotoState(stateB); 138 | ok(stateB.get('isCurrentState'), "state B should be curren state"); 139 | var result = obj1.invokeStateMethod('test', 'frozen', 'canuck'); 140 | ok(!stateA.get('testInvoked'), "state A's test method should not have been invoked"); 141 | equals(stateB.get('testInvokedCount'), 1, "state B's test method should have been invoked once"); 142 | equals(stateB.get('arg1'), 'frozen', "state B's arg1 method should be 'frozen'"); 143 | equals(stateB.get('arg2'), 'canuck', "state B's arg2 method should be 'canuck'"); 144 | equals(result, 100, "returned result should be 100"); 145 | ok(!rootState1.get('testInvoked'), "root state's test method should not have been invoked"); 146 | }); 147 | 148 | test("check obj1 - invoke method test, current state A, use callback", function() { 149 | var callbackState, callbackResult; 150 | obj1.invokeStateMethod('test', function(state, result) { 151 | callbackState = state; 152 | callbackResult = result; 153 | }); 154 | ok(stateA.get('testInvoked'), "state A's test method should have been invoked"); 155 | ok(!stateB.get('testInvoked'), "state B's test method should not have been invoked"); 156 | equals(callbackState, stateA, "state should be state A"); 157 | equals(callbackResult, undefined, "result should be undefined"); 158 | ok(!rootState1.get('testInvoked'), "root state's test method should not have been invoked"); 159 | }); 160 | 161 | test("check obj1- invoke method test, current state B, use callback", function() { 162 | var callbackState, callbackResult; 163 | obj1.gotoState(stateB); 164 | stateB.set('returnValue', 100); 165 | obj1.invokeStateMethod('test', function(state, result) { 166 | callbackState = state; 167 | callbackResult = result; 168 | }); 169 | ok(!stateA.get('testInvoked'), "state A's test method should not have been invoked"); 170 | ok(stateB.get('testInvoked'), "state B's test method should have been invoked"); 171 | equals(callbackState, stateB, "state should be state B"); 172 | equals(callbackResult, 100, "result should be 100"); 173 | ok(!rootState1.get('testInvoked'), "root state's test method should not have been invoked"); 174 | }); 175 | 176 | test("check obj1 - invoke method testX", function() { 177 | rootState1.set('returnValue', 100); 178 | var result = obj1.invokeStateMethod('testX'); 179 | equals(rootState1.get('testInvokedCount'), 1, "root state's testX method should not have been invoked once"); 180 | equals(result, 100, "result should have value 100"); 181 | ok(!stateA.get('testInvoked'), "state A's test method should have been invoked"); 182 | ok(!stateB.get('testInvoked'), "state B's test method should not have been invoked"); 183 | }); 184 | 185 | test("check obj2 - invoke method test1", function() { 186 | var result = obj2.invokeStateMethod('test1'); 187 | ok(!rootState2.get('testInvoked'), "root state test method should not have been invoked"); 188 | ok(!stateC.get('testInvoked'), "state A's test method should not have been invoked"); 189 | ok(!stateD.get('testInvoked'), "state B's test method should not have been invoked"); 190 | }); 191 | 192 | test("check obj2 - invoke test, no args, no return value", function() { 193 | var result = obj2.invokeStateMethod('test'); 194 | equals(stateC.get('testInvokedCount'), 1, "state C's test method should have been invoked once"); 195 | equals(stateD.get('testInvokedCount'), 1, "state D's test method should have been invoked once"); 196 | ok(!rootState1.get('testInvoked'), "root state test method should not have been invoked"); 197 | equals(stateC.get('arg1'), undefined, "state C's arg1 method should be undefined"); 198 | equals(stateC.get('arg2'), undefined, "state C's arg2 method should be undefined"); 199 | equals(stateD.get('arg1'), undefined, "state D's arg1 method should be undefined"); 200 | equals(stateD.get('arg2'), undefined, "state D's arg2 method should be undefined"); 201 | equals(result, undefined, "returned result should be undefined"); 202 | }); 203 | 204 | test("check obj2 - invoke test, two args, return value, callback", function() { 205 | var numCallbacks = 0, 206 | callbackInfo = {}; 207 | stateC.set('returnValue', 100); 208 | stateD.set('returnValue', 200); 209 | var result = obj2.invokeStateMethod('test', 'frozen', 'canuck', function(state, result) { 210 | numCallbacks += 1; 211 | callbackInfo['state' + numCallbacks] = state; 212 | callbackInfo['result' + numCallbacks] = result; 213 | }); 214 | 215 | ok(!rootState1.get('testInvoked'), "root state test method should not have been invoked"); 216 | equals(stateC.get('testInvokedCount'), 1, "state C's test method should have been invoked once"); 217 | equals(stateD.get('testInvokedCount'), 1, "state D's test method should have been invoked once"); 218 | 219 | equals(stateC.get('arg1'), 'frozen', "state C's arg1 method should be 'frozen'"); 220 | equals(stateC.get('arg2'), 'canuck', "state C's arg2 method should be 'canuck'"); 221 | 222 | equals(stateD.get('arg1'), 'frozen', "state D's arg1 method should be 'frozen'"); 223 | equals(stateD.get('arg2'), 'canuck', "state D's arg2 method should be 'canuck'"); 224 | 225 | equals(numCallbacks, 2, "callback should have been invoked twice"); 226 | equals(callbackInfo['state1'], stateC, "first callback state arg should be state C"); 227 | equals(callbackInfo['result1'], 100, "first callback result arg should be 100"); 228 | equals(callbackInfo['state2'], stateD, "second callback state arg should be state D"); 229 | equals(callbackInfo['result2'], 200, "second callback result arg should be 200"); 230 | 231 | equals(result, undefined, "returned result should be undefined"); 232 | }); 233 | 234 | test("check obj2 - invoke method testX", function() { 235 | rootState2.set('returnValue', 100); 236 | var result = obj2.invokeStateMethod('testX'); 237 | equals(rootState2.get('testInvokedCount'), 1, "root state's testX method should not have been invoked once"); 238 | equals(result, 100, "result should have value 100"); 239 | ok(!stateA.get('testInvoked'), "state A's test method should have been invoked"); 240 | ok(!stateB.get('testInvoked'), "state B's test method should not have been invoked"); 241 | }); -------------------------------------------------------------------------------- /frameworks/foundation/tests/event_handling/advanced/without_concurrent_states.js: -------------------------------------------------------------------------------- 1 | // ========================================================================== 2 | // Ki.Statechart Unit Test 3 | // ========================================================================== 4 | /*globals Ki */ 5 | 6 | var statechart1 = null; 7 | var statechart2 = null; 8 | var statechart3 = null; 9 | var statechart4 = null; 10 | var TestState = null; 11 | 12 | // .......................................................... 13 | // CONTENT CHANGING 14 | // 15 | 16 | module("Ki.Statechart: No Concurrent States - Advanced Event Handling Tests", { 17 | setup: function() { 18 | 19 | TestState = Ki.State.extend({ 20 | event: null, 21 | sender: null, 22 | context: null, 23 | handler: null, 24 | 25 | _handledEvent: function(handler, event, sender, context) { 26 | this.set('handler', handler); 27 | this.set('event', event); 28 | this.set('sender', sender); 29 | this.set('context', context); 30 | }, 31 | 32 | reset: function() { 33 | this.set('handler', null); 34 | this.set('event', null); 35 | this.set('sender', null); 36 | this.set('context', null); 37 | } 38 | }); 39 | 40 | statechart1 = Ki.Statechart.create({ 41 | 42 | rootState: TestState.design({ 43 | 44 | foo: function(sender, context) { 45 | this._handledEvent('foo', null, sender, context); 46 | }, 47 | 48 | eventHandlerA: function(event, sender, context) { 49 | this._handledEvent('eventHandlerA', event, sender, context); 50 | }.handleEvents('plus', 'minus', 'mulitply', 'divide'), 51 | 52 | eventHandlerB: function(event, sender, context) { 53 | this._handledEvent('eventHandlerB', event, sender, context); 54 | }.handleEvents(/num\d/), 55 | 56 | unknownEvent: function(event, sender, context) { 57 | this._handledEvent('unknownEvent', event, sender, context); 58 | } 59 | 60 | }) 61 | 62 | }); 63 | 64 | statechart2 = Ki.Statechart.create({ 65 | 66 | rootState: TestState.design({ 67 | 68 | foo: function(sender, context) { 69 | this._handledEvent('foo', null, sender, context); 70 | } 71 | 72 | }) 73 | 74 | }); 75 | 76 | statechart3 = Ki.Statechart.create({ 77 | 78 | rootState: TestState.design({ 79 | 80 | eventHandlerA: function(event, sender, context) { 81 | this._handledEvent('eventHandlerA', event, sender, context); 82 | }.handleEvents(/num\d/, 'decimal'), 83 | 84 | eventHandlerB: function(event, sender, context) { 85 | this._handledEvent('eventHandlerB', event, sender, context); 86 | }.handleEvents(/foo/, /bar/) 87 | 88 | }) 89 | 90 | }); 91 | 92 | statechart4 = Ki.Statechart.create({ 93 | 94 | rootState: TestState.design({ 95 | 96 | initialSubstate: 'a', 97 | 98 | foo: function(sender, context) { 99 | this._handledEvent('foo', null, sender, context); 100 | }, 101 | 102 | eventHandlerRoot: function(event, sender, context) { 103 | this._handledEvent('eventHandlerRoot', event, sender, context); 104 | }.handleEvents('yes', 'no'), 105 | 106 | unknownEvent: function(event, sender, context) { 107 | this._handledEvent('unknownEvent', event, sender, context); 108 | }, 109 | 110 | a: TestState.design({ 111 | 112 | initialSubstate: 'b', 113 | 114 | bar: function(sender, context) { 115 | this._handledEvent('bar', null, sender, context); 116 | }, 117 | 118 | eventHandlerA: function(event, sender, context) { 119 | this._handledEvent('eventHandlerA', event, sender, context); 120 | }.handleEvents('frozen', 'canuck'), 121 | 122 | b: TestState.design({ 123 | 124 | cat: function(sender, context) { 125 | this._handledEvent('cat', null, sender, context); 126 | }, 127 | 128 | eventHandlerB: function(event, sender, context) { 129 | this._handledEvent('eventHandlerB', event, sender, context); 130 | }.handleEvents(/apple/, /orange/) 131 | 132 | }) 133 | 134 | }) 135 | 136 | }) 137 | 138 | }); 139 | 140 | statechart1.initStatechart(); 141 | statechart2.initStatechart(); 142 | statechart3.initStatechart(); 143 | statechart4.initStatechart(); 144 | }, 145 | 146 | teardown: function() { 147 | statechart1.destroy(); 148 | statechart2.destroy(); 149 | statechart3.destroy(); 150 | statechart4.destroy(); 151 | statechart1 = null; 152 | statechart2 = null; 153 | statechart3 = null; 154 | statechart4 = null; 155 | } 156 | }); 157 | 158 | test("check statechart1 event handling", function() { 159 | var state = statechart1.get('rootState'), 160 | sender = SC.Object.create(), 161 | context = SC.Object.create(); 162 | 163 | state.reset(); 164 | statechart1.sendEvent('foo', sender, context); 165 | equals(state.get('handler'), 'foo', 'event handler should be foo'); 166 | equals(state.get('event'), null, 'event should be null'); 167 | equals(state.get('sender'), sender, 'sender should be sender object'); 168 | equals(state.get('context'), context, 'context should be context object'); 169 | 170 | state.reset(); 171 | statechart1.sendEvent('plus', sender, context); 172 | equals(state.get('handler'), 'eventHandlerA', 'event handler should be eventHandlerA'); 173 | equals(state.get('event'), 'plus', 'event should be plus'); 174 | equals(state.get('sender'), sender, 'sender should be sender object'); 175 | equals(state.get('context'), context, 'context should be context object'); 176 | 177 | state.reset(); 178 | statechart1.sendEvent('divide', sender, context); 179 | equals(state.get('handler'), 'eventHandlerA', 'event handler should be eventHandlerA'); 180 | equals(state.get('event'), 'divide', 'event should be divide'); 181 | equals(state.get('sender'), sender, 'sender should be sender object'); 182 | equals(state.get('context'), context, 'context should be context object'); 183 | 184 | state.reset(); 185 | statechart1.sendEvent('num1', sender, context); 186 | equals(state.get('handler'), 'eventHandlerB', 'event handler should be eventHandlerB'); 187 | equals(state.get('event'), 'num1', 'event should be num1'); 188 | equals(state.get('sender'), sender, 'sender should be sender object'); 189 | equals(state.get('context'), context, 'context should be context object'); 190 | 191 | state.reset(); 192 | statechart1.sendEvent('bar', sender, context); 193 | equals(state.get('handler'), 'unknownEvent', 'event handler should be unknownEvent'); 194 | equals(state.get('event'), 'bar', 'event should be bar'); 195 | equals(state.get('sender'), sender, 'sender should be sender object'); 196 | equals(state.get('context'), context, 'context should be context object'); 197 | }); 198 | 199 | test("check statechart2 event handling", function() { 200 | var state = statechart2.get('rootState'), 201 | sender = SC.Object.create(), 202 | context = SC.Object.create(); 203 | 204 | state.reset(); 205 | statechart2.sendEvent('foo', sender, context); 206 | equals(state.get('handler'), 'foo', 'event handler should be foo'); 207 | equals(state.get('event'), null, 'event should be null'); 208 | equals(state.get('sender'), sender, 'sender should be sender object'); 209 | equals(state.get('context'), context, 'context should be context object'); 210 | 211 | state.reset(); 212 | statechart2.sendEvent('bar', sender, context); 213 | equals(state.get('handler'), null, 'event handler should be null'); 214 | equals(state.get('event'), null, 'event should be null'); 215 | equals(state.get('sender'), null, 'sender should be sender null'); 216 | equals(state.get('context'), null, 'context should be context null'); 217 | }); 218 | 219 | test("check statechart3 event handling", function() { 220 | var state = statechart3.get('rootState'); 221 | 222 | state.reset(); 223 | statechart3.sendEvent('num2'); 224 | equals(state.get('handler'), 'eventHandlerA', 'event handler should be eventHandlerA'); 225 | equals(state.get('event'), 'num2', 'event should be num2'); 226 | 227 | state.reset(); 228 | statechart3.sendEvent('decimal'); 229 | equals(state.get('handler'), 'eventHandlerA', 'event handler should be eventHandlerA'); 230 | equals(state.get('event'), 'decimal', 'event should be decimal'); 231 | 232 | state.reset(); 233 | statechart3.sendEvent('foo'); 234 | equals(state.get('handler'), 'eventHandlerB', 'event handler should be eventHandlerB'); 235 | equals(state.get('event'), 'foo', 'event should be foo'); 236 | 237 | state.reset(); 238 | statechart3.sendEvent('bar'); 239 | equals(state.get('handler'), 'eventHandlerB', 'event handler should be eventHandlerB'); 240 | equals(state.get('event'), 'bar', 'event should be bar'); 241 | }); 242 | 243 | test("check statechart4 event handling", function() { 244 | var root = statechart4.get('rootState'), 245 | stateA = statechart4.getState('a'), 246 | stateB = statechart4.getState('b'); 247 | 248 | root.reset(); stateA.reset(); stateB.reset(); 249 | statechart4.sendEvent('foo'); 250 | equals(root.get('handler'), 'foo', 'root state event handler should be foo'); 251 | equals(root.get('event'), null, 'root state event should be null'); 252 | equals(stateA.get('handler'), null, 'state A event handler should be null'); 253 | equals(stateA.get('event'), null, 'state A event should be null'); 254 | equals(stateB.get('handler'), null, 'state B event handler should be null'); 255 | equals(stateB.get('event'), null, 'state B event should be null'); 256 | 257 | root.reset(); stateA.reset(); stateB.reset(); 258 | statechart4.sendEvent('yes'); 259 | equals(root.get('handler'), 'eventHandlerRoot', 'root state event handler should be eventHandlerRoot'); 260 | equals(root.get('event'), 'yes', 'root state event should be yes'); 261 | equals(stateA.get('handler'), null, 'state A event handler should be null'); 262 | equals(stateA.get('event'), null, 'state A event should be null'); 263 | equals(stateB.get('handler'), null, 'state B event handler should be null'); 264 | equals(stateB.get('event'), null, 'state B event should be null'); 265 | 266 | root.reset(); stateA.reset(); stateB.reset(); 267 | statechart4.sendEvent('xyz'); 268 | equals(root.get('handler'), 'unknownEvent', 'root state event handler should be unknownEvent'); 269 | equals(root.get('event'), 'xyz', 'root state event should be xyz'); 270 | equals(stateA.get('handler'), null, 'state A event handler should be null'); 271 | equals(stateA.get('event'), null, 'state A event should be null'); 272 | equals(stateB.get('handler'), null, 'state B event handler should be null'); 273 | equals(stateB.get('event'), null, 'state B event should be null'); 274 | 275 | root.reset(); stateA.reset(); stateB.reset(); 276 | statechart4.sendEvent('bar'); 277 | equals(root.get('handler'), null, 'root state event handler should be null'); 278 | equals(root.get('event'), null, 'root state event should be null'); 279 | equals(stateA.get('handler'), 'bar', 'state A event handler should be bar'); 280 | equals(stateA.get('event'), null, 'state A event should be null'); 281 | equals(stateB.get('handler'), null, 'state B event handler should be null'); 282 | equals(stateB.get('event'), null, 'state B event should be null'); 283 | 284 | root.reset(); stateA.reset(); stateB.reset(); 285 | statechart4.sendEvent('canuck'); 286 | equals(root.get('handler'), null, 'root state event handler should be null'); 287 | equals(root.get('event'), null, 'root state event should be null'); 288 | equals(stateA.get('handler'), 'eventHandlerA', 'state A event handler should be eventHandlerA'); 289 | equals(stateA.get('event'), 'canuck', 'state A event should be canuck'); 290 | equals(stateB.get('handler'), null, 'state B event handler should be null'); 291 | equals(stateB.get('event'), null, 'state B event should be null'); 292 | 293 | root.reset(); stateA.reset(); stateB.reset(); 294 | statechart4.sendEvent('cat'); 295 | equals(root.get('handler'), null, 'root state event handler should be null'); 296 | equals(root.get('event'), null, 'root state event should be null'); 297 | equals(stateA.get('handler'), null, 'state A event handler should be null'); 298 | equals(stateA.get('event'), null, 'state A event should be null'); 299 | equals(stateB.get('handler'), 'cat', 'state B event handler should be cat'); 300 | equals(stateB.get('event'), null, 'state B event should be null'); 301 | 302 | root.reset(); stateA.reset(); stateB.reset(); 303 | statechart4.sendEvent('orange'); 304 | equals(root.get('handler'), null, 'root state event handler should be null'); 305 | equals(root.get('event'), null, 'root state event should be null'); 306 | equals(stateA.get('handler'), null, 'state A event handler should be null'); 307 | equals(stateA.get('event'), null, 'state A event should be null'); 308 | equals(stateB.get('handler'), 'eventHandlerB', 'state B event handler should be eventHandlerB'); 309 | equals(stateB.get('event'), 'orange', 'state B event should be orange'); 310 | }); -------------------------------------------------------------------------------- /frameworks/foundation/tests/state_transitioning/standard/without_concurrent_states/core.js: -------------------------------------------------------------------------------- 1 | // ========================================================================== 2 | // Ki.Statechart Unit Test 3 | // ========================================================================== 4 | /*globals Ki */ 5 | 6 | var statechart = null; 7 | var root, stateA, stateB, stateC, stateD, stateE, stateF, stateG, stateH; 8 | var stateI, stateJ, stateK, stateL, stateM, stateN, monitor; 9 | var allState; 10 | 11 | module("Ki.Statechart: No Concurrent States - Goto State Tests", { 12 | setup: function() { 13 | 14 | statechart = Ki.Statechart.create({ 15 | 16 | monitorIsActive: YES, 17 | 18 | rootState: Ki.State.design({ 19 | 20 | initialSubstate: 'a', 21 | 22 | a: Ki.State.design({ 23 | 24 | initialSubstate: 'c', 25 | 26 | c: Ki.State.design({ 27 | initialSubstate: 'g', 28 | g: Ki.State.design(), 29 | h: Ki.State.design() 30 | }), 31 | 32 | d: Ki.State.design({ 33 | initialSubstate: 'i', 34 | i: Ki.State.design(), 35 | j: Ki.State.design() 36 | }) 37 | 38 | }), 39 | 40 | b: Ki.State.design({ 41 | 42 | initialSubstate: 'e', 43 | 44 | e: Ki.State.design({ 45 | initialSubstate: 'k', 46 | k: Ki.State.design(), 47 | l: Ki.State.design() 48 | }), 49 | 50 | f: Ki.State.design({ 51 | initialSubstate: 'm', 52 | m: Ki.State.design(), 53 | n: Ki.State.design() 54 | }) 55 | 56 | }) 57 | 58 | }) 59 | 60 | }); 61 | 62 | statechart.initStatechart(); 63 | 64 | monitor = statechart.get('monitor'); 65 | root = statechart.get('rootState'); 66 | stateA = statechart.getState('a'); 67 | stateB = statechart.getState('b'); 68 | stateC = statechart.getState('c'); 69 | stateD = statechart.getState('d'); 70 | stateE = statechart.getState('e'); 71 | stateF = statechart.getState('f'); 72 | stateG = statechart.getState('g'); 73 | stateH = statechart.getState('h'); 74 | stateI = statechart.getState('i'); 75 | stateJ = statechart.getState('j'); 76 | stateK = statechart.getState('k'); 77 | stateL = statechart.getState('l'); 78 | stateM = statechart.getState('m'); 79 | stateN = statechart.getState('n'); 80 | }, 81 | 82 | teardown: function() { 83 | statechart.destroy(); 84 | statechart = monitor = root = null; 85 | stateA = stateB = stateC = stateD = stateE = stateF = stateG = stateH = stateI = stateJ = null; 86 | stateK = stateL = stateM = stateN = null; 87 | } 88 | }); 89 | 90 | test("check statechart state objects", function() { 91 | equals(SC.none(stateG), false, 'statechart should return a state object for state with name "g"'); 92 | equals(stateG.get('name'), 'g', 'state g should have name "g"'); 93 | equals(stateG.get('isCurrentState'), true, 'state G should be current state'); 94 | equals(stateG.stateIsCurrentSubstate('g'), true, 'state g should have current substate g'); 95 | equals(statechart.stateIsCurrentState('g'), true, 'statechart should have current state g'); 96 | equals(statechart.stateIsCurrentState(stateG), true, 'statechart should have current state g'); 97 | 98 | equals(SC.none(stateM), false, 'statechart should return a state object for state with name "m"'); 99 | equals(stateM.get('name'), 'm', 'state m should have name "m"'); 100 | equals(stateM.get('isCurrentState'), false, 'state m should not be current state'); 101 | equals(stateG.stateIsCurrentSubstate('m'), false, 'state m should not have current substate m'); 102 | equals(statechart.stateIsCurrentState('m'), false, 'statechart should not have current state m'); 103 | equals(statechart.stateIsCurrentState(stateM), false, 'statechart should not have current state m'); 104 | 105 | equals(SC.none(stateA), false, 'statechart should return a state object for state with name "a"'); 106 | equals(stateA.get('isCurrentState'), false, 'state m should not be current state'); 107 | equals(stateA.stateIsCurrentSubstate('a'), false, 'state a should not have current substate g'); 108 | equals(stateA.stateIsCurrentSubstate('c'), false, 'state a should not have current substate c'); 109 | equals(stateA.stateIsCurrentSubstate('g'), true, 'state a should have current substate g'); 110 | equals(stateA.stateIsCurrentSubstate(stateG), true, 'state a should have current substate g'); 111 | equals(stateA.stateIsCurrentSubstate(stateM), false, 'state a should not have current substate m'); 112 | 113 | var stateX = statechart.getState('x'); 114 | equals(SC.none(stateX), true, 'statechart should not have a state with name "x"'); 115 | }); 116 | 117 | test("check statechart initialization", function() { 118 | equals(monitor.get('length'), 4, 'initial state sequence should be of length 4'); 119 | equals(monitor.matchSequence().begin().entered(root, 'a', 'c', 'g').end(), true, 'initial sequence should be entered[ROOT, a, c, g]'); 120 | equals(monitor.matchSequence().begin().entered(root, 'a', 'c', 'h').end(), false, 'initial sequence should not be entered[ROOT, a, c, h]'); 121 | 122 | equals(statechart.get('currentStateCount'), 1, 'current state count should be 1'); 123 | equals(statechart.stateIsCurrentState('g'), true, 'current state should be g'); 124 | 125 | ok(monitor.matchEnteredStates(root, 'a', 'c', 'g'), 'states root, A, C and G should all be entered'); 126 | }); 127 | 128 | test("go to state h", function() { 129 | monitor.reset(); 130 | statechart.gotoState('h'); 131 | 132 | equals(monitor.get('length'), 2, 'state sequence should be of length 2'); 133 | equals(monitor.matchSequence().begin().exited('g').entered('h').end(), true, 'sequence should be exited[g], entered[h]'); 134 | equals(monitor.matchSequence().begin().exited('h').entered('g').end(), false, 'sequence should not be exited[h], entered[g]'); 135 | 136 | equals(statechart.get('currentStateCount'), 1, 'current state count should be 1'); 137 | equals(statechart.stateIsCurrentState('h'), true, 'current state should be h'); 138 | 139 | ok(monitor.matchEnteredStates(root, 'a', 'c', 'h'), 'states root, A, C and H should all be entered'); 140 | }); 141 | 142 | test("go to states: h, d", function() { 143 | statechart.gotoState('h'); 144 | 145 | monitor.reset(); 146 | statechart.gotoState('d'); 147 | 148 | equals(monitor.get('length'), 4, 'state sequence should be of length 4'); 149 | equals(monitor.matchSequence().begin().exited('h', 'c').entered('d', 'i').end(), true, 'sequence should be exited[h, c], entered[d, i]'); 150 | equals(monitor.matchSequence().begin().exited('h', 'c').entered('d', 'f').end(), false, 'sequence should not be exited[h, c], entered[d, f]'); 151 | equals(monitor.matchSequence().begin().exited('g', 'c').entered('d', 'i').end(), false, 'sequence should not be exited[g, c], entered[d, f]'); 152 | equals(statechart.get('currentStateCount'), 1, 'current state count should be 1'); 153 | equals(statechart.stateIsCurrentState('i'), true, 'current state should be i'); 154 | 155 | ok(monitor.matchEnteredStates(root, 'a', 'd', 'i'), 'states root, A, D and I should all be entered'); 156 | }); 157 | 158 | test("go to states: h, d, h", function() { 159 | statechart.gotoState('h'); 160 | statechart.gotoState('d'); 161 | 162 | monitor.reset(); 163 | statechart.gotoState('h'); 164 | 165 | equals(monitor.get('length'), 4, 'state sequence should be of length 4'); 166 | equals(monitor.matchSequence().begin().exited('i', 'd').entered('c', 'h').end(), true, 'sequence should be exited[i, d], entered[c, h]'); 167 | equals(statechart.get('currentStateCount'), 1, 'current state count should be 1'); 168 | equals(statechart.stateIsCurrentState('h'), true, 'current state should be h'); 169 | 170 | ok(monitor.matchEnteredStates(root, 'a', 'c', 'h'), 'states root, A, C and H should all be entered'); 171 | }); 172 | 173 | test("go to state b", function() { 174 | monitor.reset(); 175 | statechart.gotoState('b'); 176 | 177 | equals(monitor.get('length'), 6, 'state sequence should be of length 6'); 178 | equals(monitor.matchSequence().begin().exited('g', 'c', 'a').entered('b', 'e', 'k').end(), true, 'sequence should be exited[g, c, a], entered[b, e, k]'); 179 | equals(monitor.matchSequence().begin().exited('g', 'a', 'c').entered('b', 'e', 'k').end(), false, 'sequence should not be exited[g, a, c], entered[b, e, k]'); 180 | equals(monitor.matchSequence().begin().exited('g', 'c', 'a').entered('b', 'k', 'e').end(), false, 'sequence should not be exited[g, c, a], entered[b, k, e]'); 181 | equals(statechart.get('currentStateCount'), 1, 'current state count should be 1'); 182 | equals(statechart.stateIsCurrentState('k'), true, 'current state should be k'); 183 | 184 | ok(monitor.matchEnteredStates(root, 'b', 'e', 'k'), 'states root, B, E and K should all be entered'); 185 | }); 186 | 187 | test("go to state f", function() { 188 | monitor.reset(); 189 | statechart.gotoState('f'); 190 | 191 | equals(monitor.get('length'), 6, 'state sequence should be of length 6'); 192 | equals(monitor.matchSequence().begin().exited('g', 'c', 'a').entered('b', 'f', 'm').end(), true, 'sequence should be exited[g, c, a], entered[b, f, m]'); 193 | equals(statechart.get('currentStateCount'), 1, 'current state count should be 1'); 194 | equals(statechart.stateIsCurrentState('m'), true, 'current state should be m'); 195 | 196 | ok(monitor.matchEnteredStates(root, 'b', 'f', 'm'), 'states root, B, F and M should all be entered'); 197 | }); 198 | 199 | test("go to state n", function() { 200 | monitor.reset(); 201 | statechart.gotoState('n'); 202 | 203 | equals(monitor.get('length'), 6, 'state sequence should be of length 6'); 204 | equals(monitor.matchSequence().begin().exited('g', 'c', 'a').entered('b', 'f', 'n').end(), true, 'sequence should be exited[g, c, a], entered[b, f, n]'); 205 | equals(statechart.get('currentStateCount'), 1, 'current state count should be 1'); 206 | equals(statechart.stateIsCurrentState('n'), true, 'current state should be n'); 207 | 208 | ok(monitor.matchEnteredStates(root, 'b', 'f', 'n'), 'states root, B, F and N should all be entered'); 209 | }); 210 | 211 | test("re-enter state g", function() { 212 | monitor.reset(); 213 | statechart.gotoState('g'); 214 | 215 | equals(monitor.get('length'), 2, 'state sequence should be of length 2'); 216 | equals(monitor.matchSequence().begin().exited('g').entered('g').end(), true, 'sequence should be exited[g], entered[g]'); 217 | equals(statechart.get('currentStateCount'), 1, 'current state count should be 1'); 218 | equals(statechart.stateIsCurrentState('g'), true, 'current state should be g'); 219 | 220 | monitor.reset(); 221 | equals(monitor.get('length'), 0, 'state sequence should be of length 0 after monitor reset'); 222 | 223 | var state = statechart.getState('g'); 224 | state.reenter(); 225 | 226 | equals(monitor.get('length'), 2, 'state sequence should be of length 2'); 227 | equals(monitor.matchSequence().begin().exited('g').entered('g').end(), true, 'sequence should be exited[g], entered[g]'); 228 | equals(statechart.get('currentStateCount'), 1, 'current state count should be 1'); 229 | equals(statechart.stateIsCurrentState('g'), true, 'current state should be g'); 230 | 231 | ok(monitor.matchEnteredStates(root, 'a', 'c', 'g'), 'states root, A, C and G should all be entered'); 232 | }); 233 | 234 | test("go to g state's ancestor state a", function() { 235 | monitor.reset(); 236 | statechart.gotoState('a'); 237 | 238 | equals(monitor.get('length'), 6, 'initial state sequence should be of length 6'); 239 | equals(monitor.matchSequence().begin().exited('g', 'c', 'a').entered('a', 'c', 'g').end(), true, 'sequence should be exited[g, c, a], entered[a, c, g]'); 240 | equals(statechart.get('currentStateCount'), 1, 'current state count should be 1'); 241 | equals(statechart.stateIsCurrentState('g'), true, 'current state should be g'); 242 | 243 | ok(monitor.matchEnteredStates(root, 'a', 'c', 'g'), 'states root, A, C and G should all be entered'); 244 | }); 245 | 246 | test("go to state b and then go to root state", function() { 247 | statechart.gotoState('b'); 248 | equals(statechart.get('currentStateCount'), 1, 'current state count should be 1'); 249 | equals(statechart.stateIsCurrentState('k'), true, 'current state should be k'); 250 | 251 | monitor.reset(); 252 | statechart.gotoState(statechart.get('rootState')); 253 | 254 | var root = statechart.get('rootState'); 255 | equals(monitor.get('length'), 8, 'initial state sequence should be of length 6'); 256 | equals(monitor.matchSequence().begin().exited('k', 'e', 'b', root).entered(root, 'a', 'c', 'g').end(), 257 | true, 'sequence should be exited[k, e, b, ROOT], entered[ROOT, a, c, g]'); 258 | equals(statechart.get('currentStateCount'), 1, 'current state count should be 1'); 259 | equals(statechart.stateIsCurrentState('g'), true, 'current state should be g'); 260 | 261 | ok(monitor.matchEnteredStates(root, 'a', 'c', 'g'), 'states root, A, C and G should all be entered'); 262 | }); 263 | 264 | test("from state g, go to state m calling state g\'s gotoState", function() { 265 | equals(stateG.get('isCurrentState'), true, 'state g should be current state'); 266 | equals(stateM.get('isCurrentState'), false, 'state m should not be current state'); 267 | 268 | monitor.reset(); 269 | stateG.gotoState('m'); 270 | 271 | equals(monitor.get('length'), 6, 'initial state sequence should be of length 6'); 272 | equals(monitor.matchSequence().begin().exited('g', 'c', 'a').entered('b', 'f', 'm').end(), 273 | true, 'sequence should be exited[g, c, a], entered[b, f, m]'); 274 | equals(statechart.get('currentStateCount'), 1, 'current state count should be 1'); 275 | equals(statechart.stateIsCurrentState('m'), true, 'current state should be m'); 276 | 277 | equals(stateG.get('isCurrentState'), false, 'state g should not be current state'); 278 | equals(stateM.get('isCurrentState'), true, 'state m should be current state'); 279 | 280 | ok(monitor.matchEnteredStates(root, 'b', 'f', 'm'), 'states root, B, F and M should all be entered'); 281 | }); --------------------------------------------------------------------------------