Protractor: tarea (núcleo): agregue un mejor mensaje de error para las redirecciones del lado del cliente

Creado en 28 oct. 2015  ·  49Comentarios  ·  Fuente: angular/protractor

Si la prueba hace clic en un enlace o algo que causa una redirección fuera de browser.get o browser.refresh , entonces la próxima vez que se llame a waitForAngular el usuario probablemente se encontrará con este mensaje de error ( ya que no esperamos a que Angular se cargue), lo cual no es muy útil.

A los usuarios se les debe informar lo que va mal y las posibles soluciones (por ejemplo, usar browser.get o browser.refresh para arreglar el arranque).

debuggability

Comentario más útil

@sjelin , ¿podría explicar un poco más qué se puede hacer para mitigar este problema?

Mi caso de uso: tengo una prueba que ingresa el nombre de usuario / contraseña y presiona un botón de inicio de sesión, que redirige a otra página al iniciar sesión correctamente.

Todos 49 comentarios

@sjelin , ¿podría explicar un poco más qué se puede hacer para mitigar este problema?

Mi caso de uso: tengo una prueba que ingresa el nombre de usuario / contraseña y presiona un botón de inicio de sesión, que redirige a otra página al iniciar sesión correctamente.

Si ambas páginas son páginas angulares, solo debe agregar browser.refresh después de que se cargue la nueva página para solucionar el problema

Estamos trabajando en una función para mejorar esto en el futuro.

He estado luchando con este problema durante una semana. browser.refresh() no funciona si estoy haciendo un POST.

Es bueno tenerlo en cuenta, gracias. Por ahora, probablemente podría mitigar el problema simplemente haciendo que la prueba espere unos cientos de milisegundos después de que se produzca la redirección del lado del cliente. Esto evitará que Protractor arroje el mensaje de error, aunque no permitirá que Protractor arranque correctamente, por lo que puede causar otros problemas inesperados (especialmente si está utilizando complementos o módulos simulados).

En realidad, estoy usando algo como esto como solución alternativa en la que sé que habrá una redirección:

(function (expectedUrl, timeout) {
    var loaded = false;

    browser.wait(function () {
        browser.executeScript(function () {
            return {
                url: window.location.href,
                haveAngular: !!window.angular
            };
        }).then(function (obj) {
            loaded = (obj.url == expectedUrl && obj.haveAngular);
        });

        return loaded;
    }, timeout);
})(expectedUrl, timeout);

@tassoevan , ¡eso es perfecto! Es lo que esperaría que hiciera waitForAngular: esperar a que angular esté disponible.

Cuando se inicia el transportador, siempre va a la baseUrl y luego a mi página de prueba. ¿Se considera la redirección de la que estás hablando?

No del todo @patrickliechty , quiero decir cuando haces clic en un enlace (como <a href='google.com'> ) y dirige el navegador a google.cm

@juliemr con @hankduan desaparecido, ¿hay alguien trabajando en la solución a largo plazo para esto?

@sjelin , este problema todavía existe en el transportador 3.1.1. Puedo ver que uno

+1 para una mejor solución. Me encontré con esto hoy sin idea de cómo solucionarlo de manera confiable. ¡Gracias!

¡¡¡Tener el mismo problema !!! gracias @tassoevan por una solución confiable

+1 para una solución.

Recibí este error al hacer element (). GetText () fuera de las especificaciones (sin que hubiera ocurrido un browser.get ()) - estaba tratando de crear un acceso directo para la salida de un div particular fuera de las especificaciones. Lo convirtió en una función y todo estuvo bien.

Recibo este error cuando corro contra \ node_modules \ protractor \ example \ conf.js, seguramente el ejemplo conf.js siempre debería funcionar independientemente.

Para las personas que usan Transportador sin Angular, ¿hay alguna solución?

@Overdrivr Puede configurar browser.ignoreSynchronization = true; para decirle a Transportador que no espere a Angular.

browser.ignoreSynchronization = true funciona para mí cuando cambio de ventana.

De acuerdo, pero luego necesitas configurar browser.ignoreSynchronization = false;

Recibo este error y contiene un enlace a este problema de github.

Esto es lo que tengo:

/**
 * Waits for MR job to complete.
 * <strong i="7">@param</strong> {string} url The GET URL to kick off the map reduce job.
 * <strong i="8">@return</strong> {!webdriver.promise.Promise}
 */
