在我的Ember应用程序中,我目前有一个具有findResults
功能的模型,该模型返回一个包含Google Places库以获取自动完成结果的承诺。要在我的UI中使用它,我设置了一个PromiseMixin控制器。我指示控制器观察searchText
值,当更改时,我将控制器的promise值更新为findResults
函数返回的promise,但是使用{{searchText
的新值。 1}}。当我在浏览器中使用应用程序时,这很有效,但是当我运行验收测试时,测试似乎在返回promise之前完成,因此测试失败。我将在下面提供相关文件。
我不确定如何告诉Ember在测试期间等待解决的承诺。
应用/服务/谷歌自动填充-location.js
import Ember from "ember";
var googleAutocompleteLocation = Ember.Object.extend({
placeId: null,
description: null
});
googleAutocompleteLocation.reopenClass({
findResults: function(query) {
var self = this;
var promise = new Ember.RSVP.Promise(function(resolve, reject) {
var autocompleteService = new google.maps.places.AutocompleteService();
return autocompleteService.getPlacePredictions({ input: query },
function(predictions, status) {
if (status !== google.maps.places.PlacesServiceStatus.OK) {
Ember.run(null, reject, status);
}
else {
Ember.run(null, resolve, self._decorateGoogleResults(predictions));
}
});
});
return promise;
},
_decorateGoogleResults: function(predictions) {
var locations = [];
predictions.forEach(function(prediction) {
locations.push(
googleAutocompleteLocation.create({
placeId: prediction.place_id,
description: prediction.description
})
);
});
return locations;
}
});
export default googleAutocompleteLocation;
应用/控制器/ index.js
import Ember from "ember";
import GoogleLocation from "../services/google-location";
import GoogleAutocompleteLocation from '../services/google-autocomplete-location';
export default Ember.ArrayController.extend(Ember.PromiseProxyMixin, {
searchText: '',
map: null,
mapUrl: null,
actions: {
submit: function() {
return this.transitionToRoute('entries.new');
}
},
highlightedResult: function() {
if (this.get('model').length) {
return this.get('model')[0];
} else {
return null;
}
}.property('model'),
setMap: (function() {
if (this.get('highlightedResult') === null) {
return this.set('map', null);
} else {
if (this.get('map') === null) {
return this.set('map', GoogleLocation.create({
mapContainer: Ember.$('.maps-info'),
placeId: this.get('highlightedResult').placeId
}));
} else {
return this.get('map').set('placeId', this.get('highlightedResult').placeId);
}
}
}).observes('highlightedResult'),
searchTextChanged: (function() {
if (this.get('searchText').length) {
this.set('promise',
GoogleAutocompleteLocation.findResults(this.get('searchText')));
console.log(this.get('promise'));
} else {
this.set('model', []);
}
}).observes('searchText')
});
测试/接受/创建新条目-test.js
test('finding a location', function() {
expect(1);
visit('/');
click('.location-input input');
fillIn('.location-input input', "Los Angeles, CA");
andThen(function() {
var searchResult = find('.search-results ul li:first a').text();
equal(searchResult, 'Los Angeles, CA, United States');
});
});
答案 0 :(得分:15)
解决此问题的最佳方法是注册您自己的异步测试帮助程序。我已经准备了一个JSBin,模拟了你的代码和解决方案:http://jsbin.com/ziceratana/3/edit?html,js,output
用于创建帮助程序的代码是:
Ember.Test.registerAsyncHelper('waitForControllerWithPromise', function(app, controllerName) {
return new Ember.Test.promise(function(resolve) {
// inform the test framework that there is an async operation in progress,
// so it shouldn't consider the test complete
Ember.Test.adapter.asyncStart();
// get a handle to the promise we want to wait on
var controller = app.__container__.lookup('controller:' + controllerName);
var promise = controller.get('promise');
promise.then(function(){
// wait until the afterRender queue to resolve this promise,
// to give any side effects of the promise resolving a chance to
// occur and settle
Ember.run.schedule('afterRender', null, resolve);
// inform the test framework that this async operation is complete
Ember.Test.adapter.asyncEnd();
});
});
});
它会像这样使用:
test('visiting / and searching', function() {
expect(1);
visit('/');
click('.location-input input');
fillIn('.location-input input', "Los Angeles, CA");
waitForControllerWithPromise('index'); // <-- simple & elegant!
andThen(function(){
var searchResult = find('.search-results ul li:first').text();
equal(searchResult, 'Los Angeles, CA, United States');
});
});
在ember-testing中,async-helper将自动等待先前的promises,随后的异步助手会在执行测试时等待它。有关此方面的优秀背景,请参阅Cory Forsyth的Demystifying Async Testing
答案 1 :(得分:10)
我对这些东西不熟悉,今天遇到了类似的困难。我发现andThen
只会等待使用Ember Test承诺创建的承诺,
var promise = Ember.Test.promise(function (resolve, reject) {...});
而不是承诺直接实例化的那些,即
var promise = new Ember.RSVP.Promise(function (resolve, reject) {...});
Ember.Test.promise
返回new Ember.RSVP.Promise
,但也会在返回之前将Ember.Test.lastPromise
设置为promise实例。也许这里的答案是你将Ember.Test.lastPromise
设置为你正在等待的承诺?
顺便说一句,在我的情况下,我还必须使用stop()
和start()
来阻止在调用第二个断言之前退出测试。我还需要在run.next
调用中包装第二个断言,以便为属性/ DOM提供更新的机会:
test('shows content when unresolved promise resolves true', function() {
expect(2);
var resolveTestPromise;
var testPromise = Ember.Test.promise(function (resolve) {
resolveTestPromise = resolve;
});
// creates the component instance, stubbing the security service and template properties
var component = this.subject({
securityService: CreateMockSecurityService(testPromise),
template: Ember.Handlebars.compile('<div id="if-may-test-div" />')
});
// appends the component to the page
var $component = this.append();
// our div shouldn't be visible yet
equal($component.find('div#if-may-test-div').length, 0);
stop();
Ember.run.later(null, function () {resolveTestPromise(true);}, 1000);
andThen(function () {
Ember.run.next(function () {
// div should be visible now
equal($component.find('div#if-may-test-div').length, 1);
start();
});
});
});
希望有所帮助!
答案 2 :(得分:1)
我无法在JSBin中重现您的问题,但您尝试过stop()
和start()
。在你的情况下:
test('finding a location', function() {
expect(1);
stop();
visit('/')
.click('.location-input input')
.fillIn('.location-input input', "Los Angeles, CA")
.then(function() {
var searchResult = find('.search-results ul li:first a').text();
equal(searchResult, 'Los Angeles, CA, United States');
start();
});
});
答案 3 :(得分:1)
如果您正在等待一个不会解决但拒绝的承诺,这里有一个补丁来捕获错误并仍然传递andThen
ember测试助手。
Ember.Test.registerAsyncHelper('waitForControllerWithPromise', function(app, controllerName) {
return new Ember.Test.promise(function(resolve, reject) {
// inform the test framework that there is an async operation in progress,
// so it shouldn't consider the test complete
Ember.Test.adapter.asyncStart();
// get a handle to the promise we want to wait on
var controller = app.__container__.lookup('controller:' + controllerName);
var promise = controller.get('promise');
promise.then(function(){
// wait until the afterRender queue to resolve this promise,
// to give any side effects of the promise resolving a chance to
// occur and settle
Ember.run.schedule('afterRender', null, resolve);
// inform the test framework that this async operation is complete
Ember.Test.adapter.asyncEnd();
}).catch(function() {
// when the promise is rejected, resolve it to pass in `andThen()`
Ember.run.schedule('afterRender', null, resolve);
Ember.Test.adapter.asyncEnd();
});
});
});
&#13;