Protractor: 使用 ngUpgrade 引导应用程序时,量角器失败

创建于 2017-05-16  ·  51评论  ·  资料来源: angular/protractor

我正在将 AngularJS (1.6) 应用程序迁移到 Angular (4),现在有一个混合应用程序,使用 NgUpgrade 进行引导。 这似乎完全破坏了我的量角器测试。

如果这只是我做错的事情,请原谅我,但我找不到任何关于为什么这不起作用的答案。

我得到的错误是:

失败:等待异步 Angular 任务在 11 秒后完成超时。 这可能是因为当前页面不是 Angular 应用程序。 有关更多详细信息,请参阅常见问题解答: https :

在等待带有定位器的元素时 - Locator: By(css selector, .toggle-summary-button)

混合应用更改

该应用程序似乎运行良好,AngularJS 和 Angular 组件都按预期工作。 我在引导过程中所做的更改是:

1 从 html 标签中删除了 ng-app:

<html lang="en" *ng-app="myapp">

2 添加了一个 AppModules (@NgModule) 等。

3 使用 NgUpgrade 来引导应用程序:

import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';

import { UpgradeModule } from '@angular/upgrade/static';
import { AppModule } from './app.module';

platformBrowserDynamic().bootstrapModule(AppModule).then(platformRef => {
  const upgrade = platformRef.injector.get(UpgradeModule) as UpgradeModule;
  upgrade.bootstrap(document.body, ['myapp'], {strictDi: true});
});

量角器测试

基于上面的错误,问题似乎与 Protractor 在等待 Angular 时所做的任何事情有关。 我有一个 beforeEach 块,它加载登录页面,填写详细信息并登录。奇怪的是,它仍在打开页面并在用户名字段中输入文本,但随后无法继续。

我尝试过,但没有成功:

  • 将“rootElement:'body'”添加到我的量角器配置文件中
  • 将“ng12Hybrid:true”添加到我的量角器配置文件 - 我收到一条消息,说它应该不再需要,因为它会自动检测。
  • 将 allScriptsTimeout 设置从 11000 增加到 60000,但它仍然超时。
  • 关闭 waitForAngularEnabled 设置。 这解决了登录字段的问题,但是我的 http 模拟都不起作用,测试失败。

最有用的评论

您好 - 我们正在进行一项更改,让您可以配置量角器等待哪些任务,这可以帮助您处理上述问题。 这是一个涉及 Zone.js 和 Angular 的广泛变化,没有特定的 ETA(将在几周内)。 将在此处更新进度。

所有51条评论

我有类似的问题,通过包装这个升级代码解决了这样的问题:

import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';

import { UpgradeModule } from '@angular/upgrade/static';
import { AppModule } from './app.module';

angular.element(document).ready(() => {
  platformBrowserDynamic().bootstrapModule(AppModule).then(platformRef => {
    const upgrade = platformRef.injector.get(UpgradeModule) as UpgradeModule;
    upgrade.bootstrap(document.body, ['myapp'], {strictDi: true});
  });
});

在配置中我没有使用任何一个:

//ng12Hybrid: true,
//useAllAngular2AppRoots: true,
//rootElement: 'body',

你能看看https://github.com/angular/protractor/issues/4234看看你是否有同样的问题吗? 此外,在 ngUpgrade 中, $interval现在将阻止之前没有的 Protractor(https://github.com/angular/angular/issues/16349),但我正在努力解决这个问题。

@Evilweed - 感谢您的建议,不幸的是,这似乎并没有为我解决。

@heathkit - 我通过搜索整个项目找到了一个长时间运行的 $interval,但即使删除它后,问题仍然存在。 我还注释掉了整个应用程序中 $interval 或 $timeout 的所有用法,但它仍然失败并出现相同的错误。 您是否知道是否有任何方法可以调试并找出如果异步出现问题可能会阻塞的是什么?