function waitForMapReduceJobToComplete(url) {
  browser.ignoreSynchronization = true;
  browser.get(url);
  $('a[href*="/mapreduce"]').click();
  browser.wait(()=> {
    console.log('wait');
    return $('#auto-refresh').isPresent();
  });
  browser.controlFlow().execute(function() {
    console.log('after wait, before click');
  });
  browser.wait(()=> {
    console.log('isDisplayed');
    return $('#auto-refresh').isDisplayed();
  });
  $('#auto-refresh').click();
  browser.controlFlow().execute(function() {
    console.log('after click');
  });

  browser.wait(() => {
    return $('.status-box').isPresent().then((isPresent)=> {
      if(isPresent) {
        return hasClass($('.status-box'), 'status-done')
            .then((containsClass) => {
              if(!containsClass) {
                return browser.refresh().then(() => false);
              }
              return true;
            });
      }
      return browser.sleep(constants.BROWSER_WAIT_MS)
          .then(() => false);
    })
  });

  return browser.controlFlow().execute(function() {
    browser.ignoreSynchronization = false;
  });
}
  fit('mapreduce job is idempotent', function() {
    var subBuildingUrl = '/xyz/#all';
    var mapReduceJob = '/import/xyz/';
    helpers.waitForMapReduceJobToComplete(mapReduceJob);

    browser.get(subBuildingUrl);
    var tableView = new TableView();
    var originalCount = 0;
    tableView.count().then((cnt)=>{
      originalCount = cnt;
    });

    tableView.deleteRowNumber(1);
    browser.sleep(5000);
    browser.controlFlow().execute(function() {
      expect(tableView.count()).toBeLessThan(originalCount);
    });

    helpers.waitForMapReduceJobToComplete(mapReduceJob);
    browser.get(subBuildingUrl);
    browser.controlFlow().execute(function() {
      tableView = new TableView();
      expect(tableView.count()).toEqual(originalCount);
    });
  });

//Organizar
La prueba comienza en una aplicación angular.
inicia un trabajo mapreduce en una URL, no en una aplicación angular
vuelve a la aplicación angular para registrar los recuentos de filas

//Actuar
elimina 1 registro
verifica que ocurre la eliminación

// Afirmar
inicia un trabajo de mapreduce en una URL, no en una aplicación angular
_fails_ la segunda vez antes de que podamos confirmar que el trabajo mapreduce recrea los datos (el mismo recuento de filas antes de la eliminación).

Aquí están los detalles de registro relevantes:

wait
after wait, before click
isDisplayed
isDisplayed
after click
[17:48:09] W/element - more than one element found for locator By(css selector, .status-box) - the first result will be used
[17:48:24] W/element - more than one element found for locator By(css selector, .status-box) - the first result will be used
[17:48:40] W/element - more than one element found for locator By(css selector, .status-box) - the first result will be used
[17:48:55] W/element - more than one element found for locator By(css selector, .status-box) - the first result will be used
wait
F

Failures:
  Message:
    Failed: Error while waiting for Protractor to sync with the page: "window.angular is undefined.  This could be either because this is a non-angular page or because your test involves client-side navigation, which can interfere with Protractor's bootstrapping.  See http://git.io/v4gXM for details"
  Stack:

Siempre falla la segunda vez a través de waitForMapReduceJobToComplete en el registro de 'espera'.

La solución está "documentada" aquí: https://github.com/angular/protractor/issues/2787

Dentro del browser.wait tienes que restablecer ignoreSynchronization. Hice esto para todos los browser.waits donde ignoreSynchronization ya era cierto.

  browser.wait(()=> {
    browser.ignoreSynchronization = true;
    return $('#auto-refresh').isPresent();
  });

Mi prueba pasa ahora.

¿Tiene alguna solución para esperar usando el navegador. Condiciones esperadas?

¿Cómo se verá el parche si usa este código?

browser.wait(EC.visibilityOf(element), timeout, message);?

¡Gracias!

@azachar este parece funcionar:

browser.wait(() => {
  browser.ignoreSynchronization = true;
  return EC.visibilityOf(element);
}, timeout, message);

No sé qué está pasando, por alguna razón en este bloque ignoreSynchronization sigue siendo falso hasta que lo estableces a la fuerza. Seguí en el # 2787 con esto.

ACTUALIZACIÓN: Hay condiciones de carrera si configura / restablece ignoreSynchronization , mejor ponga eso en el flujo de control. Ver # 4061.

