Protractor: 分度器と角度のある手動ブートストラップ

作成日 2013年08月30日  ·  49コメント  ·  ソース: angular/protractor

分度器0.8を実行するときに問題が発生し、手動でブートストラップされたアプリで分度器を実行するときに問題が発生します。

アプリケーションとセットアップの関連部分を明らかにするスニペットを次に示します。
index.html:

 <script type="text/javascript" data-main="js/main" src="js/libraries/require.js"></script>

js / main.js:

require({
    shim: {
        .... snip ..
            }
           }, 
  ['require', ..... ], function(require) {
    return require(['bootstrap']);

bootstrap.js:

var appElm = $('html');
appElm.addClass('ng-app'); // this
angular.bootstrap(document, ['app']);

これは実際に機能し、Angularは正しい方法で開始されます。 htmlソースを表示すると、html要素に表示されているng-app属性が表示されません。 分度器もそうしないのでしょうか?

分度器のサンプル構成を使用し、アプリに合わせて関連する値を変更しました。

specs: [
    '../src/test/javascript/protractorSpec/*_spec.js',
  ],
  baseUrl: 'http://localhost:8080/Viewer/',
  rootElement: 'html',

サンプルテスト:

describe('List SMC Charts', function() {
        var ptor= protractor.getInstance();

        beforeEach(function() {
          ptor.get('#/smcChart');
        });

        it('list, no filter', function() {
          var selectOption = ptor.findElement(ptor.By.input("selectOption"));
        });
});

このテストを実行すると、2つのエラーが表示されます。

 1) List SMC Charts   list, no filter
   Message:
     Error: Angular could not be found on the page http://localhost:8080/Viewer/#/smcChart
   Stacktrace:
     Error: Angular could not be found on the page http://localhost:8080/Viewer/#/smcChart
    at /usr/local/lib/node_modules/protractor/lib/protractor.js:392:15

2) List SMC Charts list, no filter
   Message:
     TypeError: Cannot call method 'input' of undefined
   Stacktrace:
     TypeError: Cannot call method 'input' of undefined
    at null.<anonymous> (/Users/........//Viewer/src/test/javascript/protractorSpec/some_spec.js:9:55)

最初のエラーが最も重要なエラーのようですが、最後のエラーも非常に興味深いものです。 ptor.By.inputという行が機能していないように見えます。

何か案は?

untriaged discussion

最も参考になるコメント

@sjelin手動ブートストラップをサポートするための文書化された方法は何ですか? 分度器5.0に関連する情報が見つかりません。

全てのコメント49件

Angularコードはページに含まれていますか? シム、ブートストラップなどが必要なようですが、 angular.min.jsは表示されていません。

'app': {
            deps: ['libraries/angular', 'libraries/angular-resource', 'libraries/angular-strap']
        },

上記のスニペットはshim {}要素内にありますが、あまり関連性がないと思われるため、スニペットから除外しました。 しかし、これはangular.jsがロードされる方法です。

やあ、

はい、Protractorは現在ng-appを想定しているため、手動でブートストラップされたサイトでは問題が発生します。 すぐに文書化されたこの回避策を取得したいと考えています。

更新してください。 回避策を見つけたら、この号に投稿できますか?

応答が遅いことをお詫びします-あなたはすることによって修正することができるはずです

describe('List SMC Charts', function() {
        var ptor= protractor.getInstance();

        beforeEach(function() {
          ptor.driver.get('#/smcChart'); // Just use the plain old webdriver here, it won't complain if Angular isn't loaded yet.
        });

        it('list, no filter', function() {
          var selectOption = ptor.findElement(ptor.By.input("selectOption"));
        });
});

ptor変数をどこかに設定しましたか? 例: var ptor = protractor.getInstance()

これに関する更新はありますか? 提案された修正が機能していません(protractor @ 0.10.0、angular @ 1.0.7)

角度のないログイン画面とそれに続くrequirejs、手動でブートストラップされたアプリの厄介な組み合わせがあります:

describe('User levels', function() {
    var ptor, driver;

    var findByName = function (name) {
        return driver.findElement(protractor.By.name(name));
    };

    describe('Editors', function() {
        ptor = protractor.getInstance();
        driver = ptor.driver;
        it('Can log in', function() {
            driver.get('http://localhost:8000/');
            findByName('login').sendKeys('test_editor');
            findByName('pass').sendKeys('rubbish');
            driver.findElement(protractor.By.css('button[type="submit"]')).click();
        }, 20000);

        it('Shows the homepage', function() {
            var el = ptor.findElement(protractor.By.css('h1[field="pageTitle"]'));
            expect(el.getText()).toEqual('Welcome to Home');
        }, 30000);

    });
});

そして私のmain.js

require([
    'angular',
    'app'
], function (angular, app) {
    'use strict';
    var body = document.body, name = app.name;
    body.setAttribute('ng-app', name);
    body.setAttribute('id', 'ng-app');
    angular.bootstrap(body, [name]);
});

ログイン部分は機能し、分度器がUnknownError: javascript error: angular is not definedでエラーになる前に、保護されたページの読み込みのフラッシュを見ることができます。 driver.get('...') 、さらにptor.get('...')でページを再読み込みしようとしましたが、何も機能しません。

感謝の気持ちを込めて受け取ったヒント/アドバイス/修正。

@magnetized他のテストを試す前に、ログインが完了するまで待機するようにテストに指示する何らかの方法が必要になります。 何が起こっているのかというと、Angularがページに読み込まれる前にptor.findElementを使おうとしているということだと思います。

「ログイン可能」仕様は、アプリの条件をチェックする待機ステートメントで終了する必要があります。 こちらの例を確認してください-https://github.com/angular/protractor/blob/master/spec/login/viaConfigConf.js

@juliemrブリリアント、それは完璧に機能します。

分度器が「角度がロードされるまで待つ」ことを理解していましたが、すべてのケースをカバーすることはできないと思います。

再度、感謝します。

私はまったく同じ設定をしています。 @juliemrソリューションを共有してもよろしいですか。 あなたの待機状態は何ですか? ありがとう!

@jorgenfbそのコメントを私に向けるつもりだったのかしら?

私の解決策は、Angularルーターが起動するのを待って、URLをログインシステムがリダイレクトする裸の「/」からデフォルトルート(この場合は「/#view」)に変更することでした。

すべてのテストで再利用できる関数にしました。

var waitUntilLoaded = function() {
    ptor.wait(function() {
      return ptor.driver.getCurrentUrl().then(function(url) {
        return /view/.test(url);
      });
    });
};

これがあなたのために働くかどうかはわかりません、それはあなたのルーティング設定に依存します。

@juliemrありがとう、魅力のように動作します:)

同様の問題が発生しています(SOでhttp://stackoverflow.com/questions/19391813/protractor-fails-to-find-angularにあります)。Angularが非常にわかりやすくロードされて実行されているにもかかわらず、分度器がAngularを検出しません。 簡単な解決策(Angularを見つけようとせずにテストする)は、私たちが進むべき方法ですか? Angularアプリをテストするために特別に構築されたProtractorの目的を無効にしているようです。
編集:この問題で提案された解決策は私にはうまくいかないことにも注意する必要があります。

より良い角度の手動ブートストラップドキュメントを持つための+1

これはまだ関係がありますか?

正直なところ、試してみることを諦めたのでわかりません。また、戻って何かが変わったかどうかを確認する時間がありません。 それ以降のバージョン変更を考えると、これを閉じることを検討しているのであれば、私は主張しません。 後でいつでも再度開くことができます。

私は今日分度器を使い始めたばかりで、アプリはrequire.jsを使用しています...手動でブートストラップされたアプリケーションのテストに関して更新があった場合、私は興味があります。 :-)

ここでも同じ問題があります。 Angularアプリの外部にログインページがあるアプリケーションを取得しました。 以前のコメントの回避策は時代遅れのようです。 そのようなシナリオをテストできるようにする方法についての更新されたドキュメントが非常に欲しいです。

@hankduanはい、そう思います。

私は今この問題に苦しんでいます。 背景は似ています:依存関係がRequireJSを介して管理されるより大きなアプリケーションがあります。


window.name='NG_DEFER_BOOTSTRAP!';angular.resumeBootstrap()を使用してアプリを更新しましたが、それでも機能しません。

そうは言っても、次に何をしたらいいのかわからない。 さらに調査するために、 https://github.com/knalli/protractor-with-deferred-bootstrapにテストケースを設定しました

  • git clone https://github.com/knalli/protractor-with-deferred-bootstrap.git
  • cd protractor-with-deferred-bootstrap
  • npm install
  • npm run-script webdriver (Seleniumサーバー)
  • npm run-script server (ミニAJSアプリを提供するデモアプリサーバー)
  • npm run-script e2e-test (分度器を実行します...)

結果は

Using the selenium server at http://localhost:4444/wd/hub
Login: Click
Login: Waiting...
A Jasmine spec timed out. Resetting the WebDriver Control Flow.
The last active task was: 
unknown
F

Failures:

に続く

UnknownError: unknown error: [ng:btstrpd] App Already Bootstrapped with this Element '

最後のエラーは少し奇妙で、おそらく誤解を招く可能性があります。テストが実行されました(期待が問題なく、ブラウザーに表示されるものである限り、成功しました)。

クロスチェック:デモアプリを手動インクルードを使用した非遅延ブートストラップに切り替えると(このバリアントはブランチno-deferredで利用可能です)、正常に動作します。


私はすでにいくつかのいわゆる「回避策」を試しました:

  1. http://www.sebdangerfield.me.uk/2014/01/angularjs-protractor-app-already-bootstrapped-error/
    無関係または誤解を招く->違いはありません。
  2. https://github.com/angular/protractor/issues/325#issuecomment -36738048 protractor.getInstance()ignoreSynchronizationを使用->違いなし
  3. https://github.com/angular/protractor/issues/325#issuecomment -30701683パッチ適用clientSideScripts.js/waitForAngular ->違いなし

私は昨日最初の分度器テストを書きましたが、knalliと同じ問題がありました。

例@knalliを作成していただきありがとうございます。 数日後に見ていきます

したがって、私の分度器はすべての作業をテストします(1つの失敗を除いて、それは私のせいです:-P)。
これが役に立たないかどうか教えてください。
これが私の設定です:

分度器バージョン:
バージョン1.0.0-rc4
セレンスタンドアロン:
最新です
chromedriver:
最新です

間違いなく関連するファイル:

構成

/*jshint expr: true*/
/*global browser: true*/
var TEST_DIR =  '../tests/';
exports.config = {
  seleniumAddress: 'http://localhost:4444/wd/hub'
,  specs : [     '../tests/e2e/_protractor.js'       ]
,  capabilities: {'browserName': 'chrome'    }
,  chromeDriver: './chromedriver'  // <---- I remember not being able to get chromedriver working 
                                                    // for some reason, so I just downloaded the executable and put it
                                                    // in my config directory
,  onPrepare: function() {
    "use strict";
    browser.driver.get('http://localhost:5000/register');
    browser.driver.wait(function() {
      return browser.driver.getCurrentUrl().then(function(url) {
        return /\#\/registration/.test(url);
      });
    });
  }
    //  chromeOnly: true,
    //  baseUrl: 'http://localhost:5000/register'
,  jasmineNodeOpts: {
    showColors: true
  }
};

_protractor.js

/*jshint expr: true, undef: true */
/* global element: true, by: true, browser: true, model:true */
(function(){
  "use strict";

var RegistrationPage = function(){
  browser.driver.wait(function() {
    return browser.driver.getCurrentUrl().then(function(url) {
      return /\#\/registration/.test(url);
    });
  });
};
  var account = new RegistrationPage();

  account.elem = element.all(by.name('account_exists'));
  account.accountNumber = element(by.name('account_number'));
  account.submitAccountButton = element(by.css('#account_input button'));

  account.selectHasAccount = function(){
    account.elem.get(0).click();
    return browser.getCurrentUrl();
  };

  account.enterAccountNumber = function(keys){

    account.selectHasAccount().then(function(){
      account.accountNumber.sendKeys(keys);
      return account.submitAccountButton.click()
        .then(function(){
          return browser.getCurrentUrl();
      });
      //this wont log because already returned ^
//      console.log('need to return again');
    });
  };
  account.noAccount = function(){
    account.elem.get(1).click();
  };

describe('registration page', function() {
  it("will survive", function(){
    var registrationPage = new RegistrationPage()
    , title = browser.getTitle();
    expect(title).toBe('Web Shop | Homepage');
  });
  it('will change route on account_exists radiobox click', function(){
    var elem = element.all(by.name('account_exists'))
    , _url;
    elem.get(0).click()
    .then(function(){
      _url = browser.getCurrentUrl().then(function(_url){
        expect(_url).toBe('http://localhost:5000/register#/registration/has');
      });
    })
    .then(function(){
        elem.get(1).click().then(function(){
          _url = browser.getCurrentUrl();
          expect(_url).toBe('http://localhost:5000/register#/registration/no');
        });
      });
  });
  describe('navigate the hasAccount path:', function(){
    it('will click hasAccount which will cause a route change.', function () {
      expect(account.selectHasAccount()).toBe('http://localhost:5000/register#/registration/has');
    });
    it("will submit a valid account number", function(){
      expect(account.enterAccountNumber('1231231231')).toBe('http://localhost:5000/register#/user');
    });
  });
  it('will click noAccount which will cause a route change.', function () {
    var  _url;
    account.noAccount();
    _url = browser.getCurrentUrl();
    expect(_url).toBe('http://localhost:5000/register#/registration/no');
  });
});
})();

Require / main.js:

var pathToJQuery
if('querySelector' in document
  && 'localStorage' in window
  && 'addEventListener' in window) {
  //ie9+ and all browsers
  pathToJQuery = 'lib/jquery/dist/jquery'
} else {
  //ie8 and ie7
  pathToJQuery = 'lib/jquery-1.7.2.min'
}
requirejs.config({

      paths : {

//        Paths

          "reg": 'js/registration'
        , "com": 'js/common'
        , "init": 'js/init'
        , "regD": 'js/registration/directives'
        , "regC": 'js/registration/controllers'
        , "regS": 'js/registration/services'
        , "regM": 'js/registration/modules'
//
//        Frameworks
        ,  "angular": "lib/angular/angular"
        ,  "angular.animate" : "lib/angular-animate/angular-animate"
        ,  "jquery": pathToJQuery   //< = == v    long story; don't judge me.
        ,  "jqueryMigrate" : "lib/jquery-migrate/jquery-migrate"
        ,  "mocks": "lib/angular-mocks/angular-mocks"
        ,  "ui_router": "lib/angular-ui-router/release/angular-ui-router"
        ,  "Q": "lib/q/q"

//        Init
        ,  "apps_init" : 'js/init/apps_init'
        ,  "apps_bootstrap" : 'js/init/apps_bootstrap'
//        ,  "jquery_bootstrap" : 'js/init/jquery_bootstrap'
        ,  "registration": "js/init/registration"

//       Common

//         Registration
        ,  "registration": "js/registration/registration"
//         Controllers
        ,  "account.controller" : 'js/registration/controllers/account.controller'
        ,  "user.controller" : 'js/registration/controllers/user.controller'
//         Directives
        ,  "account.directive" : 'js/registration/directives/account.directive'
//    Services
        , "user.service" : "js/registration/services/user.service"
    }
,     shim: {
        'angular': {
            exports : 'angular'
        }
    ,   'angular.animate' : {deps: ['angular']}
    ,   'jqueryMigrate' : {deps: ['jquery']}
    ,   'ui_router' : ['angular']
    ,   'mocks': { deps: ['angular'], 'exports': 'angular.mock' }
}
});

if( pathToJQuery ===  'lib/jquery/dist/jquery' ){
  requirejs([
        'angular' , 'jquery' , 'jqueryMigrate', 'init/jquery_bootstrap', 'init/apps_bootstrap' ]
      , function(angular, jquery, jquery_bootstrap, apps_bootstrap) {
      }
  );
} else {
  requirejs([
      'angular' , 'jquery' , 'jqueryMigrate', 'init/jquery_bootstrap', 'init/apps_bootstrap' ]
    , function(angular, jquery, jqueryMigrate, jquery_bootstrap, apps_bootstrap) {
    }
  );
}
console.log("working");

役立つ可能性のあるファイル:

デフォルトビュー(ハンドルバー)

<!DOCTYPE html>
<html xmlns:ng="http://angularjs.org"
            xmlns:fab="http://angularjs.org"
            xmlns:ui ="http://angularjs.org"
            lang = "en" class = "no-js" >
<head >
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
<meta http-equiv="X-UA-Compatible" content="IE=Edge"/>
<meta charset="utf-8">
<link rel="shortcut icon" type="image/x-icon" media="all" href="/images/favicon.ico" />
<script type="text/javascript" src="lib/modernizr.js"></script>

<!-- Default ie theme files:-->
<!--[if lte IE 9]><script type="text/javascript">console = {}; console.log = function(){};</script><![endif]-->
<!--[if IE 8]><link type="text/css" rel="stylesheet" href="build/css/ie8/theme.css" /><![endif]-->

<!--[if IE 7]><link type="text/css" rel="stylesheet" href="build/css/ie7/theme.css" /><![endif]-->

<!-- Normal styles -->
<link type="text/css" href="css/style.css" rel="stylesheet">


<!--[if IE 8]> <link type="text/css" rel="stylesheet" href="build/css/ie8/ie_8.css"  /> <![endif]-->
<!--[if IE 7]> <link type="text/css" rel="stylesheet" href="build/css/ie7/lte_ie7.css"/> <link type="text/css" rel="stylesheet" href="build/css/ie7/ie_7.css" /><![endif]-->


<script data-main="main" src="lib/requirejs/require.js"></script>
</head >

<body class = "page-homepage language-en" >
<div id = "wrapper" >
    <div id = "page" data-currency-iso-code = "USD" >
        {{> _header}}
        <a href = "#skip-to-content" class = "skiptocontent" >Skip to content</a >
        <a href = "#skiptonavigation" class = "skiptonavigation" >Skip to navigation menu</a >
    <!--[if lte IE 10]> <p class="browsehappy" style="margin-top: 10px; ">You are using an <strong>outdated</strong> browser. Please <a href="http://browsehappy.com/">upgrade your browser</a> to improve your experience.</p><![endif]-->
    <main ng-cloak id = "content" role = "main" class = "page_main <!--[if IE7]>ng-cloak<![endif]-->" >

        </main>
    </div >
</div >
</body >

</html >

controller.js(サーバー構成)

(function(){
    "use strict";

    //  node requires
    //==============================
    // express-hbs
    // express
    //
    const express = require('express')
        , portN = 5000
        , hbs = require('express-hbs')
        , app = express()
        , path = require('path')
        , Server = require('./server')
        , bodyParser = require('body-parser')
        , serveStatic = require('serve-static')
        , session = require('express-session')

    , router = express.Router()
    , logger  = require('morgan')
        , methodOverride = require('method-override')
        , errorhandler = require('errorhandler')


    //  view paths
    //==============================
    //
    //
    //
        , viewsD = __dirname + '/views/'
        , partialsD = viewsD + 'partials/'
        , layoutsD = viewsD + 'layouts/'
        , testsD =  __dirname + '/tests/'
    , publicD = __dirname + '/public/'
    , styleguideD = publicD+ '/styleguide/'
        , defaultF = layoutsD + 'default.hbs';
    //  Express setup.
    //==============================
    //
    //
    //
    app.use(express.static(publicD))
            .use(bodyParser())
            .use(logger('dev'))
            .use(methodOverride())
            .use(serveStatic(path.join(__dirname, 'public')))
            .use(errorhandler());

    app.set('view engine', 'hbs')
            .set('port', process.env.PORT || portN)
            .set('cache', false)
            .set('views', viewsD);

    app.engine('hbs', hbs.express3({
        partialsDir: partialsD,
        defaultLayout: defaultF,
        layoutsDir: layoutsD
    }));

    var indx = {};

    app.get( '/' ,function (req, res) {
        res.render(partialsD + '_home.hbs', indx);
    });

    app.get( '/register' ,function (req, res) {
        res.render(partialsD + '_register.hbs', indx);
    });

    new Server(app);

})();

server.js

module.exports = function(app){
  "use strict";
    var http = require('http');
    http.createServer(app).listen(app.get('port'), function(){
        console.log('Express server listening on port ' + app.get('port'));
    });
};


module.exports = function (app) {
    "use strict";
    var http = require ('http');
    http.createServer (app).listen (app.get ('port'),function () {
        console.log ('Express server listening on port ' + app.get ('port'));
    }).on ('error', function (e) {
        console.log ("got error: " + e.message);
    });
};

package.json(関連する抜粋)

"dependencies": {
    "body-parser": "^1.2.1",
    "bower": "^1.3.2",
    "errorhandler": "^1.0.1",
    "express": "^4.3.1",
    "express-hbs": "^0.7.9",
    "express-session": "^1.2.0",
    "node-dev": "^2.3.0"
  },

  "scripts": {
    "start": "node --harmony $* ./node_modules/.bin/node-dev controller.js",
  },
"devDependencies": {
  "browserstack-webdriver": "^2.41.1",
  "grunt-protractor-runner": "^1.0.0",
  "method-override": "^1.0.2",
    "morgan": "^1.1.1",
    "protractor": "^0.23.1",
    "requirejs": "^2.1.11",
    "serve-static": "^1.1.0",
},
  "author": "Andrew Luhring"

ファイル構造:

config/
   protractor.config.js
    chromedriver

server.js
controller.js

public/
   main.js
   js/
   lib/

tests/
   e2e/
      _protractor.js    <=== test file.

views/
  layouts/
      default.hbs

  partials/
      _head.hbs
      _register.hbs   <=== file being tested.

それが私のセットアップ全体だと確信しています。 私は冗長に行くだろうと考えました。 お役に立てば幸いです。

私がする必要があったのは:

  1. テスト固有の部分をURLに追加して、ブートストラップコードが「正常に」実行されているのか、分度器テストとして実行されているのかを認識できるようにします。
  2. document.readyからangular.bootstrapを呼び出します
  3. ウィンドウ名を「NG_DEFER_BOOTSTRAP!」で始まるように設定します angle.bootstrapを呼び出す前に
  4. トップレベル要素に「ng-app」属性を設定します
  5. window.angularをangularを指すように設定します

https://github.com/mikaraento/protractor-reuse/blob/master/index.jsを参照してください

ChromeおよびFirefoxブラウザで完全に実行されている分度器スクリプトがInternetExplorerで常に実行できない理由を知っていますか?

同じスクリプトが他のブラウザで完全に実行されていますが、InternetExplorerで常に同じように実行できるとは限りません。 タイムアウト例外などのエラーが頻繁に発生します
メッセージ: 'タイムアウト:仕様が完了するのを待っている30000ミリ秒後にタイムアウトしました'、
トレース:{スタック:未定義}}]
また、InternetExplorerで要素が検出されない場合もあります。

