diff --git a/web/app/(commonLayout)/__tests__/hydration-boundary.spec.tsx b/web/app/(commonLayout)/__tests__/hydration-boundary.spec.tsx index 1d3de80f988..82878ebc600 100644 --- a/web/app/(commonLayout)/__tests__/hydration-boundary.spec.tsx +++ b/web/app/(commonLayout)/__tests__/hydration-boundary.spec.tsx @@ -15,6 +15,7 @@ const mocks = vi.hoisted(() => ({ }), headers: vi.fn(), resolveServerConsoleApiUrl: vi.fn(), + basePath: '', })) vi.mock('@/context/query-client-server', () => ({ @@ -29,6 +30,12 @@ vi.mock('@/next/navigation', () => ({ redirect: (url: string) => mocks.redirect(url), })) +vi.mock('@/utils/var', () => ({ + get basePath() { + return mocks.basePath + }, +})) + vi.mock('@/features/account-profile/server', () => ({ serverUserProfileQueryOptions: () => ({ queryKey: ['common', 'user-profile'], @@ -62,6 +69,7 @@ vi.mock('@/features/system-features/server', () => ({ describe('CommonLayoutHydrationBoundary', () => { beforeEach(() => { vi.clearAllMocks() + mocks.basePath = '' mocks.queryClient = new QueryClient({ defaultOptions: { queries: { retry: false } } }) mocks.headers.mockResolvedValue( new Headers({ @@ -126,6 +134,7 @@ describe('CommonLayoutHydrationBoundary', () => { }) it('should redirect unauthorized users to the refresh route with the current path', async () => { + mocks.basePath = '/workflow' mocks.profileQueryFn.mockRejectedValue( new Response(JSON.stringify({ code: 'unauthorized' }), { status: 401 }), ) @@ -150,15 +159,17 @@ describe('CommonLayoutHydrationBoundary', () => { expect(mocks.redirect).toHaveBeenCalledWith('/auth/refresh?redirect_url=%2F') }) - it('should redirect setup errors to install', async () => { - mocks.profileQueryFn.mockRejectedValue( - new Response(JSON.stringify({ code: 'not_setup' }), { status: 401 }), - ) + it.each([ + ['not_setup', '/install'], + ['not_init_validated', '/init'], + ])('should use a basePath-relative destination for %s errors', async (code, destination) => { + mocks.basePath = '/workflow' + mocks.profileQueryFn.mockRejectedValue(new Response(JSON.stringify({ code }), { status: 401 })) const { CommonLayoutHydrationBoundary } = await import('../hydration-boundary') await expect(CommonLayoutHydrationBoundary({ children: null })).rejects.toThrow('NEXT_REDIRECT') - expect(mocks.redirect).toHaveBeenCalledWith('/install') + expect(mocks.redirect).toHaveBeenCalledWith(destination) }) it('should render children without server prefetch when the server API URL is not resolvable', async () => { diff --git a/web/app/(commonLayout)/hydration-boundary.tsx b/web/app/(commonLayout)/hydration-boundary.tsx index 52182450b04..74f7da57175 100644 --- a/web/app/(commonLayout)/hydration-boundary.tsx +++ b/web/app/(commonLayout)/hydration-boundary.tsx @@ -42,15 +42,15 @@ const getCurrentPath = async () => { const redirectToAuthRefresh = async () => { const currentPath = await getCurrentPath() - redirect(`${basePath}${AUTH_REFRESH_PATH}?redirect_url=${encodeURIComponent(currentPath)}`) + redirect(`${AUTH_REFRESH_PATH}?redirect_url=${encodeURIComponent(currentPath)}`) } const handleProfileError = async (error: unknown) => { if (!(error instanceof Response)) throw error const errorData = await parseConsoleErrorPayload(error) - if (errorData?.code === 'not_setup') redirect(`${basePath}/install`) - if (errorData?.code === 'not_init_validated') redirect(`${basePath}/init`) + if (errorData?.code === 'not_setup') redirect('/install') + if (errorData?.code === 'not_init_validated') redirect('/init') if (error.status === 401) await redirectToAuthRefresh() throw error