diff --git a/web/src/pages/dashboard/stars.test.ts b/web/src/pages/dashboard/stars.test.ts index 20e7279f..e54171b5 100644 --- a/web/src/pages/dashboard/stars.test.ts +++ b/web/src/pages/dashboard/stars.test.ts @@ -1,7 +1,18 @@ -import { describe, expect, it, vi } from 'vitest' +// @vitest-environment jsdom + +import { createElement } from 'react' +import { fireEvent, render, screen } from '@testing-library/react' +import { beforeEach, describe, expect, it, vi } from 'vitest' + +const navigate = vi.fn() vi.mock('@tanstack/react-router', () => ({ - useNavigate: () => vi.fn(), + useNavigate: () => navigate, + useLocation: () => ({ + pathname: '/dashboard/stars', + searchStr: '?page=2', + hash: '#saved', + }), })) vi.mock('react-i18next', async () => { @@ -15,7 +26,7 @@ vi.mock('react-i18next', async () => { }) vi.mock('@/features/skill/skill-card', () => ({ - SkillCard: () => null, + SkillCard: ({ onClick }: { onClick?: () => void }) => createElement('button', { onClick }, 'skill-card'), })) vi.mock('@/shared/components/pagination', () => ({ @@ -24,7 +35,12 @@ vi.mock('@/shared/components/pagination', () => ({ vi.mock('@/shared/hooks/use-user-queries', () => ({ useMyStarsPage: () => ({ - data: { items: [], total: 0, page: 0, size: 12 }, + data: { + items: [{ id: 1, namespace: 'team-a', slug: 'demo skill' }], + total: 1, + page: 0, + size: 12, + }, isLoading: false, }), })) @@ -40,7 +56,20 @@ vi.mock('@/shared/components/dashboard-page-header', () => ({ import { MyStarsPage } from './stars' describe('MyStarsPage', () => { + beforeEach(() => navigate.mockClear()) + it('exports a named component function', () => { expect(typeof MyStarsPage).toBe('function') }) + + it('preserves the favorites page when opening a skill', () => { + render(createElement(MyStarsPage)) + + fireEvent.click(screen.getByRole('button', { name: 'skill-card' })) + + expect(navigate).toHaveBeenCalledWith({ + to: '/space/team-a/demo%20skill', + search: { returnTo: '/dashboard/stars?page=2#saved' }, + }) + }) }) diff --git a/web/src/pages/dashboard/stars.tsx b/web/src/pages/dashboard/stars.tsx index 36a5dc33..7c07bef6 100644 --- a/web/src/pages/dashboard/stars.tsx +++ b/web/src/pages/dashboard/stars.tsx @@ -1,17 +1,19 @@ import { useState } from 'react' -import { useNavigate } from '@tanstack/react-router' +import { useLocation, useNavigate } from '@tanstack/react-router' import { useTranslation } from 'react-i18next' import { SkillCard } from '@/features/skill/skill-card' import { Pagination } from '@/shared/components/pagination' import { useMyStarsPage } from '@/shared/hooks/use-user-queries' import { Card } from '@/shared/ui/card' import { DashboardPageHeader } from '@/shared/components/dashboard-page-header' +import { buildReturnTo } from '@/shared/lib/auth-route' const PAGE_SIZE = 12 export function MyStarsPage() { const { t } = useTranslation() const navigate = useNavigate() + const location = useLocation() const [page, setPage] = useState(0) const { data, isLoading } = useMyStarsPage({ page, size: PAGE_SIZE }) const skills = data?.items ?? [] @@ -40,7 +42,10 @@ export function MyStarsPage() { navigate({ to: `/space/${skill.namespace}/${encodeURIComponent(skill.slug)}` })} + onClick={() => navigate({ + to: `/space/${skill.namespace}/${encodeURIComponent(skill.slug)}`, + search: { returnTo: buildReturnTo(location) }, + })} /> ))} diff --git a/web/src/pages/dashboard/subscriptions.test.ts b/web/src/pages/dashboard/subscriptions.test.ts new file mode 100644 index 00000000..9dd3b2c1 --- /dev/null +++ b/web/src/pages/dashboard/subscriptions.test.ts @@ -0,0 +1,60 @@ +// @vitest-environment jsdom + +import { createElement } from 'react' +import { fireEvent, render, screen } from '@testing-library/react' +import { beforeEach, describe, expect, it, vi } from 'vitest' + +const navigate = vi.fn() + +vi.mock('@tanstack/react-router', () => ({ + useNavigate: () => navigate, + useLocation: () => ({ + pathname: '/dashboard/subscriptions', + searchStr: '?page=1', + hash: '', + }), +})) + +vi.mock('react-i18next', async () => { + const actual = await vi.importActual('react-i18next') + return { + ...actual, + useTranslation: () => ({ t: (key: string) => key }), + } +}) + +vi.mock('@/features/skill/skill-card', () => ({ + SkillCard: ({ onClick }: { onClick?: () => void }) => createElement('button', { onClick }, 'skill-card'), +})) + +vi.mock('@/shared/components/pagination', () => ({ Pagination: () => null })) +vi.mock('@/shared/hooks/use-user-queries', () => ({ + useMySubscriptionsPage: () => ({ + data: { + items: [{ id: 1, namespace: 'team-a', slug: 'demo-skill' }], + total: 1, + page: 0, + size: 12, + }, + isLoading: false, + }), +})) +vi.mock('@/shared/ui/card', () => ({ Card: ({ children }: { children: unknown }) => children })) +vi.mock('@/shared/components/dashboard-page-header', () => ({ DashboardPageHeader: () => null })) + +import { MySubscriptionsPage } from './subscriptions' + +describe('MySubscriptionsPage', () => { + beforeEach(() => navigate.mockClear()) + + it('preserves the subscriptions page when opening a skill', () => { + render(createElement(MySubscriptionsPage)) + + fireEvent.click(screen.getByRole('button', { name: 'skill-card' })) + + expect(navigate).toHaveBeenCalledWith({ + to: '/space/team-a/demo-skill', + search: { returnTo: '/dashboard/subscriptions?page=1' }, + }) + }) +}) diff --git a/web/src/pages/dashboard/subscriptions.tsx b/web/src/pages/dashboard/subscriptions.tsx index 6c0523dd..d128ccc1 100644 --- a/web/src/pages/dashboard/subscriptions.tsx +++ b/web/src/pages/dashboard/subscriptions.tsx @@ -1,17 +1,19 @@ import { useState } from 'react' -import { useNavigate } from '@tanstack/react-router' +import { useLocation, useNavigate } from '@tanstack/react-router' import { useTranslation } from 'react-i18next' import { SkillCard } from '@/features/skill/skill-card' import { Pagination } from '@/shared/components/pagination' import { useMySubscriptionsPage } from '@/shared/hooks/use-user-queries' import { Card } from '@/shared/ui/card' import { DashboardPageHeader } from '@/shared/components/dashboard-page-header' +import { buildReturnTo } from '@/shared/lib/auth-route' const PAGE_SIZE = 12 export function MySubscriptionsPage() { const { t } = useTranslation() const navigate = useNavigate() + const location = useLocation() const [page, setPage] = useState(0) const { data, isLoading } = useMySubscriptionsPage({ page, size: PAGE_SIZE }) const skills = data?.items ?? [] @@ -40,7 +42,10 @@ export function MySubscriptionsPage() { navigate({ to: `/space/${skill.namespace}/${encodeURIComponent(skill.slug)}` })} + onClick={() => navigate({ + to: `/space/${skill.namespace}/${encodeURIComponent(skill.slug)}`, + search: { returnTo: buildReturnTo(location) }, + })} /> ))}