134 lines
5.3 KiB
TypeScript
134 lines
5.3 KiB
TypeScript
import type { RecipeItem } from '../apps/web/app/types'
|
|
import { createPinia, setActivePinia } from 'pinia'
|
|
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
|
import { useRecipeStore } from '../apps/web/app/composables/store/recipe'
|
|
import { db } from '../apps/web/app/utils/db'
|
|
|
|
const fixtures: RecipeItem[] = [
|
|
{ name: '土豆', stuff: ['土豆'], tools: ['一口大锅'] },
|
|
{ name: '腊肠饭', stuff: ['腊肠', '米'], tools: ['电饭煲'] },
|
|
{ name: '土豆腊肠饭', stuff: ['土豆', '腊肠', '米'], tools: ['电饭煲'] },
|
|
{ name: '盐煎蛋', stuff: ['鸡蛋', '盐'], tools: ['一口大锅'] },
|
|
]
|
|
|
|
beforeEach(async () => {
|
|
setActivePinia(createPinia())
|
|
await db.recipes.clear()
|
|
await db.recipes.bulkAdd(fixtures)
|
|
})
|
|
|
|
describe('recipe interaction', () => {
|
|
it('lists recipes for a tool before any ingredient is chosen, then switches to matches', async () => {
|
|
const store = useRecipeStore()
|
|
await db.recipes.add({ name: '缺三种的土豆杂烩', stuff: ['土豆', '米', '鸡蛋', '牛肉'], tools: ['电饭煲'] })
|
|
await store.loadRecipes()
|
|
expect(store.resultCount).toBe(0)
|
|
|
|
store.curTool = '电饭煲'
|
|
expect(store.toolRecipes.map(r => r.name)).toEqual(['腊肠饭', '土豆腊肠饭', '缺三种的土豆杂烩'])
|
|
expect(store.resultCount).toBe(3)
|
|
store.keyword = '土豆'
|
|
expect(store.toolRecipes.map(r => r.name)).toEqual(['土豆腊肠饭', '缺三种的土豆杂烩'])
|
|
|
|
store.keyword = ''
|
|
store.addStuff('土豆')
|
|
expect(store.toolRecipes).toEqual([])
|
|
// Recipes missing more than two ingredients are never recommended.
|
|
expect(store.recipeMatches.map(m => m.recipe.name)).toEqual(['土豆腊肠饭'])
|
|
expect(store.resultCount).toBe(1)
|
|
})
|
|
|
|
it('suggests the missing ingredient that completes the most recipes', async () => {
|
|
const store = useRecipeStore()
|
|
await db.recipes.bulkAdd([
|
|
{ name: '腊肠焖饭', stuff: ['腊肠', '米'], tools: ['电饭煲'] },
|
|
{ name: '土豆焖饭', stuff: ['土豆', '米'], tools: ['电饭煲'] },
|
|
])
|
|
await store.loadRecipes()
|
|
store.addStuff('土豆')
|
|
store.addStuff('腊肠')
|
|
// 腊肠饭、土豆腊肠饭、腊肠焖饭、土豆焖饭 all wait only for 米.
|
|
expect(store.unlockSuggestions).toEqual([{ name: '米', count: 4 }])
|
|
store.addStuff('米')
|
|
expect(store.unlockSuggestions).toEqual([])
|
|
})
|
|
|
|
it('clears ingredients, tool and filters together on reset', () => {
|
|
const store = useRecipeStore()
|
|
store.addStuff('土豆')
|
|
store.curTool = '电饭煲'
|
|
store.curMode = 'strict'
|
|
store.keyword = '腊肠'
|
|
store.reset()
|
|
expect(store.selectedStuff).toEqual([])
|
|
expect(store.curTool).toBe('')
|
|
expect(store.curMode).toBe('loose')
|
|
expect(store.keyword).toBe('')
|
|
})
|
|
|
|
it('normalizes selection, ignores duplicates and toggles canonical names', () => {
|
|
const store = useRecipeStore()
|
|
store.addStuff(' 西红柿 ')
|
|
store.addStuff('番茄')
|
|
store.addStuff(' ')
|
|
expect(store.selectedStuff).toEqual(['番茄'])
|
|
store.toggleStuff('西红柿')
|
|
expect(store.selectedStuff).toEqual([])
|
|
store.toggleTools('电饭煲')
|
|
expect(store.curTool).toBe('电饭煲')
|
|
store.toggleTools('电饭煲')
|
|
expect(store.curTool).toBe('')
|
|
})
|
|
|
|
it('loads actual results and reacts immediately to latest filters', async () => {
|
|
const store = useRecipeStore()
|
|
store.addStuff('土豆')
|
|
store.addStuff('腊肠')
|
|
await store.loadRecipes()
|
|
expect(store.recipeMatches.map(m => m.recipe.name)).toEqual(['土豆', '土豆腊肠饭', '腊肠饭'])
|
|
store.curTool = '电饭煲'
|
|
expect(store.recipeMatches.map(m => m.recipe.name)).toEqual(['土豆腊肠饭', '腊肠饭'])
|
|
store.setMode('strict')
|
|
expect(store.recipeMatches.map(m => m.recipe.name)).toEqual(['土豆腊肠饭'])
|
|
store.setMode('survival')
|
|
expect(store.recipeMatches).toEqual([])
|
|
store.addStuff('米')
|
|
expect(store.recipeMatches).toHaveLength(2)
|
|
store.keyword = '没有这道菜'
|
|
expect(store.recipeMatches).toEqual([])
|
|
store.clearFilters()
|
|
expect(store.selectedStuff).toEqual(['土豆', '腊肠', '米'])
|
|
expect(store.recipeMatches).toHaveLength(3)
|
|
})
|
|
|
|
it('updates missing ingredients when pantry changes and preserves pantry on basket reset', async () => {
|
|
const store = useRecipeStore()
|
|
store.addStuff('鸡蛋')
|
|
await store.loadRecipes()
|
|
expect(store.recipeMatches[0]?.missingStuff).toEqual([])
|
|
store.togglePantry('盐')
|
|
expect(store.recipeMatches[0]?.missingStuff).toEqual(['盐'])
|
|
store.togglePantry('蒜')
|
|
store.reset()
|
|
expect(store.recipeMatches).toEqual([])
|
|
expect(store.selectedPantry).toEqual(['油', '蒜'])
|
|
store.clearPantry()
|
|
expect(store.selectedPantry).toEqual([])
|
|
store.resetPantry()
|
|
expect(store.selectedPantry).toEqual(['油', '盐'])
|
|
})
|
|
|
|
it('shows load failure, ends loading and can retry', async () => {
|
|
const store = useRecipeStore()
|
|
const read = vi.spyOn(db.recipes, 'toArray').mockRejectedValueOnce(new Error('unavailable'))
|
|
await store.loadRecipes()
|
|
expect(store.loadError).toBeTruthy()
|
|
expect(store.isSearching).toBe(false)
|
|
store.addStuff('土豆')
|
|
await store.loadRecipes()
|
|
expect(store.loadError).toBe('')
|
|
expect(store.recipeMatches).toHaveLength(2)
|
|
read.mockRestore()
|
|
})
|
|
})
|