Obtengo esto sin ignoreSynchronization con algo como esto ...

afterAll(function(){
    adminPage.header.logout();
    // FIXME: WTF why isn't even this working 100% of the time
    setTimeout(function(){
      expect(loginPage.isVisible()).toBeTruthy();
    }, 1000 )
  })

loginPage parece tardar un segundo en cargarse y este setTimeout no ayuda. browser.wait parece ayudar un poco pero aún falla.

@tassoevan hola amigo, ¿podría decirme cómo configurar ese browser.wait () como su descripción y dónde configurarlo?

(function (expectedUrl, timeout) {
    var loaded = false;

    browser.wait(function () {
        browser.executeScript(function () {
            return {
                url: window.location.href,
                haveAngular: !!window.angular
            };
        }).then(function (obj) {
            loaded = (obj.url == expectedUrl && obj.haveAngular);
        });

        return loaded;
    }, timeout);
})(expectedUrl, timeout);

en angular (2): asegúrese de que su especificación comience con browser.get ('/');

import { browser, element, by } from 'protractor';

it('should display message saying app works', () => {
    browser.get('/');

    let title = element(by.id('title')).getText();
    expect<any>(title).toEqual('cool');
});

Después de ver la excelente solución alternativa de @tassoevan y hacer que mis pruebas de redireccionamiento funcionen, pensé en compartir mi solución TypeScript y Angular2. Puse esta función en mi clase de página abstracta, pero creo que podría encajar mejor en el objeto browser .

/**
  Wait for the page to make it to the next URL before continuing.
  A supplement to browser.waitForAngular() because protractor will continue control
  flow before navigation starts.

  Inputs:
    necessaryUrlFragment: a portion of the url that signifies you have successfully navigated
    timeout: the number of ms to wait before throwing an error. Defaults to the browsers default
      page timeout.
 */
waitForRedirect(necessaryUrlFragment: string, timeout: number = browser.getPageTimeout) {
  // Before we tell the browser to wait, assume it has not navigated
  let hasRedirected = false;

  // Passing a function to browser.wait() tells protractor to call that function repeatedly.
  // This function returns the closure variable hasRedirected, which will be set to true once the
  // necessaryUrlFragment has been found in the url
  browser.wait(() => {
    browser.getCurrentUrl()
      // Check to see if necessaryUrlFragment is in the current url
      .then(url => url.includes(necessaryUrlFragment))
      // Update our navigation status
      .then(hasNavigated => {
        hasRedirected = hasNavigated;
      });

    // Return our navigation status every time protractor asks for it - even if navigation is
    // not complete
    return hasRedirected;
  }, timeout);
}

Aquí hay un ejemplo de cómo lo usé en una prueba:

it('Should be able load the preferences page', () => {
  page.navigateToMarketing();
  page.clickButton('Manage Preferences');
  page.waitForRedirect('preferences/preferences');
  expect(page.getPageTitle()).toBe('MY PREFERENCES');
});

Mi página de inicio de sesión es una página js no angular. Una vez que inicio sesión en la aplicación, se crea la página de Angular js. Así que creé el archivo de especificaciones como se indica a continuación:

describe ('Iniciar sesión en la aplicación', function () {
var ID de usuario = elemento (por.nombre ('nombre de usuario'));
var contraseña = elemento (por.nombre ('contraseña'));
var logInButton = element (by.id ('Enviar'));

función loginToApplication (stringSSOID, stringPassword) {
userID.sendKeys (stringSSOID);
contraseña.sendKeys (stringPassword);
logInButton.click ();

}

beforeEach (function () {
browser.ignoreSynchronisation = true;
browser.get (''); // que navegará a la página de inicio de sesión de la aplicación
});

it ('tener inicio de sesión en la aplicación', function () {
loginToApplication ('77777', 'prueba')
browser.ignoreSynchronisation = false
esperar (browser.getTitle ()). toEqual ('Software');
});
});

Recibiendo el siguiente mensaje de error:
Fallido: Error mientras esperaba que el transportador se sincronizara con la página: "window.an
gular no está definido. Esto podría deberse a que se trata de una página no angular o
porque tu prueba implica navegación del lado del cliente, lo que puede interferir con Prot
bootstrapping del ractor. Consulte http://git.io/v4gXM para obtener más detalles "

