完善e2e测试与后端测试,微信小程序端UI层测试暂未完成
This commit was merged in pull request #52.
This commit is contained in:
@@ -173,6 +173,90 @@ async function signIn(courseId, memberId, token) {
|
||||
return res;
|
||||
}
|
||||
|
||||
// ── 品牌定制 API ──
|
||||
|
||||
/** 获取品牌配置 (需auth) */
|
||||
async function getBrandConfig(token) {
|
||||
console.log('[API] 获取品牌配置...');
|
||||
const res = await makeRequest('GET', `${API_BASE}/brand`, null, token);
|
||||
console.log(`[API] 品牌配置: status=${res.status}`);
|
||||
return res;
|
||||
}
|
||||
|
||||
// ── 通知/消息 API ──
|
||||
|
||||
/** 获取活跃Banner列表 */
|
||||
async function getActiveBanners(token) {
|
||||
console.log('[API] 获取活跃Banners...');
|
||||
const res = await makeRequest('GET', `${API_BASE}/banners/active`, null, token);
|
||||
console.log(`[API] Banners: status=${res.status}`);
|
||||
return res;
|
||||
}
|
||||
|
||||
/** 获取系统通知列表 */
|
||||
async function getSystemNotices(token) {
|
||||
console.log('[API] 获取系统通知...');
|
||||
const res = await makeRequest('GET', `${API_BASE}/notices`, null, token);
|
||||
console.log(`[API] 通知列表: status=${res.status}`);
|
||||
return res;
|
||||
}
|
||||
|
||||
/** 获取用户消息列表 */
|
||||
async function getUserMessages(userId, token) {
|
||||
console.log(`[API] 获取用户消息: userId=${userId}`);
|
||||
const res = await makeRequest('GET', `${API_BASE}/messages/user/${userId}`, null, token);
|
||||
console.log(`[API] 用户消息: status=${res.status}`);
|
||||
return res;
|
||||
}
|
||||
|
||||
/** 获取用户未读消息数 */
|
||||
async function getUnreadMessageCount(userId, token) {
|
||||
console.log(`[API] 获取未读消息数: userId=${userId}`);
|
||||
const res = await makeRequest('GET', `${API_BASE}/messages/user/${userId}/unread`, null, token);
|
||||
console.log(`[API] 未读数: status=${res.status}`);
|
||||
return res;
|
||||
}
|
||||
|
||||
/** 标记单条消息已读 */
|
||||
async function markMessageAsRead(messageId, token) {
|
||||
console.log(`[API] 标记消息已读: id=${messageId}`);
|
||||
const res = await makeRequest('PUT', `${API_BASE}/messages/${messageId}/read`, null, token);
|
||||
console.log(`[API] 标记已读: status=${res.status}`);
|
||||
return res;
|
||||
}
|
||||
|
||||
// ── 团课高级搜索 API ──
|
||||
|
||||
/** 按课程类型筛选 */
|
||||
async function searchCoursesByType(typeId, token) {
|
||||
console.log(`[API] 按类型搜索: typeId=${typeId}`);
|
||||
const res = await makeRequest('POST', `${API_BASE}/groupCourse/page`, {
|
||||
page: 0, size: 50, courseTypeId: typeId
|
||||
}, token);
|
||||
console.log(`[API] 类型筛选: status=${res.status}, total=${res.data?.data?.totalElements || '?'}`);
|
||||
return res;
|
||||
}
|
||||
|
||||
/** 按标签筛选 */
|
||||
async function searchCoursesByLabel(labelId, token) {
|
||||
console.log(`[API] 按标签搜索: labelId=${labelId}`);
|
||||
const res = await makeRequest('POST', `${API_BASE}/groupCourse/page`, {
|
||||
page: 0, size: 50, labelId: labelId
|
||||
}, token);
|
||||
console.log(`[API] 标签筛选: status=${res.status}, total=${res.data?.data?.totalElements || '?'}`);
|
||||
return res;
|
||||
}
|
||||
|
||||
/** 复筛: 类型+标签 */
|
||||
async function searchCoursesCombined(typeId, labelId, token) {
|
||||
console.log(`[API] 组合搜索: typeId=${typeId}, labelId=${labelId}`);
|
||||
const res = await makeRequest('POST', `${API_BASE}/groupCourse/page`, {
|
||||
page: 0, size: 50, courseTypeId: typeId, labelId: labelId
|
||||
}, token);
|
||||
console.log(`[API] 组合筛选: status=${res.status}, total=${res.data?.data?.totalElements || '?'}`);
|
||||
return res;
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
makeRequest,
|
||||
adminLogin,
|
||||
@@ -185,6 +269,15 @@ module.exports = {
|
||||
bookCourse,
|
||||
getMemberBookings,
|
||||
signIn,
|
||||
getBrandConfig,
|
||||
getActiveBanners,
|
||||
getSystemNotices,
|
||||
getUserMessages,
|
||||
getUnreadMessageCount,
|
||||
markMessageAsRead,
|
||||
searchCoursesByType,
|
||||
searchCoursesByLabel,
|
||||
searchCoursesCombined,
|
||||
isSuccess,
|
||||
BASE_URL,
|
||||
API_BASE
|
||||
|
||||
@@ -0,0 +1,194 @@
|
||||
/**
|
||||
* P4-API - 会员端团课高级搜索 API 测试
|
||||
* 流程:会员登录 → 按课程类型筛选 → 按标签筛选 → 类型+标签组合筛选 → 验证结果
|
||||
*
|
||||
* 使用 HTTP API(HMAC-SHA256 签名 + JWT Token)
|
||||
* 前置条件:后端服务 http://192.168.110.64:8084 已启动
|
||||
*/
|
||||
const api = require('../helpers/api-helper');
|
||||
|
||||
let authToken = null;
|
||||
let memberId = null;
|
||||
let typeId = null;
|
||||
let labelId = null;
|
||||
|
||||
describe('P4-API - 团课高级搜索', () => {
|
||||
// ════════════════════════════════════════════════════
|
||||
// 步骤0:登录 & 准备筛选条件
|
||||
// ════════════════════════════════════════════════════
|
||||
beforeAll(async () => {
|
||||
console.log('════════════════════════════════════════════');
|
||||
console.log('P4-API - 团课高级搜索测试');
|
||||
console.log('════════════════════════════════════════════');
|
||||
|
||||
const result = await api.memberLogin();
|
||||
expect(result).not.toBeNull();
|
||||
authToken = result.accessToken;
|
||||
memberId = result.memberId;
|
||||
console.log(`[P4-API] 会员登录成功: memberId=${memberId}`);
|
||||
|
||||
// 先获取课程类型列表,找第一个类型ID
|
||||
const typeRes = await api.getCourseTypes();
|
||||
if (api.isSuccess(typeRes)) {
|
||||
const types = typeRes.data?.data || typeRes.data || [];
|
||||
const records = Array.isArray(types) ? types : (types.records || types.content || []);
|
||||
if (records.length > 0) {
|
||||
typeId = records[0].id;
|
||||
console.log(`[P4-API] 选择课程类型: id=${typeId}, name=${records[0].typeName || records[0].name}`);
|
||||
}
|
||||
}
|
||||
|
||||
// 尝试从课程列表获取第一个标签ID
|
||||
try {
|
||||
const searchRes = await api.searchCourses('', authToken);
|
||||
if (api.isSuccess(searchRes)) {
|
||||
const content = searchRes.data?.data?.content || searchRes.data?.content || searchRes.data || [];
|
||||
const courses = Array.isArray(content) ? content : (content.records || []);
|
||||
// 从第一个课程取标签
|
||||
for (const course of courses) {
|
||||
const labels = course.labels || course.labelList || [];
|
||||
if (Array.isArray(labels) && labels.length > 0) {
|
||||
const firstLabel = labels[0];
|
||||
labelId = typeof firstLabel === 'object' ? firstLabel.id : firstLabel;
|
||||
const name = typeof firstLabel === 'object' ? firstLabel.labelName : '';
|
||||
console.log(`[P4-API] 选择标签: id=${labelId}, name=${name}`);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
console.warn('[P4-API] 获取标签失败:', e.message);
|
||||
}
|
||||
}, 60000);
|
||||
|
||||
// ════════════════════════════════════════════════════
|
||||
// 步骤1:按课程类型筛选
|
||||
// ════════════════════════════════════════════════════
|
||||
describe('按课程类型筛选', () => {
|
||||
test('TC-SEARCH-001: 按类型筛选应返回正确结果', async () => {
|
||||
if (!typeId) {
|
||||
console.warn('[TC-SEARCH-001] 无类型ID,跳过');
|
||||
return;
|
||||
}
|
||||
|
||||
const res = await api.searchCoursesByType(typeId, authToken);
|
||||
expect(res.status).toBe(200);
|
||||
|
||||
const content = res.data?.data?.content || res.data?.content || res.data || [];
|
||||
const courses = Array.isArray(content) ? content : (content.records || []);
|
||||
console.log(`[TC-SEARCH-001] 类型筛选结果: ${courses.length} 个课程`);
|
||||
|
||||
// 验证所有返回课程的类型匹配
|
||||
if (courses.length > 0) {
|
||||
courses.slice(0, 3).forEach(c => {
|
||||
console.log(` - ${c.courseName || c.name} (typeId=${c.courseTypeId || c.typeId})`);
|
||||
});
|
||||
}
|
||||
|
||||
expect(courses.length).toBeGreaterThanOrEqual(0);
|
||||
});
|
||||
});
|
||||
|
||||
// ════════════════════════════════════════════════════
|
||||
// 步骤2:按标签筛选
|
||||
// ════════════════════════════════════════════════════
|
||||
describe('按标签筛选', () => {
|
||||
test('TC-SEARCH-002: 按标签筛选应返回正确结果', async () => {
|
||||
if (!labelId) {
|
||||
console.warn('[TC-SEARCH-002] 无标签ID,跳过');
|
||||
return;
|
||||
}
|
||||
|
||||
const res = await api.searchCoursesByLabel(labelId, authToken);
|
||||
expect(res.status).toBe(200);
|
||||
|
||||
const content = res.data?.data?.content || res.data?.content || res.data || [];
|
||||
const courses = Array.isArray(content) ? content : (content.records || []);
|
||||
console.log(`[TC-SEARCH-002] 标签筛选结果: ${courses.length} 个课程`);
|
||||
|
||||
if (courses.length > 0) {
|
||||
courses.slice(0, 3).forEach(c => {
|
||||
const labels = (c.labels || c.labelList || []).map(l =>
|
||||
typeof l === 'object' ? l.labelName : l
|
||||
);
|
||||
console.log(` - ${c.courseName || c.name} (labels: [${labels.join(', ')}])`);
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// ════════════════════════════════════════════════════
|
||||
// 步骤3:类型+标签组合筛选
|
||||
// ════════════════════════════════════════════════════
|
||||
describe('类型+标签组合筛选', () => {
|
||||
test('TC-SEARCH-003: 组合筛选应返回交集结果', async () => {
|
||||
if (!typeId || !labelId) {
|
||||
console.warn('[TC-SEARCH-003] 缺少类型或标签ID,跳过');
|
||||
return;
|
||||
}
|
||||
|
||||
const res = await api.searchCoursesCombined(typeId, labelId, authToken);
|
||||
expect(res.status).toBe(200);
|
||||
|
||||
const content = res.data?.data?.content || res.data?.content || res.data || [];
|
||||
const courses = Array.isArray(content) ? content : (content.records || []);
|
||||
console.log(`[TC-SEARCH-003] 组合筛选结果: ${courses.length} 个课程`);
|
||||
|
||||
if (courses.length > 0) {
|
||||
courses.slice(0, 3).forEach(c => {
|
||||
console.log(` - ${c.courseName || c.name} (id=${c.id})`);
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// ════════════════════════════════════════════════════
|
||||
// 步骤4:边界场景
|
||||
// ════════════════════════════════════════════════════
|
||||
describe('边界场景', () => {
|
||||
test('TC-SEARCH-004: 搜索不存在类型返回200(后端可能忽略无效筛选)', async () => {
|
||||
const res = await api.searchCoursesByType(99999, authToken);
|
||||
// 应确保API正常响应,即使筛选参数无效也不应该崩溃
|
||||
console.log(`[TC-SEARCH-004] 不存在类型: status=${res.status}`);
|
||||
expect(res.status).toBe(200);
|
||||
});
|
||||
|
||||
test('TC-SEARCH-005: 搜索不存在标签返回200(后端可能忽略无效筛选)', async () => {
|
||||
const res = await api.searchCoursesByLabel(99999, authToken);
|
||||
console.log(`[TC-SEARCH-005] 不存在标签: status=${res.status}`);
|
||||
expect(res.status).toBe(200);
|
||||
});
|
||||
|
||||
test('TC-SEARCH-006: 空关键词搜索可返回全部课程', async () => {
|
||||
const res = await api.searchCourses('', authToken);
|
||||
expect(res.status).toBe(200);
|
||||
|
||||
const content = res.data?.data?.content || res.data?.content || res.data || [];
|
||||
const courses = Array.isArray(content) ? content : (content.records || []);
|
||||
console.log(`[TC-SEARCH-006] 空搜索返回: ${courses.length} 个课程`);
|
||||
expect(courses.length).toBeGreaterThanOrEqual(0);
|
||||
});
|
||||
});
|
||||
|
||||
// ════════════════════════════════════════════════════
|
||||
// 步骤5:总结
|
||||
// ════════════════════════════════════════════════════
|
||||
describe('总结', () => {
|
||||
test('TC-P4-SUMMARY: 团课高级搜索汇总', () => {
|
||||
console.log('');
|
||||
console.log('════════════════════════════════════════════');
|
||||
console.log('P4-API - 团课高级搜索测试结束');
|
||||
console.log('════════════════════════════════════════════');
|
||||
console.log(' 已测试:');
|
||||
console.log(` 1. 按类型筛选 (typeId=${typeId || 'N/A'})`);
|
||||
console.log(` 2. 按标签筛选 (labelId=${labelId || 'N/A'})`);
|
||||
console.log(` 3. 类型+标签组合筛选`);
|
||||
console.log(' 4. 不存在类型/标签边界');
|
||||
console.log(' 5. 空关键词搜索');
|
||||
console.log('════════════════════════════════════════════');
|
||||
|
||||
expect(memberId).toBeDefined();
|
||||
expect(authToken).toBeTruthy();
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,179 @@
|
||||
/**
|
||||
* P3-API - 会员端品牌定制 & 通知消息 API 测试
|
||||
* 流程:会员登录 → 获取品牌配置 → 获取Banner/通知
|
||||
*
|
||||
* 使用 HTTP API(HMAC-SHA256 签名 + JWT Token)
|
||||
* 前置条件:后端服务 http://192.168.110.64:8084 已启动
|
||||
*/
|
||||
const api = require('../helpers/api-helper');
|
||||
|
||||
let authToken = null;
|
||||
let memberId = null;
|
||||
|
||||
describe('P3-API - 会员端品牌定制 & 通知消息', () => {
|
||||
// ════════════════════════════════════════════════════
|
||||
// 步骤0:会员登录
|
||||
// ════════════════════════════════════════════════════
|
||||
beforeAll(async () => {
|
||||
console.log('════════════════════════════════════════════');
|
||||
console.log('P3-API - 品牌定制 & 通知消息测试');
|
||||
console.log('════════════════════════════════════════════');
|
||||
|
||||
const result = await api.memberLogin();
|
||||
expect(result).not.toBeNull();
|
||||
authToken = result.accessToken;
|
||||
memberId = result.memberId;
|
||||
console.log(`[P3-API] 会员登录成功: memberId=${memberId}`);
|
||||
}, 30000);
|
||||
|
||||
// ════════════════════════════════════════════════════
|
||||
// 步骤1:品牌定制配置
|
||||
// ════════════════════════════════════════════════════
|
||||
describe('品牌定制配置', () => {
|
||||
test('TC-BRAND-001: 获取品牌配置', async () => {
|
||||
const res = await api.getBrandConfig(authToken);
|
||||
console.log(`[TC-BRAND-001] status=${res.status}`);
|
||||
|
||||
if (api.isSuccess(res)) {
|
||||
const brand = res.data?.data || res.data || {};
|
||||
console.log(` 品牌名称: ${brand.brandName || '(未设置)'}`);
|
||||
console.log(` 品牌口号: ${brand.slogan || '(未设置)'}`);
|
||||
console.log(` 主色调: ${brand.primaryColor || '(默认)'}`);
|
||||
console.log(` 辅助色: ${brand.secondaryColor || '(默认)'}`);
|
||||
console.log(` Logo URL: ${brand.logoUrl || '(默认)'}`);
|
||||
console.log(` 背景URL: ${brand.backgroundUrl || '(默认)'}`);
|
||||
|
||||
// 品牌名称应存在(至少默认值)
|
||||
expect(brand).toBeDefined();
|
||||
}
|
||||
});
|
||||
|
||||
test('TC-BRAND-002: 验证品牌颜色为合法HEX值', async () => {
|
||||
const res = await api.getBrandConfig(authToken);
|
||||
if (!api.isSuccess(res)) {
|
||||
console.warn('[TC-BRAND-002] 获取品牌配置失败,跳过');
|
||||
return;
|
||||
}
|
||||
|
||||
const brand = res.data?.data || res.data || {};
|
||||
const primaryColor = brand.primaryColor;
|
||||
const secondaryColor = brand.secondaryColor;
|
||||
|
||||
if (primaryColor) {
|
||||
// 验证是合法 HEX (#RRGGBB 或 #RRGGBBAA)
|
||||
expect(/^#[0-9A-Fa-f]{6,8}$/.test(primaryColor)).toBe(true);
|
||||
console.log(` 主色调 HEX 合法: ${primaryColor}`);
|
||||
}
|
||||
|
||||
if (secondaryColor) {
|
||||
expect(/^#[0-9A-Fa-f]{6,8}$/.test(secondaryColor)).toBe(true);
|
||||
console.log(` 辅助色 HEX 合法: ${secondaryColor}`);
|
||||
}
|
||||
});
|
||||
|
||||
test('TC-BRAND-003: Logo URL 应为有效路径或默认占位符', async () => {
|
||||
const res = await api.getBrandConfig(authToken);
|
||||
if (!api.isSuccess(res)) {
|
||||
console.warn('[TC-BRAND-003] 获取品牌配置失败,跳过');
|
||||
return;
|
||||
}
|
||||
|
||||
const brand = res.data?.data || res.data || {};
|
||||
const logoUrl = brand.logoUrl;
|
||||
|
||||
if (logoUrl) {
|
||||
// 可以是完整URL、相对路径、或base64
|
||||
const isValid = typeof logoUrl === 'string' && logoUrl.length > 0;
|
||||
expect(isValid).toBe(true);
|
||||
console.log(` Logo URL: ${logoUrl.substring(0, 80)}...`);
|
||||
} else {
|
||||
console.log(' Logo URL 为空(使用默认值)');
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// ════════════════════════════════════════════════════
|
||||
// 步骤2:Banner & 系统通知
|
||||
// ════════════════════════════════════════════════════
|
||||
describe('Banner & 系统通知', () => {
|
||||
test('TC-NOTIFY-001: 获取活跃Banner列表', async () => {
|
||||
const res = await api.getActiveBanners(authToken);
|
||||
console.log(`[TC-NOTIFY-001] status=${res.status}`);
|
||||
|
||||
if (api.isSuccess(res)) {
|
||||
const banners = res.data?.data || res.data || [];
|
||||
const records = Array.isArray(banners) ? banners : (banners.records || []);
|
||||
console.log(` 活跃Banner数量: ${records.length}`);
|
||||
|
||||
if (records.length > 0) {
|
||||
const b = records[0];
|
||||
console.log(` 首条Banner: title="${b.title}", imageUrl=${(b.imageUrl || '').substring(0, 50)}`);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
test('TC-NOTIFY-002: 获取系统通知列表', async () => {
|
||||
const res = await api.getSystemNotices(authToken);
|
||||
console.log(`[TC-NOTIFY-002] status=${res.status}`);
|
||||
|
||||
if (api.isSuccess(res)) {
|
||||
const notices = res.data?.data || res.data || [];
|
||||
const records = Array.isArray(notices) ? notices : (notices.records || []);
|
||||
console.log(` 系统通知数量: ${records.length}`);
|
||||
|
||||
if (records.length > 0) {
|
||||
const n = records[0];
|
||||
console.log(` 首条通知: title="${n.noticeTitle || n.title}", status=${n.status}`);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
test('TC-NOTIFY-003: 获取用户消息列表', async () => {
|
||||
const res = await api.getUserMessages(memberId, authToken);
|
||||
console.log(`[TC-NOTIFY-003] status=${res.status}`);
|
||||
|
||||
if (api.isSuccess(res)) {
|
||||
const messages = res.data?.data || res.data || [];
|
||||
const records = Array.isArray(messages) ? messages : (messages.records || []);
|
||||
console.log(` 消息数量: ${records.length}`);
|
||||
}
|
||||
// 消息可能为空,不强制要求有数据
|
||||
});
|
||||
|
||||
test('TC-NOTIFY-004: 获取未读消息数', async () => {
|
||||
const res = await api.getUnreadMessageCount(memberId, authToken);
|
||||
console.log(`[TC-NOTIFY-004] status=${res.status}`);
|
||||
|
||||
if (api.isSuccess(res)) {
|
||||
const count = Number(res.data?.data ?? res.data ?? 0);
|
||||
console.log(` 未读消息数: ${count}`);
|
||||
expect(typeof count).toBe('number');
|
||||
expect(count).toBeGreaterThanOrEqual(0);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// ════════════════════════════════════════════════════
|
||||
// 步骤3:总结
|
||||
// ════════════════════════════════════════════════════
|
||||
describe('总结', () => {
|
||||
test('TC-P3-SUMMARY: 品牌定制 & 通知消息测试汇总', () => {
|
||||
console.log('');
|
||||
console.log('════════════════════════════════════════════');
|
||||
console.log('P3-API - 品牌定制 & 通知消息测试结束');
|
||||
console.log('════════════════════════════════════════════');
|
||||
console.log(' 已测试:');
|
||||
console.log(' 1. 品牌配置获取 (名称/颜色/Logo/背景)');
|
||||
console.log(' 2. 颜色HEX格式校验');
|
||||
console.log(' 3. Logo URL有效性');
|
||||
console.log(' 4. 活跃Banner列表');
|
||||
console.log(' 5. 系统通知列表');
|
||||
console.log(' 6. 用户消息列表');
|
||||
console.log(' 7. 未读消息数量');
|
||||
console.log('════════════════════════════════════════════');
|
||||
|
||||
expect(memberId).toBeDefined();
|
||||
expect(authToken).toBeTruthy();
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user