register-clear-selected-views.cy.ts 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409
  1. /**
  2. * 测试文件: 患者注册 - 切换患者类型清空已选体位
  3. * 功能: 验证切换患者类型时自动清空已选择体位列表
  4. *
  5. * 测试场景:
  6. * 1. 切换患者类型时清空已选体位列表
  7. * 2. 切换协议/体位模式时保持已选体位
  8. * 3. 切换身体部位时保持已选体位
  9. *
  10. * 相关需求文档: docs/实现/注册时体位过滤-需求-实现.md
  11. */
  12. import { mockLoginSuccess } from '../../support/mock/handlers/user';
  13. import {
  14. mockGetMultiplePatientTypes,
  15. mockGetBodyPartForHuman,
  16. mockGetBodyPartForSpecialType,
  17. mockGetViewsForHumanSkull,
  18. mockGetProceduresForHumanSkull,
  19. mockGetViewsByProcedure,
  20. } from '../../support/mock/handlers/patientRegistration';
  21. describe('患者注册:切换患者类型清空已选体位列表', () => {
  22. beforeEach(() => {
  23. // 设置所有必要的 Mock
  24. mockLoginSuccess();
  25. mockGetMultiplePatientTypes();
  26. mockGetBodyPartForHuman();
  27. mockGetBodyPartForSpecialType();
  28. mockGetViewsForHumanSkull();
  29. mockGetProceduresForHumanSkull();
  30. mockGetViewsByProcedure();
  31. // 登录并导航到患者注册页面
  32. cy.visit('/');
  33. cy.get('[data-testid="username"]').type('admin');
  34. cy.get('[data-testid="password"]').type('123456');
  35. cy.get('[data-testid="login-button"]').click();
  36. cy.wait('@loginSuccess');
  37. // 导航到患者管理 -> 注册页面
  38. cy.get('[data-testid="patient-management"]').click();
  39. cy.get('[data-testid="register"]').click();
  40. });
  41. /**
  42. * 测试场景 1:切换患者类型时清空已选体位列表
  43. *
  44. * Given: 用户已在患者注册页面,已选择患者类型、身体部位并添加了体位
  45. * When: 用户切换到不同的患者类型
  46. * Then: 已选择体位列表应该被清空
  47. */
  48. it('应该在切换患者类型时清空已选择的体位列表', () => {
  49. /**
  50. * Given: 用户在患者注册页面
  51. * 步骤1: 页面已加载患者类型列表
  52. */
  53. cy.wait('@getMultiplePatientTypes');
  54. /**
  55. * When: 选择患者类型 "Human"
  56. * 步骤2: 点击患者类型选择器
  57. * 步骤3: 选择 "Human" 类型
  58. */
  59. cy.get('[data-testid="patient-type-selector"]').click();
  60. cy.get('[data-testid="patient-type-option-Human"]').click();
  61. cy.wait('@getBodyPartForHuman');
  62. /**
  63. * When: 选择身体部位 "颅骨"
  64. * 步骤4: 点击身体部位选择器
  65. * 步骤5: 选择 "颅骨"
  66. */
  67. cy.get('[data-testid="body-part-selector"]').click();
  68. cy.get('[data-testid="body-part-option-Human_SKULL"]').click();
  69. /**
  70. * When: 切换到 "体位" 选择模式
  71. * 步骤6: 点击 "体位" 标签
  72. */
  73. cy.get('[data-testid="selection-mode-view"]').click();
  74. cy.wait('@getViewsForHumanSkull');
  75. /**
  76. * When: 添加体位到已选列表
  77. * 步骤7: 点击第一个体位(颅骨前后位)
  78. */
  79. cy.get('[data-testid="view-item-View_DX_H_SKULL_AP"]').click();
  80. /**
  81. * Then: 验证已选列表包含 1 个体位
  82. * 步骤8: 检查已选列表
  83. */
  84. cy.get('[data-testid="selected-views-list"]')
  85. .find('[data-testid^="selected-view-"]')
  86. .should('have.length', 1);
  87. cy.get('[data-testid="selected-view-count"]')
  88. .should('contain', '1');
  89. /**
  90. * When: 切换患者类型到 "SpecialType"
  91. * 步骤9: 打开患者类型选择器
  92. * 步骤10: 选择 "SpecialType"
  93. */
  94. cy.get('[data-testid="patient-type-selector"]').click();
  95. cy.get('[data-testid="patient-type-option-SpecialType"]').click();
  96. cy.wait('@getBodyPartForSpecialType');
  97. /**
  98. * Then: 已选择体位列表应该被清空
  99. * 步骤11: 验证已选列表为空
  100. */
  101. cy.get('[data-testid="selected-views-list"]')
  102. .find('[data-testid^="selected-view-"]')
  103. .should('have.length', 0);
  104. cy.get('[data-testid="selected-view-count"]')
  105. .should('contain', '0');
  106. /**
  107. * Then: 验证 Redux store 中的状态
  108. * 步骤12: 检查 Redux store
  109. */
  110. cy.window().its('store').invoke('getState')
  111. .its('viewSelection')
  112. .its('selectedViews')
  113. .should('have.length', 0);
  114. });
  115. /**
  116. * 测试场景 2:切换协议/体位模式时保持已选体位
  117. *
  118. * Given: 用户已选择患者类型、身体部位,并在协议模式下添加了协议
  119. * When: 用户切换到体位选择模式
  120. * Then: 已选择体位列表应该保持不变
  121. */
  122. it('应该在切换协议/体位模式时保持已选择的体位列表', () => {
  123. /**
  124. * Given: 选择患者类型和身体部位
  125. */
  126. cy.wait('@getMultiplePatientTypes');
  127. cy.get('[data-testid="patient-type-selector"]').click();
  128. cy.get('[data-testid="patient-type-option-Human"]').click();
  129. cy.wait('@getBodyPartForHuman');
  130. cy.get('[data-testid="body-part-selector"]').click();
  131. cy.get('[data-testid="body-part-option-Human_SKULL"]').click();
  132. /**
  133. * When: 在协议模式下添加协议
  134. * 步骤1: 确保在协议模式(默认)
  135. * 步骤2: 等待协议列表加载
  136. */
  137. cy.get('[data-testid="selection-mode-protocol"]').should('have.class', 'active');
  138. cy.wait('@getProceduresForHumanSkull');
  139. /**
  140. * When: 点击协议添加体位
  141. * 步骤3: 点击协议
  142. */
  143. cy.get('[data-testid="procedure-item-P_SKULL_AP_LAT"]').click();
  144. cy.wait('@getViewsByProcedure');
  145. /**
  146. * Then: 验证已选列表包含体位
  147. * 步骤4: 检查已选列表(协议包含2个体位)
  148. */
  149. cy.get('[data-testid="selected-views-list"]')
  150. .find('[data-testid^="selected-view-"]')
  151. .should('have.length', 2);
  152. /**
  153. * When: 切换到体位模式
  154. * 步骤5: 点击体位标签
  155. */
  156. cy.get('[data-testid="selection-mode-view"]').click();
  157. cy.wait('@getViewsForHumanSkull');
  158. /**
  159. * Then: 已选列表应该保持不变
  160. * 步骤6: 验证已选列表仍然包含 2 个体位
  161. */
  162. cy.get('[data-testid="selected-views-list"]')
  163. .find('[data-testid^="selected-view-"]')
  164. .should('have.length', 2);
  165. cy.get('[data-testid="selected-view-count"]')
  166. .should('contain', '2');
  167. /**
  168. * Then: 验证 Redux store 状态
  169. */
  170. cy.window().its('store').invoke('getState')
  171. .its('viewSelection')
  172. .its('selectedViews')
  173. .should('have.length', 2);
  174. });
  175. /**
  176. * 测试场景 3:切换身体部位时保持已选体位
  177. *
  178. * Given: 用户已选择患者类型、身体部位并添加了体位
  179. * When: 用户切换到不同的身体部位
  180. * Then: 已选择体位列表应该保持不变
  181. */
  182. it('应该在切换身体部位时保持已选择的体位列表', () => {
  183. /**
  184. * Given: 选择患者类型 "Human"
  185. */
  186. cy.wait('@getMultiplePatientTypes');
  187. cy.get('[data-testid="patient-type-selector"]').click();
  188. cy.get('[data-testid="patient-type-option-Human"]').click();
  189. cy.wait('@getBodyPartForHuman');
  190. /**
  191. * Given: 选择身体部位 "颅骨"
  192. */
  193. cy.get('[data-testid="body-part-selector"]').click();
  194. cy.get('[data-testid="body-part-option-Human_SKULL"]').click();
  195. /**
  196. * Given: 切换到体位模式并添加体位
  197. */
  198. cy.get('[data-testid="selection-mode-view"]').click();
  199. cy.wait('@getViewsForHumanSkull');
  200. cy.get('[data-testid="view-item-View_DX_H_SKULL_AP"]').click();
  201. /**
  202. * Then: 验证已选列表包含 1 个体位
  203. */
  204. cy.get('[data-testid="selected-views-list"]')
  205. .find('[data-testid^="selected-view-"]')
  206. .should('have.length', 1);
  207. /**
  208. * When: 切换身体部位到 "颈部"
  209. * 步骤1: 打开身体部位选择器
  210. * 步骤2: 选择 "颈部"
  211. */
  212. cy.get('[data-testid="body-part-selector"]').click();
  213. cy.get('[data-testid="body-part-option-Human_NECK"]').click();
  214. /**
  215. * Then: 已选列表应该保持不变
  216. * 步骤3: 验证已选列表仍然包含 1 个体位
  217. */
  218. cy.get('[data-testid="selected-views-list"]')
  219. .find('[data-testid^="selected-view-"]')
  220. .should('have.length', 1);
  221. cy.get('[data-testid="selected-view-count"]')
  222. .should('contain', '1');
  223. /**
  224. * Then: 验证 Redux store 状态
  225. */
  226. cy.window().its('store').invoke('getState')
  227. .its('viewSelection')
  228. .its('selectedViews')
  229. .should('have.length', 1);
  230. });
  231. /**
  232. * 测试场景 4:边界情况 - 已选列表为空时切换患者类型
  233. *
  234. * Given: 用户已选择患者类型但未添加任何体位
  235. * When: 用户切换到不同的患者类型
  236. * Then: 不应该产生错误
  237. */
  238. it('应该在已选列表为空时安全地切换患者类型', () => {
  239. /**
  240. * Given: 选择患者类型但不添加体位
  241. */
  242. cy.wait('@getMultiplePatientTypes');
  243. cy.get('[data-testid="patient-type-selector"]').click();
  244. cy.get('[data-testid="patient-type-option-Human"]').click();
  245. cy.wait('@getBodyPartForHuman');
  246. /**
  247. * Then: 验证已选列表为空
  248. */
  249. cy.get('[data-testid="selected-views-list"]')
  250. .find('[data-testid^="selected-view-"]')
  251. .should('have.length', 0);
  252. /**
  253. * When: 切换患者类型
  254. */
  255. cy.get('[data-testid="patient-type-selector"]').click();
  256. cy.get('[data-testid="patient-type-option-SpecialType"]').click();
  257. cy.wait('@getBodyPartForSpecialType');
  258. /**
  259. * Then: 应该没有错误,已选列表仍为空
  260. */
  261. cy.get('[data-testid="selected-views-list"]')
  262. .find('[data-testid^="selected-view-"]')
  263. .should('have.length', 0);
  264. // 验证没有错误提示
  265. cy.get('[data-testid="error-message"]').should('not.exist');
  266. });
  267. /**
  268. * 测试场景 5:边界情况 - 已选多个体位时切换患者类型
  269. *
  270. * Given: 用户已添加多个体位到已选列表
  271. * When: 用户切换患者类型
  272. * Then: 所有体位都应该被清空
  273. */
  274. it('应该在切换患者类型时清空所有已选择的体位', () => {
  275. /**
  276. * Given: 选择患者类型和身体部位
  277. */
  278. cy.wait('@getMultiplePatientTypes');
  279. cy.get('[data-testid="patient-type-selector"]').click();
  280. cy.get('[data-testid="patient-type-option-Human"]').click();
  281. cy.wait('@getBodyPartForHuman');
  282. cy.get('[data-testid="body-part-selector"]').click();
  283. cy.get('[data-testid="body-part-option-Human_SKULL"]').click();
  284. /**
  285. * Given: 切换到体位模式并添加多个体位
  286. */
  287. cy.get('[data-testid="selection-mode-view"]').click();
  288. cy.wait('@getViewsForHumanSkull');
  289. // 添加第一个体位
  290. cy.get('[data-testid="view-item-View_DX_H_SKULL_AP"]').click();
  291. // 添加第二个体位
  292. cy.get('[data-testid="view-item-View_DX_H_SKULL_LAT"]').click();
  293. /**
  294. * Then: 验证已选列表包含 2 个体位
  295. */
  296. cy.get('[data-testid="selected-views-list"]')
  297. .find('[data-testid^="selected-view-"]')
  298. .should('have.length', 2);
  299. /**
  300. * When: 切换患者类型
  301. */
  302. cy.get('[data-testid="patient-type-selector"]').click();
  303. cy.get('[data-testid="patient-type-option-SpecialType"]').click();
  304. cy.wait('@getBodyPartForSpecialType');
  305. /**
  306. * Then: 所有体位都应该被清空
  307. */
  308. cy.get('[data-testid="selected-views-list"]')
  309. .find('[data-testid^="selected-view-"]')
  310. .should('have.length', 0);
  311. cy.get('[data-testid="selected-view-count"]')
  312. .should('contain', '0');
  313. });
  314. /**
  315. * 测试场景 6:验证控制台日志输出
  316. *
  317. * Given: 用户已添加体位
  318. * When: 切换患者类型
  319. * Then: 应该在控制台输出清空体位的日志
  320. */
  321. it('应该在切换患者类型时输出正确的控制台日志', () => {
  322. /**
  323. * 监听控制台日志
  324. */
  325. cy.window().then((win) => {
  326. cy.spy(win.console, 'log').as('consoleLog');
  327. });
  328. /**
  329. * Given: 添加体位到已选列表
  330. */
  331. cy.wait('@getMultiplePatientTypes');
  332. cy.get('[data-testid="patient-type-selector"]').click();
  333. cy.get('[data-testid="patient-type-option-Human"]').click();
  334. cy.wait('@getBodyPartForHuman');
  335. cy.get('[data-testid="body-part-selector"]').click();
  336. cy.get('[data-testid="body-part-option-Human_SKULL"]').click();
  337. cy.get('[data-testid="selection-mode-view"]').click();
  338. cy.wait('@getViewsForHumanSkull');
  339. cy.get('[data-testid="view-item-View_DX_H_SKULL_AP"]').click();
  340. /**
  341. * When: 切换患者类型
  342. */
  343. cy.get('[data-testid="patient-type-selector"]').click();
  344. cy.get('[data-testid="patient-type-option-SpecialType"]').click();
  345. cy.wait('@getBodyPartForSpecialType');
  346. /**
  347. * Then: 验证控制台输出了清空日志
  348. */
  349. cy.get('@consoleLog').should('be.calledWith', '患者类型已变更,已选择体位列表已清空');
  350. });
  351. });