i18n-invalid-format.cy.ts 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289
  1. import { mockI18nInvalidFormat, mockI18nEmptyData, mockAllRequiredAPIs } from '../../support/mock/handlers/i18n';
  2. import LoginPage from '../../support/pageObjects/LoginPage';
  3. describe('多语言资源格式异常测试', () => {
  4. const loginPage = new LoginPage();
  5. beforeEach(() => {
  6. cy.clearAllSessionStorage();
  7. cy.clearAllLocalStorage();
  8. // Mock所有必要的API,避免影响页面加载
  9. mockAllRequiredAPIs();
  10. });
  11. it('API返回非JSON格式数据时正确处理', () => {
  12. mockI18nInvalidFormat('zh');
  13. cy.window().then((win) => {
  14. Object.defineProperty(win.navigator, 'language', {
  15. value: 'zh-CN',
  16. writable: false
  17. });
  18. });
  19. loginPage.visit();
  20. cy.wait('@getI18nZHInvalidFormat');
  21. // 验证错误提示显示
  22. cy.contains('多语言资源加载失败').should('be.visible');
  23. cy.contains('重新加载').should('be.visible');
  24. // 验证Redux状态反映错误
  25. cy.window().its('store').invoke('getState').then((state) => {
  26. expect(state.i18n.loading).to.be.false;
  27. expect(state.i18n.error).to.not.be.null;
  28. expect(state.i18n.messages).to.deep.equal({});
  29. });
  30. });
  31. it('API返回空对象时正确处理', () => {
  32. mockI18nEmptyData('en');
  33. cy.window().then((win) => {
  34. Object.defineProperty(win.navigator, 'language', {
  35. value: 'en-US',
  36. writable: false
  37. });
  38. });
  39. loginPage.visit();
  40. cy.wait('@getI18nENEmptyData');
  41. // 空数据应该被正常处理,不显示错误
  42. cy.contains('多语言资源加载失败').should('not.exist');
  43. // 验证Redux状态
  44. cy.window().its('store').invoke('getState').then((state) => {
  45. expect(state.i18n.loading).to.be.false;
  46. expect(state.i18n.error).to.be.null;
  47. expect(state.i18n.currentLocale).to.equal('en');
  48. expect(state.i18n.messages).to.deep.equal({});
  49. });
  50. });
  51. it('API返回null数据时正确处理', () => {
  52. cy.intercept('GET', '/dr/api/v1/pub/trans/zh/zh.js', (req) => {
  53. req.reply({
  54. statusCode: 200,
  55. body: null
  56. });
  57. }).as('getI18nZHNull');
  58. cy.window().then((win) => {
  59. Object.defineProperty(win.navigator, 'language', {
  60. value: 'zh-CN',
  61. writable: false
  62. });
  63. });
  64. loginPage.visit();
  65. cy.wait('@getI18nZHNull');
  66. // 验证错误处理
  67. cy.contains('多语言资源加载失败').should('be.visible');
  68. // 验证Redux状态
  69. cy.window().its('store').invoke('getState').then((state) => {
  70. expect(state.i18n.loading).to.be.false;
  71. expect(state.i18n.error).to.not.be.null;
  72. });
  73. });
  74. it('API返回数组格式数据时正确处理', () => {
  75. cy.intercept('GET', '/dr/api/v1/pub/trans/en/en.js', (req) => {
  76. req.reply({
  77. statusCode: 200,
  78. body: ['invalid', 'array', 'format']
  79. });
  80. }).as('getI18nENArray');
  81. cy.window().then((win) => {
  82. Object.defineProperty(win.navigator, 'language', {
  83. value: 'en-US',
  84. writable: false
  85. });
  86. });
  87. loginPage.visit();
  88. cy.wait('@getI18nENArray');
  89. // 数组格式应该被当作错误处理
  90. cy.contains('多语言资源加载失败').should('be.visible');
  91. // 验证Redux状态
  92. cy.window().its('store').invoke('getState').then((state) => {
  93. expect(state.i18n.loading).to.be.false;
  94. expect(state.i18n.error).to.not.be.null;
  95. });
  96. });
  97. it('API返回部分缺失字段的数据时正确处理', () => {
  98. cy.intercept('GET', '/dr/api/v1/pub/trans/zh/zh.js', (req) => {
  99. req.reply({
  100. statusCode: 200,
  101. body: {
  102. // 只有部分字段
  103. greeting: '你好',
  104. // 缺少其他必要字段
  105. }
  106. });
  107. }).as('getI18nZHPartial');
  108. cy.window().then((win) => {
  109. Object.defineProperty(win.navigator, 'language', {
  110. value: 'zh-CN',
  111. writable: false
  112. });
  113. });
  114. loginPage.visit();
  115. cy.wait('@getI18nZHPartial');
  116. // 部分数据应该被正常处理
  117. cy.contains('多语言资源加载失败').should('not.exist');
  118. // 验证Redux状态包含部分数据
  119. cy.window().its('store').invoke('getState').then((state) => {
  120. expect(state.i18n.loading).to.be.false;
  121. expect(state.i18n.error).to.be.null;
  122. expect(state.i18n.currentLocale).to.equal('zh');
  123. expect(state.i18n.messages).to.have.property('greeting', '你好');
  124. expect(state.i18n.messages).to.not.have.property('patient');
  125. });
  126. });
  127. it('API返回包含特殊字符的数据时正确处理', () => {
  128. cy.intercept('GET', '/dr/api/v1/pub/trans/en/en.js', (req) => {
  129. req.reply({
  130. statusCode: 200,
  131. body: {
  132. 'special.key': 'Value with special chars: @#$%^&*()',
  133. 'unicode.key': '测试 Unicode 字符 🚀 ✅ ❌',
  134. 'html.key': '<script>alert("test")</script>',
  135. 'json.key': '{"nested": "json"}',
  136. 'empty.key': '',
  137. 'space.key': ' ',
  138. 'newline.key': 'Line 1\nLine 2\nLine 3'
  139. }
  140. });
  141. }).as('getI18nENSpecial');
  142. cy.window().then((win) => {
  143. Object.defineProperty(win.navigator, 'language', {
  144. value: 'en-US',
  145. writable: false
  146. });
  147. });
  148. loginPage.visit();
  149. cy.wait('@getI18nENSpecial');
  150. // 特殊字符数据应该被正常处理
  151. cy.contains('多语言资源加载失败').should('not.exist');
  152. // 验证Redux状态包含特殊字符数据
  153. cy.window().its('store').invoke('getState').then((state) => {
  154. expect(state.i18n.loading).to.be.false;
  155. expect(state.i18n.error).to.be.null;
  156. expect(state.i18n.currentLocale).to.equal('en');
  157. expect(state.i18n.messages).to.have.property('special.key', 'Value with special chars: @#$%^&*()');
  158. expect(state.i18n.messages).to.have.property('unicode.key', '测试 Unicode 字符 🚀 ✅ ❌');
  159. expect(state.i18n.messages).to.have.property('html.key', '<script>alert("test")</script>');
  160. expect(state.i18n.messages).to.have.property('empty.key', '');
  161. });
  162. });
  163. it('API返回超大数据时正确处理', () => {
  164. // 生成大量数据
  165. const largeData = {};
  166. for (let i = 0; i < 1000; i++) {
  167. largeData[`key_${i}`] = `Value ${i} - ${'x'.repeat(100)}`;
  168. }
  169. cy.intercept('GET', '/dr/api/v1/pub/trans/zh/zh.js', (req) => {
  170. req.reply({
  171. statusCode: 200,
  172. body: largeData
  173. });
  174. }).as('getI18nZHLarge');
  175. cy.window().then((win) => {
  176. Object.defineProperty(win.navigator, 'language', {
  177. value: 'zh-CN',
  178. writable: false
  179. });
  180. });
  181. loginPage.visit();
  182. cy.wait('@getI18nZHLarge');
  183. // 大数据应该被正常处理
  184. cy.contains('多语言资源加载失败').should('not.exist');
  185. // 验证Redux状态包含大量数据
  186. cy.window().its('store').invoke('getState').then((state) => {
  187. expect(state.i18n.loading).to.be.false;
  188. expect(state.i18n.error).to.be.null;
  189. expect(state.i18n.currentLocale).to.equal('zh');
  190. expect(Object.keys(state.i18n.messages)).to.have.length(1000);
  191. expect(state.i18n.messages).to.have.property('key_0', 'Value 0 - ' + 'x'.repeat(100));
  192. expect(state.i18n.messages).to.have.property('key_999', 'Value 999 - ' + 'x'.repeat(100));
  193. });
  194. });
  195. it('验证格式异常后的重新加载功能', () => {
  196. // 首先返回无效格式
  197. mockI18nInvalidFormat('zh');
  198. cy.window().then((win) => {
  199. Object.defineProperty(win.navigator, 'language', {
  200. value: 'zh-CN',
  201. writable: false
  202. });
  203. });
  204. loginPage.visit();
  205. cy.wait('@getI18nZHInvalidFormat');
  206. // 验证错误显示
  207. cy.contains('多语言资源加载失败').should('be.visible');
  208. cy.contains('重新加载').should('be.visible');
  209. // 设置正确的mock用于重新加载
  210. cy.intercept('GET', '/dr/api/v1/pub/trans/zh/zh.js', (req) => {
  211. req.reply({
  212. statusCode: 200,
  213. body: {
  214. greeting: '你好,世界!',
  215. patient: '患者管理'
  216. }
  217. });
  218. }).as('getI18nZHFixed');
  219. // 点击重新加载
  220. cy.contains('重新加载').click();
  221. cy.wait('@getI18nZHFixed');
  222. // 验证成功加载
  223. cy.contains('多语言资源加载失败').should('not.exist');
  224. cy.get('body').should('contain', '患者管理');
  225. // 验证Redux状态恢复正常
  226. cy.window().its('store').invoke('getState').then((state) => {
  227. expect(state.i18n.loading).to.be.false;
  228. expect(state.i18n.error).to.be.null;
  229. expect(state.i18n.currentLocale).to.equal('zh');
  230. expect(state.i18n.messages).to.have.property('patient', '患者管理');
  231. });
  232. });
  233. });