'use strict';
/**
* @module br/test/ViewFixture
*/
/**
* @class
* @alias module:br/test/ViewFixture
*
* @classdesc
* <p>The <code>ViewFixture</code> enables interacting with the rendered view via <code>ViewFixtureHandlers</code>. An
* element in the view can be selected with jQuery selectors. In Given and When phases the selected element in the
* view as well as its desired value will be passed as arguments to the <code>set()</code> method of a
* <code>ViewFixtureHandler</code> which will update the element accordingly. In the Then phase the same arguments
* will be passed to the <code>get()</code> method of a <code>ViewFixtureHandler</code>, which will then inspect the
* selected view element and return a value of a particular property of this element to the <code>ViewFixture</code>.
* The <code>ViewFixture</code> should mainly be used to check that the bindings between view elements in templates
* and the corresponding presentation model properties have been specified correctly. A test might set a value on the
* view element in the Given or When phases and then check in the Then phase that this value has been updated after
* updating the relevant presentation model property.</p>
*
* <p>Assuming that the <code>ViewFixture</code> has been added with the identifier <code>view</code> as a subfixture
* of the <code>ComponentFixture</code> which has the identifier <code>form</code>, then the <code>ViewFixture</code>
* can be used in the following way in a test:</p>
*
* <pre>then("form.view.(.orderSummary [identifier=\'orderStatus\']).text = 'complete'");</pre>
*
* <p>In the above example the jQuery selector for the element in the view is
* <code>.spotGeneralSummary [identifier=\'dealSubmittedFor\']</code> and it must be specified within parentheses. The
* following part of the statement, <code>.text = 'test phrase'</code>, specifies the ViewFixtureHandler
* (<code>Text</code>) and the value (<code>'test phrase'</code>) which will be passed to it. The <code>Text</code>
* <code>ViewFixtureHandler</code> will then get the text value of the selected view element and return this value to
* the <code>ViewFixture</code>. The test will pass if the text value of the selected view element is indeed equal to
* <code>'test phrase'</code>.</p>
*/
var jQuery = require('jquery');
var br = require('br/Core');
var Errors = require('br/Errors');
var Fixture = require('br/test/Fixture');
/**
* Constructs a <code>br.test.ViewFixture</code>.
* @implements module:br/test/Fixture
* @alias module:br/test/ViewFixture
* @class
* @param {String} viewSelector (optional) CSS selector to identify the parent view element for this fixture
*/
function ViewFixture(viewSelector) {
this.m_sViewSelector = viewSelector || null;
var Blurred = require('br/test/viewhandler/Blurred');
var Checked = require('br/test/viewhandler/Checked');
var ChildrenCount = require('br/test/viewhandler/ChildrenCount');
var ClassName = require('br/test/viewhandler/ClassName');
var Clicked = require('br/test/viewhandler/Clicked');
var BackgroundImage = require('br/test/viewhandler/BackgroundImage');
var DoesNotHaveClass = require('br/test/viewhandler/DoesNotHaveClass');
var Enabled = require('br/test/viewhandler/Enabled');
var FocusIn = require('br/test/viewhandler/FocusIn');
var FocusOut = require('br/test/viewhandler/FocusOut');
var Focused = require('br/test/viewhandler/Focused');
var HasClass = require('br/test/viewhandler/HasClass');
var Height = require('br/test/viewhandler/Height');
var IsVisible = require('br/test/viewhandler/IsVisible');
var MouseDown = require('br/test/viewhandler/MouseDown');
var MouseMove = require('br/test/viewhandler/MouseMove');
var MouseOut = require('br/test/viewhandler/MouseOut');
var MouseOver = require('br/test/viewhandler/MouseOver');
var MouseUp = require('br/test/viewhandler/MouseUp');
var MouseWheel = require('br/test/viewhandler/MouseWheel');
var OnKeyUp = require('br/test/viewhandler/OnKeyUp');
var Options = require('br/test/viewhandler/Options');
var Readonly = require('br/test/viewhandler/Readonly');
var RightClicked = require('br/test/viewhandler/RightClicked');
var ScrolledHorizontal = require('br/test/viewhandler/ScrolledHorizontal');
var ScrolledVertical = require('br/test/viewhandler/ScrolledVertical');
var Selected = require('br/test/viewhandler/Selected');
var Text = require('br/test/viewhandler/Text');
var TypedValue = require('br/test/viewhandler/TypedValue');
var Value = require('br/test/viewhandler/Value');
var Width = require('br/test/viewhandler/Width');
var BorderWidth = require('br/test/viewhandler/BorderWidth');
var BorderColor = require('br/test/viewhandler/BorderColor');
var TopMarginWidth = require('br/test/viewhandler/TopMarginWidth');
var BottomMarginWidth = require('br/test/viewhandler/BottomMarginWidth');
var RightMarginWidth = require('br/test/viewhandler/RightMarginWidth');
var LeftMarginWidth = require('br/test/viewhandler/LeftMarginWidth');
var Color = require('br/test/viewhandler/Color');
var OnKeyDown = require('br/test/viewhandler/OnKeyDown');
var Top = require('br/test/viewhandler/Top');
this.m_mViewHandlers = {
blurred: new Blurred(),
checked: new Checked(),
childrenCount: new ChildrenCount(),
className: new ClassName(),
clicked: new Clicked(),
backgroundImage: new BackgroundImage(),
doesNotHaveClass: new DoesNotHaveClass(),
enabled: new Enabled(),
focusIn: new FocusIn(),
focusOut: new FocusOut(),
focused: new Focused(),
hasClass: new HasClass(),
height: new Height(),
isVisible: new IsVisible(),
mouseDown: new MouseDown(),
mouseMove: new MouseMove(),
mouseOut: new MouseOut(),
mouseOver: new MouseOver(),
mouseUp: new MouseUp(),
mouseWheel: new MouseWheel(),
onKeyUp: new OnKeyUp(),
options: new Options(),
readonly: new Readonly(),
rightClicked: new RightClicked(),
scrolledHorizontal: new ScrolledHorizontal(),
scrolledVertical: new ScrolledVertical(),
selected: new Selected(),
text: new Text(),
typedValue: new TypedValue(),
value: new Value(),
width: new Width(),
borderWidth: new BorderWidth(),
borderColor: new BorderColor(),
topMarginWidth: new TopMarginWidth(),
bottomMarginWidth: new BottomMarginWidth(),
rightMarginWidth: new RightMarginWidth(),
leftMarginWidth: new LeftMarginWidth(),
color: new Color(),
onKeyDown: new OnKeyDown(),
top: new Top()
};
this.m_mSelectorMappings = {};
}
br.inherit(ViewFixture, Fixture);
ViewFixture.prototype.setUp = function() {
var viewElements;
if (this.m_sViewSelector) {
viewElements = jQuery(this.m_sViewSelector);
this._verifyOnlyOneElementSelected(viewElements, this.m_sViewSelector);
this.setViewElement(viewElements[0]);
}
};
ViewFixture.prototype.tearDown = function() {
this.m_eViewElement = null;
if (this.m_oBlurHandler) {
this.m_oBlurHandler.destroy();
}
};
/**
* Allows custom view handlers to be added.
* @param {Map} viewHandlersMap A map of handler name to handler class constructor reference.
* @throws {br.Errors.InvalidParametersError} If an attempt is made to override an existing handler.
*/
ViewFixture.prototype.addViewHandlers = function(viewHandlersMap) {
var keys = Object.keys(viewHandlersMap),
existingHandlers = [];
keys.forEach(function(key) {
if (this.m_mViewHandlers.hasOwnProperty(key)) {
existingHandlers.push('\'' + key + '\'');
return;
}
}, this);
if (existingHandlers.length > 0) {
throw new Errors.InvalidParametersError(
'The following view handlers were not added to the registry as they already exist: ' +
existingHandlers.join(',')
);
}
keys.forEach(function(key) {
this.m_mViewHandlers[key] = new (viewHandlersMap[key])();
}, this);
};
/**
* Set the selector mappings to use with this fixture.
* <p>This allows users to create a shorthand for a selector, so that the same selector doesn't need to be repeated
* across different tests.</p>
* <p>Calling: <code>viewFixture.setSelectorMappings({'my-mapping': '.some .selector'});</code> then allows you to use
* that mapping in the test: <code>then("form.view.(my-mapping).text = 'foo'");</code>.</p>
* @param {Object} selectorMappings Map of selector mappings.
*/
ViewFixture.prototype.setSelectorMappings = function(selectorMappings) {
this.m_mSelectorMappings = selectorMappings;
};
ViewFixture.prototype.setViewElement = function(viewElement) {
var BlurHandler = require('br/test/viewhandler/BlurHandler');
this.m_eViewElement = viewElement;
this.m_oBlurHandler = new BlurHandler(viewElement);
};
ViewFixture.prototype.getViewElement = function() {
return this.m_eViewElement;
};
ViewFixture.prototype.setViewElementWithoutAttachingBlurHandler = function(viewElement) {
this.m_eViewElement = viewElement;
};
ViewFixture.prototype.setComponent = function(Component) {
this.m_oComponent = Component;
};
ViewFixture.prototype.getComponent = function() {
return this.m_oComponent;
};
ViewFixture.prototype.canHandleProperty = function(propertyName) {
return true;
};
ViewFixture.prototype.canHandleExactMatch = function() {
return false;
};
ViewFixture.prototype.doGivenAndDoWhen = function(propertyName, value) {
var handler = this._getHandler(propertyName, value);
if (handler.property === 'count') {
throw new Errors.InvalidTestError('The "count" property can only be used in then statements.');
} else {
handler.viewFixtureHandler.set(handler.selectedElement, value);
}
};
ViewFixture.prototype.doGiven = ViewFixture.prototype.doGivenAndDoWhen;
ViewFixture.prototype.doWhen = ViewFixture.prototype.doGivenAndDoWhen;
ViewFixture.prototype.doThen = function(propertyName, Value) {
var handler = this._getHandler(propertyName, Value);
if (handler.property === 'count') {
assertEquals('"count" should be ' + Value, Value, handler.elements.length);
} else {
assertEquals(
'"' + handler.property + '" should be ' + Value,
Value,
handler.viewFixtureHandler.get(handler.selectedElement, Value)
);
}
};
/** @private */
ViewFixture.prototype._getHandler = function(propertyName, value) {
var handler = {};
handler.property = this._getPropertyName(propertyName);
handler.elements = this._getViewElements(propertyName);
if (handler.property !== 'count') {
handler.viewFixtureHandler = this._getViewHandler(handler.property);
if (handler.elements.length === 1) {
handler.selectedElement = handler.elements[0];
} else {
this._verifyOnlyOneElementSelected(handler.elements, handler.property, propertyName, value);
}
}
return handler;
};
/** @private */
ViewFixture.prototype._getPropertyName = function (propertyName) {
return propertyName.match(/[^\.]*$/)[0];
};
/** @private */
ViewFixture.prototype._getViewElements = function(propertyName) {
var selector = propertyName.match(/\((.*)\)\.[^.]+/)[1];
if (typeof this.m_mSelectorMappings[selector] !== 'undefined') {
selector = this.m_mSelectorMappings[selector];
}
return jQuery(this.m_eViewElement).find(selector);
};
/** @private */
ViewFixture.prototype._verifyOnlyOneElementSelected = function(elements, viewHandler, propertyName, value) {
var exceptionMessage = '';
if (elements.length === 0) {
exceptionMessage = 'No view element found for "' + viewHandler + '".';
} else if (elements.length > 1) {
exceptionMessage = 'More than one view element found for "' + viewHandler + '".';
}
if (exceptionMessage !== '') {
if (typeof propertyName !== 'undefined') {
exceptionMessage += ' Processing property "' + propertyName + '" and looking for value "' + value + '".';
}
throw exceptionMessage;
}
};
ViewFixture.prototype._getViewHandler = function(propertyName) {
var handler = this.m_mViewHandlers[propertyName];
if (!handler) {
throw new Errors.InvalidTestError('Undefined view fixture handler "' + propertyName + '"');
}
return handler;
};
module.exports = ViewFixture;