/*global QUnit, sinon */
sap.ui.define([
	"sap/ui/qunit/QUnitUtils",
	"sap/ui/qunit/utils/createAndAppendDiv",
	"sap/ui/thirdparty/jquery",
	"sap/m/library",
	"sap/m/StepInput",
	"sap/m/Label",
	"sap/ui/core/library",
	"sap/ui/Device",
	"sap/ui/model/json/JSONModel",
	"sap/ui/layout/form/Form",
	"sap/ui/layout/form/FormContainer",
	"sap/ui/layout/form/FormElement",
	"sap/ui/layout/form/ResponsiveGridLayout",
	"sap/ui/layout/GridData",
	"sap/ui/events/KeyCodes",
	"sap/ui/model/type/Float",
	"sap/ui/core/Core"
], function(
	qutils,
	createAndAppendDiv,
	jQuery,
	mobileLibrary,
	StepInput,
	Label,
	coreLibrary,
	Device,
	JSONModel,
	Form,
	FormContainer,
	FormElement,
	ResponsiveGridLayout,
	GridData,
	KeyCodes,
	TypeFloat,
	oCore
) {
	"use strict";

	// shortcut for sap.m.StepInputValidationMode
	var StepInputValidationMode = mobileLibrary.StepInputValidationMode;

	// shortcut for sap.ui.core.ValueState
	var ValueState = coreLibrary.ValueState;

	// shortcut for sap.m.StepInputStepModeType
	var StepMode = mobileLibrary.StepInputStepModeType;

	createAndAppendDiv("content");



	var bSkipDestroy = new URLSearchParams(window.location.search).has("testId");

	QUnit.module("API", {
		beforeEach: function () {
			this.stepInput = new StepInput();

			this.stepInput.placeAt('qunit-fixture');
			oCore.applyChanges();
		},
		afterEach: function () {
			if (!bSkipDestroy) {
				this.stepInput.destroy();
			}
		}
	});

	QUnit.test("DOM", function (assert) {
		//assert
		var $stepInput = this.stepInput.$();
		assert.ok($stepInput.length > 0, "The control was successfully rendered");
		assert.strictEqual($stepInput.attr("tabindex"), undefined, "tabindex attribute should not be present");
	});

	QUnit.test("incrementButton", function (assert) {
		//prepare
		var oIncrementButton = this.stepInput._getIncrementButton();

		//assert
		assert.equal(oIncrementButton.getNoTabStop(), true, "the button should not be part of tabchain");
		assert.strictEqual(oIncrementButton.getMetadata().getName(), "sap.ui.core.Icon",
			"the picker aggregation holds an instance of an icon");

		assert.notOk(oIncrementButton.getTooltip(), "increment button should not have a tooltip");
		assert.strictEqual(oIncrementButton.getAlt(), oCore.getLibraryResourceBundle("sap.m").getText("STEP_INPUT_INCREASE_BTN") , "Increase icon alt is present");

		//act
		oIncrementButton.firePress();
		this.clock.tick(1000);

		//assert
		assert.strictEqual(this.stepInput.getValue(), 1,
			"The increment button successfully incremented the value");
	});

	QUnit.test("decrementButton", function (assert) {
		//prepare
		var oDecrementButton = this.stepInput._getDecrementButton();

		//assert
		assert.equal(oDecrementButton.getNoTabStop(), true, "the button should not be part of tabchain");
		assert.strictEqual(oDecrementButton.getMetadata().getName(), "sap.ui.core.Icon",
			"the step input _decrementButton aggregation holds an instance of an icon");

		assert.notOk(oDecrementButton.getTooltip(), "decrement button should not have a tooltip");
		assert.strictEqual(oDecrementButton.getAlt(), oCore.getLibraryResourceBundle("sap.m").getText("STEP_INPUT_DECREASE_BTN") , "Decrease icon alt is present");

		//act
		oDecrementButton.firePress();
		this.clock.tick(1000);

		//assert
		assert.strictEqual(this.stepInput.getValue(), -1,
			"The decrement button successfully decremented the value");
	});

	QUnit.test("increase and decrease buttons are created only when actually needed", function (assert) {
		//Prepare and Act
		var oSI = new StepInput({editable: false});
		oSI.placeAt('qunit-fixture');
		oCore.applyChanges();
		//Assert
		assert.equal(oSI._getDecrementButton().getVisible(), false,
			"No decrement button since the StepInput is not editable");
		assert.equal(oSI._getIncrementButton().getVisible(), false,
			"No increment button since the StepInput is not editable");

		//Act
		oSI.setEditable(true);
		oCore.applyChanges();

		//Assert
		assert.ok(oSI._getDecrementButton().getVisible(),
			"Decrement button is available once StepInput is editable");
		assert.ok(oSI._getIncrementButton().getVisible(),
			"Increment button is available once StepInput is editable");

		//destroy
		oSI.destroy();
	});

	QUnit.test("openInputTag is called", function (assert) {
		//Prepare and Act
		var oRenderSpy,
			oSI = new StepInput();

		oSI.placeAt('qunit-fixture');
		oCore.applyChanges();

		oRenderSpy = this.spy(oSI._getInput().getRenderer(), "openInputTag");
		//act
		oSI.setDescription("EUR");
		oCore.applyChanges();

		assert.strictEqual(oRenderSpy.callCount, 1, "openInputTag is called");

		//destroy
		oSI.destroy();
	});

	QUnit.test("closeInputTag is called", function (assert) {
		//Prepare and Act
		var oRenderSpy,
			oSI = new StepInput();

		oSI.placeAt('qunit-fixture');
		oCore.applyChanges();

		oRenderSpy = this.spy(oSI._getInput().getRenderer(), "closeInputTag");
		//act
		oSI.setDescription("EUR");
		oCore.applyChanges();

		//Assert
		assert.strictEqual(oRenderSpy.callCount, 1, "closeInputTag is called");

		//destroy
		oSI.destroy();
	});

	QUnit.test("input", function (assert) {
		//assert
		assert.strictEqual(this.stepInput._getInput().getMetadata().getName(), "sap.m.NumericInput",
			"the input aggregation holds an instance of a numeric input");
		assert.strictEqual(this.stepInput.getValue(), 0, "The default value is correctly set in the input");

		//act
		this.stepInput._getInput().setValue("random string");
		//assert
		assert.strictEqual(this.stepInput.getValue(), 0, "The value is successfully set in the input");

	});

	QUnit.test("writeInnerAttributes is called", function (assert) {
		//Prepare and Act
		var oRenderSpy,
			oSI = new StepInput();

		oSI.placeAt('qunit-fixture');
		oCore.applyChanges();

		oRenderSpy = this.spy(oSI._getInput().getRenderer(), "writeInnerAttributes");
		//act
		oSI.setDescription("EUR");
		oCore.applyChanges();

		//Assert
		assert.strictEqual(oRenderSpy.callCount, 1, "writeInnerAttributes is called");

		//destroy
		oSI.destroy();
	});

	QUnit.test("default textAlign is set to 'End'", function (assert) {
		//assert
		assert.strictEqual(this.stepInput._getInput().getTextAlign(), "End",
			"Text align is 'End'");

		//act
		this.stepInput.setEditable(false);
		//assert
		assert.strictEqual(this.stepInput._getInput().getTextAlign(), "End",
			"Text align is 'End' also when is set to read-only");

		//act
		this.stepInput.setEditable(true);
		this.stepInput.setEnabled(false);
		//assert
		assert.strictEqual(this.stepInput._getInput().getTextAlign(), "End",
			"Text align is 'End' also when the control is disabled");
	});

	QUnit.test("textAlign changed to 'Center'", function (assert) {
		//assert
		this.stepInput.setTextAlign("Center");

		//assert
		assert.strictEqual(this.stepInput.getTextAlign(), "Center", "Step input's textAlign is set to 'Center'");
		assert.strictEqual(this.stepInput._getInput().getTextAlign(), "Center", "Input's text align is 'Center'");

	});

	QUnit.test("textAlign set to 'Center'", function (assert) {
		//Prepare and Act
		var oSI = new StepInput({textAlign: "Center"});

		oSI.placeAt('qunit-fixture');
		oCore.applyChanges();

		//assert
		assert.strictEqual(oSI.getTextAlign(), "Center", "Step input's textAlign is set to 'Center'");
		assert.strictEqual(oSI._getInput().getTextAlign(), "Center", "Input's text align is 'Center'");

		//destroy
		oSI.destroy();
	});

	QUnit.test("setMin", function (assert) {
		//act
		this.stepInput.setMin(undefined);

		//assert
		assert.strictEqual(this.stepInput.getMin(), undefined,
			"Value of undefined is set");

		//act
		this.stepInput.setMin("string");

		//assert
		assert.strictEqual(this.stepInput.getMin(), undefined,
			"Value of 'min' is not changed");

		//prepare
		var oSpyDisableButtons = this.spy(this.stepInput, "_disableButtons");
		//act
		this.stepInput.setMin(9);
		oCore.applyChanges();
		//assert
		assert.equal(oSpyDisableButtons.callCount, 1, "setMin always calls _disableButtons so the buttons state is reflected");
		assert.strictEqual(oSpyDisableButtons.getCall(0).args[2], 9,
			"_disableButtons is called and argument 2 is 'min'");
	});

	QUnit.test("setMax", function (assert) {
		var oSpyDisableButtons;

		//act
		this.stepInput.setMax(undefined);
		//assert
		assert.strictEqual(this.stepInput.getMax(), undefined,
			"Value of undefined is set to the 'max'");

		//act
		this.stepInput.setMax("string");

		//assert
		assert.strictEqual(this.stepInput.getMax(), undefined,
			"Value of 'max' is not changed");

		//prepare
		oSpyDisableButtons = this.spy(this.stepInput, "_disableButtons");
		//Act
		this.stepInput.setMax(20);
		oCore.applyChanges();

		assert.equal(oSpyDisableButtons.callCount, 1,
			"setMax always calls _disableButtons so the buttons state is reflected");
		assert.strictEqual(oSpyDisableButtons.getCall(0).args[1], 20,
			"_disableButtons is called and argument 1 is 'max'");
	});

	QUnit.test("setValue", function (assert) {
		//act
		this.stepInput.setValue(11.23);
		oCore.applyChanges();

		//assert
		assert.equal(typeof this.stepInput.getValue(), typeof this.stepInput._fTempValue, "Temporary value is the same type (number) as real value when value is set as a Number");
		assert.equal(this.stepInput.getValue(), this.stepInput._fTempValue, "Temporary value is the same as real value");

		//act
		this.stepInput.setValue("3.14");
		oCore.applyChanges();

		//assert
		assert.equal(typeof this.stepInput.getValue(), typeof this.stepInput._fTempValue, "Temporary value is the same type (number) as real value when value is set as a String");
		assert.equal(this.stepInput.getValue(), this.stepInput._fTempValue, "Temporary value is the same as real value");
	});

	QUnit.test("setValueState", function (assert) {
		//prepare
		var sValue = ValueState.Error;

		//act
		this.stepInput.setValueState(sValue);
		oCore.applyChanges();

		//assert
		assert.equal(this.stepInput.getValueState(), sValue, "valueState is set to " + sValue);
		assert.equal(this.stepInput._getInput().getValueState(), sValue, "valueState is properly propagated to the input aggregation");
	});

	QUnit.test("setValueState on value change", function (assert) {
		//prepare
		var tempStepInput = new StepInput({
				valueState:"Warning",
				min:10,
				max:20,
				change: function(){
					tempStepInput.setValueState("Error");
					tempStepInput.focus();
		}});

		tempStepInput.placeAt('qunit-fixture');
		oCore.applyChanges();

		//act
		tempStepInput._handleButtonPress(1);
		oCore.applyChanges();

		//assert
		assert.equal(tempStepInput.getValueState(), "Error", "valueState is set to Error");

		//destroy
		tempStepInput.destroy();
	});

	QUnit.test("setValueStateText", function (assert) {
		assert.ok(this.stepInput.setValueStateText, "method setValueStateText exists");

		//prepare
		var sText = "Test Value";

		//act
		this.stepInput.setValueStateText(sText);

		//assert
		assert.equal(this.stepInput.getValueStateText(), sText, "valueStateText is set to " + sText);
		assert.equal(this.stepInput._getInput().getValueStateText(), sText, "valueStateText is properly propagated to the input aggregation");
	});

	QUnit.test("valueState stays the same after updating value", function (assert) {
		//prepare
		this.stepInput.setMin(1).setValue(3).setMax(5);
		oCore.applyChanges();

		//act
		this.stepInput.setValue(6);
		oCore.applyChanges();

		//assert
		assert.equal(this.stepInput.getValueState(), "None", "valueState stays None");
		assert.equal(this.stepInput._getInput().getValueState(), "None", "valueState of the propagated to the input aggregation stays the same");
	});

	QUnit.test("valueState stays the same after updating min", function (assert) {
		//prepare
		this.stepInput.setMin(1).setValue(3).setMax(5);
		oCore.applyChanges();

		//act
		this.stepInput.setMin(4);
		oCore.applyChanges();

		//assert
		assert.equal(this.stepInput.getValueState(), "None", "valueState stays None");
		assert.equal(this.stepInput._getInput().getValueState(), "None", "valueState of the propagated to the input aggregation stays the same");
	});

	QUnit.test("valueState stays the same after updating max", function (assert) {
		//prepare
		this.stepInput.setMin(1).setMax(3).setValue(2);
		oCore.applyChanges();

		//act
		this.stepInput.setMax(2);
		oCore.applyChanges();

		//assert
		assert.equal(this.stepInput.getValueState(), "None", "valueState stays None");
		assert.equal(this.stepInput._getInput().getValueState(), "None", "valueState of the propagated to the input aggregation stays the same");
	});

	QUnit.test('Testing for duplicate id', function (assert) {
		var oStepInput;

		try {
			oStepInput = new StepInput({
				editable: true
			});
			assert.equal(1, 1, "The control is created succesfully");
		} catch (e) {
			assert.equal(1, 0, "Throws an error " + e.stack);
		}

		// destroy
		if (oStepInput) {
			oStepInput.destroy();
		}
	});

	QUnit.test("description", function (assert) {
		var sNumericDescSuffix = "-descr";
		// arrange
		this.stepInput.setDescription("EUR");
		assert.strictEqual(this.stepInput.$("input" + sNumericDescSuffix).length, 0, "Description element is not yet rendered");
		oCore.applyChanges();

		//assert
		assert.strictEqual(this.stepInput._getInput().getDescription(), this.stepInput.getDescription(), "the description is passed to the inner input aggregation");
		assert.strictEqual(this.stepInput.$("input" + sNumericDescSuffix).length, 1, "Description element is inside the DOM");
	});

	QUnit.test("fieldWidth", function (assert) {
		assert.strictEqual(this.stepInput.$().find(".sapMInputBaseContentWrapper")[0].style.width, "100%", "field width is set to 100% if the description is not set");

		// arrange
		this.stepInput.setDescription("EUR");
		oCore.applyChanges();
		//assert
		assert.strictEqual(this.stepInput.$().find(".sapMInputBaseContentWrapper")[0].style.width, "50%", "field width by default is 50%");
		// arrange
		this.stepInput.setFieldWidth("70%");
		oCore.applyChanges();

		assert.strictEqual(this.stepInput._getInput().getFieldWidth(), this.stepInput.getFieldWidth(), "the fieldWidth is passed to the inner input aggregation");
		assert.strictEqual(this.stepInput.$().find(".sapMInputBaseContentWrapper")[0].style.width, "70%", "field width of 70% is correctly set to the Input wraper");
	});

	QUnit.test("validationMode", function (assert) {
		assert.strictEqual(this.stepInput.getValidationMode(), StepInputValidationMode.FocusOut, "validation mode is set to 'FocusOut' by default");

		// arrange
		this.oLiveChangeSpy = sinon.spy(this.stepInput, "_attachLiveChange");
		this.oDettachLiveChangeSpy = sinon.spy(this.stepInput, "_detachLiveChange");
		this.stepInput.setValidationMode(StepInputValidationMode.LiveChange);
		oCore.applyChanges();

		//assert
		assert.strictEqual(this.stepInput.getValidationMode(), StepInputValidationMode.LiveChange, "validation mode is set to 'LiveChange'");
		assert.strictEqual(this.oLiveChangeSpy.callCount, 1, "'LiveChange' is attached to the StepInput");
		assert.strictEqual(this.oDettachLiveChangeSpy.callCount, 0, "Detach of 'LiveChange' is not called");

		// arrange
		this.stepInput.setValidationMode(StepInputValidationMode.FocusOut);
		oCore.applyChanges();

		//assert
		assert.strictEqual(this.stepInput.getValidationMode(), StepInputValidationMode.FocusOut, "validation mode is set to 'FocusOut'");
		assert.strictEqual(this.oDettachLiveChangeSpy.callCount, 1, "'LiveChange' is detached (_dettachLiveChange is called once)");
		assert.strictEqual(this.oLiveChangeSpy.callCount, 1, "'LiveChange' is not called again");
	});

	QUnit.test("StepMode.MultiplicationAndDivision: value state when value does not fold by step", function (assert) {
		//Prepare
		this.stepInput.setStepMode(StepMode.Multiple);
		this.stepInput.setStep(5);
		this.stepInput.setValue(2);
		oCore.applyChanges();

		//Act
		this.stepInput._verifyValue();

		//Assert
		assert.equal(this.stepInput.getValueState(), "Error", "..should be set to 'Error'");
	});

	// BCP: 2070257404
	QUnit.test("no complete re-rendering when the internal control changes on live change", function(assert) {
		// arrange
		var oInnerInput = this.stepInput._getInput(),
			oDomRef1 = oInnerInput.getDomRef(),
			oDomRef2;

		oInnerInput.attachLiveChange(function() {
			this.stepInput.setValueState(ValueState.Information);
		}.bind(this));

		// act
		oInnerInput.fireLiveChange({ value: "0", newValue: "5" });
		oCore.applyChanges();

		oDomRef2 = oInnerInput.getDomRef();

		// assert
		assert.ok(oDomRef1 == oDomRef2, "no complete re-rendering");
	});

	QUnit.module("Operations", {
		beforeEach: function () {
			this.stepInput = new StepInput({
				value: 3,
				max: 10
			});

			this.stepInput.placeAt('qunit-fixture');
			oCore.applyChanges();
		},
		afterEach: function () {
			if (!bSkipDestroy) {
				this.stepInput.destroy();
			}
		}
	});


	QUnit.test("Disable button based on the value", function (assert) {
		//assert
		assert.strictEqual(this.stepInput._getIncrementButton().$().hasClass("sapMStepInputIconDisabled"), false,
			"The increment button is enabled because the value fits to limits");
		assert.strictEqual(this.stepInput._getDecrementButton().$().hasClass("sapMStepInputIconDisabled"), false,
			"The decrement button is enabled because the value fits to limits");

		//act
		this.stepInput.setMin(3);
		oCore.applyChanges();

		//assert
		assert.strictEqual(this.stepInput._getDecrementButton().$().hasClass("sapMStepInputIconDisabled"), true,
			"The decrement button is disabled because there's min and min = value");

		//act
		this.stepInput.setValue(2);
		oCore.applyChanges();
		//assert
		assert.strictEqual(this.stepInput._getDecrementButton().$().hasClass("sapMStepInputIconDisabled"), true,
			"The decrement button is disabled because value < min");

		//act
		this.stepInput.setValue(4);
		oCore.applyChanges();
		//assert
		assert.strictEqual(this.stepInput._getDecrementButton().$().hasClass("sapMStepInputIconDisabled"), false,
			"The decrement button is enabled because the value > min");

		//act
		this.stepInput.setEnabled(false);
		oCore.applyChanges();
		//assert
		assert.strictEqual(this.stepInput._getIncrementButton().$().hasClass("sapMStepInputIconDisabled"), true,
			"The increment button is disabled because setEnabled is set to false");

		//act
		this.stepInput.setEnabled(true);
		oCore.applyChanges();
		//assert
		assert.strictEqual(this.stepInput._getIncrementButton().$().hasClass("sapMStepInputIconDisabled"), false,
			"The increment button is enabled because setEnabled is set to true");
		assert.strictEqual(this.stepInput._getDecrementButton().$().hasClass("sapMStepInputIconDisabled"), false,
			"The decrement button is enabled because setEnabled is set to true");
	});

	QUnit.test("Error state when setting values out of the limit", function (assert) {
		var oInput = this.stepInput._getInput();

		//act
		this.stepInput.setMin(3);
		oInput.focus();
		oInput.setValue(2);
		oInput.$().trigger("blur");
		this.stepInput._change();
		oCore.applyChanges();

		//assert
		assert.strictEqual(this.stepInput._getInput().getValueState(), "Error",
			"The value state is Error as it should be because the value is under the limit");

		//act
		//value becomes 3
		this.stepInput._getIncrementButton().firePress();
		oCore.applyChanges();

		//assert
		assert.strictEqual(this.stepInput._getInput().getValueState(), "None",
			"The value state is None as it should be because the value is equal to the min");
	});

	QUnit.test("_shouldDisableDecrementButton should work with valid and invalid values for min", function (assert) {
		assert.ok(this.stepInput._shouldDisableDecrementButton(1, 1), "Should disable decrement button if min and value are equal");
		assert.ok(this.stepInput._shouldDisableDecrementButton(1, 2), "Should disable decrement button if min is more than value");
		assert.notOk(this.stepInput._shouldDisableDecrementButton(1, undefined), "Should NOT disable decrement button if min value is 'undefined'");
	});

	QUnit.test("_shouldDisableIncrementButton should work with valid and invalid values for max", function (assert) {
		assert.ok(this.stepInput._shouldDisableIncrementButton(1, 1), "Should disable increment button if max and value are equal");
		assert.ok(this.stepInput._shouldDisableIncrementButton(1, -1), "Should disable increment button if max is less than value");
		assert.notOk(this.stepInput._shouldDisableIncrementButton(1, undefined), "Should NOT disable increment button if max value is 'undefined'");
	});


	QUnit.module("Floating point", {
		beforeEach: function () {
			this.stepInput = new StepInput({
				step: 1.1,
				max: 2,
				displayValuePrecision: 1
			});

			this.stepInput.placeAt('qunit-fixture');
			oCore.applyChanges();
		},
		afterEach: function () {
			if (!bSkipDestroy) {
				this.stepInput.destroy();
			}
		}
	});

	QUnit.test("working with floating point", function (assert) {
		//prepare & act
		var oIncrementButton = this.stepInput._getIncrementButton(),
			oDecrementButton = this.stepInput._getDecrementButton();
		oIncrementButton.firePress();
		oCore.applyChanges();

		//assert
		assert.strictEqual(this.stepInput.getValue(), 1.1, "The value is successfuly incremented");
		oDecrementButton.firePress();
		oCore.applyChanges();
		assert.strictEqual(this.stepInput.getValue(), 0,
			"The value is successfuly decremented");

		//act
		qutils.triggerKeydown(this.stepInput.getDomRef(), KeyCodes.ARROW_UP);
		this.clock.tick(1000);

		//assert
		assert.strictEqual(this.stepInput.getAggregation("_input")._getInputValue(), "1.1",
			"The input's value is successfully incremented");

		//act
		qutils.triggerKeydown(this.stepInput.getDomRef(), KeyCodes.ARROW_DOWN);
		this.clock.tick(1000);

		//assert
		assert.strictEqual(this.stepInput.getAggregation("_input")._getInputValue(), "0.0",
			"The value is successfully decremented");

		//act
		oIncrementButton.firePress();
		oIncrementButton.firePress();
		oCore.applyChanges();

		//assert
		assert.strictEqual(this.stepInput.getValue(), 2,
			"The value is successfully decremented");
	});

	QUnit.test("working with floating point value precision set to 2", function (assert) {
		this.stepInput.setDisplayValuePrecision(2);
		this.stepInput.setValue(431.15);
		this.stepInput.setMin(410.00);
		this.stepInput.setMax(440.00);
		this.stepInput.setStep(0.05);
		oCore.applyChanges();

		var oIncrementButton = this.stepInput._getIncrementButton(),
			oDecrementButton = this.stepInput._getDecrementButton();

		oIncrementButton.firePress();
		oCore.applyChanges();
		assert.strictEqual(this.stepInput.getValue(), 431.2,
			"The value is successfuly incremented");
		oDecrementButton.firePress();
		oCore.applyChanges();
		assert.strictEqual(this.stepInput.getValue(), 431.15,
			"The value is successfuly decremented");

		qutils.triggerKeydown(this.stepInput.getDomRef(), KeyCodes.ARROW_UP);
		this.clock.tick(1000);
		assert.strictEqual(this.stepInput.getAggregation("_input")._getInputValue(), "431.20",
			"The input's value is successfully incremented");
		qutils.triggerKeydown(this.stepInput.getDomRef(), KeyCodes.ARROW_DOWN);
		this.clock.tick(1000);
		assert.strictEqual(this.stepInput.getAggregation("_input")._getInputValue(), "431.15",
			"The input's value is successfully decremented");
	});

	QUnit.test("working with floating point value precision set to 20", function (assert) {
		this.stepInput.setDisplayValuePrecision(20);
		this.stepInput.setValue(0.01);
		this.stepInput.setStep(0.06);
		oCore.applyChanges();
		var oIncrementButton = this.stepInput._getIncrementButton();

		oIncrementButton.firePress();
		assert.strictEqual(this.stepInput.getValue(), 0.07, "The value is successfuly incremented");
		assert.strictEqual(this.stepInput.getAggregation("_input")._getInputValue(), "0.07000000000000000000", "The input's value is successfully incremented");
	});

	QUnit.test("working with floating point value precision set to 17", function (assert) {
		this.stepInput.setDisplayValuePrecision(17);
		this.stepInput.setValue(0.01);
		this.stepInput.setStep(0.02);
		oCore.applyChanges();
		var oIncrementButton = this.stepInput._getIncrementButton();

		oIncrementButton.firePress();
		assert.strictEqual(this.stepInput.getValue(), 0.03, "The value is successfuly incremented");
		assert.strictEqual(this.stepInput.getAggregation("_input")._getInputValue(), "0.03000000000000000", "The input's value is successfully incremented");
	});

	QUnit.test("setting default displayValuePrecision when it is not correct", function (assert) {
		this.stepInput.setDisplayValuePrecision(25);
		oCore.applyChanges();

		assert.strictEqual(this.stepInput.getDisplayValuePrecision(), 0, "The displayValuePrecision is set to 0");
	});

	QUnit.test("setting default displayValuePrecision when it is not correct and can't be converted to int", function (assert) {
		this.stepInput.setDisplayValuePrecision("test");
		oCore.applyChanges();

		assert.strictEqual(this.stepInput.getDisplayValuePrecision(), 0, "The displayValuePrecision is set to 0");
	});

	QUnit.test("setting default displayValuePrecision when it is undefined", function (assert) {
		this.stepInput.setDisplayValuePrecision();
		oCore.applyChanges();

		assert.strictEqual(this.stepInput.getDisplayValuePrecision(), 0, "The displayValuePrecision is set to 0");
	});

	QUnit.test("setting displayValuePrecision to different values (including 0)", function (assert) {
		var oInput = this.stepInput._getInput();

		// act
		this.stepInput.setMax(20);
		this.stepInput.setDisplayValuePrecision(0);
		this.stepInput.placeAt('content');
		oCore.applyChanges();

		// assert
		assert.strictEqual(this.stepInput.getDisplayValuePrecision(), 0, "The displayValuePrecision is set to 0");

		// act
		oInput.onfocusin();
		oInput._$input.trigger("focus").val("3.12").trigger("input");
		this.clock.tick(300);
		oCore.applyChanges();

		// assert
		assert.strictEqual(oInput.getValue(), "3", "The value is proper (3)");

		// act
		this.stepInput.setDisplayValuePrecision(2);
		oCore.applyChanges();
		oInput.onfocusin();
		oInput._$input.trigger("focus").val("6.1267").trigger("input");
		this.clock.tick(300);
		oCore.applyChanges();

		// assert
		assert.strictEqual(oInput.getValue(), "6.12", "The value is proper (6.12)");

		// act
		this.stepInput.setDisplayValuePrecision(3);
		oCore.applyChanges();
		oInput.onfocusin();
		oInput._$input.trigger("focus").val("9.12588").trigger("input");
		this.clock.tick(300);
		oCore.applyChanges();

		// assert
		assert.strictEqual(oInput.getValue(), "9.125", "The value is proper (9.125)");

	});

	QUnit.test("displayValuePrecision formating when digits after the dot are more than the value precision", function (assert) {
		this.stepInput.setDisplayValuePrecision(2);
		this.stepInput.setValue(1.104);
		oCore.applyChanges();

		assert.strictEqual(this.stepInput.getValue(), 1.104, "The value is formated correctly");
		assert.strictEqual(this.stepInput.getAggregation("_input")._getInputValue(), "1.10", "The input value is correctly formatted");
	});

	QUnit.test("displayValuePrecision formating when digits after the dot are less than the value precision", function (assert) {
		this.stepInput.setDisplayValuePrecision(5);
		this.stepInput.setValue(1.104);
		oCore.applyChanges();

		assert.strictEqual(this.stepInput.getValue(), 1.104, "The value is formated correctly");
		assert.strictEqual(this.stepInput.getAggregation("_input")._getInputValue(), "1.10400", "The input value is correctly formatted");
	});

	QUnit.test("displayValuePrecision formating when there are no digits after the dot and the value precision is bigger than 0", function (assert) {
		this.stepInput.setDisplayValuePrecision(5);
		this.stepInput.setValue(0);
		oCore.applyChanges();

		assert.strictEqual(this.stepInput.getValue(), 0, "The value is formated correctly");
		assert.strictEqual(this.stepInput.getAggregation("_input")._getInputValue(), "0.00000", "The input value is correctly formatted");
	});

	QUnit.test("displayValuePrecision formating when there are digits after the dot and the value precision is 0", function (assert) {
		this.stepInput.setDisplayValuePrecision(0);
		this.stepInput.setValue(1.325);
		oCore.applyChanges();

		assert.strictEqual(this.stepInput.getValue(), 1.325, "The value is formated correctly");
		assert.strictEqual(this.stepInput.getAggregation("_input")._getInputValue(), "1", "The input value is correctly formatted");
	});

	QUnit.test("Formatting when displayValuePrecision is equal to the step precision", function (assert) {
		this.stepInput.setDisplayValuePrecision(4);
		this.stepInput.setStep(1.0035);
		this.stepInput.setValue(0.325);
		oCore.applyChanges();

		var oIncrementButton = this.stepInput._getIncrementButton();

		oIncrementButton.firePress();

		assert.strictEqual(this.stepInput.getValue(), 1.3285, "The value is formated correctly");
		assert.strictEqual(this.stepInput.getAggregation("_input")._getInputValue(), "1.3285", "The input value is correctly formatted");
	});

	QUnit.test("Formatting when displayValuePrecision is smaller than the step precision", function (assert) {
		this.stepInput.setDisplayValuePrecision(2);
		this.stepInput.setStep(1.0035);
		this.stepInput.setValue(0.325);
		oCore.applyChanges();

		var oIncrementButton = this.stepInput._getIncrementButton();

		oIncrementButton.firePress();

		assert.strictEqual(this.stepInput.getValue(), 1.3285, "The value is formated correctly");
		assert.strictEqual(this.stepInput.getAggregation("_input")._getInputValue(), "1.33", "The input value is correctly formatted");
	});

	QUnit.test("Formatting when displayValuePrecision is bigger than the step precision", function (assert) {
		this.stepInput.setDisplayValuePrecision(6);
		this.stepInput.setStep(1.0035);
		this.stepInput.setValue(0.325);
		oCore.applyChanges();

		var oIncrementButton = this.stepInput._getIncrementButton();

		oIncrementButton.firePress();

		assert.strictEqual(this.stepInput.getValue(), 1.3285, "The value is formated correctly");
		assert.strictEqual(this.stepInput.getAggregation("_input")._getInputValue(), "1.328500", "The input value is correctly formatted");
	});

	QUnit.test("Formatting when displayValuePrecision is equal to the step precision, but the given value is with smaller precision than the displayValuePrecision", function (assert) {
		var oSI = new StepInput({
			value: 431.5,
			min: 410.00,
			max: 440.00,
			step: 0.05,
			displayValuePrecision: 2
		});

		oSI.placeAt('qunit-fixture');
		oCore.applyChanges();

		assert.strictEqual(oSI.getValue(), 431.5, "The value is formated correctly");
		assert.strictEqual(oSI.getAggregation("_input")._getInputValue(), "431.50", "The input value is correctly formatted");

		oSI.destroy();
	});

	QUnit.test("Formatting when we have two different controls with values with different precision, but which will be resolved to one and the same display value", function (assert) {
		var oSI = new StepInput({
				value: 1.3267,
				step: 0.004999,
				displayValuePrecision: 3
			}),
			oIncBtn = oSI._getIncrementButton(),
			oSI2 = new StepInput({
				value: 1.326699999999999,
				step: 0.00499999999999,
				displayValuePrecision: 3
			}),
			oIncBtn2 = oSI2._getIncrementButton();

		oSI.placeAt('qunit-fixture');
		oSI2.placeAt('qunit-fixture');
		oCore.applyChanges();

		assert.strictEqual(oSI.getValue(), 1.3267, "The value is formatted correctly");
		assert.strictEqual(oSI.getAggregation("_input")._getInputValue(), "1.327", "The input value is rounded to the given displayValuePrecision");

		oIncBtn.firePress();

		assert.strictEqual(oSI.getValue(), 1.331699, "The value is formatted correctly");
		assert.strictEqual(oSI.getAggregation("_input")._getInputValue(), "1.332", "The input value is correctly formatted");

		//second test
		assert.strictEqual(oSI2.getValue(), 1.326699999999999, "The value is formated correctly");
		assert.strictEqual(oSI2.getAggregation("_input")._getInputValue(), "1.327", "The input value is rounded to the given displayValuePrecision");

		oIncBtn2.firePress();

		assert.strictEqual(oSI2.getValue(), 1.331699999999989, "The value is formated correctly");
		assert.strictEqual(oSI2.getAggregation("_input")._getInputValue(), "1.332", "The input value is correctly formatted");

		oSI.destroy();
		oSI2.destroy();
	});

	QUnit.test("entering a dot('.') as a separator does not clear the entry value", function (assert) {
		// Prepare
		var oEvent = {
			getParameter : function () {
				return "";
			}
		}, oUpdateValueSpy;
		this.stepInput.setValue(1);
		oUpdateValueSpy = this.spy(this.stepInput._getInput(), "updateDomValue");

		// Act
		this.stepInput._getInput().$("inner").val("");
		this.stepInput._inputLiveChangeHandler.call(this.stepInput._getInput(), oEvent);

		// Assert
		assert.strictEqual(oUpdateValueSpy.callCount, 0, "The value is not updated as only a separator is added");

		// Clean
		oUpdateValueSpy.restore();
	});

	QUnit.test("Correct value is set after changing the displayValuePrecision", function (assert) {
		// Prepare
		var oStepInput = new StepInput({
			value: 0
		}).placeAt("qunit-fixture");
		oCore.applyChanges();

		// Act
		oStepInput.setValue(1.23);
		oCore.applyChanges();
		oStepInput.setDisplayValuePrecision(1);
		oCore.applyChanges();

		// Assert
		assert.equal(oStepInput.getAggregation("_input")._getInputValue(), "1.2", "The input is set correctly");

		// Clean up
		oStepInput.destroy();
	});

	QUnit.module("Keyboard Handling", {
		beforeEach: function () {
			this.oChangeSpy = this.spy();
			this.stepInput = new StepInput({
				value: 4,
				max: 10,
				min: -4,
				change: this.oChangeSpy
			});

			this.stepInput.placeAt('qunit-fixture');
			oCore.applyChanges();
		},
		afterEach: function () {
			if (!bSkipDestroy) {
				this.stepInput.destroy();
			}
		}
	});

	QUnit.test("Value is changed upon Enter press", function (assert) {
		//prepare
		var oInput = this.stepInput._getInput();

		//act
		jQuery(this.stepInput).trigger("focus");
		oInput.$("inner").val(7);

		//assert
		assert.strictEqual(this.stepInput.getValue(), 4, "Value should not be changed because ENTER is not pressed yet");
		assert.equal(this.oChangeSpy.callCount, 0, "Change Event should not be called");

		//act
		qutils.triggerKeydown(oInput.getDomRef(), KeyCodes.ENTER);

		//assert
		assert.equal(this.stepInput.getValue(), 7, "Value should be changed due to ENTER pressed");
		this.clock.tick(1000); //needed because the change event is fired inside a timeout callback
		assert.equal(this.oChangeSpy.callCount, 1, "Change Event should be called once");
	});

	QUnit.test("Change event is fired only once when element is focused, changed and then + button is clicked", function (assert) {
		//prepare
		var oInput = this.stepInput._getInput(),
			oIncrementBtn = this.stepInput._getIncrementButton();

		//assert
		assert.strictEqual(this.stepInput.getValue(), 4, "Value should not be changed because ENTER is not pressed yet"); //value not changed
		assert.equal(this.oChangeSpy.callCount, 0, "Change Event should not be called");

		//act
		oInput.focus();
		oInput.$("inner").val(7);
		//the following reacts on value change of the Input
		this.stepInput._checkInputValue();

		//No way to simulate real click on the “+” button, so make sure the same event handlers are called in the same order
		// we can't focus on the IncrementButton since it doesn't have tab index therefore we call blur on the Input
		oIncrementBtn.focus();
		this.clock.tick(0);
		oIncrementBtn.firePress();

		//assert
		assert.equal(this.stepInput.getValue(), 8, "Value should be changed from 7 to 8 due to increment button pressed");
		assert.equal(this.oChangeSpy.callCount, 1, "Change Event should be called once");
	});

	QUnit.test("Change event is fired only once when element is focused, changed and then - button is clicked", function (assert) {
		//prepare
		var oInput = this.stepInput._getInput(),
			oDecrementBtn = this.stepInput._getDecrementButton();

		//assert
		assert.strictEqual(this.stepInput.getValue(), 4, "Value should not be changed because ENTER is not pressed yet"); //value not changed
		assert.equal(this.oChangeSpy.callCount, 0, "Change Event should not be called");

		//act
		oInput.focus();
		oInput.$("inner").val(7);
		//the following reacts on value change of the Input
		this.stepInput._checkInputValue();

		//No way to simulate real click on the “-” button, so make sure the same event handlers are called in the same order
		// we can't focus on the IncrementButton since it doesn't have tab index therefore we call blur on the Input
		oDecrementBtn.focus();
		this.clock.tick(0);
		oDecrementBtn.firePress();

		//assert
		assert.equal(this.stepInput.getValue(), 6, "Value should be changed from 7 to 6 due to decrement button pressed");
		assert.equal(this.oChangeSpy.callCount, 1, "Change Event should be called once");
	});

	QUnit.test("Change Event is fired when  +/- buttons are clicked", function (assert) {
		//prepare
		var oIncrementBtn = this.stepInput._getIncrementButton(),
			oDecrementBtn = this.stepInput._getDecrementButton();

		//act
		jQuery(this.stepInput).trigger("focus");
		oIncrementBtn.firePress();

		//assert
		assert.equal(this.stepInput.getValue(), 5, "Value should be changed 5 due to increment button pressed");
		assert.equal(this.oChangeSpy.callCount, 1, "Change Event should be called once");
		this.oChangeSpy.resetHistory();

		//act
		oDecrementBtn.firePress();

		//assert
		assert.equal(this.stepInput.getValue(), 4, "Value should be changed 4 due to decrement button pressed");
		assert.equal(this.oChangeSpy.callCount, 1, "Change Event should be called once");
	});

	QUnit.test("Change event is fired only once on focus out, when writing inside the input and then leaving the field", function (assert) {
		//prepare
		var oInput = this.stepInput._getInput();

		//assert
		assert.strictEqual(this.stepInput.getValue(), 4, "Value should not be changed"); //value not changed
		assert.equal(this.oChangeSpy.callCount, 0, "Change Event should not be called");

		//act
		oInput.focus();
		oInput.$("inner").val(7);
		qutils.triggerKeydown(oInput.getDomRef(), KeyCodes.ENTER);

		this.clock.tick(1000);

		//assert
		assert.equal(this.stepInput.getValue(), 7, "Value should be changed from 4 to 7 due to typing inside the input");
		assert.equal(this.oChangeSpy.callCount, 1, "Change Event should be called once");
	});

	QUnit.test("Change event is not fired on focus out, when nothing is changed inside the input", function (assert) {
		//prepare
		var oInput = this.stepInput._getInput();

		// focus the field
		jQuery(this.stepInput).trigger("focus");

		//assert
		assert.strictEqual(this.stepInput.getValue(), 4, "Value should not be changed"); //value not changed
		assert.equal(this.oChangeSpy.callCount, 0, "Change Event should not be called");

		//act
		oInput.$("inner").trigger("blur");

		//assert
		assert.equal(this.stepInput.getValue(), 4, "After focus out value should not be changed");
		assert.equal(this.oChangeSpy.callCount, 0, "After focus out Change Event should not be called");
	});

	QUnit.test("up/down increases/decreases the value", function (assert) {
		//act
		qutils.triggerKeydown(this.stepInput.getDomRef(), KeyCodes.ARROW_UP);
		this.clock.tick(1000);

		//assert
		assert.equal(this.stepInput.getAggregation("_input")._getInputValue(), 5,
			"The input's value is increasing with 1 after arrow up");

		//act
		qutils.triggerKeydown(this.stepInput.getDomRef(), KeyCodes.ARROW_DOWN);
		this.clock.tick(1000);

		//assert
		assert.equal(this.stepInput.getAggregation("_input")._getInputValue(), 4,
			"The input's value is decreasing with 1 after arrow down");

		//act
		this.stepInput.setStep(5);
		this.clock.tick(1000);
		qutils.triggerKeydown(this.stepInput.getDomRef(), KeyCodes.ARROW_UP);
		this.clock.tick(1000);

		//assert
		assert.equal(this.stepInput.getAggregation("_input")._getInputValue(), 9,
			"The input's value is decreasing with 5 after arrow up");
	});

	QUnit.test("up/down increases/decreases the value when in warning value state", function (assert) {
		//arrange
		this.stepInput.setValueState("Warning");
		oCore.applyChanges();

		//act
		qutils.triggerKeydown(this.stepInput.getDomRef(), KeyCodes.ARROW_UP);
		this.clock.tick(1000);

		//assert
		assert.equal(this.stepInput.getAggregation("_input")._getInputValue(), 5,
			"The input's value is increasing with 1 after arrow up");

		//arrange
		this.stepInput.setValueState("Warning");

		//act
		qutils.triggerKeydown(this.stepInput.getDomRef(), KeyCodes.ARROW_DOWN);
		this.clock.tick(1000);

		//assert
		assert.equal(this.stepInput.getAggregation("_input")._getInputValue(), 4,
			"The input's value is decreasing with 1 after arrow down");

		//arrange
		this.stepInput.setValueState("Warning");

		//act
		this.stepInput.setStep(5);
		this.clock.tick(1000);
		qutils.triggerKeydown(this.stepInput.getDomRef(), KeyCodes.ARROW_UP);
		this.clock.tick(1000);

		//assert
		assert.equal(this.stepInput.getAggregation("_input")._getInputValue(), 9,
			"The input's value is decreasing with 5 after arrow up");
	});

	QUnit.test("up/down/pageup/pagedown doesn't increase/decrease the value when control is not editable", function (assert) {
		//prepare
		var oFakeEvent = {
				preventDefault: function() {},
				setMarked: function() {}
			},
			oSpy = this.spy(this.stepInput, "_changeValueWithStep");

		this.stepInput.setEditable(false);
		oCore.applyChanges();

		//act
		this.stepInput.onsapup(oFakeEvent);
		this.stepInput.onsapdown(oFakeEvent);
		this.stepInput.onsappageup(oFakeEvent);
		this.stepInput.onsappagedown(oFakeEvent);

		//assert
		assert.ok(oSpy.notCalled, "Input value is not changed");

		//clean
		oSpy.restore();
	});

	QUnit.test("if the value is out of min/max range, pressing increase/decrease button the value will be set to min/max", function (assert) {
		//prepare
		var oIncrementBtn = this.stepInput._getIncrementButton(),
			oDecrementBtn = this.stepInput._getDecrementButton();

		//act
		jQuery(this.stepInput).trigger("focus");
		this.stepInput._getInput().setValue(-7);
		oIncrementBtn.firePress();

		//assert
		assert.equal(this.stepInput.getValue(), -4,
			"The input's value is restored to the min value -4 after it was set to -7 and increment button was pressed");

		//act
		this.stepInput._getInput().setValue(13);
		oDecrementBtn.firePress();

		//assert
		assert.equal(this.stepInput.getValue(), 10,
			"The input's value is restored to max value 10 after it was set to 13 and decrement button was pressed");
	});

	QUnit.test("if the value is out of min/max range, pressing up/down arrow the value will be set to min/max", function (assert) {
		//act
		this.stepInput.setValue(-7);
		oCore.applyChanges();
		qutils.triggerKeydown(this.stepInput.getDomRef(), KeyCodes.ARROW_UP);
		this.clock.tick(1000);

		//assert
		assert.equal(this.stepInput.getAggregation("_input")._getInputValue(), -4,
			"The input's value is restored to the min value -4 after it was set to -7 and arrow up was pressed");

		//act
		this.stepInput.setValue(13);
		oCore.applyChanges();
		qutils.triggerKeydown(this.stepInput.getDomRef(), KeyCodes.ARROW_DOWN);
		this.clock.tick(1000);

		//assert
		assert.equal(this.stepInput.getAggregation("_input")._getInputValue(), 10,
			"The input's value is restored to max value 10 after it was set to 13 and arrow down was pressed");
	});

	QUnit.test("if the value is out of min/max range, pressing up/down arrow + alt the value will be set to min/max", function (assert) {
		//act
		this.stepInput.setValue(-7);
		oCore.applyChanges();
		qutils.triggerKeydown(this.stepInput.getDomRef(), KeyCodes.ARROW_UP, false, true);
		this.clock.tick(1000);

		//assert
		assert.equal(this.stepInput.getAggregation("_input")._getInputValue(), -4,
				"The input's value is restored to the min value -4 after it was set to -7 and arrow up + alt was pressed");

		//act
		this.stepInput.setValue(13);
		oCore.applyChanges();
		qutils.triggerKeydown(this.stepInput.getDomRef(), KeyCodes.ARROW_DOWN, false, true);
		this.clock.tick(1000);

		//assert
		assert.equal(this.stepInput.getAggregation("_input")._getInputValue(), 10,
				"The input's value is restored to max value 10 after it was set to 13 and arrow down + alt was pressed");
	});

	QUnit.test("if the value is out of min/max range, pressing up/down arrow + ctrl the value will be set to min/max", function (assert) {
		//act
		this.stepInput.setValue(-7);
		oCore.applyChanges();
		qutils.triggerKeydown(this.stepInput.getDomRef(), KeyCodes.ARROW_UP, false, false, true);
		this.clock.tick(1000);

		//assert
		assert.equal(this.stepInput.getAggregation("_input")._getInputValue(), -4,
				"The input's value is restored to the min value -4 after it was set to -7 and arrow up + ctrl was pressed");

		//act
		this.stepInput.setValue(13);
		oCore.applyChanges();
		qutils.triggerKeydown(this.stepInput.getDomRef(), KeyCodes.ARROW_DOWN, false, false, true);
		this.clock.tick(1000);

		//assert
		assert.equal(this.stepInput.getAggregation("_input")._getInputValue(), 10,
				"The input's value is restored to max value 10 after it was set to 13 and arrow down + ctrl was pressed");
	});

	QUnit.test("pageup and pagedown increases/decreases the value with a larger step", function (assert) {
		var oSpyPageUp = this.spy(this.stepInput, "onsappageup"),
			oSpyPageDown = this.spy(this.stepInput, "onsappagedown");
		//act
		qutils.triggerKeydown(this.stepInput.getDomRef(), KeyCodes.PAGE_UP);
		this.clock.tick(1000);

		//assert
		assert.equal(oSpyPageUp.callCount, 1, "page up is called once");
		assert.equal(this.stepInput.getAggregation("_input")._getInputValue(), 6, "The input's value is increasing with step=step*2 after pageup");

		//act
		qutils.triggerKeydown(this.stepInput.getDomRef(), KeyCodes.PAGE_DOWN);
		this.clock.tick(1000);

		//assert
		assert.equal(oSpyPageDown.callCount, 1, "page down is called once");
		assert.equal(this.stepInput.getAggregation("_input")._getInputValue(), 4,
			"The input's value is decreasing with step=step*2 after pagedown");

		//act
		this.stepInput.setStep(5);
		this.clock.tick(1000);
		qutils.triggerKeydown(this.stepInput.getDomRef(), KeyCodes.PAGE_UP);
		this.clock.tick(1000);

		//assert
		assert.equal(oSpyPageUp.callCount, 2, "page up is called second time");
		assert.equal(Number(this.stepInput.getAggregation("_input")._getInputValue()), 10,
			"The input's value is decreasing with step=step*2 after pageup");
	});

	QUnit.test("shift+up/down increases/decreases the value with a larger step if specified", function (assert) {
		//act
		qutils.triggerKeydown(this.stepInput.getDomRef(), KeyCodes.ARROW_UP, true, false, false);
		this.clock.tick(1000);

		//assert
		assert.equal(this.stepInput.getAggregation("_input")._getInputValue(), 6,
			"The input's value is increasing with step=2*step after arrow up");

		//act
		qutils.triggerKeydown(this.stepInput.getDomRef(), KeyCodes.ARROW_DOWN, true, false, false);
		this.clock.tick(1000);

		//assert
		assert.equal(this.stepInput.getAggregation("_input")._getInputValue(), 4,
			"The input's value is decreasing with step=1 after arrow down");

		//act
		this.stepInput.setStep(5);
		this.clock.tick(1000);
		qutils.triggerKeydown(this.stepInput.getDomRef(), KeyCodes.ARROW_UP, true, false, false);
		this.clock.tick(1000);

		//assert
		assert.equal(Number(this.stepInput.getAggregation("_input")._getInputValue()), 10,
			"The input's value is decreasing with step=2*step after arrow up");
	});

	QUnit.test("shift+pageup/pagedown sets value to max/min", function (assert) {
		//act
		qutils.triggerKeydown(this.stepInput.getDomRef(), KeyCodes.PAGE_UP, true, false, false);
		this.clock.tick(1000);

		//assert
		assert.equal(this.stepInput.getAggregation("_input")._getInputValue(), 10,
			"The input's value is set to max after page up");

		//act
		qutils.triggerKeydown(this.stepInput.getDomRef(), KeyCodes.PAGE_DOWN, true, false, false);
		this.clock.tick(1000);

		//asser
		assert.equal(this.stepInput.getAggregation("_input")._getInputValue(), -4,
			"The input's value is set to min after page down");
	});

	QUnit.test("ctrl+shift+up/down sets value to max/min", function (assert) {
		//act
		qutils.triggerKeydown(this.stepInput.getDomRef(), KeyCodes.ARROW_UP, true, false, true);
		this.clock.tick(1000);

		//assert
		assert.equal(this.stepInput.getAggregation("_input")._getInputValue(), 10,
			"The input's value is set to max after page up");

		//act
		qutils.triggerKeydown(this.stepInput.getDomRef(), KeyCodes.ARROW_DOWN, true, false, true);
		this.clock.tick(1000);

		//assert
		assert.equal(this.stepInput.getAggregation("_input")._getInputValue(), -4,
			"The input's  value is set to min after page down");
	});

	QUnit.test("mousewheel up/down increases/decreases the value", function (assert) {
		//arrange
		var bFirefox = Device.browser.firefox;
		var sWheelEventType = bFirefox ? "DOMMouseScroll" : "mousewheel";
		var oWheelUpEvent = jQuery.Event(sWheelEventType, { originalEvent: { detail: bFirefox ? -1 : 0, wheelDelta: 13 } });
		var oWheelDownEvent = jQuery.Event(sWheelEventType, { originalEvent: { detail: bFirefox ? 1 : 0 , wheelDelta: -13 } });


		this.stepInput.getAggregation("_input").focus();
		//act
		qutils.triggerEvent(sWheelEventType, this.stepInput.getDomRef(), oWheelUpEvent);

		//assert
		assert.equal(this.stepInput.getAggregation("_input")._getInputValue(), 5,
				"The input's value is increasing with 1 after mouse wheel up");

		//act
		qutils.triggerEvent(sWheelEventType, this.stepInput.getDomRef(), oWheelDownEvent);

		//assert
		assert.equal(this.stepInput.getAggregation("_input")._getInputValue(), 4,
				"The input's value is decreasing with 1 after mouse wheel down");
	});

	QUnit.test("mousewheel up/down not increases/decreases the value if the input is not focused", function (assert) {
		//arrange
		var bFirefox = Device.browser.firefox;
		var sWheelEventType = bFirefox ? "DOMMouseScroll" : "mousewheel";
		var oWheelUpEvent = jQuery.Event(sWheelEventType, { originalEvent: { detail: bFirefox ? -1 : 0, wheelDelta: 13 } });
		var oWheelDownEvent = jQuery.Event(sWheelEventType, { originalEvent: { detail: bFirefox ? 1 : 0 , wheelDelta: -13 } });

		//act
		qutils.triggerEvent(sWheelEventType, this.stepInput.getDomRef(), oWheelUpEvent);

		//assert
		assert.equal(this.stepInput.getAggregation("_input")._getInputValue(), 4,
			"The input's value is not increased");

		//act
		qutils.triggerEvent(sWheelEventType, this.stepInput.getDomRef(), oWheelDownEvent);

		//assert
		assert.equal(this.stepInput.getAggregation("_input")._getInputValue(), 4,
			"The input's value is not decreased");
	});

	QUnit.test("mousewheel up/down not increases/decreases the value if the input is disabled or readonly", function (assert) {
		//arrange
		var bFirefox = Device.browser.firefox;
		var sWheelEventType = bFirefox ? "DOMMouseScroll" : "mousewheel";
		var oWheelUpEvent = jQuery.Event(sWheelEventType, { originalEvent: { detail: bFirefox ? -1 : 0, wheelDelta: 13 } });
		var oWheelDownEvent = jQuery.Event(sWheelEventType, { originalEvent: { detail: bFirefox ? 1 : 0 , wheelDelta: -13 } });

		//act
		this.stepInput.focus();
		this.stepInput.setEnabled(false);
		qutils.triggerEvent(sWheelEventType, this.stepInput.getDomRef(), oWheelUpEvent);

		//assert
		assert.equal(this.stepInput.getAggregation("_input")._getInputValue(), 4,
			"The input's value is not increased");

		//act
		this.stepInput.setEnabled(true);
		this.stepInput.setEditable(false);
		this.stepInput.focus();
		qutils.triggerEvent(sWheelEventType, this.stepInput.getDomRef(), oWheelDownEvent);

		//assert
		assert.equal(this.stepInput.getAggregation("_input")._getInputValue(), 4,
			"The input's value is not decreased");

		//cleanup
		this.stepInput.setEditable(true);
	});


	function calcTime(oStepInput, val) {
		var time = 0;
		// initialize values needed for _calcWaitTimeout function
		oStepInput._waitTimeout = StepInput.INITIAL_WAIT_TIMEOUT;
		oStepInput._speed = StepInput.INITIAL_SPEED;

		for (var i = 0; i <= val; i++) {
			time = time + oStepInput._calcWaitTimeout();
		}

		return time;
	}

	function callIconDelegate(sEventName, oIcon) {
		var oEvent = {
				button : 0
			};
		oIcon.aBeforeDelegates[0].oDelegate[sEventName].call(oIcon, oEvent);
	}

	QUnit.module("Mouse events on increment/decrement button", {
		beforeEach: function () {
			this.oChangeSpy = this.spy();
			this.stepInput = new StepInput({
				value: 4,
				max: 10,
				min: -4,
				change: this.oChangeSpy
			});
			this.oResetSpinSpy = this.spy(this.stepInput, "_resetSpinValues");

			this.stepInput.placeAt('qunit-fixture');
			oCore.applyChanges();
		},
		afterEach: function () {
		}
	});


	QUnit.test("Mouse down on incrementButton", function (assert) {
		// time for which the value will get from 4 to 9
		var t = calcTime(this.stepInput, 4),
			incBtn = this.stepInput._getIncrementButton();
		// Act - mouse down on increment button
		callIconDelegate("onmousedown", incBtn);
		// hold down the increment button enough time so the value can get to 9
		this.clock.tick(t);

		// Act - mouse up on increment button
		callIconDelegate("onmouseup", incBtn);

		// assert that the value get to 9
		assert.equal(this.stepInput.getValue(), 9, "Input has value of 9");
	});

	QUnit.test("Mouse down on decrementButton", function (assert) {
		// time for which the value will get from 4 to -1
		var t = calcTime(this.stepInput, 4),
			decBtn = this.stepInput._getDecrementButton();

		// Act - mouse down on decrement button
		callIconDelegate("onmousedown", decBtn);
		// hold down the decrement button enough time so the value can get to -1
		this.clock.tick(t);

		// Act - mouse up on decrement button
		callIconDelegate("onmouseup", decBtn);

		// assert that the value get to -1
		assert.equal(this.stepInput.getValue(), -1, "Input has value of -1");
	});

	QUnit.test("Mouse down on incrementButton when having max value set", function (assert) {
		// time for which the value will get from 4 to 7
		var t = calcTime(this.stepInput, 2),
			incBtn = this.stepInput._getIncrementButton();

		// Act - mouse down on increment button
		callIconDelegate("onmousedown", incBtn);
		// hold down the increment button enough time so the value can get to 7
		this.clock.tick(t);

		// assert that the value get to 7
		assert.equal(this.stepInput._getInput().getValue(), '7', "Input has value of 7");

		// keep holding until the max value of 10 is reached and the increase button will be disabled
		this.clock.tick(2000);

		// Act - mouse up on increment button
		callIconDelegate("onmouseup", incBtn);

		// assert that the value is 10 and that the change event is fired
		assert.ok(this.stepInput._getInput().getValue() === '10', "Input has reached the max value of 10");

		assert.equal(this.oChangeSpy.callCount, 1, "Change Event should be called once");
	});

	QUnit.test("Mouse down on incrementButton when having max value set and inserting value in the input field", function (assert) {
		// time for which the value will get from 4 to 7
		var iTime = calcTime(this.stepInput, 2),
			oIncBtn = this.stepInput._getIncrementButton(),
			oMockEvent = {
				"sId" : "change",
				"mParameters" : {
					"newValue" : "45.00",
					"value" : "45.00"
				},
				"oSource" : this.stepInput};

		// Act - mouse down on increment button
		callIconDelegate("onmousedown", oIncBtn);
		// hold down the increment button enough time so the value can get to 7
		this.clock.tick(iTime);

		// keep holding until the max value of 10 is reached and the increase button will be disabled
		this.clock.tick(2000);

		// Act - mouse up on increment button
		// callIconDelegate("onmouseup", oIncBtn);
		document.getElementById(this.stepInput._getInput().getId() + "-inner").value = "45";
		this.stepInput._change(oMockEvent);

		oCore.applyChanges();

		assert.strictEqual(this.stepInput.getValueState(), "Error", "The ValueState is set to error if the value is not valid");
	});

	QUnit.test("Mouse down on decrementButton when having min value set", function (assert) {
		// time for which the value will get from 4 to 1
		var t = calcTime(this.stepInput, 2),
			decBtn = this.stepInput._getDecrementButton();

		// Act - mouse down on decrement button
		callIconDelegate("onmousedown", decBtn);
		// hold down the decrement button enough time so the value can get to 1
		this.clock.tick(t);

		// assert that the value get to 1
		assert.equal(this.stepInput._getInput().getValue(), "1", "Input has value of 1");

		// keep holding until the min value of -4 is reached and the decrease button will be disabled
		this.clock.tick(2000);

		// Act - mouse up on decrement button
		callIconDelegate("onmouseup", decBtn);

		// assert that the value is 10 and that the change event is fired
		assert.ok(this.stepInput._getInput().getValue() === "-4", "Input has reached the min value of -4");
		assert.equal(this.oChangeSpy.callCount, 1, "Change Event should be called once");
	});

	QUnit.test("Mouse down and mouse out on incrementButton", function (assert) {
		// time for which the value will get from 4 to 9
		var t = calcTime(this.stepInput, 4),
			incBtn = this.stepInput._getIncrementButton();

		// Act - mouse down on increment button
		callIconDelegate("onmousedown", incBtn);
		// hold down the increment button enough time so the value can get to 7
		this.clock.tick(t);

		// Act - mouse out on increment button
		callIconDelegate("onmouseout", incBtn);

		// assert that the value get to 9
		assert.equal(this.stepInput.getValue(), 9, "Input has value of 9");

		assert.equal(this.oChangeSpy.callCount, 1, "Change Event should be called once");
		assert.equal(this.oResetSpinSpy.callCount, 1, "Reset spin should be called once");
	});


	QUnit.module("Touch events on increment/decrement button", {
		beforeEach: function () {
			this.stub(Device, "system").value({
				desktop : false,
				phone : false,
				tablet : true
			});
			this.oChangeSpy = this.spy();
			this.stepInput = new StepInput({
				value: 4,
				max: 10,
				min: -4,
				change: this.oChangeSpy
			});

			this.stepInput.placeAt('qunit-fixture');
			oCore.applyChanges();
		},
		afterEach: function () {
		}
	});

	QUnit.test("Touch start on incrementButton", function (assert) {
		// time for which the value will get from 4 to 9
		var t = calcTime(this.stepInput, 4),
			incBtn = this.stepInput._getIncrementButton();

		// Act - mouse down on increment button
		callIconDelegate("onmousedown", incBtn);
		// hold down the increment button enough time so the value can get to 9
		this.clock.tick(t);

		// Act - mouse up on increment button
		callIconDelegate("ontouchend", incBtn);

		// assert that the value get to 9
		assert.equal(this.stepInput.getValue(), 9, "Input has value of 9");
	});

	QUnit.test("Touch start on decrementButton", function (assert) {
		// time for which the value will get from 4 to -1
		var t = calcTime(this.stepInput, 4),
			decBtn = this.stepInput._getDecrementButton();

		// Act - mouse down on decrement button
		callIconDelegate("onmousedown", decBtn);
		// hold down the decrement button enough time so the value can get to -1
		this.clock.tick(t);

		// Act - mouse up on decrement button
		callIconDelegate("ontouchend", decBtn);

		// assert that the value get to -1
		assert.equal(this.stepInput.getValue(), -1, "Input has value of -1");
	});

	QUnit.test("Touch start on incrementButton when having max value set", function (assert) {
		// time for which the value will get from 4 to 7
		var t = calcTime(this.stepInput, 2),
			incBtn = this.stepInput._getIncrementButton();

		// Act - mouse down on increment button
		callIconDelegate("onmousedown", incBtn);
		// hold down the increment button enough time so the value can get to 7
		this.clock.tick(t);

		// assert that the value get to 7
		assert.equal(this.stepInput._getInput().getValue(), "7", "Input has value of 7");

		// keep holding until the max value of 10 is reached and the increase button will be disabled
		this.clock.tick(2000);

		// Act - mouse up on increment button
		callIconDelegate("ontouchend", incBtn);

		// assert that the value is 10 and that the change event is fired
		assert.ok(this.stepInput._getInput().getValue() === "10", "Input has reached the max value of 10");
		assert.equal(this.oChangeSpy.callCount, 1, "Change Event should be called once");
	});

	QUnit.test("Mouse down on decrementButton when having min value set", function (assert) {
		// time for which the value will get from 4 to 1
		var t = calcTime(this.stepInput, 2),
			decBtn = this.stepInput._getDecrementButton();

		// Act - mouse down on decrement button
		callIconDelegate("onmousedown", decBtn);
		// hold down the decrement button enough time so the value can get to 1
		this.clock.tick(t);

		// assert that the value get to 1
		assert.equal(this.stepInput._getInput().getValue(), "1", "Input has value of 1");

		// keep holding until the min value of -4 is reached and the decrease button will be disabled
		this.clock.tick(2000);

		// Act - mouse up on decrement button
		callIconDelegate("ontouchend", decBtn);

		// assert that the value is 10 and that the change event is fired
		assert.ok(this.stepInput._getInput().getValue() === "-4", "Input has reached the min value of -4");
		assert.equal(this.oChangeSpy.callCount, 1, "Change Event should be called once");
	});


	QUnit.module("Accessibility", {
		beforeEach: function () {
			this.stepInput = new StepInput({
				value: 4,
				max: 10,
				min: -4,
				ariaLabelledBy: "__text0"
			});
			this.stepInput.placeAt('qunit-fixture');
			oCore.applyChanges();
		},
		afterEach: function () {
			if (!bSkipDestroy) {
				this.stepInput.destroy();
			}
		}
	});

	QUnit.test("Internal 'sap.m.Input' has correct ARIA attributes when StepInput is initialized with default values", function (assert) {
		//prepare
		var oSUT = new StepInput(),
			oInput = oSUT._getInput(),
			$Input;
		//act
		oSUT.placeAt('qunit-fixture');
		oCore.applyChanges();
		$Input = oInput.$("inner");
		//assert
		assert.strictEqual($Input.attr("role"), "spinbutton", "The internal input has correct aria role");
		assert.ok($Input.is("[aria-valuenow]"), "Internal Input has 'aria-valuenow' attribute");
		assert.strictEqual($Input.attr("aria-valuenow"), "0", "Internal input's 'aria-valuenow' attribute has correct value");
		assert.notOk($Input.is("[aria-valuemin]"), "Internal Input doesn't have 'aria-valuemin' attribute");
		assert.notOk($Input.is("[aria-valuemax]"), "Internal Input doesn't have 'aria-valuemax' attribute");
		/* Inherited InputBase aria properties */
		assert.notOk($Input.is("[name]"), "Internal Input doesn't have 'name' attribute");
		assert.notOk($Input.is("[placeholder]"), "Internal Input doesn't have 'placeholder' attribute");
		assert.notOk($Input.is("[aria-invalid]"), "Internal Input doesn't have 'aria-invalid' attribute");
		assert.notOk($Input.is("[aria-readonly]"), "Internal Input doesn't have 'aria-readonly' attribute");
		assert.notOk($Input.is("[aria-disabled]"), "Internal Input doesn't have 'aria-disabled' attribute");
		assert.notOk($Input.is("[aria-required]"), "Internal Input doesn't have 'aria-required' attribute");
		assert.notOk($Input.is("[aria-labelledby]"), "Internal Input doesn't have 'aria-labelledby' attribute");
		assert.notOk($Input.is("[aria-describedby]"), "Internal Input doesn't have 'aria-describedby' attribute");
		//clean
		oSUT.destroy();
	});

	QUnit.test("Internal NumericInput doesn't have duplicated labels in 'aria-labelledby' and 'aria-describedby' attributes when there is 'description' property set", function (assert) {
		//prepare
		var sId = "StepInput",
			oSUT = new StepInput(sId, {
				description: "USD"
			}),
			aLabelledBy = [],
			aDescribedBy = [],
			oInput = oSUT._getInput(),
			$Input;

		//act
		oSUT.placeAt('qunit-fixture');
		oCore.applyChanges();
		$Input = oInput.$("inner");
		aLabelledBy = $Input.attr("aria-labelledby").split(" ");
		aDescribedBy = $Input.attr("aria-describedby").split(" ");

		//assert
		assert.equal(aLabelledBy.length, 1, "there are no duplicated labels in 'aria-labelledby'");
		assert.equal(aLabelledBy[0], sId + "-input-descr", "there is proper label in 'aria-labelledby'");
		assert.equal(aDescribedBy.length, 1, "there are no duplicated labels in 'aria-describedby'");
		assert.equal(aDescribedBy[0], sId + "-input-descr", "there is proper label in 'aria-describedby'");

		//clean
		oSUT.destroy();
	});

	QUnit.test("Internal 'sap.m.Input' has correct ARIA attributes when initialized with specific values", function (assert) {
		//prepare
		var oSUT = new StepInput({
				name: "useful name",
				required: true,
				editable: false,
				enabled: false,
				placeholder: 'useful placeholder',
				min: 0,
				max: 10,
				value: 15
			}),
			oInput = oSUT._getInput(),
			$Input;
		//act
		oSUT.placeAt('qunit-fixture');
		oCore.applyChanges();
		$Input = oInput.$("inner");
		//assert
		assert.ok($Input.is("[aria-valuenow]"), "Internal Input has 'aria-valuenow' attribute");
		assert.strictEqual($Input.attr("aria-valuenow"), "15", "Internal input's 'aria-valuenow' attribute has correct value");
		assert.ok($Input.is("[aria-valuemin]"), "Internal Input has 'aria-valuemin' attribute");
		assert.strictEqual($Input.attr("aria-valuemin"), "0", "Internal input's 'aria-valuemin' attribute has correct value");
		assert.ok($Input.is("[aria-valuemax]"), "Internal Input has 'aria-valuemax' attribute");
		assert.strictEqual($Input.attr("aria-valuemax"), "10", "Internal input's 'aria-valuemax' attribute has correct value");
		/* Inherited InputBase aria properties */
		assert.ok($Input.is("[name]"), "Internal Input has 'name' attribute");
		assert.strictEqual($Input.attr("name"), 'useful name', "Internal input's 'name' attribute has correct value");
		assert.ok($Input.is("[placeholder]"), "Internal Input has 'placeholder' attribute");
		assert.strictEqual($Input.attr("placeholder"), 'useful placeholder', "Internal input's 'placeholder' attribute has correct value");
		assert.notOk($Input.is("[aria-invalid]"), "Internal Input has 'aria-invalid' attribute");
		assert.ok($Input.is("[aria-readonly]"), "Internal Input has 'aria-readonly' attribute");
		assert.strictEqual($Input.attr("aria-readonly"), 'true', "Internal input's 'aria-readonly' attribute has correct value");
		assert.notOk($Input.is("[aria-disabled]"), "Internal Input does not have 'aria-disabled' attribute");
		assert.ok($Input.is("[aria-required]"), "Internal Input has 'aria-required' attribute");
		assert.strictEqual($Input.attr("aria-required"), 'true', "Internal input's 'aria-required' attribute has correct value");
		//clean
		oSUT.destroy();
	});

	QUnit.test("StepInput correctly alters its internal 'sap.m.Input' ARIA attributes on property changes", function (assert) {
		//prepare
		var oInput = this.stepInput._getInput();
		var sInputSuffix = "inner";
		//assert - initial values
		assert.ok(oInput.$(sInputSuffix).is('[aria-valuenow]'), "'aria-valuenow' attribute is rendered in the DOM");
		assert.strictEqual(oInput.$(sInputSuffix).attr("aria-valuenow"), "4", "'aria-valuenow' attribute has correct initial value");
		//act - change the 'value' property
		this.stepInput.setValue(9);
		oCore.applyChanges();
		//assert - expect aria-valuenow to be changed accordingly
		assert.strictEqual(oInput.$(sInputSuffix).attr("aria-valuenow"), "9", "'aria-valuenow' attribute was updated when the 'value' property was changed");
		//act - change the 'editable' property
		this.stepInput.setEditable(false);
		oCore.applyChanges();
		//assert - expect 'aria-readonly=true' to be rendered in the DOM
		assert.ok(oInput.$(sInputSuffix).is('[aria-readonly]'), "'aria-readonly' attribute was rendered in the DOM");
		assert.strictEqual(oInput.$(sInputSuffix).attr("aria-readonly"), "true", "'aria-readonly' attribute was updated when the 'editable' property was changed");
		//act - change the 'editable' property
		this.stepInput.setEditable(true);
		oCore.applyChanges();
		//assert - expect 'aria-readonly' not to be rendered in the DOM
		assert.notOk(oInput.$(sInputSuffix).is('[aria-readonly]'), "'aria-readonly' attribute is not rendered in the DOM");
		//act - change the 'enabled' property
		this.stepInput.setEnabled(false);
		oCore.applyChanges();
		//assert - expect 'aria-disabled=true' to be rendered in the DOM
		assert.notOk(oInput.$(sInputSuffix).is('[aria-disabled]'), "'aria-disabled' attribute is not rendered");
		this.stepInput.setEnabled(true);
		oCore.applyChanges();
		//act - simulate changing the 'value' outside the possible range with typing/
		oInput.$(sInputSuffix).val(11);
		// loose focus
		this.stepInput._change();
		oCore.applyChanges();
		//assert - expect 'aria-invalid=true' to be rendered in the DOM
		assert.ok(oInput.$(sInputSuffix).is('[aria-invalid]'), "'aria-invalid' is rendered in the DOM");
		assert.strictEqual(oInput.$(sInputSuffix).attr('aria-invalid'), "true", "'aria-invalid' attribute was updated when the invalid value is typed and confirmed");
	});

	QUnit.test("labels are redirected to the inner input", function (assert) {
		// Prepare
		var $label,
			$innerInput,
			oLabel = new Label({
				labelFor: this.stepInput,
				text: "Doesn't matter"
			});

		oLabel.placeAt("qunit-fixture");
		oCore.applyChanges();

		$label = oLabel.$();
		$innerInput = this.stepInput.$().find(".sapMInputBaseInner");

		// Assert
		assert.strictEqual($label.attr("for"), $innerInput.attr("id"), "label's 'for' points exactly towards the inner input");

		// Cleanup
		oLabel.destroy();
	});

	QUnit.test("labels are redirected to the inner input when used inside of a form", function(assert) {
		// Prepare
		var oStepInputOne = new StepInput("first", {
				value: "36",
				layoutData: new GridData({span: "L4 M4 S4"})
			}),
			oStepInputTwo = new StepInput("second", {
				value: "75",
				layoutData: new GridData({span: "L4 M4 S4"})
			}),
			oForm = new Form({
				layout: new ResponsiveGridLayout(),
				formContainers: [
					new FormContainer({
						title: "test",
						formElements: [
							new FormElement({
								label: new Label("medium", {text: "Medium"}),
								fields: [
									oStepInputOne,
									oStepInputTwo
								]
							})
						]
					})
				]
			});
		oForm.placeAt('qunit-fixture');
		oCore.applyChanges();

		// Act
		// Assert
		assert.strictEqual(oStepInputOne.$().find(".sapMInputBaseInner").attr("aria-labelledby"), "medium",
			"Inner input of the first StepInput has correct aria-lablledby argument");
		assert.strictEqual(oStepInputTwo.$().find(".sapMInputBaseInner").attr("aria-labelledby"), "medium",
			"Inner input of the second StepInput has correct aria-lablledby argument");

		// Cleanup
		oForm.destroy();
	});

	QUnit.test("Backwards reference to a label is added in aria-labelledby only on the inner input", function (assert) {
		// Prepare
		var $label,
			$innerInput,
			oLabel = new Label({
				labelFor: this.stepInput,
				text: "Doesn't matter"
			});

		oLabel.placeAt("qunit-fixture");
		oCore.applyChanges();

		$label = oLabel.$();
		$innerInput = this.stepInput.$().find(".sapMInputBaseInner");

		// Assert
		assert.notOk($label.attr("aria-labelledby"), "Root element doesn't have aria-labelledby");
		assert.strictEqual($innerInput.attr("aria-labelledby"), oLabel.getId() + " __text0",
			"Internal input has reference to the label and the default ariaLabelledBy");

		// Cleanup
		oLabel.destroy();
	});

	QUnit.test("Labels, default ariaLabelledBy and description are all added in aria-labelledby", function (assert) {
		// Prepare
		var $innerInput,
			sExpectedReferences,
			oLabel = new Label("the-label", {
				labelFor: this.stepInput,
				text: "Doesn't matter"
			});

		// Act
		this.stepInput.setDescription("Some description");
		oCore.applyChanges();

		$innerInput = this.stepInput.$().find(".sapMInputBaseInner");
		sExpectedReferences = "the-label __text0 " + this.stepInput._getInput().getId() + "-descr";

		//Assert
		assert.strictEqual($innerInput.attr("aria-labelledby"), sExpectedReferences, "All references are set");

		// Cleanup
		oLabel.destroy();
	});

	QUnit.test("_getAccessibilityInfo", function (assert) {
		// Prepare
		var oAccInfo = this.stepInput.getAccessibilityInfo();

		// Assert
		assert.strictEqual(oAccInfo.type, oCore.getLibraryResourceBundle("sap.m").getText("ACC_CTR_TYPE_STEPINPUT"), "Type correctly provided");
		assert.strictEqual(oAccInfo.description, 4, "Value correctly provided");
		assert.ok(oAccInfo.focusable, "Control is focusable");
		assert.ok(oAccInfo.enabled, "Control is enabled");
		assert.ok(oAccInfo.editable, "Control is editable");
	});

	QUnit.test("Increment/Decrement button", function (assert) {
		assert.strictEqual(this.stepInput._getDecrementButton().getDecorative(), false, "Decrement icon isn't decorative");
		assert.strictEqual(this.stepInput._getIncrementButton().getDecorative(), false, "Increment icon isn't decorative");
	});

	QUnit.module("binding", {
		beforeEach: function () {
			this.stepInput = new StepInput({
				value: {
					path: "/vValue"
				},
				displayValuePrecision: {
					path: "/vPrecision"
				},
				max: {
					path: "/vMax"
				},
				min: {
					path: "/vMin"
				}
			});
			this.oModel = new JSONModel();
			this.stepInput.placeAt('qunit-fixture');
			oCore.applyChanges();
		},
		afterEach: function () {
			if (!bSkipDestroy) {
				this.stepInput.destroy();
			}
		}
	});

	QUnit.test("Setting value to a disabled StepInput", function (assert) {
		// Prepare
		this.stepInput.setEnabled(false);
		oCore.applyChanges();

		// Act
		this.stepInput._applyValue(123);

		// Assert
		assert.equal(this.stepInput._getInput().$("inner").val(), 123, "The value is set");
	});

	QUnit.test("When value set via binding is undefined", function (assert) {
		this.oModel.setData({});
		this.stepInput.setModel(this.oModel);
		oCore.applyChanges();

		assert.equal(this.stepInput.getAggregation("_input")._getInputValue(), 0, "The input is set correctly");
		assert.strictEqual(this.stepInput.getValue(), 0, "Value is set to the default one if it was undefined");
	});

	QUnit.test("value set via binding", function (assert) {
		this.oModel.setData({
			vValue: 6
		});
		this.stepInput.setModel(this.oModel);
		oCore.applyChanges();

		assert.strictEqual(this.stepInput.getValue(), 6, "Value is set correctly");
		assert.equal(this.stepInput.getAggregation("_input")._getInputValue(), "6", "The input is set correctly");
	});

	QUnit.test("value set via binding and has precision", function (assert) {
		this.oModel.setData({
			vValue: 6,
			vPrecision: 3
		});
		this.stepInput.setModel(this.oModel);
		oCore.applyChanges();

		assert.strictEqual(this.stepInput.getValue(), 6, "Value is set correctly");
		assert.strictEqual(this.stepInput.getDisplayValuePrecision(), 3, "Value precision is set correctly");
		assert.strictEqual(this.stepInput.getAggregation("_input")._getInputValue(), "6.000", "The input is set and formated correctly");
	});

	QUnit.test("less than Min value set via binding", function (assert) {
		this.oModel.setData({
			vValue: 4,
			vMin: 5
		});
		this.stepInput.setModel(this.oModel);
		oCore.applyChanges();

		assert.strictEqual(this.stepInput.getValue(), 4, "Value is set correctly");
		assert.equal(this.stepInput.getAggregation("_input")._getInputValue(), "4", "The input is set correctly");
	});

	QUnit.test("value 0 Min value set via binding", function (assert) {
		this.oModel.setData({
			vValue: 0,
			vMin: 5
		});
		this.stepInput.setModel(this.oModel);
		oCore.applyChanges();

		assert.strictEqual(this.stepInput.getValue(), 0, "Value is set correctly");
		assert.equal(this.stepInput.getAggregation("_input")._getInputValue(), "0", "The input is set correctly");
	});


	QUnit.module("calculate decimals", {
		beforeEach: function () {
			this.stepInput = new StepInput();

			this.stepInput.placeAt('qunit-fixture');
			oCore.applyChanges();
		},
		afterEach: function () {
			if (!bSkipDestroy) {
				this.stepInput.destroy();
			}
		}
	});

	QUnit.test("Adding additional zeros to reach the precision value", function (assert) {
		assert.strictEqual(this.stepInput._padZeroesRight("34", 5), "34000", "returns '34000'");
	});

	QUnit.test("Adding zeros equal to precision value", function (assert) {
		assert.strictEqual(this.stepInput._padZeroesRight("0", 5), "00000", "returns '00000'");
	});

	QUnit.module("onsapescape()", {
		beforeEach: function () {
			this.stepInput = new StepInput({});

			this.stepInput.placeAt('qunit-fixture');
			oCore.applyChanges();
		},
		afterEach: function () {
			if (!bSkipDestroy) {
				this.stepInput.destroy();
			}
		}
	});

	QUnit.test("onsapescape()", function (assert) {
		//prepare & act
		var oSpy = sinon.spy(this.stepInput, "onsapescape");
		this.stepInput.onsapescape();
		// Assert
		assert.equal(oSpy.callCount, 1, "onsapescape() of the input is called once");
	});

	QUnit.module("Private API", {
		beforeEach: function () {
			this.stepInput = new StepInput({});

			this.stepInput.placeAt('qunit-fixture');
			oCore.applyChanges();
		},
		afterEach: function () {
			if (!bSkipDestroy) {
				this.stepInput.destroy();
			}
		}
	});

	QUnit.test("_calculateValue() with positive numbers", function (assert) {
		//assert
		assert.strictEqual(this.stepInput._calculateNewValue(1, true), 1, "The value of 0 is increased by 1");

		//act
		this.stepInput.setMin(-1);
		//assert
		assert.strictEqual(this.stepInput._calculateNewValue(2, false), this.stepInput.getMin(),
			"The value of 0 is decreased by 2 but min > 2, so it returns min");

		//act
		this.stepInput.setMax(5);
		//assert
		assert.strictEqual(this.stepInput._calculateNewValue(6, true), this.stepInput.getMax(),
			"The value of 0 is increased by 6 but max < 6, so it returns max");

		//act
		this.stepInput.setMax(6);
		this.stepInput.setValue(3);

		//assert
		assert.strictEqual(this.stepInput._calculateNewValue(2, true),
			5,
			"The value of 3 is increased by 2 * 1(default step) and max is not returned");
		assert.strictEqual(this.stepInput._calculateNewValue(5, true),
			this.stepInput.getMax(),
			"The value of 3 is increased by 5 * 1 and the max is returned");
	});

	QUnit.test("_calculateNewValue() with negative numbers", function (assert) {
		//assert
		assert.strictEqual(this.stepInput._calculateNewValue(-1, true), 1, "The value of 0 is increased by 1");

		//act
		this.stepInput.setMin(-1);
		//assert
		assert.strictEqual(this.stepInput._calculateNewValue(-2, false), this.stepInput.getMin(),
			"The value of 0 is decreased by 2 but min > 2, so it returns min");

		//act
		this.stepInput.setMax(5);
		//assert
		assert.strictEqual(this.stepInput._calculateNewValue(-6, true), this.stepInput.getMax(),
			"The value of 0 is increased by 6 but max < 6, so it returns max");

		//act
		this.stepInput.setMax(6);
		this.stepInput.setValue(3);
		//assert
		assert.strictEqual(this.stepInput._calculateNewValue(-2, true),
			5,
			"The value of 3 is increased by 2 * 1(default step) and max is not returned");
		assert.strictEqual(this.stepInput._calculateNewValue(-5, true),
			this.stepInput.getMax(),
			"The value of 3 is increased by 5 * 1 and the max is returned");
	});


	QUnit.test("_closestFoldValue with step 5, larger step 1, and increasing", function (assert) {
		assert.equal(this.stepInput._calculateClosestFoldValue(-77, 5, 1), -75, '-77 + => -75');
		assert.equal(this.stepInput._calculateClosestFoldValue(-32, 5, 1), -30, '-32 + => -30');
		assert.equal(this.stepInput._calculateClosestFoldValue(-30, 5, 1), -25, '-30 + => -25');
		assert.equal(this.stepInput._calculateClosestFoldValue(-17, 5, 1), -15, '-17 + => -15');
		assert.equal(this.stepInput._calculateClosestFoldValue(-15, 5, 1), -10, '-15 + => -10');
		assert.equal(this.stepInput._calculateClosestFoldValue(-14, 5, 1), -10, '-14 + => -10');
		assert.equal(this.stepInput._calculateClosestFoldValue(-11, 5, 1), -10, '-11 + => -10');
		assert.equal(this.stepInput._calculateClosestFoldValue(-10, 5, 1), -5, '-10 + => -5');
		assert.equal(this.stepInput._calculateClosestFoldValue(-6, 5, 1), -5, '-6 + => -5');
		assert.equal(this.stepInput._calculateClosestFoldValue(-5, 5, 1), 0, '-5 + => 0');
		assert.equal(this.stepInput._calculateClosestFoldValue(-4, 5, 1), 0, '-4 + => 0');
		assert.equal(this.stepInput._calculateClosestFoldValue(-1, 5, 1), 0, '-1 + => 0');
		assert.equal(this.stepInput._calculateClosestFoldValue(0, 5, 1), 5, '0 + => 5');
		assert.equal(this.stepInput._calculateClosestFoldValue(1, 5, 1), 5, '1 + => 5');
		assert.equal(this.stepInput._calculateClosestFoldValue(4, 5, 1), 5, '4 + => 5');
		assert.equal(this.stepInput._calculateClosestFoldValue(5, 5, 1), 10, '5 + => 10');
		assert.equal(this.stepInput._calculateClosestFoldValue(6, 5, 1), 10, '6 + => 10');
		assert.equal(this.stepInput._calculateClosestFoldValue(10, 5, 1), 15, '10 + => 15');
		assert.equal(this.stepInput._calculateClosestFoldValue(11, 5, 1), 15, '11 + => 15');
		assert.equal(this.stepInput._calculateClosestFoldValue(14, 5, 1), 15, '14 + => 15');
		assert.equal(this.stepInput._calculateClosestFoldValue(15, 5, 1), 20, '15 + => 20');
		assert.equal(this.stepInput._calculateClosestFoldValue(17, 5, 1), 20, '17 + => 20');
		assert.equal(this.stepInput._calculateClosestFoldValue(30, 5, 1), 35, '30 + => 35');
		assert.equal(this.stepInput._calculateClosestFoldValue(32, 5, 1), 35, '32 + => 35');
		assert.equal(this.stepInput._calculateClosestFoldValue(77, 5, 1), 80, '77 + => 80');
	});

	QUnit.test("_closestFoldValue with step 1, larger step 1, and decreasing", function (assert) {
		assert.equal(this.stepInput._calculateClosestFoldValue(-77, 5, -1), -80, '-80 - => -80');
		assert.equal(this.stepInput._calculateClosestFoldValue(-32, 5, -1), -35, '-35 - => -35');
		assert.equal(this.stepInput._calculateClosestFoldValue(-30, 5, -1), -35, '-35 - => -35');
		assert.equal(this.stepInput._calculateClosestFoldValue(-17, 5, -1), -20, '-20 - => -20');
		assert.equal(this.stepInput._calculateClosestFoldValue(-15, 5, -1), -20, '-20 - => -20');
		assert.equal(this.stepInput._calculateClosestFoldValue(-14, 5, -1), -15, '-15 - => -15');
		assert.equal(this.stepInput._calculateClosestFoldValue(-11, 5, -1), -15, '-15 - => -15');
		assert.equal(this.stepInput._calculateClosestFoldValue(-10, 5, -1), -15, '-15 - => -15');
		assert.equal(this.stepInput._calculateClosestFoldValue(-6, 5, -1), -10, '-10 - => -10');
		assert.equal(this.stepInput._calculateClosestFoldValue(-5, 5, -1), -10, '-10 - => -10');
		assert.equal(this.stepInput._calculateClosestFoldValue(-4, 5, -1), -5, '-5 - => -5');
		assert.equal(this.stepInput._calculateClosestFoldValue(-1, 5, -1), -5, '-5 - => -5');
		assert.equal(this.stepInput._calculateClosestFoldValue(0, 5, -1), -5, '-5 - => -5');
		assert.equal(this.stepInput._calculateClosestFoldValue(1, 5, -1), 0, '0 - => 0');
		assert.equal(this.stepInput._calculateClosestFoldValue(4, 5, -1), 0, '0 - => 0');
		assert.equal(this.stepInput._calculateClosestFoldValue(5, 5, -1), 0, '0 - => 0');
		assert.equal(this.stepInput._calculateClosestFoldValue(6, 5, -1), 5, '5 - => 5');
		assert.equal(this.stepInput._calculateClosestFoldValue(10, 5, -1), 5, '5 - => 5');
		assert.equal(this.stepInput._calculateClosestFoldValue(11, 5, -1), 10, '10 - => 10');
		assert.equal(this.stepInput._calculateClosestFoldValue(14, 5, -1), 10, '10 - => 10');
		assert.equal(this.stepInput._calculateClosestFoldValue(15, 5, -1), 10, '10 - => 10');
		assert.equal(this.stepInput._calculateClosestFoldValue(17, 5, -1), 15, '15 - => 15');
		assert.equal(this.stepInput._calculateClosestFoldValue(30, 5, -1), 25, '25 - => 25');
		assert.equal(this.stepInput._calculateClosestFoldValue(32, 5, -1), 30, '30 - => 30');
		assert.equal(this.stepInput._calculateClosestFoldValue(77, 5, -1), 75, '75 - => 75');
	});

	QUnit.test("_closestFoldValue when input value is a floating point number, but displayValuePrecision=0, step and" +
		" larger step are integers", function (assert) {
		assert.equal(this.stepInput._calculateClosestFoldValue(35.7, 5, 1), 40, '37.5 + =>50');
		assert.equal(this.stepInput._calculateClosestFoldValue(-35.7, 5, -1), -40, '-37.5 - =>-40');
	});

	QUnit.test("_calculateNewValue: next value when current value does not fold into a mandatory step(StepMode.MultiplicationAndDivision)",
		function (assert) {
			//Prepare
			var fResult;
			this.stepInput.setStepMode(StepMode.Multiple);
			this.stepInput.setValue(-1);
			this.stepInput.setStep(5);
			oCore.applyChanges();

			//Act
			fResult = this.stepInput._calculateNewValue(1, true);

			//Assert
			assert.equal(fResult, 0, "..should result in next value that folds into the step");
		});

	QUnit.test("_calculateNewValue: previous value when current value does not fold into a mandatory step(StepMode.MultiplicationAndDivision)",
		function (assert) {
			//Prepare
			var fResult;
			this.stepInput.setStepMode(StepMode.Multiple);
			this.stepInput.setValue(-1);
			this.stepInput.setStep(5);
			oCore.applyChanges();

			//Act
			fResult = this.stepInput._calculateNewValue(1, false);

			//Assert
			assert.equal(fResult, -5, "..should result in previous value that folds into the step");
		});


	QUnit.test("_calculateNewValue: next value when current value does not fold into a mandatory step(StepMode.MultiplicationAndDivision)" +
		" and larger step is defined", function (assert) {
		//Prepare
		var fResult;
		this.stepInput.setStepMode(StepMode.Multiple);
		this.stepInput.setValue(1);
		this.stepInput.setStep(5);
		this.stepInput.setLargerStep(3);
		oCore.applyChanges();

		//Act
		fResult = this.stepInput._calculateNewValue(3, true);

		//Assert
		assert.equal(fResult, 15, "..should result in next value that folds into the step");
	});

	QUnit.test("_calculateNewValue: previous value when current value does not fold into a mandatory step(StepMode.MultiplicationAndDivision)" +
		" and larger step is defined", function (assert) {
		//Prepare
		var fResult;
		this.stepInput.setStepMode(StepMode.Multiple);
		this.stepInput.setValue(-1);
		this.stepInput.setStep(5);
		this.stepInput.setLargerStep(3);
		oCore.applyChanges();

		//Act
		fResult = this.stepInput._calculateNewValue(3, false);

		//Assert
		assert.equal(fResult, -15, "..should result in previous value that folds into the step");
	});


	QUnit.test("_calculateNewValue: next value when current value does not fold into a mandatory step(StepMode.MultiplicationAndDivision) " +
		"and next value is greater than max", function (assert) {
		//Prepare
		var fResult;
		this.stepInput.setStepMode(StepMode.Multiple);
		this.stepInput.setMax(15);
		this.stepInput.setValue(12);
		this.stepInput.setStep(6);
		oCore.applyChanges();

		//Act
		fResult = this.stepInput._calculateNewValue(1, true);

		//Assert
		assert.equal(fResult, 15, "..should result in next value=max");
	});

	QUnit.test("_calculateNewValue: previous value when current value does not fold into a mandatory step(StepMode.Multiple" +
		") and previous value is less than min", function (assert) {
		//Prepare
		var fResult;
		this.stepInput.setStepMode(StepMode.Multiple);
		this.stepInput.setValue(-10);
		this.stepInput.setStep(5);
		this.stepInput.setMin(-14);
		oCore.applyChanges();

		//Act
		fResult = this.stepInput._calculateNewValue(1, false);

		//Assert
		assert.equal(fResult, -14, "..should result in previous value=min");
	});

	QUnit.test("_calculateNewValue: when value and/or step are floating number values and step is mandatory", function (assert) {
		//Prepare
		var fResult1, fResult2;
		this.stepInput.setStepMode(StepMode.Multiple);
		this.stepInput.setValue(-7.0);
		this.stepInput.setDisplayValuePrecision(1);
		this.stepInput.setStep(5.0);

		var oStepInputNoMandatory = new StepInput({
			mandatory: false,
			value: -7.0,
			displayValuePrecision: 1,
			step: 5.0
		});
		oStepInputNoMandatory.placeAt("qunit-fixture");
		oCore.applyChanges();

		//Act
		fResult1 = this.stepInput._calculateNewValue(1, true);
		fResult2 = oStepInputNoMandatory._calculateNewValue(1, true);

		//Assert
		assert.equal(fResult1, fResult2, "..should change the value as the mandatory was set to false");
	});


	QUnit.test("_calculateNewValue calls _calculateClosestFoldValue with right parameters", function (assert) {
		//Prepare
		this.stepInput.setStepMode(StepMode.Multiple);
		this.stepInput.setValue(0);
		this.stepInput._getInput().setValue(10);
		this.stepInput.setStep(5);
		this.stepInput.setLargerStep(3);

		var oSpyCalculateClosestFoldValue = this.spy(this.stepInput, "_calculateClosestFoldValue");

		//Act
		this.stepInput._calculateNewValue(3, true);
		this.stepInput._calculateNewValue(3, false);

		this.stepInput._getInput().setValue(0);//same as the StepInput.value property
		this.stepInput._calculateNewValue(3, true);


		//Assert
		assert.equal(oSpyCalculateClosestFoldValue.callCount, 3, "..certain times");
		assert.deepEqual(oSpyCalculateClosestFoldValue.getCall(0).args, [10, 15, 1], ".. with certain arguments when " +
			"called _calculateNewValue(3, true) and DOM value differs from StepInput <value>");
		assert.deepEqual(oSpyCalculateClosestFoldValue.getCall(1).args, [10, 15, -1], ".. with certain arguments when " +
			"called _calculateNewValue(3, false) and input value differs from StepInput <value>");
		assert.deepEqual(oSpyCalculateClosestFoldValue.getCall(2).args, [0, 15, 1], ".. with certain arguments when " +
			" called _calculateNewValue(3, true) and DOM value is the same as StepInput <value>");
	});

	QUnit.test("StepMode: '+', arrow Up, Page Up or Shift + Up calls _calculateNewValue", function (assert) {
		//Prepare
		this.stepInput.setStepMode(StepMode.Multiple);
		this.stepInput.setValue(-7);
		this.stepInput.setStep(5);
		oCore.applyChanges();

		var oSpyCalculateNewValue = this.spy(this.stepInput, "_calculateNewValue");

		//Act
		this.stepInput.focus();
		this.stepInput._getIncrementButton().firePress();
		qutils.triggerKeydown(this.stepInput.getDomRef(), KeyCodes.ARROW_UP);
		this.clock.tick(1000);
		qutils.triggerKeydown(this.stepInput.getDomRef(), KeyCodes.PAGE_UP);
		this.clock.tick(1000);
		qutils.triggerKeydown(this.stepInput.getDomRef(), KeyCodes.ARROW_UP, true, false, false);
		this.clock.tick(1000);

		//Assert
		assert.equal(oSpyCalculateNewValue.callCount, 4, "..certain times");
		assert.deepEqual(oSpyCalculateNewValue.getCall(0).args, [1], ".. with certain arguments when '+' pressed");
		assert.deepEqual(oSpyCalculateNewValue.getCall(1).args, [1], ".. with certain arguments when 'keyb. Up' is pressed");
		assert.deepEqual(oSpyCalculateNewValue.getCall(2).args, [this.stepInput.getLargerStep()],
			".. with certain arguments when 'keyb. Page Up' is pressed");
		assert.deepEqual(oSpyCalculateNewValue.getCall(3).args, [this.stepInput.getLargerStep()],
			".. with certain arguments when 'keyb. Shift + Up' is pressed");

		//Cleanup
		oSpyCalculateNewValue.restore();
	});

	QUnit.test("StepMode: '-', arrow Down, Page Down or Shift + Down calls _calculateNewValue", function (assert) {
		//Prepare
		this.stepInput.setStepMode(StepMode.Multiple);
		this.stepInput.setValue(-7);
		this.stepInput.setStep(5);
		oCore.applyChanges();

		var oSpyCalculateNewValue = this.spy(this.stepInput, "_calculateNewValue");

		//Act
		this.stepInput.focus();
		this.stepInput._getDecrementButton().firePress();
		qutils.triggerKeydown(this.stepInput.getDomRef(), KeyCodes.ARROW_DOWN);
		this.clock.tick(1000);
		qutils.triggerKeydown(this.stepInput.getDomRef(), KeyCodes.PAGE_DOWN);
		this.clock.tick(1000);
		qutils.triggerKeydown(this.stepInput.getDomRef(), KeyCodes.ARROW_DOWN, true, false, false);
		this.clock.tick(1000);

		//Assert
		assert.equal(oSpyCalculateNewValue.callCount, 4, "..certain times");
		assert.deepEqual(oSpyCalculateNewValue.getCall(0).args, [-1], ".. with certain arguments when '-' pressed");
		assert.deepEqual(oSpyCalculateNewValue.getCall(1).args, [-1], ".. with certain arguments when 'keyb. Down' is pressed");
		assert.deepEqual(oSpyCalculateNewValue.getCall(2).args, [-this.stepInput.getLargerStep()],
			".. with certain arguments when 'keyb. Page Down' is pressed");
		assert.deepEqual(oSpyCalculateNewValue.getCall(3).args, [-this.stepInput.getLargerStep()],
			".. with certain arguments when 'keyb. Shift + Down' is pressed");

		//Cleanup
		oSpyCalculateNewValue.restore();
	});

	QUnit.test("_isNumericLike()", function (assert) {
		//assert
		assert.strictEqual(this.stepInput._isNumericLike(0), true, "The value of 0 is a number");
		assert.strictEqual(this.stepInput._isNumericLike("not a number"), false,
			"The value of 'not a number' is not a number");
		assert.strictEqual(this.stepInput._isNumericLike(null), false,
			"The value of null is not a number");
		assert.strictEqual(this.stepInput._isNumericLike(undefined), false,
			"The value of undefined is not a number");
		assert.strictEqual(this.stepInput._isNumericLike("5"), true,
			"The value '5' is a number");
		assert.strictEqual(this.stepInput._isNumericLike(""), false,
			"Empty string is not a number");
	});

	QUnit.test("_isInteger", function (assert) {
		assert.ok(this.stepInput._isInteger(1), "1 is integer");
		assert.ok(!this.stepInput._isInteger(null), "null in not integer");
		assert.ok(!this.stepInput._isInteger(undefined), "undefined is not integer");
		assert.ok(!this.stepInput._isInteger(-2.00000000009), "-2.00000000009 is not integer");
		assert.ok(!this.stepInput._isInteger(-2.99999999999), "-2.99999999999 is not integer");
	});

	QUnit.test("_applyValue()", function (assert) {
		// Prepare & act
		var oSpy = sinon.spy(this.stepInput, "setValue");
		this.stepInput.setEnabled(false);
		this.stepInput._applyValue(7);
		// Assert
		assert.equal(oSpy.callCount, 0, "setValue() is not called after setEnabled(false)");
		assert.strictEqual(this.stepInput.getValue(), 0, "'value' still holds the old value");

		// Act
		this.stepInput.setEnabled(true);
		this.stepInput.setEditable(false);
		this.stepInput._applyValue(8);
		// Assert
		assert.equal(oSpy.callCount, 0, "setValue() is not called after setEditable(false)");
		assert.strictEqual(this.stepInput.getValue(), 0, "'value' still holds the old value");
	});

	QUnit.test("_inputLiveChangeHandler() & _restrictCharsWhenDecimal()", function (assert) {
		//prepare
		var oSpy = sinon.spy(this.stepInput._getInput(), "setProperty"),
			oRestrictSpy = sinon.spy(this.stepInput, "_restrictCharsWhenDecimal"),
			oEvent = {
				getParameter : function () {
					return "1.2345";
				},
				getSource : function () {
					return {
						getProperty : function (){
							return "1.234";
						}
					};
				}
			};

		//act
		this.stepInput.setDisplayValuePrecision(3);
		this.stepInput._inputLiveChangeHandler.call(this.stepInput._getInput(), oEvent);

		//assert
		assert.equal(oSpy.callCount, 2,
				"_inputLiveChangeHandler always calls setProperty(). If the displayValuePrecision is bigger than the characters after the decimal mark - setProperty is called two times");
		assert.equal(oRestrictSpy.callCount, 1,
				"_inputLiveChangeHandler always calls _restrictCharsWhenDecimal");
		assert.strictEqual(oSpy.getCall(0).args[0], "valueState",
				"setProperty is called and it's first parameter is 'valueState'");
		assert.strictEqual(oSpy.getCall(0).args[1], "Error",
				"setProperty is called and it's second parameter is Error");
		assert.strictEqual(oSpy.getCall(1).args[0], "value",
				"setProperty is called and it's first parameter is 'value'");
		assert.strictEqual(oSpy.getCall(1).args[1], "1.234",
				"setProperty is called and it's second parameter is 1.234");
		assert.strictEqual(oSpy.getCall(1).args[2], true,
				"setProperty is called and it's third parameter is 'true'");

		//act
		this.stepInput._restrictCharsWhenDecimal(oEvent);

		//assert
		assert.strictEqual(this.stepInput._getInput().$("inner").val(), "1.234",
				"the digits's length is correct - equal to the displayValuePrecision");
	});

	QUnit.test("_showWrongValueVisualEffect() triggers an error state for 1s and set it back to the previous state", function (assert) {
		//act
		this.stepInput._showWrongValueVisualEffect();

		//assert
		assert.strictEqual(this.stepInput._getInput().getValueState(), "Error", "input's valueState is set to Error");
		assert.strictEqual(this.stepInput.getValueState(), "None", "it's only a visual effect");

		//act
		this.clock.tick(1000);

		//assert
		assert.strictEqual(this.stepInput._getInput().getValueState(), "None", "input's valueState is back to None");
	});

	QUnit.test("_iRealPrecision is updated on each value change", function (assert) {
		//Prepare
		var oIncrementButton = this.stepInput._getIncrementButton();
		this.stepInput.setDisplayValuePrecision(2);
		this.stepInput.setStep(0.05); //make sure the precision is different than the one in the value one
		this.stepInput.setValue(1.20);
		oCore.applyChanges();

		//Act
		this.stepInput.setValue(1.567); //make sure the precision is different than the one in the step above
		oIncrementButton.firePress();
		this.clock.tick(100);

		//Assert
		assert.strictEqual(this.stepInput._iRealPrecision, 3, "iRealPrecision for value 1.567 is 3");
		assert.strictEqual(this.stepInput.getValue(), 1.617, "Value is set correctly: 1.567 + 0.05 = 1.617. ");
		assert.strictEqual(this.stepInput.getAggregation("_input")._getInputValue(), "1.62",
			"The input is set and formatted correctly: 1.57 + 0.05 = 1.62.");
	});

	QUnit.test("_sumValues", function (assert) {
		//Act && Assert
		assert.equal(this.stepInput._sumValues(1.1376, 0.2, 1, 4), 1.3376, "sumValues(1.1376, 0.2, 1, 4)=1.3376");
		assert.equal(this.stepInput._sumValues(1.1376, 0.2, -1, 4), 0.9376, "sumValues(1.1376, 0.2, -1, 4)= 0.9376");
		assert.equal(this.stepInput._sumValues(1.1376, 0.2, 1, 3), 1.337, "sumValues(1.1376, 0.2, 1, 3)=1.337");
		assert.equal(this.stepInput._sumValues(1.1376, 0.2, -1, 3), 0.937, "sumValues(1.1376, 0.2, -1, 3)=0.937");
		assert.equal(this.stepInput._sumValues(0.29, 0.01, 1, 2), 0.3, "_sumValues(0.29, 0.01, 1, 2) = 0.3");
	});

	QUnit.test("_disableButtons works accordingly when enabled: false and min & max available", function (assert) {
		this.stepInput.setMin(0).setMax(10).setEnabled(false).setValue(5);
		oCore.applyChanges();

		assert.strictEqual(this.stepInput._getIncrementButton().$().hasClass("sapMStepInputIconDisabled"), true,
			"the increment button is still disabled after setValue");
		assert.strictEqual(this.stepInput._getDecrementButton().$().hasClass("sapMStepInputIconDisabled"), true,
			"the decrement button is still disabled after setValue");
	});

	QUnit.test("_disableButtons works when the enablement of the control is toggled", function (assert) {
		this.stepInput.setValue(5);

		this.stepInput.setEnabled(false);
		oCore.applyChanges();

		this.stepInput.setEnabled(true);
		oCore.applyChanges();

		assert.strictEqual(this.stepInput._getIncrementButton().$().hasClass("sapMStepInputIconDisabled"), false,
			"the increment button is enabled after the StepInput is enabled again");
		assert.strictEqual(this.stepInput._getDecrementButton().$().hasClass("sapMStepInputIconDisabled"), false,
			"the decrement button is enabled after the StepInput is enabled again");
	});

	QUnit.test("_verifyValue does not touch the value", function(assert) {
		// arrange
		this.stepInput.setMin(2);
		this.stepInput.setDisplayValuePrecision(2);
		oCore.applyChanges();

		// act
		this.stepInput._getInput().setValue("1");
		this.stepInput._verifyValue(); // it shouldn't invalidate
		oCore.applyChanges(); // so here should not re-render

		// assert
		assert.strictEqual(this.stepInput._getInput().getValue(), "1", "value has not been touched");
	});

	QUnit.test("_verifyValue respects binding min/max constraints", function(assert) {
		var iMax = 10;

		// arrange
		this.stepInput.setModel(new JSONModel({ value: 11 }));
		this.stepInput.bindProperty("value", {
			path: "/value",
			type: new TypeFloat(null, {
				maximum: iMax
			})
		});

		// act
		this.stepInput._verifyValue();

		// assert
		assert.strictEqual(this.stepInput._getInput().getValueState(), ValueState.Error, "value state is correct");
		assert.strictEqual(this.stepInput._getInput().getValueStateText(), sap.ui.getCore().getLibraryResourceBundle("sap.ui.core").getText("EnterNumberMax", iMax), "value state text is correct");
	});

	QUnit.test("_verifyValue prefers binding max constraint over max property setting", function(assert) {
		var iMax = 10;

		// arrange
		this.stepInput.setMax(13);
		this.stepInput.setModel(new JSONModel({ value: 11 }));
		this.stepInput.bindProperty("value", {
			path: "/value",
			type: new TypeFloat(null, {
				maximum: iMax
			})
		});

		// act
		this.stepInput._verifyValue();

		// assert
		assert.strictEqual(this.stepInput._getInput().getValueStateText(), sap.ui.getCore().getLibraryResourceBundle("sap.ui.core").getText("EnterNumberMax", iMax), "value state text is correct");
	});

	QUnit.test("If maximum or minimum binding constraint is set to 0, _getMin and _getMax return 0 too", function(assert) {
		// arrange
		this.stepInput.setModel(new JSONModel({ value: 11 }));
		this.stepInput.bindProperty("value", {
			path: "/value",
			type: new TypeFloat(null, {
				maximum: 0,
				minimum: 0
			})
		});

		// assert
		assert.strictEqual(this.stepInput._getMin(), 0, "returned min value is correct");
		assert.strictEqual(this.stepInput._getMax(), 0, "returned max value is correct");
	});

	QUnit.test("_verifyValue calculates the value state correctly", function(assert) {
		// arrange
		this.stepInput.setMax(6000);
		this.stepInput._getInput().setValue("10,000");

		// act
		this.stepInput._verifyValue();

		// assert
		assert.strictEqual(this.stepInput.getValueState(), "Error", "value state is correct");
	});

	QUnit.test("_disableButtons is called with respect of min/max binding constraints", function(assert) {
		// arrange
		var oSpyDisableButtons;

		this.stepInput.setModel(new JSONModel({ value: 9 }));
		this.stepInput.bindProperty("value", {
			path: "/value",
			type: new TypeFloat(null, {
				maximum: 10
			})
		});

		oSpyDisableButtons = this.spy(this.stepInput, "_disableButtons");

		// act
		this.stepInput.setValue(11);

		// assert
		assert.strictEqual(oSpyDisableButtons.getCall(0).args[1], 10,
			"_disableButtons called with the max argument as the maximum binding cnonstraint");
		assert.strictEqual(this.stepInput._getIncrementButton().getEnabled(), false, "increment is disabled");
	});

	QUnit.module("Misc");

	QUnit.test("increment/decrement buttons enabled state", function (assert) {
		// arrange
		var oStepInput = new StepInput({
			value: 5,
			min: 6,
			max: 8
		});
		oStepInput.placeAt('qunit-fixture');
		oCore.applyChanges();

		// assert
		assert.ok(!oStepInput._getDecrementButton().getEnabled(), "Decrease button is disabled when the value is less then the min value");
		// act
		oStepInput.setValue(7);
		// assert
		assert.ok(oStepInput._getDecrementButton().getEnabled(), "Decrease button is not disabled when the value is bigger then the min value and the control is enabled");
		//act
		oStepInput.setValue(6);
		oStepInput.setEnabled(false);
		// assert
		assert.ok(!oStepInput._getDecrementButton().getEnabled(), "Decrease button is disabled when the control is disabled");

		//act
		oStepInput.setEnabled(true);
		// assert
		assert.ok(oStepInput._getIncrementButton().getEnabled(), "Increase button is not disabled when the value is less then the max value and the control is enabled");
		// act
		oStepInput.setValue(9);
		// assert
		assert.ok(!oStepInput._getIncrementButton().getEnabled(), "Increase button is disabled when the value is bigger then the max value");
		//act
		oStepInput.setValue(7);
		oStepInput.setEnabled(false);
		// assert
		assert.ok(!oStepInput._getIncrementButton().getEnabled(), "Increase button is disabled when the control is disabled");

		// clean up
		oStepInput.destroy();
	});

	QUnit.test("setProperty sets the default properties of StepInput to the Input, when the value of the propery is not valid", function (assert) {
		// arrange
		var oStepInput = new StepInput();

		oStepInput.placeAt('qunit-fixture');
		oCore.applyChanges();

		// act
		// set "undefined" alignment
		oStepInput.setTextAlign();
		oCore.applyChanges();

		// assert
		assert.strictEqual(oStepInput._getInput().getTextAlign(), oStepInput.getTextAlign(), "textAlign of the Input should be the same as the textAlign of the StepInput");

		oStepInput.destroy();
	});

	QUnit.test("Set proper value when enter empty ('') or invalid value in the StepInput", function (assert) {
		// arrange
		var oStepInput = new StepInput(),
			oInput = oStepInput._getInput(),
			sInputSuffix = "inner",
			iValidValue = 11,		// valid value to "type" in the StepInput
			iInvalidResult = 0,		// value that must be set to StepInput when "type" invalid value
			sInvalidValue = "abc",	// invalid value to "type" in the StepInput
			sInvalidEmpty = "", 	// invalid value (empty string)
			iMinValueToSet = 10;	// min value to set to StepInput


		oStepInput.placeAt('qunit-fixture');
		oCore.applyChanges();

		// act - enter valid value
		oInput.$(sInputSuffix).val(iValidValue);
		oStepInput._change();
		oCore.applyChanges();

		// assert - the result must be what entered
		assert.equal(oStepInput.getValue(), iValidValue, "Value of the StepInput is correct when enter valid value (" + iValidValue + " => " + iValidValue + ")");

		// act - enter invalid value
		oInput.$(sInputSuffix).val(sInvalidValue);
		oStepInput._change();
		oCore.applyChanges();

		// assert - the result must be 0 (default)
		assert.equal(oStepInput.getValue(), iInvalidResult, "Value of the StepInput is correct when enter invalid value (" + sInvalidValue + " => " + iInvalidResult + ")");

		// act - set min value and enter invalid value again

		// set min value
		oStepInput.setMin(iMinValueToSet);
		// enter invalid value again
		oInput.$(sInputSuffix).val(sInvalidValue);
		oStepInput._change();
		oCore.applyChanges();

		// assert - the result be at the min value (if min value > 0)
		assert.equal(oStepInput.getValue(), iMinValueToSet, "Value of the stepInput is correct when enter invalid value and there is a min value set (" + sInvalidValue + " => " + iMinValueToSet + ")");

		// act - enter valid value again
		oInput.$(sInputSuffix).val(iValidValue);
		oStepInput._change();
		oCore.applyChanges();

		// assert - the result must be what entered
		assert.equal(oStepInput.getValue(), iValidValue, "Value of the StepInput is correct when enter valid value (" + iValidValue + " => " + iValidValue + ")");

		// act - enter invalid value (empty string)
		oInput.$(sInputSuffix).val(sInvalidEmpty);
		oStepInput._change();
		oCore.applyChanges();

		// assert - the result must be min value (if min value > 0)
		assert.equal(oStepInput.getValue(), iMinValueToSet, "Value of the StepInput is correct when enter invalid value (" + sInvalidEmpty + " => " + iMinValueToSet + ")");

		// cleanup
		oStepInput.destroy();
	});

	QUnit.test("StepInput can be focused", function (assert) {
		// arrange
		var oStepInput = new StepInput();

		oStepInput.placeAt('qunit-fixture');
		oCore.applyChanges();

		// act
		oStepInput.focus();

		// assert
		assert.equal(oStepInput.getFocusDomRef().id, document.activeElement.id, "StepInput's internal NumericInput is focused");

		// cleanup
		oStepInput.destroy();
	});

});