Protractor: Busur derajat gagal saat aplikasi di-bootstrap dengan ngUpgrade

Dibuat pada 16 Mei 2017  ·  51Komentar  ·  Sumber: angular/protractor

Saya sedang mengerjakan migrasi aplikasi AngularJS (1.6) ke Angular (4) dan sekarang memiliki aplikasi hybrid, di-bootstrap dengan NgUpgrade. Ini tampaknya telah benar-benar merusak tes Busur Derajat saya.

Maafkan saya jika ini hanya sesuatu yang saya lakukan salah, tetapi saya tidak dapat menemukan jawaban mengapa ini tidak berhasil.

Kesalahan yang saya dapatkan adalah:

Gagal: Waktu habis menunggu tugas Angular asinkron selesai setelah 11 detik. Ini mungkin karena halaman saat ini bukan aplikasi Angular. Silakan lihat FAQ untuk detail lebih lanjut: https://github.com/angular/protractor/blob/master/docs/timeouts.md#waiting -for-angular

Sambil menunggu elemen dengan locator - Locator: By(css selector, .toggle-summary-button)

Perubahan aplikasi hibrida

Aplikasi tampaknya berjalan dengan baik, dan komponen AngularJS dan Angular berfungsi seperti yang diharapkan. Perubahan yang saya buat dalam proses bootstrap adalah:

1 Aplikasi ng yang dihapus dari tag html:

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

2 Menambahkan AppModules (@NgModule) dll.

3 Menggunakan NgUpgrade untuk mem-bootstrap aplikasi:

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

Tes busur derajat

Berdasarkan kesalahan di atas, masalahnya tampaknya terkait dengan apa pun yang dilakukan Protractor saat menunggu Angular. Saya memiliki blok beforeEach yang memuat halaman login, mengisi detail dan login. Anehnya, blok itu masih membuka halaman dan memasukkan teks ke dalam kolom nama pengguna, tetapi kemudian gagal untuk melangkah lebih jauh.

Saya telah mencoba, tanpa hasil:

  • menambahkan "rootElement: 'body'" ke file konfigurasi busur derajat saya
  • menambahkan "ng12Hybrid: true" ke file konfigurasi busur derajat saya - saya mendapatkan pesan yang mengatakan bahwa itu tidak lagi diperlukan karena otomatis mendeteksi.
  • meningkatkan pengaturan allScriptsTimeout dari 11000 menjadi 60000 dan waktu masih habis.
  • mematikan pengaturan waitForAngularEnabled. Ini memecahkan masalah dengan bidang login, tetapi kemudian tidak ada http saya yang mengolok-olok yang berfungsi dan tes gagal.

Komentar yang paling membantu

Hai - kami sedang mengerjakan perubahan untuk memungkinkan Anda mengonfigurasi tugas mana yang ditunggu oleh Protractor, yang dapat membantu Anda menangani masalah di atas. Ini adalah perubahan luas yang melibatkan Zone.js dan Angular dan tidak memiliki ETA spesifik (akan dalam urutan minggu). Akan memperbarui kemajuan di sini.

Semua 51 komentar

Saya memiliki masalah serupa yang diselesaikan dengan membungkus kode pemutakhiran ini seperti itu:

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

Dan dalam konfigurasi saya TIDAK MENGGUNAKAN salah satu dari itu:

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

Bisakah Anda melihat https://github.com/angular/protractor/issues/4234 dan melihat apakah Anda mengalami masalah yang sama? Juga, di ngUpgrade $interval sekarang akan memblokir busur derajat di tempat yang sebelumnya tidak (https://github.com/angular/angular/issues/16349), tapi saya sedang mengerjakannya.

@Evilweed - terima kasih atas sarannya, sayangnya itu sepertinya tidak menyelesaikannya untuk saya.

@heathkit - Saya menemukan satu $interval yang berjalan lama dengan mencari seluruh proyek, tetapi bahkan setelah menghapusnya, masalahnya tetap ada. Saya juga telah mengomentari semua penggunaan $interval atau $timeout di seluruh aplikasi dan masih gagal dengan kesalahan yang sama. Apakah Anda tahu jika ada cara untuk men-debug dan mencari tahu apa yang bisa diblokir jika ada masalah dengan sesuatu yang tidak sinkron?

