Protractor: Transferidor e bootstrap manual angular

Criado em 30 ago. 2013  ·  49Comentários  ·  Fonte: angular/protractor

Estou tendo um problema ao executar o transferidor 0.8 e tendo problemas ao executar o transferidor em nosso aplicativo que é inicializado manualmente.

Aqui estão alguns trechos que revelam as partes relevantes do aplicativo e da configuração:
index.html:

 <script type="text/javascript" data-main="js/main" src="js/libraries/require.js"></script>

js/main.js:

require({
    shim: {
        .... snip ..
            }
           }, 
  ['require', ..... ], function(require) {
    return require(['bootstrap']);

bootstrap.js:

var appElm = $('html');
appElm.addClass('ng-app'); // this
angular.bootstrap(document, ['app']);

Isso funciona na prática e o Angular é iniciado de maneira correta. Quando vejo a fonte html, não consigo ver o atributo ng-app aparecendo no elemento html. Pode ser Transferidor também não consegue fazê-lo?

Eu uso a configuração de amostra do transferidor e alterei os valores relevantes para se adequar ao meu aplicativo:

specs: [
    '../src/test/javascript/protractorSpec/*_spec.js',
  ],
  baseUrl: 'http://localhost:8080/Viewer/',
  rootElement: 'html',

Teste de amostra:

describe('List SMC Charts', function() {
        var ptor= protractor.getInstance();

        beforeEach(function() {
          ptor.get('#/smcChart');
        });

        it('list, no filter', function() {
          var selectOption = ptor.findElement(ptor.By.input("selectOption"));
        });
});

Quando executo este teste aparecem dois erros:

 1) List SMC Charts   list, no filter
   Message:
     Error: Angular could not be found on the page http://localhost:8080/Viewer/#/smcChart
   Stacktrace:
     Error: Angular could not be found on the page http://localhost:8080/Viewer/#/smcChart
    at /usr/local/lib/node_modules/protractor/lib/protractor.js:392:15

e

2) List SMC Charts list, no filter
   Message:
     TypeError: Cannot call method 'input' of undefined
   Stacktrace:
     TypeError: Cannot call method 'input' of undefined
    at null.<anonymous> (/Users/........//Viewer/src/test/javascript/protractorSpec/some_spec.js:9:55)

O primeiro erro parece ser o mais importante, mas o último erro também é bastante interessante. Parece-me que a linha: ptor.By.input não está funcionando.

Alguma ideia?

untriaged discussion

Comentários muito úteis

@sjelin Qual é a maneira documentada de oferecer suporte à inicialização manual? Não consigo encontrar as informações relevantes para o transferidor 5.0.

Todos 49 comentários

O código Angular está sendo incluído na página? Parece que você está exigindo shim, bootstrap e outros, mas angular.min.js não é mostrado.

'app': {
            deps: ['libraries/angular', 'libraries/angular-resource', 'libraries/angular-strap']
        },

O trecho acima reside dentro do elemento shim{}, deixei-o de fora dos trechos porque não me pareceu muito relevante. Mas é assim que o angular.js está sendo carregado.

Oi,

Sim, o Protractor atualmente espera um ng-app, então sites com bootstrap manualmente terão problemas. Esperando obter uma solução alternativa para isso documentado em breve.

Ok thx para a atualização. Quando você descobrir uma solução alternativa, você pode publicá-la nesta edição?

Desculpe pela resposta lenta - você deve ser capaz de corrigir fazendo

describe('List SMC Charts', function() {
        var ptor= protractor.getInstance();

        beforeEach(function() {
          ptor.driver.get('#/smcChart'); // Just use the plain old webdriver here, it won't complain if Angular isn't loaded yet.
        });

        it('list, no filter', function() {
          var selectOption = ptor.findElement(ptor.By.input("selectOption"));
        });
});

Você configurou a variável ptor em algum lugar? por exemplo var ptor = protractor.getInstance() ?

alguma atualização disso? Sua correção sugerida não está funcionando para mim ([email protected], [email protected])

Eu tenho uma combinação estranha de uma tela de login não angular seguida por um requirejs, aplicativo inicializado manualmente:

describe('User levels', function() {
    var ptor, driver;

    var findByName = function (name) {
        return driver.findElement(protractor.By.name(name));
    };

    describe('Editors', function() {
        ptor = protractor.getInstance();
        driver = ptor.driver;
        it('Can log in', function() {
            driver.get('http://localhost:8000/');
            findByName('login').sendKeys('test_editor');
            findByName('pass').sendKeys('rubbish');
            driver.findElement(protractor.By.css('button[type="submit"]')).click();
        }, 20000);

        it('Shows the homepage', function() {
            var el = ptor.findElement(protractor.By.css('h1[field="pageTitle"]'));
            expect(el.getText()).toEqual('Welcome to Home');
        }, 30000);

    });
});

E meu main.js :

require([
    'angular',
    'app'
], function (angular, app) {
    'use strict';
    var body = document.body, name = app.name;
    body.setAttribute('ng-app', name);
    body.setAttribute('id', 'ng-app');
    angular.bootstrap(body, [name]);
});

A parte de login funciona e posso ver um flash da página protegida carregando antes que o transferidor erros com UnknownError: javascript error: angular is not defined . Eu tentei recarregar a página com driver.get('...') e até ptor.get('...') mas nada funciona.

Quaisquer dicas/conselhos/correções recebidas com gratidão.

@magnetised você precisará de alguma maneira de dizer ao seu teste para esperar até que o login seja feito antes de tentar outros testes. Eu acho que o que está acontecendo é que você está tentando usar o ptor.findElement antes que o Angular seja carregado na página.

Sua especificação 'Pode fazer login' deve terminar com uma instrução de espera que verifica alguma condição para seu aplicativo. Confira o exemplo aqui - https://github.com/angular/protractor/blob/master/spec/login/viaConfigConf.js

@juliemr Brilhante, isso funciona perfeitamente.

Eu tinha entendido que o transferidor faria o "esperar até que o angular seja carregado", mas acho que você não pode cobrir todos os casos.

Obrigado novamente.

Eu tenho exatamente a mesma configuração. @juliemr você se importa em compartilhar sua solução. Qual é a sua condição de espera? Obrigado!

@jorgenfb Gostaria de saber se você queria que esse comentário fosse direcionado a mim?

Minha solução envolveu apenas esperar que o roteador angular inicializasse e alterasse a url do '/' para o qual o sistema de login o redireciona para a rota padrão - no nosso caso '/#view'.

Eu fiz isso em uma função que eu poderia reutilizá-lo em todos os testes:

var waitUntilLoaded = function() {
    ptor.wait(function() {
      return ptor.driver.getCurrentUrl().then(function(url) {
        return /view/.test(url);
      });
    });
};

Não sei se isso funcionará para você, depende da sua configuração de roteamento.

@juliemr Obrigado, funciona como um encanto :)

