Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
AngularJS Aplicado — QCon Rio 2014
Search
Felippe Nardi
September 25, 2014
Programming
1
200
AngularJS Aplicado — QCon Rio 2014
Construindo aplicações client-side bem testadas
Felippe Nardi
September 25, 2014
Tweet
Share
More Decks by Felippe Nardi
See All by Felippe Nardi
Trabalhando Remoto como Desenvolvedor Web
felippenardi
2
170
Other Decks in Programming
See All in Programming
ニーリーにおけるプロダクトエンジニア
nealle
0
740
すべてのコンテキストを、 ユーザー価値に変える
applism118
3
1.1k
明示と暗黙 ー PHPとGoの インターフェイスの違いを知る
shimabox
2
470
ペアプロ × 生成AI 現場での実践と課題について / generative-ai-in-pair-programming
codmoninc
1
6.6k
Cursor AI Agentと伴走する アプリケーションの高速リプレイス
daisuketakeda
1
130
Webの外へ飛び出せ NativePHPが切り拓くPHPの未来
takuyakatsusa
2
480
ruby.wasmで多人数リアルタイム通信ゲームを作ろう
lnit
3
370
Porting a visionOS App to Android XR
akkeylab
0
220
Quand Symfony, ApiPlatform, OpenAI et LangChain s'allient pour exploiter vos PDF : de la théorie à la production…
ahmedbhs123
0
130
プロダクト志向なエンジニアがもう一歩先の価値を目指すために意識したこと
nealle
0
120
システム成長を止めない!本番無停止テーブル移行の全貌
sakawe_ee
1
160
Discover Metal 4
rei315
2
110
Featured
See All Featured
The Cost Of JavaScript in 2023
addyosmani
51
8.5k
YesSQL, Process and Tooling at Scale
rocio
173
14k
Save Time (by Creating Custom Rails Generators)
garrettdimon
PRO
31
1.3k
The Cult of Friendly URLs
andyhume
79
6.5k
Balancing Empowerment & Direction
lara
1
400
Six Lessons from altMBA
skipperchong
28
3.9k
Become a Pro
speakerdeck
PRO
28
5.4k
Adopting Sorbet at Scale
ufuk
77
9.4k
Visualization
eitanlees
146
16k
GraphQLとの向き合い方2022年版
quramy
49
14k
Helping Users Find Their Own Way: Creating Modern Search Experiences
danielanewman
29
2.7k
Navigating Team Friction
lara
187
15k
Transcript
AngularJS aplicado Construindo aplicações client-side bem testadas @FelippeNardi
Objetivos ! • Elementos essenciais! • Testes: e2e e unitários
• Mindset AngularJS
Felippe Nardi Front-end Engineer e Designer
client-side web apps
client-side web apps
None
Service Controller View
Angular vai te ajudar: • Organizar o javascript • Criar
sites responsivos •Testar com facilidade
Framework Javascript
Extensão do HTML
None
<input type=“text” autofocus>
Service Controller View Directives 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 View Directives Expressions
Service Controller View Directives 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ém angular-translate github.com/angular-translate/angular-translate
Service Controller View Directives 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 View Directives 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
None
github.com/felippenardi/ mini-lero-lero
Nomeando o App var leroLeroApp = angular.module('leroLeroApp', []);! app.js
<html ng-app="leroLeroApp">! !<!-- (...) -->! </html>! Nomeando o App index.html
Carregando as frases app.js
Service app.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! });! !
Testando o Service appSpecs.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(inject(! ! ! ! ! ! ));! ! ! function(_geradorDeFrases_,! ! ! ! ! ! ! ! ! $httpBackend) {! ! ! ! ! } ! ! ! geradorDeFrases = _geradorDeFrases_;! ! ! ! httpBackend = $httpBackend;
}); ! ! ! ! ! ! ! ! !
! 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);
None
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);
None
Recapitulando • Carregar a aplicação • Injetar serviços • Usar
variáveis para armazenar os serviços
Mostrando as frases index.html
frase.gerar()
Expor objeto frases pra view app.js
Controller app.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 Controller appSpecs.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);! });
None
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>!
None
Recapitulando • Mesma estrutura básica! • Sobrescrever serviços! • Simular
promessas
Testes Unitários! prontos
Vamos para os Testes End to End!
None
None
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/hub specs: ['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);! });! });! });
None
None
None
Recapitulando • Rodar servidor selenium! • Preparar arquivo conf.js! •
Rodar protractor conf.js
Seus próximos passos:
github.com/felippenardi/mini-lero-lero
None
moourl.com/tryangular
None
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
@felippenardi Obrigado!