Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
c05e148
feat(minesweeper): add restart button structure
lucaslscaixeta Jul 4, 2026
f730171
style(minesweeper): move restart button visibility to stylesheet
lucaslscaixeta Jul 4, 2026
870aa65
feat(minesweeper): add game reset functionality
lucaslscaixeta Jul 4, 2026
922e6ca
refactor(minesweeper): extract board initialization logic
lucaslscaixeta Jul 4, 2026
c74197e
style(minesweeper): improve layout of flag and restart buttons
lucaslscaixeta Jul 5, 2026
e5b72ab
feat(minesweeper): add reset game logic
lucaslscaixeta Jul 5, 2026
7d9b425
feat(minesweeper): customize restart button for win and loss states
lucaslscaixeta Jul 7, 2026
242ca77
refactor(minesweeper): centralize DOM element access
lucaslscaixeta Jul 10, 2026
5594a86
refactor(minesweeper): separate game initialization responsibilities
lucaslscaixeta Jul 11, 2026
98a9d58
refactor(minesweeper): extract board tile creation
lucaslscaixeta Jul 11, 2026
04545df
refactor(minesweeper): encapsulate mutable game state
lucaslscaixeta Jul 11, 2026
2d7d599
Merge pull request #2 from lucaslscaixeta/refactor/minesweeper-solid-v2
lucaslscaixeta Jul 11, 2026
76382bd
test(minesweeper): configure Cypress environment
lucaslscaixeta Jul 12, 2026
27888b8
test(minesweeper): add Cypress acceptance tests
lucaslscaixeta Jul 12, 2026
87efd90
Merge pull request #3 from lucaslscaixeta/tests/minesweeper-cypress
lucaslscaixeta Jul 12, 2026
dcbcc0a
ci(minesweeper): configure Cypress CI pipeline
lucaslscaixeta Jul 13, 2026
d3d9312
ci(minesweeper): configure Cypress server
lucaslscaixeta Jul 13, 2026
0b3c5ef
Merge pull request #4 from lucaslscaixeta/devops/minesweeper-ci
lucaslscaixeta Jul 13, 2026
aab1f97
ci: configurar Cypress no GitHub Actions
lucaslscaixeta Jul 13, 2026
5c48ddc
Merge pull request #5 from lucaslscaixeta/devops/minesweeper-ci
lucaslscaixeta Jul 13, 2026
d1c11b3
docs: adiciona analise de code smells e aplicacao de padroes de projeto
emerson-ataide Jul 15, 2026
062fffa
chore: remove arquivo de testes adicionado por engano
emerson-ataide Jul 16, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 42 additions & 0 deletions .github/workflows/minesweeper-ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
name: Minesweeper CI

on:
push:
pull_request:

jobs:
cypress-tests:
runs-on: ubuntu-latest

defaults:
run:
working-directory: Games/Minesweeper

steps:
- name: Checkout do repositório
uses: actions/checkout@v4

- name: Configurar Node.js
uses: actions/setup-node@v4
with:
node-version: 20

- name: Instalar dependências
run: npm install

- name: Instalar servidor HTTP
run: npm install -g serve wait-on

- name: Iniciar aplicação
run: serve . -l 3000 &

- name: Aguardar servidor iniciar
run: npx wait-on http://localhost:3000

- name: Instalar Google Chrome
uses: browser-actions/setup-chrome@v1

- name: Executar testes Cypress
run: npx cypress run --browser chrome
env:
CYPRESS_BASE_URL: http://localhost:3000
1 change: 1 addition & 0 deletions Games/Minesweeper/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
node_modules/
13 changes: 13 additions & 0 deletions Games/Minesweeper/cypress.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
const { defineConfig } = require("cypress");