Estou tendo um problema semelhante (encontrado no SO em: http://stackoverflow.com/questions/19391813/protractor-fails-to-find-angular ) com o Protractor não encontrando o Angular, embora o Angular esteja carregado e em execução muito claramente. A solução fácil (testar sem tentar encontrar Angular) é o caminho que devemos seguir? Parece derrotar o propósito do Protractor ser construído especificamente para testar aplicativos Angular.
edit: também devo observar que a solução proposta neste problema não funciona para mim.

+1 por ter uma melhor documentação de bootstrap manual angular

isso ainda é relevante?

Sinceramente, não sei, já que desisti de tentar e realmente não tenho tempo para voltar e ver se algo mudou. Considerando as mudanças de versão desde então, se você deseja fechar isso, eu não discutiria. Pode sempre ser reaberto mais tarde.

Comecei a usar o transferidor hoje e nosso aplicativo usa o require.js... se houver alguma atualização com relação ao teste de um aplicativo que é inicializado manualmente, estou interessado. :-)

Mesmo problema aqui. Obteve o aplicativo com a página de login fora do aplicativo angular. A solução alternativa nos comentários anteriores parece estar desatualizada. Gostaria muito de um documento atualizado sobre como poder testar esse tipo de cenário.

@hankduan Sim, acho que sim.

Estou lutando com esse problema agora. O plano de fundo é semelhante: eu tenho um aplicativo maior cujas dependências são gerenciadas via RequireJS.


Já atualizei meu aplicativo usando window.name='NG_DEFER_BOOTSTRAP!'; & angular.resumeBootstrap() , mas ainda não funciona.

Disse isso, eu não sei o que fazer a seguir. Para investigações adicionais, configurei um caso de teste em https://github.com/knalli/protractor-with-deferred-bootstrap

  • git clone https://github.com/knalli/protractor-with-deferred-bootstrap.git
  • cd protractor-with-deferred-bootstrap
  • npm install
  • npm run-script webdriver (Servidor Selenium)
  • npm run-script server (Servidor de aplicativo de demonstração servindo um aplicativo mini AJS)
  • npm run-script e2e-test (executa o transferidor...)

O resultado é

Using the selenium server at http://localhost:4444/wd/hub
Login: Click
Login: Waiting...
A Jasmine spec timed out. Resetting the WebDriver Control Flow.
The last active task was: 
unknown
F

Failures:

Seguido por

UnknownError: unknown error: [ng:btstrpd] App Already Bootstrapped with this Element '

O último erro é um pouco estranho e talvez até enganoso: O teste foi realizado (com sucesso até onde as expectativas estavam ok e o que podemos ver no navegador).

Verificação cruzada: se eu mudar o aplicativo de demonstração para bootstrapping não diferido com inclusões manuais (esta variante está disponível no branch no-deferred ), ele funcionará com sucesso.


