Protractor: アプリがngUpgradeでブートストラップされると、分度器が失敗します

作成日 2017年05月16日  ·  51コメント  ·  ソース: angular/protractor

私はAngularJS(1.6)アプリをAngular(4)に移行する作業を行っており、NgUpgradeでブートストラップされたハイブリッドアプリケーションがあります。 これは私の分度器テストを完全に破ったようです。

これが私が間違っていることであるなら許してください、しかし私はこれがうまくいかない理由についての答えを見つけることができません。

私が得るエラーは次のとおりです。

失敗:非同期Angularタスクが11秒後に終了するのを待ってタイムアウトしました。 これは、現在のページがAngularアプリケーションではないことが原因である可能性があります。 詳細については、FAQを参照してください: https

ロケーターで要素を待っている間-ロケーター:By(cssセレクター、.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});
});

分度器テスト

上記のエラーに基づくと、問題は、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を見て、同じ問題が発生しているかどうかを確認して$intervalが以前はブロックしていなかった分度器をブロックするようになりました(https://github.com/angular/angular/issues/16349)が、私はそれに取り組んでいます。

@ Evilweed-提案に感謝しますが、残念ながらそれは私にとっては解決しないようです。

@ heathkit-プロジェクト全体を検索して、長時間実行されている$ intervalを1つ見つけましたが、それを削除した後でも、問題は解決しません。 また、アプリ全体での$ intervalまたは$ timeoutのすべての使用法をコメントアウトしましたが、それでも同じエラーで失敗します。 何か非同期の問題である場合にブロックしている可能性があるものをデバッグして見つける方法があるかどうか知っていますか?

エラー全体を投稿できますか? エラーメッセージに保留中のタスクが表示されるはずですが、一部が欠落している可能性があります。 たとえば、エラーには、Angular側からの保留中のタスクはまだ表示されません。 アプリのAngular部分で非同期タスクを長時間実行している場合は、分度器をブロックしないように、Angularゾーンの外側で非同期タスクを実行する必要があります。 (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)

私はまだこの問題を見ています。
使用:@ angle / upgrade 4.2.2
これには、 @ heathkit (https://github.com/angular/angular/pull/16836)からの修正が含まれており、

問題を示すリポジトリを誰かが共有できる場合は、さらに調査します。

私がこれまでに発見したこと

いくつか掘り下げた後、私が見ているタイムアウトの原因の1つを発見しました(またはそれらの1つ、それだけではないと思います)。 私が使用している依存関係の1つは、 window.setTimeout()を呼び出しているため、分度器の同期が妨げられていることがわかりました(私の場合は
角運動量

これは、アプリがAngularJSだけを実行している場合は正常に実行されましたが、ハイブリッドアプリとしてブートストラップするとタイムアウトの問題が発生します(おそらくゾーンでの実行方法が原因です)。

リポジトリの例

https://github.com/mattwilson1024/angular-hybridにサンプルリポジトリを作成しました。 これは、次の部分を備えたハイブリッド(NgUpgrade)アプリです。

  • 最も外側のページ(CharactersPage)は、AngularJS(1.6)によって提供されます。
  • このページには、ダウングレードされたAngular(4)コンポーネントであるCharacterListComponentが含まれています。
  • CharacterListComponentは、別のAngular4コンポーネントであるCharacterDetailコンポーネントを使用します。

マスターブランチには、動作中のアプリと、問題なく合格した単一の分度器テストが含まれています。

angle-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ゾーンの外」で特定のコードを実行する必要があるかもしれないことを理解していますが、ゾーンを挿入するTypeScriptクラスなどがないAngularJSセグメントからこれを実行する方法はわかりません。

それでも、問題のコードがサードパーティのライブラリ内で発生している場合(角運動量の例のように)、この問題を回避して、にアップグレードする前と同じようにテストを機能させるためのアプローチは何でしょうか?ハイブリッドセットアップ?

ウィンドウにゾーンが設定されているため、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がタイムアウトした非同期タスクがどこから来たのかを示すことで、これらの問題のデバッグが容易になります。 現実的には、分度器にそれを入れることができるようになるまでに数ヶ月かかるでしょう。

角運動量に関する限り、上記のようにゾーン対応にするPRを提出するのが最善の選択肢かもしれません。 それ以外の場合は、 waitForAngularを選択的にオフにして、代わりにbrowser.waitとExpectedConditionsを使用できます。 待機するタスクを制御できる、より柔軟なwaitForAngular APIに取り組んでいますが、それでもまだ道のりは遠いです。

私たちの状況では、テストは一般的な(Angular同期なしで)方法(ECを使用したbrowser.ignoreSynchronization = trueなど)で記述できますが、 browser.ignoreSynchronization = false;"Error while waiting for Protractor to sync with the page: "Cannot read property '$$testability' of undefined"設定すると失敗します

これにより、基本的に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テストスイートはわずかな変更で合格し続ける必要があることも意味します。 ユーザーの観点からアプリケーションの動作を変更していません。

angle.ioに関するその声明に照らして:

@heathkitゾーンの内外での実行に関する

私たちが根本的に間違ったことをしているのなら、私たちは何を知りたいのです。

同じ問題に直面しています! :0

ここで同じことを見る:AngularJS 1.6-> Angular 4.2.4

誰かが何らかの回避策を知っていますが、それがどんなに大きいかもしれませんか? つまり、1.6に戻さずに。

@ quinw68- @ heathkitは、このスレッドでの以前の応答で、 Angular'Zone 'の外部でコードを実行する方法を提案しています。 これは、アプリを機能させるためにアプリのソースコードを変更することを意味し、サポートを追加するサードパーティのライブラリに依存する場合もあると思います。 私が間違っている場合は訂正してください。

私が知っているもう1つの「回避策」は、既存のアプリをAngularJSアプリではなくなったように扱うことです。 これは、分度器のテストを変更して、本質的にAngularを待たないようにする必要があることを意味しますが、状態を照会する前に、ページ上の要素の存在を明示的にチェックします。 アプリのサイズによっては、これは困難な作業になる可能性があります。

お役に立てば幸いです。

私がこの問題を理解している限り、これはngUpgrade固有ではなく、Angular固有です。 つまり、純粋なAngular(2+)アプリでも、 setTimeout長いと(直接またはサードパーティのライブラリを介して)、分度器がタイムアウトします。

@heathkit 、それを確認できますか、それとも私は何かを逃しましたか? ngUpgradeは分度器を外したり、何らかの形で事態を悪化させたりしていますか?

@gkalpak確かに、これはAngular固有の問題ですが、大規模なAngularJSアプリでngUpgradeを使用すると、問題が発生します。

AngularJSでは、Protractorは$browser.defer()開始された非同期タスクのみを待機し$timeout$http呼び出しだけです。 誰かがngUpgradeを使い始めると、以前はなかった長いsetTimeout呼び出し(サードパーティライブラリからの可能性があります)が原因で、既存の分度器テストがタイムアウトし始めます。 そのため、ほとんどの場合、問題はngUpgradeが意図したとおりに機能していることであり、現在、人々のAngularJSアプリと分度器のテストは突然Angular(2+)セマンティクスの対象になります。

では、純粋なAngularアプリでこれをどのように解決するのでしょうか? サードパーティのAngularライブラリが長いsetTimeoutを設定していて、分度器がタイムアウトした場合はどうしますか?

@vikermanすぐに新しいチームメンバーを立ち上げて実行するのを手伝って

サードパーティのアプリが長いタイムアウトを設定している場合、私たちは本当に良い答えを持っていません。 現時点では、 waitForAngularをオフにして、ExpectedConditionsに依存することしかできませんが、それは理想的ではありません。 私はProtractorに機能を追加して、テストが待機するマクロタスクをより細かく制御できるように取り組んでいますが、それでもまだ道のりは遠いです。

@heathkit @vikermanこれに関する更新はありますか?

私が取り組んでいるプロジェクトでは以前にこの問題が発生していましたが、ブラウザに影響を与えないように、継続的なポーリング関数をラップして角度ゾーンの外側で実行しました。

まず、分度器チーム/貢献者に感謝します。 分度器は私のアプリの基盤であり、3年間で数十のリリース、200以上のサービス、ディレクティブ、コンポーネント、コントローラー、約400のe2e分度器テストで本番環境の問題がほとんど発生しません。

これは、私がProtractorにどれだけ依存しているかを思い出させる良い思い出ですが、この問題により、ngUpgradeとそれに付随するニーズのブートストラップに2週間専念した後、Angularのアップグレードが停止しました。 これは完了しましたが、e2eテストを実行できなくなり、展開は(まだ)目に見えるパスなしで停止されます。

@ mattwilson1024からのすばらしい情報のおかげで、 $timeoutsetTimeoutsetIntervalがアプリで使用されていないことを確認しました。

しかし、 angular-ui-bootstrap (v.2.5.6)とui-select (v0.19.8)は$timeout使用していることがわかりました。 私は両方のライブラリを幅広く使用しています。 他の多くの人もそれらに依存しているようです--angular-ui-bootstrap-400knpm downloads / mo、ui-select 100k + / mo。

angular-ui-bootstrapこの問題に特に起因する間隔$で$タイムアウトを置き換えるために2015年に問題を作成したが、残念ながらしていることを決めた「このバグは、分度器ではなく、ここで固定しなければならない。これを閉じます。」 また、 angular-ui-bootstrapは、ベータ版のAngularバージョン@ ng-bootstrap / ng-bootstrapの代わりにアップデートを提供しなくなりました。

分度器の有効性を保証しない、かなり使用されている角度ライブラリを見ると、私は驚いています。 その点で、私は@heathkitに同意します。これらのライブラリは自分で修正する必要があります。 しかし、私は分度器が一挙に介入してすべてを軽減できることを望んでいます。 素晴らしい回避策でさえ、黄金になります。 $timeout依存関係をすべて掘り下げるまで、今のところngUpgradeを取り消さなければならないことに失望しています。おそらく、ngGradeを含めて、可能であればアップグレード用のアイテムを選択し始めるのではなく、 ngUpgradeを追加するとともに、これらのサードパーティアイテムからアップグレードまたは移行する依存関係がある可能性があります。 うーん。

私は自分のアプリ(タイムアウトの原因となった特定の原因、前進する方法など)の調査を続けており、他の人に役立つ可能性のあるものはすべて報告します。

$ timeoutを見つけた場所:
angle-ui-bootstrap v2.5.6

  • UibAlertController
  • UibCarouselController
  • UibDatepickerPopupController
  • UibTypeaheadController
  • UibTooltip

ui-select

  • uiSelect
  • uiSelectMultiple
  • uiSelectSingle
  • uiSelectSort
  • uisOpenClose

angle-resource $resource$timeout注入して使用します
角度フィルター( $window.setTimeout

こんにちは-私たちは、分度器が待機するタスクを構成できるようにするための変更に取り組んでいます。これは、上記の問題を処理するのに役立ちます。 これはZone.jsとAngularを含む広範な変更であり、特定のETAはありません(数週間のオーダーになります)。 ここで進捗状況を更新します。

NgUpgradeを使用してます。
上記のコメント、特に@ mattwilson1024@tonybranfort感謝ます。これは、問題をよりよく理解するのに役立ちます。

私の場合、タイムアウトするコードの周りにbrowser.ignoreSynchronization = true/false;追加することで、回避策を見つけました。

たとえば、私のテストの1つは、ラジオボタンのグループで単純な選択を行い、正しいオプションが選択されていることを期待しています。

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');
});

このコードをデバッグしようとすると、最後のステップでタイムアウトが発生したことがわかりました。 根本的な問題は、$ timeoutを使用したangular-ui-bootstrapのツールチップだと思います: 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これに関する更新はありますか?

これらの長時間実行されるタイムアウトを見つけて、アプリケーションからそれらを取り除く方法について、誰か良いアドバイスがありますか?

差出人:分度器の問題#4290によって取り残されたAngularJSの島のCastaway(s?)
宛先@vikerman @juliemr

アホイ! この問題に関する最新情報を入手できますか? 10月16日の最後の更新は、「週単位」のETAでした。 または、この問題を閉じて、先に進んでください。

これらは私が現在検討しているいかだです:

  • すべてのangular-ui-bootstrap / ui-selectを置き換えます。 これは私にとって重要です。 とにかくそれを行う必要がありますが、私の懸念は、何か他のものがポップアップし、その作業の後もAngularJSで立ち往生することです。 教訓:サードパーティのコンポーネントを自分のコンポーネントでラップします。
  • @vladotesanovicによって提案されたすべてのテストに対してwaitForAngularEnabled(false) 。 私はアプリ全体についてこれに懐疑的ですが、テストしていません。 私は現在、1つの状況でのみ、非常に特殊なケースでのみwaitForAngularEnabledを使用しています。
  • アプリ全体をAngularJS / アップグレードします。増分移行:いいえ、私のリソースのオプションではありません。
  • Angularの代わりにVueまたはReact
  • Puppeteerに移動し、すべての分度器テストを破棄します-これはおそらく戦略的な方向性です。
  • または#4290からの修正で、ngUpgradeの使用を開始するために必要な数行のコメントを外すことができます:)

私たちのチームは複雑なアプリケーションをアップグレードしている最中であり、NgUpgradeでブートストラップすると分度器のテストが失敗します。 誤検知である失敗したテストのすべてに対処できないため、変更を元に戻す必要がありました。

この問題はどこにありますか? Angularのアップグレード計画を維持できるように、別のテストフレームワークに移行する必要があるのではないかと心配しています。

@tonybranfort :あなたとまったく同じボートで...私たちはui-selectangular-ui-bootstrap大きく依存しています-これは私たちのアップグレードの主要なブロッカーです。 この問題についてはあなたが主導権を握っているようですが、最新情報をお届けします。 😁

同じ問題が発生しています。 分度器の誰かがこれについて私たちを更新できますか?

私たちはアプリをAngularV1からアップグレードする途中であり、この原因がV1に戻って、これまでにAngularV5にアップグレードするために行われたすべての作業を放棄することを嫌います。

ここで別の票を投じて、アプリをAngularにアップグレードする過程にあり、現在、スイート全体をより大きなリスクにさらすことなく、これらを通過させるためにwaitForAngularEnabled(false)断片的なアプリケーションを使用しています。 ただし、これは非常に理想的ではないため、より良い解決策をいただければ幸いです。

特に$ intervalサービスに関しては、この問題も発生しました。 分度器がそれを待っていることもあれば、そうでないこともあります-私はまだパターンを見つけられませんでした。

長時間の編集:明らかに、間隔がいつ呼び出されるか(ゾーン内かどうか)によって異なります

こんにちは@ vikerman-分度器が待機するタスクを構成できるようにするための変更に関するニュースはありますか?

ここで同じ問題。

@vikerman更新または回避策はありますか?

同じ問題が発生しています。 この問題にいくらかの進展があれば素晴らしいでしょう。

悲しいことに、私は同じ問題を抱えています。 これに関して進展はありますか?

AngularとAngularJSにタスク追跡を追加します。これにより、Protractorがタイムアウトした非同期タスクがどこから来たのかを示すことで、これらの問題のデバッグが容易になります。

@heathkitこれはすでにどこかで行われていますか? コードベースでこれらの場所を特定するのに苦労しています。

また、分度器が熱心に待っているものをいくつかの文書があればいいのですが。 これは古くなっているようです: https ://www.protractortest.org/#/system-setup。 $timeout$intervalに置き換えることだけが言及されています。 setTimeoutまたはsetIntervalについては何もありません。
いくつかのドキュメントはここにあります: http

これは、「角度ハイブリッド+分度器」のテストを実行するのに役立つと思います。 それは私を助けました。 リンクを確認してください: https

この問題で何が起こっているのかを理解するために少し前進しました。 私たちは同じものを持っているようです。 アップグレードされたangularjsウォッチャー/ダイジェストサイクルの動作は少し異なり、無限のダイジェストサイクルループを有効にできるようです。 使用していたサードパーティの1つを見つけ、次の1つを調査しています。 しかし、基本的には、ウォッチャーが監視対象に変更を加えたり、ウォッチャーをトリガーしたりしていました。同じ問題が発生しているかどうかをテストするには、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オーバーライドすることです(サードパーティライブラリの場合)
以下のコードは、タイムアウトの問題を解決しました(念のためブラウザチェックを追加しました)。 私の場合、この問題は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);
    });
  };
})

