重新加载 Angular 应用程序时量角器测试有时会失败

Protractor test sometimes fails when reloading Angular app

我有一个 Protractor 测试,它会启动同一个 Angular 应用程序的重新加载。有时它会在 Travis 构建中失败,但会在构建一两次重新启动后通过。它很少在我的本地机器上失败。 Travis 构建使用 Firefox,但它在我的机器上失败的次数一直在使用 Firefox 和 Selenium 的 chromedriver。

我设置了 rootElement: 'html'(按照 this GitHub issue on Protractor 中的建议),因为那是 ng-app 所在的位置,但我仍然收到此错误:

Error while waiting for Protractor to sync with the page: "root element (html) has no injector. this may mean it is not inside ng-app."

错误是 angular.element('html').injector() 返回错误的结果,即使 Protractor 代码中的 Angular has loaded. A previous test 证实了这一点。

测试涉及被重定向到另一个 index.html 而恰好是相同的 angular 应用程序。重定向的原因是提供语言选择,然后以该语言重新加载应用程序。

有多个语言环境(为简单起见,此处显示两个)。 app是文档根,所以app/index.html是入口点,检测系统语言或者默认为en-gb,然后重定向到app/<locale>/index.html。前者不是 Angular 应用程序 (app/);后者是 (app/<locale>/) 并链接到 ../common/ 用于库和 Angular 模块文件。语言环境文件夹中的 index.html 文件是相同的。

访问文档根目录时会发生什么?

/ -> 重定向 /en-gb/#/language

什么时候选择德语?

/en-gb/#/language 重定向 ../de/#/menu -> /de/#/menu

目录结构:

└── app
    ├── common
    │   ├── fonts
    │   ├── images
    │   ├── lib
    │   ├── modules
    │   └── styles
    ├── de
    │   ├── i18n.js
    │   └── index.html
    ├── en-gb
    │   ├── i18n.js
    │   └── index.html
    └── index.html

测试:

(function () {

    'use strict';

    describe('Module: app, language screen:', function () {

        beforeEach(function () {
            browser.get('http://localhost:9000/en-gb/#/language/');
            this.$germanChoice = // get the clickable element to select German
            this.$buttonContinue = // get the clickable element to continue
        });

        describe('continue button', function () {
            it('should go forward to the menu', function () {
                this.$germanChoice.click();
                this.$buttonContinue.click();
                expect(browser.getCurrentUrl()).toBe('http://localhost:9000/de/#/menu/');
            });
        });

    });

}());

语言环境 index.html 使用缩小文件:

<!doctype html>
<!--[if IE 8]>         <html id="ng-app" ng-app="app" lang="{{ app.locale.html }}" class="no-js lt-ie10 lt-ie9"> <![endif]-->
<!--[if IE 9]>         <html id="ng-app" ng-app="app" lang="{{ app.locale.html }}" class="no-js lt-ie10">        <![endif]-->
<!--[if gt IE 9]><!--> <html id="ng-app" ng-app="app" lang="{{ app.locale.html }}" class="no-js">                <!--<![endif]-->
    <head>
        <meta charset="utf-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <title ng-bind="app.page.title ? app.page.title + ' - ' + app.page.titleBase : app.page.titleBase"></title>
        <meta name="viewport" content="width=device-width, initial-scale=1">

        <link rel="stylesheet" href="../common/lib/nouislider/jquery.nouislider.css" />

        <!--[if gt IE 8]><!-->
            <link rel="stylesheet" href="../common/styles/b8a085fb.main.css">
        <!--<![endif]-->

        <!--[if lt IE 9]>
            <link rel="stylesheet" href="../common/styles/f71e8123.ie.css">
        <![endif]-->

        <!--[if lt IE 9]>
            <script src="../common/scripts/a5663f12.lt-ie9.js"></script>
        <![endif]-->
    </head>

    <body ng-class="{ 'ios6': app.environment.ios6, 'gt-ios6': app.environment.gtIos6, 'cordova': app.environment.cordova }">

        <div ng-view="" autoscroll="true"></div>

        <script src="../common/scripts/cbd2241e.app.js"></script>
        <script src="i18n.js"></script>

    </body>
</html>

我偶尔 运行 遇到非 Angular 登录页面的问题(其中一些有几个步骤)。我认为您的语言选择页面可能类似。解决方案是你的测试中的一些逻辑,结合 browser.driver.

中公开的 Webdriver API
if ('some condition that determines you are on a non-Angular page') {
  browser.driver.findElement(by.id('language-choice')).click();
}

测试的最佳条件是:

  1. 测试是否显示了一些独特的元素。
  2. 获取当前页面的 URL 并将其与预期值进行比较。

并在您不在 Angular 页面时随时使用 browser.driver,因为它会绕过 Protractor。否则,Protractor 将查找 Angular,找不到它并抛出错误。您可以使用 browser.driver 执行的所有操作都可以在此处找到,位于菜单的下方区域:http://angular.github.io/protractor/#/api

然后,为了在使用 Protractor 之前等待 Angular 页面出现,请尝试以下代码段:

browser.driver.wait(function() {
    return browser.driver.getCurrentUrl().then(function(url) {
        return url.toString().indexOf('my angular page url') !== -1;
    }, function(err) {
        throw err;
    });
}, 5000, 'Timed out waiting for your Angular page to load.');

这在您的 onPrepare 语句中特别有用(只需在开头添加 return,在 browser.driver.wait 之前)。测试将等待您的 Angular 页面加载,之后您可以根据自己的喜好使用量角器。

稍加改动,同样的代码片段可用于对多步骤非 Angular 介绍性页面做出反应。并且,封装到它自己的整洁的小函数中,它可以在 Protractor 可能被转储到非 Angular 页面的任何地方重复使用。