Protractor: chore (core): Adicionar melhor mensagem de erro para redirecionamentos do lado do cliente

Criado em 28 out. 2015  ·  49Comentários  ·  Fonte: angular/protractor

Se o teste clicar em um link ou algo que cause um redirecionamento fora de browser.get ou browser.refresh , então, da próxima vez que waitForAngular for chamado, o usuário provavelmente encontrará esta mensagem de erro ( já que não esperamos o carregamento do Angular), o que não é muito útil.

Os usuários devem ser informados sobre o que está acontecendo de errado e sobre possíveis soluções (por exemplo, usando browser.get ou browser.refresh para corrigir o bootstrapping).

debuggability

Comentários muito úteis

@sjelin , você poderia explicar um pouco mais sobre o que pode ser feito para mitigar esse problema?

Meu caso de uso: tenho um teste que insere nome de usuário / senha e acerta um botão de login, que redireciona para outra página ao fazer login com sucesso.

Todos 49 comentários

@sjelin , você poderia explicar um pouco mais sobre o que pode ser feito para mitigar esse problema?

Meu caso de uso: tenho um teste que insere nome de usuário / senha e acerta um botão de login, que redireciona para outra página ao fazer login com sucesso.

Se ambas as páginas forem angulares, você deve apenas adicionar browser.refresh após o carregamento da nova página para corrigir o problema

Estamos trabalhando em um recurso para melhorar isso no futuro

Estou lutando contra esse problema há uma semana. browser.refresh() não funciona se estou fazendo um POST.

É bom ter isso em mente, obrigado. Por enquanto, você provavelmente poderia atenuar o problema fazendo seu teste aguardar algumas centenas de milissegundos depois que o redirecionamento do lado do cliente ocorrer. Isso impedirá o Protractor de lançar a mensagem de erro, embora não permita que o Protractor inicialize corretamente, por isso pode causar alguns outros problemas inesperados (especialmente se você estiver usando plug-ins ou módulos simulados).

Na verdade, estou usando algo assim como uma solução alternativa em que sei que haverá um redirecionamento:

