mirror of
https://github.com/langgenius/dify.git
synced 2026-05-04 01:18:05 +08:00
tweaks
This commit is contained in:
@ -0,0 +1,158 @@
|
||||
import type { Mock } from 'vitest'
|
||||
import { render, screen } from '@testing-library/react'
|
||||
import { useWebAppStore } from '@/context/web-app-context'
|
||||
import { useGetUserCanAccessApp } from '@/service/access-control'
|
||||
import { useGetWebAppInfo, useGetWebAppMeta, useGetWebAppParams } from '@/service/use-share'
|
||||
import AuthenticatedLayout from '../authenticated-layout'
|
||||
|
||||
const mockReplace = vi.fn()
|
||||
const mockShareCode = 'share-code'
|
||||
const mockUpdateAppInfo = vi.fn()
|
||||
const mockUpdateAppParams = vi.fn()
|
||||
const mockUpdateWebAppMeta = vi.fn()
|
||||
const mockUpdateUserCanAccessApp = vi.fn()
|
||||
|
||||
const mockAppInfo = {
|
||||
app_id: 'app-123',
|
||||
}
|
||||
|
||||
vi.mock('@/next/navigation', () => ({
|
||||
useRouter: () => ({
|
||||
replace: mockReplace,
|
||||
}),
|
||||
usePathname: () => '/chat/test-share-code',
|
||||
useSearchParams: () => new URLSearchParams(),
|
||||
}))
|
||||
|
||||
vi.mock('@/context/web-app-context', () => ({
|
||||
useWebAppStore: vi.fn(),
|
||||
}))
|
||||
|
||||
vi.mock('@/service/access-control', () => ({
|
||||
useGetUserCanAccessApp: vi.fn(),
|
||||
}))
|
||||
|
||||
vi.mock('@/service/use-share', () => ({
|
||||
useGetWebAppInfo: vi.fn(),
|
||||
useGetWebAppParams: vi.fn(),
|
||||
useGetWebAppMeta: vi.fn(),
|
||||
}))
|
||||
|
||||
vi.mock('@/service/webapp-auth', () => ({
|
||||
webAppLogout: vi.fn(),
|
||||
}))
|
||||
|
||||
describe('AuthenticatedLayout', () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks()
|
||||
|
||||
;(useWebAppStore as unknown as Mock).mockImplementation((selector: (state: Record<string, unknown>) => unknown) => {
|
||||
const state = {
|
||||
shareCode: mockShareCode,
|
||||
updateAppInfo: mockUpdateAppInfo,
|
||||
updateAppParams: mockUpdateAppParams,
|
||||
updateWebAppMeta: mockUpdateWebAppMeta,
|
||||
updateUserCanAccessApp: mockUpdateUserCanAccessApp,
|
||||
}
|
||||
return selector(state)
|
||||
})
|
||||
|
||||
;(useGetWebAppInfo as Mock).mockReturnValue({
|
||||
data: mockAppInfo,
|
||||
error: null,
|
||||
isPending: false,
|
||||
})
|
||||
|
||||
;(useGetWebAppParams as Mock).mockReturnValue({
|
||||
data: { user_input_form: [] },
|
||||
error: null,
|
||||
isPending: false,
|
||||
})
|
||||
|
||||
;(useGetWebAppMeta as Mock).mockReturnValue({
|
||||
data: { tool_icons: {} },
|
||||
error: null,
|
||||
isPending: false,
|
||||
})
|
||||
|
||||
;(useGetUserCanAccessApp as Mock).mockReturnValue({
|
||||
data: { result: true },
|
||||
error: null,
|
||||
isPending: false,
|
||||
})
|
||||
})
|
||||
|
||||
describe('Permission Gating', () => {
|
||||
it('should not render children while the app info needed for permission is still pending', () => {
|
||||
;(useGetWebAppInfo as Mock).mockReturnValue({
|
||||
data: undefined,
|
||||
error: null,
|
||||
isPending: true,
|
||||
})
|
||||
|
||||
render(
|
||||
<AuthenticatedLayout>
|
||||
<div>protected child</div>
|
||||
</AuthenticatedLayout>,
|
||||
)
|
||||
|
||||
expect(screen.queryByText('protected child')).not.toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('should not render children while the access check is still pending', () => {
|
||||
;(useGetUserCanAccessApp as Mock).mockReturnValue({
|
||||
data: undefined,
|
||||
error: null,
|
||||
isPending: true,
|
||||
})
|
||||
|
||||
render(
|
||||
<AuthenticatedLayout>
|
||||
<div>protected child</div>
|
||||
</AuthenticatedLayout>,
|
||||
)
|
||||
|
||||
expect(screen.queryByText('protected child')).not.toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('should render children once access is allowed even if metadata queries are still pending', () => {
|
||||
;(useGetWebAppParams as Mock).mockReturnValue({
|
||||
data: undefined,
|
||||
error: null,
|
||||
isPending: true,
|
||||
})
|
||||
|
||||
;(useGetWebAppMeta as Mock).mockReturnValue({
|
||||
data: undefined,
|
||||
error: null,
|
||||
isPending: true,
|
||||
})
|
||||
|
||||
render(
|
||||
<AuthenticatedLayout>
|
||||
<div>protected child</div>
|
||||
</AuthenticatedLayout>,
|
||||
)
|
||||
|
||||
expect(screen.getByText('protected child')).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('should render the no permission state when access is denied', () => {
|
||||
;(useGetUserCanAccessApp as Mock).mockReturnValue({
|
||||
data: { result: false },
|
||||
error: null,
|
||||
isPending: false,
|
||||
})
|
||||
|
||||
render(
|
||||
<AuthenticatedLayout>
|
||||
<div>protected child</div>
|
||||
</AuthenticatedLayout>,
|
||||
)
|
||||
|
||||
expect(screen.queryByText('protected child')).not.toBeInTheDocument()
|
||||
expect(screen.getByText(/403/)).toBeInTheDocument()
|
||||
expect(screen.getByText(/no permission/i)).toBeInTheDocument()
|
||||
})
|
||||
})
|
||||
})
|
||||
@ -18,9 +18,9 @@ const AuthenticatedLayout = ({ children }: { children: React.ReactNode }) => {
|
||||
const updateWebAppMeta = useWebAppStore(s => s.updateWebAppMeta)
|
||||
const updateUserCanAccessApp = useWebAppStore(s => s.updateUserCanAccessApp)
|
||||
const { data: appParams, error: appParamsError } = useGetWebAppParams()
|
||||
const { data: appInfo, error: appInfoError } = useGetWebAppInfo()
|
||||
const { data: appInfo, error: appInfoError, isPending: isPendingAppInfo } = useGetWebAppInfo()
|
||||
const { data: appMeta, error: appMetaError } = useGetWebAppMeta()
|
||||
const { data: userCanAccessApp, error: useCanAccessAppError } = useGetUserCanAccessApp({ appId: appInfo?.app_id, isInstalledApp: false })
|
||||
const { data: userCanAccessApp, error: useCanAccessAppError, isPending: isPendingUserCanAccessApp } = useGetUserCanAccessApp({ appId: appInfo?.app_id, isInstalledApp: false })
|
||||
|
||||
useEffect(() => {
|
||||
if (appInfo)
|
||||
@ -29,7 +29,8 @@ const AuthenticatedLayout = ({ children }: { children: React.ReactNode }) => {
|
||||
updateAppParams(appParams)
|
||||
if (appMeta)
|
||||
updateWebAppMeta(appMeta)
|
||||
updateUserCanAccessApp(Boolean(userCanAccessApp && userCanAccessApp?.result))
|
||||
if (userCanAccessApp)
|
||||
updateUserCanAccessApp(Boolean(userCanAccessApp.result))
|
||||
}, [appInfo, appMeta, appParams, updateAppInfo, updateAppParams, updateUserCanAccessApp, updateWebAppMeta, userCanAccessApp])
|
||||
|
||||
const router = useRouter()
|
||||
@ -84,6 +85,10 @@ const AuthenticatedLayout = ({ children }: { children: React.ReactNode }) => {
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
if (isPendingAppInfo || !appInfo?.app_id || isPendingUserCanAccessApp)
|
||||
return null
|
||||
|
||||
return <>{children}</>
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user