Bisakah Anda memposting kesalahan lengkap Anda? Seharusnya menunjukkan tugas apa yang tertunda dalam pesan kesalahan, tetapi mungkin ada beberapa yang hilang. Misalnya, kesalahan belum akan menampilkan tugas yang tertunda dari sisi Angular. Jika Anda telah lama menjalankan tugas asinkron di bagian Angular aplikasi Anda, Anda harus menjalankannya di luar zona Angular agar tidak memblokir Protractor. (Lihat https://github.com/angular/protractor/blob/master/docs/timeouts.md)

Terima kasih lagi untuk menjawab. Detail kesalahan ada di bawah. Beberapa hal yang perlu diperhatikan:

  • File pengujian saya memiliki blok beforeAll yang seharusnya mengisi bidang nama pengguna dan kata sandi untuk masuk. Itu berhasil mengisi bidang nama pengguna tetapi kemudian waktu habis di waitForAngular dan tidak mengisi bidang kata sandi. Inilah yang tampaknya menjadi bagian pertama dari kesalahan. Babak kedua adalah tes gagal yang sebenarnya (yang masuk akal, karena gagal masuk sehingga elemen tidak ada).

  • Kesalahan menunjukkan bahwa masalahnya mungkin berasal dari panggilan HTTP, jadi mungkin bukan $interval atau $timeout.

  • Saya tidak punya banyak di sisi Angular saat ini, hampir semuanya masih AngularJS. Itu tidak sejauh menunjukkan komponen Angular baru karena tidak berhasil masuk (jadi saya ragu panggilan http ada hubungannya dengan komponen 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)

Persis sama - dengan konfigurasi angular4 'quickstart' baru dan tes busur derajat yang sangat mendasar. Semuanya bekerja dengan baik pada mode non hybrid.

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)