(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 , isso é perfeito! É o que eu esperava que o waitForAngular fizesse - espere o angular estar disponível.

Quando o transferidor é iniciado, ele sempre vai para a baseUrl e depois para a minha página de teste. Isso é considerado o redirecionamento de que você está falando?

Não exatamente @patrickliechty , quero dizer, quando você clica em um link (como <a href='google.com'> ) e direciona o navegador para google.cm

@juliemr sem @hankduan, há alguém trabalhando na correção de longo prazo para isso?

@sjelin , esse problema ainda existe no transferidor 3.1.1. Posso ver aquele

1 para uma melhor correção. Corri para isso hoje sem nenhuma ideia de como contornar isso de forma confiável. Obrigado!

Tendo o mesmo problema !!! obrigado @tassoevan por uma solução confiável

1 para uma correção.

Recebi este erro ao fazer element (). GetText () fora das especificações (sem que um browser.get () tenha ocorrido) - estava tentando criar um atalho para a saída de um div específico fora das especificações. Transformei em uma função e tudo estava bem.

Recebo este erro ao executar em \ node_modules \ protractor \ example \ conf.js, certamente o exemplo conf.js deve sempre funcionar independentemente ??

Para pessoas que usam o Transferidor sem Angular, há uma solução alternativa?

@Overdrivr Você pode definir browser.ignoreSynchronization = true; para dizer ao Transferidor para não esperar pelo Angular.

browser.ignoreSynchronization = true funciona para mim quando eu alterno janelas.

OK, mas você precisa definir browser.ignoreSynchronization = false;

Estou recebendo este erro e ele contém um link para este problema do github.

Aqui está o que eu tenho:

/**
 * 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
O teste começa em um aplicativo angular.
inicia um trabalho mapreduce em um URL que não é um aplicativo angular
volta para o aplicativo angular para registrar contagens de linhas

//Aja
exclui 1 registro
verifica se a exclusão ocorre

// Assert
inicia um trabalho mapreduce em um URL que não é um aplicativo angular
_fails_ pela segunda vez antes de podermos confirmar que o trabalho mapreduce recria os dados (mesma contagem de linhas antes da exclusão).

Aqui estão os detalhes 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:

Ele sempre falha na segunda vez por meio de waitForMapReduceJobToComplete no log de 'espera'.

A solução está "documentada" aqui: https://github.com/angular/protractor/issues/2787

Dentro do browser.wait você deve redefinir ignoreSynchronization. Fiz isso para todos os browsers.waits onde ignoreSynchronization já era true.

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

Meu teste passa agora.

Você tem alguma solução para esperar usando browser.ExpectedConditions?

Como o patch ficará se você usar este código?

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

Obrigado!

@azachar este parece funcionar:

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

Eu não sei o que está acontecendo, por algum motivo neste bloco ignoreSynchronization ainda é falso até que você configure-o à força. Eu segui em # 2787 com isso.

ATUALIZAÇÃO: Existem condições de corrida se você definir / redefinir ignoreSynchronization , é melhor colocar isso no fluxo de controle. Consulte # 4061.

Estou recebendo isso sem ignoreSynchronization com algo assim ....

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 demorar um segundo para carregar e este setTimeout não ajuda. browser.wait parece ajudar um pouco, mas ainda falha.

@tassoevan oi cara, você poderia me dizer como configurar esse browser.wait () como sua descrição e onde configurá-lo

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

no angular (2) - certifique-se de que sua especificação comece com w / 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');
});

Depois de examinar a excelente solução alternativa de @tassoevan e fazer meus testes de redirecionamento funcionarem, decidi compartilhar minha solução TypeScript e Angular2. Eu coloquei esta função em minha classe de página abstrata, mas acho que pode se encaixar melhor no 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);
}

Aqui está um exemplo de como eu usei em um teste:

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

Minha página de login não é uma página js Angular. Assim que eu acessar o aplicativo, a página Angular js será criada. Então, criei o arquivo de especificações conforme fornecido abaixo:

describe ('Login para o aplicativo', function () {
var userID = element (by.name ('username'));
var senha = elemento (by.name ('senha'));
var logInButton = elemento (by.id ('Enviar'));

function loginToApplication (stringSSOID, stringPassword) {
userID.sendKeys (stringSSOID);
password.sendKeys (stringPassword);
logInButton.click ();

}

beforeEach (function () {
browser.ignoreSynchronisation = true;
browser.get (''); // que irá navegar para a página de login do aplicativo
});

it ('ter login para o aplicativo', function () {
loginToApplication ('77777', 'teste')
browser.ignoreSynchronisation = false
expect (browser.getTitle ()). toEqual ('Software');
});
});

Mensagem de erro abaixo:
Falha: Erro ao esperar que o Transferidor sincronize com a página: "window.an
gular é indefinido. Isso pode ser porque esta é uma página não angular ou
porque seu teste envolve navegação do lado do cliente, o que pode interferir com o Prot
bootstrapping do rator. Veja http://git.io/v4gXM para detalhes "

Olá, dê uma olhada em http://www.protractortest.org/#/timeouts # how-to-disable-waiting-for-angular
Além disso, existem algumas maneiras de fazer o login usando uma página não Angular, mas primeiro tente este mostrado nos documentos.
Cumprimentos
Camelo

Sei que já é uma discussão longa, mas isso pode ajudar alguém.

Essa mensagem de erro também pode ocorrer quando a página está sendo recarregada inesperadamente devido a um erro não tratado acontecendo em um componente Angular. O fato de que o console do navegador pode não ser persistente , junto com a rapidez com que os testes são executados, tornou não muito fácil entender o que estava acontecendo.

Então, talvez algo possa ser feito no que diz respeito à mensagem de erro, uma vez que o caso acima mencionado não é 'navegação do lado do cliente' per se :)

Olá @ArnaudPel

Tnx para suas informações extras. Talvez você possa adicionar um PR para explicar isso nos documentos, se quiser.

Atualmente, recebo este erro ao fazer a viagem para nossa página de login (um problema comum):

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"

Fazer o truque de ignoreSync não parece ajudar:

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

O link chega aqui neste tópico, mas o que fazer a seguir não está exatamente claro.

Testado com versões do transferidor: 5.2.1, 5.1.1, 5.1.0, 5.0.0

@benjaminapetersen - Você pode tentar usar waitForAngularEnabled vez de ignoreSynchronization . Estou olhando a página de referência da API do Protractor v5.1.2 e ela não inclui mais ignoreSynchronization , mas olhando no CHANGELOG , não vejo quando, se alguma vez, essa propriedade foi oficialmente suspensa ...

Tentei waitForAngularEnabled mas ainda recebo o mesmo erro de @benjaminapetersen . Aqui está minha configuração atual:

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 obrigado! Embora pareça que waitForAngularEnabled também está obsoleto ....

@benjaminapetersen essa tag de doc obsoleta está na propriedade ignoreSynchronization , não waitForAngularEnabled

waitForAngularEnabled também não funciona para mim. Funcionou com IgnoreSynchronization antes de atualizar para o Protractor 5. Preciso mudar para iframe não angular e agora não consigo. Por favor informar. obrigado

@ seel93 Experimente comentar rootElement: 'app' ou tente substituí-lo por rootElement: '*[ng-app]' .
Acabei de instalar a versão mais recente do transferidor e escrevi algumas especificações para um aplicativo Angular 1.6.1 e parece estar funcionando bem até agora. Encontrei essa mensagem de erro quando estava tentando fazer login em meu aplicativo, mas agora estou usando esta abordagem:

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

Observação: tenho ng-app definido na tag html

rootElement: '*[ng-app]' ? funciona apenas para pesquisar a página para qualquer nó? Não vi isso antes.

Acabei de resolver meu problema. 🥇 Causa raiz: efeito colateral do console do desenvolvedor

Eu estava tendo esse problema e a correção para mim acabou sendo mudar para o transferidor "^ 5.3.2" no meu pacote.json

Tenho uma página de login normal e redireciono para a página angular.

Portanto, não sei exatamente como ou por que funciona, porque sou realmente novo no e2e angular e angular, mas comecei a funcionar assim que usei browser.waitForAngularEnabled(false); duas vezes.

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 que ajude.

Essa última solução quase funciona para mim, a menos que eu receba um erro após a aprovação no teste:
Erro: ECONNREFUSED connect ECONNREFUSED 127.0.0.1:4444 De: Tarefa: WebDriver.getCurrentUrl ()

Recebi este erro ao fazer element (). GetText () fora das especificações (sem que um browser.get () tenha ocorrido) - estava tentando criar um atalho para a saída de um div específico fora das especificações. Transformei em uma função e tudo estava bem.

Funciona para qualquer coisa, você não pode usar mais de um 'elemento (). Qualquer função ()' fora de 'isso'.
Descobri da maneira mais difícil ...

Erro ao mudar de uma página da web para outra no Protractor:

Falha: Erro ao esperar que o Transferidor sincronize com a página: "tanto a testabilidade angularJS quanto a testabilidade angular são indefinidas. Isso pode ser porque esta é uma página não angular ou porque seu teste envolve navegação do lado do cliente, que pode interferir com a do Transferidor inicialização. Consulte http://git.io/v4gXM para obter detalhes "

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

Comecei a receber esse erro de repente, meu CT constrói. Percebi que esse erro ocorre apenas com o Firefox sem cabeça e funcionou bem com o Chrome sem cabeça no Linux. Eu atualizei do Firefox v69 para o mais recente (v72.0.2 no momento), que corrigiu esse problema.

browser.ignoreSynchronization tornou-se obsoleto e não deve ser usado. Como sugerido na postagem original, usar browser.get parece resolver o problema.

Se quiser usá-lo de uma forma que possa desabilitar e habilitar waitForAngularEnabled à vontade, você precisa usar browser.get após definir waitForAngularEnabled(true) . Por exemplo:

// 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 bloqueia até que a página Angular seja carregada.

No momento, estou encontrando este erro:

_Exceção ocorreu: Erro: Erro ao esperar que o Transferidor sincronize com a página: "tanto a testabilidade angularJS quanto a testabilidade angular são indefinidas. Isso pode ser porque esta é uma página não angular ou porque seu teste envolve navegação do lado do cliente, que pode interferir na inicialização do Protractor. Consulte http://git.io/v4gXM para obter detalhes "_

Mas apenas ao tentar depurar por meio do depurador do VS Code ou do inspetor do cromo (siga as instruções aqui para ambos os casos https://www.protractortest.org/#/debugging).

Aqui está minha configuração launch.json em 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"
            ]
        }
    ]
}

Talvez seja algo que configurei errado. Qualquer ajuda é apreciada, tentar escrevê-los sem ser capaz de depurar corretamente é extremamente difícil.

Esta página foi útil?
0 / 5 - 0 avaliações