Hola, eche un vistazo a http://www.protractortest.org/#/timeouts # how-to-disable-esperando-angular
Básicamente, hay un par de formas de iniciar sesión utilizando una página no angular, pero primero pruebe esta que se muestra en los documentos.
Atentamente
Camello

Me doy cuenta de que ya es una discusión larga, pero esto podría ayudar a alguien.

Este mensaje de error también puede ocurrir cuando la página se está recargando inesperadamente debido a un error no controlado que ocurre en un componente angular. El hecho de que la consola del navegador no se conserve , junto con la rapidez con la que se ejecutan las pruebas, hizo que no fuera fácil para mí entender lo que estaba pasando.

Entonces, tal vez se pueda hacer algo en lo que respecta al mensaje de error, ya que el caso mencionado anteriormente no es 'navegación del lado del cliente' per se :)

Hola @ArnaudPel

Tnx para obtener información adicional. ¿Quizás puedas agregar un PR para explicar esto en los documentos si quieres?

Actualmente recibo este error cuando hago el viaje a nuestra página de inicio de sesión (un problema común):

Failed: Error while waiting for Protractor to sync with the page: "window.angular is undefined.  This could be either because this is a non-angular page or because your test involves client-side navigation, which can interfere with Protractor's bootstrapping.  See http://git.io/v4gXM for details"

Hacer el truco ignoreSync no parece ayudar:

browser.ignoreSynchronization = true;
// do work here with browser.driver 
browser.ignoreSynchronization = false;

El enlace aterriza aquí en este hilo, pero qué hacer a continuación no está exactamente claro.

Probado con las versiones de transportador: 5.2.1, 5.1.1, 5.1.0, 5.0.0

@benjaminapetersen : podría intentar usar waitForAngularEnabled lugar de ignoreSynchronization . Estoy mirando la página de referencia de API para Protractor v5.1.2 y ya no incluye ignoreSynchronization , pero mirando a través del CHANGELOG no veo cuándo, si es que alguna vez, esa propiedad quedó oficialmente en desuso ...

Intenté waitForAngularEnabled pero todavía obtengo el mismo error que @benjaminapetersen . Aquí está mi configuración actual:

exports.config = {
    rootElement: 'app',
    baseUrl: 'http://localhost:1384/#/',
    framework: 'jasmine',
    seleniumAddress: 'http://localhost:4444/wd/hub',
    specs: ['./tests/e2e/*.js'],
    multiCapabilities: [{
      'browserName': 'firefox',
      //'browserName': 'chrome',
    }, 
  ],
  jasmineNodeOpts: {
    showColors: true
  },
}

//example test: 

describe('Overall navigation', function () {
  it('test link', function () {
     browser.get('http://localhost:1384/#/registrerprodukt');
  });
});

@thompsnm gracias! Aunque parece que waitForAngularEnabled también está obsoleto ...

@benjaminapetersen esa etiqueta de documento obsoleta está en la propiedad ignoreSynchronization , no en waitForAngularEnabled

waitForAngularEnabled tampoco funciona para mí. Funcionó con IgnoreSynchronization antes de actualizar a Transportador 5. Necesito cambiar a un iframe no angular, y ahora no puedo. Por favor avise. Gracias

@ seel93 Intente comentar rootElement: 'app' , o intente reemplazarlo con rootElement: '*[ng-app]' .
Acabo de instalar la última versión de transportador y escribí algunas especificaciones para una aplicación Angular 1.6.1 y parece estar funcionando bien hasta ahora. Encontré ese mensaje de error cuando intentaba iniciar sesión en mi aplicación, pero ahora estoy usando este enfoque:

onPrepare: function () {
    browser.driver.manage().timeouts().setScriptTimeout(60000);

    browser.driver.get('http://app.local.dev'+ '/login');
    browser.driver.findElement(by.id('user_login')).sendKeys('d','e','m', 'o');
    browser.driver.findElement(by.id('user_pass')).sendKeys('d','e','m', 'o');
    browser.driver.findElement(by.id('submit')).click();

    // Login takes some time, so wait until it's done.
    return browser.driver.wait(function() {
        return browser.driver.getCurrentUrl().then(function(url) {
            return /students\/agenda/.test(url);
        });
    }, 30000);
}

Nota: tengo ng-app establecido en la etiqueta html

rootElement: '*[ng-app]' ? funciona para buscar en la página cualquier nodo? No he visto esto antes.

Acabo de resolver mi problema. 🥇 Causa raíz: efecto secundario de la consola del desarrollador

