Compare commits
2 Commits
fix/multi-
...
feat/searc
| Author | SHA1 | Date | |
|---|---|---|---|
| 8f004a02cd | |||
| f34dd49dcb |
@@ -26,6 +26,14 @@ describe('Recipe Search', () => {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('searching by oil name returns recipes containing that oil', () => {
|
||||||
|
cy.get('input[placeholder*="搜索"]').type('薰衣草')
|
||||||
|
cy.wait(500)
|
||||||
|
cy.get('.search-results-section, .recipe-card', { timeout: 5000 }).should('exist')
|
||||||
|
// At least one result card should exist (any recipe using 薰衣草)
|
||||||
|
cy.get('.recipe-card').should('have.length.gte', 1)
|
||||||
|
})
|
||||||
|
|
||||||
it('clears search and restores all recipes', () => {
|
it('clears search and restores all recipes', () => {
|
||||||
cy.get('.recipe-card', { timeout: 10000 }).should('have.length.gte', 1)
|
cy.get('.recipe-card', { timeout: 10000 }).should('have.length.gte', 1)
|
||||||
cy.get('input[placeholder*="搜索"]').type('薰衣草')
|
cy.get('input[placeholder*="搜索"]').type('薰衣草')
|
||||||
|
|||||||
57
frontend/src/__tests__/recipeSearchByOil.test.js
Normal file
57
frontend/src/__tests__/recipeSearchByOil.test.js
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
import { describe, it, expect } from 'vitest'
|
||||||
|
|
||||||
|
// Mirrors the exactResults matching rule in RecipeSearch.vue
|
||||||
|
function matches(recipes, q, oilEn = (s) => '') {
|
||||||
|
const query = (q || '').trim().toLowerCase()
|
||||||
|
if (!query) return []
|
||||||
|
const isEn = /^[a-zA-Z\s]+$/.test(query)
|
||||||
|
return recipes.filter(r => {
|
||||||
|
if (r.tags && r.tags.includes('已下架')) return false
|
||||||
|
const nameMatch = r.name.toLowerCase().includes(query)
|
||||||
|
const enNameMatch = isEn && (r.en_name || '').toLowerCase().includes(query)
|
||||||
|
const oilEnMatch = isEn && (r.ingredients || []).some(ing => (oilEn(ing.oil) || '').toLowerCase().includes(query))
|
||||||
|
const oilZhMatch = query.length >= 2 && (r.ingredients || []).some(ing => ing.oil.toLowerCase().includes(query))
|
||||||
|
const tagMatch = (r.tags || []).some(t => t.toLowerCase().includes(query))
|
||||||
|
return nameMatch || enNameMatch || oilEnMatch || oilZhMatch || tagMatch
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const recipes = [
|
||||||
|
{ name: '助眠晚安', tags: [], ingredients: [{ oil: '薰衣草', drops: 3 }, { oil: '乳香', drops: 2 }] },
|
||||||
|
{ name: '提神醒脑', tags: [], ingredients: [{ oil: '椒样薄荷', drops: 2 }, { oil: '柠檬', drops: 3 }] },
|
||||||
|
{ name: '肩颈舒缓', tags: ['舒缓'], ingredients: [{ oil: '西班牙牛至', drops: 1 }, { oil: '椰子油', drops: 10 }] },
|
||||||
|
{ name: '感冒护理', tags: [], ingredients: [{ oil: '牛至呵护', drops: 2 }] },
|
||||||
|
{ name: '下架配方', tags: ['已下架'], ingredients: [{ oil: '薰衣草', drops: 1 }] },
|
||||||
|
]
|
||||||
|
|
||||||
|
describe('Recipe search by oil name', () => {
|
||||||
|
it('finds recipes containing the oil (Chinese exact)', () => {
|
||||||
|
const r = matches(recipes, '薰衣草')
|
||||||
|
expect(r.map(x => x.name)).toEqual(['助眠晚安'])
|
||||||
|
})
|
||||||
|
|
||||||
|
it('finds multiple recipes for a common oil', () => {
|
||||||
|
expect(matches(recipes, '牛至').map(x => x.name).sort()).toEqual(['感冒护理', '肩颈舒缓'])
|
||||||
|
})
|
||||||
|
|
||||||
|
it('excludes 已下架 recipes', () => {
|
||||||
|
const r = matches(recipes, '薰衣草')
|
||||||
|
expect(r.some(x => x.name === '下架配方')).toBe(false)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('single-char query does not match oil names (avoids noise)', () => {
|
||||||
|
const r = matches(recipes, '草')
|
||||||
|
expect(r).toEqual([])
|
||||||
|
})
|
||||||
|
|
||||||
|
it('still matches recipe name for short queries', () => {
|
||||||
|
const r = matches(recipes, '晚')
|
||||||
|
expect(r.map(x => x.name)).toEqual(['助眠晚安'])
|
||||||
|
})
|
||||||
|
|
||||||
|
it('matches english oil name when query is english', () => {
|
||||||
|
const oilEn = (o) => ({ '薰衣草': 'Lavender', '乳香': 'Frankincense' }[o] || '')
|
||||||
|
const r = matches(recipes, 'Lavender', oilEn)
|
||||||
|
expect(r.map(x => x.name)).toEqual(['助眠晚安'])
|
||||||
|
})
|
||||||
|
})
|
||||||
@@ -312,7 +312,7 @@ function expandQuery(q) {
|
|||||||
return terms
|
return terms
|
||||||
}
|
}
|
||||||
|
|
||||||
// Search results: exact matches (query in recipe name or tags, NOT oil names to avoid noise like 西班牙牛至)
|
// Search results: matches in recipe name, tags, oil names (zh + en)
|
||||||
const exactResults = computed(() => {
|
const exactResults = computed(() => {
|
||||||
if (!searchQuery.value.trim()) return []
|
if (!searchQuery.value.trim()) return []
|
||||||
const q = searchQuery.value.trim().toLowerCase()
|
const q = searchQuery.value.trim().toLowerCase()
|
||||||
@@ -322,9 +322,10 @@ const exactResults = computed(() => {
|
|||||||
const nameMatch = r.name.toLowerCase().includes(q)
|
const nameMatch = r.name.toLowerCase().includes(q)
|
||||||
const enNameMatch = isEn && (r.en_name || '').toLowerCase().includes(q)
|
const enNameMatch = isEn && (r.en_name || '').toLowerCase().includes(q)
|
||||||
const oilEnMatch = isEn && r.ingredients.some(ing => (oilEn(ing.oil) || '').toLowerCase().includes(q))
|
const oilEnMatch = isEn && r.ingredients.some(ing => (oilEn(ing.oil) || '').toLowerCase().includes(q))
|
||||||
|
const oilZhMatch = q.length >= 2 && r.ingredients.some(ing => ing.oil.toLowerCase().includes(q))
|
||||||
const visibleTags = auth.canEdit ? (r.tags || []) : (r.tags || []).filter(t => !EDITOR_ONLY_TAGS.includes(t))
|
const visibleTags = auth.canEdit ? (r.tags || []) : (r.tags || []).filter(t => !EDITOR_ONLY_TAGS.includes(t))
|
||||||
const tagMatch = visibleTags.some(t => t.toLowerCase().includes(q))
|
const tagMatch = visibleTags.some(t => t.toLowerCase().includes(q))
|
||||||
return nameMatch || enNameMatch || oilEnMatch || tagMatch
|
return nameMatch || enNameMatch || oilEnMatch || oilZhMatch || tagMatch
|
||||||
}).sort((a, b) => a.name.localeCompare(b.name, 'zh'))
|
}).sort((a, b) => a.name.localeCompare(b.name, 'zh'))
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user