├── cypress ├── cypress.json ├── cypress │ └── integration │ │ └── spec1.js └── Dockerfile ├── services ├── frontend │ ├── img │ │ ├── esm.png │ │ ├── design.png │ │ └── refactoring.png │ ├── index.css │ ├── index.html │ └── index.js ├── controller │ ├── shipping.js │ ├── inventory.js │ └── index.js ├── inventory │ ├── products.json │ └── index.js └── shipping │ └── index.js ├── proto ├── shipping.proto └── inventory.proto ├── LICENSE ├── package.json └── README.md /cypress/cypress.json: -------------------------------------------------------------------------------- 1 | { 2 | "fixturesFolder": false, 3 | "pluginsFile": false, 4 | "supportFile": false 5 | } -------------------------------------------------------------------------------- /services/frontend/img/esm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aserg-ufmg/demo-cypress/HEAD/services/frontend/img/esm.png -------------------------------------------------------------------------------- /services/frontend/img/design.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aserg-ufmg/demo-cypress/HEAD/services/frontend/img/design.png -------------------------------------------------------------------------------- /services/frontend/img/refactoring.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aserg-ufmg/demo-cypress/HEAD/services/frontend/img/refactoring.png -------------------------------------------------------------------------------- /cypress/cypress/integration/spec1.js: -------------------------------------------------------------------------------- 1 | describe('Meu primeiro teste', () => { 2 | it('Não faz nada', () => { 3 | expect(true).to.equal(true) 4 | }) 5 | }) 6 | -------------------------------------------------------------------------------- /cypress/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node 2 | 3 | ENV NO_UPDATE_NOTIFIER=1 4 | 5 | EXPOSE 3000 6 | EXPOSE 5000 7 | 8 | WORKDIR /app 9 | 10 | ADD . . 11 | RUN npm install --silent --loglevel=error 12 | 13 | CMD ["npm", "run", "exec"] 14 | -------------------------------------------------------------------------------- /proto/shipping.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | service ShippingService { 4 | rpc GetShippingRate(ShippingPayload) returns (ShippingResponse) {} 5 | } 6 | 7 | message ShippingPayload { 8 | string cep = 1; 9 | } 10 | 11 | message ShippingResponse { 12 | float value = 1; 13 | } 14 | -------------------------------------------------------------------------------- /proto/inventory.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | service InventoryService { 4 | rpc SearchAllProducts(Empty) returns (ProductsResponse) {} 5 | } 6 | 7 | message Empty{} 8 | 9 | message ProductResponse { 10 | int32 id = 1; 11 | string name = 2; 12 | int32 quantity = 3; 13 | float price = 4; 14 | string photo = 5; 15 | string author = 6; 16 | } 17 | 18 | message ProductsResponse { 19 | repeated ProductResponse products = 1; 20 | } 21 | -------------------------------------------------------------------------------- /services/controller/shipping.js: -------------------------------------------------------------------------------- 1 | const grpc = require('@grpc/grpc-js'); 2 | const protoLoader = require('@grpc/proto-loader'); 3 | 4 | const packageDefinition = protoLoader.loadSync('proto/shipping.proto', { 5 | keepCase: true, 6 | longs: String, 7 | enums: String, 8 | arrays: true, 9 | }); 10 | 11 | const ShippingService = grpc.loadPackageDefinition(packageDefinition).ShippingService; 12 | const client = new ShippingService('127.0.0.1:3001', grpc.credentials.createInsecure()); 13 | 14 | module.exports = client; 15 | -------------------------------------------------------------------------------- /services/controller/inventory.js: -------------------------------------------------------------------------------- 1 | const grpc = require('@grpc/grpc-js'); 2 | const protoLoader = require('@grpc/proto-loader'); 3 | 4 | const packageDefinition = protoLoader.loadSync('proto/inventory.proto', { 5 | keepCase: true, 6 | longs: String, 7 | enums: String, 8 | arrays: true, 9 | }); 10 | 11 | const InventoryService = grpc.loadPackageDefinition(packageDefinition).InventoryService; 12 | const client = new InventoryService('127.0.0.1:3002', grpc.credentials.createInsecure()); 13 | 14 | module.exports = client; 15 | -------------------------------------------------------------------------------- /services/inventory/products.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "id": 1, 4 | "name": "Refactoring", 5 | "author": "Martin Fowler", 6 | "quantity": 10, 7 | "price": 79.92, 8 | "photo": "/img/refactoring.png" 9 | }, 10 | { 11 | "id": 2, 12 | "name": "Engenharia De Software Moderna", 13 | "author": "Marco Tulio Valente", 14 | "quantity": 10, 15 | "price": 65.9, 16 | "photo": "/img/esm.png" 17 | }, 18 | { 19 | "id": 3, 20 | "name": "Design Patterns", 21 | "author": "Erich Gamma, John Vlissides, Richard Helm, Ralph Johnson", 22 | "quantity": 10, 23 | "price": 67.99, 24 | "photo": "/img/design.png" 25 | } 26 | ] 27 | -------------------------------------------------------------------------------- /services/inventory/index.js: -------------------------------------------------------------------------------- 1 | const grpc = require('@grpc/grpc-js'); 2 | const protoLoader = require('@grpc/proto-loader'); 3 | const products = require('./products.json'); 4 | 5 | const packageDefinition = protoLoader.loadSync('proto/inventory.proto', { 6 | keepCase: true, 7 | longs: String, 8 | enums: String, 9 | arrays: true, 10 | }); 11 | 12 | const inventoryProto = grpc.loadPackageDefinition(packageDefinition); 13 | 14 | const server = new grpc.Server(); 15 | 16 | // implementa os métodos do InventoryService 17 | server.addService(inventoryProto.InventoryService.service, { 18 | searchAllProducts: (_, callback) => { 19 | callback(null, { 20 | products: products, 21 | }); 22 | }, 23 | }); 24 | 25 | server.bindAsync('127.0.0.1:3002', grpc.ServerCredentials.createInsecure(), () => { 26 | console.log('Inventory Service running at http://127.0.0.1:3002'); 27 | server.start(); 28 | }); 29 | -------------------------------------------------------------------------------- /services/shipping/index.js: -------------------------------------------------------------------------------- 1 | const grpc = require('@grpc/grpc-js'); 2 | const protoLoader = require('@grpc/proto-loader'); 3 | 4 | const packageDefinition = protoLoader.loadSync('proto/shipping.proto', { 5 | keepCase: true, 6 | longs: String, 7 | enums: String, 8 | arrays: true, 9 | }); 10 | 11 | const shippingProto = grpc.loadPackageDefinition(packageDefinition); 12 | 13 | const server = new grpc.Server(); 14 | 15 | // implementa os métodos do ShippingService 16 | server.addService(shippingProto.ShippingService.service, { 17 | GetShippingRate: (_, callback) => { 18 | const shippingValue = Math.random() * 100 + 1; // Random value from R$1 to R$100 19 | 20 | callback(null, { 21 | value: shippingValue, 22 | }); 23 | }, 24 | }); 25 | 26 | server.bindAsync('0.0.0.0:3001', grpc.ServerCredentials.createInsecure(), () => { 27 | console.log('Shipping Service running at http://127.0.0.1:3001'); 28 | server.start(); 29 | }); 30 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Applied Software Engineering Research Group 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /services/controller/index.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | const shipping = require('./shipping'); 3 | const inventory = require('./inventory'); 4 | const cors = require('cors'); 5 | 6 | const app = express(); 7 | app.use(cors()); 8 | 9 | /** 10 | * Retorna a lista de produtos da loja via InventoryService 11 | */ 12 | app.get('/products', (req, res, next) => { 13 | inventory.SearchAllProducts(null, (err, data) => { 14 | if (err) { 15 | console.error(err); 16 | res.status(500).send({ error: 'something failed :(' }); 17 | } else { 18 | res.json(data.products); 19 | } 20 | }); 21 | }); 22 | 23 | /** 24 | * Consulta o frete de envio no ShippingService 25 | */ 26 | app.get('/shipping/:cep', (req, res, next) => { 27 | shipping.GetShippingRate( 28 | { 29 | cep: req.params.cep, 30 | }, 31 | (err, data) => { 32 | if (err) { 33 | console.error(err); 34 | res.status(500).send({ error: 'something failed :(' }); 35 | } else { 36 | res.json({ 37 | cep: req.params.cep, 38 | value: data.value, 39 | }); 40 | } 41 | } 42 | ); 43 | }); 44 | 45 | /** 46 | * Inicia o router 47 | */ 48 | app.listen(3000, () => { 49 | console.log('Controller Service running on http://127.0.0.1:3000'); 50 | }); 51 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "micro-livraria", 3 | "version": "1.0.0", 4 | "description": "Toy example of microservice", 5 | "main": "", 6 | "scripts": { 7 | "start": "run-p start-frontend start-controller start-shipping start-inventory", 8 | "start-controller": "nodemon services/controller/index.js", 9 | "start-shipping": "nodemon services/shipping/index.js", 10 | "start-inventory": "nodemon services/inventory/index.js", 11 | "start-frontend": "serve services/frontend", 12 | "exec": "run-p start-frontend exec-controller exec-shipping exec-inventory", 13 | "exec-controller": "node services/controller/index.js", 14 | "exec-shipping": "node services/shipping/index.js", 15 | "exec-inventory": "node services/inventory/index.js" 16 | }, 17 | "repository": { 18 | "type": "git", 19 | "url": "git+https://github.com/aserg-ufmg/micro-livraria.git" 20 | }, 21 | "author": "Rodrigo", 22 | "license": "MIT", 23 | "bugs": { 24 | "url": "https://github.com/aserg-ufmg/micro-livraria/issues" 25 | }, 26 | "homepage": "https://github.com/aserg-ufmg/micro-livraria#readme", 27 | "dependencies": { 28 | "@grpc/grpc-js": "^1.2.6", 29 | "@grpc/proto-loader": "^0.5.6", 30 | "cors": "^2.8.5", 31 | "express": "^4.17.1", 32 | "google-protobuf": "^3.15.0-rc.1" 33 | }, 34 | "devDependencies": { 35 | "serve": "^12.0.0", 36 | "nodemon": "^2.0.7", 37 | "npm-run-all": "^4.1.5" 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /services/frontend/index.css: -------------------------------------------------------------------------------- 1 | html, 2 | body { 3 | background: #eff3f4; 4 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen-Sans, Ubuntu, Cantarell, 'Helvetica Neue', 5 | sans-serif; 6 | } 7 | 8 | .hero-body .container { 9 | max-width: 700px; 10 | } 11 | 12 | .hero-body .title { 13 | color: hsl(192, 17%, 99%) !important; 14 | } 15 | 16 | .hero-body .subtitle { 17 | color: hsl(192, 17%, 99%) !important; 18 | padding-top: 2rem; 19 | line-height: 1.5; 20 | } 21 | 22 | .features { 23 | padding: 5rem 0; 24 | } 25 | 26 | .box.cta { 27 | border-radius: 0; 28 | border-left: none; 29 | border-right: none; 30 | } 31 | 32 | .card-image > .fa { 33 | font-size: 8rem; 34 | padding-top: 2rem; 35 | padding-bottom: 2rem; 36 | color: #209cee; 37 | } 38 | 39 | .card-content .content { 40 | font-size: 14px; 41 | margin: 1rem 1rem; 42 | } 43 | 44 | .card-content .content h4 { 45 | font-size: 16px; 46 | font-weight: 700; 47 | } 48 | 49 | .card { 50 | box-shadow: 0px 2px 4px rgba(0, 0, 0, 0.18); 51 | margin-bottom: 2rem; 52 | } 53 | 54 | .is-shady { 55 | animation: flyintoright 0.4s backwards; 56 | background: #fff; 57 | box-shadow: rgba(0, 0, 0, 0.1) 0 1px 0; 58 | border-radius: 4px; 59 | display: inline-block; 60 | margin: 10px; 61 | position: relative; 62 | transition: all 0.2s ease-in-out; 63 | width: 100%; 64 | } 65 | 66 | .is-shady:hover { 67 | box-shadow: 0 10px 16px rgba(0, 0, 0, 0.13), 0 6px 6px rgba(0, 0, 0, 0.19); 68 | } 69 | 70 | @media (min-width: 800px) { 71 | .book-meta { 72 | min-height: 250px; 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /services/frontend/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Micro Livraria 8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 |
16 |
17 |

