【问题标题】:Canonical way to debug Protractor-to-Angular sync issues调试量角器到角度同步问题的规范方法
【发布时间】:2017-03-17 19:06:22
【问题描述】:

问题描述:

我们最近在 Protractor 端到端测试中打开应用程序中的一个页面时遇到了这个臭名昭著的错误:

失败:等待异步 Angular 任务在 50 秒后完成时超时。这可能是因为当前页面不是 Angular 应用程序。

这发生在我们的一项测试中的 browser.get("/some/page/"); 调用中:

describe("Test", function () {
    beforeEach(function () {
        browser.get("/some/page/");
    });

    it("should test something", function () {
        // ...
    });
)};

而且,我们案例的奇怪之处在于,在我们的 Angular Web 应用程序的任何其他页面上都没有抛出错误 - Protractor 与 Angular 同步没有任何问题。 ng-app 位置方面的事情在所有页面上都是相同的 - ng-app 是在根 html 标签上定义的:

<html class="ng-scope" lang="en-us" ng-app="myApp" ng-strict-di="">

行为是一致的 - 每次我们使用 browser.get() 导航到此页面时,都会收到此错误。每当我们导航到应用程序中的任何其他页面时,同步都会起作用。

请注意,当然,我们可以关闭此页面的同步并将其视为无角度的,但这只能被视为一种解决方法。

问题:

还有什么会导致 Protractor-to-Angular 同步失败?我们应该检查什么?

一般来说,在 Protractor 中调试同步问题的推荐方法是什么?

使用当前最新的 Protractor 5.5.1、Angular 1.5.6。

【问题讨论】:

  • 我猜有同步问题。 https://github.com/angular/protractor/blob/master/docs/timeouts.md#how-to-disable-waiting-for-angular。他们告诉的解决方案是关闭同步或可能使用driver.browser.get() 而不是browser.get()
  • @KishanPatel 谢谢,但正如我在问题中提到的,我知道我们可以关闭同步并将页面视为非角度 - 这是一种解决方法,它会起作用,但我们是试图弄清楚为什么同步不适用于此特定页面以及调试此类问题的一般准则。
  • 您的错误清楚地表明它可以因为 Angular 而同步,并且有两个原因会发生这种情况$http$timeout - protractor 将等待这两个服务完成。如果它是由$timeout 引起的,那么您应该简单地将其替换为$interval,而对于$http,除非您想编写一个XHR 服务并使用它而不是$http,否则没有解决方法
  • @maurycy 对,好点——我们以前听说过$timeout 问题(不记得$http 相关问题)。但是,我怎么能确定到底是什么导致了问题呢?如何通过调试检测同步失败的原因?...谢谢。

标签: javascript angularjs selenium protractor


【解决方案1】:

好的,这个问题引起了我的兴趣,所以我想出了一个关于如何确定量角器在等待什么的程序化解决方案:

var _injector = angular.element(document).injector();
var _$browser = _injector.get('$browser');
var _$http = _injector.get('$http');
var pendingTimeout = true;

//this is actually method that protractor is using while waiting to sync
//if callback is called immediately that means there are no $timeout or $http calls
_$browser.notifyWhenNoOutstandingRequests(function callback () {
  pendingTimeout = false
});

setTimeout(function () {
  //this is to differentiate between $http and timeouts from the "notifyWhenNoOutstandingRequests" method
  if (_$http.pendingRequests.length) {
    console.log('Outstanding $http requests', _$http.pendingRequests.length)
  } else if (pendingTimeout) {
    console.log('Outstanding timeout')
  } else {
    console.log('All fine in Angular, it has to be something else')
  }
}, 100)

这里在 plunker http://plnkr.co/edit/O0CkpnsnUuwEAV8I2Jil?p=preview 你可以试验一下超时和 $http 调用,我的延迟端点会等待 10 秒才能解决调用,希望这对你有帮助

【讨论】:

    【解决方案2】:

    我同意@maurycy 的观点,即问题与 $http/$timeout 有关。简单的解决方法通常是用 $interval 替换 $timeout,如下所述:https://github.com/angular/protractor/blob/master/docs/timeouts.md

    建议: merge these sane defaults: allScriptsTimeout: 60000, // 1 minute jasmineNodeOpts: { defaultTimeoutInterval: 300000 // 5 minutes. Allows for 5 commands spanning the full synchronization timeout. }

    如果你想找到 $http/$timeout 的罪魁祸首,我会使用 Angular 装饰器围绕这些服务应用自定义逻辑。这也是模拟 Angular 服务访问第三方服务的好方法。 https://docs.angularjs.org/guide/decorators

    //DISCLOSURE: Unlinted & Untested.
    beforeAll(() => {
      browser.addMockModule('culpritMock', () => {
        angular.module('culpritMock', [])
          .config(['$httpProvider',
            $httpProvider => $httpProvider.interceptors.push('httpCounterInterceptor')
          ])
          .factory('httpCounterInterceptor', ['$q', '$window', ($q, $window) => {
            if ($window.httpCounterInterceptor == null) {
              $window.httpCounterInterceptor = {};
            }
            return {
              request: config => {
                $window.httpCounterInterceptor[config.url] = 'started';
                return config;
              },
              response: response => {
                $window.httpCounterInterceptor[response.config.url] = 'completed';
                return response;
              },
              responseError: rejection => {
                $window.httpCounterInterceptor[rejection.config.url] = 'error';
                return $q.reject(rejection);
              }
            };
          }])
          .decorator('$timeout', ['$delegate', $delegate => {
            const originalTimeout = $delegate;
            function modifiedTimeout() {
              console.log(arguments);
             return originalTimeout.apply(null, arguments);
            }
            modifiedTimeout.cancel = function(promise) {
              return $delegate.cancel(promise);
            }
            return modifiedTimeout;
          }]);
      });
    });
    

    【讨论】:

      猜你喜欢
      • 2016-09-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多