// ========================================================================== // SC.Statechart Unit Test // ========================================================================== /*globals SC */ var statechart, del, monitor, stateA, stateB, stateC; module("SC.Statechart: No Concurrent States - Trigger Routing on States Basic Tests", { setup: function() { del = SC.Object.create(SC.StatechartDelegate, { location: null, handlers: {}, statechartUpdateLocationForState: function(statechart, location, state) { this.set('location', location); }, statechartAcquireLocationForState: function(statechart, state) { return this.get('location'); }, statechartBindStateToRoute: function(statechart, state, route, handler) { this.handlers[route] = { statechart: statechart, state: state, handler: handler }; } }); statechart = SC.Statechart.create({ monitorIsActive: YES, delegate: del, initialState: 'a', a: SC.State.design({ representRoute: 'foo', info: {}, enterState: function(context) { this.info.enterState = { context: context }; } }), b: SC.State.design({ representRoute: 'bar', info: {}, enterState: function(context) { this.info.enterState = { context: context }; } }), c: SC.State.design({ representRoute: 'mah', info: {}, enterStateByRoute: function(context) { this.info.enterStateByRoute = { context: context }; }, enterState: function(context) { this.info.enterState = { context: context }; } }) }); statechart.initStatechart(); monitor = statechart.get('monitor'); stateA = statechart.getState('a'); stateB = statechart.getState('b'); stateC = statechart.getState('c'); }, teardown: function() { statechart = del = monitor = stateA = stateB = stateC = null; } }); test("check statechart initialization", function() { equals(del.get('location'), null, "del.location should be null"); var handlers = del.handlers; ok(handlers['foo'], "delegate should have a route 'foo'"); equals(handlers['foo'].statechart, statechart, "route 'foo' should be bound to statechart"); equals(handlers['foo'].state, stateA, "route 'foo' should be bound to state A"); equals(handlers['foo'].handler, stateA.routeTriggered, "route 'foo' should be bound to handler stateA.routeTriggered"); ok(handlers['bar'], "delegate should have a route 'bar'"); equals(handlers['bar'].statechart, statechart, "route 'foo' should be bound to statechart"); equals(handlers['bar'].state, stateB, "route 'bar' should be bound to state B"); equals(handlers['bar'].handler, stateB.routeTriggered, "route 'bar' should be bound to handler stateB.routeTriggered"); ok(handlers['mah'], "delegate should have a route 'mah'"); equals(handlers['mah'].statechart, statechart, "route 'foo' should be bound to statechart"); equals(handlers['mah'].state, stateC, "route 'mah' should be bound to state C"); equals(handlers['mah'].handler, stateC.routeTriggered, "route 'mah' should be bound to handler stateC.routeTriggered"); }); test("trigger state B's route", function() { var params = {}; monitor.reset(); del.set('location', 'bar'); ok(stateA.get('isCurrentState'), "state A should be a current state"); ok(!stateB.get('isCurrentState'), "state B should not be a current state"); stateB.routeTriggered(params); var seq = monitor.matchSequence().begin().exited('a').entered('b').end(); ok(seq, 'sequence should be exited[a], entered[b]'); ok(!stateA.get('isCurrentState'), "state A should not be a current state after route triggered"); ok(stateB.get('isCurrentState'), "state B should be a current state after route triggered"); var info = stateB.info; ok(info.enterState, "state B's enterState should have been invoked"); var context = info.enterState.context; ok(SC.kindOf(context, SC.StateRouteHandlerContext), "state B's enterState method should have been provided a state route handler context object"); equals(context.get('state'), stateB); equals(context.get('location'), 'bar'); equals(context.get('params'), params); equals(context.get('handler'), stateB.routeTriggered); }); test("trigger state C's route", function() { var params = {}; monitor.reset(); del.set('location', 'mah'); ok(stateA.get('isCurrentState'), "state A should be a current state"); ok(!stateC.get('isCurrentState'), "state C should not be a current state"); stateC.routeTriggered(params); var seq = monitor.matchSequence().begin().exited('a').entered('c').end(); ok(seq, 'sequence should be exited[a], entered[c]'); ok(!stateA.get('isCurrentState'), "state A should not be a current state after route triggered"); ok(stateC.get('isCurrentState'), "state C should be a current state after route triggered"); var info = stateC.info; ok(!info.enterState, "state C's enterState should not have been invoked"); ok(info.enterStateByRoute, "state C's enterStateByRoute should have been invoked"); var context = info.enterStateByRoute.context; ok(SC.kindOf(context, SC.StateRouteHandlerContext), "state C's enterState method should have been provided a state route handler context object"); equals(context.get('state'), stateC); equals(context.get('location'), 'mah'); equals(context.get('params'), params); equals(context.get('handler'), stateC.routeTriggered); }); test("Go to state C without triggering state's route", function() { var context = {}; monitor.reset(); ok(stateA.get('isCurrentState'), "state A should be a current state"); ok(!stateC.get('isCurrentState'), "state C should not be a current state"); statechart.gotoState(stateC, context); var seq = monitor.matchSequence().begin().exited('a').entered('c').end(); ok(seq, 'sequence should be exited[a], entered[c]'); ok(!stateA.get('isCurrentState'), "state A should not be a current state after route triggered"); ok(stateC.get('isCurrentState'), "state C should be a current state after route triggered"); var info = stateC.info; ok(info.enterState, "state C's enterState should have been invoked"); ok(!info.enterStateByRoute, "state C's enterStateByRoute should not have been invoked"); equals(info.enterState.context, context, "state C's enterState should have been passed a context value"); });