From 132cb706d735e36c1c5d18124b148e9ddb74d47e Mon Sep 17 00:00:00 2001 From: cat-shark <1716967236@qq.com> Date: Sun, 13 Sep 2026 23:12:23 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E5=A4=8D=E4=B9=A0=E8=AF=A6=E6=83=85?= =?UTF-8?q?=E8=BF=94=E5=9B=9E=E6=8C=89=E9=92=AE=E6=94=B9=E4=B8=BA=E8=BF=94?= =?UTF-8?q?=E5=9B=9E=E6=80=BB=E8=A7=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- e2e/review.spec.ts | 14 +++++++ src/__tests__/components/MyHead.spec.ts | 53 ++++++++++++++++++++++++- src/components/MyHead.vue | 19 +++++++-- src/router/index.ts | 10 ++++- 4 files changed, 89 insertions(+), 7 deletions(-) diff --git a/e2e/review.spec.ts b/e2e/review.spec.ts index 62fa954..17f9c61 100644 --- a/e2e/review.spec.ts +++ b/e2e/review.spec.ts @@ -82,4 +82,18 @@ test.describe('Review page', () => { await expect(page.getByRole('button', { name: '重新加载' })).toBeVisible(); await expect(page.getByText('暂无复习任务')).toHaveCount(0); }); + + test('详情页返回按钮为「返回总览」并回到复习总览', async ({ page }) => { + await page.route('**/api/review/task/**', (route) => + route.fulfill({ status: 200, contentType: 'application/json', body: JSON.stringify({ code: 200, data: [] }) }), + ); + + await page.locator('.task-card').first().click(); + await expect(page).toHaveURL(/\/review\/detail\/task\/T001/, { timeout: 5000 }); + + const backBtn = page.locator('.head .back-btn'); + await expect(backBtn).toHaveText('← 返回总览'); + await backBtn.click(); + await expect(page).toHaveURL(/\/review$/, { timeout: 5000 }); + }); }); diff --git a/src/__tests__/components/MyHead.spec.ts b/src/__tests__/components/MyHead.spec.ts index 50d6649..4e685b6 100644 --- a/src/__tests__/components/MyHead.spec.ts +++ b/src/__tests__/components/MyHead.spec.ts @@ -22,6 +22,13 @@ async function mountAt(path: string) { }) } +// 真实挂载:shallow 会存根化 el-button 且不渲染插槽,无法断言按钮文案与点击行为 +async function mountRealAt(path: string) { + await router.push(path) + await router.isReady() + return mount(MyHead, { global: { plugins: [ElementPlus, router] } }) +} + describe('MyHead.vue', () => { beforeEach(async () => { vi.clearAllMocks() @@ -30,6 +37,49 @@ describe('MyHead.vue', () => { await router.isReady() }) + it('默认返回首页,复习详情按路由 meta 返回总览', async () => { + localStorage.setItem('isLoggedIn', 'true') + + const studyWrapper = await mountRealAt('/study') + expect(studyWrapper.find('.back-btn').text()).toBe('← 返回首页') + await studyWrapper.find('.back-btn').trigger('click') + await vi.waitFor(() => expect(router.currentRoute.value.path).toBe('/welcome')) + studyWrapper.unmount() + + const taskDetailWrapper = await mountRealAt('/review/detail/task/T1') + expect(taskDetailWrapper.find('.back-btn').text()).toBe('← 返回总览') + await taskDetailWrapper.find('.back-btn').trigger('click') + await vi.waitFor(() => expect(router.currentRoute.value.path).toBe('/review')) + taskDetailWrapper.unmount() + }) + + it('多入口详情页优先返回来路,无历史时兜底到上级', async () => { + localStorage.setItem('isLoggedIn', 'true') + const backSpy = vi.spyOn(router, 'back').mockImplementation(() => {}) + + // 从回忆复习页进入:存在历史,回退到实际来路 + await mountRealAt('/review/recall/T1') + const fromRecall = await mountRealAt('/review/detail/report/9') + expect(fromRecall.find('.back-btn').text()).toBe('← 返回上一页') + await fromRecall.find('.back-btn').trigger('click') + expect(backSpy).toHaveBeenCalledTimes(1) + fromRecall.unmount() + + // 直接深链或刷新进入:没有历史,回退到该页声明的上级 + window.history.replaceState( + { back: null, current: '/review/detail/report/9', forward: null, position: 0 }, + '', + ) + const deepLink = mount(MyHead, { global: { plugins: [ElementPlus, router] } }) + expect(deepLink.find('.back-btn').text()).toBe('← 返回上一页') + await deepLink.find('.back-btn').trigger('click') + expect(backSpy).toHaveBeenCalledTimes(1) + await vi.waitFor(() => expect(router.currentRoute.value.path).toBe('/review')) + deepLink.unmount() + + backSpy.mockRestore() + }) + it('derives route-based computed properties from real route', async () => { const loginWrapper = await mountAt('/login') expect(loginWrapper.vm.isLoginRoute).toBe(true) @@ -46,8 +96,7 @@ describe('MyHead.vue', () => { expect(welcomeWrapper.vm.pageTitle).toBeUndefined() }) - it('clears login state and navigates to login on logout', async () => { - vi.mocked(logout).mockResolvedValue({ code: 200 } as any) + it('clears login state and navigates to login on logout', async () => { vi.mocked(logout).mockResolvedValue({ code: 200 } as any) localStorage.setItem('isLoggedIn', 'true') const wrapper = await mountAt('/study') diff --git a/src/components/MyHead.vue b/src/components/MyHead.vue index c6e2802..f395530 100644 --- a/src/components/MyHead.vue +++ b/src/components/MyHead.vue @@ -4,9 +4,9 @@ v-if="showBackButton" text class="back-btn" - @click="router.push('/welcome')" + @click="goBack" > - ← 返回首页 + ← {{ backLabel }}