import { mockI18nTimeout, mockAllRequiredAPIs } from '../../support/mock/handlers/i18n'; import LoginPage from '../../support/pageObjects/LoginPage'; describe('多语言资源网络超时测试', () => { const loginPage = new LoginPage(); beforeEach(() => { cy.clearAllSessionStorage(); cy.clearAllLocalStorage(); // Mock所有必要的API,避免影响页面加载 mockAllRequiredAPIs(); }); it('API请求超时时正确处理', () => { mockI18nTimeout('zh'); cy.window().then((win) => { Object.defineProperty(win.navigator, 'language', { value: 'zh-CN', writable: false }); }); loginPage.visit(); // 验证加载状态显示 cy.contains('加载多语言资源中...').should('be.visible'); // 等待超时请求(这里我们不会真的等30秒,而是验证请求被发起) cy.wait('@getI18nZHTimeout', { timeout: 5000 }).then((interception) => { // 验证请求确实被发起 expect(interception.request.url).to.include('/dr/api/v1/pub/trans/zh/zh.js'); }); // 由于实际测试中不会等待30秒,我们模拟超时后的状态 // 在真实场景中,这会触发错误处理 }); it('模拟真实超时场景的错误处理', () => { // 设置一个较短的延迟来模拟超时,然后返回错误 cy.intercept('GET', '/dr/api/v1/pub/trans/en/en.js', (req) => { // 模拟网络超时错误 req.reply({ statusCode: 408, // Request Timeout body: { message: 'Request Timeout', error: 'The request timed out' } }); }).as('getI18nENTimeoutError'); cy.window().then((win) => { Object.defineProperty(win.navigator, 'language', { value: 'en-US', writable: false }); }); loginPage.visit(); cy.wait('@getI18nENTimeoutError'); // 验证超时错误的处理 cy.contains('多语言资源加载失败').should('be.visible'); cy.contains('重新加载').should('be.visible'); // 验证Redux状态反映超时错误 cy.window().its('store').invoke('getState').then((state) => { expect(state.i18n.loading).to.be.false; expect(state.i18n.error).to.not.be.null; expect(state.i18n.messages).to.deep.equal({}); }); }); it('验证超时后重新加载的成功恢复', () => { // 首先模拟超时错误 cy.intercept('GET', '/dr/api/v1/pub/trans/zh/zh.js', (req) => { req.reply({ statusCode: 408, body: { message: 'Request Timeout' } }); }).as('getI18nZHTimeoutFirst'); cy.window().then((win) => { Object.defineProperty(win.navigator, 'language', { value: 'zh-CN', writable: false }); }); loginPage.visit(); cy.wait('@getI18nZHTimeoutFirst'); // 验证超时错误显示 cy.contains('多语言资源加载失败').should('be.visible'); cy.contains('重新加载').should('be.visible'); // 设置成功的mock用于重新加载 cy.intercept('GET', '/dr/api/v1/pub/trans/zh/zh.js', (req) => { req.reply({ statusCode: 200, body: { greeting: '你好,世界!', patient: '患者管理', register: '注册' } }); }).as('getI18nZHSuccess'); // 点击重新加载 cy.contains('重新加载').click(); cy.wait('@getI18nZHSuccess'); // 验证成功恢复 cy.contains('多语言资源加载失败').should('not.exist'); cy.get('body').should('contain', '患者管理'); // 验证Redux状态恢复正常 cy.window().its('store').invoke('getState').then((state) => { expect(state.i18n.loading).to.be.false; expect(state.i18n.error).to.be.null; expect(state.i18n.currentLocale).to.equal('zh'); expect(state.i18n.messages).to.have.property('patient', '患者管理'); }); }); it('验证慢网络环境下的用户体验', () => { // 模拟慢网络,但不超时 cy.intercept('GET', '/dr/api/v1/pub/trans/en/en.js', (req) => { req.reply({ delay: 3000, // 3秒延迟,模拟慢网络 statusCode: 200, body: { greeting: 'Hello, world!', patient: 'Patient Management' } }); }).as('getI18nENSlow'); cy.window().then((win) => { Object.defineProperty(win.navigator, 'language', { value: 'en-US', writable: false }); }); loginPage.visit(); // 验证加载状态持续显示 cy.contains('加载多语言资源中...').should('be.visible'); // 验证加载状态的样式 cy.get('div').contains('加载多语言资源中...').should('be.visible') .parent().should('have.css', 'display', 'flex') .and('have.css', 'justify-content', 'center') .and('have.css', 'align-items', 'center'); // 等待慢网络请求完成 cy.wait('@getI18nENSlow'); // 验证最终成功加载 cy.contains('加载多语言资源中...').should('not.exist'); cy.get('body').should('contain', 'Patient Management'); }); it('验证多次超时重试的行为', () => { let retryCount = 0; // 设置动态mock,前两次超时,第三次成功 cy.intercept('GET', '/dr/api/v1/pub/trans/zh/zh.js', (req) => { retryCount++; if (retryCount <= 2) { req.reply({ statusCode: 408, body: { message: 'Request Timeout' } }); } else { req.reply({ statusCode: 200, body: { greeting: '你好,世界!', patient: '患者管理' } }); } }).as('getI18nZHRetry'); cy.window().then((win) => { Object.defineProperty(win.navigator, 'language', { value: 'zh-CN', writable: false }); }); loginPage.visit(); // 第一次超时 cy.wait('@getI18nZHRetry'); cy.contains('多语言资源加载失败').should('be.visible'); // 第一次重试,仍然超时 cy.contains('重新加载').click(); cy.wait('@getI18nZHRetry'); cy.contains('多语言资源加载失败').should('be.visible'); // 第二次重试,成功 cy.contains('重新加载').click(); cy.wait('@getI18nZHRetry'); // 验证最终成功 cy.contains('多语言资源加载失败').should('not.exist'); cy.get('body').should('contain', '患者管理'); }); it('验证超时错误的详细信息显示', () => { cy.intercept('GET', '/dr/api/v1/pub/trans/en/en.js', (req) => { req.reply({ statusCode: 408, body: { message: 'Request Timeout', error: 'The server did not respond within the expected time', code: 'TIMEOUT_ERROR' } }); }).as('getI18nENTimeoutDetail'); cy.window().then((win) => { Object.defineProperty(win.navigator, 'language', { value: 'en-US', writable: false }); }); loginPage.visit(); cy.wait('@getI18nENTimeoutDetail'); // 验证错误信息显示 cy.contains('多语言资源加载失败').should('be.visible'); // 验证Redux状态包含详细错误信息 cy.window().its('store').invoke('getState').then((state) => { expect(state.i18n.loading).to.be.false; expect(state.i18n.error).to.not.be.null; expect(state.i18n.error).to.include('Failed to load i18n messages'); }); }); it('验证超时场景下其他功能不受影响', () => { cy.intercept('GET', '/dr/api/v1/pub/trans/zh/zh.js', (req) => { req.reply({ statusCode: 408, body: { message: 'Request Timeout' } }); }).as('getI18nZHTimeoutIsolated'); cy.window().then((win) => { Object.defineProperty(win.navigator, 'language', { value: 'zh-CN', writable: false }); }); loginPage.visit(); cy.wait('@getI18nZHTimeoutIsolated'); // 验证超时错误显示 cy.contains('多语言资源加载失败').should('be.visible'); // 验证页面基本结构仍然存在 cy.get('body').should('exist'); cy.get('div').should('exist'); // 验证Redux store的其他状态不受影响 cy.window().its('store').invoke('getState').then((state) => { // 验证其他slice的状态正常 expect(state.product).to.exist; expect(state.userInfo).to.exist; // 只有i18n状态反映超时错误 expect(state.i18n.loading).to.be.false; expect(state.i18n.error).to.not.be.null; expect(state.i18n.messages).to.deep.equal({}); }); }); it('验证网络恢复后的自动重试机制', () => { // 注意:这个测试模拟的是用户手动重试,而不是自动重试 // 因为我们的实现中没有自动重试机制 cy.intercept('GET', '/dr/api/v1/pub/trans/en/en.js', (req) => { req.reply({ statusCode: 408, body: { message: 'Request Timeout' } }); }).as('getI18nENNetworkDown'); cy.window().then((win) => { Object.defineProperty(win.navigator, 'language', { value: 'en-US', writable: false }); }); loginPage.visit(); cy.wait('@getI18nENNetworkDown'); // 验证网络问题时的错误显示 cy.contains('多语言资源加载失败').should('be.visible'); cy.contains('重新加载').should('be.visible'); // 模拟网络恢复 cy.intercept('GET', '/dr/api/v1/pub/trans/en/en.js', (req) => { req.reply({ statusCode: 200, body: { greeting: 'Hello, world!', patient: 'Patient Management' } }); }).as('getI18nENNetworkUp'); // 用户手动重试 cy.contains('重新加载').click(); cy.wait('@getI18nENNetworkUp'); // 验证网络恢复后成功加载 cy.contains('多语言资源加载失败').should('not.exist'); cy.get('body').should('contain', 'Patient Management'); }); });