# Aula 2.8 - Armazenamento, testes e projeto final

## Identificação

- **Duração:** 2 horas
- **Tipo:** oficina integradora
- **Entrega:** painel de tarefas final do Módulo 2
- **Laboratório:** [`../exemplos/aula-2.8/index.html`](../exemplos/aula-2.8/index.html)

## Introdução

### O que é armazenamento no navegador?

O navegador oferece mecanismos para preservar pequenas informações entre
interações ou visitas. Um deles é `localStorage`:

```js
localStorage.setItem("course-theme", "dark");

const theme = localStorage.getItem("course-theme");
```

Ele guarda pares de chave e valor como strings para uma origem específica.

### `localStorage` é banco de dados?

Não. `localStorage`:

- armazena apenas strings;
- possui espaço limitado;
- não oferece consultas relacionais;
- não possui transações como um banco relacional;
- é acessível pelo JavaScript da página;
- pode ser apagado pelo usuário;
- não sincroniza automaticamente entre dispositivos.

No curso, dados de negócio serão persistidos no MySQL pela API. Nesta aula,
`localStorage` guardará apenas preferências não sensíveis, como tema, filtro e
ordenação.

### Posso guardar senha ou token em `localStorage`?

Não é uma escolha segura para segredos. Qualquer JavaScript executado na mesma
origem pode acessar o conteúdo. Uma vulnerabilidade XSS pode expor os valores.

Não armazene:

- senha;
- chave de API;
- dados financeiros;
- informações pessoais sensíveis;
- token de autenticação apenas por conveniência.

A estratégia de autenticação será definida posteriormente no back-end, considerando
cookies seguros, duração, CSRF e modelo de ameaça.

### `localStorage`, `sessionStorage`, cookie e IndexedDB são iguais?

Não:

- `localStorage`: persiste até ser removido;
- `sessionStorage`: normalmente dura enquanto a aba estiver aberta;
- cookie: pode ser enviado automaticamente ao servidor e possui atributos de
  segurança;
- IndexedDB: banco de dados no navegador para dados estruturados e maiores.

Escolha pela necessidade, não pela familiaridade.

### O que é um teste automatizado?

Um teste automatizado executa uma regra, compara o resultado real ao esperado e
informa sucesso ou falha:

```js
const result = classifyPriority(true, true);

if (result !== "critical") {
  throw new Error("Esperava prioridade critical");
}
```

Ele pode ser repetido após cada alteração.

### Teste automatizado substitui teste manual?

Não. Eles se complementam:

- testes unitários verificam regras isoladas;
- testes de integração verificam partes trabalhando juntas;
- testes ponta a ponta verificam fluxos pela interface;
- testes manuais ajudam a explorar usabilidade e situações não previstas.

### Onde isso aparece no projeto?

O painel final reunirá:

- carregamento assíncrono de tarefas;
- filtros e ordenação com arrays;
- métricas;
- criação de tarefas;
- preferências locais;
- regras puras;
- testes executados no navegador;
- documentação para entrega.

## Objetivos

Ao final da aula, o aluno deverá conseguir:

1. explicar o que `localStorage` faz e o que não faz;
2. diferenciar mecanismos de armazenamento no navegador;
3. salvar apenas preferências não sensíveis;
4. serializar e validar preferências;
5. explicar teste unitário, integração e ponta a ponta;
6. estruturar testes com Arrange, Act e Assert;
7. testar casos normais, limites e erros;
8. manter regras puras separadas do DOM;
9. integrar os conteúdos do Módulo 2;
10. documentar e apresentar o projeto.

## Pré-requisitos

- Aulas 2.1 a 2.7 concluídas;
- navegador, DevTools e servidor local;
- domínio básico de módulos, JSON, arrays e Fetch.

## Pergunta orientadora

> Como concluir uma aplicação previsível, preservar somente preferências adequadas e provar que suas regras continuam funcionando?

## Roteiro sugerido

