Protractor: El transportador falla cuando la aplicación se inicia con ngUpgrade

Creado en 16 may. 2017  ·  51Comentarios  ·  Fuente: angular/protractor

Estoy trabajando en la migración de una aplicación AngularJS (1.6) a Angular (4) y ahora tengo una aplicación híbrida, arrancada con NgUpgrade. Esto parece haber roto completamente mis pruebas de transportador.

Perdóname si esto es solo algo que estoy haciendo mal, pero no puedo encontrar ninguna respuesta de por qué esto no funcionará.

El error que obtengo es:

Fallido: se agotó el tiempo de espera para que las tareas angulares asincrónicas terminen después de 11 segundos. Esto puede deberse a que la página actual no es una aplicación Angular. Consulte las preguntas frecuentes para obtener más detalles: https://github.com/angular/protractor/blob/master/docs/timeouts.md#waiting -for-angular

Mientras espera el elemento con localizador - Localizador: Por (selector css, .toggle-summary-button)

Cambios en la aplicación híbrida

La aplicación parece funcionar bien, y los componentes AngularJS y Angular funcionan como se esperaba. Los cambios que hice en el proceso de arranque son:

1 Se eliminó ng-app de la etiqueta html:

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

2 Se agregó un AppModules (@NgModule), etc.

3 Usé NgUpgrade para arrancar la aplicación:

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

Pruebas de transportador

Basado en el error anterior, el problema parece estar relacionado con cualquier cosa que haga Transportador cuando está esperando Angular. Tengo un bloque beforeEach que carga la página de inicio de sesión, completa los detalles e inicia sesión. Extrañamente, todavía está abriendo la página e ingresando texto en el campo de nombre de usuario, pero luego no llega más lejos.

Lo he intentado, sin éxito:

  • agregando "rootElement: 'body'" a mi archivo de configuración del transportador
  • agregando "ng12Hybrid: true" a mi archivo de configuración de transportador - aparece un mensaje que dice que ya no debería ser necesario ya que se detecta automáticamente.
  • aumentando la configuración de allScriptsTimeout de 11000 a 60000 y aún se agota el tiempo de espera.
  • desactivando la configuración waitForAngularEnabled. Esto resuelve el problema con los campos de inicio de sesión, pero ninguno de mis simulacros http funciona y las pruebas fallan.

Comentario más útil

Hola, estamos trabajando en un cambio para permitirle configurar qué tareas espera el transportador, que puede ayudarlo a manejar el problema anterior. Este es un cambio amplio que involucra a Zone.js y Angular y no tiene una ETA específica (estará en el orden de semanas). Se actualizará el progreso aquí.

Todos 51 comentarios

Tuve un problema similar que se resolvió envolviendo este código de actualización de esa manera:

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

Y en la configuración NO ESTOY UTILIZANDO ninguno de esos:

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

¿Podría echar un vistazo a https://github.com/angular/protractor/issues/4234 y ver si tiene el mismo problema? Además, en ngUpgrade $interval ahora bloqueará Transportador donde antes no lo hacía (https://github.com/angular/angular/issues/16349), pero estoy trabajando en eso.

@Evilweed : gracias por la sugerencia, desafortunadamente eso no parece resolverlo para mí.

@heathkit : encontré un $ intervalo de larga ejecución al buscar en todo el proyecto, pero incluso después de eliminarlo, el problema persiste. También he comentado todos los usos de $ interval o $ timeout en toda la aplicación y aún falla con el mismo error. ¿Sabe si hay alguna forma de depurar y averiguar qué es lo que podría estar bloqueando si se trata de un problema con algo asincrónico?

