api.test.js 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929
  1. /**
  2. * Tests for cfc-frontend utils/api.js
  3. *
  4. * Mocks uni.request to intercept and verify API calls.
  5. */
  6. import * as api from '@/utils/api'
  7. // --------------- helpers ---------------
  8. /** Helper: make uni.request call success(data) on next tick */
  9. function mockSuccess(data) {
  10. global.uni.request.mockImplementation((opts) => {
  11. process.nextTick(() => {
  12. if (opts.success) opts.success({ data })
  13. })
  14. })
  15. }
  16. /** Helper: make uni.request call fail(err) on next tick */
  17. function mockFail(err) {
  18. global.uni.request.mockImplementation((opts) => {
  19. process.nextTick(() => {
  20. if (opts.fail) opts.fail(err)
  21. })
  22. })
  23. }
  24. // --------------- global beforeEach ---------------
  25. beforeEach(() => {
  26. global.uni._storage = {}
  27. global.uni.request.mockReset()
  28. global.uni.showToast.mockReset()
  29. global.uni.getAccountInfoSync.mockReset()
  30. global.uni.getAccountInfoSync.mockReturnValue({
  31. miniProgram: { envVersion: 'develop' }
  32. })
  33. })
  34. /** Capture the last uni.request call options */
  35. function lastRequest() {
  36. const calls = global.uni.request.mock.calls
  37. return calls[calls.length - 1][0]
  38. }
  39. // --------------- auth module ---------------
  40. describe('auth API', () => {
  41. beforeEach(() => {
  42. global.uni._storage.token = 'test-token'
  43. global.uni._storage.userId = '42'
  44. })
  45. test('wechatLogin sends POST /api/auth/wechat-login', async () => {
  46. mockSuccess({ code: 200, data: { token: 'jwt' } })
  47. const res = await api.wechatLogin('code123', { nickname: 'test' })
  48. const opts = lastRequest()
  49. expect(opts.method).toBe('POST')
  50. expect(opts.url).toMatch(/\/api\/auth\/wechat-login$/)
  51. expect(opts.data.code).toBe('code123')
  52. expect(res.data.token).toBe('jwt')
  53. })
  54. test('phoneLogin sends POST with phone and code', async () => {
  55. mockSuccess({ code: 200, data: { token: 'jwt' } })
  56. await api.phoneLogin({ phone: '13800138000', code: '1234' })
  57. const opts = lastRequest()
  58. expect(opts.method).toBe('POST')
  59. expect(opts.url).toMatch(/\/api\/auth\/phone-login$/)
  60. expect(opts.data.phone).toBe('13800138000')
  61. })
  62. test('sendCode sends POST with data', async () => {
  63. mockSuccess({ code: 200 })
  64. await api.sendCode({ phone: '13800138000' })
  65. const opts = lastRequest()
  66. expect(opts.method).toBe('POST')
  67. expect(opts.url).toMatch(/\/api\/auth\/send-code$/)
  68. })
  69. test('silentLogin sends POST with code', async () => {
  70. mockSuccess({ code: 200 })
  71. await api.silentLogin('wxcode')
  72. expect(lastRequest().data.code).toBe('wxcode')
  73. })
  74. test('autoLogin sends POST with openid', async () => {
  75. mockSuccess({ code: 200 })
  76. await api.autoLogin('openid_xxx')
  77. expect(lastRequest().data.openid).toBe('openid_xxx')
  78. })
  79. test('setPassword and verifyPassword send correct endpoints', async () => {
  80. mockSuccess({ code: 200 })
  81. await api.setPassword('secret')
  82. expect(lastRequest().url).toMatch(/\/api\/auth\/set-password$/)
  83. expect(lastRequest().data.password).toBe('secret')
  84. await api.verifyPassword('secret')
  85. expect(lastRequest().url).toMatch(/\/api\/auth\/verify-password$/)
  86. })
  87. test('directRegister sends POST', async () => {
  88. mockSuccess({ code: 200 })
  89. await api.directRegister({ phone: '13800138000', nickname: 'tester' })
  90. expect(lastRequest().url).toMatch(/\/api\/auth\/direct-register$/)
  91. })
  92. test('checkPhone sends POST with phone', async () => {
  93. mockSuccess({ code: 200 })
  94. await api.checkPhone('13800138000')
  95. expect(lastRequest().data.phone).toBe('13800138000')
  96. })
  97. test('wechatPhoneLogin sends POST', async () => {
  98. mockSuccess({ code: 200 })
  99. await api.wechatPhoneLogin({ code: 'wxcode', iv: 'iv', encryptedData: 'enc' })
  100. expect(lastRequest().url).toMatch(/\/api\/auth\/wechat-phone-login$/)
  101. })
  102. test('Authorization header includes Bearer token', async () => {
  103. mockSuccess({ code: 200 })
  104. global.uni._storage.token = 'my-jwt-token'
  105. await api.getUserInfo()
  106. const opts = lastRequest()
  107. expect(opts.header.Authorization).toBe('Bearer my-jwt-token')
  108. })
  109. test('X-User-Id header is set', async () => {
  110. mockSuccess({ code: 200 })
  111. global.uni._storage.userId = '99'
  112. await api.getUserInfo()
  113. expect(lastRequest().header['X-User-Id']).toBe('99')
  114. })
  115. test('rejects on non-200 code', async () => {
  116. mockSuccess({ code: 400, message: 'Bad Request' })
  117. await expect(api.getUserInfo()).rejects.toEqual({ code: 400, message: 'Bad Request' })
  118. })
  119. test('rejects on network failure', async () => {
  120. mockFail(new Error('Network error'))
  121. await expect(api.getUserInfo()).rejects.toThrow('Network error')
  122. })
  123. })
  124. // --------------- streets module ---------------
  125. describe('street address API', () => {
  126. test('getProvinces sends POST to /api/streets/provinces', async () => {
  127. mockSuccess({ code: 200, data: [] })
  128. await api.getProvinces()
  129. expect(lastRequest().url).toMatch(/\/api\/streets\/provinces$/)
  130. })
  131. test('getStreetChildren sends POST with parentId', async () => {
  132. mockSuccess({ code: 200 })
  133. await api.getStreetChildren(110000)
  134. expect(lastRequest().url).toMatch(/\/api\/streets\/children\/110000$/)
  135. })
  136. test('getStreetById sends POST to correct URL', async () => {
  137. mockSuccess({ code: 200 })
  138. await api.getStreetById(110101)
  139. expect(lastRequest().url).toMatch(/\/api\/streets\/110101$/)
  140. })
  141. test('getStreetPath sends POST', async () => {
  142. mockSuccess({ code: 200 })
  143. await api.getStreetPath(110101)
  144. expect(lastRequest().url).toMatch(/\/api\/streets\/110101\/path$/)
  145. })
  146. test('searchStreet sends POST with keyword', async () => {
  147. mockSuccess({ code: 200 })
  148. await api.searchStreet('北京')
  149. expect(lastRequest().data.keyword).toBe('北京')
  150. })
  151. test('matchByStreet sends POST with streetId', async () => {
  152. mockSuccess({ code: 200 })
  153. await api.matchByStreet(110101)
  154. expect(lastRequest().url).toMatch(/\/110101\/match$/)
  155. })
  156. })
  157. // --------------- user / family module ---------------
  158. describe('user & family API', () => {
  159. test('getUserInfo sends POST', async () => {
  160. mockSuccess({ code: 200 })
  161. await api.getUserInfo()
  162. expect(lastRequest().url).toMatch(/\/api\/user\/info$/)
  163. })
  164. test('updateUserInfo sends POST with data', async () => {
  165. mockSuccess({ code: 200 })
  166. await api.updateUserInfo({ nickname: 'new-name' })
  167. expect(lastRequest().data.nickname).toBe('new-name')
  168. })
  169. test('joinFamily sends POST with inviteCode', async () => {
  170. mockSuccess({ code: 200 })
  171. await api.joinFamily('ABC123')
  172. expect(lastRequest().data.inviteCode).toBe('ABC123')
  173. })
  174. test('createFamily sends POST with name', async () => {
  175. mockSuccess({ code: 200 })
  176. await api.createFamily('幸福之家')
  177. expect(lastRequest().data.name).toBe('幸福之家')
  178. })
  179. test('getFamilyMembers sends POST', async () => {
  180. mockSuccess({ code: 200 })
  181. await api.getFamilyMembers()
  182. expect(lastRequest().url).toMatch(/\/api\/family\/user\/family-members$/)
  183. })
  184. test('getVisibleFamilyMembers sends POST', async () => {
  185. mockSuccess({ code: 200 })
  186. await api.getVisibleFamilyMembers()
  187. expect(lastRequest().url).toMatch(/\/api\/family\/user\/members\/visible$/)
  188. })
  189. test('updateMemberVisibility sends POST with params', async () => {
  190. mockSuccess({ code: 200 })
  191. await api.updateMemberVisibility(1, 'child', true)
  192. expect(lastRequest().data.memberId).toBe(1)
  193. expect(lastRequest().data.memberType).toBe('child')
  194. expect(lastRequest().data.showToFamily).toBe(true)
  195. })
  196. test('switchMode sends POST with mode', async () => {
  197. mockSuccess({ code: 200 })
  198. await api.switchMode('child')
  199. expect(lastRequest().data.mode).toBe('child')
  200. })
  201. test('switchToChild sends POST with childId', async () => {
  202. mockSuccess({ code: 200 })
  203. await api.switchToChild(5)
  204. expect(lastRequest().data.childId).toBe(5)
  205. })
  206. test('switchBackToParent sends POST', async () => {
  207. mockSuccess({ code: 200 })
  208. await api.switchBackToParent()
  209. expect(lastRequest().url).toMatch(/\/api\/user\/switch-back-to-parent$/)
  210. })
  211. test('getChildren sends POST', async () => {
  212. mockSuccess({ code: 200 })
  213. await api.getChildren()
  214. expect(lastRequest().url).toMatch(/\/api\/family\/user\/children\/list$/)
  215. })
  216. test('createChild sends POST with data', async () => {
  217. mockSuccess({ code: 200 })
  218. await api.createChild({ name: '小宝', gender: 'male' })
  219. expect(lastRequest().url).toMatch(/\/api\/family\/user\/children$/)
  220. expect(lastRequest().data.name).toBe('小宝')
  221. })
  222. test('updateChild sends POST merged with childId', async () => {
  223. mockSuccess({ code: 200 })
  224. await api.updateChild(5, { nickname: '大宝' })
  225. expect(lastRequest().data.childId).toBe(5)
  226. expect(lastRequest().data.nickname).toBe('大宝')
  227. })
  228. test('kickFamilyMember sends POST with targetUserId', async () => {
  229. mockSuccess({ code: 200 })
  230. await api.kickFamilyMember(10)
  231. expect(lastRequest().data.targetUserId).toBe(10)
  232. })
  233. })
  234. // --------------- membership module ---------------
  235. describe('membership API', () => {
  236. test('getMembershipLevels sends POST', async () => {
  237. mockSuccess({ code: 200 })
  238. await api.getMembershipLevels()
  239. expect(lastRequest().url).toMatch(/\/api\/membership\/levels$/)
  240. })
  241. test('getMyMembership sends POST', async () => {
  242. mockSuccess({ code: 200 })
  243. await api.getMyMembership()
  244. expect(lastRequest().url).toMatch(/\/api\/membership\/my$/)
  245. })
  246. test('createOrder sends POST with levelCode and paymentType', async () => {
  247. mockSuccess({ code: 200 })
  248. await api.createOrder('vip', 'wechat')
  249. expect(lastRequest().data.levelCode).toBe('vip')
  250. expect(lastRequest().data.paymentType).toBe('wechat')
  251. })
  252. })
  253. // --------------- tasks module ---------------
  254. describe('tasks API', () => {
  255. test('createTask sends POST with data', async () => {
  256. mockSuccess({ code: 200 })
  257. await api.createTask({ title: '晨读', childId: 5 })
  258. expect(lastRequest().url).toMatch(/\/api\/tasks$/)
  259. expect(lastRequest().data.title).toBe('晨读')
  260. })
  261. test('getTodayTasks sends POST with childId', async () => {
  262. mockSuccess({ code: 200 })
  263. await api.getTodayTasks(5)
  264. expect(lastRequest().data.childId).toBe(5)
  265. })
  266. test('getPendingReviewTasks sends POST', async () => {
  267. mockSuccess({ code: 200 })
  268. await api.getPendingReviewTasks()
  269. expect(lastRequest().url).toMatch(/\/api\/tasks\/pending-review$/)
  270. })
  271. test('approveTask sends POST with approved:true', async () => {
  272. mockSuccess({ code: 200 })
  273. await api.approveTask(10)
  274. expect(lastRequest().url).toMatch(/\/api\/tasks\/10\/review$/)
  275. expect(lastRequest().data.approved).toBe(true)
  276. })
  277. test('rejectTask sends POST with approved:false', async () => {
  278. mockSuccess({ code: 200 })
  279. await api.rejectTask(10)
  280. expect(lastRequest().data.approved).toBe(false)
  281. })
  282. test('completeTask sends POST with childId and photoUrl', async () => {
  283. mockSuccess({ code: 200 })
  284. await api.completeTask(10, 5, 'http://photo.url')
  285. expect(lastRequest().data.childId).toBe(5)
  286. expect(lastRequest().data.photoUrl).toBe('http://photo.url')
  287. })
  288. test('reviewTask sends POST with data', async () => {
  289. mockSuccess({ code: 200 })
  290. await api.reviewTask(10, { approved: true, comment: '很好' })
  291. expect(lastRequest().url).toMatch(/\/api\/tasks\/10\/review$/)
  292. expect(lastRequest().data.comment).toBe('很好')
  293. })
  294. test('getTaskHistory sends POST with pagination', async () => {
  295. mockSuccess({ code: 200 })
  296. await api.getTaskHistory(5, 1, 20)
  297. expect(lastRequest().data.childId).toBe(5)
  298. expect(lastRequest().data.page).toBe(1)
  299. expect(lastRequest().data.size).toBe(20)
  300. })
  301. test('deleteTask sends DELETE', async () => {
  302. mockSuccess({ code: 200 })
  303. await api.deleteTask(10)
  304. expect(lastRequest().method).toBe('DELETE')
  305. expect(lastRequest().url).toMatch(/\/api\/tasks\/10$/)
  306. })
  307. test('batchCompleteTasks sends POST with array', async () => {
  308. mockSuccess({ code: 200 })
  309. await api.batchCompleteTasks([1, 2, 3])
  310. expect(lastRequest().data.taskIds).toEqual([1, 2, 3])
  311. })
  312. test('getTodayParentTasks sends POST', async () => {
  313. mockSuccess({ code: 200 })
  314. await api.getTodayParentTasks()
  315. expect(lastRequest().url).toMatch(/\/api\/tasks\/today-parent$/)
  316. })
  317. })
  318. // --------------- points module ---------------
  319. describe('points API', () => {
  320. test('getPointsBalance sends POST with childId', async () => {
  321. mockSuccess({ code: 200 })
  322. await api.getPointsBalance(5)
  323. expect(lastRequest().data.childId).toBe(5)
  324. })
  325. test('getPointsLogs sends POST with pagination', async () => {
  326. mockSuccess({ code: 200 })
  327. await api.getPointsLogs(5, 2, 20)
  328. expect(lastRequest().data.childId).toBe(5)
  329. expect(lastRequest().data.page).toBe(2)
  330. expect(lastRequest().data.size).toBe(20)
  331. })
  332. })
  333. // --------------- rewards / wishes module ---------------
  334. describe('rewards & wishes API', () => {
  335. test('createReward sends POST', async () => {
  336. mockSuccess({ code: 200 })
  337. await api.createReward({ name: '玩具', points: 100 })
  338. expect(lastRequest().data.name).toBe('玩具')
  339. })
  340. test('getWishlist sends POST with childId', async () => {
  341. mockSuccess({ code: 200 })
  342. await api.getWishlist(5)
  343. expect(lastRequest().data.childId).toBe(5)
  344. })
  345. test('exchangeReward sends POST', async () => {
  346. mockSuccess({ code: 200 })
  347. await api.exchangeReward(1, 5)
  348. expect(lastRequest().data.childId).toBe(5)
  349. })
  350. test('getPendingWishes sends POST with familyId', async () => {
  351. mockSuccess({ code: 200 })
  352. await api.getPendingWishes(1)
  353. expect(lastRequest().data.familyId).toBe(1)
  354. })
  355. test('getWishes sends POST with params', async () => {
  356. mockSuccess({ code: 200 })
  357. await api.getWishes({ childId: 5, status: 'pending' })
  358. expect(lastRequest().data.childId).toBe(5)
  359. })
  360. test('setWishPrice sends POST with pointsRequired', async () => {
  361. mockSuccess({ code: 200 })
  362. await api.setWishPrice(1, 200)
  363. expect(lastRequest().data.pointsRequired).toBe(200)
  364. })
  365. test('approveWishExchange sends POST', async () => {
  366. mockSuccess({ code: 200 })
  367. await api.approveWishExchange(1, true)
  368. expect(lastRequest().data.approved).toBe(true)
  369. })
  370. test('createWish sends POST with data', async () => {
  371. mockSuccess({ code: 200 })
  372. await api.createWish({ title: '新书包', childId: 5 })
  373. expect(lastRequest().data.title).toBe('新书包')
  374. })
  375. })
  376. // --------------- guide / packages module ---------------
  377. describe('guide & packages API', () => {
  378. test('getGuidePackages sends POST to list', async () => {
  379. mockSuccess({ code: 200 })
  380. await api.getGuidePackages(1)
  381. expect(lastRequest().url).toMatch(/\/api\/guide\/packages$/)
  382. })
  383. test('registerGuide sends POST', async () => {
  384. mockSuccess({ code: 200 })
  385. await api.registerGuide({ name: '张老师', level: 'senior' })
  386. expect(lastRequest().url).toMatch(/\/api\/guide\/register$/)
  387. })
  388. test('generateBindInvite sends POST', async () => {
  389. mockSuccess({ code: 200 })
  390. await api.generateBindInvite()
  391. expect(lastRequest().url).toMatch(/\/api\/guide\/bind\/invite$/)
  392. })
  393. test('getBoundFamilies sends POST', async () => {
  394. mockSuccess({ code: 200 })
  395. await api.getBoundFamilies()
  396. expect(lastRequest().url).toMatch(/\/api\/guide\/families$/)
  397. })
  398. test('getTeamMembers sends POST', async () => {
  399. mockSuccess({ code: 200 })
  400. await api.getTeamMembers()
  401. expect(lastRequest().url).toMatch(/\/api\/guide\/team\/members$/)
  402. })
  403. test('getGuideOrders sends POST', async () => {
  404. mockSuccess({ code: 200 })
  405. await api.getGuideOrders()
  406. expect(lastRequest().url).toMatch(/\/api\/guide\/orders\/list$/)
  407. })
  408. test('getGuideMessages sends POST', async () => {
  409. mockSuccess({ code: 200 })
  410. await api.getGuideMessages()
  411. expect(lastRequest().url).toMatch(/\/api\/guide\/messages\/list$/)
  412. })
  413. })
  414. // --------------- payment module ---------------
  415. describe('payment API', () => {
  416. test('createPackageOrder sends POST', async () => {
  417. mockSuccess({ code: 200 })
  418. await api.createPackageOrder({ packageId: 1, userId: 5 })
  419. expect(lastRequest().url).toMatch(/\/api\/package\/order\/create$/)
  420. })
  421. test('createWechatPayOrder sends POST', async () => {
  422. mockSuccess({ code: 200 })
  423. await api.createWechatPayOrder('NO001', '测试订单', 100)
  424. expect(lastRequest().data.orderNo).toBe('NO001')
  425. expect(lastRequest().data.amount).toBe(100)
  426. })
  427. })
  428. // --------------- assessment module ---------------
  429. describe('assessment API', () => {
  430. test('createAssessmentAppointment sends POST', async () => {
  431. mockSuccess({ code: 200 })
  432. await api.createAssessmentAppointment({ childId: 5, date: '2026-06-20' })
  433. expect(lastRequest().url).toMatch(/\/api\/dan-assessment\/appointment\/create$/)
  434. })
  435. test('payAssessmentOrder sends POST', async () => {
  436. mockSuccess({ code: 200 })
  437. await api.payAssessmentOrder('ORDER001', 'wechat')
  438. expect(lastRequest().data.orderNo).toBe('ORDER001')
  439. })
  440. test('recordAssessmentResult sends POST', async () => {
  441. mockSuccess({ code: 200 })
  442. await api.recordAssessmentResult({ orderId: 1, score: 85 })
  443. expect(lastRequest().url).toMatch(/\/api\/dan-assessment\/result\/record$/)
  444. })
  445. })
  446. // --------------- mini-games module ---------------
  447. describe('mini-games API', () => {
  448. test('getMiniGameList sends POST', async () => {
  449. mockSuccess({ code: 200 })
  450. await api.getMiniGameList()
  451. expect(lastRequest().url).toMatch(/\/api\/mini-game\/list$/)
  452. })
  453. test('getMiniGameByCode sends POST', async () => {
  454. mockSuccess({ code: 200 })
  455. await api.getMiniGameByCode('sudoku')
  456. expect(lastRequest().url).toMatch(/\/api\/mini-game\/sudoku$/)
  457. })
  458. test('completeMiniGame sends POST with game data', async () => {
  459. mockSuccess({ code: 200 })
  460. await api.completeMiniGame(5, 'sudoku', 120, 85)
  461. expect(lastRequest().data.childId).toBe(5)
  462. expect(lastRequest().data.gameCode).toBe('sudoku')
  463. expect(lastRequest().data.completionTime).toBe(120)
  464. expect(lastRequest().data.score).toBe(85)
  465. })
  466. test('getGameHistory sends POST', async () => {
  467. mockSuccess({ code: 200 })
  468. await api.getGameHistory({ childId: 5 })
  469. expect(lastRequest().url).toMatch(/\/api\/game\/history$/)
  470. })
  471. test('getGameLeaderboard sends POST', async () => {
  472. mockSuccess({ code: 200 })
  473. await api.getGameLeaderboard({ gameCode: 'sudoku' })
  474. expect(lastRequest().url).toMatch(/\/api\/game\/leaderboard$/)
  475. })
  476. })
  477. // --------------- growth module ---------------
  478. describe('growth records API', () => {
  479. test('createGrowthRecord sends POST', async () => {
  480. mockSuccess({ code: 200 })
  481. await api.createGrowthRecord({ childId: 5, content: '长高了2cm' })
  482. expect(lastRequest().url).toMatch(/\/api\/growth\/record\/create$/)
  483. })
  484. test('getGrowthRecordList sends POST', async () => {
  485. mockSuccess({ code: 200 })
  486. await api.getGrowthRecordList()
  487. expect(lastRequest().url).toMatch(/\/api\/growth\/record\/list$/)
  488. })
  489. test('createGrowthRecord with alsoPurchaseAssessment uses merge endpoint', async () => {
  490. mockSuccess({ code: 200 })
  491. await api.createGrowthRecord({ childId: 5, alsoPurchaseAssessment: true })
  492. expect(lastRequest().url).toMatch(/\/api\/growth\/record\/create-with-order$/)
  493. })
  494. })
  495. // --------------- energy module ---------------
  496. describe('energy API', () => {
  497. test('getEnergyOverview sends POST with childId', async () => {
  498. mockSuccess({ code: 200 })
  499. await api.getEnergyOverview(5)
  500. expect(lastRequest().data.childId).toBe(5)
  501. })
  502. test('getEnergyLogs sends POST', async () => {
  503. mockSuccess({ code: 200 })
  504. await api.getEnergyLogs(5, 'body', 1, 20)
  505. expect(lastRequest().data.dimensionCode).toBe('body')
  506. })
  507. test('getFamilyEnergySandbox sends POST', async () => {
  508. mockSuccess({ code: 200 })
  509. await api.getFamilyEnergySandbox()
  510. expect(lastRequest().url).toMatch(/\/api\/energy\/sandbox$/)
  511. })
  512. })
  513. // --------------- badges module ---------------
  514. describe('badges API', () => {
  515. test('getActiveBadges sends POST', async () => {
  516. mockSuccess({ code: 200 })
  517. await api.getActiveBadges()
  518. expect(lastRequest().url).toMatch(/\/api\/badges\/list$/)
  519. })
  520. test('getChildBadges sends POST with childId', async () => {
  521. mockSuccess({ code: 200 })
  522. await api.getChildBadges(5)
  523. expect(lastRequest().data.childId).toBe(5)
  524. })
  525. test('toggleBadgeFavorite sends POST', async () => {
  526. mockSuccess({ code: 200 })
  527. await api.toggleBadgeFavorite(5, 3)
  528. expect(lastRequest().data.childId).toBe(5)
  529. expect(lastRequest().data.badgeId).toBe(3)
  530. })
  531. })
  532. // --------------- articles module ---------------
  533. describe('articles API', () => {
  534. test('getArticleCategories sends POST', async () => {
  535. mockSuccess({ code: 200 })
  536. await api.getArticleCategories()
  537. expect(lastRequest().url).toMatch(/\/api\/articles\/categories$/)
  538. })
  539. test('getArticleList sends POST with data', async () => {
  540. mockSuccess({ code: 200 })
  541. await api.getArticleList({ categoryId: 1, page: 1 })
  542. expect(lastRequest().url).toMatch(/\/api\/articles\/list$/)
  543. })
  544. test('getArticleDetail sends POST', async () => {
  545. mockSuccess({ code: 200 })
  546. await api.getArticleDetail({ id: 42 })
  547. expect(lastRequest().url).toMatch(/\/api\/articles\/detail$/)
  548. })
  549. test('getDailyTip sends POST', async () => {
  550. mockSuccess({ code: 200 })
  551. await api.getDailyTip()
  552. expect(lastRequest().url).toMatch(/\/api\/articles\/daily-tip$/)
  553. })
  554. })
  555. // --------------- contact module ---------------
  556. describe('contact API', () => {
  557. test('getContactList sends POST', async () => {
  558. mockSuccess({ code: 200 })
  559. await api.getContactList({ childId: 5 })
  560. expect(lastRequest().url).toMatch(/\/api\/contact\/list$/)
  561. })
  562. test('createContact sends POST with data', async () => {
  563. mockSuccess({ code: 200 })
  564. await api.createContact({ name: '爷爷', relation: 'grandfather' })
  565. expect(lastRequest().url).toMatch(/\/api\/contact\/create$/)
  566. })
  567. test('deleteContact sends POST with id', async () => {
  568. mockSuccess({ code: 200 })
  569. await api.deleteContact(3)
  570. expect(lastRequest().url).toMatch(/\/api\/contact\/delete$/)
  571. expect(lastRequest().data.id).toBe(3)
  572. })
  573. test('importPhoneContact sends POST', async () => {
  574. mockSuccess({ code: 200 })
  575. await api.importPhoneContact({ contacts: [{ name: '张三', phone: '13800138000' }] })
  576. expect(lastRequest().url).toMatch(/\/api\/contact\/import-phone$/)
  577. })
  578. })
  579. // --------------- vendor module ---------------
  580. describe('vendor API', () => {
  581. test('vendorApply sends POST with data', async () => {
  582. mockSuccess({ code: 200 })
  583. await api.vendorApply({ companyName: '某某公司', type: 'planner' })
  584. expect(lastRequest().url).toMatch(/\/api\/vendor\/apply$/)
  585. })
  586. test('vendorStatus sends POST', async () => {
  587. mockSuccess({ code: 200 })
  588. await api.vendorStatus()
  589. expect(lastRequest().url).toMatch(/\/api\/vendor\/status$/)
  590. })
  591. test('vendorInfo sends POST', async () => {
  592. mockSuccess({ code: 200 })
  593. await api.vendorInfo()
  594. expect(lastRequest().url).toMatch(/\/api\/vendor\/info$/)
  595. })
  596. })
  597. // --------------- product module ---------------
  598. describe('product API', () => {
  599. test('productList sends POST', () => {
  600. // productList uses new Promise without success/fail callbacks,
  601. // so we check the request synchronously.
  602. api.productList({ page: 1, size: 10 })
  603. const opts = lastRequest()
  604. expect(opts.method).toBe('POST')
  605. expect(opts.url).toMatch(/\/api\/product\/list$/)
  606. })
  607. test('productDetail sends POST', async () => {
  608. mockSuccess({ code: 200 })
  609. await api.productDetail({ id: 1 })
  610. expect(lastRequest().url).toMatch(/\/api\/product\/detail$/)
  611. })
  612. test('productCreate sends POST', async () => {
  613. mockSuccess({ code: 200 })
  614. await api.productCreate({ name: '测试商品', price: 100 })
  615. expect(lastRequest().url).toMatch(/\/api\/product\/create$/)
  616. })
  617. test('productOrderCreate sends POST', async () => {
  618. mockSuccess({ code: 200 })
  619. await api.productOrderCreate({ productId: 1, quantity: 2 })
  620. expect(lastRequest().url).toMatch(/\/api\/product\/order\/create$/)
  621. })
  622. })
  623. // --------------- commission / invite ---------------
  624. describe('commission & invite API', () => {
  625. test('getReferralCode sends POST', async () => {
  626. mockSuccess({ code: 200 })
  627. await api.getReferralCode()
  628. expect(lastRequest().url).toMatch(/\/api\/invite\/code$/)
  629. })
  630. test('bindReferral sends POST with code', async () => {
  631. mockSuccess({ code: 200 })
  632. await api.bindReferral('ABC123')
  633. expect(lastRequest().data.referralCode).toBe('ABC123')
  634. })
  635. test('getCommissionSummary sends POST', async () => {
  636. mockSuccess({ code: 200 })
  637. await api.getCommissionSummary()
  638. expect(lastRequest().url).toMatch(/\/api\/commission\/summary$/)
  639. })
  640. test('applyWithdrawal sends POST', async () => {
  641. mockSuccess({ code: 200 })
  642. await api.applyWithdrawal(500, { bank: 'ICBC', account: '6222****' })
  643. expect(lastRequest().data.amount).toBe(500)
  644. })
  645. })
  646. // --------------- stats module ---------------
  647. describe('stats API', () => {
  648. test('getChildCompletionStats sends POST', async () => {
  649. mockSuccess({ code: 200 })
  650. await api.getChildCompletionStats({ childId: 5 })
  651. expect(lastRequest().url).toMatch(/\/api\/stats\/child-completion$/)
  652. })
  653. test('getChildOverview sends POST with childId', async () => {
  654. mockSuccess({ code: 200 })
  655. await api.getChildOverview(5)
  656. expect(lastRequest().data.childId).toBe(5)
  657. })
  658. })
  659. // --------------- AI chat module ---------------
  660. describe('AI chat API', () => {
  661. test('aiSendMessage sends POST', async () => {
  662. mockSuccess({ code: 200 })
  663. await api.aiSendMessage({ conversationId: 1, content: '你好' })
  664. expect(lastRequest().url).toMatch(/\/api\/ai\/chat\/send$/)
  665. })
  666. test('aiGetConversations sends POST', async () => {
  667. mockSuccess({ code: 200 })
  668. await api.aiGetConversations()
  669. expect(lastRequest().url).toMatch(/\/api\/ai\/chat\/conversations$/)
  670. })
  671. })
  672. // --------------- health / wealth module ---------------
  673. // --------------- health report module (new) ---------------
  674. describe('health report API', () => {
  675. test('createHealthReport sends POST', async () => {
  676. mockSuccess({ code: 200 })
  677. await api.createHealthReport({ childId: 5, reportData: { height: 120, weight: 22 } })
  678. expect(lastRequest().url).toMatch(/\/api\/health\/report\/create$/)
  679. expect(lastRequest().data.childId).toBe(5)
  680. })
  681. test('getReportList sends POST with childId', async () => {
  682. mockSuccess({ code: 200 })
  683. await api.getReportList(5)
  684. expect(lastRequest().url).toMatch(/\/api\/health\/report\/list$/)
  685. expect(lastRequest().data.childId).toBe(5)
  686. })
  687. test('getReportDetail sends POST with reportId', async () => {
  688. mockSuccess({ code: 200 })
  689. await api.getReportDetail(42)
  690. expect(lastRequest().url).toMatch(/\/api\/health\/report\/detail$/)
  691. expect(lastRequest().data.reportId).toBe(42)
  692. })
  693. test('getLatestHealthReport sends POST with childId', async () => {
  694. mockSuccess({ code: 200 })
  695. await api.getLatestHealthReport(5)
  696. expect(lastRequest().url).toMatch(/\/api\/health\/report\/latest$/)
  697. expect(lastRequest().data.childId).toBe(5)
  698. })
  699. test('getHealthIndicatorList sends POST with childId', async () => {
  700. mockSuccess({ code: 200 })
  701. await api.getHealthIndicatorList(5)
  702. expect(lastRequest().url).toMatch(/\/api\/health\/indicator\/list$/)
  703. expect(lastRequest().data.childId).toBe(5)
  704. })
  705. test('createReportSurvey sends POST with reportId and childId', async () => {
  706. mockSuccess({ code: 200 })
  707. await api.createReportSurvey(10, 5)
  708. expect(lastRequest().url).toMatch(/\/api\/health\/survey\/create$/)
  709. expect(lastRequest().data.reportId).toBe(10)
  710. expect(lastRequest().data.childId).toBe(5)
  711. })
  712. test('submitReportSurvey sends POST with surveyId and data', async () => {
  713. mockSuccess({ code: 200 })
  714. await api.submitReportSurvey(1, { q1: 'yes', q2: 'no' })
  715. expect(lastRequest().url).toMatch(/\/api\/health\/survey\/submit$/)
  716. expect(lastRequest().data.surveyId).toBe(1)
  717. expect(lastRequest().data.surveyData.q1).toBe('yes')
  718. })
  719. test('getSurveyStatus sends POST with reportId', async () => {
  720. mockSuccess({ code: 200 })
  721. await api.getSurveyStatus(10)
  722. expect(lastRequest().url).toMatch(/\/api\/health\/survey\/status$/)
  723. expect(lastRequest().data.reportId).toBe(10)
  724. })
  725. })
  726. describe('health & wealth API', () => {
  727. test('healthCheckinList sends POST', async () => {
  728. mockSuccess({ code: 200 })
  729. await api.healthCheckinList({ childId: 5 })
  730. expect(lastRequest().url).toMatch(/\/api\/health\/checkin\/list$/)
  731. })
  732. test('getCheckinList sends POST', async () => {
  733. mockSuccess({ code: 200 })
  734. await api.getCheckinList({ childId: 5 })
  735. expect(lastRequest().url).toMatch(/\/api\/wealth\/checkin\/list$/)
  736. })
  737. test('getInsuranceList sends POST', async () => {
  738. mockSuccess({ code: 200 })
  739. await api.getInsuranceList({ childId: 5 })
  740. expect(lastRequest().url).toMatch(/\/api\/wealth\/insurance\/list$/)
  741. })
  742. })
  743. // --------------- invite card module ---------------
  744. describe('invite card API', () => {
  745. test('generateInviteCard sends POST', async () => {
  746. mockSuccess({ code: 200 })
  747. await api.generateInviteCard('guide', 1)
  748. expect(lastRequest().data.type).toBe('guide')
  749. })
  750. test('verifyInviteCard sends POST with code', async () => {
  751. mockSuccess({ code: 200 })
  752. await api.verifyInviteCard('CARD123')
  753. expect(lastRequest().data.code).toBe('CARD123')
  754. })
  755. })
  756. // --------------- danshop module ---------------
  757. describe('danshop API', () => {
  758. test('danshopProductList sends POST', async () => {
  759. mockSuccess({ code: 200 })
  760. await api.danshopProductList({ page: 1 })
  761. const opts = lastRequest()
  762. expect(opts.url).toMatch(/\/api\/shop\/product\/list$/)
  763. })
  764. test('danshopCartAdd sends POST with data', async () => {
  765. mockSuccess({ code: 200 })
  766. await api.danshopCartAdd({ goodsId: 1, quantity: 2 })
  767. expect(lastRequest().data.goodsId).toBe(1)
  768. })
  769. test('danshopOrderCreate sends POST', async () => {
  770. mockSuccess({ code: 200 })
  771. await api.danshopOrderCreate({ goodsIds: [1, 2] })
  772. expect(lastRequest().url).toMatch(/\/api\/shop\/order\/create$/)
  773. })
  774. })