mirror of
https://github.com/langgenius/dify.git
synced 2026-07-21 18:58:35 +08:00
fix(web): clarify unpublished explore app handling (#38260)
This commit is contained in:
parent
c1d03a888f
commit
bf46b82303
@ -215,7 +215,7 @@ describe('App Publisher Flow', () => {
|
||||
fireEvent.click(screen.getByText('common.openInExplore'))
|
||||
|
||||
await waitFor(() => {
|
||||
expect(mockToastError).toHaveBeenCalledWith('No app found in Explore')
|
||||
expect(mockToastError).toHaveBeenCalledWith('notPublishedYet')
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@ -562,7 +562,7 @@ describe('AppPublisher', () => {
|
||||
fireEvent.click(screen.getByText('publisher-open-in-explore'))
|
||||
|
||||
await waitFor(() => {
|
||||
expect(mockToastError).toHaveBeenCalledWith('No app found in Explore')
|
||||
expect(mockToastError).toHaveBeenCalledWith('notPublishedYet')
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
@ -231,7 +231,7 @@ export function AppPublisher({
|
||||
const { installed_apps } = await fetchInstalledAppList(appDetail.id)
|
||||
if (installed_apps?.length > 0)
|
||||
return `${basePath}${buildInstalledAppPath(installed_apps[0]!.id)}`
|
||||
throw new Error('No app found in Explore')
|
||||
throw new Error(t('notPublishedYet', { ns: 'app' }))
|
||||
}, {
|
||||
onError: (err) => {
|
||||
toast.error(`${err.message || err}`)
|
||||
|
||||
@ -14,6 +14,10 @@ import { StarredAppCard } from '../starred-app-card'
|
||||
|
||||
let mockWebappAuthEnabled = false
|
||||
let mockRbacEnabled = true
|
||||
const mockUserCanAccessApp = vi.hoisted(() => ({
|
||||
result: true as boolean | undefined,
|
||||
isLoading: false,
|
||||
}))
|
||||
|
||||
const render = (ui: React.ReactElement) => renderWithSystemFeatures(ui, {
|
||||
systemFeatures: {
|
||||
@ -118,15 +122,15 @@ vi.mock('@/service/explore', () => ({
|
||||
|
||||
vi.mock('@/service/access-control', () => ({
|
||||
useGetUserCanAccessApp: () => ({
|
||||
data: { result: true },
|
||||
isLoading: false,
|
||||
data: mockUserCanAccessApp.result === undefined ? undefined : { result: mockUserCanAccessApp.result },
|
||||
isLoading: mockUserCanAccessApp.isLoading,
|
||||
}),
|
||||
}))
|
||||
|
||||
vi.mock('@/service/access-control/use-app-access-control', () => ({
|
||||
useGetUserCanAccessApp: () => ({
|
||||
data: { result: true },
|
||||
isLoading: false,
|
||||
data: mockUserCanAccessApp.result === undefined ? undefined : { result: mockUserCanAccessApp.result },
|
||||
isLoading: mockUserCanAccessApp.isLoading,
|
||||
}),
|
||||
}))
|
||||
|
||||
@ -380,6 +384,8 @@ describe('AppCard', () => {
|
||||
mockOpenAsyncWindow.mockReset()
|
||||
mockWebappAuthEnabled = false
|
||||
mockRbacEnabled = true
|
||||
mockUserCanAccessApp.result = true
|
||||
mockUserCanAccessApp.isLoading = false
|
||||
mockDeleteMutationPending = false
|
||||
mockToggleStarMutationPending = false
|
||||
mockAppContext.isCurrentWorkspaceEditor = true
|
||||
@ -1733,6 +1739,28 @@ describe('AppCard', () => {
|
||||
})
|
||||
|
||||
describe('Open in Explore - No App Found', () => {
|
||||
it('should tell workflow users to publish before opening in explore', async () => {
|
||||
const workflowApp = createMockApp({
|
||||
mode: AppModeEnum.WORKFLOW,
|
||||
workflow: undefined,
|
||||
})
|
||||
render(<AppCard app={workflowApp} />)
|
||||
|
||||
fireEvent.click(screen.getByTestId('dropdown-menu-trigger'))
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText('app.openInExplore')).toBeInTheDocument()
|
||||
})
|
||||
|
||||
fireEvent.click(screen.getByText('app.openInExplore'))
|
||||
|
||||
expect(mockOpenAsyncWindow).not.toHaveBeenCalled()
|
||||
expect(exploreService.fetchInstalledAppList).not.toHaveBeenCalled()
|
||||
expect(toastMocks.record).toHaveBeenCalledWith({
|
||||
type: 'error',
|
||||
message: 'app.notPublishedYet',
|
||||
})
|
||||
})
|
||||
|
||||
it('should handle case when installed_apps is empty array', async () => {
|
||||
(exploreService.fetchInstalledAppList as Mock).mockResolvedValueOnce({ installed_apps: [] })
|
||||
|
||||
@ -1756,6 +1784,10 @@ describe('AppCard', () => {
|
||||
|
||||
await waitFor(() => {
|
||||
expect(exploreService.fetchInstalledAppList).toHaveBeenCalled()
|
||||
expect(toastMocks.record).toHaveBeenCalledWith({
|
||||
type: 'error',
|
||||
message: 'app.notPublishedYet',
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@ -1944,6 +1976,31 @@ describe('AppCard', () => {
|
||||
})
|
||||
})
|
||||
|
||||
it('should keep open in explore visible for unpublished workflow apps while access check is pending', async () => {
|
||||
mockUserCanAccessApp.result = false
|
||||
mockUserCanAccessApp.isLoading = true
|
||||
const workflowApp = createMockApp({
|
||||
mode: AppModeEnum.WORKFLOW,
|
||||
workflow: undefined,
|
||||
})
|
||||
|
||||
render(<AppCard app={workflowApp} />)
|
||||
|
||||
fireEvent.click(screen.getByTestId('dropdown-menu-trigger'))
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText('app.openInExplore')).toBeInTheDocument()
|
||||
})
|
||||
|
||||
fireEvent.click(screen.getByText('app.openInExplore'))
|
||||
|
||||
expect(mockOpenAsyncWindow).not.toHaveBeenCalled()
|
||||
expect(exploreService.fetchInstalledAppList).not.toHaveBeenCalled()
|
||||
expect(toastMocks.record).toHaveBeenCalledWith({
|
||||
type: 'error',
|
||||
message: 'app.notPublishedYet',
|
||||
})
|
||||
})
|
||||
|
||||
it('should close access control modal when onClose is called', async () => {
|
||||
render(<AppCard app={mockApp} />)
|
||||
|
||||
|
||||
@ -90,6 +90,11 @@ const ACCESS_MODE_LABEL_KEYS = {
|
||||
[AccessMode.EXTERNAL_MEMBERS]: 'accessItemsDescription.external',
|
||||
} as const
|
||||
|
||||
const APP_MODES_REQUIRING_PUBLISHED_WORKFLOW_IN_EXPLORE = new Set<AppModeEnum>([
|
||||
AppModeEnum.ADVANCED_CHAT,
|
||||
AppModeEnum.WORKFLOW,
|
||||
])
|
||||
|
||||
type AppCardProps = {
|
||||
app: App
|
||||
onlineUsers?: WorkflowOnlineUser[]
|
||||
@ -103,6 +108,10 @@ type AppAccessModeIconProps = {
|
||||
|
||||
const getAppResourceMaintainer = (app: App) => app.maintainer
|
||||
|
||||
function requiresPublishedWorkflowInExplore(app: App) {
|
||||
return APP_MODES_REQUIRING_PUBLISHED_WORKFLOW_IN_EXPLORE.has(app.mode)
|
||||
}
|
||||
|
||||
function AppAccessModeIcon({ accessMode }: AppAccessModeIconProps) {
|
||||
const { t } = useTranslation()
|
||||
|
||||
@ -182,12 +191,17 @@ function AppCardOperationsMenu({
|
||||
async function handleOpenInstalledApp(e: MouseEvent<HTMLElement>) {
|
||||
e.stopPropagation()
|
||||
e.preventDefault()
|
||||
if (requiresPublishedWorkflowInExplore(app) && !app.workflow?.id) {
|
||||
toast.error(t('notPublishedYet', { ns: 'app' }))
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
await openAsyncWindow(async () => {
|
||||
const { installed_apps } = await fetchInstalledAppList(app.id)
|
||||
if (installed_apps?.length > 0)
|
||||
return `${basePath}${buildInstalledAppPath(installed_apps[0]!.id)}`
|
||||
throw new Error('No app found in Explore')
|
||||
throw new Error(t('notPublishedYet', { ns: 'app' }))
|
||||
}, {
|
||||
onError: (err) => {
|
||||
toast.error(`${err.message || err}`)
|
||||
@ -272,10 +286,12 @@ function AppCardOperationsMenuContent(props: AppCardOperationsMenuContentProps)
|
||||
appId: props.app.id,
|
||||
enabled: systemFeatures.webapp_auth.enabled,
|
||||
})
|
||||
const needsPublishBeforeExplore = requiresPublishedWorkflowInExplore(props.app) && !props.app.workflow?.id
|
||||
|
||||
const shouldShowOpenInExploreOption = !props.app.has_draft_trigger
|
||||
&& (
|
||||
!systemFeatures.webapp_auth.enabled
|
||||
needsPublishBeforeExplore
|
||||
|| !systemFeatures.webapp_auth.enabled
|
||||
|| (!isGettingUserCanAccessApp && Boolean(userCanAccessApp?.result))
|
||||
)
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user