¿Puedes publicar tu error completo? Se supone que muestra qué tareas están pendientes en el mensaje de error, pero es posible que falten algunas. Por ejemplo, el error aún no mostrará las tareas pendientes desde el lado angular. Si tiene tareas asíncronas de ejecución prolongada en la parte angular de su aplicación, deberá ejecutar aquellas fuera de la zona angular para evitar bloquear el transportador. (Ver https://github.com/angular/protractor/blob/master/docs/timeouts.md)

Gracias de nuevo por responder. El detalle del error está a continuación. Algunas cosas a tener en cuenta:

  • Mi archivo de prueba tiene un bloque beforeAll que se supone que debe completar los campos de nombre de usuario y contraseña para iniciar sesión. Completa el campo de nombre de usuario correctamente, pero luego se agota en waitForAngular y no completa el campo de contraseña. De esto parece tratarse la primera mitad del error. La segunda mitad es la prueba fallida real (lo cual tiene sentido, porque no pudo iniciar sesión, por lo que el elemento no está presente).

  • El error sugiere que el problema puede deberse a una llamada HTTP, por lo que tal vez no sea $ interval o $ timeout.

  • No tengo mucho en el lado angular en este momento, casi todo sigue siendo AngularJS. No llega tan lejos como para mostrar el nuevo componente Angular porque no logra iniciar sesión (así que dudo que la llamada http tenga que ver con el componente 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)

Exactamente lo mismo, con una nueva configuración angular4 de 'inicio rápido' y una prueba de transportador muy básica. Todo funciona bien en modo no híbrido.

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)