Saya masih melihat masalah ini.
Menggunakan: @ angular/upgrade 4.2.2
yang berisi perbaikan dari @heathkit ( https://github.com/angular/angular/pull/16836 ) untuk mendapatkan stabil Testability api, tetapi tampaknya masih tidak berfungsi

Jika seseorang dapat membagikan repo yang menunjukkan masalahnya, saya akan memeriksanya lebih lanjut.

Apa yang saya temukan sejauh ini

Setelah beberapa penggalian, saya menemukan salah satu penyebab batas waktu yang saya lihat (atau salah satunya, saya curiga itu bukan satu-satunya). Ternyata salah satu dependensi yang saya gunakan memanggil window.setTimeout() yang mencegah sinkronisasi busur derajat (dalam kasus saya, ini
momen sudut .

Ini berjalan dengan baik ketika aplikasi saya hanya menjalankan AngularJS tetapi menyebabkan masalah batas waktu ketika bootstrap sebagai aplikasi hybrid (mungkin karena cara dijalankan di zona).

Contoh Repo

Saya telah membuat contoh repo di https://github.com/mattwilson1024/angular-hybrid . Ini adalah aplikasi hybrid (NgUpgrade) dengan bagian-bagian berikut:

  • Halaman terluar (CharacterPage) disediakan oleh AngularJS (1.6).
  • Halaman tersebut berisi CharacterListComponent, yang merupakan komponen Angular (4) yang diturunkan.
  • CharacterListComponent menggunakan komponen Angular 4 lainnya, komponen CharacterDetail.

Cabang master berisi aplikasi yang berfungsi dan tes busur derajat tunggal yang tidak melewati masalah.

Cabang momen sudut menambahkan ketergantungan pada pustaka angular-moment dan menggunakan direktif am-time-ago untuk menambahkan label ke halaman yang menunjukkan berapa tahun yang lalu sejak episode pertama. Aplikasi itu sendiri berjalan dengan baik, tetapi tes busur derajat sekarang gagal disinkronkan.

Waktu habis

Untuk mengonfirmasi hal ini terjadi, saya mencoba menambahkan berbagai jenis batas waktu ke pengontrol AngularJS, dengan hasil sebagai berikut. Ini mengkonfirmasi bahwa $interval berfungsi, tetapi $timeout, setInterval, atau setTimeout tidak.

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

Langkah selanjutnya

Setelah berhasil melacak apa yang terjadi, saya masih tidak yakin apa solusinya. Saya mengerti bahwa mungkin perlu menjalankan potongan kode tertentu "di luar zona Angular", meskipun saya tidak yakin bagaimana melakukan ini dari segmen AngularJS di mana saya tidak memiliki kelas TypeScript untuk menyuntikkan zona, dll.

Meski begitu, jika kode masalah terjadi di dalam perpustakaan pihak ketiga (seperti dalam contoh saya dengan momen sudut) - apa pendekatan untuk mengatasi masalah ini agar tes berfungsi seperti sebelum memutakhirkan ke a pengaturan hibrida?

Zona diatur di jendela, jadi Anda tidak perlu menyuntikkan NgZone. Anda dapat berlari di luar zona Angular di AngularJS seperti ini

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

Anda benar bahwa setiap penggunaan setTimeout atau setInterval, bahkan dalam kode pihak ketiga, dapat memblokir stabilitas Protractor dan menyebabkan timeout. Kami menambahkan pelacakan tugas ke Angular dan AngularJS yang seharusnya memudahkan untuk men-debug masalah ini dengan menunjukkan kepada Anda dari mana tugas asinkron yang telah habis waktu Protractor berasal. Secara realistis, itu akan memakan waktu beberapa bulan sebelum kita bisa memasukkannya ke dalam Busur Derajat.

Sejauh momen sudut berjalan, opsi terbaik Anda mungkin adalah mengirimkan PR kepada mereka dengan membuatnya sadar zona seperti yang ditunjukkan di atas. Jika tidak, Anda dapat secara selektif mematikan waitForAngular dan menggunakan browser.wait dan ExpectedConditions sebagai gantinya. Kami sedang mengerjakan API waitForAngular yang lebih fleksibel yang memungkinkan Anda mengontrol tugas mana yang Anda tunggu, tetapi itu masih jauh.

Dalam situasi kami, tes dapat ditulis dengan cara generik (tanpa sinkronisasi Sudut) ( browser.ignoreSynchronization = true menggunakan EC, dll.), tetapi gagal saat menyetel browser.ignoreSynchronization = false; dengan "Error while waiting for Protractor to sync with the page: "Cannot read property '$$testability' of undefined"

Ini pada dasarnya membatalkan seluruh rangkaian pengujian otomatis E2E kami.

Menggunakan Busur Derajat 5.1.2, jika saya menetapkan ng12Hybrid: true saya mendapatkan pesan:

Anda telah mengatur ng12Hybrid. Mulai dari Protractor 4.1.0, Protractor dapat secara otomatis menyimpulkan jika Anda menggunakan aplikasi ngUpgrade (selama ng1 dimuat sebelum Anda memanggil platformBrowserDynamic()), dan flag ini tidak lagi diperlukan untuk sebagian besar pengguna

Jadi jelas dokumentasi angular.io sudah ketinggalan zaman di sini: https://angular.io/guide/upgrade#e2e -tests

Kata-kata terakhir yang terkenal dari dokumen yang sama:

Tes E2E tidak terlalu peduli dengan struktur internal komponen aplikasi. Itu juga berarti bahwa, meskipun Anda sedikit memodifikasi proyek selama pemutakhiran, rangkaian uji E2E harus tetap lulus hanya dengan sedikit modifikasi. Anda tidak mengubah cara aplikasi berperilaku dari sudut pandang pengguna.

Mengingat pernyataan itu di angular.io:

@heathkit Terima kasih atas tip tentang menjalankan di dalam dan di luar zona, tapi jujur ​​kami mencari solusi yang berjalan dengan kode AngularJS kami yang ada, apa adanya. Kami memiliki suite Busur Derajat yang luas yang bergantung pada sinkronisasi AngularJS implisit. Tujuan kami adalah memigrasikan AngularJS kami ke Angular menggunakan ngUpgrade, tetapi masalah dengan tidak dapat menjalankan suite Protractor kami yang ada dalam mode peningkatan Hybrid telah menggagalkan inisiatif kami. Perbaikan yang masuk akal akan sangat disambut saat ini.

Jika kita melakukan sesuatu yang salah secara fundamental, kita pasti ingin tahu apa.

Menghadapi masalah yang sama! :0

Melihat hal yang sama di sini: AngularJS 1.6 -> Angular 4.2.4

Adakah yang tahu solusi apa pun, betapapun besarnya? Tanpa kembali ke 1.6 itu.

@quinw68 - @heathkit menyarankan metode menjalankan kode di luar 'Zona' Sudut dalam respons sebelumnya di utas ini. Saya pikir ini berarti perubahan pada kode sumber aplikasi Anda untuk membuatnya berfungsi, dan bahkan mungkin bergantung pada perpustakaan pihak ketiga yang menambahkan dukungan. Mohon koreksi saya jika saya salah.

'Penyelesaian' lain yang saya ketahui adalah memperlakukan aplikasi Anda yang ada seperti itu bukan lagi aplikasi AngularJS. Ini berarti bahwa Anda harus mengubah pengujian Busur Derajat agar pengujian tersebut tidak lagi menunggu Angular, tetapi secara eksplisit memeriksa keberadaan elemen pada halaman sebelum menanyakan statusnya. Bergantung pada ukuran aplikasi Anda, ini bisa menjadi tugas yang menakutkan.

Semoga membantu.

Sejauh yang saya mengerti masalahnya, ini bukan khusus ngUpgrade, melainkan khusus Angular. Maksud saya, bahkan dalam aplikasi Angular (2+) murni, memiliki setTimeout (baik secara langsung atau melalui perpustakaan pihak ketiga) akan menyebabkan waktu habis busur derajat.

@heathkit , dapatkah Anda mengonfirmasi itu atau apakah saya melewatkan sesuatu? Apakah ngUpgrade membuang Busur Derajat atau memperburuk keadaan?

@gkalpak Anda benar bahwa ini lebih merupakan masalah khusus Angular, tetapi orang-orang menghadapinya ketika mereka menggunakan ngUpgrade dengan aplikasi AngularJS yang besar.

Di AngularJS, Protractor hanya akan menunggu tugas asinkron yang dimulai dengan $browser.defer() - pada dasarnya hanya panggilan $timeout dan $http dalam kode aplikasi. Setelah seseorang mulai menggunakan ngUpgrade, pengujian Protractor mereka yang ada akan mulai kehabisan waktu karena panggilan setTimeout (mungkin dari perpustakaan pihak ketiga) yang sebelumnya tidak mereka lakukan. Jadi sebagian besar, masalahnya adalah ngUpgrade berfungsi sebagaimana dimaksud, dan sekarang aplikasi AngularJS dan tes Busur Derajat tiba-tiba tunduk pada semantik Angular (2+).

Jadi, bagaimana cara mengatasi ini untuk aplikasi Angular murni? Apa yang akan dilakukan jika perpustakaan Angular pihak ketiga menyetel setTimeout , menyebabkan waktu habis busur derajat?

@vikerman Saya mendengar Anda akan membantu mendapatkan anggota tim baru dan menjalankan yang satu ini segera, jadi tetapkan untuk Anda sampai mereka bergabung.

Kami tidak benar-benar memiliki jawaban yang baik untuk kasus di mana aplikasi pihak ketiga menyetel batas waktu yang lama. Saat ini, yang dapat Anda lakukan hanyalah mematikan waitForAngular dan mengandalkan ExpectedConditions, tetapi itu tidak ideal. Saya sedang berupaya menambahkan fitur ke Busur Derajat yang akan memberi orang lebih banyak kontrol atas tugas makro mana yang menunggu tes mereka, tetapi itu masih jauh.

@heathkit @vikerman Adakah pembaruan untuk yang ini?

Proyek yang saya kerjakan memiliki masalah ini sebelumnya tetapi mereka membungkus fungsi polling berkelanjutan untuk dijalankan di luar zona sudut sehingga tidak memengaruhi browser.waitForAngular();

Pertama-tama terima kasih kepada tim/kontributor Busur Derajat . Busur derajat telah menjadi landasan bagi aplikasi saya untuk memiliki sedikit masalah produksi selama lusinan rilis selama 3 tahun, lebih dari 200 layanan, arahan, komponen, pengontrol dengan sekitar 400 tes Busur Derajat e2e.

Ini adalah pengingat yang baik betapa saya mengandalkan Busur Derajat tetapi masalah ini telah menghentikan peningkatan Angular saya di jalurnya setelah mendedikasikan 2 minggu berkomitmen hanya untuk bootstrap ngUpgrade dan kebutuhan tambahan. Itu sudah selesai tetapi sekarang tes e2e tidak dapat dijalankan dan penerapan dihentikan tanpa jalur yang terlihat ke depan (belum).

Berkat info hebat dari @mattwilson1024 , saya telah memastikan $timeout , setTimeout , setInterval tidak digunakan di aplikasi kami.

Tapi, saya menemukan bahwa angular-ui-bootstrap (v.2.5.6) dan ui-select (v0.19.8) memang menggunakan $timeout . Saya menggunakan kedua perpustakaan secara ekstensif. Tampaknya banyak orang lain juga bergantung padanya - angular-ui-bootstrap - 400rb npm unduhan/bln, ui-select 100k+/bln.

angular-ui-bootstrap membuat masalah pada tahun 2015 untuk mengganti $timeout dengan $interval khusus karena masalah ini, tetapi sayangnya telah memutuskan "bug ini harus diperbaiki di busur derajat dan bukan di sini. Menutup ini." Juga, angular-ui-bootstrap tidak lagi menyediakan pembaruan sebagai pengganti versi Angular mereka @ng-bootstrap/ng-bootstrap yang dalam versi beta.

Saya terkejut ketika saya melihat perpustakaan sudut yang digunakan secara signifikan yang tidak memastikan validitas Busur Derajat. Tentang itu, saya setuju dengan @heathkit bahwa perpustakaan itu perlu memperbaiki diri. Tapi, saya berharap Busur Derajat dapat turun tangan dan mengurangi semuanya dengan satu gerakan. Bahkan solusi yang bagus akan menjadi emas. Saya kecewa karena saya harus membatalkan ngUpgrade saya untuk saat ini sampai saya menggali semua dependensi $timeout dan, mungkin, daripada hanya memasukkan ngGrade dan mulai memilih item untuk peningkatan jika memungkinkan, saya kemungkinan memiliki ketergantungan untuk memutakhirkan atau bermigrasi dari item pihak ketiga tersebut bersama dengan menambahkan ngUpgrade. Ugh.

Saya terus meneliti aplikasi saya (penyebab spesifik yang menyebabkan batas waktu), cara melanjutkan, dll.) dan akan melaporkan kembali apa pun yang mungkin berguna bagi orang lain.

Di mana saya menemukan $timeout:
angular-ui-bootstrap v2.5.6

  • UibAlertController
  • UibCarouselController
  • UibDatepickerPopupController
  • UibTypeaheadController
  • UibKeterangan alat

ui-pilih

  • uiPilih
  • uiPilihBeberapa
  • uiPilihSingle
  • uiPilihUrut
  • uisBukaTutup

angular-resource $resource menyuntikkan dan menggunakan $timeout
filter sudut ( $window.setTimeout )

Hai - kami sedang mengerjakan perubahan untuk memungkinkan Anda mengonfigurasi tugas mana yang ditunggu oleh Protractor, yang dapat membantu Anda menangani masalah di atas. Ini adalah perubahan luas yang melibatkan Zone.js dan Angular dan tidak memiliki ETA spesifik (akan dalam urutan minggu). Akan memperbarui kemajuan di sini.

Saya memiliki masalah yang sama ketika saya mem -bootstrap aplikasi [email protected].
Terima kasih atas komentar di atas, terutama @mattwilson1024 dan @tonybranfort , yang membantu saya lebih memahami masalah ini.

Saya telah menemukan solusi dalam kasus saya, dengan menambahkan browser.ignoreSynchronization = true/false; sekitar kode waktu habis.

Misalnya, salah satu pengujian saya melakukan pemilihan sederhana di sekelompok tombol radio, dan berharap opsi yang benar dipilih:

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

Ketika saya mencoba untuk men-debug kode ini, saya menemukan bahwa waktu habis terjadi pada langkah terakhir. Saya kira masalah mendasarnya adalah toollip dari angular-ui-bootstrap, dengan penggunaan $timeout: https://github.com/angular-ui/bootstrap/blob/master/src/tooltip/tooltip.js

Namun, tidak ada panggilan asinkron dalam pengujian ini. Jadi yang saya lakukan, cukup tambahkan abaikan Sinkronisasi di awal dan setel ulang di akhir:

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

Saya tidak tahu apakah ini cara yang disarankan atau apakah itu akan membuat tes lebih terkelupas. Tetapi untuk pengujian yang tidak memerlukan panggilan asinkron apa pun tetapi masih mengalami masalah batas waktu, itu harus memperbaikinya.

@vikerman Apakah ada pembaruan tentang ini?

Adakah yang punya saran bagus tentang cara menemukan batas waktu yang lama ini sehingga saya bisa mengeluarkannya dari aplikasi saya?

Dari : Castaway(s?) di Pulau AngularJS terdampar oleh Busur Derajat Masalah #4290
Kepada : @vikerman @juliemr

Ah! Bisakah kami mendapatkan pembaruan tentang masalah ini? Pembaruan terakhir pada 16 Oktober adalah ETA dalam "urutan minggu". Atau, tolong tutup masalah ini agar kita bisa melanjutkan.

Ini adalah rakit yang sedang saya pertimbangkan:

  • Ganti semua angular-ui-bootstrap / ui-select . Ini penting bagi saya. Itu harus tetap dilakukan tetapi kekhawatiran saya adalah sesuatu yang lain akan muncul dan saya masih akan terjebak di AngularJS setelah pekerjaan itu. Hal yang dipelajari : bungkus komponen pihak ke-3 dengan komponen saya sendiri.
  • waitForAngularEnabled(false) untuk semua tes seperti yang disarankan oleh @vladotesanovic. Saya skeptis terhadap ini untuk seluruh aplikasi tetapi saya belum mengujinya. Saya menggunakan waitForAngularEnabled hanya dalam satu situasi sekarang dan hanya untuk kasus yang sangat spesifik.
  • Tingkatkan seluruh aplikasi ke Angular daripada AngularJS/Angular migrasi inkremental berdampingan: Tidak, hanya saja bukan opsi dengan sumber daya saya.
  • Migrasi ke Vue atau React alih-alih Angular. Tinggi dalam daftar terutama mengingat keduanya dapat hidup berdampingan dengan AngularJs.
  • Pindah ke Dalang dan buang semua tes Busur Derajat - Ini kemungkinan arah strategis.
  • ATAU perbaikan dari #4290 dan saya dapat menghapus komentar beberapa baris yang diperlukan untuk mulai menggunakan ngUpgrade :)

Tim kami sedang dalam proses memutakhirkan aplikasi kompleks kami dan tes busur derajat gagal saat di-bootstrap dengan NgUpgrade. Kami harus mengembalikan perubahan kami karena sekarang kami tidak dapat menangani semua tes yang gagal yang merupakan hasil positif palsu.

Di mana kita dengan masalah ini? Saya khawatir saya harus pindah ke kerangka pengujian yang berbeda sehingga kami dapat mempertahankan rencana kami untuk memutakhirkan Angular.

@tonybranfort : Di kapal yang sama persis seperti Anda... Kami sangat bergantung pada ui-select dan angular-ui-bootstrap - ini adalah penghalang utama untuk peningkatan kami. Sepertinya Anda yang memimpin masalah ini, harap beri tahu kami! 😁

Kami memiliki masalah yang sama. Bisakah seseorang dari busur derajat memberi tahu kami tentang ini?

Kami setengah jalan dalam meningkatkan aplikasi kami dari Angular V1 dan benci memiliki alasan ini adalah kembali ke V1 dan membuang semua pekerjaan yang dilakukan untuk peningkatan ke Angular V5 sejauh ini.

Memberikan suara lain di sini, kami sedang dalam proses memutakhirkan aplikasi kami ke Angular dan kami saat ini menggunakan aplikasi sedikit demi sedikit waitForAngularEnabled(false) untuk membuatnya lulus tanpa menempatkan seluruh suite pada risiko yang lebih besar. Ini sangat tidak ideal, jadi solusi yang lebih baik akan sangat dihargai.

Kami baru saja menemukan masalah ini, khususnya dalam hal layanan $interval. Terkadang busur derajat menunggu dan terkadang tidak - saya belum menemukan polanya.

Edit lama: tampaknya itu tergantung kapan interval dipanggil (di zona atau tidak)

Hai @vikerman -

Masalah yang sama di sini.

@vikerman apakah ada pembaruan atau solusi?

Kami mengalami masalah yang sama. Akan sangat bagus jika ada kemajuan dalam masalah ini.

Sayangnya, saya memiliki masalah yang sama. Apakah ada kemajuan yang terlihat mengenai hal ini?

Kami menambahkan pelacakan tugas ke Angular dan AngularJS yang seharusnya memudahkan untuk men-debug masalah ini dengan menunjukkan kepada Anda dari mana tugas asinkron yang telah habis waktu Protractor berasal.

@heathkit Apakah ini sudah dilakukan di suatu tempat? kami mengalami kesulitan untuk mengidentifikasi tempat-tempat itu di basis kode kami.

juga akan menyenangkan untuk memiliki beberapa dokumentasi yang sangat ditunggu-tunggu oleh busur derajat. Yang ini sepertinya sudah ketinggalan zaman: https://www.protractortest.org/#/system -setup. itu hanya menyebutkan untuk mengganti $timeout dengan $interval . Tidak ada tentang setTimeout atau setInterval.
beberapa dokumen ada di sini: http://www.protractortest.org/#/timeouts. tapi tidak ada petunjuk bagaimana mendeteksi mereka...

Saya pikir ini akan membantu Anda menjalankan tes Anda "hibrida sudut + busur derajat". Itu membantu saya. Periksa tautan: https://github.com/ui-router/angular-hybrid/issues/39#issuecomment -309057814

Kami telah membuat sedikit kemajuan dalam memahami apa yang terjadi dengan masalah ini. Sepertinya kita memiliki hal yang sama. Tampaknya siklus pengamat/pencernaan angularjs yang ditingkatkan bekerja sedikit berbeda dan dapat mengaktifkan beberapa loop siklus intisari yang tak terbatas. Kami menemukan 1 dengan pihak ketiga yang kami gunakan, dan sedang menyelidiki yang berikutnya. Tetapi pada dasarnya pengamat menyebabkan perubahan pada hal yang sedang ditonton, menyebabkan pengamat memicu, dll. Untuk menguji apakah Anda mengalami masalah yang sama, di chrome cukup buka alat dev, buka kinerja, rekam beberapa detik, dan lihat apakah Anda sering melakukan panggilan NgZone. Jika ya, maka di suatu tempat dalam kode Anda, Anda memiliki siklus intisari tak terbatas. Menelusuri panggilan dapat membantu mengarahkan Anda ke arah penyebabnya

Anda juga dapat sedikit memodifikasi file zone.js lokal dan menambahkan log konsol tempat tugas batas waktu/interval ditambal untuk melihat apa yang memanggilnya. Cara mudah untuk melakukannya:

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

Sunting: sangat menyenangkan untuk memformat kode di ponsel!

@vikerman Apakah Anda memiliki pembaruan tentang ini? Akan lebih bagus jika Anda bisa memberikan pendapat Anda tentang seberapa cepat ini akan diperbaiki? Terima kasih.

Anda juga dapat sedikit memodifikasi file zone.js lokal dan menambahkan log konsol tempat tugas batas waktu/interval ditambal untuk melihat apa yang memanggilnya. Cara mudah untuk melakukannya:

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

Sunting: sangat menyenangkan untuk memformat kode di ponsel!

Di mana Anda akan meletakkan kode itu untuk melacak batas waktu/interval?

@maurycyg patchTimer -> fungsi

Utas ini menyedihkan untuk dilihat. Apakah ada pembaruan APAPUN tentang ini?

Solusi potensial untuk tidak menggunakan $timeout adalah dengan menimpanya dengan $interval (untuk perpustakaan pihak ketiga)
Kode di bawah ini menyelesaikan masalah batas waktu untuk saya (menambahkan pemeriksaan browser untuk berjaga-jaga). Dalam kasus saya, masalah ini disebabkan oleh komponen angular-bootstrap, yang secara tak terbatas disebut $timeout dengan penundaan 0.

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

Mulai dari zone.js v0.8.9, Anda dapat memilih modul API web mana yang ingin Anda tambal untuk mengurangi overhead yang ditimbulkan oleh penambalan modul ini.
https://github.com/angular/zone.js/blob/master/MODULE.md

Tambahkan file zone-flags.ts dengan konten di bawah
(window as any).__Zone_disable_timers = true;

Impor di polyfills.ts sebelum zone.js

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

Tampaknya solusi di atas dengan menonaktifkan pengatur waktu zona mengacaukan Obserables, (mis. asyncValidators). Solusi yang akhirnya berhasil bagi saya adalah setTimeout yang menambal monyet.

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

Mungkin masalahnya bukan di Busur Derajat itu sendiri, tetapi di kode aplikasi.

Dengan transisi dari Angular JS yang menggunakan siklus intisari untuk melacak perubahan, ke Aplikasi Hibrida dengan penyihir Angular2+ menggunakan zone.js untuk mendeteksi perubahan, Busur Derajat mulai turun seiring waktu habis.

Busur derajat memulai tindakan pengujian berikutnya hanya ketika aplikasi stabil. Stabilitas dalam kasus aplikasi hybrid berarti stabilitas bagian Angular JS dari aplikasi dan Angular2+ bagian dari aplikasi. Kedua bagian harus stabil.

Stabilitas JS sudut ditentukan melalui $$testability.whenStable . Stabilitas Angular 2+ ditentukan melalui window.getAngularTestability(...).whenStable . Anda dapat men-debug kode Busur Derajat dan melihatnya sendiri. Untuk melakukan ini, tambahkan pernyataan debugger; ke file waitForAngular _\node_modules\protractor\built\clientsidescripts.js_;
Anda akan melihat bahwa stabilitas zona Angular2+ tidak pernah terjadi. Untuk stabilitas , semua tugas mikro dan tugas makro harus diselesaikan.

Sekarang kita mengerti masalahnya. Angular 2+ bagian dari aplikasi selalu tidak stabil dan karena itu busur derajat menunggu tanpa batas waktu sampai menjadi stabil dan jatuh dengan batas waktu. Stabilitas JS sudut dalam kasus aplikasi hybrid seharusnya tidak berubah dan tidak mempengaruhi waktu tunggu busur derajat. Dari sini kita dapat menyimpulkan bahwa perlu untuk memperbaiki ketidakstabilan zona Angular2+ dan kemudian Busur Derajat akan mulai bekerja seperti yang diharapkan.

Upaya pertama untuk memperbaiki:
Karena hanya ada sedikit waktu dan saya ingin memperbaiki semuanya sekaligus, saya menulis kode yang mengeluarkan penjadwalan tugas dari zona 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);
    }

