import * as fs from 'fs'; import * as path from 'path'; describe('ActivityListContent (TDD 测试套件: 活动大厅陪吃·陪唱·陪喝内容与零Emoji校验)', () => { const rootDir = path.resolve(__dirname, '../../'); const mockFilePath = path.join(rootDir, 'pwuniapp', 'common', 'mock.js'); const listVuePath = path.join(rootDir, 'pwuniapp', 'pages', 'activity', 'list.vue'); const emojiRegex = /(?:\p{Extended_Pictographic}|\p{Emoji_Presentation}|[\u2600-\u27BF])/u; describe('1. mock.js 中 ACTIVITIES_LIST 内容规范校验', () => { it('活动列表中陪吃·陪唱·陪喝三大核心品类应占主导地位 (>= 80%)', () => { const mockContent = fs.readFileSync(mockFilePath, 'utf-8'); // 提取 ACTIVITIES_LIST 数组对象文本 const listMatch = mockContent.match(/export const ACTIVITIES_LIST\s*=\s*(\[[\s\S]*?\n\];)/); expect(listMatch).not.toBeNull(); // 解析执行提取对象 const evalList = new Function(`return ${listMatch![1].replace(/;\s*$/, '')}`)(); expect(Array.isArray(evalList)).toBe(true); expect(evalList.length).toBeGreaterThanOrEqual(4); const mainCategories = ['美食陪吃', '同城陪唱', '下午茶陪喝', '同城微醺']; const mainCategoryCount = evalList.filter((item: any) => mainCategories.includes(item.game || item.category) ).length; const mainRatio = mainCategoryCount / evalList.length; expect(mainRatio).toBeGreaterThanOrEqual(0.8); }); it('所有活动必须包含合法公开场所、时间与发起人信息', () => { const mockContent = fs.readFileSync(mockFilePath, 'utf-8'); const listMatch = mockContent.match(/export const ACTIVITIES_LIST\s*=\s*(\[[\s\S]*?\n\];)/); const evalList = new Function(`return ${listMatch![1].replace(/;\s*$/, '')}`)(); for (const item of evalList) { expect(item.id).toBeDefined(); expect(item.title).toBeDefined(); expect(item.location).toBeDefined(); expect(item.startTime).toBeDefined(); expect(item.creatorNickname).toBeDefined(); expect(item.creatorAvatar).toBeDefined(); expect(['AA', 'PROJECT']).toContain(item.payType); // 验证地点均为公开合法商圈/门店,不能有私人住宅等违规词 expect(item.location).not.toMatch(/(酒店客房|私人住宅|私密公寓)/); } }); it('ACTIVITIES_LIST 数据中严禁包含任何系统 Emoji 字符', () => { const mockContent = fs.readFileSync(mockFilePath, 'utf-8'); const listMatch = mockContent.match(/export const ACTIVITIES_LIST\s*=\s*(\[[\s\S]*?\n\];)/); expect(listMatch).not.toBeNull(); const listContent = listMatch![1]; const lines = listContent.split('\n'); const violations: string[] = []; lines.forEach((line, idx) => { if (emojiRegex.test(line)) { violations.push(`Line ${idx + 1}: ${line.trim()}`); } }); expect(violations).toEqual([]); }); }); describe('2. pages/activity/list.vue 页面规范校验', () => { it('活动列表页面模板与样式中严禁包含任何 Emoji 字符', () => { const content = fs.readFileSync(listVuePath, 'utf-8'); const lines = content.split('\n'); const violations: string[] = []; lines.forEach((line, idx) => { if (emojiRegex.test(line)) { violations.push(`Line ${idx + 1}: ${line.trim()}`); } }); expect(violations).toEqual([]); }); it('页面应包含陪吃·陪唱·陪喝相关的分类快捷切换或展示逻辑', () => { const content = fs.readFileSync(listVuePath, 'utf-8'); // 验证包含美食陪吃、同城陪唱等核心品类关键词 expect(content).toMatch(/(美食陪吃|陪吃)/); expect(content).toMatch(/(同城陪唱|陪唱)/); expect(content).toMatch(/(下午茶陪喝|陪喝)/); }); }); describe('3. 活动大厅综合搜索框功能与双主题规范门禁 (TDD)', () => { it('页面必须包含顶部综合搜索框组件并支持关键词双向绑定', () => { const content = fs.readFileSync(listVuePath, 'utf-8'); expect(content).toContain('header-search'); expect(content).toContain('search-input'); expect(content).toContain('searchKeyword'); }); it('搜索过滤逻辑必须同时支持标题、商圈地点、品类与局长多维度匹配', () => { const content = fs.readFileSync(listVuePath, 'utf-8'); // 必须包含基于 searchKeyword 的模糊匹配逻辑 expect(content).toContain('matchKeyword'); expect(content).toMatch(/a\.title.*includes/); expect(content).toMatch(/a\.location.*includes/); expect(content).toMatch(/a\.creatorNickname.*includes/); }); it('搜索框在 theme-warm 浅色暖阳晨曦模式下必须适配纯白背景与深色文本', () => { const content = fs.readFileSync(listVuePath, 'utf-8'); const warmSection = content.split('.activity-container.theme-warm')[1] || ''; expect(warmSection).toContain('.header-search'); expect(warmSection).toContain('.search-box'); expect(warmSection).toContain('#FFFFFF'); }); }); });