Files
biteplan/tests/integration/Converter.test.js
Davide Grilli e10bb2df4c test: aggiungi suite completa unit, integration ed e2e
- 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>
2026-03-27 14:45:31 +01:00

174 lines
6.2 KiB
JavaScript

import { describe, it, expect } from 'vitest'
import { mount } from '@vue/test-utils'
import Converter from '../../src/pages/Converter.vue'
function mountConverter() {
return mount(Converter, { attachTo: document.body })
}
describe('Converter — stato iniziale', () => {
it('mostra il messaggio hint quando non c\'è nessuna ricerca', () => {
const w = mountConverter()
expect(w.find('.hint-state').exists()).toBe(true)
expect(w.find('.converter-card').exists()).toBe(false)
})
it('non mostra risultati senza input', () => {
const w = mountConverter()
expect(w.findAll('.result-item')).toHaveLength(0)
})
})
describe('Converter — ricerca', () => {
it('mostra risultati corrispondenti alla query', async () => {
const w = mountConverter()
const input = w.find('input[type="text"]')
await input.setValue('riso')
await input.trigger('input')
expect(w.findAll('.result-item').length).toBeGreaterThan(0)
})
it('ogni risultato ha nome alimento e metodo di cottura', async () => {
const w = mountConverter()
await w.find('input[type="text"]').setValue('pollo')
await w.find('input[type="text"]').trigger('input')
const first = w.find('.result-item')
expect(first.find('.result-food').text()).toBeTruthy()
expect(first.find('.result-method').text()).toBeTruthy()
})
it('nasconde l\'hint state durante la ricerca', async () => {
const w = mountConverter()
await w.find('input[type="text"]').setValue('pasta')
await w.find('input[type="text"]').trigger('input')
expect(w.find('.hint-state').exists()).toBe(false)
})
it('svuota i risultati per query vuota', async () => {
const w = mountConverter()
const input = w.find('input[type="text"]')
await input.setValue('riso')
await input.trigger('input')
await input.setValue('')
await input.trigger('input')
expect(w.findAll('.result-item')).toHaveLength(0)
expect(w.find('.hint-state').exists()).toBe(true)
})
it('i nomi degli alimenti hanno solo l\'iniziale maiuscola', async () => {
const w = mountConverter()
await w.find('input[type="text"]').setValue('riso')
await w.find('input[type="text"]').trigger('input')
const foods = w.findAll('.result-food').map(el => el.text())
// Nessun testo deve avere lettere maiuscole dopo la prima (test multi-word)
foods.forEach(f => {
if (f.includes(' ')) {
// "Riso basmati" → la seconda parola non deve essere capitalizzata
const words = f.split(' ')
words.slice(1).forEach(w => expect(w[0]).toBe(w[0].toLowerCase()))
}
})
})
})
describe('Converter — selezione alimento', () => {
async function selectFirstResult(w, query = 'riso') {
await w.find('input[type="text"]').setValue(query)
await w.find('input[type="text"]').trigger('input')
await w.find('.result-item').trigger('click')
}
it('mostra la converter card dopo la selezione', async () => {
const w = mountConverter()
await selectFirstResult(w)
expect(w.find('.converter-card').exists()).toBe(true)
})
it('nasconde i risultati dopo la selezione', async () => {
const w = mountConverter()
await selectFirstResult(w)
expect(w.findAll('.result-item')).toHaveLength(0)
})
it('mostra nome alimento e metodo nella card', async () => {
const w = mountConverter()
await selectFirstResult(w)
expect(w.find('.food-name').text()).toBeTruthy()
expect(w.find('.food-method').text()).toBeTruthy()
})
it('disabilita il campo di ricerca dopo la selezione', async () => {
const w = mountConverter()
await selectFirstResult(w)
expect(w.find('input[type="text"]').attributes('disabled')).toBeDefined()
})
})
describe('Converter — calcolo', () => {
async function mountWithSelection(query = 'riso basmati') {
const w = mountConverter()
await w.find('input[type="text"]').setValue(query)
await w.find('input[type="text"]').trigger('input')
await w.find('.result-item').trigger('click')
return w
}
it('mostra il risultato dopo aver inserito i grammi', async () => {
const w = await mountWithSelection()
await w.find('.calc-input').setValue('100')
await w.find('.calc-input').trigger('input')
expect(w.find('.output-value').exists()).toBe(true)
})
it('non mostra risultato per input 0', async () => {
const w = await mountWithSelection()
await w.find('.calc-input').setValue('0')
await w.find('.calc-input').trigger('input')
expect(w.find('.output-value').exists()).toBe(false)
})
it('mostra il footer con fattore di resa quando c\'è un risultato', async () => {
const w = await mountWithSelection()
await w.find('.calc-input').setValue('100')
await w.find('.calc-input').trigger('input')
expect(w.find('.card-footer').exists()).toBe(true)
expect(w.find('.card-footer').text()).toContain('fattore di resa')
})
})
describe('Converter — swap direzione', () => {
it('il pulsante swap inverte le label crudo/cotto', async () => {
const w = mountConverter()
await w.find('input[type="text"]').setValue('pasta')
await w.find('input[type="text"]').trigger('input')
await w.find('.result-item').trigger('click')
expect(w.find('.calc-label').text()).toMatch(/crudo/i)
await w.find('.btn-swap').trigger('click')
expect(w.find('.calc-label').text()).toMatch(/cotto/i)
})
it('swap azzera il campo grammi', async () => {
const w = mountConverter()
await w.find('input[type="text"]').setValue('pasta')
await w.find('input[type="text"]').trigger('input')
await w.find('.result-item').trigger('click')
await w.find('.calc-input').setValue('200')
await w.find('.btn-swap').trigger('click')
// Dopo lo swap il campo deve essere vuoto (grams = null)
expect(w.find('.calc-input').element.value).toBe('')
})
})
describe('Converter — reset', () => {
it('il pulsante Cambia riporta allo stato di ricerca', async () => {
const w = mountConverter()
await w.find('input[type="text"]').setValue('riso')
await w.find('input[type="text"]').trigger('input')
await w.find('.result-item').trigger('click')
await w.find('.btn-reset').trigger('click')
expect(w.find('.converter-card').exists()).toBe(false)
expect(w.find('input[type="text"]').attributes('disabled')).toBeUndefined()
})
})