你能发布你的完整错误吗? 它应该在错误消息中显示哪些任务正在挂起,但它可能会丢失一些。 例如,错误还不会显示来自 Angular 端的待处理任务。 如果您在应用程序的 Angular 部分长时间运行异步任务,则需要在 Angular 区域之外运行这些任务,以免阻塞 Protractor。 (见 https://github.com/angular/protractor/blob/master/docs/timeouts.md)

再次感谢您的回复。 错误详情如下。 需要注意的几点:

  • 我的测试文件有一个beforeAll块,它应该填写用于登录的用户名和密码字段。 它成功地填写了用户名字段,但随后在waitForAngular并且没有填写密码字段。 这就是错误的前半部分似乎是关于的。 后半部分是实际的失败测试(这是有道理的,因为它无法登录,因此该元素不存在)。

  • 该错误表明问题可能源于 HTTP 调用,因此可能不是 $interval 或 $timeout。

  • 目前我在 Angular 方面没有太多东西,它几乎仍然是 AngularJS。 它没有显示新的 Angular 组件,因为它无法登录(所以我怀疑 http 调用与 Angular 4 组件有关)

  Message:
    Failed: Timed out waiting for asynchronous Angular tasks to finish after 11 seconds. This may be because the current page is not an Angular application. Please see the FAQ for more details: https://github.com/angular/protractor/blob/master/docs/timeouts.md#waiting-for-angular
    While waiting for element with locator - Locator: By(css selector, *[name="password"])
  Stack:
    ScriptTimeoutError: asynchronous script timeout: result was not received in 11 seconds
      (Session info: chrome=58.0.3029.110)
      (Driver info: chromedriver=2.29.461585,platform=Mac OS X 10.12.4 x86_64) (WARNING: The server did not provide any stacktrace information)
    Command duration or timeout: 11.07 seconds
    Build info: version: '3.4.0', revision: 'unknown', time: 'unknown'
    System info: host: 'Matt.lan', os.name: 'Mac OS X', os.arch: 'x86_64', os.version: '10.12.4', java.version: '1.8.0_111'
    Driver info: org.openqa.selenium.chrome.ChromeDriver
    Capabilities [{applicationCacheEnabled=false, rotatable=false, mobileEmulationEnabled=false, networkConnectionEnabled=false, chrome={chromedriverVersion=2.29.461585, takesHeapSnapshot=true, pageLoadStrategy=normal, databaseEnabled=false, handlesAlerts=true, hasTouchScreen=false, version=58.0.3029.110, platform=MAC, browserConnectionEnabled=false, nativeEvents=true, acceptSslCerts=true, locationContextEnabled=true, webStorageEnabled=true, browserName=chrome, takesScreenshot=true, javascriptEnabled=true, cssSelectorsEnabled=true, unexpectedAlertBehaviour=}]
    Session ID: 40deafceb49b72b1c0188ad0895c1179
        at Object.checkLegacyResponse (/Users/matt/web-app/node_modules/selenium-webdriver/lib/error.js:505:15)
        at parseHttpResponse (/Users/matt/web-app/node_modules/selenium-webdriver/lib/http.js:509:13)
        at doSend.then.response (/Users/matt/web-app/node_modules/selenium-webdriver/lib/http.js:440:13)
        at process._tickCallback (internal/process/next_tick.js:109:7)
    From: Task: Protractor.waitForAngular() - Locator: By(css selector, *[name="password"])
        at thenableWebDriverProxy.schedule (/Users/matt/web-app/node_modules/selenium-webdriver/lib/webdriver.js:816:17)
        at ProtractorBrowser.executeAsyncScript_ (/Users/matt/web-app/node_modules/protractor/lib/browser.ts:608:24)
        at angularAppRoot.then (/Users/matt/web-app/node_modules/protractor/lib/browser.ts:642:23)
        at ManagedPromise.invokeCallback_ (/Users/matt/web-app/node_modules/selenium-webdriver/lib/promise.js:1366:14)
        at TaskQueue.execute_ (/Users/matt/web-app/node_modules/selenium-webdriver/lib/promise.js:2970:14)
        at TaskQueue.executeNext_ (/Users/matt/web-app/node_modules/selenium-webdriver/lib/promise.js:2953:27)
        at asyncRun (/Users/matt/web-app/node_modules/selenium-webdriver/lib/promise.js:2813:27)
        at /Users/matt/web-app/node_modules/selenium-webdriver/lib/promise.js:676:7
        at process._tickCallback (internal/process/next_tick.js:109:7)Error
        at ElementArrayFinder.applyAction_ (/Users/matt/web-app/node_modules/protractor/lib/element.ts:482:23)
        at ElementArrayFinder.(anonymous function).args [as sendKeys] (/Users/matt/web-app/node_modules/protractor/lib/element.ts:96:21)
        at ElementFinder.(anonymous function).args [as sendKeys] (/Users/matt/web-app/node_modules/protractor/lib/element.ts:873:14)
        at /Users/matt/web-app/e2e-tests/modules/authentication.js:83:38
        at ManagedPromise.invokeCallback_ (/Users/matt/web-app/node_modules/selenium-webdriver/lib/promise.js:1366:14)
        at TaskQueue.execute_ (/Users/matt/web-app/node_modules/selenium-webdriver/lib/promise.js:2970:14)
        at TaskQueue.executeNext_ (/Users/matt/web-app/node_modules/selenium-webdriver/lib/promise.js:2953:27)
        at asyncRun (/Users/matt/web-app/node_modules/selenium-webdriver/lib/promise.js:2813:27)
        at /Users/matt/web-app/node_modules/selenium-webdriver/lib/promise.js:676:7
        at process._tickCallback (internal/process/next_tick.js:109:7)
    From: Task: Run beforeAll in control flow
        at Object.<anonymous> (/Users/matt/web-app/node_modules/jasminewd2/index.js:94:19)
    From asynchronous test: 
    Error
        at Suite.<anonymous> (/Users/matt/web-app/e2e-tests/specs/home/asset.spec.js:28:3)
        at Object.<anonymous> (/Users/matt/web-app/e2e-tests/specs/home/asset.spec.js:7:1)
        at Module._compile (module.js:571:32)
        at Object.Module._extensions..js (module.js:580:10)
        at Module.load (module.js:488:32)
        at tryModuleLoad (module.js:447:12)
  Message:
    Failed: No element found using locator: By(css selector, .toggle-asset-summary-button)
  Stack:
    NoSuchElementError: No element found using locator: By(css selector, .toggle-asset-summary-button)
        at elementArrayFinder.getWebElements.then (/Users/matt/web-app/node_modules/protractor/lib/element.ts:851:17)
        at ManagedPromise.invokeCallback_ (/Users/matt/web-app/node_modules/selenium-webdriver/lib/promise.js:1366:14)
        at TaskQueue.execute_ (/Users/matt/web-app/node_modules/selenium-webdriver/lib/promise.js:2970:14)
        at TaskQueue.executeNext_ (/Users/matt/web-app/node_modules/selenium-webdriver/lib/promise.js:2953:27)
        at asyncRun (/Users/matt/web-app/node_modules/selenium-webdriver/lib/promise.js:2813:27)
        at /Users/matt/web-app/node_modules/selenium-webdriver/lib/promise.js:676:7
        at process._tickCallback (internal/process/next_tick.js:109:7)Error
        at ElementArrayFinder.applyAction_ (/Users/matt/web-app/node_modules/protractor/lib/element.ts:482:23)
        at ElementArrayFinder.(anonymous function).args [as click] (/Users/matt/web-app/node_modules/protractor/lib/element.ts:96:21)
        at ElementFinder.(anonymous function).args [as click] (/Users/matt/web-app/node_modules/protractor/lib/element.ts:873:14)
        at Page.showAssetSummaryComponent (/Users/matt/web-app/e2e-tests/specs/home/page-object.js:289:31)
        at Object.<anonymous> (/Users/matt/web-app/e2e-tests/specs/home/asset.spec.js:63:12)
        at /Users/matt/web-app/node_modules/jasminewd2/index.js:112:25
        at new ManagedPromise (/Users/matt/web-app/node_modules/selenium-webdriver/lib/promise.js:1067:7)
        at ControlFlow.promise (/Users/matt/web-app/node_modules/selenium-webdriver/lib/promise.js:2396:12)
        at schedulerExecute (/Users/matt/web-app/node_modules/jasminewd2/index.js:95:18)
        at TaskQueue.execute_ (/Users/matt/web-app/node_modules/selenium-webdriver/lib/promise.js:2970:14)
    From: Task: Run fit("should open asset summary component") in control flow
        at Object.<anonymous> (/Users/matt/web-app/node_modules/jasminewd2/index.js:94:19)
        at /Users/matt/web-app/node_modules/jasminewd2/index.js:64:48
        at ControlFlow.emit (/Users/matt/web-app/node_modules/selenium-webdriver/lib/events.js:62:21)
        at ControlFlow.shutdown_ (/Users/matt/web-app/node_modules/selenium-webdriver/lib/promise.js:2565:10)
        at shutdownTask_.MicroTask (/Users/matt/web-app/node_modules/selenium-webdriver/lib/promise.js:2490:53)
        at MicroTask.asyncRun (/Users/matt/web-app/node_modules/selenium-webdriver/lib/promise.js:2619:9)
    From asynchronous test: 
    Error
        at Suite.<anonymous> (/Users/matt/web-app/e2e-tests/specs/home/asset.spec.js:62:5)
        at Suite.<anonymous> (/Users/matt/web-app/e2e-tests/specs/home/asset.spec.js:60:3)
        at Object.<anonymous> (/Users/matt/web-app/e2e-tests/specs/home/asset.spec.js:7:1)

完全相同 - 使用全新的“快速入门”angular4 配置和非常基本的量角器测试。 在非混合模式下一切正常。

Failed: Timed out waiting for asynchronous Angular tasks to finish after 50 seconds. This may be because the current page is not an Angular application. Please see the FAQ for more details: https://github.com/angular/protractor/blob/master/docs/timeouts.md#waiting-for-angular
    While waiting for element with locator - Locator: By(css selector, #loginModal h4)

我仍然看到这个问题。
使用:@angular/upgrade 4.2.2
其中包含来自@heathkit (https://github.com/angular/angular/pull/16836) 的修复程序,以使可测试性 api 稳定,但它似乎仍然不起作用

如果有人可以分享显示问题的回购,我会进一步研究。

到目前为止我发现了什么

经过一番挖掘,我发现了我看到的超时原因之一(或其中之一,我怀疑它不是唯一的)。 事实证明,我正在使用的依赖项之一是调用window.setTimeout() ,这会阻止 Protractor 同步(在我的情况下,它是
角力矩.

当我的应用程序仅运行 AngularJS 时,这运行良好,但在作为混合应用程序引导时会导致超时问题(大概是因为它在区域中运行的方式)。

示例仓库

我在https://github.com/mattwilson1024/angular-hybrid创建了一个示例存储库。 这是一个混合 (NgUpgrade) 应用程序,包含以下部分:

  • 最外面的页面(CharactersPage)由 AngularJS (1.6) 提供。
  • 该页面包含 CharacterListComponent,它是一个降级的 Angular (4) 组件。
  • CharacterListComponent 使用另一个 Angular 4 组件 CharacterDetail 组件。

master 分支包含工作应用程序和一个没有问题的量角器测试。

angular-moment 分支添加了对angular-moment库的依赖,并使用它的am-time-ago指令向页面添加一个标签,显示自第一集以来已经过去了多少年。 应用程序本身运行良好,但量角器测试现在无法同步。

超时

为了确认这是否发生,我尝试向 AngularJS 控制器添加各种不同类型的超时,结果如下。 这证实 $interval 有效,但 $timeout、setInterval 或 setTimeout 无效。

function sayHello() { console.log('Hello, world'); }

$interval(sayHello, 30000); // Succeeds (with Angular 4.2.0 and later - see https://github.com/angular/angular/pull/16836)
$timeout(sayHello, 30000); // Fails - Timed out waiting for asynchronous Angular tasks to finish after 11 seconds
setInterval(sayHello, 30000); // Fails - Timed out waiting for asynchronous Angular tasks to finish after 11 seconds
setTimeout(sayHello, 300000); // Fails - Timed out waiting for asynchronous Angular tasks to finish after 11 seconds

下一步

设法追踪正在发生的事情后,我仍然不确定解决方案是什么。 我知道可能有必要在“Angular 区域之外”运行某些代码段,但我不确定如何从 AngularJS 段执行此操作,其中我没有用于注入区域等的 TypeScript 类。

即便如此,如果问题代码发生在第三方库中(就像我的 angular-moment 示例中的情况一样) - 解决这个问题的方法是什么让测试像升级到之前那样工作混合设置?

窗口上设置了区域,因此您无需注入 NgZone。 您可以像这样在 AngularJS 中的 Angular 区域之外运行

if (Zone && Zone.current.name === 'angular') {
  let angularZone = Zone.current;
  Zone.parent.run(() => {
    $timeout(() => {
       // Stuff here is run outside the Angular zone and will not be change detected
       angularZone.run(() => {
         // Inside the zone and will be change detected.
       });
    }, 500);
  });
}

你是对的,任何使用 setTimeout 或 setInterval,即使是在第三方代码中,都可能阻碍量角器的稳定性并导致超时。 我们正在向 Angular 和 AngularJS 添加任务跟踪,通过向您展示 Protractor 超时的异步任务来自何处,可以更轻松地调试这些问题。 实际上,我们需要几个月的时间才能将其应用到量角器中。

就 angular-moment 而言,您最好的选择可能是向他们提交 PR,使其具有区域感知能力,如上所示。 否则,您可以有选择地关闭waitForAngular并改用browser.wait和 ExpectedConditions。 我们正在开发更灵活的 waitForAngular API,它可以让您控制等待哪些任务,但这还有很长的路要走。

在我们的情况下,测试可以用通用的(没有 Angular 同步)方式(使用 ECs 等的browser.ignoreSynchronization = true编写),但是在使用"Error while waiting for Protractor to sync with the page: "Cannot read property '$$testability' of undefined"设置browser.ignoreSynchronization = false;时失败

这基本上使我们整个E2E 自动化测试套件无效。

使用量角器 5.1.2,如果我设置ng12Hybrid: true我会收到消息:

您已设置 ng12Hybrid。 从 Protractor 4.1.0 开始,Protractor 可以自动推断您是否正在使用 ngUpgrade 应用程序(只要在调用 platformBrowserDynamic() 之前加载了 ng1),并且大多数用户不再需要此标志

所以很明显 angular.io 文档在这里已经过时了: https: //angular.io/guide/upgrade#e2e -tests

来自同一份文件的著名遗言:

E2E 测试并不真正关心应用程序组件的内部结构。 这也意味着,尽管您在升级期间对项目进行了相当多的修改,但 E2E 测试套件应该保持通过,只需稍作修改即可。 从用户的角度来看,您没有改变应用程序的行为方式。

根据 angular.io 上的声明:

@heathkit感谢您提供有关在区域内外运行的提示,但老实说,我们正在寻找一种可以与我们现有的 AngularJS 代码一起运行的解决方案。 我们有一个广泛的 Protractor 套件,它依赖于隐式 AngularJS 同步。 我们的目标是使用 ngUpgrade 将我们的 AngularJS 迁移到 Angular,但是这个无法在混合升级模式下运行我们现有的 Protractor 套件的问题阻碍了我们的计划。 现在非常欢迎明智的修复。

如果我们做的事情从根本上是错误的,我们肯定想知道是什么。

面临同样的问题! :0

在这里看到同样的事情:AngularJS 1.6 -> Angular 4.2.4

任何人都知道任何类型的解决方法,无论它可能有多大? 没有恢复到 1.6 那就是。

@quinw68 - @heathkit在他之前在此线程的回复中提出了一种在 Angular 'Zone' 之外运行代码的方法。 我认为这意味着更改您的应用程序的源代码以使其工作,甚至可能依赖于第三方库添加支持。 如果我错了,请纠正我。

我知道的另一个“解决方法”是将您现有的应用程序视为不再是 AngularJS 应用程序。 这意味着您将不得不更改您的量角器测试,以便它们不再固有地等待 Angular,而是在查询它们的状态之前明确检查页面上元素的存在。 根据您的应用程序的大小,这可能是一项艰巨的任务。

希望有帮助。

据我了解这个问题,这不是特定于 ngUpgrade 的,而是特定于 Angular 的。 我的意思是,即使在纯 Angular (2+) 应用程序中,拥有长setTimeout (直接或通过第 3 方库)也会导致量角器超时。

@heathkit ,你能确认一下还是我错过了什么? ngUpgrade 是在抛弃 Protractor 还是在某种程度上让事情变得更糟?

@gkalpak您说得对,这更像是一个特定于 Angular 的问题,但是人们在将 ngUpgrade 与大型 AngularJS 应用程序一起使用时会遇到它。

在 AngularJS 中,Protractor 只会等待以$browser.defer()开始的异步任务——基本上只是应用程序代码中的$timeout$http调用。 一旦有人开始使用 ngUpgrade,他们现有的 Protractor 测试将开始超时,因为他们以前没有的长时间setTimeout调用(可能来自第三方库)。 大多数情况下,问题是 ngUpgrade 按预期工作,现在人们的 AngularJS 应用程序和量角器测试突然受到 Angular (2+) 语义的影响。

那么,对于纯 Angular 应用程序,如何解决这个问题? 如果第三方 Angular 库设置了很长的setTimeout ,导致 Protractor 超时,该怎么办?

@vikerman我听说你很快就会帮助一个新的团队成员启动并运行这个,所以在他们上船之前分配给你。

对于第三方应用程序设置长时间超时的情况,我们真的没有很好的答案。 目前,您所能做的就是关闭waitForAngular并依赖 ExpectedConditions,但这并不理想。 我正在为 Protractor 添加一项功能,让人们可以更好地控制他们的测试等待哪些宏任务,但这还有很长的路要走。

@heathkit @vikerman请问这个有什么更新吗?

我工作的项目之前有这个问题,但他们包装了连续轮询功能以在角度区域之外运行,这样它就不会影响 browser.waitForAngular();

首先感谢量角器团队/贡献者。 Protractor 一直是我的应用程序的基石,在 3 年多的数十个版本中几乎没有生产问题,超过 200 个服务、指令、组件、控制器以及大约 400 个 e2e Protractor 测试。

这是一个很好的提醒,我对 Protractor 的依赖程度如何,但是在专门用于引导 ngUpgrade 和辅助需求的 2 周之后,这个问题已经阻止了我的 Angular 升级。 已完成,但现在无法运行 e2e 测试,并且在没有可见的前进路径的情况下停止部署(还)。

感谢@mattwilson1024提供的大量信息,我确保我们的应用程序中没有使用$timeoutsetTimeoutsetInterval

但是,我发现angular-ui-bootstrap (v.2.5.6) 和ui-select (v0.19.8) 确实使用了$timeout 。 我广泛使用这两个库。 似乎很多其他人也依赖它们 - angular-ui-bootstrap - 400k npm downloads/mo,ui-select 100k+/mo。

angular-ui-bootstrap在 2015 年创建了一个问题,将 $timeout 替换为 $interval 特别是由于这个问题,但不幸的是已经决定“这个错误应该在量角器中修复而不是在这里。关闭这个。” 此外, angular-ui-bootstrap不再提供其 Angular 版本 @ng-bootstrap/ng-bootstrap 的更新,该版本处于测试阶段。

当我看到大量使用并不能确保量角器有效性的角度库时,我感到很惊讶。 在这一点上,我同意@heathkit 的观点,即这些库需要自行修复。 但是,我希望 Protractor 能够介入并一举缓解这一切。 即使是一个很好的解决方法也是金的。 我很失望我现在必须退出我的 ngUpgrade,直到我挖掘出所有那些$timeout依赖项,并且可能,而不是仅仅包括 ngGrade 并在可行的情况下开始挑选升级项目,我可能依赖于从那些 3rd 方项目升级或迁移以及添加 ngUpgrade。 啊。

我正在继续研究我的应用程序(导致超时的特定罪魁祸首,如何前进等)并将报告任何可能对其他人有用的东西。

我发现 $timeout 的地方:
angular-ui-bootstrap v2.5.6

  • 通知控制器
  • UibCarouselController
  • UibDatepickerPopupController
  • UibTypeaheadController
  • 工具提示

用户界面选择

  • 选择
  • uiSelectMultiple
  • uiSelectSingle
  • 选择排序
  • uisopen关闭

angular-resource $resource注入并使用$timeout
角度过滤器( $window.setTimeout

您好 - 我们正在进行一项更改,让您可以配置量角器等待哪些任务,这可以帮助您处理上述问题。 这是一个涉及 Zone.js 和 Angular 的广泛变化,没有特定的 ETA(将在几周内)。 将在此处更新进度。

当我使用 NgUpgrade 引导我的[email protected]应用程序到[email protected]时,我遇到了同样的问题
感谢上面的评论,尤其是@mattwilson1024@tonybranfort ,这有助于我更好地理解这个问题。

通过在超时的代码周围添加browser.ignoreSynchronization = true/false; ,我发现了一个解决方法。

例如,我的一个测试在一组单选按钮中做了一个简单的选择,并期望选择正确的选项:

it('should be able to pick option', function () {
        expect(myPage.optionSelected()).toBe(false);
        myPage.pickOption('Option 1');
        expect(myPage.anyOptionSelected()).toBe(true);
        expect(myPage.getSelectedOption()).toBe('Option 1');
});

当我尝试调试这段代码时,我发现在最后一步发生了超时。 我想潜在的问题是来自 angular-ui-bootstrap 的工具,使用 $timeout: https :

但是,此测试中没有异步调用。 所以我所做的,只是在开始时添加 ignoreSynchronization 并在最后重置它:

it('should be able to pick a font', function () {
        browser.ignoreSynchronization = true;  // tell protractor do not waitForAngular
        expect(myPage.optionSelected()).toBe(false);
        myPage.PickOption('Option 1');
        expect(myPage.anyOptionSelected()).toBe(true);
        expect(myPage.getSelectedOption()).toBe('Option 1');
        browser.ignoreSynchronization = false;  // reset the flag
});

我不知道这是否是推荐的方式,或者它是否会使测试更加不稳定。 但是对于不需要任何异步调用但仍然遇到超时问题的测试,它应该修复它们。

@vikerman - 有任何更新吗? https://github.com/angular/protractor/issues/4290#issuecomment -337094393

@vikerman有这方面的更新吗?

有没有人就如何找到这些长时间运行的超时提供好的建议,以便我可以将它们从我的应用程序中删除?

来自: Castaway(s?) on Island of AngularJS 被量角器问题 #4290 搁浅
@vikerman @juliemr

哎呀! 我们能得到关于这个问题的更新吗? 10 月 16 日的最后一次更新是“周数”的预计到达时间。 或者,请关闭此问题,以便我们继续。

这些是我目前正在考虑的筏子:

  • 替换所有 angular-ui-bootstrap / ui-select 。 这对我来说意义重大。 无论如何它都需要完成,但我担心的是会弹出其他东西,并且在完成该工作后我仍然会被困在 AngularJS 上。 经验教训:用我自己的组件包装 3rd 方组件。
  • waitForAngularEnabled(false)用于@vladotesanovic 建议的waitForAngularEnabled并且仅用于非常特殊的情况。
  • 将整个应用程序升级到 Angular,而不是 AngularJS/Angular 并排增量迁移:不,这不是我的资源的选项。
  • 迁移到 Vue 或 React而不是 Angular。 在列表中名列前茅,特别是考虑到两者都可以与 AngularJs 共存。
  • 转向 Puppeteer并放弃所有量角器测试 - 这可能是战略方向。
  • 或者 #4290 中的修复,我可以取消注释开始使用 ngUpgrade 所需的那几行:)

我们的团队正在升级我们复杂的应用程序,并且量角器测试在使用 NgUpgrade 引导时失败。 我们不得不恢复我们的更改,因为现在我们无法处理所有失败的误报测试。

我们在哪里遇到这个问题? 我担心我将不得不转移到不同的测试框架,这样我们才能保持升级 Angular 的计划。

@tonybranfort :与您在同一条船上...我们严重依赖ui-selectangular-ui-bootstrap - 这是我们升级的主要障碍。 看起来你已经在这个问题上处于领先地位,但请让我们保持最新状态! 😁

我们有同样的问题。 量角器的人可以告诉我们这个吗?

我们正在从 Angular V1 升级我们的应用程序的一半,并且讨厌这个原因是回到 V1 并放弃到目前为止为升级到 Angular V5 所做的所有工作。

在这里再次投票,我们正在将我们的应用程序升级到 Angular,我们目前正在使用waitForAngularEnabled(false)的零碎应用程序来让这些应用程序通过,而不会使整个套件面临更大的风险。 但是,这是非常不理想的,因此将不胜感激更好的解决方案。

我们也遇到了这个问题,特别是在 $interval 服务方面。 有时量角器正在等待它,有时则没有 - 我还没有找到模式。

长时间编辑:显然这取决于调用间隔的时间(在区域中与否)

@vikerman - 关于让您配置量角器等待哪些任务的更改的任何消息?

同样的问题在这里。

@vikerman有任何更新或解决方法吗?

我们遇到了同样的问题。 如果在这个问题上有一些进展,那就太好了。

可悲的是,我确实有同样的问题。 这方面有什么进展吗?

我们正在向 Angular 和 AngularJS 添加任务跟踪,通过向您展示 Protractor 超时的异步任务来自何处,可以更轻松地调试这些问题。

@heathkit这已经在某处完成了吗? 我们很难在我们的代码库中识别这些位置。

最好有一些文档,量角器正在等待什么。 这个似乎已经过时了: https: //www.protractortest.org/#/system -setup。 它只提到用$interval替换$timeout $interval 。 与 setTimeout 或 setInterval 无关。
一些文档在这里: http :

我认为这将帮助您运行“角度混合 + 量角器”测试。 它帮助了我。 检查链接: https :

我们在了解此问题的情况方面取得了一些进展。 我们好像有一样的。 似乎升级后的 angularjs 观察者/摘要循环的工作方式略有不同,并且可以启用一些无限的摘要循环循环。 我们发现 1 与我们正在使用的第 3 方,并且正在调查下一个。 但基本上观察者正在导致被观察的事物发生变化,导致观察者触发等。为了测试你是否遇到同样的问题,在 Chrome 中只需打开开发工具,转到性能,记录几秒钟,然后看看你是否有很多 NgZone 调用。 如果你这样做了,那么在你的代码中的某个地方你有一个无限的摘要循环。 深入了解呼叫可以帮助您找出导致呼叫的方向

您还可以稍微修改本地 zone.js 文件并添加控制台日志,在其中修补超时/间隔任务以查看调用它们的内容。 简单的方法来做到这一点:

try {
  throw new Error("track");
} catch(err) {
  console.debug(err);
}

编辑:在移动设备上格式化代码很有趣!

@vikerman您对此有任何更新吗? 如果您能就该问题的修复时间提供您的意见,那就太好了? 谢谢。

您还可以稍微修改本地 zone.js 文件并添加控制台日志,在其中修补超时/间隔任务以查看调用它们的内容。 简单的方法来做到这一点:

try {
  throw new Error("track");
} catch(err) {
  console.debug(err);
}

编辑:在移动设备上格式化代码很有趣!

你会把这段代码放在哪里来跟踪超时/间隔?

@maurycyg patchTimer -> patchMethod函数应该可以解决问题

看到这个问题很令人沮丧。 有任何更新吗?

不使用$timeout的潜在解决方法是用$interval覆盖它(对于 3rd 方库)
下面的代码为我解决了超时问题(添加浏览器检查以防万一)。 在我的情况下,问题是由 angular-bootstrap 组件引起的,它以 0 延迟无限调用 $timeout。

app.config(function($provide) {
    function isUnderProtractor() {
        var nav = window.navigator;
        var lowercasedUserAgent = nav.userAgent;
        return !lowercasedUserAgent || lowercasedUserAgent.contains('protractor');
    }

    if (isUnderE2ETest()) {
        $provide.decorator('$timeout', ['$interval', '$delegate', function($interval, $delegate) {
            var newTimeout = function(fn, delay, invokeApply, pass) {
                if (!delay)
                    return $interval(fn, delay, 1, invokeApply, pass);
                else
                    return $delegate(fn, delay, invokeApply, pass);
            };
            newTimeout.cancel = $delegate.cancel;
            return newTimeout;
        }]);
    }
}

从 zone.js v0.8.9 开始,您可以选择要修补哪些 Web API 模块,以减少修补这些模块带来的开销。
https://github.com/angular/zone.js/blob/master/MODULE.md

添加 zone-flags.ts 文件,内容如下
(window as any).__Zone_disable_timers = true;

在 zone.js 之前将其导入 polyfills.ts

import './zone-flags';
import 'zone.js/dist/zone';  // Included with Angular CLI.

上面禁用区域计时器的解决方案似乎与 Obserables(例如 asyncValidators)混淆了。 最终对我有用的解决方案是猴子补丁 setTimeout。

platformBrowserDynamic().bootstrapModule(AppModule)
.then((moduleRef: NgModuleRef<AppModule>) => {
  const ngZone: NgZone = moduleRef.injector.get(NgZone);
  const originalSetTimeout = window.setTimeout;
  (window as any).setTimeout = function setTimeout(fn, ms) {
    return ngZone.runOutsideAngular(() => {
      return originalSetTimeout(() => {
        ngZone.run(fn);
      }, ms);
    });
  };
})

也许问题不在于量角器本身,而在于应用程序代码。

随着从使用摘要周期跟踪更改的 Angular JS 过渡到使用 Angular2+ 的混合应用程序使用 zone.js 来检测更改,Protractor 开始因超时而下降。

Protractor 仅在应用程序稳定时才开始下一个测试操作。 混合应用程序的稳定性意味着应用程序的 Angular JS 部分和应用程序的 Angular2+ 部分的稳定性。 两部分都必须稳定。

Angular JS 稳定性是通过$$testability.whenStable 。 Angular 2+ 的稳定性由window.getAngularTestability(...).whenStable 。 您可以调试 Protractor 代码并亲自查看。 为此,将debugger;语句添加到waitForAngular _\node_modules\protractor\built\clientsidescripts.js_ 文件中;
您会注意到 Angular2+ 区域的稳定性永远不会发生。 为了稳定性,必须完成所有微任务和宏任务

现在我们明白了这个问题。 应用程序的 Angular 2+ 部分始终不稳定,因此 Protractor 会无限期地等待,直到它变得稳定并因超时而下降。 混合应用程序中的 Angular JS 稳定性不应该改变并且不会影响量角器超时。 由此我们可以得出结论,有必要修复 Angular2+ 区域的不稳定性,然后 Protractor 将开始按预期工作。

第一次尝试修复:
由于时间不多,而且我想一下子解决所有问题,我编写了代码,将任务安排在 Angular2+ 区域之外。

    $provide.decorator("$animateCss", ["$delegate", createDecorator]);
    $provide.decorator("$http", ["$delegate", createDecorator]);
    $provide.decorator("$timeout", ["$delegate", createDecorator]);
    $provide.decorator("$interval", ["$delegate", createDecorator]);
    $provide.decorator("$q", ["$delegate", createDecorator]);
    $provide.decorator("$xhrFactory", ["$delegate", createDecorator]);
    $provide.decorator("$templateRequest", ["$delegate", createDecorator]);
    $provide.decorator("$httpBackend", ["$delegate", createDecorator]);

    $provide.decorator("$animate", ["$delegate", createDecorator]);
    $provide.decorator("$rootScope", ["$delegate", createDecorator]);
    $provide.decorator("$templateCache", ["$delegate", createDecorator]);
    $provide.decorator("$browser", ["$delegate", createDecorator]);

    function runInRootZone(fn, ...args) {
        if (window.Zone.current.parent) {
            return window.Zone.current.parent.run(() => runInRootZone(fn, ...args));
        }

        return fn(...args);
    }

    function decorate(decorator, $delegate) {

        const keys = Object.keys($delegate);
        for (let i = 0; i < keys.length; i++) {

            const key = keys[i];
            if (angular.isFunction($delegate[key])) {
                decorator[key] = createDecoratorForFunctionLikeService($delegate[key]);
            } else {
                if (decorator[key] !== $delegate[key]) {
                    decorator[key] = $delegate[key];
                }
            }
        }
    }

    function createDecoratorForFunctionLikeService($delegate) {

        function decorator(...args) {
            return runInRootZone($delegate, ...args);
        }

        decorate(decorator, $delegate);

        return decorator;
    }

    function createDecoratorForService($delegate) {

        decorate($delegate, $delegate);

        return $delegate;
    }

    function createDecorator($delegate) {
        if (angular.isFunction($delegate)) {
            return createDecoratorForFunctionLikeService($delegate);
        }
        return createDecoratorForService($delegate);
    }

虽然这种方法已被证明是可行的,但正如时间所表明的那样,这种方法有其自身的缺点。 主要的问题是无法确定混合应用程序的特定部分将是哪个区域(根或角度)。 Angular2+ 的正常运行要求它始终在 Angular zone 中,而装饰器可以使 Angular2+ 组件处于 root zone 中。 ngUpgrade 意味着 Angular 区域覆盖了大部分应用程序。

为了避免这种情况,进行了第二次修复尝试,对原因进行了更深入的分析,放弃了装饰器并返回到原始问题。

现在我们需要了解是什么代码使区域不稳定。 为此,zone.js 具有特殊的实用程序,例如 _TaskTrackingZoneSpec_ (_\node_modules\zone.js\dist\task-tracking.js_),它允许跟踪区域中的哪些任务及其创建位置( creationLocation )。 此外,在应用程序的 Angular2+ 部分的开头,保存 Angular 区域(window as any).a9Zone = (upgradeModule as any).ngZone._inner;并将此代码添加到某处

$rootScope.$watch(function () {
    if (window.a9Zone) {
        console.log("Angular2+ task count:", window.a9Zone._zoneDelegate._taskCounts);

        // Optional.
        window.getAllAngularTestabilities().forEach(_ => {
            _.taskTrackingZone.macroTasks.forEach(t => {
                console.log(t, t.creationLocation);
            });
        });
    }
});

因此,找到应用程序中微任务和宏任务在该区域中长时间存在的位置(_microTasks.lenght!== 0 && macroTasks.lenght!== 0_)。

然后你需要把这段代码从 Angular 区域中取出来。 为此,请将代码包装在ngZone.runOutsideAngular 。 但是,请非常小心地执行此操作,并确保根区域不会出现在不需要的地方。

在 Angular 区域变得稳定后,量角器将不再因超时而下降。

希望这可以帮助。

我想出了一个临时的 hack 来解决这个问题,方法是用下面的逻辑覆盖waitForAngular函数。

onPrepare: function() {

            var script = "var callback = arguments[arguments.length - 1];" +
                "angular.element(document.querySelector(\"body\")).injector()"+
                ".get(\"$browser\").notifyWhenNoOutstandingRequests(callback)");

            browser.waitForAngular = function() {
                return browser.executeAsyncScript(script)
            };
}
此页面是否有帮助?
0 / 5 - 0 等级

相关问题

koshkarov picture koshkarov  ·  3评论

mvolkmann picture mvolkmann  ·  3评论

smarts picture smarts  ·  3评论

psech picture psech  ·  3评论

luakri picture luakri  ·  3评论