Eu já tentei algumas das chamadas "soluções alternativas":

  1. http://www.sebdangerfield.me.uk/2014/01/angularjs-protractor-app-already-bootstrapped-error/
    Irrelevante ou enganoso --> nenhuma diferença.
  2. https://github.com/angular/protractor/issues/325#issuecomment -36738048 usando protractor.getInstance() e ignoreSynchronization --> sem diferença
  3. https://github.com/angular/protractor/issues/325#issuecomment -30701683 corrigindo clientSideScripts.js/waitForAngular --> sem diferença

Eu escrevi meus primeiros testes de transferidor ontem e tive o mesmo problema que o knalli.

Obrigado por escrever o exemplo @knalli. vou dar uma olhada em alguns dias

Então meu transferidor testa todo o trabalho (exceto por uma falha, mas a culpa é minha :-P ).
Deixe-me saber se isso ajuda/não.
Aqui está minha configuração:

versão do transferidor:
Versão 1.0.0-rc4
selênio autônomo:
está atualizado
driver de cromo:
está atualizado

Arquivos definitivamente relevantes:

Configuração

/*jshint expr: true*/
/*global browser: true*/
var TEST_DIR =  '../tests/';
exports.config = {
  seleniumAddress: 'http://localhost:4444/wd/hub'
,  specs : [     '../tests/e2e/_protractor.js'       ]
,  capabilities: {'browserName': 'chrome'    }
,  chromeDriver: './chromedriver'  // <---- I remember not being able to get chromedriver working 
                                                    // for some reason, so I just downloaded the executable and put it
                                                    // in my config directory
,  onPrepare: function() {
    "use strict";
    browser.driver.get('http://localhost:5000/register');
    browser.driver.wait(function() {
      return browser.driver.getCurrentUrl().then(function(url) {
        return /\#\/registration/.test(url);
      });
    });
  }
    //  chromeOnly: true,
    //  baseUrl: 'http://localhost:5000/register'
,  jasmineNodeOpts: {
    showColors: true
  }
};

_protractor.js

/*jshint expr: true, undef: true */
/* global element: true, by: true, browser: true, model:true */
(function(){
  "use strict";

var RegistrationPage = function(){
  browser.driver.wait(function() {
    return browser.driver.getCurrentUrl().then(function(url) {
      return /\#\/registration/.test(url);
    });
  });
};
  var account = new RegistrationPage();

  account.elem = element.all(by.name('account_exists'));
  account.accountNumber = element(by.name('account_number'));
  account.submitAccountButton = element(by.css('#account_input button'));

  account.selectHasAccount = function(){
    account.elem.get(0).click();
    return browser.getCurrentUrl();
  };

  account.enterAccountNumber = function(keys){

    account.selectHasAccount().then(function(){
      account.accountNumber.sendKeys(keys);
      return account.submitAccountButton.click()
        .then(function(){
          return browser.getCurrentUrl();
      });
      //this wont log because already returned ^
//      console.log('need to return again');
    });
  };
  account.noAccount = function(){
    account.elem.get(1).click();
  };

describe('registration page', function() {
  it("will survive", function(){
    var registrationPage = new RegistrationPage()
    , title = browser.getTitle();
    expect(title).toBe('Web Shop | Homepage');
  });
  it('will change route on account_exists radiobox click', function(){
    var elem = element.all(by.name('account_exists'))
    , _url;
    elem.get(0).click()
    .then(function(){
      _url = browser.getCurrentUrl().then(function(_url){
        expect(_url).toBe('http://localhost:5000/register#/registration/has');
      });
    })
    .then(function(){
        elem.get(1).click().then(function(){
          _url = browser.getCurrentUrl();
          expect(_url).toBe('http://localhost:5000/register#/registration/no');
        });
      });
  });
  describe('navigate the hasAccount path:', function(){
    it('will click hasAccount which will cause a route change.', function () {
      expect(account.selectHasAccount()).toBe('http://localhost:5000/register#/registration/has');
    });
    it("will submit a valid account number", function(){
      expect(account.enterAccountNumber('1231231231')).toBe('http://localhost:5000/register#/user');
    });
  });
  it('will click noAccount which will cause a route change.', function () {
    var  _url;
    account.noAccount();
    _url = browser.getCurrentUrl();
    expect(_url).toBe('http://localhost:5000/register#/registration/no');
  });
});
})();

Requer/main.js:

var pathToJQuery
if('querySelector' in document
  && 'localStorage' in window
  && 'addEventListener' in window) {
  //ie9+ and all browsers
  pathToJQuery = 'lib/jquery/dist/jquery'
} else {
  //ie8 and ie7
  pathToJQuery = 'lib/jquery-1.7.2.min'
}
requirejs.config({

      paths : {

//        Paths

          "reg": 'js/registration'
        , "com": 'js/common'
        , "init": 'js/init'
        , "regD": 'js/registration/directives'
        , "regC": 'js/registration/controllers'
        , "regS": 'js/registration/services'
        , "regM": 'js/registration/modules'
//
//        Frameworks
        ,  "angular": "lib/angular/angular"
        ,  "angular.animate" : "lib/angular-animate/angular-animate"
        ,  "jquery": pathToJQuery   //< = == v    long story; don't judge me.
        ,  "jqueryMigrate" : "lib/jquery-migrate/jquery-migrate"
        ,  "mocks": "lib/angular-mocks/angular-mocks"
        ,  "ui_router": "lib/angular-ui-router/release/angular-ui-router"
        ,  "Q": "lib/q/q"

//        Init
        ,  "apps_init" : 'js/init/apps_init'
        ,  "apps_bootstrap" : 'js/init/apps_bootstrap'
//        ,  "jquery_bootstrap" : 'js/init/jquery_bootstrap'
        ,  "registration": "js/init/registration"

//       Common

//         Registration
        ,  "registration": "js/registration/registration"
//         Controllers
        ,  "account.controller" : 'js/registration/controllers/account.controller'
        ,  "user.controller" : 'js/registration/controllers/user.controller'
//         Directives
        ,  "account.directive" : 'js/registration/directives/account.directive'
//    Services
        , "user.service" : "js/registration/services/user.service"
    }
,     shim: {
        'angular': {
            exports : 'angular'
        }
    ,   'angular.animate' : {deps: ['angular']}
    ,   'jqueryMigrate' : {deps: ['jquery']}
    ,   'ui_router' : ['angular']
    ,   'mocks': { deps: ['angular'], 'exports': 'angular.mock' }
}
});

if( pathToJQuery ===  'lib/jquery/dist/jquery' ){
  requirejs([
        'angular' , 'jquery' , 'jqueryMigrate', 'init/jquery_bootstrap', 'init/apps_bootstrap' ]
      , function(angular, jquery, jquery_bootstrap, apps_bootstrap) {
      }
  );
} else {
  requirejs([
      'angular' , 'jquery' , 'jqueryMigrate', 'init/jquery_bootstrap', 'init/apps_bootstrap' ]
    , function(angular, jquery, jqueryMigrate, jquery_bootstrap, apps_bootstrap) {
    }
  );
}
console.log("working");

Arquivos potencialmente úteis:

visualização padrão (guidões)

<!DOCTYPE html>
<html xmlns:ng="http://angularjs.org"
            xmlns:fab="http://angularjs.org"
            xmlns:ui ="http://angularjs.org"
            lang = "en" class = "no-js" >
<head >
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
<meta http-equiv="X-UA-Compatible" content="IE=Edge"/>
<meta charset="utf-8">
<link rel="shortcut icon" type="image/x-icon" media="all" href="/images/favicon.ico" />
<script type="text/javascript" src="lib/modernizr.js"></script>

<!-- Default ie theme files:-->
<!--[if lte IE 9]><script type="text/javascript">console = {}; console.log = function(){};</script><![endif]-->
<!--[if IE 8]><link type="text/css" rel="stylesheet" href="build/css/ie8/theme.css" /><![endif]-->

<!--[if IE 7]><link type="text/css" rel="stylesheet" href="build/css/ie7/theme.css" /><![endif]-->

<!-- Normal styles -->
<link type="text/css" href="css/style.css" rel="stylesheet">


<!--[if IE 8]> <link type="text/css" rel="stylesheet" href="build/css/ie8/ie_8.css"  /> <![endif]-->
<!--[if IE 7]> <link type="text/css" rel="stylesheet" href="build/css/ie7/lte_ie7.css"/> <link type="text/css" rel="stylesheet" href="build/css/ie7/ie_7.css" /><![endif]-->


<script data-main="main" src="lib/requirejs/require.js"></script>
</head >

<body class = "page-homepage language-en" >
<div id = "wrapper" >
    <div id = "page" data-currency-iso-code = "USD" >
        {{> _header}}
        <a href = "#skip-to-content" class = "skiptocontent" >Skip to content</a >
        <a href = "#skiptonavigation" class = "skiptonavigation" >Skip to navigation menu</a >
    <!--[if lte IE 10]> <p class="browsehappy" style="margin-top: 10px; ">You are using an <strong>outdated</strong> browser. Please <a href="http://browsehappy.com/">upgrade your browser</a> to improve your experience.</p><![endif]-->
    <main ng-cloak id = "content" role = "main" class = "page_main <!--[if IE7]>ng-cloak<![endif]-->" >

        </main>
    </div >
</div >
</body >

</html >

controller.js (configuração do servidor)

