- Unit (12+9): conversion.js (rawToCooked/cookedToRaw, edge case, inversa) e storage.js (save/load, round-trip, default fallback) - Integration (17+12+14): Converter (ricerca, selezione, calcolo, swap, reset), MealPlanner (rendering, add/remove, generateShopping, deduplicazione), ShoppingList (add, toggle, remove, clearAll, contatore) - E2E Playwright (6+6+7+10): navigation, meal-planner, converter, shopping-list - Configurazione: vitest.config.js + playwright.config.js + tests/setup.js - Script: test, test:coverage, test:e2e, test:e2e:ui Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
153 lines
5.0 KiB
JavaScript
153 lines
5.0 KiB
JavaScript
import { describe, it, expect } from 'vitest'
|
|
import { mount } from '@vue/test-utils'
|
|
import { nextTick } from 'vue'
|
|
import MealPlanner from '../../src/pages/MealPlanner.vue'
|
|
import { load } from '../../src/utils/storage.js'
|
|
|
|
// Stub di MealCard: espone props e può emettere add/remove
|
|
const MealCardStub = {
|
|
name: 'MealCard',
|
|
template: '<div class="meal-card-stub" />',
|
|
props: ['dayName', 'meals', 'defaultOpen'],
|
|
emits: ['add', 'remove'],
|
|
}
|
|
|
|
function mountPlanner() {
|
|
return mount(MealPlanner, {
|
|
global: { stubs: { MealCard: MealCardStub } },
|
|
})
|
|
}
|
|
|
|
// Pre-popola il localStorage con un piano pasti completo
|
|
function seedMeals(overrides = {}) {
|
|
const base = Object.fromEntries(
|
|
['lunedi', 'martedi', 'mercoledi', 'giovedi', 'venerdi', 'sabato', 'domenica'].map(
|
|
d => [d, { colazione: [], pranzo: [], cena: [] }]
|
|
)
|
|
)
|
|
const merged = { ...base, ...overrides }
|
|
localStorage.setItem('meals', JSON.stringify(merged))
|
|
return merged
|
|
}
|
|
|
|
describe('MealPlanner — rendering', () => {
|
|
it('rende 7 card giornaliere', () => {
|
|
const w = mountPlanner()
|
|
expect(w.findAll('.meal-card-stub')).toHaveLength(7)
|
|
})
|
|
|
|
it('mostra il pulsante "Genera lista della spesa"', () => {
|
|
const w = mountPlanner()
|
|
expect(w.find('.btn-generate').exists()).toBe(true)
|
|
})
|
|
|
|
it('mostra la data corrente nel sottotitolo', () => {
|
|
const w = mountPlanner()
|
|
expect(w.find('.page-subtitle').text()).toMatch(/oggi/i)
|
|
})
|
|
})
|
|
|
|
describe('MealPlanner — aggiunta e rimozione voci', () => {
|
|
it('aggiunge un alimento al pranzo di lunedì', async () => {
|
|
const w = mountPlanner()
|
|
const cards = w.findAllComponents(MealCardStub)
|
|
// lunedì è il primo giorno (indice 0)
|
|
await cards[0].vm.$emit('add', 'pranzo', 'pasta')
|
|
await nextTick()
|
|
|
|
const saved = load('meals', {})
|
|
expect(saved.lunedi.pranzo).toContain('pasta')
|
|
})
|
|
|
|
it('non aggiunge stringhe vuote', async () => {
|
|
// Seed necessario: il watcher non scatta se nulla cambia,
|
|
// quindi localStorage rimarrebbe vuoto e load() returnerebbe {}
|
|
seedMeals()
|
|
const w = mountPlanner()
|
|
const cards = w.findAllComponents(MealCardStub)
|
|
await cards[0].vm.$emit('add', 'pranzo', ' ')
|
|
await nextTick()
|
|
|
|
const saved = load('meals', {})
|
|
expect(saved.lunedi.pranzo).toHaveLength(0)
|
|
})
|
|
|
|
it('rimuove un alimento tramite indice', async () => {
|
|
seedMeals({ lunedi: { colazione: [], pranzo: ['pasta', 'insalata'], cena: [] } })
|
|
const w = mountPlanner()
|
|
const cards = w.findAllComponents(MealCardStub)
|
|
await cards[0].vm.$emit('remove', 'pranzo', 0) // rimuovi "pasta"
|
|
await nextTick()
|
|
|
|
const saved = load('meals', {})
|
|
expect(saved.lunedi.pranzo).toEqual(['insalata'])
|
|
})
|
|
|
|
it('persiste le modifiche in localStorage', async () => {
|
|
const w = mountPlanner()
|
|
const cards = w.findAllComponents(MealCardStub)
|
|
await cards[1].vm.$emit('add', 'cena', 'pollo')
|
|
await nextTick()
|
|
|
|
expect(load('meals', {}).martedi.cena).toContain('pollo')
|
|
})
|
|
})
|
|
|
|
describe('MealPlanner — genera lista della spesa', () => {
|
|
it('emette go-shop al click del pulsante', async () => {
|
|
const w = mountPlanner()
|
|
await w.find('.btn-generate').trigger('click')
|
|
expect(w.emitted('go-shop')).toBeTruthy()
|
|
})
|
|
|
|
it('salva gli alimenti del piano in localStorage come spesa', async () => {
|
|
seedMeals({
|
|
lunedi: { colazione: ['caffè'], pranzo: ['pasta'], cena: ['pollo'] },
|
|
martedi: { colazione: [], pranzo: ['riso'], cena: [] },
|
|
})
|
|
const w = mountPlanner()
|
|
await w.find('.btn-generate').trigger('click')
|
|
|
|
const shopping = load('shopping', [])
|
|
const names = shopping.map(i => i.name)
|
|
expect(names).toContain('caffè')
|
|
expect(names).toContain('pasta')
|
|
expect(names).toContain('pollo')
|
|
expect(names).toContain('riso')
|
|
})
|
|
|
|
it('non aggiunge duplicati rispetto agli elementi già in lista', async () => {
|
|
seedMeals({ lunedi: { colazione: [], pranzo: ['pasta'], cena: [] } })
|
|
// Pasta già presente nella lista della spesa
|
|
localStorage.setItem('shopping', JSON.stringify([
|
|
{ id: 1, name: 'pasta', checked: false },
|
|
]))
|
|
|
|
const w = mountPlanner()
|
|
await w.find('.btn-generate').trigger('click')
|
|
|
|
const shopping = load('shopping', [])
|
|
const pastaCount = shopping.filter(i => i.name.toLowerCase() === 'pasta').length
|
|
expect(pastaCount).toBe(1)
|
|
})
|
|
|
|
it('non aggiunge duplicati tra i giorni del piano', async () => {
|
|
seedMeals({
|
|
lunedi: { colazione: [], pranzo: ['pasta'], cena: [] },
|
|
martedi: { colazione: [], pranzo: ['pasta'], cena: [] }, // stesso alimento
|
|
})
|
|
const w = mountPlanner()
|
|
await w.find('.btn-generate').trigger('click')
|
|
|
|
const shopping = load('shopping', [])
|
|
const pastaCount = shopping.filter(i => i.name.toLowerCase() === 'pasta').length
|
|
expect(pastaCount).toBe(1)
|
|
})
|
|
|
|
it('non aggiunge nulla se il piano è vuoto', async () => {
|
|
const w = mountPlanner()
|
|
await w.find('.btn-generate').trigger('click')
|
|
expect(load('shopping', [])).toHaveLength(0)
|
|
})
|
|
})
|