Estaba teniendo este problema y la solución para mí terminó siendo cambiar al transportador "^ 5.3.2" en mi package.json

Tengo una página de inicio de sesión regular y luego redirijo a la página angular.

Entonces, no sé exactamente cómo o por qué funciona, porque soy realmente nuevo en e2e angular y angular, pero lo hice funcionar una vez que usé browser.waitForAngularEnabled(false); dos veces.

logIn() {
    const email = 'test';
    const password = 'test';

    browser.waitForAngularEnabled(false);
    browser.get('/login');

    element(by.id('username')).sendKeys(email);
    element(by.id('password')).sendKeys(password);
    element(by.css('[type="submit"]')).click();

    return browser.wait(function() {
        browser.waitForAngularEnabled(false);
        return browser.getCurrentUrl().then(function(url) {
            browser.waitForAngularEnabled(true);
            return /dashboard/.test(url);
        });
    }, 5000);
}

Espero eso ayude.

Esa última solución casi funciona para mí a menos que tenga un error después de que pase la prueba:
Error: ECONNREFUSED conectar ECONNREFUSED 127.0.0.1:4444 De: Tarea: WebDriver.getCurrentUrl ()

Recibí este error al hacer element (). GetText () fuera de las especificaciones (sin que hubiera ocurrido un browser.get ()) - estaba tratando de crear un acceso directo para la salida de un div particular fuera de las especificaciones. Lo convirtió en una función y todo estuvo bien.

Funciona para cualquier cosa, no puede usar más de un 'elemento (). Anyfunction ()' fuera de un 'eso'.
Lo descubrí por las malas ...

Obtuve un error al cambiar de una página web a otra en Transportador:

Falló: Error mientras esperaba que Transportador se sincronizara con la página: "tanto la capacidad de prueba angularJS como la capacidad de prueba angular no están definidas. Esto podría deberse a que se trata de una página no angular o porque su prueba implica la navegación del lado del cliente, lo que puede interferir con el transportador bootstrapping. Consulte http://git.io/v4gXM para obtener más información "

el código es:
var url = browser.params.login.Url;
browser.get (url + '/ #! / app / manage-users');

Comencé a recibir este error de repente, mi CT se construye. Noté que este error ocurre solo con Firefox sin cabeza y funcionó bien con Chrome sin cabeza en Linux. Actualicé de Firefox v69 al último (v72.0.2 en este momento) que solucionó este problema.

browser.ignoreSynchronization ha quedado obsoleto y no debe utilizarse. Como se sugiere en la publicación original, el uso de browser.get parece resolver el problema.

Si desea usarlo de manera que pueda deshabilitar y habilitar waitForAngularEnabled a voluntad, debe usar browser.get después de configurar waitForAngularEnabled(true) . Por ejemplo:

// do things on your Angular application

waitForAngularEnabled(false)

// do things on non-angular page

waitForAngularEnabled(true)
browser.get('/home') // this is a page from your Angular application

browser.get bloques hasta que se cargue la página Angular.

Actualmente recibo este error:

_Exception ha ocurrido: Error: Error al esperar que el transportador se sincronice con la página: "tanto la capacidad de prueba angularJS como la capacidad de prueba angular no están definidas. Esto podría deberse a que se trata de una página no angular o a que la prueba implica la navegación del lado del cliente, que puede interferir con el arranque de Protractor. Consulte http://git.io/v4gXM para obtener más detalles "_

Pero solo cuando intente depurar a través del depurador de VS Code o del inspector de Chrome (siga las instrucciones aquí para ambos casos https://www.protractortest.org/#/debugging).

Aquí está mi configuración launch.json en VSCode.

{
    "version": "0.2.0",
    "configurations": [
        {
            "type": "node",
            "request": "launch",
            "name": "E2E Test",
            "skipFiles": [
                "<node_internals>/**"
            ],
            "program": "${workspaceFolder}\\node_modules\\protractor\\bin\\protractor",
            "args": ["${workspaceFolder}\\e2e\\protractor.conf.js"],
            "outFiles": [
                "${workspaceFolder}/e2e/**/*.js",
                "${workspaceFolder}/src/**/*.js"
            ]
        }
    ]
}

Tal vez sea algo que configuré mal. Se agradece cualquier ayuda, intentar escribir estos sin poder depurar correctamente es increíblemente difícil.

¿Fue útil esta página
0 / 5 - 0 calificaciones