おそらく、問題は分度器自体ではなく、アプリケーションコードにあります。

ダイジェストサイクルを使用して変更を追跡するAngularJSから、Angular2 + witchを使用してzone.jsを使用して変更を検出するハイブリッドアプリへの移行に伴い、Protractorはタイムアウトで低下し始めました。

分度器は、アプリケーションが安定している場合にのみ次のテストアクションを開始します。 ハイブリッドアプリの場合の安定性とは、アプリケーションのAngularJS部分とアプリケーションのAngular2 +部分の安定性を意味します。 両方の部品が安定している必要があります。

Angular JSの安定性は、 $$testability.whenStable定義されます。 Angular 2+の安定性は、 window.getAngularTestability(...).whenStableによって決定されます。 分度器のコードをデバッグして、自分の目で確かめることができます。 これを行うには、 debugger;ステートメントをwaitForAngular _ \ node_modules \ protractor \ built \ clientsidescripts.js_ファイルに追加します。
Angular2 +ゾーンの安定性は決して発生しないことに気付くでしょう。 安定性のために、すべてのマイクロタスクとマクロタスクを完了する必要があります。

これで問題がわかりました。 アプリケーションのAngular2 +部分は常に不安定であるため、分度器は安定してタイムアウトで落ちるまで無期限に待機します。 ハイブリッドアプリの場合のAngularJSの安定性は変更されるべきではなく、分度器のタイムアウトには影響しません。 このことから、Angular2 +ゾーンの不安定性を修正する必要があると結論付けることができます。そうすれば、分度器は期待どおりに機能し始めます。

修正の最初の試み:
時間がほとんどなく、すべてを一度に修正したかったので、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ゾーンにある必要があり、デコレータによってAngular2 +コンポーネントがルートゾーンにある可能性があります。 ngUpgradeは、Angularゾーンがほとんどのアプリケーションをカバーすることを意味します。

これを回避するために、理由をより深く分析し、デコレータを破棄して元の問題に戻すという2回目の修正が試みられました。

次に、どのコードがゾーンを不安定にするかを理解する必要があります。 このため、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ゾーンが安定した後、分度器はタイムアウトで落下しなくなり

お役に立てれば。

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 評価