| Etapa | Duração |
|---|---:|
| Armazenamento no navegador | 25 min |
| Fundamentos de testes | 30 min |
| Integração do painel | 25 min |
| Testes e correções | 20 min |
| Documentação e apresentação | 15 min |
| Encerramento | 5 min |

## 1. Origem e persistência

Armazenamento web é separado por origem, formada por protocolo, host e porta:

```text
http://localhost
https://localhost
http://localhost:8080
```

Essas origens são diferentes. Uma página não acessa livremente o armazenamento de
outra origem.

## 2. API do `localStorage`

```js
localStorage.setItem("key", "value");
localStorage.getItem("key");
localStorage.removeItem("key");
localStorage.clear();
```

`clear()` apaga todas as chaves daquela origem e pode remover dados de outras partes
do site. Prefira remover as chaves pertencentes à aplicação.

## 3. Valores são strings

```js
localStorage.setItem("compact", true);

const value = localStorage.getItem("compact");
typeof value; // "string"
```

Para objetos:

```js
const preferences = {
  theme: "dark",
  filter: "open",
};

localStorage.setItem(
  "task-panel:preferences",
  JSON.stringify(preferences),
);
```

Leitura:

```js
const text = localStorage.getItem("task-panel:preferences");
const preferences = text
  ? JSON.parse(text)
  : defaultPreferences;
```

## 4. Validar preferências

O conteúdo pode estar ausente, corrompido ou ter sido alterado:

```js
function isValidPreferences(value) {
  return (
    typeof value === "object" &&
    value !== null &&
    ["light", "dark"].includes(value.theme) &&
    ["all", "open", "done"].includes(value.filter)
  );
}
```

Se for inválido, volte ao padrão.

## 5. Tratar falhas de armazenamento

O acesso pode falhar por política, limite ou modo do navegador:

```js
try {
  localStorage.setItem(key, value);
} catch {
  showMessage(
    "A preferência será usada apenas nesta sessão.",
  );
}
```

A funcionalidade principal não deve depender silenciosamente de uma preferência.

## 6. Nomes de chave

Evite chaves genéricas:

```js
localStorage.setItem("theme", "dark");
```

Use namespace:

```js
localStorage.setItem(
  "posia:task-panel:preferences:v1",
  json,
);
```

O sufixo de versão ajuda futuras migrações.

## 7. O que não armazenar

Mesmo que um valor caiba, não significa que deve ser salvo. Pergunte:

1. é sensível?
2. pertence ao servidor?
3. precisa sincronizar?
4. pode ser apagado?
5. existe requisito legal de retenção?
6. um script injetado poderia expô-lo?

No laboratório, tarefas permanecem em memória. Somente a preferência visual é
persistida.

## 8. O que é testar?

Testar é comparar comportamento observado com expectativa definida.

Teste manual:

```text
1. marcar urgente e importante;
2. executar classificação;
3. confirmar prioridade crítica.
```

Teste automatizado:

```js
test("classifica urgente e importante", () => {
  expect(
    classifyPriority(true, true),
  ).toBe("critical");
});
```

## 9. Pirâmide de testes

Uma estratégia comum possui:

- muitos testes unitários rápidos;
- alguns testes de integração;
- poucos testes ponta a ponta essenciais.

Não é uma regra matemática. O equilíbrio depende do risco, arquitetura e custo de
manutenção.

## 10. Teste unitário

Verifica uma unidade pequena e isolada:

```js
const result = normalizeTitle("  Estudar   testes ");
assertEqual(result, "Estudar testes");
```

Funções puras são boas candidatas porque não dependem do DOM, rede ou relógio.

## 11. Teste de integração

Verifica componentes juntos:

```text
formulário → regra de criação → lista renderizada
```

Pode revelar contratos incompatíveis que testes unitários isolados não encontram.

## 12. Teste ponta a ponta

Simula o fluxo próximo ao usuário:

```text
abrir página → preencher tarefa → enviar → confirmar cartão
```

É mais abrangente, mas normalmente mais lento e sensível ao ambiente.

## 13. Arrange, Act, Assert

Organize o teste:

```js
// Arrange
const input = "  Estudar testes  ";

// Act
const result = normalizeTitle(input);

// Assert
assertEqual(result, "Estudar testes");
```

Essa separação torna intenção e falha mais fáceis de compreender.

## 14. Nome do teste

Um bom nome descreve comportamento:

```text
retorna critical quando a tarefa é urgente e importante
```

Evite:

```text
teste 1
funciona
classifyPriority
```

## 15. Casos normais, limites e inválidos

Para `validateTitle`:

- título comum;
- exatamente 3 caracteres;
- 2 caracteres;
- string vazia;
- somente espaços;
- tipo inesperado, se fizer parte do contrato público.

Limites frequentemente revelam erros como `>` no lugar de `>=`.

## 16. Uma expectativa principal

Um teste pode conter mais de uma verificação, mas deve ter um motivo claro para
falhar. Se o teste valida comportamentos independentes, divida-o.

## 17. Testes não devem depender de ordem

Cada teste deve preparar seu próprio estado. Um teste não deveria funcionar apenas
porque outro executou antes.

Evite estado global mutável entre testes.

## 18. Testar implementação ou comportamento?

Prefira testar resultados observáveis:

```js
assertEqual(
  classifyPriority(true, false),
  "high",
);
```

Evite testes que quebrem somente porque a implementação interna foi reorganizada,
sem mudança de comportamento.

## 19. Cobertura

Cobertura mede quais partes do código foram executadas pelos testes. Ela não garante
qualidade das expectativas.

Um teste pode executar uma linha sem verificar o resultado correto. Use cobertura
como indicador, não como objetivo isolado.

## 20. Test runner do laboratório

O laboratório possui um executor pequeno para fins didáticos:

```js
test("normaliza espaços", () => {
  assertEqual(
    normalizeTitle(" A   B "),
    "A B",
  );
});
```

Projetos profissionais utilizarão ferramentas como Vitest, Jest ou o executor
nativo apropriado ao ambiente. Nosso executor mostra os princípios sem exigir
instalação.

## 21. Arquitetura do projeto final

```text
assets/
  api.js
  main.js
  storage.js
  task-rules.js
  tests.js
  styles.css
data/
  tasks.json
```

Responsabilidades:

- `api.js`: carregar dados;
- `storage.js`: preferências;
- `task-rules.js`: regras puras;
- `tests.js`: testes;
- `main.js`: coordenar DOM e estado.

## 22. Fluxo do painel

```text
Fetch → tarefas em memória
               ↓
          regras puras
               ↓
 filtro → ordenação → métricas → DOM
               ↑
     preferência local não sensível
```

## 23. Importação e exportação

Exportar:

```js
const json = JSON.stringify(tasks, null, 2);
```

Importar exige:

1. interpretar com `JSON.parse`;
2. confirmar que é array;
3. validar cada tarefa;
4. rejeitar a operação inteira ou informar itens inválidos;
5. atualizar o estado apenas após sucesso.

Não execute JSON e não insira texto com `innerHTML`.

## 24. Documentação da entrega

O README deve incluir:

- objetivo;
- como abrir;
- arquitetura;
- funcionalidades;
- decisões de armazenamento;
- como executar os testes;
- limitações;
- próximos passos.

Documentação registra decisões que não são evidentes no código.

## 25. Apresentação

Roteiro de 5 minutos:

1. problema e público;
2. demonstração do fluxo principal;
3. arquitetura modular;
4. decisão de não armazenar tarefas no navegador;
5. testes;
6. limitações e evolução com API/MySQL.

Evite narrar cada arquivo. Mostre decisões e resultados.

## 26. Laboratório guiado

Abra o [painel final](../exemplos/aula-2.8/index.html).