module.exports = defineConfig({
e2e: {
baseUrl:
process.env.CYPRESS_BASE_URL ||
"http://127.0.0.1:5500/Games/Minesweeper",

setupNodeEvents(on, config) {
return config;
},
},
});
50 changes: 50 additions & 0 deletions Games/Minesweeper/cypress/e2e/minesweeper.cy.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
describe("Minesweeper", () => {

beforeEach(() => {
cy.visit("/index.html");
});

it("Cenário 1 - Reiniciar o jogo após derrota", () => {
cy.get("#0-0").click();

cy.get("#restart-button")
.should("be.visible")
.and("contain", "Restart");

cy.get("#restart-button").click();

cy.get("#mines-count").should("have.text", "10");
cy.get(".tile-clicked").should("have.length", 0);
});

it("Cenário 2 - Adicionar e remover bandeira", () => {
cy.get("#flag-button").click();

cy.get("#2-2")
.click()
.should("have.text", "🚩");

cy.get("#2-2")
.click()
.should("have.text", "");
});

it("Cenário 3 - Vencer a partida", () => {
for (let r = 2; r < 8; r++) {
for (let c = 0; c < 8; c++) {
cy.get(`#${r}-${c}`).click();
}
}

cy.get("#1-2").click();
cy.get("#1-3").click();
cy.get("#1-4").click();
cy.get("#1-5").click();
cy.get("#1-6").click();
cy.get("#1-7").click();

cy.get("#mines-count").should("contain", "Cleared");
cy.get("#restart-button").should("contain", "AGAIN");
});

});
5 changes: 5 additions & 0 deletions Games/Minesweeper/cypress/fixtures/example.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"name": "Using fixtures to represent data",
"email": "hello@cypress.io",
"body": "Fixtures are a great way to mock data for responses to routes"
}
25 changes: 25 additions & 0 deletions Games/Minesweeper/cypress/support/commands.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// ***********************************************
// This example commands.js shows you how to
// create various custom commands and overwrite
// existing commands.
//
// For more comprehensive examples of custom
// commands please read more here:
// https://on.cypress.io/custom-commands
// ***********************************************
//
//
// -- This is a parent command --
// Cypress.Commands.add('login', (email, password) => { ... })
//
//
// -- This is a child command --
// Cypress.Commands.add('drag', { prevSubject: 'element'}, (subject, options) => { ... })
//
//
// -- This is a dual command --
// Cypress.Commands.add('dismiss', { prevSubject: 'optional'}, (subject, options) => { ... })
//
//
// -- This will overwrite an existing command --
// Cypress.Commands.overwrite('visit', (originalFn, url, options) => { ... })
17 changes: 17 additions & 0 deletions Games/Minesweeper/cypress/support/e2e.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// ***********************************************************
// This example support/e2e.js is processed and
// loaded automatically before your test files.
//
// This is a great place to put global configuration and
// behavior that modifies Cypress.
//
// You can change the location of this file or turn off
// automatically serving support files with the
// 'supportFile' configuration option.
//
// You can read more here:
// https://on.cypress.io/configuration
// ***********************************************************

// Import commands.js using ES2015 syntax:
import './commands'
145 changes: 145 additions & 0 deletions Games/Minesweeper/documentacao/padroes_e_smells.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
# Padrões de Projeto e Code Smells

Este documento apresenta a análise de qualidade de código e refatoração do arquivo principal (`script.js`) do projeto Minesweeper.

---

## 1. Code Smells Identificados e Refatorados

### Smell 1: Variáveis Globais (Global Data / God Object)
* **Problema identificado:** O uso de variáveis globais soltas (`var`) polui o escopo principal, viola o encapsulamento e aumenta a probabilidade de efeitos colaterais acidentais (side effects), dificultando o reset da partida.
* **Como estava (Antes):**
```javascript
var board = [];
var rows = 8;
var columns = 8;
var minesCount = 10;
var minesLocation = [];
var tilesClicked = 0;
var flagEnabled = false;
var gameOver = false;
```
* **Alterações feitas :** As variáveis foram encapsuladas em um único objeto chamado `gameState`, atuando como a única fonte de verdade do estado da aplicação.
```javascript
const rows = 8;
const columns = 8;
const minesCount = 10;

const gameState = {
board: [],
minesLocation: [],
tilesClicked: 0,
flagEnabled: false,
gameOver: false
};
```

### Smell 2: Lógica de Teste no Código de Produção (Test Code in Production)
* **Problema identificado:** Para viabilizar os testes automatizados E2E, inseriu-se um objeto de teste (`window.Cypress`) diretamente na regra de negócio. O código de produção não deveria conhecer o ambiente de teste, caracterizando uma violação do princípio SRP (Single Responsibility Principle).
* **Como estava ( - Geração Aleatória Padrão):**
```javascript
function setMines() {
let minesLeft = minesCount;
while (minesLeft > 0) {
let r = Math.floor(Math.random() * rows);
let c = Math.floor(Math.random() * columns);
// ...
}
}
```
* **Nossas alterações ( - Inserção do Débito Técnico):**
```javascript
function setMines() {
if (window.Cypress) {
gameState.minesLocation = [ "0-0", "0-1", "0-2", "0-3", "0-4", "0-5", "0-6", "0-7", "1-0", "1-1" ];
return;
}
// ... geração aleatória padrão
}
```
* **Solução proposta (Refatoração Futura):** Utilizar Injeção de Dependência. A função que gera as minas deve receber uma *seed* ou um array mapeado por parâmetro. O ambiente de teste passa o array fixo, enquanto a produção passa o gerador aleatório, removendo o `if` da lógica principal.

