i18n-language-fallback.cy.ts 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336
  1. import { mockI18nSuccess, 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('不支持的语言自动回退到英文', () => {
  12. // 设置英文mock,因为不支持的语言会回退到英文
  13. mockI18nSuccess('en');
  14. const unsupportedLanguages = [
  15. 'fr-FR', // 法语
  16. 'de-DE', // 德语
  17. 'ja-JP', // 日语
  18. 'ko-KR', // 韩语
  19. 'es-ES', // 西班牙语
  20. 'it-IT', // 意大利语
  21. 'pt-BR', // 葡萄牙语
  22. 'ru-RU', // 俄语
  23. 'ar-SA', // 阿拉伯语
  24. 'hi-IN' // 印地语
  25. ];
  26. unsupportedLanguages.forEach((lang, index) => {
  27. mockI18nSuccess('en'); // 每次都重新设置英文mock
  28. cy.window().then((win) => {
  29. Object.defineProperty(win.navigator, 'language', {
  30. value: lang,
  31. writable: false
  32. });
  33. });
  34. loginPage.visit();
  35. // 验证请求的是英文资源
  36. cy.wait('@getI18nENSuccess');
  37. // 验证显示英文内容
  38. cy.get('body').should('contain', 'Patient Management');
  39. // 验证Redux状态为英文
  40. cy.window().its('store').invoke('getState').then((state) => {
  41. expect(state.i18n.currentLocale).to.equal('en');
  42. expect(state.i18n.messages).to.have.property('patient', 'Patient Management');
  43. });
  44. // 清理,准备下一个测试
  45. if (index < unsupportedLanguages.length - 1) {
  46. cy.clearAllSessionStorage();
  47. cy.clearAllLocalStorage();
  48. }
  49. });
  50. });
  51. it('无效的语言代码回退到英文', () => {
  52. mockI18nSuccess('en');
  53. const invalidLanguages = [
  54. 'invalid',
  55. 'xx-XX',
  56. '123',
  57. 'zh-INVALID',
  58. 'en-INVALID',
  59. '',
  60. null,
  61. undefined
  62. ];
  63. invalidLanguages.forEach((lang, index) => {
  64. mockI18nSuccess('en');
  65. cy.window().then((win) => {
  66. if (lang === null || lang === undefined) {
  67. // 模拟navigator.language为null或undefined的情况
  68. Object.defineProperty(win.navigator, 'language', {
  69. value: lang,
  70. writable: false
  71. });
  72. } else {
  73. Object.defineProperty(win.navigator, 'language', {
  74. value: lang,
  75. writable: false
  76. });
  77. }
  78. });
  79. loginPage.visit();
  80. cy.wait('@getI18nENSuccess');
  81. // 验证回退到英文
  82. cy.get('body').should('contain', 'Patient Management');
  83. cy.window().its('store').invoke('getState').then((state) => {
  84. expect(state.i18n.currentLocale).to.equal('en');
  85. });
  86. if (index < invalidLanguages.length - 1) {
  87. cy.clearAllSessionStorage();
  88. cy.clearAllLocalStorage();
  89. }
  90. });
  91. });
  92. it('浏览器不支持navigator.language时回退到英文', () => {
  93. mockI18nSuccess('en');
  94. cy.window().then((win) => {
  95. // 模拟navigator.language不存在的情况
  96. Object.defineProperty(win.navigator, 'language', {
  97. value: undefined,
  98. writable: false
  99. });
  100. });
  101. loginPage.visit();
  102. cy.wait('@getI18nENSuccess');
  103. // 验证回退到英文
  104. cy.get('body').should('contain', 'Patient Management');
  105. cy.window().its('store').invoke('getState').then((state) => {
  106. expect(state.i18n.currentLocale).to.equal('en');
  107. expect(state.i18n.messages).to.have.property('patient', 'Patient Management');
  108. });
  109. });
  110. it('验证语言回退的逻辑正确性', () => {
  111. // 测试语言检测和回退的完整逻辑
  112. const testCases = [
  113. // 支持的语言应该正常加载
  114. { input: 'zh-CN', expected: 'zh', expectedText: '患者管理' },
  115. { input: 'zh-TW', expected: 'zh', expectedText: '患者管理' },
  116. { input: 'en-US', expected: 'en', expectedText: 'Patient Management' },
  117. { input: 'en-GB', expected: 'en', expectedText: 'Patient Management' },
  118. // 不支持的语言应该回退到英文
  119. { input: 'fr-FR', expected: 'en', expectedText: 'Patient Management' },
  120. { input: 'de-DE', expected: 'en', expectedText: 'Patient Management' },
  121. { input: 'ja-JP', expected: 'en', expectedText: 'Patient Management' },
  122. // 边界情况
  123. { input: 'zh', expected: 'zh', expectedText: '患者管理' },
  124. { input: 'en', expected: 'en', expectedText: 'Patient Management' },
  125. { input: 'fr', expected: 'en', expectedText: 'Patient Management' }
  126. ];
  127. testCases.forEach(({ input, expected, expectedText }, index) => {
  128. mockI18nSuccess(expected as 'zh' | 'en');
  129. cy.window().then((win) => {
  130. Object.defineProperty(win.navigator, 'language', {
  131. value: input,
  132. writable: false
  133. });
  134. });
  135. loginPage.visit();
  136. cy.wait(`@getI18n${expected.toUpperCase()}Success`);
  137. // 验证正确的语言内容显示
  138. cy.get('body').should('contain', expectedText);
  139. // 验证Redux状态
  140. cy.window().its('store').invoke('getState').then((state) => {
  141. expect(state.i18n.currentLocale).to.equal(expected);
  142. });
  143. if (index < testCases.length - 1) {
  144. cy.clearAllSessionStorage();
  145. cy.clearAllLocalStorage();
  146. }
  147. });
  148. });
  149. it('验证回退语言的完整性', () => {
  150. mockI18nSuccess('en');
  151. // 使用不支持的语言
  152. cy.window().then((win) => {
  153. Object.defineProperty(win.navigator, 'language', {
  154. value: 'fr-FR',
  155. writable: false
  156. });
  157. });
  158. loginPage.visit();
  159. cy.wait('@getI18nENSuccess');
  160. // 验证回退到英文后,所有必要的翻译都存在
  161. cy.window().its('store').invoke('getState').then((state) => {
  162. expect(state.i18n.currentLocale).to.equal('en');
  163. expect(state.i18n.loading).to.be.false;
  164. expect(state.i18n.error).to.be.null;
  165. // 验证关键翻译存在
  166. expect(state.i18n.messages).to.have.property('patient', 'Patient Management');
  167. expect(state.i18n.messages).to.have.property('register', 'Register');
  168. expect(state.i18n.messages).to.have.property('worklist', 'Task List');
  169. expect(state.i18n.messages).to.have.property('register.patientId', 'Patient ID');
  170. expect(state.i18n.messages).to.have.property('register.patientName', 'Patient Name');
  171. });
  172. // 验证页面显示正确
  173. cy.get('body').should('contain', 'Patient Management');
  174. });
  175. it('验证多次语言回退的稳定性', () => {
  176. // 连续测试多个不支持的语言,验证每次都能正确回退
  177. const unsupportedSequence = ['fr-FR', 'de-DE', 'ja-JP', 'ko-KR', 'es-ES'];
  178. unsupportedSequence.forEach((lang, index) => {
  179. mockI18nSuccess('en');
  180. cy.window().then((win) => {
  181. Object.defineProperty(win.navigator, 'language', {
  182. value: lang,
  183. writable: false
  184. });
  185. });
  186. loginPage.visit();
  187. cy.wait('@getI18nENSuccess');
  188. // 每次都应该回退到英文
  189. cy.get('body').should('contain', 'Patient Management');
  190. cy.window().its('store').invoke('getState').then((state) => {
  191. expect(state.i18n.currentLocale).to.equal('en');
  192. expect(state.i18n.loading).to.be.false;
  193. expect(state.i18n.error).to.be.null;
  194. });
  195. if (index < unsupportedSequence.length - 1) {
  196. cy.clearAllSessionStorage();
  197. cy.clearAllLocalStorage();
  198. }
  199. });
  200. });
  201. it('验证语言回退时的请求参数', () => {
  202. mockI18nSuccess('en');
  203. cy.window().then((win) => {
  204. Object.defineProperty(win.navigator, 'language', {
  205. value: 'fr-FR', // 不支持的语言
  206. writable: false
  207. });
  208. });
  209. loginPage.visit();
  210. // 验证请求的是英文资源,而不是法语资源
  211. cy.wait('@getI18nENSuccess').then((interception) => {
  212. expect(interception.request.url).to.include('/dr/api/v1/pub/trans/en/en.js');
  213. expect(interception.request.url).to.not.include('/dr/api/v1/pub/trans/fr/fr.js');
  214. expect(interception.request.method).to.equal('GET');
  215. });
  216. });
  217. it('验证回退语言加载失败时的处理', () => {
  218. // 模拟回退语言(英文)加载失败的情况
  219. cy.intercept('GET', '/dr/api/v1/pub/trans/en/en.js', (req) => {
  220. req.reply({
  221. statusCode: 404,
  222. body: { message: 'Not Found' }
  223. });
  224. }).as('getI18nENError');
  225. cy.window().then((win) => {
  226. Object.defineProperty(win.navigator, 'language', {
  227. value: 'fr-FR', // 不支持的语言,会回退到英文
  228. writable: false
  229. });
  230. });
  231. loginPage.visit();
  232. cy.wait('@getI18nENError');
  233. // 验证即使回退语言加载失败,也能正确显示错误
  234. cy.contains('多语言资源加载失败').should('be.visible');
  235. cy.contains('重新加载').should('be.visible');
  236. cy.window().its('store').invoke('getState').then((state) => {
  237. expect(state.i18n.loading).to.be.false;
  238. expect(state.i18n.error).to.not.be.null;
  239. // 注意:currentLocale可能仍然是'en',因为这是尝试加载的语言
  240. expect(state.i18n.messages).to.deep.equal({});
  241. });
  242. });
  243. it('验证语言回退不影响其他功能', () => {
  244. mockI18nSuccess('en');
  245. cy.window().then((win) => {
  246. Object.defineProperty(win.navigator, 'language', {
  247. value: 'de-DE', // 不支持的语言
  248. writable: false
  249. });
  250. });
  251. loginPage.visit();
  252. cy.wait('@getI18nENSuccess');
  253. // 验证语言回退后,其他Redux状态正常
  254. cy.window().its('store').invoke('getState').then((state) => {
  255. // 验证其他slice的状态不受影响
  256. expect(state.product).to.exist;
  257. expect(state.userInfo).to.exist;
  258. // i18n状态正确
  259. expect(state.i18n.currentLocale).to.equal('en');
  260. expect(state.i18n.loading).to.be.false;
  261. expect(state.i18n.error).to.be.null;
  262. });
  263. // 验证页面正常渲染
  264. cy.get('body').should('exist');
  265. cy.get('body').should('contain', 'Patient Management');
  266. });
  267. });