我的代码应该存储有关过滤器更改的信息。
在浏览器$watch
中,每次更改值时都会触发回调。
然而,当我试图在jasmine / karma测试中复制它时,它会在第一次更改后立即触发。
控制器
var tableFilter = this
;
var init0 = true
;
$scope.$watch( 'tableFilter.config.period', function () {
console.log('watch');
if ( !init0 ) {
console.log('set dirty');
tbFilterConfig.set( 'pristine', false );
tbFilterConfig.setDirty( 'period' );
}
init0 = false;
} );
测试
describe( '$scope $watch', function () {
it( 'should add period to dirty after second change', function () {
$scope.$apply(console.log(1), controller.config.period = 'test');
expect( tbFilterConfigObj.get( 'dirty' ) ).toEqual( [] );
$scope.$apply(console.log(2), controller.config.period = 'test2');
expect( tbFilterConfigObj.get( 'dirty' ) ).toEqual( [ 'period' ] );
} );
});
控制台输出:
LOG: 1
LOG: 'watch'
LOG: 2
PhantomJS 1.9.8 (Windows 8 0.0.0) Controller: TableFilterCtrl $scope $watch should add period to dirty after second change FAILED
Expected [ ] to equal [ 'period' ].
at C:/Projects/trackback-network-insight-ui/test/spec/controllers/table_filter.js:74
但是在浏览器中:
setTimeout(function ( ) {
$scope.$apply(console.log(1), tableFilter.config.period = 'test');
$scope.$apply(console.log(2), tableFilter.config.period = 'test2');
}, 1000);
给我:
LOG: watch
LOG: 1
LOG: watch
LOG: set dirty
LOG: 2
LOG: watch
LOG: set dirty
这是预期的行为。
我的考试有什么问题呢?提前谢谢!
修改
完整控制器代码:
angular.module( 'insightApp' )
.controller( 'TableFilterCtrl', [ '$rootScope', 'tbConfig', '$scope', '$http', 'API_PATH', 'uiAlerts', 'tbFilterConfig',
function ( $rootScope, tbConfig, $scope, $http, API_PATH, uiAlerts, tbFilterConfig ) {
var tableFilter = this
, newData = {}
;
/* EXTRACT INITIAL DATA OR LOAD DATA IF TYPE OF DATA IS STRING */
/* FETCH !!SELECT!! DATA FROM API */
function fetchData( url, name ) {
return $http.post( API_PATH + url, tbFilterConfig.get() )
.success( function ( data ) {
newData[ name ] = data;
} )
.error( function ( error ) {
uiAlerts.set( {
error: 'Ooops! We were unable to get filter data.'
} );
console.error( error );
} );
}
tableFilter.filters = tbConfig.get( 'filters' );
tableFilter.reports = tbConfig.get( 'reports' );
tableFilter.config = tbFilterConfig.getRaw();
/* RESET VALUES OF ALL DEPENDENT FILTERS */
function resetDependent( name ) {
var filters = tbConfig.get( 'filters' )
;
filters.forEach( function ( filter ) {
if ( filter.dependencies && filter.dependencies.indexOf( name ) > -1 ) {
if ( tbFilterConfig.get( filter.key ) !== filter.defaultVal ) {
tbFilterConfig.set( filter.key, filter.defaultVal );
tbFilterConfig.setDirty( filter.key );
}
}
} );
}
/* GET INITIAL DATA FOR SELECT OPTIONS */
tableFilter.getData = function ( name, data, defaultVal ) {
if ( !(name in newData) ) {
newData[ name ] = [];
if ( typeof data === 'string' ) {
fetchData( data, name );
} else if ( typeof data === 'object' ) {
newData[ name ] = data;
} else {
console.error( 'Unexpected data type:', typeof data, data );
}
if ( !tbFilterConfig.get( name ) ) {
tbFilterConfig.set( name, defaultVal );
}
var init = true;
$scope.$watch( 'tableFilter.config.' + name, function () {
if ( !init ) {
resetDependent( name );
tbFilterConfig.set( 'pristine', false );
tbFilterConfig.setDirty( name );
}
init = false;
} );
}
return newData[ name ];
};
/* WATCH FIXED PROPERTIES: PERIOD, DATE_FROM, DATE_TO */
var init0 = true
, init1 = true
, init2 = true
;
$scope.$watch( 'tableFilter.config.period', function () {
if ( !init0 ) {
tbFilterConfig.set( 'pristine', false );
tbFilterConfig.setDirty( 'period' );
}
init0 = false;
} );
/* WATCH FIXED PROPERTIES DATE_FORM */
$scope.$watch( 'tableFilter.config.date_from', function () {
if ( !init1 ) {
tbFilterConfig.set( 'pristine', false );
tbFilterConfig.setDirty( 'date_from' );
}
init1 = false;
} );
/* WATCH FIXED PROPERTIES DATE_TO */
$scope.$watch( 'tableFilter.config.date_to', function () {
if ( !init2 ) {
tbFilterConfig.set( 'pristine', false );
tbFilterConfig.setDirty( 'date_to' );
}
init2 = false;
} );
/* UPDATE FILTER DATA */
tableFilter.updateSelectData = function ( url, name, dependencies ) {
if ( typeof url === 'string' ) {
var touched = false;
/* CHECK IF DEPENDENCIES CHANGED */
for ( var i = 0; i < dependencies.length; i++ ) {
if ( tableFilter.config.dirty.indexOf( dependencies[ i ] ) > -1 ) {
touched = true;
break;
}
}
/* IF DEPENDENCIES CHANGED GET NEW DATA */
if ( touched ) {
return fetchData( url, name );
}
}
};
} ] );
并测试:
describe( 'Controller: TableFilterCtrl', function () {
// load the controller's module
beforeEach( function () {
module( 'insightApp' );
} );
var controller
, $scope
, alerts
, tbFilterConfigObj
;
// Initialize the controller and a mock scope
beforeEach( inject( function ( $controller, $rootScope, uiAlerts, tbFilterConfig ) {
tbFilterConfigObj = tbFilterConfig;
$scope = $rootScope.$new();
alerts = uiAlerts;
controller = $controller( 'TableFilterCtrl', {
$scope: $scope
} );
$scope.$apply();
} ) );
describe( '$scope $watch', function () {
it( 'should add period to dirty after second change', function () {
$scope.$apply( console.log( 1 ), controller.config.period = 'test' );
expect( tbFilterConfigObj.get( 'dirty' ) ).toEqual( [] );
$scope.$apply( console.log( 2 ), controller.config.period = 'test2' );
expect( tbFilterConfigObj.get( 'dirty' ) ).toEqual( [ 'period' ] );
} );
} );
});
EDIT2
我很抱歉代码质量,但它尚未生产。
答案 0 :(得分:0)
当AngularJS在您的浏览器中运行时,它会运行digest
个周期来触发所有观察者及其回调。这就是使Angular起作用的原因,它会自动发生。
这种机制在单元测试中不起作用。运行测试时,您需要通过调用$apply
或$digest
手动触发摘要机制。
让我们分解您的测试规范中发生的情况(在控制台日志中很好地显示):
$scope.$apply(console.log(1), controller.config.period = 'test');
- &gt;将1
写入控制台;更改config.period
的值。
$scope.$apply(console.log(2), controller.config.period = 'test2');
- &gt;由于tableFilter.config.period
,首次触发$apply
的观察者回调;将2
写入控制台;更改config.period.
此后第二次没有任何东西触发观察者的回调。
解决方案很简单:在每个$scope.$digest()
语句之前添加对$scope.$apply()
(或expect
)的调用。