我将此代码添加到我的组件控制器中以集中输入,它在浏览器中效果很好,但是破坏了我所有的模板测试。我以为我可以冲洗$timeout
,一切都会好起来,但事实并非如此。
vm.$onInit = init;
function init(){
focusInput();
}
function focusInput(){
$timeout(function(){
$document[0]
.querySelector('md-autocomplete-wrap')
.querySelector('input')
.focus();
}, 0);
}
但是,在我的单元测试中,Jasmine报告说.querySelector
不可用,因为第一个querySelector
的结果在测试环境中为空。
it('should render', function(){
var wrap, searchBarDirective, $scope;
$scope = $rootScope.$new();
searchBarDirective = $compile(angular.element(template))($scope);
$scope.$digest();
$timeout.flush();
wrap = searchBarDirective.find('md-autocomplete-wrap')[0];
expect(wrap).toBeDefined();
});
对我来说很明显$document
不包含呈现的指令,因此第二个querySelector
失败了。但是,为什么$ document不包含指令?
我尝试用spyOn($document[0], "querySelector").and.returnValues($document[0],$document[0])
嘲笑querySelector,但这并不能使我超越focus
。想我在这里有很多路。
*修订版*
我认为继续使用$ document很重要,但是我决定为jqLite querySelector
方法删除find
。
function focusInput(){
$timeout(function(){
var input;
try {
// can throw an error if the first find fails
input = $document.find('md-autocomplete').find('input');
}
catch (e) {
angular.noop(e);
}
if(input && angular.isFunction(input.focus)) {
input.focus();
}
}, 0);
}
我根据评论将测试更改为以下内容。我确实有Karma load jquery来简化测试,这使我可以搜索:focus
beforeEach(function(){
element = angular.element(template);
$document[0].body.appendChild(element[0]);
$scope = $rootScope.$new();
});
afterEach(function(){
element[0].remove();
});
it('should be focused', function(){
var input, searchBarDirective;
searchBarDirective = $compile(element)($scope);
$scope.$digest();
$timeout.flush();
input = searchBarDirective.find(':focus')[0];
expect(input).toBeDefined();
});
答案 0 :(得分:1)
querySelector
调用在浏览器中有效但在测试中无效的原因是,您正在使用angular.element
创建DOM元素,但从未将其附加到文档中。有两种解决方法:
首先,您可以简单地执行此操作。代替:
searchBarDirective = $compile(angular.element(template))($scope);
要做:
let element; // declare this in the describe block so it is available later
element = angular.element(template);
document.body.appendChild(element[0]);
searchBarDirective = $compile(element)($scope);
然后执行以下操作:
afterEach(() => element[0].remove());
但是,这有点混乱。除非必须这样做,否则不应在单元测试中操纵全局范围(即文档)。在您的非测试代码中最好避免访问文档,而是访问一个scope元素,或者也可以在测试中模拟的其他DOM元素。这将很难完成,因为可能需要重新架构一下代码。不过,总的来说,为了制作模块化且可测试的代码,您要避免尽可能多地访问document
对象。