Sigo viendo este problema.
Usando: @ angular / upgrade 4.2.2
que contiene la solución de @heathkit (https://github.com/angular/angular/pull/16836) para que la API de Testability sea estable, pero todavía no parece funcionar

Si alguien pudiera compartir un repositorio que muestre el problema, lo investigaré más a fondo.

Lo que he descubierto hasta ahora

Después de investigar un poco, descubrí una de las causas del tiempo de espera que estoy viendo (o una de ellas, sospecho que no es la única). Resulta que una de las dependencias que estoy usando está llamando a window.setTimeout() que impide que el transportador se sincronice (en mi caso, es
momento angular .

Esto funcionó bien cuando mi aplicación se estaba ejecutando solo AngularJS, pero causa un problema de tiempo de espera cuando se inicia como una aplicación híbrida (presumiblemente debido a la forma en que se ejecuta en las zonas).

Repo de ejemplo

Creé un repositorio de ejemplo en https://github.com/mattwilson1024/angular-hybrid . Es una aplicación híbrida (NgUpgrade) con las siguientes partes:

  • La página más externa (la CharactersPage) la proporciona AngularJS (1.6).
  • La página contiene CharacterListComponent, que es un componente Angular (4) degradado.
  • CharacterListComponent utiliza otro componente de Angular 4, el componente CharacterDetail.

La rama maestra contiene la aplicación de trabajo y pruebas de un solo transportador que no pasa ningún problema.

La rama de momento angular agrega una dependencia en la biblioteca angular-moment y usa su directiva am-time-ago para agregar una etiqueta a la página que muestra cuántos años hace desde el primer episodio. La aplicación en sí funciona bien, pero la prueba del transportador ahora no se sincroniza.

Tiempos de espera

Para confirmar que esto está sucediendo, intenté agregar varios tipos diferentes de tiempo de espera al controlador AngularJS, con los siguientes resultados. Esto confirmó que $ interval funciona, pero $ timeout, setInterval o setTimeout no.

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

Próximos pasos

Habiendo logrado rastrear lo que está sucediendo, todavía no estoy seguro de cuál es la solución. Entiendo que puede ser necesario ejecutar ciertas piezas de código "fuera de la zona angular", aunque no estoy seguro de cómo hacerlo desde segmentos de AngularJS donde no tengo una clase de TypeScript en la que inyectar la zona, etc.

Incluso entonces, si el código del problema ocurre dentro de una biblioteca de terceros (como es el caso en mi ejemplo con momento angular), ¿cuál sería el enfoque para solucionar este problema para que las pruebas funcionen como lo hicieron antes de actualizar a un configuración híbrida?

Las zonas se establecen en la ventana, por lo que no es necesario inyectar NgZone. Puede ejecutar fuera de la zona angular en AngularJS así

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

Tiene razón en que cualquier uso de setTimeout o setInterval, incluso en código de terceros, puede bloquear la estabilidad del transportador y provocar tiempos de espera. Estamos agregando seguimiento de tareas a Angular y AngularJS que debería facilitar la depuración de estos problemas al mostrarle de dónde provino la tarea asíncrona en la que se agotó el tiempo de espera de Transportador. Siendo realistas, pasarán un par de meses antes de que podamos poner eso en Transportador.

En lo que respecta al momento angular, su mejor opción podría ser enviarles un PR para que sea consciente de la zona como se muestra arriba. De lo contrario, puede desactivar selectivamente waitForAngular y usar browser.wait y ExpectedConditions en su lugar. Estamos trabajando en una API waitForAngular más flexible que le permitirá controlar qué tareas espera, pero aún queda mucho por hacer.

En nuestra situación, las pruebas se pueden escribir de forma genérica (sin sincronización angular) ( browser.ignoreSynchronization = true usando EC, etc.), pero fallan al configurar browser.ignoreSynchronization = false; con "Error while waiting for Protractor to sync with the page: "Cannot read property '$$testability' of undefined"

Esto básicamente anula todo nuestro

Usando Transportador 5.1.2, si configuro ng12Hybrid: true obtengo el mensaje:

Ha configurado ng12Hybrid. A partir de Protractor 4.1.0, Protractor puede inferir automáticamente si está utilizando una aplicación ngUpgrade (siempre que ng1 se cargue antes de llamar a platformBrowserDynamic ()), y esta marca ya no es necesaria para la mayoría de los usuarios

Entonces, obviamente, la documentación de angular.io está desactualizada aquí: https://angular.io/guide/upgrade#e2e -tests

Últimas palabras famosas del mismo documento:

Las pruebas E2E no se preocupan tanto por la estructura interna de los componentes de la aplicación. Eso también significa que, aunque modifica un poco el proyecto durante la actualización, el conjunto de pruebas E2E debería seguir pasando con modificaciones menores. No cambió el comportamiento de la aplicación desde el punto de vista del usuario.

A la luz de esa declaración sobre angular.io:

@heathkit Gracias por el consejo sobre la ejecución dentro y fuera de las zonas, pero para ser honesto, estamos buscando una solución que se ejecute con nuestro código AngularJS existente, tal como está. Tenemos una amplia suite de transportadores que se basa en la sincronización AngularJS implícita. Nuestro objetivo es migrar nuestro AngularJS a Angular usando ngUpgrade, pero este problema de no poder ejecutar nuestra suite Protractor existente en el modo de actualización híbrido ha frustrado nuestra iniciativa. Una solución sensata sería bienvenida ahora mismo.

Si estamos haciendo algo fundamentalmente mal, seguramente nos gustaría saber qué.

¡Frente al mismo problema! : 0

Viendo lo mismo aquí: AngularJS 1.6 -> Angular 4.2.4

¿Alguien conoce algún tipo de solución, por grande que sea? Sin volver a 1.6, eso es.

@ quinw68 - @heathkit sugiere un método para ejecutar el código fuera de la 'Zona' angular en su respuesta anterior en este hilo. Creo que esto significa cambios en el código fuente de su aplicación para que funcione, e incluso puede depender de que las bibliotecas de terceros agreguen soporte. Por favor corrígeme si estoy equivocado.

Otra 'solución alternativa' que conozco es tratar su aplicación existente como si ya no fuera una aplicación AngularJS. Esto significa que tendrá que cambiar sus pruebas de transportador para que ya no esperen inherentemente a Angular, sino que verifiquen explícitamente la presencia de elementos en la página antes de consultar su estado. Dependiendo del tamaño de su aplicación, esto podría ser una tarea abrumadora.

Espero que ayude.

Por lo que entiendo el problema, esto no es específico de ngUpgrade, sino específico de Angular. Quiero decir, incluso en una aplicación Angular (2+) pura, tener un setTimeout largo (ya sea directamente o mediante una biblioteca de terceros) hará que el transportador se agote.

@heathkit , ¿puedes confirmar eso o me perdí algo? ¿NgUpgrade está echando a perder a Protractor o empeorando las cosas de alguna manera?

@gkalpak Tienes razón en que se trata más de un problema específico de Angular, pero la gente lo encuentra cuando usa ngUpgrade con una gran aplicación AngularJS.

En AngularJS, Protractor solo esperaría a que las tareas asíncronas comenzaran con $browser.defer() , básicamente solo $timeout y $http llamadas en el código de la aplicación. Una vez que alguien comienza a usar ngUpgrade, sus pruebas de transportador existentes comenzarán a agotarse debido a largas llamadas setTimeout (potencialmente de bibliotecas de terceros) donde no lo hacían antes. Entonces, principalmente, el problema es que ngUpgrade está funcionando según lo previsto, y ahora la aplicación AngularJS y las pruebas de transportador de las personas están repentinamente sujetas a la semántica de Angular (2+).

Entonces, ¿cómo se resolvería esto para una aplicación Angular pura? ¿Qué haría uno si una biblioteca Angular de terceros estableciera un setTimeout largo, lo que provocara que se agotara el tiempo de espera de Transportador?

@vikerman Escuché que vas a ayudar a que un nuevo miembro del equipo esté asignará hasta que estén a bordo.

Realmente no tenemos una buena respuesta para el caso en el que una aplicación de terceros establezca un tiempo de espera prolongado. Por el momento, todo lo que puede hacer es desactivar waitForAngular y confiar en ExpectedConditions, pero eso no es lo ideal. Estoy trabajando para agregar una función a Transportador que le dará a las personas más control sobre qué macrotasks esperan sus pruebas, pero eso todavía está lejos.

@heathkit @vikerman ¿ Alguna actualización sobre este, por favor?

El proyecto en el que trabajo tenía este problema antes, pero ajustaron la función de sondeo continuo para que se ejecutara fuera de la zona angular de modo que no afectara al browser.waitForAngular ();

Primero, gracias al equipo / contribuidores de Transportador . Transportador ha sido la base para que mi aplicación tenga muy pocos problemas de producción durante docenas de lanzamientos durante 3 años, más de 200 servicios, directivas, componentes, controladores con aproximadamente 400 pruebas de transportador e2e.

Este es un buen recordatorio de cuánto confío en Protractor, pero este problema ha detenido mi actualización de Angular en seco después de haber dedicado 2 semanas comprometidas con solo arrancar ngUpgrade y las necesidades auxiliares. Eso está hecho, pero ahora las pruebas de e2e no se pueden ejecutar y la implementación se detiene sin un camino visible hacia adelante (todavía).

Gracias a la excelente información de @ mattwilson1024 , me he asegurado de que $timeout , setTimeout , setInterval no se utilicen en nuestra aplicación.

Pero, descubrí que angular-ui-bootstrap (v.2.5.6) y ui-select (v0.19.8) usan $timeout . Utilizo ampliamente ambas bibliotecas. Parece que muchos otros también confían en ellos: angular-ui-bootstrap - 400k npm descargas / mes, ui-select 100k + / mo.

angular-ui-bootstrap creó un problema en 2015 para reemplazar $ timeout con $ interval específicamente debido a este problema, pero desafortunadamente ha decidido "este error debe corregirse en el transportador y no aquí. Cerrando esto". Además, angular-ui-bootstrap ya no proporciona actualizaciones en liu de su versión angular @ ng-bootstrap / ng-bootstrap que está en beta.

Me sorprende cuando veo bibliotecas angulares significativamente utilizadas que no garantizan la validez del transportador. En eso, estoy de acuerdo con @heathkit en que esas bibliotecas deben arreglarse solas. Pero, espero que Transportador pueda intervenir y mitigarlo todo de una sola vez. Incluso una buena solución sería de oro. Estoy decepcionado de tener que retirar mi ngUpgrade por ahora hasta que descubra todas esas dependencias $timeout y, posiblemente, en lugar de solo incluir ngGrade y comenzar a seleccionar elementos para actualizar cuando sea posible, probablemente tenga una dependencia para actualizar o migrar desde esos elementos de terceros junto con la adición de ngUpgrade. Puaj.

Continúo investigando para mi aplicación (los culpables específicos que causan los tiempos de espera, cómo avanzar, etc.) y reportaré cualquier cosa que pueda ser útil para otros.

Dónde encontré $ timeout:
angular-ui-bootstrap v2.5.6

  • UibAlertController
  • UibCarouselController
  • UibDatepickerPopupController
  • UibTypeaheadController
  • UibTooltip

ui-seleccionar

  • uiSelect
  • uiSelectMultiple
  • uiSelectSingle
  • uiSelectSort
  • uisOpenClose

recurso angular $resource inyecta y usa $timeout
filtro angular ( $window.setTimeout )

Hola, estamos trabajando en un cambio para permitirle configurar qué tareas espera el transportador, que puede ayudarlo a manejar el problema anterior. Este es un cambio amplio que involucra a Zone.js y Angular y no tiene una ETA específica (estará en el orden de semanas). Se actualizará el progreso aquí.

Tengo el mismo problema cuando arranco mi aplicación [email protected] con NgUpgrade a [email protected].
Gracias por los comentarios anteriores, especialmente @ mattwilson1024 y @tonybranfort , que me ayudan a comprender mejor el problema.

Encontré un workaournd en mi caso, agregando browser.ignoreSynchronization = true/false; alrededor del código que se agota.

Por ejemplo, una de mis pruebas hizo una selección simple en un grupo de botones de radio y esperaba que se seleccionara la opción correcta:

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

Cuando trato de depurar este código, encontré que se agotó el tiempo de espera en el último paso. Supongo que el problema subyacente es un toolip de angular-ui-bootstrap, con el uso $ timeout: https://github.com/angular-ui/bootstrap/blob/master/src/tooltip/tooltip.js

Sin embargo, no hay llamadas asincrónicas en esta prueba. Entonces, lo que hago es simplemente agregar ignoreSynchronization al principio y restablecerlo al final:

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

No sé si esta es la forma recomendada o si hará que las pruebas sean más inestables. Pero para las pruebas que no requieren llamadas asíncronas pero que aún sufren el problema del tiempo de espera, debería solucionarlas.

Hola @vikerman - ¿Alguna actualización sobre esto? https://github.com/angular/protractor/issues/4290#issuecomment -337094393

@vikerman ¿Hay alguna actualización sobre esto?

¿Alguien tiene un buen consejo sobre cómo encontrar estos tiempos de espera prolongados para poder sacarlos de mi aplicación?

De : Castaway (s?) En la isla de AngularJS varado por Protractor Issue # 4290
Para : @vikerman @juliemr

¡Ahoy! ¿Podríamos obtener una actualización sobre este tema? La última actualización del 16 de octubre fue una ETA en el "orden de las semanas". O bien, cierre este problema para que podamos seguir adelante.

Estas son las balsas que estoy considerando actualmente:

  • Reemplace todos los archivos angulares-ui-bootstrap / ui-select . Esto es importante para mi. Debe hacerse de todos modos, pero mi preocupación es que aparecerá algo más y todavía estaré atascado en AngularJS después de ese trabajo. Lección aprendida: envolver componentes de terceros con mis propios componentes.
  • waitForAngularEnabled(false) para todas las pruebas sugeridas por @vladotesanovic. Soy escéptico con esto para una aplicación completa, pero no la he probado. Ahora uso waitForAngularEnabled en una sola situación y solo para un caso muy específico.
  • Actualice toda la aplicación a Angular en lugar de AngularJS / Angular en paralelo, migración incremental: No, simplemente no es una opción con mis recursos.
  • Migre a Vue o React en lugar de Angular. En lo alto de la lista, especialmente considerando que ambos pueden coexistir con AngularJs.
  • Pasar a Titiritero y deshacerse de todas las pruebas de transportador: esta es probablemente una dirección estratégica.
  • O una solución de # 4290 y puedo descomentar esas pocas líneas necesarias para comenzar a usar ngUpgrade :)

Nuestro equipo está en el proceso de actualizar nuestra aplicación compleja y las pruebas de transportador fallan cuando se arranca con NgUpgrade. Tuvimos que revertir nuestros cambios porque ahora no podemos lidiar con todas las pruebas fallidas que son falsos positivos.

¿Dónde estamos con este problema? Me preocupa tener que cambiarme a un marco de prueba diferente para que podamos mantener nuestros planes de actualización de Angular.

@tonybranfort : Dependemos en gran medida de ui-select y angular-ui-bootstrap ; este es un gran obstáculo para nuestra actualización. Sin embargo, parece que ha tomado la iniciativa en este tema, ¡manténganos informados! 😁

Estamos teniendo el mismo problema. ¿Alguien del transportador puede actualizarnos sobre esto, por favor?

Estamos a medio camino de actualizar nuestra aplicación desde Angular V1 y odio que esta causa sea volver a V1 y deshacernos de todo el trabajo realizado para la actualización a Angular V5 hasta ahora.

Lanzando otro voto aquí, estamos en el proceso de actualizar nuestra aplicación a Angular y actualmente estamos usando una aplicación fragmentada de waitForAngularEnabled(false) para que se aprueben sin poner toda la suite en mayor riesgo. Sin embargo, es extraordinariamente poco ideal, por lo que se agradecería mucho una mejor solución.

También abordamos este problema, específicamente cuando se trata del servicio $ interval. A veces, el transportador lo está esperando y, a veces, no; todavía no encontré el patrón.

Edición de larga duración: aparentemente depende de cuándo se invoca el intervalo (en la zona o no)

Hola @vikerman : ¿Alguna noticia sobre los cambios que te permitirán configurar qué tareas espera el transportador?

El mismo problema aquí.

@vikerman, ¿hay alguna actualización o solución?

Nos encontramos con los mismos problemas. Sería fantástico si hubiera algún progreso en este tema.

Lamentablemente, tengo el mismo problema. ¿Hay algún progreso a la vista al respecto?

Estamos agregando seguimiento de tareas a Angular y AngularJS que debería facilitar la depuración de estos problemas al mostrarle de dónde provino la tarea asíncrona en la que se agotó el tiempo de espera de Transportador.

@heathkit ¿Esto ya está hecho en alguna parte? estamos teniendo dificultades para identificar esos lugares en nuestro código base.

También sería bueno tener algo de documentación sobre lo que está esperando el transportador. Este parece estar desactualizado: https://www.protractortest.org/#/system -setup. solo menciona reemplazar $timeout por $interval . Nada sobre setTimeout o setInterval.
algunos documentos están aquí: http://www.protractortest.org/#/timeouts. pero no hay pistas sobre cómo detectarlos ...

Creo que esto le ayudará a ejecutar sus pruebas "angular híbrido + transportador". Me ayudó. Consulte el enlace: https://github.com/ui-router/angular-hybrid/issues/39#issuecomment -309057814

Hemos avanzado un poco en la comprensión de lo que está sucediendo con este problema. Parece que estamos teniendo el mismo. Parece que el ciclo de resumen / observador de angularjs actualizado funciona de manera un poco diferente y puede habilitar algunos ciclos de ciclo de resumen infinitos. Encontramos 1 con un tercero que estábamos usando y estamos investigando el próximo. Pero básicamente el observador estaba provocando un cambio en lo que estaba viendo, provocando que el observador se activara, etc. Para probar si tienes el mismo problema, en Chrome simplemente abre las herramientas de desarrollo, ve a rendimiento, graba unos segundos y vea si tiene muchas llamadas a NgZone. Si lo hace, entonces en algún lugar de su código tiene un ciclo de resumen infinito. Profundizar en las llamadas puede ayudarlo a indicarle la dirección de la causa.

También puede modificar un poco el archivo zone.js local y agregar registros de la consola donde las tareas de tiempo de espera / intervalo se parchean para ver qué las está llamando. Manera fácil de hacer eso:

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

Editar: ¡muy divertido formatear el código en el móvil!

@vikerman ¿Tiene alguna actualización sobre esto? Sería genial si pudiera dar su opinión sobre qué tan pronto se solucionará esto. Gracias.

También puede modificar un poco el archivo zone.js local y agregar registros de la consola donde las tareas de tiempo de espera / intervalo se parchean para ver qué las está llamando. Manera fácil de hacer eso:

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

Editar: ¡muy divertido formatear el código en el móvil!

¿Dónde pondría ese código para rastrear los tiempos de espera / intervalos?

@maurycyg patchTimer -> La función

Este hilo es desalentador de ver. ¿Hay alguna actualización sobre esto?

Una posible solución para no usar $timeout es anularlo con $interval (para bibliotecas de terceros)
El código a continuación resolvió el problema del tiempo de espera para mí (se agregó una verificación del navegador por si acaso). En mi caso, el problema fue causado por componentes de arranque angular, que llamaron infinitamente $ timeout con un retraso de 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;
        }]);
    }
}

