From 7a1e1a68e32ef21ac025999991763d093c83d88b Mon Sep 17 00:00:00 2001 From: Mike Gerwitz Date: Fri, 17 Jun 2016 12:01:12 -0400 Subject: [PATCH 1/4] GeneralStepUi answer styling method extraction This is just to enable some sort of testing without instantiating the entire class and navigating a maze of methods. * ui/step/GeneralStepUi.js (answerDataUpdate): Added (_processAnswerFields): Extracted function into answerDataUpate --- src/ui/step/GeneralStepUi.js | 31 +++++++++++++++++++++++-------- 1 file changed, 23 insertions(+), 8 deletions(-) diff --git a/src/ui/step/GeneralStepUi.js b/src/ui/step/GeneralStepUi.js index bb9eb76..1873b4d 100644 --- a/src/ui/step/GeneralStepUi.js +++ b/src/ui/step/GeneralStepUi.js @@ -407,14 +407,7 @@ module.exports = Class( 'StepUi' ) return; } - // give the UI a chance to update the DOM; otherwise, the - // answer elements we update may no longer be used (this also - // has performance benefits since it allows repainting before - // potentially heavy processing) - setTimeout( function() - { - _self._updateAnswerFieldData( data ); - }, 25 ); + _self.answerDataUpdate( data ); } ); doUpdate( bucket.getData() ); @@ -442,6 +435,28 @@ module.exports = Class( 'StepUi' ) }, + /** + * Update and style answer field data + * + * @param {Object} data key-value diff + * + * @return {undefined} + */ + 'virtual protected answerDataUpdate': function( data ) + { + var _self = this; + + // give the UI a chance to update the DOM; otherwise, the + // answer elements we update may no longer be used (this also + // has performance benefits since it allows repainting before + // potentially heavy processing) + setTimeout( function() + { + _self._updateAnswerFieldData( data ); + }, 25 ); + }, + + /** * Update DOM answer fields with respective datum in diff DATA * From 91903951c4fbbf745043dc2eb0c6c30f93336839 Mon Sep 17 00:00:00 2001 From: Mike Gerwitz Date: Mon, 20 Jun 2016 10:17:37 -0400 Subject: [PATCH 2/4] Extract direct answer context references into method This allows overriding for testing (and is a proper abstraction); eventually this will be moved out of this class entirely. * src/ui/step/GeneralStepUi.js (getAnswerContext): Added Number of methods updated to use. --- src/ui/step/GeneralStepUi.js | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/src/ui/step/GeneralStepUi.js b/src/ui/step/GeneralStepUi.js index 1873b4d..e4f8e7c 100644 --- a/src/ui/step/GeneralStepUi.js +++ b/src/ui/step/GeneralStepUi.js @@ -457,6 +457,19 @@ module.exports = Class( 'StepUi' ) }, + /** + * Retrieve answer DOM context + * + * @param {string} name field name + * + * @return {jQuery} DOM context + */ + 'virtual protected getAnswerContext': function( name ) + { + return this._answerContext[ name ]; + }, + + /** * Update DOM answer fields with respective datum in diff DATA * @@ -473,7 +486,7 @@ module.exports = Class( 'StepUi' ) // changed for ( var name in data ) { - if ( !( this._answerContext[ name ] ) ) + if ( !( this.getAnswerContext( name ) ) ) { continue; } @@ -488,7 +501,7 @@ module.exports = Class( 'StepUi' ) // update every index on the DOM i = this.styler.getAnswerElementByName( name, undefined, undefined, - this._answerContext[ name ] + this.getAnswerContext( name ) ).length; } @@ -530,7 +543,7 @@ module.exports = Class( 'StepUi' ) // if we've already found an element for this ref, then it is // referenced in multiple places; simply store the context as the // entire step - if ( _self._answerContext[ ref_id ] ) + if ( _self.getAnswerContext( ref_id ) ) { _self._answerContext[ ref_id ] = _self.$content; return; @@ -560,7 +573,7 @@ module.exports = Class( 'StepUi' ) 'private _updateAnswer': function( name, index, value ) { var $element = this.styler.getAnswerElementByName( - name, index, null, ( this._answerContext[ name ] || this.$content ) + name, index, null, ( this.getAnswerContext( name ) || this.$content ) ); var i = $element.length; From f5549795d50178ae530a95a468aeda2b3a8e8b9f Mon Sep 17 00:00:00 2001 From: Mike Gerwitz Date: Mon, 20 Jun 2016 10:52:11 -0400 Subject: [PATCH 3/4] GeneralStepUi vformat styling * package.json (devDependencies): Add sinon * src/ui/step/GeneralStepUi.js (answerDataUpdate): Attempt formatting with formatter prior to "old" answer styling. * test/ui/step/GeneralStepUiTest.js: Added --- package.json.in | 1 + src/ui/step/GeneralStepUi.js | 4 +- test/ui/step/GeneralStepUiTest.js | 138 ++++++++++++++++++++++++++++++ 3 files changed, 142 insertions(+), 1 deletion(-) create mode 100644 test/ui/step/GeneralStepUiTest.js diff --git a/package.json.in b/package.json.in index 3742cba..4c4cff8 100644 --- a/package.json.in +++ b/package.json.in @@ -20,6 +20,7 @@ "devDependencies": { "chai": ">=1.9.1", "mocha": ">=1.18.2", + "sinon": ">=1.17.4", "es6-promise": "~3" }, diff --git a/src/ui/step/GeneralStepUi.js b/src/ui/step/GeneralStepUi.js index e4f8e7c..3500848 100644 --- a/src/ui/step/GeneralStepUi.js +++ b/src/ui/step/GeneralStepUi.js @@ -446,13 +446,15 @@ module.exports = Class( 'StepUi' ) { var _self = this; + var data_fmt = _self._formatter.format( data ); + // give the UI a chance to update the DOM; otherwise, the // answer elements we update may no longer be used (this also // has performance benefits since it allows repainting before // potentially heavy processing) setTimeout( function() { - _self._updateAnswerFieldData( data ); + _self._updateAnswerFieldData( data_fmt ); }, 25 ); }, diff --git a/test/ui/step/GeneralStepUiTest.js b/test/ui/step/GeneralStepUiTest.js new file mode 100644 index 0000000..01796d7 --- /dev/null +++ b/test/ui/step/GeneralStepUiTest.js @@ -0,0 +1,138 @@ +/** + * Test case for GeneralStepUi + * + * Copyright (C) 2016 LoVullo Associates, Inc. + * + * This file is part of the Liza Data Collection Framework + * + * Liza is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +var Sut = require( '../../../' ).ui.step.GeneralStepUi, + expect = require( 'chai' ).expect, + sinon = require( 'sinon' ), + Class = require( 'easejs' ).Class; + + +describe( 'ui.GeneralStepUi', function() +{ + describe( 'on answer data change', function() + { + it( 'will attempt pre-style with formatter', function( done ) + { + var orig_data = { + foo: [ "orig" ], + }, + fmt_data = { + foo: [ "formatted" ], + }; + + var formatter = createFormatter(), + mock_fmt = sinon.mock( formatter ); + + mock_fmt.expects( 'format' ) + .once() + .withExactArgs( orig_data ) + .returns( fmt_data ); + + createSut( formatter, function( name, value ) + { + // by the time the answer data makes its way to the + // element styler, it should have already been + // formatted + expect( name ).to.equal( 'foo' ); + expect( value ).to.equal( fmt_data.foo[ 0 ] ); + + mock_fmt.verify(); + done(); + } ).answerDataUpdate( orig_data ); + } ); + } ); +} ); + + + +/** + * Create new SUT with formatter FORMATTER and generated element + * styler + * + * @param {Object} formatter validator/formatter mock + * @param {function(string,*)} style_callback styler styleAnswer method dfn + * + * @return {Sut} + */ +function createSut( formatter, style_callback ) +{ + return Sut.extend( + { + // visibility escalation + 'override answerDataUpdate': function( data ) + { + return this.__super( data ); + }, + + 'override getAnswerContext': function( name ) + { + return {}; + }, + } )( + {}, + createElementStyler( style_callback ), + formatter + ); +} + + +/** + * Create mock ElementStyler + * + * styleAnswer method is defined by STYLE_CALLBACK + * + * @return {Object} ElementStyler mock + */ +function createElementStyler( style_callback ) +{ + return { + getAnswerElementByName: function() + { + // jQuery element + return { + attributes: [], + length: 1, + + text: function() {}, + } + }, + + styleAnswer: style_callback, + }; +} + + +/** + * Create mock validator/formatter + * + * The only method provided is `format', which contains no definition; + * it is expected to be mocked by the caller. + * + * @param {Object} + */ +function createFormatter( expected, return_data ) +{ + return { + format: function() + { + }, + } +} From 2a6b170ac931c705fba39c6bb4ed8020e183cd93 Mon Sep 17 00:00:00 2001 From: Mike Gerwitz Date: Mon, 20 Jun 2016 11:39:27 -0400 Subject: [PATCH 4/4] Remove unneeded answer stylers These all have VFormat alternatives. * src/ui/ElementStyler.js (_answerStylers): Removed --- src/ui/ElementStyler.js | 86 ----------------------------------------- 1 file changed, 86 deletions(-) diff --git a/src/ui/ElementStyler.js b/src/ui/ElementStyler.js index 1fd1675..6c183be 100644 --- a/src/ui/ElementStyler.js +++ b/src/ui/ElementStyler.js @@ -84,44 +84,6 @@ module.exports = Class( 'ElementStyler', _answerStyles: { - 'currency': function( value ) - { - var pre = '$'; - - if ( !value ) - { - value = 0; - } - else if ( value < 0 ) - { - value *= -1; - pre = '-$'; - } - - var formatter = this._getAnswerStyler( 'number' ); - - return pre + formatter.call( this, ( +value ).toFixed( 2 ) ); - }, - - 'dollars': function( value ) - { - var formatter = this._getAnswerStyler( 'currency' ); - - return formatter.call( this, ( +value ).toFixed( 0 ) ); - }, - - 'float': function( value ) - { - if ( !value ) - { - value = 0; - } - - var formatter = this._getAnswerStyler( 'number' ); - - return formatter.call( this, ( +value ).toFixed( 2 ) ); - }, - 'limit': function( value, _, default_val ) { value = ( value + '' ).replace( ',', '' ); @@ -254,7 +216,6 @@ module.exports = Class( 'ElementStyler', return ret; }, - 'includeExclude': function( value, _, default_val ) { // use the default if no value @@ -268,7 +229,6 @@ module.exports = Class( 'ElementStyler', : 'Included'; }, - /* * display as a limit, rejected or default if available */ @@ -298,31 +258,6 @@ module.exports = Class( 'ElementStyler', return ret; }, - - /** - * format thousands - */ - 'number': function( value ) - { - var str = value.toString().split( '.' ); - var len = str[0].length, - ret = ''; - - for ( var i = 0; i < len; i++ ) - { - ret += str[0].charAt( i ); - - if ( ( ( len - i ) % 3 ) === 1 ) - { - ret += ','; - } - } - - str[0] = ret.replace( /,$/, '' ); - - return str.join( '.' ) - }, - 'state': function( value ) { return State.getName( value ); @@ -367,27 +302,6 @@ module.exports = Class( 'ElementStyler', : val; }, - 'manualDate': function( value ) - { - if( value.replace ) - { - return value.replace( - /^([0-9]{4})-([0-9]+)-([0-9]+)$/, - '$2/$3/$1' - ); - } - - return null; - }, - - 'date': function( value ) - { - var data = value.split( '-' ); - - // m/d/y - return data[1] + '/' + data[2] + '/' + data[0]; - }, - 'dateTime': function( value ) { var ret_val = new Date( ( +value ) * 1000 );