### Smell 3: Código Duplicado (DRY Violation)
* **Problema identificado:** A checagem das 8 células adjacentes é feita manualmente linha por linha, e depois repetida integralmente no bloco `else` (na chamada recursiva de `checkMine`). Isso gera alta complexidade cognitiva e facilita erros lógicos caso a regra de vizinhança precise mudar.
* **Como está (O problema estrutural):**
```javascript
let minesFound = 0;
minesFound += checkTile(r-1, c-1);
minesFound += checkTile(r-1, c);
minesFound += checkTile(r-1, c+1);
minesFound += checkTile(r, c-1);
// ... repetido para as 8 direções ...
```
* **Nossa proposta de refatoração:** Substituir as chamadas repetitivas por uma iteração sobre um array de vetores de direção iterando com um laço `for`.
```javascript
let minesFound = 0;
const directions = [[-1,-1], [-1,0], [-1,1], [0,-1], [0,1], [1,-1], [1,0], [1,1]];

for (let [dr, dc] of directions) {
minesFound += checkTile(r + dr, c + dc);
}
```

---

## 2. Padrões de Projeto Aplicados/Sugeridos

### Padrão 1: Facade Pattern (Padrão Estrutural)
* **Justificativa:** No código original, a inicialização misturava manipulação direta do DOM com a lógica de criação de matriz no mesmo bloco. A criação da função `startGame()` serviu como uma Fachada (Facade), escondendo do cliente a complexidade de como a UI e a estrutura de dados são montadas passo a passo.
* **Como estava :**
```javascript
function startGame() {
document.getElementById("mines-count").innerText = minesCount;
document.getElementById("flag-button").addEventListener("click", setFlag);
setMines();
for (let r = 0; r < rows; r++) {
// ... lógica complexa misturando array e document.createElement
}
}
```
* ** Alterações feitas :**
```javascript
function startGame() {
initializeUI();
initializeBoard();
// A complexidade foi abstraída para dentro destas funções auxiliares
}

// Onde initializeBoard() agora delega corretamente as ações:
function initializeBoard() {
setMines();
createBoardTiles();
}
```

### Padrão 2: State Pattern (Padrão Comportamental)
* **Justificativa:** O comportamento das funções mudava baseado em múltiplas variáveis soltas (`flagEnabled`, `gameOver`). Ao criar o objeto `gameState`, preparamos o terreno para o padrão State, centralizando as transições de estado do jogo. Sugere-se evoluir essa estrutura para classes ou manipuladores de estado distintos (ex: `PlayingState`, `FinishedState`), eliminando as longas cadeias de `if/else`.
* **Como estava (Antes):**
```javascript
function clickTile() {
// Checagem dependente de variáveis globais soltas
if (gameOver || this.classList.contains("tile-clicked")) {
return;
}
if (flagEnabled) { ... }
}
```
* **Nossas alterações (Depois - Agrupamento de Estado):**
```javascript
function clickTile() {
// O fluxo agora consulta uma única fonte de verdade de estado
if (gameState.gameOver || this.classList.contains("tile-clicked")) {
return;
}
if (gameState.flagEnabled) { ... }
}
```

---

## 3. Sugestões de Melhorias Arquiteturais Adicionais

Como parte da análise evolutiva, sugerimos as seguintes refatorações para diminuir a dívida técnica:

* **Implementação do Observer Pattern:** Atualmente, a função `checkMine` altera variáveis internas (Regras de Negócio) e manipula o DOM (View) simultaneamente adicionando classes CSS (`.classList.add`). O padrão *Observer* desacoplaria isso: a lógica apenas atualizaria o `gameState`, disparando um evento para que funções puramente de UI (View) reajam e pintem a tela, facilitando testes unitários sem mockar o navegador.
* **Extração de Configuração (Magic Numbers):** Valores literais como `8` (linhas), `8` (colunas) e `10` (minas) estão "chumbados" em constantes isoladas. Eles deveriam ser movidos para um objeto de configuração centralizado (`GAME_CONFIG = { easy: {...}, medium: {...} }`). O uso de um **Factory Method** consumiria esse objeto para construir tabuleiros de diferentes dificuldades dinamicamente.
1 change: 1 addition & 0 deletions Games/Minesweeper/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -18,5 +18,6 @@ <h1>Mines: <span id="mines-count">0</span></h1>
<div id="board"></div>
<br>
<button id="flag-button">🚩</button>
<button id="restart-button">Restart</button>
</body>
</html>
Loading
Loading