qcon 2014 simplificado -...
TRANSCRIPT
AngularJS aplicadoConstruindo aplicações client-side
bem testadas
@FelippeNardi
Objetivos!
• Elementos essenciais!• Testes: e2e e unitários • Mindset AngularJS
Felippe NardiFront-end Engineer e Designer
client-side web apps
client-side web apps
Service
Controller
View
Angular vai te ajudar:
• Organizar o javascript • Criar sites responsivos •Testar com facilidade
Framework Javascript
Extensão do HTML
<input type=“text” autofocus>
Service
Controller
ViewDirectives Expressions
ng-model
<input ng-model="teste">!Hello {{ teste }}!
AngularJS
Digest Loop
Iniciando a aplicação
<html ng-app>!!<!-- (...) -->!</html>!
<input ng-model="teste">Hello {{ teste }}!<span ng-hide="!teste">!!
</span>
<input ng-model="teste"><span ng-show="teste">!!
</span>Hello {{ teste }}!
Recapitulando
• Directives • {{ Expressions }} • Digest Loop
Service
Controller
ViewDirectives Expressions
Service
Controller
ViewDirectives Expressions
Filters
Filters
{{"Olá"}}!
Filters
{{"Olá"|uppercase}}!
Filters
{{"Olá"|lowercase}}!
Filters<input ng-model="busca">!!
{{users|filter:busca}}!
Recapitulando
• {{ expression | filter }} • Array • String
I18n
{{1228212|number}}!
I18n
{{12.82|currency}}!
{{1411676100000!|date}}!
I18n
Suporte pt-BR
<script src="angular.js">!<script src="angular-locale_pt-br.js">!
Todos os arquivos de locale do Angular
{{1228212|number}}!
I18n
{{12.82|currency}}!
I18n
{{1411676100000!|date}}!
I18n
Indo alémangular-translate
github.com/angular-translate/angular-translate
Service
Controller
ViewDirectives Expressions
function MyCtrl($scope){!!$scope.teste ="Olá!";!};!
Controllers
<div!ng-controller="MyCtrl">!!{{ teste }}!</div>!
function MyCtrl($scope){!!$scope.teste ="Olá!";!};!
Directives Essenciais
ng-repeat
<li ng-repeat="name in names">!!{{ name }}!</li>!
$scope.names = [!!"Antônio", "Carlos", "Souza"!];!
ng-pluralize<ng-pluralize count="names"!when="{'0':'Nenhum nome',!! '1':'Só um nome',!! 'other': '{} nomes'}">!</ng-pluralize>!
ng-pluralize<ng-pluralize count="names"!when="{'0':'Nenhum nome',!! '1':'Só um nome',!! 'other': '{} nomes'}">!</ng-pluralize>!
Recapitulando
• Controllers • $scope • ng-controller
Service
Controller
ViewDirectives Expressions
function MyCtrl( ){!!
};!
$scope$scope.teste ="World!";!
Service
$filter('uppercase')("Hello");!
{{"Hello"|uppercase}}!
Service
$http({ method: 'GET',!url: '/home' })!
XMLHttpRequest ou JSONP
.then(!!
!
);
!function() { /*...*/ },!!function() { /*...*/}
Service
Recapitulando:
• Data-binding!• Digest Loop!• Dependency Injection
Unit Tests!no AngularJS
TestRunner.html:• jasmine-all.js
• angular.js
• angular-mocks.js
• arquivos javascript
• arquivos de test
moourl.com/tryangular
2 exemplos!Service
Controller
github.com/felippenardi/mini-lero-lero
Nomeando o App
var leroLeroApp = angular.module('leroLeroApp', []);!
app.js
<html ng-app="leroLeroApp">!!<!-- (...) -->!</html>!
Nomeando o Appindex.html
Carregando as frasesapp.js
Serviceapp.js
});
leroLeroApp.factory('geradorDeFrases',!! function ($http) {!!
var promise =!! ! $http.get('frases.json')! .then(function (response) {! return response.data;! });!!
return {! get: function() {! return promise;! }! };!});
Syntax Jasmine
describe('Descrição', function() {! ! beforeEach(function() {!! ! // Roda antes de cada teste! });! ! afterEach(function() {!! ! // Roda depois de cada teste! });! ! it('Descrição do teste', function() {!
Testando o ServiceappSpecs.js
});
describe('Service: Gerador De Frases', function() {!!
!
!
!
!
!
!
!
!
!
!
!
! var geradorDeFrases,!! ! ! httpBackend,!! ! ! frases; beforeEach(module('leroLeroApp'));!
! beforeEach(inject(!!
!
!
!
!
));!
! ! function(_geradorDeFrases_,!! ! ! ! ! ! ! ! $httpBackend) {!!
!
! ! }
! ! ! geradorDeFrases = _geradorDeFrases_;!! ! ! httpBackend = $httpBackend;
});
describe('Service: Gerador De Frases', function() {!!
!
!
!
!
!
!
!
!
!
!
!
! var geradorDeFrases,!! ! ! httpBackend,!! ! ! frases; beforeEach(module('leroLeroApp'));!
! beforeEach(inject(!!
!
!
!
!
));!
! ! function(_geradorDeFrases_,!! ! ! ! ! ! ! ! $httpBackend) {!!
!
! ! }
! ! ! geradorDeFrases = _geradorDeFrases_;!! ! ! httpBackend = $httpBackend;
});
afteEach(function() {!! ! httpBackend! .verifyNoOutstandingExpectation();! httpBackend! .verifyNoOutstandingRequest();! });
!
!
!
!
!
!
!
!
!
beforeEach(module('leroLeroApp'));!
! beforeEach(inject(!!
!
!
!
!
));!
! ! function(_geradorDeFrases_,!! ! ! ! ! ! ! ! $httpBackend) {!!
!
! ! }
! ! ! geradorDeFrases = _geradorDeFrases_;!! ! ! httpBackend = $httpBackend;
});
!
!
!
!
!
!
!
!
!
!
beforeEach(module('leroLeroApp'));!
! beforeEach(inject(!!
!
!
!
!
));!
! ! function(_geradorDeFrases_,!! ! ! ! ! ! ! ! $httpBackend) {!!
!
! ! }
! ! ! geradorDeFrases = _geradorDeFrases_;!! ! ! httpBackend = $httpBackend;
afteEach(function() {!! ! httpBackend! .verifyNoOutstandingExpectation();! httpBackend! .verifyNoOutstandingRequest();! });
});
it('fornece frases', function() {!!
!
!
!
!
!
!
!
!
!
!
!
});
httpBackend! .expectGET('frases.json').respond([! "Frase 1", "Frase 2", "Frase 3"! ]);
geradorDeFrases.get()! .then(function(response) {! frases = response;! });
expect(frases)! .toEqual(jasmine.any(Array));expect(frases.length).toBe(3);
});
it('fornece frases', function() {!!
!
!
!
!
!
!
!
!
!
!
!
});
httpBackend! .expectGET('frases.json').respond([! "Frase 1", "Frase 2", "Frase 3"! ]);
geradorDeFrases.get()! .then(function(response) {! frases = response;! });httpBackend.flush();expect(frases)! .toEqual(jasmine.any(Array));expect(frases.length).toBe(3);
Recapitulando
• Carregar a aplicação • Injetar serviços • Usar variáveis para armazenar os serviços
Mostrando as frasesindex.html
frase.gerar()
Expor objeto frases pra view
app.js
Controllerapp.js
});
leroLeroApp.controller('MainCtrl',!! function ($scope, geradorDeFrases) {! !! ! var i = 0, frases;! geradorDeFrases.get()! .then(function(response){! frases = response;! $scope.frase.gerar();! });! $scope.frase = {! gerar: function() {! $scope.frase.atual = frases[i];! i < frases.length - 1? i++ : i = 0;! }! };!});
Testando o ControllerappSpecs.js
});
describe('Controller: MainCtrl',!function() {! var scope, MainCtrl, geradorMock, q;!!
beforeEach(module('leroLeroApp'));!!
beforeEach(inject(! function($controller, $rootScope, $q) {! q = $q;! scope = $rootScope.$new();! ! MainCtrl = $controller('MainCtrl', {! $scope: scope,! geradorDeFrases: geradorMock! });
});
!
beforeEach(inject(! function($controller, $rootScope, $q) {! q = $q;! scope = $rootScope.$new();! ! MainCtrl = $controller('MainCtrl', {! $scope: scope,! geradorDeFrases: geradorMock! });!
scope.$apply();! }! ));!
});
));! geradorMock = {! get: function() {! var frases = q.defer();! frases.resolve(["A","B","C"]);! return frases.promise;! }! };!!
it('começa com uma frase', function() {! expect(scope.frase.atual)! .toEqual(jasmine.any(String));! });
});
));! };!!
it('começa com uma frase', function() {! expect(scope.frase.atual)! .toEqual(jasmine.any(String));! });!
it('gera nova a frase', function() {! var primeiraFrase = scope.frase.atual;! ! scope.frase.gerar();! ! var segundaFrase = scope.frase.atual;! ! expect(primeiraFrase)! .not.toEqual(segundaFrase);! });
});
it('gera infinitas frases', function() {! var i = 4;! do { scope.frase.gerar() } while (--i);!!
expect(scope.frase.atual!! ! .toBeDefined();! });!});!
! scope.frase.gerar();! ! var segundaFrase = scope.frase.atual;! ! expect(primeiraFrase)! .not.toEqual(segundaFrase);! });
frase.gerar()
});
<div ng-controller="MainCtrl">!!
<h1>Lero Lero</h1>!!
<a ng-click=" ">! Gerar frase! </a>!!
<blockquote>! ! </blockquote>!!
</div>!
<div ng-controller="MainCtrl">!!
<h1>Lero Lero</h1>!!
<a ng-click="frase.gerar()">! Gerar frase! </a>!!
<blockquote>! {{ frase.atual }}! </blockquote>!!
</div>!
Recapitulando
• Mesma estrutura básica!• Sobrescrever serviços!• Simular promessas
Testes Unitários!prontos
Vamos para os Testes End to End!
1 teste:!gera frase
Setup do Protractor$ npm install -g protractor
$ webdriver-manager update
$ webdriver-manager start
$ webdriver-manager start
http://localhost:4444/wd/hub
Arquivo de configuração
// conf.js!exports.config = {! seleniumAddress:! ' ',! !}!
$ protractor conf.js
http://localhost:4444/wd/hubspecs: ['spec.js']!
beforeEach(function() {! browser.get('http://localhost:8000/');!});!!describe('Frase', function() {!! it('gera frase', function() {! var frase1,! frase2;!! element(by.binding('frase.atual')).getText()! .then(function(frase) {! frase1 = frase;! });!! element(by.id('gerar-frase')).click();
! it('gera frase', function() {! var frase1,! frase2;!! element(by.binding('frase.atual')).getText()! .then(function(frase) {! frase1 = frase;! });!! element(by.id('gerar-frase')).click();
element(by.binding('frase.atual')).getText()! .then(function(frase) {! frase2 = frase;! expect(frase1).not.toBe(frase2);! });! });!});
Recapitulando
• Rodar servidor selenium!• Preparar arquivo conf.js!• Rodar protractor conf.js
Seus próximos passos:
github.com/felippenardi/mini-lero-lero
moourl.com/tryangular
moourl.com/tryangular
angular.github.io/protractor/
angular.github.io/protractor/
jasmine.github.io
jasmine.github.io
moourl.com/angularjs
youtube: A Tiny Piece of AngularJS
egghead.io