A partir de zone.js v0.8.9, puede elegir qué módulos de API web desea parchear para reducir la sobrecarga introducida por el parche de estos módulos.
https://github.com/angular/zone.js/blob/master/MODULE.md

Agregue el archivo zone-flags.ts con el contenido a continuación
(window as any).__Zone_disable_timers = true;

Importarlo en polyfills.ts antes de zone.js

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

Parecía que la solución anterior con la desactivación de los temporizadores de zona se estropea con los Obserables, (por ejemplo, asyncValidators). La solución que finalmente funcionó para mí es el parche de mono 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);
    });
  };
})

Quizás el problema no esté en el transportador en sí, sino en el código de la aplicación.

Con la transición de Angular JS, que usa un ciclo de resumen para rastrear los cambios, a la aplicación híbrida con Angular2 + que usa zone.js para detectar cambios, Protractor comenzó a caer con tiempos de espera.

El transportador inicia la siguiente acción de prueba solo cuando la aplicación es estable. La estabilidad en el caso de una aplicación híbrida significa estabilidad de la parte Angular JS de la aplicación y de la parte Angular2 + de la aplicación. Ambas partes deben ser estables.

La estabilidad de Angular JS se define mediante $$testability.whenStable . La estabilidad de Angular 2+ se determina mediante window.getAngularTestability(...).whenStable . Puede depurar el código del transportador y comprobarlo usted mismo. Para hacer esto, agregue la instrucción debugger; al archivo waitForAngular _ \ node_modules \ protractor \ built \ clientsidescripts.js_;
Notará que la estabilidad de la zona Angular2 + nunca ocurre. Para la estabilidad , se deben completar todas las microtareas y macrotareas .