(function(){
    "use strict";

    //  node requires
    //==============================
    // express-hbs
    // express
    //
    const express = require('express')
        , portN = 5000
        , hbs = require('express-hbs')
        , app = express()
        , path = require('path')
        , Server = require('./server')
        , bodyParser = require('body-parser')
        , serveStatic = require('serve-static')
        , session = require('express-session')

    , router = express.Router()
    , logger  = require('morgan')
        , methodOverride = require('method-override')
        , errorhandler = require('errorhandler')


    //  view paths
    //==============================
    //
    //
    //
        , viewsD = __dirname + '/views/'
        , partialsD = viewsD + 'partials/'
        , layoutsD = viewsD + 'layouts/'
        , testsD =  __dirname + '/tests/'
    , publicD = __dirname + '/public/'
    , styleguideD = publicD+ '/styleguide/'
        , defaultF = layoutsD + 'default.hbs';
    //  Express setup.
    //==============================
    //
    //
    //
    app.use(express.static(publicD))
            .use(bodyParser())
            .use(logger('dev'))
            .use(methodOverride())
            .use(serveStatic(path.join(__dirname, 'public')))
            .use(errorhandler());

    app.set('view engine', 'hbs')
            .set('port', process.env.PORT || portN)
            .set('cache', false)
            .set('views', viewsD);

    app.engine('hbs', hbs.express3({
        partialsDir: partialsD,
        defaultLayout: defaultF,
        layoutsDir: layoutsD
    }));

    var indx = {};

    app.get( '/' ,function (req, res) {
        res.render(partialsD + '_home.hbs', indx);
    });

    app.get( '/register' ,function (req, res) {
        res.render(partialsD + '_register.hbs', indx);
    });

    new Server(app);

})();

server.js

module.exports = function(app){
  "use strict";
    var http = require('http');
    http.createServer(app).listen(app.get('port'), function(){
        console.log('Express server listening on port ' + app.get('port'));
    });
};


module.exports = function (app) {
    "use strict";
    var http = require ('http');
    http.createServer (app).listen (app.get ('port'),function () {
        console.log ('Express server listening on port ' + app.get ('port'));
    }).on ('error', function (e) {
        console.log ("got error: " + e.message);
    });
};

package.json (trecho relevante)

"dependencies": {
    "body-parser": "^1.2.1",
    "bower": "^1.3.2",
    "errorhandler": "^1.0.1",
    "express": "^4.3.1",
    "express-hbs": "^0.7.9",
    "express-session": "^1.2.0",
    "node-dev": "^2.3.0"
  },

  "scripts": {
    "start": "node --harmony $* ./node_modules/.bin/node-dev controller.js",
  },
"devDependencies": {
  "browserstack-webdriver": "^2.41.1",
  "grunt-protractor-runner": "^1.0.0",
  "method-override": "^1.0.2",
    "morgan": "^1.1.1",
    "protractor": "^0.23.1",
    "requirejs": "^2.1.11",
    "serve-static": "^1.1.0",
},
  "author": "Andrew Luhring"

estrutura do arquivo:

config/
   protractor.config.js
    chromedriver

server.js
controller.js

public/
   main.js
   js/
   lib/

tests/
   e2e/
      _protractor.js    <=== test file.

views/
  layouts/
      default.hbs

  partials/
      _head.hbs
      _register.hbs   <=== file being tested.

Tenho certeza que é toda a minha configuração. Achei que eu iria para verbose. Espero que ajude.

O que eu precisava fazer era:

  1. adicione uma parte específica do teste à URL, para que meu código de bootstrap saiba se está sendo executado 'normalmente' ou como um teste de transferidor, então apenas para os testes
  2. chame angular.bootstrap de document.ready
  3. defina o nome da janela para começar com 'NG_DEFER_BOOTSTRAP!' antes de chamar angular.bootstrap
  4. defina o atributo 'ng-app' no meu elemento de nível superior
  5. defina window.angular para apontar para angular

Veja https://github.com/mikaraento/protractor-reuse/blob/master/index.js

Posso saber por que os scripts transferidores que estão executando perfeitamente bem nos navegadores Chrome e Firefox não podem executar o sempre no Internet Explorer?

Embora os mesmos scripts sejam executados perfeitamente em outros navegadores, não podem ser executados sempre da mesma forma no Internet Explorer. Estou recebendo erros frequentes como exceção de tempo limite como
mensagem: 'timeout: expirou após 30.000 ms, aguardando a conclusão da especificação',
trace: { pilha: indefinido } } ]
e também os elementos às vezes não são detectados no Internet Explorer.

Embora eu tenha usado o intervalo de tempo padrão como defaultTimeou tInterval:1000000 , em meus scripts estou recebendo os erros mencionados acima durante a execução no Internet Explorer ...

Alguém pode me ajudar sobre isso?

Cumprimentos,
Santhosh Chunchu

Não vejo que seja um problema apenas com a inicialização manual? Ou você consegue executar aplicativos de inicialização manual já no Chrome e no Firefox?

