From e0ee1cba8c6a71ccebfe988e858c92b1a34b1ee5 Mon Sep 17 00:00:00 2001 From: Hera Zhao Date: Sun, 12 Apr 2026 14:07:09 +0000 Subject: [PATCH] =?UTF-8?q?test:=20=E8=A1=A5=E5=85=85recipeNameEn=E7=BF=BB?= =?UTF-8?q?=E8=AF=91=E3=80=81=E9=87=8D=E5=A4=8D=E7=B2=BE=E6=B2=B9=E3=80=81?= =?UTF-8?q?=E6=94=B9=E5=90=8D=E9=87=8D=E7=BF=BB=E8=AF=91=E3=80=81=E5=8E=BB?= =?UTF-8?q?=E9=87=8D=E8=B7=B3=E8=BF=87=E6=B5=8B=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 单元测试256个全部通过(新增14个): - recipeNameEn: 11个翻译测试 - 重复精油检查: 3个 后端: 补充"缓解"翻译词条 E2E: 改名重翻译、删除用户去重跳过 Co-Authored-By: Claude Opus 4.6 (1M context) --- backend/translate.py | 2 +- frontend/cypress/e2e/pr27-features.cy.js | 79 +++++++++++++++++++ frontend/src/__tests__/pr27Features.test.js | 86 +++++++++++++++++++++ 3 files changed, 166 insertions(+), 1 deletion(-) diff --git a/backend/translate.py b/backend/translate.py index a0fe5ed..c7f3fdc 100644 --- a/backend/translate.py +++ b/backend/translate.py @@ -51,7 +51,7 @@ _KEYWORDS = { '预防': 'Prevention', '改善': 'Improve', '祛湿': 'Dampness Relief', '驱寒': 'Warming', '化痰': 'Phlegm Relief', '健脾': 'Spleen Wellness', - '化湿': 'Dampness Clear', + '化湿': 'Dampness Clear', '缓解': 'Relief', # Beauty '美容': 'Beauty', '美发': 'Hair Care', '美体': 'Body Care', diff --git a/frontend/cypress/e2e/pr27-features.cy.js b/frontend/cypress/e2e/pr27-features.cy.js index 29df703..1b0a22e 100644 --- a/frontend/cypress/e2e/pr27-features.cy.js +++ b/frontend/cypress/e2e/pr27-features.cy.js @@ -175,6 +175,85 @@ describe('PR27 Feature Tests', () => { }) }) + // ------------------------------------------------------------------------- + // API: rename recipe auto-retranslates en_name + // ------------------------------------------------------------------------- + describe('API: rename recipe retranslates en_name', () => { + let recipeId + + after(() => { + if (recipeId) { + cy.request({ method: 'DELETE', url: `/api/recipes/${recipeId}`, headers: authHeaders, failOnStatusCode: false }) + } + }) + + it('creates recipe then renames it, en_name auto-updates', () => { + cy.request({ + method: 'POST', url: '/api/recipes', headers: authHeaders, + body: { name: '头痛', ingredients: [{ oil_name: '薰衣草', drops: 3 }], tags: [] } + }).then(res => { + recipeId = res.body.id + // Verify initial en_name + cy.request('/api/recipes').then(list => { + const r = list.body.find(x => x.id === recipeId) + expect(r.en_name).to.include('Headache') + }) + // Rename to 肩颈按摩 + cy.request({ + method: 'PUT', url: `/api/recipes/${recipeId}`, headers: authHeaders, + body: { name: '肩颈按摩' } + }).then(() => { + cy.request('/api/recipes').then(list => { + const r = list.body.find(x => x.id === recipeId) + expect(r.en_name).to.include('Neck') + expect(r.en_name).to.include('Massage') + }) + }) + }) + }) + }) + + // ------------------------------------------------------------------------- + // API: delete user skips duplicate diary by ingredient content + // ------------------------------------------------------------------------- + describe('API: delete user skips duplicate diary', () => { + const DUP_USER = 'cypress_pr27_dup' + + it('creates user with duplicate diary, deletes, verifies skip', () => { + // Create user + cy.request({ + method: 'POST', url: '/api/users', headers: authHeaders, + body: { username: DUP_USER, display_name: 'Dup Test', role: 'viewer' } + }).then(res => { + const userId = res.body.id + const userToken = res.body.token + const userAuth = { Authorization: `Bearer ${userToken}` } + + // Get a public recipe's ingredients to create a duplicate + cy.request({ url: '/api/recipes', headers: authHeaders }).then(listRes => { + const pub = listRes.body[0] + const dupIngs = pub.ingredients.map(i => ({ oil: i.oil_name, drops: i.drops })) + + // Add diary with same ingredients as public recipe (different name) + cy.request({ + method: 'POST', url: '/api/diary', headers: userAuth, + body: { name: '我的重复方', ingredients: dupIngs, note: '' } + }).then(() => { + // Delete user + cy.request({ method: 'DELETE', url: `/api/users/${userId}`, headers: authHeaders }).then(delRes => { + expect(delRes.body.ok).to.eq(true) + // Verify duplicate was NOT transferred + cy.request({ url: '/api/diary', headers: authHeaders }).then(diaryRes => { + const transferred = diaryRes.body.find(d => d.name && d.name.includes('我的重复方')) + expect(transferred).to.not.exist + }) + }) + }) + }) + }) + }) + }) + // ------------------------------------------------------------------------- // UI: 管理配方 login prompt when not logged in // ------------------------------------------------------------------------- diff --git a/frontend/src/__tests__/pr27Features.test.js b/frontend/src/__tests__/pr27Features.test.js index 01b4258..c70f15a 100644 --- a/frontend/src/__tests__/pr27Features.test.js +++ b/frontend/src/__tests__/pr27Features.test.js @@ -1,4 +1,5 @@ import { describe, it, expect } from 'vitest' +import { recipeNameEn } from '../composables/useOilTranslation' // --------------------------------------------------------------------------- // EDITOR_ONLY_TAGS includes '已下架' @@ -84,3 +85,88 @@ describe('已下架 tag filtering', () => { expect(filterDelisted(all)).toHaveLength(0) }) }) + +// --------------------------------------------------------------------------- +// recipeNameEn — front-end keyword translation +// --------------------------------------------------------------------------- +describe('recipeNameEn', () => { + it('translates 酸痛包 → Pain Relief Blend', () => { + expect(recipeNameEn('酸痛包')).toBe('Pain Relief Blend') + }) + + it('translates 助眠配方 → Sleep Aid Blend', () => { + expect(recipeNameEn('助眠配方')).toBe('Sleep Aid Blend') + }) + + it('translates 头痛 → Headache', () => { + expect(recipeNameEn('头痛')).toBe('Headache') + }) + + it('translates 肩颈按摩 → Neck & Shoulder Massage', () => { + expect(recipeNameEn('肩颈按摩')).toBe('Neck & Shoulder Massage') + }) + + it('translates 湿疹舒缓 → Eczema Soothing', () => { + expect(recipeNameEn('湿疹舒缓')).toBe('Eczema Soothing') + }) + + it('translates 淋巴排毒 → Lymph Detox', () => { + expect(recipeNameEn('淋巴排毒')).toBe('Lymph Detox') + }) + + it('translates 灰指甲 → Nail Fungus', () => { + expect(recipeNameEn('灰指甲')).toBe('Nail Fungus') + }) + + it('translates 缓解焦虑 → Relief Anxiety', () => { + expect(recipeNameEn('缓解焦虑')).toBe('Relief Anxiety') + }) + + it('returns original name for unknown text', () => { + expect(recipeNameEn('XYZXYZ')).toBe('XYZXYZ') + }) + + it('returns empty/null for empty/null input', () => { + expect(recipeNameEn('')).toBe('') + expect(recipeNameEn(null)).toBeNull() + }) + + it('does not duplicate keywords', () => { + // 酸痛 maps to Pain Relief; should not appear twice + const result = recipeNameEn('酸痛酸痛') + expect(result).toBe('Pain Relief') + }) +}) + +// --------------------------------------------------------------------------- +// Duplicate oil prevention logic +// --------------------------------------------------------------------------- +describe('duplicate oil prevention', () => { + it('detects duplicate oil in ingredient list', () => { + const ings = [ + { oil: '薰衣草', drops: 3 }, + { oil: '茶树', drops: 2 }, + ] + const newOil = '薰衣草' + const isDup = ings.some(i => i.oil === newOil) + expect(isDup).toBe(true) + }) + + it('allows non-duplicate oil', () => { + const ings = [ + { oil: '薰衣草', drops: 3 }, + { oil: '茶树', drops: 2 }, + ] + const newOil = '乳香' + const isDup = ings.some(i => i.oil === newOil) + expect(isDup).toBe(false) + }) + + it('allows same oil for the same row (editing current)', () => { + const ing = { oil: '薰衣草', drops: 3 } + const ings = [ing, { oil: '茶树', drops: 2 }] + // When selecting for the same row, exclude self + const isDup = ings.some(i => i !== ing && i.oil === '薰衣草') + expect(isDup).toBe(false) + }) +})