デフォルトの時間間隔をdefaultTimeoutInterval:1000000として使用しましたが、スクリプトでInternetExplorerで実行中に上記のエラーが発生します...

誰かがこれについて私を助けることができますか?

よろしく、
Santhosh Chunchu

手動のブートストラップだけで問題になるとは思いませんか? または、ChromeとFirefoxですでに手動でブートストラップされたアプリを実行できますか?

@hankduanすでにテストケースを調べましたか? おそらく私たちが助けることができるものは何ですか?

分度器のブートストラップの動作を変更するCLがあります(https://github.com/angular/protractor/pull/1155)。 主な目的は他の多くの問題を修正することですが、ブートストラップの方法によっては、手動のブートストラップで修正(または少なくとも役立つ)する必要があると思います。 ただし、テスト用に手動のブートストラップを設定したことはないので、これは理論上のものです。

このPRは分度器の変更とAngularの変更が必要なため、短期間でリリースされない可能性があります。

この問題は、分度器で使用するためにAngularを手動でブートストラップする方法を探すときに最も多く検索される結果のようです。したがって、これは別の解決策を提案するのに最適な場所だと思いました。 これは、インターフェース全体がAngularアプリである場合に機能します(上記の例のように、ログイン後にdivでブートストラップするようなことを行うのではありません)。

AngularがロードされるまでProtractorのテストを遅らせることは可能かもしれませんが、公式のAngularドキュメントには、Protractorで手動のブートストラップを行うことはできないと記載されています。 私は、公式ドキュメントで禁止されていると思われる回避策を敬遠する傾向があります。

私の解決策

Gruntを使用して、コードを単一のJSファイルに連結/ Uglifyしています。これは、index.htmlで参照されている唯一のスクリプトであり、スクリプトタグが付いています。 それをscript.jsと呼びましょう。 開発では、別のGruntタスクを実行します。このタスクは、残りのJSファイルを個別に、非同期に、キャッシュを防ぐためにタイムスタンプを使用してロードするために必要なファイルのみを連結します。 この連結されたスクリプトのセットは、開発中にscript.jsを置き換えます。 どちらのシナリオでも、私はindex.htmlからscript.jsをロードし、scriptタグを付けています。

発達

したがって、開発では、手動でブートストラップを実行する必要があります。 プロダクションJSに連結しない単一のファイルを含めることができるので、これは簡単です。他のすべてのJSが読み込まれた後に、このスクリプトを必ず読み込むようにします。

bootstrap_dev.js

        .element(document.body)
        .ready(function() {
            angular.bootstrap(document.body, ['App']);
        });

生産/分度器

本番環境では、分度器が気に入らないため、手動でブートストラップすることはできません。 そして、プロダクションコードに対してProtractorを実行しています。 また、HTMLから「ng-app」属性を削除したため、開発時にアプリケーションが時期尚早にブートストラップされないため、通常の自動ブートストラップは発生しません。 script.jsで他のすべてのJSの前にスクリプトを配置することで、本番環境でこれを補います。

bootstrap_prod.js

    window.document.body.setAttribute("ng-app", "myApp");

「myApp」をメインモジュールの名前に置き換えるだけです。

結論

開発では、上記を実行すると、スクリプトを非同期でロードし、手動でブートストラップを実行できます。 ただし、本番コードに対してProtractorテストを実行するだけで問題がない場合は(スクリプトタグを介して単一のファイルからロードできます。Angularをロードする前にng-appディレクティブを追加するだけで、自動ブートストラップが実行されます。開発中のさまざまなスクリプトの非同期スクリプトロード、および本番コードの分度器テストの実行。

私のために働いたのは、私のprotractor.conf.jsに次の関数を追加することでした。

onPrepare: function() 
    {
        // implicit and page load timeouts
        browser.manage().timeouts().pageLoadTimeout(40000);
        browser.manage().timeouts().implicitlyWait(25000);

        // for non-angular page
        browser.ignoreSynchronization = true;

        // sign in before all tests
     }

それを行うためのより良い方法があれば、私に知らせてください=]

同様の問題がありました。 この有用な情報が見つかりました:

起動プロセス

違いはありません。本番環境でアプリがどのようにブーストラップされるかですが、テストでは分度器がそれを行っています。

  1. window.nameを変更して、開始前に角度が一時停止されるようにします: NG_DEFER_BOOTSTRAP! (これは角度のある機能です!)
  2. モックを実行してから、angular.resumeBoostrap()を呼び出すと、アプリが実際に起動します。

したがって、Angularは1回だけブーストラップできるため、本番コードがこのプロセスと競合していないことを確認してください。angular.boostrap()またはangular.resumeBoostrap()を2回呼び出すと、分度器またはアプリが破損します。 したがって、本体のng-appはまったく必要ありません。自分でangular.boostrap()を呼び出すことができ、NG_DEFER_BOOTSTRAPを使用して分度器によって処理されます。 ちゃんと。

window.name

ロードおよび起動中に、 window.nameがアプリコードによって変更されていないことを確認してください。 私の場合、 jqueryがそれを行っていましたが、その理由を理解できません。 プロジェクトからjqueryを完全に削除し、テストが機能しました。 これが問題の根本です!

今のところ、プロジェクトで必要になっているため、テストを中断せずにjqueryを戻す方法を探しています。

ついに、

私たちの問題は、1ページにある2つのバージョンのjQueryにありました(2番目のバージョンは外部ライブラリによってロードされました)。 それを削除し、jQueryのみをページに残して、すべてが機能します。

いくつかのコードは、役に立つかもしれません:

index.html

<body>
...
<script data-main="require.conf" type="text/javascript" src="bower_components/requirejs/require.js"></script>
</body>

requirejs config

...
 deps: ["proto/main"],
...

proto.js

// We just load deps and register the module:
 define(["deps"],function(){
   angular.module('proto'...
 }

proto / main.js

define(["require", "exports", "proto", "angular"], 
    function (require, exports) {

    angular.bootstrap(document, ['proto']);
});

そして、実際には、テスト仕様

var protractor = require('protractor');
var ptor;
function prepareBrowser() {

    ptor = protractor.getInstance();

    return ptor.get(ptor["baseUrl"] + '/login').then(function () {
        console.log("got");       
        return ptor.waitForAngular().then(function () {
            console.log("ngwaited");
        });
    });
}
describe('container', function () {
    beforeEach(function () {
        return prepareBrowser();
    });
    describe('form tests', function () {
        it('form is shown', function () {
            expect($('[ng-controller=LoginContr]').isDisplayed()).toBeTruthy();
        });

    });
});

出力

Using the selenium server at http://localhost:4444/wd/hub
got
ngwaited
container
  form tests
    form is shown - pass


Finished in 3.112 seconds
1 test, 1 assertion, 0 failures

ほら!

ng-appの使用からangular.bootstrap()に切り替えたとき、projotractor.confにrootElement: 'div'を追加することが、分度器を再び機能させるために必要な唯一の変更でした。 角度1.3.0と分度器1.3.1を使用しています。

@jongunnipうん、私も働いてくれてありがとう!

@mikaraentoうまくいきました、ありがとう〜

こんにちはみんな、このスレッドに関連する奇妙なこと。

私は通常のAngularアプリを持っています-requirejsなどはありません。分度器2.0.0を使用しています。

ステージング環境でシステムテストを実行すると、機能します。
ローカルで実行すると、次のようになります。
org.openqa.selenium.WebDriverException: unknown error: [ng:btstrpd] App Already Bootstrapped with this Element ...メッセージ

grunt:serverの代わりに静的リソースを提供するようにNginxサーバーを構成すると、すべてが機能します。

開発中にlivereloadを使用して実行するかどうかは本当に重要ですか?

静的サーバーの動作が異なるようで、gruntは404またはリダイレクトを提供するため、Webドライバーはおかしくなります。

こんにちは、みんな、

何度も読んで読んだ後、私のプロジェクトにうまく機能する1つの解決策を見つけました(分度器の実行フローを修正します)

  1. アプリを分度器に開くときに、URLに?protractor-testを追加します(プロジェクトコード
  2. 角度ブートストラップコードを次のように変更します(プロジェクトコード
if(location.href.indexOf("protractor-test") < 0){
  // start angular app
  angular.bootstrap(document, [module.name]);
} else {
  // start angular app to protractor tests
  window.name = 'NG_DEFER_BOOTSTRAP!' + window.name;
  angular.bootstrap(document, [module.name]);
}

そして最後にすべてが再び仕事に戻ります\ o /

コピー@juliemr

私は最近(わずか1〜2週間)Protractorを使用してAngularアプリケーションをテストし始めました。 私のアプリケーションには、ログインページのbodyタグで定義されたng-Appがあります。 私のテストでは、「AngularがページhttP://(WebSite Address)に見つかりませんでした:AngularはresumeBootstrapを提供していません」と文句を言い続けますが、テストの実行は問題ありません。 問題についてはよくわかりません(ブラウザーのignoresyncパラメーターをオンにすると正常に動作しますが、これは正しい方法ではありません)、Juliemrが提案していることを試すことができます(レガシードライバーを使用してナビゲートします)が、アプリが文句を言う理由を理解しようとしていますbodyタグにng-Appが含まれている場合でも。 アプリの機能動作は次のようになります。ログインページ..ログインしたら、ホームページのリンクをクリックすると、別のウィンドウ/タブが開きます。 誰かがブートストラップが自動的に行われることを確認するためにすべてが確認する必要があるステップを見つけるのを手伝ってくれますか(1つはbodyタグのng-appパラメーターです)? bootstrap.jsファイルで何かをチェックする必要がありますか(ブートストラップファイルにng-appパラメーターが表示されないため、チェックします。ありがとうございます。

この問題に関する更新はありますか? ng-appを持たないAngularアプリで動作するように分度器を構成するためのベストプラクティスまたは方法は何ですか? @erkobrideeあなたの例をさらに説明してもらえますか? そして、Angularの読み込みが完了したときに分度器がどのように待機する必要があるかに関して、window.nameを適切に処理する方法を誰かが説明できますか?

@juliemr Angular 1.5では、手動でブートストラップすることがブートストラップの推奨される方法です。 途中で祝福された分度器のサポートはありますか?

+1この問題の優先度を高くする必要があると思います。 Protractorが手動ブートストラップで機能しない場合、Protractorを使用するメリットはありません。 WebdriverよりもProtractorを使用することの全体的なポイントは、Protractorがすべての角度同期を処理することです。

同じ問題があります。 すべてのアップデート?

こんにちは、これを改善するための計画やプロセスはありますか?

奇妙なことにフィードバックはありません。 ng-appがないアプリがあり、テストできません
分度器でそれら

2016年7月16日午前5時1分、「millerwx」 [email protected]は次のように書いています。

こんにちは、これを改善するための計画やプロセスはありますか?


コメントしたのでこれを受け取っています。
このメールに直接返信し、GitHubで表示してください
https://github.com/angular/protractor/issues/66#issuecomment -233104701、
またはスレッドをミュートします
https://github.com/notifications/unsubscribe-auth/AOghFs2ff0e-88DLafNGD3iCH8uCOgVaks5qWEmIgaJpZM4A82d7

2016年のこの日まで、これは私にとってまだ問題でした。 私は問題を解決するために磁化された答えに従いました。 いくつか変更を加えました。 会話の範囲外で素晴らしいのは、リダイレクトも追跡してアプリ内に戻ることです。 * Babelで作成しましたが、代わりにES5で記述できます。

`describe( 'ログイン画面'、()=> {

var urlTimeout = 9000;

const waitForUrlRegex = (regex, urlMatcher )=>{

    return driver.wait(()=>{
        return driver.getCurrentUrl().then( (url)=>{
            return regex.test(url);
        });
    }, urlTimeout, "Expectation error: Timeout for url not matching " + urlMatcher );
};

const waitForRoute = (route)=>{
    return waitForUrlRegex( new RegExp( route + "$" ), route  );
}

const waitForUrl = (url)=>{
    return waitForUrlRegex( new RegExp( "^https?:\/\/" + url ), url );
}

it('should go to homepage and click login button', ()=>{

    driver.get( app_url, urlTimeout);

    //there is a redirect to welcome route
    expect( waitForRoute("welcome") ).toBe(true);

    const loginButton = element.all(by.className("login_button")).get(0);
    loginButton.click();
});

it( 'should enter credentials', ()=>{
    expect( waitForUrl( non_angular_login_url ) ).toBe(true);
    driver.findElement( by.name("user")).sendKeys( "username");
    driver.findElement( by.name("password")).sendKeys( "userpassword");
    driver.findElement( by.tagName("button")).click();
});

it( 'should go back to app, and continue testing', ()=>{
    expect( waitForRoute( "user-screen") ).toBe(true);
});

}); `

config.onPrepare

onPrepare: ()=>{ require("babel-register"); global.driver = browser.driver; }

リダイレクトを調べたくない場合は、ドライバーをスリープ状態にしてから分度器での作業を開始してください。

`driver.get(app_url +" /#/ welcome "、urlTimeout);

    driver.sleep( 1000 );
    const loginButton = element.all(by.className("login_button")).get(0);
    loginButton.click();`

実際には手動ブートストラップをサポートしていますが、この情報は古いものです。 https://github.com/angular/protractor/issues/3857も処理すると、ブートストラップの信頼性がさらに高まります。

@sjelin手動ブートストラップをサポートするための文書化された方法は何ですか? 分度器5.0に関連する情報が見つかりません。

手動ブートストラップ中に同じ問題に直面していました。 そして、うなり声で実行するための解決策を見つけました。
分度器バージョン2.5.1を使用しました。

  1. あなたのうなり声のファイルでこれに言及してください。

grunt.initConfig({ protractor: { options: { configFile: "protractor.conf.js", // Default protractor config file keepAlive: true, // If false, the grunt process stops when the test fails. noColor: false, // If true, protractor will not use colors in its output. args: { // Arguments passed to the command } }, e2e: { options: { // Stops Grunt process if a test fails keepAlive: true } } } })

  1. npm install grunt-protractor-runner --save-devをインストールし、ロードタスクgrunt.loadNpmTasks('grunt-protractor-runner')に追加します

  2. 次のコマンドで分度器を実行します。
    grunt protractor
    またはスイート名
    grunt protractor --suite suite_name

それがあなたのために働くかどうか私に知らせてください。

これは私のために働いた。
browser.ignoreSynchronization = true;
browser.get(url);
browser.ignoreSynchronization = false;

このページは役に立ちましたか?
0 / 5 - 0 評価