Используя Ember (cli), как мне пройти приемочный тест, чтобы дождаться обещания?

В моем приложении Ember у меня есть модель сfindResults функция, которая возвращает обещание, заключающее в себе библиотеку Google Адресов, для получения результатов для автозаполнения. Чтобы использовать это в своем пользовательском интерфейсе, я установил контроллер PromiseMixin. Я приказываю контролеру смотретьsearchText значение, и когда это изменяется, я обновляю значение обещания контроллера, чтобы быть обещанием, возвращеннымfindResults функция, но с новым значением изsearchText, Это хорошо работает, когда я играю с приложением в браузере, однако, когда я запускаю свои приемочные тесты, кажется, что тест завершается до того, как обещание будет возвращено, и поэтому тесты не пройдены. Я включу соответствующие файлы ниже.

Я не знаю, как сказать Эмбер, чтобы он дождался выполнения обещания во время тестирования.

приложение / услуги / Google-автозаполнения-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')
});

тесты / прием / Create-новый въездной-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');
  });
});
 DanF06 мар. 2015 г., 20:53
Я открыл вопрос об этом поведении при тестировании угря, не стесняйтесь:github.com/emberjs/ember.js/issues/10578

Ответы на вопрос(4)

Я не могу воспроизвести вашу проблему в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();
   });
});
 jklina20 нояб. 2014 г., 01:25
Я только что попробовалstart() а такжеstop(), но не повезло. Если я заверну свои утверждения в методе setTimeout сstop() а такжеstart() это сработает, однако, если я уделю этому достаточно времени. Я подумал, что если я позвоню Ember.run в своих обещаниях, помощники по тестированию Ember будут ждать выполнения обещания.

и у меня были подобные трудности сегодня. я нашел это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 в экземпляр обещания, прежде чем вернуть его. Может быть, ответ здесь для вас, чтобы установитьEmber.Test.lastPromise на обещание вы ждете?

Кстати мне тоже пришлось пользоватьсяstop() а такжеstart() в моем случае, чтобы предотвратить выход теста до того, как был вызван второй assert. Мне также нужно было завернуть второе утверждение в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();
    });
  });
});

Надеюсь, это поможет!

 jklina22 нояб. 2014 г., 01:37
Ember.Test.promise сделал это сstop() а такжеstart(), Я не помню, чтобы видел это в документации по тестированию и даже не знал, что она существует. Так рада, что ты указал на это. Большая помощь, спасибо!

если вы ожидаете обещание, которое выиграло, но не решило, но отклонило, вот патч, чтобы поймать ошибку и все еще передатьandThen Тестовый помощник.

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();
    });
  });
});

Решение Вопроса

ника по тестированию асинхронных данных. Я подготовил 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 асинхронный помощник будет автоматически ожидать предыдущих обещаний, а последующие асинхронные помощники будут ожидать его при выполнении теста. Для превосходного фона этого см. Кори ФорсайтДемистификация асинхронного тестирования

 Buck Doyle13 дек. 2014 г., 18:27
Мне кажется неудачным добавлять код в приложение только для выполнения тестов. Это потому, что это взаимодействие с внешним сервисом? Несмотря на мой дискомфорт, в моих тестах он исправлял периодические сбои в состоянии гонки, так что спасибо.
 Luke Melia14 дек. 2014 г., 06:35
@BuckDoyle ember-testing знает о многих асинхронных вещах в приложениях Ember (например, элементы, запланированные в цикле выполнения, операциях ajax и переходах маршрутов), но когда вы делаете что-то в своем приложении вне того, о чем знает ember-testing (как на этот вопрос), вы должны найти какой-то подход к ожиданию этого асинхронного действия в ваших тестах. Re: ваш компонент вопроса, мне нужно больше информации, чтобы ответить на этот вопрос.
 Buck Doyle14 дек. 2014 г., 08:45
Мне нужно больше смотреть на ember-тестирование, потому что я не использую внешнюю службу, это Ember Data.save()значит я что то не так делаю. Что касается компонентов, я понимаю, что моя текущая настройка является нарушением «данные отключены, действия вверх», поэтому, как только я решу, что смогу использовать то же решение для контроллера. Но как только контроллеры исчезают ... ?! Спасибо за ваш ответ!
 DanF07 мар. 2015 г., 01:34
Большое спасибо за этот обходной путь и объяснение, Люк! Я был очень расстроен, обнаружив такое поведение, и даже подал в Ember сообщение об этом. Не могли бы вы там перезвонить? Как вы думаете, это проблема, или вы считаете это разумным поведением?github.com/emberjs/ember.js/issues/10578
 Buck Doyle13 дек. 2014 г., 18:27
Есть ли у вас какие-либо предложения о том, как это будет работать с компонентом?

Ваш ответ на вопрос