Meskipun pendekatan ini telah terbukti dapat diterapkan, seperti yang telah ditunjukkan oleh waktu, pendekatan ini memiliki kelemahannya sendiri. Yang utama adalah tidak mungkin untuk menentukan zona mana yang akan (root atau angular) untuk bagian tertentu dari aplikasi hybrid. Untuk operasi normal, Angular2+ mengharuskannya selalu berada di zona Angular, dan dekorator dapat menyebabkan komponen Angular2+ berada di zona root. ngUpgrade menyiratkan bahwa zona Angular mencakup sebagian besar aplikasi.

Untuk menghindari hal ini, ada upaya perbaikan ke-2 dengan analisis alasan yang lebih dalam, mengabaikan dekorator dan kembali ke masalah semula.

Sekarang kita perlu memahami kode apa yang membuat zona tidak stabil. Untuk ini, zone.js memiliki utilitas khusus, seperti _TaskTrackingZoneSpec_ (_\node_modules\zone.js\dist\task-tracking.js_), yang memungkinkan untuk melacak tugas mana yang berada di zona dan di mana mereka dibuat ( creationLocation ). Juga, di awal bagian Angular2+ dari aplikasi, simpan zona Angular (window as any).a9Zone = (upgradeModule as any).ngZone._inner; dan tambahkan kode ini di suatu tempat

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

Jadi, temukan tempat di aplikasi di mana tugas mikro dan tugas makro berada di zona untuk waktu yang lama (_microTasks.lenght! == 0 && macroTasks.lenght! == 0_).

Maka Anda perlu mengeluarkan kode ini dari zona Angular. Untuk melakukan ini, bungkus kode dalam ngZone.runOutsideAngular . Namun, lakukan dengan sangat hati-hati dan pastikan zona root tidak muncul di tempat yang tidak diperlukan.

Setelah zona Angular menjadi stabil, Busur Derajat tidak akan lagi jatuh dengan batas waktu .

Semoga ini membantu.

Saya datang dengan peretasan sementara untuk menyelesaikan masalah ini dengan mengganti fungsi waitForAngular dengan logika di bawah ini.

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)
            };
}
Apakah halaman ini membantu?
0 / 5 - 0 peringkat