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

9

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

Ваш Ответ

4   ответа
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 в экземпляр обещания, прежде чем вернуть его. Может быть, ответ здесь для вас, чтобы установить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();
    });
  });
});

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

Ember.Test.promise сделал это сstop() а такжеstart(), Я не помню, чтобы видел это в документации по тестированию и даже не знал, что она существует. Так рада, что ты указал на это. Большая помощь, спасибо! jklina
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();
   });
});
Я только что попробовалstart() а такжеstop(), но не повезло. Если я заверну свои утверждения в методе setTimeout сstop() а такжеstart() это сработает, однако, если я уделю этому достаточно времени. Я подумал, что если я позвоню Ember.run в своих обещаниях, помощники по тестированию Ember будут ждать выполнения обещания. jklina
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 асинхронный помощник будет автоматически ожидать предыдущих обещаний, а последующие асинхронные помощники будут ожидать его при выполнении теста. Для превосходного фона этого см. Кори ФорсайтДемистификация асинхронного тестирования

@BuckDoyle ember-testing знает о многих асинхронных вещах в приложениях Ember (например, элементы, запланированные в цикле выполнения, операциях ajax и переходах маршрутов), но когда вы делаете что-то в своем приложении вне того, о чем знает ember-testing (как на этот вопрос), вы должны найти какой-то подход к ожиданию этого асинхронного действия в ваших тестах. Re: ваш компонент вопроса, мне нужно больше информации, чтобы ответить на этот вопрос. Luke Melia
Мне кажется неудачным добавлять код в приложение только для выполнения тестов. Это потому, что это взаимодействие с внешним сервисом? Несмотря на мой дискомфорт, в моих тестах он исправлял периодические сбои в состоянии гонки, так что спасибо. Buck Doyle
Большое спасибо за этот обходной путь и объяснение, Люк! Я был очень расстроен, обнаружив такое поведение, и даже подал в Ember сообщение об этом. Не могли бы вы там перезвонить? Как вы думаете, это проблема, или вы считаете это разумным поведением?github.com/emberjs/ember.js/issues/10578 DanF
Мне нужно больше смотреть на ember-тестирование, потому что я не использую внешнюю службу, это Ember Data.save()значит я что то не так делаю. Что касается компонентов, я понимаю, что моя текущая настройка является нарушением «данные отключены, действия вверх», поэтому, как только я решу, что смогу использовать то же решение для контроллера. Но как только контроллеры исчезают ... ?! Спасибо за ваш ответ! Buck Doyle
Есть ли у вас какие-либо предложения о том, как это будет работать с компонентом? Buck Doyle
1

если вы ожидаете обещание, которое выиграло, но не решило, но отклонило, вот патч, чтобы поймать ошибку и все еще передать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();
    });
  });
});

Похожие вопросы