Ahora entendemos el problema. La parte de Angular 2+ de la aplicación siempre es inestable y, por lo tanto, Protractor espera indefinidamente hasta que se estabiliza y cae con tiempos de espera. La estabilidad de Angular JS en el caso de una aplicación híbrida no debería haber cambiado y no afecta los tiempos de espera del transportador. De esto podemos concluir que es necesario arreglar la inestabilidad de la zona Angular2 + y luego Transportador comenzará a funcionar como se esperaba.

Primer intento de arreglar:
Como había poco tiempo y quería arreglar todo de una vez, escribí un código que sacó la programación de tareas de la 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);
    }

Aunque este enfoque ha demostrado ser viable, como ha demostrado el tiempo, este enfoque tiene sus propias desventajas. El principal es que es imposible determinar qué zona será (raíz o angular) para una parte específica de la aplicación híbrida. Para un funcionamiento normal, Angular2 + requiere que siempre esté en la zona angular, y los decoradores pueden hacer que los componentes de Angular2 + estén en la zona raíz. ngUpgrade implica que la zona angular cubre la mayor parte de la aplicación.

Para evitar esto, hubo un segundo intento de corrección con un análisis más profundo de las razones, abandonando a los decoradores y volviendo al problema original.

Ahora necesitamos entender qué código hace que la zona sea inestable. Para ello, zone.js cuenta con utilidades especiales, como _TaskTrackingZoneSpec_ (_ \ node_modules \ zone.js \ dist \ task-tracking.js_), que permite rastrear qué tareas están en la zona y dónde fueron creadas ( creationLocation ). Además, al comienzo de la parte Angular2 + de la aplicación, guarde la zona angular (window as any).a9Zone = (upgradeModule as any).ngZone._inner; y agregue este código en algún lugar

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

Por lo tanto, busque los lugares en la aplicación donde las microtareas y macrotareas están en la zona durante mucho tiempo (_microTasks.lenght! == 0 && macroTasks.lenght! == 0_).

Entonces necesitas sacar este código de la zona angular. Para hacer esto, envuelva el código en ngZone.runOutsideAngular . Sin embargo, hágalo con mucho cuidado y asegúrese de que la zona raíz no aparezca donde no se necesita.

Después de que la zona angular se estabilice, el transportador ya no caerá con tiempos de espera .

Espero que esto ayude.

Se me ocurrió un truco temporal para resolver este problema al anular la función waitForAngular con la siguiente lógica.

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)
            };
}
¿Fue útil esta página
0 / 5 - 0 calificaciones