@hankduan Você já deu uma olhada no caso de teste? Talvez algo que possamos ajudar?

Eu tenho um CL que altera a maneira como o bootstrapping funciona para o transferidor (https://github.com/angular/protractor/pull/1155). Seu objetivo principal é corrigir vários outros problemas, mas acho que deve corrigir (ou pelo menos ajudar) com o bootstrap manual, dependendo de como você está inicializando. No entanto, eu nunca configurei o bootstrap manual para testar, então isso é apenas teórico.

Este PR requer uma mudança no transferidor e uma mudança no Angular, então pode não ser liberado em um curto período de tempo.

Esse problema parece ser o principal resultado da pesquisa ao procurar maneiras de inicializar manualmente o Angular para uso com o Protractor, então achei que esse é o melhor lugar para sugerir outra solução. Isso funciona para o caso em que toda a sua interface é um aplicativo Angular (em vez de fazer algo como bootstrap em um div após o login, como os exemplos acima).

Embora seja possível atrasar os testes do Protractor até que o Angular seja carregado, os documentos oficiais do Angular afirmam que você não pode fazer o bootstrap manual com o Protractor. Eu tendo a evitar soluções alternativas que a documentação oficial parece proibir.

Minha solução

Estou usando o Grunt para Concat/Uglify meu código em um único arquivo JS, que é o único script referenciado em index.html, com uma tag de script. Vamos chamá-lo de script.js. Em Desenvolvimento, executo uma tarefa Grunt diferente, que concatena apenas os arquivos necessários para carregar o restante dos arquivos JS, separadamente, de forma assíncrona e com carimbos de data/hora para evitar o armazenamento em cache. Este conjunto de scripts concatenados substitui script.js enquanto estou desenvolvendo. Em qualquer cenário, estou apenas carregando script.js de index.html e com uma tag de script.

Desenvolvimento

Então, em Desenvolvimento, eu tenho que fazer um bootstrap manual. Isso é fácil porque posso incluir um único arquivo que não concateno no Production JS e me certifico de carregar esse script depois que todos os outros JS forem carregados.

bootstrap_dev.js

        .element(document.body)
        .ready(function() {
            angular.bootstrap(document.body, ['App']);
        });

Produção / Transferidor

Na produção, não consigo inicializar manualmente porque o Protractor não gosta disso. E estou executando o Protractor no meu código de produção. Eu também removi o atributo "ng-app" do meu HTML, para que o aplicativo não inicialize prematuramente em Desenvolvimento, portanto, o bootstrap automático normal não está ocorrendo. Eu compenso isso na produção colocando um script antes de todos os outros JS em script.js.

bootstrap_prod.js

    window.document.body.setAttribute("ng-app", "myApp");

Basta substituir "myApp" pelo nome do seu módulo principal.

Conclusão

Em Desenvolvimento, se você fizer o acima, poderá carregar seu script de forma assíncrona e fazer um bootstrap manual. Mas se você estiver de acordo em executar apenas seus testes do Protractor em relação ao código de produção (que pode ser carregado de um único arquivo por meio de uma tag de script, basta adicionar a diretiva ng-app antes que o Angular seja carregado para que ele faça sua inicialização automática. Agora você tem carregamento de script assíncrono de seus vários scripts em desenvolvimento e execução de testes do Protractor para seu código de produção.

O que acabou dando certo para mim foi adicionar, no meu transferidor.conf.js, a seguinte função:

onPrepare: function() 
    {
        // implicit and page load timeouts
        browser.manage().timeouts().pageLoadTimeout(40000);
        browser.manage().timeouts().implicitlyWait(25000);

        // for non-angular page
        browser.ignoreSynchronization = true;

        // sign in before all tests
     }

Se houver uma maneira melhor de fazer isso, por favor me avise =]

Teve problema semelhante. Encontrei esta informação útil:

Processo de inicialização

Não há diferença, como o aplicativo é impulsionado na produção, mas nos testes o transferidor está fazendo isso:

  1. Altera window.name para que o angular seja pausado antes do início: NG_DEFER_BOOTSTRAP! (este é um recurso angular!)
  2. Faz simulações, então chama angular.resumeBoostrap() e o aplicativo realmente inicia.

Portanto, certifique-se de que seu código de produção não esteja conflitante com esse processo, pois angular pode ser boostrap apenas uma vez, a segunda chamada para angular.boostrap() ou angular.resumeBoostrap() quebrará o transferidor ou o aplicativo. Portanto, ng-app no corpo não é necessário, e você pode chamar angular.boostrap() sozinho e ele será tratado pelo transferidor usando NG_DEFER_BOOTSTRAP! devidamente.

window.name

Certifique-se de que window.name não seja alterado por nenhum código de aplicativo durante o carregamento e a inicialização. No meu caso, o jquery estava fazendo isso, não consigo descobrir o porquê. Totalmente removido jquery do meu projeto e os testes funcionaram. Essa é a raiz do problema!

Por enquanto, estou pesquisando, como trazer o jquery de volta sem quebrar os testes, pois é necessário para o meu projeto!

Finalmente,

nosso problema estava em duas versões do jQuery em uma página (a segunda versão foi carregada pela biblioteca externa). Removi e deixei apenas nosso jQuery na página e tudo funciona.

Algum código, pode ser útil:

index.html

<body>
...
<script data-main="require.conf" type="text/javascript" src="bower_components/requirejs/require.js"></script>
</body>

configuração requirejs

...
 deps: ["proto/main"],
...

proto.js

// We just load deps and register the module:
 define(["deps"],function(){
   angular.module('proto'...
 }

proto/main.js

define(["require", "exports", "proto", "angular"], 
    function (require, exports) {

    angular.bootstrap(document, ['proto']);
});

e, na verdade, a especificação de teste

var protractor = require('protractor');
var ptor;
function prepareBrowser() {

    ptor = protractor.getInstance();

    return ptor.get(ptor["baseUrl"] + '/login').then(function () {
        console.log("got");       
        return ptor.waitForAngular().then(function () {
            console.log("ngwaited");
        });
    });
}
describe('container', function () {
    beforeEach(function () {
        return prepareBrowser();
    });
    describe('form tests', function () {
        it('form is shown', function () {
            expect($('[ng-controller=LoginContr]').isDisplayed()).toBeTruthy();
        });

    });
});

Saída

Using the selenium server at http://localhost:4444/wd/hub
got
ngwaited
container
  form tests
    form is shown - pass


Finished in 3.112 seconds
1 test, 1 assertion, 0 failures

Hora!

Quando mudamos de ng-app para angular.bootstrap() , adicionar rootElement: 'div' ao projotractor.conf foi a única mudança que precisávamos para que o transferidor funcionasse novamente. Estamos usando angular 1.3.0 e transferidor 1.3.1.

@jongunnip Sim, funcionou para mim também, obrigado!

@mikaraento Funcionou para mim, obrigado ~

Oi pessoal, coisa estranha relevante para este tópico.

Eu tenho um aplicativo angular regular - sem requirejs ou qualquer coisa, estou usando o transferidor 2.0.0.

Quando executo os testes do sistema no ambiente de teste, ele funciona.
Quando eu executo localmente, ele me dá:
org.openqa.selenium.WebDriverException: unknown error: [ng:btstrpd] App Already Bootstrapped with this Element ... mensagem

Quando configuro um servidor Nginx para servir o recurso estático em vez de grunt:server tudo funciona.

Realmente importa se eu executá-lo com livereload em desenvolvimento?

Parece que os servidores estáticos se comportam de maneira diferente e o grunt serve alguns 404s ou redirecionamentos, então o webdriver enlouquece.

Olá a todos,

depois de muito tempo lendo e lendo, encontrei uma solução que funciona bem para o meu projeto ( fixar o fluxo de execução do transferidor )

  1. ao abrir o aplicativo para o transferidor, adicione ?protractor-test ao URL ( código do projeto )
  2. altere o código de bootstrap angular para ser como o abaixo ( código do projeto )
if(location.href.indexOf("protractor-test") < 0){
  // start angular app
  angular.bootstrap(document, [module.name]);
} else {
  // start angular app to protractor tests
  window.name = 'NG_DEFER_BOOTSTRAP!' + window.name;
  angular.bootstrap(document, [module.name]);
}

e finalmente tudo de volta ao trabalho novamente \o/

com cópia @juliemr

Recentemente (apenas 1-2 semanas) comecei a usar o Protractor para testar meu aplicativo Angular. Meu aplicativo tem ng-App definido na tag body na página de login. Meu teste continua reclamando dizendo "Angular não pôde ser encontrado na página httP://(WebSite Address): angular nunca fornecido resumeBootstrap" Mas a execução do teste está bem. Não tenho certeza sobre o problema (se eu ativar o parâmetro ignoresync do navegador, ele está funcionando bem, mas essa não é a maneira correta), posso tentar o que Juliemr sugere (usar driver legado para navegar), mas apenas tentando entender por que meu aplicativo está reclamando mesmo que tenha ng-App na tag do corpo. App Functional Behavior é assim.. Página de Login..Uma vez Log In, clique em algum link na Home Page e ele abre outra Janela/Aba. Alguém poderia me ajudar a encontrar as etapas que todos precisam verificar para garantir que o bootstraping seja feito de maneira automática (um é o parâmetro ng-app na tag body)? Preciso verificar algo no arquivo bootstrap.js (não vejo nenhum parâmetro ng-app no ​​arquivo bootstrap, portanto, verificando. Obrigado

Existe alguma atualização sobre este assunto? Quais são as melhores práticas ou métodos para configurar o transferidor para trabalhar com um aplicativo angular que não possui um aplicativo ng? @erkobridee você poderia explicar melhor seu exemplo? E alguém pode explicar como lidar corretamente com window.name em relação a como o transferidor deve esperar quando o angular terminar de carregar?

@juliemr Com o Angular 1.5, o bootstrap manualmente é a maneira preferida de bootstrap. Há suporte de transferidor abençoado a caminho?

+1 Acho que esse problema deveria ter maior prioridade. Se o Protractor não funcionar com bootstrap manual, não há benefício em usar o Protractor. O ponto principal de usar o Protractor sobre o Webdriver é que o Protractor lida com toda a sincronização angular para você.

Eu tenho o mesmo problema. Qualquer atualização?

oi, existe algum plano ou processo para melhorar isso?

Estranho não há feedback. Tenho aplicativos sem ng-app e não consigo testar
eles com transferidor

Em 16 de julho de 2016 05:01, "millerwx" [email protected] escreveu:

oi, existe algum plano ou processo para melhorar isso?


Você está recebendo isso porque comentou.
Responda a este e-mail diretamente, visualize-o no GitHub
https://github.com/angular/protractor/issues/66#issuecomment -233104701,
ou silenciar o thread
https://github.com/notifications/unsubscribe-auth/AOghFs2ff0e-88DLafNGD3iCH8uCOgVaks5qWEmIgaJpZM4A82d7
.

Até esta data em 2016, isso ainda era um problema para mim. Eu segui a resposta magnetizada para resolver o problema. Acabei de fazer algumas alterações. O que é legal e fora do escopo da conversa é rastrear também redirecionamentos e retornar ao seu aplicativo. *Eu fiz com Babel, mas você pode escrever em ES5.

`describe('tela de login', ()=>{

var urlTimeout = 9000;

const waitForUrlRegex = (regex, urlMatcher )=>{

    return driver.wait(()=>{
        return driver.getCurrentUrl().then( (url)=>{
            return regex.test(url);
        });
    }, urlTimeout, "Expectation error: Timeout for url not matching " + urlMatcher );
};

const waitForRoute = (route)=>{
    return waitForUrlRegex( new RegExp( route + "$" ), route  );
}

const waitForUrl = (url)=>{
    return waitForUrlRegex( new RegExp( "^https?:\/\/" + url ), url );
}

it('should go to homepage and click login button', ()=>{

    driver.get( app_url, urlTimeout);

    //there is a redirect to welcome route
    expect( waitForRoute("welcome") ).toBe(true);

    const loginButton = element.all(by.className("login_button")).get(0);
    loginButton.click();
});

it( 'should enter credentials', ()=>{
    expect( waitForUrl( non_angular_login_url ) ).toBe(true);
    driver.findElement( by.name("user")).sendKeys( "username");
    driver.findElement( by.name("password")).sendKeys( "userpassword");
    driver.findElement( by.tagName("button")).click();
});

it( 'should go back to app, and continue testing', ()=>{
    expect( waitForRoute( "user-screen") ).toBe(true);
});

});`

config.onPrepare

onPrepare: ()=>{ require("babel-register"); global.driver = browser.driver; }

Caso você não queira olhar para redirecionamentos, basta fazer o driver dormir e começar a trabalhar com o transferidor.

`driver.get( app_url+ "/#/welcome", urlTimeout);

    driver.sleep( 1000 );
    const loginButton = element.all(by.className("login_button")).get(0);
    loginButton.click();`

Na verdade, suportamos bootstrapping manual, esta informação é antiga. O bootstrapping se tornará ainda mais confiável quando lidarmos com https://github.com/angular/protractor/issues/3857 também.

@sjelin Qual é a maneira documentada de oferecer suporte à inicialização manual? Não consigo encontrar as informações relevantes para o transferidor 5.0.

Eu estava enfrentando o mesmo problema durante a inicialização manual. e encontrei a solução para rodar com grunhido.
usado transferidor versão 2.5.1.

  1. Mencione isso em seu arquivo grunt.

grunt.initConfig({ protractor: { options: { configFile: "protractor.conf.js", // Default protractor config file keepAlive: true, // If false, the grunt process stops when the test fails. noColor: false, // If true, protractor will not use colors in its output. args: { // Arguments passed to the command } }, e2e: { options: { // Stops Grunt process if a test fails keepAlive: true } } } })

  1. Instale npm install grunt-protractor-runner --save-dev e adicione para a tarefa de carregamento grunt.loadNpmTasks('grunt-protractor-runner')

  2. Execute o transferidor com o seguinte comando:
    grunt protractor
    ou com o nome da suíte
    grunt protractor --suite suite_name

Por favor, deixe-me saber se ele funciona para você.

Isso funcionou para mim.
browser.ignoreSynchronization = true;
navegador.get(url);
browser.ignoreSynchronization = false;

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