Micro Livraria

18 |
19 |
20 |
21 |
22 |

Uma simples livraria utilizando microservices

23 |
24 |
25 |
26 |
27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /services/frontend/index.js: -------------------------------------------------------------------------------- 1 | function newBook(book) { 2 | const div = document.createElement('div'); 3 | div.className = 'column is-4'; 4 | div.innerHTML = ` 5 |
6 |
7 |
8 | ${book.name} 13 |
14 |
15 |
16 |
17 |
18 |

R$${book.price.toFixed(2)}

19 |

Disponível em estoque: 5

20 |

${book.name}

21 |

${book.author}

22 |
23 |
24 |
25 | 26 |
27 | 30 |
31 | 32 |
33 |
34 |
`; 35 | return div; 36 | } 37 | 38 | function calculateShipping(id, cep) { 39 | fetch('http://localhost:3000/shipping/' + cep) 40 | .then((data) => { 41 | if (data.ok) { 42 | return data.json(); 43 | } 44 | throw data.statusText; 45 | }) 46 | .then((data) => { 47 | swal('Frete', `O frete é: R$${data.value.toFixed(2)}`, 'success'); 48 | }) 49 | .catch((err) => { 50 | swal('Erro', 'Erro ao consultar frete', 'error'); 51 | console.error(err); 52 | }); 53 | } 54 | 55 | document.addEventListener('DOMContentLoaded', function () { 56 | const books = document.querySelector('.books'); 57 | 58 | fetch('http://localhost:3000/products') 59 | .then((data) => { 60 | if (data.ok) { 61 | return data.json(); 62 | } 63 | throw data.statusText; 64 | }) 65 | .then((data) => { 66 | if (data) { 67 | data.forEach((book) => { 68 | books.appendChild(newBook(book)); 69 | }); 70 | 71 | document.querySelectorAll('.button-shipping').forEach((btn) => { 72 | btn.addEventListener('click', (e) => { 73 | const id = e.target.getAttribute('data-id'); 74 | const cep = document.querySelector(`.book[data-id="${id}"] input`).value; 75 | calculateShipping(id, cep); 76 | }); 77 | }); 78 | 79 | document.querySelectorAll('.button-buy').forEach((btn) => { 80 | btn.addEventListener('click', (e) => { 81 | swal('Compra de livro', 'Sua compra foi realizada com sucesso', 'success'); 82 | }); 83 | }); 84 | } 85 | }) 86 | .catch((err) => { 87 | swal('Erro', 'Erro ao listar os produtos', 'error'); 88 | console.error(err); 89 | }); 90 | }); 91 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Mini-Roteiro Prático sobre Testes End-to-End usando Cypress 2 | 3 | O objetivo deste pequeno roteiro é ter um primeiro contato com testes do tipo end-to-end. Esses testes são chamados também de testes de front-end, testes de sistemas, testes de interface Web ou testes de interface com o usuário. 4 | 5 | No roteiro, vamos usar uma ferramenta de código aberto para testes end-to-end, chamada [Cypress](https://www.cypress.io), que permite a escrita desses testes em JavaScript. O Cypress é parecido com o Selenium, para o qual foi mostrado um exemplo de teste no [Capítulo 8]([https://engsoftmoderna.info/cap8.html](https://engsoftmoderna.info/cap8.html#testes-de-sistema)) do livro [Engenharia de Software Moderna](https://engsoftmoderna.info). 6 | 7 | No roteiro, vamos usar o Cypress para escrever alguns testes end-to-end para uma livraria virtual. Mais especificamente, vamos reusar a [micro-livraria](https://github.com/aserg-ufmg/micro-livraria) do roteiro prático de microsserviços. 8 | 9 | ## Instalação e Execução do Cypress 10 | 11 | Para realização do roteiro, configure o seu ambiente da seguinte forma: 12 | 13 | **Passo 1:** Faça um fork deste repositório, usando o botão "Fork" no canto superior direito da tela. 14 | 15 | **Passo 2:** Clone o projeto para um diretório local: 16 | 17 | ```bash 18 | git clone https://github.com//demo-cypress.git 19 | ``` 20 | 30 | 31 | **Passo 3:** Instale o [Docker](https://docs.docker.com/get-docker/). A micro-livraria (isto é, o sistema que vamos testar) e também o Cypress serão executados por meio de containers. 32 | 33 | **Passo 4:** Coloque o sistema da micro-livraria no ar. Primeiro gere uma nova imagem, executando o seguinte comando na raiz do projeto: 34 | 35 | ```bash 36 | docker build -t micro-livraria -f cypress/Dockerfile . 37 | ``` 38 | 39 | Em seguida, execute a aplicação, chamando no mesmo diretório de antes: 40 | 41 | ```bash 42 | docker run -ti -p 3000:3000 -p 5000:5000 micro-livraria 43 | ``` 44 | 45 | **Passo 5:** Agora, vamos executar o Cypress, pela primeira vez, usando o seguinte comando na pasta `cypress` (ou seja, se estiver na raiz do projeto, execute antes `cd cypress`; essa pasta é a que contém o arquivo `cypress.json`): 46 | 47 | 52 | 53 | ```bash 54 | docker run --network="host" -it -v "$PWD":/e2e -w /e2e cypress/included:9.2.0 55 | ``` 56 | 57 | Observação: na primeira vez que for executado, esse comando pode demorar alguns minutos. pois ele vai baixar a imagem do Cypress e realizar o seu build. 58 | 59 | Veja também que essse comando já vai rodar um primeiro teste de exemplo, bem simples, que implementamos no no arquivo [spec1.js](https://github.com/aserg-ufmg/demo-cypress/blob/main/cypress/cypress/integration/spec1.js): 60 | 61 | ```javascript 62 | describe('Meu primeiro teste', () => { 63 | it('Não faz nada', () => { 64 | expect(true).to.equal(true) 65 | }) 66 | }) 67 | ``` 68 | 69 | Antes de prosseguir com o roteiro, analise e entenda, com calma, a saída produzida pelo Cypress. 70 | 71 | 77 | 78 | 97 | 98 | 107 | 108 | ## Tarefa #1: Testando o Front-end da micro-livraria 109 | 110 | Vamos agora implementar um teste end-to-end para a micro-livraria. Esse teste vai "simular" um usuário realizando as seguintes operações no site: 111 | 112 | 1. Abrir o site 113 | 2. Escolher o livro desejado 114 | 3. Inserir o CEP 115 | 4. Calcular o frete 116 | 5. Realizar a compra do livro 117 | 118 | Observe que um teste de front-end pode ser comparado com um "robô" simulando um usuário final utilizando as funcionalidades do sistema. 119 | 120 | **Passo 1:** 121 | 122 | Crie um arquivo `spec2.js` na mesma pasta do teste anterior (pasta `integration`), mas com o seguinte código: 123 | 124 | ```javascript 125 | describe('Teste End-to-End', () => { 126 | it('Teste 1: Visita Página', () => { 127 | // abre o site 128 | cy.visit('http://localhost:5000/') 129 | }) 130 | }) 131 | ``` 132 | 133 | Os comandos do Cypress são sempre executados sobre um objeto `cy`. A função `visit()` visita uma página, que, no caso da nossa micro-livraria, está no endereço `localhost:5000`. 134 | 135 | Em seguida, execute este teste usando sempre: 136 | 137 | ```bash 138 | docker run --network="host" -it -v "$PWD":/e2e -w /e2e cypress/included:9.2.0 139 | ``` 140 | 141 | Você vai perceber que o teste vai passar. 142 | 143 | **Passo 2:** 144 | 145 | Vamos agora acrescentar novos comportamentos no teste. Especificamente, vamos supor um cenário no qual um usuário compra o livro de Padrões de Projeto. 146 | 147 | Primeiro, precisamos garantir que o livro está sendo mostrado na página, do seguinte modo: 148 | 149 | ```javascript 150 | describe('Teste End-to-End', () => { 151 | it('Teste 1: Visita Página', () => { 152 | // abre o site 153 | cy.visit('http://localhost:5000/') 154 | }) 155 | it('Teste 2: Verifica item na página', () => { 156 | // Verifica se existe o livro desejado 157 | cy.get('[data-id=3]').should('contain.text', 'Design Patterns') 158 | }) 159 | }) 160 | ``` 161 | 162 | No código anterior, realizamos uma query usando a função `get` e assumimos que: 163 | 164 | * O catálogo de livros é exibido em três colunas. 165 | * O livro desejado está na terceira linha, cujo identificador é `data-id=3`. 166 | 167 | Por isso, usamos uma asserção que verifica se a terceira coluna inclui a string `Design Patterns`. 168 | 169 | Para rodar o teste, use o mesmo comando de antes. Procure também ver os vídeos que o Cypress grava automaticamente na pasta `cypress\cypress\videos`. 170 | 171 | 180 | 181 | **Passo 3:** 182 | 183 | Vamos agora incrementar nosso teste, para simular um usuário que insere o CEP no campo indicado e, sem seguida, clica no botão `Calcular Frete`: 184 | 185 | ```javascript 186 | describe('Teste End-to-End', () => { 187 | it('Teste 1: Visita Página', () => { 188 | // abre o site 189 | cy.visit('http://localhost:5000/') 190 | }) 191 | it('Teste 2: Verifica item na página', () => { 192 | // Verifica se existe o livro desejado 193 | cy.get('[data-id=3]').should('contain.text', 'Design Patterns') 194 | }) 195 | it('Teste 3: Calcula Frete', () => { 196 | // Calcula o frete 197 | cy.get('[data-id=3]').within(() => { 198 | cy.get('input').type('10000-000') 199 | cy.contains('Calcular Frete').click().then 200 | cy.wait(2000) 201 | }) 202 | cy.get('.swal-text').contains('O frete é: R$') 203 | 204 | // Fecha o pop-up com o preço do frete 205 | cy.get('.swal-button').click() 206 | }) 207 | }) 208 | ``` 209 | 210 | Primeiro, o teste busca pela terceira coluna e procura pelo campo de `input`. Em seguida, ele insere o CEP `10000-000` e pressiona o botão `Calcular Frete`. 211 | 212 | Prosseguindo, espera-se 2 segundos na função `wait()`, para garantir que a janela com o valor do frete vai carregar corretamente. 213 | 214 | Então, nessa janela, selecionamos o `swal-text` e usamos uma asserção para garantir que a mensagem é aquela que esperamos. 215 | 216 | Por fim, clicamos no botão para fechar o pop-up. 217 | 218 | Se ainda não o fez, rode o teste acima e assista também o vídeo com o passo a passo da sua execução que está na pasta `cypress\cypress\videos`. 219 | 220 | ## Tarefa #2: Testando a Compra de um Livro 221 | 222 | Agora é sua vez de incrementar o teste anterior! 223 | 224 | Basicamente, você deve acrescentar código no teste para simular a compra de um livro, conforme explicado a seguir: 225 | 226 | * Use a função `cy.contains` para selecionar o botão Comprar e para clicar nele (função `click`) 227 | * Espere que o pop-up seja exibido com a confirmação da compra (função `wait`) 228 | * Verifique se nesse pop-up temos a messagem: `Sua compra foi realizada com sucesso` 229 | * Feche o pop-up, clicando em seu botão 230 | 231 | ## Tarefa #3: Salve suas mudanças 232 | 233 | Realize um **COMMIT e PUSH** para salvar suas mudanças no teste. 234 | 235 | O commit pode usar qualquer mensagem e basta incluir o arquivo `spec2.js` 236 | 237 | 240 | 241 | ## Comentário Final 242 | 243 | Este roteiro teve como objetivo proporcionar uma primeira experiência prática com o Cypress, para que o aluno possa entender a "mecânica" básica de funcionamento de testes de interface. O site do Cypress possui uma extensa documentação sobre a ferramenta, com diversos exemplos, que pode ser útil para aqueles que quiserem aprofundar no estudo desse tipo de teste. 244 | 245 | ## Créditos 246 | 247 | Este roteiro foi elaborado por **Rodrigo Moreira**, aluno de mestrado do DCC/UFMG, como parte das suas atividades na disciplina Estágio em Docência, cursada em 2021/1, sob orientação do **Prof. Marco Tulio Valente**. 248 | --------------------------------------------------------------------------------