### Etapa 1 — Carregue os dados

O painel busca tarefas locais por Fetch e representa carregamento, sucesso e erro.

### Etapa 2 — Transforme

Altere filtro e ordenação. A preferência será persistida, mas as tarefas continuarão
somente em memória.

### Etapa 3 — Crie uma tarefa

Envie título, prioridade e estimativa. Observe normalização, validação, métricas e
renderização segura.

### Etapa 4 — Execute testes

Abra a seção de qualidade e clique em **Executar testes**. Analise cada resultado.

### Etapa 5 — Reinicie preferências

Remova somente a chave do painel. Não use `localStorage.clear()`.

## 27. Erros comuns

### Salvar tudo no navegador

Preferência local não substitui persistência de negócio no servidor.

### Não validar o JSON armazenado

Dados locais também podem estar ausentes ou corrompidos.

### Guardar segredo

Não armazene senha, chave ou dados sensíveis no front-end.

### Testar somente caminho feliz

Inclua limites, vazio e erro.

### Escrever teste depois e adaptar a expectativa ao resultado

A expectativa deve vir do requisito, não do comportamento acidental.

### Misturar DOM e regra

Regras acopladas à página ficam difíceis de testar.

## 28. Boas práticas

- persista somente o necessário;
- use chave com namespace e versão;
- trate falha de armazenamento;
- mantenha regra pura;
- dê nomes comportamentais aos testes;
- isole o estado de cada teste;
- teste limites e falhas;
- preserve acessibilidade;
- documente decisões de segurança;
- apresente limitações honestamente.

## 29. Exercícios

### Exercício 1 — Preferências

Salve tema e filtro em uma chave versionada.

### Exercício 2 — Validação

Corrompa o JSON salvo e confirme o retorno ao padrão.

### Exercício 3 — Teste de limite

Teste título com 2 e com 3 caracteres.

### Exercício 4 — Matriz de prioridade

Teste as quatro combinações de urgência e importância.

### Exercício 5 — Integração

Crie uma tarefa pela interface e confirme lista e métrica.

## 30. Desafio

Amplie o projeto:

- importação e exportação de tarefas;
- testes para JSON inválido;
- estado de API indisponível;
- tema escuro persistido;
- relatório de testes;
- documentação de ameaças;
- preparação para substituir a fonte local por uma API NestJS e MySQL.

## 31. Lista de verificação de conclusão

- [ ] Sei explicar por que `localStorage` não é banco de dados.
- [ ] Não armazeno informações sensíveis.
- [ ] Valido preferências carregadas.
- [ ] Sei diferenciar tipos de teste.
- [ ] Uso Arrange, Act e Assert.
- [ ] Testei limites e falhas.
- [ ] Regras estão separadas do DOM.
- [ ] O painel representa estados assíncronos.
- [ ] A documentação está atualizada.
- [ ] Consigo apresentar decisões e limitações.

## 32. Critérios de avaliação

| Critério | Pontos |
|---|---:|
| Integração das regras e dados | 20 |
| Filtros, métricas e interface | 15 |
| Preferências locais seguras | 15 |
| Tratamento assíncrono | 15 |
| Testes das regras essenciais | 20 |
| Acessibilidade | 5 |
| Documentação e apresentação | 10 |
| **Total** | **100** |

## Resumo

Nesta aula, aprendemos que:

- armazenamento no navegador não substitui banco de dados;
- somente preferências não sensíveis devem ser persistidas neste projeto;
- testes automatizados tornam expectativas repetíveis;
- testes unitários, integração e ponta a ponta possuem escopos diferentes;
- regras puras facilitam testes;
- uma entrega inclui código, estados, segurança, documentação e apresentação.

## Encerramento do módulo

O Módulo 2 está concluído. No próximo módulo, iniciaremos TypeScript para adicionar
contratos estáticos às estruturas e funções JavaScript construídas até aqui.
