text: /*global QUnit, sinon */
  sap.ui.define([
  	"sap/m/routing/Router",
  	"sap/m/routing/TargetHandler",
  	"sap/m/NavContainer",
  	"sap/m/SplitContainer",
  	"sap/m/Page",
  	"sap/ui/core/routing/Views",
  	"./commonIntegrationTests",
  	"sap/ui/qunit/QUnitUtils",
  	"sap/ui/qunit/utils/createAndAppendDiv",
  	"sap/ui/Device",
  	"sap/ui/core/mvc/Controller", // provides sap.ui.controller
  	"sap/ui/core/mvc/JSView" // provides sap.ui.jsview
  ], function(
  	Router,
  	TargetHandler,
  	NavContainer,
  	SplitContainer,
  	Page,
  	Views,
  	integrationTests,
  	qutils,
  	createAndAppendDiv,
  	Device
  ) {
  	"use strict";
  
  	// prepare DOM
  	createAndAppendDiv("content");
  
  
  	var fnCreateRouter = function() {
  		var args = Array.prototype.slice.call(arguments);
  
  		args.unshift(Router);
  
  		if (args.length < 3) {
  			args[2] = {};
  		}
  		if (args[2] === null) {
  			args[2] = {};
  		}
  		args[2].async = false;
  
  		return new (Function.prototype.bind.apply(Router, args))();
  	};
  
  	QUnit.module("Construction and destruction");
  
  	QUnit.test("Should pass the targetHandler to the targets instance", function (assert) {
  		// System under test
  		var oRouter = fnCreateRouter(null, null, null, {});
  
  		// Assert
  		assert.strictEqual(oRouter._oTargets._oTargetHandler, oRouter._oTargetHandler, "Did pass the target handler");
  
  		oRouter.destroy();
  	});
  
  	QUnit.module("add and execute navigations", {
  		beforeEach: function () {
  			this.oStartPage = new Page();
  			this.oNavContainer = new NavContainer({
  				pages: this.oStartPage
  			});
  			this.sPattern = "some/{eventData}";
  			this.oToPage = new Page();
  			this.oTargetConfiguration = {
  				controlId: this.oNavContainer.getId(),
  				transition: "flip",
  				viewName: "anyThingToPassValidation",
  				viewLevel: 5,
  				transitionParameters: { some: "parameter"}
  			};
  			// System under test
  			this.oRouter = fnCreateRouter({
  						myRoute: {
  							pattern: this.sPattern,
  							target: "myTarget"
  						}
  					},
  					{
  						controlAggregation: "pages"
  					},
  					null,
  					{
  						myTarget: this.oTargetConfiguration
  					});
  		},
  		afterEach: function () {
  			this.oNavContainer.destroy();
  			this.oToPage.destroy();
  			this.oStartPage.destroy();
  			this.oRouter.destroy();
  		}
  	});
  
  	QUnit.test("Should do a forward navigation", function (assert) {
  		//Arrange
  		var that = this,
  				oToSpy = this.spy(this.oNavContainer, "to"),
  				oNavigateSpy = this.spy(this.oRouter._oTargetHandler, "navigate");
  
  		this.stub(Views.prototype, "_getView").callsFake(function () {
  			return that.oToPage;
  		});
  
  		//Act
  		this.oRouter.parse("some/myData");
  
  		//Assert
  		assert.strictEqual(oToSpy.callCount, 1, "did call the 'to' function on the oNavContainer instance");
  		sinon.assert.calledWithExactly(oToSpy, this.oToPage.getId(), this.oTargetConfiguration.transition, { eventData: "myData"}, this.oTargetConfiguration.transitionParameters);
  
  		assert.strictEqual(oNavigateSpy.callCount, 1, "did call the 'navigate' function on the TargetHandler instance");
  		sinon.assert.calledWithExactly(oNavigateSpy, {
  			askHistory: true,
  			navigationIdentifier: "myTarget",
  			level: 5
  		});
  	});
  
  	///////////////////////////////////////////////////////
  	/// Integation test
  	///////////////////////////////////////////////////////
  	QUnit.module("Integration tests");
  
  	function createViewAndController(sName) {
  		sap.ui.controller(sName, {});
  		sap.ui.jsview(sName, {
  			createContent: function () {
  			},
  			getController: function () {
  				return sap.ui.controller(sName);
  			}
  		});
  
  		return sap.ui.jsview(sName);
  	}
  
  	QUnit.test("Should respect the viewLevel for multiple targets", function (assert) {
  		//Arrange
  		var oNavContainer = new NavContainer(),
  				oRouter = fnCreateRouter(
  					{
  						"route": {
  							pattern: "anyPattern",
  							target: ["first", "second"]
  						}
  					},
  					{
  						viewType: "JS",
  						controlAggregation:"pages",
  						controlId: oNavContainer.getId()
  					},
  					null,
  					{
  						first: {
  							viewName: "first"
  						},
  						second: {
  							viewName: "second",
  							viewLevel: 0
  						},
  						initial: {
  							viewName: "initial",
  							viewLevel: 1
  						}
  					}),
  				fnBackSpy = this.spy(oNavContainer, "backToPage");
  
  		// views
  		createViewAndController("first");
  		createViewAndController("second");
  		createViewAndController("initial");
  
  		oRouter.getTargets().display("initial");
  
  		// Act
  		oRouter.parse("anyPattern");
  
  		// Assert
  		assert.strictEqual(fnBackSpy.callCount, 1, "Did execute a back navigation");
  		assert.strictEqual(fnBackSpy.firstCall.args[0], oRouter.getView("second").getId(), "The second page was target of the back navigation");
  
  		// Cleanup
  		oRouter.destroy();
  	});
  
  	QUnit.test("Should take the viewLevel from the first ancester which has a viewLevel if a target doesn't have viewLevel defined", function (assert) {
  		//Arrange
  		var oNavContainer = new NavContainer(),
  			oRouter = fnCreateRouter(
  				{
  					"route": {
  						pattern: "anyPattern",
  						target: ["third"]
  					}
  				},
  				{
  					viewType: "JS",
  					controlAggregation:"pages",
  					controlId: oNavContainer.getId()
  				},
  				null,
  				{
  					first: {
  						viewName: "first",
  						viewLevel: 1
  					},
  					second: {
  						parent: "first",
  						viewName: "second"
  					},
  					third: {
  						parent: "second",
  						viewName: "third"
  					},
  					initial: {
  						viewName: "initial",
  						viewLevel: 2
  					}
  				}),
  			fnBackSpy = this.spy(oNavContainer, "backToPage");
  
  		// views
  		createViewAndController("first");
  		createViewAndController("second");
  		createViewAndController("third");
  		createViewAndController("initial");
  
  		oRouter.getTargets().display("initial");
  
  		// Act
  		oRouter.parse("anyPattern");
  
  		// Assert
  		assert.strictEqual(fnBackSpy.callCount, 1, "Did execute a back navigation");
  		assert.strictEqual(fnBackSpy.firstCall.args[0], oRouter.getView("third").getId(), "The second page was target of the back navigation");
  
  		// Cleanup
  		oRouter.destroy();
  	});
  
  
  	QUnit.test("Should pass some data to the SplitContainer", function (assert) {
  		//Arrange
  		var oSplitContainer = new SplitContainer({
  					masterPages: [createViewAndController("InitialMaster")]
  				}),
  				oRouter = fnCreateRouter({
  					"Master": {
  						targetControl: oSplitContainer.getId(),
  						pattern: "{id}",
  						view: "Master",
  						viewType: "JS",
  						targetAggregation: "masterPages"
  					}
  				}),
  				data = null;
  
  		this.stub(Device.system, "phone").value(false);
  
  		// views
  		createViewAndController("Master");
  
  		oRouter.getView("Master", "JS").addEventDelegate({
  			onBeforeShow: function (oEvent) {
  				data = oEvent.data.id;
  			}
  		});
  
  		// Act
  		oRouter.parse("5");
  
  		// Assert
  		assert.strictEqual(data, "5", "should pass 5 to the page");
  
  		// Cleanup
  		oRouter.destroy();
  	});
  
  	QUnit.test("Should pass some data to the initial page of NavContainer", function(assert) {
  		assert.expect(1);
  
  		var oNavContainer = new NavContainer(),
  			oRouter = fnCreateRouter({
  				route1: {
  					targetControl: oNavContainer.getId(),
  					pattern: "{id}",
  					view: "view1",
  					viewType: "JS",
  					targetAggregation: "pages"
  				}
  			}),
  			data = null,
  			done = assert.async();
  
  		// views
  		createViewAndController("view1");
  		oRouter.getView("view1", "JS").addEventDelegate({
  			onBeforeShow: function(oEvent) {
  				data = oEvent.data.id;
  				// Assert
  				assert.strictEqual(data, "5", "should pass 5 to the page");
  			},
  			onAfterShow: function(oEvent) {
  				// Cleanup
  				oRouter.destroy();
  				oNavContainer.destroy();
  
  				done();
  			}
  		});
  
  		oRouter.getRoute("route1").attachMatched(function() {
  			oNavContainer.placeAt("content");
  		});
  
  		// Act
  		oRouter.parse("5");
  	});
  
  	QUnit.test("Should pass some data to the initial pages of SplitContainer", function(assert) {
  		assert.expect(2);
  
  		var oSplitContainer = new SplitContainer(),
  			oRouter = fnCreateRouter({
  				route1: {
  					pattern: "{id}",
  					target: ["master", "detail"]
  				}
  			}, {
  				viewType: "JS",
  				controlId: oSplitContainer.getId()
  			}, null, {
  				master: {
  					controlAggregation: "masterPages",
  					viewName: "master"
  				},
  				detail: {
  					controlAggregation: "detailPages",
  					viewName: "detail"
  				}
  			}),
  			oMasterData = null,
  			oDetailData = null,
  			done = assert.async();
  
  		// views
  		createViewAndController("master");
  		createViewAndController("detail");
  		oRouter.getView("master", "JS").addEventDelegate({
  			onBeforeShow: function(oEvent) {
  				oMasterData = oEvent.data.id;
  			}
  		});
  		oRouter.getView("detail", "JS").addEventDelegate({
  			onBeforeShow: function(oEvent) {
  				oDetailData = oEvent.data.id;
  				// Assert
  				assert.strictEqual(oMasterData, "5", "should pass 5 to the master page");
  				assert.strictEqual(oDetailData, "5", "should pass 5 to the detail page");
  			},
  			onAfterShow: function(oEvent) {
  				// Cleanup
  				oRouter.destroy();
  				oSplitContainer.destroy();
  				done();
  			}
  		});
  
  		oRouter.getRoute("route1").attachMatched(function() {
  			oSplitContainer.placeAt("content");
  		});
  
  		// Act
  		oRouter.parse("5");
  	});
  
  	QUnit.module("Routes using targets mixed with old routes", {
  		beforeEach: function () {
  			this.oMasterDummy = new Page();
  			this.oDetailDummy = new Page();
  			this.oSplitContainer = new SplitContainer({
  				masterPages: this.oMasterDummy,
  				detailPages: this.oDetailDummy
  			});
  			this.oMasterView = createViewAndController("Master");
  			this.oDetailView = createViewAndController("Detail");
  			this.sPattern = "somePattern";
  			// System under test
  			this.oRouter = fnCreateRouter({
  						myMasterRoute: {
  							targetAggregation: "masterPages",
  							view: "Master",
  							subroutes: [
  								{
  									name: "detailRoute",
  									pattern: this.sPattern,
  									target: "detailTarget"
  								}
  							]
  						}
  					},
  					{
  						transition: "flip",
  						viewLevel: 5,
  						transitionParameters: { some: "parameter"},
  						controlId: this.oSplitContainer.getId(),
  						targetControl: this.oSplitContainer.getId(),
  						targetAggregation: "detailPages",
  						controlAggregation: "detailPages",
  						viewType: "JS"
  					},
  					null,
  					{
  						detailTarget: {
  							viewName: "Detail"
  						}
  					});
  			this.oRouter.getViews().setView("Detail", this.oDetailView);
  			this.oRouter.getViews().setView("Master", this.oMasterView);
  		},
  		afterEach: function () {
  			this.oSplitContainer.destroy();
  			this.oRouter.destroy();
  		}
  	});
  
  	QUnit.test("Should be able to handle the mixed case", function (assert) {
  		this.oRouter.parse(this.sPattern);
  
  		assert.strictEqual(this.oSplitContainer.getCurrentDetailPage(), this.oDetailView, "Did navigate to detail");
  		assert.strictEqual(this.oSplitContainer.getCurrentMasterPage(), this.oMasterView, "Did navigate to master");
  	});
  
  	integrationTests.start({
  		beforeEach: function (oConfig) {
  			var oRouter = fnCreateRouter(oConfig);
  
  			this.oRouter = oRouter;
  			return oRouter;
  		},
  		act: function (sPatternOrName) {
  			this.oRouter.parse(sPatternOrName);
  		},
  		afterEach: function () {
  			this.oRouter.destroy();
  		}
  	});
  });