From 0fea760143eb1b78835db6c7d42c7c048eee3547 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=9D=9E=E6=B3=95=E6=93=8D=E4=BD=9C?= Date: Thu, 16 Apr 2026 14:06:57 +0800 Subject: [PATCH 01/38] fix: http node key value type dropdown (#35304) --- .../nodes/http/__tests__/integration.spec.tsx | 33 ++++++++++++- .../key-value/key-value-edit/item.tsx | 46 +++++++++++++------ web/eslint-suppressions.json | 3 -- 3 files changed, 63 insertions(+), 19 deletions(-) diff --git a/web/app/components/workflow/nodes/http/__tests__/integration.spec.tsx b/web/app/components/workflow/nodes/http/__tests__/integration.spec.tsx index 99d240c6d6..93a4d08c3d 100644 --- a/web/app/components/workflow/nodes/http/__tests__/integration.spec.tsx +++ b/web/app/components/workflow/nodes/http/__tests__/integration.spec.tsx @@ -495,8 +495,8 @@ describe('http path', () => { ) fireEvent.change(screen.getAllByDisplayValue('alice')[0], { target: { value: 'bob' } }) - await user.click(screen.getByText('text')) - await user.click(screen.getByText('file')) + await user.click(screen.getAllByRole('combobox', { name: 'workflow.nodes.http.type' })[0]!) + await user.click(screen.getByRole('option', { name: /file/i })) expect(onChange).toHaveBeenCalled() }) @@ -558,6 +558,35 @@ describe('http path', () => { expect(onRemove).toHaveBeenCalled() }) + it('should show the full file-type menu and update the row type selection', async () => { + const user = userEvent.setup() + const onChange = vi.fn() + render( + , + ) + + await user.click(screen.getByRole('combobox', { name: 'workflow.nodes.http.type' })) + + const fileOption = screen.getByRole('option', { name: /file/i }) + expect(screen.getByRole('option', { name: /text/i })).toBeInTheDocument() + expect(fileOption.closest('.h-7')).toBeNull() + + await user.click(fileOption) + + expect(onChange).toHaveBeenCalledWith(expect.objectContaining({ type: 'file' })) + }) + it('should update the raw-text body payload', () => { const onChange = vi.fn() render( diff --git a/web/app/components/workflow/nodes/http/components/key-value/key-value-edit/item.tsx b/web/app/components/workflow/nodes/http/components/key-value/key-value-edit/item.tsx index f2fc2e9e1c..36e0fccbff 100644 --- a/web/app/components/workflow/nodes/http/components/key-value/key-value-edit/item.tsx +++ b/web/app/components/workflow/nodes/http/components/key-value/key-value-edit/item.tsx @@ -7,7 +7,15 @@ import { produce } from 'immer' import * as React from 'react' import { useCallback } from 'react' import { useTranslation } from 'react-i18next' -import { PortalSelect } from '@/app/components/base/select' +import { + Select, + SelectContent, + SelectItem, + SelectItemIndicator, + SelectItemText, + SelectTrigger, + SelectValue, +} from '@/app/components/base/ui/select' import { VarType } from '@/app/components/workflow/types' import VarReferencePicker from '../../../../_base/components/variable/var-reference-picker' import InputItem from './input-item' @@ -80,7 +88,7 @@ const KeyValueItem: FC = ({ return ( // group class name is for hover row show remove button -
+
{!keyNotSupportVar ? ( @@ -105,18 +113,28 @@ const KeyValueItem: FC = ({
{isSupportFile && (
- handleChange('type')(item.value as string)} - items={[ - { name: 'text', value: 'text' }, - { name: 'file', value: 'file' }, - ]} - readonly={readonly} - triggerClassName="rounded-none h-7 text-text-primary" - triggerClassNameFn={isOpen => isOpen ? 'bg-state-base-hover' : 'bg-transparent'} - popupClassName="w-[80px] h-7" - /> +
)}
Date: Thu, 16 Apr 2026 11:51:11 +0530 Subject: [PATCH 02/38] fix: apply score threshold after reranking in hybrid search (#35263) --- api/core/rag/datasource/retrieval_service.py | 32 ++++++++++++++++++-- 1 file changed, 30 insertions(+), 2 deletions(-) diff --git a/api/core/rag/datasource/retrieval_service.py b/api/core/rag/datasource/retrieval_service.py index f978e072f3..fa242a730e 100644 --- a/api/core/rag/datasource/retrieval_service.py +++ b/api/core/rag/datasource/retrieval_service.py @@ -195,6 +195,23 @@ class RetrievalService: ) return all_documents + @classmethod + def _filter_documents_by_vector_score_threshold( + cls, documents: list[Document], score_threshold: float | None + ) -> list[Document]: + """Keep documents whose stored retrieval score meets the threshold. + + Used when hybrid search skips early vector thresholding but no rerank + runner applies a threshold afterward (same rule as ``calculate_vector_score``). + """ + if score_threshold is None: + return documents + return [ + document + for document in documents + if document.metadata and document.metadata.get("score", 0) >= score_threshold + ] + @classmethod def _deduplicate_documents(cls, documents: list[Document]) -> list[Document]: """Deduplicate documents in O(n) while preserving first-seen order. @@ -294,13 +311,20 @@ class RetrievalService: vector = Vector(dataset=dataset) documents = [] + # Hybrid search merges keyword / full-text / vector hits and then reranks + # (weighted fusion or reranking model). Applying the user score threshold at + # vector retrieval time uses embedding similarity, which is not comparable to + # reranked or fused scores and incorrectly drops high-quality chunks (#35233). + embedding_score_threshold = ( + 0.0 if retrieval_method == RetrievalMethod.HYBRID_SEARCH else score_threshold + ) if query_type == QueryType.TEXT_QUERY: documents.extend( vector.search_by_vector( query, search_type="similarity_score_threshold", top_k=top_k, - score_threshold=score_threshold, + score_threshold=embedding_score_threshold, filter={"group_id": [dataset.id]}, document_ids_filter=document_ids_filter, ) @@ -312,7 +336,7 @@ class RetrievalService: vector.search_by_file( file_id=query, top_k=top_k, - score_threshold=score_threshold, + score_threshold=embedding_score_threshold, filter={"group_id": [dataset.id]}, document_ids_filter=document_ids_filter, ) @@ -844,6 +868,10 @@ class RetrievalService: top_n=top_k, query_type=QueryType.TEXT_QUERY if query else QueryType.IMAGE_QUERY, ) + if not data_post_processor.rerank_runner and score_threshold: + all_documents_item = self._filter_documents_by_vector_score_threshold( + all_documents_item, score_threshold + ) all_documents.extend(all_documents_item) From a1f990584b70c05b1b9e1da8271de5e69e0b47d4 Mon Sep 17 00:00:00 2001 From: yyh <92089059+lyzno1@users.noreply.github.com> Date: Thu, 16 Apr 2026 15:35:04 +0800 Subject: [PATCH 03/38] refactor(web): unify Base UI component props to namespace types (#35306) --- .../components/base/ui/alert-dialog/index.tsx | 14 +++++----- web/app/components/base/ui/avatar/index.tsx | 7 ++--- .../components/base/ui/context-menu/index.tsx | 28 +++++++++---------- web/app/components/base/ui/dialog/index.tsx | 8 +++--- .../base/ui/dropdown-menu/index.tsx | 28 +++++++++---------- .../components/base/ui/number-field/index.tsx | 14 +++++----- web/app/components/base/ui/popover/index.tsx | 8 +++--- .../components/base/ui/scroll-area/index.tsx | 14 +++++----- web/app/components/base/ui/select/index.tsx | 22 +++++++-------- web/app/components/base/ui/tooltip/index.tsx | 6 ++-- .../__tests__/index.spec.tsx | 2 +- .../workplace-selector/index.tsx | 2 +- 12 files changed, 76 insertions(+), 77 deletions(-) diff --git a/web/app/components/base/ui/alert-dialog/index.tsx b/web/app/components/base/ui/alert-dialog/index.tsx index e591fed2a3..f277c343fc 100644 --- a/web/app/components/base/ui/alert-dialog/index.tsx +++ b/web/app/components/base/ui/alert-dialog/index.tsx @@ -1,9 +1,9 @@ 'use client' +import type { ComponentPropsWithoutRef, ReactNode } from 'react' import type { ButtonProps } from '@/app/components/base/ui/button' import { AlertDialog as BaseAlertDialog } from '@base-ui/react/alert-dialog' import { cn } from '@langgenius/dify-ui/cn' -import * as React from 'react' import { Button } from '@/app/components/base/ui/button' export const AlertDialog = BaseAlertDialog.Root @@ -12,11 +12,11 @@ export const AlertDialogTitle = BaseAlertDialog.Title export const AlertDialogDescription = BaseAlertDialog.Description type AlertDialogContentProps = { - children: React.ReactNode + children: ReactNode className?: string overlayClassName?: string - popupProps?: Omit, 'children' | 'className'> - backdropProps?: Omit, 'className'> + popupProps?: Omit + backdropProps?: Omit } export function AlertDialogContent({ @@ -50,7 +50,7 @@ export function AlertDialogContent({ ) } -type AlertDialogActionsProps = React.ComponentPropsWithoutRef<'div'> +type AlertDialogActionsProps = ComponentPropsWithoutRef<'div'> export function AlertDialogActions({ className, ...props }: AlertDialogActionsProps) { return ( @@ -62,8 +62,8 @@ export function AlertDialogActions({ className, ...props }: AlertDialogActionsPr } type AlertDialogCancelButtonProps = Omit & { - children: React.ReactNode - closeProps?: Omit, 'children' | 'render'> + children: ReactNode + closeProps?: Omit } export function AlertDialogCancelButton({ diff --git a/web/app/components/base/ui/avatar/index.tsx b/web/app/components/base/ui/avatar/index.tsx index 1587dd2d33..6fca2e87dc 100644 --- a/web/app/components/base/ui/avatar/index.tsx +++ b/web/app/components/base/ui/avatar/index.tsx @@ -1,5 +1,4 @@ import type { ImageLoadingStatus } from '@base-ui/react/avatar' -import type * as React from 'react' import { Avatar as BaseAvatar } from '@base-ui/react/avatar' import { cn } from '@langgenius/dify-ui/cn' @@ -24,7 +23,7 @@ export type AvatarProps = { onLoadingStatusChange?: (status: ImageLoadingStatus) => void } -type AvatarRootProps = React.ComponentPropsWithRef & { +type AvatarRootProps = BaseAvatar.Root.Props & { size?: AvatarSize } @@ -45,7 +44,7 @@ export function AvatarRoot({ ) } -type AvatarFallbackProps = React.ComponentPropsWithRef & { +type AvatarFallbackProps = BaseAvatar.Fallback.Props & { size?: AvatarSize } @@ -66,7 +65,7 @@ export function AvatarFallback({ ) } -type AvatarImageProps = React.ComponentPropsWithRef +type AvatarImageProps = BaseAvatar.Image.Props export function AvatarImage({ className, diff --git a/web/app/components/base/ui/context-menu/index.tsx b/web/app/components/base/ui/context-menu/index.tsx index 4331f095b3..4ff7241b23 100644 --- a/web/app/components/base/ui/context-menu/index.tsx +++ b/web/app/components/base/ui/context-menu/index.tsx @@ -1,9 +1,9 @@ 'use client' +import type { ReactNode } from 'react' import type { Placement } from '@/app/components/base/ui/placement' import { ContextMenu as BaseContextMenu } from '@base-ui/react/context-menu' import { cn } from '@langgenius/dify-ui/cn' -import * as React from 'react' import { overlayBackdropClassName, overlayGroupLabelClassName, @@ -22,18 +22,18 @@ export const ContextMenuGroup = BaseContextMenu.Group export const ContextMenuRadioGroup = BaseContextMenu.RadioGroup type ContextMenuContentProps = { - children: React.ReactNode + children: ReactNode placement?: Placement sideOffset?: number alignOffset?: number className?: string popupClassName?: string positionerProps?: Omit< - React.ComponentPropsWithoutRef, + BaseContextMenu.Positioner.Props, 'children' | 'className' | 'side' | 'align' | 'sideOffset' | 'alignOffset' > popupProps?: Omit< - React.ComponentPropsWithoutRef, + BaseContextMenu.Popup.Props, 'children' | 'className' > } @@ -113,7 +113,7 @@ export function ContextMenuContent({ }) } -type ContextMenuItemProps = React.ComponentPropsWithoutRef & { +type ContextMenuItemProps = BaseContextMenu.Item.Props & { destructive?: boolean } @@ -130,7 +130,7 @@ export function ContextMenuItem({ ) } -type ContextMenuLinkItemProps = React.ComponentPropsWithoutRef & { +type ContextMenuLinkItemProps = BaseContextMenu.LinkItem.Props & { destructive?: boolean } @@ -152,7 +152,7 @@ export function ContextMenuLinkItem({ export function ContextMenuRadioItem({ className, ...props -}: React.ComponentPropsWithoutRef) { +}: BaseContextMenu.RadioItem.Props) { return ( ) { +}: BaseContextMenu.CheckboxItem.Props) { return ( , 'children'>) { +}: Omit) { return ( , 'children'>) { +}: Omit) { return ( & { +type ContextMenuSubTriggerProps = BaseContextMenu.SubmenuTrigger.Props & { destructive?: boolean } @@ -223,7 +223,7 @@ export function ContextMenuSubTrigger({ } type ContextMenuSubContentProps = { - children: React.ReactNode + children: ReactNode placement?: Placement sideOffset?: number alignOffset?: number @@ -258,7 +258,7 @@ export function ContextMenuSubContent({ export function ContextMenuGroupLabel({ className, ...props -}: React.ComponentPropsWithoutRef) { +}: BaseContextMenu.GroupLabel.Props) { return ( ) { +}: BaseContextMenu.Separator.Props) { return ( , 'children'> +type DialogCloseButtonProps = Omit export function DialogCloseButton({ className, @@ -40,10 +40,10 @@ export function DialogCloseButton({ } type DialogContentProps = { - children: React.ReactNode + children: ReactNode className?: string overlayClassName?: string - backdropProps?: React.ComponentPropsWithoutRef + backdropProps?: BaseDialog.Backdrop.Props } export function DialogContent({ diff --git a/web/app/components/base/ui/dropdown-menu/index.tsx b/web/app/components/base/ui/dropdown-menu/index.tsx index ca73e8b003..9046e25ca2 100644 --- a/web/app/components/base/ui/dropdown-menu/index.tsx +++ b/web/app/components/base/ui/dropdown-menu/index.tsx @@ -1,9 +1,9 @@ 'use client' +import type { ReactNode } from 'react' import type { Placement } from '@/app/components/base/ui/placement' import { Menu } from '@base-ui/react/menu' import { cn } from '@langgenius/dify-ui/cn' -import * as React from 'react' import { overlayGroupLabelClassName, overlayIndicatorClassName, @@ -23,7 +23,7 @@ export const DropdownMenuRadioGroup = Menu.RadioGroup export function DropdownMenuRadioItem({ className, ...props -}: React.ComponentPropsWithoutRef) { +}: Menu.RadioItem.Props) { return ( , 'children'>) { +}: Omit) { return ( ) { +}: Menu.CheckboxItem.Props) { return ( , 'children'>) { +}: Omit) { return ( ) { +}: Menu.GroupLabel.Props) { return ( , + Menu.Positioner.Props, 'children' | 'className' | 'side' | 'align' | 'sideOffset' | 'alignOffset' > popupProps?: Omit< - React.ComponentPropsWithoutRef, + Menu.Popup.Props, 'children' | 'className' > } @@ -170,7 +170,7 @@ export function DropdownMenuContent({ }) } -type DropdownMenuSubTriggerProps = React.ComponentPropsWithoutRef & { +type DropdownMenuSubTriggerProps = Menu.SubmenuTrigger.Props & { destructive?: boolean } @@ -192,7 +192,7 @@ export function DropdownMenuSubTrigger({ } type DropdownMenuSubContentProps = { - children: React.ReactNode + children: ReactNode placement?: Placement sideOffset?: number alignOffset?: number @@ -224,7 +224,7 @@ export function DropdownMenuSubContent({ }) } -type DropdownMenuItemProps = React.ComponentPropsWithoutRef & { +type DropdownMenuItemProps = Menu.Item.Props & { destructive?: boolean } @@ -241,7 +241,7 @@ export function DropdownMenuItem({ ) } -type DropdownMenuLinkItemProps = React.ComponentPropsWithoutRef & { +type DropdownMenuLinkItemProps = Menu.LinkItem.Props & { destructive?: boolean } @@ -263,7 +263,7 @@ export function DropdownMenuLinkItem({ export function DropdownMenuSeparator({ className, ...props -}: React.ComponentPropsWithoutRef) { +}: Menu.Separator.Props) { return ( +export type NumberFieldRootProps = BaseNumberField.Root.Props export const numberFieldGroupVariants = cva( [ @@ -32,7 +32,7 @@ export const numberFieldGroupVariants = cva( ) export type NumberFieldSize = NonNullable['size']> -export type NumberFieldGroupProps = React.ComponentPropsWithoutRef & VariantProps +export type NumberFieldGroupProps = BaseNumberField.Group.Props & VariantProps export function NumberFieldGroup({ className, @@ -67,7 +67,7 @@ export const numberFieldInputVariants = cva( }, ) -export type NumberFieldInputProps = Omit, 'size'> & VariantProps +export type NumberFieldInputProps = Omit & VariantProps export function NumberFieldInput({ className, @@ -97,7 +97,7 @@ export const numberFieldUnitVariants = cva( }, ) -export type NumberFieldUnitProps = React.HTMLAttributes & VariantProps +export type NumberFieldUnitProps = HTMLAttributes & VariantProps export function NumberFieldUnit({ className, @@ -116,7 +116,7 @@ const numberFieldControlsVariants = cva( 'flex shrink-0 flex-col items-stretch border-l border-divider-subtle bg-transparent text-text-tertiary', ) -export type NumberFieldControlsProps = React.HTMLAttributes +export type NumberFieldControlsProps = HTMLAttributes export function NumberFieldControls({ className, @@ -185,7 +185,7 @@ type NumberFieldButtonVariantProps = Omit< 'direction' > -export type NumberFieldButtonProps = React.ComponentPropsWithoutRef & NumberFieldButtonVariantProps +export type NumberFieldButtonProps = BaseNumberField.Increment.Props & NumberFieldButtonVariantProps const incrementAriaLabel = 'Increment value' const decrementAriaLabel = 'Decrement value' diff --git a/web/app/components/base/ui/popover/index.tsx b/web/app/components/base/ui/popover/index.tsx index 4383112a4b..dbde17ae21 100644 --- a/web/app/components/base/ui/popover/index.tsx +++ b/web/app/components/base/ui/popover/index.tsx @@ -1,9 +1,9 @@ 'use client' +import type { ReactNode } from 'react' import type { Placement } from '@/app/components/base/ui/placement' import { Popover as BasePopover } from '@base-ui/react/popover' import { cn } from '@langgenius/dify-ui/cn' -import * as React from 'react' import { parsePlacement } from '@/app/components/base/ui/placement' export const Popover = BasePopover.Root @@ -15,18 +15,18 @@ export const PopoverTitle = BasePopover.Title export const PopoverDescription = BasePopover.Description type PopoverContentProps = { - children: React.ReactNode + children: ReactNode placement?: Placement sideOffset?: number alignOffset?: number className?: string popupClassName?: string positionerProps?: Omit< - React.ComponentPropsWithoutRef, + BasePopover.Positioner.Props, 'children' | 'className' | 'side' | 'align' | 'sideOffset' | 'alignOffset' > popupProps?: Omit< - React.ComponentPropsWithoutRef, + BasePopover.Popup.Props, 'children' | 'className' > } diff --git a/web/app/components/base/ui/scroll-area/index.tsx b/web/app/components/base/ui/scroll-area/index.tsx index 242f735054..9a23145039 100644 --- a/web/app/components/base/ui/scroll-area/index.tsx +++ b/web/app/components/base/ui/scroll-area/index.tsx @@ -1,12 +1,12 @@ 'use client' +import type { ReactNode } from 'react' import { ScrollArea as BaseScrollArea } from '@base-ui/react/scroll-area' import { cn } from '@langgenius/dify-ui/cn' -import * as React from 'react' import './scroll-area.css' export const ScrollAreaRoot = BaseScrollArea.Root -type ScrollAreaRootProps = React.ComponentPropsWithRef +type ScrollAreaRootProps = BaseScrollArea.Root.Props export const ScrollAreaContent = BaseScrollArea.Content @@ -17,7 +17,7 @@ type ScrollAreaSlotClassNames = { } type ScrollAreaProps = Omit & { - children: React.ReactNode + children: ReactNode orientation?: 'vertical' | 'horizontal' slotClassNames?: ScrollAreaSlotClassNames label?: string @@ -45,7 +45,7 @@ const scrollAreaViewportClassName = cn( const scrollAreaCornerClassName = 'bg-transparent' -type ScrollAreaViewportProps = React.ComponentPropsWithRef +type ScrollAreaViewportProps = BaseScrollArea.Viewport.Props export function ScrollAreaViewport({ className, @@ -59,7 +59,7 @@ export function ScrollAreaViewport({ ) } -type ScrollAreaScrollbarProps = React.ComponentPropsWithRef +type ScrollAreaScrollbarProps = BaseScrollArea.Scrollbar.Props export function ScrollAreaScrollbar({ className, @@ -74,7 +74,7 @@ export function ScrollAreaScrollbar({ ) } -type ScrollAreaThumbProps = React.ComponentPropsWithRef +type ScrollAreaThumbProps = BaseScrollArea.Thumb.Props export function ScrollAreaThumb({ className, @@ -88,7 +88,7 @@ export function ScrollAreaThumb({ ) } -type ScrollAreaCornerProps = React.ComponentPropsWithRef +type ScrollAreaCornerProps = BaseScrollArea.Corner.Props export function ScrollAreaCorner({ className, diff --git a/web/app/components/base/ui/select/index.tsx b/web/app/components/base/ui/select/index.tsx index 81514a9ad5..80a32fca1e 100644 --- a/web/app/components/base/ui/select/index.tsx +++ b/web/app/components/base/ui/select/index.tsx @@ -1,9 +1,9 @@ 'use client' +import type { ReactNode } from 'react' import type { Placement } from '@/app/components/base/ui/placement' import { Select as BaseSelect } from '@base-ui/react/select' import { cn } from '@langgenius/dify-ui/cn' -import * as React from 'react' import { overlayGroupLabelClassName, overlaySeparatorClassName, @@ -21,7 +21,7 @@ const selectSizeClassName: Record = { large: 'h-9 gap-0.5 rounded-[10px] px-4 py-2 system-md-regular', } -type SelectTriggerProps = React.ComponentPropsWithoutRef & { +type SelectTriggerProps = BaseSelect.Trigger.Props & { size?: 'small' | 'regular' | 'large' } @@ -58,7 +58,7 @@ export function SelectTrigger({ export function SelectGroupLabel({ className, ...props -}: React.ComponentPropsWithoutRef) { +}: BaseSelect.GroupLabel.Props) { return ( ) { +}: BaseSelect.Separator.Props) { return ( , + BaseSelect.Positioner.Props, 'children' | 'className' | 'side' | 'align' | 'sideOffset' | 'alignOffset' > popupProps?: Omit< - React.ComponentPropsWithoutRef, + BaseSelect.Popup.Props, 'children' | 'className' > listProps?: Omit< - React.ComponentPropsWithoutRef, + BaseSelect.List.Props, 'children' | 'className' > } @@ -150,7 +150,7 @@ export function SelectContent({ export function SelectItem({ className, ...props -}: React.ComponentPropsWithoutRef) { +}: BaseSelect.Item.Props) { return ( ) { +}: BaseSelect.ItemText.Props) { return ( , 'children'>) { +}: Omit) { return ( , 'children' | 'className'> +} & Omit export function TooltipContent({ children, diff --git a/web/app/components/header/account-dropdown/workplace-selector/__tests__/index.spec.tsx b/web/app/components/header/account-dropdown/workplace-selector/__tests__/index.spec.tsx index c8224bf10d..338096a9e1 100644 --- a/web/app/components/header/account-dropdown/workplace-selector/__tests__/index.spec.tsx +++ b/web/app/components/header/account-dropdown/workplace-selector/__tests__/index.spec.tsx @@ -163,7 +163,7 @@ describe('WorkplaceSelector', () => { await waitFor(() => { expect(mockNotify).toHaveBeenCalledWith({ type: 'error', - message: 'common.provider.saveFailed', + message: 'common.actionMsg.modifiedUnsuccessfully', }) }) }) diff --git a/web/app/components/header/account-dropdown/workplace-selector/index.tsx b/web/app/components/header/account-dropdown/workplace-selector/index.tsx index aa49075621..a155df48d9 100644 --- a/web/app/components/header/account-dropdown/workplace-selector/index.tsx +++ b/web/app/components/header/account-dropdown/workplace-selector/index.tsx @@ -29,7 +29,7 @@ const WorkplaceSelector = () => { location.assign(`${location.origin}${basePath}`) } catch { - toast.error(t('provider.saveFailed', { ns: 'common' })) + toast.error(t('actionMsg.modifiedUnsuccessfully', { ns: 'common' })) } } From 7f4fe4d064245392f311227ba6eb5a2bb442bd53 Mon Sep 17 00:00:00 2001 From: jimcody1995 Date: Thu, 16 Apr 2026 00:52:11 -0700 Subject: [PATCH 04/38] test: migrate clean notion task tests to SQLAlchemy 2.0 APIs (#35159) --- .../tasks/test_clean_notion_document_task.py | 174 ++++-------------- 1 file changed, 34 insertions(+), 140 deletions(-) diff --git a/api/tests/test_containers_integration_tests/tasks/test_clean_notion_document_task.py b/api/tests/test_containers_integration_tests/tasks/test_clean_notion_document_task.py index 2fb62e0fc0..fa3ac12cf0 100644 --- a/api/tests/test_containers_integration_tests/tasks/test_clean_notion_document_task.py +++ b/api/tests/test_containers_integration_tests/tasks/test_clean_notion_document_task.py @@ -11,7 +11,8 @@ from unittest.mock import Mock, patch import pytest from faker import Faker -from sqlalchemy import func, select +from sqlalchemy import ColumnElement, func, select +from sqlalchemy.orm import Session from core.rag.index_processor.constant.index_type import IndexStructureType from models.dataset import Dataset, Document, DocumentSegment @@ -21,6 +22,14 @@ from tasks.clean_notion_document_task import clean_notion_document_task from tests.test_containers_integration_tests.helpers import generate_valid_password +def _count_documents(session: Session, condition: ColumnElement[bool]) -> int: + return session.scalar(select(func.count()).select_from(Document).where(condition)) or 0 + + +def _count_segments(session: Session, condition: ColumnElement[bool]) -> int: + return session.scalar(select(func.count()).select_from(DocumentSegment).where(condition)) or 0 + + class TestCleanNotionDocumentTask: """Integration tests for clean_notion_document_task using testcontainers.""" @@ -146,29 +155,14 @@ class TestCleanNotionDocumentTask: db_session_with_containers.commit() # Verify data exists before cleanup - assert ( - db_session_with_containers.scalar( - select(func.count()).select_from(Document).where(Document.id.in_(document_ids)) - ) - == 3 - ) - assert ( - db_session_with_containers.scalar( - select(func.count()).select_from(DocumentSegment).where(DocumentSegment.document_id.in_(document_ids)) - ) - == 6 - ) + assert _count_documents(db_session_with_containers, Document.id.in_(document_ids)) == 3 + assert _count_segments(db_session_with_containers, DocumentSegment.document_id.in_(document_ids)) == 6 # Execute cleanup task clean_notion_document_task(document_ids, dataset.id) # Verify segments are deleted - assert ( - db_session_with_containers.scalar( - select(func.count()).select_from(DocumentSegment).where(DocumentSegment.document_id.in_(document_ids)) - ) - == 0 - ) + assert _count_segments(db_session_with_containers, DocumentSegment.document_id.in_(document_ids)) == 0 # Verify index processor was called mock_processor = mock_index_processor_factory.return_value.init_index_processor.return_value @@ -328,12 +322,7 @@ class TestCleanNotionDocumentTask: # The task properly handles various index types and document configurations. # Verify segments are deleted - assert ( - db_session_with_containers.scalar( - select(func.count()).select_from(DocumentSegment).where(DocumentSegment.document_id == document.id) - ) - == 0 - ) + assert _count_segments(db_session_with_containers, DocumentSegment.document_id == document.id) == 0 # Reset mock for next iteration mock_index_processor_factory.reset_mock() @@ -416,12 +405,7 @@ class TestCleanNotionDocumentTask: clean_notion_document_task([document.id], dataset.id) # Verify segments are deleted - assert ( - db_session_with_containers.scalar( - select(func.count()).select_from(DocumentSegment).where(DocumentSegment.document_id == document.id) - ) - == 0 - ) + assert _count_segments(db_session_with_containers, DocumentSegment.document_id == document.id) == 0 # Note: This test successfully verifies that segments without index_node_ids # are properly deleted from the database. @@ -507,18 +491,8 @@ class TestCleanNotionDocumentTask: db_session_with_containers.commit() # Verify all data exists before cleanup - assert ( - db_session_with_containers.scalar( - select(func.count()).select_from(Document).where(Document.dataset_id == dataset.id) - ) - == 5 - ) - assert ( - db_session_with_containers.scalar( - select(func.count()).select_from(DocumentSegment).where(DocumentSegment.dataset_id == dataset.id) - ) - == 10 - ) + assert _count_documents(db_session_with_containers, Document.dataset_id == dataset.id) == 5 + assert _count_segments(db_session_with_containers, DocumentSegment.dataset_id == dataset.id) == 10 # Clean up only first 3 documents documents_to_clean = [doc.id for doc in documents[:3]] @@ -528,29 +502,12 @@ class TestCleanNotionDocumentTask: clean_notion_document_task(documents_to_clean, dataset.id) # Verify only specified documents' segments are deleted - assert ( - db_session_with_containers.scalar( - select(func.count()) - .select_from(DocumentSegment) - .where(DocumentSegment.document_id.in_(documents_to_clean)) - ) - == 0 - ) + assert _count_segments(db_session_with_containers, DocumentSegment.document_id.in_(documents_to_clean)) == 0 # Verify remaining documents and segments are intact remaining_docs = [doc.id for doc in documents[3:]] - assert ( - db_session_with_containers.scalar( - select(func.count()).select_from(Document).where(Document.id.in_(remaining_docs)) - ) - == 2 - ) - assert ( - db_session_with_containers.scalar( - select(func.count()).select_from(DocumentSegment).where(DocumentSegment.document_id.in_(remaining_docs)) - ) - == 4 - ) + assert _count_documents(db_session_with_containers, Document.id.in_(remaining_docs)) == 2 + assert _count_segments(db_session_with_containers, DocumentSegment.document_id.in_(remaining_docs)) == 4 # Note: This test successfully verifies partial document cleanup operations. # The database operations work correctly, isolating only the specified documents. @@ -634,23 +591,13 @@ class TestCleanNotionDocumentTask: db_session_with_containers.commit() # Verify all segments exist before cleanup - assert ( - db_session_with_containers.scalar( - select(func.count()).select_from(DocumentSegment).where(DocumentSegment.document_id == document.id) - ) - == 4 - ) + assert _count_segments(db_session_with_containers, DocumentSegment.document_id == document.id) == 4 # Execute cleanup task clean_notion_document_task([document.id], dataset.id) # Verify all segments are deleted regardless of status - assert ( - db_session_with_containers.scalar( - select(func.count()).select_from(DocumentSegment).where(DocumentSegment.document_id == document.id) - ) - == 0 - ) + assert _count_segments(db_session_with_containers, DocumentSegment.document_id == document.id) == 0 # Note: This test successfully verifies database operations. # IndexProcessor verification would require more sophisticated mocking. @@ -820,16 +767,9 @@ class TestCleanNotionDocumentTask: db_session_with_containers.commit() # Verify all data exists before cleanup + assert _count_documents(db_session_with_containers, Document.dataset_id == dataset.id) == num_documents assert ( - db_session_with_containers.scalar( - select(func.count()).select_from(Document).where(Document.dataset_id == dataset.id) - ) - == num_documents - ) - assert ( - db_session_with_containers.scalar( - select(func.count()).select_from(DocumentSegment).where(DocumentSegment.dataset_id == dataset.id) - ) + _count_segments(db_session_with_containers, DocumentSegment.dataset_id == dataset.id) == num_documents * num_segments_per_doc ) @@ -838,12 +778,7 @@ class TestCleanNotionDocumentTask: clean_notion_document_task(all_document_ids, dataset.id) # Verify all segments are deleted - assert ( - db_session_with_containers.scalar( - select(func.count()).select_from(DocumentSegment).where(DocumentSegment.dataset_id == dataset.id) - ) - == 0 - ) + assert _count_segments(db_session_with_containers, DocumentSegment.dataset_id == dataset.id) == 0 # Note: This test successfully verifies bulk document cleanup operations. # The database efficiently handles large-scale deletions. @@ -950,29 +885,12 @@ class TestCleanNotionDocumentTask: clean_notion_document_task([target_document.id], target_dataset.id) # Verify only documents' segments from target dataset are deleted - assert ( - db_session_with_containers.scalar( - select(func.count()) - .select_from(DocumentSegment) - .where(DocumentSegment.document_id == target_document.id) - ) - == 0 - ) + assert _count_segments(db_session_with_containers, DocumentSegment.document_id == target_document.id) == 0 # Verify documents from other datasets remain intact remaining_docs = [doc.id for doc in all_documents[1:]] - assert ( - db_session_with_containers.scalar( - select(func.count()).select_from(Document).where(Document.id.in_(remaining_docs)) - ) - == 2 - ) - assert ( - db_session_with_containers.scalar( - select(func.count()).select_from(DocumentSegment).where(DocumentSegment.document_id.in_(remaining_docs)) - ) - == 6 - ) + assert _count_documents(db_session_with_containers, Document.id.in_(remaining_docs)) == 2 + assert _count_segments(db_session_with_containers, DocumentSegment.document_id.in_(remaining_docs)) == 6 # Note: This test successfully verifies multi-tenant isolation. # Only documents from the target dataset are affected, maintaining tenant separation. @@ -1067,13 +985,9 @@ class TestCleanNotionDocumentTask: db_session_with_containers.commit() # Verify all data exists before cleanup - assert db_session_with_containers.scalar( - select(func.count()).select_from(Document).where(Document.dataset_id == dataset.id) - ) == len(document_statuses) + assert _count_documents(db_session_with_containers, Document.dataset_id == dataset.id) == len(document_statuses) assert ( - db_session_with_containers.scalar( - select(func.count()).select_from(DocumentSegment).where(DocumentSegment.dataset_id == dataset.id) - ) + _count_segments(db_session_with_containers, DocumentSegment.dataset_id == dataset.id) == len(document_statuses) * 2 ) @@ -1082,12 +996,7 @@ class TestCleanNotionDocumentTask: clean_notion_document_task(all_document_ids, dataset.id) # Verify all segments are deleted regardless of status - assert ( - db_session_with_containers.scalar( - select(func.count()).select_from(DocumentSegment).where(DocumentSegment.dataset_id == dataset.id) - ) - == 0 - ) + assert _count_segments(db_session_with_containers, DocumentSegment.dataset_id == dataset.id) == 0 # Note: This test successfully verifies cleanup of documents in various states. # All documents are deleted regardless of their indexing status. @@ -1185,29 +1094,14 @@ class TestCleanNotionDocumentTask: db_session_with_containers.commit() # Verify data exists before cleanup - assert ( - db_session_with_containers.scalar( - select(func.count()).select_from(Document).where(Document.id == document.id) - ) - == 1 - ) - assert ( - db_session_with_containers.scalar( - select(func.count()).select_from(DocumentSegment).where(DocumentSegment.document_id == document.id) - ) - == 3 - ) + assert _count_documents(db_session_with_containers, Document.id == document.id) == 1 + assert _count_segments(db_session_with_containers, DocumentSegment.document_id == document.id) == 3 # Execute cleanup task clean_notion_document_task([document.id], dataset.id) # Verify segments are deleted - assert ( - db_session_with_containers.scalar( - select(func.count()).select_from(DocumentSegment).where(DocumentSegment.document_id == document.id) - ) - == 0 - ) + assert _count_segments(db_session_with_containers, DocumentSegment.document_id == document.id) == 0 # Note: This test successfully verifies cleanup of documents with rich metadata. # The task properly handles complex document structures and metadata fields. From e8af6a6b3b23ad06851cfc49fdd5727a49f2a3e1 Mon Sep 17 00:00:00 2001 From: James <63717587+jamesrayammons@users.noreply.github.com> Date: Thu, 16 Apr 2026 09:55:21 +0200 Subject: [PATCH 05/38] test: migrate conversation service mock tests to testcontainers (#35198) Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> --- .../test_conversation_service_variables.py | 524 ++++++++++++ .../services/test_conversation_service.py | 795 ------------------ 2 files changed, 524 insertions(+), 795 deletions(-) create mode 100644 api/tests/test_containers_integration_tests/services/test_conversation_service_variables.py diff --git a/api/tests/test_containers_integration_tests/services/test_conversation_service_variables.py b/api/tests/test_containers_integration_tests/services/test_conversation_service_variables.py new file mode 100644 index 0000000000..1011db9063 --- /dev/null +++ b/api/tests/test_containers_integration_tests/services/test_conversation_service_variables.py @@ -0,0 +1,524 @@ +from __future__ import annotations + +from datetime import datetime, timedelta +from unittest.mock import patch +from uuid import uuid4 + +import pytest +from graphon.variables import FloatVariable, IntegerVariable, StringVariable +from sqlalchemy.orm import sessionmaker + +from core.app.entities.app_invoke_entities import InvokeFrom +from extensions.ext_database import db +from models.account import Account, Tenant, TenantAccountJoin +from models.enums import ConversationFromSource +from models.model import App, Conversation, EndUser +from models.workflow import ConversationVariable +from services.conversation_service import ConversationService +from services.errors.conversation import ( + ConversationVariableNotExistsError, + ConversationVariableTypeMismatchError, + LastConversationNotExistsError, +) + + +class ConversationServiceVariableIntegrationFactory: + @staticmethod + def create_app_and_account(db_session_with_containers): + tenant = Tenant(name=f"Tenant {uuid4()}") + db_session_with_containers.add(tenant) + db_session_with_containers.flush() + + account = Account( + name=f"Account {uuid4()}", + email=f"conversation-variable-{uuid4()}@example.com", + password="hashed-password", + password_salt="salt", + interface_language="en-US", + timezone="UTC", + ) + db_session_with_containers.add(account) + db_session_with_containers.flush() + + tenant_join = TenantAccountJoin( + tenant_id=tenant.id, + account_id=account.id, + role="owner", + current=True, + ) + db_session_with_containers.add(tenant_join) + db_session_with_containers.flush() + + app = App( + tenant_id=tenant.id, + name=f"App {uuid4()}", + description="", + mode="chat", + icon_type="emoji", + icon="bot", + icon_background="#FFFFFF", + enable_site=False, + enable_api=True, + api_rpm=100, + api_rph=100, + is_demo=False, + is_public=False, + is_universal=False, + created_by=account.id, + updated_by=account.id, + ) + db_session_with_containers.add(app) + db_session_with_containers.commit() + + return app, account + + @staticmethod + def create_end_user(db_session_with_containers, app: App): + end_user = EndUser( + tenant_id=app.tenant_id, + app_id=app.id, + type=InvokeFrom.SERVICE_API.value, + external_user_id=f"external-{uuid4()}", + name=f"End User {uuid4()}", + is_anonymous=False, + session_id=f"session-{uuid4()}", + ) + db_session_with_containers.add(end_user) + db_session_with_containers.commit() + return end_user + + @staticmethod + def create_conversation( + db_session_with_containers, + app: App, + user: Account | EndUser, + *, + name: str | None = None, + invoke_from: InvokeFrom = InvokeFrom.WEB_APP, + created_at: datetime | None = None, + updated_at: datetime | None = None, + ) -> Conversation: + conversation = Conversation( + app_id=app.id, + app_model_config_id=None, + model_provider=None, + model_id="", + override_model_configs=None, + mode=app.mode, + name=name or f"Conversation {uuid4()}", + summary="", + inputs={}, + introduction="", + system_instruction="", + system_instruction_tokens=0, + status="normal", + invoke_from=invoke_from.value, + from_source=ConversationFromSource.API if isinstance(user, EndUser) else ConversationFromSource.CONSOLE, + from_end_user_id=user.id if isinstance(user, EndUser) else None, + from_account_id=user.id if isinstance(user, Account) else None, + dialogue_count=0, + is_deleted=False, + ) + conversation.inputs = {} + if created_at is not None: + conversation.created_at = created_at + if updated_at is not None: + conversation.updated_at = updated_at + + db_session_with_containers.add(conversation) + db_session_with_containers.commit() + return conversation + + @staticmethod + def create_variable( + db_session_with_containers, + *, + app: App, + conversation: Conversation, + variable: StringVariable | FloatVariable | IntegerVariable, + created_at: datetime | None = None, + ) -> ConversationVariable: + row = ConversationVariable.from_variable(app_id=app.id, conversation_id=conversation.id, variable=variable) + if created_at is not None: + row.created_at = created_at + row.updated_at = created_at + + db_session_with_containers.add(row) + db_session_with_containers.commit() + return row + + +@pytest.fixture +def real_conversation_service_session_factory(flask_app_with_containers): + del flask_app_with_containers + real_session_maker = sessionmaker(bind=db.engine, expire_on_commit=False) + + with ( + patch("services.conversation_service.session_factory.create_session", side_effect=lambda: real_session_maker()), + patch("services.conversation_service.session_factory.get_session_maker", return_value=real_session_maker), + ): + yield + + +class TestConversationServiceVariables: + def test_get_conversational_variable_success( + self, db_session_with_containers, real_conversation_service_session_factory + ): + del real_conversation_service_session_factory + factory = ConversationServiceVariableIntegrationFactory + app, account = factory.create_app_and_account(db_session_with_containers) + conversation = factory.create_conversation(db_session_with_containers, app, account) + older_time = datetime(2024, 1, 1, 12, 0, 0) + newer_time = older_time + timedelta(minutes=5) + + first_variable = factory.create_variable( + db_session_with_containers, + app=app, + conversation=conversation, + variable=StringVariable(id=str(uuid4()), name="topic", value="billing"), + created_at=older_time, + ) + second_variable = factory.create_variable( + db_session_with_containers, + app=app, + conversation=conversation, + variable=StringVariable(id=str(uuid4()), name="priority", value="high"), + created_at=newer_time, + ) + + result = ConversationService.get_conversational_variable( + app_model=app, + conversation_id=conversation.id, + user=account, + limit=10, + last_id=None, + ) + + assert [item["id"] for item in result.data] == [first_variable.id, second_variable.id] + assert [item["name"] for item in result.data] == ["topic", "priority"] + assert result.limit == 10 + assert result.has_more is False + + def test_get_conversational_variable_with_last_id( + self, db_session_with_containers, real_conversation_service_session_factory + ): + del real_conversation_service_session_factory + factory = ConversationServiceVariableIntegrationFactory + app, account = factory.create_app_and_account(db_session_with_containers) + conversation = factory.create_conversation(db_session_with_containers, app, account) + base_time = datetime(2024, 1, 1, 9, 0, 0) + + first_variable = factory.create_variable( + db_session_with_containers, + app=app, + conversation=conversation, + variable=StringVariable(id=str(uuid4()), name="topic", value="billing"), + created_at=base_time, + ) + second_variable = factory.create_variable( + db_session_with_containers, + app=app, + conversation=conversation, + variable=StringVariable(id=str(uuid4()), name="priority", value="high"), + created_at=base_time + timedelta(minutes=1), + ) + third_variable = factory.create_variable( + db_session_with_containers, + app=app, + conversation=conversation, + variable=StringVariable(id=str(uuid4()), name="owner", value="alice"), + created_at=base_time + timedelta(minutes=2), + ) + + result = ConversationService.get_conversational_variable( + app_model=app, + conversation_id=conversation.id, + user=account, + limit=10, + last_id=first_variable.id, + ) + + assert [item["id"] for item in result.data] == [second_variable.id, third_variable.id] + assert result.has_more is False + + def test_get_conversational_variable_last_id_not_found_raises_error( + self, db_session_with_containers, real_conversation_service_session_factory + ): + del real_conversation_service_session_factory + factory = ConversationServiceVariableIntegrationFactory + app, account = factory.create_app_and_account(db_session_with_containers) + conversation = factory.create_conversation(db_session_with_containers, app, account) + + with pytest.raises(ConversationVariableNotExistsError): + ConversationService.get_conversational_variable( + app_model=app, + conversation_id=conversation.id, + user=account, + limit=10, + last_id=str(uuid4()), + ) + + def test_get_conversational_variable_sets_has_more( + self, db_session_with_containers, real_conversation_service_session_factory + ): + del real_conversation_service_session_factory + factory = ConversationServiceVariableIntegrationFactory + app, account = factory.create_app_and_account(db_session_with_containers) + conversation = factory.create_conversation(db_session_with_containers, app, account) + + for index in range(3): + factory.create_variable( + db_session_with_containers, + app=app, + conversation=conversation, + variable=StringVariable(id=str(uuid4()), name=f"var_{index}", value=f"value_{index}"), + created_at=datetime(2024, 1, 1, 10, 0, index), + ) + + result = ConversationService.get_conversational_variable( + app_model=app, + conversation_id=conversation.id, + user=account, + limit=2, + last_id=None, + ) + + assert len(result.data) == 2 + assert result.has_more is True + + def test_update_conversation_variable_success( + self, db_session_with_containers, real_conversation_service_session_factory + ): + del real_conversation_service_session_factory + factory = ConversationServiceVariableIntegrationFactory + app, account = factory.create_app_and_account(db_session_with_containers) + conversation = factory.create_conversation(db_session_with_containers, app, account) + existing = factory.create_variable( + db_session_with_containers, + app=app, + conversation=conversation, + variable=StringVariable(id=str(uuid4()), name="topic", value="billing"), + ) + updated_at = datetime(2024, 1, 1, 15, 0, 0) + + with patch("services.conversation_service.naive_utc_now", return_value=updated_at): + result = ConversationService.update_conversation_variable( + app_model=app, + conversation_id=conversation.id, + variable_id=existing.id, + user=account, + new_value="support", + ) + + db_session_with_containers.expire_all() + persisted = db_session_with_containers.get(ConversationVariable, (existing.id, conversation.id)) + + assert persisted is not None + assert persisted.to_variable().value == "support" + assert result["id"] == existing.id + assert result["value"] == "support" + assert result["updated_at"] == updated_at + + def test_update_conversation_variable_not_found_raises_error( + self, db_session_with_containers, real_conversation_service_session_factory + ): + del real_conversation_service_session_factory + factory = ConversationServiceVariableIntegrationFactory + app, account = factory.create_app_and_account(db_session_with_containers) + conversation = factory.create_conversation(db_session_with_containers, app, account) + + with pytest.raises(ConversationVariableNotExistsError): + ConversationService.update_conversation_variable( + app_model=app, + conversation_id=conversation.id, + variable_id=str(uuid4()), + user=account, + new_value="support", + ) + + def test_update_conversation_variable_type_mismatch_raises_error( + self, db_session_with_containers, real_conversation_service_session_factory + ): + del real_conversation_service_session_factory + factory = ConversationServiceVariableIntegrationFactory + app, account = factory.create_app_and_account(db_session_with_containers) + conversation = factory.create_conversation(db_session_with_containers, app, account) + existing = factory.create_variable( + db_session_with_containers, + app=app, + conversation=conversation, + variable=FloatVariable(id=str(uuid4()), name="score", value=1.5), + ) + + with pytest.raises(ConversationVariableTypeMismatchError, match="expects float"): + ConversationService.update_conversation_variable( + app_model=app, + conversation_id=conversation.id, + variable_id=existing.id, + user=account, + new_value="wrong-type", + ) + + def test_update_conversation_variable_integer_number_compatibility( + self, db_session_with_containers, real_conversation_service_session_factory + ): + del real_conversation_service_session_factory + factory = ConversationServiceVariableIntegrationFactory + app, account = factory.create_app_and_account(db_session_with_containers) + conversation = factory.create_conversation(db_session_with_containers, app, account) + existing = factory.create_variable( + db_session_with_containers, + app=app, + conversation=conversation, + variable=IntegerVariable(id=str(uuid4()), name="attempts", value=1), + ) + + result = ConversationService.update_conversation_variable( + app_model=app, + conversation_id=conversation.id, + variable_id=existing.id, + user=account, + new_value=42, + ) + + db_session_with_containers.expire_all() + persisted = db_session_with_containers.get(ConversationVariable, (existing.id, conversation.id)) + + assert persisted is not None + assert persisted.to_variable().value == 42 + assert result["value"] == 42 + + +class TestConversationServicePaginationWithContainers: + def test_pagination_by_last_id_raises_error_when_last_id_missing(self, db_session_with_containers): + factory = ConversationServiceVariableIntegrationFactory + app, account = factory.create_app_and_account(db_session_with_containers) + + with pytest.raises(LastConversationNotExistsError): + ConversationService.pagination_by_last_id( + session=db_session_with_containers, + app_model=app, + user=account, + last_id=str(uuid4()), + limit=20, + invoke_from=InvokeFrom.WEB_APP, + ) + + def test_pagination_by_last_id_with_default_desc_updated_at(self, db_session_with_containers): + factory = ConversationServiceVariableIntegrationFactory + app, account = factory.create_app_and_account(db_session_with_containers) + base_time = datetime(2024, 1, 1, 8, 0, 0) + newest = factory.create_conversation( + db_session_with_containers, + app, + account, + name="Newest", + updated_at=base_time + timedelta(minutes=2), + ) + middle = factory.create_conversation( + db_session_with_containers, + app, + account, + name="Middle", + updated_at=base_time + timedelta(minutes=1), + ) + oldest = factory.create_conversation( + db_session_with_containers, + app, + account, + name="Oldest", + updated_at=base_time, + ) + + result = ConversationService.pagination_by_last_id( + session=db_session_with_containers, + app_model=app, + user=account, + last_id=middle.id, + limit=10, + invoke_from=InvokeFrom.WEB_APP, + ) + + assert newest.id != middle.id + assert [conversation.id for conversation in result.data] == [oldest.id] + + def test_pagination_by_last_id_with_name_sort(self, db_session_with_containers): + factory = ConversationServiceVariableIntegrationFactory + app, account = factory.create_app_and_account(db_session_with_containers) + alpha = factory.create_conversation(db_session_with_containers, app, account, name="Alpha") + beta = factory.create_conversation(db_session_with_containers, app, account, name="Beta") + gamma = factory.create_conversation(db_session_with_containers, app, account, name="Gamma") + + result = ConversationService.pagination_by_last_id( + session=db_session_with_containers, + app_model=app, + user=account, + last_id=beta.id, + limit=10, + invoke_from=InvokeFrom.WEB_APP, + sort_by="name", + ) + + assert alpha.id != beta.id + assert [conversation.id for conversation in result.data] == [gamma.id] + + def test_pagination_filters_to_end_user_api_source(self, db_session_with_containers): + factory = ConversationServiceVariableIntegrationFactory + app, account = factory.create_app_and_account(db_session_with_containers) + end_user = factory.create_end_user(db_session_with_containers, app) + account_conversation = factory.create_conversation( + db_session_with_containers, + app, + account, + name="Console Conversation", + invoke_from=InvokeFrom.WEB_APP, + ) + end_user_conversation = factory.create_conversation( + db_session_with_containers, + app, + end_user, + name="API Conversation", + invoke_from=InvokeFrom.SERVICE_API, + ) + + result = ConversationService.pagination_by_last_id( + session=db_session_with_containers, + app_model=app, + user=end_user, + last_id=None, + limit=20, + invoke_from=InvokeFrom.SERVICE_API, + ) + + assert account_conversation.id != end_user_conversation.id + assert [conversation.id for conversation in result.data] == [end_user_conversation.id] + + def test_pagination_filters_to_account_console_source(self, db_session_with_containers): + factory = ConversationServiceVariableIntegrationFactory + app, account = factory.create_app_and_account(db_session_with_containers) + end_user = factory.create_end_user(db_session_with_containers, app) + account_conversation = factory.create_conversation( + db_session_with_containers, + app, + account, + name="Console Conversation", + invoke_from=InvokeFrom.WEB_APP, + ) + factory.create_conversation( + db_session_with_containers, + app, + end_user, + name="API Conversation", + invoke_from=InvokeFrom.SERVICE_API, + ) + + result = ConversationService.pagination_by_last_id( + session=db_session_with_containers, + app_model=app, + user=account, + last_id=None, + limit=20, + invoke_from=InvokeFrom.WEB_APP, + ) + + assert [conversation.id for conversation in result.data] == [account_conversation.id] diff --git a/api/tests/unit_tests/services/test_conversation_service.py b/api/tests/unit_tests/services/test_conversation_service.py index 68f4c51afe..2c7f13b79f 100644 --- a/api/tests/unit_tests/services/test_conversation_service.py +++ b/api/tests/unit_tests/services/test_conversation_service.py @@ -6,26 +6,15 @@ Tests are organized by functionality and include edge cases, error handling, and both positive and negative test scenarios. """ -from datetime import timedelta from unittest.mock import MagicMock, Mock, create_autospec, patch -import pytest from sqlalchemy import asc, desc from core.app.entities.app_invoke_entities import InvokeFrom from libs.datetime_utils import naive_utc_now -from libs.infinite_scroll_pagination import InfiniteScrollPagination from models import Account, ConversationVariable -from models.enums import ConversationFromSource from models.model import App, Conversation, EndUser, Message from services.conversation_service import ConversationService -from services.errors.conversation import ( - ConversationNotExistsError, - ConversationVariableNotExistsError, - ConversationVariableTypeMismatchError, - LastConversationNotExistsError, -) -from services.errors.message import MessageNotExistsError class ConversationServiceTestDataFactory: @@ -338,330 +327,9 @@ class TestConversationServiceHelpers: assert condition is not None -class TestConversationServiceGetConversation: - """Test conversation retrieval operations.""" - - @patch("services.conversation_service.db.session") - def test_get_conversation_success_with_account(self, mock_db_session): - """ - Test successful conversation retrieval with account user. - - Should return conversation when found with proper filters. - """ - # Arrange - app_model = ConversationServiceTestDataFactory.create_app_mock() - user = ConversationServiceTestDataFactory.create_account_mock() - conversation = ConversationServiceTestDataFactory.create_conversation_mock( - from_account_id=user.id, from_source=ConversationFromSource.CONSOLE - ) - - mock_db_session.scalar.return_value = conversation - - # Act - result = ConversationService.get_conversation(app_model, "conv-123", user) - - # Assert - assert result == conversation - - @patch("services.conversation_service.db.session") - def test_get_conversation_success_with_end_user(self, mock_db_session): - """ - Test successful conversation retrieval with end user. - - Should return conversation when found with proper filters for API user. - """ - # Arrange - app_model = ConversationServiceTestDataFactory.create_app_mock() - user = ConversationServiceTestDataFactory.create_end_user_mock() - conversation = ConversationServiceTestDataFactory.create_conversation_mock( - from_end_user_id=user.id, from_source=ConversationFromSource.API - ) - - mock_db_session.scalar.return_value = conversation - - # Act - result = ConversationService.get_conversation(app_model, "conv-123", user) - - # Assert - assert result == conversation - - @patch("services.conversation_service.db.session") - def test_get_conversation_not_found_raises_error(self, mock_db_session): - """ - Test that get_conversation raises error when conversation not found. - - Should raise ConversationNotExistsError when no matching conversation found. - """ - # Arrange - app_model = ConversationServiceTestDataFactory.create_app_mock() - user = ConversationServiceTestDataFactory.create_account_mock() - - mock_db_session.scalar.return_value = None - - # Act & Assert - with pytest.raises(ConversationNotExistsError): - ConversationService.get_conversation(app_model, "conv-123", user) - - -class TestConversationServiceRename: - """Test conversation rename operations.""" - - @patch("services.conversation_service.db.session") - @patch("services.conversation_service.ConversationService.get_conversation") - def test_rename_with_manual_name(self, mock_get_conversation, mock_db_session): - """ - Test renaming conversation with manual name. - - Should update conversation name and timestamp when auto_generate is False. - """ - # Arrange - app_model = ConversationServiceTestDataFactory.create_app_mock() - user = ConversationServiceTestDataFactory.create_account_mock() - conversation = ConversationServiceTestDataFactory.create_conversation_mock() - - mock_get_conversation.return_value = conversation - - # Act - result = ConversationService.rename( - app_model=app_model, - conversation_id="conv-123", - user=user, - name="New Name", - auto_generate=False, - ) - - # Assert - assert result == conversation - assert conversation.name == "New Name" - mock_db_session.commit.assert_called_once() - - -class TestConversationServiceAutoGenerateName: - """Test conversation auto-name generation operations.""" - - @patch("services.conversation_service.db.session") - @patch("services.conversation_service.LLMGenerator") - def test_auto_generate_name_success(self, mock_llm_generator, mock_db_session): - """ - Test successful auto-generation of conversation name. - - Should generate name using LLMGenerator and update conversation. - """ - # Arrange - app_model = ConversationServiceTestDataFactory.create_app_mock() - conversation = ConversationServiceTestDataFactory.create_conversation_mock() - message = ConversationServiceTestDataFactory.create_message_mock( - conversation_id=conversation.id, app_id=app_model.id - ) - - # Mock database query to return message - mock_db_session.scalar.return_value = message - - # Mock LLM generator - mock_llm_generator.generate_conversation_name.return_value = "Generated Name" - - # Act - result = ConversationService.auto_generate_name(app_model, conversation) - - # Assert - assert result == conversation - assert conversation.name == "Generated Name" - mock_llm_generator.generate_conversation_name.assert_called_once_with( - app_model.tenant_id, message.query, conversation.id, app_model.id - ) - mock_db_session.commit.assert_called_once() - - @patch("services.conversation_service.db.session") - def test_auto_generate_name_no_message_raises_error(self, mock_db_session): - """ - Test auto-generation fails when no message found. - - Should raise MessageNotExistsError when conversation has no messages. - """ - # Arrange - app_model = ConversationServiceTestDataFactory.create_app_mock() - conversation = ConversationServiceTestDataFactory.create_conversation_mock() - - # Mock database query to return None - mock_db_session.scalar.return_value = None - - # Act & Assert - with pytest.raises(MessageNotExistsError): - ConversationService.auto_generate_name(app_model, conversation) - - @patch("services.conversation_service.db.session") - @patch("services.conversation_service.LLMGenerator") - def test_auto_generate_name_handles_llm_exception(self, mock_llm_generator, mock_db_session): - """ - Test auto-generation handles LLM generator exceptions gracefully. - - Should continue without name when LLMGenerator fails. - """ - # Arrange - app_model = ConversationServiceTestDataFactory.create_app_mock() - conversation = ConversationServiceTestDataFactory.create_conversation_mock() - message = ConversationServiceTestDataFactory.create_message_mock( - conversation_id=conversation.id, app_id=app_model.id - ) - - # Mock database query to return message - mock_db_session.scalar.return_value = message - - # Mock LLM generator to raise exception - mock_llm_generator.generate_conversation_name.side_effect = Exception("LLM Error") - - # Act - result = ConversationService.auto_generate_name(app_model, conversation) - - # Assert - assert result == conversation - # Name should remain unchanged due to exception - mock_db_session.commit.assert_called_once() - - -class TestConversationServiceDelete: - """Test conversation deletion operations.""" - - @patch("services.conversation_service.delete_conversation_related_data") - @patch("services.conversation_service.db.session") - @patch("services.conversation_service.ConversationService.get_conversation") - def test_delete_success(self, mock_get_conversation, mock_db_session, mock_delete_task): - """ - Test successful conversation deletion. - - Should delete conversation and schedule cleanup task. - """ - # Arrange - app_model = ConversationServiceTestDataFactory.create_app_mock(name="Test App") - user = ConversationServiceTestDataFactory.create_account_mock() - conversation = ConversationServiceTestDataFactory.create_conversation_mock() - - mock_get_conversation.return_value = conversation - - # Act - ConversationService.delete(app_model, "conv-123", user) - - # Assert - mock_db_session.delete.assert_called_once_with(conversation) - mock_db_session.commit.assert_called_once() - mock_delete_task.delay.assert_called_once_with(conversation.id) - - class TestConversationServiceConversationalVariable: """Test conversational variable operations.""" - @patch("services.conversation_service.session_factory") - @patch("services.conversation_service.ConversationService.get_conversation") - def test_get_conversational_variable_success(self, mock_get_conversation, mock_session_factory): - """ - Test successful retrieval of conversational variables. - - Should return paginated list of variables for conversation. - """ - # Arrange - app_model = ConversationServiceTestDataFactory.create_app_mock() - user = ConversationServiceTestDataFactory.create_account_mock() - conversation = ConversationServiceTestDataFactory.create_conversation_mock() - - mock_get_conversation.return_value = conversation - - # Mock session and variables - mock_session = MagicMock() - mock_session_factory.create_session.return_value.__enter__.return_value = mock_session - - variable1 = ConversationServiceTestDataFactory.create_conversation_variable_mock() - variable2 = ConversationServiceTestDataFactory.create_conversation_variable_mock(variable_id="var-456") - - mock_session.scalars.return_value.all.return_value = [variable1, variable2] - - # Act - result = ConversationService.get_conversational_variable( - app_model=app_model, - conversation_id="conv-123", - user=user, - limit=10, - last_id=None, - ) - - # Assert - assert isinstance(result, InfiniteScrollPagination) - assert len(result.data) == 2 - assert result.limit == 10 - assert result.has_more is False - - @patch("services.conversation_service.session_factory") - @patch("services.conversation_service.ConversationService.get_conversation") - def test_get_conversational_variable_with_last_id(self, mock_get_conversation, mock_session_factory): - """ - Test retrieval of variables with last_id pagination. - - Should filter variables created after last_id. - """ - # Arrange - app_model = ConversationServiceTestDataFactory.create_app_mock() - user = ConversationServiceTestDataFactory.create_account_mock() - conversation = ConversationServiceTestDataFactory.create_conversation_mock() - - mock_get_conversation.return_value = conversation - - # Mock session and variables - mock_session = MagicMock() - mock_session_factory.create_session.return_value.__enter__.return_value = mock_session - - last_variable = ConversationServiceTestDataFactory.create_conversation_variable_mock( - created_at=naive_utc_now() - timedelta(hours=1) - ) - variable = ConversationServiceTestDataFactory.create_conversation_variable_mock(created_at=naive_utc_now()) - - mock_session.scalar.return_value = last_variable - mock_session.scalars.return_value.all.return_value = [variable] - - # Act - result = ConversationService.get_conversational_variable( - app_model=app_model, - conversation_id="conv-123", - user=user, - limit=10, - last_id="var-123", - ) - - # Assert - assert isinstance(result, InfiniteScrollPagination) - assert len(result.data) == 1 - assert result.limit == 10 - - @patch("services.conversation_service.session_factory") - @patch("services.conversation_service.ConversationService.get_conversation") - def test_get_conversational_variable_last_id_not_found_raises_error( - self, mock_get_conversation, mock_session_factory - ): - """ - Test that invalid last_id raises ConversationVariableNotExistsError. - - Should raise error when last_id doesn't exist. - """ - # Arrange - app_model = ConversationServiceTestDataFactory.create_app_mock() - user = ConversationServiceTestDataFactory.create_account_mock() - conversation = ConversationServiceTestDataFactory.create_conversation_mock() - - mock_get_conversation.return_value = conversation - - # Mock session - mock_session = MagicMock() - mock_session_factory.create_session.return_value.__enter__.return_value = mock_session - mock_session.scalar.return_value = None - - # Act & Assert - with pytest.raises(ConversationVariableNotExistsError): - ConversationService.get_conversational_variable( - app_model=app_model, - conversation_id="conv-123", - user=user, - limit=10, - last_id="invalid-id", - ) - @patch("services.conversation_service.session_factory") @patch("services.conversation_service.ConversationService.get_conversation") @patch("services.conversation_service.dify_config") @@ -698,466 +366,3 @@ class TestConversationServiceConversationalVariable: # Assert - JSON filter should be applied assert mock_session.scalars.called - - @patch("services.conversation_service.session_factory") - @patch("services.conversation_service.ConversationService.get_conversation") - @patch("services.conversation_service.dify_config") - def test_get_conversational_variable_with_name_filter_postgresql( - self, mock_config, mock_get_conversation, mock_session_factory - ): - """ - Test variable filtering by name for PostgreSQL databases. - - Should apply JSON extraction filter for variable names. - """ - # Arrange - app_model = ConversationServiceTestDataFactory.create_app_mock() - user = ConversationServiceTestDataFactory.create_account_mock() - conversation = ConversationServiceTestDataFactory.create_conversation_mock() - - mock_get_conversation.return_value = conversation - mock_config.DB_TYPE = "postgresql" - - # Mock session - mock_session = MagicMock() - mock_session_factory.create_session.return_value.__enter__.return_value = mock_session - mock_session.scalars.return_value.all.return_value = [] - - # Act - ConversationService.get_conversational_variable( - app_model=app_model, - conversation_id="conv-123", - user=user, - limit=10, - last_id=None, - variable_name="test_var", - ) - - # Assert - JSON filter should be applied - assert mock_session.scalars.called - - -class TestConversationServiceUpdateVariable: - """Test conversation variable update operations.""" - - @patch("services.conversation_service.variable_factory") - @patch("services.conversation_service.ConversationVariableUpdater") - @patch("services.conversation_service.session_factory") - @patch("services.conversation_service.ConversationService.get_conversation") - def test_update_conversation_variable_success( - self, mock_get_conversation, mock_session_factory, mock_updater_class, mock_variable_factory - ): - """ - Test successful update of conversation variable. - - Should update variable value and return updated data. - """ - # Arrange - app_model = ConversationServiceTestDataFactory.create_app_mock() - user = ConversationServiceTestDataFactory.create_account_mock() - conversation = ConversationServiceTestDataFactory.create_conversation_mock() - - mock_get_conversation.return_value = conversation - - # Mock session and existing variable - mock_session = MagicMock() - mock_session_factory.create_session.return_value.__enter__.return_value = mock_session - - existing_variable = ConversationServiceTestDataFactory.create_conversation_variable_mock(value_type="string") - mock_session.scalar.return_value = existing_variable - - # Mock variable factory and updater - updated_variable = Mock() - updated_variable.model_dump.return_value = {"id": "var-123", "name": "test_var", "value": "new_value"} - mock_variable_factory.build_conversation_variable_from_mapping.return_value = updated_variable - - mock_updater = MagicMock() - mock_updater_class.return_value = mock_updater - - # Act - result = ConversationService.update_conversation_variable( - app_model=app_model, - conversation_id="conv-123", - variable_id="var-123", - user=user, - new_value="new_value", - ) - - # Assert - assert result["id"] == "var-123" - assert result["value"] == "new_value" - mock_updater.update.assert_called_once_with("conv-123", updated_variable) - mock_updater.flush.assert_called_once() - - @patch("services.conversation_service.session_factory") - @patch("services.conversation_service.ConversationService.get_conversation") - def test_update_conversation_variable_not_found_raises_error(self, mock_get_conversation, mock_session_factory): - """ - Test update fails when variable doesn't exist. - - Should raise ConversationVariableNotExistsError. - """ - # Arrange - app_model = ConversationServiceTestDataFactory.create_app_mock() - user = ConversationServiceTestDataFactory.create_account_mock() - conversation = ConversationServiceTestDataFactory.create_conversation_mock() - - mock_get_conversation.return_value = conversation - - # Mock session - mock_session = MagicMock() - mock_session_factory.create_session.return_value.__enter__.return_value = mock_session - mock_session.scalar.return_value = None - - # Act & Assert - with pytest.raises(ConversationVariableNotExistsError): - ConversationService.update_conversation_variable( - app_model=app_model, - conversation_id="conv-123", - variable_id="invalid-id", - user=user, - new_value="new_value", - ) - - @patch("services.conversation_service.session_factory") - @patch("services.conversation_service.ConversationService.get_conversation") - def test_update_conversation_variable_type_mismatch_raises_error(self, mock_get_conversation, mock_session_factory): - """ - Test update fails when value type doesn't match expected type. - - Should raise ConversationVariableTypeMismatchError. - """ - # Arrange - app_model = ConversationServiceTestDataFactory.create_app_mock() - user = ConversationServiceTestDataFactory.create_account_mock() - conversation = ConversationServiceTestDataFactory.create_conversation_mock() - - mock_get_conversation.return_value = conversation - - # Mock session and existing variable - mock_session = MagicMock() - mock_session_factory.create_session.return_value.__enter__.return_value = mock_session - - existing_variable = ConversationServiceTestDataFactory.create_conversation_variable_mock(value_type="number") - mock_session.scalar.return_value = existing_variable - - # Act & Assert - Try to set string value for number variable - with pytest.raises(ConversationVariableTypeMismatchError): - ConversationService.update_conversation_variable( - app_model=app_model, - conversation_id="conv-123", - variable_id="var-123", - user=user, - new_value="string_value", # Wrong type - ) - - @patch("services.conversation_service.session_factory") - @patch("services.conversation_service.ConversationService.get_conversation") - def test_update_conversation_variable_integer_number_compatibility( - self, mock_get_conversation, mock_session_factory - ): - """ - Test that integer type accepts number values. - - Should allow number values for integer type variables. - """ - # Arrange - app_model = ConversationServiceTestDataFactory.create_app_mock() - user = ConversationServiceTestDataFactory.create_account_mock() - conversation = ConversationServiceTestDataFactory.create_conversation_mock() - - mock_get_conversation.return_value = conversation - - # Mock session and existing variable - mock_session = MagicMock() - mock_session_factory.create_session.return_value.__enter__.return_value = mock_session - - existing_variable = ConversationServiceTestDataFactory.create_conversation_variable_mock(value_type="integer") - mock_session.scalar.return_value = existing_variable - - # Mock variable factory and updater - updated_variable = Mock() - updated_variable.model_dump.return_value = {"id": "var-123", "name": "test_var", "value": 42} - - with ( - patch("services.conversation_service.variable_factory") as mock_variable_factory, - patch("services.conversation_service.ConversationVariableUpdater") as mock_updater_class, - ): - mock_variable_factory.build_conversation_variable_from_mapping.return_value = updated_variable - mock_updater = MagicMock() - mock_updater_class.return_value = mock_updater - - # Act - result = ConversationService.update_conversation_variable( - app_model=app_model, - conversation_id="conv-123", - variable_id="var-123", - user=user, - new_value=42, # Number value for integer type - ) - - # Assert - assert result["value"] == 42 - mock_updater.update.assert_called_once() - - -class TestConversationServicePaginationAdvanced: - """Advanced pagination tests for ConversationService.""" - - @patch("services.conversation_service.session_factory") - def test_pagination_by_last_id_with_last_id_not_found(self, mock_session_factory): - """ - Test pagination with invalid last_id raises error. - - Should raise LastConversationNotExistsError when last_id doesn't exist. - """ - # Arrange - mock_session = MagicMock() - mock_session_factory.create_session.return_value.__enter__.return_value = mock_session - mock_session.scalar.return_value = None - - app_model = ConversationServiceTestDataFactory.create_app_mock() - user = ConversationServiceTestDataFactory.create_account_mock() - - # Act & Assert - with pytest.raises(LastConversationNotExistsError): - ConversationService.pagination_by_last_id( - session=mock_session, - app_model=app_model, - user=user, - last_id="invalid-id", - limit=20, - invoke_from=InvokeFrom.WEB_APP, - ) - - @patch("services.conversation_service.session_factory") - def test_pagination_by_last_id_with_exclude_ids(self, mock_session_factory): - """ - Test pagination with exclude_ids filter. - - Should exclude specified conversation IDs from results. - """ - # Arrange - mock_session = MagicMock() - mock_session_factory.create_session.return_value.__enter__.return_value = mock_session - - conversation = ConversationServiceTestDataFactory.create_conversation_mock() - mock_session.scalars.return_value.all.return_value = [conversation] - mock_session.scalar.return_value = conversation - - app_model = ConversationServiceTestDataFactory.create_app_mock() - user = ConversationServiceTestDataFactory.create_account_mock() - - # Act - result = ConversationService.pagination_by_last_id( - session=mock_session, - app_model=app_model, - user=user, - last_id=None, - limit=20, - invoke_from=InvokeFrom.WEB_APP, - exclude_ids=["excluded-123"], - ) - - # Assert - assert isinstance(result, InfiniteScrollPagination) - assert len(result.data) == 1 - - @patch("services.conversation_service.session_factory") - def test_pagination_by_last_id_has_more_detection(self, mock_session_factory): - """ - Test pagination has_more detection logic. - - Should set has_more=True when there are more results beyond limit. - """ - # Arrange - mock_session = MagicMock() - mock_session_factory.create_session.return_value.__enter__.return_value = mock_session - - # Return exactly limit items to trigger has_more check - conversations = [ - ConversationServiceTestDataFactory.create_conversation_mock(conversation_id=f"conv-{i}") for i in range(20) - ] - mock_session.scalars.return_value.all.return_value = conversations - mock_session.scalar.return_value = conversations[-1] - - # Mock count query to return > 0 - mock_session.scalar.return_value = 5 # Additional items exist - - app_model = ConversationServiceTestDataFactory.create_app_mock() - user = ConversationServiceTestDataFactory.create_account_mock() - - # Act - result = ConversationService.pagination_by_last_id( - session=mock_session, - app_model=app_model, - user=user, - last_id=None, - limit=20, - invoke_from=InvokeFrom.WEB_APP, - ) - - # Assert - assert isinstance(result, InfiniteScrollPagination) - assert result.has_more is True - - @patch("services.conversation_service.session_factory") - def test_pagination_by_last_id_with_different_sort_by(self, mock_session_factory): - """ - Test pagination with different sort fields. - - Should handle various sort_by parameters correctly. - """ - # Arrange - mock_session = MagicMock() - mock_session_factory.create_session.return_value.__enter__.return_value = mock_session - - conversation = ConversationServiceTestDataFactory.create_conversation_mock() - mock_session.scalars.return_value.all.return_value = [conversation] - mock_session.scalar.return_value = conversation - - app_model = ConversationServiceTestDataFactory.create_app_mock() - user = ConversationServiceTestDataFactory.create_account_mock() - - # Test different sort fields - sort_fields = ["created_at", "-updated_at", "name", "-status"] - - for sort_by in sort_fields: - # Act - result = ConversationService.pagination_by_last_id( - session=mock_session, - app_model=app_model, - user=user, - last_id=None, - limit=20, - invoke_from=InvokeFrom.WEB_APP, - sort_by=sort_by, - ) - - # Assert - assert isinstance(result, InfiniteScrollPagination) - - -class TestConversationServiceEdgeCases: - """Test edge cases and error scenarios.""" - - @patch("services.conversation_service.session_factory") - def test_pagination_with_end_user_api_source(self, mock_session_factory): - """ - Test pagination correctly handles EndUser with API source. - - Should use 'api' as from_source for EndUser instances. - """ - # Arrange - mock_session = MagicMock() - mock_session_factory.create_session.return_value.__enter__.return_value = mock_session - - conversation = ConversationServiceTestDataFactory.create_conversation_mock( - from_source=ConversationFromSource.API, from_end_user_id="user-123" - ) - mock_session.scalars.return_value.all.return_value = [conversation] - - app_model = ConversationServiceTestDataFactory.create_app_mock() - user = ConversationServiceTestDataFactory.create_end_user_mock() - - # Act - result = ConversationService.pagination_by_last_id( - session=mock_session, - app_model=app_model, - user=user, - last_id=None, - limit=20, - invoke_from=InvokeFrom.WEB_APP, - ) - - # Assert - assert isinstance(result, InfiniteScrollPagination) - - @patch("services.conversation_service.session_factory") - def test_pagination_with_account_console_source(self, mock_session_factory): - """ - Test pagination correctly handles Account with console source. - - Should use 'console' as from_source for Account instances. - """ - # Arrange - mock_session = MagicMock() - mock_session_factory.create_session.return_value.__enter__.return_value = mock_session - - conversation = ConversationServiceTestDataFactory.create_conversation_mock( - from_source=ConversationFromSource.CONSOLE, from_account_id="account-123" - ) - mock_session.scalars.return_value.all.return_value = [conversation] - - app_model = ConversationServiceTestDataFactory.create_app_mock() - user = ConversationServiceTestDataFactory.create_account_mock() - - # Act - result = ConversationService.pagination_by_last_id( - session=mock_session, - app_model=app_model, - user=user, - last_id=None, - limit=20, - invoke_from=InvokeFrom.WEB_APP, - ) - - # Assert - assert isinstance(result, InfiniteScrollPagination) - - def test_pagination_with_include_ids_filter(self): - """ - Test pagination with include_ids filter. - - Should only return conversations with IDs in include_ids list. - """ - # Arrange - mock_session = MagicMock() - mock_session.scalars.return_value.all.return_value = [] - - app_model = ConversationServiceTestDataFactory.create_app_mock() - user = ConversationServiceTestDataFactory.create_account_mock() - - # Act - result = ConversationService.pagination_by_last_id( - session=mock_session, - app_model=app_model, - user=user, - last_id=None, - limit=20, - invoke_from=InvokeFrom.WEB_APP, - include_ids=["conv-123", "conv-456"], - ) - - # Assert - assert isinstance(result, InfiniteScrollPagination) - # Verify that include_ids filter was applied - assert mock_session.scalars.called - - def test_pagination_with_empty_exclude_ids(self): - """ - Test pagination with empty exclude_ids list. - - Should handle empty exclude_ids gracefully. - """ - # Arrange - mock_session = MagicMock() - mock_session.scalars.return_value.all.return_value = [] - - app_model = ConversationServiceTestDataFactory.create_app_mock() - user = ConversationServiceTestDataFactory.create_account_mock() - - # Act - result = ConversationService.pagination_by_last_id( - session=mock_session, - app_model=app_model, - user=user, - last_id=None, - limit=20, - invoke_from=InvokeFrom.WEB_APP, - exclude_ids=[], - ) - - # Assert - assert isinstance(result, InfiniteScrollPagination) - assert result.has_more is False From 3193e8a712d442e0f3dd484f8ebd61a64df4812d Mon Sep 17 00:00:00 2001 From: Yunlu Wen Date: Thu, 16 Apr 2026 16:50:02 +0800 Subject: [PATCH 06/38] chore: reorg imports (#35308) Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> --- api/.ruff.toml | 3 + api/controllers/common/fields.py | 2 +- api/controllers/console/app/app.py | 2 +- api/controllers/console/app/audio.py | 2 +- api/controllers/console/app/completion.py | 2 +- api/controllers/console/app/generator.py | 2 +- api/controllers/console/app/message.py | 2 +- api/controllers/console/app/workflow.py | 10 +-- .../console/app/workflow_app_log.py | 2 +- .../console/app/workflow_draft_variable.py | 8 +-- api/controllers/console/app/workflow_run.py | 4 +- api/controllers/console/auth/oauth_server.py | 2 +- api/controllers/console/datasets/datasets.py | 2 +- .../console/datasets/datasets_document.py | 4 +- .../console/datasets/datasets_segments.py | 2 +- .../console/datasets/hit_testing_base.py | 2 +- .../datasets/rag_pipeline/datasource_auth.py | 4 +- .../rag_pipeline_draft_variable.py | 2 +- .../rag_pipeline/rag_pipeline_workflow.py | 2 +- api/controllers/console/explore/audio.py | 2 +- api/controllers/console/explore/completion.py | 2 +- .../console/explore/installed_app.py | 2 +- api/controllers/console/explore/message.py | 2 +- api/controllers/console/explore/trial.py | 4 +- api/controllers/console/explore/workflow.py | 4 +- api/controllers/console/remote_files.py | 2 +- api/controllers/console/workspace/account.py | 2 +- .../console/workspace/agent_providers.py | 2 +- api/controllers/console/workspace/endpoint.py | 2 +- .../workspace/load_balancing_config.py | 4 +- .../console/workspace/model_providers.py | 6 +- api/controllers/console/workspace/models.py | 6 +- api/controllers/console/workspace/plugin.py | 2 +- .../console/workspace/tool_providers.py | 2 +- .../console/workspace/trigger_providers.py | 2 +- api/controllers/inner_api/plugin/plugin.py | 2 +- api/controllers/mcp/mcp.py | 2 +- api/controllers/service_api/app/audio.py | 2 +- api/controllers/service_api/app/completion.py | 2 +- .../service_api/app/conversation.py | 2 +- api/controllers/service_api/app/workflow.py | 6 +- .../service_api/dataset/dataset.py | 2 +- .../service_api/dataset/segment.py | 2 +- .../service_api/workspace/models.py | 2 +- api/controllers/web/audio.py | 2 +- api/controllers/web/completion.py | 2 +- api/controllers/web/message.py | 2 +- api/controllers/web/remote_files.py | 2 +- api/controllers/web/workflow.py | 4 +- api/core/agent/base_agent_runner.py | 28 ++++---- api/core/agent/cot_agent_runner.py | 17 +++-- api/core/agent/cot_chat_agent_runner.py | 3 +- api/core/agent/cot_completion_agent_runner.py | 3 +- api/core/agent/fc_agent_runner.py | 15 ++--- .../agent/output_parser/cot_output_parser.py | 3 +- .../model_config/converter.py | 7 +- .../easy_ui_based_app/model_config/manager.py | 3 +- .../prompt_template/manager.py | 3 +- .../easy_ui_based_app/variables/manager.py | 3 +- api/core/app/app_config/entities.py | 8 +-- .../features/file_upload/manager.py | 3 +- .../variables/manager.py | 3 +- .../app/apps/advanced_chat/app_generator.py | 9 ++- api/core/app/apps/advanced_chat/app_runner.py | 12 ++-- .../advanced_chat/generate_task_pipeline.py | 12 ++-- api/core/app/apps/agent_chat/app_generator.py | 2 +- api/core/app/apps/agent_chat/app_runner.py | 6 +- .../base_app_generate_response_converter.py | 3 +- api/core/app/apps/base_app_generator.py | 6 +- api/core/app/apps/base_app_queue_manager.py | 2 +- api/core/app/apps/base_app_runner.py | 21 +++--- api/core/app/apps/chat/app_generator.py | 2 +- api/core/app/apps/chat/app_runner.py | 4 +- .../common/graph_runtime_state_support.py | 3 +- .../common/workflow_response_converter.py | 26 ++++---- api/core/app/apps/completion/app_generator.py | 2 +- api/core/app/apps/completion/app_runner.py | 4 +- .../app/apps/pipeline/pipeline_generator.py | 4 +- api/core/app/apps/pipeline/pipeline_runner.py | 12 ++-- api/core/app/apps/workflow/app_generator.py | 8 +-- api/core/app/apps/workflow/app_runner.py | 11 ++-- .../apps/workflow/generate_task_pipeline.py | 6 +- api/core/app/apps/workflow_app_runner.py | 66 +++++++++---------- api/core/app/entities/app_invoke_entities.py | 4 +- api/core/app/entities/queue_entities.py | 8 +-- api/core/app/entities/task_entities.py | 8 +-- .../hosting_moderation/hosting_moderation.py | 3 +- .../conversation_variable_persist_layer.py | 5 +- .../app/layers/pause_state_persist_layer.py | 4 +- api/core/app/layers/timeslice_layer.py | 2 +- api/core/app/layers/trigger_post_layer.py | 4 +- api/core/app/llm/model_access.py | 9 ++- api/core/app/llm/quota.py | 2 +- .../based_generate_task_pipeline.py | 2 +- .../easy_ui_based_generate_task_pipeline.py | 14 ++-- .../app/task_pipeline/message_file_utils.py | 3 +- api/core/app/workflow/file_runtime.py | 7 +- api/core/app/workflow/layers/llm_quota.py | 9 ++- api/core/app/workflow/layers/observability.py | 8 +-- api/core/app/workflow/layers/persistence.py | 15 ++--- .../base/tts/app_generator_tts_publisher.py | 5 +- api/core/datasource/datasource_manager.py | 6 +- api/core/datasource/entities/api_entities.py | 2 +- .../datasource/utils/message_transformer.py | 3 +- api/core/entities/execution_extra_content.py | 2 +- api/core/entities/mcp_provider.py | 2 +- api/core/entities/model_entities.py | 3 +- api/core/entities/provider_configuration.py | 20 +++--- api/core/entities/provider_entities.py | 2 +- .../helper/code_executor/code_executor.py | 2 +- api/core/helper/moderation.py | 7 +- api/core/hosting_configuration.py | 2 +- api/core/indexing_runner.py | 2 +- api/core/llm_generator/llm_generator.py | 10 +-- .../output_parser/structured_output.py | 10 +-- api/core/mcp/server/streamable_http.py | 3 +- api/core/mcp/utils.py | 2 +- api/core/memory/token_buffer_memory.py | 18 ++--- api/core/model_manager.py | 19 +++--- .../openai_moderation/openai_moderation.py | 3 +- api/core/ops/aliyun_trace/aliyun_trace.py | 4 +- api/core/ops/aliyun_trace/utils.py | 4 +- .../arize_phoenix_trace.py | 2 +- api/core/ops/langfuse_trace/langfuse_trace.py | 2 +- .../ops/langsmith_trace/langsmith_trace.py | 2 +- api/core/ops/mlflow_trace/mlflow_trace.py | 2 +- api/core/ops/opik_trace/opik_trace.py | 2 +- api/core/ops/tencent_trace/span_builder.py | 4 +- api/core/ops/tencent_trace/tencent_trace.py | 8 +-- api/core/ops/weave_trace/weave_trace.py | 2 +- api/core/plugin/backwards_invocation/model.py | 27 ++++---- api/core/plugin/backwards_invocation/node.py | 3 +- api/core/plugin/entities/marketplace.py | 2 +- api/core/plugin/entities/plugin.py | 2 +- api/core/plugin/entities/plugin_daemon.py | 4 +- api/core/plugin/entities/request.py | 8 +-- api/core/plugin/impl/base.py | 16 ++--- api/core/plugin/impl/model.py | 13 ++-- api/core/plugin/impl/model_runtime.py | 14 ++-- api/core/plugin/impl/model_runtime_factory.py | 3 +- api/core/plugin/utils/converter.py | 3 +- api/core/prompt/advanced_prompt_transform.py | 15 ++--- .../prompt/agent_history_prompt_transform.py | 13 ++-- api/core/prompt/prompt_transform.py | 5 +- api/core/prompt/simple_prompt_transform.py | 13 ++-- api/core/prompt/utils/prompt_message_util.py | 3 +- api/core/provider_manager.py | 16 ++--- .../data_post_processor.py | 5 +- api/core/rag/datasource/retrieval_service.py | 2 +- api/core/rag/datasource/vdb/vector_factory.py | 2 +- api/core/rag/docstore/dataset_docstore.py | 2 +- api/core/rag/embedding/cached_embedding.py | 4 +- .../processor/paragraph_index_processor.py | 20 +++--- api/core/rag/models/document.py | 3 +- api/core/rag/rerank/rerank_model.py | 5 +- api/core/rag/rerank/weight_rerank.py | 2 +- api/core/rag/retrieval/dataset_retrieval.py | 10 +-- .../multi_dataset_function_call_router.py | 5 +- .../router/multi_dataset_react_route.py | 7 +- api/core/rag/splitter/fixed_text_splitter.py | 3 +- .../celery_workflow_execution_repository.py | 2 +- ...lery_workflow_node_execution_repository.py | 2 +- api/core/repositories/factory.py | 2 +- .../repositories/human_input_repository.py | 4 +- ...qlalchemy_workflow_execution_repository.py | 6 +- ...hemy_workflow_node_execution_repository.py | 8 +-- .../builtin_tool/providers/audio/tools/asr.py | 7 +- .../builtin_tool/providers/audio/tools/tts.py | 3 +- api/core/tools/builtin_tool/tool.py | 5 +- api/core/tools/custom_tool/tool.py | 2 +- api/core/tools/entities/api_entities.py | 2 +- api/core/tools/mcp_tool/tool.py | 3 +- api/core/tools/tool_engine.py | 2 +- api/core/tools/tool_file_manager.py | 2 +- api/core/tools/tool_manager.py | 5 +- .../dataset_multi_retriever_tool.py | 2 +- api/core/tools/utils/message_transformer.py | 2 +- .../tools/utils/model_invocation_utils.py | 7 +- .../utils/workflow_configuration_sync.py | 5 +- api/core/tools/workflow_as_tool/provider.py | 2 +- api/core/tools/workflow_as_tool/tool.py | 4 +- api/core/trigger/debug/event_selectors.py | 2 +- api/core/workflow/human_input_compat.py | 5 +- api/core/workflow/node_factory.py | 32 ++++----- api/core/workflow/node_runtime.py | 57 ++++++++-------- api/core/workflow/nodes/agent/agent_node.py | 5 +- api/core/workflow/nodes/agent/entities.py | 4 +- .../nodes/agent/message_transformer.py | 16 ++--- .../workflow/nodes/agent/runtime_support.py | 4 +- .../nodes/datasource/datasource_node.py | 13 ++-- .../workflow/nodes/datasource/entities.py | 5 +- .../nodes/knowledge_index/entities.py | 4 +- .../knowledge_index/knowledge_index_node.py | 11 ++-- .../nodes/knowledge_retrieval/entities.py | 6 +- .../knowledge_retrieval_node.py | 11 ++-- .../nodes/knowledge_retrieval/retrieval.py | 4 +- .../workflow/nodes/trigger_plugin/entities.py | 4 +- .../trigger_plugin/trigger_event_node.py | 5 +- .../nodes/trigger_schedule/entities.py | 4 +- .../trigger_schedule/trigger_schedule_node.py | 7 +- .../nodes/trigger_webhook/entities.py | 6 +- .../workflow/nodes/trigger_webhook/node.py | 9 ++- api/core/workflow/template_rendering.py | 3 +- api/core/workflow/workflow_entry.py | 27 ++++---- api/enterprise/telemetry/draft_trace.py | 3 +- ...rameters_cache_when_sync_draft_workflow.py | 5 +- ...oin_when_app_published_workflow_updated.py | 2 +- api/extensions/ext_sentry.py | 3 +- ..._api_workflow_node_execution_repository.py | 2 +- .../logstore_api_workflow_run_repository.py | 2 +- .../logstore_workflow_execution_repository.py | 4 +- ...tore_workflow_node_execution_repository.py | 8 +-- api/extensions/otel/parser/base.py | 10 +-- api/extensions/otel/parser/llm.py | 4 +- api/extensions/otel/parser/retrieval.py | 6 +- api/extensions/otel/parser/tool.py | 8 +-- api/factories/file_factory/builders.py | 2 +- api/factories/file_factory/message_files.py | 3 +- api/factories/file_factory/storage_keys.py | 2 +- api/factories/variable_factory.py | 11 ++-- api/fields/conversation_fields.py | 2 +- api/fields/conversation_variable_fields.py | 2 +- api/fields/member_fields.py | 2 +- api/fields/message_fields.py | 2 +- api/fields/raws.py | 1 + api/fields/workflow_fields.py | 2 +- api/libs/helper.py | 4 +- api/models/human_input.py | 2 +- api/models/model.py | 6 +- api/models/provider.py | 2 +- api/models/utils/file_input_compat.py | 3 +- api/models/workflow.py | 29 ++++---- .../api_workflow_run_repository.py | 4 +- ..._api_workflow_node_execution_repository.py | 2 +- .../sqlalchemy_api_workflow_run_repository.py | 6 +- ...hemy_execution_extra_content_repository.py | 6 +- api/services/app_dsl_service.py | 12 ++-- api/services/app_service.py | 4 +- api/services/app_task_service.py | 3 +- api/services/audio_service.py | 2 +- .../clear_free_plan_tenant_expired_logs.py | 2 +- api/services/conversation_service.py | 2 +- api/services/conversation_variable_updater.py | 2 +- api/services/dataset_service.py | 6 +- api/services/datasource_provider_service.py | 2 +- .../entities/model_provider_entities.py | 18 ++--- api/services/external_knowledge_service.py | 2 +- api/services/file_service.py | 2 +- api/services/hit_testing_service.py | 3 +- .../human_input_delivery_test_service.py | 2 +- api/services/human_input_service.py | 12 ++-- api/services/message_service.py | 2 +- api/services/model_load_balancing_service.py | 12 ++-- api/services/model_provider_service.py | 3 +- api/services/rag_pipeline/rag_pipeline.py | 18 ++--- .../rag_pipeline/rag_pipeline_dsl_service.py | 12 ++-- .../archive_paid_plan_workflow_run.py | 2 +- api/services/summary_index_service.py | 4 +- .../tools/api_tools_manage_service.py | 2 +- .../tools/workflow_tools_manage_service.py | 2 +- api/services/trigger/schedule_service.py | 2 +- api/services/trigger/trigger_service.py | 2 +- api/services/trigger/webhook_service.py | 6 +- api/services/variable_truncator.py | 3 +- api/services/vector_service.py | 2 +- api/services/workflow/workflow_converter.py | 10 +-- api/services/workflow_app_service.py | 2 +- .../workflow_draft_variable_service.py | 26 ++++---- .../workflow_event_snapshot_service.py | 8 +-- api/services/workflow_service.py | 50 +++++++------- .../app_generate/workflow_execute_task.py | 2 +- api/tasks/async_workflow_tasks.py | 2 +- .../batch_create_segment_to_index_task.py | 2 +- api/tasks/human_input_timeout_tasks.py | 4 +- api/tasks/mail_human_input_delivery_task.py | 2 +- api/tasks/trigger_processing_tasks.py | 2 +- api/tasks/workflow_execution_tasks.py | 4 +- api/tasks/workflow_node_execution_tasks.py | 6 +- .../test_datasource_manager_integration.py | 3 +- .../test_datasource_node_integration.py | 5 +- .../model_runtime/__mock/plugin_model.py | 6 +- .../test_workflow_draft_variable_service.py | 8 +-- .../test_remove_app_and_related_data_task.py | 4 +- .../workflow/nodes/__mock/model.py | 3 +- .../workflow/nodes/test_code.py | 10 +-- .../workflow/nodes/test_http.py | 13 ++-- .../workflow/nodes/test_llm.py | 11 ++-- .../nodes/test_parameter_extractor.py | 11 ++-- .../workflow/nodes/test_template_transform.py | 7 +- .../workflow/nodes/test_tool.py | 13 ++-- ...test_chat_conversation_status_count_api.py | 2 +- .../app/test_workflow_draft_variable.py | 2 +- .../layers/test_pause_state_persist_layer.py | 14 ++-- .../test_human_input_form_repository_impl.py | 2 +- .../test_human_input_resume_node_execution.py | 22 +++---- .../factories/test_storage_key_loader.py | 2 +- .../helpers/execution_extra_content.py | 1 - .../test_conversation_message_inputs.py | 2 +- .../models/test_conversation_status_count.py | 2 +- .../models/test_types_enum_text.py | 2 +- ..._api_workflow_node_execution_repository.py | 2 +- ..._sqlalchemy_api_workflow_run_repository.py | 8 +-- ...hemy_execution_extra_content_repository.py | 4 +- ...hemy_workflow_node_execution_repository.py | 10 +-- .../test_workflow_run_repository.py | 4 +- .../services/test_agent_service.py | 1 - .../services/test_app_dsl_service.py | 2 +- .../test_conversation_service_variables.py | 2 +- .../test_conversation_variable_updater.py | 2 +- .../services/test_dataset_service.py | 2 +- .../test_dataset_service_update_dataset.py | 2 +- .../test_delete_archived_workflow_run.py | 2 +- .../test_human_input_delivery_test.py | 4 +- .../test_human_input_delivery_test_service.py | 2 +- .../services/test_messages_clean_service.py | 2 +- .../services/test_model_provider_service.py | 8 +-- .../services/test_workflow_app_service.py | 2 +- .../test_workflow_draft_variable_service.py | 2 +- .../workflow/test_workflow_converter.py | 6 +- ...kflow_node_execution_service_repository.py | 2 +- .../test_mail_human_input_delivery_task.py | 6 +- .../test_remove_app_and_related_data_task.py | 4 +- .../test_workflow_pause_integration.py | 4 +- .../trigger/test_trigger_e2e.py | 2 +- .../controllers/console/app/test_audio.py | 2 +- .../app/test_conversation_variables_api.py | 2 +- .../controllers/console/app/test_workflow.py | 2 +- .../console/app/test_workflow_app_log_api.py | 3 +- .../app/test_workflow_pause_details_api.py | 8 +-- .../app/workflow_draft_variables_test.py | 2 +- .../rag_pipeline/test_datasource_auth.py | 2 +- .../test_rag_pipeline_draft_variable.py | 2 +- .../console/datasets/test_hit_testing_base.py | 2 +- .../controllers/console/explore/test_audio.py | 2 +- .../console/explore/test_message.py | 2 +- .../controllers/console/explore/test_trial.py | 2 +- .../workspace/test_load_balancing_config.py | 3 +- .../console/workspace/test_model_providers.py | 2 +- .../console/workspace/test_models.py | 4 +- .../controllers/service_api/app/test_audio.py | 2 +- .../service_api/app/test_completion.py | 2 +- .../service_api/app/test_conversation.py | 2 +- .../service_api/app/test_workflow.py | 2 +- .../service_api/app/test_workflow_fields.py | 3 +- .../unit_tests/controllers/web/test_audio.py | 2 +- .../controllers/web/test_completion.py | 2 +- .../core/agent/test_cot_agent_runner.py | 2 +- .../core/agent/test_cot_chat_agent_runner.py | 2 +- .../agent/test_cot_completion_agent_runner.py | 4 +- .../core/agent/test_fc_agent_runner.py | 10 +-- .../test_model_config_converter.py | 4 +- .../test_variables_manager.py | 2 +- .../features/file_upload/test_manager.py | 3 +- .../core/app/app_config/test_entities.py | 2 +- .../test_app_runner_conversation_variables.py | 2 +- .../test_generate_response_converter.py | 3 +- .../test_generate_task_pipeline.py | 4 +- .../test_generate_task_pipeline_core.py | 4 +- .../test_agent_chat_app_generator.py | 2 +- .../agent_chat/test_agent_chat_app_runner.py | 4 +- .../chat/test_app_generator_and_runner.py | 2 +- .../chat/test_base_app_runner_multimodal.py | 4 +- .../test_graph_runtime_state_support.py | 2 +- .../test_workflow_response_converter.py | 3 +- ...workflow_response_converter_human_input.py | 5 +- ..._workflow_response_converter_resumption.py | 5 +- ..._workflow_response_converter_truncation.py | 4 +- .../app/apps/completion/test_app_runner.py | 2 +- ...est_completion_completion_app_generator.py | 2 +- ...st_pipeline_generate_response_converter.py | 3 +- .../pipeline/test_pipeline_queue_manager.py | 2 +- .../app/apps/pipeline/test_pipeline_runner.py | 2 +- .../core/app/apps/test_base_app_generator.py | 5 +- .../core/app/apps/test_base_app_runner.py | 18 ++--- .../core/app/apps/test_pause_resume.py | 11 ++-- .../app/apps/test_workflow_app_runner_core.py | 34 +++++----- .../test_workflow_app_runner_notifications.py | 4 +- .../test_workflow_app_runner_single_node.py | 4 +- .../app/apps/test_workflow_pause_events.py | 10 +-- .../test_generate_response_converter.py | 3 +- .../workflow/test_generate_task_pipeline.py | 5 +- .../test_generate_task_pipeline_core.py | 4 +- .../core/app/entities/test_task_entities.py | 3 +- ...est_conversation_variable_persist_layer.py | 7 +- .../layers/test_pause_state_persist_layer.py | 20 +++--- .../core/app/layers/test_suspend_layer.py | 3 +- .../core/app/layers/test_timeslice_layer.py | 3 +- .../app/layers/test_trigger_post_layer.py | 5 +- .../test_based_generate_task_pipeline.py | 2 +- ...st_easy_ui_based_generate_task_pipeline.py | 4 +- ...sy_ui_based_generate_task_pipeline_core.py | 6 +- .../test_easy_ui_message_end_files.py | 2 +- .../app/test_easy_ui_model_config_manager.py | 3 +- .../app/workflow/layers/test_persistence.py | 4 +- .../core/app/workflow/test_file_runtime.py | 2 +- .../core/app/workflow/test_node_factory.py | 2 +- .../test_observability_layer_extra.py | 3 +- .../app/workflow/test_persistence_layer.py | 8 +-- .../base/test_app_generator_tts_publisher.py | 6 +- .../datasource/test_datasource_manager.py | 6 +- .../utils/test_message_transformer.py | 2 +- .../test_entities_execution_extra_content.py | 5 +- .../entities/test_entities_model_entities.py | 6 +- .../test_entities_provider_configuration.py | 22 +++---- .../test_entities_provider_entities.py | 2 +- .../unit_tests/core/helper/test_moderation.py | 2 +- .../output_parser/test_structured_output.py | 28 ++++---- .../core/llm_generator/test_llm_generator.py | 4 +- .../core/mcp/server/test_streamable_http.py | 2 +- .../core/memory/test_token_buffer_memory.py | 4 +- .../test_model_provider_factory.py | 1 + .../ops/aliyun_trace/test_aliyun_trace.py | 4 +- .../aliyun_trace/test_aliyun_trace_utils.py | 4 +- .../ops/langfuse_trace/test_langfuse_trace.py | 2 +- .../langsmith_trace/test_langsmith_trace.py | 2 +- .../ops/mlflow_trace/test_mlflow_trace.py | 2 +- .../core/ops/opik_trace/test_opik_trace.py | 2 +- .../ops/tencent_trace/test_span_builder.py | 4 +- .../ops/tencent_trace/test_tencent_trace.py | 4 +- .../core/ops/test_arize_phoenix_trace.py | 2 +- .../core/ops/test_langfuse_trace.py | 3 +- .../core/ops/weave_trace/test_weave_trace.py | 2 +- .../plugin/test_backwards_invocation_model.py | 3 +- .../core/plugin/test_model_runtime_adapter.py | 6 +- .../core/plugin/test_plugin_entities.py | 12 ++-- .../core/plugin/test_plugin_runtime.py | 16 ++--- .../core/plugin/utils/test_chunk_merger.py | 2 +- .../prompt/test_advanced_prompt_transform.py | 14 ++-- .../test_agent_history_prompt_transform.py | 15 ++--- .../core/prompt/test_prompt_message.py | 5 +- .../core/prompt/test_prompt_transform.py | 2 +- .../prompt/test_simple_prompt_transform.py | 12 ++-- .../test_data_post_processor.py | 5 +- .../rag/embedding/test_cached_embedding.py | 4 +- .../rag/embedding/test_embedding_service.py | 8 +-- .../test_paragraph_index_processor.py | 6 +- .../core/rag/indexing/test_indexing_runner.py | 2 +- .../core/rag/rerank/test_reranker.py | 2 +- .../rag/retrieval/test_dataset_retrieval.py | 4 +- ...test_multi_dataset_function_call_router.py | 3 +- .../test_multi_dataset_react_route.py | 5 +- ...st_celery_workflow_execution_repository.py | 4 +- ...lery_workflow_node_execution_repository.py | 6 +- .../test_human_input_form_repository_impl.py | 10 +-- .../test_human_input_repository.py | 4 +- ...qlalchemy_workflow_execution_repository.py | 4 +- ...hemy_workflow_node_execution_repository.py | 12 ++-- ...rkflow_node_execution_conflict_handling.py | 10 +-- ...test_workflow_node_execution_truncation.py | 10 +-- api/tests/unit_tests/core/test_file.py | 1 - .../unit_tests/core/test_model_manager.py | 2 +- .../core/test_provider_configuration.py | 18 ++--- .../unit_tests/core/test_provider_manager.py | 4 +- .../core/tools/test_builtin_tool_base.py | 2 +- .../core/tools/test_builtin_tools_extra.py | 4 +- .../core/tools/test_tool_file_manager.py | 2 +- .../utils/test_model_invocation_utils.py | 4 +- .../utils/test_workflow_configuration_sync.py | 2 +- .../tools/workflow_as_tool/test_provider.py | 2 +- .../core/tools/workflow_as_tool/test_tool.py | 2 +- .../debug/test_debug_event_selectors.py | 2 +- .../unit_tests/core/variables/test_segment.py | 10 +-- .../core/variables/test_segment_type.py | 1 + .../variables/test_segment_type_validation.py | 1 + .../core/variables/test_variables.py | 3 +- .../workflow/graph_engine/layers/conftest.py | 6 +- .../graph_engine/layers/test_llm_quota.py | 9 ++- .../graph_engine/layers/test_observability.py | 2 +- .../graph_engine/test_mock_factory.py | 3 +- .../workflow/graph_engine/test_mock_nodes.py | 9 ++- .../test_parallel_human_input_join_resume.py | 15 ++--- .../graph_engine/test_table_runner.py | 11 ++-- .../nodes/agent/test_message_transformer.py | 3 +- .../nodes/agent/test_runtime_support.py | 3 +- .../core/workflow/nodes/answer/test_answer.py | 9 ++- .../workflow/nodes/base/test_base_node.py | 4 +- .../test_get_node_type_classes_mapping.py | 3 +- .../workflow/nodes/code/code_node_spec.py | 3 +- .../nodes/datasource/test_datasource_node.py | 5 +- .../test_http_request_executor.py | 8 +-- .../http_request/test_http_request_node.py | 10 +-- .../human_input/test_email_delivery_config.py | 3 +- .../nodes/human_input/test_entities.py | 36 +++++----- .../test_human_input_form_filled_event.py | 7 +- .../test_iteration_child_engine_errors.py | 4 +- .../test_knowledge_index_node.py | 6 +- .../test_knowledge_retrieval_node.py | 8 +-- .../workflow/nodes/list_operator/node_spec.py | 4 +- .../core/workflow/nodes/llm/test_llm_utils.py | 4 +- .../core/workflow/nodes/llm/test_node.py | 26 ++++---- .../test_parameter_extractor_node.py | 4 +- .../template_transform_node_spec.py | 4 +- .../test_template_transform_node.py | 4 +- .../core/workflow/nodes/test_base_node.py | 8 +-- .../nodes/test_document_extractor_node.py | 4 +- .../core/workflow/nodes/test_if_else.py | 10 +-- .../core/workflow/nodes/test_list_operator.py | 4 +- .../nodes/test_start_node_json_object.py | 8 +-- .../workflow/nodes/tool/test_tool_node.py | 4 +- .../nodes/tool/test_tool_node_runtime.py | 10 +-- .../trigger_plugin/test_trigger_event_node.py | 9 ++- .../workflow/nodes/webhook/test_exceptions.py | 2 +- .../webhook/test_webhook_file_conversion.py | 7 +- .../nodes/webhook/test_webhook_node.py | 10 +-- .../core/workflow/test_human_input_compat.py | 2 +- .../core/workflow/test_node_factory.py | 8 +-- .../core/workflow/test_node_runtime.py | 8 +-- .../core/workflow/test_system_variable.py | 5 +- .../core/workflow/test_variable_pool.py | 18 ++--- .../core/workflow/test_workflow_entry.py | 12 ++-- .../workflow/test_workflow_entry_helpers.py | 12 ++-- .../test_workflow_entry_redis_channel.py | 5 +- .../factories/test_build_from_mapping.py | 2 +- .../factories/test_variable_factory.py | 10 +-- .../unit_tests/fields/test_file_fields.py | 2 +- .../unit_tests/libs/_human_input/support.py | 1 - .../libs/_human_input/test_form_service.py | 2 +- .../libs/_human_input/test_models.py | 2 +- .../models/test_conversation_variable.py | 3 +- api/tests/unit_tests/models/test_model.py | 2 +- api/tests/unit_tests/models/test_workflow.py | 7 +- .../unit_tests/models/test_workflow_models.py | 2 +- .../services/dataset_service_test_helpers.py | 2 +- .../services/document_service_validation.py | 2 +- .../test_rag_pipeline_dsl_service.py | 2 +- .../rag_pipeline/test_rag_pipeline_service.py | 4 +- .../test_datasource_provider_service.py | 2 +- .../services/test_human_input_service.py | 12 ++-- .../test_model_load_balancing_service.py | 6 +- .../services/test_model_provider_service.py | 4 +- ...est_model_provider_service_sanitization.py | 4 +- .../services/test_variable_truncator.py | 2 +- .../test_variable_truncator_additional.py | 2 +- .../test_webhook_service_additional.py | 2 +- .../test_workflow_run_service_pause.py | 2 +- .../services/test_workflow_service.py | 2 +- .../workflow/test_draft_var_loader_simple.py | 6 +- .../test_workflow_draft_variable_service.py | 8 +-- .../test_workflow_event_snapshot_service.py | 6 +- ...kflow_event_snapshot_service_additional.py | 4 +- .../test_workflow_human_input_delivery.py | 6 +- .../tasks/test_human_input_timeout_tasks.py | 2 +- api/tests/unit_tests/tools/test_mcp_tool.py | 2 +- .../test_structured_output_parser.py | 6 +- api/tests/workflow_test_utils.py | 5 +- 545 files changed, 1480 insertions(+), 1604 deletions(-) diff --git a/api/.ruff.toml b/api/.ruff.toml index bd9684ef65..dd78024a02 100644 --- a/api/.ruff.toml +++ b/api/.ruff.toml @@ -106,3 +106,6 @@ msg = "Use Pydantic payload/query models instead of reqparse." [lint.flake8-tidy-imports.banned-api."flask_restx.reqparse.RequestParser"] msg = "Use Pydantic payload/query models instead of reqparse." + +[lint.isort] +known-first-party = ["graphon"] \ No newline at end of file diff --git a/api/controllers/common/fields.py b/api/controllers/common/fields.py index 4fe3fc9062..8e665c1386 100644 --- a/api/controllers/common/fields.py +++ b/api/controllers/common/fields.py @@ -2,9 +2,9 @@ from __future__ import annotations from typing import Any -from graphon.file import helpers as file_helpers from pydantic import BaseModel, ConfigDict, computed_field +from graphon.file import helpers as file_helpers from models.model import IconType type JSONValue = str | int | float | bool | None | dict[str, Any] | list[Any] diff --git a/api/controllers/console/app/app.py b/api/controllers/console/app/app.py index ac0682486b..051d08aa36 100644 --- a/api/controllers/console/app/app.py +++ b/api/controllers/console/app/app.py @@ -5,7 +5,6 @@ from typing import Any, Literal from flask import request from flask_restx import Resource -from graphon.enums import WorkflowExecutionStatus from pydantic import AliasChoices, BaseModel, Field, computed_field, field_validator from sqlalchemy import select from sqlalchemy.orm import Session @@ -30,6 +29,7 @@ from core.rag.retrieval.retrieval_methods import RetrievalMethod from core.trigger.constants import TRIGGER_NODE_TYPES from extensions.ext_database import db from fields.base import ResponseModel +from graphon.enums import WorkflowExecutionStatus from libs.helper import build_icon_url from libs.login import current_account_with_tenant, login_required from models import App, DatasetPermissionEnum, Workflow diff --git a/api/controllers/console/app/audio.py b/api/controllers/console/app/audio.py index 78ddb904e1..91fbe4a85a 100644 --- a/api/controllers/console/app/audio.py +++ b/api/controllers/console/app/audio.py @@ -2,7 +2,6 @@ import logging from flask import request from flask_restx import Resource, fields -from graphon.model_runtime.errors.invoke import InvokeError from pydantic import BaseModel, Field from werkzeug.exceptions import InternalServerError @@ -23,6 +22,7 @@ from controllers.console.app.error import ( from controllers.console.app.wraps import get_app_model from controllers.console.wraps import account_initialization_required, setup_required from core.errors.error import ModelCurrentlyNotSupportError, ProviderTokenNotInitError, QuotaExceededError +from graphon.model_runtime.errors.invoke import InvokeError from libs.login import login_required from models import App, AppMode from services.audio_service import AudioService diff --git a/api/controllers/console/app/completion.py b/api/controllers/console/app/completion.py index d83925d173..fe274e4c9a 100644 --- a/api/controllers/console/app/completion.py +++ b/api/controllers/console/app/completion.py @@ -3,7 +3,6 @@ from typing import Any, Literal from flask import request from flask_restx import Resource -from graphon.model_runtime.errors.invoke import InvokeError from pydantic import BaseModel, Field, field_validator from werkzeug.exceptions import InternalServerError, NotFound @@ -27,6 +26,7 @@ from core.errors.error import ( QuotaExceededError, ) from core.helper.trace_id_helper import get_external_trace_id +from graphon.model_runtime.errors.invoke import InvokeError from libs import helper from libs.helper import uuid_value from libs.login import current_user, login_required diff --git a/api/controllers/console/app/generator.py b/api/controllers/console/app/generator.py index 7101d5df7b..c720a5e074 100644 --- a/api/controllers/console/app/generator.py +++ b/api/controllers/console/app/generator.py @@ -1,7 +1,6 @@ from collections.abc import Sequence from flask_restx import Resource -from graphon.model_runtime.errors.invoke import InvokeError from pydantic import BaseModel, Field from controllers.console import console_ns @@ -20,6 +19,7 @@ from core.helper.code_executor.python3.python3_code_provider import Python3CodeP from core.llm_generator.entities import RuleCodeGeneratePayload, RuleGeneratePayload, RuleStructuredOutputPayload from core.llm_generator.llm_generator import LLMGenerator from extensions.ext_database import db +from graphon.model_runtime.errors.invoke import InvokeError from libs.login import current_account_with_tenant, login_required from models import App from services.workflow_service import WorkflowService diff --git a/api/controllers/console/app/message.py b/api/controllers/console/app/message.py index daeed4627c..44e19b57db 100644 --- a/api/controllers/console/app/message.py +++ b/api/controllers/console/app/message.py @@ -4,7 +4,6 @@ from typing import Literal from flask import request from flask_restx import Resource -from graphon.model_runtime.errors.invoke import InvokeError from pydantic import BaseModel, Field, field_validator from sqlalchemy import exists, func, select from werkzeug.exceptions import InternalServerError, NotFound @@ -40,6 +39,7 @@ from fields.conversation_fields import ( format_files_contained, to_timestamp, ) +from graphon.model_runtime.errors.invoke import InvokeError from libs.helper import uuid_value from libs.infinite_scroll_pagination import InfiniteScrollPagination from libs.login import current_account_with_tenant, login_required diff --git a/api/controllers/console/app/workflow.py b/api/controllers/console/app/workflow.py index cca4bbee1e..478f783eb0 100644 --- a/api/controllers/console/app/workflow.py +++ b/api/controllers/console/app/workflow.py @@ -5,11 +5,6 @@ from typing import Any from flask import abort, request from flask_restx import Resource, fields, marshal, marshal_with -from graphon.enums import NodeType -from graphon.file import File -from graphon.file import helpers as file_helpers -from graphon.graph_engine.manager import GraphEngineManager -from graphon.model_runtime.utils.encoders import jsonable_encoder from pydantic import BaseModel, Field, ValidationError, field_validator from sqlalchemy.orm import sessionmaker from werkzeug.exceptions import BadRequest, Forbidden, InternalServerError, NotFound @@ -42,6 +37,11 @@ from factories import file_factory, variable_factory from fields.member_fields import simple_account_fields from fields.online_user_fields import online_user_list_fields from fields.workflow_fields import workflow_fields, workflow_pagination_fields +from graphon.enums import NodeType +from graphon.file import File +from graphon.file import helpers as file_helpers +from graphon.graph_engine.manager import GraphEngineManager +from graphon.model_runtime.utils.encoders import jsonable_encoder from libs import helper from libs.datetime_utils import naive_utc_now from libs.helper import TimestampField, uuid_value diff --git a/api/controllers/console/app/workflow_app_log.py b/api/controllers/console/app/workflow_app_log.py index 6b402898e8..4b39590235 100644 --- a/api/controllers/console/app/workflow_app_log.py +++ b/api/controllers/console/app/workflow_app_log.py @@ -4,7 +4,6 @@ from typing import Any from dateutil.parser import isoparse from flask import request from flask_restx import Resource -from graphon.enums import WorkflowExecutionStatus from pydantic import BaseModel, Field, field_validator from sqlalchemy.orm import sessionmaker @@ -16,6 +15,7 @@ from extensions.ext_database import db from fields.base import ResponseModel from fields.end_user_fields import SimpleEndUser from fields.member_fields import SimpleAccount +from graphon.enums import WorkflowExecutionStatus from libs.login import login_required from models import App from models.model import AppMode diff --git a/api/controllers/console/app/workflow_draft_variable.py b/api/controllers/console/app/workflow_draft_variable.py index 640189b070..f6319573e0 100644 --- a/api/controllers/console/app/workflow_draft_variable.py +++ b/api/controllers/console/app/workflow_draft_variable.py @@ -5,10 +5,6 @@ from typing import Any, TypedDict from flask import Response, request from flask_restx import Resource, fields, marshal, marshal_with -from graphon.file import helpers as file_helpers -from graphon.variables.segment_group import SegmentGroup -from graphon.variables.segments import ArrayFileSegment, FileSegment, Segment -from graphon.variables.types import SegmentType from pydantic import BaseModel, Field from sqlalchemy.orm import sessionmaker @@ -25,6 +21,10 @@ from extensions.ext_database import db from factories import variable_factory from factories.file_factory import build_from_mapping, build_from_mappings from factories.variable_factory import build_segment_with_type +from graphon.file import helpers as file_helpers +from graphon.variables.segment_group import SegmentGroup +from graphon.variables.segments import ArrayFileSegment, FileSegment, Segment +from graphon.variables.types import SegmentType from libs.login import current_user, login_required from models import App, AppMode from models.workflow import WorkflowDraftVariable diff --git a/api/controllers/console/app/workflow_run.py b/api/controllers/console/app/workflow_run.py index a1a075be71..6748d95d6b 100644 --- a/api/controllers/console/app/workflow_run.py +++ b/api/controllers/console/app/workflow_run.py @@ -3,8 +3,6 @@ from typing import Literal, TypedDict, cast from flask import request from flask_restx import Resource, fields, marshal_with -from graphon.entities.pause_reason import HumanInputRequired -from graphon.enums import WorkflowExecutionStatus from pydantic import BaseModel, Field, field_validator from sqlalchemy import select from sqlalchemy.orm import sessionmaker @@ -28,6 +26,8 @@ from fields.workflow_run_fields import ( workflow_run_node_execution_list_fields, workflow_run_pagination_fields, ) +from graphon.entities.pause_reason import HumanInputRequired +from graphon.enums import WorkflowExecutionStatus from libs.archive_storage import ArchiveStorageNotConfiguredError, get_archive_storage from libs.custom_inputs import time_duration from libs.helper import uuid_value diff --git a/api/controllers/console/auth/oauth_server.py b/api/controllers/console/auth/oauth_server.py index b55cda4244..727428c8e7 100644 --- a/api/controllers/console/auth/oauth_server.py +++ b/api/controllers/console/auth/oauth_server.py @@ -5,11 +5,11 @@ from typing import Concatenate from flask import jsonify, request from flask.typing import ResponseReturnValue from flask_restx import Resource -from graphon.model_runtime.utils.encoders import jsonable_encoder from pydantic import BaseModel from werkzeug.exceptions import BadRequest, NotFound from controllers.console.wraps import account_initialization_required, setup_required +from graphon.model_runtime.utils.encoders import jsonable_encoder from libs.login import current_account_with_tenant, login_required from models import Account from models.model import OAuthProviderApp diff --git a/api/controllers/console/datasets/datasets.py b/api/controllers/console/datasets/datasets.py index b2a905366a..ea0fdef0a7 100644 --- a/api/controllers/console/datasets/datasets.py +++ b/api/controllers/console/datasets/datasets.py @@ -2,7 +2,6 @@ from typing import Any, cast from flask import request from flask_restx import Resource, fields, marshal, marshal_with -from graphon.model_runtime.entities.model_entities import ModelType from pydantic import BaseModel, Field, field_validator from sqlalchemy import func, select from werkzeug.exceptions import Forbidden, NotFound @@ -49,6 +48,7 @@ from fields.dataset_fields import ( weighted_score_fields, ) from fields.document_fields import document_status_fields +from graphon.model_runtime.entities.model_entities import ModelType from libs.login import current_account_with_tenant, login_required from models import ApiToken, Dataset, Document, DocumentSegment, UploadFile from models.dataset import DatasetPermission, DatasetPermissionEnum diff --git a/api/controllers/console/datasets/datasets_document.py b/api/controllers/console/datasets/datasets_document.py index 98d4ad9412..b61e39716f 100644 --- a/api/controllers/console/datasets/datasets_document.py +++ b/api/controllers/console/datasets/datasets_document.py @@ -8,8 +8,6 @@ from typing import Any, Literal, cast import sqlalchemy as sa from flask import request, send_file from flask_restx import Resource, fields, marshal, marshal_with -from graphon.model_runtime.entities.model_entities import ModelType -from graphon.model_runtime.errors.invoke import InvokeAuthorizationError from pydantic import BaseModel, Field from sqlalchemy import asc, desc, func, select from werkzeug.exceptions import Forbidden, NotFound @@ -39,6 +37,8 @@ from fields.document_fields import ( document_status_fields, document_with_segments_fields, ) +from graphon.model_runtime.entities.model_entities import ModelType +from graphon.model_runtime.errors.invoke import InvokeAuthorizationError from libs.datetime_utils import naive_utc_now from libs.login import current_account_with_tenant, login_required from models import DatasetProcessRule, Document, DocumentSegment, UploadFile diff --git a/api/controllers/console/datasets/datasets_segments.py b/api/controllers/console/datasets/datasets_segments.py index 354c299bef..2647bb1f5a 100644 --- a/api/controllers/console/datasets/datasets_segments.py +++ b/api/controllers/console/datasets/datasets_segments.py @@ -2,7 +2,6 @@ import uuid from flask import request from flask_restx import Resource, marshal -from graphon.model_runtime.entities.model_entities import ModelType from pydantic import BaseModel, Field from sqlalchemy import String, cast, func, or_, select from sqlalchemy.dialects.postgresql import JSONB @@ -32,6 +31,7 @@ from core.rag.index_processor.constant.index_type import IndexTechniqueType from extensions.ext_database import db from extensions.ext_redis import redis_client from fields.segment_fields import child_chunk_fields, segment_fields +from graphon.model_runtime.entities.model_entities import ModelType from libs.helper import escape_like_pattern from libs.login import current_account_with_tenant, login_required from models.dataset import ChildChunk, DocumentSegment diff --git a/api/controllers/console/datasets/hit_testing_base.py b/api/controllers/console/datasets/hit_testing_base.py index 8fb3699849..699fa599c8 100644 --- a/api/controllers/console/datasets/hit_testing_base.py +++ b/api/controllers/console/datasets/hit_testing_base.py @@ -2,7 +2,6 @@ import logging from typing import Any from flask_restx import marshal -from graphon.model_runtime.errors.invoke import InvokeError from pydantic import BaseModel, Field from werkzeug.exceptions import Forbidden, InternalServerError, NotFound @@ -21,6 +20,7 @@ from core.errors.error import ( QuotaExceededError, ) from fields.hit_testing_fields import hit_testing_record_fields +from graphon.model_runtime.errors.invoke import InvokeError from libs.login import current_user from models.account import Account from services.dataset_service import DatasetService diff --git a/api/controllers/console/datasets/rag_pipeline/datasource_auth.py b/api/controllers/console/datasets/rag_pipeline/datasource_auth.py index bdf83b991e..fd0a8b33bc 100644 --- a/api/controllers/console/datasets/rag_pipeline/datasource_auth.py +++ b/api/controllers/console/datasets/rag_pipeline/datasource_auth.py @@ -2,8 +2,6 @@ from typing import Any from flask import make_response, redirect, request from flask_restx import Resource -from graphon.model_runtime.errors.validate import CredentialsValidateFailedError -from graphon.model_runtime.utils.encoders import jsonable_encoder from pydantic import BaseModel, Field from werkzeug.exceptions import Forbidden, NotFound @@ -12,6 +10,8 @@ from controllers.common.schema import register_schema_models from controllers.console import console_ns from controllers.console.wraps import account_initialization_required, edit_permission_required, setup_required from core.plugin.impl.oauth import OAuthHandler +from graphon.model_runtime.errors.validate import CredentialsValidateFailedError +from graphon.model_runtime.utils.encoders import jsonable_encoder from libs.login import current_account_with_tenant, login_required from models.provider_ids import DatasourceProviderID from services.datasource_provider_service import DatasourceProviderService diff --git a/api/controllers/console/datasets/rag_pipeline/rag_pipeline_draft_variable.py b/api/controllers/console/datasets/rag_pipeline/rag_pipeline_draft_variable.py index 3549f9542d..b31d73f27d 100644 --- a/api/controllers/console/datasets/rag_pipeline/rag_pipeline_draft_variable.py +++ b/api/controllers/console/datasets/rag_pipeline/rag_pipeline_draft_variable.py @@ -4,7 +4,6 @@ from typing import Any, NoReturn from flask import Response, request from flask_restx import Resource, marshal, marshal_with -from graphon.variables.types import SegmentType from pydantic import BaseModel, Field from sqlalchemy.orm import sessionmaker from werkzeug.exceptions import Forbidden @@ -28,6 +27,7 @@ from core.workflow.variable_prefixes import CONVERSATION_VARIABLE_NODE_ID, SYSTE from extensions.ext_database import db from factories.file_factory import build_from_mapping, build_from_mappings from factories.variable_factory import build_segment_with_type +from graphon.variables.types import SegmentType from libs.login import current_user, login_required from models import Account from models.dataset import Pipeline diff --git a/api/controllers/console/datasets/rag_pipeline/rag_pipeline_workflow.py b/api/controllers/console/datasets/rag_pipeline/rag_pipeline_workflow.py index a8077d9eb0..ee146e8287 100644 --- a/api/controllers/console/datasets/rag_pipeline/rag_pipeline_workflow.py +++ b/api/controllers/console/datasets/rag_pipeline/rag_pipeline_workflow.py @@ -4,7 +4,6 @@ from typing import Any, Literal, cast from flask import abort, request from flask_restx import Resource, marshal_with # type: ignore -from graphon.model_runtime.utils.encoders import jsonable_encoder from pydantic import BaseModel, Field, ValidationError from sqlalchemy.orm import sessionmaker from werkzeug.exceptions import BadRequest, Forbidden, InternalServerError, NotFound @@ -41,6 +40,7 @@ from core.app.apps.pipeline.pipeline_generator import PipelineGenerator from core.app.entities.app_invoke_entities import InvokeFrom from extensions.ext_database import db from factories import variable_factory +from graphon.model_runtime.utils.encoders import jsonable_encoder from libs import helper from libs.helper import TimestampField, UUIDStrOrEmpty from libs.login import current_account_with_tenant, current_user, login_required diff --git a/api/controllers/console/explore/audio.py b/api/controllers/console/explore/audio.py index a37077af42..ab660d9dc3 100644 --- a/api/controllers/console/explore/audio.py +++ b/api/controllers/console/explore/audio.py @@ -1,7 +1,6 @@ import logging from flask import request -from graphon.model_runtime.errors.invoke import InvokeError from werkzeug.exceptions import InternalServerError import services @@ -20,6 +19,7 @@ from controllers.console.app.error import ( ) from controllers.console.explore.wraps import InstalledAppResource from core.errors.error import ModelCurrentlyNotSupportError, ProviderTokenNotInitError, QuotaExceededError +from graphon.model_runtime.errors.invoke import InvokeError from services.audio_service import AudioService from services.errors.audio import ( AudioTooLargeServiceError, diff --git a/api/controllers/console/explore/completion.py b/api/controllers/console/explore/completion.py index eacd7332fe..ccdccceaa6 100644 --- a/api/controllers/console/explore/completion.py +++ b/api/controllers/console/explore/completion.py @@ -2,7 +2,6 @@ import logging from typing import Any, Literal from uuid import UUID -from graphon.model_runtime.errors.invoke import InvokeError from pydantic import BaseModel, Field, field_validator from werkzeug.exceptions import InternalServerError, NotFound @@ -26,6 +25,7 @@ from core.errors.error import ( QuotaExceededError, ) from extensions.ext_database import db +from graphon.model_runtime.errors.invoke import InvokeError from libs import helper from libs.datetime_utils import naive_utc_now from libs.login import current_user diff --git a/api/controllers/console/explore/installed_app.py b/api/controllers/console/explore/installed_app.py index 7dbb7220f4..2d9a997fbf 100644 --- a/api/controllers/console/explore/installed_app.py +++ b/api/controllers/console/explore/installed_app.py @@ -4,7 +4,6 @@ from typing import Any from flask import request from flask_restx import Resource -from graphon.file import helpers as file_helpers from pydantic import BaseModel, Field, computed_field, field_validator from sqlalchemy import and_, select from werkzeug.exceptions import BadRequest, Forbidden, NotFound @@ -15,6 +14,7 @@ from controllers.console.explore.wraps import InstalledAppResource from controllers.console.wraps import account_initialization_required, cloud_edition_billing_resource_check from extensions.ext_database import db from fields.base import ResponseModel +from graphon.file import helpers as file_helpers from libs.datetime_utils import naive_utc_now from libs.login import current_account_with_tenant, login_required from models import App, InstalledApp, RecommendedApp diff --git a/api/controllers/console/explore/message.py b/api/controllers/console/explore/message.py index 64d55d7ca3..209667d1d0 100644 --- a/api/controllers/console/explore/message.py +++ b/api/controllers/console/explore/message.py @@ -2,7 +2,6 @@ import logging from typing import Literal from flask import request -from graphon.model_runtime.errors.invoke import InvokeError from pydantic import BaseModel, TypeAdapter from werkzeug.exceptions import InternalServerError, NotFound @@ -25,6 +24,7 @@ from core.app.entities.app_invoke_entities import InvokeFrom from core.errors.error import ModelCurrentlyNotSupportError, ProviderTokenNotInitError, QuotaExceededError from fields.conversation_fields import ResultResponse from fields.message_fields import MessageInfiniteScrollPagination, MessageListItem, SuggestedQuestionsResponse +from graphon.model_runtime.errors.invoke import InvokeError from libs import helper from libs.login import current_account_with_tenant from models.enums import FeedbackRating diff --git a/api/controllers/console/explore/trial.py b/api/controllers/console/explore/trial.py index 0a3595454a..1456301a24 100644 --- a/api/controllers/console/explore/trial.py +++ b/api/controllers/console/explore/trial.py @@ -3,8 +3,6 @@ from typing import Any, Literal, cast from flask import request from flask_restx import Resource, fields, marshal, marshal_with -from graphon.graph_engine.manager import GraphEngineManager -from graphon.model_runtime.errors.invoke import InvokeError from pydantic import BaseModel from sqlalchemy import select from werkzeug.exceptions import Forbidden, InternalServerError, NotFound @@ -61,6 +59,8 @@ from fields.workflow_fields import ( workflow_fields, workflow_partial_fields, ) +from graphon.graph_engine.manager import GraphEngineManager +from graphon.model_runtime.errors.invoke import InvokeError from libs import helper from libs.helper import uuid_value from libs.login import current_user diff --git a/api/controllers/console/explore/workflow.py b/api/controllers/console/explore/workflow.py index da88de6776..438cce4fd8 100644 --- a/api/controllers/console/explore/workflow.py +++ b/api/controllers/console/explore/workflow.py @@ -1,7 +1,5 @@ import logging -from graphon.graph_engine.manager import GraphEngineManager -from graphon.model_runtime.errors.invoke import InvokeError from werkzeug.exceptions import InternalServerError from controllers.common.controller_schemas import WorkflowRunPayload @@ -23,6 +21,8 @@ from core.errors.error import ( QuotaExceededError, ) from extensions.ext_redis import redis_client +from graphon.graph_engine.manager import GraphEngineManager +from graphon.model_runtime.errors.invoke import InvokeError from libs import helper from libs.login import current_account_with_tenant from models.model import AppMode, InstalledApp diff --git a/api/controllers/console/remote_files.py b/api/controllers/console/remote_files.py index 551c86fd82..2a46d2250a 100644 --- a/api/controllers/console/remote_files.py +++ b/api/controllers/console/remote_files.py @@ -2,7 +2,6 @@ import urllib.parse import httpx from flask_restx import Resource -from graphon.file import helpers as file_helpers from pydantic import BaseModel, Field import services @@ -16,6 +15,7 @@ from controllers.console import console_ns from core.helper import ssrf_proxy from extensions.ext_database import db from fields.file_fields import FileWithSignedUrl, RemoteFileInfo +from graphon.file import helpers as file_helpers from libs.login import current_account_with_tenant, login_required from services.file_service import FileService diff --git a/api/controllers/console/workspace/account.py b/api/controllers/console/workspace/account.py index 9de56acc4d..44404005b2 100644 --- a/api/controllers/console/workspace/account.py +++ b/api/controllers/console/workspace/account.py @@ -6,7 +6,6 @@ from typing import Any, Literal import pytz from flask import request from flask_restx import Resource -from graphon.file import helpers as file_helpers from pydantic import BaseModel, Field, field_validator, model_validator from sqlalchemy import select @@ -40,6 +39,7 @@ from controllers.console.wraps import ( from extensions.ext_database import db from fields.base import ResponseModel from fields.member_fields import Account as AccountResponse +from graphon.file import helpers as file_helpers from libs.datetime_utils import naive_utc_now from libs.helper import EmailStr, extract_remote_ip, timezone from libs.login import current_account_with_tenant, login_required diff --git a/api/controllers/console/workspace/agent_providers.py b/api/controllers/console/workspace/agent_providers.py index 3fdcbc4710..764f488755 100644 --- a/api/controllers/console/workspace/agent_providers.py +++ b/api/controllers/console/workspace/agent_providers.py @@ -1,8 +1,8 @@ from flask_restx import Resource, fields -from graphon.model_runtime.utils.encoders import jsonable_encoder from controllers.console import console_ns from controllers.console.wraps import account_initialization_required, setup_required +from graphon.model_runtime.utils.encoders import jsonable_encoder from libs.login import current_account_with_tenant, login_required from services.agent_service import AgentService diff --git a/api/controllers/console/workspace/endpoint.py b/api/controllers/console/workspace/endpoint.py index b6b9deb1f9..f45b72f390 100644 --- a/api/controllers/console/workspace/endpoint.py +++ b/api/controllers/console/workspace/endpoint.py @@ -2,13 +2,13 @@ from typing import Any from flask import request from flask_restx import Resource -from graphon.model_runtime.utils.encoders import jsonable_encoder from pydantic import BaseModel, Field from controllers.common.schema import register_schema_models from controllers.console import console_ns from controllers.console.wraps import account_initialization_required, is_admin_or_owner_required, setup_required from core.plugin.impl.exc import PluginPermissionDeniedError +from graphon.model_runtime.utils.encoders import jsonable_encoder from libs.login import current_account_with_tenant, login_required from services.plugin.endpoint_service import EndpointService diff --git a/api/controllers/console/workspace/load_balancing_config.py b/api/controllers/console/workspace/load_balancing_config.py index e4cfca9fa4..2a6f37aec8 100644 --- a/api/controllers/console/workspace/load_balancing_config.py +++ b/api/controllers/console/workspace/load_balancing_config.py @@ -1,12 +1,12 @@ from flask_restx import Resource -from graphon.model_runtime.entities.model_entities import ModelType -from graphon.model_runtime.errors.validate import CredentialsValidateFailedError from pydantic import BaseModel from werkzeug.exceptions import Forbidden from controllers.common.schema import register_schema_models from controllers.console import console_ns from controllers.console.wraps import account_initialization_required, setup_required +from graphon.model_runtime.entities.model_entities import ModelType +from graphon.model_runtime.errors.validate import CredentialsValidateFailedError from libs.login import current_account_with_tenant, login_required from models import TenantAccountRole from services.model_load_balancing_service import ModelLoadBalancingService diff --git a/api/controllers/console/workspace/model_providers.py b/api/controllers/console/workspace/model_providers.py index cbb9677309..4b10561fdb 100644 --- a/api/controllers/console/workspace/model_providers.py +++ b/api/controllers/console/workspace/model_providers.py @@ -3,13 +3,13 @@ from typing import Any, Literal from flask import request, send_file from flask_restx import Resource -from graphon.model_runtime.entities.model_entities import ModelType -from graphon.model_runtime.errors.validate import CredentialsValidateFailedError -from graphon.model_runtime.utils.encoders import jsonable_encoder from pydantic import BaseModel, Field, field_validator from controllers.console import console_ns from controllers.console.wraps import account_initialization_required, is_admin_or_owner_required, setup_required +from graphon.model_runtime.entities.model_entities import ModelType +from graphon.model_runtime.errors.validate import CredentialsValidateFailedError +from graphon.model_runtime.utils.encoders import jsonable_encoder from libs.helper import uuid_value from libs.login import current_account_with_tenant, login_required from services.billing_service import BillingService diff --git a/api/controllers/console/workspace/models.py b/api/controllers/console/workspace/models.py index f8f95304f0..b2d07ff8f9 100644 --- a/api/controllers/console/workspace/models.py +++ b/api/controllers/console/workspace/models.py @@ -3,14 +3,14 @@ from typing import Any, cast from flask import request from flask_restx import Resource -from graphon.model_runtime.entities.model_entities import ModelType -from graphon.model_runtime.errors.validate import CredentialsValidateFailedError -from graphon.model_runtime.utils.encoders import jsonable_encoder from pydantic import BaseModel, Field, field_validator from controllers.common.schema import register_enum_models, register_schema_models from controllers.console import console_ns from controllers.console.wraps import account_initialization_required, is_admin_or_owner_required, setup_required +from graphon.model_runtime.entities.model_entities import ModelType +from graphon.model_runtime.errors.validate import CredentialsValidateFailedError +from graphon.model_runtime.utils.encoders import jsonable_encoder from libs.helper import uuid_value from libs.login import current_account_with_tenant, login_required from services.model_load_balancing_service import ModelLoadBalancingService diff --git a/api/controllers/console/workspace/plugin.py b/api/controllers/console/workspace/plugin.py index aa674a63b3..b3e344ccea 100644 --- a/api/controllers/console/workspace/plugin.py +++ b/api/controllers/console/workspace/plugin.py @@ -4,7 +4,6 @@ from typing import Any, Literal from flask import request, send_file from flask_restx import Resource -from graphon.model_runtime.utils.encoders import jsonable_encoder from pydantic import BaseModel, Field from werkzeug.datastructures import FileStorage from werkzeug.exceptions import Forbidden @@ -15,6 +14,7 @@ from controllers.console import console_ns from controllers.console.workspace import plugin_permission_required from controllers.console.wraps import account_initialization_required, is_admin_or_owner_required, setup_required from core.plugin.impl.exc import PluginDaemonClientSideError +from graphon.model_runtime.utils.encoders import jsonable_encoder from libs.login import current_account_with_tenant, login_required from models.account import TenantPluginAutoUpgradeStrategy, TenantPluginPermission from services.plugin.plugin_auto_upgrade_service import PluginAutoUpgradeService diff --git a/api/controllers/console/workspace/tool_providers.py b/api/controllers/console/workspace/tool_providers.py index c9956501e2..471594f349 100644 --- a/api/controllers/console/workspace/tool_providers.py +++ b/api/controllers/console/workspace/tool_providers.py @@ -5,7 +5,6 @@ from urllib.parse import urlparse from flask import make_response, redirect, request, send_file from flask_restx import Resource -from graphon.model_runtime.utils.encoders import jsonable_encoder from pydantic import BaseModel, Field, HttpUrl, field_validator, model_validator from sqlalchemy.orm import sessionmaker from werkzeug.exceptions import Forbidden @@ -28,6 +27,7 @@ from core.plugin.entities.plugin_daemon import CredentialType from core.plugin.impl.oauth import OAuthHandler from core.tools.entities.tool_entities import ApiProviderSchemaType, WorkflowToolParameterConfiguration from extensions.ext_database import db +from graphon.model_runtime.utils.encoders import jsonable_encoder from libs.helper import alphanumeric, uuid_value from libs.login import current_account_with_tenant, login_required from models.provider_ids import ToolProviderID diff --git a/api/controllers/console/workspace/trigger_providers.py b/api/controllers/console/workspace/trigger_providers.py index 7a28a09861..d11b66244f 100644 --- a/api/controllers/console/workspace/trigger_providers.py +++ b/api/controllers/console/workspace/trigger_providers.py @@ -3,7 +3,6 @@ from typing import Any from flask import make_response, redirect, request from flask_restx import Resource -from graphon.model_runtime.utils.encoders import jsonable_encoder from pydantic import BaseModel, model_validator from sqlalchemy.orm import sessionmaker from werkzeug.exceptions import BadRequest, Forbidden @@ -16,6 +15,7 @@ from core.plugin.impl.oauth import OAuthHandler from core.trigger.entities.entities import SubscriptionBuilderUpdater from core.trigger.trigger_manager import TriggerManager from extensions.ext_database import db +from graphon.model_runtime.utils.encoders import jsonable_encoder from libs.login import current_user, login_required from models.account import Account from models.provider_ids import TriggerProviderID diff --git a/api/controllers/inner_api/plugin/plugin.py b/api/controllers/inner_api/plugin/plugin.py index 83c8fa02fe..72cab3de73 100644 --- a/api/controllers/inner_api/plugin/plugin.py +++ b/api/controllers/inner_api/plugin/plugin.py @@ -1,5 +1,4 @@ from flask_restx import Resource -from graphon.model_runtime.utils.encoders import jsonable_encoder from controllers.console.wraps import setup_required from controllers.inner_api import inner_api_ns @@ -30,6 +29,7 @@ from core.plugin.entities.request import ( ) from core.tools.entities.tool_entities import ToolProviderType from core.tools.signature import get_signed_file_url_for_plugin +from graphon.model_runtime.utils.encoders import jsonable_encoder from libs.helper import length_prefixed_response from models import Account, Tenant from models.model import EndUser diff --git a/api/controllers/mcp/mcp.py b/api/controllers/mcp/mcp.py index 8066f198bb..f652bbc581 100644 --- a/api/controllers/mcp/mcp.py +++ b/api/controllers/mcp/mcp.py @@ -2,7 +2,6 @@ from typing import Any, Union from flask import Response from flask_restx import Resource -from graphon.variables.input_entities import VariableEntity, VariableEntityType from pydantic import BaseModel, Field, ValidationError from sqlalchemy import select from sqlalchemy.orm import Session, sessionmaker @@ -12,6 +11,7 @@ from controllers.mcp import mcp_ns from core.mcp import types as mcp_types from core.mcp.server.streamable_http import handle_mcp_request from extensions.ext_database import db +from graphon.variables.input_entities import VariableEntity, VariableEntityType from libs import helper from models.enums import AppMCPServerStatus from models.model import App, AppMCPServer, AppMode, EndUser diff --git a/api/controllers/service_api/app/audio.py b/api/controllers/service_api/app/audio.py index 907dd1b06d..e818573b8f 100644 --- a/api/controllers/service_api/app/audio.py +++ b/api/controllers/service_api/app/audio.py @@ -2,7 +2,6 @@ import logging from flask import request from flask_restx import Resource -from graphon.model_runtime.errors.invoke import InvokeError from werkzeug.exceptions import InternalServerError import services @@ -22,6 +21,7 @@ from controllers.service_api.app.error import ( ) from controllers.service_api.wraps import FetchUserArg, WhereisUserArg, validate_app_token from core.errors.error import ModelCurrentlyNotSupportError, ProviderTokenNotInitError, QuotaExceededError +from graphon.model_runtime.errors.invoke import InvokeError from models.model import App, EndUser from services.audio_service import AudioService from services.errors.audio import ( diff --git a/api/controllers/service_api/app/completion.py b/api/controllers/service_api/app/completion.py index 3142e5118e..31f2797d66 100644 --- a/api/controllers/service_api/app/completion.py +++ b/api/controllers/service_api/app/completion.py @@ -4,7 +4,6 @@ from uuid import UUID from flask import request from flask_restx import Resource -from graphon.model_runtime.errors.invoke import InvokeError from pydantic import BaseModel, Field, field_validator from werkzeug.exceptions import BadRequest, InternalServerError, NotFound @@ -29,6 +28,7 @@ from core.errors.error import ( QuotaExceededError, ) from core.helper.trace_id_helper import get_external_trace_id +from graphon.model_runtime.errors.invoke import InvokeError from libs import helper from libs.helper import UUIDStrOrEmpty from models.model import App, AppMode, EndUser diff --git a/api/controllers/service_api/app/conversation.py b/api/controllers/service_api/app/conversation.py index 50851aea08..c4353ca7b8 100644 --- a/api/controllers/service_api/app/conversation.py +++ b/api/controllers/service_api/app/conversation.py @@ -3,7 +3,6 @@ from typing import Any, Literal from flask import request from flask_restx import Resource -from graphon.variables.types import SegmentType from pydantic import BaseModel, Field, TypeAdapter, field_validator from sqlalchemy.orm import sessionmaker from werkzeug.exceptions import BadRequest, NotFound @@ -22,6 +21,7 @@ from fields.conversation_fields import ( ConversationInfiniteScrollPagination, SimpleConversation, ) +from graphon.variables.types import SegmentType from libs.helper import UUIDStrOrEmpty from models.model import App, AppMode, EndUser from services.conversation_service import ConversationService diff --git a/api/controllers/service_api/app/workflow.py b/api/controllers/service_api/app/workflow.py index d5544ff473..cc763fa89c 100644 --- a/api/controllers/service_api/app/workflow.py +++ b/api/controllers/service_api/app/workflow.py @@ -6,9 +6,6 @@ from typing import Literal from dateutil.parser import isoparse from flask import request from flask_restx import Resource, fields -from graphon.enums import WorkflowExecutionStatus -from graphon.graph_engine.manager import GraphEngineManager -from graphon.model_runtime.errors.invoke import InvokeError from pydantic import BaseModel, Field, field_validator from sqlalchemy.orm import sessionmaker from werkzeug.exceptions import BadRequest, InternalServerError, NotFound @@ -38,6 +35,9 @@ from extensions.ext_redis import redis_client from fields.base import ResponseModel from fields.end_user_fields import SimpleEndUser from fields.member_fields import SimpleAccount +from graphon.enums import WorkflowExecutionStatus +from graphon.graph_engine.manager import GraphEngineManager +from graphon.model_runtime.errors.invoke import InvokeError from libs import helper from models.model import App, AppMode, EndUser from models.workflow import WorkflowRun diff --git a/api/controllers/service_api/dataset/dataset.py b/api/controllers/service_api/dataset/dataset.py index fd954be6b1..76519cad0a 100644 --- a/api/controllers/service_api/dataset/dataset.py +++ b/api/controllers/service_api/dataset/dataset.py @@ -2,7 +2,6 @@ from typing import Any, Literal, cast from flask import request from flask_restx import marshal -from graphon.model_runtime.entities.model_entities import ModelType from pydantic import BaseModel, Field, TypeAdapter, field_validator from werkzeug.exceptions import Forbidden, NotFound @@ -19,6 +18,7 @@ from core.plugin.impl.model_runtime_factory import create_plugin_provider_manage from core.rag.index_processor.constant.index_type import IndexTechniqueType from fields.dataset_fields import dataset_detail_fields from fields.tag_fields import DataSetTag +from graphon.model_runtime.entities.model_entities import ModelType from libs.login import current_user from models.account import Account from models.dataset import DatasetPermissionEnum diff --git a/api/controllers/service_api/dataset/segment.py b/api/controllers/service_api/dataset/segment.py index 971b63577c..5992fa7410 100644 --- a/api/controllers/service_api/dataset/segment.py +++ b/api/controllers/service_api/dataset/segment.py @@ -2,7 +2,6 @@ from typing import Any from flask import request from flask_restx import marshal -from graphon.model_runtime.entities.model_entities import ModelType from pydantic import BaseModel, Field from sqlalchemy import select from werkzeug.exceptions import NotFound @@ -23,6 +22,7 @@ from core.model_manager import ModelManager from core.rag.index_processor.constant.index_type import IndexTechniqueType from extensions.ext_database import db from fields.segment_fields import child_chunk_fields, segment_fields +from graphon.model_runtime.entities.model_entities import ModelType from libs.login import current_account_with_tenant from models.dataset import Dataset from services.dataset_service import DatasetService, DocumentService, SegmentService diff --git a/api/controllers/service_api/workspace/models.py b/api/controllers/service_api/workspace/models.py index c0a6cb0a76..5ac65fc4e6 100644 --- a/api/controllers/service_api/workspace/models.py +++ b/api/controllers/service_api/workspace/models.py @@ -1,9 +1,9 @@ from flask_login import current_user from flask_restx import Resource -from graphon.model_runtime.utils.encoders import jsonable_encoder from controllers.service_api import service_api_ns from controllers.service_api.wraps import validate_dataset_token +from graphon.model_runtime.utils.encoders import jsonable_encoder from services.model_provider_service import ModelProviderService diff --git a/api/controllers/web/audio.py b/api/controllers/web/audio.py index 0ef4471018..3ad595f1f4 100644 --- a/api/controllers/web/audio.py +++ b/api/controllers/web/audio.py @@ -2,7 +2,6 @@ import logging from flask import request from flask_restx import fields, marshal_with -from graphon.model_runtime.errors.invoke import InvokeError from pydantic import field_validator from werkzeug.exceptions import InternalServerError @@ -22,6 +21,7 @@ from controllers.web.error import ( ) from controllers.web.wraps import WebApiResource from core.errors.error import ModelCurrentlyNotSupportError, ProviderTokenNotInitError, QuotaExceededError +from graphon.model_runtime.errors.invoke import InvokeError from libs.helper import uuid_value from models.model import App from services.audio_service import AudioService diff --git a/api/controllers/web/completion.py b/api/controllers/web/completion.py index e37f9af5f0..0528184d79 100644 --- a/api/controllers/web/completion.py +++ b/api/controllers/web/completion.py @@ -1,7 +1,6 @@ import logging from typing import Any, Literal -from graphon.model_runtime.errors.invoke import InvokeError from pydantic import BaseModel, Field, field_validator from werkzeug.exceptions import InternalServerError, NotFound @@ -26,6 +25,7 @@ from core.errors.error import ( ProviderTokenNotInitError, QuotaExceededError, ) +from graphon.model_runtime.errors.invoke import InvokeError from libs import helper from libs.helper import uuid_value from models.model import AppMode diff --git a/api/controllers/web/message.py b/api/controllers/web/message.py index 39afdd843f..07ecf8035b 100644 --- a/api/controllers/web/message.py +++ b/api/controllers/web/message.py @@ -2,7 +2,6 @@ import logging from typing import Literal from flask import request -from graphon.model_runtime.errors.invoke import InvokeError from pydantic import BaseModel, Field, TypeAdapter from werkzeug.exceptions import InternalServerError, NotFound @@ -24,6 +23,7 @@ from core.app.entities.app_invoke_entities import InvokeFrom from core.errors.error import ModelCurrentlyNotSupportError, ProviderTokenNotInitError, QuotaExceededError from fields.conversation_fields import ResultResponse from fields.message_fields import SuggestedQuestionsResponse, WebMessageInfiniteScrollPagination, WebMessageListItem +from graphon.model_runtime.errors.invoke import InvokeError from libs import helper from models.enums import FeedbackRating from models.model import AppMode diff --git a/api/controllers/web/remote_files.py b/api/controllers/web/remote_files.py index 38aeccc642..fe31e9d4ac 100644 --- a/api/controllers/web/remote_files.py +++ b/api/controllers/web/remote_files.py @@ -1,7 +1,6 @@ import urllib.parse import httpx -from graphon.file import helpers as file_helpers from pydantic import BaseModel, Field, HttpUrl import services @@ -14,6 +13,7 @@ from controllers.common.errors import ( from core.helper import ssrf_proxy from extensions.ext_database import db from fields.file_fields import FileWithSignedUrl, RemoteFileInfo +from graphon.file import helpers as file_helpers from services.file_service import FileService from ..common.schema import register_schema_models diff --git a/api/controllers/web/workflow.py b/api/controllers/web/workflow.py index 796e090976..98211193a0 100644 --- a/api/controllers/web/workflow.py +++ b/api/controllers/web/workflow.py @@ -1,7 +1,5 @@ import logging -from graphon.graph_engine.manager import GraphEngineManager -from graphon.model_runtime.errors.invoke import InvokeError from werkzeug.exceptions import InternalServerError from controllers.common.controller_schemas import WorkflowRunPayload @@ -24,6 +22,8 @@ from core.errors.error import ( QuotaExceededError, ) from extensions.ext_redis import redis_client +from graphon.graph_engine.manager import GraphEngineManager +from graphon.model_runtime.errors.invoke import InvokeError from libs import helper from models.model import App, AppMode, EndUser from services.app_generate_service import AppGenerateService diff --git a/api/core/agent/base_agent_runner.py b/api/core/agent/base_agent_runner.py index 06c746990d..790602ef5d 100644 --- a/api/core/agent/base_agent_runner.py +++ b/api/core/agent/base_agent_runner.py @@ -4,20 +4,6 @@ import uuid from decimal import Decimal from typing import Union, cast -from graphon.file import file_manager -from graphon.model_runtime.entities import ( - AssistantPromptMessage, - LLMUsage, - PromptMessage, - PromptMessageTool, - SystemPromptMessage, - TextPromptMessageContent, - ToolPromptMessage, - UserPromptMessage, -) -from graphon.model_runtime.entities.message_entities import ImagePromptMessageContent, PromptMessageContentUnionTypes -from graphon.model_runtime.entities.model_entities import ModelFeature -from graphon.model_runtime.model_providers.__base.large_language_model import LargeLanguageModel from sqlalchemy import func, select from core.agent.entities import AgentEntity, AgentToolEntity @@ -43,6 +29,20 @@ from core.tools.tool_manager import ToolManager from core.tools.utils.dataset_retriever_tool import DatasetRetrieverTool from extensions.ext_database import db from factories import file_factory +from graphon.file import file_manager +from graphon.model_runtime.entities import ( + AssistantPromptMessage, + LLMUsage, + PromptMessage, + PromptMessageTool, + SystemPromptMessage, + TextPromptMessageContent, + ToolPromptMessage, + UserPromptMessage, +) +from graphon.model_runtime.entities.message_entities import ImagePromptMessageContent, PromptMessageContentUnionTypes +from graphon.model_runtime.entities.model_entities import ModelFeature +from graphon.model_runtime.model_providers.__base.large_language_model import LargeLanguageModel from models.enums import CreatorUserRole from models.model import Conversation, Message, MessageAgentThought, MessageFile diff --git a/api/core/agent/cot_agent_runner.py b/api/core/agent/cot_agent_runner.py index f07ac64498..0bc93ad34d 100644 --- a/api/core/agent/cot_agent_runner.py +++ b/api/core/agent/cot_agent_runner.py @@ -4,15 +4,6 @@ from abc import ABC, abstractmethod from collections.abc import Generator, Mapping, Sequence from typing import Any, TypedDict -from graphon.model_runtime.entities.llm_entities import LLMResult, LLMResultChunk, LLMResultChunkDelta, LLMUsage -from graphon.model_runtime.entities.message_entities import ( - AssistantPromptMessage, - PromptMessage, - PromptMessageTool, - ToolPromptMessage, - UserPromptMessage, -) - from core.agent.base_agent_runner import BaseAgentRunner from core.agent.entities import AgentScratchpadUnit from core.agent.errors import AgentMaxIterationError @@ -24,6 +15,14 @@ from core.prompt.agent_history_prompt_transform import AgentHistoryPromptTransfo from core.tools.__base.tool import Tool from core.tools.entities.tool_entities import ToolInvokeMeta from core.tools.tool_engine import ToolEngine +from graphon.model_runtime.entities.llm_entities import LLMResult, LLMResultChunk, LLMResultChunkDelta, LLMUsage +from graphon.model_runtime.entities.message_entities import ( + AssistantPromptMessage, + PromptMessage, + PromptMessageTool, + ToolPromptMessage, + UserPromptMessage, +) from models.model import Message logger = logging.getLogger(__name__) diff --git a/api/core/agent/cot_chat_agent_runner.py b/api/core/agent/cot_chat_agent_runner.py index 2b2e26987e..a2186be100 100644 --- a/api/core/agent/cot_chat_agent_runner.py +++ b/api/core/agent/cot_chat_agent_runner.py @@ -1,5 +1,6 @@ import json +from core.agent.cot_agent_runner import CotAgentRunner from graphon.file import file_manager from graphon.model_runtime.entities import ( AssistantPromptMessage, @@ -11,8 +12,6 @@ from graphon.model_runtime.entities import ( from graphon.model_runtime.entities.message_entities import ImagePromptMessageContent, PromptMessageContentUnionTypes from graphon.model_runtime.utils.encoders import jsonable_encoder -from core.agent.cot_agent_runner import CotAgentRunner - class CotChatAgentRunner(CotAgentRunner): def _organize_system_prompt(self) -> SystemPromptMessage: diff --git a/api/core/agent/cot_completion_agent_runner.py b/api/core/agent/cot_completion_agent_runner.py index d4c52a8eb1..51a30998ae 100644 --- a/api/core/agent/cot_completion_agent_runner.py +++ b/api/core/agent/cot_completion_agent_runner.py @@ -1,5 +1,6 @@ import json +from core.agent.cot_agent_runner import CotAgentRunner from graphon.model_runtime.entities.message_entities import ( AssistantPromptMessage, PromptMessage, @@ -8,8 +9,6 @@ from graphon.model_runtime.entities.message_entities import ( ) from graphon.model_runtime.utils.encoders import jsonable_encoder -from core.agent.cot_agent_runner import CotAgentRunner - class CotCompletionAgentRunner(CotAgentRunner): def _organize_instruction_prompt(self) -> str: diff --git a/api/core/agent/fc_agent_runner.py b/api/core/agent/fc_agent_runner.py index fdffde85d0..d38d24d1e7 100644 --- a/api/core/agent/fc_agent_runner.py +++ b/api/core/agent/fc_agent_runner.py @@ -4,6 +4,13 @@ from collections.abc import Generator from copy import deepcopy from typing import Any, Union +from core.agent.base_agent_runner import BaseAgentRunner +from core.agent.errors import AgentMaxIterationError +from core.app.apps.base_app_queue_manager import PublishFrom +from core.app.entities.queue_entities import QueueAgentThoughtEvent, QueueMessageEndEvent, QueueMessageFileEvent +from core.prompt.agent_history_prompt_transform import AgentHistoryPromptTransform +from core.tools.entities.tool_entities import ToolInvokeMeta +from core.tools.tool_engine import ToolEngine from graphon.file import file_manager from graphon.model_runtime.entities import ( AssistantPromptMessage, @@ -19,14 +26,6 @@ from graphon.model_runtime.entities import ( UserPromptMessage, ) from graphon.model_runtime.entities.message_entities import ImagePromptMessageContent, PromptMessageContentUnionTypes - -from core.agent.base_agent_runner import BaseAgentRunner -from core.agent.errors import AgentMaxIterationError -from core.app.apps.base_app_queue_manager import PublishFrom -from core.app.entities.queue_entities import QueueAgentThoughtEvent, QueueMessageEndEvent, QueueMessageFileEvent -from core.prompt.agent_history_prompt_transform import AgentHistoryPromptTransform -from core.tools.entities.tool_entities import ToolInvokeMeta -from core.tools.tool_engine import ToolEngine from models.model import Message logger = logging.getLogger(__name__) diff --git a/api/core/agent/output_parser/cot_output_parser.py b/api/core/agent/output_parser/cot_output_parser.py index 8cccd2be6d..f341ca5a0b 100644 --- a/api/core/agent/output_parser/cot_output_parser.py +++ b/api/core/agent/output_parser/cot_output_parser.py @@ -3,9 +3,8 @@ import re from collections.abc import Generator from typing import Any, Union -from graphon.model_runtime.entities.llm_entities import LLMResultChunk - from core.agent.entities import AgentScratchpadUnit +from graphon.model_runtime.entities.llm_entities import LLMResultChunk class CotAgentOutputParser: diff --git a/api/core/app/app_config/easy_ui_based_app/model_config/converter.py b/api/core/app/app_config/easy_ui_based_app/model_config/converter.py index b7dd55632e..dbd7527fc6 100644 --- a/api/core/app/app_config/easy_ui_based_app/model_config/converter.py +++ b/api/core/app/app_config/easy_ui_based_app/model_config/converter.py @@ -1,14 +1,13 @@ from typing import cast -from graphon.model_runtime.entities.llm_entities import LLMMode -from graphon.model_runtime.entities.model_entities import ModelPropertyKey, ModelType -from graphon.model_runtime.model_providers.__base.large_language_model import LargeLanguageModel - from core.app.app_config.entities import EasyUIBasedAppConfig from core.app.entities.app_invoke_entities import ModelConfigWithCredentialsEntity from core.entities.model_entities import ModelStatus from core.errors.error import ModelCurrentlyNotSupportError, ProviderTokenNotInitError, QuotaExceededError from core.plugin.impl.model_runtime_factory import create_plugin_provider_manager +from graphon.model_runtime.entities.llm_entities import LLMMode +from graphon.model_runtime.entities.model_entities import ModelPropertyKey, ModelType +from graphon.model_runtime.model_providers.__base.large_language_model import LargeLanguageModel class ModelConfigConverter: diff --git a/api/core/app/app_config/easy_ui_based_app/model_config/manager.py b/api/core/app/app_config/easy_ui_based_app/model_config/manager.py index 9d980e5ca3..02498c23e1 100644 --- a/api/core/app/app_config/easy_ui_based_app/model_config/manager.py +++ b/api/core/app/app_config/easy_ui_based_app/model_config/manager.py @@ -1,10 +1,9 @@ from collections.abc import Mapping from typing import Any -from graphon.model_runtime.entities.model_entities import ModelPropertyKey, ModelType - from core.app.app_config.entities import ModelConfigEntity from core.plugin.impl.model_runtime_factory import create_plugin_model_assembly +from graphon.model_runtime.entities.model_entities import ModelPropertyKey, ModelType from models.model import AppModelConfigDict from models.provider_ids import ModelProviderID diff --git a/api/core/app/app_config/easy_ui_based_app/prompt_template/manager.py b/api/core/app/app_config/easy_ui_based_app/prompt_template/manager.py index 57c6d1c496..4c07445df3 100644 --- a/api/core/app/app_config/easy_ui_based_app/prompt_template/manager.py +++ b/api/core/app/app_config/easy_ui_based_app/prompt_template/manager.py @@ -1,7 +1,5 @@ from typing import Any -from graphon.model_runtime.entities.message_entities import PromptMessageRole - from core.app.app_config.entities import ( AdvancedChatMessageEntity, AdvancedChatPromptTemplateEntity, @@ -9,6 +7,7 @@ from core.app.app_config.entities import ( PromptTemplateEntity, ) from core.prompt.simple_prompt_transform import ModelMode +from graphon.model_runtime.entities.message_entities import PromptMessageRole from models.model import AppMode, AppModelConfigDict diff --git a/api/core/app/app_config/easy_ui_based_app/variables/manager.py b/api/core/app/app_config/easy_ui_based_app/variables/manager.py index c89e1b3c3d..ddb500cccf 100644 --- a/api/core/app/app_config/easy_ui_based_app/variables/manager.py +++ b/api/core/app/app_config/easy_ui_based_app/variables/manager.py @@ -1,10 +1,9 @@ import re from typing import Any, cast -from graphon.variables.input_entities import VariableEntity, VariableEntityType - from core.app.app_config.entities import ExternalDataVariableEntity from core.external_data_tool.factory import ExternalDataToolFactory +from graphon.variables.input_entities import VariableEntity, VariableEntityType from models.model import AppModelConfigDict _ALLOWED_VARIABLE_ENTITY_TYPE = frozenset( diff --git a/api/core/app/app_config/entities.py b/api/core/app/app_config/entities.py index 819aca864c..53563dc5da 100644 --- a/api/core/app/app_config/entities.py +++ b/api/core/app/app_config/entities.py @@ -1,14 +1,14 @@ from enum import StrEnum, auto from typing import Any, Literal -from graphon.file import FileUploadConfig -from graphon.model_runtime.entities.llm_entities import LLMMode -from graphon.model_runtime.entities.message_entities import PromptMessageRole -from graphon.variables.input_entities import VariableEntity as WorkflowVariableEntity from pydantic import BaseModel, Field from core.rag.data_post_processor.data_post_processor import RerankingModelDict, WeightsDict from core.rag.entities import MetadataFilteringCondition +from graphon.file import FileUploadConfig +from graphon.model_runtime.entities.llm_entities import LLMMode +from graphon.model_runtime.entities.message_entities import PromptMessageRole +from graphon.variables.input_entities import VariableEntity as WorkflowVariableEntity from models.model import AppMode diff --git a/api/core/app/app_config/features/file_upload/manager.py b/api/core/app/app_config/features/file_upload/manager.py index 959c3868b4..8f20ef2ff9 100644 --- a/api/core/app/app_config/features/file_upload/manager.py +++ b/api/core/app/app_config/features/file_upload/manager.py @@ -1,9 +1,8 @@ from collections.abc import Mapping from typing import Any -from graphon.file import FileUploadConfig - from constants import DEFAULT_FILE_NUMBER_LIMITS +from graphon.file import FileUploadConfig class FileUploadConfigManager: diff --git a/api/core/app/app_config/workflow_ui_based_app/variables/manager.py b/api/core/app/app_config/workflow_ui_based_app/variables/manager.py index 62e0c31d1a..13ace32fd6 100644 --- a/api/core/app/app_config/workflow_ui_based_app/variables/manager.py +++ b/api/core/app/app_config/workflow_ui_based_app/variables/manager.py @@ -1,8 +1,7 @@ import re -from graphon.variables.input_entities import VariableEntity - from core.app.app_config.entities import RagPipelineVariableEntity +from graphon.variables.input_entities import VariableEntity from models.workflow import Workflow diff --git a/api/core/app/apps/advanced_chat/app_generator.py b/api/core/app/apps/advanced_chat/app_generator.py index 985ded0f74..9e64b471cb 100644 --- a/api/core/app/apps/advanced_chat/app_generator.py +++ b/api/core/app/apps/advanced_chat/app_generator.py @@ -18,11 +18,6 @@ from constants import UUID_NIL if TYPE_CHECKING: from controllers.console.app.workflow import LoopNodeRunPayload -from graphon.graph_engine.layers import GraphEngineLayer -from graphon.model_runtime.errors.invoke import InvokeAuthorizationError -from graphon.runtime import GraphRuntimeState -from graphon.variable_loader import DUMMY_VARIABLE_LOADER, VariableLoader - from core.app.app_config.features.file_upload.manager import FileUploadConfigManager from core.app.apps.advanced_chat.app_config_manager import AdvancedChatAppConfigManager from core.app.apps.advanced_chat.app_runner import AdvancedChatAppRunner @@ -48,6 +43,10 @@ from core.repositories import DifyCoreRepositoryFactory from core.repositories.factory import WorkflowExecutionRepository, WorkflowNodeExecutionRepository from extensions.ext_database import db from factories import file_factory +from graphon.graph_engine.layers import GraphEngineLayer +from graphon.model_runtime.errors.invoke import InvokeAuthorizationError +from graphon.runtime import GraphRuntimeState +from graphon.variable_loader import DUMMY_VARIABLE_LOADER, VariableLoader from libs.flask_utils import preserve_flask_contexts from models import Account, App, Conversation, EndUser, Message, Workflow, WorkflowNodeExecutionTriggeredFrom from models.enums import WorkflowRunTriggeredFrom diff --git a/api/core/app/apps/advanced_chat/app_runner.py b/api/core/app/apps/advanced_chat/app_runner.py index 7b4cb98bd4..4e57b4dedc 100644 --- a/api/core/app/apps/advanced_chat/app_runner.py +++ b/api/core/app/apps/advanced_chat/app_runner.py @@ -3,12 +3,6 @@ import time from collections.abc import Mapping, Sequence from typing import Any, cast -from graphon.enums import WorkflowType -from graphon.graph_engine.command_channels import RedisChannel -from graphon.graph_engine.layers import GraphEngineLayer -from graphon.runtime import GraphRuntimeState, VariablePool -from graphon.variable_loader import VariableLoader -from graphon.variables.variables import Variable from sqlalchemy import select from sqlalchemy.orm import Session, sessionmaker @@ -43,6 +37,12 @@ from core.workflow.workflow_entry import WorkflowEntry from extensions.ext_database import db from extensions.ext_redis import redis_client from extensions.otel import WorkflowAppRunnerHandler, trace_span +from graphon.enums import WorkflowType +from graphon.graph_engine.command_channels import RedisChannel +from graphon.graph_engine.layers import GraphEngineLayer +from graphon.runtime import GraphRuntimeState, VariablePool +from graphon.variable_loader import VariableLoader +from graphon.variables.variables import Variable from models import Workflow from models.model import App, Conversation, Message, MessageAnnotation from models.workflow import ConversationVariable diff --git a/api/core/app/apps/advanced_chat/generate_task_pipeline.py b/api/core/app/apps/advanced_chat/generate_task_pipeline.py index 0ce9ddce9e..78b582bdf5 100644 --- a/api/core/app/apps/advanced_chat/generate_task_pipeline.py +++ b/api/core/app/apps/advanced_chat/generate_task_pipeline.py @@ -9,12 +9,6 @@ from datetime import datetime from threading import Thread from typing import Any, Union -from graphon.entities.pause_reason import HumanInputRequired -from graphon.enums import WorkflowExecutionStatus -from graphon.model_runtime.entities.llm_entities import LLMUsage -from graphon.model_runtime.utils.encoders import jsonable_encoder -from graphon.nodes import BuiltinNodeTypes -from graphon.runtime import GraphRuntimeState from sqlalchemy import select from sqlalchemy.orm import Session, sessionmaker @@ -77,6 +71,12 @@ from core.repositories.human_input_repository import HumanInputFormRepositoryImp from core.workflow.file_reference import resolve_file_record_id from core.workflow.system_variables import build_system_variables from extensions.ext_database import db +from graphon.entities.pause_reason import HumanInputRequired +from graphon.enums import WorkflowExecutionStatus +from graphon.model_runtime.entities.llm_entities import LLMUsage +from graphon.model_runtime.utils.encoders import jsonable_encoder +from graphon.nodes import BuiltinNodeTypes +from graphon.runtime import GraphRuntimeState from libs.datetime_utils import naive_utc_now from models import Account, Conversation, EndUser, Message, MessageFile from models.enums import CreatorUserRole, MessageFileBelongsTo, MessageStatus diff --git a/api/core/app/apps/agent_chat/app_generator.py b/api/core/app/apps/agent_chat/app_generator.py index 5872f6b264..5cdc477028 100644 --- a/api/core/app/apps/agent_chat/app_generator.py +++ b/api/core/app/apps/agent_chat/app_generator.py @@ -6,7 +6,6 @@ from collections.abc import Generator, Mapping from typing import Any, Literal, overload from flask import Flask, current_app -from graphon.model_runtime.errors.invoke import InvokeAuthorizationError from pydantic import ValidationError from configs import dify_config @@ -24,6 +23,7 @@ from core.app.entities.app_invoke_entities import AgentChatAppGenerateEntity, In from core.ops.ops_trace_manager import TraceQueueManager from extensions.ext_database import db from factories import file_factory +from graphon.model_runtime.errors.invoke import InvokeAuthorizationError from libs.flask_utils import preserve_flask_contexts from models import Account, App, EndUser from services.conversation_service import ConversationService diff --git a/api/core/app/apps/agent_chat/app_runner.py b/api/core/app/apps/agent_chat/app_runner.py index a20d3f3c38..09ddce327e 100644 --- a/api/core/app/apps/agent_chat/app_runner.py +++ b/api/core/app/apps/agent_chat/app_runner.py @@ -1,9 +1,6 @@ import logging from typing import cast -from graphon.model_runtime.entities.llm_entities import LLMMode -from graphon.model_runtime.entities.model_entities import ModelFeature, ModelPropertyKey -from graphon.model_runtime.model_providers.__base.large_language_model import LargeLanguageModel from sqlalchemy import select from core.agent.cot_chat_agent_runner import CotChatAgentRunner @@ -19,6 +16,9 @@ from core.memory.token_buffer_memory import TokenBufferMemory from core.model_manager import ModelInstance from core.moderation.base import ModerationError from extensions.ext_database import db +from graphon.model_runtime.entities.llm_entities import LLMMode +from graphon.model_runtime.entities.model_entities import ModelFeature, ModelPropertyKey +from graphon.model_runtime.model_providers.__base.large_language_model import LargeLanguageModel from models.model import App, Conversation, Message logger = logging.getLogger(__name__) diff --git a/api/core/app/apps/base_app_generate_response_converter.py b/api/core/app/apps/base_app_generate_response_converter.py index 406d07927e..d5edfaeb25 100644 --- a/api/core/app/apps/base_app_generate_response_converter.py +++ b/api/core/app/apps/base_app_generate_response_converter.py @@ -3,11 +3,10 @@ from abc import ABC, abstractmethod from collections.abc import Generator, Mapping from typing import Any, Union -from graphon.model_runtime.errors.invoke import InvokeError - from core.app.entities.app_invoke_entities import InvokeFrom from core.app.entities.task_entities import AppBlockingResponse, AppStreamResponse from core.errors.error import ModelCurrentlyNotSupportError, ProviderTokenNotInitError, QuotaExceededError +from graphon.model_runtime.errors.invoke import InvokeError logger = logging.getLogger(__name__) diff --git a/api/core/app/apps/base_app_generator.py b/api/core/app/apps/base_app_generator.py index 7eccd59d17..8e8ccf2b90 100644 --- a/api/core/app/apps/base_app_generator.py +++ b/api/core/app/apps/base_app_generator.py @@ -2,9 +2,6 @@ from collections.abc import Generator, Mapping, Sequence from contextlib import AbstractContextManager, nullcontext from typing import TYPE_CHECKING, Any, Union, final -from graphon.enums import NodeType -from graphon.file import File, FileUploadConfig -from graphon.variables.input_entities import VariableEntityType from sqlalchemy.orm import Session from core.app.apps.draft_variable_saver import ( @@ -16,6 +13,9 @@ from core.app.entities.app_invoke_entities import InvokeFrom, UserFrom from core.app.file_access import DatabaseFileAccessController, FileAccessScope, bind_file_access_scope from extensions.ext_database import db from factories import file_factory +from graphon.enums import NodeType +from graphon.file import File, FileUploadConfig +from graphon.variables.input_entities import VariableEntityType from libs.orjson import orjson_dumps from models import Account, EndUser from services.workflow_draft_variable_service import DraftVariableSaver as DraftVariableSaverImpl diff --git a/api/core/app/apps/base_app_queue_manager.py b/api/core/app/apps/base_app_queue_manager.py index 20bf81aeec..d1771452c5 100644 --- a/api/core/app/apps/base_app_queue_manager.py +++ b/api/core/app/apps/base_app_queue_manager.py @@ -7,7 +7,6 @@ from enum import IntEnum, auto from typing import Any from cachetools import TTLCache, cachedmethod -from graphon.runtime import GraphRuntimeState from redis.exceptions import RedisError from sqlalchemy.orm import DeclarativeMeta @@ -22,6 +21,7 @@ from core.app.entities.queue_entities import ( WorkflowQueueMessage, ) from extensions.ext_redis import redis_client +from graphon.runtime import GraphRuntimeState logger = logging.getLogger(__name__) diff --git a/api/core/app/apps/base_app_runner.py b/api/core/app/apps/base_app_runner.py index 4aebc0cb30..1251b397e2 100644 --- a/api/core/app/apps/base_app_runner.py +++ b/api/core/app/apps/base_app_runner.py @@ -5,17 +5,6 @@ from collections.abc import Generator, Mapping, Sequence from mimetypes import guess_extension from typing import TYPE_CHECKING, Any, Union -from graphon.file import FileTransferMethod, FileType -from graphon.model_runtime.entities.llm_entities import LLMResult, LLMResultChunk, LLMResultChunkDelta, LLMUsage -from graphon.model_runtime.entities.message_entities import ( - AssistantPromptMessage, - ImagePromptMessageContent, - PromptMessage, - TextPromptMessageContent, -) -from graphon.model_runtime.entities.model_entities import ModelPropertyKey -from graphon.model_runtime.errors.invoke import InvokeBadRequestError - from core.app.app_config.entities import ExternalDataVariableEntity, PromptTemplateEntity from core.app.apps.base_app_queue_manager import AppQueueManager, PublishFrom from core.app.entities.app_invoke_entities import ( @@ -41,6 +30,16 @@ from core.prompt.entities.advanced_prompt_entities import ChatModelMessage, Comp from core.prompt.simple_prompt_transform import ModelMode, SimplePromptTransform from core.tools.tool_file_manager import ToolFileManager from extensions.ext_database import db +from graphon.file import FileTransferMethod, FileType +from graphon.model_runtime.entities.llm_entities import LLMResult, LLMResultChunk, LLMResultChunkDelta, LLMUsage +from graphon.model_runtime.entities.message_entities import ( + AssistantPromptMessage, + ImagePromptMessageContent, + PromptMessage, + TextPromptMessageContent, +) +from graphon.model_runtime.entities.model_entities import ModelPropertyKey +from graphon.model_runtime.errors.invoke import InvokeBadRequestError from models.enums import CreatorUserRole, MessageFileBelongsTo from models.model import App, AppMode, Message, MessageAnnotation, MessageFile diff --git a/api/core/app/apps/chat/app_generator.py b/api/core/app/apps/chat/app_generator.py index 891dcece73..58afefe296 100644 --- a/api/core/app/apps/chat/app_generator.py +++ b/api/core/app/apps/chat/app_generator.py @@ -6,7 +6,6 @@ from collections.abc import Generator, Mapping from typing import Any, Literal, overload from flask import Flask, copy_current_request_context, current_app -from graphon.model_runtime.errors.invoke import InvokeAuthorizationError from pydantic import ValidationError from configs import dify_config @@ -24,6 +23,7 @@ from core.app.entities.app_invoke_entities import ChatAppGenerateEntity, InvokeF from core.ops.ops_trace_manager import TraceQueueManager from extensions.ext_database import db from factories import file_factory +from graphon.model_runtime.errors.invoke import InvokeAuthorizationError from models import Account from models.model import App, EndUser from services.conversation_service import ConversationService diff --git a/api/core/app/apps/chat/app_runner.py b/api/core/app/apps/chat/app_runner.py index 050f763e95..077c5239f3 100644 --- a/api/core/app/apps/chat/app_runner.py +++ b/api/core/app/apps/chat/app_runner.py @@ -1,8 +1,6 @@ import logging from typing import cast -from graphon.file import File -from graphon.model_runtime.entities.message_entities import ImagePromptMessageContent from sqlalchemy import select from core.app.apps.base_app_queue_manager import AppQueueManager, PublishFrom @@ -18,6 +16,8 @@ from core.model_manager import ModelInstance from core.moderation.base import ModerationError from core.rag.retrieval.dataset_retrieval import DatasetRetrieval from extensions.ext_database import db +from graphon.file import File +from graphon.model_runtime.entities.message_entities import ImagePromptMessageContent from models.model import App, Conversation, Message logger = logging.getLogger(__name__) diff --git a/api/core/app/apps/common/graph_runtime_state_support.py b/api/core/app/apps/common/graph_runtime_state_support.py index ab277857fe..2a90fbdad0 100644 --- a/api/core/app/apps/common/graph_runtime_state_support.py +++ b/api/core/app/apps/common/graph_runtime_state_support.py @@ -4,9 +4,8 @@ from __future__ import annotations from typing import TYPE_CHECKING -from graphon.runtime import GraphRuntimeState - from core.workflow.system_variables import SystemVariableKey, get_system_text +from graphon.runtime import GraphRuntimeState if TYPE_CHECKING: from core.app.task_pipeline.based_generate_task_pipeline import BasedGenerateTaskPipeline diff --git a/api/core/app/apps/common/workflow_response_converter.py b/api/core/app/apps/common/workflow_response_converter.py index a515531616..bd685d5189 100644 --- a/api/core/app/apps/common/workflow_response_converter.py +++ b/api/core/app/apps/common/workflow_response_converter.py @@ -6,19 +6,6 @@ from dataclasses import dataclass from datetime import datetime from typing import Any, NewType, TypedDict, Union -from graphon.entities import WorkflowStartReason -from graphon.entities.pause_reason import HumanInputRequired -from graphon.enums import ( - BuiltinNodeTypes, - WorkflowExecutionStatus, - WorkflowNodeExecutionMetadataKey, - WorkflowNodeExecutionStatus, -) -from graphon.file import FILE_MODEL_IDENTITY, File -from graphon.runtime import GraphRuntimeState -from graphon.variables.segments import ArrayFileSegment, FileSegment, Segment -from graphon.variables.variables import Variable -from graphon.workflow_type_encoder import WorkflowRuntimeTypeConverter from sqlalchemy import select from sqlalchemy.orm import Session @@ -68,6 +55,19 @@ from core.workflow.human_input_forms import load_form_tokens_by_form_id from core.workflow.system_variables import SystemVariableKey, system_variables_to_mapping from core.workflow.workflow_entry import WorkflowEntry from extensions.ext_database import db +from graphon.entities import WorkflowStartReason +from graphon.entities.pause_reason import HumanInputRequired +from graphon.enums import ( + BuiltinNodeTypes, + WorkflowExecutionStatus, + WorkflowNodeExecutionMetadataKey, + WorkflowNodeExecutionStatus, +) +from graphon.file import FILE_MODEL_IDENTITY, File +from graphon.runtime import GraphRuntimeState +from graphon.variables.segments import ArrayFileSegment, FileSegment, Segment +from graphon.variables.variables import Variable +from graphon.workflow_type_encoder import WorkflowRuntimeTypeConverter from libs.datetime_utils import naive_utc_now from models import Account, EndUser from models.human_input import HumanInputForm diff --git a/api/core/app/apps/completion/app_generator.py b/api/core/app/apps/completion/app_generator.py index 61339b316a..423bfdac51 100644 --- a/api/core/app/apps/completion/app_generator.py +++ b/api/core/app/apps/completion/app_generator.py @@ -6,7 +6,6 @@ from collections.abc import Generator, Mapping from typing import Any, Literal, overload from flask import Flask, copy_current_request_context, current_app -from graphon.model_runtime.errors.invoke import InvokeAuthorizationError from pydantic import ValidationError from sqlalchemy import select @@ -24,6 +23,7 @@ from core.app.entities.app_invoke_entities import CompletionAppGenerateEntity, I from core.ops.ops_trace_manager import TraceQueueManager from extensions.ext_database import db from factories import file_factory +from graphon.model_runtime.errors.invoke import InvokeAuthorizationError from models import Account, App, EndUser, Message from services.errors.app import MoreLikeThisDisabledError from services.errors.message import MessageNotExistsError diff --git a/api/core/app/apps/completion/app_runner.py b/api/core/app/apps/completion/app_runner.py index b216f7cf7b..6bb1ecdcb1 100644 --- a/api/core/app/apps/completion/app_runner.py +++ b/api/core/app/apps/completion/app_runner.py @@ -1,8 +1,6 @@ import logging from typing import cast -from graphon.file import File -from graphon.model_runtime.entities.message_entities import ImagePromptMessageContent from sqlalchemy import select from core.app.apps.base_app_queue_manager import AppQueueManager @@ -16,6 +14,8 @@ from core.model_manager import ModelInstance from core.moderation.base import ModerationError from core.rag.retrieval.dataset_retrieval import DatasetRetrieval from extensions.ext_database import db +from graphon.file import File +from graphon.model_runtime.entities.message_entities import ImagePromptMessageContent from models.model import App, Message logger = logging.getLogger(__name__) diff --git a/api/core/app/apps/pipeline/pipeline_generator.py b/api/core/app/apps/pipeline/pipeline_generator.py index 83c74b86e5..4b2f17189b 100644 --- a/api/core/app/apps/pipeline/pipeline_generator.py +++ b/api/core/app/apps/pipeline/pipeline_generator.py @@ -10,8 +10,6 @@ from collections.abc import Generator, Mapping from typing import Any, Literal, cast, overload from flask import Flask, current_app -from graphon.model_runtime.errors.invoke import InvokeAuthorizationError -from graphon.variable_loader import DUMMY_VARIABLE_LOADER, VariableLoader from pydantic import ValidationError from sqlalchemy import select from sqlalchemy.orm import Session, sessionmaker @@ -43,6 +41,8 @@ from core.repositories.factory import ( WorkflowNodeExecutionRepository, ) from extensions.ext_database import db +from graphon.model_runtime.errors.invoke import InvokeAuthorizationError +from graphon.variable_loader import DUMMY_VARIABLE_LOADER, VariableLoader from libs.flask_utils import preserve_flask_contexts from models import Account, EndUser, Workflow, WorkflowNodeExecutionTriggeredFrom from models.dataset import Document, DocumentPipelineExecutionLog, Pipeline diff --git a/api/core/app/apps/pipeline/pipeline_runner.py b/api/core/app/apps/pipeline/pipeline_runner.py index 36daaf09e9..2ee0ae27eb 100644 --- a/api/core/app/apps/pipeline/pipeline_runner.py +++ b/api/core/app/apps/pipeline/pipeline_runner.py @@ -2,12 +2,6 @@ import logging import time from typing import cast -from graphon.enums import WorkflowType -from graphon.graph import Graph -from graphon.graph_events import GraphEngineEvent, GraphRunFailedEvent -from graphon.runtime import GraphRuntimeState, VariablePool -from graphon.variable_loader import VariableLoader -from graphon.variables.variables import RAGPipelineVariable, RAGPipelineVariableInput from sqlalchemy import select from core.app.apps.base_app_queue_manager import AppQueueManager @@ -26,6 +20,12 @@ from core.workflow.system_variables import build_bootstrap_variables, build_syst from core.workflow.variable_pool_initializer import add_node_inputs_to_pool, add_variables_to_pool from core.workflow.workflow_entry import WorkflowEntry from extensions.ext_database import db +from graphon.enums import WorkflowType +from graphon.graph import Graph +from graphon.graph_events import GraphEngineEvent, GraphRunFailedEvent +from graphon.runtime import GraphRuntimeState, VariablePool +from graphon.variable_loader import VariableLoader +from graphon.variables.variables import RAGPipelineVariable, RAGPipelineVariableInput from models.dataset import Document, Pipeline from models.model import EndUser from models.workflow import Workflow diff --git a/api/core/app/apps/workflow/app_generator.py b/api/core/app/apps/workflow/app_generator.py index 6074e81d1e..6937014a06 100644 --- a/api/core/app/apps/workflow/app_generator.py +++ b/api/core/app/apps/workflow/app_generator.py @@ -8,10 +8,6 @@ from collections.abc import Generator, Mapping, Sequence from typing import TYPE_CHECKING, Any, Literal, overload from flask import Flask, current_app -from graphon.graph_engine.layers import GraphEngineLayer -from graphon.model_runtime.errors.invoke import InvokeAuthorizationError -from graphon.runtime import GraphRuntimeState -from graphon.variable_loader import DUMMY_VARIABLE_LOADER, VariableLoader from pydantic import ValidationError from sqlalchemy import select from sqlalchemy.orm import sessionmaker @@ -38,6 +34,10 @@ from core.repositories import DifyCoreRepositoryFactory from core.repositories.factory import WorkflowExecutionRepository, WorkflowNodeExecutionRepository from extensions.ext_database import db from factories import file_factory +from graphon.graph_engine.layers import GraphEngineLayer +from graphon.model_runtime.errors.invoke import InvokeAuthorizationError +from graphon.runtime import GraphRuntimeState +from graphon.variable_loader import DUMMY_VARIABLE_LOADER, VariableLoader from libs.flask_utils import preserve_flask_contexts from models.account import Account from models.enums import WorkflowRunTriggeredFrom diff --git a/api/core/app/apps/workflow/app_runner.py b/api/core/app/apps/workflow/app_runner.py index 2cb8088971..cfb9208486 100644 --- a/api/core/app/apps/workflow/app_runner.py +++ b/api/core/app/apps/workflow/app_runner.py @@ -3,12 +3,6 @@ import time from collections.abc import Sequence from typing import cast -from graphon.enums import WorkflowType -from graphon.graph_engine.command_channels import RedisChannel -from graphon.graph_engine.layers import GraphEngineLayer -from graphon.runtime import GraphRuntimeState, VariablePool -from graphon.variable_loader import VariableLoader - from core.app.apps.base_app_queue_manager import AppQueueManager from core.app.apps.workflow.app_config_manager import WorkflowAppConfig from core.app.apps.workflow_app_runner import WorkflowBasedAppRunner @@ -21,6 +15,11 @@ from core.workflow.variable_pool_initializer import add_node_inputs_to_pool, add from core.workflow.workflow_entry import WorkflowEntry from extensions.ext_redis import redis_client from extensions.otel import WorkflowAppRunnerHandler, trace_span +from graphon.enums import WorkflowType +from graphon.graph_engine.command_channels import RedisChannel +from graphon.graph_engine.layers import GraphEngineLayer +from graphon.runtime import GraphRuntimeState, VariablePool +from graphon.variable_loader import VariableLoader from libs.datetime_utils import naive_utc_now from models.workflow import Workflow diff --git a/api/core/app/apps/workflow/generate_task_pipeline.py b/api/core/app/apps/workflow/generate_task_pipeline.py index 96387133b1..15645add57 100644 --- a/api/core/app/apps/workflow/generate_task_pipeline.py +++ b/api/core/app/apps/workflow/generate_task_pipeline.py @@ -4,9 +4,6 @@ from collections.abc import Callable, Generator from contextlib import contextmanager from typing import Union -from graphon.entities import WorkflowStartReason -from graphon.enums import WorkflowExecutionStatus -from graphon.runtime import GraphRuntimeState from sqlalchemy.orm import Session, sessionmaker from constants.tts_auto_play_timeout import TTS_AUTO_PLAY_TIMEOUT, TTS_AUTO_PLAY_YIELD_CPU_TIME @@ -61,6 +58,9 @@ from core.base.tts import AppGeneratorTTSPublisher, AudioTrunk from core.ops.ops_trace_manager import TraceQueueManager from core.workflow.system_variables import build_system_variables from extensions.ext_database import db +from graphon.entities import WorkflowStartReason +from graphon.enums import WorkflowExecutionStatus +from graphon.runtime import GraphRuntimeState from models import Account from models.enums import CreatorUserRole from models.model import EndUser diff --git a/api/core/app/apps/workflow_app_runner.py b/api/core/app/apps/workflow_app_runner.py index 437432611d..047b54c86c 100644 --- a/api/core/app/apps/workflow_app_runner.py +++ b/api/core/app/apps/workflow_app_runner.py @@ -3,39 +3,6 @@ import time from collections.abc import Mapping, Sequence from typing import Any, cast -from graphon.entities.graph_config import NodeConfigDictAdapter -from graphon.entities.pause_reason import HumanInputRequired -from graphon.graph import Graph -from graphon.graph_engine.layers import GraphEngineLayer -from graphon.graph_events import ( - GraphEngineEvent, - GraphRunAbortedEvent, - GraphRunFailedEvent, - GraphRunPartialSucceededEvent, - GraphRunPausedEvent, - GraphRunStartedEvent, - GraphRunSucceededEvent, - NodeRunAgentLogEvent, - NodeRunExceptionEvent, - NodeRunFailedEvent, - NodeRunHumanInputFormFilledEvent, - NodeRunHumanInputFormTimeoutEvent, - NodeRunIterationFailedEvent, - NodeRunIterationNextEvent, - NodeRunIterationStartedEvent, - NodeRunIterationSucceededEvent, - NodeRunLoopFailedEvent, - NodeRunLoopNextEvent, - NodeRunLoopStartedEvent, - NodeRunLoopSucceededEvent, - NodeRunRetrieverResourceEvent, - NodeRunRetryEvent, - NodeRunStartedEvent, - NodeRunStreamChunkEvent, - NodeRunSucceededEvent, -) -from graphon.runtime import GraphRuntimeState, VariablePool -from graphon.variable_loader import DUMMY_VARIABLE_LOADER, VariableLoader, load_into_variable_pool from pydantic import ValidationError from core.app.apps.base_app_queue_manager import AppQueueManager, PublishFrom @@ -82,6 +49,39 @@ from core.workflow.system_variables import ( from core.workflow.variable_pool_initializer import add_variables_to_pool from core.workflow.workflow_entry import WorkflowEntry from core.workflow.workflow_run_outputs import project_node_outputs_for_workflow_run +from graphon.entities.graph_config import NodeConfigDictAdapter +from graphon.entities.pause_reason import HumanInputRequired +from graphon.graph import Graph +from graphon.graph_engine.layers import GraphEngineLayer +from graphon.graph_events import ( + GraphEngineEvent, + GraphRunAbortedEvent, + GraphRunFailedEvent, + GraphRunPartialSucceededEvent, + GraphRunPausedEvent, + GraphRunStartedEvent, + GraphRunSucceededEvent, + NodeRunAgentLogEvent, + NodeRunExceptionEvent, + NodeRunFailedEvent, + NodeRunHumanInputFormFilledEvent, + NodeRunHumanInputFormTimeoutEvent, + NodeRunIterationFailedEvent, + NodeRunIterationNextEvent, + NodeRunIterationStartedEvent, + NodeRunIterationSucceededEvent, + NodeRunLoopFailedEvent, + NodeRunLoopNextEvent, + NodeRunLoopStartedEvent, + NodeRunLoopSucceededEvent, + NodeRunRetrieverResourceEvent, + NodeRunRetryEvent, + NodeRunStartedEvent, + NodeRunStreamChunkEvent, + NodeRunSucceededEvent, +) +from graphon.runtime import GraphRuntimeState, VariablePool +from graphon.variable_loader import DUMMY_VARIABLE_LOADER, VariableLoader, load_into_variable_pool from models.workflow import Workflow from tasks.mail_human_input_delivery_task import dispatch_human_input_email_task diff --git a/api/core/app/entities/app_invoke_entities.py b/api/core/app/entities/app_invoke_entities.py index a3fb7b4c5d..09992f4bbf 100644 --- a/api/core/app/entities/app_invoke_entities.py +++ b/api/core/app/entities/app_invoke_entities.py @@ -2,13 +2,13 @@ from collections.abc import Mapping, Sequence from enum import StrEnum from typing import TYPE_CHECKING, Any -from graphon.file import File, FileUploadConfig -from graphon.model_runtime.entities.model_entities import AIModelEntity from pydantic import BaseModel, ConfigDict, Field, ValidationInfo, field_validator from constants import UUID_NIL from core.app.app_config.entities import EasyUIBasedAppConfig, WorkflowUIBasedAppConfig from core.entities.provider_configuration import ProviderModelBundle +from graphon.file import File, FileUploadConfig +from graphon.model_runtime.entities.model_entities import AIModelEntity if TYPE_CHECKING: from core.ops.ops_trace_manager import TraceQueueManager diff --git a/api/core/app/entities/queue_entities.py b/api/core/app/entities/queue_entities.py index 482f995d8e..221b7fb058 100644 --- a/api/core/app/entities/queue_entities.py +++ b/api/core/app/entities/queue_entities.py @@ -3,14 +3,14 @@ from datetime import datetime from enum import StrEnum, auto from typing import Any -from graphon.entities import WorkflowStartReason -from graphon.entities.pause_reason import PauseReason -from graphon.enums import NodeType, WorkflowNodeExecutionMetadataKey -from graphon.model_runtime.entities.llm_entities import LLMResult, LLMResultChunk from pydantic import BaseModel, ConfigDict, Field from core.app.entities.agent_strategy import AgentStrategyInfo from core.rag.entities import RetrievalSourceMetadata +from graphon.entities import WorkflowStartReason +from graphon.entities.pause_reason import PauseReason +from graphon.enums import NodeType, WorkflowNodeExecutionMetadataKey +from graphon.model_runtime.entities.llm_entities import LLMResult, LLMResultChunk class QueueEvent(StrEnum): diff --git a/api/core/app/entities/task_entities.py b/api/core/app/entities/task_entities.py index 88faf235d1..6e4ca69cf0 100644 --- a/api/core/app/entities/task_entities.py +++ b/api/core/app/entities/task_entities.py @@ -2,14 +2,14 @@ from collections.abc import Mapping, Sequence from enum import StrEnum from typing import Any -from graphon.entities import WorkflowStartReason -from graphon.enums import WorkflowExecutionStatus, WorkflowNodeExecutionMetadataKey, WorkflowNodeExecutionStatus -from graphon.model_runtime.entities.llm_entities import LLMResult, LLMUsage -from graphon.nodes.human_input.entities import FormInput, UserAction from pydantic import BaseModel, ConfigDict, Field from core.app.entities.agent_strategy import AgentStrategyInfo from core.rag.entities import RetrievalSourceMetadata +from graphon.entities import WorkflowStartReason +from graphon.enums import WorkflowExecutionStatus, WorkflowNodeExecutionMetadataKey, WorkflowNodeExecutionStatus +from graphon.model_runtime.entities.llm_entities import LLMResult, LLMUsage +from graphon.nodes.human_input.entities import FormInput, UserAction class AnnotationReplyAccount(BaseModel): diff --git a/api/core/app/features/hosting_moderation/hosting_moderation.py b/api/core/app/features/hosting_moderation/hosting_moderation.py index d2d2fea4fb..d59f5125e3 100644 --- a/api/core/app/features/hosting_moderation/hosting_moderation.py +++ b/api/core/app/features/hosting_moderation/hosting_moderation.py @@ -1,9 +1,8 @@ import logging -from graphon.model_runtime.entities.message_entities import PromptMessage - from core.app.entities.app_invoke_entities import EasyUIBasedAppGenerateEntity from core.helper import moderation +from graphon.model_runtime.entities.message_entities import PromptMessage logger = logging.getLogger(__name__) diff --git a/api/core/app/layers/conversation_variable_persist_layer.py b/api/core/app/layers/conversation_variable_persist_layer.py index e09869f5f8..d5e6b04a4a 100644 --- a/api/core/app/layers/conversation_variable_persist_layer.py +++ b/api/core/app/layers/conversation_variable_persist_layer.py @@ -9,11 +9,10 @@ scope updates that matter to chat applications. import logging -from graphon.graph_engine.layers import GraphEngineLayer -from graphon.graph_events import GraphEngineEvent, NodeRunVariableUpdatedEvent - from core.workflow.system_variables import SystemVariableKey, get_system_text from core.workflow.variable_prefixes import CONVERSATION_VARIABLE_NODE_ID +from graphon.graph_engine.layers import GraphEngineLayer +from graphon.graph_events import GraphEngineEvent, NodeRunVariableUpdatedEvent from services.conversation_variable_updater import ConversationVariableUpdater logger = logging.getLogger(__name__) diff --git a/api/core/app/layers/pause_state_persist_layer.py b/api/core/app/layers/pause_state_persist_layer.py index c027f42788..9811f9f830 100644 --- a/api/core/app/layers/pause_state_persist_layer.py +++ b/api/core/app/layers/pause_state_persist_layer.py @@ -1,14 +1,14 @@ from dataclasses import dataclass from typing import Annotated, Literal, Self -from graphon.graph_engine.layers import GraphEngineLayer -from graphon.graph_events import GraphEngineEvent, GraphRunPausedEvent from pydantic import BaseModel, Field from sqlalchemy import Engine from sqlalchemy.orm import Session, sessionmaker from core.app.entities.app_invoke_entities import AdvancedChatAppGenerateEntity, WorkflowAppGenerateEntity from core.workflow.system_variables import SystemVariableKey, get_system_text +from graphon.graph_engine.layers import GraphEngineLayer +from graphon.graph_events import GraphEngineEvent, GraphRunPausedEvent from models.model import AppMode from repositories.api_workflow_run_repository import APIWorkflowRunRepository from repositories.factory import DifyAPIRepositoryFactory diff --git a/api/core/app/layers/timeslice_layer.py b/api/core/app/layers/timeslice_layer.py index 8c8daf8712..bb9fc1b6fa 100644 --- a/api/core/app/layers/timeslice_layer.py +++ b/api/core/app/layers/timeslice_layer.py @@ -3,10 +3,10 @@ import uuid from typing import ClassVar from apscheduler.schedulers.background import BackgroundScheduler # type: ignore + from graphon.graph_engine.entities.commands import CommandType, GraphEngineCommand from graphon.graph_engine.layers import GraphEngineLayer from graphon.graph_events import GraphEngineEvent - from services.workflow.entities import WorkflowScheduleCFSPlanEntity from services.workflow.scheduler import CFSPlanScheduler, SchedulerCommand diff --git a/api/core/app/layers/trigger_post_layer.py b/api/core/app/layers/trigger_post_layer.py index 77c7bec67e..b60fe82ffe 100644 --- a/api/core/app/layers/trigger_post_layer.py +++ b/api/core/app/layers/trigger_post_layer.py @@ -2,12 +2,12 @@ import logging from datetime import UTC, datetime from typing import Any, ClassVar -from graphon.graph_engine.layers import GraphEngineLayer -from graphon.graph_events import GraphEngineEvent, GraphRunFailedEvent, GraphRunPausedEvent, GraphRunSucceededEvent from pydantic import TypeAdapter from core.db.session_factory import session_factory from core.workflow.system_variables import SystemVariableKey, get_system_text +from graphon.graph_engine.layers import GraphEngineLayer +from graphon.graph_events import GraphEngineEvent, GraphRunFailedEvent, GraphRunPausedEvent, GraphRunSucceededEvent from models.enums import WorkflowTriggerStatus from repositories.sqlalchemy_workflow_trigger_log_repository import SQLAlchemyWorkflowTriggerLogRepository from tasks.workflow_cfs_scheduler.cfs_scheduler import AsyncWorkflowCFSPlanEntity diff --git a/api/core/app/llm/model_access.py b/api/core/app/llm/model_access.py index 278d0cb30b..c49c4eb0ac 100644 --- a/api/core/app/llm/model_access.py +++ b/api/core/app/llm/model_access.py @@ -2,16 +2,15 @@ from __future__ import annotations from typing import Any -from graphon.model_runtime.entities.model_entities import ModelType -from graphon.nodes.llm.entities import ModelConfig -from graphon.nodes.llm.exc import LLMModeRequiredError, ModelNotExistError -from graphon.nodes.llm.protocols import CredentialsProvider - from core.app.entities.app_invoke_entities import DifyRunContext, ModelConfigWithCredentialsEntity from core.errors.error import ProviderTokenNotInitError from core.model_manager import ModelInstance, ModelManager from core.plugin.impl.model_runtime_factory import create_plugin_provider_manager from core.provider_manager import ProviderManager +from graphon.model_runtime.entities.model_entities import ModelType +from graphon.nodes.llm.entities import ModelConfig +from graphon.nodes.llm.exc import LLMModeRequiredError, ModelNotExistError +from graphon.nodes.llm.protocols import CredentialsProvider class DifyCredentialsProvider: diff --git a/api/core/app/llm/quota.py b/api/core/app/llm/quota.py index 0bb10190c4..b6039e1e4e 100644 --- a/api/core/app/llm/quota.py +++ b/api/core/app/llm/quota.py @@ -1,4 +1,3 @@ -from graphon.model_runtime.entities.llm_entities import LLMUsage from sqlalchemy import update from sqlalchemy.orm import sessionmaker @@ -8,6 +7,7 @@ from core.entities.provider_entities import ProviderQuotaType, QuotaUnit from core.errors.error import QuotaExceededError from core.model_manager import ModelInstance from extensions.ext_database import db +from graphon.model_runtime.entities.llm_entities import LLMUsage from libs.datetime_utils import naive_utc_now from models.provider import Provider, ProviderType from models.provider_ids import ModelProviderID diff --git a/api/core/app/task_pipeline/based_generate_task_pipeline.py b/api/core/app/task_pipeline/based_generate_task_pipeline.py index 10b9c36d3e..9e688589db 100644 --- a/api/core/app/task_pipeline/based_generate_task_pipeline.py +++ b/api/core/app/task_pipeline/based_generate_task_pipeline.py @@ -1,7 +1,6 @@ import logging import time -from graphon.model_runtime.errors.invoke import InvokeAuthorizationError, InvokeError from sqlalchemy import select from sqlalchemy.orm import Session @@ -18,6 +17,7 @@ from core.app.entities.task_entities import ( ) from core.errors.error import QuotaExceededError from core.moderation.output_moderation import ModerationRule, OutputModeration +from graphon.model_runtime.errors.invoke import InvokeAuthorizationError, InvokeError from models.enums import MessageStatus from models.model import Message diff --git a/api/core/app/task_pipeline/easy_ui_based_generate_task_pipeline.py b/api/core/app/task_pipeline/easy_ui_based_generate_task_pipeline.py index 6bb177fe02..dfe6133cb6 100644 --- a/api/core/app/task_pipeline/easy_ui_based_generate_task_pipeline.py +++ b/api/core/app/task_pipeline/easy_ui_based_generate_task_pipeline.py @@ -4,13 +4,6 @@ from collections.abc import Generator from threading import Thread from typing import Any, cast -from graphon.file import FileTransferMethod -from graphon.model_runtime.entities.llm_entities import LLMResult, LLMResultChunk, LLMResultChunkDelta, LLMUsage -from graphon.model_runtime.entities.message_entities import ( - AssistantPromptMessage, - TextPromptMessageContent, -) -from graphon.model_runtime.model_providers.__base.large_language_model import LargeLanguageModel from sqlalchemy import select from sqlalchemy.orm import Session, sessionmaker @@ -60,6 +53,13 @@ from core.prompt.utils.prompt_message_util import PromptMessageUtil from core.prompt.utils.prompt_template_parser import PromptTemplateParser from events.message_event import message_was_created from extensions.ext_database import db +from graphon.file import FileTransferMethod +from graphon.model_runtime.entities.llm_entities import LLMResult, LLMResultChunk, LLMResultChunkDelta, LLMUsage +from graphon.model_runtime.entities.message_entities import ( + AssistantPromptMessage, + TextPromptMessageContent, +) +from graphon.model_runtime.model_providers.__base.large_language_model import LargeLanguageModel from libs.datetime_utils import naive_utc_now from models.model import AppMode, Conversation, Message, MessageAgentThought, MessageFile, UploadFile diff --git a/api/core/app/task_pipeline/message_file_utils.py b/api/core/app/task_pipeline/message_file_utils.py index 77310baf74..1dd713821f 100644 --- a/api/core/app/task_pipeline/message_file_utils.py +++ b/api/core/app/task_pipeline/message_file_utils.py @@ -1,9 +1,8 @@ from typing import TypedDict +from core.tools.signature import sign_tool_file from graphon.file import FileTransferMethod from graphon.file import helpers as file_helpers - -from core.tools.signature import sign_tool_file from models.model import MessageFile, UploadFile MAX_TOOL_FILE_EXTENSION_LENGTH = 10 diff --git a/api/core/app/workflow/file_runtime.py b/api/core/app/workflow/file_runtime.py index 8604235ef2..68e5e5f0c8 100644 --- a/api/core/app/workflow/file_runtime.py +++ b/api/core/app/workflow/file_runtime.py @@ -9,10 +9,6 @@ import urllib.parse from collections.abc import Generator from typing import TYPE_CHECKING, Literal -from graphon.file import FileTransferMethod -from graphon.file.protocols import HttpResponseProtocol, WorkflowFileRuntimeProtocol -from graphon.file.runtime import set_workflow_file_runtime - from configs import dify_config from core.app.file_access import DatabaseFileAccessController, FileAccessControllerProtocol from core.db.session_factory import session_factory @@ -20,6 +16,9 @@ from core.helper.ssrf_proxy import ssrf_proxy from core.tools.signature import sign_tool_file from core.workflow.file_reference import parse_file_reference from extensions.ext_storage import storage +from graphon.file import FileTransferMethod +from graphon.file.protocols import HttpResponseProtocol, WorkflowFileRuntimeProtocol +from graphon.file.runtime import set_workflow_file_runtime if TYPE_CHECKING: from graphon.file import File diff --git a/api/core/app/workflow/layers/llm_quota.py b/api/core/app/workflow/layers/llm_quota.py index c577ce0754..4a7918032e 100644 --- a/api/core/app/workflow/layers/llm_quota.py +++ b/api/core/app/workflow/layers/llm_quota.py @@ -7,17 +7,16 @@ This layer centralizes model-quota deduction outside node implementations. import logging from typing import TYPE_CHECKING, cast, final, override +from core.app.entities.app_invoke_entities import DIFY_RUN_CONTEXT_KEY, DifyRunContext +from core.app.llm import deduct_llm_quota, ensure_llm_quota_available +from core.errors.error import QuotaExceededError +from core.model_manager import ModelInstance from graphon.enums import BuiltinNodeTypes from graphon.graph_engine.entities.commands import AbortCommand, CommandType from graphon.graph_engine.layers import GraphEngineLayer from graphon.graph_events import GraphEngineEvent, GraphNodeEventBase, NodeRunSucceededEvent from graphon.nodes.base.node import Node -from core.app.entities.app_invoke_entities import DIFY_RUN_CONTEXT_KEY, DifyRunContext -from core.app.llm import deduct_llm_quota, ensure_llm_quota_available -from core.errors.error import QuotaExceededError -from core.model_manager import ModelInstance - if TYPE_CHECKING: from graphon.nodes.llm.node import LLMNode from graphon.nodes.parameter_extractor.parameter_extractor_node import ParameterExtractorNode diff --git a/api/core/app/workflow/layers/observability.py b/api/core/app/workflow/layers/observability.py index 99e8015c0b..8b5a5b9d7f 100644 --- a/api/core/app/workflow/layers/observability.py +++ b/api/core/app/workflow/layers/observability.py @@ -12,10 +12,6 @@ from contextvars import Token from dataclasses import dataclass from typing import cast, final, override -from graphon.enums import BuiltinNodeTypes, NodeType -from graphon.graph_engine.layers import GraphEngineLayer -from graphon.graph_events import GraphNodeEventBase -from graphon.nodes.base.node import Node from opentelemetry import context as context_api from opentelemetry.trace import Span, SpanKind, Tracer, get_tracer, set_span_in_context @@ -28,6 +24,10 @@ from extensions.otel.parser import ( ToolNodeOTelParser, ) from extensions.otel.runtime import is_instrument_flag_enabled +from graphon.enums import BuiltinNodeTypes, NodeType +from graphon.graph_engine.layers import GraphEngineLayer +from graphon.graph_events import GraphNodeEventBase +from graphon.nodes.base.node import Node logger = logging.getLogger(__name__) diff --git a/api/core/app/workflow/layers/persistence.py b/api/core/app/workflow/layers/persistence.py index ada065a943..87f005a250 100644 --- a/api/core/app/workflow/layers/persistence.py +++ b/api/core/app/workflow/layers/persistence.py @@ -14,6 +14,13 @@ from dataclasses import dataclass from datetime import datetime from typing import Any, Union +from core.app.entities.app_invoke_entities import AdvancedChatAppGenerateEntity, WorkflowAppGenerateEntity +from core.ops.entities.trace_entity import TraceTaskName +from core.ops.ops_trace_manager import TraceQueueManager, TraceTask +from core.repositories.factory import WorkflowExecutionRepository, WorkflowNodeExecutionRepository +from core.workflow.system_variables import SystemVariableKey +from core.workflow.variable_prefixes import SYSTEM_VARIABLE_NODE_ID +from core.workflow.workflow_run_outputs import project_node_outputs_for_workflow_run from graphon.entities import WorkflowExecution, WorkflowNodeExecution from graphon.enums import ( WorkflowExecutionStatus, @@ -38,14 +45,6 @@ from graphon.graph_events import ( NodeRunSucceededEvent, ) from graphon.node_events import NodeRunResult - -from core.app.entities.app_invoke_entities import AdvancedChatAppGenerateEntity, WorkflowAppGenerateEntity -from core.ops.entities.trace_entity import TraceTaskName -from core.ops.ops_trace_manager import TraceQueueManager, TraceTask -from core.repositories.factory import WorkflowExecutionRepository, WorkflowNodeExecutionRepository -from core.workflow.system_variables import SystemVariableKey -from core.workflow.variable_prefixes import SYSTEM_VARIABLE_NODE_ID -from core.workflow.workflow_run_outputs import project_node_outputs_for_workflow_run from libs.datetime_utils import naive_utc_now diff --git a/api/core/base/tts/app_generator_tts_publisher.py b/api/core/base/tts/app_generator_tts_publisher.py index 3d8a7a54f3..9e3c187210 100644 --- a/api/core/base/tts/app_generator_tts_publisher.py +++ b/api/core/base/tts/app_generator_tts_publisher.py @@ -6,9 +6,6 @@ import re import threading from collections.abc import Iterable -from graphon.model_runtime.entities.message_entities import TextPromptMessageContent -from graphon.model_runtime.entities.model_entities import ModelType - from core.app.entities.queue_entities import ( MessageQueueMessage, QueueAgentMessageEvent, @@ -18,6 +15,8 @@ from core.app.entities.queue_entities import ( WorkflowQueueMessage, ) from core.model_manager import ModelInstance, ModelManager +from graphon.model_runtime.entities.message_entities import TextPromptMessageContent +from graphon.model_runtime.entities.model_entities import ModelType class AudioTrunk: diff --git a/api/core/datasource/datasource_manager.py b/api/core/datasource/datasource_manager.py index a5297fa33a..dc831e5cac 100644 --- a/api/core/datasource/datasource_manager.py +++ b/api/core/datasource/datasource_manager.py @@ -3,9 +3,6 @@ from collections.abc import Generator from threading import Lock from typing import Any, cast -from graphon.enums import WorkflowNodeExecutionMetadataKey, WorkflowNodeExecutionStatus -from graphon.file import File, FileTransferMethod, FileType, get_file_type_by_mime_type -from graphon.node_events import NodeRunResult, StreamChunkEvent, StreamCompletedEvent from sqlalchemy import select import contexts @@ -31,6 +28,9 @@ from core.plugin.impl.datasource import PluginDatasourceManager from core.workflow.file_reference import build_file_reference from core.workflow.nodes.datasource.entities import DatasourceParameter, OnlineDriveDownloadFileParam from factories import file_factory +from graphon.enums import WorkflowNodeExecutionMetadataKey, WorkflowNodeExecutionStatus +from graphon.file import File, FileTransferMethod, FileType, get_file_type_by_mime_type +from graphon.node_events import NodeRunResult, StreamChunkEvent, StreamCompletedEvent from models.model import UploadFile from models.tools import ToolFile from services.datasource_provider_service import DatasourceProviderService diff --git a/api/core/datasource/entities/api_entities.py b/api/core/datasource/entities/api_entities.py index 9c22d5e67c..352e6bfd49 100644 --- a/api/core/datasource/entities/api_entities.py +++ b/api/core/datasource/entities/api_entities.py @@ -1,10 +1,10 @@ from typing import Any, Literal, TypedDict -from graphon.model_runtime.utils.encoders import jsonable_encoder from pydantic import BaseModel, Field, field_validator from core.datasource.entities.datasource_entities import DatasourceParameter from core.tools.entities.common_entities import I18nObject, I18nObjectDict +from graphon.model_runtime.utils.encoders import jsonable_encoder class DatasourceApiEntity(BaseModel): diff --git a/api/core/datasource/utils/message_transformer.py b/api/core/datasource/utils/message_transformer.py index c012e128f4..6a3f9e684a 100644 --- a/api/core/datasource/utils/message_transformer.py +++ b/api/core/datasource/utils/message_transformer.py @@ -2,11 +2,10 @@ import logging from collections.abc import Generator from mimetypes import guess_extension, guess_type -from graphon.file import File, FileTransferMethod, FileType - from core.datasource.entities.datasource_entities import DatasourceMessage from core.tools.tool_file_manager import ToolFileManager from core.workflow.file_reference import parse_file_reference +from graphon.file import File, FileTransferMethod, FileType from models.tools import ToolFile logger = logging.getLogger(__name__) diff --git a/api/core/entities/execution_extra_content.py b/api/core/entities/execution_extra_content.py index d304c982cd..04ae193396 100644 --- a/api/core/entities/execution_extra_content.py +++ b/api/core/entities/execution_extra_content.py @@ -3,9 +3,9 @@ from __future__ import annotations from collections.abc import Mapping, Sequence from typing import Any, TypeAlias -from graphon.nodes.human_input.entities import FormInput, UserAction from pydantic import BaseModel, ConfigDict, Field +from graphon.nodes.human_input.entities import FormInput, UserAction from models.execution_extra_content import ExecutionContentType diff --git a/api/core/entities/mcp_provider.py b/api/core/entities/mcp_provider.py index a440829b46..bfa4f56915 100644 --- a/api/core/entities/mcp_provider.py +++ b/api/core/entities/mcp_provider.py @@ -6,7 +6,6 @@ from enum import StrEnum from typing import TYPE_CHECKING, Any from urllib.parse import urlparse -from graphon.file import helpers as file_helpers from pydantic import BaseModel from configs import dify_config @@ -16,6 +15,7 @@ from core.helper.provider_cache import NoOpProviderCredentialCache from core.mcp.types import OAuthClientInformation, OAuthClientMetadata, OAuthTokens from core.tools.entities.common_entities import I18nObject from core.tools.entities.tool_entities import ToolProviderType +from graphon.file import helpers as file_helpers if TYPE_CHECKING: from models.tools import MCPToolProvider diff --git a/api/core/entities/model_entities.py b/api/core/entities/model_entities.py index 84d95c38c6..e99a131500 100644 --- a/api/core/entities/model_entities.py +++ b/api/core/entities/model_entities.py @@ -1,10 +1,11 @@ from collections.abc import Sequence from enum import StrEnum, auto +from pydantic import BaseModel, ConfigDict + from graphon.model_runtime.entities.common_entities import I18nObject from graphon.model_runtime.entities.model_entities import ModelType, ProviderModel from graphon.model_runtime.entities.provider_entities import ProviderEntity -from pydantic import BaseModel, ConfigDict class ModelStatus(StrEnum): diff --git a/api/core/entities/provider_configuration.py b/api/core/entities/provider_configuration.py index d07f6f913a..1ab66cceee 100644 --- a/api/core/entities/provider_configuration.py +++ b/api/core/entities/provider_configuration.py @@ -8,16 +8,6 @@ from collections.abc import Iterator, Sequence from json import JSONDecodeError from typing import Any -from graphon.model_runtime.entities.model_entities import AIModelEntity, FetchFrom, ModelType -from graphon.model_runtime.entities.provider_entities import ( - ConfigurateMethod, - CredentialFormSchema, - FormType, - ProviderEntity, -) -from graphon.model_runtime.model_providers.__base.ai_model import AIModel -from graphon.model_runtime.model_providers.model_provider_factory import ModelProviderFactory -from graphon.model_runtime.runtime import ModelRuntime from pydantic import BaseModel, ConfigDict, Field, PrivateAttr, model_validator from sqlalchemy import func, select from sqlalchemy.orm import Session @@ -34,6 +24,16 @@ from core.entities.provider_entities import ( from core.helper import encrypter from core.helper.model_provider_cache import ProviderCredentialsCache, ProviderCredentialsCacheType from core.plugin.impl.model_runtime_factory import create_plugin_model_provider_factory +from graphon.model_runtime.entities.model_entities import AIModelEntity, FetchFrom, ModelType +from graphon.model_runtime.entities.provider_entities import ( + ConfigurateMethod, + CredentialFormSchema, + FormType, + ProviderEntity, +) +from graphon.model_runtime.model_providers.__base.ai_model import AIModel +from graphon.model_runtime.model_providers.model_provider_factory import ModelProviderFactory +from graphon.model_runtime.runtime import ModelRuntime from libs.datetime_utils import naive_utc_now from models.engine import db from models.enums import CredentialSourceType diff --git a/api/core/entities/provider_entities.py b/api/core/entities/provider_entities.py index 95431c0e01..72b29c2277 100644 --- a/api/core/entities/provider_entities.py +++ b/api/core/entities/provider_entities.py @@ -3,7 +3,6 @@ from __future__ import annotations from enum import StrEnum, auto from typing import Any, Union -from graphon.model_runtime.entities.model_entities import ModelType from pydantic import BaseModel, ConfigDict, Field from core.entities.parameter_entities import ( @@ -13,6 +12,7 @@ from core.entities.parameter_entities import ( ToolSelectorScope, ) from core.tools.entities.common_entities import I18nObject +from graphon.model_runtime.entities.model_entities import ModelType class ProviderQuotaType(StrEnum): diff --git a/api/core/helper/code_executor/code_executor.py b/api/core/helper/code_executor/code_executor.py index 35bfcfb6a5..951e065b2c 100644 --- a/api/core/helper/code_executor/code_executor.py +++ b/api/core/helper/code_executor/code_executor.py @@ -4,7 +4,6 @@ from threading import Lock from typing import Any import httpx -from graphon.nodes.code.entities import CodeLanguage from pydantic import BaseModel from yarl import URL @@ -14,6 +13,7 @@ from core.helper.code_executor.jinja2.jinja2_transformer import Jinja2TemplateTr from core.helper.code_executor.python3.python3_transformer import Python3TemplateTransformer from core.helper.code_executor.template_transformer import TemplateTransformer from core.helper.http_client_pooling import get_pooled_http_client +from graphon.nodes.code.entities import CodeLanguage logger = logging.getLogger(__name__) code_execution_endpoint_url = URL(str(dify_config.CODE_EXECUTION_ENDPOINT)) diff --git a/api/core/helper/moderation.py b/api/core/helper/moderation.py index a1e782a094..dc37a36943 100644 --- a/api/core/helper/moderation.py +++ b/api/core/helper/moderation.py @@ -2,14 +2,13 @@ import logging import secrets from typing import cast -from graphon.model_runtime.entities.model_entities import ModelType -from graphon.model_runtime.errors.invoke import InvokeBadRequestError -from graphon.model_runtime.model_providers.__base.moderation_model import ModerationModel - from core.app.entities.app_invoke_entities import ModelConfigWithCredentialsEntity from core.entities import DEFAULT_PLUGIN_ID from core.plugin.impl.model_runtime_factory import create_plugin_model_provider_factory from extensions.ext_hosting_provider import hosting_configuration +from graphon.model_runtime.entities.model_entities import ModelType +from graphon.model_runtime.errors.invoke import InvokeBadRequestError +from graphon.model_runtime.model_providers.__base.moderation_model import ModerationModel from models.provider import ProviderType logger = logging.getLogger(__name__) diff --git a/api/core/hosting_configuration.py b/api/core/hosting_configuration.py index f8f56e12d2..8bcb899b23 100644 --- a/api/core/hosting_configuration.py +++ b/api/core/hosting_configuration.py @@ -1,12 +1,12 @@ from typing import Any from flask import Flask -from graphon.model_runtime.entities.model_entities import ModelType from pydantic import BaseModel from configs import dify_config from core.entities import DEFAULT_PLUGIN_ID from core.entities.provider_entities import ProviderQuotaType, QuotaUnit, RestrictModel +from graphon.model_runtime.entities.model_entities import ModelType class HostingQuota(BaseModel): diff --git a/api/core/indexing_runner.py b/api/core/indexing_runner.py index 8d0a8b99b4..b6e33396d1 100644 --- a/api/core/indexing_runner.py +++ b/api/core/indexing_runner.py @@ -9,7 +9,6 @@ from collections.abc import Mapping from typing import Any from flask import Flask, current_app -from graphon.model_runtime.entities.model_entities import ModelType from sqlalchemy import delete, func, select, update from sqlalchemy.orm.exc import ObjectDeletedError @@ -35,6 +34,7 @@ from core.tools.utils.web_reader_tool import get_image_upload_file_ids from extensions.ext_database import db from extensions.ext_redis import redis_client from extensions.ext_storage import storage +from graphon.model_runtime.entities.model_entities import ModelType from libs import helper from libs.datetime_utils import naive_utc_now from models import Account diff --git a/api/core/llm_generator/llm_generator.py b/api/core/llm_generator/llm_generator.py index c43c0274cd..348526b0ef 100644 --- a/api/core/llm_generator/llm_generator.py +++ b/api/core/llm_generator/llm_generator.py @@ -5,11 +5,6 @@ from collections.abc import Sequence from typing import Any, Protocol, TypedDict, cast import json_repair -from graphon.enums import WorkflowNodeExecutionMetadataKey -from graphon.model_runtime.entities.llm_entities import LLMResult -from graphon.model_runtime.entities.message_entities import PromptMessage, SystemPromptMessage, UserPromptMessage -from graphon.model_runtime.entities.model_entities import ModelType -from graphon.model_runtime.errors.invoke import InvokeAuthorizationError, InvokeError from sqlalchemy import select from core.app.app_config.entities import ModelConfig @@ -35,6 +30,11 @@ from core.ops.utils import measure_time from core.prompt.utils.prompt_template_parser import PromptTemplateParser from extensions.ext_database import db from extensions.ext_storage import storage +from graphon.enums import WorkflowNodeExecutionMetadataKey +from graphon.model_runtime.entities.llm_entities import LLMResult +from graphon.model_runtime.entities.message_entities import PromptMessage, SystemPromptMessage, UserPromptMessage +from graphon.model_runtime.entities.model_entities import ModelType +from graphon.model_runtime.errors.invoke import InvokeAuthorizationError, InvokeError from models import App, Message, WorkflowNodeExecutionModel from models.workflow import Workflow diff --git a/api/core/llm_generator/output_parser/structured_output.py b/api/core/llm_generator/output_parser/structured_output.py index a8ad7c9179..d2e375626f 100644 --- a/api/core/llm_generator/output_parser/structured_output.py +++ b/api/core/llm_generator/output_parser/structured_output.py @@ -5,6 +5,11 @@ from enum import StrEnum from typing import Any, Literal, cast, overload import json_repair +from pydantic import TypeAdapter, ValidationError + +from core.llm_generator.output_parser.errors import OutputParserError +from core.llm_generator.prompts import STRUCTURED_OUTPUT_PROMPT +from core.model_manager import ModelInstance from graphon.model_runtime.callbacks.base_callback import Callback from graphon.model_runtime.entities.llm_entities import ( LLMResult, @@ -21,11 +26,6 @@ from graphon.model_runtime.entities.message_entities import ( TextPromptMessageContent, ) from graphon.model_runtime.entities.model_entities import AIModelEntity, ParameterRule -from pydantic import TypeAdapter, ValidationError - -from core.llm_generator.output_parser.errors import OutputParserError -from core.llm_generator.prompts import STRUCTURED_OUTPUT_PROMPT -from core.model_manager import ModelInstance class ResponseFormat(StrEnum): diff --git a/api/core/mcp/server/streamable_http.py b/api/core/mcp/server/streamable_http.py index 72171d1536..884610ca82 100644 --- a/api/core/mcp/server/streamable_http.py +++ b/api/core/mcp/server/streamable_http.py @@ -3,12 +3,11 @@ import logging from collections.abc import Mapping from typing import Any, NotRequired, TypedDict, cast -from graphon.variables.input_entities import VariableEntity, VariableEntityType - from configs import dify_config from core.app.entities.app_invoke_entities import InvokeFrom from core.app.features.rate_limiting.rate_limit import RateLimitGenerator from core.mcp import types as mcp_types +from graphon.variables.input_entities import VariableEntity, VariableEntityType from models.model import App, AppMCPServer, AppMode, EndUser from services.app_generate_service import AppGenerateService diff --git a/api/core/mcp/utils.py b/api/core/mcp/utils.py index 7e35044176..7b5a7635f1 100644 --- a/api/core/mcp/utils.py +++ b/api/core/mcp/utils.py @@ -4,11 +4,11 @@ from contextlib import AbstractContextManager import httpx import httpx_sse -from graphon.model_runtime.utils.encoders import jsonable_encoder from httpx_sse import connect_sse from configs import dify_config from core.mcp.types import ErrorData, JSONRPCError +from graphon.model_runtime.utils.encoders import jsonable_encoder HTTP_REQUEST_NODE_SSL_VERIFY = dify_config.HTTP_REQUEST_NODE_SSL_VERIFY diff --git a/api/core/memory/token_buffer_memory.py b/api/core/memory/token_buffer_memory.py index 5809d6f74a..d840ee213c 100644 --- a/api/core/memory/token_buffer_memory.py +++ b/api/core/memory/token_buffer_memory.py @@ -1,5 +1,14 @@ from collections.abc import Sequence +from sqlalchemy import select +from sqlalchemy.orm import sessionmaker + +from core.app.app_config.features.file_upload.manager import FileUploadConfigManager +from core.app.file_access import DatabaseFileAccessController +from core.model_manager import ModelInstance +from core.prompt.utils.extract_thread_messages import extract_thread_messages +from extensions.ext_database import db +from factories import file_factory from graphon.file import file_manager from graphon.model_runtime.entities import ( AssistantPromptMessage, @@ -10,15 +19,6 @@ from graphon.model_runtime.entities import ( UserPromptMessage, ) from graphon.model_runtime.entities.message_entities import PromptMessageContentUnionTypes -from sqlalchemy import select -from sqlalchemy.orm import sessionmaker - -from core.app.app_config.features.file_upload.manager import FileUploadConfigManager -from core.app.file_access import DatabaseFileAccessController -from core.model_manager import ModelInstance -from core.prompt.utils.extract_thread_messages import extract_thread_messages -from extensions.ext_database import db -from factories import file_factory from models.model import AppMode, Conversation, Message, MessageFile from models.workflow import Workflow from repositories.api_workflow_run_repository import APIWorkflowRunRepository diff --git a/api/core/model_manager.py b/api/core/model_manager.py index 36beb55d7f..d8d8dfedd8 100644 --- a/api/core/model_manager.py +++ b/api/core/model_manager.py @@ -2,6 +2,15 @@ import logging from collections.abc import Callable, Generator, Iterable, Mapping, Sequence from typing import IO, Any, Literal, Optional, Union, cast, overload +from configs import dify_config +from core.entities import PluginCredentialType +from core.entities.embedding_type import EmbeddingInputType +from core.entities.provider_configuration import ProviderConfiguration, ProviderModelBundle +from core.entities.provider_entities import ModelLoadBalancingConfiguration +from core.errors.error import ProviderTokenNotInitError +from core.plugin.impl.model_runtime_factory import create_plugin_provider_manager +from core.provider_manager import ProviderManager +from extensions.ext_redis import redis_client from graphon.model_runtime.callbacks.base_callback import Callback from graphon.model_runtime.entities.llm_entities import LLMResult from graphon.model_runtime.entities.message_entities import PromptMessage, PromptMessageTool @@ -15,16 +24,6 @@ from graphon.model_runtime.model_providers.__base.rerank_model import RerankMode from graphon.model_runtime.model_providers.__base.speech2text_model import Speech2TextModel from graphon.model_runtime.model_providers.__base.text_embedding_model import TextEmbeddingModel from graphon.model_runtime.model_providers.__base.tts_model import TTSModel - -from configs import dify_config -from core.entities import PluginCredentialType -from core.entities.embedding_type import EmbeddingInputType -from core.entities.provider_configuration import ProviderConfiguration, ProviderModelBundle -from core.entities.provider_entities import ModelLoadBalancingConfiguration -from core.errors.error import ProviderTokenNotInitError -from core.plugin.impl.model_runtime_factory import create_plugin_provider_manager -from core.provider_manager import ProviderManager -from extensions.ext_redis import redis_client from models.provider import ProviderType logger = logging.getLogger(__name__) diff --git a/api/core/moderation/openai_moderation/openai_moderation.py b/api/core/moderation/openai_moderation/openai_moderation.py index 732803b332..6e6e94502c 100644 --- a/api/core/moderation/openai_moderation/openai_moderation.py +++ b/api/core/moderation/openai_moderation/openai_moderation.py @@ -1,9 +1,8 @@ from typing import Any -from graphon.model_runtime.entities.model_entities import ModelType - from core.model_manager import ModelManager from core.moderation.base import Moderation, ModerationAction, ModerationInputsResult, ModerationOutputsResult +from graphon.model_runtime.entities.model_entities import ModelType class OpenAIModeration(Moderation): diff --git a/api/core/ops/aliyun_trace/aliyun_trace.py b/api/core/ops/aliyun_trace/aliyun_trace.py index 70aaf2a07b..76e81242f4 100644 --- a/api/core/ops/aliyun_trace/aliyun_trace.py +++ b/api/core/ops/aliyun_trace/aliyun_trace.py @@ -1,8 +1,6 @@ import logging from collections.abc import Sequence -from graphon.entities import WorkflowNodeExecution -from graphon.enums import BuiltinNodeTypes, WorkflowNodeExecutionMetadataKey from opentelemetry.trace import SpanKind from sqlalchemy.orm import sessionmaker @@ -60,6 +58,8 @@ from core.ops.entities.trace_entity import ( ) from core.repositories import DifyCoreRepositoryFactory from extensions.ext_database import db +from graphon.entities import WorkflowNodeExecution +from graphon.enums import BuiltinNodeTypes, WorkflowNodeExecutionMetadataKey from models import WorkflowNodeExecutionTriggeredFrom logger = logging.getLogger(__name__) diff --git a/api/core/ops/aliyun_trace/utils.py b/api/core/ops/aliyun_trace/utils.py index aa35ac74c2..2e02a186cc 100644 --- a/api/core/ops/aliyun_trace/utils.py +++ b/api/core/ops/aliyun_trace/utils.py @@ -2,8 +2,6 @@ import json from collections.abc import Mapping from typing import Any, TypedDict -from graphon.entities import WorkflowNodeExecution -from graphon.enums import WorkflowNodeExecutionStatus from opentelemetry.trace import Link, Status, StatusCode from core.ops.aliyun_trace.entities.semconv import ( @@ -17,6 +15,8 @@ from core.ops.aliyun_trace.entities.semconv import ( ) from core.rag.models.document import Document from extensions.ext_database import db +from graphon.entities import WorkflowNodeExecution +from graphon.enums import WorkflowNodeExecutionStatus from models import EndUser # Constants diff --git a/api/core/ops/arize_phoenix_trace/arize_phoenix_trace.py b/api/core/ops/arize_phoenix_trace/arize_phoenix_trace.py index dd5edde630..78516e1a22 100644 --- a/api/core/ops/arize_phoenix_trace/arize_phoenix_trace.py +++ b/api/core/ops/arize_phoenix_trace/arize_phoenix_trace.py @@ -6,7 +6,6 @@ from datetime import datetime, timedelta from typing import Any, Union, cast from urllib.parse import urlparse -from graphon.enums import WorkflowNodeExecutionStatus from openinference.semconv.trace import ( MessageAttributes, OpenInferenceMimeTypeValues, @@ -41,6 +40,7 @@ from core.ops.entities.trace_entity import ( from core.ops.utils import JSON_DICT_ADAPTER from core.repositories import DifyCoreRepositoryFactory from extensions.ext_database import db +from graphon.enums import WorkflowNodeExecutionStatus from models.model import EndUser, MessageFile from models.workflow import WorkflowNodeExecutionTriggeredFrom diff --git a/api/core/ops/langfuse_trace/langfuse_trace.py b/api/core/ops/langfuse_trace/langfuse_trace.py index d53aa84aed..7eacc2be46 100644 --- a/api/core/ops/langfuse_trace/langfuse_trace.py +++ b/api/core/ops/langfuse_trace/langfuse_trace.py @@ -3,7 +3,6 @@ import os import uuid from datetime import UTC, datetime, timedelta -from graphon.enums import BuiltinNodeTypes from langfuse import Langfuse from langfuse.api import ( CreateGenerationBody, @@ -40,6 +39,7 @@ from core.ops.langfuse_trace.entities.langfuse_trace_entity import ( from core.ops.utils import filter_none_values from core.repositories import DifyCoreRepositoryFactory from extensions.ext_database import db +from graphon.enums import BuiltinNodeTypes from models import EndUser, WorkflowNodeExecutionTriggeredFrom from models.enums import MessageStatus diff --git a/api/core/ops/langsmith_trace/langsmith_trace.py b/api/core/ops/langsmith_trace/langsmith_trace.py index 490c64af84..d960038f15 100644 --- a/api/core/ops/langsmith_trace/langsmith_trace.py +++ b/api/core/ops/langsmith_trace/langsmith_trace.py @@ -4,7 +4,6 @@ import uuid from datetime import datetime, timedelta from typing import cast -from graphon.enums import BuiltinNodeTypes, WorkflowNodeExecutionMetadataKey from langsmith import Client from langsmith.schemas import RunBase from sqlalchemy.orm import sessionmaker @@ -30,6 +29,7 @@ from core.ops.langsmith_trace.entities.langsmith_trace_entity import ( from core.ops.utils import filter_none_values, generate_dotted_order from core.repositories import DifyCoreRepositoryFactory from extensions.ext_database import db +from graphon.enums import BuiltinNodeTypes, WorkflowNodeExecutionMetadataKey from models import EndUser, MessageFile, WorkflowNodeExecutionTriggeredFrom logger = logging.getLogger(__name__) diff --git a/api/core/ops/mlflow_trace/mlflow_trace.py b/api/core/ops/mlflow_trace/mlflow_trace.py index c070a937be..87fcaeabcc 100644 --- a/api/core/ops/mlflow_trace/mlflow_trace.py +++ b/api/core/ops/mlflow_trace/mlflow_trace.py @@ -4,7 +4,6 @@ from datetime import datetime, timedelta from typing import Any, cast import mlflow -from graphon.enums import BuiltinNodeTypes from mlflow.entities import Document, Span, SpanEvent, SpanStatusCode, SpanType from mlflow.tracing.constant import SpanAttributeKey, TokenUsageKey, TraceMetadataKey from mlflow.tracing.fluent import start_span_no_context, update_current_trace @@ -26,6 +25,7 @@ from core.ops.entities.trace_entity import ( ) from core.ops.utils import JSON_DICT_ADAPTER from extensions.ext_database import db +from graphon.enums import BuiltinNodeTypes from models import EndUser from models.workflow import WorkflowNodeExecutionModel diff --git a/api/core/ops/opik_trace/opik_trace.py b/api/core/ops/opik_trace/opik_trace.py index e0c7b9bfe5..672efe45bd 100644 --- a/api/core/ops/opik_trace/opik_trace.py +++ b/api/core/ops/opik_trace/opik_trace.py @@ -5,7 +5,6 @@ import uuid from datetime import datetime, timedelta from typing import Any, cast -from graphon.enums import BuiltinNodeTypes, WorkflowNodeExecutionMetadataKey from opik import Opik, Trace from opik.id_helpers import uuid4_to_uuid7 from sqlalchemy.orm import sessionmaker @@ -25,6 +24,7 @@ from core.ops.entities.trace_entity import ( ) from core.repositories import DifyCoreRepositoryFactory from extensions.ext_database import db +from graphon.enums import BuiltinNodeTypes, WorkflowNodeExecutionMetadataKey from models import EndUser, MessageFile, WorkflowNodeExecutionTriggeredFrom logger = logging.getLogger(__name__) diff --git a/api/core/ops/tencent_trace/span_builder.py b/api/core/ops/tencent_trace/span_builder.py index f79095d966..36878dc58f 100644 --- a/api/core/ops/tencent_trace/span_builder.py +++ b/api/core/ops/tencent_trace/span_builder.py @@ -6,8 +6,6 @@ import json import logging from datetime import datetime -from graphon.entities import WorkflowNodeExecution -from graphon.enums import WorkflowNodeExecutionMetadataKey, WorkflowNodeExecutionStatus from opentelemetry.trace import Status, StatusCode from core.ops.entities.trace_entity import ( @@ -43,6 +41,8 @@ from core.ops.tencent_trace.entities.semconv import ( from core.ops.tencent_trace.entities.tencent_trace_entity import SpanData from core.ops.tencent_trace.utils import TencentTraceUtils from core.rag.models.document import Document +from graphon.entities import WorkflowNodeExecution +from graphon.enums import WorkflowNodeExecutionMetadataKey, WorkflowNodeExecutionStatus logger = logging.getLogger(__name__) diff --git a/api/core/ops/tencent_trace/tencent_trace.py b/api/core/ops/tencent_trace/tencent_trace.py index 84f54d8a5a..d681b9da80 100644 --- a/api/core/ops/tencent_trace/tencent_trace.py +++ b/api/core/ops/tencent_trace/tencent_trace.py @@ -4,10 +4,6 @@ Tencent APM tracing implementation with separated concerns import logging -from graphon.entities.workflow_node_execution import ( - WorkflowNodeExecution, -) -from graphon.nodes import BuiltinNodeTypes from sqlalchemy import select from sqlalchemy.orm import Session, sessionmaker @@ -29,6 +25,10 @@ from core.ops.tencent_trace.span_builder import TencentSpanBuilder from core.ops.tencent_trace.utils import TencentTraceUtils from core.repositories import SQLAlchemyWorkflowNodeExecutionRepository from extensions.ext_database import db +from graphon.entities.workflow_node_execution import ( + WorkflowNodeExecution, +) +from graphon.nodes import BuiltinNodeTypes from models import Account, App, TenantAccountJoin, WorkflowNodeExecutionTriggeredFrom logger = logging.getLogger(__name__) diff --git a/api/core/ops/weave_trace/weave_trace.py b/api/core/ops/weave_trace/weave_trace.py index 8d9ba4694d..f79544f1c7 100644 --- a/api/core/ops/weave_trace/weave_trace.py +++ b/api/core/ops/weave_trace/weave_trace.py @@ -6,7 +6,6 @@ from typing import Any, cast import wandb import weave -from graphon.enums import BuiltinNodeTypes, WorkflowNodeExecutionMetadataKey from sqlalchemy.orm import sessionmaker from weave.trace_server.trace_server_interface import ( CallEndReq, @@ -33,6 +32,7 @@ from core.ops.entities.trace_entity import ( from core.ops.weave_trace.entities.weave_trace_entity import WeaveTraceModel from core.repositories import DifyCoreRepositoryFactory from extensions.ext_database import db +from graphon.enums import BuiltinNodeTypes, WorkflowNodeExecutionMetadataKey from models import EndUser, MessageFile, WorkflowNodeExecutionTriggeredFrom logger = logging.getLogger(__name__) diff --git a/api/core/plugin/backwards_invocation/model.py b/api/core/plugin/backwards_invocation/model.py index a4b24ff849..c92438960a 100644 --- a/api/core/plugin/backwards_invocation/model.py +++ b/api/core/plugin/backwards_invocation/model.py @@ -3,20 +3,6 @@ from binascii import hexlify, unhexlify from collections.abc import Generator from typing import Any -from graphon.model_runtime.entities.llm_entities import ( - LLMResult, - LLMResultChunk, - LLMResultChunkDelta, - LLMResultChunkWithStructuredOutput, - LLMResultWithStructuredOutput, -) -from graphon.model_runtime.entities.message_entities import ( - PromptMessage, - SystemPromptMessage, - UserPromptMessage, -) -from graphon.model_runtime.entities.model_entities import ModelType - from core.app.llm import deduct_llm_quota from core.llm_generator.output_parser.structured_output import invoke_llm_with_structured_output from core.model_manager import ModelManager @@ -33,6 +19,19 @@ from core.plugin.entities.request import ( ) from core.tools.entities.tool_entities import ToolProviderType from core.tools.utils.model_invocation_utils import ModelInvocationUtils +from graphon.model_runtime.entities.llm_entities import ( + LLMResult, + LLMResultChunk, + LLMResultChunkDelta, + LLMResultChunkWithStructuredOutput, + LLMResultWithStructuredOutput, +) +from graphon.model_runtime.entities.message_entities import ( + PromptMessage, + SystemPromptMessage, + UserPromptMessage, +) +from graphon.model_runtime.entities.model_entities import ModelType from models.account import Tenant diff --git a/api/core/plugin/backwards_invocation/node.py b/api/core/plugin/backwards_invocation/node.py index 9478997494..9550e49992 100644 --- a/api/core/plugin/backwards_invocation/node.py +++ b/api/core/plugin/backwards_invocation/node.py @@ -1,3 +1,4 @@ +from core.plugin.backwards_invocation.base import BaseBackwardsInvocation from graphon.enums import BuiltinNodeTypes from graphon.nodes.llm.entities import ModelConfig as LLMModelConfig from graphon.nodes.parameter_extractor.entities import ( @@ -8,8 +9,6 @@ from graphon.nodes.question_classifier.entities import ( ClassConfig, QuestionClassifierNodeData, ) - -from core.plugin.backwards_invocation.base import BaseBackwardsInvocation from services.workflow_service import WorkflowService diff --git a/api/core/plugin/entities/marketplace.py b/api/core/plugin/entities/marketplace.py index fd2094228a..03398873e3 100644 --- a/api/core/plugin/entities/marketplace.py +++ b/api/core/plugin/entities/marketplace.py @@ -1,12 +1,12 @@ from typing import Any -from graphon.model_runtime.entities.provider_entities import ProviderEntity from pydantic import BaseModel, Field, computed_field, model_validator from core.plugin.entities.endpoint import EndpointProviderDeclaration from core.plugin.entities.plugin import PluginResourceRequirements from core.tools.entities.common_entities import I18nObject from core.tools.entities.tool_entities import ToolProviderEntity +from graphon.model_runtime.entities.provider_entities import ProviderEntity class MarketplacePluginDeclaration(BaseModel): diff --git a/api/core/plugin/entities/plugin.py b/api/core/plugin/entities/plugin.py index 4d28032a57..89e0e8881c 100644 --- a/api/core/plugin/entities/plugin.py +++ b/api/core/plugin/entities/plugin.py @@ -3,7 +3,6 @@ from collections.abc import Mapping from enum import StrEnum, auto from typing import Any -from graphon.model_runtime.entities.provider_entities import ProviderEntity from packaging.version import InvalidVersion, Version from pydantic import BaseModel, Field, field_validator, model_validator @@ -14,6 +13,7 @@ from core.plugin.entities.endpoint import EndpointProviderDeclaration from core.tools.entities.common_entities import I18nObject from core.tools.entities.tool_entities import ToolProviderEntity from core.trigger.entities.entities import TriggerProviderEntity +from graphon.model_runtime.entities.provider_entities import ProviderEntity class PluginInstallationSource(StrEnum): diff --git a/api/core/plugin/entities/plugin_daemon.py b/api/core/plugin/entities/plugin_daemon.py index e0ddb746c7..257638ad77 100644 --- a/api/core/plugin/entities/plugin_daemon.py +++ b/api/core/plugin/entities/plugin_daemon.py @@ -6,8 +6,6 @@ from datetime import datetime from enum import StrEnum from typing import Any -from graphon.model_runtime.entities.model_entities import AIModelEntity -from graphon.model_runtime.entities.provider_entities import ProviderEntity from pydantic import BaseModel, ConfigDict, Field from core.agent.plugin_entities import AgentProviderEntityWithPlugin @@ -18,6 +16,8 @@ from core.plugin.entities.plugin import PluginDeclaration, PluginEntity from core.tools.entities.common_entities import I18nObject from core.tools.entities.tool_entities import ToolProviderEntityWithPlugin from core.trigger.entities.entities import TriggerProviderEntity +from graphon.model_runtime.entities.model_entities import AIModelEntity +from graphon.model_runtime.entities.provider_entities import ProviderEntity class PluginDaemonBasicResponse[T: BaseModel | dict | list | bool | str](BaseModel): diff --git a/api/core/plugin/entities/request.py b/api/core/plugin/entities/request.py index 4a85952dcd..1474883204 100644 --- a/api/core/plugin/entities/request.py +++ b/api/core/plugin/entities/request.py @@ -4,6 +4,10 @@ from collections.abc import Mapping from typing import Any, Literal from flask import Response +from pydantic import BaseModel, ConfigDict, Field, field_validator + +from core.entities.provider_entities import BasicProviderConfig +from core.plugin.utils.http_parser import deserialize_response from graphon.model_runtime.entities.message_entities import ( AssistantPromptMessage, PromptMessage, @@ -21,10 +25,6 @@ from graphon.nodes.parameter_extractor.entities import ( from graphon.nodes.question_classifier.entities import ( ClassConfig, ) -from pydantic import BaseModel, ConfigDict, Field, field_validator - -from core.entities.provider_entities import BasicProviderConfig -from core.plugin.utils.http_parser import deserialize_response class InvokeCredentials(BaseModel): diff --git a/api/core/plugin/impl/base.py b/api/core/plugin/impl/base.py index 7f36560b49..9ee8469892 100644 --- a/api/core/plugin/impl/base.py +++ b/api/core/plugin/impl/base.py @@ -5,14 +5,6 @@ from collections.abc import Callable, Generator from typing import Any, cast import httpx -from graphon.model_runtime.errors.invoke import ( - InvokeAuthorizationError, - InvokeBadRequestError, - InvokeConnectionError, - InvokeRateLimitError, - InvokeServerUnavailableError, -) -from graphon.model_runtime.errors.validate import CredentialsValidateFailedError from pydantic import BaseModel from yarl import URL @@ -37,6 +29,14 @@ from core.trigger.errors import ( TriggerPluginInvokeError, TriggerProviderCredentialValidationError, ) +from graphon.model_runtime.errors.invoke import ( + InvokeAuthorizationError, + InvokeBadRequestError, + InvokeConnectionError, + InvokeRateLimitError, + InvokeServerUnavailableError, +) +from graphon.model_runtime.errors.validate import CredentialsValidateFailedError plugin_daemon_inner_api_baseurl = URL(str(dify_config.PLUGIN_DAEMON_URL)) _plugin_daemon_timeout_config = cast( diff --git a/api/core/plugin/impl/model.py b/api/core/plugin/impl/model.py index 703af63f7c..47608bdfa6 100644 --- a/api/core/plugin/impl/model.py +++ b/api/core/plugin/impl/model.py @@ -2,13 +2,6 @@ import binascii from collections.abc import Generator, Sequence from typing import IO, Any -from graphon.model_runtime.entities.llm_entities import LLMResultChunk -from graphon.model_runtime.entities.message_entities import PromptMessage, PromptMessageTool -from graphon.model_runtime.entities.model_entities import AIModelEntity -from graphon.model_runtime.entities.rerank_entities import MultimodalRerankInput, RerankResult -from graphon.model_runtime.entities.text_embedding_entities import EmbeddingResult -from graphon.model_runtime.utils.encoders import jsonable_encoder - from core.plugin.entities.plugin_daemon import ( PluginBasicBooleanResponse, PluginDaemonInnerError, @@ -20,6 +13,12 @@ from core.plugin.entities.plugin_daemon import ( PluginVoicesResponse, ) from core.plugin.impl.base import BasePluginClient +from graphon.model_runtime.entities.llm_entities import LLMResultChunk +from graphon.model_runtime.entities.message_entities import PromptMessage, PromptMessageTool +from graphon.model_runtime.entities.model_entities import AIModelEntity +from graphon.model_runtime.entities.rerank_entities import MultimodalRerankInput, RerankResult +from graphon.model_runtime.entities.text_embedding_entities import EmbeddingResult +from graphon.model_runtime.utils.encoders import jsonable_encoder class PluginModelClient(BasePluginClient): diff --git a/api/core/plugin/impl/model_runtime.py b/api/core/plugin/impl/model_runtime.py index 22c846b6de..e3fba4ef3a 100644 --- a/api/core/plugin/impl/model_runtime.py +++ b/api/core/plugin/impl/model_runtime.py @@ -6,13 +6,6 @@ from collections.abc import Generator, Iterable, Sequence from threading import Lock from typing import IO, Any, Union -from graphon.model_runtime.entities.llm_entities import LLMResult, LLMResultChunk -from graphon.model_runtime.entities.message_entities import PromptMessage, PromptMessageTool -from graphon.model_runtime.entities.model_entities import AIModelEntity, ModelType -from graphon.model_runtime.entities.provider_entities import ProviderEntity -from graphon.model_runtime.entities.rerank_entities import MultimodalRerankInput, RerankResult -from graphon.model_runtime.entities.text_embedding_entities import EmbeddingInputType, EmbeddingResult -from graphon.model_runtime.runtime import ModelRuntime from pydantic import ValidationError from redis import RedisError @@ -21,6 +14,13 @@ from core.plugin.entities.plugin_daemon import PluginModelProviderEntity from core.plugin.impl.asset import PluginAssetManager from core.plugin.impl.model import PluginModelClient from extensions.ext_redis import redis_client +from graphon.model_runtime.entities.llm_entities import LLMResult, LLMResultChunk +from graphon.model_runtime.entities.message_entities import PromptMessage, PromptMessageTool +from graphon.model_runtime.entities.model_entities import AIModelEntity, ModelType +from graphon.model_runtime.entities.provider_entities import ProviderEntity +from graphon.model_runtime.entities.rerank_entities import MultimodalRerankInput, RerankResult +from graphon.model_runtime.entities.text_embedding_entities import EmbeddingInputType, EmbeddingResult +from graphon.model_runtime.runtime import ModelRuntime from models.provider_ids import ModelProviderID logger = logging.getLogger(__name__) diff --git a/api/core/plugin/impl/model_runtime_factory.py b/api/core/plugin/impl/model_runtime_factory.py index 4b29a6fc56..35abd2ae8c 100644 --- a/api/core/plugin/impl/model_runtime_factory.py +++ b/api/core/plugin/impl/model_runtime_factory.py @@ -2,9 +2,8 @@ from __future__ import annotations from typing import TYPE_CHECKING -from graphon.model_runtime.model_providers.model_provider_factory import ModelProviderFactory - from core.plugin.impl.model import PluginModelClient +from graphon.model_runtime.model_providers.model_provider_factory import ModelProviderFactory if TYPE_CHECKING: from core.model_manager import ModelManager diff --git a/api/core/plugin/utils/converter.py b/api/core/plugin/utils/converter.py index 90350f8400..12d8e282b2 100644 --- a/api/core/plugin/utils/converter.py +++ b/api/core/plugin/utils/converter.py @@ -1,8 +1,7 @@ from typing import Any -from graphon.file import File - from core.tools.entities.tool_entities import ToolSelector +from graphon.file import File def convert_parameters_to_plugin_format(parameters: dict[str, Any]) -> dict[str, Any]: diff --git a/api/core/prompt/advanced_prompt_transform.py b/api/core/prompt/advanced_prompt_transform.py index 19b5e9223a..24e05ef865 100644 --- a/api/core/prompt/advanced_prompt_transform.py +++ b/api/core/prompt/advanced_prompt_transform.py @@ -1,6 +1,13 @@ from collections.abc import Mapping, Sequence from typing import cast +from core.app.entities.app_invoke_entities import ModelConfigWithCredentialsEntity +from core.helper.code_executor.jinja2.jinja2_formatter import Jinja2Formatter +from core.memory.token_buffer_memory import TokenBufferMemory +from core.model_manager import ModelInstance +from core.prompt.entities.advanced_prompt_entities import ChatModelMessage, CompletionModelPromptTemplate, MemoryConfig +from core.prompt.prompt_transform import PromptTransform +from core.prompt.utils.prompt_template_parser import PromptTemplateParser from graphon.file import File, file_manager from graphon.model_runtime.entities import ( AssistantPromptMessage, @@ -13,14 +20,6 @@ from graphon.model_runtime.entities import ( from graphon.model_runtime.entities.message_entities import ImagePromptMessageContent, PromptMessageContentUnionTypes from graphon.runtime import VariablePool -from core.app.entities.app_invoke_entities import ModelConfigWithCredentialsEntity -from core.helper.code_executor.jinja2.jinja2_formatter import Jinja2Formatter -from core.memory.token_buffer_memory import TokenBufferMemory -from core.model_manager import ModelInstance -from core.prompt.entities.advanced_prompt_entities import ChatModelMessage, CompletionModelPromptTemplate, MemoryConfig -from core.prompt.prompt_transform import PromptTransform -from core.prompt.utils.prompt_template_parser import PromptTemplateParser - class AdvancedPromptTransform(PromptTransform): """ diff --git a/api/core/prompt/agent_history_prompt_transform.py b/api/core/prompt/agent_history_prompt_transform.py index 9be70199b7..8f1d51f08a 100644 --- a/api/core/prompt/agent_history_prompt_transform.py +++ b/api/core/prompt/agent_history_prompt_transform.py @@ -1,17 +1,16 @@ from typing import cast -from graphon.model_runtime.entities.message_entities import ( - PromptMessage, - SystemPromptMessage, - UserPromptMessage, -) -from graphon.model_runtime.model_providers.__base.large_language_model import LargeLanguageModel - from core.app.entities.app_invoke_entities import ( ModelConfigWithCredentialsEntity, ) from core.memory.token_buffer_memory import TokenBufferMemory from core.prompt.prompt_transform import PromptTransform +from graphon.model_runtime.entities.message_entities import ( + PromptMessage, + SystemPromptMessage, + UserPromptMessage, +) +from graphon.model_runtime.model_providers.__base.large_language_model import LargeLanguageModel class AgentHistoryPromptTransform(PromptTransform): diff --git a/api/core/prompt/prompt_transform.py b/api/core/prompt/prompt_transform.py index 4539ae9f11..6ff2f44cdc 100644 --- a/api/core/prompt/prompt_transform.py +++ b/api/core/prompt/prompt_transform.py @@ -1,12 +1,11 @@ from typing import Any -from graphon.model_runtime.entities.message_entities import PromptMessage -from graphon.model_runtime.entities.model_entities import AIModelEntity, ModelPropertyKey - from core.app.entities.app_invoke_entities import ModelConfigWithCredentialsEntity from core.memory.token_buffer_memory import TokenBufferMemory from core.model_manager import ModelInstance from core.prompt.entities.advanced_prompt_entities import MemoryConfig +from graphon.model_runtime.entities.message_entities import PromptMessage +from graphon.model_runtime.entities.model_entities import AIModelEntity, ModelPropertyKey class PromptTransform: diff --git a/api/core/prompt/simple_prompt_transform.py b/api/core/prompt/simple_prompt_transform.py index dc8391a6a5..1665bdeb52 100644 --- a/api/core/prompt/simple_prompt_transform.py +++ b/api/core/prompt/simple_prompt_transform.py @@ -4,6 +4,12 @@ from collections.abc import Mapping, Sequence from enum import StrEnum, auto from typing import TYPE_CHECKING, Any, TypedDict, cast +from core.app.app_config.entities import PromptTemplateEntity +from core.app.entities.app_invoke_entities import ModelConfigWithCredentialsEntity +from core.memory.token_buffer_memory import TokenBufferMemory +from core.prompt.entities.advanced_prompt_entities import MemoryConfig +from core.prompt.prompt_transform import PromptTransform +from core.prompt.utils.prompt_template_parser import PromptTemplateParser from graphon.file import file_manager from graphon.model_runtime.entities.message_entities import ( ImagePromptMessageContent, @@ -13,13 +19,6 @@ from graphon.model_runtime.entities.message_entities import ( TextPromptMessageContent, UserPromptMessage, ) - -from core.app.app_config.entities import PromptTemplateEntity -from core.app.entities.app_invoke_entities import ModelConfigWithCredentialsEntity -from core.memory.token_buffer_memory import TokenBufferMemory -from core.prompt.entities.advanced_prompt_entities import MemoryConfig -from core.prompt.prompt_transform import PromptTransform -from core.prompt.utils.prompt_template_parser import PromptTemplateParser from models.model import AppMode if TYPE_CHECKING: diff --git a/api/core/prompt/utils/prompt_message_util.py b/api/core/prompt/utils/prompt_message_util.py index dbda749925..ba76eb0c4e 100644 --- a/api/core/prompt/utils/prompt_message_util.py +++ b/api/core/prompt/utils/prompt_message_util.py @@ -1,6 +1,7 @@ from collections.abc import Sequence from typing import Any, cast +from core.prompt.simple_prompt_transform import ModelMode from graphon.model_runtime.entities import ( AssistantPromptMessage, AudioPromptMessageContent, @@ -11,8 +12,6 @@ from graphon.model_runtime.entities import ( TextPromptMessageContent, ) -from core.prompt.simple_prompt_transform import ModelMode - class PromptMessageUtil: @staticmethod diff --git a/api/core/provider_manager.py b/api/core/provider_manager.py index 39ef31632e..c3bbe8fc09 100644 --- a/api/core/provider_manager.py +++ b/api/core/provider_manager.py @@ -6,14 +6,6 @@ from collections.abc import Sequence from json import JSONDecodeError from typing import TYPE_CHECKING, Any -from graphon.model_runtime.entities.model_entities import ModelType -from graphon.model_runtime.entities.provider_entities import ( - ConfigurateMethod, - CredentialFormSchema, - FormType, - ProviderEntity, -) -from graphon.model_runtime.model_providers.model_provider_factory import ModelProviderFactory from pydantic import TypeAdapter from sqlalchemy import select from sqlalchemy.exc import IntegrityError @@ -41,6 +33,14 @@ from core.helper.position_helper import is_filtered from extensions import ext_hosting_provider from extensions.ext_database import db from extensions.ext_redis import redis_client +from graphon.model_runtime.entities.model_entities import ModelType +from graphon.model_runtime.entities.provider_entities import ( + ConfigurateMethod, + CredentialFormSchema, + FormType, + ProviderEntity, +) +from graphon.model_runtime.model_providers.model_provider_factory import ModelProviderFactory from models.provider import ( LoadBalancingModelConfig, Provider, diff --git a/api/core/rag/data_post_processor/data_post_processor.py b/api/core/rag/data_post_processor/data_post_processor.py index 9ce91f52ff..ca530748ed 100644 --- a/api/core/rag/data_post_processor/data_post_processor.py +++ b/api/core/rag/data_post_processor/data_post_processor.py @@ -1,8 +1,5 @@ from typing import TypedDict -from graphon.model_runtime.entities.model_entities import ModelType -from graphon.model_runtime.errors.invoke import InvokeAuthorizationError - from core.model_manager import ModelInstance, ModelManager from core.rag.data_post_processor.reorder import ReorderRunner from core.rag.index_processor.constant.query_type import QueryType @@ -11,6 +8,8 @@ from core.rag.rerank.entity.weight import KeywordSetting, VectorSetting, Weights from core.rag.rerank.rerank_base import BaseRerankRunner from core.rag.rerank.rerank_factory import RerankRunnerFactory from core.rag.rerank.rerank_type import RerankMode +from graphon.model_runtime.entities.model_entities import ModelType +from graphon.model_runtime.errors.invoke import InvokeAuthorizationError class RerankingModelDict(TypedDict): diff --git a/api/core/rag/datasource/retrieval_service.py b/api/core/rag/datasource/retrieval_service.py index fa242a730e..7e71d67ec0 100644 --- a/api/core/rag/datasource/retrieval_service.py +++ b/api/core/rag/datasource/retrieval_service.py @@ -4,7 +4,6 @@ from concurrent.futures import ThreadPoolExecutor from typing import Any, NotRequired, TypedDict from flask import Flask, current_app -from graphon.model_runtime.entities.model_entities import ModelType from sqlalchemy import select from sqlalchemy.orm import Session, load_only @@ -24,6 +23,7 @@ from core.rag.rerank.rerank_type import RerankMode from core.rag.retrieval.retrieval_methods import RetrievalMethod from core.tools.signature import sign_upload_file from extensions.ext_database import db +from graphon.model_runtime.entities.model_entities import ModelType from models.dataset import ( ChildChunk, Dataset, diff --git a/api/core/rag/datasource/vdb/vector_factory.py b/api/core/rag/datasource/vdb/vector_factory.py index dddd5fc994..59d7f3c3c4 100644 --- a/api/core/rag/datasource/vdb/vector_factory.py +++ b/api/core/rag/datasource/vdb/vector_factory.py @@ -4,7 +4,6 @@ import time from abc import ABC, abstractmethod from typing import Any -from graphon.model_runtime.entities.model_entities import ModelType from sqlalchemy import select from configs import dify_config @@ -19,6 +18,7 @@ from core.rag.models.document import Document from extensions.ext_database import db from extensions.ext_redis import redis_client from extensions.ext_storage import storage +from graphon.model_runtime.entities.model_entities import ModelType from models.dataset import Dataset, Whitelist from models.model import UploadFile diff --git a/api/core/rag/docstore/dataset_docstore.py b/api/core/rag/docstore/dataset_docstore.py index 8e9ebdd17a..f4699f6869 100644 --- a/api/core/rag/docstore/dataset_docstore.py +++ b/api/core/rag/docstore/dataset_docstore.py @@ -3,13 +3,13 @@ from __future__ import annotations from collections.abc import Sequence from typing import Any -from graphon.model_runtime.entities.model_entities import ModelType from sqlalchemy import delete, func, select from core.model_manager import ModelManager from core.rag.index_processor.constant.index_type import IndexTechniqueType from core.rag.models.document import AttachmentDocument, Document from extensions.ext_database import db +from graphon.model_runtime.entities.model_entities import ModelType from models.dataset import ChildChunk, Dataset, DocumentSegment, SegmentAttachmentBinding diff --git a/api/core/rag/embedding/cached_embedding.py b/api/core/rag/embedding/cached_embedding.py index 9f1c73ec88..4926f44f16 100644 --- a/api/core/rag/embedding/cached_embedding.py +++ b/api/core/rag/embedding/cached_embedding.py @@ -4,8 +4,6 @@ import pickle from typing import Any, cast import numpy as np -from graphon.model_runtime.entities.model_entities import ModelPropertyKey -from graphon.model_runtime.model_providers.__base.text_embedding_model import TextEmbeddingModel from sqlalchemy import select from sqlalchemy.exc import IntegrityError @@ -15,6 +13,8 @@ from core.model_manager import ModelInstance from core.rag.embedding.embedding_base import Embeddings from extensions.ext_database import db from extensions.ext_redis import redis_client +from graphon.model_runtime.entities.model_entities import ModelPropertyKey +from graphon.model_runtime.model_providers.__base.text_embedding_model import TextEmbeddingModel from libs import helper from models.dataset import Embedding diff --git a/api/core/rag/index_processor/processor/paragraph_index_processor.py b/api/core/rag/index_processor/processor/paragraph_index_processor.py index a487c49053..f8242efe31 100644 --- a/api/core/rag/index_processor/processor/paragraph_index_processor.py +++ b/api/core/rag/index_processor/processor/paragraph_index_processor.py @@ -7,16 +7,6 @@ from typing import Any, TypedDict, cast logger = logging.getLogger(__name__) -from graphon.file import File, FileTransferMethod, FileType, file_manager -from graphon.model_runtime.entities.llm_entities import LLMResult, LLMUsage -from graphon.model_runtime.entities.message_entities import ( - ImagePromptMessageContent, - PromptMessage, - PromptMessageContentUnionTypes, - TextPromptMessageContent, - UserPromptMessage, -) -from graphon.model_runtime.entities.model_entities import ModelFeature, ModelType from sqlalchemy import select from core.app.file_access import DatabaseFileAccessController @@ -43,6 +33,16 @@ from core.tools.utils.text_processing_utils import remove_leading_symbols from core.workflow.file_reference import build_file_reference from extensions.ext_database import db from factories.file_factory import build_from_mapping +from graphon.file import File, FileTransferMethod, FileType, file_manager +from graphon.model_runtime.entities.llm_entities import LLMResult, LLMUsage +from graphon.model_runtime.entities.message_entities import ( + ImagePromptMessageContent, + PromptMessage, + PromptMessageContentUnionTypes, + TextPromptMessageContent, + UserPromptMessage, +) +from graphon.model_runtime.entities.model_entities import ModelFeature, ModelType from libs import helper from models import UploadFile from models.account import Account diff --git a/api/core/rag/models/document.py b/api/core/rag/models/document.py index 087736d0b0..4ebf095904 100644 --- a/api/core/rag/models/document.py +++ b/api/core/rag/models/document.py @@ -2,9 +2,10 @@ from abc import ABC, abstractmethod from collections.abc import Sequence from typing import Any -from graphon.file import File from pydantic import BaseModel, Field +from graphon.file import File + class ChildDocument(BaseModel): """Class for storing a piece of text and associated metadata.""" diff --git a/api/core/rag/rerank/rerank_model.py b/api/core/rag/rerank/rerank_model.py index a8d37845a5..bce08f998f 100644 --- a/api/core/rag/rerank/rerank_model.py +++ b/api/core/rag/rerank/rerank_model.py @@ -1,8 +1,5 @@ import base64 -from graphon.model_runtime.entities.model_entities import ModelType -from graphon.model_runtime.entities.rerank_entities import MultimodalRerankInput, RerankResult - from core.model_manager import ModelInstance, ModelManager from core.rag.index_processor.constant.doc_type import DocType from core.rag.index_processor.constant.query_type import QueryType @@ -10,6 +7,8 @@ from core.rag.models.document import Document from core.rag.rerank.rerank_base import BaseRerankRunner from extensions.ext_database import db from extensions.ext_storage import storage +from graphon.model_runtime.entities.model_entities import ModelType +from graphon.model_runtime.entities.rerank_entities import MultimodalRerankInput, RerankResult from models.model import UploadFile diff --git a/api/core/rag/rerank/weight_rerank.py b/api/core/rag/rerank/weight_rerank.py index 49123e13d0..d0732b269a 100644 --- a/api/core/rag/rerank/weight_rerank.py +++ b/api/core/rag/rerank/weight_rerank.py @@ -2,7 +2,6 @@ import math from collections import Counter import numpy as np -from graphon.model_runtime.entities.model_entities import ModelType from core.model_manager import ModelManager from core.rag.datasource.keyword.jieba.jieba_keyword_table_handler import JiebaKeywordTableHandler @@ -12,6 +11,7 @@ from core.rag.index_processor.constant.query_type import QueryType from core.rag.models.document import Document from core.rag.rerank.entity.weight import VectorSetting, Weights from core.rag.rerank.rerank_base import BaseRerankRunner +from graphon.model_runtime.entities.model_entities import ModelType class WeightRerankRunner(BaseRerankRunner): diff --git a/api/core/rag/retrieval/dataset_retrieval.py b/api/core/rag/retrieval/dataset_retrieval.py index 8ebc840b99..1453fe020b 100644 --- a/api/core/rag/retrieval/dataset_retrieval.py +++ b/api/core/rag/retrieval/dataset_retrieval.py @@ -9,11 +9,6 @@ from collections.abc import Generator, Mapping from typing import Any, Union, cast from flask import Flask, current_app -from graphon.file import File, FileTransferMethod, FileType -from graphon.model_runtime.entities.llm_entities import LLMMode, LLMResult, LLMUsage -from graphon.model_runtime.entities.message_entities import PromptMessage, PromptMessageRole, PromptMessageTool -from graphon.model_runtime.entities.model_entities import ModelFeature, ModelType -from graphon.model_runtime.model_providers.__base.large_language_model import LargeLanguageModel from sqlalchemy import and_, func, literal, or_, select, update from sqlalchemy.orm import sessionmaker @@ -69,6 +64,11 @@ from core.workflow.nodes.knowledge_retrieval.retrieval import ( ) from extensions.ext_database import db from extensions.ext_redis import redis_client +from graphon.file import File, FileTransferMethod, FileType +from graphon.model_runtime.entities.llm_entities import LLMMode, LLMResult, LLMUsage +from graphon.model_runtime.entities.message_entities import PromptMessage, PromptMessageRole, PromptMessageTool +from graphon.model_runtime.entities.model_entities import ModelFeature, ModelType +from graphon.model_runtime.model_providers.__base.large_language_model import LargeLanguageModel from libs.helper import parse_uuid_str_or_none from libs.json_in_md_parser import parse_and_check_json_markdown from models import UploadFile diff --git a/api/core/rag/retrieval/router/multi_dataset_function_call_router.py b/api/core/rag/retrieval/router/multi_dataset_function_call_router.py index dce7b6226c..e617a9660e 100644 --- a/api/core/rag/retrieval/router/multi_dataset_function_call_router.py +++ b/api/core/rag/retrieval/router/multi_dataset_function_call_router.py @@ -1,10 +1,9 @@ from typing import Union -from graphon.model_runtime.entities.llm_entities import LLMResult, LLMUsage -from graphon.model_runtime.entities.message_entities import PromptMessageTool, SystemPromptMessage, UserPromptMessage - from core.app.entities.app_invoke_entities import ModelConfigWithCredentialsEntity from core.model_manager import ModelInstance +from graphon.model_runtime.entities.llm_entities import LLMResult, LLMUsage +from graphon.model_runtime.entities.message_entities import PromptMessageTool, SystemPromptMessage, UserPromptMessage class FunctionCallMultiDatasetRouter: diff --git a/api/core/rag/retrieval/router/multi_dataset_react_route.py b/api/core/rag/retrieval/router/multi_dataset_react_route.py index 9b223075d8..21a9d04f7f 100644 --- a/api/core/rag/retrieval/router/multi_dataset_react_route.py +++ b/api/core/rag/retrieval/router/multi_dataset_react_route.py @@ -1,10 +1,6 @@ from collections.abc import Generator, Sequence from typing import Any, Union -from graphon.model_runtime.entities.llm_entities import LLMResult, LLMUsage -from graphon.model_runtime.entities.message_entities import PromptMessage, PromptMessageRole, PromptMessageTool -from graphon.model_runtime.entities.model_entities import ModelType - from core.app.entities.app_invoke_entities import ModelConfigWithCredentialsEntity from core.app.llm import deduct_llm_quota from core.model_manager import ModelInstance, ModelManager @@ -12,6 +8,9 @@ from core.prompt.advanced_prompt_transform import AdvancedPromptTransform from core.prompt.entities.advanced_prompt_entities import ChatModelMessage, CompletionModelPromptTemplate from core.rag.retrieval.output_parser.react_output import ReactAction from core.rag.retrieval.output_parser.structured_chat import StructuredChatOutputParser +from graphon.model_runtime.entities.llm_entities import LLMResult, LLMUsage +from graphon.model_runtime.entities.message_entities import PromptMessage, PromptMessageRole, PromptMessageTool +from graphon.model_runtime.entities.model_entities import ModelType PREFIX = """Respond to the human as helpfully and accurately as possible. You have access to the following tools:""" diff --git a/api/core/rag/splitter/fixed_text_splitter.py b/api/core/rag/splitter/fixed_text_splitter.py index 3383c7f3bd..2581c354dd 100644 --- a/api/core/rag/splitter/fixed_text_splitter.py +++ b/api/core/rag/splitter/fixed_text_splitter.py @@ -7,10 +7,9 @@ import re from collections.abc import Collection from typing import Any, Literal -from graphon.model_runtime.model_providers.__base.tokenizers.gpt2_tokenizer import GPT2Tokenizer - from core.model_manager import ModelInstance from core.rag.splitter.text_splitter import RecursiveCharacterTextSplitter +from graphon.model_runtime.model_providers.__base.tokenizers.gpt2_tokenizer import GPT2Tokenizer class EnhanceRecursiveCharacterTextSplitter(RecursiveCharacterTextSplitter): diff --git a/api/core/repositories/celery_workflow_execution_repository.py b/api/core/repositories/celery_workflow_execution_repository.py index b07c63fdf0..e87d1cd6b2 100644 --- a/api/core/repositories/celery_workflow_execution_repository.py +++ b/api/core/repositories/celery_workflow_execution_repository.py @@ -7,11 +7,11 @@ providing improved performance by offloading database operations to background w import logging -from graphon.entities import WorkflowExecution from sqlalchemy.engine import Engine from sqlalchemy.orm import sessionmaker from core.repositories.factory import WorkflowExecutionRepository +from graphon.entities import WorkflowExecution from libs.helper import extract_tenant_id from models import Account, CreatorUserRole, EndUser from models.enums import WorkflowRunTriggeredFrom diff --git a/api/core/repositories/celery_workflow_node_execution_repository.py b/api/core/repositories/celery_workflow_node_execution_repository.py index cdb3af01a8..2451563317 100644 --- a/api/core/repositories/celery_workflow_node_execution_repository.py +++ b/api/core/repositories/celery_workflow_node_execution_repository.py @@ -8,7 +8,6 @@ providing improved performance by offloading database operations to background w import logging from collections.abc import Sequence -from graphon.entities import WorkflowNodeExecution from sqlalchemy.engine import Engine from sqlalchemy.orm import sessionmaker @@ -16,6 +15,7 @@ from core.repositories.factory import ( OrderConfig, WorkflowNodeExecutionRepository, ) +from graphon.entities import WorkflowNodeExecution from libs.helper import extract_tenant_id from models import Account, CreatorUserRole, EndUser from models.workflow import WorkflowNodeExecutionTriggeredFrom diff --git a/api/core/repositories/factory.py b/api/core/repositories/factory.py index ce3ad15759..4e83e70799 100644 --- a/api/core/repositories/factory.py +++ b/api/core/repositories/factory.py @@ -9,11 +9,11 @@ from collections.abc import Sequence from dataclasses import dataclass from typing import Literal, Protocol -from graphon.entities import WorkflowExecution, WorkflowNodeExecution from sqlalchemy.engine import Engine from sqlalchemy.orm import sessionmaker from configs import dify_config +from graphon.entities import WorkflowExecution, WorkflowNodeExecution from libs.module_loading import import_string from models import Account, EndUser from models.enums import WorkflowRunTriggeredFrom diff --git a/api/core/repositories/human_input_repository.py b/api/core/repositories/human_input_repository.py index 72d9394149..02625e242f 100644 --- a/api/core/repositories/human_input_repository.py +++ b/api/core/repositories/human_input_repository.py @@ -4,8 +4,6 @@ from collections.abc import Mapping, Sequence from datetime import datetime from typing import Any, Protocol -from graphon.nodes.human_input.entities import FormDefinition, HumanInputNodeData -from graphon.nodes.human_input.enums import HumanInputFormKind, HumanInputFormStatus from sqlalchemy import select from sqlalchemy.orm import Session, selectinload @@ -19,6 +17,8 @@ from core.workflow.human_input_compat import ( InteractiveSurfaceDeliveryMethod, is_human_input_webapp_enabled, ) +from graphon.nodes.human_input.entities import FormDefinition, HumanInputNodeData +from graphon.nodes.human_input.enums import HumanInputFormKind, HumanInputFormStatus from libs.datetime_utils import naive_utc_now from libs.uuid_utils import uuidv7 from models.account import Account, TenantAccountJoin diff --git a/api/core/repositories/sqlalchemy_workflow_execution_repository.py b/api/core/repositories/sqlalchemy_workflow_execution_repository.py index d74cc8f231..6be3902317 100644 --- a/api/core/repositories/sqlalchemy_workflow_execution_repository.py +++ b/api/core/repositories/sqlalchemy_workflow_execution_repository.py @@ -5,13 +5,13 @@ SQLAlchemy implementation of the WorkflowExecutionRepository. import json import logging -from graphon.entities import WorkflowExecution -from graphon.enums import WorkflowExecutionStatus, WorkflowType -from graphon.workflow_type_encoder import WorkflowRuntimeTypeConverter from sqlalchemy.engine import Engine from sqlalchemy.orm import sessionmaker from core.repositories.factory import WorkflowExecutionRepository +from graphon.entities import WorkflowExecution +from graphon.enums import WorkflowExecutionStatus, WorkflowType +from graphon.workflow_type_encoder import WorkflowRuntimeTypeConverter from libs.helper import extract_tenant_id from models import ( Account, diff --git a/api/core/repositories/sqlalchemy_workflow_node_execution_repository.py b/api/core/repositories/sqlalchemy_workflow_node_execution_repository.py index 13e885672a..b036687bc9 100644 --- a/api/core/repositories/sqlalchemy_workflow_node_execution_repository.py +++ b/api/core/repositories/sqlalchemy_workflow_node_execution_repository.py @@ -10,10 +10,6 @@ from concurrent.futures import ThreadPoolExecutor from typing import Any import psycopg2.errors -from graphon.entities import WorkflowNodeExecution -from graphon.enums import WorkflowNodeExecutionMetadataKey, WorkflowNodeExecutionStatus -from graphon.model_runtime.utils.encoders import jsonable_encoder -from graphon.workflow_type_encoder import WorkflowRuntimeTypeConverter from sqlalchemy import UnaryExpression, asc, desc, select from sqlalchemy.engine import Engine from sqlalchemy.exc import IntegrityError @@ -23,6 +19,10 @@ from tenacity import before_sleep_log, retry, retry_if_exception, stop_after_att from configs import dify_config from core.repositories.factory import OrderConfig, WorkflowNodeExecutionRepository from extensions.ext_storage import storage +from graphon.entities import WorkflowNodeExecution +from graphon.enums import WorkflowNodeExecutionMetadataKey, WorkflowNodeExecutionStatus +from graphon.model_runtime.utils.encoders import jsonable_encoder +from graphon.workflow_type_encoder import WorkflowRuntimeTypeConverter from libs.helper import extract_tenant_id from libs.uuid_utils import uuidv7 from models import ( diff --git a/api/core/tools/builtin_tool/providers/audio/tools/asr.py b/api/core/tools/builtin_tool/providers/audio/tools/asr.py index e539074303..95660ab93b 100644 --- a/api/core/tools/builtin_tool/providers/audio/tools/asr.py +++ b/api/core/tools/builtin_tool/providers/audio/tools/asr.py @@ -2,15 +2,14 @@ import io from collections.abc import Generator from typing import Any -from graphon.file import FileType -from graphon.file.file_manager import download -from graphon.model_runtime.entities.model_entities import ModelType - from core.model_manager import ModelManager from core.plugin.entities.parameters import PluginParameterOption from core.tools.builtin_tool.tool import BuiltinTool from core.tools.entities.common_entities import I18nObject from core.tools.entities.tool_entities import ToolInvokeMessage, ToolParameter +from graphon.file import FileType +from graphon.file.file_manager import download +from graphon.model_runtime.entities.model_entities import ModelType from services.model_provider_service import ModelProviderService diff --git a/api/core/tools/builtin_tool/providers/audio/tools/tts.py b/api/core/tools/builtin_tool/providers/audio/tools/tts.py index f49c669fe0..ac3820f1ab 100644 --- a/api/core/tools/builtin_tool/providers/audio/tools/tts.py +++ b/api/core/tools/builtin_tool/providers/audio/tools/tts.py @@ -2,13 +2,12 @@ import io from collections.abc import Generator from typing import Any -from graphon.model_runtime.entities.model_entities import ModelPropertyKey, ModelType - from core.model_manager import ModelManager from core.plugin.entities.parameters import PluginParameterOption from core.tools.builtin_tool.tool import BuiltinTool from core.tools.entities.common_entities import I18nObject from core.tools.entities.tool_entities import ToolInvokeMessage, ToolParameter +from graphon.model_runtime.entities.model_entities import ModelPropertyKey, ModelType from services.model_provider_service import ModelProviderService diff --git a/api/core/tools/builtin_tool/tool.py b/api/core/tools/builtin_tool/tool.py index 14af63a962..d41503e1e6 100644 --- a/api/core/tools/builtin_tool/tool.py +++ b/api/core/tools/builtin_tool/tool.py @@ -1,12 +1,11 @@ from __future__ import annotations -from graphon.model_runtime.entities.llm_entities import LLMResult -from graphon.model_runtime.entities.message_entities import PromptMessage, SystemPromptMessage, UserPromptMessage - from core.tools.__base.tool import Tool from core.tools.__base.tool_runtime import ToolRuntime from core.tools.entities.tool_entities import ToolProviderType from core.tools.utils.model_invocation_utils import ModelInvocationUtils +from graphon.model_runtime.entities.llm_entities import LLMResult +from graphon.model_runtime.entities.message_entities import PromptMessage, SystemPromptMessage, UserPromptMessage _SUMMARY_PROMPT = """You are a professional language researcher, you are interested in the language and you can quickly aimed at the main point of an webpage and reproduce it in your own words but diff --git a/api/core/tools/custom_tool/tool.py b/api/core/tools/custom_tool/tool.py index 0a2c37c563..168e5f4493 100644 --- a/api/core/tools/custom_tool/tool.py +++ b/api/core/tools/custom_tool/tool.py @@ -6,7 +6,6 @@ from typing import Any, Union from urllib.parse import urlencode import httpx -from graphon.file.file_manager import download from core.helper import ssrf_proxy from core.tools.__base.tool import Tool @@ -14,6 +13,7 @@ from core.tools.__base.tool_runtime import ToolRuntime from core.tools.entities.tool_bundle import ApiToolBundle from core.tools.entities.tool_entities import ToolEntity, ToolInvokeMessage, ToolProviderType from core.tools.errors import ToolInvokeError, ToolParameterValidationError, ToolProviderCredentialValidationError +from graphon.file.file_manager import download API_TOOL_DEFAULT_TIMEOUT = ( int(getenv("API_TOOL_DEFAULT_CONNECT_TIMEOUT", "10")), diff --git a/api/core/tools/entities/api_entities.py b/api/core/tools/entities/api_entities.py index 410ec72baf..42a88c0003 100644 --- a/api/core/tools/entities/api_entities.py +++ b/api/core/tools/entities/api_entities.py @@ -2,7 +2,6 @@ from collections.abc import Mapping from datetime import datetime from typing import Any, Literal -from graphon.model_runtime.utils.encoders import jsonable_encoder from pydantic import BaseModel, Field, field_validator from core.entities.mcp_provider import MCPAuthentication, MCPConfiguration @@ -10,6 +9,7 @@ from core.plugin.entities.plugin_daemon import CredentialType from core.tools.__base.tool import ToolParameter from core.tools.entities.common_entities import I18nObject from core.tools.entities.tool_entities import ToolProviderType +from graphon.model_runtime.utils.encoders import jsonable_encoder class ToolApiEntity(BaseModel): diff --git a/api/core/tools/mcp_tool/tool.py b/api/core/tools/mcp_tool/tool.py index f6d09472b3..00fc8a8282 100644 --- a/api/core/tools/mcp_tool/tool.py +++ b/api/core/tools/mcp_tool/tool.py @@ -6,8 +6,6 @@ import logging from collections.abc import Generator, Mapping from typing import Any, cast -from graphon.model_runtime.entities.llm_entities import LLMUsage, LLMUsageMetadata - from core.mcp.auth_client import MCPClientWithAuthRetry from core.mcp.error import MCPConnectionError from core.mcp.types import ( @@ -23,6 +21,7 @@ from core.tools.__base.tool import Tool from core.tools.__base.tool_runtime import ToolRuntime from core.tools.entities.tool_entities import ToolEntity, ToolInvokeMessage, ToolProviderType from core.tools.errors import ToolInvokeError +from graphon.model_runtime.entities.llm_entities import LLMUsage, LLMUsageMetadata logger = logging.getLogger(__name__) diff --git a/api/core/tools/tool_engine.py b/api/core/tools/tool_engine.py index d060fa8b49..3caacb8706 100644 --- a/api/core/tools/tool_engine.py +++ b/api/core/tools/tool_engine.py @@ -7,7 +7,6 @@ from datetime import UTC, datetime from mimetypes import guess_type from typing import Any, Union, cast -from graphon.file import FileTransferMethod, FileType from yarl import URL from core.app.entities.app_invoke_entities import InvokeFrom @@ -33,6 +32,7 @@ from core.tools.errors import ( from core.tools.utils.message_transformer import ToolFileMessageTransformer, safe_json_value from core.tools.workflow_as_tool.tool import WorkflowTool from extensions.ext_database import db +from graphon.file import FileTransferMethod, FileType from models.enums import CreatorUserRole, MessageFileBelongsTo from models.model import Message, MessageFile diff --git a/api/core/tools/tool_file_manager.py b/api/core/tools/tool_file_manager.py index d8674b3af9..b3424cd9a5 100644 --- a/api/core/tools/tool_file_manager.py +++ b/api/core/tools/tool_file_manager.py @@ -9,7 +9,6 @@ from mimetypes import guess_extension, guess_type from uuid import uuid4 import httpx -from graphon.file import File, FileTransferMethod, get_file_type_by_mime_type from sqlalchemy import select from configs import dify_config @@ -17,6 +16,7 @@ from core.db.session_factory import session_factory from core.helper import ssrf_proxy from core.workflow.file_reference import build_file_reference from extensions.ext_storage import storage +from graphon.file import File, FileTransferMethod, get_file_type_by_mime_type from models.model import MessageFile from models.tools import ToolFile diff --git a/api/core/tools/tool_manager.py b/api/core/tools/tool_manager.py index be13d40f3e..f4588904d3 100644 --- a/api/core/tools/tool_manager.py +++ b/api/core/tools/tool_manager.py @@ -8,7 +8,6 @@ from threading import Lock from typing import TYPE_CHECKING, Any, Literal, Protocol, cast import sqlalchemy as sa -from graphon.runtime import VariablePool from pydantic import TypeAdapter from sqlalchemy import select from sqlalchemy.orm import Session @@ -29,14 +28,13 @@ from core.tools.plugin_tool.tool import PluginTool from core.tools.utils.uuid_utils import is_valid_uuid from core.tools.workflow_as_tool.provider import WorkflowToolProviderController from extensions.ext_database import db +from graphon.runtime import VariablePool from models.provider_ids import ToolProviderID from services.tools.mcp_tools_manage_service import MCPToolManageService if TYPE_CHECKING: pass -from graphon.model_runtime.utils.encoders import jsonable_encoder - from core.agent.entities import AgentToolEntity from core.app.entities.app_invoke_entities import InvokeFrom from core.helper.module_import_helper import load_single_subclass_from_source @@ -62,6 +60,7 @@ from core.tools.tool_label_manager import ToolLabelManager from core.tools.utils.configuration import ToolParameterConfigurationManager from core.tools.utils.encryption import create_provider_encrypter, create_tool_provider_encrypter from core.tools.workflow_as_tool.tool import WorkflowTool +from graphon.model_runtime.utils.encoders import jsonable_encoder from models.tools import ApiToolProvider, BuiltinToolProvider, WorkflowToolProvider from services.tools.tools_transform_service import ToolTransformService diff --git a/api/core/tools/utils/dataset_retriever/dataset_multi_retriever_tool.py b/api/core/tools/utils/dataset_retriever/dataset_multi_retriever_tool.py index 03e3c5918d..b6890b2611 100644 --- a/api/core/tools/utils/dataset_retriever/dataset_multi_retriever_tool.py +++ b/api/core/tools/utils/dataset_retriever/dataset_multi_retriever_tool.py @@ -1,7 +1,6 @@ import threading from flask import Flask, current_app -from graphon.model_runtime.entities.model_entities import ModelType from pydantic import BaseModel, Field from sqlalchemy import select @@ -15,6 +14,7 @@ from core.rag.rerank.rerank_model import RerankModelRunner from core.rag.retrieval.retrieval_methods import RetrievalMethod from core.tools.utils.dataset_retriever.dataset_retriever_base_tool import DatasetRetrieverBaseTool from extensions.ext_database import db +from graphon.model_runtime.entities.model_entities import ModelType from models.dataset import Dataset, Document, DocumentSegment default_retrieval_model: DefaultRetrievalModelDict = { diff --git a/api/core/tools/utils/message_transformer.py b/api/core/tools/utils/message_transformer.py index 81c85bc90d..79d0c114d4 100644 --- a/api/core/tools/utils/message_transformer.py +++ b/api/core/tools/utils/message_transformer.py @@ -9,11 +9,11 @@ from uuid import UUID import numpy as np import pytz -from graphon.file import File, FileTransferMethod, FileType from core.tools.entities.tool_entities import ToolInvokeMessage from core.tools.tool_file_manager import ToolFileManager from core.workflow.file_reference import parse_file_reference +from graphon.file import File, FileTransferMethod, FileType from libs.login import current_user from models import Account diff --git a/api/core/tools/utils/model_invocation_utils.py b/api/core/tools/utils/model_invocation_utils.py index 8d6f83dc07..9e1d41cb39 100644 --- a/api/core/tools/utils/model_invocation_utils.py +++ b/api/core/tools/utils/model_invocation_utils.py @@ -8,6 +8,9 @@ import json from decimal import Decimal from typing import cast +from core.model_manager import ModelManager +from core.tools.entities.tool_entities import ToolProviderType +from extensions.ext_database import db from graphon.model_runtime.entities.llm_entities import LLMResult from graphon.model_runtime.entities.message_entities import PromptMessage from graphon.model_runtime.entities.model_entities import ModelPropertyKey, ModelType @@ -20,10 +23,6 @@ from graphon.model_runtime.errors.invoke import ( ) from graphon.model_runtime.model_providers.__base.large_language_model import LargeLanguageModel from graphon.model_runtime.utils.encoders import jsonable_encoder - -from core.model_manager import ModelManager -from core.tools.entities.tool_entities import ToolProviderType -from extensions.ext_database import db from models.tools import ToolModelInvoke diff --git a/api/core/tools/utils/workflow_configuration_sync.py b/api/core/tools/utils/workflow_configuration_sync.py index 2159eb8638..45718cadb6 100644 --- a/api/core/tools/utils/workflow_configuration_sync.py +++ b/api/core/tools/utils/workflow_configuration_sync.py @@ -1,13 +1,12 @@ from collections.abc import Mapping, Sequence from typing import Any +from core.tools.entities.tool_entities import WorkflowToolParameterConfiguration +from core.tools.errors import WorkflowToolHumanInputNotSupportedError from graphon.enums import BuiltinNodeTypes from graphon.nodes.base.entities import OutputVariableEntity from graphon.variables.input_entities import VariableEntity -from core.tools.entities.tool_entities import WorkflowToolParameterConfiguration -from core.tools.errors import WorkflowToolHumanInputNotSupportedError - class WorkflowToolConfigurationUtils: @classmethod diff --git a/api/core/tools/workflow_as_tool/provider.py b/api/core/tools/workflow_as_tool/provider.py index a01004448a..5905fd919e 100644 --- a/api/core/tools/workflow_as_tool/provider.py +++ b/api/core/tools/workflow_as_tool/provider.py @@ -2,7 +2,6 @@ from __future__ import annotations from collections.abc import Mapping -from graphon.variables.input_entities import VariableEntity, VariableEntityType from pydantic import Field from sqlalchemy import select from sqlalchemy.orm import Session @@ -25,6 +24,7 @@ from core.tools.entities.tool_entities import ( from core.tools.utils.workflow_configuration_sync import WorkflowToolConfigurationUtils from core.tools.workflow_as_tool.tool import WorkflowTool from extensions.ext_database import db +from graphon.variables.input_entities import VariableEntity, VariableEntityType from models.account import Account from models.model import App, AppMode from models.tools import WorkflowToolProvider diff --git a/api/core/tools/workflow_as_tool/tool.py b/api/core/tools/workflow_as_tool/tool.py index 7c4f8ee03a..52ab605963 100644 --- a/api/core/tools/workflow_as_tool/tool.py +++ b/api/core/tools/workflow_as_tool/tool.py @@ -5,8 +5,6 @@ import logging from collections.abc import Generator, Mapping, Sequence from typing import Any, cast -from graphon.file import FILE_MODEL_IDENTITY, File, FileTransferMethod -from graphon.model_runtime.entities.llm_entities import LLMUsage, LLMUsageMetadata from sqlalchemy import select from core.app.file_access import DatabaseFileAccessController @@ -22,6 +20,8 @@ from core.tools.entities.tool_entities import ( from core.tools.errors import ToolInvokeError from core.workflow.file_reference import resolve_file_record_id from factories.file_factory import build_from_mapping +from graphon.file import FILE_MODEL_IDENTITY, File, FileTransferMethod +from graphon.model_runtime.entities.llm_entities import LLMUsage, LLMUsageMetadata from models import Account, Tenant from models.model import App, EndUser from models.utils.file_input_compat import build_file_from_stored_mapping diff --git a/api/core/trigger/debug/event_selectors.py b/api/core/trigger/debug/event_selectors.py index 61d1cd8540..24c1271488 100644 --- a/api/core/trigger/debug/event_selectors.py +++ b/api/core/trigger/debug/event_selectors.py @@ -8,7 +8,6 @@ from collections.abc import Mapping from datetime import datetime from typing import Any -from graphon.entities.graph_config import NodeConfigDict from pydantic import BaseModel from core.plugin.entities.request import TriggerInvokeEventResponse @@ -28,6 +27,7 @@ from core.trigger.debug.events import ( from core.workflow.nodes.trigger_plugin.entities import TriggerEventNodeData from core.workflow.nodes.trigger_schedule.entities import ScheduleConfig from extensions.ext_redis import redis_client +from graphon.entities.graph_config import NodeConfigDict from libs.datetime_utils import ensure_naive_utc, naive_utc_now from libs.schedule_utils import calculate_next_run_at from models.model import App diff --git a/api/core/workflow/human_input_compat.py b/api/core/workflow/human_input_compat.py index c95516a240..75a0a0c202 100644 --- a/api/core/workflow/human_input_compat.py +++ b/api/core/workflow/human_input_compat.py @@ -14,12 +14,13 @@ from typing import Annotated, Any, ClassVar, Literal import bleach import markdown +from markdown.extensions.tables import TableExtension +from pydantic import AliasChoices, BaseModel, ConfigDict, Field, TypeAdapter + from graphon.enums import BuiltinNodeTypes from graphon.nodes.base.variable_template_parser import VariableTemplateParser from graphon.runtime import VariablePool from graphon.variables.consts import SELECTORS_LENGTH -from markdown.extensions.tables import TableExtension -from pydantic import AliasChoices, BaseModel, ConfigDict, Field, TypeAdapter class DeliveryMethodType(enum.StrEnum): diff --git a/api/core/workflow/node_factory.py b/api/core/workflow/node_factory.py index b04ac7da3d..351da3444f 100644 --- a/api/core/workflow/node_factory.py +++ b/api/core/workflow/node_factory.py @@ -5,22 +5,6 @@ from dataclasses import dataclass from functools import lru_cache from typing import TYPE_CHECKING, Any, cast, final, override -from graphon.entities.base_node_data import BaseNodeData -from graphon.entities.graph_config import NodeConfigDict, NodeConfigDictAdapter -from graphon.enums import BuiltinNodeTypes, NodeType -from graphon.file.file_manager import file_manager -from graphon.graph.graph import NodeFactory -from graphon.model_runtime.memory import PromptMessageMemory -from graphon.model_runtime.model_providers.__base.large_language_model import LargeLanguageModel -from graphon.nodes.base.node import Node -from graphon.nodes.code.code_node import WorkflowCodeExecutor -from graphon.nodes.code.entities import CodeLanguage -from graphon.nodes.code.limits import CodeNodeLimits -from graphon.nodes.document_extractor import UnstructuredApiConfig -from graphon.nodes.http_request import build_http_request_config -from graphon.nodes.llm.entities import LLMNodeData -from graphon.nodes.parameter_extractor.entities import ParameterExtractorNodeData -from graphon.nodes.question_classifier.entities import QuestionClassifierNodeData from sqlalchemy import select from sqlalchemy.orm import Session @@ -56,6 +40,22 @@ from core.workflow.nodes.agent.runtime_support import AgentRuntimeSupport from core.workflow.system_variables import SystemVariableKey, get_system_text, system_variable_selector from core.workflow.template_rendering import CodeExecutorJinja2TemplateRenderer from extensions.ext_database import db +from graphon.entities.base_node_data import BaseNodeData +from graphon.entities.graph_config import NodeConfigDict, NodeConfigDictAdapter +from graphon.enums import BuiltinNodeTypes, NodeType +from graphon.file.file_manager import file_manager +from graphon.graph.graph import NodeFactory +from graphon.model_runtime.memory import PromptMessageMemory +from graphon.model_runtime.model_providers.__base.large_language_model import LargeLanguageModel +from graphon.nodes.base.node import Node +from graphon.nodes.code.code_node import WorkflowCodeExecutor +from graphon.nodes.code.entities import CodeLanguage +from graphon.nodes.code.limits import CodeNodeLimits +from graphon.nodes.document_extractor import UnstructuredApiConfig +from graphon.nodes.http_request import build_http_request_config +from graphon.nodes.llm.entities import LLMNodeData +from graphon.nodes.parameter_extractor.entities import ParameterExtractorNodeData +from graphon.nodes.question_classifier.entities import QuestionClassifierNodeData from models.model import Conversation if TYPE_CHECKING: diff --git a/api/core/workflow/node_runtime.py b/api/core/workflow/node_runtime.py index 19cb3a7b0a..2e632e56f0 100644 --- a/api/core/workflow/node_runtime.py +++ b/api/core/workflow/node_runtime.py @@ -4,6 +4,32 @@ from collections.abc import Callable, Generator, Mapping, Sequence from dataclasses import dataclass from typing import TYPE_CHECKING, Any, cast +from sqlalchemy import select +from sqlalchemy.orm import Session + +from core.app.entities.app_invoke_entities import DIFY_RUN_CONTEXT_KEY, DifyRunContext +from core.app.file_access import DatabaseFileAccessController +from core.callback_handler.workflow_tool_callback_handler import DifyWorkflowCallbackHandler +from core.llm_generator.output_parser.errors import OutputParserError +from core.llm_generator.output_parser.structured_output import invoke_llm_with_structured_output +from core.model_manager import ModelInstance +from core.plugin.impl.exc import PluginDaemonClientSideError, PluginInvokeError +from core.plugin.impl.plugin import PluginInstaller +from core.prompt.utils.prompt_message_util import PromptMessageUtil +from core.repositories.human_input_repository import ( + FormCreateParams, + HumanInputFormRepository, + HumanInputFormRepositoryImpl, +) +from core.tools.entities.tool_entities import ToolProviderType as CoreToolProviderType +from core.tools.errors import ToolInvokeError +from core.tools.tool_engine import ToolEngine +from core.tools.tool_file_manager import ToolFileManager +from core.tools.tool_manager import ToolManager +from core.tools.utils.message_transformer import ToolFileMessageTransformer +from core.workflow.file_reference import build_file_reference +from extensions.ext_database import db +from factories import file_factory from graphon.file import FileTransferMethod, FileType from graphon.model_runtime.entities import LLMMode from graphon.model_runtime.entities.llm_entities import ( @@ -34,32 +60,6 @@ from graphon.nodes.tool_runtime_entities import ( ToolRuntimeMessage, ToolRuntimeParameter, ) -from sqlalchemy import select -from sqlalchemy.orm import Session - -from core.app.entities.app_invoke_entities import DIFY_RUN_CONTEXT_KEY, DifyRunContext -from core.app.file_access import DatabaseFileAccessController -from core.callback_handler.workflow_tool_callback_handler import DifyWorkflowCallbackHandler -from core.llm_generator.output_parser.errors import OutputParserError -from core.llm_generator.output_parser.structured_output import invoke_llm_with_structured_output -from core.model_manager import ModelInstance -from core.plugin.impl.exc import PluginDaemonClientSideError, PluginInvokeError -from core.plugin.impl.plugin import PluginInstaller -from core.prompt.utils.prompt_message_util import PromptMessageUtil -from core.repositories.human_input_repository import ( - FormCreateParams, - HumanInputFormRepository, - HumanInputFormRepositoryImpl, -) -from core.tools.entities.tool_entities import ToolProviderType as CoreToolProviderType -from core.tools.errors import ToolInvokeError -from core.tools.tool_engine import ToolEngine -from core.tools.tool_file_manager import ToolFileManager -from core.tools.tool_manager import ToolManager -from core.tools.utils.message_transformer import ToolFileMessageTransformer -from core.workflow.file_reference import build_file_reference -from extensions.ext_database import db -from factories import file_factory from models.dataset import SegmentAttachmentBinding from models.model import UploadFile from services.tools.builtin_tools_manage_service import BuiltinToolManageService @@ -76,13 +76,12 @@ from .human_input_compat import ( from .system_variables import SystemVariableKey, get_system_text if TYPE_CHECKING: + from core.tools.__base.tool import Tool + from core.tools.entities.tool_entities import ToolInvokeMessage as CoreToolInvokeMessage from graphon.file import File from graphon.nodes.llm.file_saver import LLMFileSaver from graphon.nodes.tool.entities import ToolNodeData - from core.tools.__base.tool import Tool - from core.tools.entities.tool_entities import ToolInvokeMessage as CoreToolInvokeMessage - _file_access_controller = DatabaseFileAccessController() diff --git a/api/core/workflow/nodes/agent/agent_node.py b/api/core/workflow/nodes/agent/agent_node.py index bfd5536e4a..7b000101b0 100644 --- a/api/core/workflow/nodes/agent/agent_node.py +++ b/api/core/workflow/nodes/agent/agent_node.py @@ -3,15 +3,14 @@ from __future__ import annotations from collections.abc import Generator, Mapping, Sequence from typing import TYPE_CHECKING, Any +from core.app.entities.app_invoke_entities import DIFY_RUN_CONTEXT_KEY, DifyRunContext +from core.workflow.system_variables import SystemVariableKey, get_system_text from graphon.entities.graph_config import NodeConfigDict from graphon.enums import BuiltinNodeTypes, WorkflowNodeExecutionStatus from graphon.node_events import NodeEventBase, NodeRunResult, StreamCompletedEvent from graphon.nodes.base.node import Node from graphon.nodes.base.variable_template_parser import VariableTemplateParser -from core.app.entities.app_invoke_entities import DIFY_RUN_CONTEXT_KEY, DifyRunContext -from core.workflow.system_variables import SystemVariableKey, get_system_text - from .entities import AgentNodeData from .exceptions import ( AgentInvocationError, diff --git a/api/core/workflow/nodes/agent/entities.py b/api/core/workflow/nodes/agent/entities.py index c52aad150b..51452c29a3 100644 --- a/api/core/workflow/nodes/agent/entities.py +++ b/api/core/workflow/nodes/agent/entities.py @@ -1,12 +1,12 @@ from enum import IntEnum, StrEnum, auto from typing import Any, Literal, Union -from graphon.entities.base_node_data import BaseNodeData -from graphon.enums import BuiltinNodeTypes, NodeType from pydantic import BaseModel from core.prompt.entities.advanced_prompt_entities import MemoryConfig from core.tools.entities.tool_entities import ToolSelector +from graphon.entities.base_node_data import BaseNodeData +from graphon.enums import BuiltinNodeTypes, NodeType class AgentNodeData(BaseNodeData): diff --git a/api/core/workflow/nodes/agent/message_transformer.py b/api/core/workflow/nodes/agent/message_transformer.py index db74590ed7..f44681377d 100644 --- a/api/core/workflow/nodes/agent/message_transformer.py +++ b/api/core/workflow/nodes/agent/message_transformer.py @@ -3,6 +3,14 @@ from __future__ import annotations from collections.abc import Generator, Mapping from typing import Any, cast +from sqlalchemy import select +from sqlalchemy.orm import Session + +from core.app.file_access import DatabaseFileAccessController +from core.tools.entities.tool_entities import ToolInvokeMessage +from core.tools.utils.message_transformer import ToolFileMessageTransformer +from extensions.ext_database import db +from factories import file_factory from graphon.enums import BuiltinNodeTypes, NodeType, WorkflowNodeExecutionMetadataKey, WorkflowNodeExecutionStatus from graphon.file import File, FileTransferMethod, get_file_type_by_mime_type from graphon.model_runtime.entities.llm_entities import LLMUsage, LLMUsageMetadata @@ -15,14 +23,6 @@ from graphon.node_events import ( StreamCompletedEvent, ) from graphon.variables.segments import ArrayFileSegment -from sqlalchemy import select -from sqlalchemy.orm import Session - -from core.app.file_access import DatabaseFileAccessController -from core.tools.entities.tool_entities import ToolInvokeMessage -from core.tools.utils.message_transformer import ToolFileMessageTransformer -from extensions.ext_database import db -from factories import file_factory from models import ToolFile from services.tools.builtin_tools_manage_service import BuiltinToolManageService diff --git a/api/core/workflow/nodes/agent/runtime_support.py b/api/core/workflow/nodes/agent/runtime_support.py index be50edbc4d..a872774c98 100644 --- a/api/core/workflow/nodes/agent/runtime_support.py +++ b/api/core/workflow/nodes/agent/runtime_support.py @@ -4,8 +4,6 @@ import json from collections.abc import Sequence from typing import Any, cast -from graphon.model_runtime.entities.model_entities import AIModelEntity, ModelType -from graphon.runtime import VariablePool from packaging.version import Version from pydantic import ValidationError from sqlalchemy import select @@ -21,6 +19,8 @@ from core.tools.entities.tool_entities import ToolIdentity, ToolParameter, ToolP from core.tools.tool_manager import ToolManager from core.workflow.system_variables import SystemVariableKey, get_system_text from extensions.ext_database import db +from graphon.model_runtime.entities.model_entities import AIModelEntity, ModelType +from graphon.runtime import VariablePool from models.model import Conversation from .entities import AgentNodeData, AgentOldVersionModelFeatures, ParamsAutoGenerated diff --git a/api/core/workflow/nodes/datasource/datasource_node.py b/api/core/workflow/nodes/datasource/datasource_node.py index d9247b2593..e4f6b3b470 100644 --- a/api/core/workflow/nodes/datasource/datasource_node.py +++ b/api/core/workflow/nodes/datasource/datasource_node.py @@ -1,6 +1,12 @@ from collections.abc import Generator, Mapping, Sequence from typing import TYPE_CHECKING, Any +from core.app.entities.app_invoke_entities import DIFY_RUN_CONTEXT_KEY, DifyRunContext +from core.datasource.datasource_manager import DatasourceManager +from core.datasource.entities.datasource_entities import DatasourceProviderType +from core.plugin.impl.exc import PluginDaemonClientSideError +from core.workflow.file_reference import resolve_file_record_id +from core.workflow.system_variables import SystemVariableKey, get_system_segment from graphon.entities.graph_config import NodeConfigDict from graphon.enums import ( BuiltinNodeTypes, @@ -12,13 +18,6 @@ from graphon.node_events import NodeRunResult, StreamCompletedEvent from graphon.nodes.base.node import Node from graphon.nodes.base.variable_template_parser import VariableTemplateParser -from core.app.entities.app_invoke_entities import DIFY_RUN_CONTEXT_KEY, DifyRunContext -from core.datasource.datasource_manager import DatasourceManager -from core.datasource.entities.datasource_entities import DatasourceProviderType -from core.plugin.impl.exc import PluginDaemonClientSideError -from core.workflow.file_reference import resolve_file_record_id -from core.workflow.system_variables import SystemVariableKey, get_system_segment - from .entities import DatasourceNodeData, DatasourceParameter, OnlineDriveDownloadFileParam from .exc import DatasourceNodeError diff --git a/api/core/workflow/nodes/datasource/entities.py b/api/core/workflow/nodes/datasource/entities.py index cad32f8d5b..28966f2392 100644 --- a/api/core/workflow/nodes/datasource/entities.py +++ b/api/core/workflow/nodes/datasource/entities.py @@ -1,9 +1,10 @@ from typing import Any, Literal, Union +from pydantic import BaseModel, field_validator +from pydantic_core.core_schema import ValidationInfo + from graphon.entities.base_node_data import BaseNodeData from graphon.enums import BuiltinNodeTypes, NodeType -from pydantic import BaseModel, field_validator -from pydantic_core.core_schema import ValidationInfo class DatasourceEntity(BaseModel): diff --git a/api/core/workflow/nodes/knowledge_index/entities.py b/api/core/workflow/nodes/knowledge_index/entities.py index 04a10f9257..260881e49c 100644 --- a/api/core/workflow/nodes/knowledge_index/entities.py +++ b/api/core/workflow/nodes/knowledge_index/entities.py @@ -1,13 +1,13 @@ from typing import Union -from graphon.entities.base_node_data import BaseNodeData -from graphon.enums import NodeType from pydantic import BaseModel from core.rag.entities import RerankingModelConfig, WeightedScoreConfig from core.rag.index_processor.index_processor_base import SummaryIndexSettingDict from core.rag.retrieval.retrieval_methods import RetrievalMethod from core.workflow.nodes.knowledge_index import KNOWLEDGE_INDEX_NODE_TYPE +from graphon.entities.base_node_data import BaseNodeData +from graphon.enums import NodeType class RetrievalSetting(BaseModel): diff --git a/api/core/workflow/nodes/knowledge_index/knowledge_index_node.py b/api/core/workflow/nodes/knowledge_index/knowledge_index_node.py index bb72fe3881..d5cab05dbe 100644 --- a/api/core/workflow/nodes/knowledge_index/knowledge_index_node.py +++ b/api/core/workflow/nodes/knowledge_index/knowledge_index_node.py @@ -2,17 +2,16 @@ import logging from collections.abc import Mapping from typing import TYPE_CHECKING, Any -from graphon.entities.graph_config import NodeConfigDict -from graphon.enums import NodeExecutionType, WorkflowNodeExecutionStatus -from graphon.node_events import NodeRunResult -from graphon.nodes.base.node import Node -from graphon.nodes.base.template import Template - from core.rag.index_processor.index_processor import IndexProcessor from core.rag.index_processor.index_processor_base import SummaryIndexSettingDict from core.rag.summary_index.summary_index import SummaryIndex from core.workflow.nodes.knowledge_index import KNOWLEDGE_INDEX_NODE_TYPE from core.workflow.system_variables import SystemVariableKey, get_system_segment, get_system_text +from graphon.entities.graph_config import NodeConfigDict +from graphon.enums import NodeExecutionType, WorkflowNodeExecutionStatus +from graphon.node_events import NodeRunResult +from graphon.nodes.base.node import Node +from graphon.nodes.base.template import Template from .entities import KnowledgeIndexNodeData from .exc import ( diff --git a/api/core/workflow/nodes/knowledge_retrieval/entities.py b/api/core/workflow/nodes/knowledge_retrieval/entities.py index 460ec693ce..3825f526a2 100644 --- a/api/core/workflow/nodes/knowledge_retrieval/entities.py +++ b/api/core/workflow/nodes/knowledge_retrieval/entities.py @@ -1,11 +1,11 @@ from typing import Literal -from graphon.entities.base_node_data import BaseNodeData -from graphon.enums import BuiltinNodeTypes, NodeType -from graphon.nodes.llm.entities import ModelConfig, VisionConfig from pydantic import BaseModel, Field from core.rag.entities import Condition, MetadataFilteringCondition, RerankingModelConfig, WeightedScoreConfig +from graphon.entities.base_node_data import BaseNodeData +from graphon.enums import BuiltinNodeTypes, NodeType +from graphon.nodes.llm.entities import ModelConfig, VisionConfig __all__ = ["Condition"] diff --git a/api/core/workflow/nodes/knowledge_retrieval/knowledge_retrieval_node.py b/api/core/workflow/nodes/knowledge_retrieval/knowledge_retrieval_node.py index 13624b27b3..47ad14b499 100644 --- a/api/core/workflow/nodes/knowledge_retrieval/knowledge_retrieval_node.py +++ b/api/core/workflow/nodes/knowledge_retrieval/knowledge_retrieval_node.py @@ -8,6 +8,11 @@ import logging from collections.abc import Mapping, Sequence from typing import TYPE_CHECKING, Any, Literal +from core.app.app_config.entities import DatasetRetrieveConfigEntity +from core.app.entities.app_invoke_entities import DIFY_RUN_CONTEXT_KEY, DifyRunContext +from core.rag.data_post_processor.data_post_processor import RerankingModelDict, WeightsDict +from core.rag.retrieval.dataset_retrieval import DatasetRetrieval +from core.workflow.file_reference import parse_file_reference from graphon.entities import GraphInitParams from graphon.entities.graph_config import NodeConfigDict from graphon.enums import ( @@ -27,12 +32,6 @@ from graphon.variables import ( ) from graphon.variables.segments import ArrayObjectSegment -from core.app.app_config.entities import DatasetRetrieveConfigEntity -from core.app.entities.app_invoke_entities import DIFY_RUN_CONTEXT_KEY, DifyRunContext -from core.rag.data_post_processor.data_post_processor import RerankingModelDict, WeightsDict -from core.rag.retrieval.dataset_retrieval import DatasetRetrieval -from core.workflow.file_reference import parse_file_reference - from .entities import ( Condition, KnowledgeRetrievalNodeData, diff --git a/api/core/workflow/nodes/knowledge_retrieval/retrieval.py b/api/core/workflow/nodes/knowledge_retrieval/retrieval.py index 39e2008a2c..ea45dcf5c2 100644 --- a/api/core/workflow/nodes/knowledge_retrieval/retrieval.py +++ b/api/core/workflow/nodes/knowledge_retrieval/retrieval.py @@ -1,10 +1,10 @@ from typing import Any, Literal, Protocol -from graphon.model_runtime.entities import LLMUsage -from graphon.nodes.llm.entities import ModelConfig from pydantic import BaseModel, Field from core.rag.data_post_processor.data_post_processor import RerankingModelDict, WeightsDict +from graphon.model_runtime.entities import LLMUsage +from graphon.nodes.llm.entities import ModelConfig from .entities import MetadataFilteringCondition diff --git a/api/core/workflow/nodes/trigger_plugin/entities.py b/api/core/workflow/nodes/trigger_plugin/entities.py index bf5be2379a..23ed2cd408 100644 --- a/api/core/workflow/nodes/trigger_plugin/entities.py +++ b/api/core/workflow/nodes/trigger_plugin/entities.py @@ -1,12 +1,12 @@ from collections.abc import Mapping from typing import Any, Literal, Union -from graphon.entities.base_node_data import BaseNodeData -from graphon.enums import NodeType from pydantic import BaseModel, Field, ValidationInfo, field_validator from core.trigger.constants import TRIGGER_PLUGIN_NODE_TYPE from core.trigger.entities.entities import EventParameter +from graphon.entities.base_node_data import BaseNodeData +from graphon.enums import NodeType from .exc import TriggerEventParameterError diff --git a/api/core/workflow/nodes/trigger_plugin/trigger_event_node.py b/api/core/workflow/nodes/trigger_plugin/trigger_event_node.py index e50de11bb9..c848a86255 100644 --- a/api/core/workflow/nodes/trigger_plugin/trigger_event_node.py +++ b/api/core/workflow/nodes/trigger_plugin/trigger_event_node.py @@ -1,13 +1,12 @@ from collections.abc import Mapping from typing import Any +from core.trigger.constants import TRIGGER_PLUGIN_NODE_TYPE +from core.workflow.variable_prefixes import SYSTEM_VARIABLE_NODE_ID from graphon.enums import NodeExecutionType, WorkflowNodeExecutionMetadataKey, WorkflowNodeExecutionStatus from graphon.node_events import NodeRunResult from graphon.nodes.base.node import Node -from core.trigger.constants import TRIGGER_PLUGIN_NODE_TYPE -from core.workflow.variable_prefixes import SYSTEM_VARIABLE_NODE_ID - from .entities import TriggerEventNodeData diff --git a/api/core/workflow/nodes/trigger_schedule/entities.py b/api/core/workflow/nodes/trigger_schedule/entities.py index 04f1f7e6bb..683c8d420f 100644 --- a/api/core/workflow/nodes/trigger_schedule/entities.py +++ b/api/core/workflow/nodes/trigger_schedule/entities.py @@ -1,10 +1,10 @@ from typing import Any, Literal, Union -from graphon.entities.base_node_data import BaseNodeData -from graphon.enums import NodeType from pydantic import BaseModel, Field from core.trigger.constants import TRIGGER_SCHEDULE_NODE_TYPE +from graphon.entities.base_node_data import BaseNodeData +from graphon.enums import NodeType class TriggerScheduleNodeData(BaseNodeData): diff --git a/api/core/workflow/nodes/trigger_schedule/trigger_schedule_node.py b/api/core/workflow/nodes/trigger_schedule/trigger_schedule_node.py index a9753ab387..b46cc76a6e 100644 --- a/api/core/workflow/nodes/trigger_schedule/trigger_schedule_node.py +++ b/api/core/workflow/nodes/trigger_schedule/trigger_schedule_node.py @@ -1,11 +1,10 @@ from collections.abc import Mapping -from graphon.enums import NodeExecutionType, WorkflowNodeExecutionStatus -from graphon.node_events import NodeRunResult -from graphon.nodes.base.node import Node - from core.trigger.constants import TRIGGER_SCHEDULE_NODE_TYPE from core.workflow.variable_prefixes import SYSTEM_VARIABLE_NODE_ID +from graphon.enums import NodeExecutionType, WorkflowNodeExecutionStatus +from graphon.node_events import NodeRunResult +from graphon.nodes.base.node import Node from .entities import TriggerScheduleNodeData diff --git a/api/core/workflow/nodes/trigger_webhook/entities.py b/api/core/workflow/nodes/trigger_webhook/entities.py index a30f877e4b..b261039448 100644 --- a/api/core/workflow/nodes/trigger_webhook/entities.py +++ b/api/core/workflow/nodes/trigger_webhook/entities.py @@ -1,12 +1,12 @@ from collections.abc import Sequence from enum import StrEnum -from graphon.entities.base_node_data import BaseNodeData -from graphon.enums import NodeType -from graphon.variables.types import SegmentType from pydantic import BaseModel, Field, field_validator from core.trigger.constants import TRIGGER_WEBHOOK_NODE_TYPE +from graphon.entities.base_node_data import BaseNodeData +from graphon.enums import NodeType +from graphon.variables.types import SegmentType _WEBHOOK_HEADER_ALLOWED_TYPES: frozenset[SegmentType] = frozenset((SegmentType.STRING,)) diff --git a/api/core/workflow/nodes/trigger_webhook/node.py b/api/core/workflow/nodes/trigger_webhook/node.py index d942a718cc..13c4f05bfd 100644 --- a/api/core/workflow/nodes/trigger_webhook/node.py +++ b/api/core/workflow/nodes/trigger_webhook/node.py @@ -2,6 +2,10 @@ import logging from collections.abc import Mapping from typing import Any +from core.trigger.constants import TRIGGER_WEBHOOK_NODE_TYPE +from core.workflow.file_reference import resolve_file_record_id +from core.workflow.variable_prefixes import SYSTEM_VARIABLE_NODE_ID +from factories.variable_factory import build_segment_with_type from graphon.enums import NodeExecutionType, WorkflowNodeExecutionStatus from graphon.file import FileTransferMethod from graphon.node_events import NodeRunResult @@ -10,11 +14,6 @@ from graphon.nodes.protocols import FileReferenceFactoryProtocol from graphon.variables.types import SegmentType from graphon.variables.variables import FileVariable -from core.trigger.constants import TRIGGER_WEBHOOK_NODE_TYPE -from core.workflow.file_reference import resolve_file_record_id -from core.workflow.variable_prefixes import SYSTEM_VARIABLE_NODE_ID -from factories.variable_factory import build_segment_with_type - from .entities import ContentType, WebhookData logger = logging.getLogger(__name__) diff --git a/api/core/workflow/template_rendering.py b/api/core/workflow/template_rendering.py index d51cfadd09..b4ffb37549 100644 --- a/api/core/workflow/template_rendering.py +++ b/api/core/workflow/template_rendering.py @@ -3,11 +3,10 @@ from __future__ import annotations from collections.abc import Mapping from typing import Any +from core.helper.code_executor.code_executor import CodeExecutionError, CodeExecutor from graphon.nodes.code.entities import CodeLanguage from graphon.template_rendering import Jinja2TemplateRenderer, TemplateRenderError -from core.helper.code_executor.code_executor import CodeExecutionError, CodeExecutor - class CodeExecutorJinja2TemplateRenderer(Jinja2TemplateRenderer): """Sandbox-backed Jinja2 renderer for workflow-owned node composition.""" diff --git a/api/core/workflow/workflow_entry.py b/api/core/workflow/workflow_entry.py index f0a5fbb400..4e2f603e5b 100644 --- a/api/core/workflow/workflow_entry.py +++ b/api/core/workflow/workflow_entry.py @@ -3,20 +3,6 @@ import time from collections.abc import Generator, Mapping, Sequence from typing import Any, TypedDict -from graphon.entities import GraphInitParams -from graphon.entities.graph_config import NodeConfigDictAdapter -from graphon.errors import WorkflowNodeRunFailedError -from graphon.file import File -from graphon.graph import Graph -from graphon.graph_engine import GraphEngine, GraphEngineConfig -from graphon.graph_engine.command_channels import CommandChannel, InMemoryChannel -from graphon.graph_engine.layers import DebugLoggingLayer, ExecutionLimitsLayer -from graphon.graph_events import GraphEngineEvent, GraphNodeEventBase, GraphRunFailedEvent -from graphon.nodes import BuiltinNodeTypes -from graphon.nodes.base.node import Node -from graphon.runtime import ChildGraphNotFoundError, GraphRuntimeState, VariablePool -from graphon.variable_loader import DUMMY_VARIABLE_LOADER, VariableLoader, load_into_variable_pool - from configs import dify_config from context import capture_current_context from core.app.apps.exc import GenerateTaskStoppedError @@ -40,6 +26,19 @@ from core.workflow.variable_pool_initializer import add_node_inputs_to_pool, add from core.workflow.variable_prefixes import ENVIRONMENT_VARIABLE_NODE_ID from extensions.otel.runtime import is_instrument_flag_enabled from factories import file_factory +from graphon.entities import GraphInitParams +from graphon.entities.graph_config import NodeConfigDictAdapter +from graphon.errors import WorkflowNodeRunFailedError +from graphon.file import File +from graphon.graph import Graph +from graphon.graph_engine import GraphEngine, GraphEngineConfig +from graphon.graph_engine.command_channels import CommandChannel, InMemoryChannel +from graphon.graph_engine.layers import DebugLoggingLayer, ExecutionLimitsLayer +from graphon.graph_events import GraphEngineEvent, GraphNodeEventBase, GraphRunFailedEvent +from graphon.nodes import BuiltinNodeTypes +from graphon.nodes.base.node import Node +from graphon.runtime import ChildGraphNotFoundError, GraphRuntimeState, VariablePool +from graphon.variable_loader import DUMMY_VARIABLE_LOADER, VariableLoader, load_into_variable_pool from models.workflow import Workflow logger = logging.getLogger(__name__) diff --git a/api/enterprise/telemetry/draft_trace.py b/api/enterprise/telemetry/draft_trace.py index 5a8d0ee6f4..dff558988c 100644 --- a/api/enterprise/telemetry/draft_trace.py +++ b/api/enterprise/telemetry/draft_trace.py @@ -3,10 +3,9 @@ from __future__ import annotations from collections.abc import Mapping from typing import Any -from graphon.enums import WorkflowNodeExecutionMetadataKey - from core.telemetry import TelemetryContext, TelemetryEvent, TraceTaskName from core.telemetry import emit as telemetry_emit +from graphon.enums import WorkflowNodeExecutionMetadataKey from models.workflow import WorkflowNodeExecutionModel diff --git a/api/events/event_handlers/delete_tool_parameters_cache_when_sync_draft_workflow.py b/api/events/event_handlers/delete_tool_parameters_cache_when_sync_draft_workflow.py index 7bd8e88231..ba9758175f 100644 --- a/api/events/event_handlers/delete_tool_parameters_cache_when_sync_draft_workflow.py +++ b/api/events/event_handlers/delete_tool_parameters_cache_when_sync_draft_workflow.py @@ -1,12 +1,11 @@ import logging -from graphon.nodes import BuiltinNodeTypes -from graphon.nodes.tool.entities import ToolEntity - from core.tools.entities.tool_entities import ToolProviderType from core.tools.tool_manager import ToolManager from core.tools.utils.configuration import ToolParameterConfigurationManager from events.app_event import app_draft_workflow_was_synced +from graphon.nodes import BuiltinNodeTypes +from graphon.nodes.tool.entities import ToolEntity logger = logging.getLogger(__name__) diff --git a/api/events/event_handlers/update_app_dataset_join_when_app_published_workflow_updated.py b/api/events/event_handlers/update_app_dataset_join_when_app_published_workflow_updated.py index 86b5b2bbf0..6769b94cde 100644 --- a/api/events/event_handlers/update_app_dataset_join_when_app_published_workflow_updated.py +++ b/api/events/event_handlers/update_app_dataset_join_when_app_published_workflow_updated.py @@ -1,11 +1,11 @@ from typing import cast -from graphon.nodes import BuiltinNodeTypes from sqlalchemy import delete, select from core.workflow.nodes.knowledge_retrieval.entities import KnowledgeRetrievalNodeData from events.app_event import app_published_workflow_was_updated from extensions.ext_database import db +from graphon.nodes import BuiltinNodeTypes from models.dataset import AppDatasetJoin from models.workflow import Workflow diff --git a/api/extensions/ext_sentry.py b/api/extensions/ext_sentry.py index 5cc58f27c4..69d1f1ab07 100644 --- a/api/extensions/ext_sentry.py +++ b/api/extensions/ext_sentry.py @@ -5,11 +5,12 @@ from dify_app import DifyApp def init_app(app: DifyApp): if dify_config.SENTRY_DSN: import sentry_sdk - from graphon.model_runtime.errors.invoke import InvokeRateLimitError from sentry_sdk.integrations.celery import CeleryIntegration from sentry_sdk.integrations.flask import FlaskIntegration from werkzeug.exceptions import HTTPException + from graphon.model_runtime.errors.invoke import InvokeRateLimitError + try: from langfuse._utils import parse_error diff --git a/api/extensions/logstore/repositories/logstore_api_workflow_node_execution_repository.py b/api/extensions/logstore/repositories/logstore_api_workflow_node_execution_repository.py index db599c5d49..64ff0f0674 100644 --- a/api/extensions/logstore/repositories/logstore_api_workflow_node_execution_repository.py +++ b/api/extensions/logstore/repositories/logstore_api_workflow_node_execution_repository.py @@ -11,12 +11,12 @@ from collections.abc import Sequence from datetime import datetime from typing import Any -from graphon.enums import WorkflowNodeExecutionStatus from sqlalchemy.orm import sessionmaker from extensions.logstore.aliyun_logstore import AliyunLogStore from extensions.logstore.repositories import safe_float, safe_int from extensions.logstore.sql_escape import escape_identifier, escape_logstore_query_value +from graphon.enums import WorkflowNodeExecutionStatus from models.enums import CreatorUserRole from models.workflow import WorkflowNodeExecutionModel, WorkflowNodeExecutionTriggeredFrom from repositories.api_workflow_node_execution_repository import DifyAPIWorkflowNodeExecutionRepository diff --git a/api/extensions/logstore/repositories/logstore_api_workflow_run_repository.py b/api/extensions/logstore/repositories/logstore_api_workflow_run_repository.py index 2745141431..7f77a0437a 100644 --- a/api/extensions/logstore/repositories/logstore_api_workflow_run_repository.py +++ b/api/extensions/logstore/repositories/logstore_api_workflow_run_repository.py @@ -20,12 +20,12 @@ from collections.abc import Sequence from datetime import datetime from typing import Any, cast -from graphon.enums import WorkflowExecutionStatus from sqlalchemy.orm import sessionmaker from extensions.logstore.aliyun_logstore import AliyunLogStore from extensions.logstore.repositories import safe_float, safe_int from extensions.logstore.sql_escape import escape_identifier, escape_logstore_query_value, escape_sql_string +from graphon.enums import WorkflowExecutionStatus from libs.infinite_scroll_pagination import InfiniteScrollPagination from models.enums import CreatorUserRole, WorkflowRunTriggeredFrom from models.workflow import WorkflowRun, WorkflowType diff --git a/api/extensions/logstore/repositories/logstore_workflow_execution_repository.py b/api/extensions/logstore/repositories/logstore_workflow_execution_repository.py index d0f3e2e244..544109276d 100644 --- a/api/extensions/logstore/repositories/logstore_workflow_execution_repository.py +++ b/api/extensions/logstore/repositories/logstore_workflow_execution_repository.py @@ -3,14 +3,14 @@ import logging import os import time -from graphon.entities import WorkflowExecution -from graphon.workflow_type_encoder import WorkflowRuntimeTypeConverter from sqlalchemy.engine import Engine from sqlalchemy.orm import sessionmaker from core.repositories.factory import WorkflowExecutionRepository from core.repositories.sqlalchemy_workflow_execution_repository import SQLAlchemyWorkflowExecutionRepository from extensions.logstore.aliyun_logstore import AliyunLogStore +from graphon.entities import WorkflowExecution +from graphon.workflow_type_encoder import WorkflowRuntimeTypeConverter from libs.helper import extract_tenant_id from models import ( Account, diff --git a/api/extensions/logstore/repositories/logstore_workflow_node_execution_repository.py b/api/extensions/logstore/repositories/logstore_workflow_node_execution_repository.py index 37952d6464..dc7654a25c 100644 --- a/api/extensions/logstore/repositories/logstore_workflow_node_execution_repository.py +++ b/api/extensions/logstore/repositories/logstore_workflow_node_execution_repository.py @@ -13,10 +13,6 @@ from collections.abc import Sequence from datetime import datetime from typing import Any -from graphon.entities import WorkflowNodeExecution -from graphon.enums import WorkflowNodeExecutionMetadataKey, WorkflowNodeExecutionStatus -from graphon.model_runtime.utils.encoders import jsonable_encoder -from graphon.workflow_type_encoder import WorkflowRuntimeTypeConverter from sqlalchemy.engine import Engine from sqlalchemy.orm import sessionmaker @@ -26,6 +22,10 @@ from core.repositories.factory import OrderConfig, WorkflowNodeExecutionReposito from extensions.logstore.aliyun_logstore import AliyunLogStore from extensions.logstore.repositories import safe_float, safe_int from extensions.logstore.sql_escape import escape_identifier +from graphon.entities import WorkflowNodeExecution +from graphon.enums import WorkflowNodeExecutionMetadataKey, WorkflowNodeExecutionStatus +from graphon.model_runtime.utils.encoders import jsonable_encoder +from graphon.workflow_type_encoder import WorkflowRuntimeTypeConverter from libs.helper import extract_tenant_id from models import ( Account, diff --git a/api/extensions/otel/parser/base.py b/api/extensions/otel/parser/base.py index 23d324f9ea..fbf379b3e5 100644 --- a/api/extensions/otel/parser/base.py +++ b/api/extensions/otel/parser/base.py @@ -10,17 +10,17 @@ Gate is only active in EE (``ENTERPRISE_ENABLED=True``) when import json from typing import Any, Protocol -from graphon.enums import BuiltinNodeTypes -from graphon.file import File -from graphon.graph_events import GraphNodeEventBase -from graphon.nodes.base.node import Node -from graphon.variables import Segment from opentelemetry.trace import Span from opentelemetry.trace.status import Status, StatusCode from pydantic import BaseModel from configs import dify_config from extensions.otel.semconv.gen_ai import ChainAttributes, GenAIAttributes +from graphon.enums import BuiltinNodeTypes +from graphon.file import File +from graphon.graph_events import GraphNodeEventBase +from graphon.nodes.base.node import Node +from graphon.variables import Segment def should_include_content() -> bool: diff --git a/api/extensions/otel/parser/llm.py b/api/extensions/otel/parser/llm.py index 335c5cc29e..ec3c78a12d 100644 --- a/api/extensions/otel/parser/llm.py +++ b/api/extensions/otel/parser/llm.py @@ -6,12 +6,12 @@ import logging from collections.abc import Mapping from typing import Any -from graphon.graph_events import GraphNodeEventBase -from graphon.nodes.base.node import Node from opentelemetry.trace import Span from extensions.otel.parser.base import DefaultNodeOTelParser, safe_json_dumps from extensions.otel.semconv.gen_ai import LLMAttributes +from graphon.graph_events import GraphNodeEventBase +from graphon.nodes.base.node import Node logger = logging.getLogger(__name__) diff --git a/api/extensions/otel/parser/retrieval.py b/api/extensions/otel/parser/retrieval.py index 6df5f62c15..56672d1fd4 100644 --- a/api/extensions/otel/parser/retrieval.py +++ b/api/extensions/otel/parser/retrieval.py @@ -6,13 +6,13 @@ import logging from collections.abc import Sequence from typing import Any -from graphon.graph_events import GraphNodeEventBase -from graphon.nodes.base.node import Node -from graphon.variables import Segment from opentelemetry.trace import Span from extensions.otel.parser.base import DefaultNodeOTelParser, safe_json_dumps from extensions.otel.semconv.gen_ai import RetrieverAttributes +from graphon.graph_events import GraphNodeEventBase +from graphon.nodes.base.node import Node +from graphon.variables import Segment logger = logging.getLogger(__name__) diff --git a/api/extensions/otel/parser/tool.py b/api/extensions/otel/parser/tool.py index b9fdd9e1ca..75ddbba448 100644 --- a/api/extensions/otel/parser/tool.py +++ b/api/extensions/otel/parser/tool.py @@ -2,14 +2,14 @@ Parser for tool nodes that captures tool-specific metadata. """ -from graphon.enums import WorkflowNodeExecutionMetadataKey -from graphon.graph_events import GraphNodeEventBase -from graphon.nodes.base.node import Node -from graphon.nodes.tool.entities import ToolNodeData from opentelemetry.trace import Span from extensions.otel.parser.base import DefaultNodeOTelParser, safe_json_dumps from extensions.otel.semconv.gen_ai import ToolAttributes +from graphon.enums import WorkflowNodeExecutionMetadataKey +from graphon.graph_events import GraphNodeEventBase +from graphon.nodes.base.node import Node +from graphon.nodes.tool.entities import ToolNodeData class ToolNodeOTelParser: diff --git a/api/factories/file_factory/builders.py b/api/factories/file_factory/builders.py index 7516d18c8e..288d37d265 100644 --- a/api/factories/file_factory/builders.py +++ b/api/factories/file_factory/builders.py @@ -7,12 +7,12 @@ import uuid from collections.abc import Mapping, Sequence from typing import Any -from graphon.file import File, FileTransferMethod, FileType, FileUploadConfig, helpers, standardize_file_type from sqlalchemy import select from core.app.file_access import FileAccessControllerProtocol from core.workflow.file_reference import build_file_reference from extensions.ext_database import db +from graphon.file import File, FileTransferMethod, FileType, FileUploadConfig, helpers, standardize_file_type from models import ToolFile, UploadFile from .common import resolve_mapping_file_id diff --git a/api/factories/file_factory/message_files.py b/api/factories/file_factory/message_files.py index 5582b85c95..4b3d514238 100644 --- a/api/factories/file_factory/message_files.py +++ b/api/factories/file_factory/message_files.py @@ -4,9 +4,8 @@ from __future__ import annotations from collections.abc import Sequence -from graphon.file import File, FileBelongsTo, FileTransferMethod, FileUploadConfig - from core.app.file_access import FileAccessControllerProtocol +from graphon.file import File, FileBelongsTo, FileTransferMethod, FileUploadConfig from models import MessageFile from .builders import build_from_mapping diff --git a/api/factories/file_factory/storage_keys.py b/api/factories/file_factory/storage_keys.py index db3a7f3015..dba4c84407 100644 --- a/api/factories/file_factory/storage_keys.py +++ b/api/factories/file_factory/storage_keys.py @@ -5,12 +5,12 @@ from __future__ import annotations import uuid from collections.abc import Mapping, Sequence -from graphon.file import File, FileTransferMethod from sqlalchemy import select from sqlalchemy.orm import Session from core.app.file_access import FileAccessControllerProtocol from core.workflow.file_reference import build_file_reference, parse_file_reference +from graphon.file import File, FileTransferMethod from models import ToolFile, UploadFile diff --git a/api/factories/variable_factory.py b/api/factories/variable_factory.py index 57205b5739..fd7acb14d3 100644 --- a/api/factories/variable_factory.py +++ b/api/factories/variable_factory.py @@ -8,6 +8,11 @@ shared conversion functions for legacy callers and tests. from collections.abc import Mapping, Sequence from typing import Any, cast +from configs import dify_config +from core.workflow.variable_prefixes import ( + CONVERSATION_VARIABLE_NODE_ID, + ENVIRONMENT_VARIABLE_NODE_ID, +) from graphon.variables.exc import VariableError from graphon.variables.factory import ( TypeMismatchError, @@ -31,12 +36,6 @@ from graphon.variables.variables import ( VariableBase, ) -from configs import dify_config -from core.workflow.variable_prefixes import ( - CONVERSATION_VARIABLE_NODE_ID, - ENVIRONMENT_VARIABLE_NODE_ID, -) - __all__ = [ "TypeMismatchError", "UnsupportedSegmentTypeError", diff --git a/api/fields/conversation_fields.py b/api/fields/conversation_fields.py index 5cb1e9087c..bf5c9ffcb1 100644 --- a/api/fields/conversation_fields.py +++ b/api/fields/conversation_fields.py @@ -3,10 +3,10 @@ from __future__ import annotations from datetime import datetime from typing import Any -from graphon.file import File from pydantic import Field, field_validator, model_validator from fields.base import ResponseModel +from graphon.file import File type JSONValue = Any diff --git a/api/fields/conversation_variable_fields.py b/api/fields/conversation_variable_fields.py index cb6cdb309a..cf4a71d545 100644 --- a/api/fields/conversation_variable_fields.py +++ b/api/fields/conversation_variable_fields.py @@ -4,10 +4,10 @@ from datetime import datetime from typing import Any from flask_restx import Namespace, fields -from graphon.variables.types import SegmentType from pydantic import field_validator from fields.base import ResponseModel +from graphon.variables.types import SegmentType from libs.helper import TimestampField from ._value_type_serializer import serialize_value_type diff --git a/api/fields/member_fields.py b/api/fields/member_fields.py index cfe0015918..67b320beaa 100644 --- a/api/fields/member_fields.py +++ b/api/fields/member_fields.py @@ -3,10 +3,10 @@ from __future__ import annotations from datetime import datetime from flask_restx import fields -from graphon.file import helpers as file_helpers from pydantic import computed_field, field_validator from fields.base import ResponseModel +from graphon.file import helpers as file_helpers simple_account_fields = { "id": fields.String, diff --git a/api/fields/message_fields.py b/api/fields/message_fields.py index 1a871204a0..ca18f1c203 100644 --- a/api/fields/message_fields.py +++ b/api/fields/message_fields.py @@ -3,12 +3,12 @@ from __future__ import annotations from datetime import datetime from uuid import uuid4 -from graphon.file import File from pydantic import Field, field_validator from core.entities.execution_extra_content import ExecutionExtraContentDomainModel from fields.base import ResponseModel from fields.conversation_fields import AgentThought, JSONValue, MessageFile +from graphon.file import File type JSONValueType = JSONValue diff --git a/api/fields/raws.py b/api/fields/raws.py index 4c65cdab7a..ee6f53b360 100644 --- a/api/fields/raws.py +++ b/api/fields/raws.py @@ -1,4 +1,5 @@ from flask_restx import fields + from graphon.file import File diff --git a/api/fields/workflow_fields.py b/api/fields/workflow_fields.py index b0b6cc0b48..f9b5e98936 100644 --- a/api/fields/workflow_fields.py +++ b/api/fields/workflow_fields.py @@ -1,8 +1,8 @@ from flask_restx import fields -from graphon.variables import SecretVariable, SegmentType, VariableBase from core.helper import encrypter from fields.member_fields import simple_account_fields +from graphon.variables import SecretVariable, SegmentType, VariableBase from libs.helper import TimestampField from ._value_type_serializer import serialize_value_type diff --git a/api/libs/helper.py b/api/libs/helper.py index 69bd483515..ac69a11084 100644 --- a/api/libs/helper.py +++ b/api/libs/helper.py @@ -16,8 +16,6 @@ from zoneinfo import available_timezones from flask import Response, stream_with_context from flask_restx import fields -from graphon.file import helpers as file_helpers -from graphon.model_runtime.utils.encoders import jsonable_encoder from pydantic import BaseModel, TypeAdapter from pydantic.functional_validators import AfterValidator from typing_extensions import TypedDict @@ -25,6 +23,8 @@ from typing_extensions import TypedDict from configs import dify_config from core.app.features.rate_limiting.rate_limit import RateLimitGenerator from extensions.ext_redis import redis_client +from graphon.file import helpers as file_helpers +from graphon.model_runtime.utils.encoders import jsonable_encoder if TYPE_CHECKING: from models import Account diff --git a/api/models/human_input.py b/api/models/human_input.py index 79c5d62f6a..b4c7a634b6 100644 --- a/api/models/human_input.py +++ b/api/models/human_input.py @@ -3,11 +3,11 @@ from enum import StrEnum from typing import Annotated, Literal, Self, final import sqlalchemy as sa -from graphon.nodes.human_input.enums import HumanInputFormKind, HumanInputFormStatus from pydantic import BaseModel, Field from sqlalchemy.orm import Mapped, mapped_column, relationship from core.workflow.human_input_compat import DeliveryMethodType +from graphon.nodes.human_input.enums import HumanInputFormKind, HumanInputFormStatus from libs.helper import generate_string from .base import Base, DefaultFieldsMixin diff --git a/api/models/model.py b/api/models/model.py index 8eabf45363..7fe0731098 100644 --- a/api/models/model.py +++ b/api/models/model.py @@ -14,9 +14,6 @@ from uuid import uuid4 import sqlalchemy as sa from flask import request from flask_login import UserMixin # type: ignore[import-untyped] -from graphon.enums import WorkflowExecutionStatus -from graphon.file import FILE_MODEL_IDENTITY, File, FileTransferMethod, FileType -from graphon.file import helpers as file_helpers from sqlalchemy import BigInteger, Float, Index, PrimaryKeyConstraint, String, exists, func, select, text from sqlalchemy.orm import Mapped, Session, mapped_column, sessionmaker @@ -24,6 +21,9 @@ from configs import dify_config from constants import DEFAULT_FILE_NUMBER_LIMITS from core.tools.signature import sign_tool_file from extensions.storage.storage_type import StorageType +from graphon.enums import WorkflowExecutionStatus +from graphon.file import FILE_MODEL_IDENTITY, File, FileTransferMethod, FileType +from graphon.file import helpers as file_helpers from libs.helper import generate_string # type: ignore[import-not-found] from libs.uuid_utils import uuidv7 from models.utils.file_input_compat import build_file_from_input_mapping diff --git a/api/models/provider.py b/api/models/provider.py index 8270961b31..2bb67d605b 100644 --- a/api/models/provider.py +++ b/api/models/provider.py @@ -6,10 +6,10 @@ from functools import cached_property from uuid import uuid4 import sqlalchemy as sa -from graphon.model_runtime.entities.model_entities import ModelType from sqlalchemy import DateTime, String, func, select, text from sqlalchemy.orm import Mapped, mapped_column +from graphon.model_runtime.entities.model_entities import ModelType from libs.uuid_utils import uuidv7 from .base import TypeBase diff --git a/api/models/utils/file_input_compat.py b/api/models/utils/file_input_compat.py index 8b767779ce..a2dc8f6157 100644 --- a/api/models/utils/file_input_compat.py +++ b/api/models/utils/file_input_compat.py @@ -4,9 +4,8 @@ from collections.abc import Callable, Mapping from functools import lru_cache from typing import Any -from graphon.file import File, FileTransferMethod - from core.workflow.file_reference import parse_file_reference +from graphon.file import File, FileTransferMethod @lru_cache(maxsize=1) diff --git a/api/models/workflow.py b/api/models/workflow.py index d688043920..dfda03c2ee 100644 --- a/api/models/workflow.py +++ b/api/models/workflow.py @@ -8,19 +8,6 @@ from typing import TYPE_CHECKING, Any, Optional, TypedDict, cast from uuid import uuid4 import sqlalchemy as sa -from graphon.entities.graph_config import NodeConfigDict, NodeConfigDictAdapter -from graphon.entities.pause_reason import HumanInputRequired, PauseReason, PauseReasonType, SchedulingPause -from graphon.enums import ( - BuiltinNodeTypes, - NodeType, - WorkflowExecutionStatus, - WorkflowNodeExecutionMetadataKey, - WorkflowNodeExecutionStatus, -) -from graphon.file import File -from graphon.file.constants import maybe_file_object -from graphon.variables import utils as variable_utils -from graphon.variables.variables import FloatVariable, IntegerVariable, RAGPipelineVariable, StringVariable from sqlalchemy import ( DateTime, Index, @@ -44,6 +31,19 @@ from core.workflow.variable_prefixes import ( ) from extensions.ext_storage import Storage from factories.variable_factory import TypeMismatchError, build_segment_with_type +from graphon.entities.graph_config import NodeConfigDict, NodeConfigDictAdapter +from graphon.entities.pause_reason import HumanInputRequired, PauseReason, PauseReasonType, SchedulingPause +from graphon.enums import ( + BuiltinNodeTypes, + NodeType, + WorkflowExecutionStatus, + WorkflowNodeExecutionMetadataKey, + WorkflowNodeExecutionStatus, +) +from graphon.file import File +from graphon.file.constants import maybe_file_object +from graphon.variables import utils as variable_utils +from graphon.variables.variables import FloatVariable, IntegerVariable, RAGPipelineVariable, StringVariable from libs.datetime_utils import naive_utc_now from libs.uuid_utils import uuidv7 @@ -53,11 +53,10 @@ if TYPE_CHECKING: from .model import AppMode, UploadFile -from graphon.variables import SecretVariable, Segment, SegmentType, VariableBase - from constants import DEFAULT_FILE_NUMBER_LIMITS, HIDDEN_VALUE from core.helper import encrypter from factories import variable_factory +from graphon.variables import SecretVariable, Segment, SegmentType, VariableBase from libs import helper from .account import Account diff --git a/api/repositories/api_workflow_run_repository.py b/api/repositories/api_workflow_run_repository.py index 100589804c..72b38e7906 100644 --- a/api/repositories/api_workflow_run_repository.py +++ b/api/repositories/api_workflow_run_repository.py @@ -38,11 +38,11 @@ from collections.abc import Callable, Sequence from datetime import datetime from typing import Protocol, TypedDict -from graphon.entities.pause_reason import PauseReason -from graphon.enums import WorkflowType from sqlalchemy.orm import Session from core.repositories.factory import WorkflowExecutionRepository +from graphon.entities.pause_reason import PauseReason +from graphon.enums import WorkflowType from libs.infinite_scroll_pagination import InfiniteScrollPagination from models.enums import WorkflowRunTriggeredFrom from models.workflow import WorkflowAppLog, WorkflowArchiveLog, WorkflowPause, WorkflowPauseReason, WorkflowRun diff --git a/api/repositories/sqlalchemy_api_workflow_node_execution_repository.py b/api/repositories/sqlalchemy_api_workflow_node_execution_repository.py index d5c6a203b1..44735eb769 100644 --- a/api/repositories/sqlalchemy_api_workflow_node_execution_repository.py +++ b/api/repositories/sqlalchemy_api_workflow_node_execution_repository.py @@ -10,11 +10,11 @@ from collections.abc import Sequence from datetime import datetime from typing import Protocol, cast -from graphon.enums import WorkflowNodeExecutionMetadataKey, WorkflowNodeExecutionStatus from sqlalchemy import asc, delete, desc, func, select from sqlalchemy.engine import CursorResult from sqlalchemy.orm import Session, sessionmaker +from graphon.enums import WorkflowNodeExecutionMetadataKey, WorkflowNodeExecutionStatus from models.workflow import WorkflowNodeExecutionModel, WorkflowNodeExecutionOffload from repositories.api_workflow_node_execution_repository import ( DifyAPIWorkflowNodeExecutionRepository, diff --git a/api/repositories/sqlalchemy_api_workflow_run_repository.py b/api/repositories/sqlalchemy_api_workflow_run_repository.py index b760696c5e..474b200fc5 100644 --- a/api/repositories/sqlalchemy_api_workflow_run_repository.py +++ b/api/repositories/sqlalchemy_api_workflow_run_repository.py @@ -28,15 +28,15 @@ from decimal import Decimal from typing import Any, cast import sqlalchemy as sa -from graphon.entities.pause_reason import HumanInputRequired, PauseReason, PauseReasonType, SchedulingPause -from graphon.enums import WorkflowExecutionStatus, WorkflowType -from graphon.nodes.human_input.entities import FormDefinition from pydantic import ValidationError from sqlalchemy import and_, delete, func, null, or_, select, tuple_ from sqlalchemy.engine import CursorResult from sqlalchemy.orm import Session, selectinload, sessionmaker from extensions.ext_storage import storage +from graphon.entities.pause_reason import HumanInputRequired, PauseReason, PauseReasonType, SchedulingPause +from graphon.enums import WorkflowExecutionStatus, WorkflowType +from graphon.nodes.human_input.entities import FormDefinition from libs.datetime_utils import naive_utc_now from libs.helper import convert_datetime_to_date from libs.infinite_scroll_pagination import InfiniteScrollPagination diff --git a/api/repositories/sqlalchemy_execution_extra_content_repository.py b/api/repositories/sqlalchemy_execution_extra_content_repository.py index feba5f7eb6..67f8795d3f 100644 --- a/api/repositories/sqlalchemy_execution_extra_content_repository.py +++ b/api/repositories/sqlalchemy_execution_extra_content_repository.py @@ -7,9 +7,6 @@ from collections import defaultdict from collections.abc import Sequence from typing import Any -from graphon.nodes.human_input.entities import FormDefinition -from graphon.nodes.human_input.enums import HumanInputFormStatus -from graphon.nodes.human_input.human_input_node import HumanInputNode from sqlalchemy import select from sqlalchemy.orm import Session, selectinload, sessionmaker @@ -21,6 +18,9 @@ from core.entities.execution_extra_content import ( from core.entities.execution_extra_content import ( HumanInputContent as HumanInputContentDomainModel, ) +from graphon.nodes.human_input.entities import FormDefinition +from graphon.nodes.human_input.enums import HumanInputFormStatus +from graphon.nodes.human_input.human_input_node import HumanInputNode from models.execution_extra_content import ( ExecutionExtraContent as ExecutionExtraContentModel, ) diff --git a/api/services/app_dsl_service.py b/api/services/app_dsl_service.py index 74b800606d..78806927bc 100644 --- a/api/services/app_dsl_service.py +++ b/api/services/app_dsl_service.py @@ -10,12 +10,6 @@ from uuid import uuid4 import yaml from Crypto.Cipher import AES from Crypto.Util.Padding import pad, unpad -from graphon.enums import BuiltinNodeTypes -from graphon.model_runtime.utils.encoders import jsonable_encoder -from graphon.nodes.llm.entities import LLMNodeData -from graphon.nodes.parameter_extractor.entities import ParameterExtractorNodeData -from graphon.nodes.question_classifier.entities import QuestionClassifierNodeData -from graphon.nodes.tool.entities import ToolNodeData from packaging import version from packaging.version import parse as parse_version from pydantic import BaseModel @@ -35,6 +29,12 @@ from core.workflow.nodes.trigger_schedule.trigger_schedule_node import TriggerSc from events.app_event import app_model_config_was_updated, app_was_created from extensions.ext_redis import redis_client from factories import variable_factory +from graphon.enums import BuiltinNodeTypes +from graphon.model_runtime.utils.encoders import jsonable_encoder +from graphon.nodes.llm.entities import LLMNodeData +from graphon.nodes.parameter_extractor.entities import ParameterExtractorNodeData +from graphon.nodes.question_classifier.entities import QuestionClassifierNodeData +from graphon.nodes.tool.entities import ToolNodeData from libs.datetime_utils import naive_utc_now from models import Account, App, AppMode from models.model import AppModelConfig, AppModelConfigDict, IconType diff --git a/api/services/app_service.py b/api/services/app_service.py index ef170c50ba..afd98e2975 100644 --- a/api/services/app_service.py +++ b/api/services/app_service.py @@ -4,8 +4,6 @@ from typing import Any, TypedDict, cast import sqlalchemy as sa from flask_sqlalchemy.pagination import Pagination -from graphon.model_runtime.entities.model_entities import ModelPropertyKey, ModelType -from graphon.model_runtime.model_providers.__base.large_language_model import LargeLanguageModel from sqlalchemy import select from configs import dify_config @@ -17,6 +15,8 @@ from core.tools.tool_manager import ToolManager from core.tools.utils.configuration import ToolParameterConfigurationManager from events.app_event import app_was_created, app_was_deleted, app_was_updated from extensions.ext_database import db +from graphon.model_runtime.entities.model_entities import ModelPropertyKey, ModelType +from graphon.model_runtime.model_providers.__base.large_language_model import LargeLanguageModel from libs.datetime_utils import naive_utc_now from libs.login import current_user from models import Account diff --git a/api/services/app_task_service.py b/api/services/app_task_service.py index 0842e9d3e7..6e9d6b1c73 100644 --- a/api/services/app_task_service.py +++ b/api/services/app_task_service.py @@ -5,11 +5,10 @@ like stopping tasks, handling both legacy Redis flag mechanism and new GraphEngine command channel mechanism. """ -from graphon.graph_engine.manager import GraphEngineManager - from core.app.apps.base_app_queue_manager import AppQueueManager from core.app.entities.app_invoke_entities import InvokeFrom from extensions.ext_redis import redis_client +from graphon.graph_engine.manager import GraphEngineManager from models.model import AppMode diff --git a/api/services/audio_service.py b/api/services/audio_service.py index 1c7027efb4..60948e652b 100644 --- a/api/services/audio_service.py +++ b/api/services/audio_service.py @@ -5,12 +5,12 @@ from collections.abc import Generator from typing import cast from flask import Response, stream_with_context -from graphon.model_runtime.entities.model_entities import ModelType from werkzeug.datastructures import FileStorage from constants import AUDIO_EXTENSIONS from core.model_manager import ModelManager from extensions.ext_database import db +from graphon.model_runtime.entities.model_entities import ModelType from models.enums import MessageStatus from models.model import App, AppMode, Message from services.errors.audio import ( diff --git a/api/services/clear_free_plan_tenant_expired_logs.py b/api/services/clear_free_plan_tenant_expired_logs.py index ea12e40420..dcc93b4b0f 100644 --- a/api/services/clear_free_plan_tenant_expired_logs.py +++ b/api/services/clear_free_plan_tenant_expired_logs.py @@ -6,7 +6,6 @@ from concurrent.futures import ThreadPoolExecutor import click from flask import Flask, current_app -from graphon.model_runtime.utils.encoders import jsonable_encoder from sqlalchemy import delete, func, select from sqlalchemy.orm import Session, sessionmaker @@ -14,6 +13,7 @@ from configs import dify_config from enums.cloud_plan import CloudPlan from extensions.ext_database import db from extensions.ext_storage import storage +from graphon.model_runtime.utils.encoders import jsonable_encoder from models.account import Tenant from models.model import ( App, diff --git a/api/services/conversation_service.py b/api/services/conversation_service.py index f5085af59b..ee8a1c4edd 100644 --- a/api/services/conversation_service.py +++ b/api/services/conversation_service.py @@ -3,7 +3,6 @@ import logging from collections.abc import Callable, Sequence from typing import Any -from graphon.variables.types import SegmentType from sqlalchemy import asc, desc, func, or_, select from sqlalchemy.orm import Session @@ -13,6 +12,7 @@ from core.db.session_factory import session_factory from core.llm_generator.llm_generator import LLMGenerator from extensions.ext_database import db from factories import variable_factory +from graphon.variables.types import SegmentType from libs.datetime_utils import naive_utc_now from libs.infinite_scroll_pagination import InfiniteScrollPagination from models import Account, ConversationVariable diff --git a/api/services/conversation_variable_updater.py b/api/services/conversation_variable_updater.py index 95a8951951..287d513f48 100644 --- a/api/services/conversation_variable_updater.py +++ b/api/services/conversation_variable_updater.py @@ -1,7 +1,7 @@ -from graphon.variables.variables import VariableBase from sqlalchemy import select from sqlalchemy.orm import Session, sessionmaker +from graphon.variables.variables import VariableBase from models import ConversationVariable diff --git a/api/services/dataset_service.py b/api/services/dataset_service.py index 6c6de192c6..e6f5f80a6d 100644 --- a/api/services/dataset_service.py +++ b/api/services/dataset_service.py @@ -10,9 +10,6 @@ from collections.abc import Sequence from typing import Any, Literal, TypedDict, cast import sqlalchemy as sa -from graphon.file import helpers as file_helpers -from graphon.model_runtime.entities.model_entities import ModelFeature, ModelType -from graphon.model_runtime.model_providers.__base.text_embedding_model import TextEmbeddingModel from redis.exceptions import LockNotOwnedError from sqlalchemy import delete, exists, func, select, update from sqlalchemy.orm import Session, sessionmaker @@ -31,6 +28,9 @@ from events.dataset_event import dataset_was_deleted from events.document_event import document_was_deleted from extensions.ext_database import db from extensions.ext_redis import redis_client +from graphon.file import helpers as file_helpers +from graphon.model_runtime.entities.model_entities import ModelFeature, ModelType +from graphon.model_runtime.model_providers.__base.text_embedding_model import TextEmbeddingModel from libs import helper from libs.datetime_utils import naive_utc_now from libs.login import current_user diff --git a/api/services/datasource_provider_service.py b/api/services/datasource_provider_service.py index 364c4a86a0..416bc8cef9 100644 --- a/api/services/datasource_provider_service.py +++ b/api/services/datasource_provider_service.py @@ -3,7 +3,6 @@ import time from collections.abc import Mapping from typing import Any -from graphon.model_runtime.entities.provider_entities import FormType from sqlalchemy import delete, func, select, update from sqlalchemy.orm import Session, sessionmaker @@ -18,6 +17,7 @@ from core.plugin.impl.oauth import OAuthHandler from core.tools.utils.encryption import ProviderConfigCache, ProviderConfigEncrypter, create_provider_encrypter from extensions.ext_database import db from extensions.ext_redis import redis_client +from graphon.model_runtime.entities.provider_entities import FormType from models.oauth import DatasourceOauthParamConfig, DatasourceOauthTenantParamConfig, DatasourceProvider from models.provider_ids import DatasourceProviderID from services.plugin.plugin_service import PluginService diff --git a/api/services/entities/model_provider_entities.py b/api/services/entities/model_provider_entities.py index a944ef6acd..6679c08ebd 100644 --- a/api/services/entities/model_provider_entities.py +++ b/api/services/entities/model_provider_entities.py @@ -1,15 +1,6 @@ from collections.abc import Sequence from enum import StrEnum -from graphon.model_runtime.entities.common_entities import I18nObject -from graphon.model_runtime.entities.model_entities import ModelType -from graphon.model_runtime.entities.provider_entities import ( - ConfigurateMethod, - ModelCredentialSchema, - ProviderCredentialSchema, - ProviderHelpEntity, - SimpleProviderEntity, -) from pydantic import BaseModel, ConfigDict, model_validator from configs import dify_config @@ -24,6 +15,15 @@ from core.entities.provider_entities import ( QuotaConfiguration, UnaddedModelConfiguration, ) +from graphon.model_runtime.entities.common_entities import I18nObject +from graphon.model_runtime.entities.model_entities import ModelType +from graphon.model_runtime.entities.provider_entities import ( + ConfigurateMethod, + ModelCredentialSchema, + ProviderCredentialSchema, + ProviderHelpEntity, + SimpleProviderEntity, +) from models.provider import ProviderType diff --git a/api/services/external_knowledge_service.py b/api/services/external_knowledge_service.py index 6dcedfdced..60b457ecd0 100644 --- a/api/services/external_knowledge_service.py +++ b/api/services/external_knowledge_service.py @@ -4,13 +4,13 @@ from typing import Any, cast from urllib.parse import urlparse import httpx -from graphon.nodes.http_request.exc import InvalidHttpMethodError from sqlalchemy import func, select from constants import HIDDEN_VALUE from core.helper import ssrf_proxy from core.rag.entities import MetadataFilteringCondition from extensions.ext_database import db +from graphon.nodes.http_request.exc import InvalidHttpMethodError from libs.datetime_utils import naive_utc_now from models.dataset import ( Dataset, diff --git a/api/services/file_service.py b/api/services/file_service.py index 79a935de4b..52da2a7951 100644 --- a/api/services/file_service.py +++ b/api/services/file_service.py @@ -8,7 +8,6 @@ from tempfile import NamedTemporaryFile from typing import Literal from zipfile import ZIP_DEFLATED, ZipFile -from graphon.file import helpers as file_helpers from sqlalchemy import Engine, select from sqlalchemy.orm import Session, sessionmaker from werkzeug.exceptions import NotFound @@ -24,6 +23,7 @@ from core.rag.extractor.extract_processor import ExtractProcessor from extensions.ext_database import db from extensions.ext_storage import storage from extensions.storage.storage_type import StorageType +from graphon.file import helpers as file_helpers from libs.datetime_utils import naive_utc_now from libs.helper import extract_tenant_id from models import Account diff --git a/api/services/hit_testing_service.py b/api/services/hit_testing_service.py index 43985e49cd..ca84b2a3d8 100644 --- a/api/services/hit_testing_service.py +++ b/api/services/hit_testing_service.py @@ -3,8 +3,6 @@ import logging import time from typing import Any, TypedDict -from graphon.model_runtime.entities import LLMMode - from core.app.app_config.entities import ModelConfig from core.rag.datasource.retrieval_service import RetrievalService from core.rag.index_processor.constant.query_type import QueryType @@ -12,6 +10,7 @@ from core.rag.models.document import Document from core.rag.retrieval.dataset_retrieval import DatasetRetrieval from core.rag.retrieval.retrieval_methods import RetrievalMethod from extensions.ext_database import db +from graphon.model_runtime.entities import LLMMode from models import Account from models.dataset import Dataset, DatasetQuery from models.enums import CreatorUserRole, DatasetQuerySource diff --git a/api/services/human_input_delivery_test_service.py b/api/services/human_input_delivery_test_service.py index 77576fa4c0..68ef67dec1 100644 --- a/api/services/human_input_delivery_test_service.py +++ b/api/services/human_input_delivery_test_service.py @@ -4,7 +4,6 @@ from dataclasses import dataclass, field from enum import StrEnum from typing import Protocol -from graphon.runtime import VariablePool from sqlalchemy import Engine, select from sqlalchemy.orm import sessionmaker @@ -18,6 +17,7 @@ from core.workflow.human_input_compat import ( ) from extensions.ext_database import db from extensions.ext_mail import mail +from graphon.runtime import VariablePool from libs.email_template_renderer import render_email_template from models import Account, TenantAccountJoin from services.feature_service import FeatureService diff --git a/api/services/human_input_service.py b/api/services/human_input_service.py index 02a6620fc7..76598d31ac 100644 --- a/api/services/human_input_service.py +++ b/api/services/human_input_service.py @@ -3,12 +3,6 @@ from collections.abc import Mapping from datetime import datetime, timedelta from typing import Any -from graphon.nodes.human_input.entities import ( - FormDefinition, - HumanInputSubmissionValidationError, - validate_human_input_submission, -) -from graphon.nodes.human_input.enums import HumanInputFormKind, HumanInputFormStatus from sqlalchemy import Engine, select from sqlalchemy.orm import Session, sessionmaker @@ -17,6 +11,12 @@ from core.repositories.human_input_repository import ( HumanInputFormRecord, HumanInputFormSubmissionRepository, ) +from graphon.nodes.human_input.entities import ( + FormDefinition, + HumanInputSubmissionValidationError, + validate_human_input_submission, +) +from graphon.nodes.human_input.enums import HumanInputFormKind, HumanInputFormStatus from libs.datetime_utils import ensure_naive_utc, naive_utc_now from libs.exception import BaseHTTPException from models.human_input import RecipientType diff --git a/api/services/message_service.py b/api/services/message_service.py index 5b133b0c04..98f24dd6a6 100644 --- a/api/services/message_service.py +++ b/api/services/message_service.py @@ -1,6 +1,5 @@ from collections.abc import Sequence -from graphon.model_runtime.entities.model_entities import ModelType from pydantic import TypeAdapter from sqlalchemy import select from sqlalchemy.orm import sessionmaker @@ -14,6 +13,7 @@ from core.ops.entities.trace_entity import TraceTaskName from core.ops.ops_trace_manager import TraceQueueManager, TraceTask from core.ops.utils import measure_time from extensions.ext_database import db +from graphon.model_runtime.entities.model_entities import ModelType from libs.infinite_scroll_pagination import InfiniteScrollPagination from models import Account from models.enums import FeedbackFromSource, FeedbackRating diff --git a/api/services/model_load_balancing_service.py b/api/services/model_load_balancing_service.py index b652e049ce..c269346f5f 100644 --- a/api/services/model_load_balancing_service.py +++ b/api/services/model_load_balancing_service.py @@ -2,12 +2,6 @@ import json import logging from typing import Any, TypedDict -from graphon.model_runtime.entities.model_entities import ModelType -from graphon.model_runtime.entities.provider_entities import ( - ModelCredentialSchema, - ProviderCredentialSchema, -) -from graphon.model_runtime.model_providers.model_provider_factory import ModelProviderFactory from sqlalchemy import or_, select from constants import HIDDEN_VALUE @@ -18,6 +12,12 @@ from core.model_manager import LBModelManager from core.plugin.impl.model_runtime_factory import create_plugin_model_assembly, create_plugin_provider_manager from core.provider_manager import ProviderManager from extensions.ext_database import db +from graphon.model_runtime.entities.model_entities import ModelType +from graphon.model_runtime.entities.provider_entities import ( + ModelCredentialSchema, + ProviderCredentialSchema, +) +from graphon.model_runtime.model_providers.model_provider_factory import ModelProviderFactory from libs.datetime_utils import naive_utc_now from models.enums import CredentialSourceType from models.provider import LoadBalancingModelConfig, ProviderCredential, ProviderModelCredential diff --git a/api/services/model_provider_service.py b/api/services/model_provider_service.py index bf208c9bc7..51cda79661 100644 --- a/api/services/model_provider_service.py +++ b/api/services/model_provider_service.py @@ -1,11 +1,10 @@ import logging from typing import Any -from graphon.model_runtime.entities.model_entities import ModelType, ParameterRule - from core.entities.model_entities import ModelWithProviderEntity, ProviderModelWithStatusEntity from core.plugin.impl.model_runtime_factory import create_plugin_model_provider_factory, create_plugin_provider_manager from core.provider_manager import ProviderManager +from graphon.model_runtime.entities.model_entities import ModelType, ParameterRule from models.provider import ProviderType from services.entities.model_provider_entities import ( CustomConfigurationResponse, diff --git a/api/services/rag_pipeline/rag_pipeline.py b/api/services/rag_pipeline/rag_pipeline.py index 605689226a..968600d1bc 100644 --- a/api/services/rag_pipeline/rag_pipeline.py +++ b/api/services/rag_pipeline/rag_pipeline.py @@ -9,15 +9,6 @@ from typing import Any, cast from uuid import uuid4 from flask_login import current_user -from graphon.entities import WorkflowNodeExecution -from graphon.enums import BuiltinNodeTypes, ErrorStrategy, NodeType, WorkflowNodeExecutionStatus -from graphon.errors import WorkflowNodeRunFailedError -from graphon.graph_events import GraphNodeEventBase, NodeRunFailedEvent, NodeRunSucceededEvent -from graphon.node_events import NodeRunResult -from graphon.nodes.base.node import Node -from graphon.nodes.http_request import HTTP_REQUEST_CONFIG_FILTER_KEY, build_http_request_config -from graphon.runtime import VariablePool -from graphon.variables.variables import Variable, VariableBase from sqlalchemy import func, select from sqlalchemy.orm import Session, sessionmaker @@ -53,6 +44,15 @@ from core.workflow.variable_pool_initializer import add_variables_to_pool from core.workflow.workflow_entry import WorkflowEntry from enterprise.telemetry.draft_trace import enqueue_draft_node_execution_trace from extensions.ext_database import db +from graphon.entities import WorkflowNodeExecution +from graphon.enums import BuiltinNodeTypes, ErrorStrategy, NodeType, WorkflowNodeExecutionStatus +from graphon.errors import WorkflowNodeRunFailedError +from graphon.graph_events import GraphNodeEventBase, NodeRunFailedEvent, NodeRunSucceededEvent +from graphon.node_events import NodeRunResult +from graphon.nodes.base.node import Node +from graphon.nodes.http_request import HTTP_REQUEST_CONFIG_FILTER_KEY, build_http_request_config +from graphon.runtime import VariablePool +from graphon.variables.variables import Variable, VariableBase from libs.infinite_scroll_pagination import InfiniteScrollPagination from models import Account from models.dataset import ( # type: ignore diff --git a/api/services/rag_pipeline/rag_pipeline_dsl_service.py b/api/services/rag_pipeline/rag_pipeline_dsl_service.py index 7dd86f1581..f315d053cb 100644 --- a/api/services/rag_pipeline/rag_pipeline_dsl_service.py +++ b/api/services/rag_pipeline/rag_pipeline_dsl_service.py @@ -13,12 +13,6 @@ import yaml # type: ignore from Crypto.Cipher import AES from Crypto.Util.Padding import pad, unpad from flask_login import current_user -from graphon.enums import BuiltinNodeTypes -from graphon.model_runtime.utils.encoders import jsonable_encoder -from graphon.nodes.llm.entities import LLMNodeData -from graphon.nodes.parameter_extractor.entities import ParameterExtractorNodeData -from graphon.nodes.question_classifier.entities import QuestionClassifierNodeData -from graphon.nodes.tool.entities import ToolNodeData from packaging import version from pydantic import BaseModel from sqlalchemy import select @@ -33,6 +27,12 @@ from core.workflow.nodes.knowledge_index import KNOWLEDGE_INDEX_NODE_TYPE from core.workflow.nodes.knowledge_retrieval.entities import KnowledgeRetrievalNodeData from extensions.ext_redis import redis_client from factories import variable_factory +from graphon.enums import BuiltinNodeTypes +from graphon.model_runtime.utils.encoders import jsonable_encoder +from graphon.nodes.llm.entities import LLMNodeData +from graphon.nodes.parameter_extractor.entities import ParameterExtractorNodeData +from graphon.nodes.question_classifier.entities import QuestionClassifierNodeData +from graphon.nodes.tool.entities import ToolNodeData from models import Account from models.dataset import Dataset, DatasetCollectionBinding, Pipeline from models.enums import CollectionBindingType, DatasetRuntimeMode diff --git a/api/services/retention/workflow_run/archive_paid_plan_workflow_run.py b/api/services/retention/workflow_run/archive_paid_plan_workflow_run.py index ab60986bfe..21be411bea 100644 --- a/api/services/retention/workflow_run/archive_paid_plan_workflow_run.py +++ b/api/services/retention/workflow_run/archive_paid_plan_workflow_run.py @@ -27,13 +27,13 @@ from dataclasses import dataclass, field from typing import Any, TypedDict import click -from graphon.enums import WorkflowType from sqlalchemy import inspect from sqlalchemy.orm import Session, sessionmaker from configs import dify_config from enums.cloud_plan import CloudPlan from extensions.ext_database import db +from graphon.enums import WorkflowType from libs.archive_storage import ( ArchiveStorage, ArchiveStorageNotConfiguredError, diff --git a/api/services/summary_index_service.py b/api/services/summary_index_service.py index c906e3bca3..a91f49e9e6 100644 --- a/api/services/summary_index_service.py +++ b/api/services/summary_index_service.py @@ -6,8 +6,6 @@ import uuid from datetime import UTC, datetime from typing import TypedDict, cast -from graphon.model_runtime.entities.llm_entities import LLMUsage -from graphon.model_runtime.entities.model_entities import ModelType from sqlalchemy import select from sqlalchemy.orm import Session @@ -18,6 +16,8 @@ from core.rag.index_processor.constant.doc_type import DocType from core.rag.index_processor.constant.index_type import IndexTechniqueType from core.rag.index_processor.index_processor_base import SummaryIndexSettingDict from core.rag.models.document import Document +from graphon.model_runtime.entities.llm_entities import LLMUsage +from graphon.model_runtime.entities.model_entities import ModelType from libs import helper from models.dataset import Dataset, DocumentSegment, DocumentSegmentSummary from models.dataset import Document as DatasetDocument diff --git a/api/services/tools/api_tools_manage_service.py b/api/services/tools/api_tools_manage_service.py index 3bfa221528..8ad010f62d 100644 --- a/api/services/tools/api_tools_manage_service.py +++ b/api/services/tools/api_tools_manage_service.py @@ -2,7 +2,6 @@ import json import logging from typing import Any, TypedDict, cast -from graphon.model_runtime.utils.encoders import jsonable_encoder from httpx import get from sqlalchemy import select @@ -21,6 +20,7 @@ from core.tools.tool_manager import ToolManager from core.tools.utils.encryption import create_tool_provider_encrypter from core.tools.utils.parser import ApiBasedToolSchemaParser from extensions.ext_database import db +from graphon.model_runtime.utils.encoders import jsonable_encoder from models.tools import ApiToolProvider from services.tools.tools_transform_service import ToolTransformService diff --git a/api/services/tools/workflow_tools_manage_service.py b/api/services/tools/workflow_tools_manage_service.py index be2572b592..8f6600af03 100644 --- a/api/services/tools/workflow_tools_manage_service.py +++ b/api/services/tools/workflow_tools_manage_service.py @@ -3,7 +3,6 @@ import logging from datetime import datetime from typing import Any -from graphon.model_runtime.utils.encoders import jsonable_encoder from sqlalchemy import delete, or_, select from sqlalchemy.orm import sessionmaker @@ -15,6 +14,7 @@ from core.tools.utils.workflow_configuration_sync import WorkflowToolConfigurati from core.tools.workflow_as_tool.provider import WorkflowToolProviderController from core.tools.workflow_as_tool.tool import WorkflowTool from extensions.ext_database import db +from graphon.model_runtime.utils.encoders import jsonable_encoder from models.model import App from models.tools import WorkflowToolProvider from models.workflow import Workflow diff --git a/api/services/trigger/schedule_service.py b/api/services/trigger/schedule_service.py index 25e80770b8..a827222c1d 100644 --- a/api/services/trigger/schedule_service.py +++ b/api/services/trigger/schedule_service.py @@ -2,7 +2,6 @@ import json import logging from datetime import datetime -from graphon.entities.graph_config import NodeConfigDict from sqlalchemy import select from sqlalchemy.orm import Session @@ -14,6 +13,7 @@ from core.workflow.nodes.trigger_schedule.entities import ( VisualConfig, ) from core.workflow.nodes.trigger_schedule.exc import ScheduleConfigError, ScheduleNotFoundError +from graphon.entities.graph_config import NodeConfigDict from libs.schedule_utils import calculate_next_run_at, convert_12h_to_24h from models.account import Account, TenantAccountJoin from models.trigger import WorkflowSchedulePlan diff --git a/api/services/trigger/trigger_service.py b/api/services/trigger/trigger_service.py index 5a5d13b96d..911331e357 100644 --- a/api/services/trigger/trigger_service.py +++ b/api/services/trigger/trigger_service.py @@ -5,7 +5,6 @@ from collections.abc import Mapping from typing import Any from flask import Request, Response -from graphon.entities.graph_config import NodeConfigDict from pydantic import BaseModel from sqlalchemy import select from sqlalchemy.orm import sessionmaker @@ -21,6 +20,7 @@ from core.trigger.utils.encryption import create_trigger_provider_encrypter_for_ from core.workflow.nodes.trigger_plugin.entities import TriggerEventNodeData from extensions.ext_database import db from extensions.ext_redis import redis_client +from graphon.entities.graph_config import NodeConfigDict from models.model import App from models.provider_ids import TriggerProviderID from models.trigger import TriggerSubscription, WorkflowPluginTrigger diff --git a/api/services/trigger/webhook_service.py b/api/services/trigger/webhook_service.py index bb767a6759..ca4e43e516 100644 --- a/api/services/trigger/webhook_service.py +++ b/api/services/trigger/webhook_service.py @@ -7,9 +7,6 @@ from typing import Any, NotRequired, TypedDict import orjson from flask import request -from graphon.entities.graph_config import NodeConfigDict -from graphon.file import FileTransferMethod -from graphon.variables.types import ArrayValidation, SegmentType from pydantic import BaseModel from sqlalchemy import select from sqlalchemy.orm import Session, sessionmaker @@ -31,6 +28,9 @@ from enums.quota_type import QuotaType from extensions.ext_database import db from extensions.ext_redis import redis_client from factories import file_factory +from graphon.entities.graph_config import NodeConfigDict +from graphon.file import FileTransferMethod +from graphon.variables.types import ArrayValidation, SegmentType from models.enums import AppTriggerStatus, AppTriggerType from models.model import App from models.trigger import AppTrigger, WorkflowWebhookTrigger diff --git a/api/services/variable_truncator.py b/api/services/variable_truncator.py index 4d58a9cf12..c96050ce13 100644 --- a/api/services/variable_truncator.py +++ b/api/services/variable_truncator.py @@ -5,6 +5,7 @@ from abc import ABC, abstractmethod from collections.abc import Mapping from typing import Any, overload +from configs import dify_config from graphon.file import File from graphon.nodes.variable_assigner.common.helpers import UpdatedVariable from graphon.variables.segments import ( @@ -21,8 +22,6 @@ from graphon.variables.segments import ( ) from graphon.variables.utils import dumps_with_segments -from configs import dify_config - _MAX_DEPTH = 100 diff --git a/api/services/vector_service.py b/api/services/vector_service.py index 9827c8dfbc..58193d75a9 100644 --- a/api/services/vector_service.py +++ b/api/services/vector_service.py @@ -1,6 +1,5 @@ import logging -from graphon.model_runtime.entities.model_entities import ModelType from sqlalchemy import delete, select from core.model_manager import ModelInstance, ModelManager @@ -13,6 +12,7 @@ from core.rag.index_processor.index_processor_base import BaseIndexProcessor from core.rag.index_processor.index_processor_factory import IndexProcessorFactory from core.rag.models.document import AttachmentDocument, Document from extensions.ext_database import db +from graphon.model_runtime.entities.model_entities import ModelType from models import UploadFile from models.dataset import ChildChunk, Dataset, DatasetProcessRule, DocumentSegment, SegmentAttachmentBinding from models.dataset import Document as DatasetDocument diff --git a/api/services/workflow/workflow_converter.py b/api/services/workflow/workflow_converter.py index 1582bcd46c..5dedb9e372 100644 --- a/api/services/workflow/workflow_converter.py +++ b/api/services/workflow/workflow_converter.py @@ -1,11 +1,6 @@ import json from typing import Any, TypedDict -from graphon.file import FileUploadConfig -from graphon.model_runtime.entities.llm_entities import LLMMode -from graphon.model_runtime.utils.encoders import jsonable_encoder -from graphon.nodes import BuiltinNodeTypes -from graphon.variables.input_entities import VariableEntity from sqlalchemy import select from core.app.app_config.entities import ( @@ -24,6 +19,11 @@ from core.prompt.simple_prompt_transform import SimplePromptTransform from core.prompt.utils.prompt_template_parser import PromptTemplateParser from events.app_event import app_was_created from extensions.ext_database import db +from graphon.file import FileUploadConfig +from graphon.model_runtime.entities.llm_entities import LLMMode +from graphon.model_runtime.utils.encoders import jsonable_encoder +from graphon.nodes import BuiltinNodeTypes +from graphon.variables.input_entities import VariableEntity from models import Account from models.api_based_extension import APIBasedExtension, APIBasedExtensionPoint from models.model import App, AppMode, AppModelConfig, IconType diff --git a/api/services/workflow_app_service.py b/api/services/workflow_app_service.py index b5ab176ad2..59e02ec9b9 100644 --- a/api/services/workflow_app_service.py +++ b/api/services/workflow_app_service.py @@ -3,10 +3,10 @@ import uuid from datetime import datetime from typing import Any, TypedDict -from graphon.enums import WorkflowExecutionStatus from sqlalchemy import and_, func, or_, select from sqlalchemy.orm import Session +from graphon.enums import WorkflowExecutionStatus from models import Account, App, EndUser, TenantAccountJoin, WorkflowAppLog, WorkflowArchiveLog, WorkflowRun from models.enums import AppTriggerType, CreatorUserRole from models.trigger import WorkflowTriggerLog diff --git a/api/services/workflow_draft_variable_service.py b/api/services/workflow_draft_variable_service.py index 2cc6e21574..5ec00ee336 100644 --- a/api/services/workflow_draft_variable_service.py +++ b/api/services/workflow_draft_variable_service.py @@ -7,19 +7,6 @@ from datetime import datetime from enum import StrEnum from typing import Any, ClassVar, NotRequired, TypedDict -from graphon.enums import NodeType -from graphon.file import File -from graphon.nodes import BuiltinNodeTypes -from graphon.nodes.variable_assigner.common.helpers import get_updated_variables -from graphon.variable_loader import VariableLoader -from graphon.variables import Segment, StringSegment, VariableBase -from graphon.variables.consts import SELECTORS_LENGTH -from graphon.variables.segments import ( - ArrayFileSegment, - FileSegment, -) -from graphon.variables.types import SegmentType -from graphon.variables.utils import dumps_with_segments from sqlalchemy import Engine, delete, orm, select from sqlalchemy.dialects.mysql import insert as mysql_insert from sqlalchemy.dialects.postgresql import insert as pg_insert @@ -40,6 +27,19 @@ from core.workflow.variable_prefixes import ( from extensions.ext_storage import storage from factories.file_factory import StorageKeyLoader from factories.variable_factory import build_segment, segment_to_variable +from graphon.enums import NodeType +from graphon.file import File +from graphon.nodes import BuiltinNodeTypes +from graphon.nodes.variable_assigner.common.helpers import get_updated_variables +from graphon.variable_loader import VariableLoader +from graphon.variables import Segment, StringSegment, VariableBase +from graphon.variables.consts import SELECTORS_LENGTH +from graphon.variables.segments import ( + ArrayFileSegment, + FileSegment, +) +from graphon.variables.types import SegmentType +from graphon.variables.utils import dumps_with_segments from libs.datetime_utils import naive_utc_now from libs.uuid_utils import uuidv7 from models import Account, App, Conversation diff --git a/api/services/workflow_event_snapshot_service.py b/api/services/workflow_event_snapshot_service.py index 601e9261fc..5fca444723 100644 --- a/api/services/workflow_event_snapshot_service.py +++ b/api/services/workflow_event_snapshot_service.py @@ -9,10 +9,6 @@ from collections.abc import Generator, Mapping, Sequence from dataclasses import dataclass from typing import Any -from graphon.entities import WorkflowStartReason -from graphon.enums import WorkflowExecutionStatus, WorkflowNodeExecutionStatus -from graphon.runtime import GraphRuntimeState -from graphon.workflow_type_encoder import WorkflowRuntimeTypeConverter from sqlalchemy import desc, select from sqlalchemy.orm import Session, sessionmaker @@ -26,6 +22,10 @@ from core.app.entities.task_entities import ( WorkflowStartStreamResponse, ) from core.app.layers.pause_state_persist_layer import WorkflowResumptionContext +from graphon.entities import WorkflowStartReason +from graphon.enums import WorkflowExecutionStatus, WorkflowNodeExecutionStatus +from graphon.runtime import GraphRuntimeState +from graphon.workflow_type_encoder import WorkflowRuntimeTypeConverter from models.model import AppMode, Message from models.workflow import WorkflowNodeExecutionTriggeredFrom, WorkflowRun from repositories.api_workflow_node_execution_repository import WorkflowNodeExecutionSnapshot diff --git a/api/services/workflow_service.py b/api/services/workflow_service.py index 55a93c33b1..d71223314e 100644 --- a/api/services/workflow_service.py +++ b/api/services/workflow_service.py @@ -5,31 +5,6 @@ import uuid from collections.abc import Callable, Generator, Mapping, Sequence from typing import Any, cast -from graphon.entities import WorkflowNodeExecution -from graphon.entities.graph_config import NodeConfigDict -from graphon.entities.pause_reason import HumanInputRequired -from graphon.enums import ( - ErrorStrategy, - NodeType, - WorkflowNodeExecutionMetadataKey, - WorkflowNodeExecutionStatus, -) -from graphon.errors import WorkflowNodeRunFailedError -from graphon.file import File -from graphon.graph_events import GraphNodeEventBase, NodeRunFailedEvent, NodeRunSucceededEvent -from graphon.node_events import NodeRunResult -from graphon.nodes import BuiltinNodeTypes -from graphon.nodes.base.node import Node -from graphon.nodes.http_request import HTTP_REQUEST_CONFIG_FILTER_KEY, build_http_request_config -from graphon.nodes.human_input.entities import HumanInputNodeData, validate_human_input_submission -from graphon.nodes.human_input.enums import HumanInputFormKind -from graphon.nodes.human_input.human_input_node import HumanInputNode -from graphon.nodes.start.entities import StartNodeData -from graphon.runtime import GraphRuntimeState, VariablePool -from graphon.variable_loader import load_into_variable_pool -from graphon.variables import VariableBase -from graphon.variables.input_entities import VariableEntityType -from graphon.variables.variables import Variable from sqlalchemy import exists, select from sqlalchemy.orm import Session, sessionmaker @@ -64,6 +39,31 @@ from events.app_event import app_draft_workflow_was_synced, app_published_workfl from extensions.ext_database import db from extensions.ext_storage import storage from factories.file_factory import build_from_mapping, build_from_mappings +from graphon.entities import WorkflowNodeExecution +from graphon.entities.graph_config import NodeConfigDict +from graphon.entities.pause_reason import HumanInputRequired +from graphon.enums import ( + ErrorStrategy, + NodeType, + WorkflowNodeExecutionMetadataKey, + WorkflowNodeExecutionStatus, +) +from graphon.errors import WorkflowNodeRunFailedError +from graphon.file import File +from graphon.graph_events import GraphNodeEventBase, NodeRunFailedEvent, NodeRunSucceededEvent +from graphon.node_events import NodeRunResult +from graphon.nodes import BuiltinNodeTypes +from graphon.nodes.base.node import Node +from graphon.nodes.http_request import HTTP_REQUEST_CONFIG_FILTER_KEY, build_http_request_config +from graphon.nodes.human_input.entities import HumanInputNodeData, validate_human_input_submission +from graphon.nodes.human_input.enums import HumanInputFormKind +from graphon.nodes.human_input.human_input_node import HumanInputNode +from graphon.nodes.start.entities import StartNodeData +from graphon.runtime import GraphRuntimeState, VariablePool +from graphon.variable_loader import load_into_variable_pool +from graphon.variables import VariableBase +from graphon.variables.input_entities import VariableEntityType +from graphon.variables.variables import Variable from libs.datetime_utils import naive_utc_now from models import Account from models.human_input import HumanInputFormRecipient, RecipientType diff --git a/api/tasks/app_generate/workflow_execute_task.py b/api/tasks/app_generate/workflow_execute_task.py index 8f2f5f261e..c22e7e9918 100644 --- a/api/tasks/app_generate/workflow_execute_task.py +++ b/api/tasks/app_generate/workflow_execute_task.py @@ -7,7 +7,6 @@ from typing import Annotated, Any from celery import shared_task from flask import current_app, json -from graphon.runtime import GraphRuntimeState from pydantic import BaseModel, Discriminator, Field, Tag from sqlalchemy import Engine, select from sqlalchemy.orm import Session, sessionmaker @@ -23,6 +22,7 @@ from core.app.entities.app_invoke_entities import ( from core.app.layers.pause_state_persist_layer import PauseStateLayerConfig, WorkflowResumptionContext from core.repositories import DifyCoreRepositoryFactory from extensions.ext_database import db +from graphon.runtime import GraphRuntimeState from libs.flask_utils import set_login_user from models.account import Account from models.enums import CreatorUserRole, WorkflowRunTriggeredFrom diff --git a/api/tasks/async_workflow_tasks.py b/api/tasks/async_workflow_tasks.py index 9ff34c7c48..5809268992 100644 --- a/api/tasks/async_workflow_tasks.py +++ b/api/tasks/async_workflow_tasks.py @@ -10,7 +10,6 @@ from datetime import UTC, datetime from typing import Any, NotRequired from celery import shared_task -from graphon.runtime import GraphRuntimeState from sqlalchemy import select from sqlalchemy.orm import Session, sessionmaker from typing_extensions import TypedDict @@ -24,6 +23,7 @@ from core.app.layers.trigger_post_layer import TriggerPostLayer from core.db.session_factory import session_factory from core.repositories import DifyCoreRepositoryFactory from extensions.ext_database import db +from graphon.runtime import GraphRuntimeState from models.account import Account from models.enums import CreatorUserRole, WorkflowRunTriggeredFrom, WorkflowTriggerStatus from models.model import App, EndUser, Tenant diff --git a/api/tasks/batch_create_segment_to_index_task.py b/api/tasks/batch_create_segment_to_index_task.py index 4db551c73c..beb23d8354 100644 --- a/api/tasks/batch_create_segment_to_index_task.py +++ b/api/tasks/batch_create_segment_to_index_task.py @@ -8,7 +8,6 @@ from typing import Any import click import pandas as pd from celery import shared_task -from graphon.model_runtime.entities.model_entities import ModelType from sqlalchemy import func, select from core.db.session_factory import session_factory @@ -16,6 +15,7 @@ from core.model_manager import ModelManager from core.rag.index_processor.constant.index_type import IndexStructureType, IndexTechniqueType from extensions.ext_redis import redis_client from extensions.ext_storage import storage +from graphon.model_runtime.entities.model_entities import ModelType from libs import helper from libs.datetime_utils import naive_utc_now from models.dataset import Dataset, Document, DocumentSegment diff --git a/api/tasks/human_input_timeout_tasks.py b/api/tasks/human_input_timeout_tasks.py index ca73b4d374..fd743205a1 100644 --- a/api/tasks/human_input_timeout_tasks.py +++ b/api/tasks/human_input_timeout_tasks.py @@ -2,8 +2,6 @@ import logging from datetime import timedelta from celery import shared_task -from graphon.enums import WorkflowExecutionStatus -from graphon.nodes.human_input.enums import HumanInputFormKind, HumanInputFormStatus from sqlalchemy import or_, select from sqlalchemy.orm import sessionmaker @@ -11,6 +9,8 @@ from configs import dify_config from core.repositories.human_input_repository import HumanInputFormSubmissionRepository from extensions.ext_database import db from extensions.ext_storage import storage +from graphon.enums import WorkflowExecutionStatus +from graphon.nodes.human_input.enums import HumanInputFormKind, HumanInputFormStatus from libs.datetime_utils import ensure_naive_utc, naive_utc_now from models.human_input import HumanInputForm from models.workflow import WorkflowPause, WorkflowRun diff --git a/api/tasks/mail_human_input_delivery_task.py b/api/tasks/mail_human_input_delivery_task.py index a316eec7b9..f8ae3f4b6e 100644 --- a/api/tasks/mail_human_input_delivery_task.py +++ b/api/tasks/mail_human_input_delivery_task.py @@ -6,7 +6,6 @@ from typing import Any import click from celery import shared_task -from graphon.runtime import GraphRuntimeState, VariablePool from sqlalchemy import select from sqlalchemy.orm import Session, sessionmaker @@ -15,6 +14,7 @@ from core.app.layers.pause_state_persist_layer import WorkflowResumptionContext from core.workflow.human_input_compat import EmailDeliveryConfig, EmailDeliveryMethod from extensions.ext_database import db from extensions.ext_mail import mail +from graphon.runtime import GraphRuntimeState, VariablePool from models.human_input import ( DeliveryMethodType, HumanInputDelivery, diff --git a/api/tasks/trigger_processing_tasks.py b/api/tasks/trigger_processing_tasks.py index 56626e372e..25ea53dfac 100644 --- a/api/tasks/trigger_processing_tasks.py +++ b/api/tasks/trigger_processing_tasks.py @@ -12,7 +12,6 @@ from datetime import UTC, datetime from typing import Any from celery import shared_task -from graphon.enums import WorkflowExecutionStatus from sqlalchemy import func, select from sqlalchemy.orm import Session @@ -29,6 +28,7 @@ from core.trigger.provider import PluginTriggerProviderController from core.trigger.trigger_manager import TriggerManager from core.workflow.nodes.trigger_plugin.entities import TriggerEventNodeData from enums.quota_type import QuotaType, unlimited +from graphon.enums import WorkflowExecutionStatus from models.enums import ( AppTriggerType, CreatorUserRole, diff --git a/api/tasks/workflow_execution_tasks.py b/api/tasks/workflow_execution_tasks.py index b4f975f4da..5ca04fd7c2 100644 --- a/api/tasks/workflow_execution_tasks.py +++ b/api/tasks/workflow_execution_tasks.py @@ -10,11 +10,11 @@ import logging from typing import Any from celery import shared_task -from graphon.entities import WorkflowExecution -from graphon.workflow_type_encoder import WorkflowRuntimeTypeConverter from sqlalchemy import select from core.db.session_factory import session_factory +from graphon.entities import WorkflowExecution +from graphon.workflow_type_encoder import WorkflowRuntimeTypeConverter from models import CreatorUserRole, WorkflowRun from models.enums import WorkflowRunTriggeredFrom diff --git a/api/tasks/workflow_node_execution_tasks.py b/api/tasks/workflow_node_execution_tasks.py index 128cdd72e1..0d5475a56d 100644 --- a/api/tasks/workflow_node_execution_tasks.py +++ b/api/tasks/workflow_node_execution_tasks.py @@ -10,13 +10,13 @@ import logging from typing import Any from celery import shared_task +from sqlalchemy import select + +from core.db.session_factory import session_factory from graphon.entities.workflow_node_execution import ( WorkflowNodeExecution, ) from graphon.workflow_type_encoder import WorkflowRuntimeTypeConverter -from sqlalchemy import select - -from core.db.session_factory import session_factory from models import CreatorUserRole, WorkflowNodeExecutionModel from models.workflow import WorkflowNodeExecutionTriggeredFrom diff --git a/api/tests/integration_tests/core/datasource/test_datasource_manager_integration.py b/api/tests/integration_tests/core/datasource/test_datasource_manager_integration.py index 91245e879e..a876b0c4aa 100644 --- a/api/tests/integration_tests/core/datasource/test_datasource_manager_integration.py +++ b/api/tests/integration_tests/core/datasource/test_datasource_manager_integration.py @@ -1,9 +1,8 @@ from collections.abc import Generator -from graphon.node_events import StreamCompletedEvent - from core.datasource.datasource_manager import DatasourceManager from core.datasource.entities.datasource_entities import DatasourceMessage +from graphon.node_events import StreamCompletedEvent def _gen_var_stream() -> Generator[DatasourceMessage, None, None]: diff --git a/api/tests/integration_tests/core/workflow/nodes/datasource/test_datasource_node_integration.py b/api/tests/integration_tests/core/workflow/nodes/datasource/test_datasource_node_integration.py index 3fdea10976..b5318aaa2b 100644 --- a/api/tests/integration_tests/core/workflow/nodes/datasource/test_datasource_node_integration.py +++ b/api/tests/integration_tests/core/workflow/nodes/datasource/test_datasource_node_integration.py @@ -1,8 +1,7 @@ -from graphon.enums import WorkflowNodeExecutionStatus -from graphon.node_events import NodeRunResult, StreamCompletedEvent - from core.app.entities.app_invoke_entities import DIFY_RUN_CONTEXT_KEY from core.workflow.nodes.datasource.datasource_node import DatasourceNode +from graphon.enums import WorkflowNodeExecutionStatus +from graphon.node_events import NodeRunResult, StreamCompletedEvent class _Seg: diff --git a/api/tests/integration_tests/model_runtime/__mock/plugin_model.py b/api/tests/integration_tests/model_runtime/__mock/plugin_model.py index ce04a158a8..c4146d5ccd 100644 --- a/api/tests/integration_tests/model_runtime/__mock/plugin_model.py +++ b/api/tests/integration_tests/model_runtime/__mock/plugin_model.py @@ -4,6 +4,9 @@ from collections.abc import Generator, Sequence from decimal import Decimal from json import dumps +from core.plugin.entities.plugin_daemon import PluginModelProviderEntity +from core.plugin.impl.model import PluginModelClient + # import monkeypatch from graphon.model_runtime.entities.common_entities import I18nObject from graphon.model_runtime.entities.llm_entities import ( @@ -23,9 +26,6 @@ from graphon.model_runtime.entities.model_entities import ( ) from graphon.model_runtime.entities.provider_entities import ConfigurateMethod, ProviderEntity -from core.plugin.entities.plugin_daemon import PluginModelProviderEntity -from core.plugin.impl.model import PluginModelClient - class MockModelClass(PluginModelClient): def fetch_model_providers(self, tenant_id: str) -> Sequence[PluginModelProviderEntity]: diff --git a/api/tests/integration_tests/services/test_workflow_draft_variable_service.py b/api/tests/integration_tests/services/test_workflow_draft_variable_service.py index c7bb90f019..e130644338 100644 --- a/api/tests/integration_tests/services/test_workflow_draft_variable_service.py +++ b/api/tests/integration_tests/services/test_workflow_draft_variable_service.py @@ -3,10 +3,6 @@ import unittest import uuid import pytest -from graphon.nodes import BuiltinNodeTypes -from graphon.variables.segments import StringSegment -from graphon.variables.types import SegmentType -from graphon.variables.variables import StringVariable from sqlalchemy import delete, func, select from sqlalchemy.orm import Session @@ -15,6 +11,10 @@ from extensions.ext_database import db from extensions.ext_storage import storage from extensions.storage.storage_type import StorageType from factories.variable_factory import build_segment +from graphon.nodes import BuiltinNodeTypes +from graphon.variables.segments import StringSegment +from graphon.variables.types import SegmentType +from graphon.variables.variables import StringVariable from libs import datetime_utils from models.enums import CreatorUserRole from models.model import UploadFile diff --git a/api/tests/integration_tests/tasks/test_remove_app_and_related_data_task.py b/api/tests/integration_tests/tasks/test_remove_app_and_related_data_task.py index 3dfedd811d..4f444598b1 100644 --- a/api/tests/integration_tests/tasks/test_remove_app_and_related_data_task.py +++ b/api/tests/integration_tests/tasks/test_remove_app_and_related_data_task.py @@ -2,11 +2,11 @@ import uuid from unittest.mock import patch import pytest -from graphon.variables.segments import StringSegment from sqlalchemy import delete, func, select from core.db.session_factory import session_factory from extensions.storage.storage_type import StorageType +from graphon.variables.segments import StringSegment from models import Tenant from models.enums import CreatorUserRole from models.model import App, UploadFile @@ -209,7 +209,6 @@ class TestDeleteDraftVariablesWithOffloadIntegration: def setup_offload_test_data(self, app_and_tenant): tenant, app = app_and_tenant from graphon.variables.types import SegmentType - from libs.datetime_utils import naive_utc_now with session_factory.create_session() as session: @@ -453,7 +452,6 @@ class TestDeleteDraftVariablesSessionCommit: def setup_offload_test_data(self, app_and_tenant): """Create test data with offload files for session commit tests.""" from graphon.variables.types import SegmentType - from libs.datetime_utils import naive_utc_now tenant, app = app_and_tenant diff --git a/api/tests/integration_tests/workflow/nodes/__mock/model.py b/api/tests/integration_tests/workflow/nodes/__mock/model.py index c0143faa85..a9a2617bae 100644 --- a/api/tests/integration_tests/workflow/nodes/__mock/model.py +++ b/api/tests/integration_tests/workflow/nodes/__mock/model.py @@ -1,12 +1,11 @@ from unittest.mock import MagicMock -from graphon.model_runtime.entities.model_entities import ModelType - from core.app.entities.app_invoke_entities import ModelConfigWithCredentialsEntity from core.entities.provider_configuration import ProviderConfiguration, ProviderModelBundle from core.entities.provider_entities import CustomConfiguration, CustomProviderConfiguration, SystemConfiguration from core.model_manager import ModelInstance from core.plugin.impl.model_runtime_factory import create_plugin_model_provider_factory +from graphon.model_runtime.entities.model_entities import ModelType from models.provider import ProviderType diff --git a/api/tests/integration_tests/workflow/nodes/test_code.py b/api/tests/integration_tests/workflow/nodes/test_code.py index 4f41396c22..e3476c292b 100644 --- a/api/tests/integration_tests/workflow/nodes/test_code.py +++ b/api/tests/integration_tests/workflow/nodes/test_code.py @@ -2,17 +2,17 @@ import time import uuid import pytest + +from configs import dify_config +from core.app.entities.app_invoke_entities import InvokeFrom, UserFrom +from core.workflow.node_factory import DifyNodeFactory +from core.workflow.system_variables import build_system_variables from graphon.enums import WorkflowNodeExecutionStatus from graphon.graph import Graph from graphon.node_events import NodeRunResult from graphon.nodes.code.code_node import CodeNode from graphon.nodes.code.limits import CodeNodeLimits from graphon.runtime import GraphRuntimeState, VariablePool - -from configs import dify_config -from core.app.entities.app_invoke_entities import InvokeFrom, UserFrom -from core.workflow.node_factory import DifyNodeFactory -from core.workflow.system_variables import build_system_variables from tests.workflow_test_utils import build_test_graph_init_params pytest_plugins = ("tests.integration_tests.workflow.nodes.__mock.code_executor",) diff --git a/api/tests/integration_tests/workflow/nodes/test_http.py b/api/tests/integration_tests/workflow/nodes/test_http.py index b1f937e738..aa6cf1e021 100644 --- a/api/tests/integration_tests/workflow/nodes/test_http.py +++ b/api/tests/integration_tests/workflow/nodes/test_http.py @@ -3,11 +3,6 @@ import uuid from urllib.parse import urlencode import pytest -from graphon.enums import WorkflowNodeExecutionStatus -from graphon.file.file_manager import file_manager -from graphon.graph import Graph -from graphon.nodes.http_request import HttpRequestNode, HttpRequestNodeConfig -from graphon.runtime import GraphRuntimeState, VariablePool from configs import dify_config from core.app.entities.app_invoke_entities import InvokeFrom, UserFrom @@ -16,6 +11,11 @@ from core.tools.tool_file_manager import ToolFileManager from core.workflow.node_factory import DifyNodeFactory from core.workflow.node_runtime import DifyFileReferenceFactory from core.workflow.system_variables import build_system_variables +from graphon.enums import WorkflowNodeExecutionStatus +from graphon.file.file_manager import file_manager +from graphon.graph import Graph +from graphon.nodes.http_request import HttpRequestNode, HttpRequestNodeConfig +from graphon.runtime import GraphRuntimeState, VariablePool from tests.workflow_test_utils import build_test_graph_init_params pytest_plugins = ("tests.integration_tests.workflow.nodes.__mock.http",) @@ -192,6 +192,7 @@ def test_custom_authorization_header(setup_http_mock): @pytest.mark.parametrize("setup_http_mock", [["none"]], indirect=True) def test_custom_auth_with_empty_api_key_raises_error(setup_http_mock): """Test: In custom authentication mode, when the api_key is empty, AuthorizationConfigError should be raised.""" + from core.workflow.system_variables import build_system_variables from graphon.enums import BuiltinNodeTypes from graphon.nodes.http_request.entities import ( HttpRequestNodeAuthorization, @@ -202,8 +203,6 @@ def test_custom_auth_with_empty_api_key_raises_error(setup_http_mock): from graphon.nodes.http_request.executor import Executor from graphon.runtime import VariablePool - from core.workflow.system_variables import build_system_variables - # Create variable pool variable_pool = VariablePool( system_variables=build_system_variables(user_id="test", files=[]), diff --git a/api/tests/integration_tests/workflow/nodes/test_llm.py b/api/tests/integration_tests/workflow/nodes/test_llm.py index f0f3fcead1..fa5d63cfbf 100644 --- a/api/tests/integration_tests/workflow/nodes/test_llm.py +++ b/api/tests/integration_tests/workflow/nodes/test_llm.py @@ -4,6 +4,11 @@ import uuid from collections.abc import Generator from unittest.mock import MagicMock, patch +from core.app.entities.app_invoke_entities import InvokeFrom, UserFrom +from core.llm_generator.output_parser.structured_output import _parse_structured_output +from core.model_manager import ModelInstance +from core.workflow.system_variables import build_system_variables +from extensions.ext_database import db from graphon.enums import WorkflowNodeExecutionStatus from graphon.node_events import StreamCompletedEvent from graphon.nodes.llm.file_saver import LLMFileSaver @@ -12,12 +17,6 @@ from graphon.nodes.llm.protocols import CredentialsProvider, ModelFactory from graphon.nodes.llm.runtime_protocols import PromptMessageSerializerProtocol from graphon.nodes.protocols import HttpClientProtocol from graphon.runtime import GraphRuntimeState, VariablePool - -from core.app.entities.app_invoke_entities import InvokeFrom, UserFrom -from core.llm_generator.output_parser.structured_output import _parse_structured_output -from core.model_manager import ModelInstance -from core.workflow.system_variables import build_system_variables -from extensions.ext_database import db from tests.workflow_test_utils import build_test_graph_init_params """FOR MOCK FIXTURES, DO NOT REMOVE""" diff --git a/api/tests/integration_tests/workflow/nodes/test_parameter_extractor.py b/api/tests/integration_tests/workflow/nodes/test_parameter_extractor.py index fe512c2585..52886855b8 100644 --- a/api/tests/integration_tests/workflow/nodes/test_parameter_extractor.py +++ b/api/tests/integration_tests/workflow/nodes/test_parameter_extractor.py @@ -3,17 +3,16 @@ import time import uuid from unittest.mock import MagicMock -from graphon.enums import WorkflowNodeExecutionStatus -from graphon.model_runtime.entities import AssistantPromptMessage, UserPromptMessage -from graphon.nodes.llm.protocols import CredentialsProvider, ModelFactory -from graphon.nodes.parameter_extractor.parameter_extractor_node import ParameterExtractorNode -from graphon.runtime import GraphRuntimeState, VariablePool - from core.app.entities.app_invoke_entities import InvokeFrom, UserFrom from core.model_manager import ModelInstance from core.workflow.node_runtime import DifyPromptMessageSerializer from core.workflow.system_variables import build_system_variables from extensions.ext_database import db +from graphon.enums import WorkflowNodeExecutionStatus +from graphon.model_runtime.entities import AssistantPromptMessage, UserPromptMessage +from graphon.nodes.llm.protocols import CredentialsProvider, ModelFactory +from graphon.nodes.parameter_extractor.parameter_extractor_node import ParameterExtractorNode +from graphon.runtime import GraphRuntimeState, VariablePool from tests.integration_tests.workflow.nodes.__mock.model import get_mocked_fetch_model_instance from tests.workflow_test_utils import build_test_graph_init_params diff --git a/api/tests/integration_tests/workflow/nodes/test_template_transform.py b/api/tests/integration_tests/workflow/nodes/test_template_transform.py index 2d728569be..9e3e1a47e3 100644 --- a/api/tests/integration_tests/workflow/nodes/test_template_transform.py +++ b/api/tests/integration_tests/workflow/nodes/test_template_transform.py @@ -1,15 +1,14 @@ import time import uuid +from core.app.entities.app_invoke_entities import InvokeFrom, UserFrom +from core.workflow.node_factory import DifyNodeFactory +from core.workflow.system_variables import build_system_variables from graphon.enums import WorkflowNodeExecutionStatus from graphon.graph import Graph from graphon.nodes.template_transform.template_transform_node import TemplateTransformNode from graphon.runtime import GraphRuntimeState, VariablePool from graphon.template_rendering import TemplateRenderError - -from core.app.entities.app_invoke_entities import InvokeFrom, UserFrom -from core.workflow.node_factory import DifyNodeFactory -from core.workflow.system_variables import build_system_variables from tests.workflow_test_utils import build_test_graph_init_params diff --git a/api/tests/integration_tests/workflow/nodes/test_tool.py b/api/tests/integration_tests/workflow/nodes/test_tool.py index 750ced7075..f9ec51ee10 100644 --- a/api/tests/integration_tests/workflow/nodes/test_tool.py +++ b/api/tests/integration_tests/workflow/nodes/test_tool.py @@ -2,18 +2,17 @@ import time import uuid from unittest.mock import MagicMock, patch -from graphon.enums import WorkflowNodeExecutionStatus -from graphon.graph import Graph -from graphon.node_events import StreamCompletedEvent -from graphon.nodes.protocols import ToolFileManagerProtocol -from graphon.nodes.tool.tool_node import ToolNode -from graphon.runtime import GraphRuntimeState, VariablePool - from core.app.entities.app_invoke_entities import InvokeFrom, UserFrom from core.tools.utils.configuration import ToolParameterConfigurationManager from core.workflow.node_factory import DifyNodeFactory from core.workflow.node_runtime import DifyToolNodeRuntime from core.workflow.system_variables import build_system_variables +from graphon.enums import WorkflowNodeExecutionStatus +from graphon.graph import Graph +from graphon.node_events import StreamCompletedEvent +from graphon.nodes.protocols import ToolFileManagerProtocol +from graphon.nodes.tool.tool_node import ToolNode +from graphon.runtime import GraphRuntimeState, VariablePool from tests.workflow_test_utils import build_test_graph_init_params diff --git a/api/tests/test_containers_integration_tests/controllers/console/app/test_chat_conversation_status_count_api.py b/api/tests/test_containers_integration_tests/controllers/console/app/test_chat_conversation_status_count_api.py index ea95959a82..5a22f81a69 100644 --- a/api/tests/test_containers_integration_tests/controllers/console/app/test_chat_conversation_status_count_api.py +++ b/api/tests/test_containers_integration_tests/controllers/console/app/test_chat_conversation_status_count_api.py @@ -4,11 +4,11 @@ import json import uuid from flask.testing import FlaskClient -from graphon.enums import WorkflowExecutionStatus from sqlalchemy.orm import Session from configs import dify_config from constants import HEADER_NAME_CSRF_TOKEN +from graphon.enums import WorkflowExecutionStatus from libs.datetime_utils import naive_utc_now from libs.token import _real_cookie_name, generate_csrf_token from models import Account, DifySetup, Tenant, TenantAccountJoin diff --git a/api/tests/test_containers_integration_tests/controllers/console/app/test_workflow_draft_variable.py b/api/tests/test_containers_integration_tests/controllers/console/app/test_workflow_draft_variable.py index 8ddf867370..290be87697 100644 --- a/api/tests/test_containers_integration_tests/controllers/console/app/test_workflow_draft_variable.py +++ b/api/tests/test_containers_integration_tests/controllers/console/app/test_workflow_draft_variable.py @@ -3,12 +3,12 @@ import uuid from flask.testing import FlaskClient -from graphon.variables.segments import StringSegment from sqlalchemy import select from sqlalchemy.orm import Session from core.workflow.variable_prefixes import CONVERSATION_VARIABLE_NODE_ID, ENVIRONMENT_VARIABLE_NODE_ID from factories.variable_factory import segment_to_variable +from graphon.variables.segments import StringSegment from models import Workflow from models.model import AppMode from models.workflow import WorkflowDraftVariable diff --git a/api/tests/test_containers_integration_tests/core/app/layers/test_pause_state_persist_layer.py b/api/tests/test_containers_integration_tests/core/app/layers/test_pause_state_persist_layer.py index b4b65abdb6..c342e8994b 100644 --- a/api/tests/test_containers_integration_tests/core/app/layers/test_pause_state_persist_layer.py +++ b/api/tests/test_containers_integration_tests/core/app/layers/test_pause_state_persist_layer.py @@ -22,13 +22,6 @@ import uuid from time import time import pytest -from graphon.entities.pause_reason import SchedulingPause -from graphon.enums import WorkflowExecutionStatus -from graphon.graph_engine.entities.commands import GraphEngineCommand -from graphon.graph_engine.layers.base import GraphEngineLayerNotInitializedError -from graphon.graph_events import GraphRunPausedEvent -from graphon.model_runtime.entities.llm_entities import LLMUsage -from graphon.runtime import GraphRuntimeState, ReadOnlyGraphRuntimeState, ReadOnlyGraphRuntimeStateWrapper, VariablePool from sqlalchemy import Engine, delete, select from sqlalchemy.orm import Session @@ -40,6 +33,13 @@ from core.app.layers.pause_state_persist_layer import ( ) from core.workflow.system_variables import build_system_variables from extensions.ext_storage import storage +from graphon.entities.pause_reason import SchedulingPause +from graphon.enums import WorkflowExecutionStatus +from graphon.graph_engine.entities.commands import GraphEngineCommand +from graphon.graph_engine.layers.base import GraphEngineLayerNotInitializedError +from graphon.graph_events import GraphRunPausedEvent +from graphon.model_runtime.entities.llm_entities import LLMUsage +from graphon.runtime import GraphRuntimeState, ReadOnlyGraphRuntimeState, ReadOnlyGraphRuntimeStateWrapper, VariablePool from libs.datetime_utils import naive_utc_now from models import Account from models import WorkflowPause as WorkflowPauseModel diff --git a/api/tests/test_containers_integration_tests/core/repositories/test_human_input_form_repository_impl.py b/api/tests/test_containers_integration_tests/core/repositories/test_human_input_form_repository_impl.py index 3b1570a9a8..14d5740072 100644 --- a/api/tests/test_containers_integration_tests/core/repositories/test_human_input_form_repository_impl.py +++ b/api/tests/test_containers_integration_tests/core/repositories/test_human_input_form_repository_impl.py @@ -4,7 +4,6 @@ from __future__ import annotations from uuid import uuid4 -from graphon.nodes.human_input.entities import FormDefinition, HumanInputNodeData, UserAction from sqlalchemy import Engine, select from sqlalchemy.orm import Session @@ -18,6 +17,7 @@ from core.workflow.human_input_compat import ( MemberRecipient, WebAppDeliveryMethod, ) +from graphon.nodes.human_input.entities import FormDefinition, HumanInputNodeData, UserAction from models.account import ( Account, AccountStatus, diff --git a/api/tests/test_containers_integration_tests/core/workflow/test_human_input_resume_node_execution.py b/api/tests/test_containers_integration_tests/core/workflow/test_human_input_resume_node_execution.py index 3ecf621095..da4f8847d6 100644 --- a/api/tests/test_containers_integration_tests/core/workflow/test_human_input_resume_node_execution.py +++ b/api/tests/test_containers_integration_tests/core/workflow/test_human_input_resume_node_execution.py @@ -4,6 +4,17 @@ from datetime import timedelta from unittest.mock import MagicMock import pytest +from sqlalchemy import delete, select +from sqlalchemy.orm import Session + +from core.app.app_config.entities import WorkflowUIBasedAppConfig +from core.app.entities.app_invoke_entities import InvokeFrom, WorkflowAppGenerateEntity +from core.app.workflow.layers import PersistenceWorkflowInfo, WorkflowPersistenceLayer +from core.repositories.human_input_repository import HumanInputFormEntity, HumanInputFormRepository +from core.repositories.sqlalchemy_workflow_execution_repository import SQLAlchemyWorkflowExecutionRepository +from core.repositories.sqlalchemy_workflow_node_execution_repository import SQLAlchemyWorkflowNodeExecutionRepository +from core.workflow.node_runtime import DifyHumanInputNodeRuntime +from core.workflow.system_variables import build_system_variables from graphon.enums import WorkflowType from graphon.graph import Graph from graphon.graph_engine import GraphEngine @@ -16,17 +27,6 @@ from graphon.nodes.human_input.human_input_node import HumanInputNode from graphon.nodes.start.entities import StartNodeData from graphon.nodes.start.start_node import StartNode from graphon.runtime import GraphRuntimeState, VariablePool -from sqlalchemy import delete, select -from sqlalchemy.orm import Session - -from core.app.app_config.entities import WorkflowUIBasedAppConfig -from core.app.entities.app_invoke_entities import InvokeFrom, WorkflowAppGenerateEntity -from core.app.workflow.layers import PersistenceWorkflowInfo, WorkflowPersistenceLayer -from core.repositories.human_input_repository import HumanInputFormEntity, HumanInputFormRepository -from core.repositories.sqlalchemy_workflow_execution_repository import SQLAlchemyWorkflowExecutionRepository -from core.repositories.sqlalchemy_workflow_node_execution_repository import SQLAlchemyWorkflowNodeExecutionRepository -from core.workflow.node_runtime import DifyHumanInputNodeRuntime -from core.workflow.system_variables import build_system_variables from libs.datetime_utils import naive_utc_now from models import Account from models.account import AccountStatus, Tenant, TenantAccountJoin, TenantAccountRole, TenantStatus diff --git a/api/tests/test_containers_integration_tests/factories/test_storage_key_loader.py b/api/tests/test_containers_integration_tests/factories/test_storage_key_loader.py index cc72dc1cf3..2e207ddc67 100644 --- a/api/tests/test_containers_integration_tests/factories/test_storage_key_loader.py +++ b/api/tests/test_containers_integration_tests/factories/test_storage_key_loader.py @@ -4,13 +4,13 @@ from unittest.mock import patch from uuid import uuid4 import pytest -from graphon.file import File, FileTransferMethod, FileType from sqlalchemy.orm import Session from core.app.file_access import DatabaseFileAccessController from extensions.ext_database import db from extensions.storage.storage_type import StorageType from factories.file_factory import StorageKeyLoader +from graphon.file import File, FileTransferMethod, FileType from models import ToolFile, UploadFile from models.enums import CreatorUserRole diff --git a/api/tests/test_containers_integration_tests/helpers/execution_extra_content.py b/api/tests/test_containers_integration_tests/helpers/execution_extra_content.py index b745aed141..2fd289dfbc 100644 --- a/api/tests/test_containers_integration_tests/helpers/execution_extra_content.py +++ b/api/tests/test_containers_integration_tests/helpers/execution_extra_content.py @@ -6,7 +6,6 @@ from decimal import Decimal from uuid import uuid4 from graphon.nodes.human_input.entities import FormDefinition, UserAction - from libs.datetime_utils import naive_utc_now from models.account import Account, Tenant, TenantAccountJoin from models.enums import ConversationFromSource, InvokeFrom diff --git a/api/tests/test_containers_integration_tests/models/test_conversation_message_inputs.py b/api/tests/test_containers_integration_tests/models/test_conversation_message_inputs.py index e922c19a5a..f10f519e25 100644 --- a/api/tests/test_containers_integration_tests/models/test_conversation_message_inputs.py +++ b/api/tests/test_containers_integration_tests/models/test_conversation_message_inputs.py @@ -10,10 +10,10 @@ from unittest.mock import patch from uuid import uuid4 import pytest -from graphon.file import FILE_MODEL_IDENTITY, FileTransferMethod from sqlalchemy.orm import Session from core.workflow.file_reference import build_file_reference +from graphon.file import FILE_MODEL_IDENTITY, FileTransferMethod from models.model import App, AppMode, Conversation, Message diff --git a/api/tests/test_containers_integration_tests/models/test_conversation_status_count.py b/api/tests/test_containers_integration_tests/models/test_conversation_status_count.py index 4ca87de52d..6352f815df 100644 --- a/api/tests/test_containers_integration_tests/models/test_conversation_status_count.py +++ b/api/tests/test_containers_integration_tests/models/test_conversation_status_count.py @@ -9,9 +9,9 @@ from collections.abc import Generator from uuid import uuid4 import pytest -from graphon.enums import WorkflowExecutionStatus from sqlalchemy.orm import Session +from graphon.enums import WorkflowExecutionStatus from models.enums import ConversationFromSource, InvokeFrom from models.model import App, AppMode, Conversation, Message, Site from models.workflow import Workflow, WorkflowRun, WorkflowRunTriggeredFrom, WorkflowType diff --git a/api/tests/test_containers_integration_tests/models/test_types_enum_text.py b/api/tests/test_containers_integration_tests/models/test_types_enum_text.py index 957b7145d3..b325c97f7d 100644 --- a/api/tests/test_containers_integration_tests/models/test_types_enum_text.py +++ b/api/tests/test_containers_integration_tests/models/test_types_enum_text.py @@ -4,13 +4,13 @@ from typing import Any, NamedTuple import pytest import sqlalchemy as sa -from graphon.model_runtime.entities.model_entities import ModelType from sqlalchemy import exc as sa_exc from sqlalchemy import insert, select from sqlalchemy.engine import Connection, Engine from sqlalchemy.orm import DeclarativeBase, Mapped, Session, mapped_column from sqlalchemy.sql.sqltypes import VARCHAR +from graphon.model_runtime.entities.model_entities import ModelType from models.types import EnumText _USER_TABLE = "enum_text_users" diff --git a/api/tests/test_containers_integration_tests/repositories/test_sqlalchemy_api_workflow_node_execution_repository.py b/api/tests/test_containers_integration_tests/repositories/test_sqlalchemy_api_workflow_node_execution_repository.py index a68b3a08c7..641399c7f9 100644 --- a/api/tests/test_containers_integration_tests/repositories/test_sqlalchemy_api_workflow_node_execution_repository.py +++ b/api/tests/test_containers_integration_tests/repositories/test_sqlalchemy_api_workflow_node_execution_repository.py @@ -5,10 +5,10 @@ from __future__ import annotations from datetime import timedelta from uuid import uuid4 -from graphon.enums import WorkflowNodeExecutionStatus from sqlalchemy import Engine, delete from sqlalchemy.orm import Session, sessionmaker +from graphon.enums import WorkflowNodeExecutionStatus from libs.datetime_utils import naive_utc_now from models.enums import CreatorUserRole from models.workflow import WorkflowNodeExecutionModel diff --git a/api/tests/test_containers_integration_tests/repositories/test_sqlalchemy_api_workflow_run_repository.py b/api/tests/test_containers_integration_tests/repositories/test_sqlalchemy_api_workflow_run_repository.py index 64c93ac07c..aebe87839c 100644 --- a/api/tests/test_containers_integration_tests/repositories/test_sqlalchemy_api_workflow_run_repository.py +++ b/api/tests/test_containers_integration_tests/repositories/test_sqlalchemy_api_workflow_run_repository.py @@ -8,15 +8,15 @@ from unittest.mock import Mock from uuid import uuid4 import pytest +from sqlalchemy import Engine, delete, select +from sqlalchemy.orm import Session, sessionmaker + +from extensions.ext_storage import storage from graphon.entities import WorkflowExecution from graphon.entities.pause_reason import HumanInputRequired, PauseReasonType from graphon.enums import WorkflowExecutionStatus from graphon.nodes.human_input.entities import FormDefinition, FormInput, UserAction from graphon.nodes.human_input.enums import FormInputType, HumanInputFormStatus -from sqlalchemy import Engine, delete, select -from sqlalchemy.orm import Session, sessionmaker - -from extensions.ext_storage import storage from libs.datetime_utils import naive_utc_now from models.enums import CreatorUserRole, WorkflowRunTriggeredFrom from models.human_input import ( diff --git a/api/tests/test_containers_integration_tests/repositories/test_sqlalchemy_execution_extra_content_repository.py b/api/tests/test_containers_integration_tests/repositories/test_sqlalchemy_execution_extra_content_repository.py index 7f44eb6ca3..aaf9a85d60 100644 --- a/api/tests/test_containers_integration_tests/repositories/test_sqlalchemy_execution_extra_content_repository.py +++ b/api/tests/test_containers_integration_tests/repositories/test_sqlalchemy_execution_extra_content_repository.py @@ -12,11 +12,11 @@ from decimal import Decimal from uuid import uuid4 import pytest -from graphon.nodes.human_input.entities import FormDefinition, UserAction -from graphon.nodes.human_input.enums import HumanInputFormStatus from sqlalchemy import Engine, delete, select from sqlalchemy.orm import Session, sessionmaker +from graphon.nodes.human_input.entities import FormDefinition, UserAction +from graphon.nodes.human_input.enums import HumanInputFormStatus from libs.datetime_utils import naive_utc_now from models.account import Account, Tenant, TenantAccountJoin, TenantAccountRole from models.enums import ConversationFromSource, InvokeFrom diff --git a/api/tests/test_containers_integration_tests/repositories/test_sqlalchemy_workflow_node_execution_repository.py b/api/tests/test_containers_integration_tests/repositories/test_sqlalchemy_workflow_node_execution_repository.py index 22e0aa34ff..fa78f1c28b 100644 --- a/api/tests/test_containers_integration_tests/repositories/test_sqlalchemy_workflow_node_execution_repository.py +++ b/api/tests/test_containers_integration_tests/repositories/test_sqlalchemy_workflow_node_execution_repository.py @@ -7,6 +7,11 @@ from datetime import datetime from decimal import Decimal from uuid import uuid4 +from sqlalchemy import Engine +from sqlalchemy.orm import Session, sessionmaker + +from core.repositories import SQLAlchemyWorkflowNodeExecutionRepository +from core.repositories.factory import OrderConfig from graphon.entities import WorkflowNodeExecution from graphon.enums import ( BuiltinNodeTypes, @@ -14,11 +19,6 @@ from graphon.enums import ( WorkflowNodeExecutionStatus, ) from graphon.model_runtime.utils.encoders import jsonable_encoder -from sqlalchemy import Engine -from sqlalchemy.orm import Session, sessionmaker - -from core.repositories import SQLAlchemyWorkflowNodeExecutionRepository -from core.repositories.factory import OrderConfig from models.account import Account, Tenant from models.enums import CreatorUserRole from models.workflow import WorkflowNodeExecutionModel, WorkflowNodeExecutionTriggeredFrom diff --git a/api/tests/test_containers_integration_tests/repositories/test_workflow_run_repository.py b/api/tests/test_containers_integration_tests/repositories/test_workflow_run_repository.py index c5e9201ee3..d6f0657380 100644 --- a/api/tests/test_containers_integration_tests/repositories/test_workflow_run_repository.py +++ b/api/tests/test_containers_integration_tests/repositories/test_workflow_run_repository.py @@ -7,12 +7,12 @@ from datetime import timedelta from uuid import uuid4 import pytest -from graphon.entities import WorkflowExecution -from graphon.enums import WorkflowExecutionStatus from sqlalchemy import Engine, delete from sqlalchemy import exc as sa_exc from sqlalchemy.orm import Session, sessionmaker +from graphon.entities import WorkflowExecution +from graphon.enums import WorkflowExecutionStatus from libs.datetime_utils import naive_utc_now from models.enums import CreatorUserRole, WorkflowRunTriggeredFrom from models.workflow import WorkflowRun, WorkflowType diff --git a/api/tests/test_containers_integration_tests/services/test_agent_service.py b/api/tests/test_containers_integration_tests/services/test_agent_service.py index 4f3c0e4200..00a2f9a59f 100644 --- a/api/tests/test_containers_integration_tests/services/test_agent_service.py +++ b/api/tests/test_containers_integration_tests/services/test_agent_service.py @@ -842,7 +842,6 @@ class TestAgentService: conversation, message = self._create_test_conversation_and_message(db_session_with_containers, app, account) from graphon.file import FileTransferMethod, FileType - from models.enums import CreatorUserRole # Add files to message diff --git a/api/tests/test_containers_integration_tests/services/test_app_dsl_service.py b/api/tests/test_containers_integration_tests/services/test_app_dsl_service.py index 6c15587058..77ce28b999 100644 --- a/api/tests/test_containers_integration_tests/services/test_app_dsl_service.py +++ b/api/tests/test_containers_integration_tests/services/test_app_dsl_service.py @@ -9,7 +9,6 @@ from uuid import uuid4 import pytest import yaml from faker import Faker -from graphon.enums import BuiltinNodeTypes from core.trigger.constants import ( TRIGGER_PLUGIN_NODE_TYPE, @@ -17,6 +16,7 @@ from core.trigger.constants import ( TRIGGER_WEBHOOK_NODE_TYPE, ) from extensions.ext_redis import redis_client +from graphon.enums import BuiltinNodeTypes from models import Account, AppMode from models.model import AppModelConfig, IconType from services import app_dsl_service diff --git a/api/tests/test_containers_integration_tests/services/test_conversation_service_variables.py b/api/tests/test_containers_integration_tests/services/test_conversation_service_variables.py index 1011db9063..0b7bd9ca64 100644 --- a/api/tests/test_containers_integration_tests/services/test_conversation_service_variables.py +++ b/api/tests/test_containers_integration_tests/services/test_conversation_service_variables.py @@ -5,11 +5,11 @@ from unittest.mock import patch from uuid import uuid4 import pytest -from graphon.variables import FloatVariable, IntegerVariable, StringVariable from sqlalchemy.orm import sessionmaker from core.app.entities.app_invoke_entities import InvokeFrom from extensions.ext_database import db +from graphon.variables import FloatVariable, IntegerVariable, StringVariable from models.account import Account, Tenant, TenantAccountJoin from models.enums import ConversationFromSource from models.model import App, Conversation, EndUser diff --git a/api/tests/test_containers_integration_tests/services/test_conversation_variable_updater.py b/api/tests/test_containers_integration_tests/services/test_conversation_variable_updater.py index fb0adbbcc2..02ab3f8314 100644 --- a/api/tests/test_containers_integration_tests/services/test_conversation_variable_updater.py +++ b/api/tests/test_containers_integration_tests/services/test_conversation_variable_updater.py @@ -3,10 +3,10 @@ from uuid import uuid4 import pytest -from graphon.variables import StringVariable from sqlalchemy.orm import sessionmaker from extensions.ext_database import db +from graphon.variables import StringVariable from models.workflow import ConversationVariable from services.conversation_variable_updater import ConversationVariableNotFoundError, ConversationVariableUpdater diff --git a/api/tests/test_containers_integration_tests/services/test_dataset_service.py b/api/tests/test_containers_integration_tests/services/test_dataset_service.py index f9bfa570cb..0de3c64c4f 100644 --- a/api/tests/test_containers_integration_tests/services/test_dataset_service.py +++ b/api/tests/test_containers_integration_tests/services/test_dataset_service.py @@ -9,11 +9,11 @@ from unittest.mock import Mock, patch from uuid import uuid4 import pytest -from graphon.model_runtime.entities.model_entities import ModelType from sqlalchemy.orm import Session from core.rag.index_processor.constant.index_type import IndexStructureType, IndexTechniqueType from core.rag.retrieval.retrieval_methods import RetrievalMethod +from graphon.model_runtime.entities.model_entities import ModelType from models.account import Account, Tenant, TenantAccountJoin, TenantAccountRole from models.dataset import Dataset, DatasetPermissionEnum, Document, ExternalKnowledgeBindings, Pipeline from models.enums import DatasetRuntimeMode, DataSourceType, DocumentCreatedFrom, IndexingStatus diff --git a/api/tests/test_containers_integration_tests/services/test_dataset_service_update_dataset.py b/api/tests/test_containers_integration_tests/services/test_dataset_service_update_dataset.py index 2974e00897..ac0483a45d 100644 --- a/api/tests/test_containers_integration_tests/services/test_dataset_service_update_dataset.py +++ b/api/tests/test_containers_integration_tests/services/test_dataset_service_update_dataset.py @@ -3,10 +3,10 @@ from unittest.mock import Mock, patch from uuid import uuid4 import pytest -from graphon.model_runtime.entities.model_entities import ModelType from sqlalchemy.orm import Session from core.rag.index_processor.constant.index_type import IndexTechniqueType +from graphon.model_runtime.entities.model_entities import ModelType from models.account import ( Account, AccountStatus, diff --git a/api/tests/test_containers_integration_tests/services/test_delete_archived_workflow_run.py b/api/tests/test_containers_integration_tests/services/test_delete_archived_workflow_run.py index c8f04e9215..fe426ae516 100644 --- a/api/tests/test_containers_integration_tests/services/test_delete_archived_workflow_run.py +++ b/api/tests/test_containers_integration_tests/services/test_delete_archived_workflow_run.py @@ -5,9 +5,9 @@ Testcontainers integration tests for archived workflow run deletion service. from datetime import UTC, datetime, timedelta from uuid import uuid4 -from graphon.enums import WorkflowExecutionStatus from sqlalchemy import select +from graphon.enums import WorkflowExecutionStatus from models.enums import CreatorUserRole, WorkflowRunTriggeredFrom from models.workflow import WorkflowArchiveLog, WorkflowRun from services.retention.workflow_run.delete_archived_workflow_run import ArchivedWorkflowRunDeletion diff --git a/api/tests/test_containers_integration_tests/services/test_human_input_delivery_test.py b/api/tests/test_containers_integration_tests/services/test_human_input_delivery_test.py index c46b8fba0b..18c5320d0a 100644 --- a/api/tests/test_containers_integration_tests/services/test_human_input_delivery_test.py +++ b/api/tests/test_containers_integration_tests/services/test_human_input_delivery_test.py @@ -3,8 +3,6 @@ import uuid from unittest.mock import MagicMock import pytest -from graphon.enums import BuiltinNodeTypes -from graphon.nodes.human_input.entities import HumanInputNodeData from core.workflow.human_input_compat import ( EmailDeliveryConfig, @@ -12,6 +10,8 @@ from core.workflow.human_input_compat import ( EmailRecipients, ExternalRecipient, ) +from graphon.enums import BuiltinNodeTypes +from graphon.nodes.human_input.entities import HumanInputNodeData from models.account import Account, Tenant, TenantAccountJoin, TenantAccountRole from models.model import App, AppMode from models.workflow import Workflow, WorkflowType diff --git a/api/tests/test_containers_integration_tests/services/test_human_input_delivery_test_service.py b/api/tests/test_containers_integration_tests/services/test_human_input_delivery_test_service.py index 0f252515f7..21a54e909e 100644 --- a/api/tests/test_containers_integration_tests/services/test_human_input_delivery_test_service.py +++ b/api/tests/test_containers_integration_tests/services/test_human_input_delivery_test_service.py @@ -5,7 +5,6 @@ from unittest.mock import MagicMock, patch from uuid import uuid4 import pytest -from graphon.runtime import VariablePool from sqlalchemy.engine import Engine from configs import dify_config @@ -16,6 +15,7 @@ from core.workflow.human_input_compat import ( ExternalRecipient, MemberRecipient, ) +from graphon.runtime import VariablePool from models.account import Account, TenantAccountJoin from services import human_input_delivery_test_service as service_module from services.human_input_delivery_test_service import ( diff --git a/api/tests/test_containers_integration_tests/services/test_messages_clean_service.py b/api/tests/test_containers_integration_tests/services/test_messages_clean_service.py index 2340dd2a03..cd63d3ad6c 100644 --- a/api/tests/test_containers_integration_tests/services/test_messages_clean_service.py +++ b/api/tests/test_containers_integration_tests/services/test_messages_clean_service.py @@ -8,11 +8,11 @@ from unittest.mock import MagicMock, patch import pytest from faker import Faker -from graphon.file import FileType from sqlalchemy.orm import Session from enums.cloud_plan import CloudPlan from extensions.ext_redis import redis_client +from graphon.file import FileType from models.account import Account, Tenant, TenantAccountJoin, TenantAccountRole from models.enums import ( ConversationFromSource, diff --git a/api/tests/test_containers_integration_tests/services/test_model_provider_service.py b/api/tests/test_containers_integration_tests/services/test_model_provider_service.py index ba926bf675..8955a3b5f2 100644 --- a/api/tests/test_containers_integration_tests/services/test_model_provider_service.py +++ b/api/tests/test_containers_integration_tests/services/test_model_provider_service.py @@ -2,10 +2,10 @@ from unittest.mock import MagicMock, patch import pytest from faker import Faker -from graphon.model_runtime.entities.model_entities import FetchFrom, ModelType from sqlalchemy.orm import Session from core.entities.model_entities import ModelStatus +from graphon.model_runtime.entities.model_entities import FetchFrom, ModelType from models import Account, Tenant, TenantAccountJoin, TenantAccountRole from models.provider import Provider, ProviderModel, ProviderModelSetting, ProviderType from services.model_provider_service import ModelProviderService @@ -405,11 +405,10 @@ class TestModelProviderService: mock_provider_manager = mock_external_service_dependencies["provider_manager"].return_value # Create mock models + from core.entities.model_entities import ModelWithProviderEntity, SimpleModelProviderEntity from graphon.model_runtime.entities.common_entities import I18nObject from graphon.model_runtime.entities.provider_entities import ProviderEntity - from core.entities.model_entities import ModelWithProviderEntity, SimpleModelProviderEntity - # Create real model objects instead of mocks provider_entity_1 = SimpleModelProviderEntity( ProviderEntity( @@ -644,9 +643,8 @@ class TestModelProviderService: mock_provider_manager = mock_external_service_dependencies["provider_manager"].return_value # Create mock default model response - from graphon.model_runtime.entities.common_entities import I18nObject - from core.entities.model_entities import DefaultModelEntity, DefaultModelProviderEntity + from graphon.model_runtime.entities.common_entities import I18nObject mock_default_model = DefaultModelEntity( model="gpt-3.5-turbo", diff --git a/api/tests/test_containers_integration_tests/services/test_workflow_app_service.py b/api/tests/test_containers_integration_tests/services/test_workflow_app_service.py index 749c6fff5b..1e57b5603d 100644 --- a/api/tests/test_containers_integration_tests/services/test_workflow_app_service.py +++ b/api/tests/test_containers_integration_tests/services/test_workflow_app_service.py @@ -8,9 +8,9 @@ from unittest.mock import patch import pytest from faker import Faker -from graphon.enums import WorkflowExecutionStatus from sqlalchemy.orm import Session +from graphon.enums import WorkflowExecutionStatus from models import EndUser, Workflow, WorkflowAppLog, WorkflowArchiveLog, WorkflowRun from models.enums import AppTriggerType, CreatorUserRole, WorkflowRunTriggeredFrom from models.workflow import WorkflowAppLogCreatedFrom diff --git a/api/tests/test_containers_integration_tests/services/test_workflow_draft_variable_service.py b/api/tests/test_containers_integration_tests/services/test_workflow_draft_variable_service.py index 0c281c8c33..86cf2327c7 100644 --- a/api/tests/test_containers_integration_tests/services/test_workflow_draft_variable_service.py +++ b/api/tests/test_containers_integration_tests/services/test_workflow_draft_variable_service.py @@ -1,9 +1,9 @@ import pytest from faker import Faker -from graphon.variables.segments import StringSegment from sqlalchemy.orm import Session from core.workflow.variable_prefixes import CONVERSATION_VARIABLE_NODE_ID, SYSTEM_VARIABLE_NODE_ID +from graphon.variables.segments import StringSegment from models import App, Workflow from models.enums import DraftVariableType from models.workflow import WorkflowDraftVariable diff --git a/api/tests/test_containers_integration_tests/services/workflow/test_workflow_converter.py b/api/tests/test_containers_integration_tests/services/workflow/test_workflow_converter.py index ce2fd2eeb1..ce5c2bd162 100644 --- a/api/tests/test_containers_integration_tests/services/workflow/test_workflow_converter.py +++ b/api/tests/test_containers_integration_tests/services/workflow/test_workflow_converter.py @@ -5,9 +5,6 @@ from unittest.mock import MagicMock, patch import pytest from faker import Faker -from graphon.model_runtime.entities.llm_entities import LLMMode -from graphon.model_runtime.entities.message_entities import PromptMessageRole -from graphon.variables.input_entities import VariableEntity, VariableEntityType from sqlalchemy.orm import Session from core.app.app_config.entities import ( @@ -21,6 +18,9 @@ from core.app.app_config.entities import ( PromptTemplateEntity, ) from core.prompt.utils.prompt_template_parser import PromptTemplateParser +from graphon.model_runtime.entities.llm_entities import LLMMode +from graphon.model_runtime.entities.message_entities import PromptMessageRole +from graphon.variables.input_entities import VariableEntity, VariableEntityType from models import Account, Tenant from models.api_based_extension import APIBasedExtension, APIBasedExtensionPoint from models.model import App, AppMode, AppModelConfig diff --git a/api/tests/test_containers_integration_tests/services/workflow/test_workflow_node_execution_service_repository.py b/api/tests/test_containers_integration_tests/services/workflow/test_workflow_node_execution_service_repository.py index 7c43bf676b..4dab895135 100644 --- a/api/tests/test_containers_integration_tests/services/workflow/test_workflow_node_execution_service_repository.py +++ b/api/tests/test_containers_integration_tests/services/workflow/test_workflow_node_execution_service_repository.py @@ -1,10 +1,10 @@ from datetime import datetime, timedelta from uuid import uuid4 -from graphon.enums import WorkflowNodeExecutionStatus from sqlalchemy import Engine, select from sqlalchemy.orm import Session, sessionmaker +from graphon.enums import WorkflowNodeExecutionStatus from libs.datetime_utils import naive_utc_now from models.enums import CreatorUserRole from models.workflow import WorkflowNodeExecutionModel diff --git a/api/tests/test_containers_integration_tests/tasks/test_mail_human_input_delivery_task.py b/api/tests/test_containers_integration_tests/tasks/test_mail_human_input_delivery_task.py index 1b4dcf28ea..328bdbf055 100644 --- a/api/tests/test_containers_integration_tests/tasks/test_mail_human_input_delivery_task.py +++ b/api/tests/test_containers_integration_tests/tasks/test_mail_human_input_delivery_task.py @@ -3,9 +3,6 @@ from datetime import UTC, datetime from unittest.mock import patch import pytest -from graphon.enums import WorkflowExecutionStatus -from graphon.nodes.human_input.entities import HumanInputNodeData -from graphon.runtime import GraphRuntimeState, VariablePool from sqlalchemy import delete from configs import dify_config @@ -21,6 +18,9 @@ from core.workflow.human_input_compat import ( MemberRecipient, ) from extensions.ext_storage import storage +from graphon.enums import WorkflowExecutionStatus +from graphon.nodes.human_input.entities import HumanInputNodeData +from graphon.runtime import GraphRuntimeState, VariablePool from models.account import Account, AccountStatus, Tenant, TenantAccountJoin, TenantAccountRole from models.enums import CreatorUserRole, WorkflowRunTriggeredFrom from models.human_input import HumanInputDelivery, HumanInputForm, HumanInputFormRecipient diff --git a/api/tests/test_containers_integration_tests/tasks/test_remove_app_and_related_data_task.py b/api/tests/test_containers_integration_tests/tasks/test_remove_app_and_related_data_task.py index b5bef145d5..b43b622870 100644 --- a/api/tests/test_containers_integration_tests/tasks/test_remove_app_and_related_data_task.py +++ b/api/tests/test_containers_integration_tests/tasks/test_remove_app_and_related_data_task.py @@ -2,12 +2,12 @@ import uuid from unittest.mock import ANY, call, patch import pytest -from graphon.variables.segments import StringSegment -from graphon.variables.types import SegmentType from sqlalchemy import delete, func, select from core.db.session_factory import session_factory from extensions.storage.storage_type import StorageType +from graphon.variables.segments import StringSegment +from graphon.variables.types import SegmentType from libs.datetime_utils import naive_utc_now from models import Tenant from models.enums import CreatorUserRole diff --git a/api/tests/test_containers_integration_tests/test_workflow_pause_integration.py b/api/tests/test_containers_integration_tests/test_workflow_pause_integration.py index 6e98c0855a..b00d827e37 100644 --- a/api/tests/test_containers_integration_tests/test_workflow_pause_integration.py +++ b/api/tests/test_containers_integration_tests/test_workflow_pause_integration.py @@ -24,12 +24,12 @@ from dataclasses import dataclass from datetime import timedelta import pytest -from graphon.entities import WorkflowExecution -from graphon.enums import WorkflowExecutionStatus from sqlalchemy import delete, func, select from sqlalchemy.orm import Session, selectinload, sessionmaker from extensions.ext_storage import storage +from graphon.entities import WorkflowExecution +from graphon.enums import WorkflowExecutionStatus from libs.datetime_utils import naive_utc_now from models import Account from models import WorkflowPause as WorkflowPauseModel diff --git a/api/tests/test_containers_integration_tests/trigger/test_trigger_e2e.py b/api/tests/test_containers_integration_tests/trigger/test_trigger_e2e.py index d725fb990a..55aec49878 100644 --- a/api/tests/test_containers_integration_tests/trigger/test_trigger_e2e.py +++ b/api/tests/test_containers_integration_tests/trigger/test_trigger_e2e.py @@ -10,7 +10,6 @@ from typing import Any import pytest from flask import Flask, Response from flask.testing import FlaskClient -from graphon.enums import BuiltinNodeTypes from sqlalchemy import select from sqlalchemy.orm import Session @@ -25,6 +24,7 @@ from core.trigger.debug import event_selectors from core.trigger.debug.event_bus import TriggerDebugEventBus from core.trigger.debug.event_selectors import PluginTriggerDebugEventPoller, WebhookTriggerDebugEventPoller from core.trigger.debug.events import PluginTriggerDebugEvent, build_plugin_pool_key +from graphon.enums import BuiltinNodeTypes from libs.datetime_utils import naive_utc_now from models.account import Account, Tenant from models.enums import AppTriggerStatus, AppTriggerType, CreatorUserRole, WorkflowTriggerStatus diff --git a/api/tests/unit_tests/controllers/console/app/test_audio.py b/api/tests/unit_tests/controllers/console/app/test_audio.py index c52bc02420..2d218dac7e 100644 --- a/api/tests/unit_tests/controllers/console/app/test_audio.py +++ b/api/tests/unit_tests/controllers/console/app/test_audio.py @@ -4,7 +4,6 @@ import io from types import SimpleNamespace import pytest -from graphon.model_runtime.errors.invoke import InvokeError from werkzeug.datastructures import FileStorage from werkzeug.exceptions import InternalServerError @@ -21,6 +20,7 @@ from controllers.console.app.error import ( UnsupportedAudioTypeError, ) from core.errors.error import ModelCurrentlyNotSupportError, ProviderTokenNotInitError, QuotaExceededError +from graphon.model_runtime.errors.invoke import InvokeError from services.audio_service import AudioService from services.errors.app_model_config import AppModelConfigBrokenError from services.errors.audio import ( diff --git a/api/tests/unit_tests/controllers/console/app/test_conversation_variables_api.py b/api/tests/unit_tests/controllers/console/app/test_conversation_variables_api.py index 42b3420c31..1a412aff29 100644 --- a/api/tests/unit_tests/controllers/console/app/test_conversation_variables_api.py +++ b/api/tests/unit_tests/controllers/console/app/test_conversation_variables_api.py @@ -5,10 +5,10 @@ from datetime import UTC, datetime from types import SimpleNamespace import pytest -from graphon.variables.types import SegmentType from pydantic import ValidationError from controllers.console.app import conversation_variables as conversation_variables_module +from graphon.variables.types import SegmentType def _unwrap(func): diff --git a/api/tests/unit_tests/controllers/console/app/test_workflow.py b/api/tests/unit_tests/controllers/console/app/test_workflow.py index 9f20886a81..6ff3b19362 100644 --- a/api/tests/unit_tests/controllers/console/app/test_workflow.py +++ b/api/tests/unit_tests/controllers/console/app/test_workflow.py @@ -6,11 +6,11 @@ from types import SimpleNamespace from unittest.mock import Mock import pytest -from graphon.file import File, FileTransferMethod, FileType from werkzeug.exceptions import HTTPException, NotFound from controllers.console.app import workflow as workflow_module from controllers.console.app.error import DraftWorkflowNotExist, DraftWorkflowNotSync +from graphon.file import File, FileTransferMethod, FileType def _unwrap(func): diff --git a/api/tests/unit_tests/controllers/console/app/test_workflow_app_log_api.py b/api/tests/unit_tests/controllers/console/app/test_workflow_app_log_api.py index 2adb69c704..a9853f25b0 100644 --- a/api/tests/unit_tests/controllers/console/app/test_workflow_app_log_api.py +++ b/api/tests/unit_tests/controllers/console/app/test_workflow_app_log_api.py @@ -2,9 +2,8 @@ from __future__ import annotations from datetime import UTC, datetime -from graphon.enums import WorkflowExecutionStatus - from controllers.console.app import workflow_app_log as workflow_app_log_module +from graphon.enums import WorkflowExecutionStatus def test_workflow_app_log_query_parses_bool_and_datetime(): diff --git a/api/tests/unit_tests/controllers/console/app/test_workflow_pause_details_api.py b/api/tests/unit_tests/controllers/console/app/test_workflow_pause_details_api.py index e11102acb1..c4a8148446 100644 --- a/api/tests/unit_tests/controllers/console/app/test_workflow_pause_details_api.py +++ b/api/tests/unit_tests/controllers/console/app/test_workflow_pause_details_api.py @@ -6,14 +6,14 @@ from unittest.mock import Mock import pytest from flask import Flask -from graphon.entities.pause_reason import HumanInputRequired -from graphon.enums import WorkflowExecutionStatus -from graphon.nodes.human_input.entities import FormInput, UserAction -from graphon.nodes.human_input.enums import FormInputType from controllers.console import wraps as console_wraps from controllers.console.app import workflow_run as workflow_run_module from controllers.web.error import NotFoundError +from graphon.entities.pause_reason import HumanInputRequired +from graphon.enums import WorkflowExecutionStatus +from graphon.nodes.human_input.entities import FormInput, UserAction +from graphon.nodes.human_input.enums import FormInputType from libs import login as login_lib from models.account import Account, AccountStatus, TenantAccountRole from models.workflow import WorkflowRun diff --git a/api/tests/unit_tests/controllers/console/app/workflow_draft_variables_test.py b/api/tests/unit_tests/controllers/console/app/workflow_draft_variables_test.py index 740da1f1df..b19a1740eb 100644 --- a/api/tests/unit_tests/controllers/console/app/workflow_draft_variables_test.py +++ b/api/tests/unit_tests/controllers/console/app/workflow_draft_variables_test.py @@ -5,7 +5,6 @@ from unittest.mock import MagicMock, patch import pytest from flask_restx import marshal -from graphon.variables.types import SegmentType from controllers.console.app.workflow_draft_variable import ( _WORKFLOW_DRAFT_VARIABLE_FIELDS, @@ -16,6 +15,7 @@ from controllers.console.app.workflow_draft_variable import ( ) from core.workflow.variable_prefixes import CONVERSATION_VARIABLE_NODE_ID, SYSTEM_VARIABLE_NODE_ID from factories.variable_factory import build_segment +from graphon.variables.types import SegmentType from libs.datetime_utils import naive_utc_now from libs.uuid_utils import uuidv7 from models.workflow import WorkflowDraftVariable, WorkflowDraftVariableFile diff --git a/api/tests/unit_tests/controllers/console/datasets/rag_pipeline/test_datasource_auth.py b/api/tests/unit_tests/controllers/console/datasets/rag_pipeline/test_datasource_auth.py index 9c9f8da87c..5136922e88 100644 --- a/api/tests/unit_tests/controllers/console/datasets/rag_pipeline/test_datasource_auth.py +++ b/api/tests/unit_tests/controllers/console/datasets/rag_pipeline/test_datasource_auth.py @@ -1,7 +1,6 @@ from unittest.mock import MagicMock, patch import pytest -from graphon.model_runtime.errors.validate import CredentialsValidateFailedError from werkzeug.exceptions import Forbidden, NotFound from controllers.console import console_ns @@ -18,6 +17,7 @@ from controllers.console.datasets.rag_pipeline.datasource_auth import ( DatasourceUpdateProviderNameApi, ) from core.plugin.impl.oauth import OAuthHandler +from graphon.model_runtime.errors.validate import CredentialsValidateFailedError from services.datasource_provider_service import DatasourceProviderService from services.plugin.oauth_service import OAuthProxyService diff --git a/api/tests/unit_tests/controllers/console/datasets/rag_pipeline/test_rag_pipeline_draft_variable.py b/api/tests/unit_tests/controllers/console/datasets/rag_pipeline/test_rag_pipeline_draft_variable.py index 6ef8ccfdbd..63950736c5 100644 --- a/api/tests/unit_tests/controllers/console/datasets/rag_pipeline/test_rag_pipeline_draft_variable.py +++ b/api/tests/unit_tests/controllers/console/datasets/rag_pipeline/test_rag_pipeline_draft_variable.py @@ -2,7 +2,6 @@ from unittest.mock import MagicMock, patch import pytest from flask import Response -from graphon.variables.types import SegmentType from controllers.console import console_ns from controllers.console.app.error import DraftWorkflowNotExist @@ -16,6 +15,7 @@ from controllers.console.datasets.rag_pipeline.rag_pipeline_draft_variable impor ) from controllers.web.error import InvalidArgumentError, NotFoundError from core.workflow.variable_prefixes import SYSTEM_VARIABLE_NODE_ID +from graphon.variables.types import SegmentType from models.account import Account diff --git a/api/tests/unit_tests/controllers/console/datasets/test_hit_testing_base.py b/api/tests/unit_tests/controllers/console/datasets/test_hit_testing_base.py index 710c9be684..e4acd91b76 100644 --- a/api/tests/unit_tests/controllers/console/datasets/test_hit_testing_base.py +++ b/api/tests/unit_tests/controllers/console/datasets/test_hit_testing_base.py @@ -1,7 +1,6 @@ from unittest.mock import MagicMock, patch import pytest -from graphon.model_runtime.errors.invoke import InvokeError from werkzeug.exceptions import Forbidden, InternalServerError, NotFound import services @@ -21,6 +20,7 @@ from core.errors.error import ( ProviderTokenNotInitError, QuotaExceededError, ) +from graphon.model_runtime.errors.invoke import InvokeError from models.account import Account from services.dataset_service import DatasetService from services.hit_testing_service import HitTestingService diff --git a/api/tests/unit_tests/controllers/console/explore/test_audio.py b/api/tests/unit_tests/controllers/console/explore/test_audio.py index 66c9ba48c5..b4b57022e2 100644 --- a/api/tests/unit_tests/controllers/console/explore/test_audio.py +++ b/api/tests/unit_tests/controllers/console/explore/test_audio.py @@ -2,7 +2,6 @@ from io import BytesIO from unittest.mock import MagicMock, patch import pytest -from graphon.model_runtime.errors.invoke import InvokeError from werkzeug.exceptions import InternalServerError import controllers.console.explore.audio as audio_module @@ -20,6 +19,7 @@ from core.errors.error import ( ProviderTokenNotInitError, QuotaExceededError, ) +from graphon.model_runtime.errors.invoke import InvokeError from services.errors.audio import ( AudioTooLargeServiceError, NoAudioUploadedServiceError, diff --git a/api/tests/unit_tests/controllers/console/explore/test_message.py b/api/tests/unit_tests/controllers/console/explore/test_message.py index 2e4ca4f2a4..145cc9cdd7 100644 --- a/api/tests/unit_tests/controllers/console/explore/test_message.py +++ b/api/tests/unit_tests/controllers/console/explore/test_message.py @@ -1,7 +1,6 @@ from unittest.mock import MagicMock, patch import pytest -from graphon.model_runtime.errors.invoke import InvokeError from werkzeug.exceptions import InternalServerError, NotFound import controllers.console.explore.message as module @@ -22,6 +21,7 @@ from core.errors.error import ( ProviderTokenNotInitError, QuotaExceededError, ) +from graphon.model_runtime.errors.invoke import InvokeError from services.errors.conversation import ConversationNotExistsError from services.errors.message import ( FirstMessageNotExistsError, diff --git a/api/tests/unit_tests/controllers/console/explore/test_trial.py b/api/tests/unit_tests/controllers/console/explore/test_trial.py index a43c3ca47e..3625056af9 100644 --- a/api/tests/unit_tests/controllers/console/explore/test_trial.py +++ b/api/tests/unit_tests/controllers/console/explore/test_trial.py @@ -3,7 +3,6 @@ from unittest.mock import MagicMock, patch from uuid import uuid4 import pytest -from graphon.model_runtime.errors.invoke import InvokeError from werkzeug.exceptions import Forbidden, InternalServerError, NotFound import controllers.console.explore.trial as module @@ -26,6 +25,7 @@ from core.errors.error import ( ProviderTokenNotInitError, QuotaExceededError, ) +from graphon.model_runtime.errors.invoke import InvokeError from models import Account from models.account import TenantStatus from models.model import AppMode diff --git a/api/tests/unit_tests/controllers/console/workspace/test_load_balancing_config.py b/api/tests/unit_tests/controllers/console/workspace/test_load_balancing_config.py index 9c42ee9529..b2f949c6e2 100644 --- a/api/tests/unit_tests/controllers/console/workspace/test_load_balancing_config.py +++ b/api/tests/unit_tests/controllers/console/workspace/test_load_balancing_config.py @@ -11,9 +11,10 @@ from unittest.mock import MagicMock import pytest from flask import Flask from flask.views import MethodView +from werkzeug.exceptions import Forbidden + from graphon.model_runtime.entities.model_entities import ModelType from graphon.model_runtime.errors.validate import CredentialsValidateFailedError -from werkzeug.exceptions import Forbidden if not hasattr(builtins, "MethodView"): builtins.MethodView = MethodView # type: ignore[attr-defined] diff --git a/api/tests/unit_tests/controllers/console/workspace/test_model_providers.py b/api/tests/unit_tests/controllers/console/workspace/test_model_providers.py index fb9eec98cb..168479af1e 100644 --- a/api/tests/unit_tests/controllers/console/workspace/test_model_providers.py +++ b/api/tests/unit_tests/controllers/console/workspace/test_model_providers.py @@ -1,7 +1,6 @@ from unittest.mock import MagicMock, patch import pytest -from graphon.model_runtime.errors.validate import CredentialsValidateFailedError from pydantic_core import ValidationError from werkzeug.exceptions import Forbidden @@ -14,6 +13,7 @@ from controllers.console.workspace.model_providers import ( ModelProviderValidateApi, PreferredProviderTypeUpdateApi, ) +from graphon.model_runtime.errors.validate import CredentialsValidateFailedError VALID_UUID = "123e4567-e89b-12d3-a456-426614174000" INVALID_UUID = "123" diff --git a/api/tests/unit_tests/controllers/console/workspace/test_models.py b/api/tests/unit_tests/controllers/console/workspace/test_models.py index c829327bc7..f0d32f81fb 100644 --- a/api/tests/unit_tests/controllers/console/workspace/test_models.py +++ b/api/tests/unit_tests/controllers/console/workspace/test_models.py @@ -2,8 +2,6 @@ from unittest.mock import MagicMock, patch import pytest from flask import Flask -from graphon.model_runtime.entities.model_entities import ModelType -from graphon.model_runtime.errors.validate import CredentialsValidateFailedError from controllers.console.workspace.models import ( DefaultModelApi, @@ -16,6 +14,8 @@ from controllers.console.workspace.models import ( ModelProviderModelParameterRuleApi, ModelProviderModelValidateApi, ) +from graphon.model_runtime.entities.model_entities import ModelType +from graphon.model_runtime.errors.validate import CredentialsValidateFailedError def unwrap(func): diff --git a/api/tests/unit_tests/controllers/service_api/app/test_audio.py b/api/tests/unit_tests/controllers/service_api/app/test_audio.py index a26fea8fbd..c16ebad739 100644 --- a/api/tests/unit_tests/controllers/service_api/app/test_audio.py +++ b/api/tests/unit_tests/controllers/service_api/app/test_audio.py @@ -13,7 +13,6 @@ from types import SimpleNamespace from unittest.mock import Mock, patch import pytest -from graphon.model_runtime.errors.invoke import InvokeError from werkzeug.datastructures import FileStorage from werkzeug.exceptions import InternalServerError @@ -30,6 +29,7 @@ from controllers.service_api.app.error import ( UnsupportedAudioTypeError, ) from core.errors.error import ModelCurrentlyNotSupportError, ProviderTokenNotInitError, QuotaExceededError +from graphon.model_runtime.errors.invoke import InvokeError from services.audio_service import AudioService from services.errors.app_model_config import AppModelConfigBrokenError from services.errors.audio import ( diff --git a/api/tests/unit_tests/controllers/service_api/app/test_completion.py b/api/tests/unit_tests/controllers/service_api/app/test_completion.py index 57681d8f5b..3364c07e62 100644 --- a/api/tests/unit_tests/controllers/service_api/app/test_completion.py +++ b/api/tests/unit_tests/controllers/service_api/app/test_completion.py @@ -16,7 +16,6 @@ from types import SimpleNamespace from unittest.mock import Mock, patch import pytest -from graphon.model_runtime.errors.invoke import InvokeError from pydantic import ValidationError from werkzeug.exceptions import BadRequest, NotFound @@ -35,6 +34,7 @@ from controllers.service_api.app.error import ( NotChatAppError, ) from core.errors.error import QuotaExceededError +from graphon.model_runtime.errors.invoke import InvokeError from models.model import App, AppMode, EndUser from services.app_generate_service import AppGenerateService from services.app_task_service import AppTaskService diff --git a/api/tests/unit_tests/controllers/service_api/app/test_conversation.py b/api/tests/unit_tests/controllers/service_api/app/test_conversation.py index 97fdf1a011..14c35a9ed5 100644 --- a/api/tests/unit_tests/controllers/service_api/app/test_conversation.py +++ b/api/tests/unit_tests/controllers/service_api/app/test_conversation.py @@ -20,7 +20,6 @@ from types import SimpleNamespace from unittest.mock import Mock, patch import pytest -from graphon.variables.types import SegmentType from werkzeug.exceptions import BadRequest, NotFound import services @@ -38,6 +37,7 @@ from controllers.service_api.app.conversation import ( ConversationVariableUpdatePayload, ) from controllers.service_api.app.error import NotChatAppError +from graphon.variables.types import SegmentType from models.model import App, AppMode, EndUser from services.conversation_service import ConversationService from services.errors.conversation import ( diff --git a/api/tests/unit_tests/controllers/service_api/app/test_workflow.py b/api/tests/unit_tests/controllers/service_api/app/test_workflow.py index 74a3c75839..da09ec13ce 100644 --- a/api/tests/unit_tests/controllers/service_api/app/test_workflow.py +++ b/api/tests/unit_tests/controllers/service_api/app/test_workflow.py @@ -20,7 +20,6 @@ from types import SimpleNamespace from unittest.mock import Mock, patch import pytest -from graphon.enums import WorkflowExecutionStatus from werkzeug.exceptions import BadRequest, NotFound from controllers.service_api.app.error import NotWorkflowAppError @@ -37,6 +36,7 @@ from controllers.service_api.app.workflow import ( WorkflowTaskStopApi, ) from controllers.web.error import InvokeRateLimitError as InvokeRateLimitHttpError +from graphon.enums import WorkflowExecutionStatus from models.model import App, AppMode from services.app_generate_service import AppGenerateService from services.errors.app import IsDraftWorkflowError, WorkflowNotFoundError diff --git a/api/tests/unit_tests/controllers/service_api/app/test_workflow_fields.py b/api/tests/unit_tests/controllers/service_api/app/test_workflow_fields.py index 4b8e3a738c..eda270258d 100644 --- a/api/tests/unit_tests/controllers/service_api/app/test_workflow_fields.py +++ b/api/tests/unit_tests/controllers/service_api/app/test_workflow_fields.py @@ -1,8 +1,7 @@ from types import SimpleNamespace -from graphon.enums import WorkflowExecutionStatus - from controllers.service_api.app.workflow import WorkflowRunOutputsField, WorkflowRunStatusField +from graphon.enums import WorkflowExecutionStatus def test_workflow_run_status_field_with_enum() -> None: diff --git a/api/tests/unit_tests/controllers/web/test_audio.py b/api/tests/unit_tests/controllers/web/test_audio.py index cbfc8fa613..a6ca441801 100644 --- a/api/tests/unit_tests/controllers/web/test_audio.py +++ b/api/tests/unit_tests/controllers/web/test_audio.py @@ -8,7 +8,6 @@ from unittest.mock import MagicMock, patch import pytest from flask import Flask -from graphon.model_runtime.errors.invoke import InvokeError from controllers.web.audio import AudioApi, TextApi from controllers.web.error import ( @@ -22,6 +21,7 @@ from controllers.web.error import ( UnsupportedAudioTypeError, ) from core.errors.error import ModelCurrentlyNotSupportError, ProviderTokenNotInitError, QuotaExceededError +from graphon.model_runtime.errors.invoke import InvokeError from services.errors.audio import ( AudioTooLargeServiceError, NoAudioUploadedServiceError, diff --git a/api/tests/unit_tests/controllers/web/test_completion.py b/api/tests/unit_tests/controllers/web/test_completion.py index 49039d03fe..4f8d848637 100644 --- a/api/tests/unit_tests/controllers/web/test_completion.py +++ b/api/tests/unit_tests/controllers/web/test_completion.py @@ -7,7 +7,6 @@ from unittest.mock import MagicMock, patch import pytest from flask import Flask -from graphon.model_runtime.errors.invoke import InvokeError from controllers.web.completion import ChatApi, ChatStopApi, CompletionApi, CompletionStopApi from controllers.web.error import ( @@ -19,6 +18,7 @@ from controllers.web.error import ( ProviderQuotaExceededError, ) from core.errors.error import ModelCurrentlyNotSupportError, ProviderTokenNotInitError, QuotaExceededError +from graphon.model_runtime.errors.invoke import InvokeError def _completion_app() -> SimpleNamespace: diff --git a/api/tests/unit_tests/core/agent/test_cot_agent_runner.py b/api/tests/unit_tests/core/agent/test_cot_agent_runner.py index bc7aea0ef9..cde8820e00 100644 --- a/api/tests/unit_tests/core/agent/test_cot_agent_runner.py +++ b/api/tests/unit_tests/core/agent/test_cot_agent_runner.py @@ -2,11 +2,11 @@ import json from unittest.mock import MagicMock import pytest -from graphon.model_runtime.entities.llm_entities import LLMUsage from core.agent.cot_agent_runner import CotAgentRunner from core.agent.entities import AgentScratchpadUnit from core.agent.errors import AgentMaxIterationError +from graphon.model_runtime.entities.llm_entities import LLMUsage class DummyRunner(CotAgentRunner): diff --git a/api/tests/unit_tests/core/agent/test_cot_chat_agent_runner.py b/api/tests/unit_tests/core/agent/test_cot_chat_agent_runner.py index 97206019b9..ea8cc8aa86 100644 --- a/api/tests/unit_tests/core/agent/test_cot_chat_agent_runner.py +++ b/api/tests/unit_tests/core/agent/test_cot_chat_agent_runner.py @@ -1,9 +1,9 @@ from unittest.mock import MagicMock, patch import pytest -from graphon.model_runtime.entities.message_entities import TextPromptMessageContent from core.agent.cot_chat_agent_runner import CotChatAgentRunner +from graphon.model_runtime.entities.message_entities import TextPromptMessageContent from tests.unit_tests.core.agent.conftest import ( DummyAgentConfig, DummyAppConfig, diff --git a/api/tests/unit_tests/core/agent/test_cot_completion_agent_runner.py b/api/tests/unit_tests/core/agent/test_cot_completion_agent_runner.py index defc8b4b64..2f5873d865 100644 --- a/api/tests/unit_tests/core/agent/test_cot_completion_agent_runner.py +++ b/api/tests/unit_tests/core/agent/test_cot_completion_agent_runner.py @@ -1,6 +1,8 @@ import json import pytest + +from core.agent.cot_completion_agent_runner import CotCompletionAgentRunner from graphon.model_runtime.entities.message_entities import ( AssistantPromptMessage, ImagePromptMessageContent, @@ -8,8 +10,6 @@ from graphon.model_runtime.entities.message_entities import ( UserPromptMessage, ) -from core.agent.cot_completion_agent_runner import CotCompletionAgentRunner - # ----------------------------- # Fixtures # ----------------------------- diff --git a/api/tests/unit_tests/core/agent/test_fc_agent_runner.py b/api/tests/unit_tests/core/agent/test_fc_agent_runner.py index a44a0650eb..17ab5babcb 100644 --- a/api/tests/unit_tests/core/agent/test_fc_agent_runner.py +++ b/api/tests/unit_tests/core/agent/test_fc_agent_runner.py @@ -3,6 +3,11 @@ from typing import Any from unittest.mock import MagicMock import pytest + +from core.agent.errors import AgentMaxIterationError +from core.agent.fc_agent_runner import FunctionCallAgentRunner +from core.app.apps.base_app_queue_manager import PublishFrom +from core.app.entities.queue_entities import QueueMessageFileEvent from graphon.model_runtime.entities.llm_entities import LLMUsage from graphon.model_runtime.entities.message_entities import ( DocumentPromptMessageContent, @@ -11,11 +16,6 @@ from graphon.model_runtime.entities.message_entities import ( UserPromptMessage, ) -from core.agent.errors import AgentMaxIterationError -from core.agent.fc_agent_runner import FunctionCallAgentRunner -from core.app.apps.base_app_queue_manager import PublishFrom -from core.app.entities.queue_entities import QueueMessageFileEvent - # ============================== # Dummy Helper Classes # ============================== diff --git a/api/tests/unit_tests/core/app/app_config/easy_ui_based_app/test_model_config_converter.py b/api/tests/unit_tests/core/app/app_config/easy_ui_based_app/test_model_config_converter.py index 5ee66da94a..186b4a501d 100644 --- a/api/tests/unit_tests/core/app/app_config/easy_ui_based_app/test_model_config_converter.py +++ b/api/tests/unit_tests/core/app/app_config/easy_ui_based_app/test_model_config_converter.py @@ -2,8 +2,6 @@ from types import SimpleNamespace from unittest.mock import MagicMock import pytest -from graphon.model_runtime.entities.llm_entities import LLMMode -from graphon.model_runtime.entities.model_entities import ModelPropertyKey from core.app.app_config.easy_ui_based_app.model_config.converter import ModelConfigConverter from core.entities.model_entities import ModelStatus @@ -12,6 +10,8 @@ from core.errors.error import ( ProviderTokenNotInitError, QuotaExceededError, ) +from graphon.model_runtime.entities.llm_entities import LLMMode +from graphon.model_runtime.entities.model_entities import ModelPropertyKey class TestModelConfigConverter: diff --git a/api/tests/unit_tests/core/app/app_config/easy_ui_based_app/test_variables_manager.py b/api/tests/unit_tests/core/app/app_config/easy_ui_based_app/test_variables_manager.py index e2f3c16335..d9fe7004ff 100644 --- a/api/tests/unit_tests/core/app/app_config/easy_ui_based_app/test_variables_manager.py +++ b/api/tests/unit_tests/core/app/app_config/easy_ui_based_app/test_variables_manager.py @@ -1,9 +1,9 @@ import pytest -from graphon.variables.input_entities import VariableEntityType from core.app.app_config.easy_ui_based_app.variables.manager import ( BasicVariablesConfigManager, ) +from graphon.variables.input_entities import VariableEntityType class TestBasicVariablesConfigManagerConvert: diff --git a/api/tests/unit_tests/core/app/app_config/features/file_upload/test_manager.py b/api/tests/unit_tests/core/app/app_config/features/file_upload/test_manager.py index 8bde9c1f97..11b53dd0f9 100644 --- a/api/tests/unit_tests/core/app/app_config/features/file_upload/test_manager.py +++ b/api/tests/unit_tests/core/app/app_config/features/file_upload/test_manager.py @@ -1,8 +1,7 @@ +from core.app.app_config.features.file_upload.manager import FileUploadConfigManager from graphon.file import FileTransferMethod, FileUploadConfig, ImageConfig from graphon.model_runtime.entities.message_entities import ImagePromptMessageContent -from core.app.app_config.features.file_upload.manager import FileUploadConfigManager - def test_convert_with_vision(): config = { diff --git a/api/tests/unit_tests/core/app/app_config/test_entities.py b/api/tests/unit_tests/core/app/app_config/test_entities.py index 000f83cd5a..f2bc3076da 100644 --- a/api/tests/unit_tests/core/app/app_config/test_entities.py +++ b/api/tests/unit_tests/core/app/app_config/test_entities.py @@ -1,10 +1,10 @@ import pytest -from graphon.variables.input_entities import VariableEntity, VariableEntityType from core.app.app_config.entities import ( DatasetRetrieveConfigEntity, PromptTemplateEntity, ) +from graphon.variables.input_entities import VariableEntity, VariableEntityType class TestAppConfigEntities: diff --git a/api/tests/unit_tests/core/app/apps/advanced_chat/test_app_runner_conversation_variables.py b/api/tests/unit_tests/core/app/apps/advanced_chat/test_app_runner_conversation_variables.py index 1fb0dc6cf1..45d4b0e321 100644 --- a/api/tests/unit_tests/core/app/apps/advanced_chat/test_app_runner_conversation_variables.py +++ b/api/tests/unit_tests/core/app/apps/advanced_chat/test_app_runner_conversation_variables.py @@ -3,12 +3,12 @@ from unittest.mock import MagicMock, patch from uuid import uuid4 -from graphon.variables import SegmentType from sqlalchemy.orm import Session from core.app.apps.advanced_chat.app_runner import AdvancedChatAppRunner from core.app.entities.app_invoke_entities import AdvancedChatAppGenerateEntity, InvokeFrom from factories import variable_factory +from graphon.variables import SegmentType from models import ConversationVariable, Workflow MINIMAL_GRAPH = { diff --git a/api/tests/unit_tests/core/app/apps/advanced_chat/test_generate_response_converter.py b/api/tests/unit_tests/core/app/apps/advanced_chat/test_generate_response_converter.py index e9fdeefee4..f2df35d7d0 100644 --- a/api/tests/unit_tests/core/app/apps/advanced_chat/test_generate_response_converter.py +++ b/api/tests/unit_tests/core/app/apps/advanced_chat/test_generate_response_converter.py @@ -1,7 +1,5 @@ from collections.abc import Generator -from graphon.enums import WorkflowNodeExecutionStatus - from core.app.apps.advanced_chat.generate_response_converter import AdvancedChatAppGenerateResponseConverter from core.app.entities.task_entities import ( ChatbotAppBlockingResponse, @@ -12,6 +10,7 @@ from core.app.entities.task_entities import ( NodeStartStreamResponse, PingStreamResponse, ) +from graphon.enums import WorkflowNodeExecutionStatus class TestAdvancedChatGenerateResponseConverter: diff --git a/api/tests/unit_tests/core/app/apps/advanced_chat/test_generate_task_pipeline.py b/api/tests/unit_tests/core/app/apps/advanced_chat/test_generate_task_pipeline.py index a6d8598955..99a386cd45 100644 --- a/api/tests/unit_tests/core/app/apps/advanced_chat/test_generate_task_pipeline.py +++ b/api/tests/unit_tests/core/app/apps/advanced_chat/test_generate_task_pipeline.py @@ -6,8 +6,6 @@ from types import SimpleNamespace from unittest import mock import pytest -from graphon.entities.pause_reason import HumanInputRequired -from graphon.enums import WorkflowExecutionStatus from core.app.apps.advanced_chat import generate_task_pipeline as pipeline_module from core.app.entities.app_invoke_entities import InvokeFrom @@ -19,6 +17,8 @@ from core.app.entities.queue_entities import ( QueueWorkflowSucceededEvent, ) from core.app.entities.task_entities import StreamEvent +from graphon.entities.pause_reason import HumanInputRequired +from graphon.enums import WorkflowExecutionStatus from models.enums import MessageStatus from models.execution_extra_content import HumanInputContent from models.model import AppMode, EndUser diff --git a/api/tests/unit_tests/core/app/apps/advanced_chat/test_generate_task_pipeline_core.py b/api/tests/unit_tests/core/app/apps/advanced_chat/test_generate_task_pipeline_core.py index 82b2e51019..29fd63c063 100644 --- a/api/tests/unit_tests/core/app/apps/advanced_chat/test_generate_task_pipeline_core.py +++ b/api/tests/unit_tests/core/app/apps/advanced_chat/test_generate_task_pipeline_core.py @@ -4,8 +4,6 @@ from contextlib import contextmanager from types import SimpleNamespace import pytest -from graphon.enums import BuiltinNodeTypes -from graphon.runtime import GraphRuntimeState, VariablePool from core.app.app_config.entities import AppAdditionalFeatures, WorkflowUIBasedAppConfig from core.app.apps.advanced_chat.generate_task_pipeline import ( @@ -49,6 +47,8 @@ from core.app.entities.task_entities import ( ) from core.base.tts.app_generator_tts_publisher import AudioTrunk from core.workflow.system_variables import build_system_variables +from graphon.enums import BuiltinNodeTypes +from graphon.runtime import GraphRuntimeState, VariablePool from libs.datetime_utils import naive_utc_now from models.enums import MessageStatus from models.model import AppMode, EndUser diff --git a/api/tests/unit_tests/core/app/apps/agent_chat/test_agent_chat_app_generator.py b/api/tests/unit_tests/core/app/apps/agent_chat/test_agent_chat_app_generator.py index 7dc4358150..80f7f94b1a 100644 --- a/api/tests/unit_tests/core/app/apps/agent_chat/test_agent_chat_app_generator.py +++ b/api/tests/unit_tests/core/app/apps/agent_chat/test_agent_chat_app_generator.py @@ -1,12 +1,12 @@ import contextlib import pytest -from graphon.model_runtime.errors.invoke import InvokeAuthorizationError from pydantic import ValidationError from core.app.apps.agent_chat.app_generator import AgentChatAppGenerator from core.app.apps.exc import GenerateTaskStoppedError from core.app.entities.app_invoke_entities import InvokeFrom +from graphon.model_runtime.errors.invoke import InvokeAuthorizationError class DummyAccount: diff --git a/api/tests/unit_tests/core/app/apps/agent_chat/test_agent_chat_app_runner.py b/api/tests/unit_tests/core/app/apps/agent_chat/test_agent_chat_app_runner.py index 08250bc3b6..4567b35480 100644 --- a/api/tests/unit_tests/core/app/apps/agent_chat/test_agent_chat_app_runner.py +++ b/api/tests/unit_tests/core/app/apps/agent_chat/test_agent_chat_app_runner.py @@ -1,10 +1,10 @@ import pytest -from graphon.model_runtime.entities.llm_entities import LLMMode -from graphon.model_runtime.entities.model_entities import ModelFeature, ModelPropertyKey from core.agent.entities import AgentEntity from core.app.apps.agent_chat.app_runner import AgentChatAppRunner from core.moderation.base import ModerationError +from graphon.model_runtime.entities.llm_entities import LLMMode +from graphon.model_runtime.entities.model_entities import ModelFeature, ModelPropertyKey @pytest.fixture diff --git a/api/tests/unit_tests/core/app/apps/chat/test_app_generator_and_runner.py b/api/tests/unit_tests/core/app/apps/chat/test_app_generator_and_runner.py index 68bcffb0e8..8f3c41701b 100644 --- a/api/tests/unit_tests/core/app/apps/chat/test_app_generator_and_runner.py +++ b/api/tests/unit_tests/core/app/apps/chat/test_app_generator_and_runner.py @@ -2,7 +2,6 @@ from types import SimpleNamespace from unittest.mock import Mock, patch import pytest -from graphon.model_runtime.errors.invoke import InvokeAuthorizationError from core.app.apps.chat.app_generator import ChatAppGenerator from core.app.apps.chat.app_runner import ChatAppRunner @@ -10,6 +9,7 @@ from core.app.apps.exc import GenerateTaskStoppedError from core.app.entities.app_invoke_entities import InvokeFrom from core.app.entities.queue_entities import QueueAnnotationReplyEvent from core.moderation.base import ModerationError +from graphon.model_runtime.errors.invoke import InvokeAuthorizationError from models.model import AppMode diff --git a/api/tests/unit_tests/core/app/apps/chat/test_base_app_runner_multimodal.py b/api/tests/unit_tests/core/app/apps/chat/test_base_app_runner_multimodal.py index f255d2c7df..b3ea1a464f 100644 --- a/api/tests/unit_tests/core/app/apps/chat/test_base_app_runner_multimodal.py +++ b/api/tests/unit_tests/core/app/apps/chat/test_base_app_runner_multimodal.py @@ -4,13 +4,13 @@ from unittest.mock import MagicMock, patch from uuid import uuid4 import pytest -from graphon.file import FileTransferMethod, FileType -from graphon.model_runtime.entities.message_entities import ImagePromptMessageContent from core.app.apps.base_app_queue_manager import PublishFrom from core.app.apps.base_app_runner import AppRunner from core.app.entities.app_invoke_entities import InvokeFrom from core.app.entities.queue_entities import QueueMessageFileEvent +from graphon.file import FileTransferMethod, FileType +from graphon.model_runtime.entities.message_entities import ImagePromptMessageContent from models.enums import CreatorUserRole diff --git a/api/tests/unit_tests/core/app/apps/common/test_graph_runtime_state_support.py b/api/tests/unit_tests/core/app/apps/common/test_graph_runtime_state_support.py index 4a94a2b4f1..201923e0e4 100644 --- a/api/tests/unit_tests/core/app/apps/common/test_graph_runtime_state_support.py +++ b/api/tests/unit_tests/core/app/apps/common/test_graph_runtime_state_support.py @@ -1,11 +1,11 @@ from types import SimpleNamespace import pytest -from graphon.runtime import GraphRuntimeState, VariablePool from core.app.apps.common.graph_runtime_state_support import GraphRuntimeStateSupport from core.workflow.system_variables import build_system_variables from core.workflow.variable_pool_initializer import add_variables_to_pool +from graphon.runtime import GraphRuntimeState, VariablePool def _make_state(workflow_run_id: str | None) -> GraphRuntimeState: diff --git a/api/tests/unit_tests/core/app/apps/common/test_workflow_response_converter.py b/api/tests/unit_tests/core/app/apps/common/test_workflow_response_converter.py index 328cd12f12..3ab63aed25 100644 --- a/api/tests/unit_tests/core/app/apps/common/test_workflow_response_converter.py +++ b/api/tests/unit_tests/core/app/apps/common/test_workflow_response_converter.py @@ -1,10 +1,9 @@ from collections.abc import Mapping, Sequence +from core.app.apps.common.workflow_response_converter import WorkflowResponseConverter from graphon.file import FILE_MODEL_IDENTITY, File, FileTransferMethod, FileType from graphon.variables.segments import ArrayFileSegment, FileSegment -from core.app.apps.common.workflow_response_converter import WorkflowResponseConverter - class TestWorkflowResponseConverterFetchFilesFromVariableValue: """Test class for WorkflowResponseConverter._fetch_files_from_variable_value method""" diff --git a/api/tests/unit_tests/core/app/apps/common/test_workflow_response_converter_human_input.py b/api/tests/unit_tests/core/app/apps/common/test_workflow_response_converter_human_input.py index bc11bf4174..1bef6f69cd 100644 --- a/api/tests/unit_tests/core/app/apps/common/test_workflow_response_converter_human_input.py +++ b/api/tests/unit_tests/core/app/apps/common/test_workflow_response_converter_human_input.py @@ -1,13 +1,12 @@ from datetime import UTC, datetime from types import SimpleNamespace -from graphon.entities import WorkflowStartReason -from graphon.runtime import GraphRuntimeState, VariablePool - from core.app.apps.common.workflow_response_converter import WorkflowResponseConverter from core.app.entities.app_invoke_entities import InvokeFrom from core.app.entities.queue_entities import QueueHumanInputFormFilledEvent, QueueHumanInputFormTimeoutEvent from core.workflow.system_variables import build_system_variables +from graphon.entities import WorkflowStartReason +from graphon.runtime import GraphRuntimeState, VariablePool def _build_converter(): diff --git a/api/tests/unit_tests/core/app/apps/common/test_workflow_response_converter_resumption.py b/api/tests/unit_tests/core/app/apps/common/test_workflow_response_converter_resumption.py index c9e146ff12..936ac37e55 100644 --- a/api/tests/unit_tests/core/app/apps/common/test_workflow_response_converter_resumption.py +++ b/api/tests/unit_tests/core/app/apps/common/test_workflow_response_converter_resumption.py @@ -1,11 +1,10 @@ from types import SimpleNamespace -from graphon.entities import WorkflowStartReason -from graphon.runtime import GraphRuntimeState, VariablePool - from core.app.apps.common.workflow_response_converter import WorkflowResponseConverter from core.app.entities.app_invoke_entities import InvokeFrom from core.workflow.system_variables import build_system_variables +from graphon.entities import WorkflowStartReason +from graphon.runtime import GraphRuntimeState, VariablePool def _build_converter() -> WorkflowResponseConverter: diff --git a/api/tests/unit_tests/core/app/apps/common/test_workflow_response_converter_truncation.py b/api/tests/unit_tests/core/app/apps/common/test_workflow_response_converter_truncation.py index 0fde7565d2..b3c0eb74fa 100644 --- a/api/tests/unit_tests/core/app/apps/common/test_workflow_response_converter_truncation.py +++ b/api/tests/unit_tests/core/app/apps/common/test_workflow_response_converter_truncation.py @@ -10,8 +10,6 @@ from typing import Any from unittest.mock import Mock import pytest -from graphon.entities import WorkflowStartReason -from graphon.enums import BuiltinNodeTypes from core.app.app_config.entities import WorkflowUIBasedAppConfig from core.app.apps.common.workflow_response_converter import WorkflowResponseConverter @@ -27,6 +25,8 @@ from core.app.entities.queue_entities import ( QueueNodeSucceededEvent, ) from core.workflow.system_variables import build_system_variables +from graphon.entities import WorkflowStartReason +from graphon.enums import BuiltinNodeTypes from libs.datetime_utils import naive_utc_now from models import Account from models.model import AppMode diff --git a/api/tests/unit_tests/core/app/apps/completion/test_app_runner.py b/api/tests/unit_tests/core/app/apps/completion/test_app_runner.py index 619d66085a..aa2085177e 100644 --- a/api/tests/unit_tests/core/app/apps/completion/test_app_runner.py +++ b/api/tests/unit_tests/core/app/apps/completion/test_app_runner.py @@ -2,11 +2,11 @@ from types import SimpleNamespace from unittest.mock import MagicMock import pytest -from graphon.model_runtime.entities.message_entities import ImagePromptMessageContent import core.app.apps.completion.app_runner as module from core.app.apps.completion.app_runner import CompletionAppRunner from core.moderation.base import ModerationError +from graphon.model_runtime.entities.message_entities import ImagePromptMessageContent @pytest.fixture diff --git a/api/tests/unit_tests/core/app/apps/completion/test_completion_completion_app_generator.py b/api/tests/unit_tests/core/app/apps/completion/test_completion_completion_app_generator.py index 96af9fbdee..f2e35f9900 100644 --- a/api/tests/unit_tests/core/app/apps/completion/test_completion_completion_app_generator.py +++ b/api/tests/unit_tests/core/app/apps/completion/test_completion_completion_app_generator.py @@ -3,13 +3,13 @@ from types import SimpleNamespace from unittest.mock import MagicMock import pytest -from graphon.model_runtime.errors.invoke import InvokeAuthorizationError from pydantic import ValidationError import core.app.apps.completion.app_generator as module from core.app.apps.completion.app_generator import CompletionAppGenerator from core.app.apps.exc import GenerateTaskStoppedError from core.app.entities.app_invoke_entities import InvokeFrom +from graphon.model_runtime.errors.invoke import InvokeAuthorizationError from services.errors.app import MoreLikeThisDisabledError from services.errors.message import MessageNotExistsError diff --git a/api/tests/unit_tests/core/app/apps/pipeline/test_pipeline_generate_response_converter.py b/api/tests/unit_tests/core/app/apps/pipeline/test_pipeline_generate_response_converter.py index 6cdcab29ab..cfe797aa76 100644 --- a/api/tests/unit_tests/core/app/apps/pipeline/test_pipeline_generate_response_converter.py +++ b/api/tests/unit_tests/core/app/apps/pipeline/test_pipeline_generate_response_converter.py @@ -1,7 +1,5 @@ from collections.abc import Generator -from graphon.enums import WorkflowExecutionStatus, WorkflowNodeExecutionStatus - from core.app.apps.pipeline.generate_response_converter import WorkflowAppGenerateResponseConverter from core.app.entities.task_entities import ( AppStreamResponse, @@ -12,6 +10,7 @@ from core.app.entities.task_entities import ( WorkflowAppBlockingResponse, WorkflowAppStreamResponse, ) +from graphon.enums import WorkflowExecutionStatus, WorkflowNodeExecutionStatus def test_convert_blocking_full_and_simple_response(): diff --git a/api/tests/unit_tests/core/app/apps/pipeline/test_pipeline_queue_manager.py b/api/tests/unit_tests/core/app/apps/pipeline/test_pipeline_queue_manager.py index 4fe82efcb3..9db83f5531 100644 --- a/api/tests/unit_tests/core/app/apps/pipeline/test_pipeline_queue_manager.py +++ b/api/tests/unit_tests/core/app/apps/pipeline/test_pipeline_queue_manager.py @@ -1,5 +1,4 @@ import pytest -from graphon.model_runtime.entities.llm_entities import LLMResult import core.app.apps.pipeline.pipeline_queue_manager as module from core.app.apps.base_app_queue_manager import PublishFrom @@ -14,6 +13,7 @@ from core.app.entities.queue_entities import ( QueueWorkflowPartialSuccessEvent, QueueWorkflowSucceededEvent, ) +from graphon.model_runtime.entities.llm_entities import LLMResult def test_publish_sets_stop_listen_and_raises_on_stopped(mocker): diff --git a/api/tests/unit_tests/core/app/apps/pipeline/test_pipeline_runner.py b/api/tests/unit_tests/core/app/apps/pipeline/test_pipeline_runner.py index c8ae288e6f..618c8fd76f 100644 --- a/api/tests/unit_tests/core/app/apps/pipeline/test_pipeline_runner.py +++ b/api/tests/unit_tests/core/app/apps/pipeline/test_pipeline_runner.py @@ -22,11 +22,11 @@ from types import SimpleNamespace from unittest.mock import MagicMock import pytest -from graphon.graph_events import GraphRunFailedEvent import core.app.apps.pipeline.pipeline_runner as module from core.app.apps.pipeline.pipeline_runner import PipelineRunner from core.app.entities.app_invoke_entities import InvokeFrom, UserFrom +from graphon.graph_events import GraphRunFailedEvent def _build_app_generate_entity() -> SimpleNamespace: diff --git a/api/tests/unit_tests/core/app/apps/test_base_app_generator.py b/api/tests/unit_tests/core/app/apps/test_base_app_generator.py index 6167be3bbd..b0f8b423e1 100644 --- a/api/tests/unit_tests/core/app/apps/test_base_app_generator.py +++ b/api/tests/unit_tests/core/app/apps/test_base_app_generator.py @@ -1,7 +1,7 @@ import pytest -from graphon.variables.input_entities import VariableEntity, VariableEntityType from core.app.apps.base_app_generator import BaseAppGenerator +from graphon.variables.input_entities import VariableEntity, VariableEntityType def test_validate_inputs_with_zero(): @@ -476,9 +476,8 @@ class TestBaseAppGeneratorExtras: assert converted[1] == "event: ping\n\n" def test_get_draft_var_saver_factory_debugger(self): - from graphon.enums import BuiltinNodeTypes - from core.app.entities.app_invoke_entities import InvokeFrom + from graphon.enums import BuiltinNodeTypes from models import Account base_app_generator = BaseAppGenerator() diff --git a/api/tests/unit_tests/core/app/apps/test_base_app_runner.py b/api/tests/unit_tests/core/app/apps/test_base_app_runner.py index 1dee7fdab6..17de39ca99 100644 --- a/api/tests/unit_tests/core/app/apps/test_base_app_runner.py +++ b/api/tests/unit_tests/core/app/apps/test_base_app_runner.py @@ -4,15 +4,6 @@ from types import SimpleNamespace from unittest.mock import MagicMock import pytest -from graphon.model_runtime.entities.llm_entities import LLMResult, LLMResultChunk, LLMResultChunkDelta, LLMUsage -from graphon.model_runtime.entities.message_entities import ( - AssistantPromptMessage, - ImagePromptMessageContent, - PromptMessageRole, - TextPromptMessageContent, -) -from graphon.model_runtime.entities.model_entities import ModelPropertyKey -from graphon.model_runtime.errors.invoke import InvokeBadRequestError from core.app.app_config.entities import ( AdvancedChatMessageEntity, @@ -23,6 +14,15 @@ from core.app.app_config.entities import ( from core.app.apps.base_app_runner import AppRunner from core.app.entities.app_invoke_entities import InvokeFrom from core.app.entities.queue_entities import QueueAgentMessageEvent, QueueLLMChunkEvent, QueueMessageEndEvent +from graphon.model_runtime.entities.llm_entities import LLMResult, LLMResultChunk, LLMResultChunkDelta, LLMUsage +from graphon.model_runtime.entities.message_entities import ( + AssistantPromptMessage, + ImagePromptMessageContent, + PromptMessageRole, + TextPromptMessageContent, +) +from graphon.model_runtime.entities.model_entities import ModelPropertyKey +from graphon.model_runtime.errors.invoke import InvokeBadRequestError from models.model import AppMode diff --git a/api/tests/unit_tests/core/app/apps/test_pause_resume.py b/api/tests/unit_tests/core/app/apps/test_pause_resume.py index a126bc85f7..a04a7b7576 100644 --- a/api/tests/unit_tests/core/app/apps/test_pause_resume.py +++ b/api/tests/unit_tests/core/app/apps/test_pause_resume.py @@ -4,6 +4,11 @@ from types import ModuleType, SimpleNamespace from typing import Any import graphon.nodes.human_input.entities # noqa: F401 +from core.app.apps.advanced_chat import app_generator as adv_app_gen_module +from core.app.apps.workflow import app_generator as wf_app_gen_module +from core.app.entities.app_invoke_entities import InvokeFrom +from core.workflow.node_factory import DifyNodeFactory +from core.workflow.system_variables import build_system_variables from graphon.entities import WorkflowStartReason from graphon.entities.base_node_data import BaseNodeData, RetryConfig from graphon.entities.graph_config import NodeConfigDict, NodeConfigDictAdapter @@ -25,12 +30,6 @@ from graphon.nodes.base.node import Node from graphon.nodes.end.entities import EndNodeData from graphon.nodes.start.entities import StartNodeData from graphon.runtime import GraphRuntimeState, VariablePool - -from core.app.apps.advanced_chat import app_generator as adv_app_gen_module -from core.app.apps.workflow import app_generator as wf_app_gen_module -from core.app.entities.app_invoke_entities import InvokeFrom -from core.workflow.node_factory import DifyNodeFactory -from core.workflow.system_variables import build_system_variables from tests.workflow_test_utils import build_test_graph_init_params if "core.ops.ops_trace_manager" not in sys.modules: diff --git a/api/tests/unit_tests/core/app/apps/test_workflow_app_runner_core.py b/api/tests/unit_tests/core/app/apps/test_workflow_app_runner_core.py index de5bca161c..58c7bfa4bc 100644 --- a/api/tests/unit_tests/core/app/apps/test_workflow_app_runner_core.py +++ b/api/tests/unit_tests/core/app/apps/test_workflow_app_runner_core.py @@ -4,6 +4,23 @@ from datetime import UTC, datetime from types import SimpleNamespace import pytest + +from core.app.apps.workflow_app_runner import WorkflowBasedAppRunner +from core.app.entities.app_invoke_entities import InvokeFrom, UserFrom +from core.app.entities.queue_entities import ( + QueueAgentLogEvent, + QueueIterationCompletedEvent, + QueueLoopCompletedEvent, + QueueNodeExceptionEvent, + QueueNodeFailedEvent, + QueueNodeRetryEvent, + QueueNodeSucceededEvent, + QueueTextChunkEvent, + QueueWorkflowPausedEvent, + QueueWorkflowStartedEvent, + QueueWorkflowSucceededEvent, +) +from core.workflow.system_variables import default_system_variables from graphon.entities.pause_reason import HumanInputRequired from graphon.enums import BuiltinNodeTypes from graphon.graph_events import ( @@ -24,23 +41,6 @@ from graphon.node_events import NodeRunResult from graphon.runtime import GraphRuntimeState, VariablePool from graphon.variables.variables import StringVariable -from core.app.apps.workflow_app_runner import WorkflowBasedAppRunner -from core.app.entities.app_invoke_entities import InvokeFrom, UserFrom -from core.app.entities.queue_entities import ( - QueueAgentLogEvent, - QueueIterationCompletedEvent, - QueueLoopCompletedEvent, - QueueNodeExceptionEvent, - QueueNodeFailedEvent, - QueueNodeRetryEvent, - QueueNodeSucceededEvent, - QueueTextChunkEvent, - QueueWorkflowPausedEvent, - QueueWorkflowStartedEvent, - QueueWorkflowSucceededEvent, -) -from core.workflow.system_variables import default_system_variables - class TestWorkflowBasedAppRunner: def test_resolve_user_from(self): diff --git a/api/tests/unit_tests/core/app/apps/test_workflow_app_runner_notifications.py b/api/tests/unit_tests/core/app/apps/test_workflow_app_runner_notifications.py index aa789d9ff3..10fb2271f4 100644 --- a/api/tests/unit_tests/core/app/apps/test_workflow_app_runner_notifications.py +++ b/api/tests/unit_tests/core/app/apps/test_workflow_app_runner_notifications.py @@ -1,11 +1,11 @@ from unittest.mock import MagicMock import pytest -from graphon.entities.pause_reason import HumanInputRequired -from graphon.graph_events import GraphRunPausedEvent from core.app.apps.workflow_app_runner import WorkflowBasedAppRunner from core.app.entities.queue_entities import QueueWorkflowPausedEvent +from graphon.entities.pause_reason import HumanInputRequired +from graphon.graph_events import GraphRunPausedEvent class _DummyQueueManager: diff --git a/api/tests/unit_tests/core/app/apps/test_workflow_app_runner_single_node.py b/api/tests/unit_tests/core/app/apps/test_workflow_app_runner_single_node.py index 9e30faecf2..620a153204 100644 --- a/api/tests/unit_tests/core/app/apps/test_workflow_app_runner_single_node.py +++ b/api/tests/unit_tests/core/app/apps/test_workflow_app_runner_single_node.py @@ -4,14 +4,14 @@ from typing import Any from unittest.mock import MagicMock, patch import pytest -from graphon.entities.graph_config import NodeConfigDictAdapter -from graphon.runtime import GraphRuntimeState, VariablePool from core.app.apps.base_app_queue_manager import AppQueueManager from core.app.apps.workflow.app_runner import WorkflowAppRunner from core.app.apps.workflow_app_runner import WorkflowBasedAppRunner from core.app.entities.app_invoke_entities import InvokeFrom, WorkflowAppGenerateEntity from core.workflow.system_variables import default_system_variables +from graphon.entities.graph_config import NodeConfigDictAdapter +from graphon.runtime import GraphRuntimeState, VariablePool from models.workflow import Workflow diff --git a/api/tests/unit_tests/core/app/apps/test_workflow_pause_events.py b/api/tests/unit_tests/core/app/apps/test_workflow_pause_events.py index 8a717e1dcc..a3ab379b66 100644 --- a/api/tests/unit_tests/core/app/apps/test_workflow_pause_events.py +++ b/api/tests/unit_tests/core/app/apps/test_workflow_pause_events.py @@ -3,11 +3,6 @@ from types import SimpleNamespace from unittest.mock import MagicMock import pytest -from graphon.entities import WorkflowStartReason -from graphon.entities.pause_reason import HumanInputRequired -from graphon.graph_events import GraphRunPausedEvent -from graphon.nodes.human_input.entities import FormInput, UserAction -from graphon.nodes.human_input.enums import FormInputType from core.app.apps.common import workflow_response_converter from core.app.apps.common.workflow_response_converter import WorkflowResponseConverter @@ -16,6 +11,11 @@ from core.app.entities.app_invoke_entities import InvokeFrom from core.app.entities.queue_entities import QueueWorkflowPausedEvent from core.app.entities.task_entities import HumanInputRequiredResponse, WorkflowPauseStreamResponse from core.workflow.system_variables import build_system_variables +from graphon.entities import WorkflowStartReason +from graphon.entities.pause_reason import HumanInputRequired +from graphon.graph_events import GraphRunPausedEvent +from graphon.nodes.human_input.entities import FormInput, UserAction +from graphon.nodes.human_input.enums import FormInputType from models.account import Account from models.human_input import RecipientType diff --git a/api/tests/unit_tests/core/app/apps/workflow/test_generate_response_converter.py b/api/tests/unit_tests/core/app/apps/workflow/test_generate_response_converter.py index b768e813bd..7dd7ffd727 100644 --- a/api/tests/unit_tests/core/app/apps/workflow/test_generate_response_converter.py +++ b/api/tests/unit_tests/core/app/apps/workflow/test_generate_response_converter.py @@ -1,7 +1,5 @@ from collections.abc import Generator -from graphon.enums import WorkflowExecutionStatus, WorkflowNodeExecutionStatus - from core.app.apps.workflow.generate_response_converter import WorkflowAppGenerateResponseConverter from core.app.entities.task_entities import ( ErrorStreamResponse, @@ -11,6 +9,7 @@ from core.app.entities.task_entities import ( WorkflowAppBlockingResponse, WorkflowAppStreamResponse, ) +from graphon.enums import WorkflowExecutionStatus, WorkflowNodeExecutionStatus class TestWorkflowGenerateResponseConverter: diff --git a/api/tests/unit_tests/core/app/apps/workflow/test_generate_task_pipeline.py b/api/tests/unit_tests/core/app/apps/workflow/test_generate_task_pipeline.py index 29df903aa8..1f6e7e12ef 100644 --- a/api/tests/unit_tests/core/app/apps/workflow/test_generate_task_pipeline.py +++ b/api/tests/unit_tests/core/app/apps/workflow/test_generate_task_pipeline.py @@ -2,15 +2,14 @@ import time from contextlib import contextmanager from unittest.mock import MagicMock -from graphon.entities import WorkflowStartReason -from graphon.runtime import GraphRuntimeState - from core.app.app_config.entities import WorkflowUIBasedAppConfig from core.app.apps.base_app_queue_manager import AppQueueManager from core.app.apps.workflow.generate_task_pipeline import WorkflowAppGenerateTaskPipeline from core.app.entities.app_invoke_entities import InvokeFrom, WorkflowAppGenerateEntity from core.app.entities.queue_entities import QueueWorkflowStartedEvent from core.workflow.system_variables import build_system_variables +from graphon.entities import WorkflowStartReason +from graphon.runtime import GraphRuntimeState from models.account import Account from models.model import AppMode from tests.workflow_test_utils import build_test_variable_pool diff --git a/api/tests/unit_tests/core/app/apps/workflow/test_generate_task_pipeline_core.py b/api/tests/unit_tests/core/app/apps/workflow/test_generate_task_pipeline_core.py index d91bb85aee..99433478d3 100644 --- a/api/tests/unit_tests/core/app/apps/workflow/test_generate_task_pipeline_core.py +++ b/api/tests/unit_tests/core/app/apps/workflow/test_generate_task_pipeline_core.py @@ -5,8 +5,6 @@ from types import SimpleNamespace from unittest.mock import MagicMock import pytest -from graphon.enums import BuiltinNodeTypes, WorkflowExecutionStatus -from graphon.runtime import GraphRuntimeState, VariablePool from core.app.app_config.entities import AppAdditionalFeatures, WorkflowUIBasedAppConfig from core.app.apps.workflow.generate_task_pipeline import WorkflowAppGenerateTaskPipeline @@ -47,6 +45,8 @@ from core.app.entities.task_entities import ( ) from core.base.tts.app_generator_tts_publisher import AudioTrunk from core.workflow.system_variables import build_system_variables, system_variables_to_mapping +from graphon.enums import BuiltinNodeTypes, WorkflowExecutionStatus +from graphon.runtime import GraphRuntimeState, VariablePool from libs.datetime_utils import naive_utc_now from models.enums import CreatorUserRole from models.model import AppMode, EndUser diff --git a/api/tests/unit_tests/core/app/entities/test_task_entities.py b/api/tests/unit_tests/core/app/entities/test_task_entities.py index 014a0cba72..7c79780641 100644 --- a/api/tests/unit_tests/core/app/entities/test_task_entities.py +++ b/api/tests/unit_tests/core/app/entities/test_task_entities.py @@ -1,11 +1,10 @@ -from graphon.enums import WorkflowNodeExecutionStatus - from core.app.entities.task_entities import ( NodeFinishStreamResponse, NodeRetryStreamResponse, NodeStartStreamResponse, StreamEvent, ) +from graphon.enums import WorkflowNodeExecutionStatus class TestTaskEntities: diff --git a/api/tests/unit_tests/core/app/layers/test_conversation_variable_persist_layer.py b/api/tests/unit_tests/core/app/layers/test_conversation_variable_persist_layer.py index a78c1b428f..ba55e8f695 100644 --- a/api/tests/unit_tests/core/app/layers/test_conversation_variable_persist_layer.py +++ b/api/tests/unit_tests/core/app/layers/test_conversation_variable_persist_layer.py @@ -1,6 +1,9 @@ from collections.abc import Sequence from unittest.mock import Mock +from core.app.layers.conversation_variable_persist_layer import ConversationVariablePersistenceLayer +from core.workflow.system_variables import SystemVariableKey +from core.workflow.variable_prefixes import CONVERSATION_VARIABLE_NODE_ID from graphon.enums import BuiltinNodeTypes, WorkflowNodeExecutionStatus from graphon.graph_engine.command_channels import CommandChannel from graphon.graph_events import NodeRunSucceededEvent, NodeRunVariableUpdatedEvent @@ -8,10 +11,6 @@ from graphon.node_events import NodeRunResult from graphon.runtime import ReadOnlyGraphRuntimeState from graphon.variables import StringVariable from graphon.variables.segments import Segment, StringSegment - -from core.app.layers.conversation_variable_persist_layer import ConversationVariablePersistenceLayer -from core.workflow.system_variables import SystemVariableKey -from core.workflow.variable_prefixes import CONVERSATION_VARIABLE_NODE_ID from libs.datetime_utils import naive_utc_now diff --git a/api/tests/unit_tests/core/app/layers/test_pause_state_persist_layer.py b/api/tests/unit_tests/core/app/layers/test_pause_state_persist_layer.py index 035e64325b..539944d683 100644 --- a/api/tests/unit_tests/core/app/layers/test_pause_state_persist_layer.py +++ b/api/tests/unit_tests/core/app/layers/test_pause_state_persist_layer.py @@ -4,6 +4,16 @@ from time import time from unittest.mock import Mock import pytest + +from core.app.app_config.entities import WorkflowUIBasedAppConfig +from core.app.entities.app_invoke_entities import AdvancedChatAppGenerateEntity, InvokeFrom, WorkflowAppGenerateEntity +from core.app.layers.pause_state_persist_layer import ( + PauseStatePersistenceLayer, + WorkflowResumptionContext, + _AdvancedChatAppGenerateEntityWrapper, + _WorkflowGenerateEntityWrapper, +) +from core.workflow.system_variables import SystemVariableKey from graphon.entities.pause_reason import SchedulingPause from graphon.graph_engine.entities.commands import GraphEngineCommand from graphon.graph_engine.layers.base import GraphEngineLayerNotInitializedError @@ -15,16 +25,6 @@ from graphon.graph_events import ( ) from graphon.runtime import ReadOnlyVariablePool from graphon.variables.segments import Segment - -from core.app.app_config.entities import WorkflowUIBasedAppConfig -from core.app.entities.app_invoke_entities import AdvancedChatAppGenerateEntity, InvokeFrom, WorkflowAppGenerateEntity -from core.app.layers.pause_state_persist_layer import ( - PauseStatePersistenceLayer, - WorkflowResumptionContext, - _AdvancedChatAppGenerateEntityWrapper, - _WorkflowGenerateEntityWrapper, -) -from core.workflow.system_variables import SystemVariableKey from models.model import AppMode from repositories.factory import DifyAPIRepositoryFactory diff --git a/api/tests/unit_tests/core/app/layers/test_suspend_layer.py b/api/tests/unit_tests/core/app/layers/test_suspend_layer.py index 95931f4f8b..12d49be0f1 100644 --- a/api/tests/unit_tests/core/app/layers/test_suspend_layer.py +++ b/api/tests/unit_tests/core/app/layers/test_suspend_layer.py @@ -1,6 +1,5 @@ -from graphon.graph_events import GraphRunPausedEvent - from core.app.layers.suspend_layer import SuspendLayer +from graphon.graph_events import GraphRunPausedEvent class TestSuspendLayer: diff --git a/api/tests/unit_tests/core/app/layers/test_timeslice_layer.py b/api/tests/unit_tests/core/app/layers/test_timeslice_layer.py index 7cf6eb4f31..1ac9a4d8c0 100644 --- a/api/tests/unit_tests/core/app/layers/test_timeslice_layer.py +++ b/api/tests/unit_tests/core/app/layers/test_timeslice_layer.py @@ -1,8 +1,7 @@ from unittest.mock import Mock, patch -from graphon.graph_engine.entities.commands import CommandType, GraphEngineCommand - from core.app.layers.timeslice_layer import TimeSliceLayer +from graphon.graph_engine.entities.commands import CommandType, GraphEngineCommand from services.workflow.entities import WorkflowScheduleCFSPlanEntity from services.workflow.scheduler import SchedulerCommand diff --git a/api/tests/unit_tests/core/app/layers/test_trigger_post_layer.py b/api/tests/unit_tests/core/app/layers/test_trigger_post_layer.py index aa9285789b..d3bd15b6f3 100644 --- a/api/tests/unit_tests/core/app/layers/test_trigger_post_layer.py +++ b/api/tests/unit_tests/core/app/layers/test_trigger_post_layer.py @@ -2,11 +2,10 @@ from datetime import UTC, datetime, timedelta from types import SimpleNamespace from unittest.mock import Mock, patch -from graphon.graph_events import GraphRunFailedEvent, GraphRunSucceededEvent -from graphon.runtime import VariablePool - from core.app.layers.trigger_post_layer import TriggerPostLayer from core.workflow.system_variables import build_system_variables +from graphon.graph_events import GraphRunFailedEvent, GraphRunSucceededEvent +from graphon.runtime import VariablePool from models.enums import WorkflowTriggerStatus diff --git a/api/tests/unit_tests/core/app/task_pipeline/test_based_generate_task_pipeline.py b/api/tests/unit_tests/core/app/task_pipeline/test_based_generate_task_pipeline.py index 58aa7d7478..c246f7b783 100644 --- a/api/tests/unit_tests/core/app/task_pipeline/test_based_generate_task_pipeline.py +++ b/api/tests/unit_tests/core/app/task_pipeline/test_based_generate_task_pipeline.py @@ -2,11 +2,11 @@ from types import SimpleNamespace from unittest.mock import Mock import pytest -from graphon.model_runtime.errors.invoke import InvokeAuthorizationError, InvokeError from core.app.entities.queue_entities import QueueErrorEvent from core.app.task_pipeline.based_generate_task_pipeline import BasedGenerateTaskPipeline from core.errors.error import QuotaExceededError +from graphon.model_runtime.errors.invoke import InvokeAuthorizationError, InvokeError from models.enums import MessageStatus diff --git a/api/tests/unit_tests/core/app/task_pipeline/test_easy_ui_based_generate_task_pipeline.py b/api/tests/unit_tests/core/app/task_pipeline/test_easy_ui_based_generate_task_pipeline.py index 4aaa10a81a..1c1bf391d3 100644 --- a/api/tests/unit_tests/core/app/task_pipeline/test_easy_ui_based_generate_task_pipeline.py +++ b/api/tests/unit_tests/core/app/task_pipeline/test_easy_ui_based_generate_task_pipeline.py @@ -2,8 +2,6 @@ from types import SimpleNamespace from unittest.mock import ANY, Mock, patch import pytest -from graphon.model_runtime.entities.llm_entities import LLMResult as RuntimeLLMResult -from graphon.model_runtime.entities.message_entities import TextPromptMessageContent from core.app.apps.base_app_queue_manager import AppQueueManager from core.app.entities.app_invoke_entities import ChatAppGenerateEntity @@ -28,6 +26,8 @@ from core.app.entities.task_entities import ( from core.app.task_pipeline.easy_ui_based_generate_task_pipeline import EasyUIBasedGenerateTaskPipeline from core.base.tts import AppGeneratorTTSPublisher from core.ops.ops_trace_manager import TraceQueueManager +from graphon.model_runtime.entities.llm_entities import LLMResult as RuntimeLLMResult +from graphon.model_runtime.entities.message_entities import TextPromptMessageContent from models.model import AppMode diff --git a/api/tests/unit_tests/core/app/task_pipeline/test_easy_ui_based_generate_task_pipeline_core.py b/api/tests/unit_tests/core/app/task_pipeline/test_easy_ui_based_generate_task_pipeline_core.py index f22602a400..a20d89d807 100644 --- a/api/tests/unit_tests/core/app/task_pipeline/test_easy_ui_based_generate_task_pipeline_core.py +++ b/api/tests/unit_tests/core/app/task_pipeline/test_easy_ui_based_generate_task_pipeline_core.py @@ -5,9 +5,6 @@ from types import SimpleNamespace from unittest.mock import Mock import pytest -from graphon.file import FileTransferMethod -from graphon.model_runtime.entities.llm_entities import LLMResult, LLMResultChunk, LLMResultChunkDelta, LLMUsage -from graphon.model_runtime.entities.message_entities import AssistantPromptMessage, TextPromptMessageContent from core.app.app_config.entities import ( AppAdditionalFeatures, @@ -41,6 +38,9 @@ from core.app.entities.task_entities import ( ) from core.app.task_pipeline.easy_ui_based_generate_task_pipeline import EasyUIBasedGenerateTaskPipeline from core.base.tts import AudioTrunk +from graphon.file import FileTransferMethod +from graphon.model_runtime.entities.llm_entities import LLMResult, LLMResultChunk, LLMResultChunkDelta, LLMUsage +from graphon.model_runtime.entities.message_entities import AssistantPromptMessage, TextPromptMessageContent from models.model import AppMode diff --git a/api/tests/unit_tests/core/app/task_pipeline/test_easy_ui_message_end_files.py b/api/tests/unit_tests/core/app/task_pipeline/test_easy_ui_message_end_files.py index 31b7313066..595d716666 100644 --- a/api/tests/unit_tests/core/app/task_pipeline/test_easy_ui_message_end_files.py +++ b/api/tests/unit_tests/core/app/task_pipeline/test_easy_ui_message_end_files.py @@ -17,11 +17,11 @@ import uuid from unittest.mock import MagicMock, Mock, patch import pytest -from graphon.file import FileTransferMethod, FileType from sqlalchemy.orm import Session from core.app.entities.task_entities import MessageEndStreamResponse from core.app.task_pipeline.easy_ui_based_generate_task_pipeline import EasyUIBasedGenerateTaskPipeline +from graphon.file import FileTransferMethod, FileType from models.model import MessageFile, UploadFile diff --git a/api/tests/unit_tests/core/app/test_easy_ui_model_config_manager.py b/api/tests/unit_tests/core/app/test_easy_ui_model_config_manager.py index 29df7eea86..21c761c579 100644 --- a/api/tests/unit_tests/core/app/test_easy_ui_model_config_manager.py +++ b/api/tests/unit_tests/core/app/test_easy_ui_model_config_manager.py @@ -1,10 +1,9 @@ from types import SimpleNamespace from unittest.mock import patch -from graphon.model_runtime.entities.model_entities import ModelPropertyKey - from core.app.app_config.easy_ui_based_app.model_config.manager import ModelConfigManager from core.app.app_config.entities import ModelConfigEntity +from graphon.model_runtime.entities.model_entities import ModelPropertyKey from models.provider_ids import ModelProviderID diff --git a/api/tests/unit_tests/core/app/workflow/layers/test_persistence.py b/api/tests/unit_tests/core/app/workflow/layers/test_persistence.py index dc2d82ccd6..5c50cb78da 100644 --- a/api/tests/unit_tests/core/app/workflow/layers/test_persistence.py +++ b/api/tests/unit_tests/core/app/workflow/layers/test_persistence.py @@ -2,14 +2,14 @@ from datetime import UTC, datetime from unittest.mock import Mock import pytest -from graphon.enums import BuiltinNodeTypes, WorkflowNodeExecutionStatus, WorkflowType -from graphon.node_events import NodeRunResult from core.app.workflow.layers.persistence import ( PersistenceWorkflowInfo, WorkflowPersistenceLayer, _NodeRuntimeSnapshot, ) +from graphon.enums import BuiltinNodeTypes, WorkflowNodeExecutionStatus, WorkflowType +from graphon.node_events import NodeRunResult def _build_layer() -> WorkflowPersistenceLayer: diff --git a/api/tests/unit_tests/core/app/workflow/test_file_runtime.py b/api/tests/unit_tests/core/app/workflow/test_file_runtime.py index 7be9d6ac1e..cddd03f4b0 100644 --- a/api/tests/unit_tests/core/app/workflow/test_file_runtime.py +++ b/api/tests/unit_tests/core/app/workflow/test_file_runtime.py @@ -8,13 +8,13 @@ from unittest.mock import MagicMock, patch from urllib.parse import parse_qs, urlparse import pytest -from graphon.file import File, FileTransferMethod, FileType from core.app.entities.app_invoke_entities import InvokeFrom, UserFrom from core.app.file_access import DatabaseFileAccessController, FileAccessScope from core.app.workflow import file_runtime from core.app.workflow.file_runtime import DifyWorkflowFileRuntime, bind_dify_workflow_file_runtime from core.workflow.file_reference import build_file_reference +from graphon.file import File, FileTransferMethod, FileType from models import ToolFile, UploadFile diff --git a/api/tests/unit_tests/core/app/workflow/test_node_factory.py b/api/tests/unit_tests/core/app/workflow/test_node_factory.py index 8497261d45..c4bfb23272 100644 --- a/api/tests/unit_tests/core/app/workflow/test_node_factory.py +++ b/api/tests/unit_tests/core/app/workflow/test_node_factory.py @@ -1,10 +1,10 @@ from types import SimpleNamespace import pytest -from graphon.enums import BuiltinNodeTypes from core.app.entities.app_invoke_entities import InvokeFrom, UserFrom, build_dify_run_context from core.workflow.node_factory import DifyNodeFactory +from graphon.enums import BuiltinNodeTypes class DummyNode: diff --git a/api/tests/unit_tests/core/app/workflow/test_observability_layer_extra.py b/api/tests/unit_tests/core/app/workflow/test_observability_layer_extra.py index a47d3db6f5..82552470a9 100644 --- a/api/tests/unit_tests/core/app/workflow/test_observability_layer_extra.py +++ b/api/tests/unit_tests/core/app/workflow/test_observability_layer_extra.py @@ -2,9 +2,8 @@ from __future__ import annotations from types import SimpleNamespace -from graphon.enums import BuiltinNodeTypes - from core.app.workflow.layers.observability import ObservabilityLayer +from graphon.enums import BuiltinNodeTypes class TestObservabilityLayerExtras: diff --git a/api/tests/unit_tests/core/app/workflow/test_persistence_layer.py b/api/tests/unit_tests/core/app/workflow/test_persistence_layer.py index d8a68f6d00..cacb4dd4fa 100644 --- a/api/tests/unit_tests/core/app/workflow/test_persistence_layer.py +++ b/api/tests/unit_tests/core/app/workflow/test_persistence_layer.py @@ -4,6 +4,10 @@ from datetime import UTC, datetime from types import SimpleNamespace import pytest + +from core.app.entities.app_invoke_entities import WorkflowAppGenerateEntity +from core.app.workflow.layers.persistence import PersistenceWorkflowInfo, WorkflowPersistenceLayer +from core.workflow.system_variables import SystemVariableKey, build_system_variables from graphon.entities import WorkflowNodeExecution from graphon.entities.pause_reason import SchedulingPause from graphon.enums import ( @@ -29,10 +33,6 @@ from graphon.graph_events import ( from graphon.node_events import NodeRunResult from graphon.runtime import GraphRuntimeState, ReadOnlyGraphRuntimeStateWrapper, VariablePool -from core.app.entities.app_invoke_entities import WorkflowAppGenerateEntity -from core.app.workflow.layers.persistence import PersistenceWorkflowInfo, WorkflowPersistenceLayer -from core.workflow.system_variables import SystemVariableKey, build_system_variables - class _RepoRecorder: def __init__(self) -> None: diff --git a/api/tests/unit_tests/core/base/test_app_generator_tts_publisher.py b/api/tests/unit_tests/core/base/test_app_generator_tts_publisher.py index 5ff9774b52..7b433ab57b 100644 --- a/api/tests/unit_tests/core/base/test_app_generator_tts_publisher.py +++ b/api/tests/unit_tests/core/base/test_app_generator_tts_publisher.py @@ -301,6 +301,7 @@ class TestAppGeneratorTTSPublisher: publisher = AppGeneratorTTSPublisher("tenant", "voice1") publisher.executor = MagicMock() + from core.app.entities.queue_entities import QueueAgentMessageEvent from graphon.model_runtime.entities.llm_entities import LLMResultChunk, LLMResultChunkDelta from graphon.model_runtime.entities.message_entities import ( AssistantPromptMessage, @@ -308,8 +309,6 @@ class TestAppGeneratorTTSPublisher: TextPromptMessageContent, ) - from core.app.entities.queue_entities import QueueAgentMessageEvent - chunk = LLMResultChunk( model="model", delta=LLMResultChunkDelta( @@ -337,11 +336,10 @@ class TestAppGeneratorTTSPublisher: publisher = AppGeneratorTTSPublisher("tenant", "voice1") publisher.executor = MagicMock() + from core.app.entities.queue_entities import QueueAgentMessageEvent from graphon.model_runtime.entities.llm_entities import LLMResultChunk, LLMResultChunkDelta from graphon.model_runtime.entities.message_entities import AssistantPromptMessage - from core.app.entities.queue_entities import QueueAgentMessageEvent - chunk = LLMResultChunk( model="model", delta=LLMResultChunkDelta( diff --git a/api/tests/unit_tests/core/datasource/test_datasource_manager.py b/api/tests/unit_tests/core/datasource/test_datasource_manager.py index d338cadb77..81315d2508 100644 --- a/api/tests/unit_tests/core/datasource/test_datasource_manager.py +++ b/api/tests/unit_tests/core/datasource/test_datasource_manager.py @@ -2,15 +2,15 @@ import types from collections.abc import Generator import pytest -from graphon.enums import WorkflowNodeExecutionStatus -from graphon.file import File, FileTransferMethod, FileType -from graphon.node_events import StreamChunkEvent, StreamCompletedEvent from contexts.wrapper import RecyclableContextVar from core.datasource.datasource_manager import DatasourceManager from core.datasource.entities.datasource_entities import DatasourceMessage, DatasourceProviderType from core.datasource.errors import DatasourceProviderNotFoundError from core.workflow.file_reference import parse_file_reference +from graphon.enums import WorkflowNodeExecutionStatus +from graphon.file import File, FileTransferMethod, FileType +from graphon.node_events import StreamChunkEvent, StreamCompletedEvent def _gen_messages_text_only(text: str) -> Generator[DatasourceMessage, None, None]: diff --git a/api/tests/unit_tests/core/datasource/utils/test_message_transformer.py b/api/tests/unit_tests/core/datasource/utils/test_message_transformer.py index fbaf6d497d..0fca43cd0b 100644 --- a/api/tests/unit_tests/core/datasource/utils/test_message_transformer.py +++ b/api/tests/unit_tests/core/datasource/utils/test_message_transformer.py @@ -1,10 +1,10 @@ from unittest.mock import MagicMock, patch import pytest -from graphon.file import File, FileTransferMethod, FileType from core.datasource.entities.datasource_entities import DatasourceMessage from core.datasource.utils.message_transformer import DatasourceFileMessageTransformer +from graphon.file import File, FileTransferMethod, FileType from models.tools import ToolFile diff --git a/api/tests/unit_tests/core/entities/test_entities_execution_extra_content.py b/api/tests/unit_tests/core/entities/test_entities_execution_extra_content.py index ff9fd0d8f3..ef8f360dbf 100644 --- a/api/tests/unit_tests/core/entities/test_entities_execution_extra_content.py +++ b/api/tests/unit_tests/core/entities/test_entities_execution_extra_content.py @@ -1,12 +1,11 @@ -from graphon.nodes.human_input.entities import FormInput, UserAction -from graphon.nodes.human_input.enums import FormInputType - from core.entities.execution_extra_content import ( ExecutionExtraContentDomainModel, HumanInputContent, HumanInputFormDefinition, HumanInputFormSubmissionData, ) +from graphon.nodes.human_input.entities import FormInput, UserAction +from graphon.nodes.human_input.enums import FormInputType from models.execution_extra_content import ExecutionContentType diff --git a/api/tests/unit_tests/core/entities/test_entities_model_entities.py b/api/tests/unit_tests/core/entities/test_entities_model_entities.py index 2acd278a31..a0b2820157 100644 --- a/api/tests/unit_tests/core/entities/test_entities_model_entities.py +++ b/api/tests/unit_tests/core/entities/test_entities_model_entities.py @@ -8,9 +8,6 @@ drive provider mapping behavior. """ import pytest -from graphon.model_runtime.entities.common_entities import I18nObject -from graphon.model_runtime.entities.model_entities import FetchFrom, ModelType -from graphon.model_runtime.entities.provider_entities import ConfigurateMethod, ProviderEntity from core.entities.model_entities import ( DefaultModelEntity, @@ -19,6 +16,9 @@ from core.entities.model_entities import ( ProviderModelWithStatusEntity, SimpleModelProviderEntity, ) +from graphon.model_runtime.entities.common_entities import I18nObject +from graphon.model_runtime.entities.model_entities import FetchFrom, ModelType +from graphon.model_runtime.entities.provider_entities import ConfigurateMethod, ProviderEntity def _build_model_with_status(status: ModelStatus) -> ProviderModelWithStatusEntity: diff --git a/api/tests/unit_tests/core/entities/test_entities_provider_configuration.py b/api/tests/unit_tests/core/entities/test_entities_provider_configuration.py index 8cf0409c4c..fe2c226843 100644 --- a/api/tests/unit_tests/core/entities/test_entities_provider_configuration.py +++ b/api/tests/unit_tests/core/entities/test_entities_provider_configuration.py @@ -6,17 +6,6 @@ from typing import Any from unittest.mock import Mock, patch import pytest -from graphon.model_runtime.entities.common_entities import I18nObject -from graphon.model_runtime.entities.model_entities import AIModelEntity, FetchFrom, ModelType -from graphon.model_runtime.entities.provider_entities import ( - ConfigurateMethod, - CredentialFormSchema, - FieldModelSchema, - FormType, - ModelCredentialSchema, - ProviderCredentialSchema, - ProviderEntity, -) from constants import HIDDEN_VALUE from core.entities.model_entities import ModelStatus @@ -35,6 +24,17 @@ from core.entities.provider_entities import ( SystemConfiguration, SystemConfigurationStatus, ) +from graphon.model_runtime.entities.common_entities import I18nObject +from graphon.model_runtime.entities.model_entities import AIModelEntity, FetchFrom, ModelType +from graphon.model_runtime.entities.provider_entities import ( + ConfigurateMethod, + CredentialFormSchema, + FieldModelSchema, + FormType, + ModelCredentialSchema, + ProviderCredentialSchema, + ProviderEntity, +) from models.enums import CredentialSourceType from models.provider import ProviderType from models.provider_ids import ModelProviderID diff --git a/api/tests/unit_tests/core/entities/test_entities_provider_entities.py b/api/tests/unit_tests/core/entities/test_entities_provider_entities.py index 8685d16283..a159d3ad4d 100644 --- a/api/tests/unit_tests/core/entities/test_entities_provider_entities.py +++ b/api/tests/unit_tests/core/entities/test_entities_provider_entities.py @@ -1,5 +1,4 @@ import pytest -from graphon.model_runtime.entities.model_entities import ModelType from core.entities.parameter_entities import AppSelectorScope from core.entities.provider_entities import ( @@ -9,6 +8,7 @@ from core.entities.provider_entities import ( ProviderQuotaType, ) from core.tools.entities.common_entities import I18nObject +from graphon.model_runtime.entities.model_entities import ModelType def test_provider_quota_type_value_of_returns_enum_member() -> None: diff --git a/api/tests/unit_tests/core/helper/test_moderation.py b/api/tests/unit_tests/core/helper/test_moderation.py index 4a84099b74..a0dfa86d20 100644 --- a/api/tests/unit_tests/core/helper/test_moderation.py +++ b/api/tests/unit_tests/core/helper/test_moderation.py @@ -2,11 +2,11 @@ from types import SimpleNamespace from typing import cast import pytest -from graphon.model_runtime.errors.invoke import InvokeBadRequestError from pytest_mock import MockerFixture from core.app.entities.app_invoke_entities import ModelConfigWithCredentialsEntity from core.helper.moderation import check_moderation +from graphon.model_runtime.errors.invoke import InvokeBadRequestError from models.provider import ProviderType diff --git a/api/tests/unit_tests/core/llm_generator/output_parser/test_structured_output.py b/api/tests/unit_tests/core/llm_generator/output_parser/test_structured_output.py index b45f6fd9a7..6ed9ddb476 100644 --- a/api/tests/unit_tests/core/llm_generator/output_parser/test_structured_output.py +++ b/api/tests/unit_tests/core/llm_generator/output_parser/test_structured_output.py @@ -2,20 +2,6 @@ import json from unittest.mock import MagicMock, patch import pytest -from graphon.model_runtime.entities.llm_entities import ( - LLMResult, - LLMResultChunk, - LLMResultChunkDelta, - LLMResultWithStructuredOutput, - LLMUsage, -) -from graphon.model_runtime.entities.message_entities import ( - AssistantPromptMessage, - SystemPromptMessage, - TextPromptMessageContent, - UserPromptMessage, -) -from graphon.model_runtime.entities.model_entities import AIModelEntity, ParameterRule, ParameterType from core.llm_generator.output_parser.errors import OutputParserError from core.llm_generator.output_parser.structured_output import ( @@ -30,6 +16,20 @@ from core.llm_generator.output_parser.structured_output import ( remove_additional_properties, ) from core.model_manager import ModelInstance +from graphon.model_runtime.entities.llm_entities import ( + LLMResult, + LLMResultChunk, + LLMResultChunkDelta, + LLMResultWithStructuredOutput, + LLMUsage, +) +from graphon.model_runtime.entities.message_entities import ( + AssistantPromptMessage, + SystemPromptMessage, + TextPromptMessageContent, + UserPromptMessage, +) +from graphon.model_runtime.entities.model_entities import AIModelEntity, ParameterRule, ParameterType class TestStructuredOutput: diff --git a/api/tests/unit_tests/core/llm_generator/test_llm_generator.py b/api/tests/unit_tests/core/llm_generator/test_llm_generator.py index 7cdfb31189..2716f4712c 100644 --- a/api/tests/unit_tests/core/llm_generator/test_llm_generator.py +++ b/api/tests/unit_tests/core/llm_generator/test_llm_generator.py @@ -2,12 +2,12 @@ import json from unittest.mock import MagicMock, patch import pytest -from graphon.model_runtime.entities.llm_entities import LLMMode, LLMResult -from graphon.model_runtime.errors.invoke import InvokeAuthorizationError, InvokeError from core.app.app_config.entities import ModelConfig from core.llm_generator.entities import RuleCodeGeneratePayload, RuleGeneratePayload, RuleStructuredOutputPayload from core.llm_generator.llm_generator import LLMGenerator +from graphon.model_runtime.entities.llm_entities import LLMMode, LLMResult +from graphon.model_runtime.errors.invoke import InvokeAuthorizationError, InvokeError class TestLLMGenerator: diff --git a/api/tests/unit_tests/core/mcp/server/test_streamable_http.py b/api/tests/unit_tests/core/mcp/server/test_streamable_http.py index 9a815fb94d..57456085c3 100644 --- a/api/tests/unit_tests/core/mcp/server/test_streamable_http.py +++ b/api/tests/unit_tests/core/mcp/server/test_streamable_http.py @@ -3,7 +3,6 @@ from unittest.mock import Mock, patch import jsonschema import pytest -from graphon.variables.input_entities import VariableEntity, VariableEntityType from core.app.features.rate_limiting.rate_limit import RateLimitGenerator from core.mcp import types @@ -19,6 +18,7 @@ from core.mcp.server.streamable_http import ( prepare_tool_arguments, process_mapping_response, ) +from graphon.variables.input_entities import VariableEntity, VariableEntityType from models.model import App, AppMCPServer, AppMode, EndUser diff --git a/api/tests/unit_tests/core/memory/test_token_buffer_memory.py b/api/tests/unit_tests/core/memory/test_token_buffer_memory.py index 9a5fb319d7..f459250b8e 100644 --- a/api/tests/unit_tests/core/memory/test_token_buffer_memory.py +++ b/api/tests/unit_tests/core/memory/test_token_buffer_memory.py @@ -4,6 +4,8 @@ from unittest.mock import MagicMock, patch from uuid import uuid4 import pytest + +from core.memory.token_buffer_memory import TokenBufferMemory from graphon.model_runtime.entities import ( AssistantPromptMessage, ImagePromptMessageContent, @@ -11,8 +13,6 @@ from graphon.model_runtime.entities import ( TextPromptMessageContent, UserPromptMessage, ) - -from core.memory.token_buffer_memory import TokenBufferMemory from models.model import AppMode # --------------------------------------------------------------------------- diff --git a/api/tests/unit_tests/core/model_runtime/test_model_provider_factory.py b/api/tests/unit_tests/core/model_runtime/test_model_provider_factory.py index 6a672fdfd5..249ecb5006 100644 --- a/api/tests/unit_tests/core/model_runtime/test_model_provider_factory.py +++ b/api/tests/unit_tests/core/model_runtime/test_model_provider_factory.py @@ -1,6 +1,7 @@ from unittest.mock import Mock import pytest + from graphon.model_runtime.entities.common_entities import I18nObject from graphon.model_runtime.entities.model_entities import AIModelEntity, FetchFrom, ModelType from graphon.model_runtime.entities.provider_entities import ( diff --git a/api/tests/unit_tests/core/ops/aliyun_trace/test_aliyun_trace.py b/api/tests/unit_tests/core/ops/aliyun_trace/test_aliyun_trace.py index 62d631a754..c2324fdec4 100644 --- a/api/tests/unit_tests/core/ops/aliyun_trace/test_aliyun_trace.py +++ b/api/tests/unit_tests/core/ops/aliyun_trace/test_aliyun_trace.py @@ -5,8 +5,6 @@ from types import SimpleNamespace from unittest.mock import MagicMock import pytest -from graphon.entities import WorkflowNodeExecution -from graphon.enums import BuiltinNodeTypes, WorkflowNodeExecutionMetadataKey from opentelemetry.trace import Link, SpanContext, SpanKind, Status, StatusCode, TraceFlags import core.ops.aliyun_trace.aliyun_trace as aliyun_trace_module @@ -36,6 +34,8 @@ from core.ops.entities.trace_entity import ( ToolTraceInfo, WorkflowTraceInfo, ) +from graphon.entities import WorkflowNodeExecution +from graphon.enums import BuiltinNodeTypes, WorkflowNodeExecutionMetadataKey class RecordingTraceClient: diff --git a/api/tests/unit_tests/core/ops/aliyun_trace/test_aliyun_trace_utils.py b/api/tests/unit_tests/core/ops/aliyun_trace/test_aliyun_trace_utils.py index 2d2be12f05..e4d8f2d5ea 100644 --- a/api/tests/unit_tests/core/ops/aliyun_trace/test_aliyun_trace_utils.py +++ b/api/tests/unit_tests/core/ops/aliyun_trace/test_aliyun_trace_utils.py @@ -1,8 +1,6 @@ import json from unittest.mock import MagicMock -from graphon.entities import WorkflowNodeExecution -from graphon.enums import WorkflowNodeExecutionStatus from opentelemetry.trace import Link, StatusCode from core.ops.aliyun_trace.entities.semconv import ( @@ -26,6 +24,8 @@ from core.ops.aliyun_trace.utils import ( serialize_json_data, ) from core.rag.models.document import Document +from graphon.entities import WorkflowNodeExecution +from graphon.enums import WorkflowNodeExecutionStatus from models import EndUser diff --git a/api/tests/unit_tests/core/ops/langfuse_trace/test_langfuse_trace.py b/api/tests/unit_tests/core/ops/langfuse_trace/test_langfuse_trace.py index 374371fb42..a0bcc92795 100644 --- a/api/tests/unit_tests/core/ops/langfuse_trace/test_langfuse_trace.py +++ b/api/tests/unit_tests/core/ops/langfuse_trace/test_langfuse_trace.py @@ -5,7 +5,6 @@ from types import SimpleNamespace from unittest.mock import MagicMock import pytest -from graphon.enums import BuiltinNodeTypes from core.ops.entities.config_entity import LangfuseConfig from core.ops.entities.trace_entity import ( @@ -26,6 +25,7 @@ from core.ops.langfuse_trace.entities.langfuse_trace_entity import ( UnitEnum, ) from core.ops.langfuse_trace.langfuse_trace import LangFuseDataTrace +from graphon.enums import BuiltinNodeTypes from models import EndUser from models.enums import MessageStatus diff --git a/api/tests/unit_tests/core/ops/langsmith_trace/test_langsmith_trace.py b/api/tests/unit_tests/core/ops/langsmith_trace/test_langsmith_trace.py index bfe916f018..34c64c54a1 100644 --- a/api/tests/unit_tests/core/ops/langsmith_trace/test_langsmith_trace.py +++ b/api/tests/unit_tests/core/ops/langsmith_trace/test_langsmith_trace.py @@ -3,7 +3,6 @@ from datetime import datetime, timedelta from unittest.mock import MagicMock import pytest -from graphon.enums import BuiltinNodeTypes, WorkflowNodeExecutionMetadataKey from core.ops.entities.config_entity import LangSmithConfig from core.ops.entities.trace_entity import ( @@ -22,6 +21,7 @@ from core.ops.langsmith_trace.entities.langsmith_trace_entity import ( LangSmithRunUpdateModel, ) from core.ops.langsmith_trace.langsmith_trace import LangSmithDataTrace +from graphon.enums import BuiltinNodeTypes, WorkflowNodeExecutionMetadataKey from models import EndUser diff --git a/api/tests/unit_tests/core/ops/mlflow_trace/test_mlflow_trace.py b/api/tests/unit_tests/core/ops/mlflow_trace/test_mlflow_trace.py index f4c485a9fc..afc5726ede 100644 --- a/api/tests/unit_tests/core/ops/mlflow_trace/test_mlflow_trace.py +++ b/api/tests/unit_tests/core/ops/mlflow_trace/test_mlflow_trace.py @@ -9,7 +9,6 @@ from types import SimpleNamespace from unittest.mock import MagicMock, patch import pytest -from graphon.enums import BuiltinNodeTypes from core.ops.entities.config_entity import DatabricksConfig, MLflowConfig from core.ops.entities.trace_entity import ( @@ -22,6 +21,7 @@ from core.ops.entities.trace_entity import ( WorkflowTraceInfo, ) from core.ops.mlflow_trace.mlflow_trace import MLflowDataTrace, datetime_to_nanoseconds +from graphon.enums import BuiltinNodeTypes # ── Helpers ────────────────────────────────────────────────────────────────── diff --git a/api/tests/unit_tests/core/ops/opik_trace/test_opik_trace.py b/api/tests/unit_tests/core/ops/opik_trace/test_opik_trace.py index 1cb32f2ee0..c02ac413f2 100644 --- a/api/tests/unit_tests/core/ops/opik_trace/test_opik_trace.py +++ b/api/tests/unit_tests/core/ops/opik_trace/test_opik_trace.py @@ -5,7 +5,6 @@ from types import SimpleNamespace from unittest.mock import MagicMock import pytest -from graphon.enums import BuiltinNodeTypes, WorkflowNodeExecutionMetadataKey from core.ops.entities.config_entity import OpikConfig from core.ops.entities.trace_entity import ( @@ -19,6 +18,7 @@ from core.ops.entities.trace_entity import ( WorkflowTraceInfo, ) from core.ops.opik_trace.opik_trace import OpikDataTrace, prepare_opik_uuid, wrap_dict, wrap_metadata +from graphon.enums import BuiltinNodeTypes, WorkflowNodeExecutionMetadataKey from models import EndUser from models.enums import MessageStatus diff --git a/api/tests/unit_tests/core/ops/tencent_trace/test_span_builder.py b/api/tests/unit_tests/core/ops/tencent_trace/test_span_builder.py index 696f859b6f..6113e5c6c8 100644 --- a/api/tests/unit_tests/core/ops/tencent_trace/test_span_builder.py +++ b/api/tests/unit_tests/core/ops/tencent_trace/test_span_builder.py @@ -1,8 +1,6 @@ from datetime import datetime from unittest.mock import MagicMock, patch -from graphon.entities import WorkflowNodeExecution -from graphon.enums import WorkflowNodeExecutionMetadataKey, WorkflowNodeExecutionStatus from opentelemetry.trace import StatusCode from core.ops.entities.trace_entity import ( @@ -27,6 +25,8 @@ from core.ops.tencent_trace.entities.semconv import ( ) from core.ops.tencent_trace.span_builder import TencentSpanBuilder from core.rag.models.document import Document +from graphon.entities import WorkflowNodeExecution +from graphon.enums import WorkflowNodeExecutionMetadataKey, WorkflowNodeExecutionStatus class TestTencentSpanBuilder: diff --git a/api/tests/unit_tests/core/ops/tencent_trace/test_tencent_trace.py b/api/tests/unit_tests/core/ops/tencent_trace/test_tencent_trace.py index f67abba807..7afd0b824a 100644 --- a/api/tests/unit_tests/core/ops/tencent_trace/test_tencent_trace.py +++ b/api/tests/unit_tests/core/ops/tencent_trace/test_tencent_trace.py @@ -2,8 +2,6 @@ import logging from unittest.mock import MagicMock, patch import pytest -from graphon.entities import WorkflowNodeExecution -from graphon.enums import BuiltinNodeTypes from core.ops.entities.config_entity import TencentConfig from core.ops.entities.trace_entity import ( @@ -16,6 +14,8 @@ from core.ops.entities.trace_entity import ( WorkflowTraceInfo, ) from core.ops.tencent_trace.tencent_trace import TencentDataTrace +from graphon.entities import WorkflowNodeExecution +from graphon.enums import BuiltinNodeTypes from models import Account, App, TenantAccountJoin logger = logging.getLogger(__name__) diff --git a/api/tests/unit_tests/core/ops/test_arize_phoenix_trace.py b/api/tests/unit_tests/core/ops/test_arize_phoenix_trace.py index 6b5cb5b09a..4b925390d9 100644 --- a/api/tests/unit_tests/core/ops/test_arize_phoenix_trace.py +++ b/api/tests/unit_tests/core/ops/test_arize_phoenix_trace.py @@ -1,7 +1,7 @@ -from graphon.enums import BUILT_IN_NODE_TYPES, BuiltinNodeTypes from openinference.semconv.trace import OpenInferenceSpanKindValues from core.ops.arize_phoenix_trace.arize_phoenix_trace import _NODE_TYPE_TO_SPAN_KIND, _get_node_span_kind +from graphon.enums import BUILT_IN_NODE_TYPES, BuiltinNodeTypes class TestGetNodeSpanKind: diff --git a/api/tests/unit_tests/core/ops/test_langfuse_trace.py b/api/tests/unit_tests/core/ops/test_langfuse_trace.py index f8951d2b4a..017ac8c891 100644 --- a/api/tests/unit_tests/core/ops/test_langfuse_trace.py +++ b/api/tests/unit_tests/core/ops/test_langfuse_trace.py @@ -4,11 +4,10 @@ from datetime import datetime, timedelta from types import SimpleNamespace from unittest.mock import MagicMock, patch -from graphon.enums import BuiltinNodeTypes - from core.ops.entities.config_entity import LangfuseConfig from core.ops.entities.trace_entity import MessageTraceInfo, WorkflowTraceInfo from core.ops.langfuse_trace.langfuse_trace import LangFuseDataTrace +from graphon.enums import BuiltinNodeTypes def _create_trace_instance() -> LangFuseDataTrace: diff --git a/api/tests/unit_tests/core/ops/weave_trace/test_weave_trace.py b/api/tests/unit_tests/core/ops/weave_trace/test_weave_trace.py index 5014f40afc..531c7de05f 100644 --- a/api/tests/unit_tests/core/ops/weave_trace/test_weave_trace.py +++ b/api/tests/unit_tests/core/ops/weave_trace/test_weave_trace.py @@ -7,7 +7,6 @@ from types import SimpleNamespace from unittest.mock import MagicMock, patch import pytest -from graphon.enums import BuiltinNodeTypes, WorkflowNodeExecutionMetadataKey from weave.trace_server.trace_server_interface import TraceStatus from core.ops.entities.config_entity import WeaveConfig @@ -23,6 +22,7 @@ from core.ops.entities.trace_entity import ( ) from core.ops.weave_trace.entities.weave_trace_entity import WeaveTraceModel from core.ops.weave_trace.weave_trace import WeaveDataTrace +from graphon.enums import BuiltinNodeTypes, WorkflowNodeExecutionMetadataKey # ── Helpers ────────────────────────────────────────────────────────────────── diff --git a/api/tests/unit_tests/core/plugin/test_backwards_invocation_model.py b/api/tests/unit_tests/core/plugin/test_backwards_invocation_model.py index 543b278715..c24d3ac012 100644 --- a/api/tests/unit_tests/core/plugin/test_backwards_invocation_model.py +++ b/api/tests/unit_tests/core/plugin/test_backwards_invocation_model.py @@ -1,10 +1,9 @@ from types import SimpleNamespace from unittest.mock import patch -from graphon.model_runtime.entities.message_entities import UserPromptMessage - from core.plugin.backwards_invocation.model import PluginModelBackwardsInvocation from core.plugin.entities.request import RequestInvokeSummary +from graphon.model_runtime.entities.message_entities import UserPromptMessage def test_system_model_helpers_forward_user_id(): diff --git a/api/tests/unit_tests/core/plugin/test_model_runtime_adapter.py b/api/tests/unit_tests/core/plugin/test_model_runtime_adapter.py index f8d0e127b1..68aa130518 100644 --- a/api/tests/unit_tests/core/plugin/test_model_runtime_adapter.py +++ b/api/tests/unit_tests/core/plugin/test_model_runtime_adapter.py @@ -6,15 +6,15 @@ from types import SimpleNamespace from unittest.mock import Mock, sentinel import pytest -from graphon.model_runtime.entities.common_entities import I18nObject -from graphon.model_runtime.entities.model_entities import AIModelEntity, FetchFrom, ModelType -from graphon.model_runtime.entities.provider_entities import ConfigurateMethod, ProviderEntity from core.plugin.entities.plugin_daemon import PluginModelProviderEntity from core.plugin.impl import model_runtime as model_runtime_module from core.plugin.impl.model import PluginModelClient from core.plugin.impl.model_runtime import TENANT_SCOPE_SCHEMA_CACHE_USER_ID, PluginModelRuntime from core.plugin.impl.model_runtime_factory import create_plugin_model_runtime +from graphon.model_runtime.entities.common_entities import I18nObject +from graphon.model_runtime.entities.model_entities import AIModelEntity, FetchFrom, ModelType +from graphon.model_runtime.entities.provider_entities import ConfigurateMethod, ProviderEntity def _build_model_schema() -> AIModelEntity: diff --git a/api/tests/unit_tests/core/plugin/test_plugin_entities.py b/api/tests/unit_tests/core/plugin/test_plugin_entities.py index a812b01c5b..f1c4c7e700 100644 --- a/api/tests/unit_tests/core/plugin/test_plugin_entities.py +++ b/api/tests/unit_tests/core/plugin/test_plugin_entities.py @@ -4,12 +4,6 @@ from enum import StrEnum import pytest from flask import Response -from graphon.model_runtime.entities.message_entities import ( - AssistantPromptMessage, - SystemPromptMessage, - ToolPromptMessage, - UserPromptMessage, -) from pydantic import ValidationError from core.plugin.entities.endpoint import EndpointEntityWithInstance @@ -31,6 +25,12 @@ from core.plugin.entities.request import ( ) from core.plugin.utils.http_parser import serialize_response from core.tools.entities.common_entities import I18nObject +from graphon.model_runtime.entities.message_entities import ( + AssistantPromptMessage, + SystemPromptMessage, + ToolPromptMessage, + UserPromptMessage, +) class TestEndpointEntity: diff --git a/api/tests/unit_tests/core/plugin/test_plugin_runtime.py b/api/tests/unit_tests/core/plugin/test_plugin_runtime.py index a3b1e5f6b0..704b82adc0 100644 --- a/api/tests/unit_tests/core/plugin/test_plugin_runtime.py +++ b/api/tests/unit_tests/core/plugin/test_plugin_runtime.py @@ -17,14 +17,6 @@ from unittest.mock import MagicMock, patch import httpx import pytest -from graphon.model_runtime.errors.invoke import ( - InvokeAuthorizationError, - InvokeBadRequestError, - InvokeConnectionError, - InvokeRateLimitError, - InvokeServerUnavailableError, -) -from graphon.model_runtime.errors.validate import CredentialsValidateFailedError from pydantic import BaseModel from core.plugin.entities.plugin_daemon import ( @@ -45,6 +37,14 @@ from core.plugin.impl.exc import ( ) from core.plugin.impl.plugin import PluginInstaller from core.plugin.impl.tool import PluginToolManager +from graphon.model_runtime.errors.invoke import ( + InvokeAuthorizationError, + InvokeBadRequestError, + InvokeConnectionError, + InvokeRateLimitError, + InvokeServerUnavailableError, +) +from graphon.model_runtime.errors.validate import CredentialsValidateFailedError @pytest.fixture(autouse=True) diff --git a/api/tests/unit_tests/core/plugin/utils/test_chunk_merger.py b/api/tests/unit_tests/core/plugin/utils/test_chunk_merger.py index 90730dff5a..d49b6e4b71 100644 --- a/api/tests/unit_tests/core/plugin/utils/test_chunk_merger.py +++ b/api/tests/unit_tests/core/plugin/utils/test_chunk_merger.py @@ -1,12 +1,12 @@ from collections.abc import Generator import pytest -from graphon.file import File, FileTransferMethod, FileType from core.agent.entities import AgentInvokeMessage from core.plugin.utils.chunk_merger import FileChunk, merge_blob_chunks from core.plugin.utils.converter import convert_parameters_to_plugin_format from core.tools.entities.tool_entities import ToolInvokeMessage, ToolParameter, ToolSelector +from graphon.file import File, FileTransferMethod, FileType class TestChunkMerger: diff --git a/api/tests/unit_tests/core/prompt/test_advanced_prompt_transform.py b/api/tests/unit_tests/core/prompt/test_advanced_prompt_transform.py index 2b280dd674..395d392127 100644 --- a/api/tests/unit_tests/core/prompt/test_advanced_prompt_transform.py +++ b/api/tests/unit_tests/core/prompt/test_advanced_prompt_transform.py @@ -2,6 +2,13 @@ from typing import cast from unittest.mock import MagicMock, patch import pytest + +from configs import dify_config +from core.app.app_config.entities import ModelConfigEntity +from core.memory.token_buffer_memory import TokenBufferMemory +from core.prompt.advanced_prompt_transform import AdvancedPromptTransform +from core.prompt.entities.advanced_prompt_entities import ChatModelMessage, CompletionModelPromptTemplate, MemoryConfig +from core.prompt.utils.prompt_template_parser import PromptTemplateParser from graphon.file import File, FileTransferMethod, FileType from graphon.model_runtime.entities.message_entities import ( AssistantPromptMessage, @@ -11,13 +18,6 @@ from graphon.model_runtime.entities.message_entities import ( TextPromptMessageContent, UserPromptMessage, ) - -from configs import dify_config -from core.app.app_config.entities import ModelConfigEntity -from core.memory.token_buffer_memory import TokenBufferMemory -from core.prompt.advanced_prompt_transform import AdvancedPromptTransform -from core.prompt.entities.advanced_prompt_entities import ChatModelMessage, CompletionModelPromptTemplate, MemoryConfig -from core.prompt.utils.prompt_template_parser import PromptTemplateParser from models.model import Conversation diff --git a/api/tests/unit_tests/core/prompt/test_agent_history_prompt_transform.py b/api/tests/unit_tests/core/prompt/test_agent_history_prompt_transform.py index 4a54649b28..803afa54d7 100644 --- a/api/tests/unit_tests/core/prompt/test_agent_history_prompt_transform.py +++ b/api/tests/unit_tests/core/prompt/test_agent_history_prompt_transform.py @@ -1,19 +1,18 @@ from unittest.mock import MagicMock -from graphon.model_runtime.entities.message_entities import ( - AssistantPromptMessage, - SystemPromptMessage, - ToolPromptMessage, - UserPromptMessage, -) -from graphon.model_runtime.model_providers.__base.large_language_model import LargeLanguageModel - from core.app.entities.app_invoke_entities import ( ModelConfigWithCredentialsEntity, ) from core.entities.provider_configuration import ProviderModelBundle from core.memory.token_buffer_memory import TokenBufferMemory from core.prompt.agent_history_prompt_transform import AgentHistoryPromptTransform +from graphon.model_runtime.entities.message_entities import ( + AssistantPromptMessage, + SystemPromptMessage, + ToolPromptMessage, + UserPromptMessage, +) +from graphon.model_runtime.model_providers.__base.large_language_model import LargeLanguageModel from models.model import Conversation diff --git a/api/tests/unit_tests/core/prompt/test_prompt_message.py b/api/tests/unit_tests/core/prompt/test_prompt_message.py index a4b3960b0a..5d865d934c 100644 --- a/api/tests/unit_tests/core/prompt/test_prompt_message.py +++ b/api/tests/unit_tests/core/prompt/test_prompt_message.py @@ -1,3 +1,5 @@ +from core.prompt.simple_prompt_transform import ModelMode +from core.prompt.utils.prompt_message_util import PromptMessageUtil from graphon.model_runtime.entities.message_entities import ( AssistantPromptMessage, AudioPromptMessageContent, @@ -7,9 +9,6 @@ from graphon.model_runtime.entities.message_entities import ( UserPromptMessage, ) -from core.prompt.simple_prompt_transform import ModelMode -from core.prompt.utils.prompt_message_util import PromptMessageUtil - def test_build_prompt_message_with_prompt_message_contents(): prompt = UserPromptMessage(content=[TextPromptMessageContent(data="Hello, World!")]) diff --git a/api/tests/unit_tests/core/prompt/test_prompt_transform.py b/api/tests/unit_tests/core/prompt/test_prompt_transform.py index e35ce2c48a..9f9ea33695 100644 --- a/api/tests/unit_tests/core/prompt/test_prompt_transform.py +++ b/api/tests/unit_tests/core/prompt/test_prompt_transform.py @@ -2,9 +2,9 @@ from types import SimpleNamespace from unittest.mock import MagicMock, patch import pytest -from graphon.model_runtime.entities.model_entities import ModelPropertyKey from core.prompt.prompt_transform import PromptTransform +from graphon.model_runtime.entities.model_entities import ModelPropertyKey # from core.app.app_config.entities import ModelConfigEntity # from core.entities.provider_configuration import ProviderConfiguration, ProviderModelBundle diff --git a/api/tests/unit_tests/core/prompt/test_simple_prompt_transform.py b/api/tests/unit_tests/core/prompt/test_simple_prompt_transform.py index 3f188cfbb4..0dc74b33df 100644 --- a/api/tests/unit_tests/core/prompt/test_simple_prompt_transform.py +++ b/api/tests/unit_tests/core/prompt/test_simple_prompt_transform.py @@ -2,12 +2,6 @@ from types import SimpleNamespace from unittest.mock import MagicMock, patch import pytest -from graphon.model_runtime.entities.message_entities import ( - AssistantPromptMessage, - ImagePromptMessageContent, - TextPromptMessageContent, - UserPromptMessage, -) from core.app.entities.app_invoke_entities import ModelConfigWithCredentialsEntity from core.memory.token_buffer_memory import TokenBufferMemory @@ -24,6 +18,12 @@ from core.prompt.prompt_templates.advanced_prompt_templates import ( CONTEXT, ) from core.prompt.simple_prompt_transform import SimplePromptTransform +from graphon.model_runtime.entities.message_entities import ( + AssistantPromptMessage, + ImagePromptMessageContent, + TextPromptMessageContent, + UserPromptMessage, +) from models.model import AppMode, Conversation diff --git a/api/tests/unit_tests/core/rag/data_post_processor/test_data_post_processor.py b/api/tests/unit_tests/core/rag/data_post_processor/test_data_post_processor.py index 006b4e7345..1f3247590c 100644 --- a/api/tests/unit_tests/core/rag/data_post_processor/test_data_post_processor.py +++ b/api/tests/unit_tests/core/rag/data_post_processor/test_data_post_processor.py @@ -1,13 +1,12 @@ from unittest.mock import MagicMock, patch -from graphon.model_runtime.entities.model_entities import ModelType -from graphon.model_runtime.errors.invoke import InvokeAuthorizationError - from core.rag.data_post_processor.data_post_processor import DataPostProcessor from core.rag.data_post_processor.reorder import ReorderRunner from core.rag.index_processor.constant.query_type import QueryType from core.rag.models.document import Document from core.rag.rerank.rerank_type import RerankMode +from graphon.model_runtime.entities.model_entities import ModelType +from graphon.model_runtime.errors.invoke import InvokeAuthorizationError def _doc(content: str) -> Document: diff --git a/api/tests/unit_tests/core/rag/embedding/test_cached_embedding.py b/api/tests/unit_tests/core/rag/embedding/test_cached_embedding.py index 3563186186..051a1455ae 100644 --- a/api/tests/unit_tests/core/rag/embedding/test_cached_embedding.py +++ b/api/tests/unit_tests/core/rag/embedding/test_cached_embedding.py @@ -12,11 +12,11 @@ from unittest.mock import Mock, patch import numpy as np import pytest -from graphon.model_runtime.entities.model_entities import ModelPropertyKey -from graphon.model_runtime.entities.text_embedding_entities import EmbeddingResult, EmbeddingUsage from sqlalchemy.exc import IntegrityError from core.rag.embedding.cached_embedding import CacheEmbedding +from graphon.model_runtime.entities.model_entities import ModelPropertyKey +from graphon.model_runtime.entities.text_embedding_entities import EmbeddingResult, EmbeddingUsage from models.dataset import Embedding diff --git a/api/tests/unit_tests/core/rag/embedding/test_embedding_service.py b/api/tests/unit_tests/core/rag/embedding/test_embedding_service.py index 408cf14a51..4b8175b0b4 100644 --- a/api/tests/unit_tests/core/rag/embedding/test_embedding_service.py +++ b/api/tests/unit_tests/core/rag/embedding/test_embedding_service.py @@ -49,6 +49,10 @@ from unittest.mock import Mock, patch import numpy as np import pytest +from sqlalchemy.exc import IntegrityError + +from core.entities.embedding_type import EmbeddingInputType +from core.rag.embedding.cached_embedding import CacheEmbedding from graphon.model_runtime.entities.model_entities import ModelPropertyKey from graphon.model_runtime.entities.text_embedding_entities import EmbeddingResult, EmbeddingUsage from graphon.model_runtime.errors.invoke import ( @@ -56,10 +60,6 @@ from graphon.model_runtime.errors.invoke import ( InvokeConnectionError, InvokeRateLimitError, ) -from sqlalchemy.exc import IntegrityError - -from core.entities.embedding_type import EmbeddingInputType -from core.rag.embedding.cached_embedding import CacheEmbedding from models.dataset import Embedding diff --git a/api/tests/unit_tests/core/rag/indexing/processor/test_paragraph_index_processor.py b/api/tests/unit_tests/core/rag/indexing/processor/test_paragraph_index_processor.py index 7ae0da03ff..4ba4d54fa0 100644 --- a/api/tests/unit_tests/core/rag/indexing/processor/test_paragraph_index_processor.py +++ b/api/tests/unit_tests/core/rag/indexing/processor/test_paragraph_index_processor.py @@ -3,14 +3,14 @@ from typing import Any from unittest.mock import Mock, patch import pytest -from graphon.model_runtime.entities.llm_entities import LLMResult, LLMUsage -from graphon.model_runtime.entities.message_entities import AssistantPromptMessage, ImagePromptMessageContent -from graphon.model_runtime.entities.model_entities import ModelFeature from core.entities.knowledge_entities import PreviewDetail from core.rag.index_processor.constant.index_type import IndexTechniqueType from core.rag.index_processor.processor.paragraph_index_processor import ParagraphIndexProcessor from core.rag.models.document import AttachmentDocument, Document +from graphon.model_runtime.entities.llm_entities import LLMResult, LLMUsage +from graphon.model_runtime.entities.message_entities import AssistantPromptMessage, ImagePromptMessageContent +from graphon.model_runtime.entities.model_entities import ModelFeature class TestParagraphIndexProcessor: diff --git a/api/tests/unit_tests/core/rag/indexing/test_indexing_runner.py b/api/tests/unit_tests/core/rag/indexing/test_indexing_runner.py index 641c5d9ba0..7c4defc180 100644 --- a/api/tests/unit_tests/core/rag/indexing/test_indexing_runner.py +++ b/api/tests/unit_tests/core/rag/indexing/test_indexing_runner.py @@ -53,7 +53,6 @@ from typing import Any from unittest.mock import MagicMock, Mock, patch import pytest -from graphon.model_runtime.entities.model_entities import ModelType from sqlalchemy.orm.exc import ObjectDeletedError from core.errors.error import ProviderTokenNotInitError @@ -64,6 +63,7 @@ from core.indexing_runner import ( ) from core.rag.index_processor.constant.index_type import IndexStructureType, IndexTechniqueType from core.rag.models.document import ChildDocument, Document +from graphon.model_runtime.entities.model_entities import ModelType from libs.datetime_utils import naive_utc_now from models.dataset import Dataset, DatasetProcessRule from models.dataset import Document as DatasetDocument diff --git a/api/tests/unit_tests/core/rag/rerank/test_reranker.py b/api/tests/unit_tests/core/rag/rerank/test_reranker.py index c279b00d3b..8bc7dbf70d 100644 --- a/api/tests/unit_tests/core/rag/rerank/test_reranker.py +++ b/api/tests/unit_tests/core/rag/rerank/test_reranker.py @@ -17,7 +17,6 @@ from types import SimpleNamespace from unittest.mock import MagicMock, Mock, patch import pytest -from graphon.model_runtime.entities.rerank_entities import RerankDocument, RerankResult from core.model_manager import ModelInstance from core.rag.index_processor.constant.doc_type import DocType @@ -29,6 +28,7 @@ from core.rag.rerank.rerank_factory import RerankRunnerFactory from core.rag.rerank.rerank_model import RerankModelRunner from core.rag.rerank.rerank_type import RerankMode from core.rag.rerank.weight_rerank import WeightRerankRunner +from graphon.model_runtime.entities.rerank_entities import RerankDocument, RerankResult def create_mock_model_instance() -> ModelInstance: diff --git a/api/tests/unit_tests/core/rag/retrieval/test_dataset_retrieval.py b/api/tests/unit_tests/core/rag/retrieval/test_dataset_retrieval.py index 508fe80e2b..89830f7517 100644 --- a/api/tests/unit_tests/core/rag/retrieval/test_dataset_retrieval.py +++ b/api/tests/unit_tests/core/rag/retrieval/test_dataset_retrieval.py @@ -7,8 +7,6 @@ from uuid import uuid4 import pytest from flask import Flask, current_app -from graphon.model_runtime.entities.llm_entities import LLMUsage -from graphon.model_runtime.entities.model_entities import ModelFeature from core.app.app_config.entities import ( DatasetEntity, @@ -35,6 +33,8 @@ from core.rag.retrieval.dataset_retrieval import DatasetRetrieval from core.rag.retrieval.retrieval_methods import RetrievalMethod from core.workflow.nodes.knowledge_retrieval import exc from core.workflow.nodes.knowledge_retrieval.retrieval import KnowledgeRetrievalRequest +from graphon.model_runtime.entities.llm_entities import LLMUsage +from graphon.model_runtime.entities.model_entities import ModelFeature from models.dataset import Dataset from models.enums import CreatorUserRole diff --git a/api/tests/unit_tests/core/rag/retrieval/test_multi_dataset_function_call_router.py b/api/tests/unit_tests/core/rag/retrieval/test_multi_dataset_function_call_router.py index 5a2ecb8220..43c521dcfd 100644 --- a/api/tests/unit_tests/core/rag/retrieval/test_multi_dataset_function_call_router.py +++ b/api/tests/unit_tests/core/rag/retrieval/test_multi_dataset_function_call_router.py @@ -1,8 +1,7 @@ from unittest.mock import Mock -from graphon.model_runtime.entities.llm_entities import LLMUsage - from core.rag.retrieval.router.multi_dataset_function_call_router import FunctionCallMultiDatasetRouter +from graphon.model_runtime.entities.llm_entities import LLMUsage class TestFunctionCallMultiDatasetRouter: diff --git a/api/tests/unit_tests/core/rag/retrieval/test_multi_dataset_react_route.py b/api/tests/unit_tests/core/rag/retrieval/test_multi_dataset_react_route.py index 539ac0f849..c56528cf55 100644 --- a/api/tests/unit_tests/core/rag/retrieval/test_multi_dataset_react_route.py +++ b/api/tests/unit_tests/core/rag/retrieval/test_multi_dataset_react_route.py @@ -1,13 +1,12 @@ from types import SimpleNamespace from unittest.mock import Mock, patch +from core.rag.retrieval.output_parser.react_output import ReactAction, ReactFinish +from core.rag.retrieval.router.multi_dataset_react_route import ReactMultiDatasetRouter from graphon.model_runtime.entities.llm_entities import LLMUsage from graphon.model_runtime.entities.message_entities import PromptMessageRole from graphon.model_runtime.entities.model_entities import ModelType -from core.rag.retrieval.output_parser.react_output import ReactAction, ReactFinish -from core.rag.retrieval.router.multi_dataset_react_route import ReactMultiDatasetRouter - class TestReactMultiDatasetRouter: def test_invoke_returns_none_when_no_tools(self) -> None: diff --git a/api/tests/unit_tests/core/repositories/test_celery_workflow_execution_repository.py b/api/tests/unit_tests/core/repositories/test_celery_workflow_execution_repository.py index e229d5fc1a..3d3322094e 100644 --- a/api/tests/unit_tests/core/repositories/test_celery_workflow_execution_repository.py +++ b/api/tests/unit_tests/core/repositories/test_celery_workflow_execution_repository.py @@ -9,10 +9,10 @@ from unittest.mock import Mock, patch from uuid import uuid4 import pytest -from graphon.entities import WorkflowExecution -from graphon.enums import WorkflowType from core.repositories.celery_workflow_execution_repository import CeleryWorkflowExecutionRepository +from graphon.entities import WorkflowExecution +from graphon.enums import WorkflowType from libs.datetime_utils import naive_utc_now from models import Account, EndUser from models.enums import WorkflowRunTriggeredFrom diff --git a/api/tests/unit_tests/core/repositories/test_celery_workflow_node_execution_repository.py b/api/tests/unit_tests/core/repositories/test_celery_workflow_node_execution_repository.py index 7dbf78d0f0..05b4f3a053 100644 --- a/api/tests/unit_tests/core/repositories/test_celery_workflow_node_execution_repository.py +++ b/api/tests/unit_tests/core/repositories/test_celery_workflow_node_execution_repository.py @@ -9,14 +9,14 @@ from unittest.mock import Mock, patch from uuid import uuid4 import pytest + +from core.repositories.celery_workflow_node_execution_repository import CeleryWorkflowNodeExecutionRepository +from core.repositories.factory import OrderConfig from graphon.entities.workflow_node_execution import ( WorkflowNodeExecution, WorkflowNodeExecutionStatus, ) from graphon.enums import BuiltinNodeTypes - -from core.repositories.celery_workflow_node_execution_repository import CeleryWorkflowNodeExecutionRepository -from core.repositories.factory import OrderConfig from libs.datetime_utils import naive_utc_now from models import Account, EndUser from models.workflow import WorkflowNodeExecutionTriggeredFrom diff --git a/api/tests/unit_tests/core/repositories/test_human_input_form_repository_impl.py b/api/tests/unit_tests/core/repositories/test_human_input_form_repository_impl.py index 0fc82dda53..8be1ac318c 100644 --- a/api/tests/unit_tests/core/repositories/test_human_input_form_repository_impl.py +++ b/api/tests/unit_tests/core/repositories/test_human_input_form_repository_impl.py @@ -7,11 +7,6 @@ from datetime import datetime from types import SimpleNamespace import pytest -from graphon.nodes.human_input.entities import ( - FormDefinition, - UserAction, -) -from graphon.nodes.human_input.enums import HumanInputFormKind, HumanInputFormStatus from core.repositories.human_input_repository import ( HumanInputFormRecord, @@ -26,6 +21,11 @@ from core.workflow.human_input_compat import ( ExternalRecipient, MemberRecipient, ) +from graphon.nodes.human_input.entities import ( + FormDefinition, + UserAction, +) +from graphon.nodes.human_input.enums import HumanInputFormKind, HumanInputFormStatus from libs.datetime_utils import naive_utc_now from models.human_input import ( EmailExternalRecipientPayload, diff --git a/api/tests/unit_tests/core/repositories/test_human_input_repository.py b/api/tests/unit_tests/core/repositories/test_human_input_repository.py index 8ff0e40587..1297a95df1 100644 --- a/api/tests/unit_tests/core/repositories/test_human_input_repository.py +++ b/api/tests/unit_tests/core/repositories/test_human_input_repository.py @@ -9,8 +9,6 @@ from typing import Any from unittest.mock import MagicMock import pytest -from graphon.nodes.human_input.entities import HumanInputNodeData, UserAction -from graphon.nodes.human_input.enums import HumanInputFormKind, HumanInputFormStatus from core.repositories.human_input_repository import ( FormCreateParams, @@ -31,6 +29,8 @@ from core.workflow.human_input_compat import ( MemberRecipient, WebAppDeliveryMethod, ) +from graphon.nodes.human_input.entities import HumanInputNodeData, UserAction +from graphon.nodes.human_input.enums import HumanInputFormKind, HumanInputFormStatus from libs.datetime_utils import naive_utc_now from models.human_input import HumanInputFormRecipient, RecipientType diff --git a/api/tests/unit_tests/core/repositories/test_sqlalchemy_workflow_execution_repository.py b/api/tests/unit_tests/core/repositories/test_sqlalchemy_workflow_execution_repository.py index e5c3e85487..a08c5729cb 100644 --- a/api/tests/unit_tests/core/repositories/test_sqlalchemy_workflow_execution_repository.py +++ b/api/tests/unit_tests/core/repositories/test_sqlalchemy_workflow_execution_repository.py @@ -3,12 +3,12 @@ from unittest.mock import MagicMock from uuid import uuid4 import pytest -from graphon.entities import WorkflowExecution -from graphon.enums import WorkflowExecutionStatus, WorkflowType from sqlalchemy.engine import Engine from sqlalchemy.orm import sessionmaker from core.repositories.sqlalchemy_workflow_execution_repository import SQLAlchemyWorkflowExecutionRepository +from graphon.entities import WorkflowExecution +from graphon.enums import WorkflowExecutionStatus, WorkflowType from models import Account, CreatorUserRole, EndUser, WorkflowRun from models.enums import WorkflowRunTriggeredFrom diff --git a/api/tests/unit_tests/core/repositories/test_sqlalchemy_workflow_node_execution_repository.py b/api/tests/unit_tests/core/repositories/test_sqlalchemy_workflow_node_execution_repository.py index 5b4d26b780..6af7b02d4c 100644 --- a/api/tests/unit_tests/core/repositories/test_sqlalchemy_workflow_node_execution_repository.py +++ b/api/tests/unit_tests/core/repositories/test_sqlalchemy_workflow_node_execution_repository.py @@ -10,12 +10,6 @@ from unittest.mock import MagicMock, Mock import psycopg2.errors import pytest -from graphon.entities import WorkflowNodeExecution -from graphon.enums import ( - BuiltinNodeTypes, - WorkflowNodeExecutionMetadataKey, - WorkflowNodeExecutionStatus, -) from sqlalchemy import Engine, create_engine from sqlalchemy.exc import IntegrityError from sqlalchemy.orm import sessionmaker @@ -29,6 +23,12 @@ from core.repositories.sqlalchemy_workflow_node_execution_repository import ( _find_first, _replace_or_append_offload, ) +from graphon.entities import WorkflowNodeExecution +from graphon.enums import ( + BuiltinNodeTypes, + WorkflowNodeExecutionMetadataKey, + WorkflowNodeExecutionStatus, +) from models import Account, EndUser from models.enums import ExecutionOffLoadType from models.workflow import WorkflowNodeExecutionModel, WorkflowNodeExecutionOffload, WorkflowNodeExecutionTriggeredFrom diff --git a/api/tests/unit_tests/core/repositories/test_workflow_node_execution_conflict_handling.py b/api/tests/unit_tests/core/repositories/test_workflow_node_execution_conflict_handling.py index 84fe522388..abdbc72085 100644 --- a/api/tests/unit_tests/core/repositories/test_workflow_node_execution_conflict_handling.py +++ b/api/tests/unit_tests/core/repositories/test_workflow_node_execution_conflict_handling.py @@ -4,17 +4,17 @@ from unittest.mock import MagicMock, Mock import psycopg2.errors import pytest -from graphon.entities.workflow_node_execution import ( - WorkflowNodeExecution, - WorkflowNodeExecutionStatus, -) -from graphon.enums import BuiltinNodeTypes from sqlalchemy.exc import IntegrityError from sqlalchemy.orm import sessionmaker from core.repositories.sqlalchemy_workflow_node_execution_repository import ( SQLAlchemyWorkflowNodeExecutionRepository, ) +from graphon.entities.workflow_node_execution import ( + WorkflowNodeExecution, + WorkflowNodeExecutionStatus, +) +from graphon.enums import BuiltinNodeTypes from libs.datetime_utils import naive_utc_now from models import Account, WorkflowNodeExecutionTriggeredFrom diff --git a/api/tests/unit_tests/core/repositories/test_workflow_node_execution_truncation.py b/api/tests/unit_tests/core/repositories/test_workflow_node_execution_truncation.py index 27729e7f06..5af1376a0a 100644 --- a/api/tests/unit_tests/core/repositories/test_workflow_node_execution_truncation.py +++ b/api/tests/unit_tests/core/repositories/test_workflow_node_execution_truncation.py @@ -11,17 +11,17 @@ from datetime import UTC, datetime from typing import Any from unittest.mock import MagicMock -from graphon.entities.workflow_node_execution import ( - WorkflowNodeExecution, - WorkflowNodeExecutionStatus, -) -from graphon.enums import BuiltinNodeTypes from sqlalchemy import Engine from configs import dify_config from core.repositories.sqlalchemy_workflow_node_execution_repository import ( SQLAlchemyWorkflowNodeExecutionRepository, ) +from graphon.entities.workflow_node_execution import ( + WorkflowNodeExecution, + WorkflowNodeExecutionStatus, +) +from graphon.enums import BuiltinNodeTypes from models import Account, WorkflowNodeExecutionTriggeredFrom from models.enums import ExecutionOffLoadType from models.workflow import WorkflowNodeExecutionModel, WorkflowNodeExecutionOffload diff --git a/api/tests/unit_tests/core/test_file.py b/api/tests/unit_tests/core/test_file.py index ac65d0c02b..f17927f16b 100644 --- a/api/tests/unit_tests/core/test_file.py +++ b/api/tests/unit_tests/core/test_file.py @@ -1,7 +1,6 @@ import json from graphon.file import File, FileTransferMethod, FileType, FileUploadConfig - from models.workflow import Workflow diff --git a/api/tests/unit_tests/core/test_model_manager.py b/api/tests/unit_tests/core/test_model_manager.py index f5efb78b61..afea9144c0 100644 --- a/api/tests/unit_tests/core/test_model_manager.py +++ b/api/tests/unit_tests/core/test_model_manager.py @@ -2,12 +2,12 @@ from unittest.mock import MagicMock, patch import pytest import redis -from graphon.model_runtime.entities.model_entities import ModelType from pytest_mock import MockerFixture from core.entities.provider_entities import ModelLoadBalancingConfiguration from core.model_manager import LBModelManager from extensions.ext_redis import redis_client +from graphon.model_runtime.entities.model_entities import ModelType @pytest.fixture diff --git a/api/tests/unit_tests/core/test_provider_configuration.py b/api/tests/unit_tests/core/test_provider_configuration.py index 331166fe63..b19a21d7f4 100644 --- a/api/tests/unit_tests/core/test_provider_configuration.py +++ b/api/tests/unit_tests/core/test_provider_configuration.py @@ -1,15 +1,6 @@ from unittest.mock import Mock, patch import pytest -from graphon.model_runtime.entities.common_entities import I18nObject -from graphon.model_runtime.entities.model_entities import ModelType -from graphon.model_runtime.entities.provider_entities import ( - ConfigurateMethod, - CredentialFormSchema, - FormOption, - FormType, - ProviderEntity, -) from core.entities.provider_configuration import ProviderConfiguration, SystemConfigurationStatus from core.entities.provider_entities import ( @@ -21,6 +12,15 @@ from core.entities.provider_entities import ( RestrictModel, SystemConfiguration, ) +from graphon.model_runtime.entities.common_entities import I18nObject +from graphon.model_runtime.entities.model_entities import ModelType +from graphon.model_runtime.entities.provider_entities import ( + ConfigurateMethod, + CredentialFormSchema, + FormOption, + FormType, + ProviderEntity, +) from models.provider import Provider, ProviderType diff --git a/api/tests/unit_tests/core/test_provider_manager.py b/api/tests/unit_tests/core/test_provider_manager.py index ee26172459..f45b43082c 100644 --- a/api/tests/unit_tests/core/test_provider_manager.py +++ b/api/tests/unit_tests/core/test_provider_manager.py @@ -2,12 +2,12 @@ from types import SimpleNamespace from unittest.mock import MagicMock, Mock, PropertyMock, patch import pytest -from graphon.model_runtime.entities.common_entities import I18nObject -from graphon.model_runtime.entities.model_entities import ModelType from pytest_mock import MockerFixture from core.entities.provider_entities import ModelSettings from core.provider_manager import ProviderManager +from graphon.model_runtime.entities.common_entities import I18nObject +from graphon.model_runtime.entities.model_entities import ModelType from models.provider import LoadBalancingModelConfig, ProviderModelSetting, TenantDefaultModel from models.provider_ids import ModelProviderID diff --git a/api/tests/unit_tests/core/tools/test_builtin_tool_base.py b/api/tests/unit_tests/core/tools/test_builtin_tool_base.py index 5d744f88c9..1ff81f6120 100644 --- a/api/tests/unit_tests/core/tools/test_builtin_tool_base.py +++ b/api/tests/unit_tests/core/tools/test_builtin_tool_base.py @@ -6,13 +6,13 @@ from typing import Any from unittest.mock import patch import pytest -from graphon.model_runtime.entities.message_entities import UserPromptMessage from core.app.entities.app_invoke_entities import InvokeFrom from core.tools.__base.tool_runtime import ToolRuntime from core.tools.builtin_tool.tool import BuiltinTool from core.tools.entities.common_entities import I18nObject from core.tools.entities.tool_entities import ToolEntity, ToolIdentity, ToolInvokeMessage, ToolProviderType +from graphon.model_runtime.entities.message_entities import UserPromptMessage class _BuiltinDummyTool(BuiltinTool): diff --git a/api/tests/unit_tests/core/tools/test_builtin_tools_extra.py b/api/tests/unit_tests/core/tools/test_builtin_tools_extra.py index ee0ce51eec..c7829fc0d7 100644 --- a/api/tests/unit_tests/core/tools/test_builtin_tools_extra.py +++ b/api/tests/unit_tests/core/tools/test_builtin_tools_extra.py @@ -6,8 +6,6 @@ from datetime import date from types import SimpleNamespace import pytest -from graphon.file import FileType -from graphon.model_runtime.entities.model_entities import ModelPropertyKey from core.app.entities.app_invoke_entities import InvokeFrom from core.tools.__base.tool_runtime import ToolRuntime @@ -29,6 +27,8 @@ from core.tools.builtin_tool.tool import BuiltinTool from core.tools.entities.common_entities import I18nObject from core.tools.entities.tool_entities import ToolEntity, ToolIdentity, ToolInvokeMessage from core.tools.errors import ToolInvokeError +from graphon.file import FileType +from graphon.model_runtime.entities.model_entities import ModelPropertyKey def _build_builtin_tool(tool_cls: type[BuiltinTool]) -> BuiltinTool: diff --git a/api/tests/unit_tests/core/tools/test_tool_file_manager.py b/api/tests/unit_tests/core/tools/test_tool_file_manager.py index 2889cb9db1..ccffdf16d1 100644 --- a/api/tests/unit_tests/core/tools/test_tool_file_manager.py +++ b/api/tests/unit_tests/core/tools/test_tool_file_manager.py @@ -12,9 +12,9 @@ from unittest.mock import MagicMock, Mock, patch import httpx import pytest -from graphon.file import FileTransferMethod from core.tools.tool_file_manager import ToolFileManager +from graphon.file import FileTransferMethod def _setup_tool_file_signing(monkeypatch: pytest.MonkeyPatch) -> dict[str, str]: diff --git a/api/tests/unit_tests/core/tools/utils/test_model_invocation_utils.py b/api/tests/unit_tests/core/tools/utils/test_model_invocation_utils.py index 84b3f71d5e..44785f939c 100644 --- a/api/tests/unit_tests/core/tools/utils/test_model_invocation_utils.py +++ b/api/tests/unit_tests/core/tools/utils/test_model_invocation_utils.py @@ -14,6 +14,8 @@ from typing import Any from unittest.mock import Mock, patch import pytest + +from core.tools.utils.model_invocation_utils import InvokeModelError, ModelInvocationUtils from graphon.model_runtime.entities.model_entities import ModelPropertyKey from graphon.model_runtime.errors.invoke import ( InvokeAuthorizationError, @@ -23,8 +25,6 @@ from graphon.model_runtime.errors.invoke import ( InvokeServerUnavailableError, ) -from core.tools.utils.model_invocation_utils import InvokeModelError, ModelInvocationUtils - def _mock_model_instance(*, schema: dict[str, Any] | None = None) -> SimpleNamespace: model_type_instance = Mock() diff --git a/api/tests/unit_tests/core/tools/utils/test_workflow_configuration_sync.py b/api/tests/unit_tests/core/tools/utils/test_workflow_configuration_sync.py index 0e3a7e623a..43f3fbd5c9 100644 --- a/api/tests/unit_tests/core/tools/utils/test_workflow_configuration_sync.py +++ b/api/tests/unit_tests/core/tools/utils/test_workflow_configuration_sync.py @@ -1,9 +1,9 @@ import pytest -from graphon.variables.input_entities import VariableEntity, VariableEntityType from core.tools.entities.tool_entities import ToolParameter, WorkflowToolParameterConfiguration from core.tools.errors import WorkflowToolHumanInputNotSupportedError from core.tools.utils.workflow_configuration_sync import WorkflowToolConfigurationUtils +from graphon.variables.input_entities import VariableEntity, VariableEntityType def test_ensure_no_human_input_nodes_passes_for_non_human_input(): diff --git a/api/tests/unit_tests/core/tools/workflow_as_tool/test_provider.py b/api/tests/unit_tests/core/tools/workflow_as_tool/test_provider.py index 4767480a5a..5a585c609a 100644 --- a/api/tests/unit_tests/core/tools/workflow_as_tool/test_provider.py +++ b/api/tests/unit_tests/core/tools/workflow_as_tool/test_provider.py @@ -4,7 +4,6 @@ from types import SimpleNamespace from unittest.mock import MagicMock, Mock, patch import pytest -from graphon.variables.input_entities import VariableEntity, VariableEntityType from core.tools.entities.common_entities import I18nObject from core.tools.entities.tool_entities import ( @@ -14,6 +13,7 @@ from core.tools.entities.tool_entities import ( ToolProviderType, ) from core.tools.workflow_as_tool.provider import WorkflowToolProviderController +from graphon.variables.input_entities import VariableEntity, VariableEntityType def _controller() -> WorkflowToolProviderController: diff --git a/api/tests/unit_tests/core/tools/workflow_as_tool/test_tool.py b/api/tests/unit_tests/core/tools/workflow_as_tool/test_tool.py index c20edd7400..72a73dd936 100644 --- a/api/tests/unit_tests/core/tools/workflow_as_tool/test_tool.py +++ b/api/tests/unit_tests/core/tools/workflow_as_tool/test_tool.py @@ -11,7 +11,6 @@ from typing import Any from unittest.mock import MagicMock, Mock, patch import pytest -from graphon.file import FILE_MODEL_IDENTITY, FileTransferMethod, FileType from core.app.entities.app_invoke_entities import InvokeFrom from core.tools.__base.tool_runtime import ToolRuntime @@ -25,6 +24,7 @@ from core.tools.entities.tool_entities import ( ) from core.tools.errors import ToolInvokeError from core.tools.workflow_as_tool.tool import WorkflowTool +from graphon.file import FILE_MODEL_IDENTITY, FileTransferMethod, FileType class StubScalars: diff --git a/api/tests/unit_tests/core/trigger/debug/test_debug_event_selectors.py b/api/tests/unit_tests/core/trigger/debug/test_debug_event_selectors.py index 0c67effe90..fb7dc52838 100644 --- a/api/tests/unit_tests/core/trigger/debug/test_debug_event_selectors.py +++ b/api/tests/unit_tests/core/trigger/debug/test_debug_event_selectors.py @@ -12,7 +12,6 @@ from typing import Any from unittest.mock import MagicMock, patch import pytest -from graphon.enums import BuiltinNodeTypes, NodeType from core.plugin.entities.request import TriggerInvokeEventResponse from core.trigger.constants import ( @@ -28,6 +27,7 @@ from core.trigger.debug.event_selectors import ( select_trigger_debug_events, ) from core.trigger.debug.events import PluginTriggerDebugEvent, WebhookDebugEvent +from graphon.enums import BuiltinNodeTypes, NodeType from tests.unit_tests.core.trigger.conftest import VALID_PROVIDER_ID diff --git a/api/tests/unit_tests/core/variables/test_segment.py b/api/tests/unit_tests/core/variables/test_segment.py index 7406b88270..72052c8c05 100644 --- a/api/tests/unit_tests/core/variables/test_segment.py +++ b/api/tests/unit_tests/core/variables/test_segment.py @@ -2,6 +2,11 @@ import dataclasses import orjson import pytest +from pydantic import BaseModel + +from core.helper import encrypter +from core.workflow.system_variables import build_bootstrap_variables, build_system_variables +from core.workflow.variable_pool_initializer import add_variables_to_pool from graphon.file import File, FileTransferMethod, FileType from graphon.runtime import VariablePool from graphon.variables.segment_group import SegmentGroup @@ -42,11 +47,6 @@ from graphon.variables.variables import ( StringVariable, Variable, ) -from pydantic import BaseModel - -from core.helper import encrypter -from core.workflow.system_variables import build_bootstrap_variables, build_system_variables -from core.workflow.variable_pool_initializer import add_variables_to_pool def _build_variable_pool( diff --git a/api/tests/unit_tests/core/variables/test_segment_type.py b/api/tests/unit_tests/core/variables/test_segment_type.py index 37ecd2890b..d4e862220a 100644 --- a/api/tests/unit_tests/core/variables/test_segment_type.py +++ b/api/tests/unit_tests/core/variables/test_segment_type.py @@ -1,4 +1,5 @@ import pytest + from graphon.variables.segment_group import SegmentGroup from graphon.variables.segments import StringSegment from graphon.variables.types import ArrayValidation, SegmentType diff --git a/api/tests/unit_tests/core/variables/test_segment_type_validation.py b/api/tests/unit_tests/core/variables/test_segment_type_validation.py index 09254e17a3..94e788edb2 100644 --- a/api/tests/unit_tests/core/variables/test_segment_type_validation.py +++ b/api/tests/unit_tests/core/variables/test_segment_type_validation.py @@ -9,6 +9,7 @@ from dataclasses import dataclass from typing import Any import pytest + from graphon.file import File, FileTransferMethod, FileType from graphon.variables.segment_group import SegmentGroup from graphon.variables.segments import ( diff --git a/api/tests/unit_tests/core/variables/test_variables.py b/api/tests/unit_tests/core/variables/test_variables.py index 75b01bf42e..dae5e1ce98 100644 --- a/api/tests/unit_tests/core/variables/test_variables.py +++ b/api/tests/unit_tests/core/variables/test_variables.py @@ -1,4 +1,6 @@ import pytest +from pydantic import ValidationError + from graphon.variables import ( ArrayFileVariable, ArrayVariable, @@ -10,7 +12,6 @@ from graphon.variables import ( StringVariable, ) from graphon.variables.variables import VariableBase -from pydantic import ValidationError def test_frozen_variables(): diff --git a/api/tests/unit_tests/core/workflow/graph_engine/layers/conftest.py b/api/tests/unit_tests/core/workflow/graph_engine/layers/conftest.py index 41627f5e0b..025d79b25d 100644 --- a/api/tests/unit_tests/core/workflow/graph_engine/layers/conftest.py +++ b/api/tests/unit_tests/core/workflow/graph_engine/layers/conftest.py @@ -5,12 +5,13 @@ Shared fixtures for ObservabilityLayer tests. from unittest.mock import MagicMock, patch import pytest -from graphon.enums import BuiltinNodeTypes from opentelemetry.sdk.trace import TracerProvider from opentelemetry.sdk.trace.export import SimpleSpanProcessor from opentelemetry.sdk.trace.export.in_memory_span_exporter import InMemorySpanExporter from opentelemetry.trace import set_tracer_provider +from graphon.enums import BuiltinNodeTypes + @pytest.fixture def memory_span_exporter(): @@ -61,9 +62,8 @@ def mock_llm_node(): @pytest.fixture def mock_tool_node(): """Create a mock Tool Node with tool-specific attributes.""" - from graphon.nodes.tool.entities import ToolNodeData - from core.tools.entities.tool_entities import ToolProviderType + from graphon.nodes.tool.entities import ToolNodeData node = MagicMock() node.id = "test-tool-node-id" diff --git a/api/tests/unit_tests/core/workflow/graph_engine/layers/test_llm_quota.py b/api/tests/unit_tests/core/workflow/graph_engine/layers/test_llm_quota.py index 99d131737e..5d6667257f 100644 --- a/api/tests/unit_tests/core/workflow/graph_engine/layers/test_llm_quota.py +++ b/api/tests/unit_tests/core/workflow/graph_engine/layers/test_llm_quota.py @@ -3,17 +3,16 @@ from datetime import datetime from types import SimpleNamespace from unittest.mock import MagicMock, patch +from core.app.entities.app_invoke_entities import DifyRunContext, InvokeFrom, UserFrom +from core.app.workflow.layers.llm_quota import LLMQuotaLayer +from core.errors.error import QuotaExceededError +from core.model_manager import ModelInstance from graphon.enums import BuiltinNodeTypes, WorkflowNodeExecutionStatus from graphon.graph_engine.entities.commands import CommandType from graphon.graph_events import NodeRunSucceededEvent from graphon.model_runtime.entities.llm_entities import LLMUsage from graphon.node_events import NodeRunResult -from core.app.entities.app_invoke_entities import DifyRunContext, InvokeFrom, UserFrom -from core.app.workflow.layers.llm_quota import LLMQuotaLayer -from core.errors.error import QuotaExceededError -from core.model_manager import ModelInstance - def _build_dify_context() -> DifyRunContext: return DifyRunContext( diff --git a/api/tests/unit_tests/core/workflow/graph_engine/layers/test_observability.py b/api/tests/unit_tests/core/workflow/graph_engine/layers/test_observability.py index 9cf72763ee..919f15efd0 100644 --- a/api/tests/unit_tests/core/workflow/graph_engine/layers/test_observability.py +++ b/api/tests/unit_tests/core/workflow/graph_engine/layers/test_observability.py @@ -13,10 +13,10 @@ Test coverage: from unittest.mock import patch import pytest -from graphon.enums import BuiltinNodeTypes from opentelemetry.trace import StatusCode from core.app.workflow.layers.observability import ObservabilityLayer +from graphon.enums import BuiltinNodeTypes class TestObservabilityLayerInitialization: diff --git a/api/tests/unit_tests/core/workflow/graph_engine/test_mock_factory.py b/api/tests/unit_tests/core/workflow/graph_engine/test_mock_factory.py index 88989db856..76b2984a4b 100644 --- a/api/tests/unit_tests/core/workflow/graph_engine/test_mock_factory.py +++ b/api/tests/unit_tests/core/workflow/graph_engine/test_mock_factory.py @@ -7,12 +7,11 @@ requiring external services (LLM, Agent, Tool, Knowledge Retrieval, HTTP Request from typing import TYPE_CHECKING, Any +from core.workflow.node_factory import DifyNodeFactory from graphon.entities.graph_config import NodeConfigDict, NodeConfigDictAdapter from graphon.enums import BuiltinNodeTypes, NodeType from graphon.nodes.base.node import Node -from core.workflow.node_factory import DifyNodeFactory - from .test_mock_nodes import ( MockAgentNode, MockCodeNode, diff --git a/api/tests/unit_tests/core/workflow/graph_engine/test_mock_nodes.py b/api/tests/unit_tests/core/workflow/graph_engine/test_mock_nodes.py index 8b7fbd1b30..971b9b2bbf 100644 --- a/api/tests/unit_tests/core/workflow/graph_engine/test_mock_nodes.py +++ b/api/tests/unit_tests/core/workflow/graph_engine/test_mock_nodes.py @@ -10,6 +10,10 @@ from collections.abc import Generator, Mapping from typing import TYPE_CHECKING, Any, Optional from unittest.mock import MagicMock +from core.model_manager import ModelInstance +from core.workflow.node_runtime import DifyToolNodeRuntime +from core.workflow.nodes.agent import AgentNode +from core.workflow.nodes.knowledge_retrieval.knowledge_retrieval_node import KnowledgeRetrievalNode from graphon.enums import WorkflowNodeExecutionMetadataKey, WorkflowNodeExecutionStatus from graphon.model_runtime.entities.llm_entities import LLMUsage from graphon.node_events import NodeRunResult, StreamChunkEvent, StreamCompletedEvent @@ -27,11 +31,6 @@ from graphon.nodes.template_transform import TemplateTransformNode from graphon.nodes.tool import ToolNode from graphon.template_rendering import Jinja2TemplateRenderer, TemplateRenderError -from core.model_manager import ModelInstance -from core.workflow.node_runtime import DifyToolNodeRuntime -from core.workflow.nodes.agent import AgentNode -from core.workflow.nodes.knowledge_retrieval.knowledge_retrieval_node import KnowledgeRetrievalNode - if TYPE_CHECKING: from graphon.entities import GraphInitParams from graphon.runtime import GraphRuntimeState diff --git a/api/tests/unit_tests/core/workflow/graph_engine/test_parallel_human_input_join_resume.py b/api/tests/unit_tests/core/workflow/graph_engine/test_parallel_human_input_join_resume.py index 8311a1e847..55a329eba9 100644 --- a/api/tests/unit_tests/core/workflow/graph_engine/test_parallel_human_input_join_resume.py +++ b/api/tests/unit_tests/core/workflow/graph_engine/test_parallel_human_input_join_resume.py @@ -4,6 +4,13 @@ from dataclasses import dataclass from datetime import datetime, timedelta from typing import Any, Protocol +from core.repositories.human_input_repository import ( + FormCreateParams, + HumanInputFormEntity, + HumanInputFormRepository, +) +from core.workflow.node_runtime import DifyHumanInputNodeRuntime +from core.workflow.system_variables import build_system_variables from graphon.entities import WorkflowStartReason from graphon.graph import Graph from graphon.graph_engine import GraphEngine, GraphEngineConfig @@ -23,14 +30,6 @@ from graphon.nodes.human_input.human_input_node import HumanInputNode from graphon.nodes.start.entities import StartNodeData from graphon.nodes.start.start_node import StartNode from graphon.runtime import GraphRuntimeState, VariablePool - -from core.repositories.human_input_repository import ( - FormCreateParams, - HumanInputFormEntity, - HumanInputFormRepository, -) -from core.workflow.node_runtime import DifyHumanInputNodeRuntime -from core.workflow.system_variables import build_system_variables from libs.datetime_utils import naive_utc_now from tests.workflow_test_utils import build_test_graph_init_params diff --git a/api/tests/unit_tests/core/workflow/graph_engine/test_table_runner.py b/api/tests/unit_tests/core/workflow/graph_engine/test_table_runner.py index b11f957677..7d23b63049 100644 --- a/api/tests/unit_tests/core/workflow/graph_engine/test_table_runner.py +++ b/api/tests/unit_tests/core/workflow/graph_engine/test_table_runner.py @@ -19,6 +19,11 @@ from functools import lru_cache from pathlib import Path from typing import Any +from core.app.entities.app_invoke_entities import DIFY_RUN_CONTEXT_KEY, InvokeFrom, UserFrom +from core.tools.utils.yaml_utils import _load_yaml_file +from core.workflow.node_factory import DifyNodeFactory, get_default_root_node_id +from core.workflow.system_variables import build_bootstrap_variables, build_system_variables +from core.workflow.variable_pool_initializer import add_node_inputs_to_pool, add_variables_to_pool from graphon.entities import GraphInitParams from graphon.graph import Graph from graphon.graph_engine import GraphEngine, GraphEngineConfig @@ -39,12 +44,6 @@ from graphon.variables import ( StringVariable, ) -from core.app.entities.app_invoke_entities import DIFY_RUN_CONTEXT_KEY, InvokeFrom, UserFrom -from core.tools.utils.yaml_utils import _load_yaml_file -from core.workflow.node_factory import DifyNodeFactory, get_default_root_node_id -from core.workflow.system_variables import build_bootstrap_variables, build_system_variables -from core.workflow.variable_pool_initializer import add_node_inputs_to_pool, add_variables_to_pool - from .test_mock_config import MockConfig from .test_mock_factory import MockNodeFactory diff --git a/api/tests/unit_tests/core/workflow/nodes/agent/test_message_transformer.py b/api/tests/unit_tests/core/workflow/nodes/agent/test_message_transformer.py index cbc920705c..1f4509af9a 100644 --- a/api/tests/unit_tests/core/workflow/nodes/agent/test_message_transformer.py +++ b/api/tests/unit_tests/core/workflow/nodes/agent/test_message_transformer.py @@ -1,9 +1,8 @@ from unittest.mock import patch -from graphon.enums import BuiltinNodeTypes - from core.tools.utils.message_transformer import ToolFileMessageTransformer from core.workflow.nodes.agent.message_transformer import AgentMessageTransformer +from graphon.enums import BuiltinNodeTypes def test_transform_passes_conversation_id_to_tool_file_message_transformer() -> None: diff --git a/api/tests/unit_tests/core/workflow/nodes/agent/test_runtime_support.py b/api/tests/unit_tests/core/workflow/nodes/agent/test_runtime_support.py index 59dd763b59..c86de7f6e6 100644 --- a/api/tests/unit_tests/core/workflow/nodes/agent/test_runtime_support.py +++ b/api/tests/unit_tests/core/workflow/nodes/agent/test_runtime_support.py @@ -1,9 +1,8 @@ from types import SimpleNamespace from unittest.mock import Mock, patch -from graphon.model_runtime.entities.model_entities import ModelType - from core.workflow.nodes.agent.runtime_support import AgentRuntimeSupport +from graphon.model_runtime.entities.model_entities import ModelType def test_fetch_model_reuses_single_model_assembly(): diff --git a/api/tests/unit_tests/core/workflow/nodes/answer/test_answer.py b/api/tests/unit_tests/core/workflow/nodes/answer/test_answer.py index 7195471eb6..9c0ad25b58 100644 --- a/api/tests/unit_tests/core/workflow/nodes/answer/test_answer.py +++ b/api/tests/unit_tests/core/workflow/nodes/answer/test_answer.py @@ -2,15 +2,14 @@ import time import uuid from unittest.mock import MagicMock -from graphon.enums import WorkflowNodeExecutionStatus -from graphon.graph import Graph -from graphon.nodes.answer.answer_node import AnswerNode -from graphon.runtime import GraphRuntimeState, VariablePool - from core.app.entities.app_invoke_entities import InvokeFrom, UserFrom from core.workflow.node_factory import DifyNodeFactory from core.workflow.system_variables import build_system_variables from extensions.ext_database import db +from graphon.enums import WorkflowNodeExecutionStatus +from graphon.graph import Graph +from graphon.nodes.answer.answer_node import AnswerNode +from graphon.runtime import GraphRuntimeState, VariablePool from tests.workflow_test_utils import build_test_graph_init_params diff --git a/api/tests/unit_tests/core/workflow/nodes/base/test_base_node.py b/api/tests/unit_tests/core/workflow/nodes/base/test_base_node.py index 343bcd3919..ec4cef1955 100644 --- a/api/tests/unit_tests/core/workflow/nodes/base/test_base_node.py +++ b/api/tests/unit_tests/core/workflow/nodes/base/test_base_node.py @@ -1,10 +1,10 @@ import pytest + +from core.workflow.node_factory import get_node_type_classes_mapping from graphon.entities.base_node_data import BaseNodeData from graphon.enums import BuiltinNodeTypes, NodeType from graphon.nodes.base.node import Node -from core.workflow.node_factory import get_node_type_classes_mapping - # Ensures that all production node classes are imported and registered. _ = get_node_type_classes_mapping() diff --git a/api/tests/unit_tests/core/workflow/nodes/base/test_get_node_type_classes_mapping.py b/api/tests/unit_tests/core/workflow/nodes/base/test_get_node_type_classes_mapping.py index b9371a34f4..ef0df55995 100644 --- a/api/tests/unit_tests/core/workflow/nodes/base/test_get_node_type_classes_mapping.py +++ b/api/tests/unit_tests/core/workflow/nodes/base/test_get_node_type_classes_mapping.py @@ -1,6 +1,7 @@ import types from collections.abc import Mapping +from core.workflow.node_factory import get_node_type_classes_mapping from graphon.entities.base_node_data import BaseNodeData from graphon.enums import BuiltinNodeTypes, NodeType from graphon.nodes.base.node import Node @@ -13,8 +14,6 @@ from graphon.nodes.variable_assigner.v2.node import ( VariableAssignerNode as VariableAssignerV2, ) -from core.workflow.node_factory import get_node_type_classes_mapping - def test_variable_assigner_latest_prefers_highest_numeric_version(): # Act diff --git a/api/tests/unit_tests/core/workflow/nodes/code/code_node_spec.py b/api/tests/unit_tests/core/workflow/nodes/code/code_node_spec.py index d155124c50..ce0c9b79c6 100644 --- a/api/tests/unit_tests/core/workflow/nodes/code/code_node_spec.py +++ b/api/tests/unit_tests/core/workflow/nodes/code/code_node_spec.py @@ -1,3 +1,4 @@ +from configs import dify_config from graphon.nodes.code.code_node import CodeNode from graphon.nodes.code.entities import CodeLanguage, CodeNodeData from graphon.nodes.code.exc import ( @@ -8,8 +9,6 @@ from graphon.nodes.code.exc import ( from graphon.nodes.code.limits import CodeNodeLimits from graphon.variables.types import SegmentType -from configs import dify_config - CodeNode._limits = CodeNodeLimits( max_string_length=dify_config.CODE_MAX_STRING_LENGTH, max_number=dify_config.CODE_MAX_NUMBER, diff --git a/api/tests/unit_tests/core/workflow/nodes/datasource/test_datasource_node.py b/api/tests/unit_tests/core/workflow/nodes/datasource/test_datasource_node.py index fb03ae9998..9cceadde49 100644 --- a/api/tests/unit_tests/core/workflow/nodes/datasource/test_datasource_node.py +++ b/api/tests/unit_tests/core/workflow/nodes/datasource/test_datasource_node.py @@ -1,8 +1,7 @@ -from graphon.enums import WorkflowNodeExecutionStatus -from graphon.node_events import NodeRunResult, StreamChunkEvent, StreamCompletedEvent - from core.app.entities.app_invoke_entities import DIFY_RUN_CONTEXT_KEY from core.workflow.nodes.datasource.datasource_node import DatasourceNode +from graphon.enums import WorkflowNodeExecutionStatus +from graphon.node_events import NodeRunResult, StreamChunkEvent, StreamCompletedEvent class _VarSeg: diff --git a/api/tests/unit_tests/core/workflow/nodes/http_request/test_http_request_executor.py b/api/tests/unit_tests/core/workflow/nodes/http_request/test_http_request_executor.py index a5026b40cf..be7cc073db 100644 --- a/api/tests/unit_tests/core/workflow/nodes/http_request/test_http_request_executor.py +++ b/api/tests/unit_tests/core/workflow/nodes/http_request/test_http_request_executor.py @@ -1,4 +1,8 @@ import pytest + +from configs import dify_config +from core.helper.ssrf_proxy import ssrf_proxy +from core.workflow.system_variables import default_system_variables from graphon.file.file_manager import file_manager from graphon.nodes.http_request import ( BodyData, @@ -12,10 +16,6 @@ from graphon.nodes.http_request.exc import AuthorizationConfigError from graphon.nodes.http_request.executor import Executor from graphon.runtime import VariablePool -from configs import dify_config -from core.helper.ssrf_proxy import ssrf_proxy -from core.workflow.system_variables import default_system_variables - HTTP_REQUEST_CONFIG = HttpRequestNodeConfig( max_connect_timeout=dify_config.HTTP_REQUEST_MAX_CONNECT_TIMEOUT, max_read_timeout=dify_config.HTTP_REQUEST_MAX_READ_TIMEOUT, diff --git a/api/tests/unit_tests/core/workflow/nodes/http_request/test_http_request_node.py b/api/tests/unit_tests/core/workflow/nodes/http_request/test_http_request_node.py index 4705b3f76e..a3cadc0681 100644 --- a/api/tests/unit_tests/core/workflow/nodes/http_request/test_http_request_node.py +++ b/api/tests/unit_tests/core/workflow/nodes/http_request/test_http_request_node.py @@ -3,17 +3,17 @@ from typing import Any import httpx import pytest -from graphon.enums import WorkflowNodeExecutionStatus -from graphon.file.file_manager import file_manager -from graphon.nodes.http_request import HTTP_REQUEST_CONFIG_FILTER_KEY, HttpRequestNode, HttpRequestNodeConfig -from graphon.nodes.http_request.entities import HttpRequestNodeTimeout, Response -from graphon.runtime import GraphRuntimeState, VariablePool from core.app.entities.app_invoke_entities import InvokeFrom, UserFrom from core.helper.ssrf_proxy import ssrf_proxy from core.tools.tool_file_manager import ToolFileManager from core.workflow.node_runtime import DifyFileReferenceFactory from core.workflow.system_variables import build_system_variables +from graphon.enums import WorkflowNodeExecutionStatus +from graphon.file.file_manager import file_manager +from graphon.nodes.http_request import HTTP_REQUEST_CONFIG_FILTER_KEY, HttpRequestNode, HttpRequestNodeConfig +from graphon.nodes.http_request.entities import HttpRequestNodeTimeout, Response +from graphon.runtime import GraphRuntimeState, VariablePool from tests.workflow_test_utils import build_test_graph_init_params HTTP_REQUEST_CONFIG = HttpRequestNodeConfig( diff --git a/api/tests/unit_tests/core/workflow/nodes/human_input/test_email_delivery_config.py b/api/tests/unit_tests/core/workflow/nodes/human_input/test_email_delivery_config.py index d16e1233ac..1d6a4da7c4 100644 --- a/api/tests/unit_tests/core/workflow/nodes/human_input/test_email_delivery_config.py +++ b/api/tests/unit_tests/core/workflow/nodes/human_input/test_email_delivery_config.py @@ -1,6 +1,5 @@ -from graphon.runtime import VariablePool - from core.workflow.human_input_compat import EmailDeliveryConfig, EmailRecipients +from graphon.runtime import VariablePool def test_render_body_template_replaces_variable_values(): diff --git a/api/tests/unit_tests/core/workflow/nodes/human_input/test_entities.py b/api/tests/unit_tests/core/workflow/nodes/human_input/test_entities.py index a2cdbbf132..c0e21d0bf7 100644 --- a/api/tests/unit_tests/core/workflow/nodes/human_input/test_entities.py +++ b/api/tests/unit_tests/core/workflow/nodes/human_input/test_entities.py @@ -10,24 +10,6 @@ from typing import Any from unittest.mock import MagicMock import pytest -from graphon.entities import GraphInitParams -from graphon.node_events import PauseRequestedEvent -from graphon.node_events.node import StreamCompletedEvent -from graphon.nodes.human_input.entities import ( - FormInput, - FormInputDefault, - HumanInputNodeData, - UserAction, -) -from graphon.nodes.human_input.enums import ( - ButtonStyle, - FormInputType, - HumanInputFormStatus, - PlaceholderType, - TimeoutUnit, -) -from graphon.nodes.human_input.human_input_node import HumanInputNode -from graphon.runtime import GraphRuntimeState, VariablePool from pydantic import ValidationError from core.app.entities.app_invoke_entities import DIFY_RUN_CONTEXT_KEY @@ -50,6 +32,24 @@ from core.workflow.human_input_compat import ( ) from core.workflow.node_runtime import DifyHumanInputNodeRuntime from core.workflow.system_variables import build_system_variables +from graphon.entities import GraphInitParams +from graphon.node_events import PauseRequestedEvent +from graphon.node_events.node import StreamCompletedEvent +from graphon.nodes.human_input.entities import ( + FormInput, + FormInputDefault, + HumanInputNodeData, + UserAction, +) +from graphon.nodes.human_input.enums import ( + ButtonStyle, + FormInputType, + HumanInputFormStatus, + PlaceholderType, + TimeoutUnit, +) +from graphon.nodes.human_input.human_input_node import HumanInputNode +from graphon.runtime import GraphRuntimeState, VariablePool from libs.datetime_utils import naive_utc_now diff --git a/api/tests/unit_tests/core/workflow/nodes/human_input/test_human_input_form_filled_event.py b/api/tests/unit_tests/core/workflow/nodes/human_input/test_human_input_form_filled_event.py index 52802c7ce1..bc98028d5b 100644 --- a/api/tests/unit_tests/core/workflow/nodes/human_input/test_human_input_form_filled_event.py +++ b/api/tests/unit_tests/core/workflow/nodes/human_input/test_human_input_form_filled_event.py @@ -1,6 +1,9 @@ import datetime from types import SimpleNamespace +from core.app.entities.app_invoke_entities import DIFY_RUN_CONTEXT_KEY, InvokeFrom, UserFrom +from core.workflow.node_runtime import DifyHumanInputNodeRuntime +from core.workflow.system_variables import default_system_variables from graphon.entities import GraphInitParams from graphon.enums import BuiltinNodeTypes from graphon.graph_events import ( @@ -11,10 +14,6 @@ from graphon.graph_events import ( from graphon.nodes.human_input.enums import HumanInputFormStatus from graphon.nodes.human_input.human_input_node import HumanInputNode from graphon.runtime import GraphRuntimeState, VariablePool - -from core.app.entities.app_invoke_entities import DIFY_RUN_CONTEXT_KEY, InvokeFrom, UserFrom -from core.workflow.node_runtime import DifyHumanInputNodeRuntime -from core.workflow.system_variables import default_system_variables from libs.datetime_utils import naive_utc_now diff --git a/api/tests/unit_tests/core/workflow/nodes/iteration/test_iteration_child_engine_errors.py b/api/tests/unit_tests/core/workflow/nodes/iteration/test_iteration_child_engine_errors.py index bbfe350f7e..82cc734274 100644 --- a/api/tests/unit_tests/core/workflow/nodes/iteration/test_iteration_child_engine_errors.py +++ b/api/tests/unit_tests/core/workflow/nodes/iteration/test_iteration_child_engine_errors.py @@ -2,6 +2,8 @@ from collections.abc import Mapping from typing import Any import pytest + +from core.workflow.system_variables import default_system_variables from graphon.entities import GraphInitParams from graphon.nodes.iteration.exc import IterationGraphNotFoundError from graphon.nodes.iteration.iteration_node import IterationNode @@ -11,8 +13,6 @@ from graphon.runtime import ( GraphRuntimeState, VariablePool, ) - -from core.workflow.system_variables import default_system_variables from tests.workflow_test_utils import build_test_graph_init_params diff --git a/api/tests/unit_tests/core/workflow/nodes/knowledge_index/test_knowledge_index_node.py b/api/tests/unit_tests/core/workflow/nodes/knowledge_index/test_knowledge_index_node.py index f8802138b5..a6fca1bfb4 100644 --- a/api/tests/unit_tests/core/workflow/nodes/knowledge_index/test_knowledge_index_node.py +++ b/api/tests/unit_tests/core/workflow/nodes/knowledge_index/test_knowledge_index_node.py @@ -3,9 +3,6 @@ import uuid from unittest.mock import Mock import pytest -from graphon.enums import WorkflowNodeExecutionStatus -from graphon.runtime import GraphRuntimeState, VariablePool -from graphon.variables.segments import StringSegment from core.app.entities.app_invoke_entities import InvokeFrom, UserFrom from core.rag.index_processor.constant.index_type import IndexTechniqueType @@ -19,6 +16,9 @@ from core.workflow.nodes.knowledge_index.protocols import ( SummaryIndexServiceProtocol, ) from core.workflow.system_variables import SystemVariableKey, build_system_variables +from graphon.enums import WorkflowNodeExecutionStatus +from graphon.runtime import GraphRuntimeState, VariablePool +from graphon.variables.segments import StringSegment from tests.workflow_test_utils import build_test_graph_init_params diff --git a/api/tests/unit_tests/core/workflow/nodes/knowledge_retrieval/test_knowledge_retrieval_node.py b/api/tests/unit_tests/core/workflow/nodes/knowledge_retrieval/test_knowledge_retrieval_node.py index ab64be59ad..45e8ae7d20 100644 --- a/api/tests/unit_tests/core/workflow/nodes/knowledge_retrieval/test_knowledge_retrieval_node.py +++ b/api/tests/unit_tests/core/workflow/nodes/knowledge_retrieval/test_knowledge_retrieval_node.py @@ -3,10 +3,6 @@ import uuid from unittest.mock import Mock import pytest -from graphon.enums import WorkflowNodeExecutionStatus -from graphon.model_runtime.entities.llm_entities import LLMUsage -from graphon.runtime import GraphRuntimeState, VariablePool -from graphon.variables import StringSegment from core.app.entities.app_invoke_entities import InvokeFrom, UserFrom from core.workflow.nodes.knowledge_retrieval.entities import ( @@ -21,6 +17,10 @@ from core.workflow.nodes.knowledge_retrieval.exc import RateLimitExceededError from core.workflow.nodes.knowledge_retrieval.knowledge_retrieval_node import KnowledgeRetrievalNode from core.workflow.nodes.knowledge_retrieval.retrieval import RAGRetrievalProtocol, Source from core.workflow.system_variables import build_system_variables +from graphon.enums import WorkflowNodeExecutionStatus +from graphon.model_runtime.entities.llm_entities import LLMUsage +from graphon.runtime import GraphRuntimeState, VariablePool +from graphon.variables import StringSegment from tests.workflow_test_utils import build_test_graph_init_params diff --git a/api/tests/unit_tests/core/workflow/nodes/list_operator/node_spec.py b/api/tests/unit_tests/core/workflow/nodes/list_operator/node_spec.py index fdf1706765..eca34f05be 100644 --- a/api/tests/unit_tests/core/workflow/nodes/list_operator/node_spec.py +++ b/api/tests/unit_tests/core/workflow/nodes/list_operator/node_spec.py @@ -1,14 +1,14 @@ from unittest.mock import MagicMock import pytest + +from core.app.entities.app_invoke_entities import DIFY_RUN_CONTEXT_KEY from graphon.entities import GraphInitParams from graphon.enums import BuiltinNodeTypes, WorkflowNodeExecutionStatus from graphon.nodes.list_operator.node import ListOperatorNode from graphon.runtime import GraphRuntimeState from graphon.variables import ArrayNumberSegment, ArrayStringSegment -from core.app.entities.app_invoke_entities import DIFY_RUN_CONTEXT_KEY - class TestListOperatorNode: """Comprehensive tests for ListOperatorNode.""" diff --git a/api/tests/unit_tests/core/workflow/nodes/llm/test_llm_utils.py b/api/tests/unit_tests/core/workflow/nodes/llm/test_llm_utils.py index c784f805c0..4186bbdc93 100644 --- a/api/tests/unit_tests/core/workflow/nodes/llm/test_llm_utils.py +++ b/api/tests/unit_tests/core/workflow/nodes/llm/test_llm_utils.py @@ -1,6 +1,8 @@ from unittest import mock import pytest + +from core.model_manager import ModelInstance from graphon.file import File, FileTransferMethod, FileType from graphon.model_runtime.entities import ( ImagePromptMessageContent, @@ -33,8 +35,6 @@ from graphon.nodes.llm.exc import ( from graphon.runtime import VariablePool from graphon.variables import ArrayAnySegment, ArrayFileSegment, NoneSegment -from core.model_manager import ModelInstance - def _build_model_schema( *, diff --git a/api/tests/unit_tests/core/workflow/nodes/llm/test_node.py b/api/tests/unit_tests/core/workflow/nodes/llm/test_node.py index 7841bf05ad..b1f81b6c48 100644 --- a/api/tests/unit_tests/core/workflow/nodes/llm/test_node.py +++ b/api/tests/unit_tests/core/workflow/nodes/llm/test_node.py @@ -5,6 +5,19 @@ from collections.abc import Sequence from unittest import mock import pytest + +from core.app.entities.app_invoke_entities import DifyRunContext, InvokeFrom, ModelConfigWithCredentialsEntity, UserFrom +from core.app.llm.model_access import ( + DifyCredentialsProvider, + DifyModelFactory, + build_dify_model_access, + fetch_model_config, +) +from core.entities.provider_configuration import ProviderConfiguration, ProviderModelBundle +from core.entities.provider_entities import CustomConfiguration, SystemConfiguration +from core.plugin.impl.model_runtime_factory import create_plugin_model_runtime +from core.prompt.entities.advanced_prompt_entities import MemoryConfig +from core.workflow.system_variables import default_system_variables from graphon.entities import GraphInitParams from graphon.file import File, FileTransferMethod, FileType from graphon.model_runtime.entities.common_entities import I18nObject @@ -67,19 +80,6 @@ from graphon.nodes.llm.runtime_protocols import PromptMessageSerializerProtocol from graphon.runtime import GraphRuntimeState, VariablePool from graphon.template_rendering import TemplateRenderError from graphon.variables import ArrayAnySegment, ArrayFileSegment, NoneSegment - -from core.app.entities.app_invoke_entities import DifyRunContext, InvokeFrom, ModelConfigWithCredentialsEntity, UserFrom -from core.app.llm.model_access import ( - DifyCredentialsProvider, - DifyModelFactory, - build_dify_model_access, - fetch_model_config, -) -from core.entities.provider_configuration import ProviderConfiguration, ProviderModelBundle -from core.entities.provider_entities import CustomConfiguration, SystemConfiguration -from core.plugin.impl.model_runtime_factory import create_plugin_model_runtime -from core.prompt.entities.advanced_prompt_entities import MemoryConfig -from core.workflow.system_variables import default_system_variables from models.provider import ProviderType from tests.workflow_test_utils import build_test_graph_init_params diff --git a/api/tests/unit_tests/core/workflow/nodes/parameter_extractor/test_parameter_extractor_node.py b/api/tests/unit_tests/core/workflow/nodes/parameter_extractor/test_parameter_extractor_node.py index 1c362a0a03..8f8ec49f14 100644 --- a/api/tests/unit_tests/core/workflow/nodes/parameter_extractor/test_parameter_extractor_node.py +++ b/api/tests/unit_tests/core/workflow/nodes/parameter_extractor/test_parameter_extractor_node.py @@ -6,6 +6,8 @@ from dataclasses import dataclass from typing import Any import pytest + +from factories.variable_factory import build_segment_with_type from graphon.model_runtime.entities import LLMMode from graphon.nodes.llm import ModelConfig, VisionConfig from graphon.nodes.parameter_extractor.entities import ParameterConfig, ParameterExtractorNodeData @@ -18,8 +20,6 @@ from graphon.nodes.parameter_extractor.exc import ( from graphon.nodes.parameter_extractor.parameter_extractor_node import ParameterExtractorNode from graphon.variables.types import SegmentType -from factories.variable_factory import build_segment_with_type - @dataclass class ValidTestCase: diff --git a/api/tests/unit_tests/core/workflow/nodes/template_transform/template_transform_node_spec.py b/api/tests/unit_tests/core/workflow/nodes/template_transform/template_transform_node_spec.py index d86e0efe02..bc44ececd8 100644 --- a/api/tests/unit_tests/core/workflow/nodes/template_transform/template_transform_node_spec.py +++ b/api/tests/unit_tests/core/workflow/nodes/template_transform/template_transform_node_spec.py @@ -1,6 +1,8 @@ from unittest.mock import MagicMock import pytest + +from core.app.entities.app_invoke_entities import InvokeFrom, UserFrom from graphon.enums import BuiltinNodeTypes, ErrorStrategy, WorkflowNodeExecutionStatus from graphon.graph import Graph from graphon.nodes.base.entities import VariableSelector @@ -8,8 +10,6 @@ from graphon.nodes.template_transform.entities import TemplateTransformNodeData from graphon.nodes.template_transform.template_transform_node import TemplateTransformNode from graphon.runtime import GraphRuntimeState from graphon.template_rendering import TemplateRenderError - -from core.app.entities.app_invoke_entities import InvokeFrom, UserFrom from tests.workflow_test_utils import build_test_graph_init_params diff --git a/api/tests/unit_tests/core/workflow/nodes/template_transform/test_template_transform_node.py b/api/tests/unit_tests/core/workflow/nodes/template_transform/test_template_transform_node.py index bd22a8e318..636237e56e 100644 --- a/api/tests/unit_tests/core/workflow/nodes/template_transform/test_template_transform_node.py +++ b/api/tests/unit_tests/core/workflow/nodes/template_transform/test_template_transform_node.py @@ -1,14 +1,14 @@ from unittest.mock import MagicMock import pytest + +from core.app.entities.app_invoke_entities import InvokeFrom, UserFrom from graphon.nodes.base.entities import VariableSelector from graphon.nodes.template_transform.template_transform_node import ( DEFAULT_TEMPLATE_TRANSFORM_MAX_OUTPUT_LENGTH, TemplateTransformNode, ) from graphon.runtime import GraphRuntimeState - -from core.app.entities.app_invoke_entities import InvokeFrom, UserFrom from tests.workflow_test_utils import build_test_graph_init_params from .template_transform_node_spec import TestTemplateTransformNode # noqa: F401 diff --git a/api/tests/unit_tests/core/workflow/nodes/test_base_node.py b/api/tests/unit_tests/core/workflow/nodes/test_base_node.py index e11ebf6eb8..0522dd9d14 100644 --- a/api/tests/unit_tests/core/workflow/nodes/test_base_node.py +++ b/api/tests/unit_tests/core/workflow/nodes/test_base_node.py @@ -1,16 +1,16 @@ from collections.abc import Mapping import pytest + +from core.app.entities.app_invoke_entities import InvokeFrom, UserFrom +from core.workflow.node_runtime import resolve_dify_run_context +from core.workflow.system_variables import build_system_variables from graphon.entities import GraphInitParams from graphon.entities.base_node_data import BaseNodeData from graphon.entities.graph_config import NodeConfigDict, NodeConfigDictAdapter from graphon.enums import BuiltinNodeTypes from graphon.nodes.base.node import Node from graphon.runtime import GraphRuntimeState, VariablePool - -from core.app.entities.app_invoke_entities import InvokeFrom, UserFrom -from core.workflow.node_runtime import resolve_dify_run_context -from core.workflow.system_variables import build_system_variables from tests.workflow_test_utils import build_test_graph_init_params diff --git a/api/tests/unit_tests/core/workflow/nodes/test_document_extractor_node.py b/api/tests/unit_tests/core/workflow/nodes/test_document_extractor_node.py index 555ff0c945..87ec2d5bce 100644 --- a/api/tests/unit_tests/core/workflow/nodes/test_document_extractor_node.py +++ b/api/tests/unit_tests/core/workflow/nodes/test_document_extractor_node.py @@ -4,6 +4,8 @@ from unittest.mock import Mock, patch import pandas as pd import pytest from docx.oxml.text.paragraph import CT_P + +from core.app.entities.app_invoke_entities import InvokeFrom, UserFrom from graphon.entities import GraphInitParams from graphon.enums import BuiltinNodeTypes, WorkflowNodeExecutionStatus from graphon.file import File, FileTransferMethod @@ -19,8 +21,6 @@ from graphon.nodes.document_extractor.node import ( from graphon.variables import ArrayFileSegment from graphon.variables.segments import ArrayStringSegment from graphon.variables.variables import StringVariable - -from core.app.entities.app_invoke_entities import InvokeFrom, UserFrom from tests.workflow_test_utils import build_test_graph_init_params diff --git a/api/tests/unit_tests/core/workflow/nodes/test_if_else.py b/api/tests/unit_tests/core/workflow/nodes/test_if_else.py index 1b14f0ab13..782750e02e 100644 --- a/api/tests/unit_tests/core/workflow/nodes/test_if_else.py +++ b/api/tests/unit_tests/core/workflow/nodes/test_if_else.py @@ -3,6 +3,11 @@ import uuid from unittest.mock import MagicMock, Mock import pytest + +from core.app.entities.app_invoke_entities import DIFY_RUN_CONTEXT_KEY, InvokeFrom, UserFrom +from core.workflow.node_factory import DifyNodeFactory +from core.workflow.system_variables import build_system_variables +from extensions.ext_database import db from graphon.enums import WorkflowNodeExecutionStatus from graphon.file import File, FileTransferMethod, FileType from graphon.graph import Graph @@ -11,11 +16,6 @@ from graphon.nodes.if_else.if_else_node import IfElseNode from graphon.runtime import GraphRuntimeState, VariablePool from graphon.utils.condition.entities import Condition, SubCondition, SubVariableCondition from graphon.variables import ArrayFileSegment - -from core.app.entities.app_invoke_entities import DIFY_RUN_CONTEXT_KEY, InvokeFrom, UserFrom -from core.workflow.node_factory import DifyNodeFactory -from core.workflow.system_variables import build_system_variables -from extensions.ext_database import db from tests.workflow_test_utils import build_test_graph_init_params diff --git a/api/tests/unit_tests/core/workflow/nodes/test_list_operator.py b/api/tests/unit_tests/core/workflow/nodes/test_list_operator.py index d28c3e01e5..b217e4e8e7 100644 --- a/api/tests/unit_tests/core/workflow/nodes/test_list_operator.py +++ b/api/tests/unit_tests/core/workflow/nodes/test_list_operator.py @@ -1,6 +1,8 @@ from unittest.mock import MagicMock import pytest + +from core.app.entities.app_invoke_entities import DIFY_RUN_CONTEXT_KEY, InvokeFrom, UserFrom from graphon.enums import WorkflowNodeExecutionStatus from graphon.file import File, FileTransferMethod, FileType from graphon.nodes.list_operator.entities import ( @@ -16,8 +18,6 @@ from graphon.nodes.list_operator.exc import InvalidKeyError from graphon.nodes.list_operator.node import ListOperatorNode, _get_file_extract_string_func from graphon.variables import ArrayFileSegment -from core.app.entities.app_invoke_entities import DIFY_RUN_CONTEXT_KEY, InvokeFrom, UserFrom - @pytest.fixture def list_operator_node(): diff --git a/api/tests/unit_tests/core/workflow/nodes/test_start_node_json_object.py b/api/tests/unit_tests/core/workflow/nodes/test_start_node_json_object.py index 833c303052..543f9878de 100644 --- a/api/tests/unit_tests/core/workflow/nodes/test_start_node_json_object.py +++ b/api/tests/unit_tests/core/workflow/nodes/test_start_node_json_object.py @@ -2,16 +2,16 @@ import json import time import pytest +from pydantic import ValidationError as PydanticValidationError + +from core.workflow.system_variables import build_system_variables +from core.workflow.variable_prefixes import CONVERSATION_VARIABLE_NODE_ID, ENVIRONMENT_VARIABLE_NODE_ID from graphon.nodes.start.entities import StartNodeData from graphon.nodes.start.start_node import StartNode from graphon.runtime import GraphRuntimeState from graphon.variables import build_segment, segment_to_variable from graphon.variables.input_entities import VariableEntity, VariableEntityType from graphon.variables.variables import Variable -from pydantic import ValidationError as PydanticValidationError - -from core.workflow.system_variables import build_system_variables -from core.workflow.variable_prefixes import CONVERSATION_VARIABLE_NODE_ID, ENVIRONMENT_VARIABLE_NODE_ID from tests.workflow_test_utils import build_test_graph_init_params, build_test_variable_pool diff --git a/api/tests/unit_tests/core/workflow/nodes/tool/test_tool_node.py b/api/tests/unit_tests/core/workflow/nodes/tool/test_tool_node.py index 1587014802..c806181340 100644 --- a/api/tests/unit_tests/core/workflow/nodes/tool/test_tool_node.py +++ b/api/tests/unit_tests/core/workflow/nodes/tool/test_tool_node.py @@ -8,14 +8,14 @@ from typing import TYPE_CHECKING, Any from unittest.mock import MagicMock import pytest + +from core.workflow.system_variables import build_system_variables from graphon.file import File, FileTransferMethod, FileType from graphon.model_runtime.entities.llm_entities import LLMUsage from graphon.node_events import StreamChunkEvent, StreamCompletedEvent from graphon.nodes.tool_runtime_entities import ToolRuntimeHandle, ToolRuntimeMessage from graphon.runtime import GraphRuntimeState, VariablePool from graphon.variables.segments import ArrayFileSegment - -from core.workflow.system_variables import build_system_variables from tests.workflow_test_utils import build_test_graph_init_params if TYPE_CHECKING: # pragma: no cover - imported for type checking only diff --git a/api/tests/unit_tests/core/workflow/nodes/tool/test_tool_node_runtime.py b/api/tests/unit_tests/core/workflow/nodes/tool/test_tool_node_runtime.py index c4dfc5a179..438af211f3 100644 --- a/api/tests/unit_tests/core/workflow/nodes/tool/test_tool_node_runtime.py +++ b/api/tests/unit_tests/core/workflow/nodes/tool/test_tool_node_runtime.py @@ -6,11 +6,6 @@ from types import SimpleNamespace from unittest.mock import MagicMock, patch import pytest -from graphon.model_runtime.entities.llm_entities import LLMUsage -from graphon.nodes.tool.entities import ToolNodeData, ToolProviderType -from graphon.nodes.tool.exc import ToolRuntimeInvocationError -from graphon.nodes.tool_runtime_entities import ToolRuntimeHandle, ToolRuntimeMessage -from graphon.runtime import VariablePool from core.callback_handler.workflow_tool_callback_handler import DifyWorkflowCallbackHandler from core.plugin.impl.exc import PluginDaemonClientSideError, PluginInvokeError @@ -22,6 +17,11 @@ from core.tools.tool_manager import ToolManager from core.tools.utils.message_transformer import ToolFileMessageTransformer from core.workflow.node_runtime import DifyToolNodeRuntime from core.workflow.system_variables import build_system_variables +from graphon.model_runtime.entities.llm_entities import LLMUsage +from graphon.nodes.tool.entities import ToolNodeData, ToolProviderType +from graphon.nodes.tool.exc import ToolRuntimeInvocationError +from graphon.nodes.tool_runtime_entities import ToolRuntimeHandle, ToolRuntimeMessage +from graphon.runtime import VariablePool from tests.workflow_test_utils import build_test_graph_init_params, build_test_variable_pool diff --git a/api/tests/unit_tests/core/workflow/nodes/trigger_plugin/test_trigger_event_node.py b/api/tests/unit_tests/core/workflow/nodes/trigger_plugin/test_trigger_event_node.py index 952e798430..c8ddc53284 100644 --- a/api/tests/unit_tests/core/workflow/nodes/trigger_plugin/test_trigger_event_node.py +++ b/api/tests/unit_tests/core/workflow/nodes/trigger_plugin/test_trigger_event_node.py @@ -1,13 +1,12 @@ from collections.abc import Mapping -from graphon.entities import GraphInitParams -from graphon.entities.graph_config import NodeConfigDict, NodeConfigDictAdapter -from graphon.enums import WorkflowNodeExecutionMetadataKey, WorkflowNodeExecutionStatus -from graphon.runtime import GraphRuntimeState - from core.trigger.constants import TRIGGER_PLUGIN_NODE_TYPE from core.workflow.nodes.trigger_plugin.trigger_event_node import TriggerEventNode from core.workflow.system_variables import build_system_variables +from graphon.entities import GraphInitParams +from graphon.entities.graph_config import NodeConfigDict, NodeConfigDictAdapter +from graphon.enums import WorkflowNodeExecutionMetadataKey, WorkflowNodeExecutionStatus +from graphon.runtime import GraphRuntimeState from tests.workflow_test_utils import build_test_graph_init_params, build_test_variable_pool diff --git a/api/tests/unit_tests/core/workflow/nodes/webhook/test_exceptions.py b/api/tests/unit_tests/core/workflow/nodes/webhook/test_exceptions.py index f1132af02b..617554ee17 100644 --- a/api/tests/unit_tests/core/workflow/nodes/webhook/test_exceptions.py +++ b/api/tests/unit_tests/core/workflow/nodes/webhook/test_exceptions.py @@ -1,5 +1,4 @@ import pytest -from graphon.entities.exc import BaseNodeError from core.workflow.nodes.trigger_webhook.exc import ( WebhookConfigError, @@ -7,6 +6,7 @@ from core.workflow.nodes.trigger_webhook.exc import ( WebhookNotFoundError, WebhookTimeoutError, ) +from graphon.entities.exc import BaseNodeError def test_webhook_node_error_inheritance(): diff --git a/api/tests/unit_tests/core/workflow/nodes/webhook/test_webhook_file_conversion.py b/api/tests/unit_tests/core/workflow/nodes/webhook/test_webhook_file_conversion.py index 8056217479..1bbc12b23f 100644 --- a/api/tests/unit_tests/core/workflow/nodes/webhook/test_webhook_file_conversion.py +++ b/api/tests/unit_tests/core/workflow/nodes/webhook/test_webhook_file_conversion.py @@ -9,10 +9,6 @@ when passing files to downstream LLM nodes. from typing import Any from unittest.mock import Mock, patch -from graphon.entities import GraphInitParams -from graphon.enums import WorkflowNodeExecutionStatus -from graphon.runtime import GraphRuntimeState, VariablePool - from core.app.entities.app_invoke_entities import DIFY_RUN_CONTEXT_KEY, InvokeFrom, UserFrom from core.workflow.nodes.trigger_webhook.entities import ( ContentType, @@ -22,6 +18,9 @@ from core.workflow.nodes.trigger_webhook.entities import ( ) from core.workflow.nodes.trigger_webhook.node import TriggerWebhookNode from core.workflow.system_variables import default_system_variables +from graphon.entities import GraphInitParams +from graphon.enums import WorkflowNodeExecutionStatus +from graphon.runtime import GraphRuntimeState, VariablePool from tests.workflow_test_utils import build_test_variable_pool diff --git a/api/tests/unit_tests/core/workflow/nodes/webhook/test_webhook_node.py b/api/tests/unit_tests/core/workflow/nodes/webhook/test_webhook_node.py index c19e28bbd5..427afa96ec 100644 --- a/api/tests/unit_tests/core/workflow/nodes/webhook/test_webhook_node.py +++ b/api/tests/unit_tests/core/workflow/nodes/webhook/test_webhook_node.py @@ -2,11 +2,6 @@ from typing import Any from unittest.mock import patch import pytest -from graphon.entities import GraphInitParams -from graphon.enums import WorkflowNodeExecutionStatus -from graphon.file import File, FileTransferMethod, FileType -from graphon.runtime import GraphRuntimeState, VariablePool -from graphon.variables import FileVariable, StringVariable from core.app.entities.app_invoke_entities import DIFY_RUN_CONTEXT_KEY, InvokeFrom, UserFrom from core.trigger.constants import TRIGGER_WEBHOOK_NODE_TYPE @@ -19,6 +14,11 @@ from core.workflow.nodes.trigger_webhook.entities import ( ) from core.workflow.nodes.trigger_webhook.node import TriggerWebhookNode from core.workflow.system_variables import default_system_variables +from graphon.entities import GraphInitParams +from graphon.enums import WorkflowNodeExecutionStatus +from graphon.file import File, FileTransferMethod, FileType +from graphon.runtime import GraphRuntimeState, VariablePool +from graphon.variables import FileVariable, StringVariable from tests.workflow_test_utils import build_test_variable_pool diff --git a/api/tests/unit_tests/core/workflow/test_human_input_compat.py b/api/tests/unit_tests/core/workflow/test_human_input_compat.py index cd41c43e4a..0623800b30 100644 --- a/api/tests/unit_tests/core/workflow/test_human_input_compat.py +++ b/api/tests/unit_tests/core/workflow/test_human_input_compat.py @@ -1,6 +1,5 @@ from types import SimpleNamespace -from graphon.enums import BuiltinNodeTypes from pydantic import BaseModel from core.workflow.human_input_compat import ( @@ -16,6 +15,7 @@ from core.workflow.human_input_compat import ( normalize_node_data_for_graph, parse_human_input_delivery_methods, ) +from graphon.enums import BuiltinNodeTypes def test_email_delivery_config_helpers_render_and_sanitize_text() -> None: diff --git a/api/tests/unit_tests/core/workflow/test_node_factory.py b/api/tests/unit_tests/core/workflow/test_node_factory.py index dfe1a47e37..424c50eb26 100644 --- a/api/tests/unit_tests/core/workflow/test_node_factory.py +++ b/api/tests/unit_tests/core/workflow/test_node_factory.py @@ -2,15 +2,15 @@ from types import SimpleNamespace from unittest.mock import MagicMock, patch, sentinel import pytest -from graphon.entities.base_node_data import BaseNodeData -from graphon.enums import BuiltinNodeTypes, NodeType -from graphon.nodes.code.entities import CodeLanguage -from graphon.variables.segments import StringSegment from core.app.entities.app_invoke_entities import DIFY_RUN_CONTEXT_KEY, DifyRunContext, InvokeFrom, UserFrom from core.workflow import node_factory from core.workflow import template_rendering as workflow_template_rendering from core.workflow.nodes.knowledge_index import KNOWLEDGE_INDEX_NODE_TYPE +from graphon.entities.base_node_data import BaseNodeData +from graphon.enums import BuiltinNodeTypes, NodeType +from graphon.nodes.code.entities import CodeLanguage +from graphon.variables.segments import StringSegment def _assert_typed_node_config(config, *, node_id: str, node_type: NodeType, version: str = "1") -> None: diff --git a/api/tests/unit_tests/core/workflow/test_node_runtime.py b/api/tests/unit_tests/core/workflow/test_node_runtime.py index 4f9c1dad59..71a2afb28a 100644 --- a/api/tests/unit_tests/core/workflow/test_node_runtime.py +++ b/api/tests/unit_tests/core/workflow/test_node_runtime.py @@ -2,10 +2,6 @@ from types import SimpleNamespace from unittest.mock import MagicMock, Mock, sentinel import pytest -from graphon.file import FileTransferMethod, FileType -from graphon.model_runtime.entities.common_entities import I18nObject -from graphon.model_runtime.entities.model_entities import AIModelEntity, FetchFrom, ModelType -from graphon.nodes.human_input.entities import HumanInputNodeData from core.app.entities.app_invoke_entities import DIFY_RUN_CONTEXT_KEY, DifyRunContext, InvokeFrom, UserFrom from core.llm_generator.output_parser.errors import OutputParserError @@ -30,6 +26,10 @@ from core.workflow.node_runtime import ( build_dify_llm_file_saver, resolve_dify_run_context, ) +from graphon.file import FileTransferMethod, FileType +from graphon.model_runtime.entities.common_entities import I18nObject +from graphon.model_runtime.entities.model_entities import AIModelEntity, FetchFrom, ModelType +from graphon.nodes.human_input.entities import HumanInputNodeData from tests.workflow_test_utils import build_test_run_context diff --git a/api/tests/unit_tests/core/workflow/test_system_variable.py b/api/tests/unit_tests/core/workflow/test_system_variable.py index 05ea3dc311..bdeab1eda8 100644 --- a/api/tests/unit_tests/core/workflow/test_system_variable.py +++ b/api/tests/unit_tests/core/workflow/test_system_variable.py @@ -1,14 +1,13 @@ from types import SimpleNamespace -from graphon.file import File, FileTransferMethod, FileType -from graphon.nodes import BuiltinNodeTypes - from core.workflow.system_variables import ( build_system_variables, default_system_variables, get_node_creation_preload_selectors, system_variables_to_mapping, ) +from graphon.file import File, FileTransferMethod, FileType +from graphon.nodes import BuiltinNodeTypes def test_build_system_variables_normalizes_workflow_execution_id(): diff --git a/api/tests/unit_tests/core/workflow/test_variable_pool.py b/api/tests/unit_tests/core/workflow/test_variable_pool.py index e7b2b2914a..dddd6eb00c 100644 --- a/api/tests/unit_tests/core/workflow/test_variable_pool.py +++ b/api/tests/unit_tests/core/workflow/test_variable_pool.py @@ -2,6 +2,15 @@ import uuid from collections import defaultdict import pytest + +from core.workflow.system_variables import build_system_variables, system_variables_to_mapping +from core.workflow.variable_pool_initializer import add_variables_to_pool +from core.workflow.variable_prefixes import ( + CONVERSATION_VARIABLE_NODE_ID, + ENVIRONMENT_VARIABLE_NODE_ID, + SYSTEM_VARIABLE_NODE_ID, +) +from factories.variable_factory import build_segment, segment_to_variable from graphon.file import File, FileTransferMethod, FileType from graphon.runtime import VariablePool from graphon.variables import FileSegment, StringSegment @@ -27,15 +36,6 @@ from graphon.variables.variables import ( Variable, ) -from core.workflow.system_variables import build_system_variables, system_variables_to_mapping -from core.workflow.variable_pool_initializer import add_variables_to_pool -from core.workflow.variable_prefixes import ( - CONVERSATION_VARIABLE_NODE_ID, - ENVIRONMENT_VARIABLE_NODE_ID, - SYSTEM_VARIABLE_NODE_ID, -) -from factories.variable_factory import build_segment, segment_to_variable - @pytest.fixture def pool(): diff --git a/api/tests/unit_tests/core/workflow/test_workflow_entry.py b/api/tests/unit_tests/core/workflow/test_workflow_entry.py index d8361d06c4..041c5cc612 100644 --- a/api/tests/unit_tests/core/workflow/test_workflow_entry.py +++ b/api/tests/unit_tests/core/workflow/test_workflow_entry.py @@ -1,12 +1,6 @@ from types import SimpleNamespace import pytest -from graphon.entities.graph_config import NodeConfigDictAdapter -from graphon.file import File, FileTransferMethod, FileType -from graphon.nodes.code.code_node import CodeNode -from graphon.nodes.code.limits import CodeNodeLimits -from graphon.runtime import VariablePool -from graphon.variables.variables import StringVariable from configs import dify_config from core.helper.code_executor.code_executor import CodeLanguage @@ -16,6 +10,12 @@ from core.workflow.variable_prefixes import ( ENVIRONMENT_VARIABLE_NODE_ID, ) from core.workflow.workflow_entry import WorkflowEntry +from graphon.entities.graph_config import NodeConfigDictAdapter +from graphon.file import File, FileTransferMethod, FileType +from graphon.nodes.code.code_node import CodeNode +from graphon.nodes.code.limits import CodeNodeLimits +from graphon.runtime import VariablePool +from graphon.variables.variables import StringVariable @pytest.fixture(autouse=True) diff --git a/api/tests/unit_tests/core/workflow/test_workflow_entry_helpers.py b/api/tests/unit_tests/core/workflow/test_workflow_entry_helpers.py index 6dcaed1143..55800ffc03 100644 --- a/api/tests/unit_tests/core/workflow/test_workflow_entry_helpers.py +++ b/api/tests/unit_tests/core/workflow/test_workflow_entry_helpers.py @@ -4,6 +4,12 @@ from types import SimpleNamespace from unittest.mock import MagicMock, patch, sentinel import pytest + +from core.app.apps.exc import GenerateTaskStoppedError +from core.app.entities.app_invoke_entities import InvokeFrom, UserFrom +from core.model_manager import ModelInstance +from core.workflow import workflow_entry +from core.workflow.system_variables import default_system_variables from graphon.entities.base_node_data import BaseNodeData from graphon.entities.graph_config import NodeConfigDictAdapter from graphon.enums import NodeType, WorkflowNodeExecutionStatus @@ -17,12 +23,6 @@ from graphon.nodes import BuiltinNodeTypes from graphon.nodes.base.node import Node from graphon.runtime import ChildGraphNotFoundError, VariablePool from graphon.variables.variables import StringVariable - -from core.app.apps.exc import GenerateTaskStoppedError -from core.app.entities.app_invoke_entities import InvokeFrom, UserFrom -from core.model_manager import ModelInstance -from core.workflow import workflow_entry -from core.workflow.system_variables import default_system_variables from tests.workflow_test_utils import build_test_graph_init_params, build_test_variable_pool diff --git a/api/tests/unit_tests/core/workflow/test_workflow_entry_redis_channel.py b/api/tests/unit_tests/core/workflow/test_workflow_entry_redis_channel.py index 4b2f98aeff..80dc8927fa 100644 --- a/api/tests/unit_tests/core/workflow/test_workflow_entry_redis_channel.py +++ b/api/tests/unit_tests/core/workflow/test_workflow_entry_redis_channel.py @@ -2,11 +2,10 @@ from unittest.mock import MagicMock, patch -from graphon.graph_engine.command_channels import RedisChannel -from graphon.runtime import GraphRuntimeState, VariablePool - from core.app.entities.app_invoke_entities import InvokeFrom, UserFrom from core.workflow.workflow_entry import WorkflowEntry +from graphon.graph_engine.command_channels import RedisChannel +from graphon.runtime import GraphRuntimeState, VariablePool class TestWorkflowEntryRedisChannel: diff --git a/api/tests/unit_tests/factories/test_build_from_mapping.py b/api/tests/unit_tests/factories/test_build_from_mapping.py index 4fe3f2cb28..511192001e 100644 --- a/api/tests/unit_tests/factories/test_build_from_mapping.py +++ b/api/tests/unit_tests/factories/test_build_from_mapping.py @@ -2,13 +2,13 @@ import uuid from unittest.mock import MagicMock, patch import pytest -from graphon.file import File, FileTransferMethod, FileType, FileUploadConfig from httpx import Response from core.app.entities.app_invoke_entities import InvokeFrom, UserFrom from core.app.file_access import DatabaseFileAccessController, FileAccessScope, bind_file_access_scope from core.workflow.file_reference import build_file_reference, parse_file_reference, resolve_file_record_id from factories.file_factory.builders import build_from_mapping as _build_from_mapping +from graphon.file import File, FileTransferMethod, FileType, FileUploadConfig from models import ToolFile, UploadFile # Test Data diff --git a/api/tests/unit_tests/factories/test_variable_factory.py b/api/tests/unit_tests/factories/test_variable_factory.py index a06c42507d..c35e80a826 100644 --- a/api/tests/unit_tests/factories/test_variable_factory.py +++ b/api/tests/unit_tests/factories/test_variable_factory.py @@ -4,6 +4,11 @@ from typing import Any from uuid import uuid4 import pytest +from hypothesis import HealthCheck, given, settings +from hypothesis import strategies as st + +from factories import variable_factory +from factories.variable_factory import TypeMismatchError, build_segment, build_segment_with_type from graphon.file import File, FileTransferMethod, FileType from graphon.variables import ( ArrayNumberVariable, @@ -31,11 +36,6 @@ from graphon.variables.segments import ( StringSegment, ) from graphon.variables.types import SegmentType -from hypothesis import HealthCheck, given, settings -from hypothesis import strategies as st - -from factories import variable_factory -from factories.variable_factory import TypeMismatchError, build_segment, build_segment_with_type def test_string_variable(): diff --git a/api/tests/unit_tests/fields/test_file_fields.py b/api/tests/unit_tests/fields/test_file_fields.py index 0e848d6ef5..9d9f626b9e 100644 --- a/api/tests/unit_tests/fields/test_file_fields.py +++ b/api/tests/unit_tests/fields/test_file_fields.py @@ -4,11 +4,11 @@ from datetime import datetime from types import SimpleNamespace import pytest -from graphon.file import File, FileTransferMethod, FileType from core.workflow.file_reference import build_file_reference from fields import conversation_fields, message_fields from fields.file_fields import FileResponse, FileWithSignedUrl, RemoteFileInfo, UploadConfig +from graphon.file import File, FileTransferMethod, FileType def test_file_response_serializes_datetime() -> None: diff --git a/api/tests/unit_tests/libs/_human_input/support.py b/api/tests/unit_tests/libs/_human_input/support.py index 13577b7ca5..e6cc23161e 100644 --- a/api/tests/unit_tests/libs/_human_input/support.py +++ b/api/tests/unit_tests/libs/_human_input/support.py @@ -6,7 +6,6 @@ from typing import Any from graphon.nodes.human_input.entities import FormInput from graphon.nodes.human_input.enums import TimeoutUnit - from libs.datetime_utils import naive_utc_now diff --git a/api/tests/unit_tests/libs/_human_input/test_form_service.py b/api/tests/unit_tests/libs/_human_input/test_form_service.py index f1ce1a2c1c..fa2c02020b 100644 --- a/api/tests/unit_tests/libs/_human_input/test_form_service.py +++ b/api/tests/unit_tests/libs/_human_input/test_form_service.py @@ -5,6 +5,7 @@ Unit tests for FormService. from datetime import timedelta import pytest + from graphon.nodes.human_input.entities import ( FormInput, UserAction, @@ -13,7 +14,6 @@ from graphon.nodes.human_input.enums import ( FormInputType, TimeoutUnit, ) - from libs.datetime_utils import naive_utc_now from .support import ( diff --git a/api/tests/unit_tests/libs/_human_input/test_models.py b/api/tests/unit_tests/libs/_human_input/test_models.py index 0babfbb315..866ee61b3e 100644 --- a/api/tests/unit_tests/libs/_human_input/test_models.py +++ b/api/tests/unit_tests/libs/_human_input/test_models.py @@ -5,6 +5,7 @@ Unit tests for human input form models. from datetime import datetime, timedelta import pytest + from graphon.nodes.human_input.entities import ( FormInput, UserAction, @@ -13,7 +14,6 @@ from graphon.nodes.human_input.enums import ( FormInputType, TimeoutUnit, ) - from libs.datetime_utils import naive_utc_now from .support import FormSubmissionData, FormSubmissionRequest, HumanInputForm diff --git a/api/tests/unit_tests/models/test_conversation_variable.py b/api/tests/unit_tests/models/test_conversation_variable.py index 86163f1554..bb3a6db1a1 100644 --- a/api/tests/unit_tests/models/test_conversation_variable.py +++ b/api/tests/unit_tests/models/test_conversation_variable.py @@ -1,8 +1,7 @@ from uuid import uuid4 -from graphon.variables import SegmentType - from factories import variable_factory +from graphon.variables import SegmentType from models import ConversationVariable diff --git a/api/tests/unit_tests/models/test_model.py b/api/tests/unit_tests/models/test_model.py index 3f6d6bfbe3..a87dd7f15a 100644 --- a/api/tests/unit_tests/models/test_model.py +++ b/api/tests/unit_tests/models/test_model.py @@ -2,9 +2,9 @@ import importlib import types import pytest -from graphon.file import FILE_MODEL_IDENTITY, FileTransferMethod from core.workflow.file_reference import build_file_reference +from graphon.file import FILE_MODEL_IDENTITY, FileTransferMethod from models.model import Conversation, Message diff --git a/api/tests/unit_tests/models/test_workflow.py b/api/tests/unit_tests/models/test_workflow.py index e7c0479757..f7bdc97eb5 100644 --- a/api/tests/unit_tests/models/test_workflow.py +++ b/api/tests/unit_tests/models/test_workflow.py @@ -3,14 +3,13 @@ import json from unittest import mock from uuid import uuid4 -from graphon.file import File, FileTransferMethod, FileType -from graphon.variables import FloatVariable, IntegerVariable, SecretVariable, StringVariable -from graphon.variables.segments import IntegerSegment, Segment - from constants import HIDDEN_VALUE from core.helper import encrypter from core.workflow.file_reference import build_file_reference from factories.variable_factory import build_segment +from graphon.file import File, FileTransferMethod, FileType +from graphon.variables import FloatVariable, IntegerVariable, SecretVariable, StringVariable +from graphon.variables.segments import IntegerSegment, Segment from models.workflow import ( Workflow, WorkflowDraftVariable, diff --git a/api/tests/unit_tests/models/test_workflow_models.py b/api/tests/unit_tests/models/test_workflow_models.py index 507e1c8c3a..eb9fef7587 100644 --- a/api/tests/unit_tests/models/test_workflow_models.py +++ b/api/tests/unit_tests/models/test_workflow_models.py @@ -13,12 +13,12 @@ from datetime import UTC, datetime from uuid import uuid4 import pytest + from graphon.enums import ( BuiltinNodeTypes, WorkflowExecutionStatus, WorkflowNodeExecutionStatus, ) - from models.enums import CreatorUserRole, WorkflowRunTriggeredFrom from models.workflow import ( Workflow, diff --git a/api/tests/unit_tests/services/dataset_service_test_helpers.py b/api/tests/unit_tests/services/dataset_service_test_helpers.py index c6972b5ef4..3349c1fd8c 100644 --- a/api/tests/unit_tests/services/dataset_service_test_helpers.py +++ b/api/tests/unit_tests/services/dataset_service_test_helpers.py @@ -11,7 +11,6 @@ from typing import Any from unittest.mock import MagicMock, Mock, create_autospec, patch import pytest -from graphon.model_runtime.entities.model_entities import ModelFeature, ModelType from werkzeug.exceptions import Forbidden, NotFound from core.errors.error import LLMBadRequestError, ProviderTokenNotInitError @@ -20,6 +19,7 @@ from core.rag.index_processor.constant.built_in_field import BuiltInField from core.rag.index_processor.constant.index_type import IndexStructureType from core.rag.retrieval.retrieval_methods import RetrievalMethod from enums.cloud_plan import CloudPlan +from graphon.model_runtime.entities.model_entities import ModelFeature, ModelType from models import Account, TenantAccountRole from models.dataset import ( ChildChunk, diff --git a/api/tests/unit_tests/services/document_service_validation.py b/api/tests/unit_tests/services/document_service_validation.py index 6903c47a24..71df8c4e20 100644 --- a/api/tests/unit_tests/services/document_service_validation.py +++ b/api/tests/unit_tests/services/document_service_validation.py @@ -109,11 +109,11 @@ This test suite follows a comprehensive testing strategy that covers: from unittest.mock import Mock, patch import pytest -from graphon.model_runtime.entities.model_entities import ModelType from core.errors.error import LLMBadRequestError, ProviderTokenNotInitError from core.rag.entities import PreProcessingRule, Rule, Segmentation from core.rag.index_processor.constant.index_type import IndexStructureType, IndexTechniqueType +from graphon.model_runtime.entities.model_entities import ModelType from models.dataset import Dataset, DatasetProcessRule, Document from services.dataset_service import DatasetService, DocumentService from services.entities.knowledge_entities.knowledge_entities import ( diff --git a/api/tests/unit_tests/services/rag_pipeline/test_rag_pipeline_dsl_service.py b/api/tests/unit_tests/services/rag_pipeline/test_rag_pipeline_dsl_service.py index 434a99b5ae..337659b15f 100644 --- a/api/tests/unit_tests/services/rag_pipeline/test_rag_pipeline_dsl_service.py +++ b/api/tests/unit_tests/services/rag_pipeline/test_rag_pipeline_dsl_service.py @@ -4,10 +4,10 @@ from unittest.mock import MagicMock, Mock import pytest import yaml -from graphon.enums import BuiltinNodeTypes from sqlalchemy.orm import Session from core.workflow.nodes.knowledge_index import KNOWLEDGE_INDEX_NODE_TYPE +from graphon.enums import BuiltinNodeTypes from services.entities.knowledge_entities.rag_pipeline_entities import IconInfo, RagPipelineDatasetCreateEntity from services.rag_pipeline.rag_pipeline_dsl_service import ( ImportStatus, diff --git a/api/tests/unit_tests/services/rag_pipeline/test_rag_pipeline_service.py b/api/tests/unit_tests/services/rag_pipeline/test_rag_pipeline_service.py index 941a665308..327281d07f 100644 --- a/api/tests/unit_tests/services/rag_pipeline/test_rag_pipeline_service.py +++ b/api/tests/unit_tests/services/rag_pipeline/test_rag_pipeline_service.py @@ -787,7 +787,6 @@ def test_retry_error_document_success(mocker, rag_pipeline_service) -> None: def test_set_datasource_variables_success(mocker, rag_pipeline_service) -> None: from graphon.entities.workflow_node_execution import WorkflowNodeExecution - from models.dataset import Pipeline # 1. Setup mocks @@ -1483,12 +1482,11 @@ def test_handle_node_run_result_raises_when_no_terminal_event(mocker, rag_pipeli def test_handle_node_run_result_marks_document_error_for_published_invoke(mocker, rag_pipeline_service) -> None: + from core.app.entities.app_invoke_entities import InvokeFrom from graphon.enums import WorkflowNodeExecutionStatus from graphon.graph_events import NodeRunFailedEvent from graphon.node_events.base import NodeRunResult - from core.app.entities.app_invoke_entities import InvokeFrom - class FakeVariablePool: def __init__(self): self._values = { diff --git a/api/tests/unit_tests/services/test_datasource_provider_service.py b/api/tests/unit_tests/services/test_datasource_provider_service.py index c00a4938bb..d304e0ec44 100644 --- a/api/tests/unit_tests/services/test_datasource_provider_service.py +++ b/api/tests/unit_tests/services/test_datasource_provider_service.py @@ -2,10 +2,10 @@ from unittest.mock import MagicMock, patch import httpx import pytest -from graphon.model_runtime.entities.provider_entities import FormType from sqlalchemy.orm import Session from core.plugin.entities.plugin_daemon import CredentialType +from graphon.model_runtime.entities.provider_entities import FormType from models.account import Account from models.model import EndUser from models.oauth import DatasourceProvider diff --git a/api/tests/unit_tests/services/test_human_input_service.py b/api/tests/unit_tests/services/test_human_input_service.py index 9be475d043..55af564821 100644 --- a/api/tests/unit_tests/services/test_human_input_service.py +++ b/api/tests/unit_tests/services/test_human_input_service.py @@ -3,18 +3,18 @@ from datetime import datetime, timedelta from unittest.mock import MagicMock import pytest -from graphon.nodes.human_input.entities import ( - FormDefinition, - FormInput, - UserAction, -) -from graphon.nodes.human_input.enums import FormInputType, HumanInputFormKind, HumanInputFormStatus import services.human_input_service as human_input_service_module from core.repositories.human_input_repository import ( HumanInputFormRecord, HumanInputFormSubmissionRepository, ) +from graphon.nodes.human_input.entities import ( + FormDefinition, + FormInput, + UserAction, +) +from graphon.nodes.human_input.enums import FormInputType, HumanInputFormKind, HumanInputFormStatus from libs.datetime_utils import naive_utc_now from models.human_input import RecipientType from services.human_input_service import ( diff --git a/api/tests/unit_tests/services/test_model_load_balancing_service.py b/api/tests/unit_tests/services/test_model_load_balancing_service.py index bea288fb9b..3119af40a2 100644 --- a/api/tests/unit_tests/services/test_model_load_balancing_service.py +++ b/api/tests/unit_tests/services/test_model_load_balancing_service.py @@ -6,6 +6,9 @@ from typing import Any, cast from unittest.mock import MagicMock import pytest +from pytest_mock import MockerFixture + +from constants import HIDDEN_VALUE from graphon.model_runtime.entities.common_entities import I18nObject from graphon.model_runtime.entities.model_entities import ModelType from graphon.model_runtime.entities.provider_entities import ( @@ -15,9 +18,6 @@ from graphon.model_runtime.entities.provider_entities import ( ModelCredentialSchema, ProviderCredentialSchema, ) -from pytest_mock import MockerFixture - -from constants import HIDDEN_VALUE from models.provider import LoadBalancingModelConfig from services.model_load_balancing_service import ModelLoadBalancingService diff --git a/api/tests/unit_tests/services/test_model_provider_service.py b/api/tests/unit_tests/services/test_model_provider_service.py index 756d3e9b59..28d459eac9 100644 --- a/api/tests/unit_tests/services/test_model_provider_service.py +++ b/api/tests/unit_tests/services/test_model_provider_service.py @@ -3,10 +3,10 @@ from typing import Any from unittest.mock import MagicMock import pytest -from graphon.model_runtime.entities.common_entities import I18nObject -from graphon.model_runtime.entities.model_entities import FetchFrom, ModelType, ParameterRule, ParameterType from core.entities.model_entities import ModelStatus +from graphon.model_runtime.entities.common_entities import I18nObject +from graphon.model_runtime.entities.model_entities import FetchFrom, ModelType, ParameterRule, ParameterType from models.provider import ProviderType from services import model_provider_service as service_module from services.errors.app_model_config import ProviderNotFoundError diff --git a/api/tests/unit_tests/services/test_model_provider_service_sanitization.py b/api/tests/unit_tests/services/test_model_provider_service_sanitization.py index 1bd979b9ec..97f3bd6f01 100644 --- a/api/tests/unit_tests/services/test_model_provider_service_sanitization.py +++ b/api/tests/unit_tests/services/test_model_provider_service_sanitization.py @@ -1,11 +1,11 @@ import types import pytest + +from core.entities.provider_entities import CredentialConfiguration, CustomModelConfiguration from graphon.model_runtime.entities.common_entities import I18nObject from graphon.model_runtime.entities.model_entities import ModelType from graphon.model_runtime.entities.provider_entities import ConfigurateMethod - -from core.entities.provider_entities import CredentialConfiguration, CustomModelConfiguration from models.provider import ProviderType from services.model_provider_service import ModelProviderService diff --git a/api/tests/unit_tests/services/test_variable_truncator.py b/api/tests/unit_tests/services/test_variable_truncator.py index 98ec6fb77c..4b864dd221 100644 --- a/api/tests/unit_tests/services/test_variable_truncator.py +++ b/api/tests/unit_tests/services/test_variable_truncator.py @@ -16,6 +16,7 @@ from typing import Any from uuid import uuid4 import pytest + from graphon.file import File, FileTransferMethod, FileType from graphon.variables.segments import ( ArrayFileSegment, @@ -28,7 +29,6 @@ from graphon.variables.segments import ( ObjectSegment, StringSegment, ) - from services.variable_truncator import ( DummyVariableTruncator, MaxDepthExceededError, diff --git a/api/tests/unit_tests/services/test_variable_truncator_additional.py b/api/tests/unit_tests/services/test_variable_truncator_additional.py index 4f705cf5f4..e9427c4ab3 100644 --- a/api/tests/unit_tests/services/test_variable_truncator_additional.py +++ b/api/tests/unit_tests/services/test_variable_truncator_additional.py @@ -2,10 +2,10 @@ from collections.abc import Mapping from typing import Any import pytest + from graphon.nodes.variable_assigner.common.helpers import UpdatedVariable from graphon.variables.segments import IntegerSegment, ObjectSegment, StringSegment from graphon.variables.types import SegmentType - from services import variable_truncator as truncator_module from services.variable_truncator import BaseTruncator, TruncationResult, VariableTruncator diff --git a/api/tests/unit_tests/services/test_webhook_service_additional.py b/api/tests/unit_tests/services/test_webhook_service_additional.py index 8a7a463d33..776cb5dc3f 100644 --- a/api/tests/unit_tests/services/test_webhook_service_additional.py +++ b/api/tests/unit_tests/services/test_webhook_service_additional.py @@ -4,7 +4,6 @@ from unittest.mock import MagicMock import pytest from flask import Flask -from graphon.variables.types import SegmentType from werkzeug.exceptions import RequestEntityTooLarge from core.workflow.nodes.trigger_webhook.entities import ( @@ -13,6 +12,7 @@ from core.workflow.nodes.trigger_webhook.entities import ( WebhookData, WebhookParameter, ) +from graphon.variables.types import SegmentType from services.trigger import webhook_service as service_module from services.trigger.webhook_service import WebhookService diff --git a/api/tests/unit_tests/services/test_workflow_run_service_pause.py b/api/tests/unit_tests/services/test_workflow_run_service_pause.py index a62c9f4555..239cc83518 100644 --- a/api/tests/unit_tests/services/test_workflow_run_service_pause.py +++ b/api/tests/unit_tests/services/test_workflow_run_service_pause.py @@ -13,10 +13,10 @@ from datetime import datetime from unittest.mock import MagicMock, create_autospec, patch import pytest -from graphon.enums import WorkflowExecutionStatus from sqlalchemy import Engine from sqlalchemy.orm import Session, sessionmaker +from graphon.enums import WorkflowExecutionStatus from models.workflow import WorkflowPause from repositories.api_workflow_run_repository import APIWorkflowRunRepository from repositories.sqlalchemy_api_workflow_run_repository import _PrivateWorkflowPauseEntity diff --git a/api/tests/unit_tests/services/test_workflow_service.py b/api/tests/unit_tests/services/test_workflow_service.py index 351f6ffb5f..287f5f2e5e 100644 --- a/api/tests/unit_tests/services/test_workflow_service.py +++ b/api/tests/unit_tests/services/test_workflow_service.py @@ -15,6 +15,7 @@ from typing import Any, cast from unittest.mock import ANY, MagicMock, Mock, patch import pytest + from graphon.entities import WorkflowNodeExecution from graphon.enums import ( BuiltinNodeTypes, @@ -28,7 +29,6 @@ from graphon.model_runtime.entities.model_entities import ModelType from graphon.node_events import NodeRunResult from graphon.nodes.http_request import HTTP_REQUEST_CONFIG_FILTER_KEY, HttpRequestNode, HttpRequestNodeConfig from graphon.variables.input_entities import VariableEntityType - from libs.datetime_utils import naive_utc_now from models.human_input import RecipientType from models.model import App, AppMode diff --git a/api/tests/unit_tests/services/workflow/test_draft_var_loader_simple.py b/api/tests/unit_tests/services/workflow/test_draft_var_loader_simple.py index 8525672da8..60beec7964 100644 --- a/api/tests/unit_tests/services/workflow/test_draft_var_loader_simple.py +++ b/api/tests/unit_tests/services/workflow/test_draft_var_loader_simple.py @@ -4,12 +4,12 @@ import json from unittest.mock import Mock, patch import pytest -from graphon.file import File, FileTransferMethod, FileType -from graphon.variables.segments import ObjectSegment, StringSegment -from graphon.variables.types import SegmentType from sqlalchemy import Engine from core.workflow.file_reference import build_file_reference +from graphon.file import File, FileTransferMethod, FileType +from graphon.variables.segments import ObjectSegment, StringSegment +from graphon.variables.types import SegmentType from models.model import UploadFile from models.workflow import WorkflowDraftVariable, WorkflowDraftVariableFile from services.workflow_draft_variable_service import DraftVarLoader diff --git a/api/tests/unit_tests/services/workflow/test_workflow_draft_variable_service.py b/api/tests/unit_tests/services/workflow/test_workflow_draft_variable_service.py index e7e72793a3..f6bdb6a60e 100644 --- a/api/tests/unit_tests/services/workflow/test_workflow_draft_variable_service.py +++ b/api/tests/unit_tests/services/workflow/test_workflow_draft_variable_service.py @@ -4,10 +4,6 @@ import uuid from unittest.mock import MagicMock, Mock, patch import pytest -from graphon.enums import BuiltinNodeTypes -from graphon.file import File, FileTransferMethod, FileType -from graphon.variables.segments import StringSegment -from graphon.variables.types import SegmentType from sqlalchemy import Engine from sqlalchemy.orm import Session @@ -17,6 +13,10 @@ from core.workflow.variable_prefixes import ( ENVIRONMENT_VARIABLE_NODE_ID, SYSTEM_VARIABLE_NODE_ID, ) +from graphon.enums import BuiltinNodeTypes +from graphon.file import File, FileTransferMethod, FileType +from graphon.variables.segments import StringSegment +from graphon.variables.types import SegmentType from libs.uuid_utils import uuidv7 from models.account import Account from models.enums import DraftVariableType diff --git a/api/tests/unit_tests/services/workflow/test_workflow_event_snapshot_service.py b/api/tests/unit_tests/services/workflow/test_workflow_event_snapshot_service.py index 4146fd312b..d570dce107 100644 --- a/api/tests/unit_tests/services/workflow/test_workflow_event_snapshot_service.py +++ b/api/tests/unit_tests/services/workflow/test_workflow_event_snapshot_service.py @@ -6,13 +6,13 @@ from datetime import UTC, datetime from threading import Event import pytest -from graphon.entities.pause_reason import HumanInputRequired -from graphon.enums import WorkflowExecutionStatus, WorkflowNodeExecutionStatus -from graphon.runtime import GraphRuntimeState, VariablePool from core.app.app_config.entities import WorkflowUIBasedAppConfig from core.app.entities.app_invoke_entities import InvokeFrom, WorkflowAppGenerateEntity from core.app.layers.pause_state_persist_layer import WorkflowResumptionContext, _WorkflowGenerateEntityWrapper +from graphon.entities.pause_reason import HumanInputRequired +from graphon.enums import WorkflowExecutionStatus, WorkflowNodeExecutionStatus +from graphon.runtime import GraphRuntimeState, VariablePool from models.enums import CreatorUserRole from models.model import AppMode from models.workflow import WorkflowRun diff --git a/api/tests/unit_tests/services/workflow/test_workflow_event_snapshot_service_additional.py b/api/tests/unit_tests/services/workflow/test_workflow_event_snapshot_service_additional.py index 5e96eb4518..d2634d7d7b 100644 --- a/api/tests/unit_tests/services/workflow/test_workflow_event_snapshot_service_additional.py +++ b/api/tests/unit_tests/services/workflow/test_workflow_event_snapshot_service_additional.py @@ -10,14 +10,14 @@ from typing import Any, cast from unittest.mock import MagicMock import pytest -from graphon.enums import WorkflowExecutionStatus -from graphon.runtime import GraphRuntimeState, VariablePool from sqlalchemy.orm import Session, sessionmaker from core.app.app_config.entities import WorkflowUIBasedAppConfig from core.app.entities.app_invoke_entities import InvokeFrom, WorkflowAppGenerateEntity from core.app.entities.task_entities import StreamEvent from core.app.layers.pause_state_persist_layer import WorkflowResumptionContext, _WorkflowGenerateEntityWrapper +from graphon.enums import WorkflowExecutionStatus +from graphon.runtime import GraphRuntimeState, VariablePool from models.enums import CreatorUserRole from models.model import AppMode from models.workflow import WorkflowRun diff --git a/api/tests/unit_tests/services/workflow/test_workflow_human_input_delivery.py b/api/tests/unit_tests/services/workflow/test_workflow_human_input_delivery.py index 98d057e41f..d7192994b2 100644 --- a/api/tests/unit_tests/services/workflow/test_workflow_human_input_delivery.py +++ b/api/tests/unit_tests/services/workflow/test_workflow_human_input_delivery.py @@ -3,9 +3,6 @@ from types import SimpleNamespace from unittest.mock import MagicMock import pytest -from graphon.entities.graph_config import NodeConfigDict, NodeConfigDictAdapter -from graphon.enums import BuiltinNodeTypes -from graphon.nodes.human_input.entities import HumanInputNodeData from sqlalchemy.orm import sessionmaker from core.workflow.human_input_compat import ( @@ -15,6 +12,9 @@ from core.workflow.human_input_compat import ( ExternalRecipient, MemberRecipient, ) +from graphon.entities.graph_config import NodeConfigDict, NodeConfigDictAdapter +from graphon.enums import BuiltinNodeTypes +from graphon.nodes.human_input.entities import HumanInputNodeData from services import workflow_service as workflow_service_module from services.workflow_service import WorkflowService diff --git a/api/tests/unit_tests/tasks/test_human_input_timeout_tasks.py b/api/tests/unit_tests/tasks/test_human_input_timeout_tasks.py index 7119217e94..591da56f49 100644 --- a/api/tests/unit_tests/tasks/test_human_input_timeout_tasks.py +++ b/api/tests/unit_tests/tasks/test_human_input_timeout_tasks.py @@ -5,8 +5,8 @@ from types import SimpleNamespace from typing import Any import pytest -from graphon.nodes.human_input.enums import HumanInputFormKind, HumanInputFormStatus +from graphon.nodes.human_input.enums import HumanInputFormKind, HumanInputFormStatus from tasks import human_input_timeout_tasks as task_module diff --git a/api/tests/unit_tests/tools/test_mcp_tool.py b/api/tests/unit_tests/tools/test_mcp_tool.py index 544e89fcee..689b973097 100644 --- a/api/tests/unit_tests/tools/test_mcp_tool.py +++ b/api/tests/unit_tests/tools/test_mcp_tool.py @@ -4,7 +4,6 @@ from typing import Any from unittest.mock import Mock, patch import pytest -from graphon.model_runtime.entities.llm_entities import LLMUsage from core.mcp.types import ( AudioContent, @@ -19,6 +18,7 @@ from core.tools.__base.tool_runtime import ToolRuntime from core.tools.entities.common_entities import I18nObject from core.tools.entities.tool_entities import ToolEntity, ToolIdentity, ToolInvokeMessage from core.tools.mcp_tool.tool import MCPTool +from graphon.model_runtime.entities.llm_entities import LLMUsage def _make_mcp_tool(output_schema: dict[str, Any] | None = None) -> MCPTool: diff --git a/api/tests/unit_tests/utils/structured_output_parser/test_structured_output_parser.py b/api/tests/unit_tests/utils/structured_output_parser/test_structured_output_parser.py index ffa6833524..c166a946d9 100644 --- a/api/tests/unit_tests/utils/structured_output_parser/test_structured_output_parser.py +++ b/api/tests/unit_tests/utils/structured_output_parser/test_structured_output_parser.py @@ -2,6 +2,9 @@ from decimal import Decimal from unittest.mock import MagicMock, patch import pytest + +from core.llm_generator.output_parser.errors import OutputParserError +from core.llm_generator.output_parser.structured_output import invoke_llm_with_structured_output from graphon.model_runtime.entities.llm_entities import ( LLMResult, LLMResultChunk, @@ -18,9 +21,6 @@ from graphon.model_runtime.entities.message_entities import ( ) from graphon.model_runtime.entities.model_entities import AIModelEntity, ModelType -from core.llm_generator.output_parser.errors import OutputParserError -from core.llm_generator.output_parser.structured_output import invoke_llm_with_structured_output - def create_mock_usage(prompt_tokens: int = 10, completion_tokens: int = 5) -> LLMUsage: """Create a mock LLMUsage with all required fields""" diff --git a/api/tests/workflow_test_utils.py b/api/tests/workflow_test_utils.py index d33ac2c710..1415bb1d52 100644 --- a/api/tests/workflow_test_utils.py +++ b/api/tests/workflow_test_utils.py @@ -1,13 +1,12 @@ from collections.abc import Mapping from typing import Any +from core.app.entities.app_invoke_entities import InvokeFrom, UserFrom, build_dify_run_context +from core.workflow.variable_pool_initializer import add_node_inputs_to_pool, add_variables_to_pool from graphon.entities import GraphInitParams from graphon.runtime import VariablePool from graphon.variables.variables import Variable -from core.app.entities.app_invoke_entities import InvokeFrom, UserFrom, build_dify_run_context -from core.workflow.variable_pool_initializer import add_node_inputs_to_pool, add_variables_to_pool - def build_test_run_context( *, From 3445469385527ac67242f44c954d712ab3589707 Mon Sep 17 00:00:00 2001 From: yyh <92089059+lyzno1@users.noreply.github.com> Date: Thu, 16 Apr 2026 17:53:16 +0800 Subject: [PATCH 07/38] perf(web): optimize first-screen rendering performance (#35313) --- web/app/(commonLayout)/layout.tsx | 6 +++++- web/app/components/devtools/react-scan/loader.tsx | 2 +- web/app/components/splash.tsx | 8 +++----- 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/web/app/(commonLayout)/layout.tsx b/web/app/(commonLayout)/layout.tsx index 5ac39f1e39..49e9431940 100644 --- a/web/app/(commonLayout)/layout.tsx +++ b/web/app/(commonLayout)/layout.tsx @@ -5,7 +5,6 @@ import InSiteMessageNotification from '@/app/components/app/in-site-message/noti import AmplitudeProvider from '@/app/components/base/amplitude' import GA, { GaType } from '@/app/components/base/ga' import Zendesk from '@/app/components/base/zendesk' -import GotoAnything from '@/app/components/goto-anything' import Header from '@/app/components/header' import HeaderWrapper from '@/app/components/header/header-wrapper' import ReadmePanel from '@/app/components/plugins/readme-panel' @@ -13,10 +12,15 @@ import { AppContextProvider } from '@/context/app-context-provider' import { EventEmitterContextProvider } from '@/context/event-emitter-provider' import { ModalContextProvider } from '@/context/modal-context-provider' import { ProviderContextProvider } from '@/context/provider-context-provider' +import dynamic from '@/next/dynamic' import PartnerStack from '../components/billing/partner-stack' import Splash from '../components/splash' import RoleRouteGuard from './role-route-guard' +const GotoAnything = dynamic(() => import('@/app/components/goto-anything'), { + ssr: false, +}) + const Layout = ({ children }: { children: ReactNode }) => { return ( <> diff --git a/web/app/components/devtools/react-scan/loader.tsx b/web/app/components/devtools/react-scan/loader.tsx index 8e933c2b24..bd310f292f 100644 --- a/web/app/components/devtools/react-scan/loader.tsx +++ b/web/app/components/devtools/react-scan/loader.tsx @@ -9,7 +9,7 @@ export function ReactScanLoader() { .txt' }) render(, { wrapper: createWrapper() }) - expect(screen.getByText('.txt')).toBeInTheDocument() + expect(screen.getByText('.txt'))!.toBeInTheDocument() }) it('should memoize the component', () => { @@ -343,7 +344,7 @@ describe('DocumentTableRow', () => { const { rerender } = render(, { wrapper }) rerender() - expect(screen.getByRole('row')).toBeInTheDocument() + expect(screen.getByRole('row'))!.toBeInTheDocument() }) }) }) diff --git a/web/app/components/datasets/documents/components/document-list/components/document-source-icon.tsx b/web/app/components/datasets/documents/components/document-list/components/document-source-icon.tsx index 5461f34921..0d51837cf2 100644 --- a/web/app/components/datasets/documents/components/document-list/components/document-source-icon.tsx +++ b/web/app/components/datasets/documents/components/document-list/components/document-source-icon.tsx @@ -39,7 +39,7 @@ const getFileExtension = (fileName: string): string => { const parts = fileName.split('.') if (parts.length <= 1 || (parts[0] === '' && parts.length === 2)) return '' - return parts[parts.length - 1].toLowerCase() + return parts[parts.length - 1]!.toLowerCase() } const DocumentSourceIcon: FC = React.memo(({ diff --git a/web/app/components/datasets/documents/create-from-pipeline/__tests__/index.spec.tsx b/web/app/components/datasets/documents/create-from-pipeline/__tests__/index.spec.tsx index 8a2e251770..7daff43a8b 100644 --- a/web/app/components/datasets/documents/create-from-pipeline/__tests__/index.spec.tsx +++ b/web/app/components/datasets/documents/create-from-pipeline/__tests__/index.spec.tsx @@ -569,7 +569,7 @@ describe('StepOneContent', () => { it('should render VectorSpaceFull when isShowVectorSpaceFull is true', () => { render() - expect(screen.getByTestId('vector-space-full')).toBeInTheDocument() + expect(screen.getByTestId('vector-space-full'))!.toBeInTheDocument() }) it('should not render VectorSpaceFull when isShowVectorSpaceFull is false', () => { @@ -587,7 +587,7 @@ describe('StepOneContent', () => { localFileListLength={2} />, ) - expect(screen.getByTestId('upgrade-card')).toBeInTheDocument() + expect(screen.getByTestId('upgrade-card'))!.toBeInTheDocument() }) it('should not render UpgradeCard when supportBatchUpload is true', () => { @@ -618,7 +618,7 @@ describe('StepOneContent', () => { render() const nextButton = screen.getByRole('button', { name: /datasetCreation.stepOne.button/i }) - expect(nextButton).toBeDisabled() + expect(nextButton)!.toBeDisabled() }) }) @@ -664,17 +664,17 @@ describe('StepTwoContent', () => { it('should render ProcessDocuments component', () => { render() - expect(screen.getByTestId('process-documents')).toBeInTheDocument() + expect(screen.getByTestId('process-documents'))!.toBeInTheDocument() }) it('should pass dataSourceNodeId to ProcessDocuments', () => { render() - expect(screen.getByTestId('datasource-node-id')).toHaveTextContent('custom-node') + expect(screen.getByTestId('datasource-node-id'))!.toHaveTextContent('custom-node') }) it('should pass isRunning to ProcessDocuments', () => { render() - expect(screen.getByTestId('is-running')).toHaveTextContent('true') + expect(screen.getByTestId('is-running'))!.toHaveTextContent('true') }) it('should call onProcess when process button is clicked', () => { @@ -709,18 +709,18 @@ describe('StepThreeContent', () => { it('should render Processing component', () => { render() - expect(screen.getByTestId('processing')).toBeInTheDocument() + expect(screen.getByTestId('processing'))!.toBeInTheDocument() }) it('should pass batchId to Processing', () => { render() - expect(screen.getByTestId('batch-id')).toHaveTextContent('batch-123') + expect(screen.getByTestId('batch-id'))!.toHaveTextContent('batch-123') }) it('should pass documents count to Processing', () => { const documents = [{ id: '1' }, { id: '2' }] render() - expect(screen.getByTestId('documents-count')).toHaveTextContent('2') + expect(screen.getByTestId('documents-count'))!.toHaveTextContent('2') }) }) @@ -787,8 +787,8 @@ describe('StepOnePreview', () => { currentLocalFile={createMockFile()} />, ) - expect(screen.getByTestId('file-preview')).toBeInTheDocument() - expect(screen.getByTestId('file-name')).toHaveTextContent('test.txt') + expect(screen.getByTestId('file-preview'))!.toBeInTheDocument() + expect(screen.getByTestId('file-name'))!.toHaveTextContent('test.txt') }) it('should render OnlineDocumentPreview when currentDocument is set', () => { @@ -799,7 +799,7 @@ describe('StepOnePreview', () => { currentDocument={createMockNotionPage()} />, ) - expect(screen.getByTestId('online-document-preview')).toBeInTheDocument() + expect(screen.getByTestId('online-document-preview'))!.toBeInTheDocument() }) it('should render WebsitePreview when currentWebsite is set', () => { @@ -809,7 +809,7 @@ describe('StepOnePreview', () => { currentWebsite={createMockCrawlResult()} />, ) - expect(screen.getByTestId('web-preview')).toBeInTheDocument() + expect(screen.getByTestId('web-preview'))!.toBeInTheDocument() }) it('should call hidePreviewLocalFile when hide button is clicked', () => { @@ -868,22 +868,22 @@ describe('StepTwoPreview', () => { it('should render ChunkPreview component', () => { render() - expect(screen.getByTestId('chunk-preview')).toBeInTheDocument() + expect(screen.getByTestId('chunk-preview'))!.toBeInTheDocument() }) it('should pass datasourceType to ChunkPreview', () => { render() - expect(screen.getByTestId('datasource-type')).toHaveTextContent(DatasourceType.onlineDocument) + expect(screen.getByTestId('datasource-type'))!.toHaveTextContent(DatasourceType.onlineDocument) }) it('should pass isIdle to ChunkPreview', () => { render() - expect(screen.getByTestId('is-idle')).toHaveTextContent('false') + expect(screen.getByTestId('is-idle'))!.toHaveTextContent('false') }) it('should pass isPendingPreview to ChunkPreview', () => { render() - expect(screen.getByTestId('is-pending')).toHaveTextContent('true') + expect(screen.getByTestId('is-pending'))!.toHaveTextContent('true') }) it('should call onPreview when preview button is clicked', () => { @@ -1092,7 +1092,7 @@ describe('Store Hooks', () => { mockStoreState.selectedFileIds = ['file-1'] const { result } = renderHook(() => useOnlineDrive()) expect(result.current.selectedOnlineDriveFileList).toHaveLength(1) - expect(result.current.selectedOnlineDriveFileList[0].id).toBe('file-1') + expect(result.current.selectedOnlineDriveFileList[0]!.id).toBe('file-1') }) }) }) @@ -1166,8 +1166,8 @@ describe('useDatasourceOptions', () => { const { result } = renderHook(() => useDatasourceOptions(mockNodes)) expect(result.current).toHaveLength(1) - expect(result.current[0].label).toBe('Local File Source') - expect(result.current[0].value).toBe('node-1') + expect(result.current[0]!.label).toBe('Local File Source') + expect(result.current[0]!.value).toBe('node-1') }) it('should return multiple options for multiple data source nodes', () => { @@ -1616,7 +1616,7 @@ describe('StepOneContent - All Datasource Types', () => { datasourceType={DatasourceType.onlineDocument} />, ) - expect(screen.getByTestId('online-documents-component')).toBeInTheDocument() + expect(screen.getByTestId('online-documents-component'))!.toBeInTheDocument() }) it('should render WebsiteCrawl when datasourceType is websiteCrawl', () => { @@ -1632,7 +1632,7 @@ describe('StepOneContent - All Datasource Types', () => { datasourceType={DatasourceType.websiteCrawl} />, ) - expect(screen.getByTestId('website-crawl-component')).toBeInTheDocument() + expect(screen.getByTestId('website-crawl-component'))!.toBeInTheDocument() }) it('should render OnlineDrive when datasourceType is onlineDrive', () => { @@ -1648,7 +1648,7 @@ describe('StepOneContent - All Datasource Types', () => { datasourceType={DatasourceType.onlineDrive} />, ) - expect(screen.getByTestId('online-drive-component')).toBeInTheDocument() + expect(screen.getByTestId('online-drive-component'))!.toBeInTheDocument() }) it('should render LocalFile when datasourceType is localFile', () => { @@ -1659,7 +1659,7 @@ describe('StepOneContent - All Datasource Types', () => { datasourceType={DatasourceType.localFile} />, ) - expect(screen.getByTestId('local-file-component')).toBeInTheDocument() + expect(screen.getByTestId('local-file-component'))!.toBeInTheDocument() }) }) @@ -1690,7 +1690,8 @@ describe('StepTwoPreview - File List Mapping', () => { ) // ChunkPreview should be rendered - expect(screen.getByTestId('chunk-preview')).toBeInTheDocument() + // ChunkPreview should be rendered + expect(screen.getByTestId('chunk-preview'))!.toBeInTheDocument() }) }) diff --git a/web/app/components/datasets/documents/create-from-pipeline/__tests__/step-indicator.spec.tsx b/web/app/components/datasets/documents/create-from-pipeline/__tests__/step-indicator.spec.tsx index 7103dced26..7bffe3577e 100644 --- a/web/app/components/datasets/documents/create-from-pipeline/__tests__/step-indicator.spec.tsx +++ b/web/app/components/datasets/documents/create-from-pipeline/__tests__/step-indicator.spec.tsx @@ -19,14 +19,14 @@ describe('StepIndicator', () => { const { container } = render() const dots = container.querySelectorAll('.rounded-lg') // Second step (index 1) should be active - expect(dots[1].className).toContain('bg-state-accent-solid') - expect(dots[1].className).toContain('w-2') + expect(dots[1]!.className).toContain('bg-state-accent-solid') + expect(dots[1]!.className).toContain('w-2') }) it('should not apply active style to non-current steps', () => { const { container } = render() const dots = container.querySelectorAll('.rounded-lg') - expect(dots[1].className).toContain('bg-divider-solid') - expect(dots[2].className).toContain('bg-divider-solid') + expect(dots[1]!.className).toContain('bg-divider-solid') + expect(dots[2]!.className).toContain('bg-divider-solid') }) }) diff --git a/web/app/components/datasets/documents/create-from-pipeline/data-source-options/__tests__/index.spec.tsx b/web/app/components/datasets/documents/create-from-pipeline/data-source-options/__tests__/index.spec.tsx index 0ac2dfce20..78542ad522 100644 --- a/web/app/components/datasets/documents/create-from-pipeline/data-source-options/__tests__/index.spec.tsx +++ b/web/app/components/datasets/documents/create-from-pipeline/data-source-options/__tests__/index.spec.tsx @@ -129,7 +129,7 @@ describe('DatasourceIcon', () => { it('should render without crashing', () => { const { container } = render() - expect(container.firstChild).toBeInTheDocument() + expect(container.firstChild)!.toBeInTheDocument() }) it('should render icon with background image', () => { @@ -138,15 +138,16 @@ describe('DatasourceIcon', () => { const { container } = render() const iconDiv = container.querySelector('[style*="background-image"]') - expect(iconDiv).toHaveStyle({ backgroundImage: `url(${iconUrl})` }) + expect(iconDiv)!.toHaveStyle({ backgroundImage: `url(${iconUrl})` }) }) it('should render with default size (sm)', () => { const { container } = render() // Assert - Default size is 'sm' which maps to 'w-5 h-5' - expect(container.firstChild).toHaveClass('w-5') - expect(container.firstChild).toHaveClass('h-5') + // Assert - Default size is 'sm' which maps to 'w-5 h-5' + expect(container.firstChild)!.toHaveClass('w-5') + expect(container.firstChild)!.toHaveClass('h-5') }) }) @@ -157,9 +158,9 @@ describe('DatasourceIcon', () => { , ) - expect(container.firstChild).toHaveClass('w-4') - expect(container.firstChild).toHaveClass('h-4') - expect(container.firstChild).toHaveClass('rounded-[5px]') + expect(container.firstChild)!.toHaveClass('w-4') + expect(container.firstChild)!.toHaveClass('h-4') + expect(container.firstChild)!.toHaveClass('rounded-[5px]') }) it('should render with sm size', () => { @@ -167,9 +168,9 @@ describe('DatasourceIcon', () => { , ) - expect(container.firstChild).toHaveClass('w-5') - expect(container.firstChild).toHaveClass('h-5') - expect(container.firstChild).toHaveClass('rounded-md') + expect(container.firstChild)!.toHaveClass('w-5') + expect(container.firstChild)!.toHaveClass('h-5') + expect(container.firstChild)!.toHaveClass('rounded-md') }) it('should render with md size', () => { @@ -177,9 +178,9 @@ describe('DatasourceIcon', () => { , ) - expect(container.firstChild).toHaveClass('w-6') - expect(container.firstChild).toHaveClass('h-6') - expect(container.firstChild).toHaveClass('rounded-lg') + expect(container.firstChild)!.toHaveClass('w-6') + expect(container.firstChild)!.toHaveClass('h-6') + expect(container.firstChild)!.toHaveClass('rounded-lg') }) }) @@ -189,7 +190,7 @@ describe('DatasourceIcon', () => { , ) - expect(container.firstChild).toHaveClass('custom-class') + expect(container.firstChild)!.toHaveClass('custom-class') }) it('should merge custom className with default classes', () => { @@ -197,9 +198,9 @@ describe('DatasourceIcon', () => { , ) - expect(container.firstChild).toHaveClass('custom-class') - expect(container.firstChild).toHaveClass('w-5') - expect(container.firstChild).toHaveClass('h-5') + expect(container.firstChild)!.toHaveClass('custom-class') + expect(container.firstChild)!.toHaveClass('w-5') + expect(container.firstChild)!.toHaveClass('h-5') }) }) @@ -208,7 +209,7 @@ describe('DatasourceIcon', () => { const { container } = render() const iconDiv = container.querySelector('[style*="background-image"]') - expect(iconDiv).toHaveStyle({ backgroundImage: 'url()' }) + expect(iconDiv)!.toHaveStyle({ backgroundImage: 'url()' }) }) it('should handle special characters in iconUrl', () => { @@ -217,7 +218,7 @@ describe('DatasourceIcon', () => { const { container } = render() const iconDiv = container.querySelector('[style*="background-image"]') - expect(iconDiv).toHaveStyle({ backgroundImage: `url(${iconUrl})` }) + expect(iconDiv)!.toHaveStyle({ backgroundImage: `url(${iconUrl})` }) }) it('should handle data URL as iconUrl', () => { @@ -226,7 +227,7 @@ describe('DatasourceIcon', () => { const { container } = render() const iconDiv = container.querySelector('[style*="background-image"]') - expect(iconDiv).toBeInTheDocument() + expect(iconDiv)!.toBeInTheDocument() }) }) }) @@ -235,25 +236,26 @@ describe('DatasourceIcon', () => { it('should have flex container classes', () => { const { container } = render() - expect(container.firstChild).toHaveClass('flex') - expect(container.firstChild).toHaveClass('items-center') - expect(container.firstChild).toHaveClass('justify-center') + expect(container.firstChild)!.toHaveClass('flex') + expect(container.firstChild)!.toHaveClass('items-center') + expect(container.firstChild)!.toHaveClass('justify-center') }) it('should have shadow-xs class from size map', () => { const { container } = render() // Assert - Default size 'sm' has shadow-xs - expect(container.firstChild).toHaveClass('shadow-xs') + // Assert - Default size 'sm' has shadow-xs + expect(container.firstChild)!.toHaveClass('shadow-xs') }) it('should have inner div with bg-cover class', () => { const { container } = render() const innerDiv = container.querySelector('.bg-cover') - expect(innerDiv).toBeInTheDocument() - expect(innerDiv).toHaveClass('bg-center') - expect(innerDiv).toHaveClass('rounded-md') + expect(innerDiv)!.toBeInTheDocument() + expect(innerDiv)!.toHaveClass('bg-center') + expect(innerDiv)!.toHaveClass('rounded-md') }) }) }) @@ -519,13 +521,13 @@ describe('OptionCard', () => { it('should render without crashing', () => { renderWithProviders() - expect(screen.getByText('Test Option')).toBeInTheDocument() + expect(screen.getByText('Test Option'))!.toBeInTheDocument() }) it('should render label text', () => { renderWithProviders() - expect(screen.getByText('Custom Label')).toBeInTheDocument() + expect(screen.getByText('Custom Label'))!.toBeInTheDocument() }) it('should render DatasourceIcon component', () => { @@ -533,7 +535,7 @@ describe('OptionCard', () => { // Assert - DatasourceIcon container should exist const iconContainer = container.querySelector('.size-8') - expect(iconContainer).toBeInTheDocument() + expect(iconContainer)!.toBeInTheDocument() }) it('should set title attribute for label truncation', () => { @@ -542,7 +544,7 @@ describe('OptionCard', () => { renderWithProviders() const labelElement = screen.getByText(longLabel) - expect(labelElement).toHaveAttribute('title', longLabel) + expect(labelElement)!.toHaveAttribute('title', longLabel) }) }) @@ -554,8 +556,8 @@ describe('OptionCard', () => { ) const card = container.firstChild - expect(card).toHaveClass('border-components-option-card-option-selected-border') - expect(card).toHaveClass('bg-components-option-card-option-selected-bg') + expect(card)!.toHaveClass('border-components-option-card-option-selected-border') + expect(card)!.toHaveClass('bg-components-option-card-option-selected-bg') }) it('should apply unselected styles when selected is false', () => { @@ -564,22 +566,22 @@ describe('OptionCard', () => { ) const card = container.firstChild - expect(card).toHaveClass('border-components-option-card-option-border') - expect(card).toHaveClass('bg-components-option-card-option-bg') + expect(card)!.toHaveClass('border-components-option-card-option-border') + expect(card)!.toHaveClass('bg-components-option-card-option-bg') }) it('should apply text-text-primary to label when selected', () => { renderWithProviders() const label = screen.getByText('Test Option') - expect(label).toHaveClass('text-text-primary') + expect(label)!.toHaveClass('text-text-primary') }) it('should apply text-text-secondary to label when not selected', () => { renderWithProviders() const label = screen.getByText('Test Option') - expect(label).toHaveClass('text-text-secondary') + expect(label)!.toHaveClass('text-text-secondary') }) }) @@ -593,7 +595,7 @@ describe('OptionCard', () => { // Act - Click on the label text's parent card const labelElement = screen.getByText('Test Option') const card = labelElement.closest('[class*="cursor-pointer"]') - expect(card).toBeInTheDocument() + expect(card)!.toBeInTheDocument() fireEvent.click(card!) expect(mockOnClick).toHaveBeenCalledTimes(1) @@ -607,11 +609,12 @@ describe('OptionCard', () => { // Act - Click on the label text's parent card should not throw const labelElement = screen.getByText('Test Option') const card = labelElement.closest('[class*="cursor-pointer"]') - expect(card).toBeInTheDocument() + expect(card)!.toBeInTheDocument() fireEvent.click(card!) // Assert - Component should still be rendered - expect(screen.getByText('Test Option')).toBeInTheDocument() + // Assert - Component should still be rendered + expect(screen.getByText('Test Option'))!.toBeInTheDocument() }) }) @@ -631,35 +634,35 @@ describe('OptionCard', () => { it('should have cursor-pointer class', () => { const { container } = renderWithProviders() - expect(container.firstChild).toHaveClass('cursor-pointer') + expect(container.firstChild)!.toHaveClass('cursor-pointer') }) it('should have flex layout classes', () => { const { container } = renderWithProviders() - expect(container.firstChild).toHaveClass('flex') - expect(container.firstChild).toHaveClass('items-center') - expect(container.firstChild).toHaveClass('gap-2') + expect(container.firstChild)!.toHaveClass('flex') + expect(container.firstChild)!.toHaveClass('items-center') + expect(container.firstChild)!.toHaveClass('gap-2') }) it('should have rounded-xl border', () => { const { container } = renderWithProviders() - expect(container.firstChild).toHaveClass('rounded-xl') - expect(container.firstChild).toHaveClass('border') + expect(container.firstChild)!.toHaveClass('rounded-xl') + expect(container.firstChild)!.toHaveClass('border') }) it('should have padding p-3', () => { const { container } = renderWithProviders() - expect(container.firstChild).toHaveClass('p-3') + expect(container.firstChild)!.toHaveClass('p-3') }) it('should have line-clamp-2 for label truncation', () => { renderWithProviders() const label = screen.getByText('Test Option') - expect(label).toHaveClass('line-clamp-2') + expect(label)!.toHaveClass('line-clamp-2') }) }) @@ -669,7 +672,7 @@ describe('OptionCard', () => { expect(OptionCard).toBeDefined() // React.memo wraps the component, so we check it renders correctly const { container } = renderWithProviders() - expect(container.firstChild).toBeInTheDocument() + expect(container.firstChild)!.toBeInTheDocument() }) }) }) @@ -698,27 +701,27 @@ describe('DataSourceOptions', () => { it('should render without crashing', () => { renderWithProviders() - expect(screen.getByText('Data Source 1')).toBeInTheDocument() - expect(screen.getByText('Data Source 2')).toBeInTheDocument() - expect(screen.getByText('Data Source 3')).toBeInTheDocument() + expect(screen.getByText('Data Source 1'))!.toBeInTheDocument() + expect(screen.getByText('Data Source 2'))!.toBeInTheDocument() + expect(screen.getByText('Data Source 3'))!.toBeInTheDocument() }) it('should render correct number of option cards', () => { renderWithProviders() - expect(screen.getByText('Data Source 1')).toBeInTheDocument() - expect(screen.getByText('Data Source 2')).toBeInTheDocument() - expect(screen.getByText('Data Source 3')).toBeInTheDocument() + expect(screen.getByText('Data Source 1'))!.toBeInTheDocument() + expect(screen.getByText('Data Source 2'))!.toBeInTheDocument() + expect(screen.getByText('Data Source 3'))!.toBeInTheDocument() }) it('should render with grid layout', () => { const { container } = renderWithProviders() const gridContainer = container.firstChild - expect(gridContainer).toHaveClass('grid') - expect(gridContainer).toHaveClass('w-full') - expect(gridContainer).toHaveClass('grid-cols-4') - expect(gridContainer).toHaveClass('gap-1') + expect(gridContainer)!.toHaveClass('grid') + expect(gridContainer)!.toHaveClass('w-full') + expect(gridContainer)!.toHaveClass('grid-cols-4') + expect(gridContainer)!.toHaveClass('gap-1') }) it('should render no option cards when options is empty', () => { @@ -728,16 +731,17 @@ describe('DataSourceOptions', () => { expect(screen.queryByText('Data Source')).not.toBeInTheDocument() // Grid container should still exist - expect(container.firstChild).toHaveClass('grid') + // Grid container should still exist + expect(container.firstChild)!.toHaveClass('grid') }) it('should render single option card when only one option exists', () => { - const singleOption = [createMockDatasourceOption(defaultNodes[0])] + const singleOption = [createMockDatasourceOption(defaultNodes[0]!)] mockUseDatasourceOptions.mockReturnValue(singleOption) renderWithProviders() - expect(screen.getByText('Data Source 1')).toBeInTheDocument() + expect(screen.getByText('Data Source 1'))!.toBeInTheDocument() expect(screen.queryByText('Data Source 2')).not.toBeInTheDocument() }) }) @@ -778,7 +782,7 @@ describe('DataSourceOptions', () => { // Assert - Check for selected styling on second card const cards = container.querySelectorAll('.rounded-xl.border') - expect(cards[1]).toHaveClass('border-components-option-card-option-selected-border') + expect(cards[1])!.toHaveClass('border-components-option-card-option-selected-border') }) it('should show no selection when datasourceNodeId is empty', () => { @@ -816,7 +820,7 @@ describe('DataSourceOptions', () => { // Assert initial selection let cards = container.querySelectorAll('.rounded-xl.border') - expect(cards[0]).toHaveClass('border-components-option-card-option-selected-border') + expect(cards[0])!.toHaveClass('border-components-option-card-option-selected-border') // Act - Change selection rerender( @@ -831,7 +835,7 @@ describe('DataSourceOptions', () => { // Assert new selection cards = container.querySelectorAll('.rounded-xl.border') expect(cards[0]).not.toHaveClass('border-components-option-card-option-selected-border') - expect(cards[1]).toHaveClass('border-components-option-card-option-selected-border') + expect(cards[1])!.toHaveClass('border-components-option-card-option-selected-border') }) }) @@ -847,7 +851,8 @@ describe('DataSourceOptions', () => { ) // Assert - Component renders without error - expect(screen.getByText('Data Source 1')).toBeInTheDocument() + // Assert - Component renders without error + expect(screen.getByText('Data Source 1'))!.toBeInTheDocument() }) }) }) @@ -870,7 +875,7 @@ describe('DataSourceOptions', () => { expect(mockOnSelect).toHaveBeenCalledTimes(1) expect(mockOnSelect).toHaveBeenCalledWith({ nodeId: 'node-1', - nodeData: defaultOptions[0].data, + nodeData: defaultOptions[0]!.data, } satisfies Datasource) }) @@ -948,7 +953,8 @@ describe('DataSourceOptions', () => { ) // Get initial click handlers - expect(screen.getByText('Data Source 1')).toBeInTheDocument() + // Get initial click handlers + expect(screen.getByText('Data Source 1'))!.toBeInTheDocument() // Trigger clicks to test handlers work fireEvent.click(screen.getByText('Data Source 1')) @@ -1003,7 +1009,7 @@ describe('DataSourceOptions', () => { expect(mockOnSelect2).toHaveBeenCalledTimes(1) expect(mockOnSelect2).toHaveBeenCalledWith({ nodeId: 'node-3', - nodeData: defaultOptions[2].data, + nodeData: defaultOptions[2]!.data, }) }) @@ -1022,7 +1028,7 @@ describe('DataSourceOptions', () => { fireEvent.click(screen.getByText('Data Source 1')) expect(mockOnSelect).toHaveBeenCalledWith({ nodeId: 'node-1', - nodeData: defaultOptions[0].data, + nodeData: defaultOptions[0]!.data, }) // Act - Change options @@ -1045,8 +1051,8 @@ describe('DataSourceOptions', () => { // Assert - Callback receives new option data expect(mockOnSelect).toHaveBeenLastCalledWith({ - nodeId: newOptions[0].value, - nodeData: newOptions[0].data, + nodeId: newOptions[0]!.value, + nodeData: newOptions[0]!.data, }) }) }) @@ -1070,7 +1076,7 @@ describe('DataSourceOptions', () => { expect(mockOnSelect).toHaveBeenCalledTimes(1) expect(mockOnSelect).toHaveBeenCalledWith({ nodeId: 'node-2', - nodeData: defaultOptions[1].data, + nodeData: defaultOptions[1]!.data, } satisfies Datasource) }) @@ -1090,7 +1096,7 @@ describe('DataSourceOptions', () => { expect(mockOnSelect).toHaveBeenCalledTimes(1) expect(mockOnSelect).toHaveBeenCalledWith({ nodeId: 'node-1', - nodeData: defaultOptions[0].data, + nodeData: defaultOptions[0]!.data, }) }) @@ -1112,15 +1118,15 @@ describe('DataSourceOptions', () => { expect(mockOnSelect).toHaveBeenCalledTimes(3) expect(mockOnSelect).toHaveBeenNthCalledWith(1, { nodeId: 'node-1', - nodeData: defaultOptions[0].data, + nodeData: defaultOptions[0]!.data, }) expect(mockOnSelect).toHaveBeenNthCalledWith(2, { nodeId: 'node-2', - nodeData: defaultOptions[1].data, + nodeData: defaultOptions[1]!.data, }) expect(mockOnSelect).toHaveBeenNthCalledWith(3, { nodeId: 'node-3', - nodeData: defaultOptions[2].data, + nodeData: defaultOptions[2]!.data, }) }) }) @@ -1164,7 +1170,7 @@ describe('DataSourceOptions', () => { />, ) - expect(container.firstChild).toBeInTheDocument() + expect(container.firstChild)!.toBeInTheDocument() }) it('should not crash when datasourceNodeId is undefined', () => { @@ -1176,7 +1182,7 @@ describe('DataSourceOptions', () => { />, ) - expect(screen.getByText('Data Source 1')).toBeInTheDocument() + expect(screen.getByText('Data Source 1'))!.toBeInTheDocument() }) }) @@ -1202,7 +1208,7 @@ describe('DataSourceOptions', () => { renderWithProviders() - expect(screen.getByText('Minimal Option')).toBeInTheDocument() + expect(screen.getByText('Minimal Option'))!.toBeInTheDocument() }) }) @@ -1219,8 +1225,8 @@ describe('DataSourceOptions', () => { />, ) - expect(screen.getByText('Data Source 1')).toBeInTheDocument() - expect(screen.getByText('Data Source 50')).toBeInTheDocument() + expect(screen.getByText('Data Source 1'))!.toBeInTheDocument() + expect(screen.getByText('Data Source 50'))!.toBeInTheDocument() }) }) @@ -1243,7 +1249,8 @@ describe('DataSourceOptions', () => { ) // Assert - Special characters should be escaped/rendered safely - expect(screen.getByText('Data Source ')).toBeInTheDocument() + // Assert - Special characters should be escaped/rendered safely + expect(screen.getByText('Data Source '))!.toBeInTheDocument() }) it('should handle unicode characters in option labels', () => { @@ -1263,7 +1270,7 @@ describe('DataSourceOptions', () => { />, ) - expect(screen.getByText('数据源 📁 Source émoji')).toBeInTheDocument() + expect(screen.getByText('数据源 📁 Source émoji'))!.toBeInTheDocument() }) it('should handle empty string as option value', () => { @@ -1276,13 +1283,13 @@ describe('DataSourceOptions', () => { renderWithProviders() - expect(screen.getByText('Empty Value Option')).toBeInTheDocument() + expect(screen.getByText('Empty Value Option'))!.toBeInTheDocument() }) }) describe('Boundary Conditions', () => { it('should handle single option selection correctly', () => { - const singleOption = [createMockDatasourceOption(defaultNodes[0])] + const singleOption = [createMockDatasourceOption(defaultNodes[0]!)] mockUseDatasourceOptions.mockReturnValue(singleOption) const mockOnSelect = vi.fn() @@ -1327,7 +1334,7 @@ describe('DataSourceOptions', () => { const labels = screen.getAllByText('Duplicate Label') expect(labels).toHaveLength(2) - fireEvent.click(labels[1]) + fireEvent.click(labels[1]!) expect(mockOnSelect).toHaveBeenCalledWith({ nodeId: 'node-b', nodeData: expect.objectContaining({ plugin_id: 'plugin-b' }), @@ -1347,6 +1354,37 @@ describe('DataSourceOptions', () => { unmount() + // Assert - No errors thrown, component cleanly unmounted + // Assert - No errors thrown, component cleanly unmounted + // Assert - No errors thrown, component cleanly unmounted + // Assert - No errors thrown, component cleanly unmounted + // Assert - No errors thrown, component cleanly unmounted + // Assert - No errors thrown, component cleanly unmounted + // Assert - No errors thrown, component cleanly unmounted + // Assert - No errors thrown, component cleanly unmounted + // Assert - No errors thrown, component cleanly unmounted + // Assert - No errors thrown, component cleanly unmounted + // Assert - No errors thrown, component cleanly unmounted + // Assert - No errors thrown, component cleanly unmounted + // Assert - No errors thrown, component cleanly unmounted + // Assert - No errors thrown, component cleanly unmounted + // Assert - No errors thrown, component cleanly unmounted + // Assert - No errors thrown, component cleanly unmounted + // Assert - No errors thrown, component cleanly unmounted + // Assert - No errors thrown, component cleanly unmounted + // Assert - No errors thrown, component cleanly unmounted + // Assert - No errors thrown, component cleanly unmounted + // Assert - No errors thrown, component cleanly unmounted + // Assert - No errors thrown, component cleanly unmounted + // Assert - No errors thrown, component cleanly unmounted + // Assert - No errors thrown, component cleanly unmounted + // Assert - No errors thrown, component cleanly unmounted + // Assert - No errors thrown, component cleanly unmounted + // Assert - No errors thrown, component cleanly unmounted + // Assert - No errors thrown, component cleanly unmounted + // Assert - No errors thrown, component cleanly unmounted + // Assert - No errors thrown, component cleanly unmounted + // Assert - No errors thrown, component cleanly unmounted // Assert - No errors thrown, component cleanly unmounted expect(screen.queryByText('Data Source 1')).not.toBeInTheDocument() }) @@ -1367,6 +1405,37 @@ describe('DataSourceOptions', () => { // Unmount during/after interaction unmount() + // Assert - Should not throw + // Assert - Should not throw + // Assert - Should not throw + // Assert - Should not throw + // Assert - Should not throw + // Assert - Should not throw + // Assert - Should not throw + // Assert - Should not throw + // Assert - Should not throw + // Assert - Should not throw + // Assert - Should not throw + // Assert - Should not throw + // Assert - Should not throw + // Assert - Should not throw + // Assert - Should not throw + // Assert - Should not throw + // Assert - Should not throw + // Assert - Should not throw + // Assert - Should not throw + // Assert - Should not throw + // Assert - Should not throw + // Assert - Should not throw + // Assert - Should not throw + // Assert - Should not throw + // Assert - Should not throw + // Assert - Should not throw + // Assert - Should not throw + // Assert - Should not throw + // Assert - Should not throw + // Assert - Should not throw + // Assert - Should not throw // Assert - Should not throw expect(screen.queryByText('Data Source 1')).not.toBeInTheDocument() }) @@ -1392,7 +1461,7 @@ describe('DataSourceOptions', () => { const cards = container.querySelectorAll('.rounded-xl.border') expect(cards[0]).not.toHaveClass('border-components-option-card-option-selected-border') - expect(cards[1]).toHaveClass('border-components-option-card-option-selected-border') + expect(cards[1])!.toHaveClass('border-components-option-card-option-selected-border') expect(cards[2]).not.toHaveClass('border-components-option-card-option-selected-border') }) @@ -1427,7 +1496,7 @@ describe('DataSourceOptions', () => { />, ) - expect(screen.getByText('Data Source 1')).toBeInTheDocument() + expect(screen.getByText('Data Source 1'))!.toBeInTheDocument() }) it.each([ @@ -1449,7 +1518,7 @@ describe('DataSourceOptions', () => { ) if (count > 0) - expect(screen.getByText('Data Source 1')).toBeInTheDocument() + expect(screen.getByText('Data Source 1'))!.toBeInTheDocument() else expect(screen.queryByText('Data Source 1')).not.toBeInTheDocument() }) diff --git a/web/app/components/datasets/documents/create-from-pipeline/data-source-options/index.tsx b/web/app/components/datasets/documents/create-from-pipeline/data-source-options/index.tsx index 8e3a29cfe5..51cf34d273 100644 --- a/web/app/components/datasets/documents/create-from-pipeline/data-source-options/index.tsx +++ b/web/app/components/datasets/documents/create-from-pipeline/data-source-options/index.tsx @@ -31,7 +31,7 @@ const DataSourceOptions = ({ useEffect(() => { if (options.length > 0 && !datasourceNodeId) - handelSelect(options[0].value) + handelSelect(options[0]!.value) }, []) return ( diff --git a/web/app/components/datasets/documents/create-from-pipeline/data-source/base/credential-selector/__tests__/index.spec.tsx b/web/app/components/datasets/documents/create-from-pipeline/data-source/base/credential-selector/__tests__/index.spec.tsx index d595a50fe1..49b0cb0789 100644 --- a/web/app/components/datasets/documents/create-from-pipeline/data-source/base/credential-selector/__tests__/index.spec.tsx +++ b/web/app/components/datasets/documents/create-from-pipeline/data-source/base/credential-selector/__tests__/index.spec.tsx @@ -97,8 +97,8 @@ describe('CredentialSelector', () => { render() - expect(screen.getByTestId('portal-root')).toBeInTheDocument() - expect(screen.getByTestId('portal-trigger')).toBeInTheDocument() + expect(screen.getByTestId('portal-root'))!.toBeInTheDocument() + expect(screen.getByTestId('portal-trigger'))!.toBeInTheDocument() }) it('should render current credential name in trigger', () => { @@ -106,7 +106,7 @@ describe('CredentialSelector', () => { render() - expect(screen.getByText('Credential 1')).toBeInTheDocument() + expect(screen.getByText('Credential 1'))!.toBeInTheDocument() }) it('should render credential icon with correct props', () => { @@ -116,8 +116,8 @@ describe('CredentialSelector', () => { // Assert - CredentialIcon renders an img when avatarUrl is provided const iconImg = container.querySelector('img') - expect(iconImg).toBeInTheDocument() - expect(iconImg).toHaveAttribute('src', 'https://example.com/avatar-1.png') + expect(iconImg)!.toBeInTheDocument() + expect(iconImg)!.toHaveAttribute('src', 'https://example.com/avatar-1.png') }) it('should render dropdown arrow icon', () => { @@ -126,7 +126,7 @@ describe('CredentialSelector', () => { const { container } = render() const svgIcon = container.querySelector('svg') - expect(svgIcon).toBeInTheDocument() + expect(svgIcon)!.toBeInTheDocument() }) it('should not render dropdown content initially', () => { @@ -146,7 +146,8 @@ describe('CredentialSelector', () => { fireEvent.click(trigger) // Assert - All credentials should be visible (current credential appears in both trigger and list) - expect(screen.getByTestId('portal-content')).toBeInTheDocument() + // Assert - All credentials should be visible (current credential appears in both trigger and list) + expect(screen.getByTestId('portal-content'))!.toBeInTheDocument() // 3 in dropdown list + 1 in trigger (current) = 4 total expect(screen.getAllByText(/Credential \d/)).toHaveLength(4) }) @@ -160,7 +161,7 @@ describe('CredentialSelector', () => { render() - expect(screen.getByText('Credential 1')).toBeInTheDocument() + expect(screen.getByText('Credential 1'))!.toBeInTheDocument() }) it('should display second credential when currentCredentialId matches second', () => { @@ -168,7 +169,7 @@ describe('CredentialSelector', () => { render() - expect(screen.getByText('Credential 2')).toBeInTheDocument() + expect(screen.getByText('Credential 2'))!.toBeInTheDocument() }) it('should display third credential when currentCredentialId matches third', () => { @@ -176,7 +177,7 @@ describe('CredentialSelector', () => { render() - expect(screen.getByText('Credential 3')).toBeInTheDocument() + expect(screen.getByText('Credential 3'))!.toBeInTheDocument() }) it.each([ @@ -188,7 +189,7 @@ describe('CredentialSelector', () => { render() - expect(screen.getByText(expectedName)).toBeInTheDocument() + expect(screen.getByText(expectedName))!.toBeInTheDocument() }) }) @@ -201,7 +202,7 @@ describe('CredentialSelector', () => { render() - expect(screen.getByText('Test Credential')).toBeInTheDocument() + expect(screen.getByText('Test Credential'))!.toBeInTheDocument() }) it('should render multiple credentials in dropdown', () => { @@ -226,7 +227,7 @@ describe('CredentialSelector', () => { render() - expect(screen.getByText('Test & Credential ')).toBeInTheDocument() + expect(screen.getByText('Test & Credential '))!.toBeInTheDocument() }) }) @@ -293,6 +294,37 @@ describe('CredentialSelector', () => { const props = createDefaultProps() render() + // Assert - Initially closed + // Assert - Initially closed + // Assert - Initially closed + // Assert - Initially closed + // Assert - Initially closed + // Assert - Initially closed + // Assert - Initially closed + // Assert - Initially closed + // Assert - Initially closed + // Assert - Initially closed + // Assert - Initially closed + // Assert - Initially closed + // Assert - Initially closed + // Assert - Initially closed + // Assert - Initially closed + // Assert - Initially closed + // Assert - Initially closed + // Assert - Initially closed + // Assert - Initially closed + // Assert - Initially closed + // Assert - Initially closed + // Assert - Initially closed + // Assert - Initially closed + // Assert - Initially closed + // Assert - Initially closed + // Assert - Initially closed + // Assert - Initially closed + // Assert - Initially closed + // Assert - Initially closed + // Assert - Initially closed + // Assert - Initially closed // Assert - Initially closed expect(screen.queryByTestId('portal-content')).not.toBeInTheDocument() @@ -301,7 +333,8 @@ describe('CredentialSelector', () => { fireEvent.click(trigger) // Assert - Now open - expect(screen.getByTestId('portal-content')).toBeInTheDocument() + // Assert - Now open + expect(screen.getByTestId('portal-content'))!.toBeInTheDocument() }) it('should call onCredentialChange when clicking a credential item', () => { @@ -327,7 +360,7 @@ describe('CredentialSelector', () => { const trigger = screen.getByTestId('portal-trigger') fireEvent.click(trigger) - expect(screen.getByTestId('portal-content')).toBeInTheDocument() + expect(screen.getByTestId('portal-content'))!.toBeInTheDocument() const credential2 = screen.getByText('Credential 2') fireEvent.click(credential2) @@ -347,7 +380,8 @@ describe('CredentialSelector', () => { fireEvent.click(trigger) // Assert - Should not crash - expect(trigger).toBeInTheDocument() + // Assert - Should not crash + expect(trigger)!.toBeInTheDocument() }) it('should allow selecting credentials multiple times', () => { @@ -504,7 +538,8 @@ describe('CredentialSelector', () => { render() // Assert - Should display credential 2 - expect(screen.getByText('Credential 2')).toBeInTheDocument() + // Assert - Should display credential 2 + expect(screen.getByText('Credential 2'))!.toBeInTheDocument() }) it('should update currentCredential when currentCredentialId changes', () => { @@ -512,13 +547,15 @@ describe('CredentialSelector', () => { const { rerender } = render() // Assert initial - expect(screen.getByText('Credential 1')).toBeInTheDocument() + // Assert initial + expect(screen.getByText('Credential 1'))!.toBeInTheDocument() // Act - Change currentCredentialId rerender() // Assert - Should now display credential 3 - expect(screen.getByText('Credential 3')).toBeInTheDocument() + // Assert - Should now display credential 3 + expect(screen.getByText('Credential 3'))!.toBeInTheDocument() }) it('should update currentCredential when credentials array changes', () => { @@ -526,7 +563,8 @@ describe('CredentialSelector', () => { const { rerender } = render() // Assert initial - expect(screen.getByText('Credential 1')).toBeInTheDocument() + // Assert initial + expect(screen.getByText('Credential 1'))!.toBeInTheDocument() // Act - Change credentials const newCredentials = [ @@ -535,7 +573,8 @@ describe('CredentialSelector', () => { rerender() // Assert - Should display updated name - expect(screen.getByText('Updated Credential 1')).toBeInTheDocument() + // Assert - Should display updated name + expect(screen.getByText('Updated Credential 1'))!.toBeInTheDocument() }) it('should return undefined currentCredential when id not found', () => { @@ -581,11 +620,12 @@ describe('CredentialSelector', () => { const { rerender } = render() // Assert initial - expect(screen.getByText('Credential 1')).toBeInTheDocument() + // Assert initial + expect(screen.getByText('Credential 1'))!.toBeInTheDocument() rerender() - expect(screen.getByText('Credential 2')).toBeInTheDocument() + expect(screen.getByText('Credential 2'))!.toBeInTheDocument() }) it('should re-render when credentials array reference changes', () => { @@ -598,7 +638,7 @@ describe('CredentialSelector', () => { ] rerender() - expect(screen.getByText('New Name 1')).toBeInTheDocument() + expect(screen.getByText('New Name 1'))!.toBeInTheDocument() }) it('should re-render when onCredentialChange reference changes', () => { @@ -631,7 +671,8 @@ describe('CredentialSelector', () => { render() // Assert - Should render without crashing - expect(screen.getByTestId('portal-root')).toBeInTheDocument() + // Assert - Should render without crashing + expect(screen.getByTestId('portal-root'))!.toBeInTheDocument() }) it('should handle undefined avatar_url in credential', () => { @@ -648,12 +689,14 @@ describe('CredentialSelector', () => { const { container } = render() // Assert - Should render without crashing and show first letter fallback - expect(screen.getByText('No Avatar Credential')).toBeInTheDocument() + // Assert - Should render without crashing and show first letter fallback + expect(screen.getByText('No Avatar Credential'))!.toBeInTheDocument() // When avatar_url is undefined, CredentialIcon shows first letter instead of img const iconImg = container.querySelector('img') expect(iconImg).not.toBeInTheDocument() // First letter 'N' should be displayed - expect(screen.getByText('N')).toBeInTheDocument() + // First letter 'N' should be displayed + expect(screen.getByText('N'))!.toBeInTheDocument() }) it('should handle empty string name in credential', () => { @@ -669,7 +712,8 @@ describe('CredentialSelector', () => { render() // Assert - Should render without crashing - expect(screen.getByTestId('portal-trigger')).toBeInTheDocument() + // Assert - Should render without crashing + expect(screen.getByTestId('portal-trigger'))!.toBeInTheDocument() }) it('should handle very long credential name', () => { @@ -685,7 +729,7 @@ describe('CredentialSelector', () => { render() - expect(screen.getByText(longName)).toBeInTheDocument() + expect(screen.getByText(longName))!.toBeInTheDocument() }) it('should handle special characters in credential name', () => { @@ -701,7 +745,7 @@ describe('CredentialSelector', () => { render() - expect(screen.getByText(specialName)).toBeInTheDocument() + expect(screen.getByText(specialName))!.toBeInTheDocument() }) it('should handle numeric id as string', () => { @@ -716,7 +760,7 @@ describe('CredentialSelector', () => { render() - expect(screen.getByText('Numeric ID Credential')).toBeInTheDocument() + expect(screen.getByText('Numeric ID Credential'))!.toBeInTheDocument() }) it('should handle large number of credentials', () => { @@ -728,7 +772,7 @@ describe('CredentialSelector', () => { render() - expect(screen.getByText('Credential 50')).toBeInTheDocument() + expect(screen.getByText('Credential 50'))!.toBeInTheDocument() }) it('should handle credential selection with duplicate names', () => { @@ -752,7 +796,7 @@ describe('CredentialSelector', () => { const sameNameElements = screen.getAllByText('Same Name') expect(sameNameElements.length).toBe(3) - fireEvent.click(sameNameElements[2]) + fireEvent.click(sameNameElements[2]!) // Assert - Should call with the correct id even with duplicate names expect(mockOnChange).toHaveBeenCalledWith('cred-2') @@ -787,7 +831,8 @@ describe('CredentialSelector', () => { render() // Assert - Should render without crashing - expect(screen.getByTestId('portal-trigger')).toBeInTheDocument() + // Assert - Should render without crashing + expect(screen.getByTestId('portal-trigger'))!.toBeInTheDocument() }) }) @@ -799,7 +844,7 @@ describe('CredentialSelector', () => { render() const trigger = screen.getByTestId('portal-trigger') - expect(trigger).toHaveClass('overflow-hidden') + expect(trigger)!.toHaveClass('overflow-hidden') }) it('should apply grow class to trigger', () => { @@ -808,7 +853,7 @@ describe('CredentialSelector', () => { render() const trigger = screen.getByTestId('portal-trigger') - expect(trigger).toHaveClass('grow') + expect(trigger)!.toHaveClass('grow') }) it('should apply z-10 class to dropdown content', () => { @@ -819,7 +864,7 @@ describe('CredentialSelector', () => { fireEvent.click(trigger) const content = screen.getByTestId('portal-content') - expect(content).toHaveClass('z-10') + expect(content)!.toHaveClass('z-10') }) }) @@ -831,7 +876,8 @@ describe('CredentialSelector', () => { render() // Assert - Trigger should display the correct credential - expect(screen.getByText('Credential 2')).toBeInTheDocument() + // Assert - Trigger should display the correct credential + expect(screen.getByText('Credential 2'))!.toBeInTheDocument() }) it('should pass isOpen state to Trigger component', () => { @@ -840,14 +886,15 @@ describe('CredentialSelector', () => { // Assert - Initially closed const portalRoot = screen.getByTestId('portal-root') - expect(portalRoot).toHaveAttribute('data-open', 'false') + expect(portalRoot)!.toHaveAttribute('data-open', 'false') // Act - Open const trigger = screen.getByTestId('portal-trigger') fireEvent.click(trigger) // Assert - Now open - expect(portalRoot).toHaveAttribute('data-open', 'true') + // Assert - Now open + expect(portalRoot)!.toHaveAttribute('data-open', 'true') }) it('should pass credentials to List component', () => { @@ -899,7 +946,7 @@ describe('CredentialSelector', () => { const props = createDefaultProps() render() - expect(screen.getByTestId('portal-root')).toBeInTheDocument() + expect(screen.getByTestId('portal-root'))!.toBeInTheDocument() }) it('should configure PortalToFollowElem with offset mainAxis 4', () => { @@ -907,7 +954,7 @@ describe('CredentialSelector', () => { const props = createDefaultProps() render() - expect(screen.getByTestId('portal-root')).toBeInTheDocument() + expect(screen.getByTestId('portal-root'))!.toBeInTheDocument() }) }) }) diff --git a/web/app/components/datasets/documents/create-from-pipeline/data-source/base/credential-selector/index.tsx b/web/app/components/datasets/documents/create-from-pipeline/data-source/base/credential-selector/index.tsx index 2f14b0f3b8..116b762277 100644 --- a/web/app/components/datasets/documents/create-from-pipeline/data-source/base/credential-selector/index.tsx +++ b/web/app/components/datasets/documents/create-from-pipeline/data-source/base/credential-selector/index.tsx @@ -29,7 +29,7 @@ const CredentialSelector = ({ useEffect(() => { if (!currentCredential && credentials.length) - onCredentialChange(credentials[0].id) + onCredentialChange(credentials[0]!.id) }, [currentCredential, credentials]) const handleCredentialChange = useCallback((credentialId: string) => { diff --git a/web/app/components/datasets/documents/create-from-pipeline/data-source/local-file/hooks/__tests__/use-local-file-upload.spec.tsx b/web/app/components/datasets/documents/create-from-pipeline/data-source/local-file/hooks/__tests__/use-local-file-upload.spec.tsx index cc531aad8f..80a8fc854d 100644 --- a/web/app/components/datasets/documents/create-from-pipeline/data-source/local-file/hooks/__tests__/use-local-file-upload.spec.tsx +++ b/web/app/components/datasets/documents/create-from-pipeline/data-source/local-file/hooks/__tests__/use-local-file-upload.spec.tsx @@ -404,7 +404,7 @@ describe('useLocalFileUpload', () => { // Should only process first 5 files (batch_count_limit) const firstCall = mockSetLocalFileList.mock.calls[0] - expect(firstCall[0].length).toBeLessThanOrEqual(5) + expect(firstCall![0].length).toBeLessThanOrEqual(5) }) }) @@ -591,7 +591,8 @@ describe('useLocalFileUpload', () => { }) // dragover should not throw - expect(dropzone).toBeInTheDocument() + // dragover should not throw + expect(dropzone)!.toBeInTheDocument() }) it('should set dragging false on dragleave from drag overlay', async () => { @@ -715,7 +716,7 @@ describe('useLocalFileUpload', () => { await waitFor(() => { expect(mockSetLocalFileList).toHaveBeenCalled() // Should only have 1 file (limited by supportBatchUpload: false) - const callArgs = mockSetLocalFileList.mock.calls[0][0] + const callArgs = mockSetLocalFileList.mock.calls[0]![0] expect(callArgs.length).toBe(1) }) }) @@ -873,7 +874,7 @@ describe('useLocalFileUpload', () => { }) await waitFor(() => { - const callArgs = mockSetLocalFileList.mock.calls[0][0] + const callArgs = mockSetLocalFileList.mock.calls[0]![0] expect(callArgs[0].progress).toBe(PROGRESS_NOT_STARTED) }) }) @@ -899,7 +900,7 @@ describe('useLocalFileUpload', () => { await waitFor(() => { const calls = mockSetLocalFileList.mock.calls - const lastCall = calls[calls.length - 1][0] + const lastCall = calls[calls.length - 1]![0] expect(lastCall.some((f: FileItem) => f.progress === PROGRESS_ERROR)).toBe(true) }) }) diff --git a/web/app/components/datasets/documents/create-from-pipeline/data-source/online-documents/index.tsx b/web/app/components/datasets/documents/create-from-pipeline/data-source/online-documents/index.tsx index 5051d343cb..5321986cd7 100644 --- a/web/app/components/datasets/documents/create-from-pipeline/data-source/online-documents/index.tsx +++ b/web/app/components/datasets/documents/create-from-pipeline/data-source/online-documents/index.tsx @@ -115,7 +115,7 @@ const OnlineDocuments = ({ const handleSelectPages = useCallback((newSelectedPagesId: Set) => { const { setSelectedPagesId, setOnlineDocuments } = dataSourceStore.getState() - const selectedPages = Array.from(newSelectedPagesId).map(pageId => PagesMapAndSelectedPagesId[pageId]) + const selectedPages = Array.from(newSelectedPagesId).map(pageId => PagesMapAndSelectedPagesId[pageId]!) setSelectedPagesId(new Set(Array.from(newSelectedPagesId))) setOnlineDocuments(selectedPages) }, [dataSourceStore, PagesMapAndSelectedPagesId]) @@ -160,7 +160,7 @@ const OnlineDocuments = ({ checkedIds={selectedPagesId} disabledValue={new Set()} searchValue={searchValue} - list={documentsData[0].pages || []} + list={documentsData[0]!.pages || []} pagesMap={PagesMapAndSelectedPagesId} onSelect={handleSelectPages} canPreview={!isInPipeline} diff --git a/web/app/components/datasets/documents/create-from-pipeline/data-source/online-documents/page-selector/__tests__/index.spec.tsx b/web/app/components/datasets/documents/create-from-pipeline/data-source/online-documents/page-selector/__tests__/index.spec.tsx index a6d5738e2d..04676156e6 100644 --- a/web/app/components/datasets/documents/create-from-pipeline/data-source/online-documents/page-selector/__tests__/index.spec.tsx +++ b/web/app/components/datasets/documents/create-from-pipeline/data-source/online-documents/page-selector/__tests__/index.spec.tsx @@ -83,7 +83,7 @@ describe('PageSelector', () => { render() - expect(screen.getByTestId('virtual-list')).toBeInTheDocument() + expect(screen.getByTestId('virtual-list'))!.toBeInTheDocument() }) it('should render empty state when list is empty', () => { @@ -94,7 +94,7 @@ describe('PageSelector', () => { render() - expect(screen.getByText('common.dataSource.notion.selector.noSearchResult')).toBeInTheDocument() + expect(screen.getByText('common.dataSource.notion.selector.noSearchResult'))!.toBeInTheDocument() expect(screen.queryByTestId('virtual-list')).not.toBeInTheDocument() }) @@ -110,8 +110,8 @@ describe('PageSelector', () => { render() - expect(screen.getByText('Page 1')).toBeInTheDocument() - expect(screen.getByText('Page 2')).toBeInTheDocument() + expect(screen.getByText('Page 1'))!.toBeInTheDocument() + expect(screen.getByText('Page 2'))!.toBeInTheDocument() }) it('should render checkboxes when isMultipleChoice is true', () => { @@ -119,7 +119,7 @@ describe('PageSelector', () => { render() - expect(getCheckbox()).toBeInTheDocument() + expect(getCheckbox())!.toBeInTheDocument() }) it('should render radio buttons when isMultipleChoice is false', () => { @@ -127,7 +127,7 @@ describe('PageSelector', () => { render() - expect(getRadio()).toBeInTheDocument() + expect(getRadio())!.toBeInTheDocument() }) it('should render preview button when canPreview is true', () => { @@ -135,7 +135,7 @@ describe('PageSelector', () => { render() - expect(screen.getByText('common.dataSource.notion.selector.preview')).toBeInTheDocument() + expect(screen.getByText('common.dataSource.notion.selector.preview'))!.toBeInTheDocument() }) it('should not render preview button when canPreview is false', () => { @@ -153,7 +153,7 @@ describe('PageSelector', () => { // Assert - NotionIcon renders svg when page_icon is null const notionIcon = document.querySelector('.h-5.w-5') - expect(notionIcon).toBeInTheDocument() + expect(notionIcon)!.toBeInTheDocument() }) it('should render page name', () => { @@ -164,7 +164,7 @@ describe('PageSelector', () => { render() - expect(screen.getByText('My Custom Page')).toBeInTheDocument() + expect(screen.getByText('My Custom Page'))!.toBeInTheDocument() }) }) @@ -181,7 +181,7 @@ describe('PageSelector', () => { render() const checkbox = getCheckbox() - expect(checkbox).toBeInTheDocument() + expect(checkbox)!.toBeInTheDocument() expect(isCheckboxChecked(checkbox)).toBe(true) }) @@ -196,7 +196,7 @@ describe('PageSelector', () => { render() const checkbox = getCheckbox() - expect(checkbox).toBeInTheDocument() + expect(checkbox)!.toBeInTheDocument() expect(isCheckboxChecked(checkbox)).toBe(false) }) @@ -206,7 +206,7 @@ describe('PageSelector', () => { render() const checkbox = getCheckbox() - expect(checkbox).toBeInTheDocument() + expect(checkbox)!.toBeInTheDocument() expect(isCheckboxChecked(checkbox)).toBe(false) }) @@ -225,9 +225,9 @@ describe('PageSelector', () => { render() const checkboxes = getAllCheckboxes() - expect(isCheckboxChecked(checkboxes[0])).toBe(true) - expect(isCheckboxChecked(checkboxes[1])).toBe(false) - expect(isCheckboxChecked(checkboxes[2])).toBe(true) + expect(isCheckboxChecked(checkboxes[0]!)).toBe(true) + expect(isCheckboxChecked(checkboxes[1]!)).toBe(false) + expect(isCheckboxChecked(checkboxes[2]!)).toBe(true) }) }) @@ -243,7 +243,7 @@ describe('PageSelector', () => { render() const checkbox = getCheckbox() - expect(checkbox).toBeInTheDocument() + expect(checkbox)!.toBeInTheDocument() expect(isCheckboxDisabled(checkbox)).toBe(true) }) @@ -258,7 +258,7 @@ describe('PageSelector', () => { render() const checkbox = getCheckbox() - expect(checkbox).toBeInTheDocument() + expect(checkbox)!.toBeInTheDocument() expect(isCheckboxDisabled(checkbox)).toBe(false) }) @@ -276,8 +276,8 @@ describe('PageSelector', () => { render() const checkboxes = getAllCheckboxes() - expect(isCheckboxDisabled(checkboxes[0])).toBe(true) - expect(isCheckboxDisabled(checkboxes[1])).toBe(false) + expect(isCheckboxDisabled(checkboxes[0]!)).toBe(true) + expect(isCheckboxDisabled(checkboxes[1]!)).toBe(false) }) }) @@ -301,6 +301,37 @@ describe('PageSelector', () => { expect(screen.getAllByText('Apple Page').length).toBeGreaterThan(0) expect(screen.getAllByText('Apple Pie').length).toBeGreaterThan(0) // Banana Page is filtered out because it doesn't contain "Apple" + // Banana Page is filtered out because it doesn't contain "Apple" + // Banana Page is filtered out because it doesn't contain "Apple" + // Banana Page is filtered out because it doesn't contain "Apple" + // Banana Page is filtered out because it doesn't contain "Apple" + // Banana Page is filtered out because it doesn't contain "Apple" + // Banana Page is filtered out because it doesn't contain "Apple" + // Banana Page is filtered out because it doesn't contain "Apple" + // Banana Page is filtered out because it doesn't contain "Apple" + // Banana Page is filtered out because it doesn't contain "Apple" + // Banana Page is filtered out because it doesn't contain "Apple" + // Banana Page is filtered out because it doesn't contain "Apple" + // Banana Page is filtered out because it doesn't contain "Apple" + // Banana Page is filtered out because it doesn't contain "Apple" + // Banana Page is filtered out because it doesn't contain "Apple" + // Banana Page is filtered out because it doesn't contain "Apple" + // Banana Page is filtered out because it doesn't contain "Apple" + // Banana Page is filtered out because it doesn't contain "Apple" + // Banana Page is filtered out because it doesn't contain "Apple" + // Banana Page is filtered out because it doesn't contain "Apple" + // Banana Page is filtered out because it doesn't contain "Apple" + // Banana Page is filtered out because it doesn't contain "Apple" + // Banana Page is filtered out because it doesn't contain "Apple" + // Banana Page is filtered out because it doesn't contain "Apple" + // Banana Page is filtered out because it doesn't contain "Apple" + // Banana Page is filtered out because it doesn't contain "Apple" + // Banana Page is filtered out because it doesn't contain "Apple" + // Banana Page is filtered out because it doesn't contain "Apple" + // Banana Page is filtered out because it doesn't contain "Apple" + // Banana Page is filtered out because it doesn't contain "Apple" + // Banana Page is filtered out because it doesn't contain "Apple" + // Banana Page is filtered out because it doesn't contain "Apple" expect(screen.queryByText('Banana Page')).not.toBeInTheDocument() }) @@ -314,7 +345,7 @@ describe('PageSelector', () => { render() - expect(screen.getByText('common.dataSource.notion.selector.noSearchResult')).toBeInTheDocument() + expect(screen.getByText('common.dataSource.notion.selector.noSearchResult'))!.toBeInTheDocument() }) it('should show all pages when searchValue is empty', () => { @@ -330,8 +361,8 @@ describe('PageSelector', () => { render() - expect(screen.getByText('Page 1')).toBeInTheDocument() - expect(screen.getByText('Page 2')).toBeInTheDocument() + expect(screen.getByText('Page 1'))!.toBeInTheDocument() + expect(screen.getByText('Page 2'))!.toBeInTheDocument() }) it('should show breadcrumbs when searchValue is present', () => { @@ -345,7 +376,8 @@ describe('PageSelector', () => { render() // Assert - page name should be visible - expect(screen.getByText('Grandchild 1')).toBeInTheDocument() + // Assert - page name should be visible + expect(screen.getByText('Grandchild 1'))!.toBeInTheDocument() }) it('should perform case-sensitive search', () => { @@ -374,7 +406,7 @@ describe('PageSelector', () => { render() - expect(screen.getByText('common.dataSource.notion.selector.preview')).toBeInTheDocument() + expect(screen.getByText('common.dataSource.notion.selector.preview'))!.toBeInTheDocument() }) it('should hide preview button when canPreview is false', () => { @@ -391,7 +423,7 @@ describe('PageSelector', () => { render() - expect(screen.getByText('common.dataSource.notion.selector.preview')).toBeInTheDocument() + expect(screen.getByText('common.dataSource.notion.selector.preview'))!.toBeInTheDocument() }) }) @@ -401,7 +433,7 @@ describe('PageSelector', () => { render() - expect(getCheckbox()).toBeInTheDocument() + expect(getCheckbox())!.toBeInTheDocument() expect(getRadio()).not.toBeInTheDocument() }) @@ -410,7 +442,7 @@ describe('PageSelector', () => { render() - expect(getRadio()).toBeInTheDocument() + expect(getRadio())!.toBeInTheDocument() expect(getCheckbox()).not.toBeInTheDocument() }) @@ -420,7 +452,7 @@ describe('PageSelector', () => { render() - expect(getCheckbox()).toBeInTheDocument() + expect(getCheckbox())!.toBeInTheDocument() }) }) @@ -449,7 +481,7 @@ describe('PageSelector', () => { render() fireEvent.click(getCheckbox()) - const calledSet = mockOnSelect.mock.calls[0][0] as Set + const calledSet = mockOnSelect.mock.calls[0]![0] as Set expect(calledSet.has('page-1')).toBe(true) }) }) @@ -498,13 +530,15 @@ describe('PageSelector', () => { const { rerender } = render() // Assert - Initial render - expect(screen.getByText('Page 1')).toBeInTheDocument() + // Assert - Initial render + expect(screen.getByText('Page 1'))!.toBeInTheDocument() // Rerender with new credential rerender() // Assert - Should still show pages (reset and rebuild) - expect(screen.getByText('Page 1')).toBeInTheDocument() + // Assert - Should still show pages (reset and rebuild) + expect(screen.getByText('Page 1'))!.toBeInTheDocument() }) }) }) @@ -521,7 +555,39 @@ describe('PageSelector', () => { render() // Assert - Only root level page should be visible initially - expect(screen.getByText(rootPage.page_name)).toBeInTheDocument() + // Assert - Only root level page should be visible initially + expect(screen.getByText(rootPage.page_name))!.toBeInTheDocument() + // Child pages should not be visible until expanded + // Child pages should not be visible until expanded + // Child pages should not be visible until expanded + // Child pages should not be visible until expanded + // Child pages should not be visible until expanded + // Child pages should not be visible until expanded + // Child pages should not be visible until expanded + // Child pages should not be visible until expanded + // Child pages should not be visible until expanded + // Child pages should not be visible until expanded + // Child pages should not be visible until expanded + // Child pages should not be visible until expanded + // Child pages should not be visible until expanded + // Child pages should not be visible until expanded + // Child pages should not be visible until expanded + // Child pages should not be visible until expanded + // Child pages should not be visible until expanded + // Child pages should not be visible until expanded + // Child pages should not be visible until expanded + // Child pages should not be visible until expanded + // Child pages should not be visible until expanded + // Child pages should not be visible until expanded + // Child pages should not be visible until expanded + // Child pages should not be visible until expanded + // Child pages should not be visible until expanded + // Child pages should not be visible until expanded + // Child pages should not be visible until expanded + // Child pages should not be visible until expanded + // Child pages should not be visible until expanded + // Child pages should not be visible until expanded + // Child pages should not be visible until expanded // Child pages should not be visible until expanded expect(screen.queryByText(childPage1.page_name)).not.toBeInTheDocument() }) @@ -540,9 +606,9 @@ describe('PageSelector', () => { if (arrowButton) fireEvent.click(arrowButton) - expect(screen.getByText(rootPage.page_name)).toBeInTheDocument() - expect(screen.getByText(childPage1.page_name)).toBeInTheDocument() - expect(screen.getByText(childPage2.page_name)).toBeInTheDocument() + expect(screen.getByText(rootPage.page_name))!.toBeInTheDocument() + expect(screen.getByText(childPage1.page_name))!.toBeInTheDocument() + expect(screen.getByText(childPage2.page_name))!.toBeInTheDocument() }) it('should maintain currentPreviewPageId state', () => { @@ -560,7 +626,7 @@ describe('PageSelector', () => { render() const previewButtons = screen.getAllByText('common.dataSource.notion.selector.preview') - fireEvent.click(previewButtons[0]) + fireEvent.click(previewButtons[0]!) expect(mockOnPreview).toHaveBeenCalledWith('page-1') }) @@ -596,13 +662,14 @@ describe('PageSelector', () => { }) const { rerender } = render() - expect(screen.getByText('Page 1')).toBeInTheDocument() + expect(screen.getByText('Page 1'))!.toBeInTheDocument() // Change credential rerender() // Assert - Component should still render correctly - expect(screen.getByText('Page 1')).toBeInTheDocument() + // Assert - Component should still render correctly + expect(screen.getByText('Page 1'))!.toBeInTheDocument() }) it('should filter root pages correctly on initialization', () => { @@ -615,7 +682,8 @@ describe('PageSelector', () => { render() // Assert - Only root level pages visible - expect(screen.getByText(rootPage.page_name)).toBeInTheDocument() + // Assert - Only root level pages visible + expect(screen.getByText(rootPage.page_name))!.toBeInTheDocument() expect(screen.queryByText(childPage1.page_name)).not.toBeInTheDocument() }) @@ -633,7 +701,8 @@ describe('PageSelector', () => { render() // Assert - Orphan page should be visible at root level - expect(screen.getByText('Orphan Page')).toBeInTheDocument() + // Assert - Orphan page should be visible at root level + expect(screen.getByText('Orphan Page'))!.toBeInTheDocument() }) }) @@ -654,8 +723,9 @@ describe('PageSelector', () => { fireEvent.click(expandArrow) // Assert - Children should be visible - expect(screen.getByText(childPage1.page_name)).toBeInTheDocument() - expect(screen.getByText(childPage2.page_name)).toBeInTheDocument() + // Assert - Children should be visible + expect(screen.getByText(childPage1.page_name))!.toBeInTheDocument() + expect(screen.getByText(childPage2.page_name))!.toBeInTheDocument() }) it('should have stable handleToggle that collapses descendants', () => { @@ -675,6 +745,37 @@ describe('PageSelector', () => { fireEvent.click(expandArrow) } + // Assert - Children should be hidden again + // Assert - Children should be hidden again + // Assert - Children should be hidden again + // Assert - Children should be hidden again + // Assert - Children should be hidden again + // Assert - Children should be hidden again + // Assert - Children should be hidden again + // Assert - Children should be hidden again + // Assert - Children should be hidden again + // Assert - Children should be hidden again + // Assert - Children should be hidden again + // Assert - Children should be hidden again + // Assert - Children should be hidden again + // Assert - Children should be hidden again + // Assert - Children should be hidden again + // Assert - Children should be hidden again + // Assert - Children should be hidden again + // Assert - Children should be hidden again + // Assert - Children should be hidden again + // Assert - Children should be hidden again + // Assert - Children should be hidden again + // Assert - Children should be hidden again + // Assert - Children should be hidden again + // Assert - Children should be hidden again + // Assert - Children should be hidden again + // Assert - Children should be hidden again + // Assert - Children should be hidden again + // Assert - Children should be hidden again + // Assert - Children should be hidden again + // Assert - Children should be hidden again + // Assert - Children should be hidden again // Assert - Children should be hidden again expect(screen.queryByText(childPage1.page_name)).not.toBeInTheDocument() expect(screen.queryByText(childPage2.page_name)).not.toBeInTheDocument() @@ -698,7 +799,7 @@ describe('PageSelector', () => { // Assert - onSelect should be called with the page and its descendants expect(mockOnSelect).toHaveBeenCalled() - const selectedSet = mockOnSelect.mock.calls[0][0] as Set + const selectedSet = mockOnSelect.mock.calls[0]![0] as Set expect(selectedSet.has('root-page')).toBe(true) }) @@ -752,7 +853,7 @@ describe('PageSelector', () => { // Assert - Tree structure should be built (verified by expand functionality) const expandArrow = document.querySelector('[class*="hover:bg-components-button-ghost-bg-hover"]') - expect(expandArrow).toBeInTheDocument() // Root page has children + expect(expandArrow)!.toBeInTheDocument() // Root page has children }) it('should recompute listMapWithChildrenAndDescendants when list changes', () => { @@ -763,7 +864,7 @@ describe('PageSelector', () => { }) const { rerender } = render() - expect(screen.getByText('Page 1')).toBeInTheDocument() + expect(screen.getByText('Page 1'))!.toBeInTheDocument() // Update with new list const newList = [ @@ -772,7 +873,7 @@ describe('PageSelector', () => { ] rerender() - expect(screen.getByText('Page 1')).toBeInTheDocument() + expect(screen.getByText('Page 1'))!.toBeInTheDocument() // Page 2 won't show because dataList state hasn't updated (only resets on credentialId change) }) @@ -793,7 +894,8 @@ describe('PageSelector', () => { rerender() // Assert - Should not throw - expect(screen.getByText('Page 1')).toBeInTheDocument() + // Assert - Should not throw + expect(screen.getByText('Page 1'))!.toBeInTheDocument() }) it('should handle empty list in memoization', () => { @@ -804,7 +906,7 @@ describe('PageSelector', () => { render() - expect(screen.getByText('common.dataSource.notion.selector.noSearchResult')).toBeInTheDocument() + expect(screen.getByText('common.dataSource.notion.selector.noSearchResult'))!.toBeInTheDocument() }) }) @@ -819,6 +921,37 @@ describe('PageSelector', () => { render() + // Initially children are hidden + // Initially children are hidden + // Initially children are hidden + // Initially children are hidden + // Initially children are hidden + // Initially children are hidden + // Initially children are hidden + // Initially children are hidden + // Initially children are hidden + // Initially children are hidden + // Initially children are hidden + // Initially children are hidden + // Initially children are hidden + // Initially children are hidden + // Initially children are hidden + // Initially children are hidden + // Initially children are hidden + // Initially children are hidden + // Initially children are hidden + // Initially children are hidden + // Initially children are hidden + // Initially children are hidden + // Initially children are hidden + // Initially children are hidden + // Initially children are hidden + // Initially children are hidden + // Initially children are hidden + // Initially children are hidden + // Initially children are hidden + // Initially children are hidden + // Initially children are hidden // Initially children are hidden expect(screen.queryByText(childPage1.page_name)).not.toBeInTheDocument() @@ -827,7 +960,8 @@ describe('PageSelector', () => { fireEvent.click(expandArrow) // Children become visible - expect(screen.getByText(childPage1.page_name)).toBeInTheDocument() + // Children become visible + expect(screen.getByText(childPage1.page_name))!.toBeInTheDocument() }) it('should check/uncheck page when clicking checkbox', () => { @@ -873,11 +1007,11 @@ describe('PageSelector', () => { render() const radios = getAllRadios() - fireEvent.click(radios[1]) // Click on page-2 + fireEvent.click(radios[1]!) // Click on page-2 // Assert - Should clear page-1 and select page-2 expect(mockOnSelect).toHaveBeenCalled() - const selectedSet = mockOnSelect.mock.calls[0][0] as Set + const selectedSet = mockOnSelect.mock.calls[0]![0] as Set expect(selectedSet.has('page-2')).toBe(true) expect(selectedSet.has('page-1')).toBe(false) }) @@ -912,7 +1046,7 @@ describe('PageSelector', () => { // Assert - Only the clicked page should be selected (no descendants) expect(mockOnSelect).toHaveBeenCalled() - const selectedSet = mockOnSelect.mock.calls[0][0] as Set + const selectedSet = mockOnSelect.mock.calls[0]![0] as Set expect(selectedSet.size).toBe(1) expect(selectedSet.has('root-page')).toBe(true) }) @@ -927,7 +1061,7 @@ describe('PageSelector', () => { render() - expect(screen.getByText('common.dataSource.notion.selector.noSearchResult')).toBeInTheDocument() + expect(screen.getByText('common.dataSource.notion.selector.noSearchResult'))!.toBeInTheDocument() }) it('should handle null page_icon', () => { @@ -941,7 +1075,7 @@ describe('PageSelector', () => { // Assert - NotionIcon renders svg (RiFileTextLine) when page_icon is null const notionIcon = document.querySelector('.h-5.w-5') - expect(notionIcon).toBeInTheDocument() + expect(notionIcon)!.toBeInTheDocument() }) it('should handle page_icon with all properties', () => { @@ -956,7 +1090,8 @@ describe('PageSelector', () => { render() // Assert - NotionIcon renders the emoji - expect(screen.getByText('📄')).toBeInTheDocument() + // Assert - NotionIcon renders the emoji + expect(screen.getByText('📄'))!.toBeInTheDocument() }) it('should handle empty searchValue correctly', () => { @@ -964,7 +1099,7 @@ describe('PageSelector', () => { render() - expect(screen.getByTestId('virtual-list')).toBeInTheDocument() + expect(screen.getByTestId('virtual-list'))!.toBeInTheDocument() }) it('should handle special characters in page name', () => { @@ -976,7 +1111,7 @@ describe('PageSelector', () => { render() - expect(screen.getByText('Test ')).toBeInTheDocument() + expect(screen.getByText('Test '))!.toBeInTheDocument() }) it('should handle unicode characters in page name', () => { @@ -988,7 +1123,7 @@ describe('PageSelector', () => { render() - expect(screen.getByText('测试页面 🔍 привет')).toBeInTheDocument() + expect(screen.getByText('测试页面 🔍 привет'))!.toBeInTheDocument() }) it('should handle very long page names', () => { @@ -1001,7 +1136,7 @@ describe('PageSelector', () => { render() - expect(screen.getByText(longName)).toBeInTheDocument() + expect(screen.getByText(longName))!.toBeInTheDocument() }) it('should handle deeply nested hierarchy', () => { @@ -1027,7 +1162,8 @@ describe('PageSelector', () => { render() // Assert - Only root level visible - expect(screen.getByText('Level 0')).toBeInTheDocument() + // Assert - Only root level visible + expect(screen.getByText('Level 0'))!.toBeInTheDocument() expect(screen.queryByText('Level 1')).not.toBeInTheDocument() }) @@ -1048,7 +1184,8 @@ describe('PageSelector', () => { render() // Assert - Should render the orphan page at root level - expect(screen.getByText('Orphan Page')).toBeInTheDocument() + // Assert - Should render the orphan page at root level + expect(screen.getByText('Orphan Page'))!.toBeInTheDocument() }) it('should handle empty checkedIds Set', () => { @@ -1057,7 +1194,7 @@ describe('PageSelector', () => { render() const checkbox = getCheckbox() - expect(checkbox).toBeInTheDocument() + expect(checkbox)!.toBeInTheDocument() expect(isCheckboxChecked(checkbox)).toBe(false) }) @@ -1067,7 +1204,7 @@ describe('PageSelector', () => { render() const checkbox = getCheckbox() - expect(checkbox).toBeInTheDocument() + expect(checkbox)!.toBeInTheDocument() expect(isCheckboxDisabled(checkbox)).toBe(false) }) @@ -1112,16 +1249,16 @@ describe('PageSelector', () => { render() - expect(screen.getByTestId('virtual-list')).toBeInTheDocument() + expect(screen.getByTestId('virtual-list'))!.toBeInTheDocument() if (propVariation.canPreview) - expect(screen.getByText('common.dataSource.notion.selector.preview')).toBeInTheDocument() + expect(screen.getByText('common.dataSource.notion.selector.preview'))!.toBeInTheDocument() else expect(screen.queryByText('common.dataSource.notion.selector.preview')).not.toBeInTheDocument() if (propVariation.isMultipleChoice) - expect(getCheckbox()).toBeInTheDocument() + expect(getCheckbox())!.toBeInTheDocument() else - expect(getRadio()).toBeInTheDocument() + expect(getRadio())!.toBeInTheDocument() }) it('should handle all default prop values', () => { @@ -1140,8 +1277,9 @@ describe('PageSelector', () => { render() // Assert - Defaults should be applied - expect(getCheckbox()).toBeInTheDocument() - expect(screen.getByText('common.dataSource.notion.selector.preview')).toBeInTheDocument() + // Assert - Defaults should be applied + expect(getCheckbox())!.toBeInTheDocument() + expect(screen.getByText('common.dataSource.notion.selector.preview'))!.toBeInTheDocument() }) }) @@ -1166,8 +1304,8 @@ describe('PageSelector', () => { recursivePushInParentDescendants(pagesMap, listTreeMap, childEntry, childEntry) expect(listTreeMap.parent).toBeDefined() - expect(listTreeMap.parent.children.has('child')).toBe(true) - expect(listTreeMap.parent.descendants.has('child')).toBe(true) + expect(listTreeMap.parent!.children.has('child')).toBe(true) + expect(listTreeMap.parent!.descendants.has('child')).toBe(true) expect(childEntry.depth).toBe(1) expect(childEntry.ancestors).toContain('Parent') }) @@ -1274,8 +1412,8 @@ describe('PageSelector', () => { expect(l2Entry.depth).toBe(2) expect(l2Entry.ancestors).toEqual(['Level 0', 'Level 1']) - expect(listTreeMap.l1.children.has('l2')).toBe(true) - expect(listTreeMap.l0.descendants.has('l2')).toBe(true) + expect(listTreeMap.l1!.children.has('l2')).toBe(true) + expect(listTreeMap.l0!.descendants.has('l2')).toBe(true) }) it('should update existing parent entry', () => { @@ -1329,7 +1467,7 @@ describe('PageSelector', () => { // Assert - Item should have preview styling class const itemContainer = screen.getByText('Test Page').closest('[class*="group"]') - expect(itemContainer).toHaveClass('bg-state-base-hover') + expect(itemContainer)!.toHaveClass('bg-state-base-hover') }) it('should show arrow for pages with children', () => { @@ -1343,7 +1481,7 @@ describe('PageSelector', () => { // Assert - Root page should have expand arrow const arrowContainer = document.querySelector('[class*="hover:bg-components-button-ghost-bg-hover"]') - expect(arrowContainer).toBeInTheDocument() + expect(arrowContainer)!.toBeInTheDocument() }) it('should not show arrow for leaf pages', () => { diff --git a/web/app/components/datasets/documents/create-from-pipeline/data-source/online-documents/page-selector/__tests__/utils.spec.ts b/web/app/components/datasets/documents/create-from-pipeline/data-source/online-documents/page-selector/__tests__/utils.spec.ts index 2a081ef418..a7175a47de 100644 --- a/web/app/components/datasets/documents/create-from-pipeline/data-source/online-documents/page-selector/__tests__/utils.spec.ts +++ b/web/app/components/datasets/documents/create-from-pipeline/data-source/online-documents/page-selector/__tests__/utils.spec.ts @@ -28,11 +28,11 @@ describe('recursivePushInParentDescendants', () => { child1: makePageEntry({ page_id: 'child1', parent_id: 'parent1', page_name: 'Child' }), } - recursivePushInParentDescendants(pagesMap, listTreeMap, listTreeMap.child1, listTreeMap.child1) + recursivePushInParentDescendants(pagesMap, listTreeMap, listTreeMap.child1!, listTreeMap.child1!) expect(listTreeMap.parent1).toBeDefined() - expect(listTreeMap.parent1.children.has('child1')).toBe(true) - expect(listTreeMap.parent1.descendants.has('child1')).toBe(true) + expect(listTreeMap.parent1!.children.has('child1')).toBe(true) + expect(listTreeMap.parent1!.descendants.has('child1')).toBe(true) }) it('should recursively populate ancestors for deeply nested items', () => { @@ -47,11 +47,11 @@ describe('recursivePushInParentDescendants', () => { child: makePageEntry({ page_id: 'child', parent_id: 'parent', page_name: 'Child' }), } - recursivePushInParentDescendants(pagesMap, listTreeMap, listTreeMap.child, listTreeMap.child) + recursivePushInParentDescendants(pagesMap, listTreeMap, listTreeMap.child!, listTreeMap.child!) - expect(listTreeMap.child.depth).toBe(2) - expect(listTreeMap.child.ancestors).toContain('Grandparent') - expect(listTreeMap.child.ancestors).toContain('Parent') + expect(listTreeMap.child!.depth).toBe(2) + expect(listTreeMap.child!.ancestors).toContain('Grandparent') + expect(listTreeMap.child!.ancestors).toContain('Parent') }) it('should do nothing for root parent', () => { @@ -63,7 +63,7 @@ describe('recursivePushInParentDescendants', () => { root_child: makePageEntry({ page_id: 'root_child', parent_id: 'root', page_name: 'Root Child' }), } - recursivePushInParentDescendants(pagesMap, listTreeMap, listTreeMap.root_child, listTreeMap.root_child) + recursivePushInParentDescendants(pagesMap, listTreeMap, listTreeMap.root_child!, listTreeMap.root_child!) // No new entries should be added since parent is root expect(Object.keys(listTreeMap)).toEqual(['root_child']) @@ -76,7 +76,7 @@ describe('recursivePushInParentDescendants', () => { // Should not throw recursivePushInParentDescendants(pagesMap, listTreeMap, current, current) - expect(listTreeMap.orphan.depth).toBe(0) + expect(listTreeMap.orphan!.depth).toBe(0) }) it('should add to existing parent entry when parent already in tree', () => { @@ -91,10 +91,10 @@ describe('recursivePushInParentDescendants', () => { child2: makePageEntry({ page_id: 'child2', parent_id: 'parent', page_name: 'Child2' }), } - recursivePushInParentDescendants(pagesMap, listTreeMap, listTreeMap.child2, listTreeMap.child2) + recursivePushInParentDescendants(pagesMap, listTreeMap, listTreeMap.child2!, listTreeMap.child2!) - expect(listTreeMap.parent.children.has('child2')).toBe(true) - expect(listTreeMap.parent.descendants.has('child2')).toBe(true) - expect(listTreeMap.parent.children.has('child1')).toBe(true) + expect(listTreeMap.parent!.children.has('child2')).toBe(true) + expect(listTreeMap.parent!.descendants.has('child2')).toBe(true) + expect(listTreeMap.parent!.children.has('child1')).toBe(true) }) }) diff --git a/web/app/components/datasets/documents/create-from-pipeline/data-source/online-drive/__tests__/index.spec.tsx b/web/app/components/datasets/documents/create-from-pipeline/data-source/online-drive/__tests__/index.spec.tsx index 7c1941afd9..6eed119ede 100644 --- a/web/app/components/datasets/documents/create-from-pipeline/data-source/online-drive/__tests__/index.spec.tsx +++ b/web/app/components/datasets/documents/create-from-pipeline/data-source/online-drive/__tests__/index.spec.tsx @@ -259,8 +259,8 @@ describe('OnlineDrive', () => { render() - expect(screen.getByTestId('header')).toBeInTheDocument() - expect(screen.getByTestId('file-list')).toBeInTheDocument() + expect(screen.getByTestId('header'))!.toBeInTheDocument() + expect(screen.getByTestId('file-list'))!.toBeInTheDocument() }) it('should render Header with correct props', () => { @@ -271,9 +271,9 @@ describe('OnlineDrive', () => { render() - expect(screen.getByTestId('header-doc-title')).toHaveTextContent('Docs') - expect(screen.getByTestId('header-plugin-name')).toHaveTextContent('My Online Drive') - expect(screen.getByTestId('header-credential-id')).toHaveTextContent('cred-123') + expect(screen.getByTestId('header-doc-title'))!.toHaveTextContent('Docs') + expect(screen.getByTestId('header-plugin-name'))!.toHaveTextContent('My Online Drive') + expect(screen.getByTestId('header-credential-id'))!.toHaveTextContent('cred-123') }) it('should render FileList with correct props', () => { @@ -290,11 +290,11 @@ describe('OnlineDrive', () => { render() - expect(screen.getByTestId('file-list')).toBeInTheDocument() - expect(screen.getByTestId('file-list-keywords')).toHaveTextContent('search-term') - expect(screen.getByTestId('file-list-breadcrumbs')).toHaveTextContent('folder1/folder2') - expect(screen.getByTestId('file-list-bucket')).toHaveTextContent('my-bucket') - expect(screen.getByTestId('file-list-selected-count')).toHaveTextContent('2') + expect(screen.getByTestId('file-list'))!.toBeInTheDocument() + expect(screen.getByTestId('file-list-keywords'))!.toHaveTextContent('search-term') + expect(screen.getByTestId('file-list-breadcrumbs'))!.toHaveTextContent('folder1/folder2') + expect(screen.getByTestId('file-list-bucket'))!.toHaveTextContent('my-bucket') + expect(screen.getByTestId('file-list-selected-count'))!.toHaveTextContent('2') }) it('should pass docLink with correct path to Header', () => { @@ -371,7 +371,7 @@ describe('OnlineDrive', () => { render() - expect(screen.getByTestId('header-plugin-name')).toHaveTextContent('Custom Online Drive') + expect(screen.getByTestId('header-plugin-name'))!.toHaveTextContent('Custom Online Drive') }) }) @@ -411,7 +411,7 @@ describe('OnlineDrive', () => { render() - expect(screen.getByTestId('file-list-is-in-pipeline')).toHaveTextContent('true') + expect(screen.getByTestId('file-list-is-in-pipeline'))!.toHaveTextContent('true') }) }) @@ -421,7 +421,7 @@ describe('OnlineDrive', () => { render() - expect(screen.getByTestId('file-list-support-batch')).toHaveTextContent('true') + expect(screen.getByTestId('file-list-support-batch'))!.toHaveTextContent('true') }) it('should pass supportBatchUpload false to FileList when supportBatchUpload is false', () => { @@ -429,7 +429,7 @@ describe('OnlineDrive', () => { render() - expect(screen.getByTestId('file-list-support-batch')).toHaveTextContent('false') + expect(screen.getByTestId('file-list-support-batch'))!.toHaveTextContent('false') }) it.each([ @@ -441,7 +441,7 @@ describe('OnlineDrive', () => { render() - expect(screen.getByTestId('file-list-support-batch')).toHaveTextContent(expected) + expect(screen.getByTestId('file-list-support-batch'))!.toHaveTextContent(expected) }) }) @@ -504,7 +504,7 @@ describe('OnlineDrive', () => { render() await waitFor(() => { - expect(screen.getByTestId('file-list-loading')).toHaveTextContent('true') + expect(screen.getByTestId('file-list-loading'))!.toHaveTextContent('true') }) }) @@ -566,7 +566,8 @@ describe('OnlineDrive', () => { render() // Assert - filteredOnlineDriveFileList should have 2 items matching 'test' - expect(screen.getByTestId('file-list-count')).toHaveTextContent('2') + // Assert - filteredOnlineDriveFileList should have 2 items matching 'test' + expect(screen.getByTestId('file-list-count'))!.toHaveTextContent('2') }) it('should return all files when keywords is empty', () => { @@ -580,7 +581,7 @@ describe('OnlineDrive', () => { render() - expect(screen.getByTestId('file-list-count')).toHaveTextContent('3') + expect(screen.getByTestId('file-list-count'))!.toHaveTextContent('3') }) it('should filter files case-insensitively', () => { @@ -594,7 +595,7 @@ describe('OnlineDrive', () => { render() - expect(screen.getByTestId('file-list-count')).toHaveTextContent('2') + expect(screen.getByTestId('file-list-count'))!.toHaveTextContent('2') }) }) @@ -932,7 +933,7 @@ describe('OnlineDrive', () => { render() - expect(screen.getByTestId('header-credentials-count')).toHaveTextContent('0') + expect(screen.getByTestId('header-credentials-count'))!.toHaveTextContent('0') }) it('should handle undefined credentials data', () => { @@ -943,7 +944,7 @@ describe('OnlineDrive', () => { render() - expect(screen.getByTestId('header-credentials-count')).toHaveTextContent('0') + expect(screen.getByTestId('header-credentials-count'))!.toHaveTextContent('0') }) it('should handle undefined pipelineId', async () => { @@ -969,7 +970,7 @@ describe('OnlineDrive', () => { render() - expect(screen.getByTestId('file-list-count')).toHaveTextContent('0') + expect(screen.getByTestId('file-list-count'))!.toHaveTextContent('0') }) it('should handle empty breadcrumbs', () => { @@ -978,7 +979,7 @@ describe('OnlineDrive', () => { render() - expect(screen.getByTestId('file-list-breadcrumbs')).toHaveTextContent('') + expect(screen.getByTestId('file-list-breadcrumbs'))!.toHaveTextContent('') }) it('should handle empty bucket', () => { @@ -987,7 +988,7 @@ describe('OnlineDrive', () => { render() - expect(screen.getByTestId('file-list-bucket')).toHaveTextContent('') + expect(screen.getByTestId('file-list-bucket'))!.toHaveTextContent('') }) it('should handle special characters in keywords', () => { @@ -1001,7 +1002,8 @@ describe('OnlineDrive', () => { render() // Assert - Should find file with special characters - expect(screen.getByTestId('file-list-count')).toHaveTextContent('1') + // Assert - Should find file with special characters + expect(screen.getByTestId('file-list-count'))!.toHaveTextContent('1') }) it('should handle very long file names', () => { @@ -1013,7 +1015,7 @@ describe('OnlineDrive', () => { render() - expect(screen.getByTestId('file-list-count')).toHaveTextContent('1') + expect(screen.getByTestId('file-list-count'))!.toHaveTextContent('1') }) it('should handle bucket list initiation response', async () => { @@ -1051,10 +1053,10 @@ describe('OnlineDrive', () => { render() - expect(screen.getByTestId('header')).toBeInTheDocument() - expect(screen.getByTestId('file-list')).toBeInTheDocument() - expect(screen.getByTestId('file-list-is-in-pipeline')).toHaveTextContent(String(propVariation.isInPipeline)) - expect(screen.getByTestId('file-list-support-batch')).toHaveTextContent(String(propVariation.supportBatchUpload)) + expect(screen.getByTestId('header'))!.toBeInTheDocument() + expect(screen.getByTestId('file-list'))!.toBeInTheDocument() + expect(screen.getByTestId('file-list-is-in-pipeline'))!.toHaveTextContent(String(propVariation.isInPipeline)) + expect(screen.getByTestId('file-list-support-batch'))!.toHaveTextContent(String(propVariation.supportBatchUpload)) }) it.each([ @@ -1117,7 +1119,7 @@ describe('Header', () => { render(
) - expect(screen.getByText('Documentation')).toBeInTheDocument() + expect(screen.getByText('Documentation'))!.toBeInTheDocument() }) it('should render doc link with correct href', () => { @@ -1129,9 +1131,9 @@ describe('Header', () => { render(
) const link = screen.getByRole('link') - expect(link).toHaveAttribute('href', 'https://custom-docs.com/path') - expect(link).toHaveAttribute('target', '_blank') - expect(link).toHaveAttribute('rel', 'noopener noreferrer') + expect(link)!.toHaveAttribute('href', 'https://custom-docs.com/path') + expect(link)!.toHaveAttribute('target', '_blank') + expect(link)!.toHaveAttribute('rel', 'noopener noreferrer') }) it('should render doc title text', () => { @@ -1139,7 +1141,7 @@ describe('Header', () => { render(
) - expect(screen.getByText('My Documentation Title')).toBeInTheDocument() + expect(screen.getByText('My Documentation Title'))!.toBeInTheDocument() }) it('should render configuration button', () => { @@ -1147,7 +1149,7 @@ describe('Header', () => { render(
) - expect(screen.getByRole('button')).toBeInTheDocument() + expect(screen.getByRole('button'))!.toBeInTheDocument() }) }) @@ -1164,7 +1166,7 @@ describe('Header', () => { render(
) if (docTitle) - expect(screen.getByText(docTitle)).toBeInTheDocument() + expect(screen.getByText(docTitle))!.toBeInTheDocument() }) }) @@ -1178,7 +1180,7 @@ describe('Header', () => { render(
) - expect(screen.getByRole('link')).toHaveAttribute('href', docLink) + expect(screen.getByRole('link'))!.toHaveAttribute('href', docLink) }) }) @@ -1209,7 +1211,7 @@ describe('Header', () => { render(
) const titleSpan = screen.getByTitle('Accessible Title') - expect(titleSpan).toBeInTheDocument() + expect(titleSpan)!.toBeInTheDocument() }) }) }) @@ -1437,10 +1439,10 @@ describe('utils', () => { const result = convertOnlineDriveData(data, [], 'my-bucket') expect(result.fileList).toHaveLength(4) - expect(result.fileList[0].type).toBe(OnlineDriveFileType.folder) - expect(result.fileList[1].type).toBe(OnlineDriveFileType.file) - expect(result.fileList[2].type).toBe(OnlineDriveFileType.folder) - expect(result.fileList[3].type).toBe(OnlineDriveFileType.file) + expect(result.fileList[0]!.type).toBe(OnlineDriveFileType.folder) + expect(result.fileList[1]!.type).toBe(OnlineDriveFileType.file) + expect(result.fileList[2]!.type).toBe(OnlineDriveFileType.folder) + expect(result.fileList[3]!.type).toBe(OnlineDriveFileType.file) }) }) @@ -1539,7 +1541,7 @@ describe('utils', () => { const result = convertOnlineDriveData(data, [], 'my-bucket') - expect(result.fileList[0].size).toBe(0) + expect(result.fileList[0]!.size).toBe(0) }) it('should handle files with very large size', () => { @@ -1555,7 +1557,7 @@ describe('utils', () => { const result = convertOnlineDriveData(data, [], 'my-bucket') - expect(result.fileList[0].size).toBe(largeSize) + expect(result.fileList[0]!.size).toBe(largeSize) }) it('should handle files with special characters in name', () => { @@ -1574,9 +1576,9 @@ describe('utils', () => { const result = convertOnlineDriveData(data, [], 'my-bucket') - expect(result.fileList[0].name).toBe('file[1] (copy).txt') - expect(result.fileList[1].name).toBe('doc-with-dash_and_underscore.pdf') - expect(result.fileList[2].name).toBe('file with spaces.txt') + expect(result.fileList[0]!.name).toBe('file[1] (copy).txt') + expect(result.fileList[1]!.name).toBe('doc-with-dash_and_underscore.pdf') + expect(result.fileList[2]!.name).toBe('file with spaces.txt') }) it('should handle complex next_page_parameters', () => { diff --git a/web/app/components/datasets/documents/create-from-pipeline/data-source/online-drive/__tests__/utils.spec.ts b/web/app/components/datasets/documents/create-from-pipeline/data-source/online-drive/__tests__/utils.spec.ts index 7c5761be8a..9ac2ef9f89 100644 --- a/web/app/components/datasets/documents/create-from-pipeline/data-source/online-drive/__tests__/utils.spec.ts +++ b/web/app/components/datasets/documents/create-from-pipeline/data-source/online-drive/__tests__/utils.spec.ts @@ -93,10 +93,10 @@ describe('online-drive utils', () => { const result = convertOnlineDriveData(data, [], 'bucket-1') expect(result.fileList).toHaveLength(2) - expect(result.fileList[0].type).toBe(OnlineDriveFileType.file) - expect(result.fileList[0].size).toBe(100) - expect(result.fileList[1].type).toBe(OnlineDriveFileType.folder) - expect(result.fileList[1].size).toBeUndefined() + expect(result.fileList[0]!.type).toBe(OnlineDriveFileType.file) + expect(result.fileList[0]!.size).toBe(100) + expect(result.fileList[1]!.type).toBe(OnlineDriveFileType.folder) + expect(result.fileList[1]!.size).toBeUndefined() expect(result.isTruncated).toBe(true) expect(result.nextPageParameters).toEqual({ token: 'next' }) expect(result.hasBucket).toBe(true) diff --git a/web/app/components/datasets/documents/create-from-pipeline/data-source/online-drive/file-list/header/__tests__/index.spec.tsx b/web/app/components/datasets/documents/create-from-pipeline/data-source/online-drive/file-list/header/__tests__/index.spec.tsx index 07308361ad..dcb1922fe9 100644 --- a/web/app/components/datasets/documents/create-from-pipeline/data-source/online-drive/file-list/header/__tests__/index.spec.tsx +++ b/web/app/components/datasets/documents/create-from-pipeline/data-source/online-drive/file-list/header/__tests__/index.spec.tsx @@ -60,7 +60,8 @@ describe('Header', () => { render(
) // Assert - search input should be visible - expect(screen.getByPlaceholderText('datasetPipeline.onlineDrive.breadcrumbs.searchPlaceholder')).toBeInTheDocument() + // Assert - search input should be visible + expect(screen.getByPlaceholderText('datasetPipeline.onlineDrive.breadcrumbs.searchPlaceholder'))!.toBeInTheDocument() }) it('should render with correct container styles', () => { @@ -70,12 +71,12 @@ describe('Header', () => { // Assert - container should have correct class names const wrapper = container.firstChild as HTMLElement - expect(wrapper).toHaveClass('flex') - expect(wrapper).toHaveClass('items-center') - expect(wrapper).toHaveClass('gap-x-2') - expect(wrapper).toHaveClass('bg-components-panel-bg') - expect(wrapper).toHaveClass('p-1') - expect(wrapper).toHaveClass('pl-3') + expect(wrapper)!.toHaveClass('flex') + expect(wrapper)!.toHaveClass('items-center') + expect(wrapper)!.toHaveClass('gap-x-2') + expect(wrapper)!.toHaveClass('bg-components-panel-bg') + expect(wrapper)!.toHaveClass('p-1') + expect(wrapper)!.toHaveClass('pl-3') }) it('should render Input component with correct props', () => { @@ -84,8 +85,8 @@ describe('Header', () => { render(
) const input = screen.getByPlaceholderText('datasetPipeline.onlineDrive.breadcrumbs.searchPlaceholder') - expect(input).toBeInTheDocument() - expect(input).toHaveValue('test-value') + expect(input)!.toBeInTheDocument() + expect(input)!.toHaveValue('test-value') }) it('should render Input with search icon', () => { @@ -95,7 +96,7 @@ describe('Header', () => { // Assert - Input should have search icon class const searchIcon = container.querySelector('.i-ri-search-line.h-4.w-4') - expect(searchIcon).toBeInTheDocument() + expect(searchIcon)!.toBeInTheDocument() }) it('should render Input with correct wrapper width', () => { @@ -105,7 +106,7 @@ describe('Header', () => { // Assert - Input wrapper should have w-[200px] class const inputWrapper = container.querySelector('.w-\\[200px\\]') - expect(inputWrapper).toBeInTheDocument() + expect(inputWrapper)!.toBeInTheDocument() }) }) @@ -117,7 +118,7 @@ describe('Header', () => { render(
) const input = screen.getByPlaceholderText('datasetPipeline.onlineDrive.breadcrumbs.searchPlaceholder') - expect(input).toHaveValue('') + expect(input)!.toHaveValue('') }) it('should display input value correctly', () => { @@ -126,7 +127,7 @@ describe('Header', () => { render(
) const input = screen.getByPlaceholderText('datasetPipeline.onlineDrive.breadcrumbs.searchPlaceholder') - expect(input).toHaveValue('search-query') + expect(input)!.toHaveValue('search-query') }) it('should handle special characters in inputValue', () => { @@ -136,7 +137,7 @@ describe('Header', () => { render(
) const input = screen.getByPlaceholderText('datasetPipeline.onlineDrive.breadcrumbs.searchPlaceholder') - expect(input).toHaveValue(specialChars) + expect(input)!.toHaveValue(specialChars) }) it('should handle unicode characters in inputValue', () => { @@ -146,7 +147,7 @@ describe('Header', () => { render(
) const input = screen.getByPlaceholderText('datasetPipeline.onlineDrive.breadcrumbs.searchPlaceholder') - expect(input).toHaveValue(unicodeValue) + expect(input)!.toHaveValue(unicodeValue) }) }) @@ -157,7 +158,8 @@ describe('Header', () => { render(
) // Assert - Component should render without errors - expect(screen.getByPlaceholderText('datasetPipeline.onlineDrive.breadcrumbs.searchPlaceholder')).toBeInTheDocument() + // Assert - Component should render without errors + expect(screen.getByPlaceholderText('datasetPipeline.onlineDrive.breadcrumbs.searchPlaceholder'))!.toBeInTheDocument() }) it('should render with single breadcrumb', () => { @@ -165,7 +167,7 @@ describe('Header', () => { render(
) - expect(screen.getByPlaceholderText('datasetPipeline.onlineDrive.breadcrumbs.searchPlaceholder')).toBeInTheDocument() + expect(screen.getByPlaceholderText('datasetPipeline.onlineDrive.breadcrumbs.searchPlaceholder'))!.toBeInTheDocument() }) it('should render with multiple breadcrumbs', () => { @@ -173,7 +175,7 @@ describe('Header', () => { render(
) - expect(screen.getByPlaceholderText('datasetPipeline.onlineDrive.breadcrumbs.searchPlaceholder')).toBeInTheDocument() + expect(screen.getByPlaceholderText('datasetPipeline.onlineDrive.breadcrumbs.searchPlaceholder'))!.toBeInTheDocument() }) }) @@ -184,7 +186,8 @@ describe('Header', () => { render(
) // Assert - keywords are passed through, component renders - expect(screen.getByPlaceholderText('datasetPipeline.onlineDrive.breadcrumbs.searchPlaceholder')).toBeInTheDocument() + // Assert - keywords are passed through, component renders + expect(screen.getByPlaceholderText('datasetPipeline.onlineDrive.breadcrumbs.searchPlaceholder'))!.toBeInTheDocument() }) }) @@ -194,7 +197,7 @@ describe('Header', () => { render(
) - expect(screen.getByPlaceholderText('datasetPipeline.onlineDrive.breadcrumbs.searchPlaceholder')).toBeInTheDocument() + expect(screen.getByPlaceholderText('datasetPipeline.onlineDrive.breadcrumbs.searchPlaceholder'))!.toBeInTheDocument() }) it('should render with bucket value', () => { @@ -202,7 +205,7 @@ describe('Header', () => { render(
) - expect(screen.getByPlaceholderText('datasetPipeline.onlineDrive.breadcrumbs.searchPlaceholder')).toBeInTheDocument() + expect(screen.getByPlaceholderText('datasetPipeline.onlineDrive.breadcrumbs.searchPlaceholder'))!.toBeInTheDocument() }) }) @@ -212,7 +215,7 @@ describe('Header', () => { render(
) - expect(screen.getByPlaceholderText('datasetPipeline.onlineDrive.breadcrumbs.searchPlaceholder')).toBeInTheDocument() + expect(screen.getByPlaceholderText('datasetPipeline.onlineDrive.breadcrumbs.searchPlaceholder'))!.toBeInTheDocument() }) it('should handle positive search results', () => { @@ -221,7 +224,8 @@ describe('Header', () => { render(
) // Assert - Breadcrumbs will show search results text when keywords exist and results > 0 - expect(screen.getByPlaceholderText('datasetPipeline.onlineDrive.breadcrumbs.searchPlaceholder')).toBeInTheDocument() + // Assert - Breadcrumbs will show search results text when keywords exist and results > 0 + expect(screen.getByPlaceholderText('datasetPipeline.onlineDrive.breadcrumbs.searchPlaceholder'))!.toBeInTheDocument() }) it('should handle large search results count', () => { @@ -229,7 +233,7 @@ describe('Header', () => { render(
) - expect(screen.getByPlaceholderText('datasetPipeline.onlineDrive.breadcrumbs.searchPlaceholder')).toBeInTheDocument() + expect(screen.getByPlaceholderText('datasetPipeline.onlineDrive.breadcrumbs.searchPlaceholder'))!.toBeInTheDocument() }) }) @@ -239,7 +243,7 @@ describe('Header', () => { render(
) - expect(screen.getByPlaceholderText('datasetPipeline.onlineDrive.breadcrumbs.searchPlaceholder')).toBeInTheDocument() + expect(screen.getByPlaceholderText('datasetPipeline.onlineDrive.breadcrumbs.searchPlaceholder'))!.toBeInTheDocument() }) it('should render correctly when isInPipeline is true', () => { @@ -247,7 +251,7 @@ describe('Header', () => { render(
) - expect(screen.getByPlaceholderText('datasetPipeline.onlineDrive.breadcrumbs.searchPlaceholder')).toBeInTheDocument() + expect(screen.getByPlaceholderText('datasetPipeline.onlineDrive.breadcrumbs.searchPlaceholder'))!.toBeInTheDocument() }) }) }) @@ -265,7 +269,7 @@ describe('Header', () => { expect(mockHandleInputChange).toHaveBeenCalledTimes(1) // Verify that onChange event was triggered (React's synthetic event structure) - expect(mockHandleInputChange.mock.calls[0][0]).toHaveProperty('type', 'change') + expect(mockHandleInputChange.mock.calls[0]![0]).toHaveProperty('type', 'change') }) it('should call handleInputChange on each keystroke', () => { @@ -290,7 +294,7 @@ describe('Header', () => { fireEvent.change(input, { target: { value: '' } }) expect(mockHandleInputChange).toHaveBeenCalledTimes(1) - expect(mockHandleInputChange.mock.calls[0][0]).toHaveProperty('type', 'change') + expect(mockHandleInputChange.mock.calls[0]![0]).toHaveProperty('type', 'change') }) it('should handle whitespace-only input', () => { @@ -302,7 +306,7 @@ describe('Header', () => { fireEvent.change(input, { target: { value: ' ' } }) expect(mockHandleInputChange).toHaveBeenCalledTimes(1) - expect(mockHandleInputChange.mock.calls[0][0]).toHaveProperty('type', 'change') + expect(mockHandleInputChange.mock.calls[0]![0]).toHaveProperty('type', 'change') }) }) @@ -317,7 +321,7 @@ describe('Header', () => { // Act - Find and click the clear icon container const clearButton = screen.getByTestId('input-clear') - expect(clearButton).toBeInTheDocument() + expect(clearButton)!.toBeInTheDocument() fireEvent.click(clearButton!) expect(mockHandleResetKeywords).toHaveBeenCalledTimes(1) @@ -338,7 +342,7 @@ describe('Header', () => { // Act & Assert - Clear icon should be visible const clearIcon = screen.getByTestId('input-clear') - expect(clearIcon).toBeInTheDocument() + expect(clearIcon)!.toBeInTheDocument() }) }) }) @@ -365,21 +369,23 @@ describe('Header', () => { rerender(
) // Assert - Component renders without errors - expect(screen.getByPlaceholderText('datasetPipeline.onlineDrive.breadcrumbs.searchPlaceholder')).toBeInTheDocument() + // Assert - Component renders without errors + expect(screen.getByPlaceholderText('datasetPipeline.onlineDrive.breadcrumbs.searchPlaceholder'))!.toBeInTheDocument() }) it('should re-render when inputValue changes', () => { const props = createDefaultProps({ inputValue: 'initial' }) const { rerender } = render(
) const input = screen.getByPlaceholderText('datasetPipeline.onlineDrive.breadcrumbs.searchPlaceholder') - expect(input).toHaveValue('initial') + expect(input)!.toHaveValue('initial') // Act - Rerender with different inputValue const newProps = createDefaultProps({ inputValue: 'changed' }) rerender(
) // Assert - Input value should be updated - expect(input).toHaveValue('changed') + // Assert - Input value should be updated + expect(input)!.toHaveValue('changed') }) it('should re-render when breadcrumbs change', () => { @@ -391,7 +397,8 @@ describe('Header', () => { rerender(
) // Assert - Component renders without errors - expect(screen.getByPlaceholderText('datasetPipeline.onlineDrive.breadcrumbs.searchPlaceholder')).toBeInTheDocument() + // Assert - Component renders without errors + expect(screen.getByPlaceholderText('datasetPipeline.onlineDrive.breadcrumbs.searchPlaceholder'))!.toBeInTheDocument() }) it('should re-render when keywords change', () => { @@ -403,7 +410,8 @@ describe('Header', () => { rerender(
) // Assert - Component renders without errors - expect(screen.getByPlaceholderText('datasetPipeline.onlineDrive.breadcrumbs.searchPlaceholder')).toBeInTheDocument() + // Assert - Component renders without errors + expect(screen.getByPlaceholderText('datasetPipeline.onlineDrive.breadcrumbs.searchPlaceholder'))!.toBeInTheDocument() }) }) @@ -415,7 +423,7 @@ describe('Header', () => { render(
) const input = screen.getByPlaceholderText('datasetPipeline.onlineDrive.breadcrumbs.searchPlaceholder') - expect(input).toHaveValue(longValue) + expect(input)!.toHaveValue(longValue) }) it('should handle very long breadcrumb paths', () => { @@ -424,7 +432,7 @@ describe('Header', () => { render(
) - expect(screen.getByPlaceholderText('datasetPipeline.onlineDrive.breadcrumbs.searchPlaceholder')).toBeInTheDocument() + expect(screen.getByPlaceholderText('datasetPipeline.onlineDrive.breadcrumbs.searchPlaceholder'))!.toBeInTheDocument() }) it('should handle breadcrumbs with special characters', () => { @@ -433,7 +441,7 @@ describe('Header', () => { render(
) - expect(screen.getByPlaceholderText('datasetPipeline.onlineDrive.breadcrumbs.searchPlaceholder')).toBeInTheDocument() + expect(screen.getByPlaceholderText('datasetPipeline.onlineDrive.breadcrumbs.searchPlaceholder'))!.toBeInTheDocument() }) it('should handle breadcrumbs with unicode names', () => { @@ -442,7 +450,7 @@ describe('Header', () => { render(
) - expect(screen.getByPlaceholderText('datasetPipeline.onlineDrive.breadcrumbs.searchPlaceholder')).toBeInTheDocument() + expect(screen.getByPlaceholderText('datasetPipeline.onlineDrive.breadcrumbs.searchPlaceholder'))!.toBeInTheDocument() }) it('should handle bucket with special characters', () => { @@ -450,7 +458,7 @@ describe('Header', () => { render(
) - expect(screen.getByPlaceholderText('datasetPipeline.onlineDrive.breadcrumbs.searchPlaceholder')).toBeInTheDocument() + expect(screen.getByPlaceholderText('datasetPipeline.onlineDrive.breadcrumbs.searchPlaceholder'))!.toBeInTheDocument() }) it('should pass the event object to handleInputChange callback', () => { @@ -463,7 +471,7 @@ describe('Header', () => { // Assert - Verify the event object is passed correctly expect(mockHandleInputChange).toHaveBeenCalledTimes(1) - const eventArg = mockHandleInputChange.mock.calls[0][0] + const eventArg = mockHandleInputChange.mock.calls[0]![0] expect(eventArg).toHaveProperty('type', 'change') expect(eventArg).toHaveProperty('target') }) @@ -480,7 +488,7 @@ describe('Header', () => { render(
) - expect(screen.getByPlaceholderText('datasetPipeline.onlineDrive.breadcrumbs.searchPlaceholder')).toBeInTheDocument() + expect(screen.getByPlaceholderText('datasetPipeline.onlineDrive.breadcrumbs.searchPlaceholder'))!.toBeInTheDocument() }) it.each([ @@ -493,7 +501,7 @@ describe('Header', () => { render(
) - expect(screen.getByPlaceholderText('datasetPipeline.onlineDrive.breadcrumbs.searchPlaceholder')).toBeInTheDocument() + expect(screen.getByPlaceholderText('datasetPipeline.onlineDrive.breadcrumbs.searchPlaceholder'))!.toBeInTheDocument() }) it.each([ @@ -507,7 +515,7 @@ describe('Header', () => { render(
) const input = screen.getByPlaceholderText('datasetPipeline.onlineDrive.breadcrumbs.searchPlaceholder') - expect(input).toHaveValue(inputValue) + expect(input)!.toHaveValue(inputValue) }) }) @@ -525,7 +533,8 @@ describe('Header', () => { render(
) // Assert - Component should render successfully, meaning props are passed correctly - expect(screen.getByPlaceholderText('datasetPipeline.onlineDrive.breadcrumbs.searchPlaceholder')).toBeInTheDocument() + // Assert - Component should render successfully, meaning props are passed correctly + expect(screen.getByPlaceholderText('datasetPipeline.onlineDrive.breadcrumbs.searchPlaceholder'))!.toBeInTheDocument() }) it('should pass correct props to Input component', () => { @@ -540,7 +549,7 @@ describe('Header', () => { render(
) const input = screen.getByPlaceholderText('datasetPipeline.onlineDrive.breadcrumbs.searchPlaceholder') - expect(input).toHaveValue('test-input') + expect(input)!.toHaveValue('test-input') // Test onChange handler fireEvent.change(input, { target: { value: 'new-value' } }) diff --git a/web/app/components/datasets/documents/create-from-pipeline/data-source/online-drive/file-list/header/breadcrumbs/__tests__/bucket.spec.tsx b/web/app/components/datasets/documents/create-from-pipeline/data-source/online-drive/file-list/header/breadcrumbs/__tests__/bucket.spec.tsx index c407be51ac..83e17e6e04 100644 --- a/web/app/components/datasets/documents/create-from-pipeline/data-source/online-drive/file-list/header/breadcrumbs/__tests__/bucket.spec.tsx +++ b/web/app/components/datasets/documents/create-from-pipeline/data-source/online-drive/file-list/header/breadcrumbs/__tests__/bucket.spec.tsx @@ -22,18 +22,18 @@ describe('Bucket', () => { it('should render bucket name', () => { render() - expect(screen.getByText('my-bucket')).toBeInTheDocument() + expect(screen.getByText('my-bucket'))!.toBeInTheDocument() }) it('should render bucket icon', () => { render() - expect(screen.getByTestId('buckets-gray')).toBeInTheDocument() + expect(screen.getByTestId('buckets-gray'))!.toBeInTheDocument() }) it('should call handleBackToBucketList on icon button click', () => { render() const buttons = screen.getAllByRole('button') - fireEvent.click(buttons[0]) + fireEvent.click(buttons[0]!) expect(defaultProps.handleBackToBucketList).toHaveBeenCalledOnce() }) diff --git a/web/app/components/datasets/documents/create-from-pipeline/data-source/online-drive/file-list/header/breadcrumbs/__tests__/index.spec.tsx b/web/app/components/datasets/documents/create-from-pipeline/data-source/online-drive/file-list/header/breadcrumbs/__tests__/index.spec.tsx index a6aaf3a50b..24a241ff93 100644 --- a/web/app/components/datasets/documents/create-from-pipeline/data-source/online-drive/file-list/header/breadcrumbs/__tests__/index.spec.tsx +++ b/web/app/components/datasets/documents/create-from-pipeline/data-source/online-drive/file-list/header/breadcrumbs/__tests__/index.spec.tsx @@ -58,7 +58,7 @@ describe('Breadcrumbs', () => { // Assert - Container should be in the document const container = document.querySelector('.flex.grow') - expect(container).toBeInTheDocument() + expect(container)!.toBeInTheDocument() }) it('should render with correct container styles', () => { @@ -67,10 +67,10 @@ describe('Breadcrumbs', () => { const { container } = render() const wrapper = container.firstChild as HTMLElement - expect(wrapper).toHaveClass('flex') - expect(wrapper).toHaveClass('grow') - expect(wrapper).toHaveClass('items-center') - expect(wrapper).toHaveClass('overflow-hidden') + expect(wrapper)!.toHaveClass('flex') + expect(wrapper)!.toHaveClass('grow') + expect(wrapper)!.toHaveClass('items-center') + expect(wrapper)!.toHaveClass('overflow-hidden') }) describe('Search Results Display', () => { @@ -84,7 +84,8 @@ describe('Breadcrumbs', () => { render() // Assert - Search result text should be displayed - expect(screen.getByText(/datasetPipeline\.onlineDrive\.breadcrumbs\.searchResult/)).toBeInTheDocument() + // Assert - Search result text should be displayed + expect(screen.getByText(/datasetPipeline\.onlineDrive\.breadcrumbs\.searchResult/))!.toBeInTheDocument() }) it('should not show search results when keywords is empty', () => { @@ -121,7 +122,8 @@ describe('Breadcrumbs', () => { render() // Assert - Should use bucket name in search result - expect(screen.getByText(/searchResult.*my-bucket/i)).toBeInTheDocument() + // Assert - Should use bucket name in search result + expect(screen.getByText(/searchResult.*my-bucket/i))!.toBeInTheDocument() }) it('should use last breadcrumb as folderName when breadcrumbs exist', () => { @@ -135,7 +137,8 @@ describe('Breadcrumbs', () => { render() // Assert - Should use last breadcrumb in search result - expect(screen.getByText(/searchResult.*folder2/i)).toBeInTheDocument() + // Assert - Should use last breadcrumb in search result + expect(screen.getByText(/searchResult.*folder2/i))!.toBeInTheDocument() }) }) @@ -150,7 +153,7 @@ describe('Breadcrumbs', () => { render() - expect(screen.getByText('datasetPipeline.onlineDrive.breadcrumbs.allBuckets')).toBeInTheDocument() + expect(screen.getByText('datasetPipeline.onlineDrive.breadcrumbs.allBuckets'))!.toBeInTheDocument() }) it('should not show all buckets title when breadcrumbs exist', () => { @@ -174,6 +177,37 @@ describe('Breadcrumbs', () => { render() + // Assert - Should show bucket name instead + // Assert - Should show bucket name instead + // Assert - Should show bucket name instead + // Assert - Should show bucket name instead + // Assert - Should show bucket name instead + // Assert - Should show bucket name instead + // Assert - Should show bucket name instead + // Assert - Should show bucket name instead + // Assert - Should show bucket name instead + // Assert - Should show bucket name instead + // Assert - Should show bucket name instead + // Assert - Should show bucket name instead + // Assert - Should show bucket name instead + // Assert - Should show bucket name instead + // Assert - Should show bucket name instead + // Assert - Should show bucket name instead + // Assert - Should show bucket name instead + // Assert - Should show bucket name instead + // Assert - Should show bucket name instead + // Assert - Should show bucket name instead + // Assert - Should show bucket name instead + // Assert - Should show bucket name instead + // Assert - Should show bucket name instead + // Assert - Should show bucket name instead + // Assert - Should show bucket name instead + // Assert - Should show bucket name instead + // Assert - Should show bucket name instead + // Assert - Should show bucket name instead + // Assert - Should show bucket name instead + // Assert - Should show bucket name instead + // Assert - Should show bucket name instead // Assert - Should show bucket name instead expect(screen.queryByText('datasetPipeline.onlineDrive.breadcrumbs.allBuckets')).not.toBeInTheDocument() }) @@ -190,7 +224,8 @@ describe('Breadcrumbs', () => { render() // Assert - Bucket name should be displayed - expect(screen.getByText('test-bucket')).toBeInTheDocument() + // Assert - Bucket name should be displayed + expect(screen.getByText('test-bucket'))!.toBeInTheDocument() }) it('should not render Bucket when hasBucket is false', () => { @@ -202,6 +237,37 @@ describe('Breadcrumbs', () => { render() + // Assert - Bucket should not be displayed, Drive should be shown instead + // Assert - Bucket should not be displayed, Drive should be shown instead + // Assert - Bucket should not be displayed, Drive should be shown instead + // Assert - Bucket should not be displayed, Drive should be shown instead + // Assert - Bucket should not be displayed, Drive should be shown instead + // Assert - Bucket should not be displayed, Drive should be shown instead + // Assert - Bucket should not be displayed, Drive should be shown instead + // Assert - Bucket should not be displayed, Drive should be shown instead + // Assert - Bucket should not be displayed, Drive should be shown instead + // Assert - Bucket should not be displayed, Drive should be shown instead + // Assert - Bucket should not be displayed, Drive should be shown instead + // Assert - Bucket should not be displayed, Drive should be shown instead + // Assert - Bucket should not be displayed, Drive should be shown instead + // Assert - Bucket should not be displayed, Drive should be shown instead + // Assert - Bucket should not be displayed, Drive should be shown instead + // Assert - Bucket should not be displayed, Drive should be shown instead + // Assert - Bucket should not be displayed, Drive should be shown instead + // Assert - Bucket should not be displayed, Drive should be shown instead + // Assert - Bucket should not be displayed, Drive should be shown instead + // Assert - Bucket should not be displayed, Drive should be shown instead + // Assert - Bucket should not be displayed, Drive should be shown instead + // Assert - Bucket should not be displayed, Drive should be shown instead + // Assert - Bucket should not be displayed, Drive should be shown instead + // Assert - Bucket should not be displayed, Drive should be shown instead + // Assert - Bucket should not be displayed, Drive should be shown instead + // Assert - Bucket should not be displayed, Drive should be shown instead + // Assert - Bucket should not be displayed, Drive should be shown instead + // Assert - Bucket should not be displayed, Drive should be shown instead + // Assert - Bucket should not be displayed, Drive should be shown instead + // Assert - Bucket should not be displayed, Drive should be shown instead + // Assert - Bucket should not be displayed, Drive should be shown instead // Assert - Bucket should not be displayed, Drive should be shown instead expect(screen.queryByText('test-bucket')).not.toBeInTheDocument() }) @@ -217,7 +283,8 @@ describe('Breadcrumbs', () => { render() // Assert - "All Files" should be displayed - expect(screen.getByText('datasetPipeline.onlineDrive.breadcrumbs.allFiles')).toBeInTheDocument() + // Assert - "All Files" should be displayed + expect(screen.getByText('datasetPipeline.onlineDrive.breadcrumbs.allFiles'))!.toBeInTheDocument() }) it('should not render Drive component when hasBucket is true', () => { @@ -243,8 +310,8 @@ describe('Breadcrumbs', () => { render() - expect(screen.getByText('folder1')).toBeInTheDocument() - expect(screen.getByText('folder2')).toBeInTheDocument() + expect(screen.getByText('folder1'))!.toBeInTheDocument() + expect(screen.getByText('folder2'))!.toBeInTheDocument() }) it('should render last breadcrumb as active', () => { @@ -257,8 +324,8 @@ describe('Breadcrumbs', () => { // Assert - Last breadcrumb should have active styles const lastBreadcrumb = screen.getByText('folder2') - expect(lastBreadcrumb).toHaveClass('system-sm-medium') - expect(lastBreadcrumb).toHaveClass('text-text-secondary') + expect(lastBreadcrumb)!.toHaveClass('system-sm-medium') + expect(lastBreadcrumb)!.toHaveClass('text-text-secondary') }) it('should render non-last breadcrumbs with tertiary styles', () => { @@ -271,8 +338,8 @@ describe('Breadcrumbs', () => { // Assert - First breadcrumb should have tertiary styles const firstBreadcrumb = screen.getByText('folder1') - expect(firstBreadcrumb).toHaveClass('system-sm-regular') - expect(firstBreadcrumb).toHaveClass('text-text-tertiary') + expect(firstBreadcrumb)!.toHaveClass('system-sm-regular') + expect(firstBreadcrumb)!.toHaveClass('text-text-tertiary') }) }) @@ -287,7 +354,8 @@ describe('Breadcrumbs', () => { render() // Assert - Dropdown trigger (more button) should be present - expect(screen.getByRole('button', { name: '' })).toBeInTheDocument() + // Assert - Dropdown trigger (more button) should be present + expect(screen.getByRole('button', { name: '' }))!.toBeInTheDocument() }) it('should not show dropdown when breadcrumbs do not exceed displayBreadcrumbNum', () => { @@ -301,8 +369,10 @@ describe('Breadcrumbs', () => { // Assert - Should not have dropdown, just regular breadcrumbs // All breadcrumbs should be directly visible - expect(screen.getByText('folder1')).toBeInTheDocument() - expect(screen.getByText('folder2')).toBeInTheDocument() + // Assert - Should not have dropdown, just regular breadcrumbs + // All breadcrumbs should be directly visible + expect(screen.getByText('folder1'))!.toBeInTheDocument() + expect(screen.getByText('folder2'))!.toBeInTheDocument() // Count buttons - should be 3 (allFiles + folder1 + folder2) const buttons = container.querySelectorAll('button') expect(buttons.length).toBe(3) @@ -318,9 +388,41 @@ describe('Breadcrumbs', () => { render() // Assert - First breadcrumb and last breadcrumb should be visible - expect(screen.getByText('folder1')).toBeInTheDocument() - expect(screen.getByText('folder2')).toBeInTheDocument() - expect(screen.getByText('folder5')).toBeInTheDocument() + // Assert - First breadcrumb and last breadcrumb should be visible + expect(screen.getByText('folder1'))!.toBeInTheDocument() + expect(screen.getByText('folder2'))!.toBeInTheDocument() + expect(screen.getByText('folder5'))!.toBeInTheDocument() + // Middle breadcrumbs should be in dropdown + // Middle breadcrumbs should be in dropdown + // Middle breadcrumbs should be in dropdown + // Middle breadcrumbs should be in dropdown + // Middle breadcrumbs should be in dropdown + // Middle breadcrumbs should be in dropdown + // Middle breadcrumbs should be in dropdown + // Middle breadcrumbs should be in dropdown + // Middle breadcrumbs should be in dropdown + // Middle breadcrumbs should be in dropdown + // Middle breadcrumbs should be in dropdown + // Middle breadcrumbs should be in dropdown + // Middle breadcrumbs should be in dropdown + // Middle breadcrumbs should be in dropdown + // Middle breadcrumbs should be in dropdown + // Middle breadcrumbs should be in dropdown + // Middle breadcrumbs should be in dropdown + // Middle breadcrumbs should be in dropdown + // Middle breadcrumbs should be in dropdown + // Middle breadcrumbs should be in dropdown + // Middle breadcrumbs should be in dropdown + // Middle breadcrumbs should be in dropdown + // Middle breadcrumbs should be in dropdown + // Middle breadcrumbs should be in dropdown + // Middle breadcrumbs should be in dropdown + // Middle breadcrumbs should be in dropdown + // Middle breadcrumbs should be in dropdown + // Middle breadcrumbs should be in dropdown + // Middle breadcrumbs should be in dropdown + // Middle breadcrumbs should be in dropdown + // Middle breadcrumbs should be in dropdown // Middle breadcrumbs should be in dropdown expect(screen.queryByText('folder3')).not.toBeInTheDocument() expect(screen.queryByText('folder4')).not.toBeInTheDocument() @@ -341,8 +443,8 @@ describe('Breadcrumbs', () => { // Assert - Collapsed breadcrumbs should be visible await waitFor(() => { - expect(screen.getByText('folder3')).toBeInTheDocument() - expect(screen.getByText('folder4')).toBeInTheDocument() + expect(screen.getByText('folder3'))!.toBeInTheDocument() + expect(screen.getByText('folder4'))!.toBeInTheDocument() }) }) }) @@ -357,7 +459,8 @@ describe('Breadcrumbs', () => { render() // Assert - Only Drive should be visible - expect(screen.getByText('datasetPipeline.onlineDrive.breadcrumbs.allFiles')).toBeInTheDocument() + // Assert - Only Drive should be visible + expect(screen.getByText('datasetPipeline.onlineDrive.breadcrumbs.allFiles'))!.toBeInTheDocument() }) it('should handle single breadcrumb', () => { @@ -366,7 +469,7 @@ describe('Breadcrumbs', () => { render() - expect(screen.getByText('single-folder')).toBeInTheDocument() + expect(screen.getByText('single-folder'))!.toBeInTheDocument() }) it('should handle breadcrumbs with special characters', () => { @@ -377,8 +480,8 @@ describe('Breadcrumbs', () => { render() - expect(screen.getByText('folder [1]')).toBeInTheDocument() - expect(screen.getByText('folder (copy)')).toBeInTheDocument() + expect(screen.getByText('folder [1]'))!.toBeInTheDocument() + expect(screen.getByText('folder (copy)'))!.toBeInTheDocument() }) it('should handle breadcrumbs with unicode characters', () => { @@ -389,8 +492,8 @@ describe('Breadcrumbs', () => { render() - expect(screen.getByText('文件夹')).toBeInTheDocument() - expect(screen.getByText('フォルダ')).toBeInTheDocument() + expect(screen.getByText('文件夹'))!.toBeInTheDocument() + expect(screen.getByText('フォルダ'))!.toBeInTheDocument() }) }) @@ -403,7 +506,7 @@ describe('Breadcrumbs', () => { render() - expect(screen.getByText(/searchResult/)).toBeInTheDocument() + expect(screen.getByText(/searchResult/))!.toBeInTheDocument() }) it('should handle whitespace keywords', () => { @@ -415,7 +518,8 @@ describe('Breadcrumbs', () => { render() // Assert - Whitespace is truthy, so should show search results - expect(screen.getByText(/searchResult/)).toBeInTheDocument() + // Assert - Whitespace is truthy, so should show search results + expect(screen.getByText(/searchResult/))!.toBeInTheDocument() }) }) @@ -428,7 +532,7 @@ describe('Breadcrumbs', () => { render() - expect(screen.getByText('production-bucket')).toBeInTheDocument() + expect(screen.getByText('production-bucket'))!.toBeInTheDocument() }) it('should handle bucket with special characters', () => { @@ -439,7 +543,7 @@ describe('Breadcrumbs', () => { render() - expect(screen.getByText('bucket-v2.0_backup')).toBeInTheDocument() + expect(screen.getByText('bucket-v2.0_backup'))!.toBeInTheDocument() }) }) @@ -452,6 +556,37 @@ describe('Breadcrumbs', () => { render() + // Assert - Should not show search results + // Assert - Should not show search results + // Assert - Should not show search results + // Assert - Should not show search results + // Assert - Should not show search results + // Assert - Should not show search results + // Assert - Should not show search results + // Assert - Should not show search results + // Assert - Should not show search results + // Assert - Should not show search results + // Assert - Should not show search results + // Assert - Should not show search results + // Assert - Should not show search results + // Assert - Should not show search results + // Assert - Should not show search results + // Assert - Should not show search results + // Assert - Should not show search results + // Assert - Should not show search results + // Assert - Should not show search results + // Assert - Should not show search results + // Assert - Should not show search results + // Assert - Should not show search results + // Assert - Should not show search results + // Assert - Should not show search results + // Assert - Should not show search results + // Assert - Should not show search results + // Assert - Should not show search results + // Assert - Should not show search results + // Assert - Should not show search results + // Assert - Should not show search results + // Assert - Should not show search results // Assert - Should not show search results expect(screen.queryByText(/searchResult/)).not.toBeInTheDocument() }) @@ -464,7 +599,7 @@ describe('Breadcrumbs', () => { render() - expect(screen.getByText(/searchResult.*10000/)).toBeInTheDocument() + expect(screen.getByText(/searchResult.*10000/))!.toBeInTheDocument() }) }) @@ -495,9 +630,10 @@ describe('Breadcrumbs', () => { render() // Assert - Should NOT collapse because 3 <= 3 - expect(screen.getByText('folder1')).toBeInTheDocument() - expect(screen.getByText('folder2')).toBeInTheDocument() - expect(screen.getByText('folder3')).toBeInTheDocument() + // Assert - Should NOT collapse because 3 <= 3 + expect(screen.getByText('folder1'))!.toBeInTheDocument() + expect(screen.getByText('folder2'))!.toBeInTheDocument() + expect(screen.getByText('folder3'))!.toBeInTheDocument() }) it('should reduce displayBreadcrumbNum by 1 when bucket is set', () => { @@ -533,9 +669,11 @@ describe('Breadcrumbs', () => { // Assert - displayBreadcrumbNum = 3, so 4 breadcrumbs should collapse // First 2 visible, dropdown, last 1 visible - expect(screen.getByText('a')).toBeInTheDocument() - expect(screen.getByText('b')).toBeInTheDocument() - expect(screen.getByText('d')).toBeInTheDocument() + // Assert - displayBreadcrumbNum = 3, so 4 breadcrumbs should collapse + // First 2 visible, dropdown, last 1 visible + expect(screen.getByText('a'))!.toBeInTheDocument() + expect(screen.getByText('b'))!.toBeInTheDocument() + expect(screen.getByText('d'))!.toBeInTheDocument() expect(screen.queryByText('c')).not.toBeInTheDocument() }) @@ -550,8 +688,9 @@ describe('Breadcrumbs', () => { render() // Assert - displayBreadcrumbNum = 2, so 3 breadcrumbs should collapse - expect(screen.getByText('a')).toBeInTheDocument() - expect(screen.getByText('c')).toBeInTheDocument() + // Assert - displayBreadcrumbNum = 2, so 3 breadcrumbs should collapse + expect(screen.getByText('a'))!.toBeInTheDocument() + expect(screen.getByText('c'))!.toBeInTheDocument() expect(screen.queryByText('b')).not.toBeInTheDocument() }) @@ -566,8 +705,9 @@ describe('Breadcrumbs', () => { render() // Assert - displayBreadcrumbNum = 3 - 1 = 2, so 3 breadcrumbs should collapse - expect(screen.getByText('a')).toBeInTheDocument() - expect(screen.getByText('c')).toBeInTheDocument() + // Assert - displayBreadcrumbNum = 3 - 1 = 2, so 3 breadcrumbs should collapse + expect(screen.getByText('a'))!.toBeInTheDocument() + expect(screen.getByText('c'))!.toBeInTheDocument() expect(screen.queryByText('b')).not.toBeInTheDocument() }) }) @@ -589,12 +729,15 @@ describe('Breadcrumbs', () => { // prefixBreadcrumbs = ['f1', 'f2'] // collapsedBreadcrumbs = ['f3', 'f4'] // lastBreadcrumb = 'f5' - expect(screen.getByText('f1')).toBeInTheDocument() - expect(screen.getByText('f2')).toBeInTheDocument() - expect(screen.getByText('f5')).toBeInTheDocument() + // prefixBreadcrumbs = ['f1', 'f2'] + // collapsedBreadcrumbs = ['f3', 'f4'] + // lastBreadcrumb = 'f5' + expect(screen.getByText('f1'))!.toBeInTheDocument() + expect(screen.getByText('f2'))!.toBeInTheDocument() + expect(screen.getByText('f5'))!.toBeInTheDocument() await waitFor(() => { - expect(screen.getByText('f3')).toBeInTheDocument() - expect(screen.getByText('f4')).toBeInTheDocument() + expect(screen.getByText('f3'))!.toBeInTheDocument() + expect(screen.getByText('f4'))!.toBeInTheDocument() }) }) @@ -608,8 +751,9 @@ describe('Breadcrumbs', () => { render() // Assert - All breadcrumbs should be visible - expect(screen.getByText('f1')).toBeInTheDocument() - expect(screen.getByText('f2')).toBeInTheDocument() + // Assert - All breadcrumbs should be visible + expect(screen.getByText('f1'))!.toBeInTheDocument() + expect(screen.getByText('f2'))!.toBeInTheDocument() }) }) }) @@ -627,7 +771,7 @@ describe('Breadcrumbs', () => { // Act - Click bucket icon button (first button in Bucket component) const buttons = screen.getAllByRole('button') - fireEvent.click(buttons[0]) // Bucket icon button + fireEvent.click(buttons[0]!) // Bucket icon button expect(mockStoreState.setOnlineDriveFileList).toHaveBeenCalledWith([]) expect(mockStoreState.setSelectedFileIds).toHaveBeenCalledWith([]) @@ -744,7 +888,7 @@ describe('Breadcrumbs', () => { fireEvent.click(dropdownTrigger) await waitFor(() => { - expect(screen.getByText('f3')).toBeInTheDocument() + expect(screen.getByText('f3'))!.toBeInTheDocument() }) fireEvent.click(screen.getByText('f3')) @@ -771,19 +915,19 @@ describe('Breadcrumbs', () => { // Assert - Component should render without errors const container = document.querySelector('.flex.grow') - expect(container).toBeInTheDocument() + expect(container)!.toBeInTheDocument() }) it('should re-render when breadcrumbs change', () => { mockStoreState.hasBucket = false const props = createDefaultProps({ breadcrumbs: ['folder1'] }) const { rerender } = render() - expect(screen.getByText('folder1')).toBeInTheDocument() + expect(screen.getByText('folder1'))!.toBeInTheDocument() // Act - Rerender with different breadcrumbs rerender() - expect(screen.getByText('folder2')).toBeInTheDocument() + expect(screen.getByText('folder2'))!.toBeInTheDocument() }) }) @@ -798,7 +942,7 @@ describe('Breadcrumbs', () => { render() - expect(screen.getByText(longName)).toBeInTheDocument() + expect(screen.getByText(longName))!.toBeInTheDocument() }) it('should handle many breadcrumbs', async () => { @@ -815,11 +959,12 @@ describe('Breadcrumbs', () => { fireEvent.click(dropdownTrigger) // Assert - First, last, and collapsed should be accessible - expect(screen.getByText('folder-0')).toBeInTheDocument() - expect(screen.getByText('folder-1')).toBeInTheDocument() - expect(screen.getByText('folder-19')).toBeInTheDocument() + // Assert - First, last, and collapsed should be accessible + expect(screen.getByText('folder-0'))!.toBeInTheDocument() + expect(screen.getByText('folder-1'))!.toBeInTheDocument() + expect(screen.getByText('folder-19'))!.toBeInTheDocument() await waitFor(() => { - expect(screen.getByText('folder-2')).toBeInTheDocument() + expect(screen.getByText('folder-2'))!.toBeInTheDocument() }) }) @@ -833,7 +978,8 @@ describe('Breadcrumbs', () => { render() // Assert - Should show all buckets title - expect(screen.getByText('datasetPipeline.onlineDrive.breadcrumbs.allBuckets')).toBeInTheDocument() + // Assert - Should show all buckets title + expect(screen.getByText('datasetPipeline.onlineDrive.breadcrumbs.allBuckets'))!.toBeInTheDocument() }) it('should handle breadcrumb with only whitespace', () => { @@ -845,7 +991,8 @@ describe('Breadcrumbs', () => { render() // Assert - Both should be rendered - expect(screen.getByText('normal-folder')).toBeInTheDocument() + // Assert - Both should be rendered + expect(screen.getByText('normal-folder'))!.toBeInTheDocument() }) }) @@ -863,7 +1010,7 @@ describe('Breadcrumbs', () => { // Assert - Component should render without errors const container = document.querySelector('.flex.grow') - expect(container).toBeInTheDocument() + expect(container)!.toBeInTheDocument() }) it.each([ @@ -916,7 +1063,8 @@ describe('Breadcrumbs', () => { render() // Assert - Search result should be shown, navigation elements should be hidden - expect(screen.getByText(/searchResult/)).toBeInTheDocument() + // Assert - Search result should be shown, navigation elements should be hidden + expect(screen.getByText(/searchResult/))!.toBeInTheDocument() expect(screen.queryByText('my-bucket')).not.toBeInTheDocument() }) }) diff --git a/web/app/components/datasets/documents/create-from-pipeline/data-source/online-drive/file-list/header/breadcrumbs/dropdown/__tests__/index.spec.tsx b/web/app/components/datasets/documents/create-from-pipeline/data-source/online-drive/file-list/header/breadcrumbs/dropdown/__tests__/index.spec.tsx index 0157d3cf79..f7112c243b 100644 --- a/web/app/components/datasets/documents/create-from-pipeline/data-source/online-drive/file-list/header/breadcrumbs/dropdown/__tests__/index.spec.tsx +++ b/web/app/components/datasets/documents/create-from-pipeline/data-source/online-drive/file-list/header/breadcrumbs/dropdown/__tests__/index.spec.tsx @@ -23,7 +23,8 @@ describe('Dropdown', () => { render() // Assert - Trigger button should be visible - expect(screen.getByRole('button')).toBeInTheDocument() + // Assert - Trigger button should be visible + expect(screen.getByRole('button'))!.toBeInTheDocument() }) it('should render trigger button with more icon', () => { @@ -33,8 +34,8 @@ describe('Dropdown', () => { // Assert - Button should have RiMoreFill icon (rendered as svg) const button = screen.getByRole('button') - expect(button).toBeInTheDocument() - expect(container.querySelector('svg')).toBeInTheDocument() + expect(button)!.toBeInTheDocument() + expect(container.querySelector('svg'))!.toBeInTheDocument() }) it('should render separator after dropdown', () => { @@ -43,7 +44,8 @@ describe('Dropdown', () => { render() // Assert - Separator "/" should be visible - expect(screen.getByText('/')).toBeInTheDocument() + // Assert - Separator "/" should be visible + expect(screen.getByText('/'))!.toBeInTheDocument() }) it('should render trigger button with correct default styles', () => { @@ -52,11 +54,11 @@ describe('Dropdown', () => { render() const button = screen.getByRole('button') - expect(button).toHaveClass('flex') - expect(button).toHaveClass('size-6') - expect(button).toHaveClass('items-center') - expect(button).toHaveClass('justify-center') - expect(button).toHaveClass('rounded-md') + expect(button)!.toHaveClass('flex') + expect(button)!.toHaveClass('size-6') + expect(button)!.toHaveClass('items-center') + expect(button)!.toHaveClass('justify-center') + expect(button)!.toHaveClass('rounded-md') }) it('should not render menu content when closed', () => { @@ -64,6 +66,37 @@ describe('Dropdown', () => { render() + // Assert - Menu content should not be visible when dropdown is closed + // Assert - Menu content should not be visible when dropdown is closed + // Assert - Menu content should not be visible when dropdown is closed + // Assert - Menu content should not be visible when dropdown is closed + // Assert - Menu content should not be visible when dropdown is closed + // Assert - Menu content should not be visible when dropdown is closed + // Assert - Menu content should not be visible when dropdown is closed + // Assert - Menu content should not be visible when dropdown is closed + // Assert - Menu content should not be visible when dropdown is closed + // Assert - Menu content should not be visible when dropdown is closed + // Assert - Menu content should not be visible when dropdown is closed + // Assert - Menu content should not be visible when dropdown is closed + // Assert - Menu content should not be visible when dropdown is closed + // Assert - Menu content should not be visible when dropdown is closed + // Assert - Menu content should not be visible when dropdown is closed + // Assert - Menu content should not be visible when dropdown is closed + // Assert - Menu content should not be visible when dropdown is closed + // Assert - Menu content should not be visible when dropdown is closed + // Assert - Menu content should not be visible when dropdown is closed + // Assert - Menu content should not be visible when dropdown is closed + // Assert - Menu content should not be visible when dropdown is closed + // Assert - Menu content should not be visible when dropdown is closed + // Assert - Menu content should not be visible when dropdown is closed + // Assert - Menu content should not be visible when dropdown is closed + // Assert - Menu content should not be visible when dropdown is closed + // Assert - Menu content should not be visible when dropdown is closed + // Assert - Menu content should not be visible when dropdown is closed + // Assert - Menu content should not be visible when dropdown is closed + // Assert - Menu content should not be visible when dropdown is closed + // Assert - Menu content should not be visible when dropdown is closed + // Assert - Menu content should not be visible when dropdown is closed // Assert - Menu content should not be visible when dropdown is closed expect(screen.queryByText('visible-folder')).not.toBeInTheDocument() }) @@ -77,8 +110,8 @@ describe('Dropdown', () => { // Assert - Menu items should be visible await waitFor(() => { - expect(screen.getByText('test-folder1')).toBeInTheDocument() - expect(screen.getByText('test-folder2')).toBeInTheDocument() + expect(screen.getByText('test-folder1'))!.toBeInTheDocument() + expect(screen.getByText('test-folder2'))!.toBeInTheDocument() }) }) }) @@ -98,7 +131,7 @@ describe('Dropdown', () => { fireEvent.click(screen.getByRole('button')) await waitFor(() => { - expect(screen.getByText('folder1')).toBeInTheDocument() + expect(screen.getByText('folder1'))!.toBeInTheDocument() }) fireEvent.click(screen.getByText('folder1')) @@ -120,7 +153,7 @@ describe('Dropdown', () => { fireEvent.click(screen.getByRole('button')) await waitFor(() => { - expect(screen.getByText('folder2')).toBeInTheDocument() + expect(screen.getByText('folder2'))!.toBeInTheDocument() }) fireEvent.click(screen.getByText('folder2')) @@ -140,9 +173,9 @@ describe('Dropdown', () => { fireEvent.click(screen.getByRole('button')) await waitFor(() => { - expect(screen.getByText('folder-a')).toBeInTheDocument() - expect(screen.getByText('folder-b')).toBeInTheDocument() - expect(screen.getByText('folder-c')).toBeInTheDocument() + expect(screen.getByText('folder-a'))!.toBeInTheDocument() + expect(screen.getByText('folder-b'))!.toBeInTheDocument() + expect(screen.getByText('folder-c'))!.toBeInTheDocument() }) }) @@ -155,7 +188,7 @@ describe('Dropdown', () => { fireEvent.click(screen.getByRole('button')) await waitFor(() => { - expect(screen.getByText('single-folder')).toBeInTheDocument() + expect(screen.getByText('single-folder'))!.toBeInTheDocument() }) }) @@ -170,7 +203,8 @@ describe('Dropdown', () => { // Assert - Menu should be rendered but with no items await waitFor(() => { // The menu container should exist but be empty - expect(screen.getByRole('button')).toBeInTheDocument() + // The menu container should exist but be empty + expect(screen.getByRole('button'))!.toBeInTheDocument() }) }) @@ -183,9 +217,9 @@ describe('Dropdown', () => { fireEvent.click(screen.getByRole('button')) await waitFor(() => { - expect(screen.getByText('folder [1]')).toBeInTheDocument() - expect(screen.getByText('folder (copy)')).toBeInTheDocument() - expect(screen.getByText('folder-v2.0')).toBeInTheDocument() + expect(screen.getByText('folder [1]'))!.toBeInTheDocument() + expect(screen.getByText('folder (copy)'))!.toBeInTheDocument() + expect(screen.getByText('folder-v2.0'))!.toBeInTheDocument() }) }) @@ -198,9 +232,9 @@ describe('Dropdown', () => { fireEvent.click(screen.getByRole('button')) await waitFor(() => { - expect(screen.getByText('文件夹')).toBeInTheDocument() - expect(screen.getByText('フォルダ')).toBeInTheDocument() - expect(screen.getByText('Папка')).toBeInTheDocument() + expect(screen.getByText('文件夹'))!.toBeInTheDocument() + expect(screen.getByText('フォルダ'))!.toBeInTheDocument() + expect(screen.getByText('Папка'))!.toBeInTheDocument() }) }) }) @@ -218,7 +252,7 @@ describe('Dropdown', () => { fireEvent.click(screen.getByRole('button')) await waitFor(() => { - expect(screen.getByText('folder1')).toBeInTheDocument() + expect(screen.getByText('folder1'))!.toBeInTheDocument() }) fireEvent.click(screen.getByText('folder1')) @@ -236,6 +270,37 @@ describe('Dropdown', () => { render() + // Assert - Menu content should not be visible + // Assert - Menu content should not be visible + // Assert - Menu content should not be visible + // Assert - Menu content should not be visible + // Assert - Menu content should not be visible + // Assert - Menu content should not be visible + // Assert - Menu content should not be visible + // Assert - Menu content should not be visible + // Assert - Menu content should not be visible + // Assert - Menu content should not be visible + // Assert - Menu content should not be visible + // Assert - Menu content should not be visible + // Assert - Menu content should not be visible + // Assert - Menu content should not be visible + // Assert - Menu content should not be visible + // Assert - Menu content should not be visible + // Assert - Menu content should not be visible + // Assert - Menu content should not be visible + // Assert - Menu content should not be visible + // Assert - Menu content should not be visible + // Assert - Menu content should not be visible + // Assert - Menu content should not be visible + // Assert - Menu content should not be visible + // Assert - Menu content should not be visible + // Assert - Menu content should not be visible + // Assert - Menu content should not be visible + // Assert - Menu content should not be visible + // Assert - Menu content should not be visible + // Assert - Menu content should not be visible + // Assert - Menu content should not be visible + // Assert - Menu content should not be visible // Assert - Menu content should not be visible expect(screen.queryByText('test-folder')).not.toBeInTheDocument() }) @@ -247,7 +312,7 @@ describe('Dropdown', () => { fireEvent.click(screen.getByRole('button')) await waitFor(() => { - expect(screen.getByText('test-folder')).toBeInTheDocument() + expect(screen.getByText('test-folder'))!.toBeInTheDocument() }) }) @@ -258,7 +323,7 @@ describe('Dropdown', () => { // Act - Open and then close fireEvent.click(screen.getByRole('button')) await waitFor(() => { - expect(screen.getByText('test-folder')).toBeInTheDocument() + expect(screen.getByText('test-folder'))!.toBeInTheDocument() }) fireEvent.click(screen.getByRole('button')) @@ -280,7 +345,7 @@ describe('Dropdown', () => { fireEvent.click(screen.getByRole('button')) await waitFor(() => { - expect(screen.getByText('test-folder')).toBeInTheDocument() + expect(screen.getByText('test-folder'))!.toBeInTheDocument() }) fireEvent.click(screen.getByText('test-folder')) @@ -297,14 +362,15 @@ describe('Dropdown', () => { const button = screen.getByRole('button') // Assert - Initial state (closed): should have hover:bg-state-base-hover - expect(button).toHaveClass('hover:bg-state-base-hover') + // Assert - Initial state (closed): should have hover:bg-state-base-hover + expect(button)!.toHaveClass('hover:bg-state-base-hover') // Act - Open dropdown fireEvent.click(button) // Assert - Open state: should have bg-state-base-hover await waitFor(() => { - expect(button).toHaveClass('bg-state-base-hover') + expect(button)!.toHaveClass('bg-state-base-hover') }) }) }) @@ -317,6 +383,37 @@ describe('Dropdown', () => { const props = createDefaultProps({ breadcrumbs: ['folder'] }) render() + // Act & Assert - Initially closed + // Act & Assert - Initially closed + // Act & Assert - Initially closed + // Act & Assert - Initially closed + // Act & Assert - Initially closed + // Act & Assert - Initially closed + // Act & Assert - Initially closed + // Act & Assert - Initially closed + // Act & Assert - Initially closed + // Act & Assert - Initially closed + // Act & Assert - Initially closed + // Act & Assert - Initially closed + // Act & Assert - Initially closed + // Act & Assert - Initially closed + // Act & Assert - Initially closed + // Act & Assert - Initially closed + // Act & Assert - Initially closed + // Act & Assert - Initially closed + // Act & Assert - Initially closed + // Act & Assert - Initially closed + // Act & Assert - Initially closed + // Act & Assert - Initially closed + // Act & Assert - Initially closed + // Act & Assert - Initially closed + // Act & Assert - Initially closed + // Act & Assert - Initially closed + // Act & Assert - Initially closed + // Act & Assert - Initially closed + // Act & Assert - Initially closed + // Act & Assert - Initially closed + // Act & Assert - Initially closed // Act & Assert - Initially closed expect(screen.queryByText('folder')).not.toBeInTheDocument() @@ -325,7 +422,7 @@ describe('Dropdown', () => { // Assert - Now open await waitFor(() => { - expect(screen.getByText('folder')).toBeInTheDocument() + expect(screen.getByText('folder'))!.toBeInTheDocument() }) }) @@ -338,7 +435,7 @@ describe('Dropdown', () => { // 1st click - open fireEvent.click(button) await waitFor(() => { - expect(screen.getByText('folder')).toBeInTheDocument() + expect(screen.getByText('folder'))!.toBeInTheDocument() }) // 2nd click - close @@ -350,7 +447,7 @@ describe('Dropdown', () => { // 3rd click - open again fireEvent.click(button) await waitFor(() => { - expect(screen.getByText('folder')).toBeInTheDocument() + expect(screen.getByText('folder'))!.toBeInTheDocument() }) }) }) @@ -368,7 +465,7 @@ describe('Dropdown', () => { fireEvent.click(screen.getByRole('button')) await waitFor(() => { - expect(screen.getByText('folder1')).toBeInTheDocument() + expect(screen.getByText('folder1'))!.toBeInTheDocument() }) fireEvent.click(screen.getByText('folder1')) @@ -394,7 +491,7 @@ describe('Dropdown', () => { fireEvent.click(screen.getByRole('button')) await waitFor(() => { - expect(screen.getByText('folder1')).toBeInTheDocument() + expect(screen.getByText('folder1'))!.toBeInTheDocument() }) fireEvent.click(screen.getByText('folder1')) @@ -423,7 +520,7 @@ describe('Dropdown', () => { // Act - Open and click fireEvent.click(screen.getByRole('button')) await waitFor(() => { - expect(screen.getByText('folder')).toBeInTheDocument() + expect(screen.getByText('folder'))!.toBeInTheDocument() }) fireEvent.click(screen.getByText('folder')) @@ -431,7 +528,7 @@ describe('Dropdown', () => { rerender() fireEvent.click(screen.getByRole('button')) await waitFor(() => { - expect(screen.getByText('folder')).toBeInTheDocument() + expect(screen.getByText('folder'))!.toBeInTheDocument() }) fireEvent.click(screen.getByText('folder')) @@ -450,7 +547,7 @@ describe('Dropdown', () => { // Act - Open and click with first callback fireEvent.click(screen.getByRole('button')) await waitFor(() => { - expect(screen.getByText('folder')).toBeInTheDocument() + expect(screen.getByText('folder'))!.toBeInTheDocument() }) fireEvent.click(screen.getByText('folder')) @@ -466,7 +563,7 @@ describe('Dropdown', () => { // Open and click with second callback fireEvent.click(screen.getByRole('button')) await waitFor(() => { - expect(screen.getByText('folder')).toBeInTheDocument() + expect(screen.getByText('folder'))!.toBeInTheDocument() }) fireEvent.click(screen.getByText('folder')) @@ -482,7 +579,8 @@ describe('Dropdown', () => { rerender() // Assert - Component should render without errors - expect(screen.getByRole('button')).toBeInTheDocument() + // Assert - Component should render without errors + expect(screen.getByRole('button'))!.toBeInTheDocument() }) }) @@ -499,7 +597,7 @@ describe('Dropdown', () => { // Assert - Should handle gracefully (open after odd number of clicks) await waitFor(() => { - expect(screen.getByText('folder')).toBeInTheDocument() + expect(screen.getByText('folder'))!.toBeInTheDocument() }) }) @@ -513,7 +611,7 @@ describe('Dropdown', () => { fireEvent.click(screen.getByRole('button')) await waitFor(() => { - expect(screen.getByText(longName)).toBeInTheDocument() + expect(screen.getByText(longName))!.toBeInTheDocument() }) }) @@ -528,8 +626,8 @@ describe('Dropdown', () => { // Assert - First and last items should be visible await waitFor(() => { - expect(screen.getByText('folder-0')).toBeInTheDocument() - expect(screen.getByText('folder-19')).toBeInTheDocument() + expect(screen.getByText('folder-0'))!.toBeInTheDocument() + expect(screen.getByText('folder-19'))!.toBeInTheDocument() }) }) @@ -544,7 +642,7 @@ describe('Dropdown', () => { fireEvent.click(screen.getByRole('button')) await waitFor(() => { - expect(screen.getByText('folder')).toBeInTheDocument() + expect(screen.getByText('folder'))!.toBeInTheDocument() }) fireEvent.click(screen.getByText('folder')) @@ -562,7 +660,7 @@ describe('Dropdown', () => { fireEvent.click(screen.getByRole('button')) await waitFor(() => { - expect(screen.getByText('folder')).toBeInTheDocument() + expect(screen.getByText('folder'))!.toBeInTheDocument() }) fireEvent.click(screen.getByText('folder')) @@ -578,7 +676,7 @@ describe('Dropdown', () => { fireEvent.click(screen.getByRole('button')) await waitFor(() => { - expect(screen.getByText('normal-folder')).toBeInTheDocument() + expect(screen.getByText('normal-folder'))!.toBeInTheDocument() }) }) @@ -591,7 +689,7 @@ describe('Dropdown', () => { fireEvent.click(screen.getByRole('button')) await waitFor(() => { - expect(screen.getByText('folder')).toBeInTheDocument() + expect(screen.getByText('folder'))!.toBeInTheDocument() }) }) }) @@ -613,9 +711,9 @@ describe('Dropdown', () => { fireEvent.click(screen.getByRole('button')) await waitFor(() => { - expect(screen.getByText(breadcrumbs[0])).toBeInTheDocument() + expect(screen.getByText(breadcrumbs[0]!))!.toBeInTheDocument() }) - fireEvent.click(screen.getByText(breadcrumbs[0])) + fireEvent.click(screen.getByText(breadcrumbs[0]!)) expect(mockOnBreadcrumbClick).toHaveBeenCalledWith(expectedIndex) }) @@ -634,7 +732,7 @@ describe('Dropdown', () => { // Assert - Should render without errors await waitFor(() => { if (breadcrumbs.length > 0) - expect(screen.getByText(breadcrumbs[0])).toBeInTheDocument() + expect(screen.getByText(breadcrumbs[0]!))!.toBeInTheDocument() }) }) }) @@ -650,9 +748,9 @@ describe('Dropdown', () => { fireEvent.click(screen.getByRole('button')) await waitFor(() => { - expect(screen.getByText('Documents')).toBeInTheDocument() - expect(screen.getByText('Projects')).toBeInTheDocument() - expect(screen.getByText('Archive')).toBeInTheDocument() + expect(screen.getByText('Documents'))!.toBeInTheDocument() + expect(screen.getByText('Projects'))!.toBeInTheDocument() + expect(screen.getByText('Archive'))!.toBeInTheDocument() }) }) @@ -668,7 +766,7 @@ describe('Dropdown', () => { // Act - Open and click on second item fireEvent.click(screen.getByRole('button')) await waitFor(() => { - expect(screen.getByText('second')).toBeInTheDocument() + expect(screen.getByText('second'))!.toBeInTheDocument() }) fireEvent.click(screen.getByText('second')) @@ -687,7 +785,7 @@ describe('Dropdown', () => { // Act - Open and click on middle item fireEvent.click(screen.getByRole('button')) await waitFor(() => { - expect(screen.getByText('item2')).toBeInTheDocument() + expect(screen.getByText('item2'))!.toBeInTheDocument() }) fireEvent.click(screen.getByText('item2')) @@ -714,7 +812,7 @@ describe('Dropdown', () => { fireEvent.click(screen.getByRole('button')) await waitFor(() => { - expect(screen.getByText(`folder-${String.fromCharCode(97 + i)}`)).toBeInTheDocument() + expect(screen.getByText(`folder-${String.fromCharCode(97 + i)}`))!.toBeInTheDocument() }) fireEvent.click(screen.getByText(`folder-${String.fromCharCode(97 + i)}`)) @@ -731,7 +829,7 @@ describe('Dropdown', () => { render() const button = screen.getByRole('button') - expect(button).toBeInTheDocument() + expect(button)!.toBeInTheDocument() expect(button.tagName).toBe('BUTTON') }) @@ -741,7 +839,7 @@ describe('Dropdown', () => { render() const button = screen.getByRole('button') - expect(button).toHaveAttribute('type', 'button') + expect(button)!.toHaveAttribute('type', 'button') }) }) }) diff --git a/web/app/components/datasets/documents/create-from-pipeline/data-source/online-drive/file-list/header/breadcrumbs/index.tsx b/web/app/components/datasets/documents/create-from-pipeline/data-source/online-drive/file-list/header/breadcrumbs/index.tsx index 714b393d03..91d3acc1d1 100644 --- a/web/app/components/datasets/documents/create-from-pipeline/data-source/online-drive/file-list/header/breadcrumbs/index.tsx +++ b/web/app/components/datasets/documents/create-from-pipeline/data-source/online-drive/file-list/header/breadcrumbs/index.tsx @@ -152,7 +152,7 @@ const Breadcrumbs = ({ { const { setNextPageParameters, currentNextPageParametersRef, isTruncated } = dataSourceStore.getState() - if (entries[0].isIntersecting && isTruncated.current && !isLoading) + if (entries[0]!.isIntersecting && isTruncated.current && !isLoading) setNextPageParameters(currentNextPageParametersRef.current) }, { rootMargin: '100px', diff --git a/web/app/components/datasets/documents/create-from-pipeline/data-source/online-drive/file-list/list/utils.ts b/web/app/components/datasets/documents/create-from-pipeline/data-source/online-drive/file-list/list/utils.ts index 6b367ebe3c..07b23a56ff 100644 --- a/web/app/components/datasets/documents/create-from-pipeline/data-source/online-drive/file-list/list/utils.ts +++ b/web/app/components/datasets/documents/create-from-pipeline/data-source/online-drive/file-list/list/utils.ts @@ -8,7 +8,7 @@ export const getFileExtension = (fileName: string): string => { if (parts.length <= 1 || (parts[0] === '' && parts.length === 2)) return '' - return parts[parts.length - 1].toLowerCase() + return parts[parts.length - 1]!.toLowerCase() } export const getFileType = (fileName: string) => { @@ -17,13 +17,13 @@ export const getFileType = (fileName: string) => { if (extension === 'gif') return FileAppearanceTypeEnum.gif - if (FILE_EXTS.image.includes(extension.toUpperCase())) + if (FILE_EXTS.image!.includes(extension.toUpperCase())) return FileAppearanceTypeEnum.image - if (FILE_EXTS.video.includes(extension.toUpperCase())) + if (FILE_EXTS.video!.includes(extension.toUpperCase())) return FileAppearanceTypeEnum.video - if (FILE_EXTS.audio.includes(extension.toUpperCase())) + if (FILE_EXTS.audio!.includes(extension.toUpperCase())) return FileAppearanceTypeEnum.audio if (extension === 'html' || extension === 'htm' || extension === 'xml' || extension === 'json') @@ -44,7 +44,7 @@ export const getFileType = (fileName: string) => { if (extension === 'pptx' || extension === 'ppt') return FileAppearanceTypeEnum.ppt - if (FILE_EXTS.document.includes(extension.toUpperCase())) + if (FILE_EXTS.document!.includes(extension.toUpperCase())) return FileAppearanceTypeEnum.document return FileAppearanceTypeEnum.custom diff --git a/web/app/components/datasets/documents/create-from-pipeline/data-source/online-drive/utils.ts b/web/app/components/datasets/documents/create-from-pipeline/data-source/online-drive/utils.ts index 89b84069fe..8f0ff13ac5 100644 --- a/web/app/components/datasets/documents/create-from-pipeline/data-source/online-drive/utils.ts +++ b/web/app/components/datasets/documents/create-from-pipeline/data-source/online-drive/utils.ts @@ -10,7 +10,7 @@ export const isBucketListInitiation = (data: OnlineDriveData[], prefix: string[] if (bucket || prefix.length > 0) return false const hasBucket = data.every(item => !!item.bucket) - return hasBucket && (data.length > 1 || (data.length === 1 && !!data[0].bucket && data[0].files.length === 0)) + return hasBucket && (data.length > 1 || (data.length === 1 && !!data[0]!.bucket && data[0]!.files.length === 0)) } export const convertOnlineDriveData = (data: OnlineDriveData[], prefix: string[], bucket: string): { @@ -38,7 +38,7 @@ export const convertOnlineDriveData = (data: OnlineDriveData[], prefix: string[] hasBucket = true } else { - data[0].files.forEach((file) => { + data[0]!.files.forEach((file) => { const { id, name, size, type } = file const isFileType = isFile(type) fileList.push({ @@ -48,9 +48,9 @@ export const convertOnlineDriveData = (data: OnlineDriveData[], prefix: string[] type: isFileType ? OnlineDriveFileType.file : OnlineDriveFileType.folder, }) }) - isTruncated = data[0].is_truncated ?? false - nextPageParameters = data[0].next_page_parameters ?? {} - hasBucket = !!data[0].bucket + isTruncated = data[0]!.is_truncated ?? false + nextPageParameters = data[0]!.next_page_parameters ?? {} + hasBucket = !!data[0]!.bucket } return { fileList, isTruncated, nextPageParameters, hasBucket } } diff --git a/web/app/components/datasets/documents/create-from-pipeline/data-source/website-crawl/base/__tests__/crawled-result.spec.tsx b/web/app/components/datasets/documents/create-from-pipeline/data-source/website-crawl/base/__tests__/crawled-result.spec.tsx index 9c71f91d8d..6c476e6dd6 100644 --- a/web/app/components/datasets/documents/create-from-pipeline/data-source/website-crawl/base/__tests__/crawled-result.spec.tsx +++ b/web/app/components/datasets/documents/create-from-pipeline/data-source/website-crawl/base/__tests__/crawled-result.spec.tsx @@ -49,9 +49,9 @@ const createItem = (url: string): CrawlResultItem => ({ }) const defaultList: CrawlResultItem[] = [ - createItem('https://example.com/a'), - createItem('https://example.com/b'), - createItem('https://example.com/c'), + createItem('https://example.com/a')!, + createItem('https://example.com/b')!, + createItem('https://example.com/c')!, ] describe('CrawledResult', () => { @@ -70,19 +70,18 @@ describe('CrawledResult', () => { it('should render scrap time info with correct total and time', () => { render() - expect( - screen.getByText(/scrapTimeInfo/), - ).toBeInTheDocument() + expect(screen.getByText(/scrapTimeInfo/))!.toBeInTheDocument() // The global i18n mock serialises params, so verify total and time appear - expect(screen.getByText(/"total":3/)).toBeInTheDocument() - expect(screen.getByText(/"time":"12.3"/)).toBeInTheDocument() + // The global i18n mock serialises params, so verify total and time appear + expect(screen.getByText(/"total":3/))!.toBeInTheDocument() + expect(screen.getByText(/"time":"12.3"/))!.toBeInTheDocument() }) it('should render all items from list', () => { render() for (const item of defaultList) { - expect(screen.getByTestId(`crawled-item-${item.source_url}`)).toBeInTheDocument() + expect(screen.getByTestId(`crawled-item-${item.source_url}`))!.toBeInTheDocument() } }) @@ -91,7 +90,7 @@ describe('CrawledResult', () => { , ) - expect(container.firstChild).toHaveClass('my-custom-class') + expect(container.firstChild)!.toHaveClass('my-custom-class') }) }) @@ -100,7 +99,7 @@ describe('CrawledResult', () => { it('should show check-all checkbox in multiple choice mode', () => { render() - expect(screen.getByTestId('check-all-checkbox')).toBeInTheDocument() + expect(screen.getByTestId('check-all-checkbox'))!.toBeInTheDocument() }) it('should hide check-all checkbox in single choice mode', () => { @@ -117,7 +116,7 @@ describe('CrawledResult', () => { render( , ) @@ -150,13 +149,13 @@ describe('CrawledResult', () => { render( , ) - fireEvent.click(screen.getByTestId(`check-${defaultList[1].source_url}`)) + fireEvent.click(screen.getByTestId(`check-${defaultList[1]!.source_url}`)) expect(onSelectedChange).toHaveBeenCalledWith([defaultList[0], defaultList[1]]) }) @@ -166,13 +165,13 @@ describe('CrawledResult', () => { render( , ) - fireEvent.click(screen.getByTestId(`check-${defaultList[1].source_url}`)) + fireEvent.click(screen.getByTestId(`check-${defaultList[1]!.source_url}`)) expect(onSelectedChange).toHaveBeenCalledWith([defaultList[1]]) }) @@ -182,13 +181,13 @@ describe('CrawledResult', () => { render( , ) - fireEvent.click(screen.getByTestId(`check-${defaultList[0].source_url}`)) + fireEvent.click(screen.getByTestId(`check-${defaultList[0]!.source_url}`)) expect(onSelectedChange).toHaveBeenCalledWith([defaultList[1]]) }) @@ -206,7 +205,7 @@ describe('CrawledResult', () => { />, ) - fireEvent.click(screen.getByTestId(`preview-${defaultList[1].source_url}`)) + fireEvent.click(screen.getByTestId(`preview-${defaultList[1]!.source_url}`)) expect(onPreview).toHaveBeenCalledWith(defaultList[1], 1) }) diff --git a/web/app/components/datasets/documents/create-from-pipeline/data-source/website-crawl/base/__tests__/index.spec.tsx b/web/app/components/datasets/documents/create-from-pipeline/data-source/website-crawl/base/__tests__/index.spec.tsx index 99002e687c..fa5633e2df 100644 --- a/web/app/components/datasets/documents/create-from-pipeline/data-source/website-crawl/base/__tests__/index.spec.tsx +++ b/web/app/components/datasets/documents/create-from-pipeline/data-source/website-crawl/base/__tests__/index.spec.tsx @@ -39,7 +39,7 @@ describe('CheckboxWithLabel', () => { it('should render without crashing', () => { render() - expect(screen.getByText('Test Label')).toBeInTheDocument() + expect(screen.getByText('Test Label'))!.toBeInTheDocument() }) it('should render checkbox in unchecked state', () => { @@ -47,7 +47,7 @@ describe('CheckboxWithLabel', () => { // Assert - Custom checkbox component uses div with data-testid const checkbox = container.querySelector('[data-testid^="checkbox"]') - expect(checkbox).toBeInTheDocument() + expect(checkbox)!.toBeInTheDocument() expect(checkbox).not.toHaveClass('bg-components-checkbox-bg') }) @@ -56,7 +56,7 @@ describe('CheckboxWithLabel', () => { // Assert - Checked state has check icon const checkIcon = container.querySelector('[data-testid^="check-icon"]') - expect(checkIcon).toBeInTheDocument() + expect(checkIcon)!.toBeInTheDocument() }) it('should render tooltip when provided', () => { @@ -64,7 +64,7 @@ describe('CheckboxWithLabel', () => { // Assert - Tooltip trigger should be present const tooltipTrigger = document.querySelector('[class*="ml-0.5"]') - expect(tooltipTrigger).toBeInTheDocument() + expect(tooltipTrigger)!.toBeInTheDocument() }) it('should not render tooltip when not provided', () => { @@ -82,14 +82,14 @@ describe('CheckboxWithLabel', () => { ) const label = container.querySelector('label') - expect(label).toHaveClass('custom-class') + expect(label)!.toHaveClass('custom-class') }) it('should apply custom labelClassName', () => { render() const labelText = screen.getByText('Test Label') - expect(labelText).toHaveClass('custom-label-class') + expect(labelText)!.toHaveClass('custom-label-class') }) }) @@ -147,8 +147,8 @@ describe('CrawledResultItem', () => { it('should render without crashing', () => { render() - expect(screen.getByText('Test Page Title')).toBeInTheDocument() - expect(screen.getByText('https://example.com/page1')).toBeInTheDocument() + expect(screen.getByText('Test Page Title'))!.toBeInTheDocument() + expect(screen.getByText('https://example.com/page1'))!.toBeInTheDocument() }) it('should render checkbox when isMultipleChoice is true', () => { @@ -156,7 +156,7 @@ describe('CrawledResultItem', () => { // Assert - Custom checkbox uses data-testid const checkbox = container.querySelector('[data-testid^="checkbox"]') - expect(checkbox).toBeInTheDocument() + expect(checkbox)!.toBeInTheDocument() }) it('should render radio when isMultipleChoice is false', () => { @@ -164,7 +164,7 @@ describe('CrawledResultItem', () => { // Assert - Radio component has size-4 rounded-full classes const radio = container.querySelector('.size-4.rounded-full') - expect(radio).toBeInTheDocument() + expect(radio)!.toBeInTheDocument() }) it('should render checkbox as checked when isChecked is true', () => { @@ -172,13 +172,13 @@ describe('CrawledResultItem', () => { // Assert - Checked state shows check icon const checkIcon = container.querySelector('[data-testid^="check-icon"]') - expect(checkIcon).toBeInTheDocument() + expect(checkIcon)!.toBeInTheDocument() }) it('should render preview button when showPreview is true', () => { render() - expect(screen.getByRole('button')).toBeInTheDocument() + expect(screen.getByRole('button'))!.toBeInTheDocument() }) it('should not render preview button when showPreview is false', () => { @@ -191,15 +191,15 @@ describe('CrawledResultItem', () => { const { container } = render() const item = container.firstChild - expect(item).toHaveClass('bg-state-base-active') + expect(item)!.toHaveClass('bg-state-base-active') }) it('should apply hover styles when isPreview is false', () => { const { container } = render() const item = container.firstChild - expect(item).toHaveClass('group') - expect(item).toHaveClass('hover:bg-state-base-hover') + expect(item)!.toHaveClass('group') + expect(item)!.toHaveClass('hover:bg-state-base-hover') }) }) @@ -209,7 +209,7 @@ describe('CrawledResultItem', () => { render() - expect(screen.getByText('Custom Title')).toBeInTheDocument() + expect(screen.getByText('Custom Title'))!.toBeInTheDocument() }) it('should display payload source_url', () => { @@ -217,7 +217,7 @@ describe('CrawledResultItem', () => { render() - expect(screen.getByText('https://custom.url/path')).toBeInTheDocument() + expect(screen.getByText('https://custom.url/path'))!.toBeInTheDocument() }) it('should set title attribute for truncation tooltip', () => { @@ -226,7 +226,7 @@ describe('CrawledResultItem', () => { render() const titleElement = screen.getByText('Very Long Title') - expect(titleElement).toHaveAttribute('title', 'Very Long Title') + expect(titleElement)!.toHaveAttribute('title', 'Very Long Title') }) }) @@ -310,22 +310,24 @@ describe('CrawledResult', () => { render() // Assert - Check for time info which contains total count - expect(screen.getByText(/1.5/)).toBeInTheDocument() + // Assert - Check for time info which contains total count + expect(screen.getByText(/1.5/))!.toBeInTheDocument() }) it('should render all list items', () => { render() - expect(screen.getByText('Page 1')).toBeInTheDocument() - expect(screen.getByText('Page 2')).toBeInTheDocument() - expect(screen.getByText('Page 3')).toBeInTheDocument() + expect(screen.getByText('Page 1'))!.toBeInTheDocument() + expect(screen.getByText('Page 2'))!.toBeInTheDocument() + expect(screen.getByText('Page 3'))!.toBeInTheDocument() }) it('should display scrape time info', () => { render() // Assert - Check for the time display - expect(screen.getByText(/2.5/)).toBeInTheDocument() + // Assert - Check for the time display + expect(screen.getByText(/2.5/))!.toBeInTheDocument() }) it('should render select all checkbox when isMultipleChoice is true', () => { @@ -350,7 +352,7 @@ describe('CrawledResult', () => { it('should show "Select All" when not all items are checked', () => { render() - expect(screen.getByText(/selectAll|Select All/i)).toBeInTheDocument() + expect(screen.getByText(/selectAll|Select All/i))!.toBeInTheDocument() }) it('should show "Reset All" when all items are checked', () => { @@ -358,7 +360,7 @@ describe('CrawledResult', () => { render() - expect(screen.getByText(/resetAll|Reset All/i)).toBeInTheDocument() + expect(screen.getByText(/resetAll|Reset All/i))!.toBeInTheDocument() }) }) @@ -368,7 +370,7 @@ describe('CrawledResult', () => { , ) - expect(container.firstChild).toHaveClass('custom-class') + expect(container.firstChild)!.toHaveClass('custom-class') }) it('should highlight item at previewIndex', () => { @@ -378,7 +380,7 @@ describe('CrawledResult', () => { // Assert - Second item should have active state const items = container.querySelectorAll('[class*="rounded-lg"][class*="cursor-pointer"]') - expect(items[1]).toHaveClass('bg-state-base-active') + expect(items[1])!.toHaveClass('bg-state-base-active') }) it('should pass showPreview to items', () => { @@ -411,7 +413,7 @@ describe('CrawledResult', () => { // Act - Click select all checkbox (first checkbox) const checkboxes = container.querySelectorAll('[data-testid^="checkbox"]') - fireEvent.click(checkboxes[0]) + fireEvent.click(checkboxes[0]!) expect(mockOnSelectedChange).toHaveBeenCalledWith(list) }) @@ -429,7 +431,7 @@ describe('CrawledResult', () => { ) const checkboxes = container.querySelectorAll('[data-testid^="checkbox"]') - fireEvent.click(checkboxes[0]) + fireEvent.click(checkboxes[0]!) expect(mockOnSelectedChange).toHaveBeenCalledWith([]) }) @@ -441,14 +443,14 @@ describe('CrawledResult', () => { , ) // Act - Click second item checkbox (index 2, accounting for select all) const checkboxes = container.querySelectorAll('[data-testid^="checkbox"]') - fireEvent.click(checkboxes[2]) + fireEvent.click(checkboxes[2]!) expect(mockOnSelectedChange).toHaveBeenCalledWith([list[0], list[1]]) }) @@ -460,14 +462,14 @@ describe('CrawledResult', () => { , ) // Act - Uncheck first item (index 1, after select all) const checkboxes = container.querySelectorAll('[data-testid^="checkbox"]') - fireEvent.click(checkboxes[1]) + fireEvent.click(checkboxes[1]!) expect(mockOnSelectedChange).toHaveBeenCalledWith([list[1]]) }) @@ -479,7 +481,7 @@ describe('CrawledResult', () => { , @@ -487,7 +489,7 @@ describe('CrawledResult', () => { // Act - Click second item radio (Radio uses size-4 rounded-full classes) const radios = container.querySelectorAll('.size-4.rounded-full') - fireEvent.click(radios[1]) + fireEvent.click(radios[1]!) // Assert - Should only select the clicked item expect(mockOnSelectedChange).toHaveBeenCalledWith([list[1]]) @@ -506,7 +508,7 @@ describe('CrawledResult', () => { ) const buttons = screen.getAllByRole('button') - fireEvent.click(buttons[1]) // Second item's preview button + fireEvent.click(buttons[1]!) // Second item's preview button expect(mockOnPreview).toHaveBeenCalledWith(list[1], 1) }) @@ -525,10 +527,11 @@ describe('CrawledResult', () => { // Act - Click preview button should trigger early return in handlePreview const buttons = screen.getAllByRole('button') - fireEvent.click(buttons[0]) + fireEvent.click(buttons[0]!) // Assert - Should not throw error, component still renders - expect(screen.getByText('Page 1')).toBeInTheDocument() + // Assert - Should not throw error, component still renders + expect(screen.getByText('Page 1'))!.toBeInTheDocument() }) }) @@ -537,7 +540,8 @@ describe('CrawledResult', () => { render() // Assert - Should show time info with 0 count - expect(screen.getByText(/0.5/)).toBeInTheDocument() + // Assert - Should show time info with 0 count + expect(screen.getByText(/0.5/))!.toBeInTheDocument() }) it('should handle single item list', () => { @@ -545,13 +549,13 @@ describe('CrawledResult', () => { render() - expect(screen.getByText('Test Page Title')).toBeInTheDocument() + expect(screen.getByText('Test Page Title'))!.toBeInTheDocument() }) it('should format usedTime to one decimal place', () => { render() - expect(screen.getByText(/1.6/)).toBeInTheDocument() + expect(screen.getByText(/1.6/))!.toBeInTheDocument() }) }) }) @@ -571,13 +575,13 @@ describe('Crawling', () => { it('should render without crashing', () => { render() - expect(screen.getByText(/5\/10/)).toBeInTheDocument() + expect(screen.getByText(/5\/10/))!.toBeInTheDocument() }) it('should display crawled count and total', () => { render() - expect(screen.getByText(/3\/15/)).toBeInTheDocument() + expect(screen.getByText(/3\/15/))!.toBeInTheDocument() }) it('should render skeleton items', () => { @@ -602,19 +606,19 @@ describe('Crawling', () => { , ) - expect(container.firstChild).toHaveClass('custom-crawling-class') + expect(container.firstChild)!.toHaveClass('custom-crawling-class') }) it('should handle zero values', () => { render() - expect(screen.getByText(/0\/0/)).toBeInTheDocument() + expect(screen.getByText(/0\/0/))!.toBeInTheDocument() }) it('should handle large numbers', () => { render() - expect(screen.getByText(/999\/1000/)).toBeInTheDocument() + expect(screen.getByText(/999\/1000/))!.toBeInTheDocument() }) }) @@ -623,9 +627,10 @@ describe('Crawling', () => { const { container } = render() // Assert - Check for various width classes - expect(container.querySelector('.w-\\[35\\%\\]')).toBeInTheDocument() - expect(container.querySelector('.w-\\[50\\%\\]')).toBeInTheDocument() - expect(container.querySelector('.w-\\[40\\%\\]')).toBeInTheDocument() + // Assert - Check for various width classes + expect(container.querySelector('.w-\\[35\\%\\]'))!.toBeInTheDocument() + expect(container.querySelector('.w-\\[50\\%\\]'))!.toBeInTheDocument() + expect(container.querySelector('.w-\\[40\\%\\]'))!.toBeInTheDocument() }) }) }) @@ -644,27 +649,27 @@ describe('ErrorMessage', () => { it('should render without crashing', () => { render() - expect(screen.getByText('Error Title')).toBeInTheDocument() + expect(screen.getByText('Error Title'))!.toBeInTheDocument() }) it('should render error icon', () => { const { container } = render() const icon = container.querySelector('svg') - expect(icon).toBeInTheDocument() - expect(icon).toHaveClass('text-text-destructive') + expect(icon)!.toBeInTheDocument() + expect(icon)!.toHaveClass('text-text-destructive') }) it('should render title', () => { render() - expect(screen.getByText('Custom Error Title')).toBeInTheDocument() + expect(screen.getByText('Custom Error Title'))!.toBeInTheDocument() }) it('should render error message when provided', () => { render() - expect(screen.getByText('Detailed error description')).toBeInTheDocument() + expect(screen.getByText('Detailed error description'))!.toBeInTheDocument() }) it('should not render error message when not provided', () => { @@ -682,14 +687,15 @@ describe('ErrorMessage', () => { , ) - expect(container.firstChild).toHaveClass('custom-error-class') + expect(container.firstChild)!.toHaveClass('custom-error-class') }) it('should render with empty errorMsg', () => { render() // Assert - Empty string should not render message div - expect(screen.getByText('Error Title')).toBeInTheDocument() + // Assert - Empty string should not render message div + expect(screen.getByText('Error Title'))!.toBeInTheDocument() }) it('should handle long title text', () => { @@ -697,7 +703,7 @@ describe('ErrorMessage', () => { render() - expect(screen.getByText(longTitle)).toBeInTheDocument() + expect(screen.getByText(longTitle))!.toBeInTheDocument() }) it('should handle long error message', () => { @@ -705,7 +711,7 @@ describe('ErrorMessage', () => { render() - expect(screen.getByText(longErrorMsg)).toBeInTheDocument() + expect(screen.getByText(longErrorMsg))!.toBeInTheDocument() }) }) @@ -713,19 +719,19 @@ describe('ErrorMessage', () => { it('should have error background styling', () => { const { container } = render() - expect(container.firstChild).toHaveClass('bg-toast-error-bg') + expect(container.firstChild)!.toHaveClass('bg-toast-error-bg') }) it('should have border styling', () => { const { container } = render() - expect(container.firstChild).toHaveClass('border-components-panel-border') + expect(container.firstChild)!.toHaveClass('border-components-panel-border') }) it('should have rounded-sm corners', () => { const { container } = render() - expect(container.firstChild).toHaveClass('rounded-xl') + expect(container.firstChild)!.toHaveClass('rounded-xl') }) }) }) @@ -744,8 +750,9 @@ describe('Base Components Integration', () => { ) // Assert - Both items should render - expect(screen.getByText('Page 1')).toBeInTheDocument() - expect(screen.getByText('Page 2')).toBeInTheDocument() + // Assert - Both items should render + expect(screen.getByText('Page 1'))!.toBeInTheDocument() + expect(screen.getByText('Page 2'))!.toBeInTheDocument() }) it('should render CrawledResult with CheckboxWithLabel for select all', () => { @@ -784,13 +791,13 @@ describe('Base Components Integration', () => { // Act - Select first item (index 1, after select all) const checkboxes = container.querySelectorAll('[data-testid^="checkbox"]') - fireEvent.click(checkboxes[1]) + fireEvent.click(checkboxes[1]!) expect(mockOnSelectedChange).toHaveBeenCalledWith([list[0]]) // Act - Preview second item const previewButtons = screen.getAllByRole('button') - fireEvent.click(previewButtons[1]) + fireEvent.click(previewButtons[1]!) expect(mockOnPreview).toHaveBeenCalledWith(list[1], 1) }) diff --git a/web/app/components/datasets/documents/create-from-pipeline/data-source/website-crawl/base/crawled-result.tsx b/web/app/components/datasets/documents/create-from-pipeline/data-source/website-crawl/base/crawled-result.tsx index 8cd9a46054..b91c5bd6bc 100644 --- a/web/app/components/datasets/documents/create-from-pipeline/data-source/website-crawl/base/crawled-result.tsx +++ b/web/app/components/datasets/documents/create-from-pipeline/data-source/website-crawl/base/crawled-result.tsx @@ -59,7 +59,7 @@ const CrawledResult = ({ const handlePreview = useCallback((index: number) => { if (!onPreview) return - onPreview(list[index], index) + onPreview(list[index]!, index) }, [list, onPreview]) return ( diff --git a/web/app/components/datasets/documents/create-from-pipeline/data-source/website-crawl/base/options/index.tsx b/web/app/components/datasets/documents/create-from-pipeline/data-source/website-crawl/base/options/index.tsx index 899c70e216..c8a06ea807 100644 --- a/web/app/components/datasets/documents/create-from-pipeline/data-source/website-crawl/base/options/index.tsx +++ b/web/app/components/datasets/documents/create-from-pipeline/data-source/website-crawl/base/options/index.tsx @@ -43,7 +43,7 @@ const Options = ({ if (!result.success) { const issues = result.error.issues const firstIssue = issues[0] - const errorMessage = `"${firstIssue.path.join('.')}" ${firstIssue.message}` + const errorMessage = `"${firstIssue!.path.join('.')}" ${firstIssue!.message}` toast.error(errorMessage) return errorMessage } diff --git a/web/app/components/datasets/documents/create-from-pipeline/hooks/__tests__/use-datasource-store.spec.ts b/web/app/components/datasets/documents/create-from-pipeline/hooks/__tests__/use-datasource-store.spec.ts index 155b41541b..70d95000d7 100644 --- a/web/app/components/datasets/documents/create-from-pipeline/hooks/__tests__/use-datasource-store.spec.ts +++ b/web/app/components/datasets/documents/create-from-pipeline/hooks/__tests__/use-datasource-store.spec.ts @@ -85,7 +85,7 @@ describe('useOnlineDocument', () => { const { result } = renderHook(() => useOnlineDocument(), { wrapper: createWrapper(store) }) expect(result.current.PagesMapAndSelectedPagesId).toHaveProperty('p1') - expect(result.current.PagesMapAndSelectedPagesId.p1.workspace_id).toBe('w1') + expect(result.current.PagesMapAndSelectedPagesId.p1!.workspace_id).toBe('w1') }) it('should hide preview online document', () => { diff --git a/web/app/components/datasets/documents/create-from-pipeline/hooks/use-datasource-actions.ts b/web/app/components/datasets/documents/create-from-pipeline/hooks/use-datasource-actions.ts index 66bd325c33..bad3b779fa 100644 --- a/web/app/components/datasets/documents/create-from-pipeline/hooks/use-datasource-actions.ts +++ b/web/app/components/datasets/documents/create-from-pipeline/hooks/use-datasource-actions.ts @@ -251,7 +251,7 @@ export const useDatasourceActions = ({ if (datasourceType === DatasourceType.onlineDocument) { const allIds = currentWorkspacePages?.map(page => page.page_id) || [] if (onlineDocuments.length < allIds.length) { - const selectedPages = Array.from(allIds).map(pageId => PagesMapAndSelectedPagesId[pageId]) + const selectedPages = Array.from(allIds).map(pageId => PagesMapAndSelectedPagesId[pageId]!) setOnlineDocuments(selectedPages) setSelectedPagesId(new Set(allIds)) } diff --git a/web/app/components/datasets/documents/create-from-pipeline/hooks/use-datasource-ui-state.ts b/web/app/components/datasets/documents/create-from-pipeline/hooks/use-datasource-ui-state.ts index e398f90a48..f4c222f652 100644 --- a/web/app/components/datasets/documents/create-from-pipeline/hooks/use-datasource-ui-state.ts +++ b/web/app/components/datasets/documents/create-from-pipeline/hooks/use-datasource-ui-state.ts @@ -122,7 +122,7 @@ export const useDatasourceUIState = ({ return { datasourceType, - isShowVectorSpaceFull, + isShowVectorSpaceFull: isShowVectorSpaceFull!, nextBtnDisabled, showSelect, totalOptions, diff --git a/web/app/components/datasets/documents/create-from-pipeline/index.tsx b/web/app/components/datasets/documents/create-from-pipeline/index.tsx index 62c1b919fe..8b8fad5885 100644 --- a/web/app/components/datasets/documents/create-from-pipeline/index.tsx +++ b/web/app/components/datasets/documents/create-from-pipeline/index.tsx @@ -133,7 +133,7 @@ const CreateFormPipeline = () => { [DatasourceType.onlineDrive]: selectedFileIds.length, } const count = datasourceType ? multipleCheckMap[datasourceType] : 0 - if (count > 1) { + if (count! > 1) { showPlanUpgradeModal() return } diff --git a/web/app/components/datasets/documents/create-from-pipeline/preview/__tests__/file-preview.spec.tsx b/web/app/components/datasets/documents/create-from-pipeline/preview/__tests__/file-preview.spec.tsx index 715d1650df..82011102b6 100644 --- a/web/app/components/datasets/documents/create-from-pipeline/preview/__tests__/file-preview.spec.tsx +++ b/web/app/components/datasets/documents/create-from-pipeline/preview/__tests__/file-preview.spec.tsx @@ -39,30 +39,30 @@ describe('FilePreview', () => { it('should render preview label', () => { render() - expect(screen.getByText('datasetPipeline.addDocuments.stepOne.preview')).toBeInTheDocument() + expect(screen.getByText('datasetPipeline.addDocuments.stepOne.preview'))!.toBeInTheDocument() }) it('should render file name', () => { render() - expect(screen.getByText('document.pdf')).toBeInTheDocument() + expect(screen.getByText('document.pdf'))!.toBeInTheDocument() }) it('should render file content when loaded', () => { render() - expect(screen.getByText('file content here with some text')).toBeInTheDocument() + expect(screen.getByText('file content here with some text'))!.toBeInTheDocument() }) it('should render loading state', () => { mockIsFetching = true render() - expect(screen.getByTestId('loading')).toBeInTheDocument() + expect(screen.getByTestId('loading'))!.toBeInTheDocument() }) it('should call hidePreview when close button clicked', () => { render() const buttons = screen.getAllByRole('button') const closeBtn = buttons[buttons.length - 1] - fireEvent.click(closeBtn) + fireEvent.click(closeBtn!) expect(defaultProps.hidePreview).toHaveBeenCalled() }) }) diff --git a/web/app/components/datasets/documents/create-from-pipeline/preview/__tests__/web-preview.spec.tsx b/web/app/components/datasets/documents/create-from-pipeline/preview/__tests__/web-preview.spec.tsx index 1f59e11035..7bef4d0716 100644 --- a/web/app/components/datasets/documents/create-from-pipeline/preview/__tests__/web-preview.spec.tsx +++ b/web/app/components/datasets/documents/create-from-pipeline/preview/__tests__/web-preview.spec.tsx @@ -20,29 +20,29 @@ describe('WebPreview', () => { it('should render preview label', () => { render() - expect(screen.getByText('datasetPipeline.addDocuments.stepOne.preview')).toBeInTheDocument() + expect(screen.getByText('datasetPipeline.addDocuments.stepOne.preview'))!.toBeInTheDocument() }) it('should render page title', () => { render() - expect(screen.getByText('Test Page')).toBeInTheDocument() + expect(screen.getByText('Test Page'))!.toBeInTheDocument() }) it('should render source URL', () => { render() - expect(screen.getByText('https://example.com')).toBeInTheDocument() + expect(screen.getByText('https://example.com'))!.toBeInTheDocument() }) it('should render markdown content', () => { render() - expect(screen.getByText('Hello **markdown** content')).toBeInTheDocument() + expect(screen.getByText('Hello **markdown** content'))!.toBeInTheDocument() }) it('should call hidePreview when close button clicked', () => { render() const buttons = screen.getAllByRole('button') const closeBtn = buttons[buttons.length - 1] - fireEvent.click(closeBtn) + fireEvent.click(closeBtn!) expect(defaultProps.hidePreview).toHaveBeenCalled() }) }) diff --git a/web/app/components/datasets/documents/create-from-pipeline/preview/chunk-preview.tsx b/web/app/components/datasets/documents/create-from-pipeline/preview/chunk-preview.tsx index a56e5bd6dc..2d729ee079 100644 --- a/web/app/components/datasets/documents/create-from-pipeline/preview/chunk-preview.tsx +++ b/web/app/components/datasets/documents/create-from-pipeline/preview/chunk-preview.tsx @@ -55,9 +55,9 @@ const ChunkPreview = ({ const currentDocForm = useDatasetDetailContextWithSelector(s => s.dataset?.doc_form) const [previewFile, setPreviewFile] = useState(localFiles[0] as DocumentItem) - const [previewOnlineDocument, setPreviewOnlineDocument] = useState(onlineDocuments[0]) - const [previewWebsitePage, setPreviewWebsitePage] = useState(websitePages[0]) - const [previewOnlineDriveFile, setPreviewOnlineDriveFile] = useState(onlineDriveFiles[0]) + const [previewOnlineDocument, setPreviewOnlineDocument] = useState(onlineDocuments[0]!) + const [previewWebsitePage, setPreviewWebsitePage] = useState(websitePages[0]!) + const [previewOnlineDriveFile, setPreviewOnlineDriveFile] = useState(onlineDriveFiles[0]!) return ( { diff --git a/web/app/components/datasets/documents/create-from-pipeline/steps/__tests__/step-three-content.spec.tsx b/web/app/components/datasets/documents/create-from-pipeline/steps/__tests__/step-three-content.spec.tsx index e217248d2b..8e80ab2878 100644 --- a/web/app/components/datasets/documents/create-from-pipeline/steps/__tests__/step-three-content.spec.tsx +++ b/web/app/components/datasets/documents/create-from-pipeline/steps/__tests__/step-three-content.spec.tsx @@ -56,42 +56,42 @@ describe('StepThreeContent', () => { describe('Rendering', () => { it('should render without crashing', () => { render() - expect(screen.getByTestId('embedding-process')).toBeInTheDocument() + expect(screen.getByTestId('embedding-process'))!.toBeInTheDocument() }) it('should render Processing component', () => { render() - expect(screen.getByTestId('embedding-process')).toBeInTheDocument() + expect(screen.getByTestId('embedding-process'))!.toBeInTheDocument() }) }) describe('Props', () => { it('should pass batchId to Processing component', () => { render() - expect(screen.getByTestId('batch-id')).toHaveTextContent('test-batch-id') + expect(screen.getByTestId('batch-id'))!.toHaveTextContent('test-batch-id') }) it('should pass documents to Processing component', () => { render() - expect(screen.getByTestId('documents-count')).toHaveTextContent('2') + expect(screen.getByTestId('documents-count'))!.toHaveTextContent('2') }) it('should handle empty documents array', () => { render() - expect(screen.getByTestId('documents-count')).toHaveTextContent('0') + expect(screen.getByTestId('documents-count'))!.toHaveTextContent('0') }) }) describe('Edge Cases', () => { it('should render with different batchId', () => { render() - expect(screen.getByTestId('batch-id')).toHaveTextContent('another-batch-id') + expect(screen.getByTestId('batch-id'))!.toHaveTextContent('another-batch-id') }) it('should render with single document', () => { - const singleDocument = [mockDocuments[0]] + const singleDocument = [mockDocuments[0]!] render() - expect(screen.getByTestId('documents-count')).toHaveTextContent('1') + expect(screen.getByTestId('documents-count'))!.toHaveTextContent('1') }) }) }) diff --git a/web/app/components/datasets/documents/detail/__tests__/new-segment.spec.tsx b/web/app/components/datasets/documents/detail/__tests__/new-segment.spec.tsx index f243f85f29..a46b659f11 100644 --- a/web/app/components/datasets/documents/detail/__tests__/new-segment.spec.tsx +++ b/web/app/components/datasets/documents/detail/__tests__/new-segment.spec.tsx @@ -145,37 +145,37 @@ describe('NewSegmentModal', () => { it('should render without crashing', () => { const { container } = render() - expect(container.firstChild).toBeInTheDocument() + expect(container.firstChild)!.toBeInTheDocument() }) it('should render title text', () => { render() - expect(screen.getByText(/segment\.addChunk/i)).toBeInTheDocument() + expect(screen.getByText(/segment\.addChunk/i))!.toBeInTheDocument() }) it('should render chunk content component', () => { render() - expect(screen.getByTestId('chunk-content')).toBeInTheDocument() + expect(screen.getByTestId('chunk-content'))!.toBeInTheDocument() }) it('should render image uploader', () => { render() - expect(screen.getByTestId('image-uploader')).toBeInTheDocument() + expect(screen.getByTestId('image-uploader'))!.toBeInTheDocument() }) it('should render segment index tag', () => { render() - expect(screen.getByTestId('segment-index-tag')).toBeInTheDocument() + expect(screen.getByTestId('segment-index-tag'))!.toBeInTheDocument() }) it('should render dot separator', () => { render() - expect(screen.getByTestId('dot')).toBeInTheDocument() + expect(screen.getByTestId('dot'))!.toBeInTheDocument() }) }) @@ -186,7 +186,7 @@ describe('NewSegmentModal', () => { render() - expect(screen.getByTestId('keywords')).toBeInTheDocument() + expect(screen.getByTestId('keywords'))!.toBeInTheDocument() }) it('should not show keywords when indexing is QUALIFIED', () => { @@ -207,7 +207,7 @@ describe('NewSegmentModal', () => { const closeButtons = container.querySelectorAll('.cursor-pointer') // The close button is the second cursor-pointer element if (closeButtons.length > 1) - fireEvent.click(closeButtons[1]) + fireEvent.click(closeButtons[1]!) expect(mockOnCancel).toHaveBeenCalled() }) @@ -218,7 +218,7 @@ describe('NewSegmentModal', () => { fireEvent.change(questionInput, { target: { value: 'New question content' } }) - expect(questionInput).toHaveValue('New question content') + expect(questionInput)!.toHaveValue('New question content') }) it('should update answer when docForm is QA and typing', () => { @@ -227,7 +227,7 @@ describe('NewSegmentModal', () => { fireEvent.change(answerInput, { target: { value: 'New answer content' } }) - expect(answerInput).toHaveValue('New answer content') + expect(answerInput)!.toHaveValue('New answer content') }) it('should toggle add another checkbox', () => { @@ -237,7 +237,8 @@ describe('NewSegmentModal', () => { fireEvent.click(checkbox) // Assert - checkbox state should toggle - expect(checkbox).toBeInTheDocument() + // Assert - checkbox state should toggle + expect(checkbox)!.toBeInTheDocument() }) }) @@ -329,7 +330,7 @@ describe('NewSegmentModal', () => { const { container } = render() const header = container.querySelector('.border-divider-subtle') - expect(header).toBeInTheDocument() + expect(header)!.toBeInTheDocument() }) it('should show action buttons in header when fullScreen', () => { @@ -337,7 +338,7 @@ describe('NewSegmentModal', () => { render() - expect(screen.getByTestId('action-buttons')).toBeInTheDocument() + expect(screen.getByTestId('action-buttons'))!.toBeInTheDocument() }) it('should show add another in header when fullScreen', () => { @@ -345,7 +346,7 @@ describe('NewSegmentModal', () => { render() - expect(screen.getByTestId('add-another')).toBeInTheDocument() + expect(screen.getByTestId('add-another'))!.toBeInTheDocument() }) it('should call toggleFullScreen when expand button is clicked', () => { @@ -354,7 +355,7 @@ describe('NewSegmentModal', () => { // Act - click the expand button (first cursor-pointer) const expandButtons = container.querySelectorAll('.cursor-pointer') if (expandButtons.length > 0) - fireEvent.click(expandButtons[0]) + fireEvent.click(expandButtons[0]!) expect(mockToggleFullScreen).toHaveBeenCalled() }) @@ -365,13 +366,13 @@ describe('NewSegmentModal', () => { it('should pass actionType add to ActionButtons', () => { render() - expect(screen.getByTestId('action-type')).toHaveTextContent('add') + expect(screen.getByTestId('action-type'))!.toHaveTextContent('add') }) it('should pass isEditMode true to ChunkContent', () => { render() - expect(screen.getByTestId('edit-mode')).toHaveTextContent('editing') + expect(screen.getByTestId('edit-mode'))!.toHaveTextContent('editing') }) }) @@ -384,7 +385,7 @@ describe('NewSegmentModal', () => { target: { value: 'keyword1,keyword2' }, }) - expect(screen.getByTestId('keywords-input')).toHaveValue('keyword1,keyword2') + expect(screen.getByTestId('keywords-input'))!.toHaveValue('keyword1,keyword2') }) it('should handle image upload', () => { @@ -393,7 +394,8 @@ describe('NewSegmentModal', () => { fireEvent.click(screen.getByTestId('upload-image-btn')) // Assert - image uploader should be rendered - expect(screen.getByTestId('image-uploader')).toBeInTheDocument() + // Assert - image uploader should be rendered + expect(screen.getByTestId('image-uploader'))!.toBeInTheDocument() }) it('should maintain structure when rerendered with different docForm', () => { @@ -401,7 +403,7 @@ describe('NewSegmentModal', () => { rerender() - expect(screen.getByTestId('answer-input')).toBeInTheDocument() + expect(screen.getByTestId('answer-input'))!.toBeInTheDocument() }) }) @@ -591,7 +593,9 @@ describe('NewSegmentModal', () => { // Assert - should show count of 5 (3 + 2) // The component uses formatNumber and shows "X characters" - expect(screen.getByText(/5/)).toBeInTheDocument() + // Assert - should show count of 5 (3 + 2) + // The component uses formatNumber and shows "X characters" + expect(screen.getByText(/5/))!.toBeInTheDocument() }) }) @@ -602,8 +606,9 @@ describe('NewSegmentModal', () => { render() // Assert - footer should have both AddAnother and ActionButtons - expect(screen.getByTestId('add-another')).toBeInTheDocument() - expect(screen.getByTestId('action-buttons')).toBeInTheDocument() + // Assert - footer should have both AddAnother and ActionButtons + expect(screen.getByTestId('add-another'))!.toBeInTheDocument() + expect(screen.getByTestId('action-buttons'))!.toBeInTheDocument() }) }) }) diff --git a/web/app/components/datasets/documents/detail/completed/__tests__/child-segment-detail.spec.tsx b/web/app/components/datasets/documents/detail/completed/__tests__/child-segment-detail.spec.tsx index 4e3c7acd2b..b3bec1b0b8 100644 --- a/web/app/components/datasets/documents/detail/completed/__tests__/child-segment-detail.spec.tsx +++ b/web/app/components/datasets/documents/detail/completed/__tests__/child-segment-detail.spec.tsx @@ -92,37 +92,37 @@ describe('ChildSegmentDetail', () => { it('should render without crashing', () => { const { container } = render() - expect(container.firstChild).toBeInTheDocument() + expect(container.firstChild)!.toBeInTheDocument() }) it('should render edit child chunk title', () => { render() - expect(screen.getByText(/segment\.editChildChunk/i)).toBeInTheDocument() + expect(screen.getByText(/segment\.editChildChunk/i))!.toBeInTheDocument() }) it('should render chunk content component', () => { render() - expect(screen.getByTestId('chunk-content')).toBeInTheDocument() + expect(screen.getByTestId('chunk-content'))!.toBeInTheDocument() }) it('should render segment index tag', () => { render() - expect(screen.getByTestId('segment-index-tag')).toBeInTheDocument() + expect(screen.getByTestId('segment-index-tag'))!.toBeInTheDocument() }) it('should render word count', () => { render() - expect(screen.getByText(/segment\.characters/i)).toBeInTheDocument() + expect(screen.getByText(/segment\.characters/i))!.toBeInTheDocument() }) it('should render edit time', () => { render() - expect(screen.getByText(/segment\.editedAt/i)).toBeInTheDocument() + expect(screen.getByText(/segment\.editedAt/i))!.toBeInTheDocument() }) }) @@ -135,7 +135,7 @@ describe('ChildSegmentDetail', () => { const closeButtons = container.querySelectorAll('.cursor-pointer') if (closeButtons.length > 1) - fireEvent.click(closeButtons[1]) + fireEvent.click(closeButtons[1]!) expect(mockOnCancel).toHaveBeenCalled() }) @@ -145,7 +145,7 @@ describe('ChildSegmentDetail', () => { const expandButtons = container.querySelectorAll('.cursor-pointer') if (expandButtons.length > 0) - fireEvent.click(expandButtons[0]) + fireEvent.click(expandButtons[0]!) expect(mockToggleFullScreen).toHaveBeenCalled() }) @@ -170,7 +170,7 @@ describe('ChildSegmentDetail', () => { target: { value: 'Updated content' }, }) - expect(screen.getByTestId('content-input')).toHaveValue('Updated content') + expect(screen.getByTestId('content-input'))!.toHaveValue('Updated content') }) }) @@ -181,7 +181,7 @@ describe('ChildSegmentDetail', () => { render() - expect(screen.getByTestId('action-buttons')).toBeInTheDocument() + expect(screen.getByTestId('action-buttons'))!.toBeInTheDocument() }) it('should not show footer action buttons when fullScreen is true', () => { @@ -200,7 +200,7 @@ describe('ChildSegmentDetail', () => { render() - expect(screen.getByTestId('action-buttons')).toBeInTheDocument() + expect(screen.getByTestId('action-buttons'))!.toBeInTheDocument() }) }) @@ -209,13 +209,13 @@ describe('ChildSegmentDetail', () => { it('should pass isChildChunk true to ActionButtons', () => { render() - expect(screen.getByTestId('is-child-chunk')).toHaveTextContent('true') + expect(screen.getByTestId('is-child-chunk'))!.toHaveTextContent('true') }) it('should pass isEditMode true to ChunkContent', () => { render() - expect(screen.getByTestId('edit-mode')).toHaveTextContent('editing') + expect(screen.getByTestId('edit-mode'))!.toHaveTextContent('editing') }) }) @@ -225,7 +225,7 @@ describe('ChildSegmentDetail', () => { , ) - expect(container.firstChild).toBeInTheDocument() + expect(container.firstChild)!.toBeInTheDocument() }) it('should handle empty content', () => { @@ -233,7 +233,7 @@ describe('ChildSegmentDetail', () => { render() - expect(screen.getByTestId('content-input')).toHaveValue('') + expect(screen.getByTestId('content-input'))!.toHaveValue('') }) it('should maintain structure when rerendered', () => { @@ -242,7 +242,7 @@ describe('ChildSegmentDetail', () => { const updatedInfo = { ...defaultChildChunkInfo, content: 'New content' } rerender() - expect(screen.getByTestId('content-input')).toBeInTheDocument() + expect(screen.getByTestId('content-input'))!.toBeInTheDocument() }) }) @@ -258,6 +258,37 @@ describe('ChildSegmentDetail', () => { it('should have save button enabled by default', () => { render() + // Assert - save button should be enabled initially + // Assert - save button should be enabled initially + // Assert - save button should be enabled initially + // Assert - save button should be enabled initially + // Assert - save button should be enabled initially + // Assert - save button should be enabled initially + // Assert - save button should be enabled initially + // Assert - save button should be enabled initially + // Assert - save button should be enabled initially + // Assert - save button should be enabled initially + // Assert - save button should be enabled initially + // Assert - save button should be enabled initially + // Assert - save button should be enabled initially + // Assert - save button should be enabled initially + // Assert - save button should be enabled initially + // Assert - save button should be enabled initially + // Assert - save button should be enabled initially + // Assert - save button should be enabled initially + // Assert - save button should be enabled initially + // Assert - save button should be enabled initially + // Assert - save button should be enabled initially + // Assert - save button should be enabled initially + // Assert - save button should be enabled initially + // Assert - save button should be enabled initially + // Assert - save button should be enabled initially + // Assert - save button should be enabled initially + // Assert - save button should be enabled initially + // Assert - save button should be enabled initially + // Assert - save button should be enabled initially + // Assert - save button should be enabled initially + // Assert - save button should be enabled initially // Assert - save button should be enabled initially expect(screen.getByTestId('save-btn')).not.toBeDisabled() }) diff --git a/web/app/components/datasets/documents/detail/completed/__tests__/index.spec.tsx b/web/app/components/datasets/documents/detail/completed/__tests__/index.spec.tsx index aae0b226db..b7344d7a0f 100644 --- a/web/app/components/datasets/documents/detail/completed/__tests__/index.spec.tsx +++ b/web/app/components/datasets/documents/detail/completed/__tests__/index.spec.tsx @@ -287,10 +287,10 @@ describe('SegmentListContext', () => { render() - expect(screen.getByTestId('isCollapsed')).toHaveTextContent('true') - expect(screen.getByTestId('fullScreen')).toHaveTextContent('false') - expect(screen.getByTestId('currSegmentShowModal')).toHaveTextContent('false') - expect(screen.getByTestId('currChildChunkShowModal')).toHaveTextContent('false') + expect(screen.getByTestId('isCollapsed'))!.toHaveTextContent('true') + expect(screen.getByTestId('fullScreen'))!.toHaveTextContent('false') + expect(screen.getByTestId('currSegmentShowModal'))!.toHaveTextContent('false') + expect(screen.getByTestId('currChildChunkShowModal'))!.toHaveTextContent('false') }) }) @@ -324,9 +324,9 @@ describe('SegmentListContext', () => { , ) - expect(screen.getByTestId('isCollapsed')).toHaveTextContent('false') - expect(screen.getByTestId('fullScreen')).toHaveTextContent('true') - expect(screen.getByTestId('currSegmentShowModal')).toHaveTextContent('true') + expect(screen.getByTestId('isCollapsed'))!.toHaveTextContent('false') + expect(screen.getByTestId('fullScreen'))!.toHaveTextContent('true') + expect(screen.getByTestId('currSegmentShowModal'))!.toHaveTextContent('true') }) }) @@ -356,8 +356,8 @@ describe('SegmentListContext', () => { , ) - expect(screen.getByTestId('isCollapsed')).toHaveTextContent('true') - expect(screen.getByTestId('fullScreen')).toHaveTextContent('false') + expect(screen.getByTestId('isCollapsed'))!.toHaveTextContent('true') + expect(screen.getByTestId('fullScreen'))!.toHaveTextContent('false') // Rerender with changed values rerender( @@ -373,8 +373,8 @@ describe('SegmentListContext', () => { , ) - expect(screen.getByTestId('isCollapsed')).toHaveTextContent('false') - expect(screen.getByTestId('fullScreen')).toHaveTextContent('true') + expect(screen.getByTestId('isCollapsed'))!.toHaveTextContent('false') + expect(screen.getByTestId('fullScreen'))!.toHaveTextContent('true') }) }) }) @@ -400,7 +400,7 @@ describe('Completed Component', () => { it('should render MenuBar when not in full-doc mode', () => { render(, { wrapper: createWrapper() }) - expect(screen.getByTestId('menu-bar')).toBeInTheDocument() + expect(screen.getByTestId('menu-bar'))!.toBeInTheDocument() }) it('should not render MenuBar when in full-doc mode', () => { @@ -415,7 +415,7 @@ describe('Completed Component', () => { it('should render GeneralModeContent when not in full-doc mode', () => { render(, { wrapper: createWrapper() }) - expect(screen.getByTestId('general-mode-content')).toBeInTheDocument() + expect(screen.getByTestId('general-mode-content'))!.toBeInTheDocument() }) it('should render FullDocModeContent when in full-doc mode', () => { @@ -424,25 +424,25 @@ describe('Completed Component', () => { render(, { wrapper: createWrapper() }) - expect(screen.getByTestId('full-doc-mode-content')).toBeInTheDocument() + expect(screen.getByTestId('full-doc-mode-content'))!.toBeInTheDocument() }) it('should render Pagination component', () => { render(, { wrapper: createWrapper() }) - expect(screen.getByTestId('pagination')).toBeInTheDocument() + expect(screen.getByTestId('pagination'))!.toBeInTheDocument() }) it('should render Divider component', () => { render(, { wrapper: createWrapper() }) - expect(screen.getByTestId('divider')).toBeInTheDocument() + expect(screen.getByTestId('divider'))!.toBeInTheDocument() }) it('should render DrawerGroup when docForm is available', () => { render(, { wrapper: createWrapper() }) - expect(screen.getByTestId('drawer-group')).toBeInTheDocument() + expect(screen.getByTestId('drawer-group'))!.toBeInTheDocument() }) it('should not render DrawerGroup when docForm is undefined', () => { @@ -458,7 +458,7 @@ describe('Completed Component', () => { it('should start with page 0 (current - 1)', () => { render(, { wrapper: createWrapper() }) - expect(screen.getByTestId('current-page')).toHaveTextContent('0') + expect(screen.getByTestId('current-page'))!.toHaveTextContent('0') }) it('should update page when pagination changes', async () => { @@ -468,7 +468,7 @@ describe('Completed Component', () => { fireEvent.click(nextPageButton) await waitFor(() => { - expect(screen.getByTestId('current-page')).toHaveTextContent('1') + expect(screen.getByTestId('current-page'))!.toHaveTextContent('1') }) }) @@ -479,7 +479,8 @@ describe('Completed Component', () => { fireEvent.click(changeLimitButton) // Limit change is handled internally - expect(changeLimitButton).toBeInTheDocument() + // Limit change is handled internally + expect(changeLimitButton)!.toBeInTheDocument() }) }) @@ -495,19 +496,19 @@ describe('Completed Component', () => { it('should handle archived prop', () => { render(, { wrapper: createWrapper() }) - expect(screen.getByTestId('general-mode-content')).toBeInTheDocument() + expect(screen.getByTestId('general-mode-content'))!.toBeInTheDocument() }) it('should handle embeddingAvailable prop', () => { render(, { wrapper: createWrapper() }) - expect(screen.getByTestId('general-mode-content')).toBeInTheDocument() + expect(screen.getByTestId('general-mode-content'))!.toBeInTheDocument() }) it('should handle showNewSegmentModal prop', () => { render(, { wrapper: createWrapper() }) - expect(screen.getByTestId('drawer-group')).toBeInTheDocument() + expect(screen.getByTestId('drawer-group'))!.toBeInTheDocument() }) }) @@ -517,7 +518,8 @@ describe('Completed Component', () => { render(, { wrapper: createWrapper() }) // Context is provided, components should render without errors - expect(screen.getByTestId('general-mode-content')).toBeInTheDocument() + // Context is provided, components should render without errors + expect(screen.getByTestId('general-mode-content'))!.toBeInTheDocument() }) }) }) @@ -544,7 +546,7 @@ describe('Edge Cases', () => { render(, { wrapper: createWrapper() }) - expect(screen.getByTestId('general-mode-content')).toBeInTheDocument() + expect(screen.getByTestId('general-mode-content'))!.toBeInTheDocument() }) it('should handle empty documentId', () => { @@ -552,19 +554,19 @@ describe('Edge Cases', () => { render(, { wrapper: createWrapper() }) - expect(screen.getByTestId('general-mode-content')).toBeInTheDocument() + expect(screen.getByTestId('general-mode-content'))!.toBeInTheDocument() }) it('should handle undefined importStatus', () => { render(, { wrapper: createWrapper() }) - expect(screen.getByTestId('general-mode-content')).toBeInTheDocument() + expect(screen.getByTestId('general-mode-content'))!.toBeInTheDocument() }) it('should handle ProcessStatus.COMPLETED importStatus', () => { render(, { wrapper: createWrapper() }) - expect(screen.getByTestId('general-mode-content')).toBeInTheDocument() + expect(screen.getByTestId('general-mode-content'))!.toBeInTheDocument() }) it('should handle all ChunkingMode values', () => { @@ -575,7 +577,7 @@ describe('Edge Cases', () => { const { unmount } = render(, { wrapper: createWrapper() }) - expect(screen.getByTestId('pagination')).toBeInTheDocument() + expect(screen.getByTestId('pagination'))!.toBeInTheDocument() unmount() }) @@ -591,7 +593,7 @@ describe('Edge Cases', () => { const { unmount } = render(, { wrapper: createWrapper() }) - expect(screen.getByTestId('pagination')).toBeInTheDocument() + expect(screen.getByTestId('pagination'))!.toBeInTheDocument() unmount() }) @@ -617,33 +619,34 @@ describe('Integration Tests', () => { render(, { wrapper: createWrapper() }) // All components should render without errors - expect(screen.getByTestId('menu-bar')).toBeInTheDocument() - expect(screen.getByTestId('general-mode-content')).toBeInTheDocument() - expect(screen.getByTestId('pagination')).toBeInTheDocument() - expect(screen.getByTestId('drawer-group')).toBeInTheDocument() + // All components should render without errors + expect(screen.getByTestId('menu-bar'))!.toBeInTheDocument() + expect(screen.getByTestId('general-mode-content'))!.toBeInTheDocument() + expect(screen.getByTestId('pagination'))!.toBeInTheDocument() + expect(screen.getByTestId('drawer-group'))!.toBeInTheDocument() }) it('should update UI when mode changes', () => { const { rerender } = render(, { wrapper: createWrapper() }) - expect(screen.getByTestId('general-mode-content')).toBeInTheDocument() + expect(screen.getByTestId('general-mode-content'))!.toBeInTheDocument() mockDocForm.current = ChunkingModeEnum.parentChild mockParentMode.current = 'full-doc' rerender() - expect(screen.getByTestId('full-doc-mode-content')).toBeInTheDocument() + expect(screen.getByTestId('full-doc-mode-content'))!.toBeInTheDocument() }) it('should handle prop updates correctly', () => { const { rerender } = render(, { wrapper: createWrapper() }) - expect(screen.getByTestId('drawer-group')).toBeInTheDocument() + expect(screen.getByTestId('drawer-group'))!.toBeInTheDocument() rerender() - expect(screen.getByTestId('drawer-group')).toBeInTheDocument() + expect(screen.getByTestId('drawer-group'))!.toBeInTheDocument() }) }) @@ -696,6 +699,37 @@ describe('Batch Action Callbacks', () => { it('should not render batch actions when no segments are selected initially', async () => { render(, { wrapper: createWrapper() }) + // Initially no segments are selected, so batch action should not be visible + // Initially no segments are selected, so batch action should not be visible + // Initially no segments are selected, so batch action should not be visible + // Initially no segments are selected, so batch action should not be visible + // Initially no segments are selected, so batch action should not be visible + // Initially no segments are selected, so batch action should not be visible + // Initially no segments are selected, so batch action should not be visible + // Initially no segments are selected, so batch action should not be visible + // Initially no segments are selected, so batch action should not be visible + // Initially no segments are selected, so batch action should not be visible + // Initially no segments are selected, so batch action should not be visible + // Initially no segments are selected, so batch action should not be visible + // Initially no segments are selected, so batch action should not be visible + // Initially no segments are selected, so batch action should not be visible + // Initially no segments are selected, so batch action should not be visible + // Initially no segments are selected, so batch action should not be visible + // Initially no segments are selected, so batch action should not be visible + // Initially no segments are selected, so batch action should not be visible + // Initially no segments are selected, so batch action should not be visible + // Initially no segments are selected, so batch action should not be visible + // Initially no segments are selected, so batch action should not be visible + // Initially no segments are selected, so batch action should not be visible + // Initially no segments are selected, so batch action should not be visible + // Initially no segments are selected, so batch action should not be visible + // Initially no segments are selected, so batch action should not be visible + // Initially no segments are selected, so batch action should not be visible + // Initially no segments are selected, so batch action should not be visible + // Initially no segments are selected, so batch action should not be visible + // Initially no segments are selected, so batch action should not be visible + // Initially no segments are selected, so batch action should not be visible + // Initially no segments are selected, so batch action should not be visible // Initially no segments are selected, so batch action should not be visible expect(screen.queryByTestId('batch-action')).not.toBeInTheDocument() }) @@ -708,7 +742,7 @@ describe('Batch Action Callbacks', () => { // Now batch actions should be visible await waitFor(() => { - expect(screen.getByTestId('batch-action')).toBeInTheDocument() + expect(screen.getByTestId('batch-action'))!.toBeInTheDocument() }) }) @@ -721,7 +755,7 @@ describe('Batch Action Callbacks', () => { // Wait for batch actions to appear await waitFor(() => { - expect(screen.getByTestId('batch-action')).toBeInTheDocument() + expect(screen.getByTestId('batch-action'))!.toBeInTheDocument() }) const enableButton = screen.getByTestId('batch-enable') @@ -739,7 +773,7 @@ describe('Batch Action Callbacks', () => { // Wait for batch actions to appear await waitFor(() => { - expect(screen.getByTestId('batch-action')).toBeInTheDocument() + expect(screen.getByTestId('batch-action'))!.toBeInTheDocument() }) const disableButton = screen.getByTestId('batch-disable') @@ -757,7 +791,7 @@ describe('Batch Action Callbacks', () => { // Wait for batch actions to appear await waitFor(() => { - expect(screen.getByTestId('batch-action')).toBeInTheDocument() + expect(screen.getByTestId('batch-action'))!.toBeInTheDocument() }) const deleteButton = screen.getByTestId('batch-delete') @@ -887,14 +921,15 @@ describe('refreshChunkListDataWithDetailChanged branch coverage', () => { // Create a refreshMap similar to the component const refreshMap: Record void> = { - true: () => { + true: (() => { mockInvalidAll() mockInvalidDisabled() - }, + })!, } // Execute the 'true' branch - refreshMap.true() + // Execute the 'true' branch + refreshMap.true!() expect(mockInvalidAll).toHaveBeenCalled() expect(mockInvalidDisabled).toHaveBeenCalled() @@ -907,14 +942,15 @@ describe('refreshChunkListDataWithDetailChanged branch coverage', () => { // Create a refreshMap similar to the component const refreshMap: Record void> = { - false: () => { + false: (() => { mockInvalidAll() mockInvalidEnabled() - }, + })!, } // Execute the 'false' branch - refreshMap.false() + // Execute the 'false' branch + refreshMap.false!() expect(mockInvalidAll).toHaveBeenCalled() expect(mockInvalidEnabled).toHaveBeenCalled() @@ -989,13 +1025,13 @@ describe('Inline callback and hook initialization coverage', () => { fireEvent.click(screen.getByTestId('next-page')) await waitFor(() => { - expect(screen.getByTestId('current-page')).toHaveTextContent('1') + expect(screen.getByTestId('current-page'))!.toHaveTextContent('1') }) fireEvent.click(screen.getByTestId('status-enabled')) await waitFor(() => { - expect(screen.getByTestId('current-page')).toHaveTextContent('0') + expect(screen.getByTestId('current-page'))!.toHaveTextContent('0') }) }) @@ -1006,7 +1042,7 @@ describe('Inline callback and hook initialization coverage', () => { , { wrapper: createWrapper() }, ) - expect(screen.getByTestId('drawer-group')).toBeInTheDocument() + expect(screen.getByTestId('drawer-group'))!.toBeInTheDocument() }) // Covers lines 74-90: refreshChunkListDataWithDetailChanged with status true @@ -1056,7 +1092,7 @@ describe('Inline callback and hook initialization coverage', () => { fireEvent.click(screen.getByTestId('select-all-button')) await waitFor(() => { - expect(screen.getByTestId('batch-action')).toBeInTheDocument() + expect(screen.getByTestId('batch-action'))!.toBeInTheDocument() }) fireEvent.click(screen.getByTestId('cancel-batch')) @@ -1072,7 +1108,7 @@ describe('Inline callback and hook initialization coverage', () => { fireEvent.click(screen.getByTestId('select-all-button')) await waitFor(() => { - expect(screen.getByTestId('batch-action')).toBeInTheDocument() + expect(screen.getByTestId('batch-action'))!.toBeInTheDocument() }) fireEvent.click(screen.getByTestId('batch-enable')) @@ -1086,7 +1122,7 @@ describe('Inline callback and hook initialization coverage', () => { fireEvent.click(screen.getByTestId('select-all-button')) await waitFor(() => { - expect(screen.getByTestId('batch-action')).toBeInTheDocument() + expect(screen.getByTestId('batch-action'))!.toBeInTheDocument() }) fireEvent.click(screen.getByTestId('batch-disable')) @@ -1100,7 +1136,7 @@ describe('Inline callback and hook initialization coverage', () => { fireEvent.click(screen.getByTestId('select-all-button')) await waitFor(() => { - expect(screen.getByTestId('batch-action')).toBeInTheDocument() + expect(screen.getByTestId('batch-action'))!.toBeInTheDocument() }) fireEvent.click(screen.getByTestId('batch-delete')) @@ -1115,12 +1151,12 @@ describe('Inline callback and hook initialization coverage', () => { fireEvent.click(screen.getByTestId('next-page')) await waitFor(() => { - expect(screen.getByTestId('current-page')).toHaveTextContent('1') + expect(screen.getByTestId('current-page'))!.toHaveTextContent('1') }) fireEvent.click(screen.getByTestId('next-page')) await waitFor(() => { - expect(screen.getByTestId('current-page')).toHaveTextContent('2') + expect(screen.getByTestId('current-page'))!.toHaveTextContent('2') }) }) @@ -1132,7 +1168,7 @@ describe('Inline callback and hook initialization coverage', () => { render(, { wrapper: createWrapper() }) - expect(screen.getByTestId('total-items')).toHaveTextContent('42') + expect(screen.getByTestId('total-items'))!.toHaveTextContent('42') }) // Covers search input change @@ -1142,6 +1178,6 @@ describe('Inline callback and hook initialization coverage', () => { const searchInput = screen.getByTestId('search-input') fireEvent.change(searchInput, { target: { value: 'test query' } }) - expect(searchInput).toHaveValue('test query') + expect(searchInput)!.toHaveValue('test query') }) }) diff --git a/web/app/components/datasets/documents/detail/completed/__tests__/new-child-segment.spec.tsx b/web/app/components/datasets/documents/detail/completed/__tests__/new-child-segment.spec.tsx index 150d399a5d..ff9fb4c524 100644 --- a/web/app/components/datasets/documents/detail/completed/__tests__/new-child-segment.spec.tsx +++ b/web/app/components/datasets/documents/detail/completed/__tests__/new-child-segment.spec.tsx @@ -110,31 +110,31 @@ describe('NewChildSegmentModal', () => { it('should render without crashing', () => { const { container } = render() - expect(container.firstChild).toBeInTheDocument() + expect(container.firstChild)!.toBeInTheDocument() }) it('should render add child chunk title', () => { render() - expect(screen.getByText(/segment\.addChildChunk/i)).toBeInTheDocument() + expect(screen.getByText(/segment\.addChildChunk/i))!.toBeInTheDocument() }) it('should render chunk content component', () => { render() - expect(screen.getByTestId('chunk-content')).toBeInTheDocument() + expect(screen.getByTestId('chunk-content'))!.toBeInTheDocument() }) it('should render segment index tag with new child chunk label', () => { render() - expect(screen.getByTestId('segment-index-tag')).toBeInTheDocument() + expect(screen.getByTestId('segment-index-tag'))!.toBeInTheDocument() }) it('should render add another checkbox', () => { render() - expect(screen.getByTestId('add-another')).toBeInTheDocument() + expect(screen.getByTestId('add-another'))!.toBeInTheDocument() }) }) @@ -147,7 +147,7 @@ describe('NewChildSegmentModal', () => { const closeButtons = container.querySelectorAll('.cursor-pointer') if (closeButtons.length > 1) - fireEvent.click(closeButtons[1]) + fireEvent.click(closeButtons[1]!) expect(mockOnCancel).toHaveBeenCalled() }) @@ -157,7 +157,7 @@ describe('NewChildSegmentModal', () => { const expandButtons = container.querySelectorAll('.cursor-pointer') if (expandButtons.length > 0) - fireEvent.click(expandButtons[0]) + fireEvent.click(expandButtons[0]!) expect(mockToggleFullScreen).toHaveBeenCalled() }) @@ -169,7 +169,7 @@ describe('NewChildSegmentModal', () => { target: { value: 'New content' }, }) - expect(screen.getByTestId('content-input')).toHaveValue('New content') + expect(screen.getByTestId('content-input'))!.toHaveValue('New content') }) it('should toggle add another checkbox', () => { @@ -178,7 +178,7 @@ describe('NewChildSegmentModal', () => { fireEvent.click(checkbox) - expect(checkbox).toBeInTheDocument() + expect(checkbox)!.toBeInTheDocument() }) }) @@ -253,7 +253,7 @@ describe('NewChildSegmentModal', () => { render() - expect(screen.getByTestId('action-buttons')).toBeInTheDocument() + expect(screen.getByTestId('action-buttons'))!.toBeInTheDocument() }) it('should show add another in header when fullScreen', () => { @@ -261,7 +261,7 @@ describe('NewChildSegmentModal', () => { render() - expect(screen.getByTestId('add-another')).toBeInTheDocument() + expect(screen.getByTestId('add-another'))!.toBeInTheDocument() }) }) @@ -270,19 +270,19 @@ describe('NewChildSegmentModal', () => { it('should pass actionType add to ActionButtons', () => { render() - expect(screen.getByTestId('action-type')).toHaveTextContent('add') + expect(screen.getByTestId('action-type'))!.toHaveTextContent('add') }) it('should pass isChildChunk true to ActionButtons', () => { render() - expect(screen.getByTestId('is-child-chunk')).toHaveTextContent('true') + expect(screen.getByTestId('is-child-chunk'))!.toHaveTextContent('true') }) it('should pass isEditMode true to ChunkContent', () => { render() - expect(screen.getByTestId('edit-mode')).toHaveTextContent('editing') + expect(screen.getByTestId('edit-mode'))!.toHaveTextContent('editing') }) }) @@ -292,7 +292,7 @@ describe('NewChildSegmentModal', () => { const { container } = render() - expect(container.firstChild).toBeInTheDocument() + expect(container.firstChild)!.toBeInTheDocument() }) it('should maintain structure when rerendered', () => { @@ -300,7 +300,7 @@ describe('NewChildSegmentModal', () => { rerender() - expect(screen.getByTestId('chunk-content')).toBeInTheDocument() + expect(screen.getByTestId('chunk-content'))!.toBeInTheDocument() }) }) @@ -351,7 +351,7 @@ describe('NewChildSegmentModal', () => { // Assert - modal should not close, only content cleared await waitFor(() => { - expect(screen.getByTestId('content-input')).toHaveValue('') + expect(screen.getByTestId('content-input'))!.toHaveValue('') }) }) }) diff --git a/web/app/components/datasets/documents/detail/completed/__tests__/segment-detail.spec.tsx b/web/app/components/datasets/documents/detail/completed/__tests__/segment-detail.spec.tsx index dbce9b7f22..4e17cd39b3 100644 --- a/web/app/components/datasets/documents/detail/completed/__tests__/segment-detail.spec.tsx +++ b/web/app/components/datasets/documents/detail/completed/__tests__/segment-detail.spec.tsx @@ -174,37 +174,37 @@ describe('SegmentDetail', () => { it('should render without crashing', () => { const { container } = render() - expect(container.firstChild).toBeInTheDocument() + expect(container.firstChild)!.toBeInTheDocument() }) it('should render title for view mode', () => { render() - expect(screen.getByText(/segment\.chunkDetail/i)).toBeInTheDocument() + expect(screen.getByText(/segment\.chunkDetail/i))!.toBeInTheDocument() }) it('should render title for edit mode', () => { render() - expect(screen.getByText(/segment\.editChunk/i)).toBeInTheDocument() + expect(screen.getByText(/segment\.editChunk/i))!.toBeInTheDocument() }) it('should render chunk content component', () => { render() - expect(screen.getByTestId('chunk-content')).toBeInTheDocument() + expect(screen.getByTestId('chunk-content'))!.toBeInTheDocument() }) it('should render image uploader', () => { render() - expect(screen.getByTestId('image-uploader')).toBeInTheDocument() + expect(screen.getByTestId('image-uploader'))!.toBeInTheDocument() }) it('should render segment index tag', () => { render() - expect(screen.getByTestId('segment-index-tag')).toBeInTheDocument() + expect(screen.getByTestId('segment-index-tag'))!.toBeInTheDocument() }) }) @@ -213,25 +213,25 @@ describe('SegmentDetail', () => { it('should pass isEditMode to ChunkContent', () => { render() - expect(screen.getByTestId('edit-mode')).toHaveTextContent('editing') + expect(screen.getByTestId('edit-mode'))!.toHaveTextContent('editing') }) it('should disable image uploader in view mode', () => { render() - expect(screen.getByTestId('uploader-disabled')).toHaveTextContent('disabled') + expect(screen.getByTestId('uploader-disabled'))!.toHaveTextContent('disabled') }) it('should enable image uploader in edit mode', () => { render() - expect(screen.getByTestId('uploader-disabled')).toHaveTextContent('enabled') + expect(screen.getByTestId('uploader-disabled'))!.toHaveTextContent('enabled') }) it('should show action buttons in edit mode', () => { render() - expect(screen.getByTestId('action-buttons')).toBeInTheDocument() + expect(screen.getByTestId('action-buttons'))!.toBeInTheDocument() }) it('should not show action buttons in view mode (non-fullscreen)', () => { @@ -248,7 +248,7 @@ describe('SegmentDetail', () => { render() - expect(screen.getByTestId('keywords')).toBeInTheDocument() + expect(screen.getByTestId('keywords'))!.toBeInTheDocument() }) it('should not show keywords when indexing is QUALIFIED', () => { @@ -264,7 +264,7 @@ describe('SegmentDetail', () => { render() - expect(screen.getByTestId('keywords-action')).toHaveTextContent('view') + expect(screen.getByTestId('keywords-action'))!.toHaveTextContent('view') }) it('should pass edit action type when in edit mode', () => { @@ -272,7 +272,7 @@ describe('SegmentDetail', () => { render() - expect(screen.getByTestId('keywords-action')).toHaveTextContent('edit') + expect(screen.getByTestId('keywords-action'))!.toHaveTextContent('edit') }) }) @@ -283,7 +283,7 @@ describe('SegmentDetail', () => { const closeButtons = container.querySelectorAll('.cursor-pointer') if (closeButtons.length > 1) - fireEvent.click(closeButtons[1]) + fireEvent.click(closeButtons[1]!) expect(mockOnCancel).toHaveBeenCalled() }) @@ -293,7 +293,7 @@ describe('SegmentDetail', () => { const expandButtons = container.querySelectorAll('.cursor-pointer') if (expandButtons.length > 0) - fireEvent.click(expandButtons[0]) + fireEvent.click(expandButtons[0]!) expect(mockToggleFullScreen).toHaveBeenCalled() }) @@ -322,7 +322,7 @@ describe('SegmentDetail', () => { target: { value: 'Updated content' }, }) - expect(screen.getByTestId('question-input')).toHaveValue('Updated content') + expect(screen.getByTestId('question-input'))!.toHaveValue('Updated content') }) }) @@ -333,7 +333,7 @@ describe('SegmentDetail', () => { render() - expect(screen.getByTestId('regenerate-btn')).toBeInTheDocument() + expect(screen.getByTestId('regenerate-btn'))!.toBeInTheDocument() }) it('should not show regeneration button when runtimeMode is not general', () => { @@ -349,7 +349,7 @@ describe('SegmentDetail', () => { fireEvent.click(screen.getByTestId('regenerate-btn')) - expect(screen.getByTestId('regeneration-modal')).toBeInTheDocument() + expect(screen.getByTestId('regeneration-modal'))!.toBeInTheDocument() }) it('should call onModalStateChange when regeneration modal opens', () => { @@ -392,7 +392,7 @@ describe('SegmentDetail', () => { render() - expect(screen.getByTestId('action-buttons')).toBeInTheDocument() + expect(screen.getByTestId('action-buttons'))!.toBeInTheDocument() }) it('should apply full screen styling when fullScreen is true', () => { @@ -401,7 +401,7 @@ describe('SegmentDetail', () => { const { container } = render() const header = container.querySelector('.border-divider-subtle') - expect(header).toBeInTheDocument() + expect(header)!.toBeInTheDocument() }) }) @@ -415,7 +415,7 @@ describe('SegmentDetail', () => { const { container } = render() - expect(container.firstChild).toBeInTheDocument() + expect(container.firstChild)!.toBeInTheDocument() }) it('should handle empty keywords array', () => { @@ -424,7 +424,7 @@ describe('SegmentDetail', () => { render() - expect(screen.getByTestId('keywords-input')).toHaveValue('') + expect(screen.getByTestId('keywords-input'))!.toHaveValue('') }) it('should maintain structure when rerendered', () => { @@ -432,7 +432,7 @@ describe('SegmentDetail', () => { rerender() - expect(screen.getByTestId('action-buttons')).toBeInTheDocument() + expect(screen.getByTestId('action-buttons'))!.toBeInTheDocument() }) }) @@ -443,7 +443,7 @@ describe('SegmentDetail', () => { fireEvent.click(screen.getByTestId('add-attachment-btn')) - expect(screen.getByTestId('attachments-count')).toHaveTextContent('1') + expect(screen.getByTestId('attachments-count'))!.toHaveTextContent('1') }) it('should pass attachments to onUpdate when save is clicked', () => { @@ -476,7 +476,7 @@ describe('SegmentDetail', () => { render() - expect(screen.getByTestId('attachments-count')).toHaveTextContent('1') + expect(screen.getByTestId('attachments-count'))!.toHaveTextContent('1') }) }) @@ -529,7 +529,7 @@ describe('SegmentDetail', () => { it('should render answer input in QA mode', () => { render() - expect(screen.getByTestId('answer-input')).toBeInTheDocument() + expect(screen.getByTestId('answer-input'))!.toBeInTheDocument() }) it('should update answer when input changes', () => { @@ -539,14 +539,15 @@ describe('SegmentDetail', () => { target: { value: 'Updated answer' }, }) - expect(screen.getByTestId('answer-input')).toHaveValue('Updated answer') + expect(screen.getByTestId('answer-input'))!.toHaveValue('Updated answer') }) it('should calculate word count correctly in QA mode', () => { render() // Assert - should show combined length of question and answer - expect(screen.getByText(/segment\.characters/i)).toBeInTheDocument() + // Assert - should show combined length of question and answer + expect(screen.getByText(/segment\.characters/i))!.toBeInTheDocument() }) }) @@ -557,7 +558,7 @@ describe('SegmentDetail', () => { render() - expect(screen.getByTestId('segment-index-tag')).toBeInTheDocument() + expect(screen.getByTestId('segment-index-tag'))!.toBeInTheDocument() }) }) @@ -571,7 +572,7 @@ describe('SegmentDetail', () => { target: { value: 'new,keywords' }, }) - expect(screen.getByTestId('keywords-input')).toHaveValue('new,keywords') + expect(screen.getByTestId('keywords-input'))!.toHaveValue('new,keywords') }) }) }) diff --git a/web/app/components/datasets/documents/detail/completed/common/__tests__/action-buttons.spec.tsx b/web/app/components/datasets/documents/detail/completed/common/__tests__/action-buttons.spec.tsx index edf4b30922..c8500e3769 100644 --- a/web/app/components/datasets/documents/detail/completed/common/__tests__/action-buttons.spec.tsx +++ b/web/app/components/datasets/documents/detail/completed/common/__tests__/action-buttons.spec.tsx @@ -63,7 +63,7 @@ describe('ActionButtons', () => { { wrapper: createWrapper({}) }, ) - expect(container.firstChild).toBeInTheDocument() + expect(container.firstChild)!.toBeInTheDocument() }) it('should render cancel button', () => { @@ -76,7 +76,7 @@ describe('ActionButtons', () => { { wrapper: createWrapper({}) }, ) - expect(screen.getByText(/operation\.cancel/i)).toBeInTheDocument() + expect(screen.getByText(/operation\.cancel/i))!.toBeInTheDocument() }) it('should render save button', () => { @@ -89,7 +89,7 @@ describe('ActionButtons', () => { { wrapper: createWrapper({}) }, ) - expect(screen.getByText(/operation\.save/i)).toBeInTheDocument() + expect(screen.getByText(/operation\.save/i))!.toBeInTheDocument() }) it('should render ESC keyboard hint on cancel button', () => { @@ -102,7 +102,7 @@ describe('ActionButtons', () => { { wrapper: createWrapper({}) }, ) - expect(screen.getByText('ESC')).toBeInTheDocument() + expect(screen.getByText('ESC'))!.toBeInTheDocument() }) it('should render S keyboard hint on save button', () => { @@ -115,7 +115,7 @@ describe('ActionButtons', () => { { wrapper: createWrapper({}) }, ) - expect(screen.getByText('S')).toBeInTheDocument() + expect(screen.getByText('S'))!.toBeInTheDocument() }) }) @@ -132,7 +132,7 @@ describe('ActionButtons', () => { ) const cancelButton = screen.getAllByRole('button')[0] - fireEvent.click(cancelButton) + fireEvent.click(cancelButton!) expect(mockHandleCancel).toHaveBeenCalledTimes(1) }) @@ -150,7 +150,7 @@ describe('ActionButtons', () => { const buttons = screen.getAllByRole('button') const saveButton = buttons[buttons.length - 1] // Save button is last - fireEvent.click(saveButton) + fireEvent.click(saveButton!) expect(mockHandleSave).toHaveBeenCalledTimes(1) }) @@ -167,7 +167,7 @@ describe('ActionButtons', () => { const buttons = screen.getAllByRole('button') const saveButton = buttons[buttons.length - 1] - expect(saveButton).toBeDisabled() + expect(saveButton)!.toBeDisabled() }) }) @@ -187,7 +187,7 @@ describe('ActionButtons', () => { { wrapper: createWrapper({ docForm: ChunkingMode.parentChild, parentMode: 'paragraph' }) }, ) - expect(screen.getByText(/operation\.saveAndRegenerate/i)).toBeInTheDocument() + expect(screen.getByText(/operation\.saveAndRegenerate/i))!.toBeInTheDocument() }) it('should not show regeneration button when isChildChunk is true', () => { @@ -278,7 +278,7 @@ describe('ActionButtons', () => { ) const regenerationButton = screen.getByText(/operation\.saveAndRegenerate/i).closest('button') - expect(regenerationButton).toBeDisabled() + expect(regenerationButton)!.toBeDisabled() }) }) @@ -298,7 +298,8 @@ describe('ActionButtons', () => { ) // Assert - regeneration button should show with default actionType='edit' - expect(screen.getByText(/operation\.saveAndRegenerate/i)).toBeInTheDocument() + // Assert - regeneration button should show with default actionType='edit' + expect(screen.getByText(/operation\.saveAndRegenerate/i))!.toBeInTheDocument() }) it('should use default isChildChunk of false', () => { @@ -316,7 +317,8 @@ describe('ActionButtons', () => { ) // Assert - regeneration button should show with default isChildChunk=false - expect(screen.getByText(/operation\.saveAndRegenerate/i)).toBeInTheDocument() + // Assert - regeneration button should show with default isChildChunk=false + expect(screen.getByText(/operation\.saveAndRegenerate/i))!.toBeInTheDocument() }) it('should use default showRegenerationButton of true', () => { @@ -334,7 +336,8 @@ describe('ActionButtons', () => { ) // Assert - regeneration button should show with default showRegenerationButton=true - expect(screen.getByText(/operation\.saveAndRegenerate/i)).toBeInTheDocument() + // Assert - regeneration button should show with default showRegenerationButton=true + expect(screen.getByText(/operation\.saveAndRegenerate/i))!.toBeInTheDocument() }) }) @@ -373,8 +376,8 @@ describe('ActionButtons', () => { , ) - expect(screen.getByText(/operation\.cancel/i)).toBeInTheDocument() - expect(screen.getByText(/operation\.save/i)).toBeInTheDocument() + expect(screen.getByText(/operation\.cancel/i))!.toBeInTheDocument() + expect(screen.getByText(/operation\.save/i))!.toBeInTheDocument() }) }) diff --git a/web/app/components/datasets/documents/detail/completed/common/__tests__/chunk-content.spec.tsx b/web/app/components/datasets/documents/detail/completed/common/__tests__/chunk-content.spec.tsx index 115db9ad61..a92cc9d350 100644 --- a/web/app/components/datasets/documents/detail/completed/common/__tests__/chunk-content.spec.tsx +++ b/web/app/components/datasets/documents/detail/completed/common/__tests__/chunk-content.spec.tsx @@ -34,19 +34,50 @@ describe('ChunkContent', () => { it('should render without crashing', () => { const { container } = render() - expect(container.firstChild).toBeInTheDocument() + expect(container.firstChild)!.toBeInTheDocument() }) it('should render textarea in edit mode with text docForm', () => { render() const textarea = screen.getByRole('textbox') - expect(textarea).toBeInTheDocument() + expect(textarea)!.toBeInTheDocument() }) it('should render Markdown content in view mode with text docForm', () => { const { container } = render() + // Assert - In view mode, textarea should not be present, Markdown renders instead + // Assert - In view mode, textarea should not be present, Markdown renders instead + // Assert - In view mode, textarea should not be present, Markdown renders instead + // Assert - In view mode, textarea should not be present, Markdown renders instead + // Assert - In view mode, textarea should not be present, Markdown renders instead + // Assert - In view mode, textarea should not be present, Markdown renders instead + // Assert - In view mode, textarea should not be present, Markdown renders instead + // Assert - In view mode, textarea should not be present, Markdown renders instead + // Assert - In view mode, textarea should not be present, Markdown renders instead + // Assert - In view mode, textarea should not be present, Markdown renders instead + // Assert - In view mode, textarea should not be present, Markdown renders instead + // Assert - In view mode, textarea should not be present, Markdown renders instead + // Assert - In view mode, textarea should not be present, Markdown renders instead + // Assert - In view mode, textarea should not be present, Markdown renders instead + // Assert - In view mode, textarea should not be present, Markdown renders instead + // Assert - In view mode, textarea should not be present, Markdown renders instead + // Assert - In view mode, textarea should not be present, Markdown renders instead + // Assert - In view mode, textarea should not be present, Markdown renders instead + // Assert - In view mode, textarea should not be present, Markdown renders instead + // Assert - In view mode, textarea should not be present, Markdown renders instead + // Assert - In view mode, textarea should not be present, Markdown renders instead + // Assert - In view mode, textarea should not be present, Markdown renders instead + // Assert - In view mode, textarea should not be present, Markdown renders instead + // Assert - In view mode, textarea should not be present, Markdown renders instead + // Assert - In view mode, textarea should not be present, Markdown renders instead + // Assert - In view mode, textarea should not be present, Markdown renders instead + // Assert - In view mode, textarea should not be present, Markdown renders instead + // Assert - In view mode, textarea should not be present, Markdown renders instead + // Assert - In view mode, textarea should not be present, Markdown renders instead + // Assert - In view mode, textarea should not be present, Markdown renders instead + // Assert - In view mode, textarea should not be present, Markdown renders instead // Assert - In view mode, textarea should not be present, Markdown renders instead expect(container.querySelector('textarea')).not.toBeInTheDocument() }) @@ -66,8 +97,9 @@ describe('ChunkContent', () => { ) // Assert - QA mode has QUESTION and ANSWER labels - expect(screen.getByText('QUESTION')).toBeInTheDocument() - expect(screen.getByText('ANSWER')).toBeInTheDocument() + // Assert - QA mode has QUESTION and ANSWER labels + expect(screen.getByText('QUESTION'))!.toBeInTheDocument() + expect(screen.getByText('ANSWER'))!.toBeInTheDocument() }) it('should display question value in QA mode', () => { @@ -83,7 +115,7 @@ describe('ChunkContent', () => { ) const textareas = screen.getAllByRole('textbox') - expect(textareas[0]).toHaveValue('My question') + expect(textareas[0])!.toHaveValue('My question') }) it('should display answer value in QA mode', () => { @@ -99,7 +131,7 @@ describe('ChunkContent', () => { ) const textareas = screen.getAllByRole('textbox') - expect(textareas[1]).toHaveValue('My answer') + expect(textareas[1])!.toHaveValue('My answer') }) }) @@ -133,7 +165,7 @@ describe('ChunkContent', () => { ) const textareas = screen.getAllByRole('textbox') - fireEvent.change(textareas[0], { target: { value: 'New question' } }) + fireEvent.change(textareas[0]!, { target: { value: 'New question' } }) expect(mockOnQuestionChange).toHaveBeenCalledWith('New question') }) @@ -151,7 +183,7 @@ describe('ChunkContent', () => { ) const textareas = screen.getAllByRole('textbox') - fireEvent.change(textareas[1], { target: { value: 'New answer' } }) + fireEvent.change(textareas[1]!, { target: { value: 'New answer' } }) expect(mockOnAnswerChange).toHaveBeenCalledWith('New answer') }) @@ -161,6 +193,37 @@ describe('ChunkContent', () => { , ) + // Assert - In view mode, Markdown is rendered instead of textarea + // Assert - In view mode, Markdown is rendered instead of textarea + // Assert - In view mode, Markdown is rendered instead of textarea + // Assert - In view mode, Markdown is rendered instead of textarea + // Assert - In view mode, Markdown is rendered instead of textarea + // Assert - In view mode, Markdown is rendered instead of textarea + // Assert - In view mode, Markdown is rendered instead of textarea + // Assert - In view mode, Markdown is rendered instead of textarea + // Assert - In view mode, Markdown is rendered instead of textarea + // Assert - In view mode, Markdown is rendered instead of textarea + // Assert - In view mode, Markdown is rendered instead of textarea + // Assert - In view mode, Markdown is rendered instead of textarea + // Assert - In view mode, Markdown is rendered instead of textarea + // Assert - In view mode, Markdown is rendered instead of textarea + // Assert - In view mode, Markdown is rendered instead of textarea + // Assert - In view mode, Markdown is rendered instead of textarea + // Assert - In view mode, Markdown is rendered instead of textarea + // Assert - In view mode, Markdown is rendered instead of textarea + // Assert - In view mode, Markdown is rendered instead of textarea + // Assert - In view mode, Markdown is rendered instead of textarea + // Assert - In view mode, Markdown is rendered instead of textarea + // Assert - In view mode, Markdown is rendered instead of textarea + // Assert - In view mode, Markdown is rendered instead of textarea + // Assert - In view mode, Markdown is rendered instead of textarea + // Assert - In view mode, Markdown is rendered instead of textarea + // Assert - In view mode, Markdown is rendered instead of textarea + // Assert - In view mode, Markdown is rendered instead of textarea + // Assert - In view mode, Markdown is rendered instead of textarea + // Assert - In view mode, Markdown is rendered instead of textarea + // Assert - In view mode, Markdown is rendered instead of textarea + // Assert - In view mode, Markdown is rendered instead of textarea // Assert - In view mode, Markdown is rendered instead of textarea expect(container.querySelector('textarea')).not.toBeInTheDocument() }) @@ -178,7 +241,7 @@ describe('ChunkContent', () => { const textareas = screen.getAllByRole('textbox') textareas.forEach((textarea) => { - expect(textarea).toBeDisabled() + expect(textarea)!.toBeDisabled() }) }) }) @@ -188,7 +251,7 @@ describe('ChunkContent', () => { it('should handle ChunkingMode.text', () => { render() - expect(screen.getByRole('textbox')).toBeInTheDocument() + expect(screen.getByRole('textbox'))!.toBeInTheDocument() }) it('should handle ChunkingMode.qa', () => { @@ -203,8 +266,9 @@ describe('ChunkContent', () => { ) // Assert - QA mode should show both question and answer - expect(screen.getByText('QUESTION')).toBeInTheDocument() - expect(screen.getByText('ANSWER')).toBeInTheDocument() + // Assert - QA mode should show both question and answer + expect(screen.getByText('QUESTION'))!.toBeInTheDocument() + expect(screen.getByText('ANSWER'))!.toBeInTheDocument() }) it('should handle ChunkingMode.parentChild similar to text mode', () => { @@ -217,7 +281,8 @@ describe('ChunkContent', () => { ) // Assert - parentChild should render like text mode - expect(screen.getByRole('textbox')).toBeInTheDocument() + // Assert - parentChild should render like text mode + expect(screen.getByRole('textbox'))!.toBeInTheDocument() }) }) @@ -232,7 +297,7 @@ describe('ChunkContent', () => { ) const textarea = screen.getByRole('textbox') - expect(textarea).toHaveValue('') + expect(textarea)!.toHaveValue('') }) it('should handle empty answer in QA mode', () => { @@ -248,7 +313,7 @@ describe('ChunkContent', () => { ) const textareas = screen.getAllByRole('textbox') - expect(textareas[1]).toHaveValue('') + expect(textareas[1])!.toHaveValue('') }) it('should handle undefined answer in QA mode', () => { @@ -261,7 +326,8 @@ describe('ChunkContent', () => { ) // Assert - should render without crashing - expect(screen.getByText('QUESTION')).toBeInTheDocument() + // Assert - should render without crashing + expect(screen.getByText('QUESTION'))!.toBeInTheDocument() }) it('should maintain structure when rerendered', () => { @@ -274,7 +340,7 @@ describe('ChunkContent', () => { ) const textarea = screen.getByRole('textbox') - expect(textarea).toHaveValue('Updated') + expect(textarea)!.toHaveValue('Updated') }) }) }) diff --git a/web/app/components/datasets/documents/detail/completed/components/segment-list-content.tsx b/web/app/components/datasets/documents/detail/completed/components/segment-list-content.tsx index 116ba38856..d675763843 100644 --- a/web/app/components/datasets/documents/detail/completed/components/segment-list-content.tsx +++ b/web/app/components/datasets/documents/detail/completed/components/segment-list-content.tsx @@ -49,7 +49,7 @@ export const FullDocModeContent: FC = ({ > onClickCard(firstSegment)} + onClick={() => onClickCard(firstSegment!)} loading={isLoadingSegmentList} focused={{ segmentIndex: currSegmentId === firstSegment?.id, @@ -57,7 +57,7 @@ export const FullDocModeContent: FC = ({ }} /> { expect(updateSegmentInCache).toHaveBeenCalledWith('seg-1', expect.any(Function)) // Verify the updater function filters correctly - const updaterFn = updateSegmentInCache.mock.calls[0][1] + const updaterFn = updateSegmentInCache.mock.calls[0]![1] const testSegment = createMockSegment({ child_chunks: [ createMockChildChunk({ id: 'child-1' }), @@ -499,7 +499,7 @@ describe('useChildSegmentData', () => { expect(updateSegmentInCache).toHaveBeenCalledWith('seg-1', expect.any(Function)) // Verify the updater function maps correctly - const updaterFn = updateSegmentInCache.mock.calls[0][1] + const updaterFn = updateSegmentInCache.mock.calls[0]![1] const testSegment = createMockSegment({ child_chunks: [ createMockChildChunk({ id: 'child-1', content: 'old content' }), @@ -564,8 +564,8 @@ describe('useChildSegmentData', () => { total: 2, total_pages: 1, }) as ChildSegmentsResponse - expect(resultWithData.data[0].content).toBe('new content') - expect(resultWithData.data[1].content).toBe('other') + expect(resultWithData.data[0]!.content).toBe('new content') + expect(resultWithData.data[1]!.content).toBe('other') } }) diff --git a/web/app/components/datasets/documents/detail/completed/segment-list.tsx b/web/app/components/datasets/documents/detail/completed/segment-list.tsx index 9e5f0ab2fe..3a554fc0b6 100644 --- a/web/app/components/datasets/documents/detail/completed/segment-list.tsx +++ b/web/app/components/datasets/documents/detail/completed/segment-list.tsx @@ -71,7 +71,7 @@ const SegmentList = (
{ items.map((segItem) => { - const isLast = items[items.length - 1].id === segItem.id + const isLast = items[items.length - 1]!.id === segItem.id const segmentIndexFocused = currSegment?.segInfo?.id === segItem.id || (!currSegment && currChildChunk?.childChunkInfo?.segment_id === segItem.id) diff --git a/web/app/components/datasets/documents/detail/embedding/hooks/__tests__/use-embedding-status.spec.tsx b/web/app/components/datasets/documents/detail/embedding/hooks/__tests__/use-embedding-status.spec.tsx index 893484afeb..149d0a60ea 100644 --- a/web/app/components/datasets/documents/detail/embedding/hooks/__tests__/use-embedding-status.spec.tsx +++ b/web/app/components/datasets/documents/detail/embedding/hooks/__tests__/use-embedding-status.spec.tsx @@ -342,7 +342,7 @@ describe('use-embedding-status', () => { await waitFor(() => { expect(onError).toHaveBeenCalled() - expect(onError.mock.calls[0][0]).toEqual(error) + expect(onError.mock.calls[0]![0]).toEqual(error) }) }) }) diff --git a/web/app/components/datasets/documents/detail/metadata/__tests__/index.spec.tsx b/web/app/components/datasets/documents/detail/metadata/__tests__/index.spec.tsx index 2d60e5f322..5b268e554c 100644 --- a/web/app/components/datasets/documents/detail/metadata/__tests__/index.spec.tsx +++ b/web/app/components/datasets/documents/detail/metadata/__tests__/index.spec.tsx @@ -181,24 +181,55 @@ describe('Metadata', () => { it('should render without crashing', () => { const { container } = render() - expect(container.firstChild).toBeInTheDocument() + expect(container.firstChild)!.toBeInTheDocument() }) it('should render metadata title', () => { render() - expect(screen.getByText(/metadata\.title/i)).toBeInTheDocument() + expect(screen.getByText(/metadata\.title/i))!.toBeInTheDocument() }) it('should render edit button', () => { render() - expect(screen.getByText(/operation\.edit/i)).toBeInTheDocument() + expect(screen.getByText(/operation\.edit/i))!.toBeInTheDocument() }) it('should show loading state', () => { render() + // Assert - Loading component should be rendered, title should not + // Assert - Loading component should be rendered, title should not + // Assert - Loading component should be rendered, title should not + // Assert - Loading component should be rendered, title should not + // Assert - Loading component should be rendered, title should not + // Assert - Loading component should be rendered, title should not + // Assert - Loading component should be rendered, title should not + // Assert - Loading component should be rendered, title should not + // Assert - Loading component should be rendered, title should not + // Assert - Loading component should be rendered, title should not + // Assert - Loading component should be rendered, title should not + // Assert - Loading component should be rendered, title should not + // Assert - Loading component should be rendered, title should not + // Assert - Loading component should be rendered, title should not + // Assert - Loading component should be rendered, title should not + // Assert - Loading component should be rendered, title should not + // Assert - Loading component should be rendered, title should not + // Assert - Loading component should be rendered, title should not + // Assert - Loading component should be rendered, title should not + // Assert - Loading component should be rendered, title should not + // Assert - Loading component should be rendered, title should not + // Assert - Loading component should be rendered, title should not + // Assert - Loading component should be rendered, title should not + // Assert - Loading component should be rendered, title should not + // Assert - Loading component should be rendered, title should not + // Assert - Loading component should be rendered, title should not + // Assert - Loading component should be rendered, title should not + // Assert - Loading component should be rendered, title should not + // Assert - Loading component should be rendered, title should not + // Assert - Loading component should be rendered, title should not + // Assert - Loading component should be rendered, title should not // Assert - Loading component should be rendered, title should not expect(screen.queryByText(/metadata\.title/i)).not.toBeInTheDocument() }) @@ -206,7 +237,7 @@ describe('Metadata', () => { it('should display document type icon and text', () => { render() - expect(screen.getByText('Book')).toBeInTheDocument() + expect(screen.getByText('Book'))!.toBeInTheDocument() }) }) @@ -217,8 +248,8 @@ describe('Metadata', () => { fireEvent.click(screen.getByText(/operation\.edit/i)) - expect(screen.getByText(/operation\.cancel/i)).toBeInTheDocument() - expect(screen.getByText(/operation\.save/i)).toBeInTheDocument() + expect(screen.getByText(/operation\.cancel/i))!.toBeInTheDocument() + expect(screen.getByText(/operation\.save/i))!.toBeInTheDocument() }) it('should show change link in edit mode', () => { @@ -226,7 +257,7 @@ describe('Metadata', () => { fireEvent.click(screen.getByText(/operation\.edit/i)) - expect(screen.getByText(/operation\.change/i)).toBeInTheDocument() + expect(screen.getByText(/operation\.change/i))!.toBeInTheDocument() }) it('should cancel edit and restore values when cancel is clicked', () => { @@ -238,7 +269,8 @@ describe('Metadata', () => { fireEvent.click(screen.getByText(/operation\.cancel/i)) // Assert - should be back to view mode - expect(screen.getByText(/operation\.edit/i)).toBeInTheDocument() + // Assert - should be back to view mode + expect(screen.getByText(/operation\.edit/i))!.toBeInTheDocument() }) it('should save metadata when save button is clicked', async () => { @@ -299,7 +331,7 @@ describe('Metadata', () => { render() - expect(screen.getByText(/metadata\.docTypeSelectTitle/i)).toBeInTheDocument() + expect(screen.getByText(/metadata\.docTypeSelectTitle/i))!.toBeInTheDocument() }) it('should show description when no doc_type exists', () => { @@ -307,7 +339,7 @@ describe('Metadata', () => { render() - expect(screen.getByText(/metadata\.desc/i)).toBeInTheDocument() + expect(screen.getByText(/metadata\.desc/i))!.toBeInTheDocument() }) it('should show change link in edit mode when doc_type exists', () => { @@ -316,7 +348,7 @@ describe('Metadata', () => { // Enter edit mode fireEvent.click(screen.getByText(/operation\.edit/i)) - expect(screen.getByText(/operation\.change/i)).toBeInTheDocument() + expect(screen.getByText(/operation\.change/i))!.toBeInTheDocument() }) it('should show doc type change title after clicking change', () => { @@ -327,7 +359,7 @@ describe('Metadata', () => { fireEvent.click(screen.getByText(/operation\.change/i)) - expect(screen.getByText(/metadata\.docTypeChangeTitle/i)).toBeInTheDocument() + expect(screen.getByText(/metadata\.docTypeChangeTitle/i))!.toBeInTheDocument() }) }) @@ -337,14 +369,15 @@ describe('Metadata', () => { render() // Assert - expect(screen.getByText('Data Source Type')).toBeInTheDocument() + // Assert + expect(screen.getByText('Data Source Type'))!.toBeInTheDocument() }) it('should render technical parameters fields', () => { render() - expect(screen.getByText('Segment Count')).toBeInTheDocument() - expect(screen.getByText('Hit Count')).toBeInTheDocument() + expect(screen.getByText('Segment Count'))!.toBeInTheDocument() + expect(screen.getByText('Hit Count'))!.toBeInTheDocument() }) }) @@ -355,27 +388,30 @@ describe('Metadata', () => { const { container } = render() // Assert - expect(container.firstChild).toBeInTheDocument() + // Assert + expect(container.firstChild)!.toBeInTheDocument() }) it('should handle undefined docDetail gracefully', () => { const { container } = render() // Assert - expect(container.firstChild).toBeInTheDocument() + // Assert + expect(container.firstChild)!.toBeInTheDocument() }) it('should update document type display when docDetail changes', () => { const { rerender } = render() // Act - verify initial state shows Book - expect(screen.getByText('Book')).toBeInTheDocument() + // Act - verify initial state shows Book + expect(screen.getByText('Book'))!.toBeInTheDocument() // Update with new doc type const updatedDocDetail = createMockDocDetail({ doc_type: 'paper' }) rerender() - expect(screen.getByText('Paper')).toBeInTheDocument() + expect(screen.getByText('Paper'))!.toBeInTheDocument() }) }) @@ -386,7 +422,7 @@ describe('Metadata', () => { render() - expect(screen.getByText(/metadata\.firstMetaAction/i)).toBeInTheDocument() + expect(screen.getByText(/metadata\.firstMetaAction/i))!.toBeInTheDocument() }) }) }) @@ -407,19 +443,19 @@ describe('FieldInfo', () => { it('should render without crashing', () => { const { container } = render() - expect(container.firstChild).toBeInTheDocument() + expect(container.firstChild)!.toBeInTheDocument() }) it('should render label', () => { render() - expect(screen.getByText('Test Label')).toBeInTheDocument() + expect(screen.getByText('Test Label'))!.toBeInTheDocument() }) it('should render displayed value in view mode', () => { render() - expect(screen.getByText('Test Display Value')).toBeInTheDocument() + expect(screen.getByText('Test Display Value'))!.toBeInTheDocument() }) }) @@ -428,7 +464,7 @@ describe('FieldInfo', () => { it('should render input when showEdit is true and inputType is input', () => { render() - expect(screen.getByRole('textbox')).toBeInTheDocument() + expect(screen.getByRole('textbox'))!.toBeInTheDocument() }) it('should render select when showEdit is true and inputType is select', () => { @@ -443,13 +479,14 @@ describe('FieldInfo', () => { ) // Assert - SimpleSelect should be rendered - expect(screen.getByRole('button')).toBeInTheDocument() + // Assert - SimpleSelect should be rendered + expect(screen.getByRole('button'))!.toBeInTheDocument() }) it('should render textarea when showEdit is true and inputType is textarea', () => { render() - expect(screen.getByRole('textbox')).toBeInTheDocument() + expect(screen.getByRole('textbox'))!.toBeInTheDocument() }) it('should call onUpdate when input value changes', () => { @@ -476,14 +513,14 @@ describe('FieldInfo', () => { it('should render value icon when provided', () => { render(Icon} />) - expect(screen.getByTestId('value-icon')).toBeInTheDocument() + expect(screen.getByTestId('value-icon'))!.toBeInTheDocument() }) it('should use defaultValue when provided', () => { render() const input = screen.getByRole('textbox') - expect(input).toHaveAttribute('placeholder') + expect(input)!.toHaveAttribute('placeholder') }) }) }) @@ -510,13 +547,15 @@ describe('useMetadataState coverage', () => { fireEvent.click(screen.getByText(/operation\.change/i)) // Now in doc type selector mode — should show cancel button - expect(screen.getByText(/operation\.cancel/i)).toBeInTheDocument() + // Now in doc type selector mode — should show cancel button + expect(screen.getByText(/operation\.cancel/i))!.toBeInTheDocument() // Act — cancel the doc type change fireEvent.click(screen.getByText(/operation\.cancel/i)) // Assert — should be back to edit mode (cancel + save buttons visible) - expect(screen.getByText(/operation\.save/i)).toBeInTheDocument() + // Assert — should be back to edit mode (cancel + save buttons visible) + expect(screen.getByText(/operation\.save/i))!.toBeInTheDocument() }) }) @@ -530,14 +569,16 @@ describe('useMetadataState coverage', () => { fireEvent.click(screen.getByText(/operation\.change/i)) // DocTypeSelector shows save/cancel buttons - expect(screen.getByText(/metadata\.docTypeChangeTitle/i)).toBeInTheDocument() + // DocTypeSelector shows save/cancel buttons + expect(screen.getByText(/metadata\.docTypeChangeTitle/i))!.toBeInTheDocument() // Act — click save to confirm same doc type (tempDocType='book') fireEvent.click(screen.getByText(/operation\.save/i)) // Assert — should return to edit mode with metadata fields visible - expect(screen.getByText(/operation\.cancel/i)).toBeInTheDocument() - expect(screen.getByText(/operation\.save/i)).toBeInTheDocument() + // Assert — should return to edit mode with metadata fields visible + expect(screen.getByText(/operation\.cancel/i))!.toBeInTheDocument() + expect(screen.getByText(/operation\.save/i))!.toBeInTheDocument() }) }) @@ -551,17 +592,20 @@ describe('useMetadataState coverage', () => { // 'others' is normalized to '' → useEffect fires (doc_type truthy) → view mode // The rendered type uses default 'book' fallback for display - expect(screen.getByText(/operation\.edit/i)).toBeInTheDocument() + // 'others' is normalized to '' → useEffect fires (doc_type truthy) → view mode + // The rendered type uses default 'book' fallback for display + expect(screen.getByText(/operation\.edit/i))!.toBeInTheDocument() // Enter edit mode fireEvent.click(screen.getByText(/operation\.edit/i)) - expect(screen.getByText(/operation\.cancel/i)).toBeInTheDocument() + expect(screen.getByText(/operation\.cancel/i))!.toBeInTheDocument() // Act — cancel edit; internally docType is '' so cancelEdit goes to showDocTypes fireEvent.click(screen.getByText(/operation\.cancel/i)) // Assert — should show doc type selection since normalized docType was '' - expect(screen.getByText(/metadata\.docTypeSelectTitle/i)).toBeInTheDocument() + // Assert — should show doc type selection since normalized docType was '' + expect(screen.getByText(/metadata\.docTypeSelectTitle/i))!.toBeInTheDocument() }) }) @@ -576,10 +620,11 @@ describe('useMetadataState coverage', () => { // Act — find an input and change its value (Title field) const inputs = screen.getAllByRole('textbox') expect(inputs.length).toBeGreaterThan(0) - fireEvent.change(inputs[0], { target: { value: 'Updated Title' } }) + fireEvent.change(inputs[0]!, { target: { value: 'Updated Title' } }) // Assert — the input should have the new value - expect(inputs[0]).toHaveValue('Updated Title') + // Assert — the input should have the new value + expect(inputs[0])!.toHaveValue('Updated Title') }) }) @@ -629,7 +674,8 @@ describe('useMetadataState coverage', () => { ) // Assert — should render without crashing, showing Paper type - expect(screen.getByText('Paper')).toBeInTheDocument() + // Assert — should render without crashing, showing Paper type + expect(screen.getByText('Paper'))!.toBeInTheDocument() }) }) }) diff --git a/web/app/components/datasets/documents/detail/metadata/components/__tests__/metadata-field-list.spec.tsx b/web/app/components/datasets/documents/detail/metadata/components/__tests__/metadata-field-list.spec.tsx index cc5b16fc3e..9f07c06d77 100644 --- a/web/app/components/datasets/documents/detail/metadata/components/__tests__/metadata-field-list.spec.tsx +++ b/web/app/components/datasets/documents/detail/metadata/components/__tests__/metadata-field-list.spec.tsx @@ -42,9 +42,9 @@ describe('MetadataFieldList', () => { />, ) - expect(screen.getByText('Title')).toBeInTheDocument() - expect(screen.getByText('Language')).toBeInTheDocument() - expect(screen.getByText('Author')).toBeInTheDocument() + expect(screen.getByText('Title'))!.toBeInTheDocument() + expect(screen.getByText('Language'))!.toBeInTheDocument() + expect(screen.getByText('Author'))!.toBeInTheDocument() }) it('should return null when mainField is empty', () => { @@ -76,7 +76,7 @@ describe('MetadataFieldList', () => { />, ) - expect(screen.getByText('English')).toBeInTheDocument() + expect(screen.getByText('English'))!.toBeInTheDocument() }) }) @@ -109,7 +109,7 @@ describe('MetadataFieldList', () => { // Find the first textbox and type in it const inputs = screen.getAllByRole('textbox') - fireEvent.change(inputs[0], { target: { value: 'New Title' } }) + fireEvent.change(inputs[0]!, { target: { value: 'New Title' } }) expect(onUpdate).toHaveBeenCalled() }) @@ -128,8 +128,8 @@ describe('MetadataFieldList', () => { />, ) - expect(screen.getByText('Source')).toBeInTheDocument() - expect(screen.getByText('Web')).toBeInTheDocument() + expect(screen.getByText('Source'))!.toBeInTheDocument() + expect(screen.getByText('Web'))!.toBeInTheDocument() }) it('should render custom render function output for fields with render', () => { @@ -143,7 +143,7 @@ describe('MetadataFieldList', () => { />, ) - expect(screen.getByText('15 / 5')).toBeInTheDocument() + expect(screen.getByText('15 / 5'))!.toBeInTheDocument() }) }) }) diff --git a/web/app/components/datasets/documents/detail/metadata/components/metadata-field-list.tsx b/web/app/components/datasets/documents/detail/metadata/components/metadata-field-list.tsx index 9f452279ed..03af49359d 100644 --- a/web/app/components/datasets/documents/detail/metadata/components/metadata-field-list.tsx +++ b/web/app/components/datasets/documents/detail/metadata/components/metadata-field-list.tsx @@ -7,7 +7,7 @@ import { useBookCategories, useBusinessDocCategories, useLanguages, useMetadataM import FieldInfo from './field-info' const map2Options = (map: Record) => { - return Object.keys(map).map(key => ({ value: key, name: map[key] })) + return Object.keys(map).map(key => ({ value: key, name: map[key]! })) } function useCategoryMapResolver(mainField: metadataType | '') { @@ -72,7 +72,7 @@ const MetadataFieldList: FC = ({ {Object.keys(fieldMap).map(field => ( { }) await waitFor(() => expect(onUrlUpdate).toHaveBeenCalled()) - const update = onUrlUpdate.mock.calls[onUrlUpdate.mock.calls.length - 1][0] + const update = onUrlUpdate.mock.calls[onUrlUpdate.mock.calls.length - 1]![0] expect(update.searchParams.get('page')).toBe('2') expect(update.options.history).toBe('push') }) @@ -177,7 +177,7 @@ describe('useDocumentListQueryState', () => { }) await waitFor(() => expect(onUrlUpdate).toHaveBeenCalled()) - const update = onUrlUpdate.mock.calls[onUrlUpdate.mock.calls.length - 1][0] + const update = onUrlUpdate.mock.calls[onUrlUpdate.mock.calls.length - 1]![0] expect(update.searchParams.get('status')).toBe('error') }) @@ -189,7 +189,7 @@ describe('useDocumentListQueryState', () => { }) await waitFor(() => expect(onUrlUpdate).toHaveBeenCalled()) - const update = onUrlUpdate.mock.calls[onUrlUpdate.mock.calls.length - 1][0] + const update = onUrlUpdate.mock.calls[onUrlUpdate.mock.calls.length - 1]![0] expect(update.searchParams.has('status')).toBe(false) }) @@ -201,7 +201,7 @@ describe('useDocumentListQueryState', () => { }) await waitFor(() => expect(onUrlUpdate).toHaveBeenCalled()) - const update = onUrlUpdate.mock.calls[onUrlUpdate.mock.calls.length - 1][0] + const update = onUrlUpdate.mock.calls[onUrlUpdate.mock.calls.length - 1]![0] expect(update.searchParams.get('sort')).toBe('hit_count') }) @@ -213,7 +213,7 @@ describe('useDocumentListQueryState', () => { }) await waitFor(() => expect(onUrlUpdate).toHaveBeenCalled()) - const update = onUrlUpdate.mock.calls[onUrlUpdate.mock.calls.length - 1][0] + const update = onUrlUpdate.mock.calls[onUrlUpdate.mock.calls.length - 1]![0] expect(update.searchParams.has('sort')).toBe(false) }) @@ -225,7 +225,7 @@ describe('useDocumentListQueryState', () => { }) await waitFor(() => expect(onUrlUpdate).toHaveBeenCalled()) - const update = onUrlUpdate.mock.calls[onUrlUpdate.mock.calls.length - 1][0] + const update = onUrlUpdate.mock.calls[onUrlUpdate.mock.calls.length - 1]![0] expect(update.searchParams.get('keyword')).toBe('test query') expect(update.options.history).toBe('replace') }) @@ -238,7 +238,7 @@ describe('useDocumentListQueryState', () => { }) await waitFor(() => expect(onUrlUpdate).toHaveBeenCalled()) - const update = onUrlUpdate.mock.calls[onUrlUpdate.mock.calls.length - 1][0] + const update = onUrlUpdate.mock.calls[onUrlUpdate.mock.calls.length - 1]![0] expect(update.searchParams.get('keyword')).toBe('hello') expect(update.searchParams.has('page')).toBe(false) expect(update.options.history).toBe('replace') @@ -252,7 +252,7 @@ describe('useDocumentListQueryState', () => { }) await waitFor(() => expect(onUrlUpdate).toHaveBeenCalled()) - const update = onUrlUpdate.mock.calls[onUrlUpdate.mock.calls.length - 1][0] + const update = onUrlUpdate.mock.calls[onUrlUpdate.mock.calls.length - 1]![0] expect(update.searchParams.has('keyword')).toBe(false) expect(update.options.history).toBe('replace') }) @@ -265,7 +265,7 @@ describe('useDocumentListQueryState', () => { }) await waitFor(() => expect(onUrlUpdate).toHaveBeenCalled()) - const update = onUrlUpdate.mock.calls[onUrlUpdate.mock.calls.length - 1][0] + const update = onUrlUpdate.mock.calls[onUrlUpdate.mock.calls.length - 1]![0] expect(update.searchParams.has('keyword')).toBe(false) expect(result.current.query.keyword).toBe('') }) @@ -278,7 +278,7 @@ describe('useDocumentListQueryState', () => { }) await waitFor(() => expect(onUrlUpdate).toHaveBeenCalled()) - const update = onUrlUpdate.mock.calls[onUrlUpdate.mock.calls.length - 1][0] + const update = onUrlUpdate.mock.calls[onUrlUpdate.mock.calls.length - 1]![0] expect(update.searchParams.get('keyword')).toBe('%2F') expect(result.current.query.keyword).toBe('%2F') }) @@ -302,7 +302,7 @@ describe('useDocumentListQueryState', () => { }) await waitFor(() => expect(onUrlUpdate).toHaveBeenCalled()) - const update = onUrlUpdate.mock.calls[onUrlUpdate.mock.calls.length - 1][0] + const update = onUrlUpdate.mock.calls[onUrlUpdate.mock.calls.length - 1]![0] expect(update.searchParams.has('status')).toBe(false) }) @@ -314,7 +314,7 @@ describe('useDocumentListQueryState', () => { }) await waitFor(() => expect(onUrlUpdate).toHaveBeenCalled()) - const update = onUrlUpdate.mock.calls[onUrlUpdate.mock.calls.length - 1][0] + const update = onUrlUpdate.mock.calls[onUrlUpdate.mock.calls.length - 1]![0] expect(update.searchParams.has('sort')).toBe(false) }) @@ -326,7 +326,7 @@ describe('useDocumentListQueryState', () => { }) await waitFor(() => expect(onUrlUpdate).toHaveBeenCalled()) - const update = onUrlUpdate.mock.calls[onUrlUpdate.mock.calls.length - 1][0] + const update = onUrlUpdate.mock.calls[onUrlUpdate.mock.calls.length - 1]![0] expect(update.searchParams.has('page')).toBe(false) }) @@ -338,7 +338,7 @@ describe('useDocumentListQueryState', () => { }) await waitFor(() => expect(onUrlUpdate).toHaveBeenCalled()) - const update = onUrlUpdate.mock.calls[onUrlUpdate.mock.calls.length - 1][0] + const update = onUrlUpdate.mock.calls[onUrlUpdate.mock.calls.length - 1]![0] expect(update.searchParams.get('page')).toBe('2') }) @@ -350,7 +350,7 @@ describe('useDocumentListQueryState', () => { }) await waitFor(() => expect(onUrlUpdate).toHaveBeenCalled()) - const update = onUrlUpdate.mock.calls[onUrlUpdate.mock.calls.length - 1][0] + const update = onUrlUpdate.mock.calls[onUrlUpdate.mock.calls.length - 1]![0] expect(update.searchParams.get('limit')).toBe('25') }) @@ -362,7 +362,7 @@ describe('useDocumentListQueryState', () => { }) await waitFor(() => expect(onUrlUpdate).toHaveBeenCalled()) - const update = onUrlUpdate.mock.calls[onUrlUpdate.mock.calls.length - 1][0] + const update = onUrlUpdate.mock.calls[onUrlUpdate.mock.calls.length - 1]![0] expect(update.searchParams.has('page')).toBe(false) expect(result.current.query.page).toBe(1) }) @@ -375,7 +375,7 @@ describe('useDocumentListQueryState', () => { }) await waitFor(() => expect(onUrlUpdate).toHaveBeenCalled()) - const update = onUrlUpdate.mock.calls[onUrlUpdate.mock.calls.length - 1][0] + const update = onUrlUpdate.mock.calls[onUrlUpdate.mock.calls.length - 1]![0] expect(update.searchParams.has('limit')).toBe(false) expect(result.current.query.limit).toBe(10) }) @@ -406,7 +406,7 @@ describe('useDocumentListQueryState', () => { }) await waitFor(() => expect(onUrlUpdate).toHaveBeenCalled()) - const update = onUrlUpdate.mock.calls[onUrlUpdate.mock.calls.length - 1][0] + const update = onUrlUpdate.mock.calls[onUrlUpdate.mock.calls.length - 1]![0] expect(update.searchParams.has('page')).toBe(false) expect(update.searchParams.has('status')).toBe(false) }) diff --git a/web/app/components/datasets/external-api/external-api-modal/__tests__/index.spec.tsx b/web/app/components/datasets/external-api/external-api-modal/__tests__/index.spec.tsx index 73579db413..e24712887a 100644 --- a/web/app/components/datasets/external-api/external-api-modal/__tests__/index.spec.tsx +++ b/web/app/components/datasets/external-api/external-api-modal/__tests__/index.spec.tsx @@ -46,42 +46,42 @@ describe('AddExternalAPIModal', () => { describe('Rendering', () => { it('should render without crashing', () => { render() - expect(screen.getByText('dataset.createExternalAPI')).toBeInTheDocument() + expect(screen.getByText('dataset.createExternalAPI'))!.toBeInTheDocument() }) it('should render create title when not in edit mode', () => { render() - expect(screen.getByText('dataset.createExternalAPI')).toBeInTheDocument() + expect(screen.getByText('dataset.createExternalAPI'))!.toBeInTheDocument() }) it('should render edit title when in edit mode', () => { render() - expect(screen.getByText('dataset.editExternalAPIFormTitle')).toBeInTheDocument() + expect(screen.getByText('dataset.editExternalAPIFormTitle'))!.toBeInTheDocument() }) it('should render form fields', () => { render() - expect(screen.getByLabelText(/name/i)).toBeInTheDocument() - expect(screen.getByLabelText(/api endpoint/i)).toBeInTheDocument() - expect(screen.getByLabelText(/api key/i)).toBeInTheDocument() + expect(screen.getByLabelText(/name/i))!.toBeInTheDocument() + expect(screen.getByLabelText(/api endpoint/i))!.toBeInTheDocument() + expect(screen.getByLabelText(/api key/i))!.toBeInTheDocument() }) it('should render cancel and save buttons', () => { render() - expect(screen.getByText('dataset.externalAPIForm.cancel')).toBeInTheDocument() - expect(screen.getByText('dataset.externalAPIForm.save')).toBeInTheDocument() + expect(screen.getByText('dataset.externalAPIForm.cancel'))!.toBeInTheDocument() + expect(screen.getByText('dataset.externalAPIForm.save'))!.toBeInTheDocument() }) it('should render encryption notice', () => { render() - expect(screen.getByText('PKCS1_OAEP')).toBeInTheDocument() + expect(screen.getByText('PKCS1_OAEP'))!.toBeInTheDocument() }) it('should render close button', () => { render() // Close button is rendered in a portal const closeButton = document.body.querySelector('.action-btn') - expect(closeButton).toBeInTheDocument() + expect(closeButton)!.toBeInTheDocument() }) }) @@ -99,7 +99,7 @@ describe('AddExternalAPIModal', () => { datasetBindings={datasetBindings} />, ) - expect(screen.getByText('dataset.editExternalAPIFormWarning.front')).toBeInTheDocument() + expect(screen.getByText('dataset.editExternalAPIFormWarning.front'))!.toBeInTheDocument() // Verify the count is displayed in the warning section const warningElement = screen.getByText('dataset.editExternalAPIFormWarning.front').parentElement expect(warningElement?.textContent).toContain('2') @@ -124,22 +124,22 @@ describe('AddExternalAPIModal', () => { const nameInput = screen.getByLabelText(/name/i) fireEvent.change(nameInput, { target: { value: 'New API Name' } }) - expect(nameInput).toHaveValue('New API Name') + expect(nameInput)!.toHaveValue('New API Name') }) it('should initialize form with data in edit mode', () => { render() - expect(screen.getByLabelText(/name/i)).toHaveValue('Test API') - expect(screen.getByLabelText(/api endpoint/i)).toHaveValue('https://api.example.com') - expect(screen.getByLabelText(/api key/i)).toHaveValue('test-key-12345') + expect(screen.getByLabelText(/name/i))!.toHaveValue('Test API') + expect(screen.getByLabelText(/api endpoint/i))!.toHaveValue('https://api.example.com') + expect(screen.getByLabelText(/api key/i))!.toHaveValue('test-key-12345') }) it('should disable save button when form has empty inputs', () => { render() const saveButton = screen.getByText('dataset.externalAPIForm.save').closest('button') - expect(saveButton).toBeDisabled() + expect(saveButton)!.toBeDisabled() }) it('should enable save button when all fields are filled', () => { @@ -302,7 +302,7 @@ describe('AddExternalAPIModal', () => { fireEvent.click(saveButton) await waitFor(() => { - expect(screen.getByRole('button', { name: /confirm/i })).toBeInTheDocument() + expect(screen.getByRole('button', { name: /confirm/i }))!.toBeInTheDocument() }) }) @@ -334,7 +334,7 @@ describe('AddExternalAPIModal', () => { fireEvent.click(saveButton) await waitFor(() => { - expect(screen.getByRole('button', { name: /confirm/i })).toBeInTheDocument() + expect(screen.getByRole('button', { name: /confirm/i }))!.toBeInTheDocument() }) const confirmButton = screen.getByRole('button', { name: /confirm/i }) @@ -363,13 +363,13 @@ describe('AddExternalAPIModal', () => { fireEvent.click(saveButton) await waitFor(() => { - expect(screen.getByRole('button', { name: /confirm/i })).toBeInTheDocument() + expect(screen.getByRole('button', { name: /confirm/i }))!.toBeInTheDocument() }) // There are multiple cancel buttons, find the one in the confirm dialog const cancelButtons = screen.getAllByRole('button', { name: /cancel/i }) const confirmDialogCancelButton = cancelButtons[cancelButtons.length - 1] - fireEvent.click(confirmDialogCancelButton) + fireEvent.click(confirmDialogCancelButton!) await waitFor(() => { // Confirm button should be gone after canceling @@ -404,7 +404,7 @@ describe('AddExternalAPIModal', () => { describe('Edge Cases', () => { it('should handle undefined data in edit mode', () => { render() - expect(screen.getByLabelText(/name/i)).toHaveValue('') + expect(screen.getByLabelText(/name/i))!.toHaveValue('') }) it('should handle null datasetBindings', () => { @@ -422,8 +422,8 @@ describe('AddExternalAPIModal', () => { it('should render documentation link in encryption notice', () => { render() const link = screen.getByRole('link', { name: 'PKCS1_OAEP' }) - expect(link).toHaveAttribute('href', 'https://pycryptodome.readthedocs.io/en/latest/src/cipher/oaep.html') - expect(link).toHaveAttribute('target', '_blank') + expect(link)!.toHaveAttribute('href', 'https://pycryptodome.readthedocs.io/en/latest/src/cipher/oaep.html') + expect(link)!.toHaveAttribute('target', '_blank') }) }) }) diff --git a/web/app/components/datasets/external-api/external-api-panel/__tests__/index.spec.tsx b/web/app/components/datasets/external-api/external-api-panel/__tests__/index.spec.tsx index eb7c0558ac..964b8fe344 100644 --- a/web/app/components/datasets/external-api/external-api-panel/__tests__/index.spec.tsx +++ b/web/app/components/datasets/external-api/external-api-panel/__tests__/index.spec.tsx @@ -50,31 +50,31 @@ describe('ExternalAPIPanel', () => { describe('Rendering', () => { it('should render without crashing', () => { render() - expect(screen.getByText('dataset.externalAPIPanelTitle')).toBeInTheDocument() + expect(screen.getByText('dataset.externalAPIPanelTitle'))!.toBeInTheDocument() }) it('should render panel title and description', () => { render() - expect(screen.getByText('dataset.externalAPIPanelTitle')).toBeInTheDocument() - expect(screen.getByText('dataset.externalAPIPanelDescription')).toBeInTheDocument() + expect(screen.getByText('dataset.externalAPIPanelTitle'))!.toBeInTheDocument() + expect(screen.getByText('dataset.externalAPIPanelDescription'))!.toBeInTheDocument() }) it('should render documentation link', () => { render() const docLink = screen.getByText('dataset.externalAPIPanelDocumentation') - expect(docLink).toBeInTheDocument() - expect(docLink.closest('a')).toHaveAttribute('href', 'https://docs.example.com/use-dify/knowledge/external-knowledge-api') + expect(docLink)!.toBeInTheDocument() + expect(docLink.closest('a'))!.toHaveAttribute('href', 'https://docs.example.com/use-dify/knowledge/external-knowledge-api') }) it('should render create button', () => { render() - expect(screen.getByText('dataset.createExternalAPI')).toBeInTheDocument() + expect(screen.getByText('dataset.createExternalAPI'))!.toBeInTheDocument() }) it('should render close button', () => { const { container } = render() const closeButton = container.querySelector('[class*="action-button"]') || screen.getAllByRole('button')[0] - expect(closeButton).toBeInTheDocument() + expect(closeButton)!.toBeInTheDocument() }) }) @@ -121,10 +121,10 @@ describe('ExternalAPIPanel', () => { }, ] render() - expect(screen.getByTestId('api-card-api-1')).toBeInTheDocument() - expect(screen.getByTestId('api-card-api-2')).toBeInTheDocument() - expect(screen.getByText('Test API 1')).toBeInTheDocument() - expect(screen.getByText('Test API 2')).toBeInTheDocument() + expect(screen.getByTestId('api-card-api-1'))!.toBeInTheDocument() + expect(screen.getByTestId('api-card-api-2'))!.toBeInTheDocument() + expect(screen.getByText('Test API 1'))!.toBeInTheDocument() + expect(screen.getByText('Test API 2'))!.toBeInTheDocument() }) }) @@ -136,7 +136,7 @@ describe('ExternalAPIPanel', () => { const buttons = screen.getAllByRole('button') const closeButton = buttons.find(btn => btn.querySelector('svg[class*="ri-close"]')) || buttons[0] - fireEvent.click(closeButton) + fireEvent.click(closeButton!) expect(onClose).toHaveBeenCalledTimes(1) }) @@ -162,7 +162,7 @@ describe('ExternalAPIPanel', () => { const createButton = screen.getByText('dataset.createExternalAPI').closest('button')! fireEvent.click(createButton) - const callArgs = mockSetShowExternalKnowledgeAPIModal.mock.calls[0][0] + const callArgs = mockSetShowExternalKnowledgeAPIModal.mock.calls[0]![0] callArgs.onSaveCallback() expect(mockMutateExternalKnowledgeApis).toHaveBeenCalled() @@ -173,7 +173,7 @@ describe('ExternalAPIPanel', () => { const createButton = screen.getByText('dataset.createExternalAPI').closest('button')! fireEvent.click(createButton) - const callArgs = mockSetShowExternalKnowledgeAPIModal.mock.calls[0][0] + const callArgs = mockSetShowExternalKnowledgeAPIModal.mock.calls[0]![0] callArgs.onCancelCallback() expect(mockMutateExternalKnowledgeApis).toHaveBeenCalled() @@ -195,13 +195,13 @@ describe('ExternalAPIPanel', () => { }, ] render() - expect(screen.getByTestId('api-card-single-api')).toBeInTheDocument() + expect(screen.getByTestId('api-card-single-api'))!.toBeInTheDocument() }) it('should render documentation link with correct target', () => { render() const docLink = screen.getByText('dataset.externalAPIPanelDocumentation').closest('a') - expect(docLink).toHaveAttribute('target', '_blank') + expect(docLink)!.toHaveAttribute('target', '_blank') }) }) }) diff --git a/web/app/components/datasets/external-api/external-knowledge-api-card/__tests__/index.spec.tsx b/web/app/components/datasets/external-api/external-knowledge-api-card/__tests__/index.spec.tsx index bc1c923876..6dfd8ff13d 100644 --- a/web/app/components/datasets/external-api/external-knowledge-api-card/__tests__/index.spec.tsx +++ b/web/app/components/datasets/external-api/external-knowledge-api-card/__tests__/index.spec.tsx @@ -56,17 +56,17 @@ describe('ExternalKnowledgeAPICard', () => { describe('Rendering', () => { it('should render without crashing', () => { render() - expect(screen.getByText('Test External API')).toBeInTheDocument() + expect(screen.getByText('Test External API'))!.toBeInTheDocument() }) it('should render API name', () => { render() - expect(screen.getByText('Test External API')).toBeInTheDocument() + expect(screen.getByText('Test External API'))!.toBeInTheDocument() }) it('should render API endpoint', () => { render() - expect(screen.getByText('https://api.example.com/knowledge')).toBeInTheDocument() + expect(screen.getByText('https://api.example.com/knowledge'))!.toBeInTheDocument() }) it('should render edit and delete buttons', () => { @@ -78,7 +78,7 @@ describe('ExternalKnowledgeAPICard', () => { it('should render API connection icon', () => { const { container } = render() const icon = container.querySelector('svg') - expect(icon).toBeInTheDocument() + expect(icon)!.toBeInTheDocument() }) }) @@ -103,7 +103,7 @@ describe('ExternalKnowledgeAPICard', () => { const buttons = container.querySelectorAll('button') const editButton = buttons[0] - fireEvent.click(editButton) + fireEvent.click(editButton!) await waitFor(() => { expect(fetchExternalAPI).toHaveBeenCalledWith({ apiTemplateId: 'api-123' }) @@ -131,7 +131,7 @@ describe('ExternalKnowledgeAPICard', () => { const buttons = container.querySelectorAll('button') const editButton = buttons[0] - fireEvent.click(editButton) + fireEvent.click(editButton!) await waitFor(() => { expect(consoleSpy).toHaveBeenCalledWith( @@ -162,14 +162,14 @@ describe('ExternalKnowledgeAPICard', () => { const { container } = render() const editButton = container.querySelectorAll('button')[0] - fireEvent.click(editButton) + fireEvent.click(editButton!) await waitFor(() => { expect(mockSetShowExternalKnowledgeAPIModal).toHaveBeenCalled() }) // Simulate save callback - const modalCall = mockSetShowExternalKnowledgeAPIModal.mock.calls[0][0] + const modalCall = mockSetShowExternalKnowledgeAPIModal.mock.calls[0]![0] modalCall.onSaveCallback() expect(mockMutateExternalKnowledgeApis).toHaveBeenCalled() @@ -194,14 +194,14 @@ describe('ExternalKnowledgeAPICard', () => { const { container } = render() const editButton = container.querySelectorAll('button')[0] - fireEvent.click(editButton) + fireEvent.click(editButton!) await waitFor(() => { expect(mockSetShowExternalKnowledgeAPIModal).toHaveBeenCalled() }) // Simulate cancel callback - const modalCall = mockSetShowExternalKnowledgeAPIModal.mock.calls[0][0] + const modalCall = mockSetShowExternalKnowledgeAPIModal.mock.calls[0]![0] modalCall.onCancelCallback() expect(mockMutateExternalKnowledgeApis).toHaveBeenCalled() @@ -216,7 +216,7 @@ describe('ExternalKnowledgeAPICard', () => { const buttons = container.querySelectorAll('button') const deleteButton = buttons[1] - fireEvent.click(deleteButton) + fireEvent.click(deleteButton!) await waitFor(() => { expect(checkUsageExternalAPI).toHaveBeenCalledWith({ apiTemplateId: 'api-123' }) @@ -224,7 +224,7 @@ describe('ExternalKnowledgeAPICard', () => { // Confirm dialog should be shown await waitFor(() => { - expect(screen.getByRole('button', { name: /cancel/i })).toBeInTheDocument() + expect(screen.getByRole('button', { name: /cancel/i }))!.toBeInTheDocument() }) }) @@ -234,10 +234,10 @@ describe('ExternalKnowledgeAPICard', () => { const { container } = render() const deleteButton = container.querySelectorAll('button')[1] - fireEvent.click(deleteButton) + fireEvent.click(deleteButton!) await waitFor(() => { - expect(screen.getByText(/3/)).toBeInTheDocument() + expect(screen.getByText(/3/))!.toBeInTheDocument() }) }) @@ -248,10 +248,10 @@ describe('ExternalKnowledgeAPICard', () => { const { container } = render() const deleteButton = container.querySelectorAll('button')[1] - fireEvent.click(deleteButton) + fireEvent.click(deleteButton!) await waitFor(() => { - expect(screen.getByRole('button', { name: /confirm/i })).toBeInTheDocument() + expect(screen.getByRole('button', { name: /confirm/i }))!.toBeInTheDocument() }) const confirmButton = screen.getByRole('button', { name: /confirm/i }) @@ -269,10 +269,10 @@ describe('ExternalKnowledgeAPICard', () => { const { container } = render() const deleteButton = container.querySelectorAll('button')[1] - fireEvent.click(deleteButton) + fireEvent.click(deleteButton!) await waitFor(() => { - expect(screen.getByRole('button', { name: /cancel/i })).toBeInTheDocument() + expect(screen.getByRole('button', { name: /cancel/i }))!.toBeInTheDocument() }) const cancelButton = screen.getByRole('button', { name: /cancel/i }) @@ -291,10 +291,10 @@ describe('ExternalKnowledgeAPICard', () => { const { container } = render() const deleteButton = container.querySelectorAll('button')[1] - fireEvent.click(deleteButton) + fireEvent.click(deleteButton!) await waitFor(() => { - expect(screen.getByRole('button', { name: /confirm/i })).toBeInTheDocument() + expect(screen.getByRole('button', { name: /confirm/i }))!.toBeInTheDocument() }) const confirmButton = screen.getByRole('button', { name: /confirm/i }) @@ -317,7 +317,7 @@ describe('ExternalKnowledgeAPICard', () => { const { container } = render() const deleteButton = container.querySelectorAll('button')[1] - fireEvent.click(deleteButton) + fireEvent.click(deleteButton!) await waitFor(() => { expect(consoleSpy).toHaveBeenCalledWith( @@ -336,11 +336,11 @@ describe('ExternalKnowledgeAPICard', () => { const deleteButton = container.querySelectorAll('button')[1] const cardContainer = container.querySelector('[class*="shadows-shadow"]') - fireEvent.mouseEnter(deleteButton) - expect(cardContainer).toHaveClass('border-state-destructive-border') - expect(cardContainer).toHaveClass('bg-state-destructive-hover') + fireEvent.mouseEnter(deleteButton!) + expect(cardContainer)!.toHaveClass('border-state-destructive-border') + expect(cardContainer)!.toHaveClass('bg-state-destructive-hover') - fireEvent.mouseLeave(deleteButton) + fireEvent.mouseLeave(deleteButton!) expect(cardContainer).not.toHaveClass('border-state-destructive-border') }) }) @@ -352,7 +352,7 @@ describe('ExternalKnowledgeAPICard', () => { settings: { endpoint: '', api_key: 'key' }, } render() - expect(screen.getByText('Test External API')).toBeInTheDocument() + expect(screen.getByText('Test External API'))!.toBeInTheDocument() }) it('should handle delete response with unsuccessful result', async () => { @@ -363,10 +363,10 @@ describe('ExternalKnowledgeAPICard', () => { const { container } = render() const deleteButton = container.querySelectorAll('button')[1] - fireEvent.click(deleteButton) + fireEvent.click(deleteButton!) await waitFor(() => { - expect(screen.getByRole('button', { name: /confirm/i })).toBeInTheDocument() + expect(screen.getByRole('button', { name: /confirm/i }))!.toBeInTheDocument() }) const confirmButton = screen.getByRole('button', { name: /confirm/i }) diff --git a/web/app/components/datasets/external-knowledge-base/create/ExternalApiSelection.tsx b/web/app/components/datasets/external-knowledge-base/create/ExternalApiSelection.tsx index 387bf9843c..fea71a7e78 100644 --- a/web/app/components/datasets/external-knowledge-base/create/ExternalApiSelection.tsx +++ b/web/app/components/datasets/external-knowledge-base/create/ExternalApiSelection.tsx @@ -33,7 +33,7 @@ const ExternalApiSelection: React.FC = ({ external_kn useEffect(() => { if (apiItems.length > 0) { - const newSelectedId = external_knowledge_api_id || apiItems[0].value + const newSelectedId = external_knowledge_api_id || apiItems[0]!.value setSelectedApiId(newSelectedId) if (newSelectedId !== external_knowledge_api_id) onChange({ external_knowledge_api_id: newSelectedId, external_knowledge_id }) @@ -56,7 +56,7 @@ const ExternalApiSelection: React.FC = ({ external_kn useEffect(() => { if (!external_knowledge_api_id && apiItems.length > 0) - onChange({ external_knowledge_api_id: apiItems[0].value, external_knowledge_id }) + onChange({ external_knowledge_api_id: apiItems[0]!.value, external_knowledge_id }) }, []) return ( diff --git a/web/app/components/datasets/external-knowledge-base/create/__tests__/index.spec.tsx b/web/app/components/datasets/external-knowledge-base/create/__tests__/index.spec.tsx index a3282e441c..f132def602 100644 --- a/web/app/components/datasets/external-knowledge-base/create/__tests__/index.spec.tsx +++ b/web/app/components/datasets/external-knowledge-base/create/__tests__/index.spec.tsx @@ -90,64 +90,68 @@ describe('ExternalKnowledgeBaseCreate', () => { it('should render without crashing', () => { renderComponent() - expect(screen.getByText('dataset.connectDataset')).toBeInTheDocument() + expect(screen.getByText('dataset.connectDataset'))!.toBeInTheDocument() }) it('should render KnowledgeBaseInfo component with correct labels', () => { renderComponent() // KnowledgeBaseInfo renders these labels - expect(screen.getByText('dataset.externalKnowledgeName')).toBeInTheDocument() - expect(screen.getByText('dataset.externalKnowledgeDescription')).toBeInTheDocument() + // KnowledgeBaseInfo renders these labels + expect(screen.getByText('dataset.externalKnowledgeName'))!.toBeInTheDocument() + expect(screen.getByText('dataset.externalKnowledgeDescription'))!.toBeInTheDocument() }) it('should render ExternalApiSelection component', () => { renderComponent() // ExternalApiSelection renders this label - expect(screen.getByText('dataset.externalAPIPanelTitle')).toBeInTheDocument() - expect(screen.getByText('dataset.externalKnowledgeId')).toBeInTheDocument() + // ExternalApiSelection renders this label + expect(screen.getByText('dataset.externalAPIPanelTitle'))!.toBeInTheDocument() + expect(screen.getByText('dataset.externalKnowledgeId'))!.toBeInTheDocument() }) it('should render RetrievalSettings component', () => { renderComponent() // RetrievalSettings renders this label - expect(screen.getByText('dataset.retrievalSettings')).toBeInTheDocument() + // RetrievalSettings renders this label + expect(screen.getByText('dataset.retrievalSettings'))!.toBeInTheDocument() }) it('should render InfoPanel component', () => { renderComponent() // InfoPanel renders these texts - expect(screen.getByText('dataset.connectDatasetIntro.title')).toBeInTheDocument() - expect(screen.getByText('dataset.connectDatasetIntro.learnMore')).toBeInTheDocument() + // InfoPanel renders these texts + expect(screen.getByText('dataset.connectDatasetIntro.title'))!.toBeInTheDocument() + expect(screen.getByText('dataset.connectDatasetIntro.learnMore'))!.toBeInTheDocument() }) it('should render helper text with translation keys', () => { renderComponent() - expect(screen.getByText('dataset.connectHelper.helper1')).toBeInTheDocument() - expect(screen.getByText('dataset.connectHelper.helper2')).toBeInTheDocument() - expect(screen.getByText('dataset.connectHelper.helper3')).toBeInTheDocument() - expect(screen.getByText('dataset.connectHelper.helper4')).toBeInTheDocument() - expect(screen.getByText('dataset.connectHelper.helper5')).toBeInTheDocument() + expect(screen.getByText('dataset.connectHelper.helper1'))!.toBeInTheDocument() + expect(screen.getByText('dataset.connectHelper.helper2'))!.toBeInTheDocument() + expect(screen.getByText('dataset.connectHelper.helper3'))!.toBeInTheDocument() + expect(screen.getByText('dataset.connectHelper.helper4'))!.toBeInTheDocument() + expect(screen.getByText('dataset.connectHelper.helper5'))!.toBeInTheDocument() }) it('should render cancel and connect buttons', () => { renderComponent() - expect(screen.getByText('dataset.externalKnowledgeForm.cancel')).toBeInTheDocument() - expect(screen.getByText('dataset.externalKnowledgeForm.connect')).toBeInTheDocument() + expect(screen.getByText('dataset.externalKnowledgeForm.cancel'))!.toBeInTheDocument() + expect(screen.getByText('dataset.externalKnowledgeForm.connect'))!.toBeInTheDocument() }) it('should render documentation link with correct href', () => { renderComponent() const docLink = screen.getByText('dataset.connectHelper.helper4') - expect(docLink).toHaveAttribute('href', 'https://docs.dify.ai/en/use-dify/knowledge/connect-external-knowledge-base') - expect(docLink).toHaveAttribute('target', '_blank') - expect(docLink).toHaveAttribute('rel', 'noopener noreferrer') + expect(docLink)!.toHaveAttribute('href', 'https://docs.dify.ai/en/use-dify/knowledge/connect-external-knowledge-base') + expect(docLink)!.toHaveAttribute('target', '_blank') + expect(docLink)!.toHaveAttribute('rel', 'noopener noreferrer') }) }) @@ -157,7 +161,7 @@ describe('ExternalKnowledgeBaseCreate', () => { renderComponent({ loading: true }) const connectButton = screen.getByText('dataset.externalKnowledgeForm.connect').closest('button') - expect(connectButton).toBeInTheDocument() + expect(connectButton)!.toBeInTheDocument() }) it('should call onConnect with form data when connect button is clicked', async () => { @@ -198,7 +202,7 @@ describe('ExternalKnowledgeBaseCreate', () => { renderComponent({ onConnect }) const connectButton = screen.getByText('dataset.externalKnowledgeForm.connect').closest('button') - expect(connectButton).toBeDisabled() + expect(connectButton)!.toBeDisabled() await user.click(connectButton!) expect(onConnect).not.toHaveBeenCalled() @@ -280,7 +284,7 @@ describe('ExternalKnowledgeBaseCreate', () => { fireEvent.change(knowledgeIdInput, { target: { value: 'knowledge-456' } }) const connectButton = screen.getByText('dataset.externalKnowledgeForm.connect').closest('button') - expect(connectButton).toBeDisabled() + expect(connectButton)!.toBeDisabled() }) it('should disable connect button when name is only whitespace', async () => { @@ -293,7 +297,7 @@ describe('ExternalKnowledgeBaseCreate', () => { fireEvent.change(knowledgeIdInput, { target: { value: 'knowledge-456' } }) const connectButton = screen.getByText('dataset.externalKnowledgeForm.connect').closest('button') - expect(connectButton).toBeDisabled() + expect(connectButton)!.toBeDisabled() }) it('should disable connect button when external_knowledge_id is empty', () => { @@ -303,7 +307,7 @@ describe('ExternalKnowledgeBaseCreate', () => { fireEvent.change(nameInput, { target: { value: 'Test Name' } }) const connectButton = screen.getByText('dataset.externalKnowledgeForm.connect').closest('button') - expect(connectButton).toBeDisabled() + expect(connectButton)!.toBeDisabled() }) it('should enable connect button when all required fields are filled', async () => { @@ -429,7 +433,8 @@ describe('ExternalKnowledgeBaseCreate', () => { renderComponent() // The ExternalApiSelect should show the first selected API name - expect(screen.getByText('Test API 1')).toBeInTheDocument() + // The ExternalApiSelect should show the first selected API name + expect(screen.getByText('Test API 1'))!.toBeInTheDocument() }) it('should allow selecting different API from dropdown', async () => { @@ -473,7 +478,8 @@ describe('ExternalKnowledgeBaseCreate', () => { renderComponent() // Should show "no external knowledge" button - expect(screen.getByText('dataset.noExternalKnowledge')).toBeInTheDocument() + // Should show "no external knowledge" button + expect(screen.getByText('dataset.noExternalKnowledge'))!.toBeInTheDocument() }) it('should open add API modal when add button is clicked', async () => { @@ -504,7 +510,7 @@ describe('ExternalKnowledgeBaseCreate', () => { await user.click(addButton!) // Get the callback and invoke it - const modalCall = mockSetShowExternalKnowledgeAPIModal.mock.calls[0][0] + const modalCall = mockSetShowExternalKnowledgeAPIModal.mock.calls[0]![0] await modalCall.onSaveCallback() expect(mockMutateExternalKnowledgeApis).toHaveBeenCalled() @@ -521,7 +527,7 @@ describe('ExternalKnowledgeBaseCreate', () => { await user.click(addButton!) // Get the callback and invoke it - const modalCall = mockSetShowExternalKnowledgeAPIModal.mock.calls[0][0] + const modalCall = mockSetShowExternalKnowledgeAPIModal.mock.calls[0]![0] modalCall.onCancelCallback() expect(mockMutateExternalKnowledgeApis).toHaveBeenCalled() @@ -535,8 +541,9 @@ describe('ExternalKnowledgeBaseCreate', () => { await user.click(apiSelector) // Should show API URLs - expect(screen.getByText('https://api1.example.com')).toBeInTheDocument() - expect(screen.getByText('https://api2.example.com')).toBeInTheDocument() + // Should show API URLs + expect(screen.getByText('https://api1.example.com'))!.toBeInTheDocument() + expect(screen.getByText('https://api2.example.com'))!.toBeInTheDocument() }) it('should show create new API option in dropdown', async () => { @@ -547,7 +554,8 @@ describe('ExternalKnowledgeBaseCreate', () => { await user.click(apiSelector) // Should show create new API option - expect(screen.getByText('dataset.createNewExternalAPI')).toBeInTheDocument() + // Should show create new API option + expect(screen.getByText('dataset.createNewExternalAPI'))!.toBeInTheDocument() }) it('should open add API modal when clicking create new API in dropdown', async () => { @@ -580,7 +588,7 @@ describe('ExternalKnowledgeBaseCreate', () => { await user.click(createNewApiOption) // Get the callback from the modal call and invoke it - const modalCall = mockSetShowExternalKnowledgeAPIModal.mock.calls[0][0] + const modalCall = mockSetShowExternalKnowledgeAPIModal.mock.calls[0]![0] await modalCall.onSaveCallback() expect(mockMutateExternalKnowledgeApis).toHaveBeenCalled() @@ -598,7 +606,7 @@ describe('ExternalKnowledgeBaseCreate', () => { await user.click(createNewApiOption) // Get the callback from the modal call and invoke it - const modalCall = mockSetShowExternalKnowledgeAPIModal.mock.calls[0][0] + const modalCall = mockSetShowExternalKnowledgeAPIModal.mock.calls[0]![0] modalCall.onCancelCallback() expect(mockMutateExternalKnowledgeApis).toHaveBeenCalled() @@ -612,12 +620,44 @@ describe('ExternalKnowledgeBaseCreate', () => { await user.click(apiSelector) // Dropdown should be open - API URLs visible - expect(screen.getByText('https://api1.example.com')).toBeInTheDocument() + // Dropdown should be open - API URLs visible + expect(screen.getByText('https://api1.example.com'))!.toBeInTheDocument() // Select the second API const secondApi = screen.getByText('Test API 2') await user.click(secondApi) + // Dropdown should be closed - API URLs not visible + // Dropdown should be closed - API URLs not visible + // Dropdown should be closed - API URLs not visible + // Dropdown should be closed - API URLs not visible + // Dropdown should be closed - API URLs not visible + // Dropdown should be closed - API URLs not visible + // Dropdown should be closed - API URLs not visible + // Dropdown should be closed - API URLs not visible + // Dropdown should be closed - API URLs not visible + // Dropdown should be closed - API URLs not visible + // Dropdown should be closed - API URLs not visible + // Dropdown should be closed - API URLs not visible + // Dropdown should be closed - API URLs not visible + // Dropdown should be closed - API URLs not visible + // Dropdown should be closed - API URLs not visible + // Dropdown should be closed - API URLs not visible + // Dropdown should be closed - API URLs not visible + // Dropdown should be closed - API URLs not visible + // Dropdown should be closed - API URLs not visible + // Dropdown should be closed - API URLs not visible + // Dropdown should be closed - API URLs not visible + // Dropdown should be closed - API URLs not visible + // Dropdown should be closed - API URLs not visible + // Dropdown should be closed - API URLs not visible + // Dropdown should be closed - API URLs not visible + // Dropdown should be closed - API URLs not visible + // Dropdown should be closed - API URLs not visible + // Dropdown should be closed - API URLs not visible + // Dropdown should be closed - API URLs not visible + // Dropdown should be closed - API URLs not visible + // Dropdown should be closed - API URLs not visible // Dropdown should be closed - API URLs not visible expect(screen.queryByText('https://api1.example.com')).not.toBeInTheDocument() }) @@ -628,7 +668,7 @@ describe('ExternalKnowledgeBaseCreate', () => { const apiSelector = screen.getByText('Test API 1') await user.click(apiSelector) - expect(screen.getByText('https://api1.example.com')).toBeInTheDocument() + expect(screen.getByText('https://api1.example.com'))!.toBeInTheDocument() await user.click(apiSelector) expect(screen.queryByText('https://api1.example.com')).not.toBeInTheDocument() @@ -783,14 +823,14 @@ describe('ExternalKnowledgeBaseCreate', () => { renderComponent({ loading: true }) const connectButton = screen.getByText('dataset.externalKnowledgeForm.connect').closest('button') - expect(connectButton).toBeInTheDocument() + expect(connectButton)!.toBeInTheDocument() }) it('should render correctly when not loading', () => { renderComponent({ loading: false }) const connectButton = screen.getByText('dataset.externalKnowledgeForm.connect').closest('button') - expect(connectButton).toBeInTheDocument() + expect(connectButton)!.toBeInTheDocument() }) }) @@ -804,7 +844,7 @@ describe('ExternalKnowledgeBaseCreate', () => { // Find and click the switch for score threshold const switches = screen.getAllByRole('switch') const scoreThresholdSwitch = switches[0] // The score threshold switch - await user.click(scoreThresholdSwitch) + await user.click(scoreThresholdSwitch!) // Fill required fields const nameInput = screen.getByPlaceholderText('dataset.externalKnowledgeNamePlaceholder') @@ -834,10 +874,12 @@ describe('ExternalKnowledgeBaseCreate', () => { renderComponent() // Should show the retrieval settings section title - expect(screen.getByText('dataset.retrievalSettings')).toBeInTheDocument() + // Should show the retrieval settings section title + expect(screen.getByText('dataset.retrievalSettings'))!.toBeInTheDocument() // Should show Top K and Score Threshold labels - expect(screen.getByText('appDebug.datasetConfig.top_k')).toBeInTheDocument() - expect(screen.getByText('appDebug.datasetConfig.score_threshold')).toBeInTheDocument() + // Should show Top K and Score Threshold labels + expect(screen.getByText('appDebug.datasetConfig.top_k'))!.toBeInTheDocument() + expect(screen.getByText('appDebug.datasetConfig.score_threshold'))!.toBeInTheDocument() }) }) @@ -855,6 +897,37 @@ describe('ExternalKnowledgeBaseCreate', () => { />, ) + // In hit testing mode, the title should not be shown + // In hit testing mode, the title should not be shown + // In hit testing mode, the title should not be shown + // In hit testing mode, the title should not be shown + // In hit testing mode, the title should not be shown + // In hit testing mode, the title should not be shown + // In hit testing mode, the title should not be shown + // In hit testing mode, the title should not be shown + // In hit testing mode, the title should not be shown + // In hit testing mode, the title should not be shown + // In hit testing mode, the title should not be shown + // In hit testing mode, the title should not be shown + // In hit testing mode, the title should not be shown + // In hit testing mode, the title should not be shown + // In hit testing mode, the title should not be shown + // In hit testing mode, the title should not be shown + // In hit testing mode, the title should not be shown + // In hit testing mode, the title should not be shown + // In hit testing mode, the title should not be shown + // In hit testing mode, the title should not be shown + // In hit testing mode, the title should not be shown + // In hit testing mode, the title should not be shown + // In hit testing mode, the title should not be shown + // In hit testing mode, the title should not be shown + // In hit testing mode, the title should not be shown + // In hit testing mode, the title should not be shown + // In hit testing mode, the title should not be shown + // In hit testing mode, the title should not be shown + // In hit testing mode, the title should not be shown + // In hit testing mode, the title should not be shown + // In hit testing mode, the title should not be shown // In hit testing mode, the title should not be shown expect(screen.queryByText('dataset.retrievalSettings')).not.toBeInTheDocument() }) @@ -871,6 +944,37 @@ describe('ExternalKnowledgeBaseCreate', () => { />, ) + // In retrieval setting mode, the title should not be shown + // In retrieval setting mode, the title should not be shown + // In retrieval setting mode, the title should not be shown + // In retrieval setting mode, the title should not be shown + // In retrieval setting mode, the title should not be shown + // In retrieval setting mode, the title should not be shown + // In retrieval setting mode, the title should not be shown + // In retrieval setting mode, the title should not be shown + // In retrieval setting mode, the title should not be shown + // In retrieval setting mode, the title should not be shown + // In retrieval setting mode, the title should not be shown + // In retrieval setting mode, the title should not be shown + // In retrieval setting mode, the title should not be shown + // In retrieval setting mode, the title should not be shown + // In retrieval setting mode, the title should not be shown + // In retrieval setting mode, the title should not be shown + // In retrieval setting mode, the title should not be shown + // In retrieval setting mode, the title should not be shown + // In retrieval setting mode, the title should not be shown + // In retrieval setting mode, the title should not be shown + // In retrieval setting mode, the title should not be shown + // In retrieval setting mode, the title should not be shown + // In retrieval setting mode, the title should not be shown + // In retrieval setting mode, the title should not be shown + // In retrieval setting mode, the title should not be shown + // In retrieval setting mode, the title should not be shown + // In retrieval setting mode, the title should not be shown + // In retrieval setting mode, the title should not be shown + // In retrieval setting mode, the title should not be shown + // In retrieval setting mode, the title should not be shown + // In retrieval setting mode, the title should not be shown // In retrieval setting mode, the title should not be shown expect(screen.queryByText('dataset.retrievalSettings')).not.toBeInTheDocument() }) @@ -889,7 +993,7 @@ describe('ExternalKnowledgeBaseCreate', () => { // Find and click the switch const switches = screen.getAllByRole('switch') - await user.click(switches[0]) + await user.click(switches[0]!) expect(onChange).toHaveBeenCalledWith({ score_threshold_enabled: true }) }) @@ -908,7 +1012,7 @@ describe('ExternalKnowledgeBaseCreate', () => { // The TopKItem renders the visible number-field input as a textbox. const inputs = screen.getAllByRole('textbox') const topKInput = inputs[0] - fireEvent.change(topKInput, { target: { value: '8' } }) + fireEvent.change(topKInput!, { target: { value: '8' } }) expect(onChange).toHaveBeenCalledWith({ top_k: 8 }) }) @@ -927,7 +1031,7 @@ describe('ExternalKnowledgeBaseCreate', () => { // The ScoreThresholdItem renders the visible number-field input as a textbox. const inputs = screen.getAllByRole('textbox') const scoreThresholdInput = inputs[1] - fireEvent.change(scoreThresholdInput, { target: { value: '0.8' } }) + fireEvent.change(scoreThresholdInput!, { target: { value: '0.8' } }) expect(onChange).toHaveBeenCalledWith({ score_threshold: 0.8 }) }) @@ -976,7 +1080,7 @@ describe('ExternalKnowledgeBaseCreate', () => { // Toggle score threshold switch const switches = screen.getAllByRole('switch') const scoreThresholdSwitch = switches[0] - await user.click(scoreThresholdSwitch) + await user.click(scoreThresholdSwitch!) // Fill required fields const nameInput = screen.getByPlaceholderText('dataset.externalKnowledgeNamePlaceholder') @@ -1018,17 +1122,18 @@ describe('ExternalKnowledgeBaseCreate', () => { const externalLink = screen.getByText('dataset.connectHelper.helper4') expect(externalLink.tagName).toBe('A') - expect(externalLink).toHaveAttribute('target', '_blank') - expect(externalLink).toHaveAttribute('rel', 'noopener noreferrer') + expect(externalLink)!.toHaveAttribute('target', '_blank') + expect(externalLink)!.toHaveAttribute('rel', 'noopener noreferrer') }) it('should have labels for form inputs', () => { renderComponent() // Check labels exist - expect(screen.getByText('dataset.externalKnowledgeName')).toBeInTheDocument() - expect(screen.getByText('dataset.externalKnowledgeDescription')).toBeInTheDocument() - expect(screen.getByText('dataset.externalKnowledgeId')).toBeInTheDocument() + // Check labels exist + expect(screen.getByText('dataset.externalKnowledgeName'))!.toBeInTheDocument() + expect(screen.getByText('dataset.externalKnowledgeDescription'))!.toBeInTheDocument() + expect(screen.getByText('dataset.externalKnowledgeId'))!.toBeInTheDocument() }) }) }) diff --git a/web/app/components/datasets/hit-testing/__tests__/index.spec.tsx b/web/app/components/datasets/hit-testing/__tests__/index.spec.tsx index 2dda6ecaae..df0e39554a 100644 --- a/web/app/components/datasets/hit-testing/__tests__/index.spec.tsx +++ b/web/app/components/datasets/hit-testing/__tests__/index.spec.tsx @@ -309,25 +309,26 @@ describe('HitTestingPage', () => { describe('Rendering', () => { it('should render without crashing', () => { const { container } = renderWithProviders() - expect(container.firstChild).toBeInTheDocument() + expect(container.firstChild)!.toBeInTheDocument() }) it('should render page title', () => { renderWithProviders() // Look for heading element const heading = screen.getByRole('heading', { level: 1 }) - expect(heading).toBeInTheDocument() + expect(heading)!.toBeInTheDocument() }) it('should render records section', () => { const { container } = renderWithProviders() // The records section should be present - expect(container.querySelector('.flex-col')).toBeInTheDocument() + // The records section should be present + expect(container.querySelector('.flex-col'))!.toBeInTheDocument() }) it('should render query input', () => { renderWithProviders() - expect(screen.getByRole('textbox')).toBeInTheDocument() + expect(screen.getByRole('textbox'))!.toBeInTheDocument() }) }) @@ -343,7 +344,7 @@ describe('HitTestingPage', () => { const { container } = renderWithProviders() // Loading component should be visible - look for the loading animation const loadingElement = container.querySelector('[class*="animate"]') || container.querySelector('.flex-1') - expect(loadingElement).toBeInTheDocument() + expect(loadingElement)!.toBeInTheDocument() }) }) @@ -353,7 +354,7 @@ describe('HitTestingPage', () => { // EmptyRecords component should be rendered - check that the component is mounted // The EmptyRecords has a specific structure with bg-workflow-process-bg class const mainContainer = container.querySelector('.flex.h-full') - expect(mainContainer).toBeInTheDocument() + expect(mainContainer)!.toBeInTheDocument() }) }) @@ -373,7 +374,7 @@ describe('HitTestingPage', () => { } as unknown as ReturnType) renderWithProviders() - expect(screen.getByText('Test query')).toBeInTheDocument() + expect(screen.getByText('Test query'))!.toBeInTheDocument() }) }) @@ -404,7 +405,7 @@ describe('HitTestingPage', () => { const { container } = renderWithProviders() // The right panel should be present (on non-mobile) const rightPanel = container.querySelector('.rounded-tl-2xl') - expect(rightPanel).toBeInTheDocument() + expect(rightPanel)!.toBeInTheDocument() }) }) @@ -423,7 +424,8 @@ describe('HitTestingPage', () => { fireEvent.click(methodSelector) // The component should still be functional after the click - expect(container.firstChild).toBeInTheDocument() + // The component should still be functional after the click + expect(container.firstChild)!.toBeInTheDocument() }) }) @@ -445,7 +447,8 @@ describe('HitTestingPage', () => { const { container } = renderWithProviders() // The right panel should show empty state initially - expect(container.querySelector('.rounded-tl-2xl')).toBeInTheDocument() + // The right panel should show empty state initially + expect(container.querySelector('.rounded-tl-2xl'))!.toBeInTheDocument() }) it('should render loading skeleton when retrieval is in progress', async () => { @@ -458,7 +461,8 @@ describe('HitTestingPage', () => { const { container } = renderWithProviders() // Component should render without crashing - expect(container.firstChild).toBeInTheDocument() + // Component should render without crashing + expect(container.firstChild)!.toBeInTheDocument() }) it('should render results when hit testing returns data', async () => { @@ -479,7 +483,8 @@ describe('HitTestingPage', () => { const { container } = renderWithProviders() // The component should render the result display area - expect(container.querySelector('.bg-background-body')).toBeInTheDocument() + // The component should render the result display area + expect(container.querySelector('.bg-background-body'))!.toBeInTheDocument() }) }) @@ -513,7 +518,8 @@ describe('HitTestingPage', () => { fireEvent.click(row) // The query input should be updated - this causes re-render with new key - expect(screen.getByRole('textbox')).toBeInTheDocument() + // The query input should be updated - this causes re-render with new key + expect(screen.getByRole('textbox'))!.toBeInTheDocument() }) }) @@ -536,7 +542,8 @@ describe('HitTestingPage', () => { const { container } = renderWithProviders() // Component should render - expect(container.firstChild).toBeInTheDocument() + // Component should render + expect(container.firstChild)!.toBeInTheDocument() }) }) @@ -549,7 +556,8 @@ describe('HitTestingPage', () => { const { container } = renderWithProviders() // Component should still render - expect(container.firstChild).toBeInTheDocument() + // Component should still render + expect(container.firstChild)!.toBeInTheDocument() }) }) @@ -562,7 +570,7 @@ describe('HitTestingPage', () => { const { rerender, container } = renderWithProviders() - expect(container.firstChild).toBeInTheDocument() + expect(container.firstChild)!.toBeInTheDocument() // Re-render with mobile vi.mocked(useBreakpoints.default).mockReturnValue('mobile' as unknown as ReturnType) @@ -573,7 +581,7 @@ describe('HitTestingPage', () => { , ) - expect(container.firstChild).toBeInTheDocument() + expect(container.firstChild)!.toBeInTheDocument() }) }) }) @@ -638,7 +646,8 @@ describe('Integration: Hit Testing Flow', () => { fireEvent.change(textarea, { target: { value: 'Test query' } }) // Component should still be functional - check for the main container - expect(container.firstChild).toBeInTheDocument() + // Component should still be functional - check for the main container + expect(container.firstChild)!.toBeInTheDocument() }) it('should render hit results after successful submission', async () => { @@ -762,7 +771,8 @@ describe('Integration: Hit Testing Flow', () => { const { container } = renderWithProviders() // Component should render - expect(container.firstChild).toBeInTheDocument() + // Component should render + expect(container.firstChild)!.toBeInTheDocument() // Wait for textbox with timeout for CI const textarea = await waitFor( @@ -781,7 +791,7 @@ describe('Integration: Hit Testing Flow', () => { // Verify component is still functional after submission await waitFor( () => { - expect(screen.getByRole('textbox')).toBeInTheDocument() + expect(screen.getByRole('textbox'))!.toBeInTheDocument() }, { timeout: 3000 }, ) @@ -819,12 +829,14 @@ describe('Drawer and Modal Interactions', () => { await waitFor(() => { // The drawer should open - verify container is still there - expect(container.firstChild).toBeInTheDocument() + // The drawer should open - verify container is still there + expect(container.firstChild)!.toBeInTheDocument() }) } // Component should still be functional - verify main container - expect(container.querySelector('.overflow-y-auto')).toBeInTheDocument() + // Component should still be functional - verify main container + expect(container.querySelector('.overflow-y-auto'))!.toBeInTheDocument() }) it('should close retrieval modal when onHide is called', async () => { @@ -841,7 +853,8 @@ describe('Drawer and Modal Interactions', () => { } // Component should still be functional - expect(container.firstChild).toBeInTheDocument() + // Component should still be functional + expect(container.firstChild)!.toBeInTheDocument() }) }) @@ -901,7 +914,7 @@ describe('renderHitResults Coverage', () => { // Verify component is functional await waitFor(() => { - expect(container.firstChild).toBeInTheDocument() + expect(container.firstChild)!.toBeInTheDocument() }) }) @@ -928,7 +941,7 @@ describe('renderHitResults Coverage', () => { fireEvent.click(submitButton) await waitFor(() => { - expect(container.firstChild).toBeInTheDocument() + expect(container.firstChild)!.toBeInTheDocument() }) }) }) @@ -954,12 +967,13 @@ describe('ModifyRetrievalModal onSave Coverage', () => { // Wait for drawer to open await waitFor(() => { - expect(container.firstChild).toBeInTheDocument() + expect(container.firstChild)!.toBeInTheDocument() }) } // Verify component renders correctly - expect(container.querySelector('.overflow-y-auto')).toBeInTheDocument() + // Verify component renders correctly + expect(container.querySelector('.overflow-y-auto'))!.toBeInTheDocument() }) it('should close modal after saving', async () => { @@ -975,7 +989,8 @@ describe('ModifyRetrievalModal onSave Coverage', () => { fireEvent.click(methodSelector) // Component should still be rendered - expect(container.firstChild).toBeInTheDocument() + // Component should still be rendered + expect(container.firstChild)!.toBeInTheDocument() }) }) @@ -1038,7 +1053,7 @@ describe('HitTestingPage Internal Functions Coverage', () => { // Wait for state updates await waitFor(() => { - expect(container.firstChild).toBeInTheDocument() + expect(container.firstChild)!.toBeInTheDocument() }, { timeout: 3000 }) // Verify mutation was called @@ -1059,18 +1074,19 @@ describe('HitTestingPage Internal Functions Coverage', () => { // Wait for drawer content await waitFor(() => { - expect(container.firstChild).toBeInTheDocument() + expect(container.firstChild)!.toBeInTheDocument() }) // Try to find save button in the drawer const saveButtons = screen.queryAllByText(/save/i) if (saveButtons.length > 0) { - fireEvent.click(saveButtons[0]) + fireEvent.click(saveButtons[0]!) } } // Component should still work - expect(container.firstChild).toBeInTheDocument() + // Component should still work + expect(container.firstChild)!.toBeInTheDocument() }) it('should show hit count in results panel after successful query', async () => { @@ -1101,7 +1117,7 @@ describe('HitTestingPage Internal Functions Coverage', () => { // Verify the component renders await waitFor(() => { - expect(container.firstChild).toBeInTheDocument() + expect(container.firstChild)!.toBeInTheDocument() }, { timeout: 3000 }) }) }) diff --git a/web/app/components/datasets/hit-testing/components/query-input/__tests__/index.spec.tsx b/web/app/components/datasets/hit-testing/components/query-input/__tests__/index.spec.tsx index 25b7abe7ea..d9427f5117 100644 --- a/web/app/components/datasets/hit-testing/components/query-input/__tests__/index.spec.tsx +++ b/web/app/components/datasets/hit-testing/components/query-input/__tests__/index.spec.tsx @@ -79,17 +79,17 @@ describe('QueryInput', () => { it('should render title', () => { render() - expect(screen.getByText('datasetHitTesting.input.title')).toBeInTheDocument() + expect(screen.getByText('datasetHitTesting.input.title'))!.toBeInTheDocument() }) it('should render textarea with query text', () => { render() - expect(screen.getByTestId('textarea')).toBeInTheDocument() + expect(screen.getByTestId('textarea'))!.toBeInTheDocument() }) it('should render submit button', () => { render() - expect(screen.getByRole('button', { name: /input\.testing/ })).toBeInTheDocument() + expect(screen.getByRole('button', { name: /input\.testing/ }))!.toBeInTheDocument() }) it('should disable submit button when text is empty', () => { @@ -98,17 +98,17 @@ describe('QueryInput', () => { queries: [{ content: '', content_type: 'text_query', file_info: null }] satisfies Query[], } render() - expect(screen.getByRole('button', { name: /input\.testing/ })).toBeDisabled() + expect(screen.getByRole('button', { name: /input\.testing/ }))!.toBeDisabled() }) it('should render retrieval method for non-external mode', () => { render() - expect(screen.getByText('dataset.retrieval.semantic_search.title')).toBeInTheDocument() + expect(screen.getByText('dataset.retrieval.semantic_search.title'))!.toBeInTheDocument() }) it('should render settings button for external mode', () => { render() - expect(screen.getByText('datasetHitTesting.settingTitle')).toBeInTheDocument() + expect(screen.getByText('datasetHitTesting.settingTitle'))!.toBeInTheDocument() }) it('should disable submit button when text exceeds 200 characters', () => { @@ -117,15 +117,15 @@ describe('QueryInput', () => { queries: [{ content: 'a'.repeat(201), content_type: 'text_query', file_info: null }] satisfies Query[], } render() - expect(screen.getByRole('button', { name: /input\.testing/ })).toBeDisabled() + expect(screen.getByRole('button', { name: /input\.testing/ }))!.toBeDisabled() }) it('should show loading state on submit button when loading', () => { render() const submitButton = screen.getByRole('button', { name: /input\.testing/ }) - expect(submitButton).toBeDisabled() - expect(submitButton).toHaveAttribute('aria-busy', 'true') - expect(submitButton.querySelector('.animate-spin')).toBeInTheDocument() + expect(submitButton)!.toBeDisabled() + expect(submitButton)!.toHaveAttribute('aria-busy', 'true') + expect(submitButton.querySelector('.animate-spin'))!.toBeInTheDocument() }) // Cover line 83: images useMemo with image_query data @@ -141,6 +141,37 @@ describe('QueryInput', () => { ] render() + // Submit should be enabled since we have text + uploaded image + // Submit should be enabled since we have text + uploaded image + // Submit should be enabled since we have text + uploaded image + // Submit should be enabled since we have text + uploaded image + // Submit should be enabled since we have text + uploaded image + // Submit should be enabled since we have text + uploaded image + // Submit should be enabled since we have text + uploaded image + // Submit should be enabled since we have text + uploaded image + // Submit should be enabled since we have text + uploaded image + // Submit should be enabled since we have text + uploaded image + // Submit should be enabled since we have text + uploaded image + // Submit should be enabled since we have text + uploaded image + // Submit should be enabled since we have text + uploaded image + // Submit should be enabled since we have text + uploaded image + // Submit should be enabled since we have text + uploaded image + // Submit should be enabled since we have text + uploaded image + // Submit should be enabled since we have text + uploaded image + // Submit should be enabled since we have text + uploaded image + // Submit should be enabled since we have text + uploaded image + // Submit should be enabled since we have text + uploaded image + // Submit should be enabled since we have text + uploaded image + // Submit should be enabled since we have text + uploaded image + // Submit should be enabled since we have text + uploaded image + // Submit should be enabled since we have text + uploaded image + // Submit should be enabled since we have text + uploaded image + // Submit should be enabled since we have text + uploaded image + // Submit should be enabled since we have text + uploaded image + // Submit should be enabled since we have text + uploaded image + // Submit should be enabled since we have text + uploaded image + // Submit should be enabled since we have text + uploaded image + // Submit should be enabled since we have text + uploaded image // Submit should be enabled since we have text + uploaded image expect(screen.getByRole('button', { name: /input\.testing/ })).not.toBeDisabled() }) @@ -153,7 +184,7 @@ describe('QueryInput', () => { // Click settings button to open modal fireEvent.click(screen.getByRole('button', { name: /settingTitle/ })) - expect(screen.getByTestId('external-retrieval-modal')).toBeInTheDocument() + expect(screen.getByTestId('external-retrieval-modal'))!.toBeInTheDocument() // Close modal fireEvent.click(screen.getByTestId('modal-close')) @@ -165,7 +196,7 @@ describe('QueryInput', () => { // Open modal fireEvent.click(screen.getByRole('button', { name: /settingTitle/ })) - expect(screen.getByTestId('external-retrieval-modal')).toBeInTheDocument() + expect(screen.getByTestId('external-retrieval-modal'))!.toBeInTheDocument() // Save settings fireEvent.click(screen.getByTestId('modal-save')) @@ -274,7 +305,7 @@ describe('QueryInput', () => { ]), ) // Should not contain image_query - const calledWith = defaultProps.setQueries.mock.calls[0][0] as Query[] + const calledWith = defaultProps.setQueries.mock.calls[0]![0] as Query[] expect(calledWith.filter(q => q.content_type === 'image_query')).toHaveLength(0) }) }) @@ -412,7 +443,7 @@ describe('QueryInput', () => { it('should show keyword_search when isEconomy is true', () => { render() - expect(screen.getByText('dataset.retrieval.keyword_search.title')).toBeInTheDocument() + expect(screen.getByText('dataset.retrieval.keyword_search.title'))!.toBeInTheDocument() }) }) }) diff --git a/web/app/components/datasets/list/datasets.tsx b/web/app/components/datasets/list/datasets.tsx index 817b927e6b..8d7006a08a 100644 --- a/web/app/components/datasets/list/datasets.tsx +++ b/web/app/components/datasets/list/datasets.tsx @@ -45,7 +45,7 @@ const Datasets = ({ useEffect(() => { if (anchorRef.current) { observerRef.current = new IntersectionObserver((entries) => { - if (entries[0].isIntersecting && hasNextPage && !isFetching) + if (entries[0]!.isIntersecting && hasNextPage && !isFetching) fetchNextPage() }, { rootMargin: '100px', diff --git a/web/app/components/datasets/metadata/edit-metadata-batch/__tests__/modal.spec.tsx b/web/app/components/datasets/metadata/edit-metadata-batch/__tests__/modal.spec.tsx index d9b88e20bb..40c925222c 100644 --- a/web/app/components/datasets/metadata/edit-metadata-batch/__tests__/modal.spec.tsx +++ b/web/app/components/datasets/metadata/edit-metadata-batch/__tests__/modal.spec.tsx @@ -120,14 +120,14 @@ describe('EditMetadataBatchModal', () => { it('should render without crashing', async () => { render() await waitFor(() => { - expect(screen.getByRole('dialog')).toBeInTheDocument() + expect(screen.getByRole('dialog'))!.toBeInTheDocument() }) }) it('should render document count', async () => { render() await waitFor(() => { - expect(screen.getByText(/5/)).toBeInTheDocument() + expect(screen.getByText(/5/))!.toBeInTheDocument() }) }) @@ -142,8 +142,8 @@ describe('EditMetadataBatchModal', () => { it('should render field names for existing items', async () => { render() await waitFor(() => { - expect(screen.getByText('field_one')).toBeInTheDocument() - expect(screen.getByText('field_two')).toBeInTheDocument() + expect(screen.getByText('field_one'))!.toBeInTheDocument() + expect(screen.getByText('field_two'))!.toBeInTheDocument() }) }) @@ -158,7 +158,7 @@ describe('EditMetadataBatchModal', () => { it('should render select metadata modal', async () => { render() await waitFor(() => { - expect(screen.getByTestId('select-modal')).toBeInTheDocument() + expect(screen.getByTestId('select-modal'))!.toBeInTheDocument() }) }) }) @@ -169,7 +169,7 @@ describe('EditMetadataBatchModal', () => { render() await waitFor(() => { - expect(screen.getByRole('dialog')).toBeInTheDocument() + expect(screen.getByRole('dialog'))!.toBeInTheDocument() }) const cancelButton = screen.getByText(/cancel/i) @@ -183,7 +183,7 @@ describe('EditMetadataBatchModal', () => { render() await waitFor(() => { - expect(screen.getByRole('dialog')).toBeInTheDocument() + expect(screen.getByRole('dialog'))!.toBeInTheDocument() }) // Find the primary save button (not the one in SelectMetadataModal) @@ -196,17 +196,17 @@ describe('EditMetadataBatchModal', () => { render() await waitFor(() => { - expect(screen.getByRole('dialog')).toBeInTheDocument() + expect(screen.getByRole('dialog'))!.toBeInTheDocument() }) const checkboxContainer = document.querySelector('[data-testid*="checkbox"]') - expect(checkboxContainer).toBeInTheDocument() + expect(checkboxContainer)!.toBeInTheDocument() if (checkboxContainer) { fireEvent.click(checkboxContainer) await waitFor(() => { const checkIcon = screen.getByTestId('check-icon-apply-to-all') - expect(checkIcon).toBeInTheDocument() + expect(checkIcon)!.toBeInTheDocument() }) } }) @@ -216,7 +216,7 @@ describe('EditMetadataBatchModal', () => { render() await waitFor(() => { - expect(screen.getByRole('dialog')).toBeInTheDocument() + expect(screen.getByRole('dialog'))!.toBeInTheDocument() }) }) }) @@ -226,7 +226,7 @@ describe('EditMetadataBatchModal', () => { render() await waitFor(() => { - expect(screen.getByRole('dialog')).toBeInTheDocument() + expect(screen.getByRole('dialog'))!.toBeInTheDocument() }) fireEvent.click(screen.getByTestId('change-1')) @@ -239,7 +239,7 @@ describe('EditMetadataBatchModal', () => { render() await waitFor(() => { - expect(screen.getByRole('dialog')).toBeInTheDocument() + expect(screen.getByRole('dialog'))!.toBeInTheDocument() }) fireEvent.click(screen.getByTestId('remove-1')) @@ -252,7 +252,7 @@ describe('EditMetadataBatchModal', () => { render() await waitFor(() => { - expect(screen.getByRole('dialog')).toBeInTheDocument() + expect(screen.getByRole('dialog'))!.toBeInTheDocument() }) // First change the item @@ -269,14 +269,14 @@ describe('EditMetadataBatchModal', () => { render() await waitFor(() => { - expect(screen.getByRole('dialog')).toBeInTheDocument() + expect(screen.getByRole('dialog'))!.toBeInTheDocument() }) fireEvent.click(screen.getByTestId('select-metadata')) // Should now have add-row for the new item await waitFor(() => { - expect(screen.getByTestId('add-row')).toBeInTheDocument() + expect(screen.getByTestId('add-row'))!.toBeInTheDocument() }) }) @@ -284,14 +284,14 @@ describe('EditMetadataBatchModal', () => { render() await waitFor(() => { - expect(screen.getByRole('dialog')).toBeInTheDocument() + expect(screen.getByRole('dialog'))!.toBeInTheDocument() }) // First add an item fireEvent.click(screen.getByTestId('select-metadata')) await waitFor(() => { - expect(screen.getByTestId('add-row')).toBeInTheDocument() + expect(screen.getByTestId('add-row'))!.toBeInTheDocument() }) // Then remove it @@ -306,20 +306,20 @@ describe('EditMetadataBatchModal', () => { render() await waitFor(() => { - expect(screen.getByRole('dialog')).toBeInTheDocument() + expect(screen.getByRole('dialog'))!.toBeInTheDocument() }) // First add an item fireEvent.click(screen.getByTestId('select-metadata')) await waitFor(() => { - expect(screen.getByTestId('add-row')).toBeInTheDocument() + expect(screen.getByTestId('add-row'))!.toBeInTheDocument() }) // Then change it fireEvent.click(screen.getByTestId('add-change-new-1')) - expect(screen.getByTestId('add-row')).toBeInTheDocument() + expect(screen.getByTestId('add-row'))!.toBeInTheDocument() }) it('should call doAddMetaData when saving new metadata with valid name', async () => { @@ -328,7 +328,7 @@ describe('EditMetadataBatchModal', () => { render() await waitFor(() => { - expect(screen.getByRole('dialog')).toBeInTheDocument() + expect(screen.getByRole('dialog'))!.toBeInTheDocument() }) fireEvent.click(screen.getByTestId('save-metadata')) @@ -344,7 +344,7 @@ describe('EditMetadataBatchModal', () => { render() await waitFor(() => { - expect(screen.getByRole('dialog')).toBeInTheDocument() + expect(screen.getByRole('dialog'))!.toBeInTheDocument() }) fireEvent.click(screen.getByTestId('save-metadata')) @@ -368,7 +368,7 @@ describe('EditMetadataBatchModal', () => { render() await waitFor(() => { - expect(screen.getByRole('dialog')).toBeInTheDocument() + expect(screen.getByRole('dialog'))!.toBeInTheDocument() }) fireEvent.click(screen.getByTestId('save-metadata')) @@ -388,7 +388,7 @@ describe('EditMetadataBatchModal', () => { render() await waitFor(() => { - expect(screen.getByRole('dialog')).toBeInTheDocument() + expect(screen.getByRole('dialog'))!.toBeInTheDocument() }) fireEvent.click(screen.getByTestId('manage-metadata')) @@ -401,14 +401,14 @@ describe('EditMetadataBatchModal', () => { it('should pass correct datasetId', async () => { render() await waitFor(() => { - expect(screen.getByRole('dialog')).toBeInTheDocument() + expect(screen.getByRole('dialog'))!.toBeInTheDocument() }) }) it('should display correct document number', async () => { render() await waitFor(() => { - expect(screen.getByText(/10/)).toBeInTheDocument() + expect(screen.getByText(/10/))!.toBeInTheDocument() }) }) @@ -427,7 +427,7 @@ describe('EditMetadataBatchModal', () => { ] render() await waitFor(() => { - expect(screen.getByTestId('edit-row')).toBeInTheDocument() + expect(screen.getByTestId('edit-row'))!.toBeInTheDocument() }) }) @@ -436,7 +436,7 @@ describe('EditMetadataBatchModal', () => { render() await waitFor(() => { - expect(screen.getByRole('dialog')).toBeInTheDocument() + expect(screen.getByRole('dialog'))!.toBeInTheDocument() }) // Find the primary save button @@ -453,7 +453,7 @@ describe('EditMetadataBatchModal', () => { render() await waitFor(() => { - expect(screen.getByRole('dialog')).toBeInTheDocument() + expect(screen.getByRole('dialog'))!.toBeInTheDocument() }) fireEvent.click(screen.getByRole('button', { name: 'common.operation.save' })) @@ -470,7 +470,7 @@ describe('EditMetadataBatchModal', () => { render() await waitFor(() => { - expect(screen.getByRole('dialog')).toBeInTheDocument() + expect(screen.getByRole('dialog'))!.toBeInTheDocument() }) const checkboxContainer = document.querySelector('[data-testid*="checkbox"]') @@ -493,7 +493,7 @@ describe('EditMetadataBatchModal', () => { render() await waitFor(() => { - expect(screen.getByRole('dialog')).toBeInTheDocument() + expect(screen.getByRole('dialog'))!.toBeInTheDocument() }) // Remove an item @@ -503,7 +503,7 @@ describe('EditMetadataBatchModal', () => { expect(onSave).toHaveBeenCalled() // The first argument should not contain the deleted item (id '1') - const savedList = onSave.mock.calls[0][0] as MetadataItemInBatchEdit[] + const savedList = onSave.mock.calls[0]![0] as MetadataItemInBatchEdit[] const hasDeletedItem = savedList.some(item => item.id === '1') expect(hasDeletedItem).toBe(false) }) @@ -512,13 +512,13 @@ describe('EditMetadataBatchModal', () => { render() await waitFor(() => { - expect(screen.getByRole('dialog')).toBeInTheDocument() + expect(screen.getByRole('dialog'))!.toBeInTheDocument() }) // Add first item fireEvent.click(screen.getByTestId('select-metadata')) await waitFor(() => { - expect(screen.getByTestId('add-row')).toBeInTheDocument() + expect(screen.getByTestId('add-row'))!.toBeInTheDocument() }) // Remove it @@ -531,7 +531,7 @@ describe('EditMetadataBatchModal', () => { // Add again fireEvent.click(screen.getByTestId('select-metadata')) await waitFor(() => { - expect(screen.getByTestId('add-row')).toBeInTheDocument() + expect(screen.getByTestId('add-row'))!.toBeInTheDocument() }) }) }) diff --git a/web/app/components/datasets/metadata/edit-metadata-batch/modal.tsx b/web/app/components/datasets/metadata/edit-metadata-batch/modal.tsx index 2dfff611ed..974ce977e7 100644 --- a/web/app/components/datasets/metadata/edit-metadata-batch/modal.tsx +++ b/web/app/components/datasets/metadata/edit-metadata-batch/modal.tsx @@ -47,8 +47,8 @@ const EditMetadataBatchModal: FC = ({ datasetId, documentNum, list, onSav const newTempleList = produce(templeList, (draft) => { const index = draft.findIndex(i => i.id === id) if (index !== -1) { - draft[index].isUpdated = true - draft[index].updateType = UpdateType.delete + draft[index]!.isUpdated = true + draft[index]!.updateType = UpdateType.delete } }) setTempleList(newTempleList) @@ -57,9 +57,9 @@ const EditMetadataBatchModal: FC = ({ datasetId, documentNum, list, onSav const newTempleList = produce(templeList, (draft) => { const index = draft.findIndex(i => i.id === id) if (index !== -1) { - draft[index] = { ...list[index] } - draft[index].isUpdated = false - delete draft[index].updateType + draft[index] = { ...list[index]! } + draft[index]!.isUpdated = false + delete draft[index]!.updateType } }) setTempleList(newTempleList) diff --git a/web/app/components/datasets/metadata/hooks/__tests__/use-batch-edit-document-metadata.spec.ts b/web/app/components/datasets/metadata/hooks/__tests__/use-batch-edit-document-metadata.spec.ts index 05088cabec..a4650bac43 100644 --- a/web/app/components/datasets/metadata/hooks/__tests__/use-batch-edit-document-metadata.spec.ts +++ b/web/app/components/datasets/metadata/hooks/__tests__/use-batch-edit-document-metadata.spec.ts @@ -239,7 +239,7 @@ describe('useBatchEditDocumentMetadata', () => { // Should only have one item for field '1', marked as multiple const fieldItems = result.current.originalList.filter(item => item.id === '1') expect(fieldItems.length).toBe(1) - expect(fieldItems[0].isMultipleValue).toBe(true) + expect(fieldItems[0]!.isMultipleValue).toBe(true) }) }) @@ -458,7 +458,7 @@ describe('useBatchEditDocumentMetadata', () => { // Both documents should have the field after applying to all expect(mockMutateAsync).toHaveBeenCalled() - const callArgs = mockMutateAsync.mock.calls[0][0] + const callArgs = mockMutateAsync.mock.calls[0]![0] expect(callArgs.metadata_list.length).toBe(2) }) @@ -651,7 +651,7 @@ describe('useBatchEditDocumentMetadata', () => { await result.current.handleSave(editedList, [], false) }) - const callArgs = mockMutateAsync.mock.calls[0][0] + const callArgs = mockMutateAsync.mock.calls[0]![0] const sentItem = callArgs.metadata_list[0].metadata_list[0] // Only id, name, type, value should be present @@ -693,7 +693,7 @@ describe('useBatchEditDocumentMetadata', () => { await result.current.handleSave(editedList, [], false) }) - const callArgs = mockMutateAsync.mock.calls[0][0] + const callArgs = mockMutateAsync.mock.calls[0]![0] const sentItem = callArgs.metadata_list[0].metadata_list[0] // value should be null, not undefined diff --git a/web/app/components/datasets/metadata/hooks/__tests__/use-metadata-document.spec.ts b/web/app/components/datasets/metadata/hooks/__tests__/use-metadata-document.spec.ts index 67c8ffe3b3..9d8b73aaaa 100644 --- a/web/app/components/datasets/metadata/hooks/__tests__/use-metadata-document.spec.ts +++ b/web/app/components/datasets/metadata/hooks/__tests__/use-metadata-document.spec.ts @@ -262,7 +262,7 @@ describe('useMetadataDocument', () => { // Try to add existing item if (result.current.tempList.length > 0) { act(() => { - result.current.handleSelectMetaData(result.current.tempList[0]) + result.current.handleSelectMetaData(result.current.tempList[0]!) }) expect(result.current.tempList.length).toBe(initialLength) diff --git a/web/app/components/datasets/metadata/hooks/use-batch-edit-document-metadata.ts b/web/app/components/datasets/metadata/hooks/use-batch-edit-document-metadata.ts index b0feea8c56..16252909a2 100644 --- a/web/app/components/datasets/metadata/hooks/use-batch-edit-document-metadata.ts +++ b/web/app/components/datasets/metadata/hooks/use-batch-edit-document-metadata.ts @@ -45,7 +45,7 @@ const useBatchEditDocumentMetadata = ({ datasetId, docList, selectedDocumentIds, } } if (itemInRes && itemInRes.value !== item.value) { - idNameValue[item.id].isMultipleValue = true + idNameValue[item.id]!.isMultipleValue = true itemInRes.isMultipleValue = true itemInRes.value = null return @@ -82,7 +82,7 @@ const useBatchEditDocumentMetadata = ({ datasetId, docList, selectedDocumentIds, // Find the document in docList to get its metadata const docIndex = docList.findIndex(doc => doc.id === documentId) const oldMetadataList = docIndex >= 0 ? metaDataList[docIndex] : [] - let newMetadataList: MetadataItemWithValue[] = [...oldMetadataList, ...addedList] + let newMetadataList: MetadataItemWithValue[] = [...(oldMetadataList ?? []), ...addedList] .filter((item) => { return !removedList.find(removedItem => removedItem.id === item.id) }) diff --git a/web/app/components/datasets/metadata/hooks/use-metadata-document.ts b/web/app/components/datasets/metadata/hooks/use-metadata-document.ts index 2e9edbfa75..d70870690a 100644 --- a/web/app/components/datasets/metadata/hooks/use-metadata-document.ts +++ b/web/app/components/datasets/metadata/hooks/use-metadata-document.ts @@ -95,9 +95,9 @@ const useMetadataDocument = ({ datasetId, documentId, docDetail }: Props) => { const fieldList = Object.keys(fieldMap).map((key) => { const field = fieldMap[key] return { - id: field?.label, + id: field?.label!, type: DataType.string, - name: field?.label, + name: field?.label!, value: getTargetValue(key), } }) @@ -120,8 +120,8 @@ const useMetadataDocument = ({ datasetId, documentId, docDetail }: Props) => { startToEdit, handleSave, handleCancel, - originInfo, - technicalParameters, + originInfo: originInfo!, + technicalParameters: technicalParameters!, } } export default useMetadataDocument diff --git a/web/app/components/datasets/metadata/metadata-dataset/__tests__/dataset-metadata-drawer.spec.tsx b/web/app/components/datasets/metadata/metadata-dataset/__tests__/dataset-metadata-drawer.spec.tsx index 353a39450e..95e9c130ed 100644 --- a/web/app/components/datasets/metadata/metadata-dataset/__tests__/dataset-metadata-drawer.spec.tsx +++ b/web/app/components/datasets/metadata/metadata-dataset/__tests__/dataset-metadata-drawer.spec.tsx @@ -90,23 +90,23 @@ describe('DatasetMetadataDrawer', () => { it('should render without crashing', async () => { render() await waitFor(() => { - expect(screen.getByRole('dialog')).toBeInTheDocument() + expect(screen.getByRole('dialog'))!.toBeInTheDocument() }) }) it('should render user metadata items', async () => { render() await waitFor(() => { - expect(screen.getByText('field_one')).toBeInTheDocument() - expect(screen.getByText('field_two')).toBeInTheDocument() + expect(screen.getByText('field_one'))!.toBeInTheDocument() + expect(screen.getByText('field_two'))!.toBeInTheDocument() }) }) it('should render built-in metadata items', async () => { render() await waitFor(() => { - expect(screen.getByText('created_at')).toBeInTheDocument() - expect(screen.getByText('modified_at')).toBeInTheDocument() + expect(screen.getByText('created_at'))!.toBeInTheDocument() + expect(screen.getByText('modified_at'))!.toBeInTheDocument() }) }) @@ -121,7 +121,7 @@ describe('DatasetMetadataDrawer', () => { it('should render add metadata button', async () => { render() await waitFor(() => { - expect(screen.getByTestId('create-trigger')).toBeInTheDocument() + expect(screen.getByTestId('create-trigger'))!.toBeInTheDocument() }) }) @@ -129,7 +129,7 @@ describe('DatasetMetadataDrawer', () => { render() await waitFor(() => { const switchBtn = screen.getByRole('switch') - expect(switchBtn).toBeInTheDocument() + expect(switchBtn)!.toBeInTheDocument() }) }) }) @@ -145,7 +145,7 @@ describe('DatasetMetadataDrawer', () => { ) await waitFor(() => { - expect(screen.getByRole('dialog')).toBeInTheDocument() + expect(screen.getByRole('dialog'))!.toBeInTheDocument() }) const switchBtn = screen.getByRole('switch') @@ -160,14 +160,14 @@ describe('DatasetMetadataDrawer', () => { render() await waitFor(() => { - expect(screen.getByRole('dialog')).toBeInTheDocument() + expect(screen.getByRole('dialog'))!.toBeInTheDocument() }) const trigger = screen.getByTestId('create-trigger') fireEvent.click(trigger) await waitFor(() => { - expect(screen.getByTestId('create-modal')).toBeInTheDocument() + expect(screen.getByTestId('create-modal'))!.toBeInTheDocument() }) }) @@ -176,7 +176,7 @@ describe('DatasetMetadataDrawer', () => { render() await waitFor(() => { - expect(screen.getByRole('dialog')).toBeInTheDocument() + expect(screen.getByRole('dialog'))!.toBeInTheDocument() }) // Open create modal @@ -184,7 +184,7 @@ describe('DatasetMetadataDrawer', () => { fireEvent.click(trigger) await waitFor(() => { - expect(screen.getByTestId('create-modal')).toBeInTheDocument() + expect(screen.getByTestId('create-modal'))!.toBeInTheDocument() }) // Save new metadata @@ -208,14 +208,14 @@ describe('DatasetMetadataDrawer', () => { render() await waitFor(() => { - expect(screen.getByRole('dialog')).toBeInTheDocument() + expect(screen.getByRole('dialog'))!.toBeInTheDocument() }) // Open create modal fireEvent.click(screen.getByTestId('create-trigger')) await waitFor(() => { - expect(screen.getByTestId('create-modal')).toBeInTheDocument() + expect(screen.getByTestId('create-modal'))!.toBeInTheDocument() }) fireEvent.click(screen.getByTestId('create-save')) @@ -231,7 +231,7 @@ describe('DatasetMetadataDrawer', () => { render() await waitFor(() => { - expect(screen.getByRole('dialog')).toBeInTheDocument() + expect(screen.getByRole('dialog'))!.toBeInTheDocument() }) // Find user metadata items with group/item class (these have edit/delete icons) @@ -240,14 +240,14 @@ describe('DatasetMetadataDrawer', () => { expect(items.length).toBe(2) // 2 user metadata items // Find the hidden container with edit/delete icons - const actionsContainer = items[0].querySelector('.hidden.items-center') + const actionsContainer = items[0]!.querySelector('.hidden.items-center') expect(actionsContainer).toBeTruthy() // Find and click the first SVG (edit icon) if (actionsContainer) { const svgs = actionsContainer.querySelectorAll('svg') expect(svgs.length).toBeGreaterThan(0) - fireEvent.click(svgs[0]) + fireEvent.click(svgs[0]!) } // Wait for rename modal (contains input) @@ -262,16 +262,16 @@ describe('DatasetMetadataDrawer', () => { render() await waitFor(() => { - expect(screen.getByRole('dialog')).toBeInTheDocument() + expect(screen.getByRole('dialog'))!.toBeInTheDocument() }) // Find and click edit icon const dialog = screen.getByRole('dialog') const items = dialog.querySelectorAll('.group\\/item') - const actionsContainer = items[0].querySelector('.hidden.items-center') + const actionsContainer = items[0]!.querySelector('.hidden.items-center') if (actionsContainer) { const svgs = actionsContainer.querySelectorAll('svg') - fireEvent.click(svgs[0]) + fireEvent.click(svgs[0]!) } // Change name and save @@ -281,7 +281,7 @@ describe('DatasetMetadataDrawer', () => { }) const inputs = document.querySelectorAll('input') - fireEvent.change(inputs[0], { target: { value: 'renamed_field' } }) + fireEvent.change(inputs[0]!, { target: { value: 'renamed_field' } }) // Find and click save button fireEvent.click(screen.getByRole('button', { name: 'common.operation.save' })) @@ -303,16 +303,16 @@ describe('DatasetMetadataDrawer', () => { render() await waitFor(() => { - expect(screen.getByRole('dialog')).toBeInTheDocument() + expect(screen.getByRole('dialog'))!.toBeInTheDocument() }) // Find and click edit icon const dialog = screen.getByRole('dialog') const items = dialog.querySelectorAll('.group\\/item') - const actionsContainer = items[0].querySelector('.hidden.items-center') + const actionsContainer = items[0]!.querySelector('.hidden.items-center') if (actionsContainer) { const svgs = actionsContainer.querySelectorAll('svg') - fireEvent.click(svgs[0]) + fireEvent.click(svgs[0]!) } // Wait for modal and click cancel @@ -323,7 +323,7 @@ describe('DatasetMetadataDrawer', () => { // Change name first const inputs = document.querySelectorAll('input') - fireEvent.change(inputs[0], { target: { value: 'changed_name' } }) + fireEvent.change(inputs[0]!, { target: { value: 'changed_name' } }) // Find and click cancel button fireEvent.click(screen.getByRole('button', { name: 'common.operation.cancel' })) @@ -339,16 +339,16 @@ describe('DatasetMetadataDrawer', () => { render() await waitFor(() => { - expect(screen.getByRole('dialog')).toBeInTheDocument() + expect(screen.getByRole('dialog'))!.toBeInTheDocument() }) // Find and click edit icon const dialog = screen.getByRole('dialog') const items = dialog.querySelectorAll('.group\\/item') - const actionsContainer = items[0].querySelector('.hidden.items-center') + const actionsContainer = items[0]!.querySelector('.hidden.items-center') if (actionsContainer) { const svgs = actionsContainer.querySelectorAll('svg') - fireEvent.click(svgs[0]) + fireEvent.click(svgs[0]!) } // Wait for rename modal @@ -382,7 +382,7 @@ describe('DatasetMetadataDrawer', () => { render() await waitFor(() => { - expect(screen.getByRole('dialog')).toBeInTheDocument() + expect(screen.getByRole('dialog'))!.toBeInTheDocument() }) // Find user metadata items @@ -390,7 +390,7 @@ describe('DatasetMetadataDrawer', () => { const items = dialog.querySelectorAll('.group\\/item') // Find the delete container - const deleteContainer = items[0].querySelector('.hover\\:text-text-destructive') + const deleteContainer = items[0]!.querySelector('.hover\\:text-text-destructive') expect(deleteContainer).toBeTruthy() if (deleteContainer) { @@ -414,13 +414,13 @@ describe('DatasetMetadataDrawer', () => { render() await waitFor(() => { - expect(screen.getByRole('dialog')).toBeInTheDocument() + expect(screen.getByRole('dialog'))!.toBeInTheDocument() }) // Find and click delete icon const dialog = screen.getByRole('dialog') const items = dialog.querySelectorAll('.group\\/item') - const deleteContainer = items[0].querySelector('.hover\\:text-text-destructive') + const deleteContainer = items[0]!.querySelector('.hover\\:text-text-destructive') if (deleteContainer) { const deleteIcon = deleteContainer.querySelector('svg') if (deleteIcon) @@ -460,13 +460,13 @@ describe('DatasetMetadataDrawer', () => { render() await waitFor(() => { - expect(screen.getByRole('dialog')).toBeInTheDocument() + expect(screen.getByRole('dialog'))!.toBeInTheDocument() }) // Find and click delete icon const dialog = screen.getByRole('dialog') const items = dialog.querySelectorAll('.group\\/item') - const deleteContainer = items[0].querySelector('.hover\\:text-text-destructive') + const deleteContainer = items[0]!.querySelector('.hover\\:text-text-destructive') if (deleteContainer) { const deleteIcon = deleteContainer.querySelector('svg') if (deleteIcon) @@ -495,14 +495,14 @@ describe('DatasetMetadataDrawer', () => { it('should handle empty userMetadata', async () => { render() await waitFor(() => { - expect(screen.getByRole('dialog')).toBeInTheDocument() + expect(screen.getByRole('dialog'))!.toBeInTheDocument() }) }) it('should handle empty builtInMetadata', async () => { render() await waitFor(() => { - expect(screen.getByRole('dialog')).toBeInTheDocument() + expect(screen.getByRole('dialog'))!.toBeInTheDocument() }) }) }) @@ -514,7 +514,7 @@ describe('DatasetMetadataDrawer', () => { ) await waitFor(() => { - expect(screen.getByRole('dialog')).toBeInTheDocument() + expect(screen.getByRole('dialog'))!.toBeInTheDocument() }) const dialog = screen.getByRole('dialog') @@ -528,7 +528,7 @@ describe('DatasetMetadataDrawer', () => { ) await waitFor(() => { - expect(screen.getByRole('dialog')).toBeInTheDocument() + expect(screen.getByRole('dialog'))!.toBeInTheDocument() }) }) }) @@ -540,7 +540,7 @@ describe('DatasetMetadataDrawer', () => { ] render() await waitFor(() => { - expect(screen.getByText('field_with_underscore')).toBeInTheDocument() + expect(screen.getByText('field_with_underscore'))!.toBeInTheDocument() }) }) @@ -550,7 +550,7 @@ describe('DatasetMetadataDrawer', () => { ] render() await waitFor(() => { - expect(screen.getByText('only_field')).toBeInTheDocument() + expect(screen.getByText('only_field'))!.toBeInTheDocument() }) }) @@ -560,7 +560,7 @@ describe('DatasetMetadataDrawer', () => { ] render() await waitFor(() => { - expect(screen.getByText('created_at')).toBeInTheDocument() + expect(screen.getByText('created_at'))!.toBeInTheDocument() }) }) @@ -570,7 +570,7 @@ describe('DatasetMetadataDrawer', () => { ] render() await waitFor(() => { - expect(screen.getByText('empty_field')).toBeInTheDocument() + expect(screen.getByText('empty_field'))!.toBeInTheDocument() }) }) }) diff --git a/web/app/components/datasets/metadata/metadata-document/__tests__/index.spec.tsx b/web/app/components/datasets/metadata/metadata-document/__tests__/index.spec.tsx index ddd624a076..71f23324f6 100644 --- a/web/app/components/datasets/metadata/metadata-document/__tests__/index.spec.tsx +++ b/web/app/components/datasets/metadata/metadata-document/__tests__/index.spec.tsx @@ -99,7 +99,7 @@ describe('MetadataDocument', () => { docDetail={mockDocDetail as Parameters[0]['docDetail']} />, ) - expect(container.firstChild).toBeInTheDocument() + expect(container.firstChild)!.toBeInTheDocument() }) it('should render metadata fields when hasData is true', () => { @@ -110,8 +110,8 @@ describe('MetadataDocument', () => { docDetail={mockDocDetail as Parameters[0]['docDetail']} />, ) - expect(screen.getByText('field_one')).toBeInTheDocument() - expect(screen.getByText('field_two')).toBeInTheDocument() + expect(screen.getByText('field_one'))!.toBeInTheDocument() + expect(screen.getByText('field_two'))!.toBeInTheDocument() }) it('should render no-data state when hasData is false and not in edit mode', () => { @@ -147,8 +147,8 @@ describe('MetadataDocument', () => { />, ) - expect(screen.getByText(/save/i)).toBeInTheDocument() - expect(screen.getByText(/cancel/i)).toBeInTheDocument() + expect(screen.getByText(/save/i))!.toBeInTheDocument() + expect(screen.getByText(/cancel/i))!.toBeInTheDocument() }) it('should render built-in section when builtInEnabled is true', () => { @@ -166,7 +166,7 @@ describe('MetadataDocument', () => { />, ) - expect(screen.getByText('created_at')).toBeInTheDocument() + expect(screen.getByText('created_at'))!.toBeInTheDocument() }) it('should render divider when builtInEnabled is true', () => { @@ -185,7 +185,7 @@ describe('MetadataDocument', () => { ) const divider = container.querySelector('.bg-linear-to-r') - expect(divider).toBeInTheDocument() + expect(divider)!.toBeInTheDocument() }) it('should render origin info section', () => { @@ -202,7 +202,7 @@ describe('MetadataDocument', () => { />, ) - expect(screen.getByText('source')).toBeInTheDocument() + expect(screen.getByText('source'))!.toBeInTheDocument() }) it('should render technical parameters section', () => { @@ -219,7 +219,7 @@ describe('MetadataDocument', () => { />, ) - expect(screen.getByText('word_count')).toBeInTheDocument() + expect(screen.getByText('word_count'))!.toBeInTheDocument() }) it('should render all sections together', () => { @@ -239,10 +239,10 @@ describe('MetadataDocument', () => { />, ) - expect(screen.getByText('field_one')).toBeInTheDocument() - expect(screen.getByText('created_at')).toBeInTheDocument() - expect(screen.getByText('source')).toBeInTheDocument() - expect(screen.getByText('word_count')).toBeInTheDocument() + expect(screen.getByText('field_one'))!.toBeInTheDocument() + expect(screen.getByText('created_at'))!.toBeInTheDocument() + expect(screen.getByText('source'))!.toBeInTheDocument() + expect(screen.getByText('word_count'))!.toBeInTheDocument() }) }) @@ -255,7 +255,7 @@ describe('MetadataDocument', () => { docDetail={mockDocDetail as Parameters[0]['docDetail']} />, ) - expect(screen.getByText(/edit/i)).toBeInTheDocument() + expect(screen.getByText(/edit/i))!.toBeInTheDocument() }) it('should call startToEdit when edit button is clicked', () => { @@ -362,8 +362,9 @@ describe('MetadataDocument', () => { ) // Should show save/cancel buttons - expect(screen.getByText(/save/i)).toBeInTheDocument() - expect(screen.getByText(/cancel/i)).toBeInTheDocument() + // Should show save/cancel buttons + expect(screen.getByText(/save/i))!.toBeInTheDocument() + expect(screen.getByText(/cancel/i))!.toBeInTheDocument() }) }) @@ -386,7 +387,7 @@ describe('MetadataDocument', () => { const inputs = container.querySelectorAll('input') if (inputs.length > 0) { - fireEvent.change(inputs[0], { target: { value: 'new value' } }) + fireEvent.change(inputs[0]!, { target: { value: 'new value' } }) await waitFor(() => { expect(setTempList).toHaveBeenCalled() @@ -454,7 +455,7 @@ describe('MetadataDocument', () => { const inputs = container.querySelectorAll('input') if (inputs.length > 0) { - fireEvent.change(inputs[0], { target: { value: 'updated' } }) + fireEvent.change(inputs[0]!, { target: { value: 'updated' } }) await waitFor(() => { expect(setTempList).toHaveBeenCalled() }) @@ -483,7 +484,7 @@ describe('MetadataDocument', () => { expect(deleteContainers.length).toBeGreaterThan(0) if (deleteContainers.length > 0) { - const deleteIcon = deleteContainers[0].querySelector('svg') + const deleteIcon = deleteContainers[0]!.querySelector('svg') if (deleteIcon) fireEvent.click(deleteIcon) @@ -504,7 +505,7 @@ describe('MetadataDocument', () => { className="custom-class" />, ) - expect(container.firstChild).toHaveClass('custom-class') + expect(container.firstChild)!.toHaveClass('custom-class') }) it('should use tempList when in edit mode', () => { @@ -524,7 +525,7 @@ describe('MetadataDocument', () => { />, ) - expect(screen.getByText('temp_field')).toBeInTheDocument() + expect(screen.getByText('temp_field'))!.toBeInTheDocument() }) it('should use list when not in edit mode', () => { @@ -536,8 +537,8 @@ describe('MetadataDocument', () => { />, ) - expect(screen.getByText('field_one')).toBeInTheDocument() - expect(screen.getByText('field_two')).toBeInTheDocument() + expect(screen.getByText('field_one'))!.toBeInTheDocument() + expect(screen.getByText('field_two'))!.toBeInTheDocument() }) it('should pass datasetId to child components', () => { @@ -549,7 +550,8 @@ describe('MetadataDocument', () => { />, ) // Component should render without errors - expect(screen.getByText('field_one')).toBeInTheDocument() + // Component should render without errors + expect(screen.getByText('field_one'))!.toBeInTheDocument() }) }) @@ -588,6 +590,37 @@ describe('MetadataDocument', () => { />, ) + // NoData component should not be rendered + // NoData component should not be rendered + // NoData component should not be rendered + // NoData component should not be rendered + // NoData component should not be rendered + // NoData component should not be rendered + // NoData component should not be rendered + // NoData component should not be rendered + // NoData component should not be rendered + // NoData component should not be rendered + // NoData component should not be rendered + // NoData component should not be rendered + // NoData component should not be rendered + // NoData component should not be rendered + // NoData component should not be rendered + // NoData component should not be rendered + // NoData component should not be rendered + // NoData component should not be rendered + // NoData component should not be rendered + // NoData component should not be rendered + // NoData component should not be rendered + // NoData component should not be rendered + // NoData component should not be rendered + // NoData component should not be rendered + // NoData component should not be rendered + // NoData component should not be rendered + // NoData component should not be rendered + // NoData component should not be rendered + // NoData component should not be rendered + // NoData component should not be rendered + // NoData component should not be rendered // NoData component should not be rendered expect(screen.queryByText(/start/i)).not.toBeInTheDocument() }) @@ -607,6 +640,37 @@ describe('MetadataDocument', () => { />, ) + // headerRight should be null/undefined + // headerRight should be null/undefined + // headerRight should be null/undefined + // headerRight should be null/undefined + // headerRight should be null/undefined + // headerRight should be null/undefined + // headerRight should be null/undefined + // headerRight should be null/undefined + // headerRight should be null/undefined + // headerRight should be null/undefined + // headerRight should be null/undefined + // headerRight should be null/undefined + // headerRight should be null/undefined + // headerRight should be null/undefined + // headerRight should be null/undefined + // headerRight should be null/undefined + // headerRight should be null/undefined + // headerRight should be null/undefined + // headerRight should be null/undefined + // headerRight should be null/undefined + // headerRight should be null/undefined + // headerRight should be null/undefined + // headerRight should be null/undefined + // headerRight should be null/undefined + // headerRight should be null/undefined + // headerRight should be null/undefined + // headerRight should be null/undefined + // headerRight should be null/undefined + // headerRight should be null/undefined + // headerRight should be null/undefined + // headerRight should be null/undefined // headerRight should be null/undefined expect(screen.queryByText(/^edit$/i)).not.toBeInTheDocument() }) @@ -628,7 +692,7 @@ describe('MetadataDocument', () => { docDetail={mockDocDetail as Parameters[0]['docDetail']} />, ) - expect(container.firstChild).toBeInTheDocument() + expect(container.firstChild)!.toBeInTheDocument() }) it('should render correctly with minimal props', () => { @@ -639,7 +703,7 @@ describe('MetadataDocument', () => { docDetail={mockDocDetail as Parameters[0]['docDetail']} />, ) - expect(container.firstChild).toBeInTheDocument() + expect(container.firstChild)!.toBeInTheDocument() }) it('should handle switching between view and edit mode', () => { @@ -651,7 +715,7 @@ describe('MetadataDocument', () => { />, ) - expect(screen.getByText(/edit/i)).toBeInTheDocument() + expect(screen.getByText(/edit/i))!.toBeInTheDocument() unmount() @@ -668,8 +732,8 @@ describe('MetadataDocument', () => { />, ) - expect(screen.getByText(/save/i)).toBeInTheDocument() - expect(screen.getByText(/cancel/i)).toBeInTheDocument() + expect(screen.getByText(/save/i))!.toBeInTheDocument() + expect(screen.getByText(/cancel/i))!.toBeInTheDocument() }) it('should handle multiple items in all sections', () => { @@ -702,11 +766,11 @@ describe('MetadataDocument', () => { />, ) - expect(screen.getByText('user_field_1')).toBeInTheDocument() - expect(screen.getByText('user_field_2')).toBeInTheDocument() - expect(screen.getByText('created_at')).toBeInTheDocument() - expect(screen.getByText('source')).toBeInTheDocument() - expect(screen.getByText('word_count')).toBeInTheDocument() + expect(screen.getByText('user_field_1'))!.toBeInTheDocument() + expect(screen.getByText('user_field_2'))!.toBeInTheDocument() + expect(screen.getByText('created_at'))!.toBeInTheDocument() + expect(screen.getByText('source'))!.toBeInTheDocument() + expect(screen.getByText('word_count'))!.toBeInTheDocument() }) it('should handle null values in metadata', () => { @@ -725,7 +789,7 @@ describe('MetadataDocument', () => { />, ) - expect(screen.getByText('null_field')).toBeInTheDocument() + expect(screen.getByText('null_field'))!.toBeInTheDocument() }) it('should handle undefined values in metadata', () => { @@ -744,7 +808,7 @@ describe('MetadataDocument', () => { />, ) - expect(screen.getByText('undefined_field')).toBeInTheDocument() + expect(screen.getByText('undefined_field'))!.toBeInTheDocument() }) }) }) diff --git a/web/app/components/datasets/metadata/metadata-document/__tests__/info-group.spec.tsx b/web/app/components/datasets/metadata/metadata-document/__tests__/info-group.spec.tsx index d783b882a8..c176979a5a 100644 --- a/web/app/components/datasets/metadata/metadata-document/__tests__/info-group.spec.tsx +++ b/web/app/components/datasets/metadata/metadata-document/__tests__/info-group.spec.tsx @@ -90,14 +90,14 @@ describe('InfoGroup', () => { const { container } = render( , ) - expect(container.firstChild).toBeInTheDocument() + expect(container.firstChild)!.toBeInTheDocument() }) it('should render title when provided', () => { render( , ) - expect(screen.getByText('Test Title')).toBeInTheDocument() + expect(screen.getByText('Test Title'))!.toBeInTheDocument() }) it('should not render header when noHeader is true', () => { @@ -126,7 +126,7 @@ describe('InfoGroup', () => { ) // Tooltip icon should be present const tooltipIcon = screen.getByText('Test').closest('.flex')?.querySelector('svg') - expect(tooltipIcon).toBeInTheDocument() + expect(tooltipIcon)!.toBeInTheDocument() }) it('should render headerRight content', () => { @@ -138,7 +138,7 @@ describe('InfoGroup', () => { headerRight={} />, ) - expect(screen.getByTestId('header-right-btn')).toBeInTheDocument() + expect(screen.getByTestId('header-right-btn'))!.toBeInTheDocument() }) }) @@ -147,7 +147,7 @@ describe('InfoGroup', () => { render( , ) - expect(screen.getByTestId('add-metadata-btn')).toBeInTheDocument() + expect(screen.getByTestId('add-metadata-btn'))!.toBeInTheDocument() }) it('should not render add metadata button when isEdit is false', () => { @@ -182,7 +182,7 @@ describe('InfoGroup', () => { ) const inputs = screen.getAllByTestId('input-combined') - fireEvent.change(inputs[0], { target: { value: 'New Value' } }) + fireEvent.change(inputs[0]!, { target: { value: 'New Value' } }) expect(handleChange).toHaveBeenCalled() }) @@ -196,7 +196,7 @@ describe('InfoGroup', () => { // Find delete icons (RiDeleteBinLine SVGs inside cursor-pointer divs) const deleteButtons = container.querySelectorAll('svg.size-4') if (deleteButtons.length > 0) - fireEvent.click(deleteButtons[0]) + fireEvent.click(deleteButtons[0]!) expect(handleDelete).toHaveBeenCalled() }) @@ -239,7 +239,8 @@ describe('InfoGroup', () => { fireEvent.click(screen.getByTestId('manage-action')) // The onManage callback triggers the navigation - expect(screen.getByTestId('manage-action')).toBeInTheDocument() + // The onManage callback triggers the navigation + expect(screen.getByTestId('manage-action'))!.toBeInTheDocument() }) }) @@ -248,7 +249,7 @@ describe('InfoGroup', () => { const { container } = render( , ) - expect(container.firstChild).toHaveClass('custom-class') + expect(container.firstChild)!.toHaveClass('custom-class') }) it('should apply contentClassName', () => { @@ -256,7 +257,7 @@ describe('InfoGroup', () => { , ) const contentDiv = container.querySelector('.content-custom') - expect(contentDiv).toBeInTheDocument() + expect(contentDiv)!.toBeInTheDocument() }) it('should use uppercase title by default', () => { @@ -264,7 +265,7 @@ describe('InfoGroup', () => { , ) const titleElement = screen.getByText('Test Title') - expect(titleElement).toHaveClass('system-xs-semibold-uppercase') + expect(titleElement)!.toHaveClass('system-xs-semibold-uppercase') }) it('should not use uppercase when uppercaseTitle is false', () => { @@ -272,7 +273,7 @@ describe('InfoGroup', () => { , ) const titleElement = screen.getByText('Test Title') - expect(titleElement).toHaveClass('system-md-semibold') + expect(titleElement)!.toHaveClass('system-md-semibold') }) }) @@ -284,7 +285,7 @@ describe('InfoGroup', () => { render( , ) - expect(screen.getByText('Test Value')).toBeInTheDocument() + expect(screen.getByText('Test Value'))!.toBeInTheDocument() }) it('should display number value', () => { @@ -294,7 +295,7 @@ describe('InfoGroup', () => { render( , ) - expect(screen.getByText('123')).toBeInTheDocument() + expect(screen.getByText('123'))!.toBeInTheDocument() }) it('should format time value', () => { @@ -305,7 +306,8 @@ describe('InfoGroup', () => { , ) // The mock formatTime returns formatted date - expect(screen.getByTestId('field-content')).toBeInTheDocument() + // The mock formatTime returns formatted date + expect(screen.getByTestId('field-content'))!.toBeInTheDocument() }) }) @@ -314,7 +316,7 @@ describe('InfoGroup', () => { const { container } = render( , ) - expect(container.firstChild).toBeInTheDocument() + expect(container.firstChild)!.toBeInTheDocument() }) it('should handle null value in list', () => { @@ -324,7 +326,7 @@ describe('InfoGroup', () => { render( , ) - expect(screen.getByTestId('field')).toBeInTheDocument() + expect(screen.getByTestId('field'))!.toBeInTheDocument() }) it('should handle items with built-in id', () => { @@ -334,7 +336,7 @@ describe('InfoGroup', () => { render( , ) - expect(screen.getByTestId('field')).toBeInTheDocument() + expect(screen.getByTestId('field'))!.toBeInTheDocument() }) }) }) diff --git a/web/app/components/datasets/preview/__tests__/container.spec.tsx b/web/app/components/datasets/preview/__tests__/container.spec.tsx index 86f6e3f85b..473d91f857 100644 --- a/web/app/components/datasets/preview/__tests__/container.spec.tsx +++ b/web/app/components/datasets/preview/__tests__/container.spec.tsx @@ -12,16 +12,16 @@ describe('PreviewContainer', () => { it('should render header content in a header element', () => { render(Header Title}>Body) - expect(screen.getByText('Header Title')).toBeInTheDocument() + expect(screen.getByText('Header Title'))!.toBeInTheDocument() const headerEl = screen.getByText('Header Title').closest('header') - expect(headerEl).toBeInTheDocument() + expect(headerEl)!.toBeInTheDocument() }) it('should render children in a main element', () => { render(Main content) const mainEl = screen.getByRole('main') - expect(mainEl).toHaveTextContent('Main content') + expect(mainEl)!.toHaveTextContent('Main content') }) it('should render both header and children simultaneously', () => { @@ -31,14 +31,14 @@ describe('PreviewContainer', () => { , ) - expect(screen.getByText('My Header')).toBeInTheDocument() - expect(screen.getByText('Body paragraph')).toBeInTheDocument() + expect(screen.getByText('My Header'))!.toBeInTheDocument() + expect(screen.getByText('Body paragraph'))!.toBeInTheDocument() }) it('should render without children', () => { render() - expect(screen.getByRole('main')).toBeInTheDocument() + expect(screen.getByRole('main'))!.toBeInTheDocument() expect(screen.getByRole('main').childElementCount).toBe(0) }) }) @@ -49,7 +49,7 @@ describe('PreviewContainer', () => { Content, ) - expect(container.firstElementChild).toHaveClass('outer-class') + expect(container.firstElementChild)!.toHaveClass('outer-class') }) it('should apply mainClassName to the main element', () => { @@ -58,9 +58,10 @@ describe('PreviewContainer', () => { ) const mainEl = screen.getByRole('main') - expect(mainEl).toHaveClass('custom-main') + expect(mainEl)!.toHaveClass('custom-main') // Default classes should still be present - expect(mainEl).toHaveClass('w-full', 'grow', 'overflow-y-auto', 'px-6', 'py-5') + // Default classes should still be present + expect(mainEl)!.toHaveClass('w-full', 'grow', 'overflow-y-auto', 'px-6', 'py-5') }) it('should forward ref to the inner container div', () => { @@ -70,7 +71,7 @@ describe('PreviewContainer', () => { ) expect(ref).toHaveBeenCalled() - const refArg = ref.mock.calls[0][0] + const refArg = ref.mock.calls[0]![0] expect(refArg).toBeInstanceOf(HTMLDivElement) }) @@ -82,7 +83,7 @@ describe('PreviewContainer', () => { ) const inner = screen.getByTestId('inner-container') - expect(inner).toHaveAttribute('id', 'container-1') + expect(inner)!.toHaveAttribute('id', 'container-1') }) it('should render ReactNode as header', () => { @@ -92,8 +93,8 @@ describe('PreviewContainer', () => { , ) - expect(screen.getByTestId('complex-header')).toBeInTheDocument() - expect(screen.getByText('Complex')).toBeInTheDocument() + expect(screen.getByTestId('complex-header'))!.toBeInTheDocument() + expect(screen.getByText('Complex'))!.toBeInTheDocument() }) }) @@ -103,7 +104,7 @@ describe('PreviewContainer', () => { render(Content) const headerEl = screen.getByText('Header').closest('header') - expect(headerEl).toHaveClass('border-b', 'border-divider-subtle') + expect(headerEl)!.toHaveClass('border-b', 'border-divider-subtle') }) it('should have inner div with flex column layout', () => { @@ -112,13 +113,13 @@ describe('PreviewContainer', () => { ) const inner = screen.getByTestId('inner') - expect(inner).toHaveClass('flex', 'h-full', 'w-full', 'flex-col') + expect(inner)!.toHaveClass('flex', 'h-full', 'w-full', 'flex-col') }) it('should have main with overflow-y-auto for scrolling', () => { render(Content) - expect(screen.getByRole('main')).toHaveClass('overflow-y-auto') + expect(screen.getByRole('main'))!.toHaveClass('overflow-y-auto') }) }) @@ -134,13 +135,13 @@ describe('PreviewContainer', () => { render(Content) const headerEl = screen.getByRole('banner') - expect(headerEl).toBeInTheDocument() + expect(headerEl)!.toBeInTheDocument() }) it('should render with null children', () => { render({null}) - expect(screen.getByRole('main')).toBeInTheDocument() + expect(screen.getByRole('main'))!.toBeInTheDocument() }) it('should render with multiple children', () => { @@ -152,9 +153,9 @@ describe('PreviewContainer', () => { , ) - expect(screen.getByText('Child 1')).toBeInTheDocument() - expect(screen.getByText('Child 2')).toBeInTheDocument() - expect(screen.getByText('Child 3')).toBeInTheDocument() + expect(screen.getByText('Child 1'))!.toBeInTheDocument() + expect(screen.getByText('Child 2'))!.toBeInTheDocument() + expect(screen.getByText('Child 3'))!.toBeInTheDocument() }) it('should not crash on re-render with different props', () => { @@ -166,8 +167,8 @@ describe('PreviewContainer', () => { Content B, ) - expect(screen.getByText('Second')).toBeInTheDocument() - expect(screen.getByText('Content B')).toBeInTheDocument() + expect(screen.getByText('Second'))!.toBeInTheDocument() + expect(screen.getByText('Content B'))!.toBeInTheDocument() }) }) }) diff --git a/web/app/components/datasets/rename-modal/__tests__/index.spec.tsx b/web/app/components/datasets/rename-modal/__tests__/index.spec.tsx index dbae959d8d..600e6c3434 100644 --- a/web/app/components/datasets/rename-modal/__tests__/index.spec.tsx +++ b/web/app/components/datasets/rename-modal/__tests__/index.spec.tsx @@ -138,43 +138,46 @@ describe('RenameDatasetModal', () => { it('should render without crashing', () => { render() // Check title is rendered (translation mock returns 'datasetSettings.title') - expect(screen.getByText('datasetSettings.title')).toBeInTheDocument() + // Check title is rendered (translation mock returns 'datasetSettings.title') + expect(screen.getByText('datasetSettings.title'))!.toBeInTheDocument() }) it('should render modal when show is true', () => { render() - expect(screen.getByText('datasetSettings.title')).toBeInTheDocument() + expect(screen.getByText('datasetSettings.title'))!.toBeInTheDocument() }) it('should render name input with dataset name', () => { render() const nameInput = screen.getByDisplayValue('Test Dataset') - expect(nameInput).toBeInTheDocument() + expect(nameInput)!.toBeInTheDocument() }) it('should render description textarea with dataset description', () => { render() const descriptionTextarea = screen.getByDisplayValue('Test description') - expect(descriptionTextarea).toBeInTheDocument() + expect(descriptionTextarea)!.toBeInTheDocument() }) it('should render cancel and save buttons', () => { render() - expect(screen.getByText('common.operation.cancel')).toBeInTheDocument() - expect(screen.getByText('common.operation.save')).toBeInTheDocument() + expect(screen.getByText('common.operation.cancel'))!.toBeInTheDocument() + expect(screen.getByText('common.operation.save'))!.toBeInTheDocument() }) it('should render close icon button', () => { render() // The modal renders with title and other elements // The close functionality is tested in user interactions - expect(screen.getByText('datasetSettings.title')).toBeInTheDocument() + // The modal renders with title and other elements + // The close functionality is tested in user interactions + expect(screen.getByText('datasetSettings.title'))!.toBeInTheDocument() }) it('should render form labels', () => { render() - expect(screen.getByText('datasetSettings.form.name')).toBeInTheDocument() - expect(screen.getByText('datasetSettings.form.desc')).toBeInTheDocument() + expect(screen.getByText('datasetSettings.form.name'))!.toBeInTheDocument() + expect(screen.getByText('datasetSettings.form.desc'))!.toBeInTheDocument() }) }) @@ -182,13 +185,13 @@ describe('RenameDatasetModal', () => { it('should render with emoji icon dataset', () => { const dataset = createMockDataset() render() - expect(screen.getByDisplayValue('Test Dataset')).toBeInTheDocument() + expect(screen.getByDisplayValue('Test Dataset'))!.toBeInTheDocument() }) it('should render with image icon dataset', () => { const dataset = createMockDatasetWithImageIcon() render() - expect(screen.getByDisplayValue('Test Dataset')).toBeInTheDocument() + expect(screen.getByDisplayValue('Test Dataset'))!.toBeInTheDocument() }) it('should render with empty description', () => { @@ -196,30 +199,30 @@ describe('RenameDatasetModal', () => { render() // Find the textarea by its placeholder const descriptionTextarea = screen.getByPlaceholderText('datasetSettings.form.descPlaceholder') - expect(descriptionTextarea).toHaveValue('') + expect(descriptionTextarea)!.toHaveValue('') }) it('should render with external knowledge dataset', () => { const dataset = createMockExternalDataset() render() - expect(screen.getByDisplayValue('Test Dataset')).toBeInTheDocument() + expect(screen.getByDisplayValue('Test Dataset'))!.toBeInTheDocument() }) it('should handle undefined onSuccess callback', () => { render() - expect(screen.getByDisplayValue('Test Dataset')).toBeInTheDocument() + expect(screen.getByDisplayValue('Test Dataset'))!.toBeInTheDocument() }) }) describe('State Management', () => { it('should initialize name state with dataset name', () => { render() - expect(screen.getByDisplayValue('Test Dataset')).toBeInTheDocument() + expect(screen.getByDisplayValue('Test Dataset'))!.toBeInTheDocument() }) it('should initialize description state with dataset description', () => { render() - expect(screen.getByDisplayValue('Test description')).toBeInTheDocument() + expect(screen.getByDisplayValue('Test description'))!.toBeInTheDocument() }) it('should update name state when input changes', () => { @@ -228,7 +231,7 @@ describe('RenameDatasetModal', () => { fireEvent.change(nameInput, { target: { value: 'New Dataset Name' } }) - expect(screen.getByDisplayValue('New Dataset Name')).toBeInTheDocument() + expect(screen.getByDisplayValue('New Dataset Name'))!.toBeInTheDocument() }) it('should update description state when textarea changes', () => { @@ -237,7 +240,7 @@ describe('RenameDatasetModal', () => { fireEvent.change(descriptionTextarea, { target: { value: 'New description' } }) - expect(screen.getByDisplayValue('New description')).toBeInTheDocument() + expect(screen.getByDisplayValue('New description'))!.toBeInTheDocument() }) it('should clear name when input is cleared', () => { @@ -246,7 +249,7 @@ describe('RenameDatasetModal', () => { fireEvent.change(nameInput, { target: { value: '' } }) - expect(nameInput).toHaveValue('') + expect(nameInput)!.toHaveValue('') }) it('should handle special characters in name', () => { @@ -255,7 +258,7 @@ describe('RenameDatasetModal', () => { fireEvent.change(nameInput, { target: { value: 'Dataset ' } }) - expect(screen.getByDisplayValue('Dataset ')).toBeInTheDocument() + expect(screen.getByDisplayValue('Dataset '))!.toBeInTheDocument() }) it('should handle very long name input', () => { @@ -265,7 +268,7 @@ describe('RenameDatasetModal', () => { fireEvent.change(nameInput, { target: { value: longName } }) - expect(screen.getByDisplayValue(longName)).toBeInTheDocument() + expect(screen.getByDisplayValue(longName))!.toBeInTheDocument() }) it('should handle multiline description', () => { @@ -276,7 +279,8 @@ describe('RenameDatasetModal', () => { fireEvent.change(descriptionTextarea, { target: { value: multilineDesc } }) // Verify the textarea contains the multiline value - expect(descriptionTextarea).toHaveValue(multilineDesc) + // Verify the textarea contains the multiline value + expect(descriptionTextarea)!.toHaveValue(multilineDesc) }) }) @@ -338,7 +342,7 @@ describe('RenameDatasetModal', () => { }) await waitFor(() => { - expect(saveButton).toBeDisabled() + expect(saveButton)!.toBeDisabled() }) // Resolve the promise to clean up @@ -354,6 +358,37 @@ describe('RenameDatasetModal', () => { // Verify the input can receive focus nameInput.focus() + // Just verify the element is focusable (don't check activeElement as it may differ in test environment) + // Just verify the element is focusable (don't check activeElement as it may differ in test environment) + // Just verify the element is focusable (don't check activeElement as it may differ in test environment) + // Just verify the element is focusable (don't check activeElement as it may differ in test environment) + // Just verify the element is focusable (don't check activeElement as it may differ in test environment) + // Just verify the element is focusable (don't check activeElement as it may differ in test environment) + // Just verify the element is focusable (don't check activeElement as it may differ in test environment) + // Just verify the element is focusable (don't check activeElement as it may differ in test environment) + // Just verify the element is focusable (don't check activeElement as it may differ in test environment) + // Just verify the element is focusable (don't check activeElement as it may differ in test environment) + // Just verify the element is focusable (don't check activeElement as it may differ in test environment) + // Just verify the element is focusable (don't check activeElement as it may differ in test environment) + // Just verify the element is focusable (don't check activeElement as it may differ in test environment) + // Just verify the element is focusable (don't check activeElement as it may differ in test environment) + // Just verify the element is focusable (don't check activeElement as it may differ in test environment) + // Just verify the element is focusable (don't check activeElement as it may differ in test environment) + // Just verify the element is focusable (don't check activeElement as it may differ in test environment) + // Just verify the element is focusable (don't check activeElement as it may differ in test environment) + // Just verify the element is focusable (don't check activeElement as it may differ in test environment) + // Just verify the element is focusable (don't check activeElement as it may differ in test environment) + // Just verify the element is focusable (don't check activeElement as it may differ in test environment) + // Just verify the element is focusable (don't check activeElement as it may differ in test environment) + // Just verify the element is focusable (don't check activeElement as it may differ in test environment) + // Just verify the element is focusable (don't check activeElement as it may differ in test environment) + // Just verify the element is focusable (don't check activeElement as it may differ in test environment) + // Just verify the element is focusable (don't check activeElement as it may differ in test environment) + // Just verify the element is focusable (don't check activeElement as it may differ in test environment) + // Just verify the element is focusable (don't check activeElement as it may differ in test environment) + // Just verify the element is focusable (don't check activeElement as it may differ in test environment) + // Just verify the element is focusable (don't check activeElement as it may differ in test environment) + // Just verify the element is focusable (don't check activeElement as it may differ in test environment) // Just verify the element is focusable (don't check activeElement as it may differ in test environment) expect(nameInput).not.toBeDisabled() }) @@ -365,6 +400,37 @@ describe('RenameDatasetModal', () => { // Verify the textarea can receive focus descriptionTextarea.focus() + // Just verify the element is focusable + // Just verify the element is focusable + // Just verify the element is focusable + // Just verify the element is focusable + // Just verify the element is focusable + // Just verify the element is focusable + // Just verify the element is focusable + // Just verify the element is focusable + // Just verify the element is focusable + // Just verify the element is focusable + // Just verify the element is focusable + // Just verify the element is focusable + // Just verify the element is focusable + // Just verify the element is focusable + // Just verify the element is focusable + // Just verify the element is focusable + // Just verify the element is focusable + // Just verify the element is focusable + // Just verify the element is focusable + // Just verify the element is focusable + // Just verify the element is focusable + // Just verify the element is focusable + // Just verify the element is focusable + // Just verify the element is focusable + // Just verify the element is focusable + // Just verify the element is focusable + // Just verify the element is focusable + // Just verify the element is focusable + // Just verify the element is focusable + // Just verify the element is focusable + // Just verify the element is focusable // Just verify the element is focusable expect(descriptionTextarea).not.toBeDisabled() }) @@ -429,7 +495,7 @@ describe('RenameDatasetModal', () => { await waitFor(() => { expect(mockUpdateDatasetSetting).toHaveBeenCalled() - const callArgs = mockUpdateDatasetSetting.mock.calls[0][0] + const callArgs = mockUpdateDatasetSetting.mock.calls[0]![0] expect(callArgs.body.external_knowledge_id).toBeUndefined() expect(callArgs.body.external_knowledge_api_id).toBeUndefined() }) @@ -604,6 +670,37 @@ describe('RenameDatasetModal', () => { expect(mockToast.error).toHaveBeenCalled() }) + // Save button should be enabled again + // Save button should be enabled again + // Save button should be enabled again + // Save button should be enabled again + // Save button should be enabled again + // Save button should be enabled again + // Save button should be enabled again + // Save button should be enabled again + // Save button should be enabled again + // Save button should be enabled again + // Save button should be enabled again + // Save button should be enabled again + // Save button should be enabled again + // Save button should be enabled again + // Save button should be enabled again + // Save button should be enabled again + // Save button should be enabled again + // Save button should be enabled again + // Save button should be enabled again + // Save button should be enabled again + // Save button should be enabled again + // Save button should be enabled again + // Save button should be enabled again + // Save button should be enabled again + // Save button should be enabled again + // Save button should be enabled again + // Save button should be enabled again + // Save button should be enabled again + // Save button should be enabled again + // Save button should be enabled again + // Save button should be enabled again // Save button should be enabled again expect(saveButton).not.toBeDisabled() }) @@ -660,8 +757,10 @@ describe('RenameDatasetModal', () => { render() // The modal should render with name label and input // AppIcon is rendered alongside the name input - expect(screen.getByText('datasetSettings.form.name')).toBeInTheDocument() - expect(screen.getByDisplayValue('Test Dataset')).toBeInTheDocument() + // The modal should render with name label and input + // AppIcon is rendered alongside the name input + expect(screen.getByText('datasetSettings.form.name'))!.toBeInTheDocument() + expect(screen.getByDisplayValue('Test Dataset'))!.toBeInTheDocument() }) it('should initialize icon state from dataset', () => { @@ -669,14 +768,16 @@ describe('RenameDatasetModal', () => { render() // The component initializes with the dataset's icon_info // This is verified by checking the form renders correctly - expect(screen.getByDisplayValue('Test Dataset')).toBeInTheDocument() + // The component initializes with the dataset's icon_info + // This is verified by checking the form renders correctly + expect(screen.getByDisplayValue('Test Dataset'))!.toBeInTheDocument() }) it('should initialize icon state from image icon dataset', () => { // Test with image icon - this triggers the icon_type === 'image' branch const imageDataset = createMockDatasetWithImageIcon() render() - expect(screen.getByDisplayValue('Test Dataset')).toBeInTheDocument() + expect(screen.getByDisplayValue('Test Dataset'))!.toBeInTheDocument() // The component should render successfully with image icon dataset }) @@ -734,6 +835,37 @@ describe('RenameDatasetModal', () => { it('should open icon picker when app icon is clicked (handleOpenAppIconPicker)', async () => { render() + // Initially picker should not be visible + // Initially picker should not be visible + // Initially picker should not be visible + // Initially picker should not be visible + // Initially picker should not be visible + // Initially picker should not be visible + // Initially picker should not be visible + // Initially picker should not be visible + // Initially picker should not be visible + // Initially picker should not be visible + // Initially picker should not be visible + // Initially picker should not be visible + // Initially picker should not be visible + // Initially picker should not be visible + // Initially picker should not be visible + // Initially picker should not be visible + // Initially picker should not be visible + // Initially picker should not be visible + // Initially picker should not be visible + // Initially picker should not be visible + // Initially picker should not be visible + // Initially picker should not be visible + // Initially picker should not be visible + // Initially picker should not be visible + // Initially picker should not be visible + // Initially picker should not be visible + // Initially picker should not be visible + // Initially picker should not be visible + // Initially picker should not be visible + // Initially picker should not be visible + // Initially picker should not be visible // Initially picker should not be visible expect(screen.queryByTestId('app-icon-picker')).not.toBeInTheDocument() @@ -743,7 +875,8 @@ describe('RenameDatasetModal', () => { }) // Picker should now be visible - expect(screen.getByTestId('app-icon-picker')).toBeInTheDocument() + // Picker should now be visible + expect(screen.getByTestId('app-icon-picker'))!.toBeInTheDocument() }) it('should select emoji icon and close picker (handleSelectAppIcon)', async () => { @@ -761,6 +894,37 @@ describe('RenameDatasetModal', () => { fireEvent.click(selectEmojiBtn) }) + // Picker should close after selection + // Picker should close after selection + // Picker should close after selection + // Picker should close after selection + // Picker should close after selection + // Picker should close after selection + // Picker should close after selection + // Picker should close after selection + // Picker should close after selection + // Picker should close after selection + // Picker should close after selection + // Picker should close after selection + // Picker should close after selection + // Picker should close after selection + // Picker should close after selection + // Picker should close after selection + // Picker should close after selection + // Picker should close after selection + // Picker should close after selection + // Picker should close after selection + // Picker should close after selection + // Picker should close after selection + // Picker should close after selection + // Picker should close after selection + // Picker should close after selection + // Picker should close after selection + // Picker should close after selection + // Picker should close after selection + // Picker should close after selection + // Picker should close after selection + // Picker should close after selection // Picker should close after selection expect(screen.queryByTestId('app-icon-picker')).not.toBeInTheDocument() @@ -800,6 +964,37 @@ describe('RenameDatasetModal', () => { fireEvent.click(selectImageBtn) }) + // Picker should close after selection + // Picker should close after selection + // Picker should close after selection + // Picker should close after selection + // Picker should close after selection + // Picker should close after selection + // Picker should close after selection + // Picker should close after selection + // Picker should close after selection + // Picker should close after selection + // Picker should close after selection + // Picker should close after selection + // Picker should close after selection + // Picker should close after selection + // Picker should close after selection + // Picker should close after selection + // Picker should close after selection + // Picker should close after selection + // Picker should close after selection + // Picker should close after selection + // Picker should close after selection + // Picker should close after selection + // Picker should close after selection + // Picker should close after selection + // Picker should close after selection + // Picker should close after selection + // Picker should close after selection + // Picker should close after selection + // Picker should close after selection + // Picker should close after selection + // Picker should close after selection // Picker should close after selection expect(screen.queryByTestId('app-icon-picker')).not.toBeInTheDocument() @@ -839,6 +1034,37 @@ describe('RenameDatasetModal', () => { fireEvent.click(closeBtn) }) + // Picker should close + // Picker should close + // Picker should close + // Picker should close + // Picker should close + // Picker should close + // Picker should close + // Picker should close + // Picker should close + // Picker should close + // Picker should close + // Picker should close + // Picker should close + // Picker should close + // Picker should close + // Picker should close + // Picker should close + // Picker should close + // Picker should close + // Picker should close + // Picker should close + // Picker should close + // Picker should close + // Picker should close + // Picker should close + // Picker should close + // Picker should close + // Picker should close + // Picker should close + // Picker should close + // Picker should close // Picker should close expect(screen.queryByTestId('app-icon-picker')).not.toBeInTheDocument() @@ -876,7 +1102,7 @@ describe('RenameDatasetModal', () => { }) render() - expect(screen.getByDisplayValue('Test Dataset')).toBeInTheDocument() + expect(screen.getByDisplayValue('Test Dataset'))!.toBeInTheDocument() }) it('should handle image icon with empty icon_url', async () => { @@ -891,7 +1117,7 @@ describe('RenameDatasetModal', () => { }) render() - expect(screen.getByDisplayValue('Test Dataset')).toBeInTheDocument() + expect(screen.getByDisplayValue('Test Dataset'))!.toBeInTheDocument() // Save and verify the icon_url is handled correctly const saveButton = screen.getByText('common.operation.save') @@ -925,7 +1151,7 @@ describe('RenameDatasetModal', () => { }) render() - expect(screen.getByDisplayValue('Test Dataset')).toBeInTheDocument() + expect(screen.getByDisplayValue('Test Dataset'))!.toBeInTheDocument() // Save and verify the icon is handled correctly const saveButton = screen.getByText('common.operation.save') @@ -958,7 +1184,7 @@ describe('RenameDatasetModal', () => { }) render() - expect(screen.getByDisplayValue('Test Dataset')).toBeInTheDocument() + expect(screen.getByDisplayValue('Test Dataset'))!.toBeInTheDocument() }) it('should handle rapid input changes', async () => { @@ -975,7 +1201,7 @@ describe('RenameDatasetModal', () => { fireEvent.change(nameInput, { target: { value: 'New Nam' } }) fireEvent.change(nameInput, { target: { value: 'New Name' } }) - expect(screen.getByDisplayValue('New Name')).toBeInTheDocument() + expect(screen.getByDisplayValue('New Name'))!.toBeInTheDocument() }) it('should handle double click on save button', async () => { @@ -995,7 +1221,8 @@ describe('RenameDatasetModal', () => { }) // Button should be disabled now - expect(saveButton).toBeDisabled() + // Button should be disabled now + expect(saveButton)!.toBeDisabled() // Second click should not trigger another API call because button is disabled await act(async () => { @@ -1017,7 +1244,7 @@ describe('RenameDatasetModal', () => { fireEvent.change(nameInput, { target: { value: '数据集 🎉 Dataset' } }) - expect(screen.getByDisplayValue('数据集 🎉 Dataset')).toBeInTheDocument() + expect(screen.getByDisplayValue('数据集 🎉 Dataset'))!.toBeInTheDocument() }) it('should handle unicode characters in description', () => { @@ -1026,7 +1253,7 @@ describe('RenameDatasetModal', () => { fireEvent.change(descriptionTextarea, { target: { value: '这是一个测试描述 🚀' } }) - expect(screen.getByDisplayValue('这是一个测试描述 🚀')).toBeInTheDocument() + expect(screen.getByDisplayValue('这是一个测试描述 🚀'))!.toBeInTheDocument() }) it('should preserve whitespace in description', () => { @@ -1036,7 +1263,7 @@ describe('RenameDatasetModal', () => { const testValue = 'Leading spaces with content' fireEvent.change(descriptionTextarea, { target: { value: testValue } }) - expect(descriptionTextarea).toHaveValue(testValue) + expect(descriptionTextarea)!.toHaveValue(testValue) }) }) @@ -1044,20 +1271,22 @@ describe('RenameDatasetModal', () => { it('should update when dataset prop changes', () => { const { rerender } = render() - expect(screen.getByDisplayValue('Test Dataset')).toBeInTheDocument() + expect(screen.getByDisplayValue('Test Dataset'))!.toBeInTheDocument() const newDataset = createMockDataset({ name: 'Different Dataset', description: 'Different description' }) rerender() // Note: The component uses useState with initial value, so it won't update // This tests that the initial render works correctly with different props - expect(screen.getByDisplayValue('Test Dataset')).toBeInTheDocument() + // Note: The component uses useState with initial value, so it won't update + // This tests that the initial render works correctly with different props + expect(screen.getByDisplayValue('Test Dataset'))!.toBeInTheDocument() }) it('should handle show prop toggle', () => { const { rerender } = render() - expect(screen.getByText('datasetSettings.title')).toBeInTheDocument() + expect(screen.getByText('datasetSettings.title'))!.toBeInTheDocument() rerender() @@ -1084,8 +1313,8 @@ describe('RenameDatasetModal', () => { const cancelButton = screen.getByText('common.operation.cancel') const saveButton = screen.getByText('common.operation.save') - expect(cancelButton).toBeEnabled() - expect(saveButton).toBeEnabled() + expect(cancelButton)!.toBeEnabled() + expect(saveButton)!.toBeEnabled() }) }) @@ -1105,7 +1334,7 @@ describe('RenameDatasetModal', () => { // Button should be disabled during loading await waitFor(() => { - expect(saveButton).toBeDisabled() + expect(saveButton)!.toBeDisabled() }) // Resolve promise to complete the test @@ -1148,6 +1377,37 @@ describe('RenameDatasetModal', () => { expect(mockToast.error).toHaveBeenCalledWith('common.actionMsg.modifiedUnsuccessfully') }) + // Button should be re-enabled after error + // Button should be re-enabled after error + // Button should be re-enabled after error + // Button should be re-enabled after error + // Button should be re-enabled after error + // Button should be re-enabled after error + // Button should be re-enabled after error + // Button should be re-enabled after error + // Button should be re-enabled after error + // Button should be re-enabled after error + // Button should be re-enabled after error + // Button should be re-enabled after error + // Button should be re-enabled after error + // Button should be re-enabled after error + // Button should be re-enabled after error + // Button should be re-enabled after error + // Button should be re-enabled after error + // Button should be re-enabled after error + // Button should be re-enabled after error + // Button should be re-enabled after error + // Button should be re-enabled after error + // Button should be re-enabled after error + // Button should be re-enabled after error + // Button should be re-enabled after error + // Button should be re-enabled after error + // Button should be re-enabled after error + // Button should be re-enabled after error + // Button should be re-enabled after error + // Button should be re-enabled after error + // Button should be re-enabled after error + // Button should be re-enabled after error // Button should be re-enabled after error expect(saveButton).not.toBeDisabled() }) diff --git a/web/app/components/datasets/settings/chunk-structure/__tests__/hooks.spec.tsx b/web/app/components/datasets/settings/chunk-structure/__tests__/hooks.spec.tsx index 8d44d19d09..518c74d0e1 100644 --- a/web/app/components/datasets/settings/chunk-structure/__tests__/hooks.spec.tsx +++ b/web/app/components/datasets/settings/chunk-structure/__tests__/hooks.spec.tsx @@ -23,49 +23,49 @@ describe('useChunkStructure', () => { const { result } = renderHook(() => useChunkStructure()) const generalOption = result.current.options[0] - expect(generalOption.id).toBe('text_model') + expect(generalOption!.id).toBe('text_model') }) it('should have icon for General option', () => { const { result } = renderHook(() => useChunkStructure()) const generalOption = result.current.options[0] - expect(generalOption.icon).toBeDefined() + expect(generalOption!.icon).toBeDefined() }) it('should have correct iconActiveColor for General option', () => { const { result } = renderHook(() => useChunkStructure()) const generalOption = result.current.options[0] - expect(generalOption.iconActiveColor).toBe('text-util-colors-indigo-indigo-600') + expect(generalOption!.iconActiveColor).toBe('text-util-colors-indigo-indigo-600') }) it('should have title for General option', () => { const { result } = renderHook(() => useChunkStructure()) const generalOption = result.current.options[0] - expect(generalOption.title).toBe('General') + expect(generalOption!.title).toBe('General') }) it('should have description for General option', () => { const { result } = renderHook(() => useChunkStructure()) const generalOption = result.current.options[0] - expect(generalOption.description).toBeDefined() + expect(generalOption!.description).toBeDefined() }) it('should have indigo effectColor for General option', () => { const { result } = renderHook(() => useChunkStructure()) const generalOption = result.current.options[0] - expect(generalOption.effectColor).toBe(EffectColor.indigo) + expect(generalOption!.effectColor).toBe(EffectColor.indigo) }) it('should have showEffectColor true for General option', () => { const { result } = renderHook(() => useChunkStructure()) const generalOption = result.current.options[0] - expect(generalOption.showEffectColor).toBe(true) + expect(generalOption!.showEffectColor).toBe(true) }) }) @@ -74,49 +74,49 @@ describe('useChunkStructure', () => { const { result } = renderHook(() => useChunkStructure()) const parentChildOption = result.current.options[1] - expect(parentChildOption.id).toBe('hierarchical_model') + expect(parentChildOption!.id).toBe('hierarchical_model') }) it('should have icon for Parent-Child option', () => { const { result } = renderHook(() => useChunkStructure()) const parentChildOption = result.current.options[1] - expect(parentChildOption.icon).toBeDefined() + expect(parentChildOption!.icon).toBeDefined() }) it('should have correct iconActiveColor for Parent-Child option', () => { const { result } = renderHook(() => useChunkStructure()) const parentChildOption = result.current.options[1] - expect(parentChildOption.iconActiveColor).toBe('text-util-colors-blue-light-blue-light-500') + expect(parentChildOption!.iconActiveColor).toBe('text-util-colors-blue-light-blue-light-500') }) it('should have title for Parent-Child option', () => { const { result } = renderHook(() => useChunkStructure()) const parentChildOption = result.current.options[1] - expect(parentChildOption.title).toBe('Parent-Child') + expect(parentChildOption!.title).toBe('Parent-Child') }) it('should have description for Parent-Child option', () => { const { result } = renderHook(() => useChunkStructure()) const parentChildOption = result.current.options[1] - expect(parentChildOption.description).toBeDefined() + expect(parentChildOption!.description).toBeDefined() }) it('should have blueLight effectColor for Parent-Child option', () => { const { result } = renderHook(() => useChunkStructure()) const parentChildOption = result.current.options[1] - expect(parentChildOption.effectColor).toBe(EffectColor.blueLight) + expect(parentChildOption!.effectColor).toBe(EffectColor.blueLight) }) it('should have showEffectColor true for Parent-Child option', () => { const { result } = renderHook(() => useChunkStructure()) const parentChildOption = result.current.options[1] - expect(parentChildOption.showEffectColor).toBe(true) + expect(parentChildOption!.showEffectColor).toBe(true) }) }) @@ -125,49 +125,49 @@ describe('useChunkStructure', () => { const { result } = renderHook(() => useChunkStructure()) const qaOption = result.current.options[2] - expect(qaOption.id).toBe('qa_model') + expect(qaOption!.id).toBe('qa_model') }) it('should have icon for Q&A option', () => { const { result } = renderHook(() => useChunkStructure()) const qaOption = result.current.options[2] - expect(qaOption.icon).toBeDefined() + expect(qaOption!.icon).toBeDefined() }) it('should have title for Q&A option', () => { const { result } = renderHook(() => useChunkStructure()) const qaOption = result.current.options[2] - expect(qaOption.title).toBe('Q&A') + expect(qaOption!.title).toBe('Q&A') }) it('should have description for Q&A option', () => { const { result } = renderHook(() => useChunkStructure()) const qaOption = result.current.options[2] - expect(qaOption.description).toBeDefined() + expect(qaOption!.description).toBeDefined() }) it('should not have effectColor for Q&A option', () => { const { result } = renderHook(() => useChunkStructure()) const qaOption = result.current.options[2] - expect(qaOption.effectColor).toBeUndefined() + expect(qaOption!.effectColor).toBeUndefined() }) it('should not have showEffectColor for Q&A option', () => { const { result } = renderHook(() => useChunkStructure()) const qaOption = result.current.options[2] - expect(qaOption.showEffectColor).toBeUndefined() + expect(qaOption!.showEffectColor).toBeUndefined() }) it('should not have iconActiveColor for Q&A option', () => { const { result } = renderHook(() => useChunkStructure()) const qaOption = result.current.options[2] - expect(qaOption.iconActiveColor).toBeUndefined() + expect(qaOption!.iconActiveColor).toBeUndefined() }) }) diff --git a/web/app/components/datasets/settings/form/components/__tests__/basic-info-section.spec.tsx b/web/app/components/datasets/settings/form/components/__tests__/basic-info-section.spec.tsx index 618a28d498..f34269f058 100644 --- a/web/app/components/datasets/settings/form/components/__tests__/basic-info-section.spec.tsx +++ b/web/app/components/datasets/settings/form/components/__tests__/basic-info-section.spec.tsx @@ -142,42 +142,43 @@ describe('BasicInfoSection', () => { describe('Rendering', () => { it('should render without crashing', () => { render() - expect(screen.getByText(/form\.nameAndIcon/i)).toBeInTheDocument() + expect(screen.getByText(/form\.nameAndIcon/i))!.toBeInTheDocument() }) it('should render name and icon section', () => { render() - expect(screen.getByText(/form\.nameAndIcon/i)).toBeInTheDocument() + expect(screen.getByText(/form\.nameAndIcon/i))!.toBeInTheDocument() }) it('should render description section', () => { render() - expect(screen.getByText(/form\.desc/i)).toBeInTheDocument() + expect(screen.getByText(/form\.desc/i))!.toBeInTheDocument() }) it('should render permissions section', () => { render() // Use exact match to avoid matching "permissionsOnlyMe" - expect(screen.getByText('datasetSettings.form.permissions')).toBeInTheDocument() + // Use exact match to avoid matching "permissionsOnlyMe" + expect(screen.getByText('datasetSettings.form.permissions'))!.toBeInTheDocument() }) it('should render name input with correct value', () => { render() const nameInput = screen.getByDisplayValue('Test Dataset') - expect(nameInput).toBeInTheDocument() + expect(nameInput)!.toBeInTheDocument() }) it('should render description textarea with correct value', () => { render() const descriptionTextarea = screen.getByDisplayValue('Test description') - expect(descriptionTextarea).toBeInTheDocument() + expect(descriptionTextarea)!.toBeInTheDocument() }) it('should render app icon with emoji', () => { const { container } = render() // The icon section should be rendered (emoji may be in a span or SVG) const iconSection = container.querySelector('[class*="cursor-pointer"]') - expect(iconSection).toBeInTheDocument() + expect(iconSection)!.toBeInTheDocument() }) }) @@ -197,7 +198,7 @@ describe('BasicInfoSection', () => { render() const nameInput = screen.getByDisplayValue('Test Dataset') - expect(nameInput).toBeDisabled() + expect(nameInput)!.toBeDisabled() }) it('should enable name input when embedding is available', () => { @@ -212,7 +213,7 @@ describe('BasicInfoSection', () => { // Find the name input by its structure - may be type=text or just input const nameInput = container.querySelector('input') - expect(nameInput).toHaveValue('') + expect(nameInput)!.toHaveValue('') }) }) @@ -232,14 +233,14 @@ describe('BasicInfoSection', () => { render() const descriptionTextarea = screen.getByDisplayValue('Test description') - expect(descriptionTextarea).toBeDisabled() + expect(descriptionTextarea)!.toBeDisabled() }) it('should render placeholder', () => { render() const descriptionTextarea = screen.getByPlaceholderText(/form\.descPlaceholder/i) - expect(descriptionTextarea).toBeInTheDocument() + expect(descriptionTextarea)!.toBeInTheDocument() }) }) @@ -261,12 +262,45 @@ describe('BasicInfoSection', () => { // AppIconPicker renders a modal with emoji tabs and options via portal // We just verify the component renders without crashing when picker is shown - expect(baseElement).toBeInTheDocument() + // AppIconPicker renders a modal with emoji tabs and options via portal + // We just verify the component renders without crashing when picker is shown + expect(baseElement)!.toBeInTheDocument() }) it('should not render AppIconPicker when showAppIconPicker is false', () => { const { container } = render() + // Check that AppIconPicker is not rendered + // Check that AppIconPicker is not rendered + // Check that AppIconPicker is not rendered + // Check that AppIconPicker is not rendered + // Check that AppIconPicker is not rendered + // Check that AppIconPicker is not rendered + // Check that AppIconPicker is not rendered + // Check that AppIconPicker is not rendered + // Check that AppIconPicker is not rendered + // Check that AppIconPicker is not rendered + // Check that AppIconPicker is not rendered + // Check that AppIconPicker is not rendered + // Check that AppIconPicker is not rendered + // Check that AppIconPicker is not rendered + // Check that AppIconPicker is not rendered + // Check that AppIconPicker is not rendered + // Check that AppIconPicker is not rendered + // Check that AppIconPicker is not rendered + // Check that AppIconPicker is not rendered + // Check that AppIconPicker is not rendered + // Check that AppIconPicker is not rendered + // Check that AppIconPicker is not rendered + // Check that AppIconPicker is not rendered + // Check that AppIconPicker is not rendered + // Check that AppIconPicker is not rendered + // Check that AppIconPicker is not rendered + // Check that AppIconPicker is not rendered + // Check that AppIconPicker is not rendered + // Check that AppIconPicker is not rendered + // Check that AppIconPicker is not rendered + // Check that AppIconPicker is not rendered // Check that AppIconPicker is not rendered expect(container.querySelector('[data-testid="app-icon-picker"]')).not.toBeInTheDocument() }) @@ -283,7 +317,7 @@ describe('BasicInfoSection', () => { // For image type, it renders an img element const img = screen.queryByRole('img') if (img) { - expect(img).toHaveAttribute('src', expect.stringContaining('icon.png')) + expect(img)!.toHaveAttribute('src', expect.stringContaining('icon.png')) } }) }) @@ -292,13 +326,13 @@ describe('BasicInfoSection', () => { it('should render with correct permission value', () => { render() - expect(screen.getByText(/form\.permissionsOnlyMe/i)).toBeInTheDocument() + expect(screen.getByText(/form\.permissionsOnlyMe/i))!.toBeInTheDocument() }) it('should render all team members permission', () => { render() - expect(screen.getByText(/form\.permissionsAllMember/i)).toBeInTheDocument() + expect(screen.getByText(/form\.permissionsAllMember/i))!.toBeInTheDocument() }) it('should be disabled when embedding is not available', () => { @@ -309,7 +343,7 @@ describe('BasicInfoSection', () => { // Check for disabled state via cursor-not-allowed class const disabledElement = container.querySelector('[class*="cursor-not-allowed"]') - expect(disabledElement).toBeInTheDocument() + expect(disabledElement)!.toBeInTheDocument() }) it('should be disabled when user is dataset operator', () => { @@ -318,7 +352,7 @@ describe('BasicInfoSection', () => { ) const disabledElement = container.querySelector('[class*="cursor-not-allowed"]') - expect(disabledElement).toBeInTheDocument() + expect(disabledElement)!.toBeInTheDocument() }) it('should call setPermission when permission changes', async () => { @@ -330,7 +364,7 @@ describe('BasicInfoSection', () => { await waitFor(() => { const allMemberOptions = screen.getAllByText(/form\.permissionsAllMember/i) - fireEvent.click(allMemberOptions[0]) + fireEvent.click(allMemberOptions[0]!) }) expect(setPermission).toHaveBeenCalledWith(DatasetPermission.allTeamMembers) @@ -349,7 +383,10 @@ describe('BasicInfoSection', () => { // For partial members permission, the member selector should be visible // The exact interaction depends on the MemberSelector component // We verify the component renders without crashing - expect(container).toBeInTheDocument() + // For partial members permission, the member selector should be visible + // The exact interaction depends on the MemberSelector component + // We verify the component renders without crashing + expect(container)!.toBeInTheDocument() }) }) @@ -358,7 +395,8 @@ describe('BasicInfoSection', () => { render() // Should still render but inputs might behave differently - expect(screen.getByText(/form\.nameAndIcon/i)).toBeInTheDocument() + // Should still render but inputs might behave differently + expect(screen.getByText(/form\.nameAndIcon/i))!.toBeInTheDocument() }) }) @@ -366,31 +404,31 @@ describe('BasicInfoSection', () => { it('should update when name prop changes', () => { const { rerender } = render() - expect(screen.getByDisplayValue('Initial Name')).toBeInTheDocument() + expect(screen.getByDisplayValue('Initial Name'))!.toBeInTheDocument() rerender() - expect(screen.getByDisplayValue('Updated Name')).toBeInTheDocument() + expect(screen.getByDisplayValue('Updated Name'))!.toBeInTheDocument() }) it('should update when description prop changes', () => { const { rerender } = render() - expect(screen.getByDisplayValue('Initial Description')).toBeInTheDocument() + expect(screen.getByDisplayValue('Initial Description'))!.toBeInTheDocument() rerender() - expect(screen.getByDisplayValue('Updated Description')).toBeInTheDocument() + expect(screen.getByDisplayValue('Updated Description'))!.toBeInTheDocument() }) it('should update when permission prop changes', () => { const { rerender } = render() - expect(screen.getByText(/form\.permissionsOnlyMe/i)).toBeInTheDocument() + expect(screen.getByText(/form\.permissionsOnlyMe/i))!.toBeInTheDocument() rerender() - expect(screen.getByText(/form\.permissionsAllMember/i)).toBeInTheDocument() + expect(screen.getByText(/form\.permissionsAllMember/i))!.toBeInTheDocument() }) }) @@ -406,7 +444,9 @@ describe('BasicInfoSection', () => { // For partial members, a member selector component should be rendered // We verify it renders without crashing - expect(container).toBeInTheDocument() + // For partial members, a member selector component should be rendered + // We verify it renders without crashing + expect(container)!.toBeInTheDocument() }) it('should handle empty member list', () => { @@ -417,7 +457,7 @@ describe('BasicInfoSection', () => { />, ) - expect(screen.getByText(/form\.permissionsOnlyMe/i)).toBeInTheDocument() + expect(screen.getByText(/form\.permissionsOnlyMe/i))!.toBeInTheDocument() }) }) diff --git a/web/app/components/datasets/settings/form/hooks/__tests__/use-form-state.spec.ts b/web/app/components/datasets/settings/form/hooks/__tests__/use-form-state.spec.ts index 00462619aa..ece275c433 100644 --- a/web/app/components/datasets/settings/form/hooks/__tests__/use-form-state.spec.ts +++ b/web/app/components/datasets/settings/form/hooks/__tests__/use-form-state.spec.ts @@ -174,7 +174,7 @@ describe('useFormState', () => { const { result } = renderHook(() => useFormState()) expect(result.current.memberList).toHaveLength(2) - expect(result.current.memberList[0].name).toBe('User 1') + expect(result.current.memberList[0]!.name).toBe('User 1') }) it('should return currentDataset from context', () => { diff --git a/web/app/components/datasets/settings/index-method/__tests__/index.spec.tsx b/web/app/components/datasets/settings/index-method/__tests__/index.spec.tsx index 7441274155..5e81611fc4 100644 --- a/web/app/components/datasets/settings/index-method/__tests__/index.spec.tsx +++ b/web/app/components/datasets/settings/index-method/__tests__/index.spec.tsx @@ -19,12 +19,12 @@ describe('IndexMethod', () => { describe('Rendering', () => { it('should render without crashing', () => { render() - expect(screen.getByText(/stepTwo\.qualified/)).toBeInTheDocument() + expect(screen.getByText(/stepTwo\.qualified/))!.toBeInTheDocument() }) it('should render High Quality option', () => { render() - expect(screen.getByText(/stepTwo\.qualified/)).toBeInTheDocument() + expect(screen.getByText(/stepTwo\.qualified/))!.toBeInTheDocument() }) it('should render Economy option', () => { @@ -34,17 +34,17 @@ describe('IndexMethod', () => { it('should render High Quality description', () => { render() - expect(screen.getByText(/form\.indexMethodHighQualityTip/)).toBeInTheDocument() + expect(screen.getByText(/form\.indexMethodHighQualityTip/))!.toBeInTheDocument() }) it('should render Economy description', () => { render() - expect(screen.getByText(/form\.indexMethodEconomyTip/)).toBeInTheDocument() + expect(screen.getByText(/form\.indexMethodEconomyTip/))!.toBeInTheDocument() }) it('should render recommended badge on High Quality', () => { render() - expect(screen.getByText(/stepTwo\.recommend/)).toBeInTheDocument() + expect(screen.getByText(/stepTwo\.recommend/))!.toBeInTheDocument() }) }) @@ -82,7 +82,7 @@ describe('IndexMethod', () => { // Find and click Economy option - use getAllByText and get the first one (title) const economyTitles = screen.getAllByText(/form\.indexMethodEconomy/) const economyTitle = economyTitles[0] - const card = economyTitle.closest('div')?.parentElement?.parentElement?.parentElement + const card = economyTitle!.closest('div')?.parentElement?.parentElement?.parentElement fireEvent.click(card!) expect(handleChange).toHaveBeenCalledWith(IndexingType.ECONOMICAL) @@ -114,7 +114,7 @@ describe('IndexMethod', () => { // Try to click Economy option - use getAllByText and get the first one (title) const economyTitles = screen.getAllByText(/form\.indexMethodEconomy/) const economyTitle = economyTitles[0] - const card = economyTitle.closest('div')?.parentElement?.parentElement?.parentElement + const card = economyTitle!.closest('div')?.parentElement?.parentElement?.parentElement fireEvent.click(card!) // Should not call onChange because Economy is disabled when current is QUALIFIED @@ -125,13 +125,13 @@ describe('IndexMethod', () => { describe('KeywordNumber', () => { it('should render KeywordNumber component inside Economy option', () => { render() - expect(getKeywordSlider()).toBeInTheDocument() + expect(getKeywordSlider())!.toBeInTheDocument() }) it('should pass keywordNumber to KeywordNumber component', () => { render() const input = screen.getByRole('textbox') - expect(input).toHaveValue('25') + expect(input)!.toHaveValue('25') }) it('should call onKeywordNumberChange when KeywordNumber changes', () => { @@ -160,13 +160,13 @@ describe('IndexMethod', () => { it('should show orange effect color for High Quality option', () => { const { container } = render() const orangeEffect = container.querySelector('.bg-util-colors-orange-orange-500') - expect(orangeEffect).toBeInTheDocument() + expect(orangeEffect)!.toBeInTheDocument() }) it('should show indigo effect color for Economy option', () => { const { container } = render() const indigoEffect = container.querySelector('.bg-util-colors-indigo-indigo-600') - expect(indigoEffect).toBeInTheDocument() + expect(indigoEffect)!.toBeInTheDocument() }) }) @@ -188,19 +188,20 @@ describe('IndexMethod', () => { it('should handle undefined currentValue', () => { render() // Should render without error - expect(screen.getByText(/stepTwo\.qualified/)).toBeInTheDocument() + // Should render without error + expect(screen.getByText(/stepTwo\.qualified/))!.toBeInTheDocument() }) it('should handle minimum keywordNumber', () => { render() const input = screen.getByRole('textbox') - expect(input).toHaveValue('0') + expect(input)!.toHaveValue('0') }) it('should handle max keywordNumber', () => { render() const input = screen.getByRole('textbox') - expect(input).toHaveValue('50') + expect(input)!.toHaveValue('50') }) }) }) diff --git a/web/app/components/datasets/settings/permission-selector/__tests__/index.spec.tsx b/web/app/components/datasets/settings/permission-selector/__tests__/index.spec.tsx index b17b1c9b11..355946402b 100644 --- a/web/app/components/datasets/settings/permission-selector/__tests__/index.spec.tsx +++ b/web/app/components/datasets/settings/permission-selector/__tests__/index.spec.tsx @@ -16,15 +16,15 @@ vi.mock('@/context/app-context', () => ({ describe('PermissionSelector', () => { const mockMemberList: Member[] = [ - { id: 'user-1', name: 'Current User', email: 'current@example.com', avatar: '', avatar_url: '', role: 'owner', last_login_at: '', created_at: '', status: 'active' }, - { id: 'user-2', name: 'John Doe', email: 'john@example.com', avatar: '', avatar_url: '', role: 'admin', last_login_at: '', created_at: '', status: 'active' }, - { id: 'user-3', name: 'Jane Smith', email: 'jane@example.com', avatar: '', avatar_url: '', role: 'editor', last_login_at: '', created_at: '', status: 'active' }, - { id: 'user-4', name: 'Dataset Operator', email: 'operator@example.com', avatar: '', avatar_url: '', role: 'dataset_operator', last_login_at: '', created_at: '', status: 'active' }, + { id: 'user-1', name: 'Current User', email: 'current@example.com', avatar: '', avatar_url: '', role: 'owner', last_login_at: '', created_at: '', status: 'active' }!, + { id: 'user-2', name: 'John Doe', email: 'john@example.com', avatar: '', avatar_url: '', role: 'admin', last_login_at: '', created_at: '', status: 'active' }!, + { id: 'user-3', name: 'Jane Smith', email: 'jane@example.com', avatar: '', avatar_url: '', role: 'editor', last_login_at: '', created_at: '', status: 'active' }!, + { id: 'user-4', name: 'Dataset Operator', email: 'operator@example.com', avatar: '', avatar_url: '', role: 'dataset_operator', last_login_at: '', created_at: '', status: 'active' }!, ] const defaultProps = { permission: DatasetPermission.onlyMe, - value: ['user-1'], + value: ['user-1'!], memberList: mockMemberList, onChange: vi.fn(), onMemberSelect: vi.fn(), @@ -37,17 +37,17 @@ describe('PermissionSelector', () => { describe('Rendering', () => { it('should render without crashing', () => { render() - expect(screen.getByText(/form\.permissionsOnlyMe/)).toBeInTheDocument() + expect(screen.getByText(/form\.permissionsOnlyMe/))!.toBeInTheDocument() }) it('should render Only Me option when permission is onlyMe', () => { render() - expect(screen.getByText(/form\.permissionsOnlyMe/)).toBeInTheDocument() + expect(screen.getByText(/form\.permissionsOnlyMe/))!.toBeInTheDocument() }) it('should render All Team Members option when permission is allTeamMembers', () => { render() - expect(screen.getByText(/form\.permissionsAllMember/)).toBeInTheDocument() + expect(screen.getByText(/form\.permissionsAllMember/))!.toBeInTheDocument() }) it('should render selected member names when permission is partialMembers', () => { @@ -59,7 +59,8 @@ describe('PermissionSelector', () => { />, ) // Should show member names - expect(screen.getByTitle(/Current User/)).toBeInTheDocument() + // Should show member names + expect(screen.getByTitle(/Current User/))!.toBeInTheDocument() }) }) @@ -97,7 +98,7 @@ describe('PermissionSelector', () => { await waitFor(() => { const onlyMeOptions = screen.getAllByText(/form\.permissionsOnlyMe/) - fireEvent.click(onlyMeOptions[0]) + fireEvent.click(onlyMeOptions[0]!) }) expect(handleChange).toHaveBeenCalledWith(DatasetPermission.onlyMe) @@ -112,7 +113,7 @@ describe('PermissionSelector', () => { await waitFor(() => { const allMemberOptions = screen.getAllByText(/form\.permissionsAllMember/) - fireEvent.click(allMemberOptions[0]) + fireEvent.click(allMemberOptions[0]!) }) expect(handleChange).toHaveBeenCalledWith(DatasetPermission.allTeamMembers) @@ -134,7 +135,7 @@ describe('PermissionSelector', () => { await waitFor(() => { const invitedOptions = screen.getAllByText(/form\.permissionsInvitedMembers/) - fireEvent.click(invitedOptions[0]) + fireEvent.click(invitedOptions[0]!) }) expect(handleChange).toHaveBeenCalledWith(DatasetPermission.partialMembers) @@ -156,8 +157,9 @@ describe('PermissionSelector', () => { await waitFor(() => { // Should show member list - expect(screen.getByText('John Doe')).toBeInTheDocument() - expect(screen.getByText('Jane Smith')).toBeInTheDocument() + // Should show member list + expect(screen.getByText('John Doe'))!.toBeInTheDocument() + expect(screen.getByText('Jane Smith'))!.toBeInTheDocument() }) }) @@ -222,7 +224,7 @@ describe('PermissionSelector', () => { // Type in search fireEvent.change(searchInput, { target: { value: 'John' } }) - expect(searchInput).toHaveValue('John') + expect(searchInput)!.toHaveValue('John') }) it('should render search input in partial members mode', async () => { @@ -238,7 +240,7 @@ describe('PermissionSelector', () => { // Wait for dropdown to open and search input to be available const searchInput = await screen.findByRole('textbox') - expect(searchInput).toBeInTheDocument() + expect(searchInput)!.toBeInTheDocument() }) it('should filter members after debounce completes', async () => { @@ -261,7 +263,7 @@ describe('PermissionSelector', () => { // Wait for debounce (500ms) + buffer await waitFor( () => { - expect(screen.getByText('John Doe')).toBeInTheDocument() + expect(screen.getByText('John Doe'))!.toBeInTheDocument() }, { timeout: 1000 }, ) @@ -283,14 +285,14 @@ describe('PermissionSelector', () => { // Type in search fireEvent.change(searchInput, { target: { value: 'test' } }) - expect(searchInput).toHaveValue('test') + expect(searchInput)!.toHaveValue('test') const clearButton = screen.getByTestId('input-clear') fireEvent.click(clearButton) // After clicking clear, input should be empty await waitFor(() => { - expect(searchInput).toHaveValue('') + expect(searchInput)!.toHaveValue('') }) }) @@ -314,7 +316,7 @@ describe('PermissionSelector', () => { // Wait for debounce await waitFor( () => { - expect(screen.getByText('John Doe')).toBeInTheDocument() + expect(screen.getByText('John Doe'))!.toBeInTheDocument() }, { timeout: 1000 }, ) @@ -340,7 +342,7 @@ describe('PermissionSelector', () => { // Wait for debounce and no results message await waitFor( () => { - expect(screen.getByText(/form\.onSearchResults/)).toBeInTheDocument() + expect(screen.getByText(/form\.onSearchResults/))!.toBeInTheDocument() }, { timeout: 1000 }, ) @@ -365,7 +367,9 @@ describe('PermissionSelector', () => { // Current user (showMe) should remain visible based on name match // The component uses useMemo to check if userProfile.name.includes(searchKeywords) - expect(searchInput).toHaveValue('Current') + // Current user (showMe) should remain visible based on name match + // The component uses useMemo to check if userProfile.name.includes(searchKeywords) + expect(searchInput)!.toHaveValue('Current') // Current User label appears multiple times (trigger + member list) expect(screen.getAllByText('Current User').length).toBeGreaterThanOrEqual(1) }) @@ -388,7 +392,8 @@ describe('PermissionSelector', () => { fireEvent.change(searchInput, { target: { value: 'current@' } }) // The component checks userProfile.email.includes(searchKeywords) - expect(searchInput).toHaveValue('current@') + // The component checks userProfile.email.includes(searchKeywords) + expect(searchInput)!.toHaveValue('current@') // Current User should remain visible based on email match expect(screen.getAllByText('Current User').length).toBeGreaterThanOrEqual(1) }) @@ -399,7 +404,7 @@ describe('PermissionSelector', () => { const { container } = render() // When disabled, the component has cursor-not-allowed! class (escaped in Tailwind) const triggerElement = container.querySelector('[class*="cursor-not-allowed"]') - expect(triggerElement).toBeInTheDocument() + expect(triggerElement)!.toBeInTheDocument() }) }) @@ -414,7 +419,8 @@ describe('PermissionSelector', () => { ) // Should display single avatar - expect(screen.getByTitle(/Current User/)).toBeInTheDocument() + // Should display single avatar + expect(screen.getByTitle(/Current User/))!.toBeInTheDocument() }) it('should display two avatars when two or more members selected', () => { @@ -427,7 +433,8 @@ describe('PermissionSelector', () => { ) // Should display member names - expect(screen.getByTitle(/Current User, John Doe/)).toBeInTheDocument() + // Should display member names + expect(screen.getByTitle(/Current User, John Doe/))!.toBeInTheDocument() }) }) @@ -440,18 +447,18 @@ describe('PermissionSelector', () => { />, ) - expect(screen.getByText(/form\.permissionsOnlyMe/)).toBeInTheDocument() + expect(screen.getByText(/form\.permissionsOnlyMe/))!.toBeInTheDocument() }) it('should handle member list with only current user', () => { render( , ) - expect(screen.getByText(/form\.permissionsOnlyMe/)).toBeInTheDocument() + expect(screen.getByText(/form\.permissionsOnlyMe/))!.toBeInTheDocument() }) it('should only show members with allowed roles', () => { @@ -472,7 +479,8 @@ describe('PermissionSelector', () => { ) // The component renders - the filtering logic is internal - expect(screen.getByTitle(/Current User/)).toBeInTheDocument() + // The component renders - the filtering logic is internal + expect(screen.getByTitle(/Current User/))!.toBeInTheDocument() }) }) @@ -480,11 +488,11 @@ describe('PermissionSelector', () => { it('should update when permission prop changes', () => { const { rerender } = render() - expect(screen.getByText(/form\.permissionsOnlyMe/)).toBeInTheDocument() + expect(screen.getByText(/form\.permissionsOnlyMe/))!.toBeInTheDocument() rerender() - expect(screen.getByText(/form\.permissionsAllMember/)).toBeInTheDocument() + expect(screen.getByText(/form\.permissionsAllMember/))!.toBeInTheDocument() }) }) }) diff --git a/web/app/components/datasets/settings/permission-selector/index.tsx b/web/app/components/datasets/settings/permission-selector/index.tsx index a7182d8f79..8c31799add 100644 --- a/web/app/components/datasets/settings/permission-selector/index.tsx +++ b/web/app/components/datasets/settings/permission-selector/index.tsx @@ -133,8 +133,8 @@ const PermissionSelector = ({ { selectedMembers.length === 1 && ( ) @@ -143,14 +143,14 @@ const PermissionSelector = ({ selectedMembers.length >= 2 && ( <> diff --git a/web/app/components/develop/code.tsx b/web/app/components/develop/code.tsx index 59d6c94e5d..86d57c806d 100644 --- a/web/app/components/develop/code.tsx +++ b/web/app/components/develop/code.tsx @@ -226,7 +226,7 @@ function useTabGroupProps(availableLanguages: string[]) { const activeLanguage = [...(availableLanguages || [])].sort( (a, z) => preferredLanguages.indexOf(z) - preferredLanguages.indexOf(a), )[0] - const languageIndex = availableLanguages?.indexOf(activeLanguage) || 0 + const languageIndex = availableLanguages?.indexOf(activeLanguage!) || 0 const newSelectedIndex = languageIndex === -1 ? selectedIndex : languageIndex if (newSelectedIndex !== selectedIndex) setSelectedIndex(newSelectedIndex) diff --git a/web/app/components/develop/hooks/use-doc-toc.ts b/web/app/components/develop/hooks/use-doc-toc.ts index d42cb68b00..1eba8ca8e3 100644 --- a/web/app/components/develop/hooks/use-doc-toc.ts +++ b/web/app/components/develop/hooks/use-doc-toc.ts @@ -57,7 +57,7 @@ export const useDocToc = ({ appDetail, locale }: UseDocTocOptions) => { const tocItems = extractTocFromArticle() setToc(tocItems) if (tocItems.length > 0) - setActiveSection(getTargetId(tocItems[0].href)) + setActiveSection(getTargetId(tocItems[0]!.href)) }, 0) return () => clearTimeout(timer) }, [appDetail, locale]) diff --git a/web/app/components/develop/secret-key/__tests__/secret-key-button.spec.tsx b/web/app/components/develop/secret-key/__tests__/secret-key-button.spec.tsx index e5134b995d..b4f5c139e0 100644 --- a/web/app/components/develop/secret-key/__tests__/secret-key-button.spec.tsx +++ b/web/app/components/develop/secret-key/__tests__/secret-key-button.spec.tsx @@ -19,18 +19,18 @@ describe('SecretKeyButton', () => { describe('rendering', () => { it('should render the button', () => { render() - expect(screen.getByRole('button')).toBeInTheDocument() + expect(screen.getByRole('button'))!.toBeInTheDocument() }) it('should render the API key text', () => { render() - expect(screen.getByText('appApi.apiKey')).toBeInTheDocument() + expect(screen.getByText('appApi.apiKey'))!.toBeInTheDocument() }) it('should render the key icon', () => { const { container } = render() const svg = container.querySelector('svg') - expect(svg).toBeInTheDocument() + expect(svg)!.toBeInTheDocument() }) it('should not show modal initially', () => { @@ -49,7 +49,7 @@ describe('SecretKeyButton', () => { await user.click(button) }) - expect(screen.getByTestId('secret-key-modal')).toBeInTheDocument() + expect(screen.getByTestId('secret-key-modal'))!.toBeInTheDocument() }) it('should close modal when onClose is called', async () => { @@ -61,7 +61,7 @@ describe('SecretKeyButton', () => { await user.click(button) }) - expect(screen.getByTestId('secret-key-modal')).toBeInTheDocument() + expect(screen.getByTestId('secret-key-modal'))!.toBeInTheDocument() const closeButton = screen.getByTestId('close-modal') await act(async () => { @@ -80,7 +80,7 @@ describe('SecretKeyButton', () => { await act(async () => { await user.click(button) }) - expect(screen.getByTestId('secret-key-modal')).toBeInTheDocument() + expect(screen.getByTestId('secret-key-modal'))!.toBeInTheDocument() const closeButton = screen.getByTestId('close-modal') await act(async () => { @@ -91,7 +91,7 @@ describe('SecretKeyButton', () => { await act(async () => { await user.click(button) }) - expect(screen.getByTestId('secret-key-modal')).toBeInTheDocument() + expect(screen.getByTestId('secret-key-modal'))!.toBeInTheDocument() }) }) @@ -111,7 +111,7 @@ describe('SecretKeyButton', () => { await user.click(button) }) - expect(screen.getByText('Modal for app-123')).toBeInTheDocument() + expect(screen.getByText('Modal for app-123'))!.toBeInTheDocument() }) it('should handle undefined appId', async () => { @@ -123,7 +123,7 @@ describe('SecretKeyButton', () => { await user.click(button) }) - expect(screen.getByText('Modal for no-app')).toBeInTheDocument() + expect(screen.getByText('Modal for no-app'))!.toBeInTheDocument() }) it('should apply custom textCls', () => { @@ -145,19 +145,19 @@ describe('SecretKeyButton', () => { it('should have icon container with flex layout', () => { const { container } = render() const iconContainer = container.querySelector('.flex.items-center.justify-center') - expect(iconContainer).toBeInTheDocument() + expect(iconContainer)!.toBeInTheDocument() }) it('should have correct icon dimensions', () => { const { container } = render() const iconContainer = container.querySelector('.h-3\\.5.w-3\\.5') - expect(iconContainer).toBeInTheDocument() + expect(iconContainer)!.toBeInTheDocument() }) it('should have tertiary text color on icon', () => { const { container } = render() const icon = container.querySelector('.text-text-tertiary') - expect(icon).toBeInTheDocument() + expect(icon)!.toBeInTheDocument() }) }) @@ -193,7 +193,7 @@ describe('SecretKeyButton', () => { await user.click(button) }) - expect(screen.getByTestId('secret-key-modal')).toBeInTheDocument() + expect(screen.getByTestId('secret-key-modal'))!.toBeInTheDocument() }) it('should pass onClose callback to modal', async () => { @@ -218,7 +218,7 @@ describe('SecretKeyButton', () => { it('should have accessible button', () => { render() const button = screen.getByRole('button') - expect(button).toBeInTheDocument() + expect(button)!.toBeInTheDocument() }) it('should be keyboard accessible', async () => { @@ -233,7 +233,7 @@ describe('SecretKeyButton', () => { await user.keyboard('{Enter}') }) - expect(screen.getByTestId('secret-key-modal')).toBeInTheDocument() + expect(screen.getByTestId('secret-key-modal'))!.toBeInTheDocument() }) }) @@ -251,10 +251,10 @@ describe('SecretKeyButton', () => { expect(buttons).toHaveLength(2) await act(async () => { - await user.click(buttons[0]) + await user.click(buttons[0]!) }) - expect(screen.getByText('Modal for app-1')).toBeInTheDocument() + expect(screen.getByText('Modal for app-1'))!.toBeInTheDocument() const closeButton = screen.getByTestId('close-modal') await act(async () => { @@ -262,10 +262,10 @@ describe('SecretKeyButton', () => { }) await act(async () => { - await user.click(buttons[1]) + await user.click(buttons[1]!) }) - expect(screen.getByText('Modal for app-2')).toBeInTheDocument() + expect(screen.getByText('Modal for app-2'))!.toBeInTheDocument() }) }) }) diff --git a/web/app/components/develop/tag.tsx b/web/app/components/develop/tag.tsx index 3178046338..082d63a798 100644 --- a/web/app/components/develop/tag.tsx +++ b/web/app/components/develop/tag.tsx @@ -53,7 +53,7 @@ export function Tag({ }: ITagProps) { return ( {children} diff --git a/web/app/components/explore/create-app-modal/__tests__/index.spec.tsx b/web/app/components/explore/create-app-modal/__tests__/index.spec.tsx index fd6877d421..5a14807478 100644 --- a/web/app/components/explore/create-app-modal/__tests__/index.spec.tsx +++ b/web/app/components/explore/create-app-modal/__tests__/index.spec.tsx @@ -106,24 +106,24 @@ describe('CreateAppModal', () => { it('should render create title and actions when creating', async () => { await setup({ appName: 'My App', isEditModal: false }) - expect(screen.getByText('explore.appCustomize.title:{"name":"My App"}')).toBeInTheDocument() - expect(screen.getByRole('button', { name: /common\.operation\.create/ })).toBeInTheDocument() - expect(screen.getByRole('button', { name: 'common.operation.cancel' })).toBeInTheDocument() + expect(screen.getByText('explore.appCustomize.title:{"name":"My App"}'))!.toBeInTheDocument() + expect(screen.getByRole('button', { name: /common\.operation\.create/ }))!.toBeInTheDocument() + expect(screen.getByRole('button', { name: 'common.operation.cancel' }))!.toBeInTheDocument() }) it('should render edit-only fields when editing a chat app', async () => { await setup({ isEditModal: true, appMode: AppModeEnum.CHAT, max_active_requests: 5 }) - expect(screen.getByText('app.editAppTitle')).toBeInTheDocument() - expect(screen.getByRole('button', { name: /common\.operation\.save/ })).toBeInTheDocument() - expect(screen.getByRole('switch')).toBeInTheDocument() + expect(screen.getByText('app.editAppTitle'))!.toBeInTheDocument() + expect(screen.getByRole('button', { name: /common\.operation\.save/ }))!.toBeInTheDocument() + expect(screen.getByRole('switch'))!.toBeInTheDocument() expect((screen.getByRole('spinbutton') as HTMLInputElement).value).toBe('5') }) it.each([AppModeEnum.ADVANCED_CHAT, AppModeEnum.AGENT_CHAT])('should render answer icon switch when editing %s app', async (mode) => { await setup({ isEditModal: true, appMode: mode }) - expect(screen.getByRole('switch')).toBeInTheDocument() + expect(screen.getByRole('switch'))!.toBeInTheDocument() }) it('should not render answer icon switch when editing a non-chat app', async () => { @@ -143,13 +143,13 @@ describe('CreateAppModal', () => { it('should disable confirm action when confirmDisabled is true', async () => { await setup({ confirmDisabled: true }) - expect(screen.getByRole('button', { name: /common\.operation\.create/ })).toBeDisabled() + expect(screen.getByRole('button', { name: /common\.operation\.create/ }))!.toBeDisabled() }) it('should disable confirm action when appName is empty', async () => { await setup({ appName: ' ' }) - expect(screen.getByRole('button', { name: /common\.operation\.create/ })).toBeDisabled() + expect(screen.getByRole('button', { name: /common\.operation\.create/ }))!.toBeDisabled() }) }) @@ -204,8 +204,8 @@ describe('CreateAppModal', () => { await setup({ isEditModal: false }) - expect(screen.getByText('billing.apps.fullTip2')).toBeInTheDocument() - expect(screen.getByRole('button', { name: /common\.operation\.create/ })).toBeDisabled() + expect(screen.getByText('billing.apps.fullTip2'))!.toBeInTheDocument() + expect(screen.getByRole('button', { name: /common\.operation\.create/ }))!.toBeDisabled() }) it('should allow saving when apps quota is reached in edit mode', async () => { @@ -217,7 +217,7 @@ describe('CreateAppModal', () => { await setup({ isEditModal: true }) expect(screen.queryByText('billing.apps.fullTip2')).not.toBeInTheDocument() - expect(screen.getByRole('button', { name: /common\.operation\.save/ })).toBeEnabled() + expect(screen.getByRole('button', { name: /common\.operation\.save/ }))!.toBeEnabled() }) }) @@ -314,7 +314,7 @@ describe('CreateAppModal', () => { fireEvent.click(getAppIconTrigger()) - expect(screen.getByRole('button', { name: 'app.iconPicker.cancel' })).toBeInTheDocument() + expect(screen.getByRole('button', { name: 'app.iconPicker.cancel' }))!.toBeInTheDocument() fireEvent.click(screen.getByRole('button', { name: 'app.iconPicker.cancel' })) @@ -347,7 +347,7 @@ describe('CreateAppModal', () => { }) expect(onConfirm).toHaveBeenCalledTimes(1) - const payload = onConfirm.mock.calls[0][0] + const payload = onConfirm.mock.calls[0]![0] expect(payload).toMatchObject({ icon_type: 'emoji', icon: '😀', @@ -392,7 +392,7 @@ describe('CreateAppModal', () => { }) expect(onConfirm).toHaveBeenCalledTimes(1) - const payload = onConfirm.mock.calls[0][0] + const payload = onConfirm.mock.calls[0]![0] expect(payload).toMatchObject({ icon_type: 'emoji', icon: '🤖', @@ -431,7 +431,7 @@ describe('CreateAppModal', () => { expect(onConfirm).toHaveBeenCalledTimes(1) expect(onHide).toHaveBeenCalledTimes(1) - const payload = onConfirm.mock.calls[0][0] + const payload = onConfirm.mock.calls[0]![0] expect(payload).toMatchObject({ name: 'My App', icon_type: 'emoji', @@ -453,7 +453,7 @@ describe('CreateAppModal', () => { }) expect(onConfirm).toHaveBeenCalledTimes(1) - expect(onConfirm.mock.calls[0][0]).toMatchObject({ description: 'Updated description' }) + expect(onConfirm.mock.calls[0]![0]).toMatchObject({ description: 'Updated description' }) }) it('should omit icon_background when submitting with image icon', async () => { @@ -469,7 +469,7 @@ describe('CreateAppModal', () => { vi.advanceTimersByTime(300) }) - const payload = onConfirm.mock.calls[0][0] + const payload = onConfirm.mock.calls[0]![0] expect(payload).toMatchObject({ icon_type: 'image', icon: 'file-123', @@ -493,7 +493,7 @@ describe('CreateAppModal', () => { vi.advanceTimersByTime(300) }) - const payload = onConfirm.mock.calls[0][0] + const payload = onConfirm.mock.calls[0]![0] expect(payload).toMatchObject({ use_icon_as_answer_icon: true, max_active_requests: 12, @@ -508,7 +508,7 @@ describe('CreateAppModal', () => { vi.advanceTimersByTime(300) }) - const payload = onConfirm.mock.calls[0][0] + const payload = onConfirm.mock.calls[0]![0] expect(payload.max_active_requests).toBeUndefined() }) @@ -521,7 +521,7 @@ describe('CreateAppModal', () => { vi.advanceTimersByTime(300) }) - const payload = onConfirm.mock.calls[0][0] + const payload = onConfirm.mock.calls[0]![0] expect(payload.max_active_requests).toBeUndefined() }) diff --git a/web/app/components/explore/try-app/app-info/__tests__/use-get-requirements.spec.ts b/web/app/components/explore/try-app/app-info/__tests__/use-get-requirements.spec.ts index c6c3353a57..076278b3ed 100644 --- a/web/app/components/explore/try-app/app-info/__tests__/use-get-requirements.spec.ts +++ b/web/app/components/explore/try-app/app-info/__tests__/use-get-requirements.spec.ts @@ -59,8 +59,8 @@ describe('useGetRequirements', () => { ) expect(result.current.requirements).toHaveLength(1) - expect(result.current.requirements[0].name).toBe('openai') - expect(result.current.requirements[0].iconUrl).toBe('https://marketplace.api/plugins/langgenius/openai/icon') + expect(result.current.requirements[0]!.name).toBe('openai') + expect(result.current.requirements[0]!.iconUrl).toBe('https://marketplace.api/plugins/langgenius/openai/icon') }) it('returns model provider for completion mode', () => { @@ -84,7 +84,7 @@ describe('useGetRequirements', () => { ) expect(result.current.requirements).toHaveLength(1) - expect(result.current.requirements[0].name).toBe('claude') + expect(result.current.requirements[0]!.name).toBe('claude') }) it('returns model provider and tools for agent-chat mode', () => { @@ -166,7 +166,7 @@ describe('useGetRequirements', () => { ) expect(result.current.requirements).toHaveLength(1) - expect(result.current.requirements[0].name).toBe('openai') + expect(result.current.requirements[0]!.name).toBe('openai') }) }) @@ -232,7 +232,7 @@ describe('useGetRequirements', () => { ) expect(result.current.requirements).toHaveLength(1) - expect(result.current.requirements[0].name).toBe('claude-3-opus') + expect(result.current.requirements[0]!.name).toBe('claude-3-opus') }) it('returns empty requirements when flow data has no nodes', () => { @@ -373,7 +373,7 @@ describe('useGetRequirements', () => { ) expect(result.current.requirements).toHaveLength(1) - expect(result.current.requirements[0].name).toBe('gpt-4') + expect(result.current.requirements[0]!.name).toBe('gpt-4') }) }) @@ -398,7 +398,7 @@ describe('useGetRequirements', () => { useGetRequirements({ appDetail, appId: 'test-app-id' }), ) - expect(result.current.requirements[0].iconUrl).toBe('https://marketplace.api/plugins/org/plugin/icon') + expect(result.current.requirements[0]!.iconUrl).toBe('https://marketplace.api/plugins/org/plugin/icon') }) it('maps google model provider to gemini plugin icon URL', () => { @@ -421,7 +421,7 @@ describe('useGetRequirements', () => { useGetRequirements({ appDetail, appId: 'test-app-id' }), ) - expect(result.current.requirements[0].iconUrl).toBe('https://marketplace.api/plugins/langgenius/gemini/icon') + expect(result.current.requirements[0]!.iconUrl).toBe('https://marketplace.api/plugins/langgenius/gemini/icon') }) it('maps special builtin tool providers to *_tool plugin icon URL', () => { diff --git a/web/app/components/explore/try-app/app-info/use-get-requirements.ts b/web/app/components/explore/try-app/app-info/use-get-requirements.ts index 6458c76037..3ad89eee7e 100644 --- a/web/app/components/explore/try-app/app-info/use-get-requirements.ts +++ b/web/app/components/explore/try-app/app-info/use-get-requirements.ts @@ -44,13 +44,13 @@ const parseProviderId = (providerId: string): ProviderInfo | null => { if (segments.length === 1) { return { organization: 'langgenius', - providerName: segments[0], + providerName: segments[0]!, } } return { - organization: segments[0], - providerName: segments[1], + organization: segments[0]!, + providerName: segments[1]!, } } diff --git a/web/app/components/explore/try-app/preview/basic-app-preview.tsx b/web/app/components/explore/try-app/preview/basic-app-preview.tsx index 6954546b2e..43fb00600f 100644 --- a/web/app/components/explore/try-app/preview/basic-app-preview.tsx +++ b/web/app/components/explore/try-app/preview/basic-app-preview.tsx @@ -238,7 +238,7 @@ const BasicAppPreview: FC = ({ }, enabled: !!(modelConfig.file_upload?.enabled || modelConfig.file_upload?.image?.enabled), allowed_file_types: modelConfig.file_upload?.allowed_file_types || [], - allowed_file_extensions: modelConfig.file_upload?.allowed_file_extensions || [...FILE_EXTS[SupportUploadFileTypes.image], ...FILE_EXTS[SupportUploadFileTypes.video]].map(ext => `.${ext}`), + allowed_file_extensions: modelConfig.file_upload?.allowed_file_extensions || [...(FILE_EXTS[SupportUploadFileTypes.image] ?? []), ...(FILE_EXTS[SupportUploadFileTypes.video] ?? [])].map(ext => `.${ext}`), allowed_file_upload_methods: modelConfig.file_upload?.allowed_file_upload_methods || modelConfig.file_upload?.image?.transfer_methods || ['local_file', 'remote_url'], number_limits: modelConfig.file_upload?.number_limits || modelConfig.file_upload?.image?.number_limits || 3, fileUploadConfig: {}, diff --git a/web/app/components/goto-anything/actions/__tests__/index.spec.ts b/web/app/components/goto-anything/actions/__tests__/index.spec.ts index 12bdb192f2..ff7ab1e575 100644 --- a/web/app/components/goto-anything/actions/__tests__/index.spec.ts +++ b/web/app/components/goto-anything/actions/__tests__/index.spec.ts @@ -81,20 +81,20 @@ describe('createActions', () => { const actions = createActions(true, false) as Record expect(actions).toHaveProperty('node') - expect(actions.node.title).toBe('Workflow Nodes') + expect(actions.node!.title).toBe('Workflow Nodes') }) it('includes rag-pipeline nodes action on rag-pipeline pages', () => { const actions = createActions(false, true) as Record expect(actions).toHaveProperty('node') - expect(actions.node.title).toBe('RAG Pipeline Nodes') + expect(actions.node!.title).toBe('RAG Pipeline Nodes') }) it('rag-pipeline page takes priority over workflow page', () => { const actions = createActions(true, true) as Record - expect(actions.node.title).toBe('RAG Pipeline Nodes') + expect(actions.node!.title).toBe('RAG Pipeline Nodes') }) }) @@ -180,7 +180,7 @@ describe('searchAnything', () => { const results = await searchAnything('en', 'my query', undefined, dynamicActions) - expect(dynamicActions.slash.search).not.toHaveBeenCalled() + expect(dynamicActions.slash!.search).not.toHaveBeenCalled() expect(results).toHaveLength(2) expect(results).toEqual(expect.arrayContaining([ expect.objectContaining({ id: 'a1' }), @@ -206,7 +206,7 @@ describe('searchAnything', () => { const results = await searchAnything('en', 'query', undefined, dynamicActions) expect(results).toHaveLength(1) - expect(results[0].id).toBe('k1') + expect(results[0]!.id).toBe('k1') expect(warnSpy).toHaveBeenCalled() warnSpy.mockRestore() }) diff --git a/web/app/components/goto-anything/actions/__tests__/knowledge.spec.ts b/web/app/components/goto-anything/actions/__tests__/knowledge.spec.ts index 55592cd487..2124eccd81 100644 --- a/web/app/components/goto-anything/actions/__tests__/knowledge.spec.ts +++ b/web/app/components/goto-anything/actions/__tests__/knowledge.spec.ts @@ -59,7 +59,7 @@ describe('knowledgeAction', () => { const results = await knowledgeAction.search('@knowledge', '', 'en') - expect(results[0].path).toBe('/datasets/ds-ext/hitTesting') + expect(results[0]!.path).toBe('/datasets/ds-ext/hitTesting') }) it('generates correct path for non-external provider', async () => { @@ -76,7 +76,7 @@ describe('knowledgeAction', () => { const results = await knowledgeAction.search('@knowledge', '', 'en') - expect(results[0].path).toBe('/datasets/ds-2/documents') + expect(results[0]!.path).toBe('/datasets/ds-2/documents') }) it('returns empty array on API failure', async () => { diff --git a/web/app/components/goto-anything/actions/__tests__/recent-store.spec.ts b/web/app/components/goto-anything/actions/__tests__/recent-store.spec.ts index d2fb346286..da1dc86124 100644 --- a/web/app/components/goto-anything/actions/__tests__/recent-store.spec.ts +++ b/web/app/components/goto-anything/actions/__tests__/recent-store.spec.ts @@ -51,7 +51,7 @@ describe('recent-store', () => { const stored = getRecentItems() expect(stored.map(i => i.id)).toEqual(['a', 'b']) - expect(stored[0].title).toBe('A updated') + expect(stored[0]!.title).toBe('A updated') }) it('caps the list at 8 items, evicting the oldest', () => { @@ -60,8 +60,8 @@ describe('recent-store', () => { const stored = getRecentItems() expect(stored).toHaveLength(8) - expect(stored[0].id).toBe('item-9') - expect(stored[7].id).toBe('item-2') + expect(stored[0]!.id).toBe('item-9') + expect(stored[7]!.id).toBe('item-2') }) it('silently swallows storage errors', () => { diff --git a/web/app/components/goto-anything/actions/commands/__tests__/direct-commands.spec.ts b/web/app/components/goto-anything/actions/commands/__tests__/direct-commands.spec.ts index 88bd8b1045..f14098e970 100644 --- a/web/app/components/goto-anything/actions/commands/__tests__/direct-commands.spec.ts +++ b/web/app/components/goto-anything/actions/commands/__tests__/direct-commands.spec.ts @@ -70,7 +70,7 @@ describe('docsCommand', () => { const results = await docsCommand.search('', 'en') - expect(results[0].description).toBe('Open help documentation') + expect(results[0]!.description).toBe('Open help documentation') mockT.mockImplementation((key: string) => key) }) @@ -82,8 +82,8 @@ describe('docsCommand', () => { it('registered handler opens doc URL with correct locale', async () => { docsCommand.register?.({} as Record) const openSpy = vi.spyOn(window, 'open').mockImplementation(() => null) - const handlers = vi.mocked(registerCommands).mock.calls[0][0] - await handlers['navigation.doc']() + const handlers = vi.mocked(registerCommands).mock.calls[0]![0] + await handlers['navigation.doc']!() expect(openSpy).toHaveBeenCalledWith('https://docs.dify.ai/en', '_blank', 'noopener,noreferrer') openSpy.mockRestore() @@ -183,7 +183,7 @@ describe('communityCommand', () => { const results = await communityCommand.search('', 'en') - expect(results[0].description).toBe('Open Discord community') + expect(results[0]!.description).toBe('Open Discord community') mockT.mockImplementation((key: string) => key) }) @@ -195,8 +195,8 @@ describe('communityCommand', () => { it('registered handler opens URL from args', async () => { communityCommand.register?.({} as Record) const openSpy = vi.spyOn(window, 'open').mockImplementation(() => null) - const handlers = vi.mocked(registerCommands).mock.calls[0][0] - await handlers['navigation.community']({ url: 'https://custom-url.com' }) + const handlers = vi.mocked(registerCommands).mock.calls[0]![0] + await handlers['navigation.community']!({ url: 'https://custom-url.com' }) expect(openSpy).toHaveBeenCalledWith('https://custom-url.com', '_blank', 'noopener,noreferrer') openSpy.mockRestore() @@ -205,8 +205,8 @@ describe('communityCommand', () => { it('registered handler falls back to default URL when no args', async () => { communityCommand.register?.({} as Record) const openSpy = vi.spyOn(window, 'open').mockImplementation(() => null) - const handlers = vi.mocked(registerCommands).mock.calls[0][0] - await handlers['navigation.community']() + const handlers = vi.mocked(registerCommands).mock.calls[0]![0] + await handlers['navigation.community']!() expect(openSpy).toHaveBeenCalledWith('https://discord.gg/5AEfbxcd9k', '_blank', 'noopener,noreferrer') openSpy.mockRestore() @@ -260,7 +260,7 @@ describe('forumCommand', () => { const results = await forumCommand.search('', 'en') - expect(results[0].description).toBe('Open community feedback discussions') + expect(results[0]!.description).toBe('Open community feedback discussions') mockT.mockImplementation((key: string) => key) }) @@ -272,8 +272,8 @@ describe('forumCommand', () => { it('registered handler opens URL from args', async () => { forumCommand.register?.({} as Record) const openSpy = vi.spyOn(window, 'open').mockImplementation(() => null) - const handlers = vi.mocked(registerCommands).mock.calls[0][0] - await handlers['navigation.forum']({ url: 'https://custom-forum.com' }) + const handlers = vi.mocked(registerCommands).mock.calls[0]![0] + await handlers['navigation.forum']!({ url: 'https://custom-forum.com' }) expect(openSpy).toHaveBeenCalledWith('https://custom-forum.com', '_blank', 'noopener,noreferrer') openSpy.mockRestore() @@ -282,8 +282,8 @@ describe('forumCommand', () => { it('registered handler falls back to default URL when no args', async () => { forumCommand.register?.({} as Record) const openSpy = vi.spyOn(window, 'open').mockImplementation(() => null) - const handlers = vi.mocked(registerCommands).mock.calls[0][0] - await handlers['navigation.forum']() + const handlers = vi.mocked(registerCommands).mock.calls[0]![0] + await handlers['navigation.forum']!() expect(openSpy).toHaveBeenCalledWith('https://forum.dify.ai', '_blank', 'noopener,noreferrer') openSpy.mockRestore() diff --git a/web/app/components/goto-anything/actions/commands/__tests__/go.spec.tsx b/web/app/components/goto-anything/actions/commands/__tests__/go.spec.tsx index 719eddf77b..82cdfa330c 100644 --- a/web/app/components/goto-anything/actions/commands/__tests__/go.spec.tsx +++ b/web/app/components/goto-anything/actions/commands/__tests__/go.spec.tsx @@ -40,15 +40,15 @@ describe('goCommand', () => { const results = await goCommand.search('plugins', 'en') expect(results).toHaveLength(1) - expect(results[0].id).toBe('go-plugins') + expect(results[0]!.id).toBe('go-plugins') }) it('filters by label match (case-insensitive)', async () => { const results = await goCommand.search('Knowledge', 'en') expect(results).toHaveLength(1) - expect(results[0].id).toBe('go-datasets') - expect(results[0].title).toBe('Knowledge') + expect(results[0]!.id).toBe('go-datasets') + expect(results[0]!.title).toBe('Knowledge') }) it('returns command results with navigation.go data', async () => { @@ -85,9 +85,9 @@ describe('goCommand', () => { it('registered handler navigates to the provided path', async () => { Object.defineProperty(window, 'location', { value: { href: '' }, writable: true }) goCommand.register?.({} as Record) - const handlers = vi.mocked(registerCommands).mock.calls[0][0] + const handlers = vi.mocked(registerCommands).mock.calls[0]![0] - await handlers['navigation.go']({ path: '/datasets' }) + await handlers['navigation.go']!({ path: '/datasets' }) expect(window.location.href).toBe('/datasets') }) @@ -95,10 +95,10 @@ describe('goCommand', () => { it('registered handler does nothing when path is missing', async () => { Object.defineProperty(window, 'location', { value: { href: '/current' }, writable: true }) goCommand.register?.({} as Record) - const handlers = vi.mocked(registerCommands).mock.calls[0][0] + const handlers = vi.mocked(registerCommands).mock.calls[0]![0] - await handlers['navigation.go']() - await handlers['navigation.go']({}) + await handlers['navigation.go']!() + await handlers['navigation.go']!({}) expect(window.location.href).toBe('/current') }) diff --git a/web/app/components/goto-anything/actions/commands/__tests__/language.spec.ts b/web/app/components/goto-anything/actions/commands/__tests__/language.spec.ts index 54aa28d24a..da6ede6a3a 100644 --- a/web/app/components/goto-anything/actions/commands/__tests__/language.spec.ts +++ b/web/app/components/goto-anything/actions/commands/__tests__/language.spec.ts @@ -42,14 +42,14 @@ describe('languageCommand', () => { const results = await languageCommand.search('english', 'en') expect(results).toHaveLength(1) - expect(results[0].id).toBe('lang-en-US') + expect(results[0]!.id).toBe('lang-en-US') }) it('filters languages by value query', async () => { const results = await languageCommand.search('zh', 'en') expect(results).toHaveLength(1) - expect(results[0].id).toBe('lang-zh-Hans') + expect(results[0]!.id).toBe('lang-zh-Hans') }) it('returns command data with i18n.set command', async () => { diff --git a/web/app/components/goto-anything/actions/commands/__tests__/registry.spec.ts b/web/app/components/goto-anything/actions/commands/__tests__/registry.spec.ts index 2a13ffd1ea..d2623be434 100644 --- a/web/app/components/goto-anything/actions/commands/__tests__/registry.spec.ts +++ b/web/app/components/goto-anything/actions/commands/__tests__/registry.spec.ts @@ -202,7 +202,7 @@ describe('SlashCommandRegistry', () => { const results = await registry.search('/hem') expect(results).toHaveLength(1) - expect(results[0].title).toBe('/theme') + expect(results[0]!.title).toBe('/theme') }) it('fuzzy search also matches aliases', async () => { @@ -236,7 +236,7 @@ describe('SlashCommandRegistry', () => { const results = await registry.search('/') expect(results).toHaveLength(1) - expect(results[0].title).toBe('/docs') + expect(results[0]!.title).toBe('/docs') }) it('skips unavailable handler in exact match', async () => { diff --git a/web/app/components/goto-anything/actions/commands/__tests__/theme.spec.ts b/web/app/components/goto-anything/actions/commands/__tests__/theme.spec.ts index 3dd45aad11..4a79da5518 100644 --- a/web/app/components/goto-anything/actions/commands/__tests__/theme.spec.ts +++ b/web/app/components/goto-anything/actions/commands/__tests__/theme.spec.ts @@ -41,7 +41,7 @@ describe('themeCommand', () => { const results = await themeCommand.search('dark', 'en') expect(results).toHaveLength(1) - expect(results[0].id).toBe('dark') + expect(results[0]!.id).toBe('dark') }) }) diff --git a/web/app/components/goto-anything/command-selector.tsx b/web/app/components/goto-anything/command-selector.tsx index 96385abd0a..93430eafc4 100644 --- a/web/app/components/goto-anything/command-selector.tsx +++ b/web/app/components/goto-anything/command-selector.tsx @@ -64,7 +64,7 @@ const CommandSelector: FC = ({ actions, onCommandSelect, searchFilter, co if (allItems.length > 0 && onCommandValueChange) { const currentValueExists = allItems.some(item => item.shortcut === commandValue) if (!currentValueExists) - onCommandValueChange(allItems[0].shortcut) + onCommandValueChange(allItems[0]!.shortcut) } }, [searchFilter, allItems.length]) diff --git a/web/app/components/goto-anything/hooks/__tests__/use-goto-anything-results.spec.ts b/web/app/components/goto-anything/hooks/__tests__/use-goto-anything-results.spec.ts index b1b543d35a..e5393a05a9 100644 --- a/web/app/components/goto-anything/hooks/__tests__/use-goto-anything-results.spec.ts +++ b/web/app/components/goto-anything/hooks/__tests__/use-goto-anything-results.spec.ts @@ -111,8 +111,8 @@ describe('useGotoAnythingResults', () => { const { result } = renderHook(() => useGotoAnythingResults(createMockOptions())) expect(result.current.dedupedResults).toHaveLength(2) - expect(result.current.dedupedResults[0].id).toBe('1') - expect(result.current.dedupedResults[1].id).toBe('2') + expect(result.current.dedupedResults[0]!.id).toBe('1') + expect(result.current.dedupedResults[1]!.id).toBe('2') }) it('should keep first occurrence when duplicates exist', () => { @@ -129,7 +129,7 @@ describe('useGotoAnythingResults', () => { const { result } = renderHook(() => useGotoAnythingResults(createMockOptions())) expect(result.current.dedupedResults).toHaveLength(1) - expect(result.current.dedupedResults[0].title).toBe('First') + expect(result.current.dedupedResults[0]!.title).toBe('First') }) it('should handle different types with same id', () => { diff --git a/web/app/components/goto-anything/hooks/use-goto-anything-results.ts b/web/app/components/goto-anything/hooks/use-goto-anything-results.ts index 36e8397b6f..2f9174fc53 100644 --- a/web/app/components/goto-anything/hooks/use-goto-anything-results.ts +++ b/web/app/components/goto-anything/hooks/use-goto-anything-results.ts @@ -110,7 +110,7 @@ export const useGotoAnythingResults = ( if (!acc[result.type]) acc[result.type] = [] - acc[result.type].push(result) + acc[result.type]!.push(result) return acc }, {} as Record), [dedupedResults]) @@ -125,7 +125,7 @@ export const useGotoAnythingResults = ( const currentValueExists = dedupedResults.some(result => `${result.type}-${result.id}` === cmdVal) if (!currentValueExists) - setCmdVal(`${dedupedResults[0].type}-${dedupedResults[0].id}`) + setCmdVal(`${dedupedResults[0]!.type}-${dedupedResults[0]!.id}`) }, [isCommandsMode, dedupedResults, cmdVal, setCmdVal]) return { diff --git a/web/app/components/goto-anything/index.tsx b/web/app/components/goto-anything/index.tsx index 829a95f7e7..f3bf40f62a 100644 --- a/web/app/components/goto-anything/index.tsx +++ b/web/app/components/goto-anything/index.tsx @@ -111,7 +111,7 @@ const GotoAnything: FC = ({ // Check if it's a complete slash command if (query.startsWith('/')) { const commandName = query.substring(1).split(' ')[0] - const handler = slashCommandRegistry.findCommand(commandName) + const handler = slashCommandRegistry.findCommand(commandName!) // If it's a direct mode command, execute immediately const isAvailable = handler?.isAvailable?.() ?? true diff --git a/web/app/components/header/account-dropdown/__tests__/compliance.spec.tsx b/web/app/components/header/account-dropdown/__tests__/compliance.spec.tsx index 8165434b3a..15439b157f 100644 --- a/web/app/components/header/account-dropdown/__tests__/compliance.spec.tsx +++ b/web/app/components/header/account-dropdown/__tests__/compliance.spec.tsx @@ -99,7 +99,8 @@ describe('Compliance', () => { renderCompliance() // Assert - expect(screen.getByText('common.userProfile.compliance')).toBeInTheDocument() + // Assert + expect(screen.getByText('common.userProfile.compliance'))!.toBeInTheDocument() }) it('should show SOC2, ISO, GDPR items when opened', () => { @@ -107,10 +108,11 @@ describe('Compliance', () => { openMenuAndRender() // Assert - expect(screen.getByText('common.compliance.soc2Type1')).toBeInTheDocument() - expect(screen.getByText('common.compliance.soc2Type2')).toBeInTheDocument() - expect(screen.getByText('common.compliance.iso27001')).toBeInTheDocument() - expect(screen.getByText('common.compliance.gdpr')).toBeInTheDocument() + // Assert + expect(screen.getByText('common.compliance.soc2Type1'))!.toBeInTheDocument() + expect(screen.getByText('common.compliance.soc2Type2'))!.toBeInTheDocument() + expect(screen.getByText('common.compliance.iso27001'))!.toBeInTheDocument() + expect(screen.getByText('common.compliance.gdpr'))!.toBeInTheDocument() }) }) @@ -158,7 +160,7 @@ describe('Compliance', () => { // Act openMenuAndRender() const downloadButtons = screen.getAllByText('common.operation.download') - fireEvent.click(downloadButtons[0]) + fireEvent.click(downloadButtons[0]!) // Assert await waitFor(() => { @@ -183,7 +185,7 @@ describe('Compliance', () => { // Act openMenuAndRender() const downloadButtons = screen.getAllByText('common.operation.download') - fireEvent.click(downloadButtons[0]) + fireEvent.click(downloadButtons[0]!) // Assert await waitFor(() => { @@ -198,7 +200,7 @@ describe('Compliance', () => { // Act openMenuAndRender() const upgradeBadges = screen.getAllByText('billing.upgradeBtn.encourageShort') - fireEvent.click(upgradeBadges[0]) + fireEvent.click(upgradeBadges[0]!) // Assert expect(mockSetShowPricingModal).toHaveBeenCalled() @@ -218,7 +220,7 @@ describe('Compliance', () => { openMenuAndRender() // SOC2 Type II is restricted for professional const upgradeBadges = screen.getAllByText('billing.upgradeBtn.encourageShort') - fireEvent.click(upgradeBadges[0]) + fireEvent.click(upgradeBadges[0]!) // Assert expect(mockSetShowAccountSettingModal).toHaveBeenCalledWith({ @@ -251,7 +253,7 @@ describe('Compliance', () => { await waitFor(() => { const busyButton = menuItem!.querySelector('button[aria-busy="true"]') expect(busyButton).not.toBeNull() - expect(busyButton).toBeDisabled() + expect(busyButton)!.toBeDisabled() expect(busyButton!.querySelector('.animate-spin')).not.toBeNull() }, { timeout: 10000 }) @@ -286,7 +288,7 @@ describe('Compliance', () => { await waitFor(() => { const busyButton = menuItem!.querySelector('button[aria-busy="true"]') expect(busyButton).not.toBeNull() - expect(busyButton).toBeDisabled() + expect(busyButton)!.toBeDisabled() expect(getDocDownloadUrl).toHaveBeenCalledTimes(1) }, { timeout: 10000 }) diff --git a/web/app/components/header/account-dropdown/workplace-selector/__tests__/index.spec.tsx b/web/app/components/header/account-dropdown/workplace-selector/__tests__/index.spec.tsx index 338096a9e1..e1d5216555 100644 --- a/web/app/components/header/account-dropdown/workplace-selector/__tests__/index.spec.tsx +++ b/web/app/components/header/account-dropdown/workplace-selector/__tests__/index.spec.tsx @@ -115,11 +115,11 @@ describe('WorkplaceSelector', () => { it('should render current workspace and available workspace options', () => { renderComponent() - expect(screen.getByTestId('workplace-selector-trigger')).toHaveTextContent('Workspace 1') - expect(screen.getByTestId('workspace-option-1')).toBeInTheDocument() - expect(screen.getByTestId('workspace-option-2')).toBeInTheDocument() - expect(screen.getByTestId('workspace-option-1')).toHaveTextContent('Workspace 1') - expect(screen.getByTestId('workspace-option-2')).toHaveTextContent('Workspace 2') + expect(screen.getByTestId('workplace-selector-trigger'))!.toHaveTextContent('Workspace 1') + expect(screen.getByTestId('workspace-option-1'))!.toBeInTheDocument() + expect(screen.getByTestId('workspace-option-2'))!.toBeInTheDocument() + expect(screen.getByTestId('workspace-option-1'))!.toHaveTextContent('Workspace 1') + expect(screen.getByTestId('workspace-option-2'))!.toHaveTextContent('Workspace 2') }) }) @@ -127,7 +127,7 @@ describe('WorkplaceSelector', () => { it('should switch workspace successfully', async () => { vi.mocked(switchWorkspace).mockResolvedValue({ result: 'success', - new_tenant: mockWorkspaces[1], + new_tenant: mockWorkspaces[1]!, }) renderComponent() diff --git a/web/app/components/header/account-setting/__tests__/index.spec.tsx b/web/app/components/header/account-setting/__tests__/index.spec.tsx index d4e093bd30..d37b0bbcf2 100644 --- a/web/app/components/header/account-setting/__tests__/index.spec.tsx +++ b/web/app/components/header/account-setting/__tests__/index.spec.tsx @@ -200,13 +200,14 @@ describe('AccountSetting', () => { renderAccountSetting() // Assert - expect(screen.getByText('common.userProfile.settings')).toBeInTheDocument() - expect(screen.getByText('common.settings.provider')).toBeInTheDocument() + // Assert + expect(screen.getByText('common.userProfile.settings'))!.toBeInTheDocument() + expect(screen.getByText('common.settings.provider'))!.toBeInTheDocument() expect(screen.getAllByText('common.settings.members').length).toBeGreaterThan(0) - expect(screen.getByText('common.settings.billing')).toBeInTheDocument() - expect(screen.getByText('common.settings.dataSource')).toBeInTheDocument() - expect(screen.getByText('common.settings.apiBasedExtension')).toBeInTheDocument() - expect(screen.getByText('custom.custom')).toBeInTheDocument() + expect(screen.getByText('common.settings.billing'))!.toBeInTheDocument() + expect(screen.getByText('common.settings.dataSource'))!.toBeInTheDocument() + expect(screen.getByText('common.settings.apiBasedExtension'))!.toBeInTheDocument() + expect(screen.getByText('custom.custom'))!.toBeInTheDocument() expect(screen.getAllByText('common.settings.language').length).toBeGreaterThan(0) }) @@ -228,6 +229,68 @@ describe('AccountSetting', () => { // Act renderAccountSetting() + // Assert + // On mobile, the labels should not be rendered as per the implementation + // Assert + // On mobile, the labels should not be rendered as per the implementation + // Assert + // On mobile, the labels should not be rendered as per the implementation + // Assert + // On mobile, the labels should not be rendered as per the implementation + // Assert + // On mobile, the labels should not be rendered as per the implementation + // Assert + // On mobile, the labels should not be rendered as per the implementation + // Assert + // On mobile, the labels should not be rendered as per the implementation + // Assert + // On mobile, the labels should not be rendered as per the implementation + // Assert + // On mobile, the labels should not be rendered as per the implementation + // Assert + // On mobile, the labels should not be rendered as per the implementation + // Assert + // On mobile, the labels should not be rendered as per the implementation + // Assert + // On mobile, the labels should not be rendered as per the implementation + // Assert + // On mobile, the labels should not be rendered as per the implementation + // Assert + // On mobile, the labels should not be rendered as per the implementation + // Assert + // On mobile, the labels should not be rendered as per the implementation + // Assert + // On mobile, the labels should not be rendered as per the implementation + // Assert + // On mobile, the labels should not be rendered as per the implementation + // Assert + // On mobile, the labels should not be rendered as per the implementation + // Assert + // On mobile, the labels should not be rendered as per the implementation + // Assert + // On mobile, the labels should not be rendered as per the implementation + // Assert + // On mobile, the labels should not be rendered as per the implementation + // Assert + // On mobile, the labels should not be rendered as per the implementation + // Assert + // On mobile, the labels should not be rendered as per the implementation + // Assert + // On mobile, the labels should not be rendered as per the implementation + // Assert + // On mobile, the labels should not be rendered as per the implementation + // Assert + // On mobile, the labels should not be rendered as per the implementation + // Assert + // On mobile, the labels should not be rendered as per the implementation + // Assert + // On mobile, the labels should not be rendered as per the implementation + // Assert + // On mobile, the labels should not be rendered as per the implementation + // Assert + // On mobile, the labels should not be rendered as per the implementation + // Assert + // On mobile, the labels should not be rendered as per the implementation // Assert // On mobile, the labels should not be rendered as per the implementation expect(screen.queryByText('common.settings.provider')).not.toBeInTheDocument() @@ -243,10 +306,41 @@ describe('AccountSetting', () => { // Act renderAccountSetting() + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert // Assert expect(screen.queryByText('common.settings.provider')).not.toBeInTheDocument() expect(screen.queryByText('common.settings.members')).not.toBeInTheDocument() - expect(screen.getByText('common.settings.language')).toBeInTheDocument() + expect(screen.getByText('common.settings.language'))!.toBeInTheDocument() }) it('should hide billing and custom tabs when disabled', () => { @@ -260,6 +354,37 @@ describe('AccountSetting', () => { // Act renderAccountSetting() + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert // Assert expect(screen.queryByText('common.settings.billing')).not.toBeInTheDocument() expect(screen.queryByText('custom.custom')).not.toBeInTheDocument() @@ -277,7 +402,8 @@ describe('AccountSetting', () => { // Assert expect(mockOnTabChange).toHaveBeenCalledWith(ACCOUNT_SETTING_TAB.PROVIDER) // Check for content from ModelProviderPage - expect(screen.getByText('common.modelProvider.models')).toBeInTheDocument() + // Check for content from ModelProviderPage + expect(screen.getByText('common.modelProvider.models'))!.toBeInTheDocument() }) it('should navigate through various tabs and show correct details', () => { @@ -304,11 +430,11 @@ describe('AccountSetting', () => { expect(screen.getAllByText('custom.custom').length).toBeGreaterThan(1) // Language - fireEvent.click(screen.getAllByText('common.settings.language')[0]) + fireEvent.click(screen.getAllByText('common.settings.language')[0]!) expect(screen.getAllByText('common.settings.language').length).toBeGreaterThan(1) // Members - fireEvent.click(screen.getAllByText('common.settings.members')[0]) + fireEvent.click(screen.getAllByText('common.settings.members')[0]!) expect(screen.getAllByText('common.settings.members').length).toBeGreaterThan(1) }) }) @@ -344,8 +470,9 @@ describe('AccountSetting', () => { fireEvent.change(input, { target: { value: 'test-search' } }) // Assert - expect(input).toHaveValue('test-search') - expect(screen.getByText('common.modelProvider.models')).toBeInTheDocument() + // Assert + expect(input)!.toHaveValue('test-search') + expect(screen.getByText('common.modelProvider.models'))!.toBeInTheDocument() }) it('should handle scroll event in panel', () => { @@ -354,11 +481,12 @@ describe('AccountSetting', () => { const scrollContainer = screen.getByRole('dialog').querySelector('.overscroll-contain') // Assert - expect(scrollContainer).toBeInTheDocument() + // Assert + expect(scrollContainer)!.toBeInTheDocument() if (scrollContainer) { // Scroll down fireEvent.scroll(scrollContainer, { target: { scrollTop: 100 } }) - expect(scrollContainer).toHaveClass('overscroll-contain') + expect(scrollContainer)!.toHaveClass('overscroll-contain') // Scroll back up fireEvent.scroll(scrollContainer, { target: { scrollTop: 0 } }) diff --git a/web/app/components/header/account-setting/api-based-extension-page/__tests__/index.spec.tsx b/web/app/components/header/account-setting/api-based-extension-page/__tests__/index.spec.tsx index 58b694e44e..adac0f7c6c 100644 --- a/web/app/components/header/account-setting/api-based-extension-page/__tests__/index.spec.tsx +++ b/web/app/components/header/account-setting/api-based-extension-page/__tests__/index.spec.tsx @@ -38,7 +38,8 @@ describe('ApiBasedExtensionPage', () => { render() // Assert - expect(screen.getByText('common.apiBasedExtension.title')).toBeInTheDocument() + // Assert + expect(screen.getByText('common.apiBasedExtension.title'))!.toBeInTheDocument() }) it('should render list of extensions when data exists', () => { @@ -58,10 +59,11 @@ describe('ApiBasedExtensionPage', () => { render() // Assert - expect(screen.getByText('Extension 1')).toBeInTheDocument() - expect(screen.getByText('url1')).toBeInTheDocument() - expect(screen.getByText('Extension 2')).toBeInTheDocument() - expect(screen.getByText('url2')).toBeInTheDocument() + // Assert + expect(screen.getByText('Extension 1'))!.toBeInTheDocument() + expect(screen.getByText('url1'))!.toBeInTheDocument() + expect(screen.getByText('Extension 2'))!.toBeInTheDocument() + expect(screen.getByText('url2'))!.toBeInTheDocument() }) it('should handle loading state', () => { @@ -75,9 +77,40 @@ describe('ApiBasedExtensionPage', () => { // Act render() + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert // Assert expect(screen.queryByText('common.apiBasedExtension.title')).not.toBeInTheDocument() - expect(screen.getByText('common.apiBasedExtension.add')).toBeInTheDocument() + expect(screen.getByText('common.apiBasedExtension.add'))!.toBeInTheDocument() }) }) @@ -113,7 +146,7 @@ describe('ApiBasedExtensionPage', () => { fireEvent.click(screen.getByText('common.apiBasedExtension.add')) // Trigger callback manually from the mock call - const callArgs = mockSetShowApiBasedExtensionModal.mock.calls[0][0] + const callArgs = mockSetShowApiBasedExtensionModal.mock.calls[0]![0] if (typeof callArgs === 'object' && callArgs !== null && 'onSaveCallback' in callArgs) { if (callArgs.onSaveCallback) { callArgs.onSaveCallback() @@ -138,7 +171,7 @@ describe('ApiBasedExtensionPage', () => { fireEvent.click(screen.getByText('common.operation.edit')) // Retrieve the onSaveCallback from the modal call and execute it - const callArgs = mockSetShowApiBasedExtensionModal.mock.calls[0][0] + const callArgs = mockSetShowApiBasedExtensionModal.mock.calls[0]![0] if (typeof callArgs === 'object' && callArgs !== null && 'onSaveCallback' in callArgs) { if (callArgs.onSaveCallback) callArgs.onSaveCallback() diff --git a/web/app/components/header/account-setting/api-based-extension-page/__tests__/item.spec.tsx b/web/app/components/header/account-setting/api-based-extension-page/__tests__/item.spec.tsx index cf5c46a509..38dcf9dec5 100644 --- a/web/app/components/header/account-setting/api-based-extension-page/__tests__/item.spec.tsx +++ b/web/app/components/header/account-setting/api-based-extension-page/__tests__/item.spec.tsx @@ -39,8 +39,9 @@ describe('Item Component', () => { render() // Assert - expect(screen.getByText('Test Extension')).toBeInTheDocument() - expect(screen.getByText('https://api.example.com')).toBeInTheDocument() + // Assert + expect(screen.getByText('Test Extension'))!.toBeInTheDocument() + expect(screen.getByText('https://api.example.com'))!.toBeInTheDocument() }) it('should render with minimal extension data', () => { @@ -51,8 +52,9 @@ describe('Item Component', () => { render() // Assert - expect(screen.getByText('common.operation.edit')).toBeInTheDocument() - expect(screen.getByText('common.operation.delete')).toBeInTheDocument() + // Assert + expect(screen.getByText('common.operation.edit'))!.toBeInTheDocument() + expect(screen.getByText('common.operation.delete'))!.toBeInTheDocument() }) }) @@ -66,7 +68,7 @@ describe('Item Component', () => { expect(mockSetShowApiBasedExtensionModal).toHaveBeenCalledWith(expect.objectContaining({ payload: mockData, })) - const lastCall = mockSetShowApiBasedExtensionModal.mock.calls[0][0] + const lastCall = mockSetShowApiBasedExtensionModal.mock.calls[0]![0] if (typeof lastCall === 'object' && lastCall !== null && 'onSaveCallback' in lastCall) expect(lastCall.onSaveCallback).toBeInstanceOf(Function) }) @@ -77,7 +79,7 @@ describe('Item Component', () => { fireEvent.click(screen.getByText('common.operation.edit')) // Assert - const modalCallArg = mockSetShowApiBasedExtensionModal.mock.calls[0][0] + const modalCallArg = mockSetShowApiBasedExtensionModal.mock.calls[0]![0] if (typeof modalCallArg === 'object' && modalCallArg !== null && 'onSaveCallback' in modalCallArg) { const onSaveCallback = modalCallArg.onSaveCallback if (onSaveCallback) { @@ -95,7 +97,8 @@ describe('Item Component', () => { fireEvent.click(screen.getByText('common.operation.delete')) // Assert - expect(screen.getByText(/common\.operation\.delete.*Test Extension.*\?/i)).toBeInTheDocument() + // Assert + expect(screen.getByText(/common\.operation\.delete.*Test Extension.*\?/i))!.toBeInTheDocument() }) it('should call delete API and triggers onUpdate when confirming deletion', async () => { @@ -188,7 +191,8 @@ describe('Item Component', () => { fireEvent.click(deleteBtn) // Assert - expect(screen.getByText(/.*Test Extension.*\?/i)).toBeInTheDocument() + // Assert + expect(screen.getByText(/.*Test Extension.*\?/i))!.toBeInTheDocument() useTranslationSpy.mockRestore() }) diff --git a/web/app/components/header/account-setting/api-based-extension-page/__tests__/selector.spec.tsx b/web/app/components/header/account-setting/api-based-extension-page/__tests__/selector.spec.tsx index 3e16528ff0..82acd22d97 100644 --- a/web/app/components/header/account-setting/api-based-extension-page/__tests__/selector.spec.tsx +++ b/web/app/components/header/account-setting/api-based-extension-page/__tests__/selector.spec.tsx @@ -46,7 +46,8 @@ describe('ApiBasedExtensionSelector', () => { render() // Assert - expect(screen.getByText('common.apiBasedExtension.selector.placeholder')).toBeInTheDocument() + // Assert + expect(screen.getByText('common.apiBasedExtension.selector.placeholder'))!.toBeInTheDocument() }) it('should render selected item name', async () => { @@ -54,7 +55,8 @@ describe('ApiBasedExtensionSelector', () => { render() // Assert - expect(screen.getByText('Extension 1')).toBeInTheDocument() + // Assert + expect(screen.getByText('Extension 1'))!.toBeInTheDocument() }) }) @@ -66,7 +68,8 @@ describe('ApiBasedExtensionSelector', () => { fireEvent.click(trigger) // Assert - expect(await screen.findByText('common.apiBasedExtension.selector.title')).toBeInTheDocument() + // Assert + expect(await screen.findByText('common.apiBasedExtension.selector.title'))!.toBeInTheDocument() }) it('should call onChange and closes dropdown when an extension is selected', async () => { @@ -111,7 +114,7 @@ describe('ApiBasedExtensionSelector', () => { })) // Trigger callback - const lastCall = mockSetShowApiBasedExtensionModal.mock.calls[0][0] + const lastCall = mockSetShowApiBasedExtensionModal.mock.calls[0]![0] if (typeof lastCall === 'object' && lastCall !== null && 'onSaveCallback' in lastCall) { if (lastCall.onSaveCallback) { lastCall.onSaveCallback() diff --git a/web/app/components/header/account-setting/data-source-page-new/__tests__/card.spec.tsx b/web/app/components/header/account-setting/data-source-page-new/__tests__/card.spec.tsx index 12e37553fc..fedb7d8a4e 100644 --- a/web/app/components/header/account-setting/data-source-page-new/__tests__/card.spec.tsx +++ b/web/app/components/header/account-setting/data-source-page-new/__tests__/card.spec.tsx @@ -110,7 +110,7 @@ describe('Card Component', () => { type: CredentialTypeEnum.API_KEY, is_default: true, avatar_url: 'avatar1', - }, + }!, ], } @@ -144,11 +144,12 @@ describe('Card Component', () => { render() // Assert - expect(screen.getByText('Test Label')).toBeInTheDocument() - expect(screen.getByText(/Test Author/)).toBeInTheDocument() - expect(screen.getByText(/test-name/)).toBeInTheDocument() - expect(screen.getByRole('img')).toHaveAttribute('src', 'test-icon-url') - expect(screen.getByText('Credential 1')).toBeInTheDocument() + // Assert + expect(screen.getByText('Test Label'))!.toBeInTheDocument() + expect(screen.getByText(/Test Author/))!.toBeInTheDocument() + expect(screen.getByText(/test-name/))!.toBeInTheDocument() + expect(screen.getByRole('img'))!.toHaveAttribute('src', 'test-icon-url') + expect(screen.getByText('Credential 1'))!.toBeInTheDocument() expect(usePluginAuthAction).toHaveBeenCalledWith( expect.objectContaining({ @@ -168,7 +169,8 @@ describe('Card Component', () => { render() // Assert - expect(screen.getByText(/plugin.auth.emptyAuth/)).toBeInTheDocument() + // Assert + expect(screen.getByText(/plugin.auth.emptyAuth/))!.toBeInTheDocument() }) }) @@ -218,7 +220,7 @@ describe('Card Component', () => { const oAuthItem = { ...mockItem, credentials_list: [{ - ...mockItem.credentials_list[0], + ...mockItem.credentials_list[0]!, type: CredentialTypeEnum.OAUTH2, }], } @@ -245,7 +247,7 @@ describe('Card Component', () => { const oAuthItem = { ...mockItem, credentials_list: [{ - ...mockItem.credentials_list[0], + ...mockItem.credentials_list[0]!, type: CredentialTypeEnum.OAUTH2, }], } @@ -268,7 +270,7 @@ describe('Card Component', () => { const oAuthItem = { ...mockItem, credentials_list: [{ - ...mockItem.credentials_list[0], + ...mockItem.credentials_list[0]!, type: CredentialTypeEnum.OAUTH2, }], } @@ -297,9 +299,10 @@ describe('Card Component', () => { render() // Assert - expect(screen.getByText(/list.delete.title/)).toBeInTheDocument() + // Assert + expect(screen.getByText(/list.delete.title/))!.toBeInTheDocument() const confirmButton = screen.getByText(/operation.confirm/).closest('button') - expect(confirmButton).toBeEnabled() + expect(confirmButton)!.toBeEnabled() // Act - Cancel fireEvent.click(screen.getByText(/operation.cancel/)) @@ -317,8 +320,9 @@ describe('Card Component', () => { render() // Assert - expect(screen.getByTestId('mock-api-key-modal')).toBeInTheDocument() - expect(screen.getByTestId('mock-api-key-modal')).toHaveAttribute('data-disabled', 'false') + // Assert + expect(screen.getByTestId('mock-api-key-modal'))!.toBeInTheDocument() + expect(screen.getByTestId('mock-api-key-modal'))!.toHaveAttribute('data-disabled', 'false') // Act fireEvent.click(screen.getByTestId('modal-close')) @@ -337,7 +341,8 @@ describe('Card Component', () => { render() // Assert - expect(screen.getByTestId('mock-api-key-modal')).toHaveAttribute('data-disabled', 'true') + // Assert + expect(screen.getByTestId('mock-api-key-modal'))!.toHaveAttribute('data-disabled', 'true') }) }) diff --git a/web/app/components/header/account-setting/data-source-page-new/hooks/use-marketplace-all-plugins.ts b/web/app/components/header/account-setting/data-source-page-new/hooks/use-marketplace-all-plugins.ts index 90ef6e78a4..7e2dc572fc 100644 --- a/web/app/components/header/account-setting/data-source-page-new/hooks/use-marketplace-all-plugins.ts +++ b/web/app/components/header/account-setting/data-source-page-new/hooks/use-marketplace-all-plugins.ts @@ -54,8 +54,8 @@ export const useMarketplaceAllPlugins = (providers: any[], searchText: string) = for (let i = 0; i < plugins.length; i++) { const plugin = plugins[i] - if (plugin.type !== 'bundle' && !allPlugins.find(p => p.plugin_id === plugin.plugin_id)) - allPlugins.push(plugin) + if (plugin!.type !== 'bundle' && !allPlugins.find(p => p.plugin_id === plugin!.plugin_id)) + allPlugins.push(plugin!) } } diff --git a/web/app/components/header/account-setting/index.tsx b/web/app/components/header/account-setting/index.tsx index 64dff27cf6..3ec6dea315 100644 --- a/web/app/components/header/account-setting/index.tsx +++ b/web/app/components/header/account-setting/index.tsx @@ -130,7 +130,7 @@ export default function AccountSetting({ ], }, ] - const activeItem = [...menuItems[0].items, ...menuItems[1].items].find(item => item.key === activeMenu) + const activeItem = [...menuItems[0]!.items, ...menuItems[1]!.items].find(item => item.key === activeMenu) const [searchValue, setSearchValue] = useState('') diff --git a/web/app/components/header/account-setting/members-page/__tests__/index.spec.tsx b/web/app/components/header/account-setting/members-page/__tests__/index.spec.tsx index 9513eeee02..b50073b1f2 100644 --- a/web/app/components/header/account-setting/members-page/__tests__/index.spec.tsx +++ b/web/app/components/header/account-setting/members-page/__tests__/index.spec.tsx @@ -129,9 +129,9 @@ describe('MembersPage', () => { it('should render workspace and member information', () => { render() - expect(screen.getByText('Test Workspace')).toBeInTheDocument() - expect(screen.getByText('Owner User')).toBeInTheDocument() - expect(screen.getByText('Admin User')).toBeInTheDocument() + expect(screen.getByText('Test Workspace'))!.toBeInTheDocument() + expect(screen.getByText('Owner User'))!.toBeInTheDocument() + expect(screen.getByText('Admin User'))!.toBeInTheDocument() }) it('should open and close invite modal', async () => { @@ -140,7 +140,7 @@ describe('MembersPage', () => { render() await user.click(screen.getByRole('button', { name: /invite/i })) - expect(screen.getByText('Invite Modal')).toBeInTheDocument() + expect(screen.getByText('Invite Modal'))!.toBeInTheDocument() await user.click(screen.getByRole('button', { name: 'Close Invite Modal' })) expect(screen.queryByText('Invite Modal')).not.toBeInTheDocument() @@ -154,7 +154,7 @@ describe('MembersPage', () => { await user.click(screen.getByRole('button', { name: /invite/i })) await user.click(screen.getByRole('button', { name: 'Send Invite Results' })) - expect(screen.getByText('Invited Modal')).toBeInTheDocument() + expect(screen.getByText('Invited Modal'))!.toBeInTheDocument() expect(mockRefetch).toHaveBeenCalled() await user.click(screen.getByRole('button', { name: 'Close Invited Modal' })) @@ -167,7 +167,7 @@ describe('MembersPage', () => { render() await user.click(screen.getByRole('button', { name: /transfer ownership/i })) - expect(screen.getByText('Transfer Ownership Modal')).toBeInTheDocument() + expect(screen.getByText('Transfer Ownership Modal'))!.toBeInTheDocument() }) it('should show non-interactive owner role when transfer ownership is not allowed', () => { @@ -178,7 +178,7 @@ describe('MembersPage', () => { render() - expect(screen.getByText('common.members.owner')).toBeInTheDocument() + expect(screen.getByText('common.members.owner'))!.toBeInTheDocument() expect(screen.queryByRole('button', { name: /transfer ownership/i })).not.toBeInTheDocument() }) @@ -202,7 +202,7 @@ describe('MembersPage', () => { render() await user.click(screen.getByTestId('edit-workspace-pencil')) - expect(screen.getByText('Edit Workspace Modal')).toBeInTheDocument() + expect(screen.getByText('Edit Workspace Modal'))!.toBeInTheDocument() await user.click(screen.getByRole('button', { name: 'Close Edit Workspace' })) expect(screen.queryByText('Edit Workspace Modal')).not.toBeInTheDocument() @@ -214,7 +214,7 @@ describe('MembersPage', () => { render() await user.click(screen.getByRole('button', { name: /transfer ownership/i })) - expect(screen.getByText('Transfer Ownership Modal')).toBeInTheDocument() + expect(screen.getByText('Transfer Ownership Modal'))!.toBeInTheDocument() await user.click(screen.getByRole('button', { name: 'Close Transfer Modal' })) expect(screen.queryByText('Transfer Ownership Modal')).not.toBeInTheDocument() @@ -222,7 +222,7 @@ describe('MembersPage', () => { it('should show pending status and you indicator', () => { const pendingAccount: Member = { - ...mockAccounts[1], + ...mockAccounts[1]!, status: 'pending', } vi.mocked(useMembers).mockReturnValue({ @@ -232,8 +232,8 @@ describe('MembersPage', () => { render() - expect(screen.getByText(/members\.pending/i)).toBeInTheDocument() - expect(screen.getByText(/members\.you/i)).toBeInTheDocument() // Current user is owner@example.com + expect(screen.getByText(/members\.pending/i))!.toBeInTheDocument() + expect(screen.getByText(/members\.you/i))!.toBeInTheDocument() // Current user is owner@example.com }) it('should show billing information for limited plan', () => { @@ -247,10 +247,10 @@ describe('MembersPage', () => { render() - expect(screen.getByText(/plansCommon\.member/i)).toBeInTheDocument() - expect(screen.getByText('2')).toBeInTheDocument() // accounts.length - expect(screen.getByText('/')).toBeInTheDocument() - expect(screen.getByText('5')).toBeInTheDocument() // plan.total.teamMembers + expect(screen.getByText(/plansCommon\.member/i))!.toBeInTheDocument() + expect(screen.getByText('2'))!.toBeInTheDocument() // accounts.length + expect(screen.getByText('/'))!.toBeInTheDocument() + expect(screen.getByText('5'))!.toBeInTheDocument() // plan.total.teamMembers }) it('should show unlimited billing information', () => { @@ -264,7 +264,7 @@ describe('MembersPage', () => { render() - expect(screen.getByText(/plansCommon\.unlimited/i)).toBeInTheDocument() + expect(screen.getByText(/plansCommon\.unlimited/i))!.toBeInTheDocument() }) it('should show non-billing member format for team plan even when billing is enabled', () => { @@ -279,7 +279,8 @@ describe('MembersPage', () => { render() // Plan.team is an unlimited member plan → isNotUnlimitedMemberPlan=false → non-billing layout - expect(screen.getByText(/plansCommon\.memberAfter/i)).toBeInTheDocument() + // Plan.team is an unlimited member plan → isNotUnlimitedMemberPlan=false → non-billing layout + expect(screen.getByText(/plansCommon\.memberAfter/i))!.toBeInTheDocument() }) it('should show invite button when user is manager but not owner', () => { @@ -292,13 +293,13 @@ describe('MembersPage', () => { render() - expect(screen.getByRole('button', { name: /invite/i })).toBeInTheDocument() + expect(screen.getByRole('button', { name: /invite/i }))!.toBeInTheDocument() expect(screen.queryByRole('button', { name: /transfer ownership/i })).not.toBeInTheDocument() }) it('should use created_at as fallback when last_active_at is empty', () => { const memberNoLastActive: Member = { - ...mockAccounts[1], + ...mockAccounts[1]!, last_active_at: '', created_at: '1700000000', } @@ -327,8 +328,8 @@ describe('MembersPage', () => { render() - expect(screen.getByText(/plansCommon\.member/i)).toBeInTheDocument() - expect(screen.getByText('1')).toBeInTheDocument() + expect(screen.getByText(/plansCommon\.member/i))!.toBeInTheDocument() + expect(screen.getByText('1'))!.toBeInTheDocument() }) it('should not show plural s when only one account in non-billing layout', () => { @@ -339,8 +340,8 @@ describe('MembersPage', () => { render() - expect(screen.getByText(/plansCommon\.memberAfter/i)).toBeInTheDocument() - expect(screen.getByText('1')).toBeInTheDocument() + expect(screen.getByText(/plansCommon\.memberAfter/i))!.toBeInTheDocument() + expect(screen.getByText('1'))!.toBeInTheDocument() }) it('should show normal role as fallback for unknown role', () => { @@ -357,7 +358,7 @@ describe('MembersPage', () => { render() - expect(screen.getByText('common.members.normal')).toBeInTheDocument() + expect(screen.getByText('common.members.normal'))!.toBeInTheDocument() }) it('should show upgrade button when member limit is full', () => { @@ -371,6 +372,6 @@ describe('MembersPage', () => { render() - expect(screen.getByText('Upgrade Button')).toBeInTheDocument() + expect(screen.getByText('Upgrade Button'))!.toBeInTheDocument() }) }) diff --git a/web/app/components/header/account-setting/model-provider-page/__tests__/hooks.spec.ts b/web/app/components/header/account-setting/model-provider-page/__tests__/hooks.spec.ts index 1d50ee43b0..9b592e9fff 100644 --- a/web/app/components/header/account-setting/model-provider-page/__tests__/hooks.spec.ts +++ b/web/app/components/header/account-setting/model-provider-page/__tests__/hooks.spec.ts @@ -758,7 +758,7 @@ describe('hooks', () => { expect(result.current.textGenerationModelList).toEqual(modelList) expect(result.current.activeTextGenerationModelList).toHaveLength(1) - expect(result.current.activeTextGenerationModelList[0].provider).toBe('openai') + expect(result.current.activeTextGenerationModelList[0]!.provider).toBe('openai') }) it('should filter active models correctly', () => { @@ -770,7 +770,7 @@ describe('hooks', () => { const { result } = renderHook(() => useTextGenerationCurrentProviderAndModelAndModelList()) expect(result.current.activeTextGenerationModelList).toHaveLength(1) - expect(result.current.activeTextGenerationModelList[0].status).toBe(ModelStatusEnum.active) + expect(result.current.activeTextGenerationModelList[0]!.status).toBe(ModelStatusEnum.active) }) it('should find current provider and model', () => { @@ -1145,7 +1145,7 @@ describe('hooks', () => { const { result } = renderHook(() => useMarketplaceAllPlugins(providers, '')) expect(result.current.plugins!).toHaveLength(1) - expect(result.current.plugins![0].plugin_id).toBe('other') + expect(result.current.plugins![0]!.plugin_id).toBe('other') }) it('should use search when searchText is provided', () => { @@ -1186,7 +1186,7 @@ describe('hooks', () => { const { result } = renderHook(() => useMarketplaceAllPlugins([], '')) expect(result.current.plugins!).toHaveLength(1) - expect(result.current.plugins![0].plugin_id).toBe('plugin1') + expect(result.current.plugins![0]!.plugin_id).toBe('plugin1') }) it('should deduplicate plugins that exist in both collections and regular plugins', () => { @@ -1613,7 +1613,7 @@ describe('hooks', () => { ) }) - const callArgs = setShowModelModal.mock.calls[0][0] + const callArgs = setShowModelModal.mock.calls[0]![0] const newPayload = { test: 'data' } const formValues = { field: 'value' } @@ -1636,7 +1636,7 @@ describe('hooks', () => { result.current(provider, ConfigurationMethodEnum.predefinedModel) }) - const callArgs = setShowModelModal.mock.calls[0][0] + const callArgs = setShowModelModal.mock.calls[0]![0] // Should not throw when onUpdate is not provided expect(() => { diff --git a/web/app/components/header/account-setting/model-provider-page/__tests__/utils.spec.ts b/web/app/components/header/account-setting/model-provider-page/__tests__/utils.spec.ts index d49343e0c6..e2a2aea22c 100644 --- a/web/app/components/header/account-setting/model-provider-page/__tests__/utils.spec.ts +++ b/web/app/components/header/account-setting/model-provider-page/__tests__/utils.spec.ts @@ -277,7 +277,7 @@ describe('utils', () => { const schema = genModelTypeFormSchema([ModelTypeEnum.textGeneration]) expect(schema.type).toBe(FormTypeEnum.select) expect(schema.variable).toBe('__model_type') - expect(schema.options[0].value).toBe(ModelTypeEnum.textGeneration) + expect(schema.options[0]!.value).toBe(ModelTypeEnum.textGeneration) }) }) diff --git a/web/app/components/header/account-setting/model-provider-page/hooks.ts b/web/app/components/header/account-setting/model-provider-page/hooks.ts index 791a42deb5..27b84f81bb 100644 --- a/web/app/components/header/account-setting/model-provider-page/hooks.ts +++ b/web/app/components/header/account-setting/model-provider-page/hooks.ts @@ -313,8 +313,8 @@ export const useMarketplaceAllPlugins = (providers: ModelProvider[], searchText: for (let i = 0; i < plugins.length; i++) { const plugin = plugins[i] - if (plugin.type !== 'bundle' && !allPlugins.find(p => p.plugin_id === plugin.plugin_id)) - allPlugins.push(plugin) + if (plugin!.type !== 'bundle' && !allPlugins.find(p => p.plugin_id === plugin!.plugin_id)) + allPlugins.push(plugin!) } } diff --git a/web/app/components/header/account-setting/model-provider-page/model-auth/__tests__/add-credential-in-load-balancing.spec.tsx b/web/app/components/header/account-setting/model-provider-page/model-auth/__tests__/add-credential-in-load-balancing.spec.tsx index bf26880a8b..c72264dd7a 100644 --- a/web/app/components/header/account-setting/model-provider-page/model-auth/__tests__/add-credential-in-load-balancing.spec.tsx +++ b/web/app/components/header/account-setting/model-provider-page/model-auth/__tests__/add-credential-in-load-balancing.spec.tsx @@ -18,7 +18,7 @@ vi.mock('@/app/components/header/account-setting/model-provider-page/model-auth'
{renderTrigger(false)} - +
), })) @@ -57,7 +57,7 @@ describe('AddCredentialInLoadBalancing', () => { />, ) - expect(screen.getByText(/modelProvider.auth.addCredential/i)).toBeInTheDocument() + expect(screen.getByText(/modelProvider.auth.addCredential/i))!.toBeInTheDocument() }) it('should forward update payload when update action happens', () => { @@ -127,7 +127,7 @@ describe('AddCredentialInLoadBalancing', () => { // The trigger div rendered by renderTrigger(true) should have bg-state-base-hover // (the static class applied when open=true via cn()) const triggerDiv = container.querySelector('[data-testid="open-trigger"] > div') - expect(triggerDiv).toBeInTheDocument() + expect(triggerDiv)!.toBeInTheDocument() expect(triggerDiv!.className).toContain('bg-state-base-hover') } finally { @@ -148,7 +148,7 @@ describe('AddCredentialInLoadBalancing', () => { />, ) - expect(screen.getByText(/modelProvider.auth.addCredential/i)).toBeInTheDocument() + expect(screen.getByText(/modelProvider.auth.addCredential/i))!.toBeInTheDocument() }) it('should handle undefined available_credentials gracefully using nullish coalescing', () => { @@ -169,7 +169,8 @@ describe('AddCredentialInLoadBalancing', () => { ) // Component should render without error - the ?? [] fallback is used - expect(screen.getByText(/modelProvider.auth.addCredential/i)).toBeInTheDocument() + // Component should render without error - the ?? [] fallback is used + expect(screen.getByText(/modelProvider.auth.addCredential/i))!.toBeInTheDocument() }) it('should not throw when update action fires without onUpdate prop', () => { diff --git a/web/app/components/header/account-setting/model-provider-page/model-auth/__tests__/switch-credential-in-load-balancing.spec.tsx b/web/app/components/header/account-setting/model-provider-page/model-auth/__tests__/switch-credential-in-load-balancing.spec.tsx index 9b58984af9..0232adcf4a 100644 --- a/web/app/components/header/account-setting/model-provider-page/model-auth/__tests__/switch-credential-in-load-balancing.spec.tsx +++ b/web/app/components/header/account-setting/model-provider-page/model-auth/__tests__/switch-credential-in-load-balancing.spec.tsx @@ -7,7 +7,7 @@ import SwitchCredentialInLoadBalancing from '../switch-credential-in-load-balanc vi.mock('../authorized', () => ({ default: ({ renderTrigger, onItemClick, items }: { renderTrigger: () => React.ReactNode, onItemClick: (c: unknown) => void, items: { credentials: unknown[] }[] }) => (
-
onItemClick(items[0].credentials[0])}> +
onItemClick(items[0]!.credentials[0])}> {renderTrigger()}
@@ -55,8 +55,8 @@ describe('SwitchCredentialInLoadBalancing', () => { />, ) - expect(screen.getByText('Key 1')).toBeInTheDocument() - expect(screen.getByTestId('indicator-green')).toBeInTheDocument() + expect(screen.getByText('Key 1'))!.toBeInTheDocument() + expect(screen.getByTestId('indicator-green'))!.toBeInTheDocument() }) it('should render auth removed status when selected credential is not in list', () => { @@ -70,8 +70,8 @@ describe('SwitchCredentialInLoadBalancing', () => { />, ) - expect(screen.getByText(/modelProvider.auth.authRemoved/)).toBeInTheDocument() - expect(screen.getByTestId('indicator-red')).toBeInTheDocument() + expect(screen.getByText(/modelProvider.auth.authRemoved/))!.toBeInTheDocument() + expect(screen.getByTestId('indicator-red'))!.toBeInTheDocument() }) it('should render unavailable status when credentials list is empty', () => { @@ -85,7 +85,7 @@ describe('SwitchCredentialInLoadBalancing', () => { />, ) - expect(screen.getByText(/auth.credentialUnavailableInButton/)).toBeInTheDocument() + expect(screen.getByText(/auth.credentialUnavailableInButton/))!.toBeInTheDocument() expect(screen.queryByTestId(/indicator-/)).not.toBeInTheDocument() }) @@ -117,7 +117,7 @@ describe('SwitchCredentialInLoadBalancing', () => { ) fireEvent.mouseEnter(screen.getByText(/auth.credentialUnavailableInButton/)) - expect(screen.getByText('plugin.auth.credentialUnavailable')).toBeInTheDocument() + expect(screen.getByText('plugin.auth.credentialUnavailable'))!.toBeInTheDocument() }) // Empty credentials with allowed custom: no tooltip but still shows unavailable text @@ -134,7 +134,8 @@ describe('SwitchCredentialInLoadBalancing', () => { ) // Assert - expect(screen.getByText(/auth.credentialUnavailableInButton/)).toBeInTheDocument() + // Assert + expect(screen.getByText(/auth.credentialUnavailableInButton/))!.toBeInTheDocument() expect(screen.queryByText('plugin.auth.credentialUnavailable')).not.toBeInTheDocument() }) @@ -152,8 +153,8 @@ describe('SwitchCredentialInLoadBalancing', () => { />, ) - expect(screen.getByTestId('indicator-red')).toBeInTheDocument() - expect(screen.getByText(/auth.credentialUnavailableInButton/)).toBeInTheDocument() + expect(screen.getByTestId('indicator-red'))!.toBeInTheDocument() + expect(screen.getByText(/auth.credentialUnavailableInButton/))!.toBeInTheDocument() }) // from_enterprise=true on the selected credential: Enterprise badge appears in the trigger @@ -170,7 +171,7 @@ describe('SwitchCredentialInLoadBalancing', () => { />, ) - expect(screen.getByText('Enterprise')).toBeInTheDocument() + expect(screen.getByText('Enterprise'))!.toBeInTheDocument() }) // non-empty credentials with allow_custom_token=false: no tooltip (tooltip only for empty+notAllowCustom) @@ -189,7 +190,7 @@ describe('SwitchCredentialInLoadBalancing', () => { fireEvent.mouseEnter(screen.getByText('Key 1')) expect(screen.queryByText('plugin.auth.credentialUnavailable')).not.toBeInTheDocument() - expect(screen.getByText('Key 1')).toBeInTheDocument() + expect(screen.getByText('Key 1'))!.toBeInTheDocument() }) it('should pass undefined currentCustomConfigurationModelFixedFields when model is undefined', () => { @@ -205,8 +206,9 @@ describe('SwitchCredentialInLoadBalancing', () => { ) // Component still renders (Authorized receives undefined currentCustomConfigurationModelFixedFields) - expect(screen.getByTestId('authorized-mock')).toBeInTheDocument() - expect(screen.getByText('Key 1')).toBeInTheDocument() + // Component still renders (Authorized receives undefined currentCustomConfigurationModelFixedFields) + expect(screen.getByTestId('authorized-mock'))!.toBeInTheDocument() + expect(screen.getByText('Key 1'))!.toBeInTheDocument() }) it('should treat undefined credentials as empty list', () => { @@ -221,7 +223,8 @@ describe('SwitchCredentialInLoadBalancing', () => { ) // credentials is undefined → empty=true → unavailable text shown - expect(screen.getByText(/auth.credentialUnavailableInButton/)).toBeInTheDocument() + // credentials is undefined → empty=true → unavailable text shown + expect(screen.getByText(/auth.credentialUnavailableInButton/))!.toBeInTheDocument() expect(screen.queryByTestId(/indicator-/)).not.toBeInTheDocument() }) @@ -239,7 +242,39 @@ describe('SwitchCredentialInLoadBalancing', () => { ) // indicator-green shown (not authRemoved, not unavailable, not empty) - expect(screen.getByTestId('indicator-green')).toBeInTheDocument() + // indicator-green shown (not authRemoved, not unavailable, not empty) + expect(screen.getByTestId('indicator-green'))!.toBeInTheDocument() + // credential_name is empty so nothing printed for name + // credential_name is empty so nothing printed for name + // credential_name is empty so nothing printed for name + // credential_name is empty so nothing printed for name + // credential_name is empty so nothing printed for name + // credential_name is empty so nothing printed for name + // credential_name is empty so nothing printed for name + // credential_name is empty so nothing printed for name + // credential_name is empty so nothing printed for name + // credential_name is empty so nothing printed for name + // credential_name is empty so nothing printed for name + // credential_name is empty so nothing printed for name + // credential_name is empty so nothing printed for name + // credential_name is empty so nothing printed for name + // credential_name is empty so nothing printed for name + // credential_name is empty so nothing printed for name + // credential_name is empty so nothing printed for name + // credential_name is empty so nothing printed for name + // credential_name is empty so nothing printed for name + // credential_name is empty so nothing printed for name + // credential_name is empty so nothing printed for name + // credential_name is empty so nothing printed for name + // credential_name is empty so nothing printed for name + // credential_name is empty so nothing printed for name + // credential_name is empty so nothing printed for name + // credential_name is empty so nothing printed for name + // credential_name is empty so nothing printed for name + // credential_name is empty so nothing printed for name + // credential_name is empty so nothing printed for name + // credential_name is empty so nothing printed for name + // credential_name is empty so nothing printed for name // credential_name is empty so nothing printed for name expect(screen.queryByText('Key 1')).not.toBeInTheDocument() }) diff --git a/web/app/components/header/account-setting/model-provider-page/model-auth/authorized/__tests__/authorized-item.spec.tsx b/web/app/components/header/account-setting/model-provider-page/model-auth/authorized/__tests__/authorized-item.spec.tsx index 57e80fa989..c0ce70a5d0 100644 --- a/web/app/components/header/account-setting/model-provider-page/model-auth/authorized/__tests__/authorized-item.spec.tsx +++ b/web/app/components/header/account-setting/model-provider-page/model-auth/authorized/__tests__/authorized-item.spec.tsx @@ -51,10 +51,10 @@ describe('AuthorizedItem', () => { />, ) - expect(screen.getByTestId('credential-item-cred-1')).toBeInTheDocument() - expect(screen.getByTestId('credential-item-cred-2')).toBeInTheDocument() - expect(screen.getByText('API Key 1')).toBeInTheDocument() - expect(screen.getByText('API Key 2')).toBeInTheDocument() + expect(screen.getByTestId('credential-item-cred-1'))!.toBeInTheDocument() + expect(screen.getByTestId('credential-item-cred-2'))!.toBeInTheDocument() + expect(screen.getByText('API Key 1'))!.toBeInTheDocument() + expect(screen.getByText('API Key 2'))!.toBeInTheDocument() }) it('should render model title when showModelTitle is true', () => { @@ -67,7 +67,7 @@ describe('AuthorizedItem', () => { />, ) - expect(screen.getByTestId('model-icon')).toBeInTheDocument() + expect(screen.getByTestId('model-icon'))!.toBeInTheDocument() expect(screen.getAllByText('gpt-4')).toHaveLength(2) }) @@ -94,7 +94,7 @@ describe('AuthorizedItem', () => { />, ) - expect(screen.getByText('Custom Title')).toBeInTheDocument() + expect(screen.getByText('Custom Title'))!.toBeInTheDocument() }) it('should handle empty credentials array', () => { @@ -122,7 +122,7 @@ describe('AuthorizedItem', () => { />, ) - screen.getAllByText('Edit')[0].click() + screen.getAllByText('Edit')[0]!.click() expect(onEdit).toHaveBeenCalledWith(mockCredentials[0], mockModel) }) @@ -139,7 +139,7 @@ describe('AuthorizedItem', () => { />, ) - screen.getAllByText('Delete')[0].click() + screen.getAllByText('Delete')[0]!.click() expect(onDelete).toHaveBeenCalledWith(mockCredentials[0], mockModel) }) @@ -156,7 +156,7 @@ describe('AuthorizedItem', () => { />, ) - screen.getAllByText('Click')[0].click() + screen.getAllByText('Click')[0]!.click() expect(onItemClick).toHaveBeenCalledWith(mockCredentials[0], mockModel) }) diff --git a/web/app/components/header/account-setting/model-provider-page/model-auth/authorized/__tests__/index.spec.tsx b/web/app/components/header/account-setting/model-provider-page/model-auth/authorized/__tests__/index.spec.tsx index e1d9a611f9..a331181619 100644 --- a/web/app/components/header/account-setting/model-provider-page/model-auth/authorized/__tests__/index.spec.tsx +++ b/web/app/components/header/account-setting/model-provider-page/model-auth/authorized/__tests__/index.spec.tsx @@ -90,8 +90,8 @@ describe('Authorized', () => { ) fireEvent.click(screen.getByRole('button', { name: /trigger\s*closed/i })) - expect(screen.getByTestId('authorized-item')).toBeInTheDocument() - expect(screen.getByRole('button', { name: /addApiKey/i })).toBeInTheDocument() + expect(screen.getByTestId('authorized-item'))!.toBeInTheDocument() + expect(screen.getByRole('button', { name: /addApiKey/i }))!.toBeInTheDocument() }) it('should call handleOpenModal when triggerOnlyOpenModal is true', () => { @@ -123,9 +123,9 @@ describe('Authorized', () => { ) fireEvent.click(screen.getByRole('button', { name: /trigger\s*closed/i })) - fireEvent.click(screen.getAllByRole('button', { name: 'Select' })[0]) + fireEvent.click(screen.getAllByRole('button', { name: 'Select' })[0]!) - expect(onItemClick).toHaveBeenCalledWith(mockCredentials[0], mockItems[0].model) + expect(onItemClick).toHaveBeenCalledWith(mockCredentials[0], mockItems[0]!.model) }) it('should call handleActiveCredential when onItemClick is not provided', () => { @@ -139,9 +139,9 @@ describe('Authorized', () => { ) fireEvent.click(screen.getByRole('button', { name: /trigger\s*closed/i })) - fireEvent.click(screen.getAllByRole('button', { name: 'Select' })[0]) + fireEvent.click(screen.getAllByRole('button', { name: 'Select' })[0]!) - expect(mockHandleActiveCredential).toHaveBeenCalledWith(mockCredentials[0], mockItems[0].model) + expect(mockHandleActiveCredential).toHaveBeenCalledWith(mockCredentials[0], mockItems[0]!.model) }) it('should call handleOpenModal with fixed model fields when adding model credential', () => { @@ -231,6 +231,6 @@ describe('Authorized', () => { ) const dialog = screen.getByRole('alertdialog') - expect(within(dialog).getByRole('button', { name: /common.operation.confirm/i })).toBeDisabled() + expect(within(dialog).getByRole('button', { name: /common.operation.confirm/i }))!.toBeDisabled() }) }) diff --git a/web/app/components/header/account-setting/model-provider-page/model-auth/hooks/__tests__/use-auth-service.spec.tsx b/web/app/components/header/account-setting/model-provider-page/model-auth/hooks/__tests__/use-auth-service.spec.tsx index bd4f857047..7cc1445041 100644 --- a/web/app/components/header/account-setting/model-provider-page/model-auth/hooks/__tests__/use-auth-service.spec.tsx +++ b/web/app/components/header/account-setting/model-provider-page/model-auth/hooks/__tests__/use-auth-service.spec.tsx @@ -80,15 +80,15 @@ describe('useAuthService hooks', () => { const { result } = renderHook(() => useAuthService('openai'), { wrapper }) // Provider services - expect(result.current.getAddCredentialService(false)).toBe(vi.mocked(useAddProviderCredential).mock.results[0].value.mutateAsync) - expect(result.current.getEditCredentialService(false)).toBe(vi.mocked(useEditProviderCredential).mock.results[0].value.mutateAsync) - expect(result.current.getDeleteCredentialService(false)).toBe(vi.mocked(useDeleteProviderCredential).mock.results[0].value.mutateAsync) - expect(result.current.getActiveCredentialService(false)).toBe(vi.mocked(useActiveProviderCredential).mock.results[0].value.mutateAsync) + expect(result.current.getAddCredentialService(false)).toBe(vi.mocked(useAddProviderCredential).mock.results[0]!.value.mutateAsync) + expect(result.current.getEditCredentialService(false)).toBe(vi.mocked(useEditProviderCredential).mock.results[0]!.value.mutateAsync) + expect(result.current.getDeleteCredentialService(false)).toBe(vi.mocked(useDeleteProviderCredential).mock.results[0]!.value.mutateAsync) + expect(result.current.getActiveCredentialService(false)).toBe(vi.mocked(useActiveProviderCredential).mock.results[0]!.value.mutateAsync) // Model services - expect(result.current.getAddCredentialService(true)).toBe(vi.mocked(useAddModelCredential).mock.results[0].value.mutateAsync) - expect(result.current.getEditCredentialService(true)).toBe(vi.mocked(useEditModelCredential).mock.results[0].value.mutateAsync) - expect(result.current.getDeleteCredentialService(true)).toBe(vi.mocked(useDeleteModelCredential).mock.results[0].value.mutateAsync) - expect(result.current.getActiveCredentialService(true)).toBe(vi.mocked(useActiveModelCredential).mock.results[0].value.mutateAsync) + expect(result.current.getAddCredentialService(true)).toBe(vi.mocked(useAddModelCredential).mock.results[0]!.value.mutateAsync) + expect(result.current.getEditCredentialService(true)).toBe(vi.mocked(useEditModelCredential).mock.results[0]!.value.mutateAsync) + expect(result.current.getDeleteCredentialService(true)).toBe(vi.mocked(useDeleteModelCredential).mock.results[0]!.value.mutateAsync) + expect(result.current.getActiveCredentialService(true)).toBe(vi.mocked(useActiveModelCredential).mock.results[0]!.value.mutateAsync) }) }) diff --git a/web/app/components/header/account-setting/model-provider-page/model-auth/hooks/__tests__/use-custom-models.spec.tsx b/web/app/components/header/account-setting/model-provider-page/model-auth/hooks/__tests__/use-custom-models.spec.tsx index 2b5804c3f1..c98ead095f 100644 --- a/web/app/components/header/account-setting/model-provider-page/model-auth/hooks/__tests__/use-custom-models.spec.tsx +++ b/web/app/components/header/account-setting/model-provider-page/model-auth/hooks/__tests__/use-custom-models.spec.tsx @@ -15,7 +15,7 @@ describe('useCustomModels and useCanAddedModels', () => { const { result } = renderHook(() => useCustomModels(mockProvider)) expect(result.current).toHaveLength(2) - expect(result.current[0].model).toBe('gpt-4') + expect(result.current[0]!.model).toBe('gpt-4') const { result: emptyRes } = renderHook(() => useCustomModels({ custom_configuration: {} } as unknown as ModelProvider)) expect(emptyRes.current).toEqual([]) @@ -30,7 +30,7 @@ describe('useCustomModels and useCanAddedModels', () => { const { result } = renderHook(() => useCanAddedModels(mockProvider)) expect(result.current).toHaveLength(1) - expect(result.current[0].model).toBe('gpt-4-turbo') + expect(result.current[0]!.model).toBe('gpt-4-turbo') const { result: emptyRes } = renderHook(() => useCanAddedModels({ custom_configuration: {} } as unknown as ModelProvider)) expect(emptyRes.current).toEqual([]) diff --git a/web/app/components/header/account-setting/model-provider-page/model-auth/hooks/__tests__/use-model-form-schemas.spec.tsx b/web/app/components/header/account-setting/model-provider-page/model-auth/hooks/__tests__/use-model-form-schemas.spec.tsx index 237d99c7e7..dbe513163b 100644 --- a/web/app/components/header/account-setting/model-provider-page/model-auth/hooks/__tests__/use-model-form-schemas.spec.tsx +++ b/web/app/components/header/account-setting/model-provider-page/model-auth/hooks/__tests__/use-model-form-schemas.spec.tsx @@ -69,7 +69,7 @@ describe('useModelFormSchemas', () => { const { result: custom } = renderHook(() => useModelFormSchemas(mockProvider, false)) expect(custom.current.modelNameAndTypeFormSchemas).toHaveLength(2) - expect(custom.current.modelNameAndTypeFormSchemas[0].variable).toBe('__model_name') + expect(custom.current.modelNameAndTypeFormSchemas[0]!.variable).toBe('__model_name') const mockModel = { model: 'custom', model_type: 'text' } as unknown as CustomModelCredential const { result: customWithVal } = renderHook(() => useModelFormSchemas(mockProvider, false, undefined, undefined, mockModel)) diff --git a/web/app/components/header/account-setting/model-provider-page/model-modal/__tests__/Form.spec.tsx b/web/app/components/header/account-setting/model-provider-page/model-modal/__tests__/Form.spec.tsx index 14120ec08c..bf1623fbef 100644 --- a/web/app/components/header/account-setting/model-provider-page/model-modal/__tests__/Form.spec.tsx +++ b/web/app/components/header/account-setting/model-provider-page/model-modal/__tests__/Form.spec.tsx @@ -192,9 +192,9 @@ describe('Form', () => { />, ) - expect(screen.getByPlaceholderText('API Key')).toHaveValue('default-key') - expect(screen.getByPlaceholderText('Secret')).toHaveValue('top-secret') - expect(screen.getByPlaceholderText('Limit')).toHaveValue(5) + expect(screen.getByPlaceholderText('API Key'))!.toHaveValue('default-key') + expect(screen.getByPlaceholderText('Secret'))!.toHaveValue('top-secret') + expect(screen.getByPlaceholderText('Limit'))!.toHaveValue(5) expect(screen.queryByText('Hidden')).not.toBeInTheDocument() expect(screen.getAllByText('*')).toHaveLength(1) }) @@ -234,7 +234,7 @@ describe('Form', () => { await waitFor(() => { expect(onChange).toHaveBeenCalledWith({ api_key: 'new-key', dependent: 'reset' }) - expect(screen.getByText('Validating...')).toBeInTheDocument() + expect(screen.getByText('Validating...'))!.toBeInTheDocument() }) }) @@ -279,7 +279,7 @@ describe('Form', () => { />, ) - expect(screen.getByText('EU')).toBeInTheDocument() + expect(screen.getByText('EU'))!.toBeInTheDocument() expect(screen.queryByText('Hidden Region')).not.toBeInTheDocument() fireEvent.click(screen.getByText('EU')) fireEvent.click(screen.getByText('Locked A')) @@ -338,7 +338,7 @@ describe('Form', () => { />, ) - expect(screen.getByText('Select A')).toBeInTheDocument() + expect(screen.getByText('Select A'))!.toBeInTheDocument() fireEvent.click(screen.getByText('Select A')) fireEvent.click(screen.getByText('Select B')) @@ -459,12 +459,12 @@ describe('Form', () => { />, ) - expect(screen.getByText('Override Field')).toBeInTheDocument() - expect(screen.getByText(/Custom Render:.*custom_field/)).toBeInTheDocument() + expect(screen.getByText('Override Field'))!.toBeInTheDocument() + expect(screen.getByText(/Custom Render:.*custom_field/))!.toBeInTheDocument() expect(screen.getAllByText('allowed')).toHaveLength(3) expect(screen.getAllByText('blocked')).toHaveLength(1) - fireEvent.click(screen.getAllByText('Pick Variable')[0]) + fireEvent.click(screen.getAllByText('Pick Variable')[0]!) expect(onChange).toHaveBeenCalledWith({ override: '', any_var: [{ name: 'var-1' }], any_without_scope: [], custom_field: '' }) expect(screen.getAllByText('Extra Info')).toHaveLength(2) @@ -497,7 +497,8 @@ describe('Form', () => { ) // Assert - expect(screen.getByPlaceholderText('API Key')).toBeDisabled() + // Assert + expect(screen.getByPlaceholderText('API Key'))!.toBeDisabled() }) // Override returns null: falls through to default renderer @@ -528,7 +529,8 @@ describe('Form', () => { ) // Assert - should fall through to default textInput renderer - expect(screen.getByPlaceholderText('Field 1')).toBeInTheDocument() + // Assert - should fall through to default textInput renderer + expect(screen.getByPlaceholderText('Field 1'))!.toBeInTheDocument() }) // isShowDefaultValue=true, value is null → default shown @@ -559,7 +561,8 @@ describe('Form', () => { ) // Assert - expect(screen.getByPlaceholderText('Nullable')).toHaveValue('default-val') + // Assert + expect(screen.getByPlaceholderText('Nullable'))!.toHaveValue('default-val') }) // isShowDefaultValue=true, value is undefined → default shown @@ -590,7 +593,8 @@ describe('Form', () => { ) // Assert - expect(screen.getByPlaceholderText('Undef')).toHaveValue('default-undef') + // Assert + expect(screen.getByPlaceholderText('Undef'))!.toHaveValue('default-undef') }) // isEditMode=true, variable=__model_type → textInput disabled @@ -619,7 +623,8 @@ describe('Form', () => { ) // Assert - expect(screen.getByPlaceholderText('Model Type')).toBeDisabled() + // Assert + expect(screen.getByPlaceholderText('Model Type'))!.toBeDisabled() }) // Label with missing language key → en_US fallback used @@ -649,7 +654,8 @@ describe('Form', () => { ) // Assert - expect(screen.getByText('English Label')).toBeInTheDocument() + // Assert + expect(screen.getByText('English Label'))!.toBeInTheDocument() }) // Select field with isShowDefaultValue=true @@ -680,7 +686,8 @@ describe('Form', () => { ) // Assert - Select B should be the rendered default - expect(screen.getByText('Select B')).toBeInTheDocument() + // Assert - Select B should be the rendered default + expect(screen.getByText('Select B'))!.toBeInTheDocument() }) // Radio option with show_on condition not met → option filtered out @@ -712,7 +719,8 @@ describe('Form', () => { ) // Assert - expect(screen.getByText('Always Visible')).toBeInTheDocument() + // Assert + expect(screen.getByText('Always Visible'))!.toBeInTheDocument() expect(screen.queryByText('Conditional')).not.toBeInTheDocument() }) @@ -804,8 +812,9 @@ describe('Form', () => { ) // Both rendered successfully - expect(screen.getByPlaceholderText('Secret')).toBeInTheDocument() - expect(screen.getByPlaceholderText('Number')).toBeInTheDocument() + // Both rendered successfully + expect(screen.getByPlaceholderText('Secret'))!.toBeInTheDocument() + expect(screen.getByPlaceholderText('Number'))!.toBeInTheDocument() }) // Placeholder fallback: null placeholder @@ -830,7 +839,7 @@ describe('Form', () => { />, ) - expect(screen.getByText('No Placeholder')).toBeInTheDocument() + expect(screen.getByText('No Placeholder'))!.toBeInTheDocument() }) // validating=true + changeKey matches variable: ValidatingTip shown @@ -866,7 +875,8 @@ describe('Form', () => { fireEvent.change(screen.getByPlaceholderText('API Key'), { target: { value: 'new' } }) // ValidatingTip should appear for api_key - expect(screen.getByText('Validating...')).toBeInTheDocument() + // ValidatingTip should appear for api_key + expect(screen.getByText('Validating...'))!.toBeInTheDocument() }) // Select with show_on not met: hidden @@ -923,7 +933,7 @@ describe('Form', () => { />, ) - expect(screen.getByText('Always')).toBeInTheDocument() + expect(screen.getByText('Always'))!.toBeInTheDocument() expect(screen.queryByText('Conditional')).not.toBeInTheDocument() }) @@ -1009,7 +1019,7 @@ describe('Form', () => { />, ) - expect(screen.getByPlaceholderText('Field')).toHaveValue('') + expect(screen.getByPlaceholderText('Field'))!.toHaveValue('') }) // Radio with disabled=true in edit mode for __model_type @@ -1066,7 +1076,7 @@ describe('Form', () => { />, ) - expect(screen.getByText('Select Tools')).toBeInTheDocument() + expect(screen.getByText('Select Tools'))!.toBeInTheDocument() }) // Override with non-matching type: falls through to default @@ -1095,7 +1105,7 @@ describe('Form', () => { ) expect(screen.queryByText('Override Hit')).not.toBeInTheDocument() - expect(screen.getByPlaceholderText('Secret Field')).toBeInTheDocument() + expect(screen.getByPlaceholderText('Secret Field'))!.toBeInTheDocument() }) // Select with isShowDefaultValue: null value shows default @@ -1123,7 +1133,7 @@ describe('Form', () => { />, ) - expect(screen.getByText('Select B')).toBeInTheDocument() + expect(screen.getByText('Select B'))!.toBeInTheDocument() }) // Select with isShowDefaultValue: undefined value shows default @@ -1151,7 +1161,7 @@ describe('Form', () => { />, ) - expect(screen.getByText('Select A')).toBeInTheDocument() + expect(screen.getByText('Select A'))!.toBeInTheDocument() }) // No fieldMoreInfo: should not crash @@ -1177,7 +1187,7 @@ describe('Form', () => { />, ) - expect(screen.getByPlaceholderText('Field 1')).toBeInTheDocument() + expect(screen.getByPlaceholderText('Field 1'))!.toBeInTheDocument() }) it('should render tooltip when schema has tooltip property', () => { @@ -1220,10 +1230,10 @@ describe('Form', () => { />, ) - expect(screen.getByText('API Key')).toBeInTheDocument() - expect(screen.getByText('Region')).toBeInTheDocument() - expect(screen.getByText('Model')).toBeInTheDocument() - expect(screen.getByText('Agree')).toBeInTheDocument() + expect(screen.getByText('API Key'))!.toBeInTheDocument() + expect(screen.getByText('Region'))!.toBeInTheDocument() + expect(screen.getByText('Model'))!.toBeInTheDocument() + expect(screen.getByText('Agree'))!.toBeInTheDocument() }) it('should render required asterisk for radio, select, checkbox, and other field types', () => { @@ -1319,7 +1329,7 @@ describe('Form', () => { ) fireEvent.click(screen.getByText('Option B')) - expect(screen.getByText('Validating...')).toBeInTheDocument() + expect(screen.getByText('Validating...'))!.toBeInTheDocument() }) it('should render textInput with show_on condition met', () => { @@ -1345,7 +1355,7 @@ describe('Form', () => { />, ) - expect(screen.getByPlaceholderText('Conditional')).toBeInTheDocument() + expect(screen.getByPlaceholderText('Conditional'))!.toBeInTheDocument() }) it('should render radio with show_on condition met', () => { @@ -1370,7 +1380,7 @@ describe('Form', () => { />, ) - expect(screen.getByText('Cond Radio')).toBeInTheDocument() + expect(screen.getByText('Cond Radio'))!.toBeInTheDocument() }) it('should proceed with onChange when isEditMode is true but key is not locked', () => { @@ -1424,6 +1434,37 @@ describe('Form', () => { />, ) + // Should not crash - the field simply doesn't render + // Should not crash - the field simply doesn't render + // Should not crash - the field simply doesn't render + // Should not crash - the field simply doesn't render + // Should not crash - the field simply doesn't render + // Should not crash - the field simply doesn't render + // Should not crash - the field simply doesn't render + // Should not crash - the field simply doesn't render + // Should not crash - the field simply doesn't render + // Should not crash - the field simply doesn't render + // Should not crash - the field simply doesn't render + // Should not crash - the field simply doesn't render + // Should not crash - the field simply doesn't render + // Should not crash - the field simply doesn't render + // Should not crash - the field simply doesn't render + // Should not crash - the field simply doesn't render + // Should not crash - the field simply doesn't render + // Should not crash - the field simply doesn't render + // Should not crash - the field simply doesn't render + // Should not crash - the field simply doesn't render + // Should not crash - the field simply doesn't render + // Should not crash - the field simply doesn't render + // Should not crash - the field simply doesn't render + // Should not crash - the field simply doesn't render + // Should not crash - the field simply doesn't render + // Should not crash - the field simply doesn't render + // Should not crash - the field simply doesn't render + // Should not crash - the field simply doesn't render + // Should not crash - the field simply doesn't render + // Should not crash - the field simply doesn't render + // Should not crash - the field simply doesn't render // Should not crash - the field simply doesn't render expect(screen.queryByText('Unknown')).not.toBeInTheDocument() }) @@ -1452,7 +1493,7 @@ describe('Form', () => { />, ) - expect(screen.getByText('Check Extra')).toBeInTheDocument() + expect(screen.getByText('Check Extra'))!.toBeInTheDocument() }) }) @@ -1497,10 +1538,10 @@ describe('Form', () => { />, ) - expect(screen.getByText('API Key Fallback')).toBeInTheDocument() - expect(screen.getByText('Region Fallback')).toBeInTheDocument() - expect(screen.getByText('Model Fallback')).toBeInTheDocument() - expect(screen.getByText('Agree Fallback')).toBeInTheDocument() + expect(screen.getByText('API Key Fallback'))!.toBeInTheDocument() + expect(screen.getByText('Region Fallback'))!.toBeInTheDocument() + expect(screen.getByText('Model Fallback'))!.toBeInTheDocument() + expect(screen.getByText('Agree Fallback'))!.toBeInTheDocument() }) it('should fallback to en_US for modelSelector, toolSelector, and appSelector labels', () => { @@ -1542,10 +1583,10 @@ describe('Form', () => { />, ) - expect(screen.getByText('ModelSel Fallback')).toBeInTheDocument() - expect(screen.getByText('ToolSel Fallback')).toBeInTheDocument() - expect(screen.getByText('AppSel Fallback')).toBeInTheDocument() - expect(screen.getByText('Any Fallback')).toBeInTheDocument() + expect(screen.getByText('ModelSel Fallback'))!.toBeInTheDocument() + expect(screen.getByText('ToolSel Fallback'))!.toBeInTheDocument() + expect(screen.getByText('AppSel Fallback'))!.toBeInTheDocument() + expect(screen.getByText('Any Fallback'))!.toBeInTheDocument() }) it('should not change value when __model_type is edited in edit mode', () => { @@ -1602,7 +1643,7 @@ describe('Form', () => { />, ) - expect(screen.getByDisplayValue('actual-value')).toBeInTheDocument() + expect(screen.getByDisplayValue('actual-value'))!.toBeInTheDocument() }) it('should pass nodeOutputVars and availableNodes to toolSelector', () => { @@ -1632,7 +1673,7 @@ describe('Form', () => { />, ) - expect(screen.getByText('Select Tool')).toBeInTheDocument() + expect(screen.getByText('Select Tool'))!.toBeInTheDocument() expect(toolSelectorPropsSpy).toHaveBeenCalledWith(expect.objectContaining({ nodeOutputVars, availableNodes, @@ -1663,7 +1704,7 @@ describe('Form', () => { />, ) - expect(screen.getByText('Select Model')).toBeInTheDocument() + expect(screen.getByText('Select Model'))!.toBeInTheDocument() expect(modelSelectorPropsSpy).toHaveBeenCalledWith(expect.objectContaining({ isAgentStrategy: true, })) @@ -1695,7 +1736,8 @@ describe('Form', () => { ) // Assert - should render without crash (value[variable] || [] path taken) - expect(screen.getByText('Select Tools')).toBeInTheDocument() + // Assert - should render without crash (value[variable] || [] path taken) + expect(screen.getByText('Select Tools'))!.toBeInTheDocument() }) it('should show ValidatingTip for multiToolSelector field being validated', () => { @@ -1726,7 +1768,8 @@ describe('Form', () => { fireEvent.click(screen.getByText('Select Tools')) // Assert - expect(screen.getByText('Validating...')).toBeInTheDocument() + // Assert + expect(screen.getByText('Validating...'))!.toBeInTheDocument() }) it('should show ValidatingTip for appSelector field being validated', () => { @@ -1757,7 +1800,8 @@ describe('Form', () => { fireEvent.click(screen.getByText('Select App')) // Assert - expect(screen.getByText('Validating...')).toBeInTheDocument() + // Assert + expect(screen.getByText('Validating...'))!.toBeInTheDocument() }) it('should show ValidatingTip for any-type field being validated', () => { @@ -1789,7 +1833,8 @@ describe('Form', () => { fireEvent.click(screen.getByText('Pick Variable')) // Assert - expect(screen.getByText('Validating...')).toBeInTheDocument() + // Assert + expect(screen.getByText('Validating...'))!.toBeInTheDocument() }) it('should use empty string fallback for nodeId in any-type when nodeId is not provided', () => { @@ -1818,7 +1863,8 @@ describe('Form', () => { ) // Assert - should render without crash - expect(screen.getByText('Any Field')).toBeInTheDocument() + // Assert - should render without crash + expect(screen.getByText('Any Field'))!.toBeInTheDocument() }) it('should use en_US label fallback for multiToolSelector when language key is missing', () => { @@ -1849,7 +1895,8 @@ describe('Form', () => { ) // Assert - MultipleToolSelector mock renders with the label prop - expect(screen.getByText('Select Tools')).toBeInTheDocument() + // Assert - MultipleToolSelector mock renders with the label prop + expect(screen.getByText('Select Tools'))!.toBeInTheDocument() }) it('should show ValidatingTip for select field being validated', () => { @@ -1882,7 +1929,8 @@ describe('Form', () => { fireEvent.click(screen.getByText('Select B')) // Assert: ValidatingTip shows for the select field - expect(screen.getByText('Validating...')).toBeInTheDocument() + // Assert: ValidatingTip shows for the select field + expect(screen.getByText('Validating...'))!.toBeInTheDocument() }) it('should show ValidatingTip for toolSelector field being validated', () => { @@ -1914,7 +1962,8 @@ describe('Form', () => { fireEvent.click(screen.getByText('Select Tool')) // Assert - expect(screen.getByText('Validating...')).toBeInTheDocument() + // Assert + expect(screen.getByText('Validating...'))!.toBeInTheDocument() }) it('should not render customRenderField for a FormTypeEnum value that is unhandled by Form', () => { diff --git a/web/app/components/header/account-setting/model-provider-page/model-modal/__tests__/index.spec.tsx b/web/app/components/header/account-setting/model-provider-page/model-modal/__tests__/index.spec.tsx index a47b5eb170..a7e85bd9eb 100644 --- a/web/app/components/header/account-setting/model-provider-page/model-modal/__tests__/index.spec.tsx +++ b/web/app/components/header/account-setting/model-provider-page/model-modal/__tests__/index.spec.tsx @@ -196,10 +196,10 @@ describe('ModelModal', () => { const predefined = renderModal() - expect(screen.getByText('common.modelProvider.auth.apiKeyModal.title')).toBeInTheDocument() - expect(screen.getByText('common.modelProvider.auth.apiKeyModal.desc')).toBeInTheDocument() - expect(screen.getByRole('status')).toBeInTheDocument() - expect(screen.getByRole('button', { name: 'common.operation.save' })).toBeDisabled() + expect(screen.getByText('common.modelProvider.auth.apiKeyModal.title'))!.toBeInTheDocument() + expect(screen.getByText('common.modelProvider.auth.apiKeyModal.desc'))!.toBeInTheDocument() + expect(screen.getByRole('status'))!.toBeInTheDocument() + expect(screen.getByRole('button', { name: 'common.operation.save' }))!.toBeDisabled() predefined.unmount() const customizable = renderModal({ configurateMethod: ConfigurationMethodEnum.customizableModel }) @@ -208,7 +208,7 @@ describe('ModelModal', () => { mockState.credentialData = { credentials: {}, available_credentials: [] } renderModal({ mode: ModelModalModeEnum.configModelCredential, model: { model: 'gpt-4', model_type: ModelTypeEnum.textGeneration } }) - expect(screen.getByText('common.modelProvider.auth.addModelCredential')).toBeInTheDocument() + expect(screen.getByText('common.modelProvider.auth.addModelCredential'))!.toBeInTheDocument() }) it('should reveal the credential label when adding a new credential', () => { @@ -218,7 +218,7 @@ describe('ModelModal', () => { fireEvent.click(screen.getByText('Add New')) - expect(screen.getByText('common.modelProvider.auth.modelCredential')).toBeInTheDocument() + expect(screen.getByText('common.modelProvider.auth.modelCredential'))!.toBeInTheDocument() }) it('should call onCancel when the cancel button is clicked', () => { @@ -245,7 +245,7 @@ describe('ModelModal', () => { const { onCancel } = renderModal({ credential }) const alertDialog = screen.getByRole('alertdialog', { hidden: true }) - expect(alertDialog).toHaveTextContent('common.modelProvider.confirmDelete') + expect(alertDialog)!.toHaveTextContent('common.modelProvider.confirmDelete') fireEvent.click(within(alertDialog).getByRole('button', { hidden: true, name: 'common.operation.confirm' })) @@ -261,7 +261,7 @@ describe('ModelModal', () => { { isCheckValidated: true, values: { __authorization_name__: 'Auth Name', api_key: 'secret' } }, ] const configCustomModel = renderModal({ mode: ModelModalModeEnum.configCustomModel }) - fireEvent.click(screen.getAllByText('Model Name Change')[0]) + fireEvent.click(screen.getAllByText('Model Name Change')[0]!) fireEvent.click(screen.getByRole('button', { name: 'common.operation.add' })) expect(mockFormState.setFieldValue).toHaveBeenCalledWith('__model_name', 'updated-model') diff --git a/web/app/components/header/account-setting/model-provider-page/model-parameter-modal/__tests__/parameter-item.spec.tsx b/web/app/components/header/account-setting/model-provider-page/model-parameter-modal/__tests__/parameter-item.spec.tsx index 8a923d065c..2e928c36cc 100644 --- a/web/app/components/header/account-setting/model-provider-page/model-parameter-modal/__tests__/parameter-item.spec.tsx +++ b/web/app/components/header/account-setting/model-provider-page/model-parameter-modal/__tests__/parameter-item.spec.tsx @@ -68,7 +68,7 @@ describe('ParameterItem', () => { const input = screen.getByRole('spinbutton') fireEvent.change(input, { target: { value: '1.4' } }) expect(onChange).toHaveBeenCalledWith(1) - expect(screen.getByTestId('slider-btn')).toBeInTheDocument() + expect(screen.getByTestId('slider-btn'))!.toBeInTheDocument() }) it('should clamp float numeric input to min', () => { @@ -91,19 +91,19 @@ describe('ParameterItem', () => { it('should adjust step based on max for int type', () => { const { rerender } = render() - expect(screen.getByRole('spinbutton')).toHaveAttribute('step', '1') + expect(screen.getByRole('spinbutton'))!.toHaveAttribute('step', '1') rerender() - expect(screen.getByRole('spinbutton')).toHaveAttribute('step', '10') + expect(screen.getByRole('spinbutton'))!.toHaveAttribute('step', '10') rerender() - expect(screen.getByRole('spinbutton')).toHaveAttribute('step', '100') + expect(screen.getByRole('spinbutton'))!.toHaveAttribute('step', '100') }) it('should render int input without slider if min or max is missing', () => { render() expect(screen.queryByRole('slider')).not.toBeInTheDocument() - expect(screen.getByRole('spinbutton')).toHaveAttribute('step', '0') + expect(screen.getByRole('spinbutton'))!.toHaveAttribute('step', '0') }) it('should handle slide change and clamp values', () => { @@ -125,20 +125,20 @@ describe('ParameterItem', () => { const onChange = vi.fn() const { container } = render() const textarea = container.querySelector('textarea')! - expect(textarea).toBeInTheDocument() + expect(textarea)!.toBeInTheDocument() fireEvent.change(textarea, { target: { value: 'new long text' } }) expect(onChange).toHaveBeenCalledWith('new long text') }) it('should render select for string with options', () => { render() - expect(screen.getByText('a')).toBeInTheDocument() + expect(screen.getByText('a'))!.toBeInTheDocument() }) it('should render tag input for tag type', () => { const onChange = vi.fn() render() - expect(screen.getByText('placeholder')).toBeInTheDocument() + expect(screen.getByText('placeholder'))!.toBeInTheDocument() fireEvent.click(screen.getByTestId('tag-input')) expect(onChange).toHaveBeenCalledWith(['tag1', 'tag2']) }) @@ -166,17 +166,17 @@ describe('ParameterItem', () => { it('should use default values if value is undefined', () => { const { rerender } = render() - expect(screen.getByRole('spinbutton')).toHaveValue(0.5) + expect(screen.getByRole('spinbutton'))!.toHaveValue(0.5) rerender() - expect(screen.getByRole('textbox')).toHaveValue('hello') + expect(screen.getByRole('textbox'))!.toHaveValue('hello') rerender() - expect(screen.getByText('True')).toBeInTheDocument() - expect(screen.getByText('False')).toBeInTheDocument() + expect(screen.getByText('True'))!.toBeInTheDocument() + expect(screen.getByText('False'))!.toBeInTheDocument() rerender() - expect(screen.getByRole('spinbutton')).toHaveValue(0) + expect(screen.getByRole('spinbutton'))!.toHaveValue(0) }) it('should reset input to actual bound value on blur-sm', () => { @@ -184,7 +184,7 @@ describe('ParameterItem', () => { const input = screen.getByRole('spinbutton') fireEvent.change(input, { target: { value: '5' } }) fireEvent.blur(input) - expect(input).toHaveValue(1) + expect(input)!.toHaveValue(1) }) it('should render no input for unsupported parameter type', () => { @@ -216,12 +216,12 @@ describe('ParameterItem', () => { ) const editor = screen.getByTestId('prompt-editor') - expect(editor).toBeInTheDocument() - expect(editor).toHaveAttribute('data-has-workflow-vars', 'true') + expect(editor)!.toBeInTheDocument() + expect(editor)!.toHaveAttribute('data-has-workflow-vars', 'true') expect(capturedWorkflowNodesMap).toBeDefined() - expect(capturedWorkflowNodesMap!.node1.title).toBe('LLM Node') - expect(capturedWorkflowNodesMap!.sys.title).toBe('workflow.blocks.start') - expect(capturedWorkflowNodesMap!.sys.type).toBe(BlockEnum.Start) + expect(capturedWorkflowNodesMap!.node1!.title).toBe('LLM Node') + expect(capturedWorkflowNodesMap!.sys!.title).toBe('workflow.blocks.start') + expect(capturedWorkflowNodesMap!.sys!.type).toBe(BlockEnum.Start) promptEditorOnChange?.('updated text') expect(onChange).toHaveBeenCalledWith('updated text') @@ -241,8 +241,8 @@ describe('ParameterItem', () => { ) const editor = screen.getByTestId('prompt-editor') - expect(editor).toBeInTheDocument() - expect(editor).toHaveAttribute('data-has-workflow-vars', 'true') + expect(editor)!.toBeInTheDocument() + expect(editor)!.toHaveAttribute('data-has-workflow-vars', 'true') expect(capturedWorkflowNodesMap).toBeDefined() promptEditorOnChange?.('new long text') @@ -258,7 +258,7 @@ describe('ParameterItem', () => { ) expect(screen.queryByTestId('prompt-editor')).not.toBeInTheDocument() - expect(screen.getByRole('textbox')).toBeInTheDocument() + expect(screen.getByRole('textbox'))!.toBeInTheDocument() }) it('should return undefined workflowNodesMap when not in workflow mode', () => { diff --git a/web/app/components/header/account-setting/model-provider-page/model-selector/__tests__/popup-item.spec.tsx b/web/app/components/header/account-setting/model-provider-page/model-selector/__tests__/popup-item.spec.tsx index 050bb099d8..6728791120 100644 --- a/web/app/components/header/account-setting/model-provider-page/model-selector/__tests__/popup-item.spec.tsx +++ b/web/app/components/header/account-setting/model-provider-page/model-selector/__tests__/popup-item.spec.tsx @@ -185,7 +185,7 @@ describe('PopupItem', () => { expect(mockSetShowModelModal).toHaveBeenCalled() - const call = mockSetShowModelModal.mock.calls[0][0] as { onSaveCallback?: () => void } + const call = mockSetShowModelModal.mock.calls[0]![0] as { onSaveCallback?: () => void } call.onSaveCallback?.() expect(mockUpdateModelProviders).toHaveBeenCalled() @@ -220,7 +220,7 @@ describe('PopupItem', () => { />, ) - expect(screen.getByText('GPT-4')).toBeInTheDocument() + expect(screen.getByText('GPT-4'))!.toBeInTheDocument() }) it('should fall back to english labels when the current language is unavailable', () => { @@ -237,14 +237,14 @@ describe('PopupItem', () => { />, ) - expect(screen.getByText('OpenAI only')).toBeInTheDocument() - expect(screen.getByText('GPT-4 only')).toBeInTheDocument() + expect(screen.getByText('OpenAI only'))!.toBeInTheDocument() + expect(screen.getByText('GPT-4 only'))!.toBeInTheDocument() }) it('should toggle collapsed state when clicking provider header', () => { render() - expect(screen.getByText('GPT-4')).toBeInTheDocument() + expect(screen.getByText('GPT-4'))!.toBeInTheDocument() fireEvent.click(screen.getByText('OpenAI')) @@ -252,13 +252,13 @@ describe('PopupItem', () => { fireEvent.click(screen.getByText('OpenAI')) - expect(screen.getByText('GPT-4')).toBeInTheDocument() + expect(screen.getByText('GPT-4'))!.toBeInTheDocument() }) it('should show credential name when using custom provider', () => { render() - expect(screen.getByText('my-api-key')).toBeInTheDocument() + expect(screen.getByText('my-api-key'))!.toBeInTheDocument() }) it('should render the inactive credential badge when the api key is not active', () => { @@ -275,7 +275,7 @@ describe('PopupItem', () => { render() - expect(screen.getByText('stale-key')).toBeInTheDocument() + expect(screen.getByText('stale-key'))!.toBeInTheDocument() expect(document.querySelector('.bg-components-badge-status-light-error-bg')).not.toBeNull() }) @@ -301,7 +301,7 @@ describe('PopupItem', () => { render() - expect(screen.getByText(/modelProvider\.selector\.configureRequired/)).toBeInTheDocument() + expect(screen.getByText(/modelProvider\.selector\.configureRequired/))!.toBeInTheDocument() }) it('should show credits info when using system provider with remaining credits', () => { @@ -323,7 +323,7 @@ describe('PopupItem', () => { render() - expect(screen.getByText(/modelProvider\.selector\.aiCredits/)).toBeInTheDocument() + expect(screen.getByText(/modelProvider\.selector\.aiCredits/))!.toBeInTheDocument() }) it('should show credits exhausted when system provider has no credits', () => { @@ -348,7 +348,7 @@ describe('PopupItem', () => { render() - expect(screen.getByText(/modelProvider\.selector\.creditsExhausted/)).toBeInTheDocument() + expect(screen.getByText(/modelProvider\.selector\.creditsExhausted/))!.toBeInTheDocument() }) it('should close the dropdown through dropdown content callbacks', () => { diff --git a/web/app/components/header/account-setting/model-provider-page/model-selector/__tests__/popup.spec.tsx b/web/app/components/header/account-setting/model-provider-page/model-selector/__tests__/popup.spec.tsx index dee16e394e..61d6bb4466 100644 --- a/web/app/components/header/account-setting/model-provider-page/model-selector/__tests__/popup.spec.tsx +++ b/web/app/components/header/account-setting/model-provider-page/model-selector/__tests__/popup.spec.tsx @@ -191,14 +191,14 @@ describe('Popup', () => { />, ) - expect(screen.getByText('openai')).toBeInTheDocument() + expect(screen.getByText('openai'))!.toBeInTheDocument() const input = screen.getByPlaceholderText('datasetSettings.form.searchModel') fireEvent.change(input, { target: { value: 'not-found' } }) - expect(screen.getByText('No model found for \u201Cnot-found\u201D')).toBeInTheDocument() + expect(screen.getByText('No model found for \u201Cnot-found\u201D'))!.toBeInTheDocument() const clearIcon = container.querySelector('.i-custom-vender-solid-general-x-circle') - expect(clearIcon).toBeInTheDocument() + expect(clearIcon)!.toBeInTheDocument() fireEvent.click(clearIcon!) expect((input as HTMLInputElement).value).toBe('') }) @@ -225,9 +225,9 @@ describe('Popup', () => { />, ) - expect(screen.getByTestId('compatible-models-banner')).toBeInTheDocument() - expect(screen.getByText('common.modelProvider.selector.onlyCompatibleModelsShown')).toBeInTheDocument() - expect(container.querySelector('.i-ri-information-2-fill')).toBeInTheDocument() + expect(screen.getByTestId('compatible-models-banner'))!.toBeInTheDocument() + expect(screen.getByText('common.modelProvider.selector.onlyCompatibleModelsShown'))!.toBeInTheDocument() + expect(container.querySelector('.i-ri-information-2-fill'))!.toBeInTheDocument() }) it('should filter by scope features including toolCall and non-toolCall checks', () => { @@ -244,7 +244,7 @@ describe('Popup', () => { scopeFeatures={[ModelFeatureEnum.toolCall, ModelFeatureEnum.vision]} />, ) - expect(screen.getByText('No model found for \u201C\u201D')).toBeInTheDocument() + expect(screen.getByText('No model found for \u201C\u201D'))!.toBeInTheDocument() unmount() mockSupportFunctionCall.mockReturnValue(true) @@ -256,7 +256,7 @@ describe('Popup', () => { scopeFeatures={[ModelFeatureEnum.toolCall, ModelFeatureEnum.vision]} />, ) - expect(screen.getByText('openai')).toBeInTheDocument() + expect(screen.getByText('openai'))!.toBeInTheDocument() unmount2() const { unmount: unmount3 } = render( @@ -267,7 +267,7 @@ describe('Popup', () => { scopeFeatures={[ModelFeatureEnum.vision]} />, ) - expect(screen.getByText('openai')).toBeInTheDocument() + expect(screen.getByText('openai'))!.toBeInTheDocument() unmount3() render( @@ -278,7 +278,7 @@ describe('Popup', () => { scopeFeatures={[ModelFeatureEnum.vision]} />, ) - expect(screen.getByText('No model found for \u201C\u201D')).toBeInTheDocument() + expect(screen.getByText('No model found for \u201C\u201D'))!.toBeInTheDocument() }) it('should match model labels from fallback languages when current language key is missing', () => { @@ -305,7 +305,7 @@ describe('Popup', () => { { target: { value: 'openai' } }, ) - expect(screen.getByText('openai')).toBeInTheDocument() + expect(screen.getByText('openai'))!.toBeInTheDocument() }) it('should show credits exhausted alert when an exhausted provider supports credits', () => { @@ -331,7 +331,7 @@ describe('Popup', () => { />, ) - expect(screen.getByTestId('credits-exhausted-alert')).toHaveAttribute('data-has-api-key-fallback', 'false') + expect(screen.getByTestId('credits-exhausted-alert'))!.toHaveAttribute('data-has-api-key-fallback', 'false') }) it('should not show credits exhausted alert when only non-trial system providers are exhausted', () => { @@ -419,8 +419,8 @@ describe('Popup', () => { />, ) - expect(screen.getByText(/modelProvider\.selector\.noProviderConfigured(?!Desc)/)).toBeInTheDocument() - expect(screen.getByText(/modelProvider\.selector\.noProviderConfiguredDesc/)).toBeInTheDocument() + expect(screen.getByText(/modelProvider\.selector\.noProviderConfigured(?!Desc)/))!.toBeInTheDocument() + expect(screen.getByText(/modelProvider\.selector\.noProviderConfiguredDesc/))!.toBeInTheDocument() fireEvent.click(screen.getByText(/modelProvider\.selector\.configure/)) expect(onHide).toHaveBeenCalled() @@ -441,9 +441,9 @@ describe('Popup', () => { ) expect(screen.queryByText('TestOpenAI')).not.toBeInTheDocument() - expect(screen.getByText('TestAnthropic')).toBeInTheDocument() - expect(screen.getByText(/modelProvider\.selector\.fromMarketplace/)).toBeInTheDocument() - expect(screen.getByText(/modelProvider\.selector\.discoverMoreInMarketplace/)).toBeInTheDocument() + expect(screen.getByText('TestAnthropic'))!.toBeInTheDocument() + expect(screen.getByText(/modelProvider\.selector\.fromMarketplace/))!.toBeInTheDocument() + expect(screen.getByText(/modelProvider\.selector\.discoverMoreInMarketplace/))!.toBeInTheDocument() }) it('should show installed marketplace providers without models when AI credits are available', () => { @@ -462,8 +462,8 @@ describe('Popup', () => { />, ) - expect(screen.getByText('test-anthropic')).toBeInTheDocument() - expect(screen.getByText('TestOpenAI')).toBeInTheDocument() + expect(screen.getByText('test-anthropic'))!.toBeInTheDocument() + expect(screen.getByText('TestOpenAI'))!.toBeInTheDocument() }) it('should hide installed marketplace providers without models when AI credits are exhausted', () => { @@ -489,7 +489,7 @@ describe('Popup', () => { expect(screen.queryByText('test-anthropic')).not.toBeInTheDocument() expect(screen.queryByText('TestAnthropic')).not.toBeInTheDocument() - expect(screen.getByText('TestOpenAI')).toBeInTheDocument() + expect(screen.getByText('TestOpenAI'))!.toBeInTheDocument() }) it('should toggle marketplace section collapse', () => { @@ -501,7 +501,7 @@ describe('Popup', () => { />, ) - expect(screen.getByText('TestOpenAI')).toBeInTheDocument() + expect(screen.getByText('TestOpenAI'))!.toBeInTheDocument() fireEvent.click(screen.getByText(/modelProvider\.selector\.fromMarketplace/)) @@ -509,7 +509,7 @@ describe('Popup', () => { fireEvent.click(screen.getByText(/modelProvider\.selector\.fromMarketplace/)) - expect(screen.getByText('TestOpenAI')).toBeInTheDocument() + expect(screen.getByText('TestOpenAI'))!.toBeInTheDocument() }) it('should install plugin when clicking install button', async () => { @@ -527,7 +527,7 @@ describe('Popup', () => { ) const installButtons = screen.getAllByText(/common\.modelProvider\.selector\.install/) - fireEvent.click(installButtons[0]) + fireEvent.click(installButtons[0]!) await waitFor(() => { expect(mockInstallMutateAsync).toHaveBeenCalledWith('langgenius/openai:1.0.0') @@ -550,7 +550,7 @@ describe('Popup', () => { ) const installButtons = screen.getAllByText(/common\.modelProvider\.selector\.install/) - fireEvent.click(installButtons[0]) + fireEvent.click(installButtons[0]!) await waitFor(() => { expect(mockInstallMutateAsync).toHaveBeenCalled() @@ -576,7 +576,7 @@ describe('Popup', () => { ) const installButtons = screen.getAllByText(/common\.modelProvider\.selector\.install/) - fireEvent.click(installButtons[0]) + fireEvent.click(installButtons[0]!) await waitFor(() => { expect(mockCheck).toHaveBeenCalledWith({ @@ -601,7 +601,7 @@ describe('Popup', () => { />, ) - fireEvent.click(screen.getAllByText(/common\.modelProvider\.selector\.install/)[0]) + fireEvent.click(screen.getAllByText(/common\.modelProvider\.selector\.install/)[0]!) await waitFor(() => { expect(mockInstallMutateAsync).not.toHaveBeenCalled() @@ -619,7 +619,7 @@ describe('Popup', () => { />, ) - fireEvent.click(screen.getAllByText(/common\.modelProvider\.selector\.install/)[0]) + fireEvent.click(screen.getAllByText(/common\.modelProvider\.selector\.install/)[0]!) await waitFor(() => { expect(mockInstallMutateAsync).not.toHaveBeenCalled() @@ -640,6 +640,6 @@ describe('Popup', () => { ) const providerLabels = screen.getAllByText(/openai|anthropic/) - expect(providerLabels[0]).toHaveTextContent('anthropic') + expect(providerLabels[0])!.toHaveTextContent('anthropic') }) }) diff --git a/web/app/components/header/account-setting/model-provider-page/model-selector/popup-item.tsx b/web/app/components/header/account-setting/model-provider-page/model-selector/popup-item.tsx index d1ad017455..e38cc07ca2 100644 --- a/web/app/components/header/account-setting/model-provider-page/model-selector/popup-item.tsx +++ b/web/app/components/header/account-setting/model-provider-page/model-selector/popup-item.tsx @@ -77,7 +77,7 @@ const PopupItem: FC = ({ onSaveCallback: () => { updateModelProviders() - const modelType = model.models[0].model_type + const modelType = model.models[0]!.model_type if (modelType) updateModelList(modelType) diff --git a/web/app/components/header/account-setting/model-provider-page/provider-added-card/__tests__/model-list.spec.tsx b/web/app/components/header/account-setting/model-provider-page/provider-added-card/__tests__/model-list.spec.tsx index 70a0cb985a..e435835807 100644 --- a/web/app/components/header/account-setting/model-provider-page/provider-added-card/__tests__/model-list.spec.tsx +++ b/web/app/components/header/account-setting/model-provider-page/provider-added-card/__tests__/model-list.spec.tsx @@ -59,8 +59,8 @@ describe('ModelList', () => { />, ) expect(screen.getAllByText(/modelProvider\.modelsNum/).length).toBeGreaterThan(0) - expect(screen.getByRole('button', { name: 'gpt-4' })).toBeInTheDocument() - expect(screen.getByRole('button', { name: 'gpt-3.5' })).toBeInTheDocument() + expect(screen.getByRole('button', { name: 'gpt-4' }))!.toBeInTheDocument() + expect(screen.getByRole('button', { name: 'gpt-3.5' }))!.toBeInTheDocument() }) it('should trigger collapse when collapsed label is clicked', () => { @@ -74,7 +74,7 @@ describe('ModelList', () => { ) const countElements = screen.getAllByText(/modelProvider\.modelsNum/) - fireEvent.click(countElements[1]) + fireEvent.click(countElements[1]!) expect(mockOnCollapse).toHaveBeenCalled() }) @@ -125,6 +125,37 @@ describe('ModelList', () => { />, ) + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert // Assert expect(screen.queryByTestId('manage-credentials')).not.toBeInTheDocument() expect(screen.queryByTestId('add-custom-model')).not.toBeInTheDocument() @@ -143,7 +174,7 @@ describe('ModelList', () => { fireEvent.click(screen.getByRole('button', { name: 'gpt-4' })) expect(mockSetShowModelLoadBalancingModal).toHaveBeenCalled() - const callArg = mockSetShowModelLoadBalancingModal.mock.calls[0][0] + const callArg = mockSetShowModelLoadBalancingModal.mock.calls[0]![0] callArg.onSave('test-provider') expect(mockOnChange).toHaveBeenCalledWith('test-provider') @@ -170,6 +201,37 @@ describe('ModelList', () => { />, ) + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert // Assert expect(screen.queryByTestId('manage-credentials')).not.toBeInTheDocument() expect(screen.queryByTestId('add-custom-model')).not.toBeInTheDocument() @@ -195,8 +257,9 @@ describe('ModelList', () => { ) // Assert: custom model actions are shown (isConfigurable=true && isCurrentWorkspaceManager=true) - expect(screen.getByTestId('manage-credentials')).toBeInTheDocument() - expect(screen.getByTestId('add-custom-model')).toBeInTheDocument() + // Assert: custom model actions are shown (isConfigurable=true && isCurrentWorkspaceManager=true) + expect(screen.getByTestId('manage-credentials'))!.toBeInTheDocument() + expect(screen.getByTestId('add-custom-model'))!.toBeInTheDocument() }) it('should hide custom model actions when provider is configurable but user is not workspace manager', () => { @@ -218,6 +281,37 @@ describe('ModelList', () => { />, ) + // Assert: custom model actions are hidden (isCurrentWorkspaceManager=false covers the && short-circuit) + // Assert: custom model actions are hidden (isCurrentWorkspaceManager=false covers the && short-circuit) + // Assert: custom model actions are hidden (isCurrentWorkspaceManager=false covers the && short-circuit) + // Assert: custom model actions are hidden (isCurrentWorkspaceManager=false covers the && short-circuit) + // Assert: custom model actions are hidden (isCurrentWorkspaceManager=false covers the && short-circuit) + // Assert: custom model actions are hidden (isCurrentWorkspaceManager=false covers the && short-circuit) + // Assert: custom model actions are hidden (isCurrentWorkspaceManager=false covers the && short-circuit) + // Assert: custom model actions are hidden (isCurrentWorkspaceManager=false covers the && short-circuit) + // Assert: custom model actions are hidden (isCurrentWorkspaceManager=false covers the && short-circuit) + // Assert: custom model actions are hidden (isCurrentWorkspaceManager=false covers the && short-circuit) + // Assert: custom model actions are hidden (isCurrentWorkspaceManager=false covers the && short-circuit) + // Assert: custom model actions are hidden (isCurrentWorkspaceManager=false covers the && short-circuit) + // Assert: custom model actions are hidden (isCurrentWorkspaceManager=false covers the && short-circuit) + // Assert: custom model actions are hidden (isCurrentWorkspaceManager=false covers the && short-circuit) + // Assert: custom model actions are hidden (isCurrentWorkspaceManager=false covers the && short-circuit) + // Assert: custom model actions are hidden (isCurrentWorkspaceManager=false covers the && short-circuit) + // Assert: custom model actions are hidden (isCurrentWorkspaceManager=false covers the && short-circuit) + // Assert: custom model actions are hidden (isCurrentWorkspaceManager=false covers the && short-circuit) + // Assert: custom model actions are hidden (isCurrentWorkspaceManager=false covers the && short-circuit) + // Assert: custom model actions are hidden (isCurrentWorkspaceManager=false covers the && short-circuit) + // Assert: custom model actions are hidden (isCurrentWorkspaceManager=false covers the && short-circuit) + // Assert: custom model actions are hidden (isCurrentWorkspaceManager=false covers the && short-circuit) + // Assert: custom model actions are hidden (isCurrentWorkspaceManager=false covers the && short-circuit) + // Assert: custom model actions are hidden (isCurrentWorkspaceManager=false covers the && short-circuit) + // Assert: custom model actions are hidden (isCurrentWorkspaceManager=false covers the && short-circuit) + // Assert: custom model actions are hidden (isCurrentWorkspaceManager=false covers the && short-circuit) + // Assert: custom model actions are hidden (isCurrentWorkspaceManager=false covers the && short-circuit) + // Assert: custom model actions are hidden (isCurrentWorkspaceManager=false covers the && short-circuit) + // Assert: custom model actions are hidden (isCurrentWorkspaceManager=false covers the && short-circuit) + // Assert: custom model actions are hidden (isCurrentWorkspaceManager=false covers the && short-circuit) + // Assert: custom model actions are hidden (isCurrentWorkspaceManager=false covers the && short-circuit) // Assert: custom model actions are hidden (isCurrentWorkspaceManager=false covers the && short-circuit) expect(screen.queryByTestId('manage-credentials')).not.toBeInTheDocument() expect(screen.queryByTestId('add-custom-model')).not.toBeInTheDocument() diff --git a/web/app/components/header/account-setting/model-provider-page/provider-added-card/__tests__/model-load-balancing-modal.spec.tsx b/web/app/components/header/account-setting/model-provider-page/provider-added-card/__tests__/model-load-balancing-modal.spec.tsx index c7368a2a54..eedc04115c 100644 --- a/web/app/components/header/account-setting/model-provider-page/provider-added-card/__tests__/model-load-balancing-modal.spec.tsx +++ b/web/app/components/header/account-setting/model-provider-page/provider-added-card/__tests__/model-load-balancing-modal.spec.tsx @@ -180,7 +180,7 @@ describe('ModelLoadBalancingModal', () => { />, ) - expect(screen.getByRole('status')).toBeInTheDocument() + expect(screen.getByRole('status'))!.toBeInTheDocument() }) it('should render predefined model content', () => { @@ -193,9 +193,9 @@ describe('ModelLoadBalancingModal', () => { />, ) - expect(screen.getByText(/modelProvider\.auth\.configLoadBalancing/)).toBeInTheDocument() - expect(screen.getByText(/modelProvider\.auth\.providerManaged$/)).toBeInTheDocument() - expect(screen.getByText(/operation\.save/)).toBeInTheDocument() + expect(screen.getByText(/modelProvider\.auth\.configLoadBalancing/))!.toBeInTheDocument() + expect(screen.getByText(/modelProvider\.auth\.providerManaged$/))!.toBeInTheDocument() + expect(screen.getByText(/operation\.save/))!.toBeInTheDocument() }) it('should render custom model actions and close when update has no credentials', async () => { @@ -211,8 +211,8 @@ describe('ModelLoadBalancingModal', () => { />, ) - expect(screen.getByText(/modelProvider\.auth\.removeModel/)).toBeInTheDocument() - expect(screen.getByRole('button', { name: 'switch credential' })).toBeInTheDocument() + expect(screen.getByText(/modelProvider\.auth\.removeModel/))!.toBeInTheDocument() + expect(screen.getByRole('button', { name: 'switch credential' }))!.toBeInTheDocument() await user.click(screen.getByRole('button', { name: 'config add credential' })) await waitFor(() => { expect(onClose).toHaveBeenCalled() @@ -241,8 +241,8 @@ describe('ModelLoadBalancingModal', () => { await waitFor(() => { expect(mockRefetch).toHaveBeenCalled() expect(mockMutateAsync).toHaveBeenCalled() - const payload = mockMutateAsync.mock.calls[0][0] as { load_balancing: { configs: Array<{ credentials: { api_key: string } }> } } - expect(payload.load_balancing.configs[0].credentials.api_key).toBe('[__HIDDEN__]') + const payload = mockMutateAsync.mock.calls[0]![0] as { load_balancing: { configs: Array<{ credentials: { api_key: string } }> } } + expect(payload.load_balancing.configs[0]!.credentials.api_key).toBe('[__HIDDEN__]') expect(mockNotify).toHaveBeenCalled() expect(mockHandleRefreshModel).toHaveBeenCalled() expect(onSave).toHaveBeenCalledWith('test-provider') @@ -313,7 +313,7 @@ describe('ModelLoadBalancingModal', () => { />, ) - expect(screen.getByText(/modelProvider\.auth\.configModel/)).toBeInTheDocument() + expect(screen.getByText(/modelProvider\.auth\.configModel/))!.toBeInTheDocument() }) // Modal hidden when open=false @@ -409,7 +409,7 @@ describe('ModelLoadBalancingModal', () => { />, ) - expect(screen.getByRole('button', { name: 'switch credential' })).toBeInTheDocument() + expect(screen.getByRole('button', { name: 'switch credential' }))!.toBeInTheDocument() }) it('should disable save button when less than 2 configs are enabled', () => { @@ -433,7 +433,7 @@ describe('ModelLoadBalancingModal', () => { />, ) - expect(screen.getByText(/operation\.save/)).toBeDisabled() + expect(screen.getByText(/operation\.save/))!.toBeDisabled() }) it('should encode config entry without id as non-hidden value', async () => { @@ -463,9 +463,9 @@ describe('ModelLoadBalancingModal', () => { await waitFor(() => { expect(mockMutateAsync).toHaveBeenCalled() - const payload = mockMutateAsync.mock.calls[0][0] as { load_balancing: { configs: Array<{ credentials: { api_key: string } }> } } + const payload = mockMutateAsync.mock.calls[0]![0] as { load_balancing: { configs: Array<{ credentials: { api_key: string } }> } } // Entry without id should NOT be encoded as hidden - expect(payload.load_balancing.configs[0].credentials.api_key).toBe('new-key') + expect(payload.load_balancing.configs[0]!.credentials.api_key).toBe('new-key') }) }) @@ -536,7 +536,7 @@ describe('ModelLoadBalancingModal', () => { await waitFor(() => { expect(mockMutateAsync).toHaveBeenCalled() // The payload configs should only have the original 2 entries (no new one added) - const payload = mockMutateAsync.mock.calls[0][0] as { load_balancing: { configs: unknown[] } } + const payload = mockMutateAsync.mock.calls[0]![0] as { load_balancing: { configs: unknown[] } } expect(payload.load_balancing.configs).toHaveLength(2) }) }) @@ -552,14 +552,16 @@ describe('ModelLoadBalancingModal', () => { ) // draftConfig.enabled=true → title shows configLoadBalancing - expect(screen.getByText(/modelProvider\.auth\.configLoadBalancing/)).toBeInTheDocument() + // draftConfig.enabled=true → title shows configLoadBalancing + expect(screen.getByText(/modelProvider\.auth\.configLoadBalancing/))!.toBeInTheDocument() // Clicking the card when enabled=true toggles to disabled const card = screen.getByText(/modelProvider\.auth\.providerManaged$/).closest('div[class]')!.closest('div[class]')! await user.click(card) // After toggling, title should show configModel (disabled state) - expect(screen.getByText(/modelProvider\.auth\.configModel/)).toBeInTheDocument() + // After toggling, title should show configModel (disabled state) + expect(screen.getByText(/modelProvider\.auth\.configModel/))!.toBeInTheDocument() }) it('should use customModelCredential credential_id when present in handleSave', async () => { @@ -589,7 +591,7 @@ describe('ModelLoadBalancingModal', () => { await waitFor(() => { expect(mockMutateAsync).toHaveBeenCalled() - const payload = mockMutateAsync.mock.calls[0][0] as { credential_id: string } + const payload = mockMutateAsync.mock.calls[0]![0] as { credential_id: string } // credential_id should come from customModelCredential expect(payload.credential_id).toBe('cred-1') }) @@ -665,7 +667,8 @@ describe('ModelLoadBalancingModal', () => { ) // Assert: component renders without error (extendedSecretFormSchemas = []) - expect(screen.getByText(/modelProvider\.auth\.configLoadBalancing/)).toBeInTheDocument() + // Assert: component renders without error (extendedSecretFormSchemas = []) + expect(screen.getByText(/modelProvider\.auth\.configLoadBalancing/))!.toBeInTheDocument() }) it('should use custom model credential schema without fallback when credential_form_schemas is undefined', () => { @@ -727,7 +730,7 @@ describe('ModelLoadBalancingModal', () => { await waitFor(() => { expect(mockMutateAsync).toHaveBeenCalled() - const payload = mockMutateAsync.mock.calls[0][0] as { load_balancing: { configs: unknown[] } } + const payload = mockMutateAsync.mock.calls[0]![0] as { load_balancing: { configs: unknown[] } } // Config count unchanged (still 2 from original) expect(payload.load_balancing.configs).toHaveLength(2) }) diff --git a/web/app/components/header/account-setting/model-provider-page/provider-added-card/model-auth-dropdown/__tests__/usage-priority-section.spec.tsx b/web/app/components/header/account-setting/model-provider-page/provider-added-card/model-auth-dropdown/__tests__/usage-priority-section.spec.tsx index 20ef9cf066..c400d05f22 100644 --- a/web/app/components/header/account-setting/model-provider-page/provider-added-card/model-auth-dropdown/__tests__/usage-priority-section.spec.tsx +++ b/web/app/components/header/account-setting/model-provider-page/provider-added-card/model-auth-dropdown/__tests__/usage-priority-section.spec.tsx @@ -14,7 +14,7 @@ describe('UsagePrioritySection', () => { it('should render title and both option buttons', () => { render() - expect(screen.getByText(/usagePriority/)).toBeInTheDocument() + expect(screen.getByText(/usagePriority/))!.toBeInTheDocument() expect(screen.getAllByRole('button')).toHaveLength(2) }) }) @@ -25,23 +25,23 @@ describe('UsagePrioritySection', () => { render() const buttons = screen.getAllByRole('button') - expect(buttons[0].className).toContain('border-components-option-card-option-selected-border') - expect(buttons[1].className).not.toContain('border-components-option-card-option-selected-border') + expect(buttons[0]!.className).toContain('border-components-option-card-option-selected-border') + expect(buttons[1]!.className).not.toContain('border-components-option-card-option-selected-border') }) it('should highlight API key option when value is apiKey', () => { render() const buttons = screen.getAllByRole('button') - expect(buttons[0].className).not.toContain('border-components-option-card-option-selected-border') - expect(buttons[1].className).toContain('border-components-option-card-option-selected-border') + expect(buttons[0]!.className).not.toContain('border-components-option-card-option-selected-border') + expect(buttons[1]!.className).toContain('border-components-option-card-option-selected-border') }) it('should highlight API key option when value is apiKeyOnly', () => { render() const buttons = screen.getAllByRole('button') - expect(buttons[1].className).toContain('border-components-option-card-option-selected-border') + expect(buttons[1]!.className).toContain('border-components-option-card-option-selected-border') }) }) @@ -50,7 +50,7 @@ describe('UsagePrioritySection', () => { it('should call onSelect with system when clicking AI credits option', () => { render() - fireEvent.click(screen.getAllByRole('button')[0]) + fireEvent.click(screen.getAllByRole('button')[0]!) expect(onSelect).toHaveBeenCalledWith(PreferredProviderTypeEnum.system) }) @@ -58,7 +58,7 @@ describe('UsagePrioritySection', () => { it('should call onSelect with custom when clicking API key option', () => { render() - fireEvent.click(screen.getAllByRole('button')[1]) + fireEvent.click(screen.getAllByRole('button')[1]!) expect(onSelect).toHaveBeenCalledWith(PreferredProviderTypeEnum.custom) }) diff --git a/web/app/components/header/account-setting/model-provider-page/provider-added-card/model-auth-dropdown/__tests__/use-activate-credential.spec.tsx b/web/app/components/header/account-setting/model-provider-page/provider-added-card/model-auth-dropdown/__tests__/use-activate-credential.spec.tsx index c38d783e73..12acf479c0 100644 --- a/web/app/components/header/account-setting/model-provider-page/provider-added-card/model-auth-dropdown/__tests__/use-activate-credential.spec.tsx +++ b/web/app/components/header/account-setting/model-provider-page/provider-added-card/model-auth-dropdown/__tests__/use-activate-credential.spec.tsx @@ -92,7 +92,7 @@ describe('useActivateCredential', () => { }), ) - const [, callbacks] = mockMutate.mock.calls[0] + const [, callbacks] = (mockMutate.mock.calls[0] ?? []) as [unknown, any] act(() => { callbacks.onSuccess() @@ -113,7 +113,7 @@ describe('useActivateCredential', () => { expect(result.current.selectedCredentialId).toBe('cred-2') - const [, callbacks] = mockMutate.mock.calls[0] + const [, callbacks] = (mockMutate.mock.calls[0] ?? []) as [unknown, any] act(() => { callbacks.onError() diff --git a/web/app/components/header/account-setting/model-provider-page/provider-added-card/model-load-balancing-configs.tsx b/web/app/components/header/account-setting/model-provider-page/provider-added-card/model-load-balancing-configs.tsx index 7fd1618d64..ea4edace30 100644 --- a/web/app/components/header/account-setting/model-provider-page/provider-added-card/model-load-balancing-configs.tsx +++ b/web/app/components/header/account-setting/model-provider-page/provider-added-card/model-load-balancing-configs.tsx @@ -64,7 +64,7 @@ const ModelLoadBalancingConfigs = ({ if (!prev) return prev const newConfigs = [...prev.configs] - const modifiedConfig = modifier(newConfigs[index]) + const modifiedConfig = modifier(newConfigs[index]!) if (modifiedConfig) newConfigs[index] = modifiedConfig else diff --git a/web/app/components/header/account-setting/model-provider-page/provider-added-card/model-load-balancing-modal.tsx b/web/app/components/header/account-setting/model-provider-page/provider-added-card/model-load-balancing-modal.tsx index 3556777395..652630be67 100644 --- a/web/app/components/header/account-setting/model-provider-page/provider-added-card/model-load-balancing-modal.tsx +++ b/web/app/components/header/account-setting/model-provider-page/provider-added-card/model-load-balancing-modal.tsx @@ -164,7 +164,7 @@ const ModelLoadBalancingModal = ({ provider, configurateMethod, currentCustomCon const prevIndex = newConfigs.findIndex(item => item.credential_id === modelCredential.credential_id && item.name !== '__inherit__') const newIndex = available_credentials.findIndex(c => c.credential_id === modelCredential.credential_id) if (newIndex > -1 && prevIndex > -1) - newConfigs[prevIndex].name = available_credentials[newIndex].credential_name || '' + newConfigs[prevIndex]!.name = available_credentials[newIndex]!.credential_name || '' return { ...prev, configs: newConfigs, diff --git a/web/app/components/header/account-setting/plugin-page/index.tsx b/web/app/components/header/account-setting/plugin-page/index.tsx index a71c3ee072..7f8e9a32f7 100644 --- a/web/app/components/header/account-setting/plugin-page/index.tsx +++ b/web/app/components/header/account-setting/plugin-page/index.tsx @@ -16,7 +16,7 @@ const PluginPage = () => { return (
- {plugins?.map(plugin => Plugin_MAP[plugin.tool_name](plugin))} + {plugins?.map(plugin => Plugin_MAP[plugin.tool_name]!(plugin))}
diff --git a/web/app/components/header/app-nav/index.tsx b/web/app/components/header/app-nav/index.tsx index 54ddf75711..2e7a77a891 100644 --- a/web/app/components/header/app-nav/index.tsx +++ b/web/app/components/header/app-nav/index.tsx @@ -93,7 +93,7 @@ const AppNav = () => { const newNavItems = produce(navItems, (draft: NavItem[]) => { navItems.forEach((app, index) => { if (app.id === appDetail.id) - draft[index].name = appDetail.name + draft[index]!.name = appDetail.name }) }) setNavItems(newNavItems) diff --git a/web/app/components/header/app-selector/__tests__/index.spec.tsx b/web/app/components/header/app-selector/__tests__/index.spec.tsx index eddb7e52aa..2d255c006e 100644 --- a/web/app/components/header/app-selector/__tests__/index.spec.tsx +++ b/web/app/components/header/app-selector/__tests__/index.spec.tsx @@ -32,7 +32,7 @@ describe('AppSelector Component', () => { { id: '1', name: 'App 1' }, { id: '2', name: 'App 2' }, ] as unknown as AppDetailResponse[] - const mockCurApp = mockAppItems[0] + const mockCurApp = mockAppItems[0]! beforeEach(() => { vi.clearAllMocks() @@ -47,7 +47,7 @@ describe('AppSelector Component', () => { describe('Rendering', () => { it('should render current app name', () => { render() - expect(screen.getByText('App 1')).toBeInTheDocument() + expect(screen.getByText('App 1'))!.toBeInTheDocument() }) }) @@ -60,7 +60,7 @@ describe('AppSelector Component', () => { fireEvent.click(button) }) - expect(screen.getByText('App 2')).toBeInTheDocument() + expect(screen.getByText('App 2'))!.toBeInTheDocument() }) it('should navigate to configuration when an app is clicked and user is editor', async () => { @@ -114,7 +114,7 @@ describe('AppSelector Component', () => { fireEvent.click(newAppBtn) }) - expect(screen.getByTestId('create-app-dialog')).toBeInTheDocument() + expect(screen.getByTestId('create-app-dialog'))!.toBeInTheDocument() }) it('should not show "New App" button for non-editor', async () => { @@ -165,7 +165,8 @@ describe('AppSelector Component', () => { expect(screen.queryByText('App 2')).not.toBeInTheDocument() // "New App" should still be there if editor - expect(screen.getByText('common.menus.newApp')).toBeInTheDocument() + // "New App" should still be there if editor + expect(screen.getByText('common.menus.newApp'))!.toBeInTheDocument() }) }) }) diff --git a/web/app/components/header/nav/nav-selector/__tests__/index.spec.tsx b/web/app/components/header/nav/nav-selector/__tests__/index.spec.tsx index 152901b79c..97443bb759 100644 --- a/web/app/components/header/nav/nav-selector/__tests__/index.spec.tsx +++ b/web/app/components/header/nav/nav-selector/__tests__/index.spec.tsx @@ -105,7 +105,7 @@ describe('NavSelector Component', () => { }, ] - const { link: _link, ...curNavWithoutLink } = navigationItems[0] + const { link: _link, ...curNavWithoutLink } = navigationItems[0]! const defaultProps: INavSelectorProps = { curNav: curNavWithoutLink, @@ -131,7 +131,7 @@ describe('NavSelector Component', () => { describe('Rendering', () => { it('should render current nav name', () => { render() - expect(screen.getByText('Item 1')).toBeInTheDocument() + expect(screen.getByText('Item 1'))!.toBeInTheDocument() }) it('should show loading indicator when isLoadingMore is true', async () => { @@ -140,7 +140,7 @@ describe('NavSelector Component', () => { await act(async () => { fireEvent.click(button) }) - expect(screen.getByRole('status')).toBeInTheDocument() + expect(screen.getByRole('status'))!.toBeInTheDocument() }) }) @@ -151,7 +151,7 @@ describe('NavSelector Component', () => { await act(async () => { fireEvent.click(button) }) - expect(screen.getByText('Item 2')).toBeInTheDocument() + expect(screen.getByText('Item 2'))!.toBeInTheDocument() }) it('should navigate and call setAppDetail when an item is clicked', async () => { diff --git a/web/app/components/plugins/__tests__/hooks.spec.ts b/web/app/components/plugins/__tests__/hooks.spec.ts index b12121d626..1e4f18ec5d 100644 --- a/web/app/components/plugins/__tests__/hooks.spec.ts +++ b/web/app/components/plugins/__tests__/hooks.spec.ts @@ -86,15 +86,15 @@ describe('useCategories', () => { it('should use plural labels by default', () => { const { result } = renderHook(() => useCategories()) - expect(result.current.categoriesMap.tool.label).toBe('plugin.category.tools') - expect(result.current.categoriesMap['agent-strategy'].label).toBe('plugin.category.agents') + expect(result.current.categoriesMap.tool!.label).toBe('plugin.category.tools') + expect(result.current.categoriesMap['agent-strategy']!.label).toBe('plugin.category.agents') }) it('should use singular labels when isSingle is true', () => { const { result } = renderHook(() => useCategories(true)) - expect(result.current.categoriesMap.tool.label).toBe('plugin.categorySingle.tool') - expect(result.current.categoriesMap['agent-strategy'].label).toBe('plugin.categorySingle.agent') + expect(result.current.categoriesMap.tool!.label).toBe('plugin.categorySingle.tool') + expect(result.current.categoriesMap['agent-strategy']!.label).toBe('plugin.categorySingle.agent') }) }) diff --git a/web/app/components/plugins/card/index.tsx b/web/app/components/plugins/card/index.tsx index 9f9b89cbbf..dd7f92184d 100644 --- a/web/app/components/plugins/card/index.tsx +++ b/web/app/components/plugins/card/index.tsx @@ -77,7 +77,7 @@ const Card = ({ return (
- {!hideCornerMark && } + {!hideCornerMark && } {/* Header */}
diff --git a/web/app/components/plugins/install-plugin/hooks.ts b/web/app/components/plugins/install-plugin/hooks.ts index f86e6ad672..4759953e30 100644 --- a/web/app/components/plugins/install-plugin/hooks.ts +++ b/web/app/components/plugins/install-plugin/hooks.ts @@ -52,7 +52,7 @@ export const checkForUpdates = (fetchedReleases: GitHubRepoReleaseResponse[], cu const versions = fetchedReleases.map(release => release.tag_name) const latestVersion = getLatestVersion(versions) try { - needUpdate = compareVersion(latestVersion, currentVersion) === 1 + needUpdate = compareVersion(latestVersion!, currentVersion) === 1 if (needUpdate) toastProps.message = `New version available: ${latestVersion}` } diff --git a/web/app/components/plugins/install-plugin/install-bundle/steps/__tests__/install-multi.spec.tsx b/web/app/components/plugins/install-plugin/install-bundle/steps/__tests__/install-multi.spec.tsx index 4507c1295b..d45b05bfd1 100644 --- a/web/app/components/plugins/install-plugin/install-bundle/steps/__tests__/install-multi.spec.tsx +++ b/web/app/components/plugins/install-plugin/install-bundle/steps/__tests__/install-multi.spec.tsx @@ -260,14 +260,14 @@ describe('InstallMulti Component', () => { it('should render without crashing', () => { render() - expect(screen.getByTestId('package-item')).toBeInTheDocument() + expect(screen.getByTestId('package-item'))!.toBeInTheDocument() }) it('should render PackageItem for package type dependency', () => { render() - expect(screen.getByTestId('package-item')).toBeInTheDocument() - expect(screen.getByTestId('package-item-name')).toHaveTextContent('Package Plugin 0') + expect(screen.getByTestId('package-item'))!.toBeInTheDocument() + expect(screen.getByTestId('package-item-name'))!.toHaveTextContent('Package Plugin 0') }) it('should render GithubItem for github type dependency', async () => { @@ -279,9 +279,9 @@ describe('InstallMulti Component', () => { render() await waitFor(() => { - expect(screen.getByTestId('github-item')).toBeInTheDocument() + expect(screen.getByTestId('github-item'))!.toBeInTheDocument() }) - expect(screen.getByTestId('github-item-repo')).toHaveTextContent('test-org/plugin-0') + expect(screen.getByTestId('github-item-repo'))!.toHaveTextContent('test-org/plugin-0') }) it('should render MarketplaceItem for marketplace type dependency', async () => { @@ -293,7 +293,7 @@ describe('InstallMulti Component', () => { render() await waitFor(() => { - expect(screen.getByTestId('marketplace-item')).toBeInTheDocument() + expect(screen.getByTestId('marketplace-item'))!.toBeInTheDocument() }) }) @@ -309,8 +309,8 @@ describe('InstallMulti Component', () => { render() await waitFor(() => { - expect(screen.getByTestId('package-item')).toBeInTheDocument() - expect(screen.getByTestId('github-item')).toBeInTheDocument() + expect(screen.getByTestId('package-item'))!.toBeInTheDocument() + expect(screen.getByTestId('github-item'))!.toBeInTheDocument() }) }) @@ -328,7 +328,7 @@ describe('InstallMulti Component', () => { render() await waitFor(() => { - expect(screen.queryByTestId('github-item')).toBeInTheDocument() + expect(screen.queryByTestId('github-item'))!.toBeInTheDocument() }) }) }) @@ -355,13 +355,13 @@ describe('InstallMulti Component', () => { render() - expect(screen.getByTestId('package-item-checked')).toHaveTextContent('checked') + expect(screen.getByTestId('package-item-checked'))!.toHaveTextContent('checked') }) it('should show unchecked state when plugin is not selected', () => { render() - expect(screen.getByTestId('package-item-checked')).toHaveTextContent('unchecked') + expect(screen.getByTestId('package-item-checked'))!.toHaveTextContent('unchecked') }) }) @@ -427,7 +427,7 @@ describe('InstallMulti Component', () => { // The getVersionInfo function returns hasInstalled, installedVersion, toInstallVersion // These are passed to child components await waitFor(() => { - expect(screen.getByTestId('package-item')).toBeInTheDocument() + expect(screen.getByTestId('package-item'))!.toBeInTheDocument() }) }) }) @@ -443,7 +443,7 @@ describe('InstallMulti Component', () => { render() await waitFor(() => { - expect(screen.getByTestId('github-item')).toBeInTheDocument() + expect(screen.getByTestId('github-item'))!.toBeInTheDocument() }) // The onFetchedPayload callback should have been called by the mock @@ -462,7 +462,7 @@ describe('InstallMulti Component', () => { render() await waitFor(() => { - expect(screen.getByTestId('marketplace-item')).toBeInTheDocument() + expect(screen.getByTestId('marketplace-item'))!.toBeInTheDocument() }) }) }) @@ -485,7 +485,7 @@ describe('InstallMulti Component', () => { render() await waitFor(() => { - expect(screen.getByTestId('package-item')).toBeInTheDocument() + expect(screen.getByTestId('package-item'))!.toBeInTheDocument() }) }) @@ -498,7 +498,7 @@ describe('InstallMulti Component', () => { render() await waitFor(() => { - expect(screen.getByTestId('package-item')).toBeInTheDocument() + expect(screen.getByTestId('package-item'))!.toBeInTheDocument() }) }) }) @@ -509,7 +509,8 @@ describe('InstallMulti Component', () => { render() // Package plugins are initialized immediately - expect(screen.getByTestId('package-item')).toBeInTheDocument() + // Package plugins are initialized immediately + expect(screen.getByTestId('package-item'))!.toBeInTheDocument() }) it('should update plugins when GitHub plugin is fetched', async () => { @@ -521,7 +522,7 @@ describe('InstallMulti Component', () => { render() await waitFor(() => { - expect(screen.getByTestId('github-item')).toBeInTheDocument() + expect(screen.getByTestId('github-item'))!.toBeInTheDocument() }) }) }) @@ -553,7 +554,7 @@ describe('InstallMulti Component', () => { render() await waitFor(() => { - expect(screen.getByTestId('package-item')).toBeInTheDocument() + expect(screen.getByTestId('package-item'))!.toBeInTheDocument() }) }) @@ -613,11 +614,12 @@ describe('InstallMulti Component', () => { render() await waitFor(() => { - expect(screen.getByTestId('marketplace-item')).toBeInTheDocument() + expect(screen.getByTestId('marketplace-item'))!.toBeInTheDocument() }) // Version should be displayed - expect(screen.getByTestId('marketplace-item-version')).toBeInTheDocument() + // Version should be displayed + expect(screen.getByTestId('marketplace-item-version'))!.toBeInTheDocument() }) }) @@ -706,7 +708,8 @@ describe('InstallMulti Component', () => { await waitFor(() => { // Component should render - expect(document.body).toBeInTheDocument() + // Component should render + expect(document.body)!.toBeInTheDocument() }) }) }) @@ -717,7 +720,7 @@ describe('InstallMulti Component', () => { render() await waitFor(() => { - expect(screen.getByTestId('package-item')).toBeInTheDocument() + expect(screen.getByTestId('package-item'))!.toBeInTheDocument() }) // The getVersionInfo callback should return correct structure @@ -738,10 +741,10 @@ describe('InstallMulti Component', () => { render() await waitFor(() => { - expect(screen.getByTestId('github-item')).toBeInTheDocument() + expect(screen.getByTestId('github-item'))!.toBeInTheDocument() }) - expect(screen.getByTestId('github-item-checked')).toHaveTextContent('checked') + expect(screen.getByTestId('github-item-checked'))!.toHaveTextContent('checked') }) it('should show checked state for marketplace item when selected', async () => { @@ -755,7 +758,7 @@ describe('InstallMulti Component', () => { render() await waitFor(() => { - expect(screen.getByTestId('marketplace-item')).toBeInTheDocument() + expect(screen.getByTestId('marketplace-item'))!.toBeInTheDocument() }) // The checked prop should be passed to the item @@ -771,10 +774,10 @@ describe('InstallMulti Component', () => { render() await waitFor(() => { - expect(screen.getByTestId('github-item')).toBeInTheDocument() + expect(screen.getByTestId('github-item'))!.toBeInTheDocument() }) - expect(screen.getByTestId('github-item-checked')).toHaveTextContent('unchecked') + expect(screen.getByTestId('github-item-checked'))!.toHaveTextContent('unchecked') }) }) @@ -823,7 +826,7 @@ describe('InstallMulti Component', () => { const packageItems = screen.getAllByTestId('package-item') await act(async () => { - fireEvent.click(packageItems[0]) + fireEvent.click(packageItems[0]!) }) // onSelect should be called with filtered plugin count @@ -842,11 +845,12 @@ describe('InstallMulti Component', () => { render() await waitFor(() => { - expect(screen.getByTestId('marketplace-item')).toBeInTheDocument() + expect(screen.getByTestId('marketplace-item'))!.toBeInTheDocument() }) // The version should be displayed (from dependency or plugin) - expect(screen.getByTestId('marketplace-item-version')).toBeInTheDocument() + // The version should be displayed (from dependency or plugin) + expect(screen.getByTestId('marketplace-item-version'))!.toBeInTheDocument() }) }) @@ -856,7 +860,7 @@ describe('InstallMulti Component', () => { render() await waitFor(() => { - expect(screen.getByTestId('package-item')).toBeInTheDocument() + expect(screen.getByTestId('package-item'))!.toBeInTheDocument() }) // The component should pass versionInfo to items @@ -868,7 +872,7 @@ describe('InstallMulti Component', () => { render() await waitFor(() => { - expect(screen.getByTestId('package-item')).toBeInTheDocument() + expect(screen.getByTestId('package-item'))!.toBeInTheDocument() expect(defaultProps.onLoadedAllPlugin).toHaveBeenCalled() }) }) diff --git a/web/app/components/plugins/install-plugin/install-bundle/steps/hooks/use-install-multi-state.ts b/web/app/components/plugins/install-plugin/install-bundle/steps/hooks/use-install-multi-state.ts index 86dd287c99..ca06f33c16 100644 --- a/web/app/components/plugins/install-plugin/install-bundle/steps/hooks/use-install-multi-state.ts +++ b/web/app/components/plugins/install-plugin/install-bundle/steps/hooks/use-install-multi-state.ts @@ -35,7 +35,7 @@ function parseMarketplaceIdentifier(identifier?: string): MarketplacePluginInfo return null const withoutHash = identifier.split('@')[0] - const [organization, nameAndVersionPart] = withoutHash.split('/') + const [organization, nameAndVersionPart] = withoutHash!.split('/') if (!organization || !nameAndVersionPart) return null diff --git a/web/app/components/plugins/install-plugin/install-bundle/steps/install.tsx b/web/app/components/plugins/install-plugin/install-bundle/steps/install.tsx index 9eb4db7a27..df94331d58 100644 --- a/web/app/components/plugins/install-plugin/install-bundle/steps/install.tsx +++ b/web/app/components/plugins/install-plugin/install-bundle/steps/install.tsx @@ -83,7 +83,7 @@ const Install: FC = ({ onInstalled(selectedPlugins, res.map((r, i) => { return ({ success: r.status === TaskStatus.success, - isFromMarketPlace: allPlugins[selectedIndexes[i]].type === 'marketplace', + isFromMarketPlace: allPlugins[selectedIndexes[i]!]!.type === 'marketplace', }) })) const hasInstallSuccess = res.some(r => r.status === TaskStatus.success) @@ -101,7 +101,7 @@ const Install: FC = ({ if (item.status !== TaskStatus.running) { return { success: item.status === TaskStatus.success, - isFromMarketPlace: allPlugins[selectedIndexes[index]].type === 'marketplace', + isFromMarketPlace: allPlugins[selectedIndexes[index]!]!.type === 'marketplace', } } const { status } = await check({ @@ -110,7 +110,7 @@ const Install: FC = ({ }) return { success: status === TaskStatus.success, - isFromMarketPlace: allPlugins[selectedIndexes[index]].type === 'marketplace', + isFromMarketPlace: allPlugins[selectedIndexes[index]!]!.type === 'marketplace', } })) onInstalled(selectedPlugins, installStatus) diff --git a/web/app/components/plugins/install-plugin/install-bundle/steps/installed.tsx b/web/app/components/plugins/install-plugin/install-bundle/steps/installed.tsx index a7808736e9..1c64e73d62 100644 --- a/web/app/components/plugins/install-plugin/install-bundle/steps/installed.tsx +++ b/web/app/components/plugins/install-plugin/install-bundle/steps/installed.tsx @@ -36,10 +36,10 @@ const Installed: FC = ({ className="w-full" payload={{ ...plugin, - icon: installStatus[index].isFromMarketPlace ? `${MARKETPLACE_API_PREFIX}/plugins/${plugin.org}/${plugin.name}/icon` : getIconUrl(plugin.icon), + icon: installStatus[index]!.isFromMarketPlace ? `${MARKETPLACE_API_PREFIX}/plugins/${plugin.org}/${plugin.name}/icon` : getIconUrl(plugin.icon), }} - installed={installStatus[index].success} - installFailed={!installStatus[index].success} + installed={installStatus[index]!.success} + installFailed={!installStatus[index]!.success} titleLeft={plugin.version ? {plugin.version} : null} /> ) diff --git a/web/app/components/plugins/marketplace/__tests__/query.spec.tsx b/web/app/components/plugins/marketplace/__tests__/query.spec.tsx index 80d8e6a932..5353cdba91 100644 --- a/web/app/components/plugins/marketplace/__tests__/query.spec.tsx +++ b/web/app/components/plugins/marketplace/__tests__/query.spec.tsx @@ -143,7 +143,7 @@ describe('useMarketplacePlugins', () => { }) expect(result.current.data?.pages).toHaveLength(1) - expect(result.current.data?.pages[0].plugins).toHaveLength(1) + expect(result.current.data?.pages[0]!.plugins).toHaveLength(1) }) it('should handle bundle type in query params', async () => { @@ -185,8 +185,8 @@ describe('useMarketplacePlugins', () => { expect(result.current.data).toBeDefined() }) - expect(result.current.data?.pages[0].plugins).toEqual([]) - expect(result.current.data?.pages[0].total).toBe(0) + expect(result.current.data?.pages[0]!.plugins).toEqual([]) + expect(result.current.data?.pages[0]!.total).toBe(0) }) it('should determine next page correctly via getNextPageParam', async () => { diff --git a/web/app/components/plugins/marketplace/__tests__/utils.spec.ts b/web/app/components/plugins/marketplace/__tests__/utils.spec.ts index 92bed9be62..972a9ba913 100644 --- a/web/app/components/plugins/marketplace/__tests__/utils.spec.ts +++ b/web/app/components/plugins/marketplace/__tests__/utils.spec.ts @@ -264,7 +264,7 @@ describe('getMarketplacePluginsByCollectionId', () => { expect(mockCollectionPlugins).toHaveBeenCalled() const call = mockCollectionPlugins.mock.calls[0] - expect(call[1]).toMatchObject({ signal: controller.signal }) + expect(call![1]).toMatchObject({ signal: controller.signal }) }) }) @@ -313,7 +313,7 @@ describe('getMarketplaceCollectionsAndPlugins', () => { expect(mockCollections).toHaveBeenCalled() const call = mockCollections.mock.calls[0] - expect(call[0]).toMatchObject({ query: expect.objectContaining({ condition: 'category=tool', type: 'bundle' }) }) + expect(call![0]).toMatchObject({ query: expect.objectContaining({ condition: 'category=tool', type: 'bundle' }) }) }) }) @@ -393,7 +393,7 @@ describe('getMarketplacePlugins', () => { expect(result.plugins).toHaveLength(1) const call = mockSearchAdvanced.mock.calls[0] - expect(call[0].params.kind).toBe('bundles') + expect(call![0].params.kind).toBe('bundles') }) it('should use empty category when category is all', async () => { @@ -408,7 +408,7 @@ describe('getMarketplacePlugins', () => { }, 1) const call = mockSearchAdvanced.mock.calls[0] - expect(call[0].body.category).toBe('') + expect(call![0].body.category).toBe('') }) it('should handle API error and return empty result', async () => { @@ -437,7 +437,7 @@ describe('getMarketplacePlugins', () => { await getMarketplacePlugins({ query: 'test' }, 1, controller.signal) const call = mockSearchAdvanced.mock.calls[0] - expect(call[1]).toMatchObject({ signal: controller.signal }) + expect(call![1]).toMatchObject({ signal: controller.signal }) }) it('should default page_size to 40 when not provided', async () => { diff --git a/web/app/components/plugins/marketplace/list/list-with-collection.tsx b/web/app/components/plugins/marketplace/list/list-with-collection.tsx index 21094f39a5..744885ae09 100644 --- a/web/app/components/plugins/marketplace/list/list-with-collection.tsx +++ b/web/app/components/plugins/marketplace/list/list-with-collection.tsx @@ -60,7 +60,7 @@ const ListWithCollection = ({ )} > { - marketplaceCollectionPluginsMap[collection.name].map((plugin) => { + marketplaceCollectionPluginsMap[collection.name]!.map((plugin) => { if (cardRender) return cardRender(plugin) diff --git a/web/app/components/plugins/marketplace/search-box/__tests__/index.spec.tsx b/web/app/components/plugins/marketplace/search-box/__tests__/index.spec.tsx index 31cb5d8445..8609ba5539 100644 --- a/web/app/components/plugins/marketplace/search-box/__tests__/index.spec.tsx +++ b/web/app/components/plugins/marketplace/search-box/__tests__/index.spec.tsx @@ -124,7 +124,7 @@ describe('SearchBox', () => { it('should render without crashing', () => { render() - expect(screen.getByRole('textbox')).toBeInTheDocument() + expect(screen.getByRole('textbox'))!.toBeInTheDocument() }) it('should render with marketplace mode styling', () => { @@ -133,7 +133,8 @@ describe('SearchBox', () => { ) // In marketplace mode, TagsFilter comes before input - expect(container.querySelector('.rounded-xl')).toBeInTheDocument() + // In marketplace mode, TagsFilter comes before input + expect(container.querySelector('.rounded-xl'))!.toBeInTheDocument() }) it('should render with non-marketplace mode styling', () => { @@ -142,25 +143,26 @@ describe('SearchBox', () => { ) // In non-marketplace mode, search icon appears first - expect(container.querySelector('.rounded-lg')).toBeInTheDocument() + // In non-marketplace mode, search icon appears first + expect(container.querySelector('.rounded-lg'))!.toBeInTheDocument() }) it('should render placeholder correctly', () => { render() - expect(screen.getByPlaceholderText('Search here...')).toBeInTheDocument() + expect(screen.getByPlaceholderText('Search here...'))!.toBeInTheDocument() }) it('should render search input with current value', () => { render() - expect(screen.getByDisplayValue('test query')).toBeInTheDocument() + expect(screen.getByDisplayValue('test query'))!.toBeInTheDocument() }) it('should render TagsFilter component', () => { render() - expect(screen.getByTestId('portal-elem')).toBeInTheDocument() + expect(screen.getByTestId('portal-elem'))!.toBeInTheDocument() }) }) @@ -175,8 +177,9 @@ describe('SearchBox', () => { const input = screen.getByRole('textbox') // Both should be rendered - expect(portalElem).toBeInTheDocument() - expect(input).toBeInTheDocument() + // Both should be rendered + expect(portalElem)!.toBeInTheDocument() + expect(input)!.toBeInTheDocument() }) it('should render clear button when search has value in marketplace mode', () => { @@ -207,7 +210,8 @@ describe('SearchBox', () => { ) // Search icon should be present - expect(container.querySelector('.text-components-input-text-placeholder')).toBeInTheDocument() + // Search icon should be present + expect(container.querySelector('.text-components-input-text-placeholder'))!.toBeInTheDocument() }) it('should render clear button when search has value', () => { @@ -223,8 +227,8 @@ describe('SearchBox', () => { const portalElem = screen.getByTestId('portal-elem') const input = screen.getByRole('textbox') - expect(portalElem).toBeInTheDocument() - expect(input).toBeInTheDocument() + expect(portalElem)!.toBeInTheDocument() + expect(input)!.toBeInTheDocument() }) it('should set autoFocus when prop is true', () => { @@ -232,7 +236,8 @@ describe('SearchBox', () => { const input = screen.getByRole('textbox') // autoFocus is a boolean attribute that React handles specially - expect(input).toBeInTheDocument() + // autoFocus is a boolean attribute that React handles specially + expect(input)!.toBeInTheDocument() }) }) @@ -264,7 +269,7 @@ describe('SearchBox', () => { const buttons = screen.getAllByRole('button') // Find the clear button (the one in the search area) const clearButton = buttons[buttons.length - 1] - fireEvent.click(clearButton) + fireEvent.click(clearButton!) expect(onSearchChange).toHaveBeenCalledWith('') }) @@ -282,7 +287,7 @@ describe('SearchBox', () => { const buttons = screen.getAllByRole('button') // First button should be the clear button in non-marketplace mode - fireEvent.click(buttons[0]) + fireEvent.click(buttons[0]!) expect(onSearchChange).toHaveBeenCalledWith('') }) @@ -356,7 +361,7 @@ describe('SearchBox', () => { , ) - expect(container.querySelector('.custom-wrapper-class')).toBeInTheDocument() + expect(container.querySelector('.custom-wrapper-class'))!.toBeInTheDocument() }) it('should apply inputClassName correctly', () => { @@ -364,19 +369,19 @@ describe('SearchBox', () => { , ) - expect(container.querySelector('.custom-input-class')).toBeInTheDocument() + expect(container.querySelector('.custom-input-class'))!.toBeInTheDocument() }) it('should handle empty placeholder', () => { render() - expect(screen.getByRole('textbox')).toHaveAttribute('placeholder', '') + expect(screen.getByRole('textbox'))!.toHaveAttribute('placeholder', '') }) it('should use default placeholder when not provided', () => { render() - expect(screen.getByRole('textbox')).toHaveAttribute('placeholder', '') + expect(screen.getByRole('textbox'))!.toHaveAttribute('placeholder', '') }) }) @@ -387,14 +392,14 @@ describe('SearchBox', () => { it('should handle empty search value', () => { render() - expect(screen.getByRole('textbox')).toBeInTheDocument() - expect(screen.getByRole('textbox')).toHaveValue('') + expect(screen.getByRole('textbox'))!.toBeInTheDocument() + expect(screen.getByRole('textbox'))!.toHaveValue('') }) it('should handle empty tags array', () => { render() - expect(screen.getByTestId('portal-elem')).toBeInTheDocument() + expect(screen.getByTestId('portal-elem'))!.toBeInTheDocument() }) it('should handle special characters in search', () => { @@ -411,7 +416,7 @@ describe('SearchBox', () => { const longString = 'a'.repeat(1000) render() - expect(screen.getByDisplayValue(longString)).toBeInTheDocument() + expect(screen.getByDisplayValue(longString))!.toBeInTheDocument() }) it('should handle whitespace-only search', () => { @@ -439,20 +444,21 @@ describe('SearchBoxWrapper', () => { it('should render without crashing', () => { render() - expect(screen.getByRole('textbox')).toBeInTheDocument() + expect(screen.getByRole('textbox'))!.toBeInTheDocument() }) it('should render in marketplace mode', () => { const { container } = render() - expect(container.querySelector('.rounded-xl')).toBeInTheDocument() + expect(container.querySelector('.rounded-xl'))!.toBeInTheDocument() }) it('should apply correct wrapper classes', () => { const { container } = render() // Check for z-11 class from wrapper - expect(container.querySelector('.z-11')).toBeInTheDocument() + // Check for z-11 class from wrapper + expect(container.querySelector('.z-11'))!.toBeInTheDocument() }) }) @@ -471,7 +477,7 @@ describe('SearchBoxWrapper', () => { it('should use translation for placeholder', () => { render() - expect(screen.getByPlaceholderText('Search plugins')).toBeInTheDocument() + expect(screen.getByPlaceholderText('Search plugins'))!.toBeInTheDocument() }) }) }) @@ -496,13 +502,13 @@ describe('MarketplaceTrigger', () => { it('should render without crashing', () => { render() - expect(screen.getByText('All Tags')).toBeInTheDocument() + expect(screen.getByText('All Tags'))!.toBeInTheDocument() }) it('should show "All Tags" when no tags selected', () => { render() - expect(screen.getByText('All Tags')).toBeInTheDocument() + expect(screen.getByText('All Tags'))!.toBeInTheDocument() }) it('should show arrow down icon when no tags selected', () => { @@ -511,7 +517,8 @@ describe('MarketplaceTrigger', () => { ) // Arrow down icon should be present - expect(container.querySelector('.size-4')).toBeInTheDocument() + // Arrow down icon should be present + expect(container.querySelector('.size-4'))!.toBeInTheDocument() }) }) @@ -525,7 +532,7 @@ describe('MarketplaceTrigger', () => { />, ) - expect(screen.getByText('Agent')).toBeInTheDocument() + expect(screen.getByText('Agent'))!.toBeInTheDocument() }) it('should show multiple tag labels separated by comma', () => { @@ -537,7 +544,7 @@ describe('MarketplaceTrigger', () => { />, ) - expect(screen.getByText('Agent,RAG')).toBeInTheDocument() + expect(screen.getByText('Agent,RAG'))!.toBeInTheDocument() }) it('should show +N indicator when more than 2 tags selected', () => { @@ -549,7 +556,7 @@ describe('MarketplaceTrigger', () => { />, ) - expect(screen.getByText('+2')).toBeInTheDocument() + expect(screen.getByText('+2'))!.toBeInTheDocument() }) it('should only show first 2 tags in label', () => { @@ -561,7 +568,7 @@ describe('MarketplaceTrigger', () => { />, ) - expect(screen.getByText('Agent,RAG')).toBeInTheDocument() + expect(screen.getByText('Agent,RAG'))!.toBeInTheDocument() expect(screen.queryByText('Search')).not.toBeInTheDocument() }) }) @@ -577,7 +584,8 @@ describe('MarketplaceTrigger', () => { ) // RiCloseCircleFill icon should be present - expect(container.querySelector('.text-text-quaternary')).toBeInTheDocument() + // RiCloseCircleFill icon should be present + expect(container.querySelector('.text-text-quaternary'))!.toBeInTheDocument() }) it('should not show clear button when no tags selected', () => { @@ -585,6 +593,37 @@ describe('MarketplaceTrigger', () => { , ) + // Clear button should not be present + // Clear button should not be present + // Clear button should not be present + // Clear button should not be present + // Clear button should not be present + // Clear button should not be present + // Clear button should not be present + // Clear button should not be present + // Clear button should not be present + // Clear button should not be present + // Clear button should not be present + // Clear button should not be present + // Clear button should not be present + // Clear button should not be present + // Clear button should not be present + // Clear button should not be present + // Clear button should not be present + // Clear button should not be present + // Clear button should not be present + // Clear button should not be present + // Clear button should not be present + // Clear button should not be present + // Clear button should not be present + // Clear button should not be present + // Clear button should not be present + // Clear button should not be present + // Clear button should not be present + // Clear button should not be present + // Clear button should not be present + // Clear button should not be present + // Clear button should not be present // Clear button should not be present expect(container.querySelector('.text-text-quaternary')).not.toBeInTheDocument() }) @@ -614,7 +653,7 @@ describe('MarketplaceTrigger', () => { , ) - expect(container.querySelector('.bg-state-base-hover')).toBeInTheDocument() + expect(container.querySelector('.bg-state-base-hover'))!.toBeInTheDocument() }) it('should apply border styling when tags are selected', () => { @@ -626,7 +665,7 @@ describe('MarketplaceTrigger', () => { />, ) - expect(container.querySelector('.border-components-button-secondary-border')).toBeInTheDocument() + expect(container.querySelector('.border-components-button-secondary-border'))!.toBeInTheDocument() }) }) @@ -636,7 +675,7 @@ describe('MarketplaceTrigger', () => { , ) - expect(container).toBeInTheDocument() + expect(container)!.toBeInTheDocument() }) }) }) @@ -661,13 +700,13 @@ describe('ToolSelectorTrigger', () => { it('should render without crashing', () => { const { container } = render() - expect(container).toBeInTheDocument() + expect(container)!.toBeInTheDocument() }) it('should render price tag icon', () => { const { container } = render() - expect(container.querySelector('.size-4')).toBeInTheDocument() + expect(container.querySelector('.size-4'))!.toBeInTheDocument() }) }) @@ -681,7 +720,7 @@ describe('ToolSelectorTrigger', () => { />, ) - expect(screen.getByText('Agent')).toBeInTheDocument() + expect(screen.getByText('Agent'))!.toBeInTheDocument() }) it('should show multiple tag labels separated by comma', () => { @@ -693,7 +732,7 @@ describe('ToolSelectorTrigger', () => { />, ) - expect(screen.getByText('Agent,RAG')).toBeInTheDocument() + expect(screen.getByText('Agent,RAG'))!.toBeInTheDocument() }) it('should show +N indicator when more than 2 tags selected', () => { @@ -705,7 +744,7 @@ describe('ToolSelectorTrigger', () => { />, ) - expect(screen.getByText('+2')).toBeInTheDocument() + expect(screen.getByText('+2'))!.toBeInTheDocument() }) it('should not show tag labels when no tags selected', () => { @@ -725,7 +764,7 @@ describe('ToolSelectorTrigger', () => { />, ) - expect(container.querySelector('.text-text-quaternary')).toBeInTheDocument() + expect(container.querySelector('.text-text-quaternary'))!.toBeInTheDocument() }) it('should not show clear button when no tags selected', () => { @@ -785,7 +824,7 @@ describe('ToolSelectorTrigger', () => { , ) - expect(container.querySelector('.bg-state-base-hover')).toBeInTheDocument() + expect(container.querySelector('.bg-state-base-hover'))!.toBeInTheDocument() }) it('should apply border styling when tags are selected', () => { @@ -797,7 +836,7 @@ describe('ToolSelectorTrigger', () => { />, ) - expect(container.querySelector('.border-components-button-secondary-border')).toBeInTheDocument() + expect(container.querySelector('.border-components-button-secondary-border'))!.toBeInTheDocument() }) it('should not apply hover styling when open but has tags', () => { @@ -811,7 +850,8 @@ describe('ToolSelectorTrigger', () => { ) // Should have border styling, not hover - expect(container.querySelector('.border-components-button-secondary-border')).toBeInTheDocument() + // Should have border styling, not hover + expect(container.querySelector('.border-components-button-secondary-border'))!.toBeInTheDocument() }) }) @@ -826,7 +866,7 @@ describe('ToolSelectorTrigger', () => { />, ) - expect(screen.getByText('Agent')).toBeInTheDocument() + expect(screen.getByText('Agent'))!.toBeInTheDocument() }) }) }) @@ -854,7 +894,7 @@ describe('TagsFilter', () => { />, ) - expect(screen.getByTestId('portal-elem')).toBeInTheDocument() + expect(screen.getByTestId('portal-elem'))!.toBeInTheDocument() }) it('should pass usedInMarketplace prop to TagsFilter', () => { @@ -869,7 +909,8 @@ describe('TagsFilter', () => { ) // MarketplaceTrigger should show "All Tags" - expect(screen.getByText('All Tags')).toBeInTheDocument() + // MarketplaceTrigger should show "All Tags" + expect(screen.getByText('All Tags'))!.toBeInTheDocument() }) it('should show selected tags count in TagsFilter trigger', () => { @@ -883,7 +924,7 @@ describe('TagsFilter', () => { />, ) - expect(screen.getByText('+1')).toBeInTheDocument() + expect(screen.getByText('+1'))!.toBeInTheDocument() }) }) @@ -902,7 +943,7 @@ describe('TagsFilter', () => { fireEvent.click(trigger) await waitFor(() => { - expect(screen.getByTestId('portal-content')).toBeInTheDocument() + expect(screen.getByTestId('portal-content'))!.toBeInTheDocument() }) }) @@ -921,7 +962,7 @@ describe('TagsFilter', () => { // Open fireEvent.click(trigger) await waitFor(() => { - expect(screen.getByTestId('portal-content')).toBeInTheDocument() + expect(screen.getByTestId('portal-content'))!.toBeInTheDocument() }) // Close @@ -947,8 +988,8 @@ describe('TagsFilter', () => { fireEvent.click(trigger) await waitFor(() => { - expect(screen.getByText('Agent')).toBeInTheDocument() - expect(screen.getByText('RAG')).toBeInTheDocument() + expect(screen.getByText('Agent'))!.toBeInTheDocument() + expect(screen.getByText('RAG'))!.toBeInTheDocument() }) }) @@ -967,7 +1008,7 @@ describe('TagsFilter', () => { fireEvent.click(trigger) await waitFor(() => { - expect(screen.getByText('Agent')).toBeInTheDocument() + expect(screen.getByText('Agent'))!.toBeInTheDocument() }) const agentOption = screen.getByText('Agent') @@ -1018,7 +1059,7 @@ describe('TagsFilter', () => { fireEvent.click(trigger) await waitFor(() => { - expect(screen.getByText('RAG')).toBeInTheDocument() + expect(screen.getByText('RAG'))!.toBeInTheDocument() }) const ragOption = screen.getByText('RAG') @@ -1061,7 +1102,7 @@ describe('TagsFilter', () => { fireEvent.click(trigger) await waitFor(() => { - expect(screen.getByText('Agent')).toBeInTheDocument() + expect(screen.getByText('Agent'))!.toBeInTheDocument() }) const inputs = screen.getAllByRole('textbox') @@ -1071,7 +1112,7 @@ describe('TagsFilter', () => { if (searchInput) { fireEvent.change(searchInput, { target: { value: 'agent' } }) - expect(screen.getByText('Agent')).toBeInTheDocument() + expect(screen.getByText('Agent'))!.toBeInTheDocument() } }) }) @@ -1097,7 +1138,8 @@ describe('TagsFilter', () => { }) // Verify dropdown content is rendered - expect(screen.getByTestId('portal-content')).toBeInTheDocument() + // Verify dropdown content is rendered + expect(screen.getByTestId('portal-content'))!.toBeInTheDocument() }) it('should render tag options when dropdown is open', async () => { @@ -1114,13 +1156,14 @@ describe('TagsFilter', () => { fireEvent.click(trigger) await waitFor(() => { - expect(screen.getByTestId('portal-content')).toBeInTheDocument() + expect(screen.getByTestId('portal-content'))!.toBeInTheDocument() }) // When no tags selected, these should appear once each in dropdown - expect(screen.getByText('Agent')).toBeInTheDocument() - expect(screen.getByText('RAG')).toBeInTheDocument() - expect(screen.getByText('Search')).toBeInTheDocument() + // When no tags selected, these should appear once each in dropdown + expect(screen.getByText('Agent'))!.toBeInTheDocument() + expect(screen.getByText('RAG'))!.toBeInTheDocument() + expect(screen.getByText('Search'))!.toBeInTheDocument() }) }) }) @@ -1146,8 +1189,8 @@ describe('Accessibility', () => { ) const input = screen.getByRole('textbox') - expect(input).toBeInTheDocument() - expect(input).toHaveAttribute('placeholder', 'Search plugins') + expect(input)!.toBeInTheDocument() + expect(input)!.toHaveAttribute('placeholder', 'Search plugins') }) it('should have clickable tag options in dropdown', async () => { @@ -1156,7 +1199,7 @@ describe('Accessibility', () => { fireEvent.click(screen.getByTestId('portal-trigger')) await waitFor(() => { - expect(screen.getByText('Agent')).toBeInTheDocument() + expect(screen.getByText('Agent'))!.toBeInTheDocument() }) }) }) @@ -1192,7 +1235,7 @@ describe('Combined Workflows', () => { fireEvent.click(trigger) await waitFor(() => { - expect(screen.getByText('Agent')).toBeInTheDocument() + expect(screen.getByText('Agent'))!.toBeInTheDocument() }) const agentOption = screen.getByText('Agent') @@ -1217,9 +1260,9 @@ describe('Combined Workflows', () => { />, ) - expect(screen.getByDisplayValue('test')).toBeInTheDocument() - expect(screen.getByText('Agent,RAG')).toBeInTheDocument() - expect(screen.getByTestId('portal-elem')).toBeInTheDocument() + expect(screen.getByDisplayValue('test'))!.toBeInTheDocument() + expect(screen.getByText('Agent,RAG'))!.toBeInTheDocument() + expect(screen.getByTestId('portal-elem'))!.toBeInTheDocument() }) it('should handle prop changes correctly', () => { @@ -1234,7 +1277,7 @@ describe('Combined Workflows', () => { />, ) - expect(screen.getByDisplayValue('initial')).toBeInTheDocument() + expect(screen.getByDisplayValue('initial'))!.toBeInTheDocument() rerender( { />, ) - expect(screen.getByDisplayValue('updated')).toBeInTheDocument() + expect(screen.getByDisplayValue('updated'))!.toBeInTheDocument() }) }) diff --git a/web/app/components/plugins/marketplace/search-box/trigger/marketplace.tsx b/web/app/components/plugins/marketplace/search-box/trigger/marketplace.tsx index 414216c532..167d6fcd70 100644 --- a/web/app/components/plugins/marketplace/search-box/trigger/marketplace.tsx +++ b/web/app/components/plugins/marketplace/search-box/trigger/marketplace.tsx @@ -39,7 +39,7 @@ const MarketplaceTrigger = ({ { !!selectedTagsLength && ( - {tags.map(tag => tagsMap[tag].label).slice(0, 2).join(',')} + {tags.map(tag => tagsMap[tag]!.label).slice(0, 2).join(',')} ) } diff --git a/web/app/components/plugins/marketplace/search-box/trigger/tool-selector.tsx b/web/app/components/plugins/marketplace/search-box/trigger/tool-selector.tsx index 6c733ea546..d351414ee8 100644 --- a/web/app/components/plugins/marketplace/search-box/trigger/tool-selector.tsx +++ b/web/app/components/plugins/marketplace/search-box/trigger/tool-selector.tsx @@ -33,7 +33,7 @@ const ToolSelectorTrigger = ({ !!selectedTagsLength && (
- {tags.map(tag => tagsMap[tag].label).slice(0, 2).join(',')} + {tags.map(tag => tagsMap[tag]!.label).slice(0, 2).join(',')} { selectedTagsLength > 2 && ( diff --git a/web/app/components/plugins/marketplace/sort-dropdown/index.tsx b/web/app/components/plugins/marketplace/sort-dropdown/index.tsx index dddfab5402..d8b6939d1a 100644 --- a/web/app/components/plugins/marketplace/sort-dropdown/index.tsx +++ b/web/app/components/plugins/marketplace/sort-dropdown/index.tsx @@ -56,7 +56,7 @@ const SortDropdown = () => { {t('marketplace.sortBy', { ns: 'plugin' })} - {selectedOption.text} + {selectedOption!.text}
diff --git a/web/app/components/plugins/plugin-auth/__tests__/authorized-in-node.spec.tsx b/web/app/components/plugins/plugin-auth/__tests__/authorized-in-node.spec.tsx index f4eb9462fc..cba8dc96c9 100644 --- a/web/app/components/plugins/plugin-auth/__tests__/authorized-in-node.spec.tsx +++ b/web/app/components/plugins/plugin-auth/__tests__/authorized-in-node.spec.tsx @@ -111,7 +111,7 @@ describe('AuthorizedInNode Component', () => { , { wrapper: createWrapper() }, ) - expect(screen.getByText('plugin.auth.workspaceDefault')).toBeInTheDocument() + expect(screen.getByText('plugin.auth.workspaceDefault'))!.toBeInTheDocument() }) it('should render credential name when credentialId matches', async () => { @@ -127,7 +127,7 @@ describe('AuthorizedInNode Component', () => { , { wrapper: createWrapper() }, ) - expect(screen.getByText('My Credential')).toBeInTheDocument() + expect(screen.getByText('My Credential'))!.toBeInTheDocument() }) it('should show auth removed when credentialId not found', async () => { @@ -142,7 +142,7 @@ describe('AuthorizedInNode Component', () => { , { wrapper: createWrapper() }, ) - expect(screen.getByText('plugin.auth.authRemoved')).toBeInTheDocument() + expect(screen.getByText('plugin.auth.authRemoved'))!.toBeInTheDocument() }) it('should show unavailable when credential is not allowed', async () => { @@ -195,7 +195,7 @@ describe('AuthorizedInNode Component', () => { { wrapper: createWrapper() }, ) const buttons = screen.getAllByRole('button') - fireEvent.click(buttons[0]) + fireEvent.click(buttons[0]!) expect(screen.getAllByRole('button').length).toBeGreaterThan(0) }) diff --git a/web/app/components/plugins/plugin-auth/__tests__/plugin-auth-in-agent.spec.tsx b/web/app/components/plugins/plugin-auth/__tests__/plugin-auth-in-agent.spec.tsx index 4c26712fdc..6afe1ae334 100644 --- a/web/app/components/plugins/plugin-auth/__tests__/plugin-auth-in-agent.spec.tsx +++ b/web/app/components/plugins/plugin-auth/__tests__/plugin-auth-in-agent.spec.tsx @@ -116,7 +116,7 @@ describe('PluginAuthInAgent Component', () => { , { wrapper: createWrapper() }, ) - expect(screen.getByRole('button')).toBeInTheDocument() + expect(screen.getByRole('button'))!.toBeInTheDocument() }) it('should render Authorized with workspace default when authorized', async () => { @@ -126,8 +126,8 @@ describe('PluginAuthInAgent Component', () => { , { wrapper: createWrapper() }, ) - expect(screen.getByRole('button')).toBeInTheDocument() - expect(screen.getByText('plugin.auth.workspaceDefault')).toBeInTheDocument() + expect(screen.getByRole('button'))!.toBeInTheDocument() + expect(screen.getByText('plugin.auth.workspaceDefault'))!.toBeInTheDocument() }) it('should show credential name when credentialId is provided', async () => { @@ -143,7 +143,7 @@ describe('PluginAuthInAgent Component', () => { , { wrapper: createWrapper() }, ) - expect(screen.getByText('Selected Credential')).toBeInTheDocument() + expect(screen.getByText('Selected Credential'))!.toBeInTheDocument() }) it('should show auth removed when credential not found', async () => { @@ -158,7 +158,7 @@ describe('PluginAuthInAgent Component', () => { , { wrapper: createWrapper() }, ) - expect(screen.getByText('plugin.auth.authRemoved')).toBeInTheDocument() + expect(screen.getByText('plugin.auth.authRemoved'))!.toBeInTheDocument() }) it('should show unavailable when credential is not allowed to use', async () => { @@ -192,7 +192,7 @@ describe('PluginAuthInAgent Component', () => { { wrapper: createWrapper() }, ) const buttons = screen.getAllByRole('button') - fireEvent.click(buttons[0]) + fireEvent.click(buttons[0]!) expect(screen.getAllByRole('button').length).toBeGreaterThan(0) }) @@ -214,7 +214,7 @@ describe('PluginAuthInAgent Component', () => { fireEvent.click(triggerButton) const workspaceDefaultItems = screen.getAllByText('plugin.auth.workspaceDefault') const popupItem = workspaceDefaultItems.length > 1 ? workspaceDefaultItems[1] : workspaceDefaultItems[0] - fireEvent.click(popupItem) + fireEvent.click(popupItem!) expect(onAuthorizationItemClick).toHaveBeenCalledWith('') }) @@ -240,7 +240,7 @@ describe('PluginAuthInAgent Component', () => { fireEvent.click(triggerButton) const credentialItems = screen.getAllByText('Specific Credential') const popupItem = credentialItems[credentialItems.length - 1] - fireEvent.click(popupItem) + fireEvent.click(popupItem!) expect(onAuthorizationItemClick).toHaveBeenCalledWith('specific-cred-id') }) diff --git a/web/app/components/plugins/plugin-auth/authorized/__tests__/index.spec.tsx b/web/app/components/plugins/plugin-auth/authorized/__tests__/index.spec.tsx index f911dcd4e0..a127067526 100644 --- a/web/app/components/plugins/plugin-auth/authorized/__tests__/index.spec.tsx +++ b/web/app/components/plugins/plugin-auth/authorized/__tests__/index.spec.tsx @@ -147,7 +147,7 @@ describe('Authorized Component', () => { { wrapper: createWrapper() }, ) - expect(screen.getByRole('button')).toBeInTheDocument() + expect(screen.getByRole('button'))!.toBeInTheDocument() }) it('should render with custom trigger when renderTrigger is provided', () => { @@ -163,8 +163,8 @@ describe('Authorized Component', () => { { wrapper: createWrapper() }, ) - expect(screen.getByTestId('custom-trigger')).toBeInTheDocument() - expect(screen.getByText('Closed')).toBeInTheDocument() + expect(screen.getByTestId('custom-trigger'))!.toBeInTheDocument() + expect(screen.getByText('Closed'))!.toBeInTheDocument() }) it('should show singular authorization text for 1 credential', () => { @@ -180,7 +180,8 @@ describe('Authorized Component', () => { ) // Text is split by elements, use regex to find partial match - expect(screen.getByText(/plugin\.auth\.authorization/)).toBeInTheDocument() + // Text is split by elements, use regex to find partial match + expect(screen.getByText(/plugin\.auth\.authorization/))!.toBeInTheDocument() }) it('should show plural authorizations text for multiple credentials', () => { @@ -199,7 +200,8 @@ describe('Authorized Component', () => { ) // Text is split by elements, use regex to find partial match - expect(screen.getByText(/plugin\.auth\.authorizations/)).toBeInTheDocument() + // Text is split by elements, use regex to find partial match + expect(screen.getByText(/plugin\.auth\.authorizations/))!.toBeInTheDocument() }) it('should show unavailable count when there are unavailable credentials', () => { @@ -217,7 +219,7 @@ describe('Authorized Component', () => { { wrapper: createWrapper() }, ) - expect(screen.getByText(/plugin\.auth\.unavailable/)).toBeInTheDocument() + expect(screen.getByText(/plugin\.auth\.unavailable/))!.toBeInTheDocument() }) it('should show gray indicator when default credential is unavailable', () => { @@ -235,7 +237,8 @@ describe('Authorized Component', () => { ) // The indicator should be rendered - expect(container.querySelector('[data-testid="status-indicator"]')).toBeInTheDocument() + // The indicator should be rendered + expect(container.querySelector('[data-testid="status-indicator"]'))!.toBeInTheDocument() }) }) @@ -257,7 +260,8 @@ describe('Authorized Component', () => { fireEvent.click(trigger) // Popup should be open - check for popup content - expect(screen.getByText('API Keys')).toBeInTheDocument() + // Popup should be open - check for popup content + expect(screen.getByText('API Keys'))!.toBeInTheDocument() }) it('should use controlled open state when isOpen and onOpenChange are provided', () => { @@ -276,11 +280,12 @@ describe('Authorized Component', () => { ) // Popup should be open since isOpen is true - expect(screen.getByText('API Keys')).toBeInTheDocument() + // Popup should be open since isOpen is true + expect(screen.getByText('API Keys'))!.toBeInTheDocument() // Click trigger to close - get all buttons and click the first one (trigger) const buttons = screen.getAllByRole('button') - fireEvent.click(buttons[0]) + fireEvent.click(buttons[0]!) expect(onOpenChange).toHaveBeenCalledWith(false) }) @@ -301,7 +306,7 @@ describe('Authorized Component', () => { // Open fireEvent.click(trigger) - expect(screen.getByText('API Keys')).toBeInTheDocument() + expect(screen.getByText('API Keys'))!.toBeInTheDocument() // Close fireEvent.click(trigger) @@ -326,8 +331,8 @@ describe('Authorized Component', () => { { wrapper: createWrapper() }, ) - expect(screen.getByText('OAuth')).toBeInTheDocument() - expect(screen.getByText('OAuth Cred')).toBeInTheDocument() + expect(screen.getByText('OAuth'))!.toBeInTheDocument() + expect(screen.getByText('OAuth Cred'))!.toBeInTheDocument() }) it('should render API Key credentials section when apiKeyCredentials exist', () => { @@ -345,8 +350,8 @@ describe('Authorized Component', () => { { wrapper: createWrapper() }, ) - expect(screen.getByText('API Keys')).toBeInTheDocument() - expect(screen.getByText('API Key Cred')).toBeInTheDocument() + expect(screen.getByText('API Keys'))!.toBeInTheDocument() + expect(screen.getByText('API Key Cred'))!.toBeInTheDocument() }) it('should render both OAuth and API Key sections when both exist', () => { @@ -365,8 +370,8 @@ describe('Authorized Component', () => { { wrapper: createWrapper() }, ) - expect(screen.getByText('OAuth')).toBeInTheDocument() - expect(screen.getByText('API Keys')).toBeInTheDocument() + expect(screen.getByText('OAuth'))!.toBeInTheDocument() + expect(screen.getByText('API Keys'))!.toBeInTheDocument() }) it('should render extra authorization items when provided', () => { @@ -386,7 +391,7 @@ describe('Authorized Component', () => { { wrapper: createWrapper() }, ) - expect(screen.getByText('Extra Item')).toBeInTheDocument() + expect(screen.getByText('Extra Item'))!.toBeInTheDocument() }) it('should pass showSelectedIcon and selectedCredentialId to items', () => { @@ -405,7 +410,8 @@ describe('Authorized Component', () => { ) // Selected icon should be visible - expect(document.querySelector('.text-text-accent')).toBeInTheDocument() + // Selected icon should be visible + expect(document.querySelector('.text-text-accent'))!.toBeInTheDocument() }) }) @@ -431,7 +437,7 @@ describe('Authorized Component', () => { // Confirm dialog should appear await waitFor(() => { - expect(screen.getByText('datasetDocuments.list.delete.title')).toBeInTheDocument() + expect(screen.getByText('datasetDocuments.list.delete.title'))!.toBeInTheDocument() }) } }) @@ -451,7 +457,7 @@ describe('Authorized Component', () => { // Wait for OAuth section to render await waitFor(() => { - expect(screen.getByText('OAuth')).toBeInTheDocument() + expect(screen.getByText('OAuth'))!.toBeInTheDocument() }) // Find all SVG icons in the action area and try to find delete button @@ -482,7 +488,8 @@ describe('Authorized Component', () => { } // Component should render correctly regardless of button finding - expect(screen.getByText('OAuth')).toBeInTheDocument() + // Component should render correctly regardless of button finding + expect(screen.getByText('OAuth'))!.toBeInTheDocument() }) it('should call deletePluginCredential when confirm is clicked', async () => { @@ -506,7 +513,7 @@ describe('Authorized Component', () => { fireEvent.click(deleteButton) await waitFor(() => { - expect(screen.getByText('datasetDocuments.list.delete.title')).toBeInTheDocument() + expect(screen.getByText('datasetDocuments.list.delete.title'))!.toBeInTheDocument() }) // Click confirm button @@ -693,7 +700,8 @@ describe('Authorized Component', () => { } else { // Verify component renders properly - expect(screen.getByText('OAuth')).toBeInTheDocument() + // Verify component renders properly + expect(screen.getByText('OAuth'))!.toBeInTheDocument() } }) @@ -716,7 +724,8 @@ describe('Authorized Component', () => { ) // Verify component renders - expect(screen.getByText('OAuth')).toBeInTheDocument() + // Verify component renders + expect(screen.getByText('OAuth'))!.toBeInTheDocument() }) it('should execute handleRename function body when saving', async () => { @@ -745,8 +754,9 @@ describe('Authorized Component', () => { ) // Wait for component to render - expect(screen.getByText('OAuth')).toBeInTheDocument() - expect(screen.getByText('Execute Rename Test')).toBeInTheDocument() + // Wait for component to render + expect(screen.getByText('OAuth'))!.toBeInTheDocument() + expect(screen.getByText('Execute Rename Test'))!.toBeInTheDocument() // The handleRename is tested through the "should call updatePluginCredential when rename is confirmed" test // This test verifies the component properly renders OAuth credentials @@ -778,7 +788,8 @@ describe('Authorized Component', () => { ) // Verify OAuth section renders - expect(screen.getByText('OAuth')).toBeInTheDocument() + // Verify OAuth section renders + expect(screen.getByText('OAuth'))!.toBeInTheDocument() // Find all action buttons in the credential item // The rename button should be present for OAuth credentials @@ -857,7 +868,8 @@ describe('Authorized Component', () => { // ApiKeyModal should appear - look for modal content await waitFor(() => { // The modal should be rendered - expect(document.querySelector('.fixed')).toBeInTheDocument() + // The modal should be rendered + expect(document.querySelector('.fixed'))!.toBeInTheDocument() }) } }) @@ -887,7 +899,7 @@ describe('Authorized Component', () => { fireEvent.click(editButton) await waitFor(() => { - expect(document.querySelector('.fixed')).toBeInTheDocument() + expect(document.querySelector('.fixed'))!.toBeInTheDocument() }) // Find and click close/cancel button in the modal @@ -907,7 +919,8 @@ describe('Authorized Component', () => { await waitFor(() => { // Verify component state is cleared by checking we can open again - expect(screen.getByText('API Keys')).toBeInTheDocument() + // Verify component state is cleared by checking we can open again + expect(screen.getByText('API Keys'))!.toBeInTheDocument() }) } } @@ -971,13 +984,14 @@ describe('Authorized Component', () => { // Verify state was reset - we should be able to see the credential list again await waitFor(() => { - expect(screen.getByText('API Keys')).toBeInTheDocument() + expect(screen.getByText('API Keys'))!.toBeInTheDocument() }) } } else { // Verify component renders - expect(screen.getByText('API Keys')).toBeInTheDocument() + // Verify component renders + expect(screen.getByText('API Keys'))!.toBeInTheDocument() } }) @@ -1002,12 +1016,13 @@ describe('Authorized Component', () => { ) // Wait for component to render - expect(screen.getByText('API Keys')).toBeInTheDocument() + // Wait for component to render + expect(screen.getByText('API Keys'))!.toBeInTheDocument() // Find edit button by looking for settings icon const settingsIcons = document.querySelectorAll('svg.ri-equalizer-2-line') if (settingsIcons.length > 0) { - const editButton = settingsIcons[0].closest('button') + const editButton = settingsIcons[0]!.closest('button') if (editButton) { // Click to open edit modal await act(async () => { @@ -1033,7 +1048,7 @@ describe('Authorized Component', () => { // Verify the modal is closed and state is reset // The component should render normally after close await waitFor(() => { - expect(screen.getByText('API Keys')).toBeInTheDocument() + expect(screen.getByText('API Keys'))!.toBeInTheDocument() }) break } @@ -1063,12 +1078,13 @@ describe('Authorized Component', () => { ) // Wait for component to render - expect(screen.getByText('API Keys')).toBeInTheDocument() + // Wait for component to render + expect(screen.getByText('API Keys'))!.toBeInTheDocument() // Find and click edit button to open ApiKeyModal const settingsIcons = document.querySelectorAll('svg.ri-equalizer-2-line') if (settingsIcons.length > 0) { - const editButton = settingsIcons[0].closest('button') + const editButton = settingsIcons[0]!.closest('button') if (editButton) { await act(async () => { fireEvent.click(editButton) @@ -1093,7 +1109,7 @@ describe('Authorized Component', () => { await waitFor(() => { const confirmDialog = screen.queryByText('datasetDocuments.list.delete.title') if (confirmDialog) { - expect(confirmDialog).toBeInTheDocument() + expect(confirmDialog)!.toBeInTheDocument() } }, { timeout: 1000 }) } @@ -1122,7 +1138,8 @@ describe('Authorized Component', () => { ) // Verify API Keys section is shown - expect(screen.getByText('API Keys')).toBeInTheDocument() + // Verify API Keys section is shown + expect(screen.getByText('API Keys'))!.toBeInTheDocument() // Find edit button - look for buttons in the action area const actionAreaButtons = Array.from(document.querySelectorAll('.group-hover\\:flex button, .hidden button')) @@ -1149,7 +1166,8 @@ describe('Authorized Component', () => { } // Verify component renders correctly - expect(screen.getByText('API Keys')).toBeInTheDocument() + // Verify component renders correctly + expect(screen.getByText('API Keys'))!.toBeInTheDocument() }) it('should trigger handleRemove when remove button is clicked in ApiKeyModal', async () => { @@ -1173,7 +1191,8 @@ describe('Authorized Component', () => { ) // Verify component renders - expect(screen.getByText('API Keys')).toBeInTheDocument() + // Verify component renders + expect(screen.getByText('API Keys'))!.toBeInTheDocument() // Find edit button by looking for action buttons (not in the confirm dialog) // These are grouped in hidden elements that show on hover @@ -1203,7 +1222,8 @@ describe('Authorized Component', () => { } // Verify component still works - expect(screen.getByText('API Keys')).toBeInTheDocument() + // Verify component still works + expect(screen.getByText('API Keys'))!.toBeInTheDocument() }) it('should show confirm dialog when remove is clicked from edit modal', async () => { @@ -1230,7 +1250,7 @@ describe('Authorized Component', () => { fireEvent.click(editButton) await waitFor(() => { - expect(document.querySelector('.fixed')).toBeInTheDocument() + expect(document.querySelector('.fixed'))!.toBeInTheDocument() }) // Find remove button in modal (usually has delete/remove text) @@ -1242,7 +1262,7 @@ describe('Authorized Component', () => { // Confirm dialog should appear await waitFor(() => { - expect(screen.getByText('datasetDocuments.list.delete.title')).toBeInTheDocument() + expect(screen.getByText('datasetDocuments.list.delete.title'))!.toBeInTheDocument() }) } } @@ -1284,7 +1304,7 @@ describe('Authorized Component', () => { // Wait for modal to open await waitFor(() => { const modal = document.querySelector('.fixed') - expect(modal).toBeInTheDocument() + expect(modal)!.toBeInTheDocument() }) // Find the close/cancel button @@ -1304,13 +1324,14 @@ describe('Authorized Component', () => { // Verify component still works after closing await waitFor(() => { - expect(screen.getByText('API Keys')).toBeInTheDocument() + expect(screen.getByText('API Keys'))!.toBeInTheDocument() }) } } else { // If no edit button found, just verify the component renders - expect(screen.getByText('API Keys')).toBeInTheDocument() + // If no edit button found, just verify the component renders + expect(screen.getByText('API Keys'))!.toBeInTheDocument() } }) }) @@ -1359,7 +1380,8 @@ describe('Authorized Component', () => { ) // Should have divider and authorize buttons - expect(document.querySelector('.bg-divider-subtle')).toBeInTheDocument() + // Should have divider and authorize buttons + expect(document.querySelector('.bg-divider-subtle'))!.toBeInTheDocument() }) it('should not render Authorize component when notAllowCustomCredential is true', () => { @@ -1400,7 +1422,7 @@ describe('Authorized Component', () => { { wrapper: createWrapper() }, ) - expect(document.querySelector('.custom-popup-class')).toBeInTheDocument() + expect(document.querySelector('.custom-popup-class'))!.toBeInTheDocument() }) it('should pass placement to PortalToFollowElem', () => { @@ -1419,7 +1441,8 @@ describe('Authorized Component', () => { ) // Component should render without error - expect(screen.getByText('API Keys')).toBeInTheDocument() + // Component should render without error + expect(screen.getByText('API Keys'))!.toBeInTheDocument() }) it('should pass disabled to Item components', () => { @@ -1441,11 +1464,12 @@ describe('Authorized Component', () => { const setDefaultButton = screen.queryByText('plugin.auth.setDefault') if (setDefaultButton) { const button = setDefaultButton.closest('button') - expect(button).toBeDisabled() + expect(button)!.toBeDisabled() } else { // If no set default button, verify the component rendered - expect(screen.getByText('API Keys')).toBeInTheDocument() + // If no set default button, verify the component rendered + expect(screen.getByText('API Keys'))!.toBeInTheDocument() } }) @@ -1463,6 +1487,37 @@ describe('Authorized Component', () => { { wrapper: createWrapper() }, ) + // Set default button should not be visible + // Set default button should not be visible + // Set default button should not be visible + // Set default button should not be visible + // Set default button should not be visible + // Set default button should not be visible + // Set default button should not be visible + // Set default button should not be visible + // Set default button should not be visible + // Set default button should not be visible + // Set default button should not be visible + // Set default button should not be visible + // Set default button should not be visible + // Set default button should not be visible + // Set default button should not be visible + // Set default button should not be visible + // Set default button should not be visible + // Set default button should not be visible + // Set default button should not be visible + // Set default button should not be visible + // Set default button should not be visible + // Set default button should not be visible + // Set default button should not be visible + // Set default button should not be visible + // Set default button should not be visible + // Set default button should not be visible + // Set default button should not be visible + // Set default button should not be visible + // Set default button should not be visible + // Set default button should not be visible + // Set default button should not be visible // Set default button should not be visible expect(screen.queryByText('plugin.auth.setDefault')).not.toBeInTheDocument() }) @@ -1492,7 +1547,7 @@ describe('Authorized Component', () => { fireEvent.click(deleteButton) await waitFor(() => { - expect(screen.getByText('datasetDocuments.list.delete.title')).toBeInTheDocument() + expect(screen.getByText('datasetDocuments.list.delete.title'))!.toBeInTheDocument() }) const confirmButton = screen.getByText('common.operation.confirm') @@ -1623,7 +1678,8 @@ describe('Authorized Component', () => { ) // Component should render without error - expect(screen.getByText('API Keys')).toBeInTheDocument() + // Component should render without error + expect(screen.getByText('API Keys'))!.toBeInTheDocument() }) }) @@ -1663,7 +1719,7 @@ describe('Authorized Component', () => { // Wait for component to render await waitFor(() => { - expect(screen.getByText('OAuth')).toBeInTheDocument() + expect(screen.getByText('OAuth'))!.toBeInTheDocument() }) // Find all buttons in the credential item's action area @@ -1698,7 +1754,7 @@ describe('Authorized Component', () => { // Verify confirm dialog appears await waitFor(() => { - expect(screen.getByText('datasetDocuments.list.delete.title')).toBeInTheDocument() + expect(screen.getByText('datasetDocuments.list.delete.title'))!.toBeInTheDocument() }) // Click confirm - this calls handleConfirm @@ -1730,7 +1786,8 @@ describe('Authorized Component', () => { } else { // Component should still render correctly - expect(screen.getByText('OAuth')).toBeInTheDocument() + // Component should still render correctly + expect(screen.getByText('OAuth'))!.toBeInTheDocument() } }) @@ -1753,7 +1810,8 @@ describe('Authorized Component', () => { ) // Verify component renders - expect(screen.getByText('API Keys')).toBeInTheDocument() + // Verify component renders + expect(screen.getByText('API Keys'))!.toBeInTheDocument() }) it('should prevent handleConfirm when doingAction is true', async () => { @@ -1812,7 +1870,8 @@ describe('Authorized Component', () => { if (!foundDeleteButton) { // Verify component renders - expect(screen.getByText('OAuth')).toBeInTheDocument() + // Verify component renders + expect(screen.getByText('OAuth'))!.toBeInTheDocument() } }) @@ -1831,6 +1890,99 @@ describe('Authorized Component', () => { { wrapper: createWrapper() }, ) + // With no credentials, there's no way to trigger openConfirm, + // so pendingOperationCredentialId stays null + // This edge case is handled by the component's internal logic + // With no credentials, there's no way to trigger openConfirm, + // so pendingOperationCredentialId stays null + // This edge case is handled by the component's internal logic + // With no credentials, there's no way to trigger openConfirm, + // so pendingOperationCredentialId stays null + // This edge case is handled by the component's internal logic + // With no credentials, there's no way to trigger openConfirm, + // so pendingOperationCredentialId stays null + // This edge case is handled by the component's internal logic + // With no credentials, there's no way to trigger openConfirm, + // so pendingOperationCredentialId stays null + // This edge case is handled by the component's internal logic + // With no credentials, there's no way to trigger openConfirm, + // so pendingOperationCredentialId stays null + // This edge case is handled by the component's internal logic + // With no credentials, there's no way to trigger openConfirm, + // so pendingOperationCredentialId stays null + // This edge case is handled by the component's internal logic + // With no credentials, there's no way to trigger openConfirm, + // so pendingOperationCredentialId stays null + // This edge case is handled by the component's internal logic + // With no credentials, there's no way to trigger openConfirm, + // so pendingOperationCredentialId stays null + // This edge case is handled by the component's internal logic + // With no credentials, there's no way to trigger openConfirm, + // so pendingOperationCredentialId stays null + // This edge case is handled by the component's internal logic + // With no credentials, there's no way to trigger openConfirm, + // so pendingOperationCredentialId stays null + // This edge case is handled by the component's internal logic + // With no credentials, there's no way to trigger openConfirm, + // so pendingOperationCredentialId stays null + // This edge case is handled by the component's internal logic + // With no credentials, there's no way to trigger openConfirm, + // so pendingOperationCredentialId stays null + // This edge case is handled by the component's internal logic + // With no credentials, there's no way to trigger openConfirm, + // so pendingOperationCredentialId stays null + // This edge case is handled by the component's internal logic + // With no credentials, there's no way to trigger openConfirm, + // so pendingOperationCredentialId stays null + // This edge case is handled by the component's internal logic + // With no credentials, there's no way to trigger openConfirm, + // so pendingOperationCredentialId stays null + // This edge case is handled by the component's internal logic + // With no credentials, there's no way to trigger openConfirm, + // so pendingOperationCredentialId stays null + // This edge case is handled by the component's internal logic + // With no credentials, there's no way to trigger openConfirm, + // so pendingOperationCredentialId stays null + // This edge case is handled by the component's internal logic + // With no credentials, there's no way to trigger openConfirm, + // so pendingOperationCredentialId stays null + // This edge case is handled by the component's internal logic + // With no credentials, there's no way to trigger openConfirm, + // so pendingOperationCredentialId stays null + // This edge case is handled by the component's internal logic + // With no credentials, there's no way to trigger openConfirm, + // so pendingOperationCredentialId stays null + // This edge case is handled by the component's internal logic + // With no credentials, there's no way to trigger openConfirm, + // so pendingOperationCredentialId stays null + // This edge case is handled by the component's internal logic + // With no credentials, there's no way to trigger openConfirm, + // so pendingOperationCredentialId stays null + // This edge case is handled by the component's internal logic + // With no credentials, there's no way to trigger openConfirm, + // so pendingOperationCredentialId stays null + // This edge case is handled by the component's internal logic + // With no credentials, there's no way to trigger openConfirm, + // so pendingOperationCredentialId stays null + // This edge case is handled by the component's internal logic + // With no credentials, there's no way to trigger openConfirm, + // so pendingOperationCredentialId stays null + // This edge case is handled by the component's internal logic + // With no credentials, there's no way to trigger openConfirm, + // so pendingOperationCredentialId stays null + // This edge case is handled by the component's internal logic + // With no credentials, there's no way to trigger openConfirm, + // so pendingOperationCredentialId stays null + // This edge case is handled by the component's internal logic + // With no credentials, there's no way to trigger openConfirm, + // so pendingOperationCredentialId stays null + // This edge case is handled by the component's internal logic + // With no credentials, there's no way to trigger openConfirm, + // so pendingOperationCredentialId stays null + // This edge case is handled by the component's internal logic + // With no credentials, there's no way to trigger openConfirm, + // so pendingOperationCredentialId stays null + // This edge case is handled by the component's internal logic // With no credentials, there's no way to trigger openConfirm, // so pendingOperationCredentialId stays null // This edge case is handled by the component's internal logic @@ -1859,7 +2011,7 @@ describe('Authorized Component', () => { // Wait for component to render await waitFor(() => { - expect(screen.getByText('OAuth')).toBeInTheDocument() + expect(screen.getByText('OAuth'))!.toBeInTheDocument() }) // Find delete button in action area @@ -1908,7 +2060,7 @@ describe('Authorized Component', () => { ) await waitFor(() => { - expect(screen.getByText('OAuth')).toBeInTheDocument() + expect(screen.getByText('OAuth'))!.toBeInTheDocument() }) // Find and trigger delete to open confirm dialog @@ -1921,7 +2073,7 @@ describe('Authorized Component', () => { const confirmTitle = screen.queryByText('datasetDocuments.list.delete.title') if (confirmTitle) { - expect(confirmTitle).toBeInTheDocument() + expect(confirmTitle)!.toBeInTheDocument() // Now click cancel to execute closeConfirm const cancelBtn = screen.getByText('common.operation.cancel') @@ -1940,7 +2092,7 @@ describe('Authorized Component', () => { }) await waitFor(() => { - expect(screen.getByText('datasetDocuments.list.delete.title')).toBeInTheDocument() + expect(screen.getByText('datasetDocuments.list.delete.title'))!.toBeInTheDocument() }) break } @@ -1966,7 +2118,7 @@ describe('Authorized Component', () => { ) await waitFor(() => { - expect(screen.getByText('OAuth')).toBeInTheDocument() + expect(screen.getByText('OAuth'))!.toBeInTheDocument() }) // Find and trigger delete to open confirm dialog @@ -2012,7 +2164,7 @@ describe('Authorized Component', () => { ) await waitFor(() => { - expect(screen.getByText('OAuth')).toBeInTheDocument() + expect(screen.getByText('OAuth'))!.toBeInTheDocument() }) // Find and trigger delete to open confirm dialog @@ -2066,7 +2218,7 @@ describe('Authorized Component', () => { // Wait for component to render await waitFor(() => { - expect(screen.getByText('API Keys')).toBeInTheDocument() + expect(screen.getByText('API Keys'))!.toBeInTheDocument() }) // Find edit button in action area @@ -2093,7 +2245,7 @@ describe('Authorized Component', () => { await waitFor(() => { const confirmTitle = screen.queryByText('datasetDocuments.list.delete.title') if (confirmTitle) { - expect(confirmTitle).toBeInTheDocument() + expect(confirmTitle)!.toBeInTheDocument() } }, { timeout: 2000 }) } @@ -2103,7 +2255,8 @@ describe('Authorized Component', () => { } // Verify component renders correctly - expect(screen.getByText('API Keys')).toBeInTheDocument() + // Verify component renders correctly + expect(screen.getByText('API Keys'))!.toBeInTheDocument() }) it('should execute handleRemove to set deleteCredentialId from pendingOperationCredentialId', async () => { @@ -2127,7 +2280,7 @@ describe('Authorized Component', () => { // Wait for component to render await waitFor(() => { - expect(screen.getByText('API Keys')).toBeInTheDocument() + expect(screen.getByText('API Keys'))!.toBeInTheDocument() }) // Find and click edit button to open ApiKeyModal @@ -2155,7 +2308,7 @@ describe('Authorized Component', () => { const confirmTitle = screen.queryByText('datasetDocuments.list.delete.title') // If confirm dialog appears, handleRemove was called if (confirmTitle) { - expect(confirmTitle).toBeInTheDocument() + expect(confirmTitle)!.toBeInTheDocument() } }, { timeout: 1000 }) } @@ -2165,7 +2318,8 @@ describe('Authorized Component', () => { } // Verify component still renders correctly - expect(screen.getByText('API Keys')).toBeInTheDocument() + // Verify component still renders correctly + expect(screen.getByText('API Keys'))!.toBeInTheDocument() }) }) @@ -2195,7 +2349,7 @@ describe('Authorized Component', () => { // Wait for component to render await waitFor(() => { - expect(screen.getByText('OAuth')).toBeInTheDocument() + expect(screen.getByText('OAuth'))!.toBeInTheDocument() }) // Find rename button in action area @@ -2259,7 +2413,7 @@ describe('Authorized Component', () => { ) await waitFor(() => { - expect(screen.getByText('OAuth')).toBeInTheDocument() + expect(screen.getByText('OAuth'))!.toBeInTheDocument() }) // Find rename button @@ -2324,7 +2478,7 @@ describe('Authorized Component', () => { // Wait for component to render await waitFor(() => { - expect(screen.getByText('API Keys')).toBeInTheDocument() + expect(screen.getByText('API Keys'))!.toBeInTheDocument() }) // Find and click edit button to open modal @@ -2346,12 +2500,12 @@ describe('Authorized Component', () => { if (cancelBtns.length > 0) { // Click the first cancel button (modal's cancel) await act(async () => { - fireEvent.click(cancelBtns[0]) + fireEvent.click(cancelBtns[0]!) }) // Modal should be closed await waitFor(() => { - expect(screen.getByText('API Keys')).toBeInTheDocument() + expect(screen.getByText('API Keys'))!.toBeInTheDocument() }) } break @@ -2380,7 +2534,7 @@ describe('Authorized Component', () => { ) await waitFor(() => { - expect(screen.getByText('API Keys')).toBeInTheDocument() + expect(screen.getByText('API Keys'))!.toBeInTheDocument() }) // Open edit modal by clicking edit button @@ -2398,12 +2552,12 @@ describe('Authorized Component', () => { const cancelButtons = screen.queryAllByText('common.operation.cancel') if (cancelButtons.length > 0) { await act(async () => { - fireEvent.click(cancelButtons[0]) + fireEvent.click(cancelButtons[0]!) }) // After onClose, editValues should be null so modal won't render await waitFor(() => { - expect(screen.getByText('API Keys')).toBeInTheDocument() + expect(screen.getByText('API Keys'))!.toBeInTheDocument() }) // Try opening modal again to verify state was properly reset @@ -2413,7 +2567,7 @@ describe('Authorized Component', () => { await waitFor(() => { const newModal = document.querySelector('.fixed') - expect(newModal).toBeInTheDocument() + expect(newModal)!.toBeInTheDocument() }) } break @@ -2451,7 +2605,7 @@ describe('Authorized Component', () => { // Wait for modal await waitFor(() => { - expect(document.querySelector('.fixed')).toBeInTheDocument() + expect(document.querySelector('.fixed'))!.toBeInTheDocument() }) // Close the modal via cancel @@ -2468,7 +2622,7 @@ describe('Authorized Component', () => { // Verify component can render again normally await waitFor(() => { - expect(screen.getByText('API Keys')).toBeInTheDocument() + expect(screen.getByText('API Keys'))!.toBeInTheDocument() }) // Verify we can open the modal again (state was properly reset) @@ -2481,7 +2635,7 @@ describe('Authorized Component', () => { }) await waitFor(() => { - expect(document.querySelector('.fixed')).toBeInTheDocument() + expect(document.querySelector('.fixed'))!.toBeInTheDocument() }) } } @@ -2518,7 +2672,7 @@ describe('Authorized Component', () => { // Confirm dialog should appear with the correct credential id await waitFor(() => { - expect(screen.getByText('datasetDocuments.list.delete.title')).toBeInTheDocument() + expect(screen.getByText('datasetDocuments.list.delete.title'))!.toBeInTheDocument() }) // Now click confirm to verify the correct id is used diff --git a/web/app/components/plugins/plugin-auth/hooks/__tests__/use-credential.spec.ts b/web/app/components/plugins/plugin-auth/hooks/__tests__/use-credential.spec.ts index 7777fbff97..e0e241cbbe 100644 --- a/web/app/components/plugins/plugin-auth/hooks/__tests__/use-credential.spec.ts +++ b/web/app/components/plugins/plugin-auth/hooks/__tests__/use-credential.spec.ts @@ -90,7 +90,7 @@ describe('use-credential hooks', () => { result.current() - const invalidFn = mockUseInvalidPluginCredentialInfo.mock.results[0].value + const invalidFn = mockUseInvalidPluginCredentialInfo.mock.results[0]!.value expect(invalidFn).toHaveBeenCalled() expect(mockInvalidToolsByType).toHaveBeenCalled() }) diff --git a/web/app/components/plugins/plugin-detail-panel/__tests__/agent-strategy-list.spec.tsx b/web/app/components/plugins/plugin-detail-panel/__tests__/agent-strategy-list.spec.tsx index 34015c0487..02a2431ead 100644 --- a/web/app/components/plugins/plugin-detail-panel/__tests__/agent-strategy-list.spec.tsx +++ b/web/app/components/plugins/plugin-detail-panel/__tests__/agent-strategy-list.spec.tsx @@ -81,15 +81,15 @@ describe('AgentStrategyList', () => { it('should render strategy items when data is available', () => { render() - expect(screen.getByText('plugin.detailPanel.strategyNum:{"num":1,"strategy":"strategy"}')).toBeInTheDocument() - expect(screen.getByTestId('strategy-item')).toBeInTheDocument() + expect(screen.getByText('plugin.detailPanel.strategyNum:{"num":1,"strategy":"strategy"}'))!.toBeInTheDocument() + expect(screen.getByTestId('strategy-item'))!.toBeInTheDocument() }) it('should return null when no strategy provider detail', () => { mockStrategyProviderDetail = undefined const { container } = render() - expect(container).toBeEmptyDOMElement() + expect(container)!.toBeEmptyDOMElement() }) it('should render multiple strategies', () => { @@ -98,13 +98,13 @@ describe('AgentStrategyList', () => { identity: { author: 'test', name: 'test' }, strategies: [ ...mockStrategies, - { ...mockStrategies[0], identity: { ...mockStrategies[0].identity, name: 'strategy-2' } }, + { ...mockStrategies[0]!, identity: { ...mockStrategies[0]!.identity, name: 'strategy-2' } }, ], }, } render() - expect(screen.getByText('plugin.detailPanel.strategyNum:{"num":2,"strategy":"strategies"}')).toBeInTheDocument() + expect(screen.getByText('plugin.detailPanel.strategyNum:{"num":2,"strategy":"strategies"}'))!.toBeInTheDocument() expect(screen.getAllByTestId('strategy-item')).toHaveLength(2) }) }) @@ -115,7 +115,7 @@ describe('AgentStrategyList', () => { detail.tenant_id = 'custom-tenant' render() - expect(screen.getByTestId('strategy-item')).toBeInTheDocument() + expect(screen.getByTestId('strategy-item'))!.toBeInTheDocument() }) }) }) diff --git a/web/app/components/plugins/plugin-detail-panel/__tests__/detail-header.spec.tsx b/web/app/components/plugins/plugin-detail-panel/__tests__/detail-header.spec.tsx index 74ce8525a9..0eacbf3bd3 100644 --- a/web/app/components/plugins/plugin-detail-panel/__tests__/detail-header.spec.tsx +++ b/web/app/components/plugins/plugin-detail-panel/__tests__/detail-header.spec.tsx @@ -297,13 +297,13 @@ describe('DetailHeader', () => { it('should render plugin title', () => { render() - expect(screen.getByTestId('title')).toBeInTheDocument() + expect(screen.getByTestId('title'))!.toBeInTheDocument() }) it('should render plugin icon with correct src', () => { render() - expect(screen.getByTestId('card-icon')).toBeInTheDocument() + expect(screen.getByTestId('card-icon'))!.toBeInTheDocument() }) it('should render icon with http url directly', () => { @@ -315,13 +315,13 @@ describe('DetailHeader', () => { }) render() - expect(screen.getByTestId('card-icon')).toHaveAttribute('data-src', 'https://example.com/icon.png') + expect(screen.getByTestId('card-icon'))!.toHaveAttribute('data-src', 'https://example.com/icon.png') }) it('should render description when not in readme view', () => { render() - expect(screen.getByTestId('description')).toBeInTheDocument() + expect(screen.getByTestId('description'))!.toBeInTheDocument() }) it('should not render description in readme view', () => { @@ -333,7 +333,7 @@ describe('DetailHeader', () => { it('should render verified badge when verified', () => { render() - expect(screen.getByTestId('verified-badge')).toBeInTheDocument() + expect(screen.getByTestId('verified-badge'))!.toBeInTheDocument() }) }) @@ -346,7 +346,8 @@ describe('DetailHeader', () => { render() // Badge component should render with the version - expect(screen.getByText('1.0.0')).toBeInTheDocument() + // Badge component should render with the version + expect(screen.getByText('1.0.0'))!.toBeInTheDocument() }) it('should not show new version indicator when versions match', () => { @@ -357,7 +358,8 @@ describe('DetailHeader', () => { render() // Badge component should render with the version - expect(screen.getByText('1.0.0')).toBeInTheDocument() + // Badge component should render with the version + expect(screen.getByText('1.0.0'))!.toBeInTheDocument() }) it('should show update button when new version is available', () => { @@ -367,7 +369,7 @@ describe('DetailHeader', () => { }) render() - expect(screen.getByText('plugin.detailPanel.operation.update')).toBeInTheDocument() + expect(screen.getByText('plugin.detailPanel.operation.update'))!.toBeInTheDocument() }) it('should show update button for GitHub source', () => { @@ -377,7 +379,7 @@ describe('DetailHeader', () => { }) render() - expect(screen.getByText('plugin.detailPanel.operation.update')).toBeInTheDocument() + expect(screen.getByText('plugin.detailPanel.operation.update'))!.toBeInTheDocument() }) }) @@ -393,7 +395,7 @@ describe('DetailHeader', () => { render() - expect(screen.getByTestId('title')).toBeInTheDocument() + expect(screen.getByTestId('title'))!.toBeInTheDocument() }) it('should render component when strategy is disabled', () => { @@ -407,7 +409,7 @@ describe('DetailHeader', () => { render() - expect(screen.getByTestId('title')).toBeInTheDocument() + expect(screen.getByTestId('title'))!.toBeInTheDocument() }) it('should enable auto upgrade for update_all mode', () => { @@ -422,7 +424,8 @@ describe('DetailHeader', () => { render() // Auto upgrade badge should be rendered - expect(screen.getByTestId('title')).toBeInTheDocument() + // Auto upgrade badge should be rendered + expect(screen.getByTestId('title'))!.toBeInTheDocument() }) it('should enable auto upgrade for partial mode when plugin is included', () => { @@ -436,7 +439,7 @@ describe('DetailHeader', () => { render() - expect(screen.getByTestId('title')).toBeInTheDocument() + expect(screen.getByTestId('title'))!.toBeInTheDocument() }) it('should not enable auto upgrade for partial mode when plugin is not included', () => { @@ -450,7 +453,7 @@ describe('DetailHeader', () => { render() - expect(screen.getByTestId('title')).toBeInTheDocument() + expect(screen.getByTestId('title'))!.toBeInTheDocument() }) it('should enable auto upgrade for exclude mode when plugin is not excluded', () => { @@ -464,7 +467,7 @@ describe('DetailHeader', () => { render() - expect(screen.getByTestId('title')).toBeInTheDocument() + expect(screen.getByTestId('title'))!.toBeInTheDocument() }) it('should not enable auto upgrade for exclude mode when plugin is excluded', () => { @@ -478,7 +481,7 @@ describe('DetailHeader', () => { render() - expect(screen.getByTestId('title')).toBeInTheDocument() + expect(screen.getByTestId('title'))!.toBeInTheDocument() }) it('should not enable auto upgrade for non-marketplace plugins', () => { @@ -496,7 +499,7 @@ describe('DetailHeader', () => { }) render() - expect(screen.getByTestId('title')).toBeInTheDocument() + expect(screen.getByTestId('title'))!.toBeInTheDocument() }) it('should not enable auto upgrade when marketplace feature is disabled', () => { @@ -512,7 +515,8 @@ describe('DetailHeader', () => { render() // Component should still render but auto upgrade should be disabled - expect(screen.getByTestId('title')).toBeInTheDocument() + // Component should still render but auto upgrade should be disabled + expect(screen.getByTestId('title'))!.toBeInTheDocument() }) }) @@ -522,7 +526,7 @@ describe('DetailHeader', () => { // Find the close button (ActionButton with action-btn class) const actionButtons = screen.getAllByRole('button').filter(btn => btn.classList.contains('action-btn')) - fireEvent.click(actionButtons[actionButtons.length - 1]) + fireEvent.click(actionButtons[actionButtons.length - 1]!) expect(mockOnHide).toHaveBeenCalled() }) @@ -533,7 +537,7 @@ describe('DetailHeader', () => { const infoBtn = screen.getByTestId('info-btn') fireEvent.click(infoBtn) - expect(infoBtn).toBeInTheDocument() + expect(infoBtn)!.toBeInTheDocument() }) it('should have check version button available', () => { @@ -542,7 +546,7 @@ describe('DetailHeader', () => { const checkBtn = screen.getByTestId('check-version-btn') fireEvent.click(checkBtn) - expect(checkBtn).toBeInTheDocument() + expect(checkBtn)!.toBeInTheDocument() }) }) @@ -557,7 +561,7 @@ describe('DetailHeader', () => { const updateBtn = screen.getByText('plugin.detailPanel.operation.update') fireEvent.click(updateBtn) - expect(updateBtn).toBeInTheDocument() + expect(updateBtn)!.toBeInTheDocument() }) it('should have version picker select button', () => { @@ -566,7 +570,7 @@ describe('DetailHeader', () => { const selectBtn = screen.getByTestId('select-version-btn') fireEvent.click(selectBtn) - expect(selectBtn).toBeInTheDocument() + expect(selectBtn)!.toBeInTheDocument() }) it('should have downgrade button', () => { @@ -575,7 +579,7 @@ describe('DetailHeader', () => { const downgradeBtn = screen.getByTestId('select-downgrade-btn') fireEvent.click(downgradeBtn) - expect(downgradeBtn).toBeInTheDocument() + expect(downgradeBtn)!.toBeInTheDocument() }) }) @@ -651,7 +655,7 @@ describe('DetailHeader', () => { const removeBtn = screen.getByTestId('remove-btn') fireEvent.click(removeBtn) - expect(removeBtn).toBeInTheDocument() + expect(removeBtn)!.toBeInTheDocument() }) it('should have uninstallPlugin mock defined', () => { @@ -671,13 +675,13 @@ describe('DetailHeader', () => { }) render() - expect(screen.getByTestId('remove-btn')).toBeInTheDocument() + expect(screen.getByTestId('remove-btn'))!.toBeInTheDocument() }) it('should render correctly for tool plugin delete', () => { render() - expect(screen.getByTestId('remove-btn')).toBeInTheDocument() + expect(screen.getByTestId('remove-btn'))!.toBeInTheDocument() }) }) @@ -689,21 +693,21 @@ describe('DetailHeader', () => { }) render() - expect(screen.getByTestId('title')).toBeInTheDocument() + expect(screen.getByTestId('title'))!.toBeInTheDocument() }) it('should render local source icon', () => { const detail = createPluginDetail({ source: PluginSource.local }) render() - expect(screen.getByTestId('title')).toBeInTheDocument() + expect(screen.getByTestId('title'))!.toBeInTheDocument() }) it('should render debugging source icon', () => { const detail = createPluginDetail({ source: PluginSource.debugging }) render() - expect(screen.getByTestId('title')).toBeInTheDocument() + expect(screen.getByTestId('title'))!.toBeInTheDocument() }) it('should not render deprecation notice for non-marketplace source', () => { @@ -722,20 +726,20 @@ describe('DetailHeader', () => { }) render() - expect(screen.getByTestId('operation-dropdown')).toBeInTheDocument() + expect(screen.getByTestId('operation-dropdown'))!.toBeInTheDocument() }) it('should render marketplace source correctly', () => { render() - expect(screen.getByTestId('operation-dropdown')).toBeInTheDocument() + expect(screen.getByTestId('operation-dropdown'))!.toBeInTheDocument() }) it('should render local source correctly', () => { const detail = createPluginDetail({ source: PluginSource.local }) render() - expect(screen.getByTestId('operation-dropdown')).toBeInTheDocument() + expect(screen.getByTestId('operation-dropdown'))!.toBeInTheDocument() }) }) @@ -743,7 +747,7 @@ describe('DetailHeader', () => { it('should render plugin auth for tool category', () => { render() - expect(screen.getByTestId('plugin-auth')).toBeInTheDocument() + expect(screen.getByTestId('plugin-auth'))!.toBeInTheDocument() }) it('should not render plugin auth for non-tool category', () => { @@ -770,7 +774,7 @@ describe('DetailHeader', () => { const detail = createPluginDetail({ version: '' }) render() - expect(screen.getByTestId('title')).toBeInTheDocument() + expect(screen.getByTestId('title'))!.toBeInTheDocument() }) it('should handle plugin with name containing slash', () => { @@ -782,7 +786,7 @@ describe('DetailHeader', () => { }) render() - expect(screen.getByTestId('org-info')).toBeInTheDocument() + expect(screen.getByTestId('org-info'))!.toBeInTheDocument() }) it('should handle empty icon', () => { @@ -794,7 +798,7 @@ describe('DetailHeader', () => { }) render() - expect(screen.getByTestId('card-icon')).toHaveAttribute('data-src', '') + expect(screen.getByTestId('card-icon'))!.toHaveAttribute('data-src', '') }) }) @@ -805,7 +809,7 @@ describe('DetailHeader', () => { fireEvent.click(screen.getByTestId('remove-btn')) await waitFor(() => { - expect(screen.getByRole('alertdialog')).toBeInTheDocument() + expect(screen.getByRole('alertdialog'))!.toBeInTheDocument() }) }) @@ -814,7 +818,7 @@ describe('DetailHeader', () => { fireEvent.click(screen.getByTestId('remove-btn')) await waitFor(() => { - expect(screen.getByRole('alertdialog')).toBeInTheDocument() + expect(screen.getByRole('alertdialog'))!.toBeInTheDocument() }) fireEvent.click(screen.getByRole('button', { name: 'common.operation.cancel' })) @@ -829,7 +833,7 @@ describe('DetailHeader', () => { fireEvent.click(screen.getByTestId('remove-btn')) await waitFor(() => { - expect(screen.getByRole('alertdialog')).toBeInTheDocument() + expect(screen.getByRole('alertdialog'))!.toBeInTheDocument() }) fireEvent.click(screen.getByRole('button', { name: 'common.operation.confirm' })) @@ -844,7 +848,7 @@ describe('DetailHeader', () => { fireEvent.click(screen.getByTestId('remove-btn')) await waitFor(() => { - expect(screen.getByRole('alertdialog')).toBeInTheDocument() + expect(screen.getByRole('alertdialog'))!.toBeInTheDocument() }) fireEvent.click(screen.getByRole('button', { name: 'common.operation.confirm' })) @@ -865,7 +869,7 @@ describe('DetailHeader', () => { fireEvent.click(screen.getByTestId('remove-btn')) await waitFor(() => { - expect(screen.getByRole('alertdialog')).toBeInTheDocument() + expect(screen.getByRole('alertdialog'))!.toBeInTheDocument() }) fireEvent.click(screen.getByRole('button', { name: 'common.operation.confirm' })) @@ -880,7 +884,7 @@ describe('DetailHeader', () => { fireEvent.click(screen.getByTestId('remove-btn')) await waitFor(() => { - expect(screen.getByRole('alertdialog')).toBeInTheDocument() + expect(screen.getByRole('alertdialog'))!.toBeInTheDocument() }) fireEvent.click(screen.getByRole('button', { name: 'common.operation.confirm' })) @@ -895,7 +899,7 @@ describe('DetailHeader', () => { fireEvent.click(screen.getByTestId('remove-btn')) await waitFor(() => { - expect(screen.getByRole('alertdialog')).toBeInTheDocument() + expect(screen.getByRole('alertdialog'))!.toBeInTheDocument() }) fireEvent.click(screen.getByRole('button', { name: 'common.operation.confirm' })) @@ -917,7 +921,7 @@ describe('DetailHeader', () => { fireEvent.click(screen.getByText('plugin.detailPanel.operation.update')) await waitFor(() => { - expect(screen.getByTestId('update-modal')).toBeInTheDocument() + expect(screen.getByTestId('update-modal'))!.toBeInTheDocument() }) }) @@ -930,7 +934,7 @@ describe('DetailHeader', () => { fireEvent.click(screen.getByText('plugin.detailPanel.operation.update')) await waitFor(() => { - expect(screen.getByTestId('update-modal')).toBeInTheDocument() + expect(screen.getByTestId('update-modal'))!.toBeInTheDocument() }) fireEvent.click(screen.getByTestId('update-modal-save')) @@ -949,7 +953,7 @@ describe('DetailHeader', () => { fireEvent.click(screen.getByText('plugin.detailPanel.operation.update')) await waitFor(() => { - expect(screen.getByTestId('update-modal')).toBeInTheDocument() + expect(screen.getByTestId('update-modal'))!.toBeInTheDocument() }) fireEvent.click(screen.getByTestId('update-modal-cancel')) @@ -967,7 +971,7 @@ describe('DetailHeader', () => { fireEvent.click(screen.getByTestId('info-btn')) await waitFor(() => { - expect(screen.getByTestId('plugin-info')).toBeInTheDocument() + expect(screen.getByTestId('plugin-info'))!.toBeInTheDocument() }) }) @@ -976,7 +980,7 @@ describe('DetailHeader', () => { fireEvent.click(screen.getByTestId('info-btn')) await waitFor(() => { - expect(screen.getByTestId('plugin-info')).toBeInTheDocument() + expect(screen.getByTestId('plugin-info'))!.toBeInTheDocument() }) fireEvent.click(screen.getByTestId('plugin-info-close')) @@ -993,7 +997,7 @@ describe('DetailHeader', () => { }) render() - expect(screen.getByTestId('info-btn')).toBeInTheDocument() + expect(screen.getByTestId('info-btn'))!.toBeInTheDocument() }) }) }) diff --git a/web/app/components/plugins/plugin-detail-panel/__tests__/endpoint-card.spec.tsx b/web/app/components/plugins/plugin-detail-panel/__tests__/endpoint-card.spec.tsx index 3b87fdccb6..15c231cb17 100644 --- a/web/app/components/plugins/plugin-detail-panel/__tests__/endpoint-card.spec.tsx +++ b/web/app/components/plugins/plugin-detail-panel/__tests__/endpoint-card.spec.tsx @@ -161,30 +161,30 @@ describe('EndpointCard', () => { it('should render endpoint name', () => { render() - expect(screen.getByText('Test Endpoint')).toBeInTheDocument() + expect(screen.getByText('Test Endpoint'))!.toBeInTheDocument() }) it('should render visible endpoints only', () => { render() - expect(screen.getByText('GET')).toBeInTheDocument() - expect(screen.getByText('https://api.example.com/api/test')).toBeInTheDocument() + expect(screen.getByText('GET'))!.toBeInTheDocument() + expect(screen.getByText('https://api.example.com/api/test'))!.toBeInTheDocument() expect(screen.queryByText('POST')).not.toBeInTheDocument() }) it('should show active status when enabled', () => { render() - expect(screen.getByText('plugin.detailPanel.serviceOk')).toBeInTheDocument() - expect(screen.getByTestId('indicator')).toHaveAttribute('data-color', 'green') + expect(screen.getByText('plugin.detailPanel.serviceOk'))!.toBeInTheDocument() + expect(screen.getByTestId('indicator'))!.toHaveAttribute('data-color', 'green') }) it('should show disabled status when not enabled', () => { const disabledData = { ...mockEndpointData, enabled: false } render() - expect(screen.getByText('plugin.detailPanel.disabled')).toBeInTheDocument() - expect(screen.getByTestId('indicator')).toHaveAttribute('data-color', 'gray') + expect(screen.getByText('plugin.detailPanel.disabled'))!.toBeInTheDocument() + expect(screen.getByTestId('indicator'))!.toHaveAttribute('data-color', 'gray') }) }) @@ -194,7 +194,7 @@ describe('EndpointCard', () => { fireEvent.click(screen.getByRole('switch')) - expect(screen.getByText('plugin.detailPanel.endpointDisableTip')).toBeInTheDocument() + expect(screen.getByText('plugin.detailPanel.endpointDisableTip'))!.toBeInTheDocument() }) it('should call disableEndpoint when confirm disable', () => { @@ -211,16 +211,16 @@ describe('EndpointCard', () => { render() const allButtons = screen.getAllByRole('button') - fireEvent.click(allButtons[1]) + fireEvent.click(allButtons[1]!) - expect(screen.getByText('plugin.detailPanel.endpointDeleteTip')).toBeInTheDocument() + expect(screen.getByText('plugin.detailPanel.endpointDeleteTip'))!.toBeInTheDocument() }) it('should call deleteEndpoint when confirm delete', () => { render() const allButtons = screen.getAllByRole('button') - fireEvent.click(allButtons[1]) + fireEvent.click(allButtons[1]!) fireEvent.click(screen.getByRole('button', { name: 'common.operation.confirm' })) expect(mockDeleteEndpoint).toHaveBeenCalledWith('ep-1') @@ -230,16 +230,16 @@ describe('EndpointCard', () => { render() const allButtons = screen.getAllByRole('button') - fireEvent.click(allButtons[0]) + fireEvent.click(allButtons[0]!) - expect(screen.getByTestId('endpoint-modal')).toBeInTheDocument() + expect(screen.getByTestId('endpoint-modal'))!.toBeInTheDocument() }) it('should call updateEndpoint when save in modal', () => { render() const allButtons = screen.getAllByRole('button') - fireEvent.click(allButtons[0]) + fireEvent.click(allButtons[0]!) fireEvent.click(screen.getByTestId('modal-save')) expect(mockUpdateEndpoint).toHaveBeenCalled() @@ -252,13 +252,13 @@ describe('EndpointCard', () => { render() const allButtons = screen.getAllByRole('button') - fireEvent.click(allButtons[2]) + fireEvent.click(allButtons[2]!) act(() => { vi.advanceTimersByTime(2000) }) - expect(screen.getByText('Test Endpoint')).toBeInTheDocument() + expect(screen.getByText('Test Endpoint'))!.toBeInTheDocument() }) }) @@ -270,7 +270,7 @@ describe('EndpointCard', () => { } render() - expect(screen.getByText('Test Endpoint')).toBeInTheDocument() + expect(screen.getByText('Test Endpoint'))!.toBeInTheDocument() }) it('should call handleChange after enable', () => { @@ -286,20 +286,20 @@ describe('EndpointCard', () => { render() fireEvent.click(screen.getByRole('switch')) - expect(screen.getByText('plugin.detailPanel.endpointDisableTip')).toBeInTheDocument() + expect(screen.getByText('plugin.detailPanel.endpointDisableTip'))!.toBeInTheDocument() fireEvent.click(screen.getByRole('button', { name: 'common.operation.cancel' })) await waitForAlertDialogToClose() - expect(screen.getByRole('switch')).toHaveAttribute('aria-checked', 'true') + expect(screen.getByRole('switch'))!.toHaveAttribute('aria-checked', 'true') }) it('should hide delete confirm when cancel clicked', async () => { render() const allButtons = screen.getAllByRole('button') - fireEvent.click(allButtons[1]) - expect(screen.getByText('plugin.detailPanel.endpointDeleteTip')).toBeInTheDocument() + fireEvent.click(allButtons[1]!) + expect(screen.getByText('plugin.detailPanel.endpointDeleteTip'))!.toBeInTheDocument() fireEvent.click(screen.getByRole('button', { name: 'common.operation.cancel' })) await waitForAlertDialogToClose() @@ -309,8 +309,8 @@ describe('EndpointCard', () => { render() const allButtons = screen.getAllByRole('button') - fireEvent.click(allButtons[0]) - expect(screen.getByTestId('endpoint-modal')).toBeInTheDocument() + fireEvent.click(allButtons[0]!) + expect(screen.getByTestId('endpoint-modal'))!.toBeInTheDocument() fireEvent.click(screen.getByTestId('modal-cancel')) @@ -344,7 +344,7 @@ describe('EndpointCard', () => { render() const allButtons = screen.getAllByRole('button') - fireEvent.click(allButtons[1]) + fireEvent.click(allButtons[1]!) fireEvent.click(screen.getByRole('button', { name: 'common.operation.confirm' })) expect(mockDeleteEndpoint).toHaveBeenCalled() @@ -354,9 +354,9 @@ describe('EndpointCard', () => { render() const allButtons = screen.getAllByRole('button') - fireEvent.click(allButtons[0]) + fireEvent.click(allButtons[0]!) - expect(screen.getByTestId('endpoint-modal')).toBeInTheDocument() + expect(screen.getByTestId('endpoint-modal'))!.toBeInTheDocument() failureFlags.update = true fireEvent.click(screen.getByTestId('modal-save')) diff --git a/web/app/components/plugins/plugin-detail-panel/__tests__/endpoint-list.spec.tsx b/web/app/components/plugins/plugin-detail-panel/__tests__/endpoint-list.spec.tsx index 50d63742ac..e31afc7cb2 100644 --- a/web/app/components/plugins/plugin-detail-panel/__tests__/endpoint-list.spec.tsx +++ b/web/app/components/plugins/plugin-detail-panel/__tests__/endpoint-list.spec.tsx @@ -85,28 +85,28 @@ describe('EndpointList', () => { it('should render endpoint list', () => { render() - expect(screen.getByText('plugin.detailPanel.endpoints')).toBeInTheDocument() + expect(screen.getByText('plugin.detailPanel.endpoints'))!.toBeInTheDocument() }) it('should render endpoint cards', () => { render() - expect(screen.getByTestId('endpoint-card')).toBeInTheDocument() - expect(screen.getByText('Endpoint 1')).toBeInTheDocument() + expect(screen.getByTestId('endpoint-card'))!.toBeInTheDocument() + expect(screen.getByText('Endpoint 1'))!.toBeInTheDocument() }) it('should return null when no data', () => { mockEndpointListData = undefined const { container } = render() - expect(container).toBeEmptyDOMElement() + expect(container)!.toBeEmptyDOMElement() }) it('should show empty message when no endpoints', () => { mockEndpointListData = { endpoints: [] } render() - expect(screen.getByText('plugin.detailPanel.endpointsEmpty')).toBeInTheDocument() + expect(screen.getByText('plugin.detailPanel.endpointsEmpty'))!.toBeInTheDocument() }) it('should render add button', () => { @@ -121,17 +121,17 @@ describe('EndpointList', () => { render() const addButton = screen.getAllByRole('button')[0] - fireEvent.click(addButton) + fireEvent.click(addButton!) - expect(screen.getByTestId('endpoint-modal')).toBeInTheDocument() + expect(screen.getByTestId('endpoint-modal'))!.toBeInTheDocument() }) it('should hide modal when cancel clicked', () => { render() const addButton = screen.getAllByRole('button')[0] - fireEvent.click(addButton) - expect(screen.getByTestId('endpoint-modal')).toBeInTheDocument() + fireEvent.click(addButton!) + expect(screen.getByTestId('endpoint-modal'))!.toBeInTheDocument() fireEvent.click(screen.getByTestId('modal-cancel')) expect(screen.queryByTestId('endpoint-modal')).not.toBeInTheDocument() @@ -141,7 +141,7 @@ describe('EndpointList', () => { render() const addButton = screen.getAllByRole('button')[0] - fireEvent.click(addButton) + fireEvent.click(addButton!) fireEvent.click(screen.getByTestId('modal-save')) expect(mockCreateEndpoint).toHaveBeenCalled() @@ -154,7 +154,7 @@ describe('EndpointList', () => { detail.declaration.tool = {} as PluginDetail['declaration']['tool'] render() - expect(screen.getByText('plugin.detailPanel.endpoints')).toBeInTheDocument() + expect(screen.getByText('plugin.detailPanel.endpoints'))!.toBeInTheDocument() }) }) @@ -177,7 +177,7 @@ describe('EndpointList', () => { render() const addButton = screen.getAllByRole('button')[0] - fireEvent.click(addButton) + fireEvent.click(addButton!) fireEvent.click(screen.getByTestId('modal-save')) expect(mockInvalidateEndpointList).toHaveBeenCalledWith('test-plugin') @@ -187,7 +187,7 @@ describe('EndpointList', () => { render() const addButton = screen.getAllByRole('button')[0] - fireEvent.click(addButton) + fireEvent.click(addButton!) fireEvent.click(screen.getByTestId('modal-save')) expect(mockCreateEndpoint).toHaveBeenCalledWith({ diff --git a/web/app/components/plugins/plugin-detail-panel/__tests__/endpoint-modal.spec.tsx b/web/app/components/plugins/plugin-detail-panel/__tests__/endpoint-modal.spec.tsx index a467de7142..0be17f07d4 100644 --- a/web/app/components/plugins/plugin-detail-panel/__tests__/endpoint-modal.spec.tsx +++ b/web/app/components/plugins/plugin-detail-panel/__tests__/endpoint-modal.spec.tsx @@ -101,7 +101,7 @@ describe('EndpointModal', () => { />, ) - expect(screen.getByRole('dialog')).toBeInTheDocument() + expect(screen.getByRole('dialog'))!.toBeInTheDocument() }) it('should render title and description', () => { @@ -114,8 +114,8 @@ describe('EndpointModal', () => { />, ) - expect(screen.getByText('plugin.detailPanel.endpointModalTitle')).toBeInTheDocument() - expect(screen.getByText('plugin.detailPanel.endpointModalDesc')).toBeInTheDocument() + expect(screen.getByText('plugin.detailPanel.endpointModalTitle'))!.toBeInTheDocument() + expect(screen.getByText('plugin.detailPanel.endpointModalDesc'))!.toBeInTheDocument() }) it('should render form with fieldMoreInfo url link', () => { @@ -128,8 +128,8 @@ describe('EndpointModal', () => { />, ) - expect(screen.getByTestId('field-more-info')).toBeInTheDocument() - expect(screen.getByText('tools.howToGet')).toBeInTheDocument() + expect(screen.getByTestId('field-more-info'))!.toBeInTheDocument() + expect(screen.getByText('tools.howToGet'))!.toBeInTheDocument() }) it('should render readme entrance', () => { @@ -142,7 +142,7 @@ describe('EndpointModal', () => { />, ) - expect(screen.getByTestId('readme-entrance')).toBeInTheDocument() + expect(screen.getByTestId('readme-entrance'))!.toBeInTheDocument() }) }) @@ -173,7 +173,7 @@ describe('EndpointModal', () => { ) const allButtons = screen.getAllByRole('button') - fireEvent.click(allButtons[0]) + fireEvent.click(allButtons[0]!) expect(mockOnCancel).toHaveBeenCalledTimes(1) }) @@ -191,7 +191,7 @@ describe('EndpointModal', () => { const input = screen.getByTestId('form-input') fireEvent.change(input, { target: { value: 'Test Name' } }) - expect(input).toHaveValue('Test Name') + expect(input)!.toHaveValue('Test Name') }) }) @@ -207,7 +207,7 @@ describe('EndpointModal', () => { />, ) - expect(screen.getByTestId('form-input')).toHaveValue('Default Name') + expect(screen.getByTestId('form-input'))!.toHaveValue('Default Name') }) it('should extract default values from schemas when no defaultValues', () => { @@ -224,7 +224,7 @@ describe('EndpointModal', () => { />, ) - expect(screen.getByTestId('form-input')).toHaveValue('Schema Default') + expect(screen.getByTestId('form-input'))!.toHaveValue('Schema Default') }) it('should handle schemas without default values', () => { @@ -241,7 +241,7 @@ describe('EndpointModal', () => { />, ) - expect(screen.getByTestId('form')).toBeInTheDocument() + expect(screen.getByTestId('form'))!.toBeInTheDocument() }) }) diff --git a/web/app/components/plugins/plugin-detail-panel/__tests__/strategy-detail.spec.tsx b/web/app/components/plugins/plugin-detail-panel/__tests__/strategy-detail.spec.tsx index 350bf6956b..b5fbc03b45 100644 --- a/web/app/components/plugins/plugin-detail-panel/__tests__/strategy-detail.spec.tsx +++ b/web/app/components/plugins/plugin-detail-panel/__tests__/strategy-detail.spec.tsx @@ -69,40 +69,40 @@ describe('StrategyDetail', () => { it('should render drawer', () => { render() - expect(screen.getByRole('dialog')).toBeInTheDocument() + expect(screen.getByRole('dialog'))!.toBeInTheDocument() }) it('should render provider label', () => { render() - expect(screen.getByText('Test Provider')).toBeInTheDocument() + expect(screen.getByText('Test Provider'))!.toBeInTheDocument() }) it('should render strategy label', () => { render() - expect(screen.getByText('Strategy Label')).toBeInTheDocument() + expect(screen.getByText('Strategy Label'))!.toBeInTheDocument() }) it('should render parameters section', () => { render() - expect(screen.getByText('tools.setBuiltInTools.parameters')).toBeInTheDocument() - expect(screen.getByText('Parameter 1')).toBeInTheDocument() + expect(screen.getByText('tools.setBuiltInTools.parameters'))!.toBeInTheDocument() + expect(screen.getByText('Parameter 1'))!.toBeInTheDocument() }) it('should render output schema section', () => { render() - expect(screen.getByText('OUTPUT')).toBeInTheDocument() - expect(screen.getByText('result')).toBeInTheDocument() - expect(screen.getByText('String')).toBeInTheDocument() + expect(screen.getByText('OUTPUT'))!.toBeInTheDocument() + expect(screen.getByText('result'))!.toBeInTheDocument() + expect(screen.getByText('String'))!.toBeInTheDocument() }) it('should render BACK button', () => { render() - expect(screen.getByText('BACK')).toBeInTheDocument() + expect(screen.getByText('BACK'))!.toBeInTheDocument() }) }) @@ -131,51 +131,51 @@ describe('StrategyDetail', () => { it('should display correct type for number-input', () => { const detailWithNumber = { ...mockDetail, - parameters: [{ ...mockDetail.parameters[0], type: 'number-input' }], + parameters: [{ ...mockDetail.parameters[0]!, type: 'number-input' }], } render() - expect(screen.getByText('tools.setBuiltInTools.number')).toBeInTheDocument() + expect(screen.getByText('tools.setBuiltInTools.number'))!.toBeInTheDocument() }) it('should display correct type for checkbox', () => { const detailWithCheckbox = { ...mockDetail, - parameters: [{ ...mockDetail.parameters[0], type: 'checkbox' }], + parameters: [{ ...mockDetail.parameters[0]!, type: 'checkbox' }], } render() - expect(screen.getByText('boolean')).toBeInTheDocument() + expect(screen.getByText('boolean'))!.toBeInTheDocument() }) it('should display correct type for file', () => { const detailWithFile = { ...mockDetail, - parameters: [{ ...mockDetail.parameters[0], type: 'file' }], + parameters: [{ ...mockDetail.parameters[0]!, type: 'file' }], } render() - expect(screen.getByText('tools.setBuiltInTools.file')).toBeInTheDocument() + expect(screen.getByText('tools.setBuiltInTools.file'))!.toBeInTheDocument() }) it('should display correct type for array[tools]', () => { const detailWithArrayTools = { ...mockDetail, - parameters: [{ ...mockDetail.parameters[0], type: 'array[tools]' }], + parameters: [{ ...mockDetail.parameters[0]!, type: 'array[tools]' }], } render() - expect(screen.getByText('multiple-tool-select')).toBeInTheDocument() + expect(screen.getByText('multiple-tool-select'))!.toBeInTheDocument() }) it('should display original type for unknown types', () => { const detailWithUnknown = { ...mockDetail, - parameters: [{ ...mockDetail.parameters[0], type: 'custom-type' }], + parameters: [{ ...mockDetail.parameters[0]!, type: 'custom-type' }], } render() - expect(screen.getByText('custom-type')).toBeInTheDocument() + expect(screen.getByText('custom-type'))!.toBeInTheDocument() }) }) @@ -184,7 +184,7 @@ describe('StrategyDetail', () => { const detailEmpty = { ...mockDetail, parameters: [] } render() - expect(screen.getByText('tools.setBuiltInTools.parameters')).toBeInTheDocument() + expect(screen.getByText('tools.setBuiltInTools.parameters'))!.toBeInTheDocument() }) it('should handle no output schema', () => { diff --git a/web/app/components/plugins/plugin-detail-panel/app-selector/__tests__/index.spec.tsx b/web/app/components/plugins/plugin-detail-panel/app-selector/__tests__/index.spec.tsx index 4dd604a03e..f7dd1921e4 100644 --- a/web/app/components/plugins/plugin-detail-panel/app-selector/__tests__/index.spec.tsx +++ b/web/app/components/plugins/plugin-detail-panel/app-selector/__tests__/index.spec.tsx @@ -393,19 +393,20 @@ describe('AppTrigger', () => { it('should render placeholder when no app is selected', () => { render() // i18n mock returns key with namespace in dot format - expect(screen.getByText('app.appSelector.placeholder')).toBeInTheDocument() + // i18n mock returns key with namespace in dot format + expect(screen.getByText('app.appSelector.placeholder'))!.toBeInTheDocument() }) it('should render app details when app is selected', () => { const app = createMockApp({ name: 'My Test App' }) render() - expect(screen.getByText('My Test App')).toBeInTheDocument() + expect(screen.getByText('My Test App'))!.toBeInTheDocument() }) it('should apply open state styling', () => { const { container } = render() const trigger = container.querySelector('.bg-state-base-hover-alt') - expect(trigger).toBeInTheDocument() + expect(trigger)!.toBeInTheDocument() }) it('should render AppIcon when app is provided', () => { @@ -413,21 +414,21 @@ describe('AppTrigger', () => { const { container } = render() // AppIcon renders with a specific class when app is provided const iconContainer = container.querySelector('.mr-2') - expect(iconContainer).toBeInTheDocument() + expect(iconContainer)!.toBeInTheDocument() }) }) describe('Props', () => { it('should handle undefined appDetail gracefully', () => { render() - expect(screen.getByText('app.appSelector.placeholder')).toBeInTheDocument() + expect(screen.getByText('app.appSelector.placeholder'))!.toBeInTheDocument() }) it('should display app name with title attribute', () => { const app = createMockApp({ name: 'Long App Name For Testing' }) render() const nameElement = screen.getByTitle('Long App Name For Testing') - expect(nameElement).toBeInTheDocument() + expect(nameElement)!.toBeInTheDocument() }) }) @@ -435,14 +436,14 @@ describe('AppTrigger', () => { it('should have correct base classes', () => { const { container } = render() const trigger = container.firstChild as HTMLElement - expect(trigger).toHaveClass('group', 'flex', 'cursor-pointer') + expect(trigger)!.toHaveClass('group', 'flex', 'cursor-pointer') }) it('should apply different padding when app is provided', () => { const app = createMockApp() const { container } = render() const trigger = container.firstChild as HTMLElement - expect(trigger).toHaveClass('py-1.5', 'pl-1.5') + expect(trigger)!.toHaveClass('py-1.5', 'pl-1.5') }) }) }) @@ -479,18 +480,18 @@ describe('AppPicker', () => { describe('Rendering', () => { it('should render trigger element', () => { render() - expect(screen.getByText('Select App')).toBeInTheDocument() + expect(screen.getByText('Select App'))!.toBeInTheDocument() }) it('should render app list when open', () => { render() - expect(screen.getByText('App 1')).toBeInTheDocument() - expect(screen.getByText('App 2')).toBeInTheDocument() + expect(screen.getByText('App 1'))!.toBeInTheDocument() + expect(screen.getByText('App 2'))!.toBeInTheDocument() }) it('should show loading indicator when isLoading is true', () => { render() - expect(screen.getByText('common.loading')).toBeInTheDocument() + expect(screen.getByText('common.loading'))!.toBeInTheDocument() }) it('should not render content when isShow is false', () => { @@ -538,31 +539,31 @@ describe('AppPicker', () => { it('should display correct app type for CHAT', () => { const apps = [createMockApp({ id: 'chat-app', name: 'Chat App', mode: AppModeEnum.CHAT })] render() - expect(screen.getByText('chat')).toBeInTheDocument() + expect(screen.getByText('chat'))!.toBeInTheDocument() }) it('should display correct app type for WORKFLOW', () => { const apps = [createMockApp({ id: 'workflow-app', name: 'Workflow App', mode: AppModeEnum.WORKFLOW })] render() - expect(screen.getByText('workflow')).toBeInTheDocument() + expect(screen.getByText('workflow'))!.toBeInTheDocument() }) it('should display correct app type for ADVANCED_CHAT', () => { const apps = [createMockApp({ id: 'chatflow-app', name: 'Chatflow App', mode: AppModeEnum.ADVANCED_CHAT })] render() - expect(screen.getByText('chatflow')).toBeInTheDocument() + expect(screen.getByText('chatflow'))!.toBeInTheDocument() }) it('should display correct app type for AGENT_CHAT', () => { const apps = [createMockApp({ id: 'agent-app', name: 'Agent App', mode: AppModeEnum.AGENT_CHAT })] render() - expect(screen.getByText('agent')).toBeInTheDocument() + expect(screen.getByText('agent'))!.toBeInTheDocument() }) it('should display correct app type for COMPLETION', () => { const apps = [createMockApp({ id: 'completion-app', name: 'Completion App', mode: AppModeEnum.COMPLETION })] render() - expect(screen.getByText('completion')).toBeInTheDocument() + expect(screen.getByText('completion'))!.toBeInTheDocument() }) }) @@ -575,7 +576,7 @@ describe('AppPicker', () => { it('should handle search text with value', () => { render() const input = screen.getByTestId('input') - expect(input).toHaveValue('test search') + expect(input)!.toHaveValue('test search') }) }) @@ -641,7 +642,8 @@ describe('AppPicker', () => { render() // The component should render without errors - expect(screen.getByTestId('portal-to-follow-elem')).toBeInTheDocument() + // The component should render without errors + expect(screen.getByTestId('portal-to-follow-elem'))!.toBeInTheDocument() }) it('should handle isShow toggle correctly', () => { @@ -654,7 +656,8 @@ describe('AppPicker', () => { rerender() // Should not crash - expect(screen.getByTestId('portal-to-follow-elem')).toBeInTheDocument() + // Should not crash + expect(screen.getByTestId('portal-to-follow-elem'))!.toBeInTheDocument() }) it('should setup intersection observer when isShow is true', () => { @@ -674,7 +677,8 @@ describe('AppPicker', () => { rerender() // Component should render without errors - expect(screen.getByTestId('portal-to-follow-elem')).toBeInTheDocument() + // Component should render without errors + expect(screen.getByTestId('portal-to-follow-elem'))!.toBeInTheDocument() }) it('should cleanup observer on component unmount', () => { @@ -691,7 +695,8 @@ describe('AppPicker', () => { triggerMutationObserver() // Component should still work correctly - expect(screen.getByTestId('portal-to-follow-elem')).toBeInTheDocument() + // Component should still work correctly + expect(screen.getByTestId('portal-to-follow-elem'))!.toBeInTheDocument() }) it('should not setup IntersectionObserver when observerTarget is null', () => { @@ -699,7 +704,8 @@ describe('AppPicker', () => { render() // The guard at line 84 should prevent setup - expect(screen.getByTestId('portal-to-follow-elem')).toBeInTheDocument() + // The guard at line 84 should prevent setup + expect(screen.getByTestId('portal-to-follow-elem'))!.toBeInTheDocument() }) it('should debounce onLoadMore calls using loadingRef', () => { @@ -798,8 +804,8 @@ describe('AppInputsForm', () => { { type: InputVarType.textInput, label: 'Name', variable: 'name', required: false }, ] render() - expect(screen.getByText('Name')).toBeInTheDocument() - expect(screen.getByPlaceholderText('Name')).toBeInTheDocument() + expect(screen.getByText('Name'))!.toBeInTheDocument() + expect(screen.getByPlaceholderText('Name'))!.toBeInTheDocument() }) it('should render number input field', () => { @@ -807,7 +813,7 @@ describe('AppInputsForm', () => { { type: InputVarType.number, label: 'Count', variable: 'count', required: false }, ] render() - expect(screen.getByText('Count')).toBeInTheDocument() + expect(screen.getByText('Count'))!.toBeInTheDocument() }) it('should render paragraph (textarea) field', () => { @@ -815,7 +821,7 @@ describe('AppInputsForm', () => { { type: InputVarType.paragraph, label: 'Description', variable: 'desc', required: false }, ] render() - expect(screen.getByText('Description')).toBeInTheDocument() + expect(screen.getByText('Description'))!.toBeInTheDocument() }) it('should render select field', () => { @@ -840,8 +846,8 @@ describe('AppInputsForm', () => { }, ] render() - expect(screen.getByText('Single File Upload')).toBeInTheDocument() - expect(screen.getByTestId('file-uploader')).toBeInTheDocument() + expect(screen.getByText('Single File Upload'))!.toBeInTheDocument() + expect(screen.getByTestId('file-uploader'))!.toBeInTheDocument() }) it('should render file uploader for single file with existing value', () => { @@ -859,7 +865,8 @@ describe('AppInputsForm', () => { ] render() // The file uploader should receive the existing file as an array - expect(screen.getByTestId('file-value')).toHaveTextContent(JSON.stringify([existingFile])) + // The file uploader should receive the existing file as an array + expect(screen.getByTestId('file-value'))!.toHaveTextContent(JSON.stringify([existingFile])) }) it('should render file uploader for multi files', () => { @@ -876,7 +883,7 @@ describe('AppInputsForm', () => { }, ] render() - expect(screen.getByText('Attachments')).toBeInTheDocument() + expect(screen.getByText('Attachments'))!.toBeInTheDocument() }) it('should show optional label for non-required fields', () => { @@ -884,7 +891,7 @@ describe('AppInputsForm', () => { { type: InputVarType.textInput, label: 'Name', variable: 'name', required: false }, ] render() - expect(screen.getByText('workflow.panel.optional')).toBeInTheDocument() + expect(screen.getByText('workflow.panel.optional'))!.toBeInTheDocument() }) it('should not show optional label for required fields', () => { @@ -1026,7 +1033,7 @@ describe('AppInputsForm', () => { render() const input = screen.getByPlaceholderText('Name') - expect(input).toHaveValue('existing') + expect(input)!.toHaveValue('existing') }) it('should handle empty string value', () => { @@ -1036,7 +1043,7 @@ describe('AppInputsForm', () => { render() const input = screen.getByPlaceholderText('Name') - expect(input).toHaveValue('') + expect(input)!.toHaveValue('') }) it('should handle undefined variable value', () => { @@ -1046,7 +1053,7 @@ describe('AppInputsForm', () => { render() const input = screen.getByPlaceholderText('Name') - expect(input).toHaveValue('') + expect(input)!.toHaveValue('') }) it('should handle multiple form fields', () => { @@ -1057,9 +1064,9 @@ describe('AppInputsForm', () => { ] render() - expect(screen.getByText('Name')).toBeInTheDocument() - expect(screen.getByText('Age')).toBeInTheDocument() - expect(screen.getByText('Bio')).toBeInTheDocument() + expect(screen.getByText('Name'))!.toBeInTheDocument() + expect(screen.getByText('Age'))!.toBeInTheDocument() + expect(screen.getByText('Bio'))!.toBeInTheDocument() }) it('should handle unknown form type gracefully', () => { @@ -1068,7 +1075,7 @@ describe('AppInputsForm', () => { ] // Should not throw error, just not render the field render() - expect(screen.getByText('Unknown')).toBeInTheDocument() + expect(screen.getByText('Unknown'))!.toBeInTheDocument() }) }) }) @@ -1093,18 +1100,49 @@ describe('AppInputsPanel', () => { describe('Rendering', () => { it('should render without crashing', () => { renderWithQueryClient() - expect(screen.getByText('app.appSelector.params')).toBeInTheDocument() + expect(screen.getByText('app.appSelector.params'))!.toBeInTheDocument() }) it('should show no params message when form schema is empty', () => { renderWithQueryClient() - expect(screen.getByText('app.appSelector.noParams')).toBeInTheDocument() + expect(screen.getByText('app.appSelector.noParams'))!.toBeInTheDocument() }) it('should show loading state when app is loading', () => { mockAppDetailLoading = true renderWithQueryClient() // Loading component should be rendered + // Loading component should be rendered + // Loading component should be rendered + // Loading component should be rendered + // Loading component should be rendered + // Loading component should be rendered + // Loading component should be rendered + // Loading component should be rendered + // Loading component should be rendered + // Loading component should be rendered + // Loading component should be rendered + // Loading component should be rendered + // Loading component should be rendered + // Loading component should be rendered + // Loading component should be rendered + // Loading component should be rendered + // Loading component should be rendered + // Loading component should be rendered + // Loading component should be rendered + // Loading component should be rendered + // Loading component should be rendered + // Loading component should be rendered + // Loading component should be rendered + // Loading component should be rendered + // Loading component should be rendered + // Loading component should be rendered + // Loading component should be rendered + // Loading component should be rendered + // Loading component should be rendered + // Loading component should be rendered + // Loading component should be rendered + // Loading component should be rendered expect(screen.queryByText('app.appSelector.params')).not.toBeInTheDocument() }) @@ -1119,19 +1157,19 @@ describe('AppInputsPanel', () => { describe('Props', () => { it('should handle undefined value', () => { renderWithQueryClient() - expect(screen.getByText('app.appSelector.params')).toBeInTheDocument() + expect(screen.getByText('app.appSelector.params'))!.toBeInTheDocument() }) it('should handle different app modes', () => { const workflowApp = createMockApp({ mode: AppModeEnum.WORKFLOW }) renderWithQueryClient() - expect(screen.getByText('app.appSelector.params')).toBeInTheDocument() + expect(screen.getByText('app.appSelector.params'))!.toBeInTheDocument() }) it('should handle advanced chat mode', () => { const advancedChatApp = createMockApp({ mode: AppModeEnum.ADVANCED_CHAT }) renderWithQueryClient() - expect(screen.getByText('app.appSelector.params')).toBeInTheDocument() + expect(screen.getByText('app.appSelector.params'))!.toBeInTheDocument() }) }) @@ -1147,7 +1185,7 @@ describe('AppInputsPanel', () => { }, }) renderWithQueryClient() - expect(screen.getByText('app.appSelector.params')).toBeInTheDocument() + expect(screen.getByText('app.appSelector.params'))!.toBeInTheDocument() }) it('should generate schema for number input', () => { @@ -1161,7 +1199,7 @@ describe('AppInputsPanel', () => { }, }) renderWithQueryClient() - expect(screen.getByText('app.appSelector.params')).toBeInTheDocument() + expect(screen.getByText('app.appSelector.params'))!.toBeInTheDocument() }) it('should generate schema for checkbox input', () => { @@ -1175,7 +1213,7 @@ describe('AppInputsPanel', () => { }, }) renderWithQueryClient() - expect(screen.getByText('app.appSelector.params')).toBeInTheDocument() + expect(screen.getByText('app.appSelector.params'))!.toBeInTheDocument() }) it('should generate schema for select input', () => { @@ -1189,7 +1227,7 @@ describe('AppInputsPanel', () => { }, }) renderWithQueryClient() - expect(screen.getByText('app.appSelector.params')).toBeInTheDocument() + expect(screen.getByText('app.appSelector.params'))!.toBeInTheDocument() }) it('should generate schema for file-list input', () => { @@ -1203,7 +1241,7 @@ describe('AppInputsPanel', () => { }, }) renderWithQueryClient() - expect(screen.getByText('app.appSelector.params')).toBeInTheDocument() + expect(screen.getByText('app.appSelector.params'))!.toBeInTheDocument() }) it('should generate schema for file input', () => { @@ -1217,7 +1255,7 @@ describe('AppInputsPanel', () => { }, }) renderWithQueryClient() - expect(screen.getByText('app.appSelector.params')).toBeInTheDocument() + expect(screen.getByText('app.appSelector.params'))!.toBeInTheDocument() }) it('should generate schema for json_object input', () => { @@ -1231,7 +1269,7 @@ describe('AppInputsPanel', () => { }, }) renderWithQueryClient() - expect(screen.getByText('app.appSelector.params')).toBeInTheDocument() + expect(screen.getByText('app.appSelector.params'))!.toBeInTheDocument() }) it('should generate schema for text-input (default)', () => { @@ -1245,7 +1283,7 @@ describe('AppInputsPanel', () => { }, }) renderWithQueryClient() - expect(screen.getByText('app.appSelector.params')).toBeInTheDocument() + expect(screen.getByText('app.appSelector.params'))!.toBeInTheDocument() }) it('should filter external_data_tool items', () => { @@ -1260,7 +1298,7 @@ describe('AppInputsPanel', () => { }, }) renderWithQueryClient() - expect(screen.getByText('app.appSelector.params')).toBeInTheDocument() + expect(screen.getByText('app.appSelector.params'))!.toBeInTheDocument() }) }) @@ -1283,7 +1321,7 @@ describe('AppInputsPanel', () => { } const workflowApp = createMockApp({ mode: AppModeEnum.WORKFLOW }) renderWithQueryClient() - expect(screen.getByText('app.appSelector.params')).toBeInTheDocument() + expect(screen.getByText('app.appSelector.params'))!.toBeInTheDocument() }) it('should generate schema for workflow with singleFile variable', () => { @@ -1304,7 +1342,7 @@ describe('AppInputsPanel', () => { } const workflowApp = createMockApp({ mode: AppModeEnum.WORKFLOW }) renderWithQueryClient() - expect(screen.getByText('app.appSelector.params')).toBeInTheDocument() + expect(screen.getByText('app.appSelector.params'))!.toBeInTheDocument() }) it('should generate schema for workflow with regular variable', () => { @@ -1325,7 +1363,7 @@ describe('AppInputsPanel', () => { } const workflowApp = createMockApp({ mode: AppModeEnum.WORKFLOW }) renderWithQueryClient() - expect(screen.getByText('app.appSelector.params')).toBeInTheDocument() + expect(screen.getByText('app.appSelector.params'))!.toBeInTheDocument() }) }) @@ -1344,7 +1382,7 @@ describe('AppInputsPanel', () => { }) const completionApp = createMockApp({ mode: AppModeEnum.COMPLETION }) renderWithQueryClient() - expect(screen.getByText('app.appSelector.params')).toBeInTheDocument() + expect(screen.getByText('app.appSelector.params'))!.toBeInTheDocument() }) it('should add image upload schema for WORKFLOW mode with file upload enabled', () => { @@ -1364,7 +1402,7 @@ describe('AppInputsPanel', () => { } const workflowApp = createMockApp({ mode: AppModeEnum.WORKFLOW }) renderWithQueryClient() - expect(screen.getByText('app.appSelector.params')).toBeInTheDocument() + expect(screen.getByText('app.appSelector.params'))!.toBeInTheDocument() }) }) @@ -1372,7 +1410,7 @@ describe('AppInputsPanel', () => { it('should call onFormChange when form is updated', () => { const onFormChange = vi.fn() renderWithQueryClient() - expect(screen.getByText('app.appSelector.params')).toBeInTheDocument() + expect(screen.getByText('app.appSelector.params'))!.toBeInTheDocument() }) it('should call onFormChange with updated values when text input changes', () => { @@ -1424,7 +1462,7 @@ describe('AppInputsPanel', () => { , ) - expect(screen.getByText('app.appSelector.params')).toBeInTheDocument() + expect(screen.getByText('app.appSelector.params'))!.toBeInTheDocument() }) }) @@ -1432,7 +1470,7 @@ describe('AppInputsPanel', () => { it('should return empty schema when currentApp is null', () => { mockAppDetailData = null renderWithQueryClient() - expect(screen.getByText('app.appSelector.noParams')).toBeInTheDocument() + expect(screen.getByText('app.appSelector.noParams'))!.toBeInTheDocument() }) it('should handle workflow without start node', () => { @@ -1442,7 +1480,7 @@ describe('AppInputsPanel', () => { } const workflowApp = createMockApp({ mode: AppModeEnum.WORKFLOW }) renderWithQueryClient() - expect(screen.getByText('app.appSelector.params')).toBeInTheDocument() + expect(screen.getByText('app.appSelector.params'))!.toBeInTheDocument() }) }) }) @@ -1477,12 +1515,12 @@ describe('AppSelector', () => { describe('Rendering', () => { it('should render without crashing', () => { renderWithQueryClient() - expect(screen.getByTestId('portal-to-follow-elem')).toBeInTheDocument() + expect(screen.getByTestId('portal-to-follow-elem'))!.toBeInTheDocument() }) it('should render trigger component', () => { renderWithQueryClient() - expect(screen.getByText('app.appSelector.placeholder')).toBeInTheDocument() + expect(screen.getByText('app.appSelector.placeholder'))!.toBeInTheDocument() }) it('should show selected app info when value is provided', () => { @@ -1493,19 +1531,20 @@ describe('AppSelector', () => { />, ) // Should show the app trigger with app info - expect(screen.getByTestId('portal-to-follow-elem')).toBeInTheDocument() + // Should show the app trigger with app info + expect(screen.getByTestId('portal-to-follow-elem'))!.toBeInTheDocument() }) }) describe('Props', () => { it('should handle different placement values', () => { renderWithQueryClient() - expect(screen.getByTestId('portal-to-follow-elem')).toBeInTheDocument() + expect(screen.getByTestId('portal-to-follow-elem'))!.toBeInTheDocument() }) it('should handle different offset values', () => { renderWithQueryClient() - expect(screen.getByTestId('portal-to-follow-elem')).toBeInTheDocument() + expect(screen.getByTestId('portal-to-follow-elem'))!.toBeInTheDocument() }) it('should handle disabled state', () => { @@ -1513,12 +1552,13 @@ describe('AppSelector', () => { const trigger = screen.getByTestId('portal-trigger') fireEvent.click(trigger) // Portal should remain closed when disabled - expect(screen.getByTestId('portal-to-follow-elem')).toHaveAttribute('data-open', 'false') + // Portal should remain closed when disabled + expect(screen.getByTestId('portal-to-follow-elem'))!.toHaveAttribute('data-open', 'false') }) it('should handle scope prop', () => { renderWithQueryClient() - expect(screen.getByTestId('portal-to-follow-elem')).toBeInTheDocument() + expect(screen.getByTestId('portal-to-follow-elem'))!.toBeInTheDocument() }) it('should handle value with inputs', () => { @@ -1528,7 +1568,7 @@ describe('AppSelector', () => { value={{ app_id: 'app-1', inputs: { name: 'test' }, files: [] }} />, ) - expect(screen.getByTestId('portal-to-follow-elem')).toBeInTheDocument() + expect(screen.getByTestId('portal-to-follow-elem'))!.toBeInTheDocument() }) it('should handle value with files', () => { @@ -1538,7 +1578,7 @@ describe('AppSelector', () => { value={{ app_id: 'app-1', inputs: {}, files: [{ id: 'file-1' }] }} />, ) - expect(screen.getByTestId('portal-to-follow-elem')).toBeInTheDocument() + expect(screen.getByTestId('portal-to-follow-elem'))!.toBeInTheDocument() }) }) @@ -1547,10 +1587,11 @@ describe('AppSelector', () => { renderWithQueryClient() const trigger = screen.getAllByTestId('portal-trigger')[0] - fireEvent.click(trigger) + fireEvent.click(trigger!) // The portal state should update synchronously - get the first one (outer portal) - expect(screen.getAllByTestId('portal-to-follow-elem')[0]).toHaveAttribute('data-open', 'true') + // The portal state should update synchronously - get the first one (outer portal) + expect(screen.getAllByTestId('portal-to-follow-elem')[0])!.toHaveAttribute('data-open', 'true') }) it('should not toggle isShow when disabled', () => { @@ -1559,7 +1600,7 @@ describe('AppSelector', () => { const trigger = screen.getByTestId('portal-trigger') fireEvent.click(trigger) - expect(screen.getByTestId('portal-to-follow-elem')).toHaveAttribute('data-open', 'false') + expect(screen.getByTestId('portal-to-follow-elem'))!.toHaveAttribute('data-open', 'false') }) it('should manage search text state', () => { @@ -1569,7 +1610,8 @@ describe('AppSelector', () => { fireEvent.click(trigger) // Portal content should be visible after click - expect(screen.getByTestId('portal-content')).toBeInTheDocument() + // Portal content should be visible after click + expect(screen.getByTestId('portal-content'))!.toBeInTheDocument() }) it('should render correctly during load more setup', () => { @@ -1579,7 +1621,8 @@ describe('AppSelector', () => { renderWithQueryClient() // Trigger should be rendered - expect(screen.getByTestId('portal-trigger')).toBeInTheDocument() + // Trigger should be rendered + expect(screen.getByTestId('portal-trigger'))!.toBeInTheDocument() }) }) @@ -1592,7 +1635,7 @@ describe('AppSelector', () => { // Open the portal fireEvent.click(screen.getByTestId('portal-trigger')) - expect(screen.getByTestId('portal-content')).toBeInTheDocument() + expect(screen.getByTestId('portal-content'))!.toBeInTheDocument() }) it('should call onSelect with correct value structure', () => { @@ -1606,7 +1649,8 @@ describe('AppSelector', () => { ) // The component should maintain the correct value structure - expect(screen.getByTestId('portal-to-follow-elem')).toBeInTheDocument() + // The component should maintain the correct value structure + expect(screen.getByTestId('portal-to-follow-elem'))!.toBeInTheDocument() }) it('should clear inputs when selecting different app', () => { @@ -1620,7 +1664,8 @@ describe('AppSelector', () => { ) // Component renders with existing value - expect(screen.getByTestId('portal-to-follow-elem')).toBeInTheDocument() + // Component renders with existing value + expect(screen.getByTestId('portal-to-follow-elem'))!.toBeInTheDocument() }) it('should preserve inputs when selecting same app', () => { @@ -1633,7 +1678,7 @@ describe('AppSelector', () => { />, ) - expect(screen.getByTestId('portal-to-follow-elem')).toBeInTheDocument() + expect(screen.getByTestId('portal-to-follow-elem'))!.toBeInTheDocument() }) }) @@ -1647,7 +1692,7 @@ describe('AppSelector', () => { } renderWithQueryClient() - expect(screen.getByTestId('portal-to-follow-elem')).toBeInTheDocument() + expect(screen.getByTestId('portal-to-follow-elem'))!.toBeInTheDocument() }) it('should memoize currentAppInfo correctly', () => { @@ -1662,7 +1707,7 @@ describe('AppSelector', () => { />, ) - expect(screen.getByTestId('portal-to-follow-elem')).toBeInTheDocument() + expect(screen.getByTestId('portal-to-follow-elem'))!.toBeInTheDocument() }) it('should memoize formattedValue correctly', () => { @@ -1673,7 +1718,7 @@ describe('AppSelector', () => { />, ) - expect(screen.getByTestId('portal-to-follow-elem')).toBeInTheDocument() + expect(screen.getByTestId('portal-to-follow-elem'))!.toBeInTheDocument() }) it('should be wrapped with React.memo', () => { @@ -1690,7 +1735,7 @@ describe('AppSelector', () => { , ) - expect(screen.getByTestId('portal-to-follow-elem')).toBeInTheDocument() + expect(screen.getByTestId('portal-to-follow-elem'))!.toBeInTheDocument() }) }) @@ -1698,7 +1743,7 @@ describe('AppSelector', () => { it('should handle load more when hasMore is true', async () => { mockHasNextPage = true renderWithQueryClient() - expect(screen.getByTestId('portal-to-follow-elem')).toBeInTheDocument() + expect(screen.getByTestId('portal-to-follow-elem'))!.toBeInTheDocument() }) it('should not trigger load more when already loading', async () => { @@ -1724,7 +1769,7 @@ describe('AppSelector', () => { vi.advanceTimersByTime(500) }) - expect(screen.getByTestId('portal-to-follow-elem')).toBeInTheDocument() + expect(screen.getByTestId('portal-to-follow-elem'))!.toBeInTheDocument() }) it('should render load more area when hasMore is true', () => { @@ -1735,10 +1780,11 @@ describe('AppSelector', () => { renderWithQueryClient() // Open the portal - fireEvent.click(screen.getAllByTestId('portal-trigger')[0]) + fireEvent.click(screen.getAllByTestId('portal-trigger')[0]!) // Should render without errors - expect(screen.getByTestId('portal-content')).toBeInTheDocument() + // Should render without errors + expect(screen.getByTestId('portal-content'))!.toBeInTheDocument() }) it('should handle fetchNextPage rejection gracefully in handleLoadMore', async () => { @@ -1748,7 +1794,8 @@ describe('AppSelector', () => { renderWithQueryClient() // Should not crash even if fetchNextPage rejects - expect(screen.getByTestId('portal-to-follow-elem')).toBeInTheDocument() + // Should not crash even if fetchNextPage rejects + expect(screen.getByTestId('portal-to-follow-elem'))!.toBeInTheDocument() }) it('should call fetchNextPage when intersection observer triggers handleLoadMore', async () => { @@ -1759,11 +1806,11 @@ describe('AppSelector', () => { renderWithQueryClient() // Open the main portal - fireEvent.click(screen.getAllByTestId('portal-trigger')[0]) + fireEvent.click(screen.getAllByTestId('portal-trigger')[0]!) // Open the inner app picker portal const triggers = screen.getAllByTestId('portal-trigger') - fireEvent.click(triggers[1]) + fireEvent.click(triggers[1]!) // Simulate intersection to trigger handleLoadMore triggerIntersection([{ isIntersecting: true } as IntersectionObserverEntry]) @@ -1780,9 +1827,9 @@ describe('AppSelector', () => { renderWithQueryClient() // Open portals - fireEvent.click(screen.getAllByTestId('portal-trigger')[0]) + fireEvent.click(screen.getAllByTestId('portal-trigger')[0]!) const triggers = screen.getAllByTestId('portal-trigger') - fireEvent.click(triggers[1]) + fireEvent.click(triggers[1]!) // Trigger first intersection triggerIntersection([{ isIntersecting: true } as IntersectionObserverEntry]) @@ -1806,9 +1853,9 @@ describe('AppSelector', () => { renderWithQueryClient() // Open portals - fireEvent.click(screen.getAllByTestId('portal-trigger')[0]) + fireEvent.click(screen.getAllByTestId('portal-trigger')[0]!) const triggers = screen.getAllByTestId('portal-trigger') - fireEvent.click(triggers[1]) + fireEvent.click(triggers[1]!) // Trigger intersection triggerIntersection([{ isIntersecting: true } as IntersectionObserverEntry]) @@ -1825,9 +1872,9 @@ describe('AppSelector', () => { renderWithQueryClient() // Open portals - fireEvent.click(screen.getAllByTestId('portal-trigger')[0]) + fireEvent.click(screen.getAllByTestId('portal-trigger')[0]!) const triggers = screen.getAllByTestId('portal-trigger') - fireEvent.click(triggers[1]) + fireEvent.click(triggers[1]!) // Trigger intersection triggerIntersection([{ isIntersecting: true } as IntersectionObserverEntry]) @@ -1847,7 +1894,7 @@ describe('AppSelector', () => { />, ) - expect(screen.getByTestId('portal-to-follow-elem')).toBeInTheDocument() + expect(screen.getByTestId('portal-to-follow-elem'))!.toBeInTheDocument() }) it('should handle form change without image file', () => { @@ -1860,7 +1907,7 @@ describe('AppSelector', () => { />, ) - expect(screen.getByTestId('portal-to-follow-elem')).toBeInTheDocument() + expect(screen.getByTestId('portal-to-follow-elem'))!.toBeInTheDocument() }) it('should extract #image# from inputs and add to files array', () => { @@ -1874,7 +1921,7 @@ describe('AppSelector', () => { />, ) - expect(screen.getByTestId('portal-to-follow-elem')).toBeInTheDocument() + expect(screen.getByTestId('portal-to-follow-elem'))!.toBeInTheDocument() }) it('should preserve existing files when no #image# in inputs', () => { @@ -1887,7 +1934,7 @@ describe('AppSelector', () => { />, ) - expect(screen.getByTestId('portal-to-follow-elem')).toBeInTheDocument() + expect(screen.getByTestId('portal-to-follow-elem'))!.toBeInTheDocument() }) }) @@ -1907,9 +1954,9 @@ describe('AppSelector', () => { ) // Open the main portal - fireEvent.click(screen.getAllByTestId('portal-trigger')[0]) + fireEvent.click(screen.getAllByTestId('portal-trigger')[0]!) - expect(screen.getByTestId('portal-content')).toBeInTheDocument() + expect(screen.getByTestId('portal-content'))!.toBeInTheDocument() }) it('should preserve inputs when selecting the same app', () => { @@ -1926,7 +1973,7 @@ describe('AppSelector', () => { />, ) - expect(screen.getByTestId('portal-to-follow-elem')).toBeInTheDocument() + expect(screen.getByTestId('portal-to-follow-elem'))!.toBeInTheDocument() }) it('should handle app selection with empty value', () => { @@ -1944,34 +1991,34 @@ describe('AppSelector', () => { ) // Open the main portal - fireEvent.click(screen.getAllByTestId('portal-trigger')[0]) + fireEvent.click(screen.getAllByTestId('portal-trigger')[0]!) - expect(screen.getByTestId('portal-content')).toBeInTheDocument() + expect(screen.getByTestId('portal-content'))!.toBeInTheDocument() }) }) describe('Edge Cases', () => { it('should handle undefined value', () => { renderWithQueryClient() - expect(screen.getByText('app.appSelector.placeholder')).toBeInTheDocument() + expect(screen.getByText('app.appSelector.placeholder'))!.toBeInTheDocument() }) it('should handle empty pages array', () => { mockAppListData = { pages: [] } renderWithQueryClient() - expect(screen.getByTestId('portal-to-follow-elem')).toBeInTheDocument() + expect(screen.getByTestId('portal-to-follow-elem'))!.toBeInTheDocument() }) it('should handle undefined data', () => { mockAppListData = undefined renderWithQueryClient() - expect(screen.getByTestId('portal-to-follow-elem')).toBeInTheDocument() + expect(screen.getByTestId('portal-to-follow-elem'))!.toBeInTheDocument() }) it('should handle loading state', () => { mockIsLoading = true renderWithQueryClient() - expect(screen.getByTestId('portal-to-follow-elem')).toBeInTheDocument() + expect(screen.getByTestId('portal-to-follow-elem'))!.toBeInTheDocument() }) it('should handle app not found in displayedApps', () => { @@ -1986,7 +2033,7 @@ describe('AppSelector', () => { />, ) - expect(screen.getByTestId('portal-to-follow-elem')).toBeInTheDocument() + expect(screen.getByTestId('portal-to-follow-elem'))!.toBeInTheDocument() }) it('should handle value with empty inputs and files', () => { @@ -1997,7 +2044,7 @@ describe('AppSelector', () => { />, ) - expect(screen.getByTestId('portal-to-follow-elem')).toBeInTheDocument() + expect(screen.getByTestId('portal-to-follow-elem'))!.toBeInTheDocument() }) }) @@ -2009,7 +2056,8 @@ describe('AppSelector', () => { renderWithQueryClient() // Should not crash - expect(screen.getByTestId('portal-to-follow-elem')).toBeInTheDocument() + // Should not crash + expect(screen.getByTestId('portal-to-follow-elem'))!.toBeInTheDocument() }) }) }) @@ -2043,10 +2091,11 @@ describe('AppSelector Integration', () => { renderWithQueryClient() // 1. Click trigger to open picker - get first trigger (outer portal) - fireEvent.click(screen.getAllByTestId('portal-trigger')[0]) + fireEvent.click(screen.getAllByTestId('portal-trigger')[0]!) // Get the first portal element (outer portal) - expect(screen.getAllByTestId('portal-to-follow-elem')[0]).toHaveAttribute('data-open', 'true') + // Get the first portal element (outer portal) + expect(screen.getAllByTestId('portal-to-follow-elem')[0])!.toHaveAttribute('data-open', 'true') }) it('should handle app change with input preservation logic', () => { @@ -2058,7 +2107,7 @@ describe('AppSelector Integration', () => { />, ) - expect(screen.getByTestId('portal-to-follow-elem')).toBeInTheDocument() + expect(screen.getByTestId('portal-to-follow-elem'))!.toBeInTheDocument() }) }) @@ -2067,7 +2116,8 @@ describe('AppSelector Integration', () => { renderWithQueryClient() // AppTrigger should show placeholder when no app selected - expect(screen.getByText('app.appSelector.placeholder')).toBeInTheDocument() + // AppTrigger should show placeholder when no app selected + expect(screen.getByText('app.appSelector.placeholder'))!.toBeInTheDocument() }) it('should pass correct props to AppPicker', () => { @@ -2075,7 +2125,7 @@ describe('AppSelector Integration', () => { fireEvent.click(screen.getByTestId('portal-trigger')) - expect(screen.getByTestId('portal-content')).toBeInTheDocument() + expect(screen.getByTestId('portal-content'))!.toBeInTheDocument() }) }) @@ -2088,7 +2138,7 @@ describe('AppSelector Integration', () => { />, ) - expect(screen.getByTestId('portal-to-follow-elem')).toBeInTheDocument() + expect(screen.getByTestId('portal-to-follow-elem'))!.toBeInTheDocument() }) it('should handle search filtering through app list', () => { @@ -2096,7 +2146,7 @@ describe('AppSelector Integration', () => { fireEvent.click(screen.getByTestId('portal-trigger')) - expect(screen.getByTestId('portal-content')).toBeInTheDocument() + expect(screen.getByTestId('portal-content'))!.toBeInTheDocument() }) }) @@ -2115,13 +2165,13 @@ describe('AppSelector Integration', () => { ) // Open the main portal - fireEvent.click(screen.getAllByTestId('portal-trigger')[0]) + fireEvent.click(screen.getAllByTestId('portal-trigger')[0]!) // The inner AppPicker portal is closed by default (isShowChooseApp = false) // We need to click on the inner trigger to open it const innerTriggers = screen.getAllByTestId('portal-trigger') // The second trigger is the inner AppPicker trigger - fireEvent.click(innerTriggers[1]) + fireEvent.click(innerTriggers[1]!) // Now the inner portal should be open and show the app list // Find and click on app-2 @@ -2150,16 +2200,16 @@ describe('AppSelector Integration', () => { ) // Open the main portal - fireEvent.click(screen.getAllByTestId('portal-trigger')[0]) + fireEvent.click(screen.getAllByTestId('portal-trigger')[0]!) // Click on the inner trigger to open app picker const innerTriggers = screen.getAllByTestId('portal-trigger') - fireEvent.click(innerTriggers[1]) + fireEvent.click(innerTriggers[1]!) // Click on the same app - need to get the one in the app list, not the trigger const appItems = screen.getAllByText('App 1') // The last one should be in the dropdown list - fireEvent.click(appItems[appItems.length - 1]) + fireEvent.click(appItems[appItems.length - 1]!) // onSelect should be called with preserved inputs since it's the same app expect(onSelect).toHaveBeenCalledWith({ @@ -2183,15 +2233,15 @@ describe('AppSelector Integration', () => { ) // Open the main portal - fireEvent.click(screen.getAllByTestId('portal-trigger')[0]) + fireEvent.click(screen.getAllByTestId('portal-trigger')[0]!) // Click on inner trigger to open app picker const innerTriggers = screen.getAllByTestId('portal-trigger') - fireEvent.click(innerTriggers[1]) + fireEvent.click(innerTriggers[1]!) // Click on an app from the dropdown const app1Elements = screen.getAllByText('App 1') - fireEvent.click(app1Elements[app1Elements.length - 1]) + fireEvent.click(app1Elements[app1Elements.length - 1]!) // onSelect should be called with new app and empty inputs/files expect(onSelect).toHaveBeenCalledWith({ @@ -2211,9 +2261,9 @@ describe('AppSelector Integration', () => { renderWithQueryClient() // Open the portal to render the app picker - fireEvent.click(screen.getAllByTestId('portal-trigger')[0]) + fireEvent.click(screen.getAllByTestId('portal-trigger')[0]!) - expect(screen.getByTestId('portal-content')).toBeInTheDocument() + expect(screen.getByTestId('portal-content'))!.toBeInTheDocument() }) it('should stay stable after fetchNextPage completes', async () => { @@ -2223,9 +2273,9 @@ describe('AppSelector Integration', () => { renderWithQueryClient() - fireEvent.click(screen.getAllByTestId('portal-trigger')[0]) + fireEvent.click(screen.getAllByTestId('portal-trigger')[0]!) - expect(screen.getByTestId('portal-content')).toBeInTheDocument() + expect(screen.getByTestId('portal-content'))!.toBeInTheDocument() }) it('should not call fetchNextPage when conditions prevent it', () => { @@ -2234,7 +2284,7 @@ describe('AppSelector Integration', () => { renderWithQueryClient() - fireEvent.click(screen.getAllByTestId('portal-trigger')[0]) + fireEvent.click(screen.getAllByTestId('portal-trigger')[0]!) // fetchNextPage should not be called expect(mockFetchNextPage).not.toHaveBeenCalled() @@ -2256,7 +2306,7 @@ describe('AppSelector Integration', () => { ) // Open portal - fireEvent.click(screen.getAllByTestId('portal-trigger')[0]) + fireEvent.click(screen.getAllByTestId('portal-trigger')[0]!) // formattedValue should include #image# from files expect(screen.getAllByTestId('portal-content').length).toBeGreaterThan(0) @@ -2275,7 +2325,7 @@ describe('AppSelector Integration', () => { />, ) - fireEvent.click(screen.getAllByTestId('portal-trigger')[0]) + fireEvent.click(screen.getAllByTestId('portal-trigger')[0]!) expect(screen.getAllByTestId('portal-content').length).toBeGreaterThan(0) }) @@ -2293,7 +2343,7 @@ describe('AppSelector Integration', () => { />, ) - fireEvent.click(screen.getAllByTestId('portal-trigger')[0]) + fireEvent.click(screen.getAllByTestId('portal-trigger')[0]!) expect(screen.getAllByTestId('portal-content').length).toBeGreaterThan(0) }) @@ -2324,12 +2374,12 @@ describe('AppSelector Integration', () => { ) // Open portal to render AppInputsPanel - fireEvent.click(screen.getAllByTestId('portal-trigger')[0]) + fireEvent.click(screen.getAllByTestId('portal-trigger')[0]!) // Find and interact with the form input (may not exist if schema is empty) const formInputs = screen.queryAllByPlaceholderText('FormInputField') if (formInputs.length > 0) { - fireEvent.change(formInputs[0], { target: { value: 'test value' } }) + fireEvent.change(formInputs[0]!, { target: { value: 'test value' } }) // handleFormChange in index.tsx should have been called expect(onSelect).toHaveBeenCalledWith({ @@ -2376,12 +2426,12 @@ describe('AppSelector Integration', () => { />, ) - fireEvent.click(screen.getAllByTestId('portal-trigger')[0]) + fireEvent.click(screen.getAllByTestId('portal-trigger')[0]!) // Find file uploader and trigger upload - the #image# field will be extracted const uploadBtns = screen.queryAllByTestId('upload-file-btn') if (uploadBtns.length > 0) { - fireEvent.click(uploadBtns[0]) + fireEvent.click(uploadBtns[0]!) // handleFormChange should extract #image# and convert to files expect(onSelect).toHaveBeenCalled() } @@ -2414,12 +2464,12 @@ describe('AppSelector Integration', () => { />, ) - fireEvent.click(screen.getAllByTestId('portal-trigger')[0]) + fireEvent.click(screen.getAllByTestId('portal-trigger')[0]!) // Find form input (may not exist if schema is empty) const inputs = screen.queryAllByPlaceholderText('PreserveField') if (inputs.length > 0) { - fireEvent.change(inputs[0], { target: { value: 'updated name' } }) + fireEvent.change(inputs[0]!, { target: { value: 'updated name' } }) // onSelect should be called preserving existing files (no #image# in inputs) expect(onSelect).toHaveBeenCalledWith({ @@ -2465,7 +2515,7 @@ describe('AppSelector Integration', () => { />, ) - fireEvent.click(screen.getAllByTestId('portal-trigger')[0]) + fireEvent.click(screen.getAllByTestId('portal-trigger')[0]!) // Try to find and click the upload button which triggers #image# form change const uploadBtn = screen.queryByTestId('upload-file-btn') @@ -2499,11 +2549,11 @@ describe('AppSelector Integration', () => { />, ) - fireEvent.click(screen.getAllByTestId('portal-trigger')[0]) + fireEvent.click(screen.getAllByTestId('portal-trigger')[0]!) const inputs = screen.queryAllByPlaceholderText('SimpleInput') if (inputs.length > 0) { - fireEvent.change(inputs[0], { target: { value: 'changed' } }) + fireEvent.change(inputs[0]!, { target: { value: 'changed' } }) // handleFormChange should preserve existing files when no #image# in inputs expect(onSelect).toHaveBeenCalledWith({ app_id: 'app-1', diff --git a/web/app/components/plugins/plugin-detail-panel/app-selector/app-picker.tsx b/web/app/components/plugins/plugin-detail-panel/app-selector/app-picker.tsx index c4cb4f4da8..4c96b69beb 100644 --- a/web/app/components/plugins/plugin-detail-panel/app-selector/app-picker.tsx +++ b/web/app/components/plugins/plugin-detail-panel/app-selector/app-picker.tsx @@ -78,7 +78,7 @@ const AppPicker: FC = ({ const handleIntersection = useCallback((entries: IntersectionObserverEntry[]) => { const target = entries[0] - if (!target.isIntersecting || loadingRef.current || !hasMore || isLoading) + if (!target!.isIntersecting || loadingRef.current || !hasMore || isLoading) return loadingRef.current = true diff --git a/web/app/components/plugins/plugin-detail-panel/app-selector/hooks/use-app-inputs-form-schema.ts b/web/app/components/plugins/plugin-detail-panel/app-selector/hooks/use-app-inputs-form-schema.ts index 51bbcde18d..8f3d5f0249 100644 --- a/web/app/components/plugins/plugin-detail-panel/app-selector/hooks/use-app-inputs-form-schema.ts +++ b/web/app/components/plugins/plugin-detail-panel/app-selector/hooks/use-app-inputs-form-schema.ts @@ -53,7 +53,7 @@ function buildFileConfig(fileConfig: FileUpload | undefined) { enabled: !!(fileConfig?.enabled || fileConfig?.image?.enabled), allowed_file_types: fileConfig?.allowed_file_types || [SupportUploadFileTypes.image], allowed_file_extensions: fileConfig?.allowed_file_extensions - || [...FILE_EXTS[SupportUploadFileTypes.image]].map(ext => `.${ext}`), + || [...(FILE_EXTS[SupportUploadFileTypes.image] ?? [])].map(ext => `.${ext}`), allowed_file_upload_methods: fileConfig?.allowed_file_upload_methods || fileConfig?.image?.transfer_methods || ['local_file', 'remote_url'], diff --git a/web/app/components/plugins/plugin-detail-panel/model-selector/__tests__/tts-params-panel.spec.tsx b/web/app/components/plugins/plugin-detail-panel/model-selector/__tests__/tts-params-panel.spec.tsx index c3f7f221b5..e423359872 100644 --- a/web/app/components/plugins/plugin-detail-panel/model-selector/__tests__/tts-params-panel.spec.tsx +++ b/web/app/components/plugins/plugin-detail-panel/model-selector/__tests__/tts-params-panel.spec.tsx @@ -161,7 +161,8 @@ describe('TTSParamsPanel', () => { const { container } = render() // Assert - expect(container).toBeInTheDocument() + // Assert + expect(container)!.toBeInTheDocument() }) it('should render language label', () => { @@ -172,7 +173,8 @@ describe('TTSParamsPanel', () => { render() // Assert - expect(screen.getByText('appDebug.voice.voiceSettings.language')).toBeInTheDocument() + // Assert + expect(screen.getByText('appDebug.voice.voiceSettings.language'))!.toBeInTheDocument() }) it('should render voice label', () => { @@ -183,7 +185,8 @@ describe('TTSParamsPanel', () => { render() // Assert - expect(screen.getByText('appDebug.voice.voiceSettings.voice')).toBeInTheDocument() + // Assert + expect(screen.getByText('appDebug.voice.voiceSettings.voice'))!.toBeInTheDocument() }) it('should render two Select components', () => { @@ -207,7 +210,7 @@ describe('TTSParamsPanel', () => { // Assert const values = screen.getAllByTestId('selected-value') - expect(values[0]).toHaveTextContent('zh-Hans') + expect(values[0])!.toHaveTextContent('zh-Hans') }) it('should render voice select with correct value', () => { @@ -219,7 +222,7 @@ describe('TTSParamsPanel', () => { // Assert const values = screen.getAllByTestId('selected-value') - expect(values[1]).toHaveTextContent('echo') + expect(values[1])!.toHaveTextContent('echo') }) it('should only show supported languages in language select', () => { @@ -230,9 +233,10 @@ describe('TTSParamsPanel', () => { render() // Assert - expect(screen.getByTestId('select-item-en-US')).toBeInTheDocument() - expect(screen.getByTestId('select-item-zh-Hans')).toBeInTheDocument() - expect(screen.getByTestId('select-item-ja-JP')).toBeInTheDocument() + // Assert + expect(screen.getByTestId('select-item-en-US'))!.toBeInTheDocument() + expect(screen.getByTestId('select-item-zh-Hans'))!.toBeInTheDocument() + expect(screen.getByTestId('select-item-ja-JP'))!.toBeInTheDocument() expect(screen.queryByTestId('select-item-unsupported-lang')).not.toBeInTheDocument() }) @@ -244,9 +248,10 @@ describe('TTSParamsPanel', () => { render() // Assert - expect(screen.getByTestId('select-item-alloy')).toBeInTheDocument() - expect(screen.getByTestId('select-item-echo')).toBeInTheDocument() - expect(screen.getByTestId('select-item-fable')).toBeInTheDocument() + // Assert + expect(screen.getByTestId('select-item-alloy'))!.toBeInTheDocument() + expect(screen.getByTestId('select-item-echo'))!.toBeInTheDocument() + expect(screen.getByTestId('select-item-fable'))!.toBeInTheDocument() }) }) @@ -260,8 +265,9 @@ describe('TTSParamsPanel', () => { render() // Assert - expect(screen.getByTestId('tts-language-select-trigger')).toHaveAttribute('data-class', 'w-full') - expect(screen.getByTestId('tts-voice-select-trigger')).toHaveAttribute('data-class', 'w-full') + // Assert + expect(screen.getByTestId('tts-language-select-trigger'))!.toHaveAttribute('data-class', 'w-full') + expect(screen.getByTestId('tts-voice-select-trigger'))!.toHaveAttribute('data-class', 'w-full') }) it('should apply popup className to SelectContent', () => { @@ -273,8 +279,8 @@ describe('TTSParamsPanel', () => { // Assert const contents = screen.getAllByTestId('select-content') - expect(contents[0]).toHaveAttribute('data-popup-class', 'w-[354px]') - expect(contents[1]).toHaveAttribute('data-popup-class', 'w-[354px]') + expect(contents[0])!.toHaveAttribute('data-popup-class', 'w-[354px]') + expect(contents[1])!.toHaveAttribute('data-popup-class', 'w-[354px]') }) }) @@ -396,6 +402,37 @@ describe('TTSParamsPanel', () => { // Act render() + // Assert - no voice items should be rendered + // Assert - no voice items should be rendered + // Assert - no voice items should be rendered + // Assert - no voice items should be rendered + // Assert - no voice items should be rendered + // Assert - no voice items should be rendered + // Assert - no voice items should be rendered + // Assert - no voice items should be rendered + // Assert - no voice items should be rendered + // Assert - no voice items should be rendered + // Assert - no voice items should be rendered + // Assert - no voice items should be rendered + // Assert - no voice items should be rendered + // Assert - no voice items should be rendered + // Assert - no voice items should be rendered + // Assert - no voice items should be rendered + // Assert - no voice items should be rendered + // Assert - no voice items should be rendered + // Assert - no voice items should be rendered + // Assert - no voice items should be rendered + // Assert - no voice items should be rendered + // Assert - no voice items should be rendered + // Assert - no voice items should be rendered + // Assert - no voice items should be rendered + // Assert - no voice items should be rendered + // Assert - no voice items should be rendered + // Assert - no voice items should be rendered + // Assert - no voice items should be rendered + // Assert - no voice items should be rendered + // Assert - no voice items should be rendered + // Assert - no voice items should be rendered // Assert - no voice items should be rendered expect(screen.queryByTestId('select-item-alloy')).not.toBeInTheDocument() expect(screen.queryByTestId('select-item-echo')).not.toBeInTheDocument() @@ -413,6 +450,37 @@ describe('TTSParamsPanel', () => { // Act render() + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert // Assert expect(screen.queryByTestId('select-item-alloy')).not.toBeInTheDocument() }) @@ -430,8 +498,9 @@ describe('TTSParamsPanel', () => { render() // Assert - expect(screen.getByTestId('select-item-voice-1')).toBeInTheDocument() - expect(screen.getByTestId('select-item-voice-2')).toBeInTheDocument() + // Assert + expect(screen.getByTestId('select-item-voice-1'))!.toBeInTheDocument() + expect(screen.getByTestId('select-item-voice-2'))!.toBeInTheDocument() }) it('should handle currentModel with empty voices array', () => { @@ -444,7 +513,7 @@ describe('TTSParamsPanel', () => { render() // Assert - no voice items (except language items) - expect(screen.getAllByTestId('select-content')[1].children).toHaveLength(0) + expect(screen.getAllByTestId('select-content')[1]!.children).toHaveLength(0) expect(screen.queryByTestId('select-item-alloy')).not.toBeInTheDocument() }) @@ -460,7 +529,8 @@ describe('TTSParamsPanel', () => { render() // Assert - expect(screen.getByTestId('select-item-single-voice')).toBeInTheDocument() + // Assert + expect(screen.getByTestId('select-item-single-voice'))!.toBeInTheDocument() }) }) @@ -475,7 +545,7 @@ describe('TTSParamsPanel', () => { // Assert const values = screen.getAllByTestId('selected-value') - expect(values[0]).toHaveTextContent('') + expect(values[0])!.toHaveTextContent('') }) it('should handle empty voice value', () => { @@ -487,7 +557,7 @@ describe('TTSParamsPanel', () => { // Assert const values = screen.getAllByTestId('selected-value') - expect(values[1]).toHaveTextContent('') + expect(values[1])!.toHaveTextContent('') }) it('should handle many voices', () => { @@ -504,8 +574,9 @@ describe('TTSParamsPanel', () => { render() // Assert - expect(screen.getByTestId('select-item-voice-0')).toBeInTheDocument() - expect(screen.getByTestId('select-item-voice-19')).toBeInTheDocument() + // Assert + expect(screen.getByTestId('select-item-voice-0'))!.toBeInTheDocument() + expect(screen.getByTestId('select-item-voice-19'))!.toBeInTheDocument() }) it('should handle voice with special characters in mode', () => { @@ -520,7 +591,8 @@ describe('TTSParamsPanel', () => { render() // Assert - expect(screen.getByTestId('select-item-voice-with_special.chars')).toBeInTheDocument() + // Assert + expect(screen.getByTestId('select-item-voice-with_special.chars'))!.toBeInTheDocument() }) it('should handle onChange not being called multiple times', () => { @@ -546,13 +618,13 @@ describe('TTSParamsPanel', () => { // Act const { rerender } = render() const values = screen.getAllByTestId('selected-value') - expect(values[0]).toHaveTextContent('en-US') + expect(values[0])!.toHaveTextContent('en-US') rerender() // Assert const updatedValues = screen.getAllByTestId('selected-value') - expect(updatedValues[0]).toHaveTextContent('zh-Hans') + expect(updatedValues[0])!.toHaveTextContent('zh-Hans') }) it('should update when voice prop changes', () => { @@ -562,13 +634,13 @@ describe('TTSParamsPanel', () => { // Act const { rerender } = render() const values = screen.getAllByTestId('selected-value') - expect(values[1]).toHaveTextContent('alloy') + expect(values[1])!.toHaveTextContent('alloy') rerender() // Assert const updatedValues = screen.getAllByTestId('selected-value') - expect(updatedValues[1]).toHaveTextContent('echo') + expect(updatedValues[1])!.toHaveTextContent('echo') }) it('should update voice list when currentModel changes', () => { @@ -580,7 +652,7 @@ describe('TTSParamsPanel', () => { // Act const { rerender } = render() - expect(screen.getByTestId('select-item-alloy')).toBeInTheDocument() + expect(screen.getByTestId('select-item-alloy'))!.toBeInTheDocument() expect(screen.queryByTestId('select-item-nova')).not.toBeInTheDocument() const newModel = createCurrentModel([ @@ -590,8 +662,9 @@ describe('TTSParamsPanel', () => { rerender() // Assert - expect(screen.getByTestId('select-item-alloy')).toBeInTheDocument() - expect(screen.getByTestId('select-item-nova')).toBeInTheDocument() + // Assert + expect(screen.getByTestId('select-item-alloy'))!.toBeInTheDocument() + expect(screen.getByTestId('select-item-nova'))!.toBeInTheDocument() }) it('should handle currentModel becoming null', () => { @@ -600,10 +673,41 @@ describe('TTSParamsPanel', () => { // Act const { rerender } = render() - expect(screen.getByTestId('select-item-alloy')).toBeInTheDocument() + expect(screen.getByTestId('select-item-alloy'))!.toBeInTheDocument() rerender() + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert // Assert expect(screen.queryByTestId('select-item-alloy')).not.toBeInTheDocument() }) @@ -636,7 +740,7 @@ describe('TTSParamsPanel', () => { // Assert const languageLabel = screen.getByText('appDebug.voice.voiceSettings.language') - expect(languageLabel).toHaveClass('system-sm-semibold') + expect(languageLabel)!.toHaveClass('system-sm-semibold') }) it('should have proper label structure for voice select', () => { @@ -648,7 +752,7 @@ describe('TTSParamsPanel', () => { // Assert const voiceLabel = screen.getByText('appDebug.voice.voiceSettings.voice') - expect(voiceLabel).toHaveClass('system-sm-semibold') + expect(voiceLabel)!.toHaveClass('system-sm-semibold') }) }) }) diff --git a/web/app/components/plugins/plugin-detail-panel/subscription-list/__tests__/index.spec.tsx b/web/app/components/plugins/plugin-detail-panel/subscription-list/__tests__/index.spec.tsx index d41dfaa7d0..19a2d5a9f1 100644 --- a/web/app/components/plugins/plugin-detail-panel/subscription-list/__tests__/index.spec.tsx +++ b/web/app/components/plugins/plugin-detail-panel/subscription-list/__tests__/index.spec.tsx @@ -100,8 +100,8 @@ describe('SubscriptionList', () => { it('should render list view by default', () => { render() - expect(screen.getByText(/pluginTrigger\.subscription\.listNum/)).toBeInTheDocument() - expect(screen.getByText('Subscription One')).toBeInTheDocument() + expect(screen.getByText(/pluginTrigger\.subscription\.listNum/))!.toBeInTheDocument() + expect(screen.getByText('Subscription One'))!.toBeInTheDocument() }) it('should render loading state when subscriptions are loading', () => { @@ -112,7 +112,7 @@ describe('SubscriptionList', () => { render() - expect(screen.getByRole('status')).toBeInTheDocument() + expect(screen.getByRole('status'))!.toBeInTheDocument() expect(screen.queryByText('Subscription One')).not.toBeInTheDocument() }) @@ -121,7 +121,7 @@ describe('SubscriptionList', () => { render() - expect(screen.getByText('Subscription One')).toBeInTheDocument() + expect(screen.getByText('Subscription One'))!.toBeInTheDocument() }) it('should render without list entries when subscriptions are empty', () => { @@ -141,7 +141,7 @@ describe('SubscriptionList', () => { it('should render selector view when mode is selector', () => { render() - expect(screen.getByText('Subscription One')).toBeInTheDocument() + expect(screen.getByText('Subscription One'))!.toBeInTheDocument() }) it('should visually distinguish selected subscription from unselected', () => { @@ -182,7 +182,7 @@ describe('SubscriptionList', () => { fireEvent.click(screen.getByRole('button', { name: 'Subscription One' })) expect(onSelect).toHaveBeenCalledTimes(1) - const [selectedSubscription, callback] = onSelect.mock.calls[0] + const [selectedSubscription, callback] = (onSelect.mock.calls[0] ?? []) as [any, any] expect(selectedSubscription).toMatchObject({ id: 'sub-1', name: 'Subscription One' }) expect(typeof callback).toBe('function') @@ -212,7 +212,7 @@ describe('SubscriptionList', () => { fireEvent.click(deleteButton) expect(onSelect).not.toHaveBeenCalled() - expect(screen.getByText(/pluginTrigger\.subscription\.list\.item\.actions\.deleteConfirm\.title/)).toBeInTheDocument() + expect(screen.getByText(/pluginTrigger\.subscription\.list\.item\.actions\.deleteConfirm\.title/))!.toBeInTheDocument() }) }) @@ -222,7 +222,7 @@ describe('SubscriptionList', () => { render() - expect(await screen.findByText('Something went wrong')).toBeInTheDocument() + expect(await screen.findByText('Something went wrong'))!.toBeInTheDocument() }) }) }) diff --git a/web/app/components/plugins/plugin-detail-panel/subscription-list/__tests__/subscription-card.spec.tsx b/web/app/components/plugins/plugin-detail-panel/subscription-list/__tests__/subscription-card.spec.tsx index 4665c921ca..19edb65dfb 100644 --- a/web/app/components/plugins/plugin-detail-panel/subscription-list/__tests__/subscription-card.spec.tsx +++ b/web/app/components/plugins/plugin-detail-panel/subscription-list/__tests__/subscription-card.spec.tsx @@ -62,14 +62,14 @@ describe('SubscriptionCard', () => { it('should render subscription name and endpoint', () => { render() - expect(screen.getByText('Subscription One')).toBeInTheDocument() - expect(screen.getByText('https://example.com')).toBeInTheDocument() + expect(screen.getByText('Subscription One'))!.toBeInTheDocument() + expect(screen.getByText('https://example.com'))!.toBeInTheDocument() }) it('should render used-by text when workflows are present', () => { render() - expect(screen.getByText(/pluginTrigger\.subscription\.list\.item\.usedByNum/)).toBeInTheDocument() + expect(screen.getByText(/pluginTrigger\.subscription\.list\.item\.usedByNum/))!.toBeInTheDocument() }) it('should open delete confirmation when delete action is clicked', () => { @@ -79,15 +79,15 @@ describe('SubscriptionCard', () => { expect(deleteButton).toBeTruthy() fireEvent.click(deleteButton) - expect(screen.getByText(/pluginTrigger\.subscription\.list\.item\.actions\.deleteConfirm\.title/)).toBeInTheDocument() + expect(screen.getByText(/pluginTrigger\.subscription\.list\.item\.actions\.deleteConfirm\.title/))!.toBeInTheDocument() }) it('should open edit modal when edit action is clicked', () => { const { container } = render() const editButton = container.querySelectorAll('button')[0] - fireEvent.click(editButton) + fireEvent.click(editButton!) - expect(screen.getByText(/pluginTrigger\.subscription\.list\.item\.actions\.edit\.title/)).toBeInTheDocument() + expect(screen.getByText(/pluginTrigger\.subscription\.list\.item\.actions\.edit\.title/))!.toBeInTheDocument() }) }) diff --git a/web/app/components/plugins/plugin-detail-panel/subscription-list/create/__tests__/index.spec.tsx b/web/app/components/plugins/plugin-detail-panel/subscription-list/create/__tests__/index.spec.tsx index a36c108160..0714fbf554 100644 --- a/web/app/components/plugins/plugin-detail-panel/subscription-list/create/__tests__/index.spec.tsx +++ b/web/app/components/plugins/plugin-detail-panel/subscription-list/create/__tests__/index.spec.tsx @@ -246,7 +246,8 @@ describe('CreateSubscriptionButton', () => { const { container } = render() // Assert - expect(container).toBeEmptyDOMElement() + // Assert + expect(container)!.toBeEmptyDOMElement() }) it('should render without crashing when supportedMethods is provided', () => { @@ -260,6 +261,37 @@ describe('CreateSubscriptionButton', () => { // Act const { container } = render() + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert // Assert expect(container).not.toBeEmptyDOMElement() }) @@ -276,7 +308,8 @@ describe('CreateSubscriptionButton', () => { render() // Assert - expect(screen.getByRole('button')).toBeInTheDocument() + // Assert + expect(screen.getByRole('button'))!.toBeInTheDocument() }) it('should render icon button when buttonType is ICON_BUTTON', () => { @@ -292,7 +325,7 @@ describe('CreateSubscriptionButton', () => { // Assert const actionButton = screen.getByTestId('custom-trigger') - expect(actionButton).toBeInTheDocument() + expect(actionButton)!.toBeInTheDocument() }) }) @@ -309,7 +342,8 @@ describe('CreateSubscriptionButton', () => { render() // Assert - expect(screen.getByRole('button')).toBeInTheDocument() + // Assert + expect(screen.getByRole('button'))!.toBeInTheDocument() }) it('should apply shape prop correctly', () => { @@ -324,7 +358,8 @@ describe('CreateSubscriptionButton', () => { render() // Assert - expect(screen.getByTestId('custom-trigger')).toBeInTheDocument() + // Assert + expect(screen.getByTestId('custom-trigger'))!.toBeInTheDocument() }) }) @@ -348,8 +383,8 @@ describe('CreateSubscriptionButton', () => { // Assert await waitFor(() => { - expect(screen.getByTestId('common-create-modal')).toBeInTheDocument() - expect(screen.getByTestId('common-create-modal')).toHaveAttribute('data-create-type', SupportedCreationMethods.MANUAL) + expect(screen.getByTestId('common-create-modal'))!.toBeInTheDocument() + expect(screen.getByTestId('common-create-modal'))!.toHaveAttribute('data-create-type', SupportedCreationMethods.MANUAL) }) }) @@ -371,7 +406,7 @@ describe('CreateSubscriptionButton', () => { fireEvent.click(manualOption) await waitFor(() => { - expect(screen.getByTestId('common-create-modal')).toBeInTheDocument() + expect(screen.getByTestId('common-create-modal'))!.toBeInTheDocument() }) // Close modal @@ -403,7 +438,7 @@ describe('CreateSubscriptionButton', () => { // Assert await waitFor(() => { - expect(screen.getByTestId('oauth-client-modal')).toBeInTheDocument() + expect(screen.getByTestId('oauth-client-modal'))!.toBeInTheDocument() }) }) @@ -432,7 +467,7 @@ describe('CreateSubscriptionButton', () => { fireEvent.click(oauthOption) await waitFor(() => { - expect(screen.getByTestId('oauth-client-modal')).toBeInTheDocument() + expect(screen.getByTestId('oauth-client-modal'))!.toBeInTheDocument() }) // Close modal @@ -463,7 +498,7 @@ describe('CreateSubscriptionButton', () => { // Assert - OAuth mode renders with settings button, use getAllByRole const buttons = screen.getAllByRole('button') - expect(buttons[0]).toHaveTextContent('pluginTrigger.subscription.createButton.oauth') + expect(buttons[0])!.toHaveTextContent('pluginTrigger.subscription.createButton.oauth') }) it('should display correct button text for APIKEY method', () => { @@ -480,7 +515,8 @@ describe('CreateSubscriptionButton', () => { render() // Assert - expect(screen.getByRole('button')).toHaveTextContent('pluginTrigger.subscription.createButton.apiKey') + // Assert + expect(screen.getByRole('button'))!.toHaveTextContent('pluginTrigger.subscription.createButton.apiKey') }) it('should display correct button text for MANUAL method', () => { @@ -497,7 +533,8 @@ describe('CreateSubscriptionButton', () => { render() // Assert - expect(screen.getByRole('button')).toHaveTextContent('pluginTrigger.subscription.createButton.manual') + // Assert + expect(screen.getByRole('button'))!.toHaveTextContent('pluginTrigger.subscription.createButton.manual') }) it('should display default button text when multiple methods are supported', () => { @@ -514,7 +551,8 @@ describe('CreateSubscriptionButton', () => { render() // Assert - expect(screen.getByRole('button')).toHaveTextContent('pluginTrigger.subscription.empty.button') + // Assert + expect(screen.getByRole('button'))!.toHaveTextContent('pluginTrigger.subscription.empty.button') }) }) @@ -535,7 +573,7 @@ describe('CreateSubscriptionButton', () => { // Assert const customSelect = screen.getByTestId('custom-select') - expect(customSelect).toHaveAttribute('data-options-count', '1') + expect(customSelect)!.toHaveAttribute('data-options-count', '1') }) it('should show all options when all methods are supported', () => { @@ -558,7 +596,7 @@ describe('CreateSubscriptionButton', () => { // Assert const customSelect = screen.getByTestId('custom-select') - expect(customSelect).toHaveAttribute('data-options-count', '3') + expect(customSelect)!.toHaveAttribute('data-options-count', '3') }) it('should show custom badge when OAuth custom is enabled and configured', () => { @@ -581,7 +619,7 @@ describe('CreateSubscriptionButton', () => { // Assert - Custom badge should appear in the button const buttons = screen.getAllByRole('button') - expect(buttons[0]).toHaveTextContent('plugin.auth.custom') + expect(buttons[0])!.toHaveTextContent('plugin.auth.custom') }) it('should not show custom badge when OAuth custom is not configured', () => { @@ -624,7 +662,7 @@ describe('CreateSubscriptionButton', () => { // Assert const customSelect = screen.getByTestId('custom-select') - expect(customSelect).toHaveAttribute('data-value', DEFAULT_METHOD) + expect(customSelect)!.toHaveAttribute('data-value', DEFAULT_METHOD) }) it('should set methodType to single method when only one supported', () => { @@ -642,7 +680,7 @@ describe('CreateSubscriptionButton', () => { // Assert const customSelect = screen.getByTestId('custom-select') - expect(customSelect).toHaveAttribute('data-value', SupportedCreationMethods.MANUAL) + expect(customSelect)!.toHaveAttribute('data-value', SupportedCreationMethods.MANUAL) }) }) @@ -669,6 +707,37 @@ describe('CreateSubscriptionButton', () => { const button = screen.getByRole('button') fireEvent.click(button) + // Assert - modal should not open + // Assert - modal should not open + // Assert - modal should not open + // Assert - modal should not open + // Assert - modal should not open + // Assert - modal should not open + // Assert - modal should not open + // Assert - modal should not open + // Assert - modal should not open + // Assert - modal should not open + // Assert - modal should not open + // Assert - modal should not open + // Assert - modal should not open + // Assert - modal should not open + // Assert - modal should not open + // Assert - modal should not open + // Assert - modal should not open + // Assert - modal should not open + // Assert - modal should not open + // Assert - modal should not open + // Assert - modal should not open + // Assert - modal should not open + // Assert - modal should not open + // Assert - modal should not open + // Assert - modal should not open + // Assert - modal should not open + // Assert - modal should not open + // Assert - modal should not open + // Assert - modal should not open + // Assert - modal should not open + // Assert - modal should not open // Assert - modal should not open expect(screen.queryByTestId('common-create-modal')).not.toBeInTheDocument() }) @@ -689,7 +758,8 @@ describe('CreateSubscriptionButton', () => { fireEvent.click(button) // Assert - modal should open - expect(screen.getByTestId('common-create-modal')).toBeInTheDocument() + // Assert - modal should open + expect(screen.getByTestId('common-create-modal'))!.toBeInTheDocument() }) it('should not call onChooseCreateType for DEFAULT_METHOD or single OAuth', () => { @@ -707,8 +777,70 @@ describe('CreateSubscriptionButton', () => { render() // For OAuth mode, there are multiple buttons; get the primary button (first one) const buttons = screen.getAllByRole('button') - fireEvent.click(buttons[0]) + fireEvent.click(buttons[0]!) + // Assert - For single OAuth, should not directly create but wait for dropdown + // The modal should not immediately open + // Assert - For single OAuth, should not directly create but wait for dropdown + // The modal should not immediately open + // Assert - For single OAuth, should not directly create but wait for dropdown + // The modal should not immediately open + // Assert - For single OAuth, should not directly create but wait for dropdown + // The modal should not immediately open + // Assert - For single OAuth, should not directly create but wait for dropdown + // The modal should not immediately open + // Assert - For single OAuth, should not directly create but wait for dropdown + // The modal should not immediately open + // Assert - For single OAuth, should not directly create but wait for dropdown + // The modal should not immediately open + // Assert - For single OAuth, should not directly create but wait for dropdown + // The modal should not immediately open + // Assert - For single OAuth, should not directly create but wait for dropdown + // The modal should not immediately open + // Assert - For single OAuth, should not directly create but wait for dropdown + // The modal should not immediately open + // Assert - For single OAuth, should not directly create but wait for dropdown + // The modal should not immediately open + // Assert - For single OAuth, should not directly create but wait for dropdown + // The modal should not immediately open + // Assert - For single OAuth, should not directly create but wait for dropdown + // The modal should not immediately open + // Assert - For single OAuth, should not directly create but wait for dropdown + // The modal should not immediately open + // Assert - For single OAuth, should not directly create but wait for dropdown + // The modal should not immediately open + // Assert - For single OAuth, should not directly create but wait for dropdown + // The modal should not immediately open + // Assert - For single OAuth, should not directly create but wait for dropdown + // The modal should not immediately open + // Assert - For single OAuth, should not directly create but wait for dropdown + // The modal should not immediately open + // Assert - For single OAuth, should not directly create but wait for dropdown + // The modal should not immediately open + // Assert - For single OAuth, should not directly create but wait for dropdown + // The modal should not immediately open + // Assert - For single OAuth, should not directly create but wait for dropdown + // The modal should not immediately open + // Assert - For single OAuth, should not directly create but wait for dropdown + // The modal should not immediately open + // Assert - For single OAuth, should not directly create but wait for dropdown + // The modal should not immediately open + // Assert - For single OAuth, should not directly create but wait for dropdown + // The modal should not immediately open + // Assert - For single OAuth, should not directly create but wait for dropdown + // The modal should not immediately open + // Assert - For single OAuth, should not directly create but wait for dropdown + // The modal should not immediately open + // Assert - For single OAuth, should not directly create but wait for dropdown + // The modal should not immediately open + // Assert - For single OAuth, should not directly create but wait for dropdown + // The modal should not immediately open + // Assert - For single OAuth, should not directly create but wait for dropdown + // The modal should not immediately open + // Assert - For single OAuth, should not directly create but wait for dropdown + // The modal should not immediately open + // Assert - For single OAuth, should not directly create but wait for dropdown + // The modal should not immediately open // Assert - For single OAuth, should not directly create but wait for dropdown // The modal should not immediately open expect(screen.queryByTestId('common-create-modal')).not.toBeInTheDocument() @@ -736,7 +868,7 @@ describe('CreateSubscriptionButton', () => { // Assert await waitFor(() => { - expect(screen.getByTestId('oauth-client-modal')).toBeInTheDocument() + expect(screen.getByTestId('oauth-client-modal'))!.toBeInTheDocument() }) }) @@ -783,8 +915,8 @@ describe('CreateSubscriptionButton', () => { // Assert await waitFor(() => { - expect(screen.getByTestId('common-create-modal')).toBeInTheDocument() - expect(screen.getByTestId('common-create-modal')).toHaveAttribute('data-create-type', SupportedCreationMethods.APIKEY) + expect(screen.getByTestId('common-create-modal'))!.toBeInTheDocument() + expect(screen.getByTestId('common-create-modal'))!.toHaveAttribute('data-create-type', SupportedCreationMethods.APIKEY) }) }) @@ -807,8 +939,8 @@ describe('CreateSubscriptionButton', () => { // Assert await waitFor(() => { - expect(screen.getByTestId('common-create-modal')).toBeInTheDocument() - expect(screen.getByTestId('common-create-modal')).toHaveAttribute('data-create-type', SupportedCreationMethods.MANUAL) + expect(screen.getByTestId('common-create-modal'))!.toBeInTheDocument() + expect(screen.getByTestId('common-create-modal'))!.toHaveAttribute('data-create-type', SupportedCreationMethods.MANUAL) }) }) }) @@ -831,16 +963,17 @@ describe('CreateSubscriptionButton', () => { // Find the settings div inside the button (p-2 class) const buttons = screen.getAllByRole('button') const primaryButton = buttons[0] - const settingsDiv = primaryButton.querySelector('.p-2') + const settingsDiv = primaryButton!.querySelector('.p-2') // Assert that settings div exists and click it - expect(settingsDiv).toBeInTheDocument() + // Assert that settings div exists and click it + expect(settingsDiv)!.toBeInTheDocument() if (settingsDiv) { fireEvent.click(settingsDiv) // Assert await waitFor(() => { - expect(screen.getByTestId('oauth-client-modal')).toBeInTheDocument() + expect(screen.getByTestId('oauth-client-modal'))!.toBeInTheDocument() }) } }) @@ -860,7 +993,8 @@ describe('CreateSubscriptionButton', () => { render() // Assert - Component renders, which means hook was called - expect(screen.getByTestId('custom-select')).toBeInTheDocument() + // Assert - Component renders, which means hook was called + expect(screen.getByTestId('custom-select'))!.toBeInTheDocument() }) it('should handle OAuth initiation success', async () => { @@ -908,8 +1042,8 @@ describe('CreateSubscriptionButton', () => { // Assert - modal should open with OAuth type and builder await waitFor(() => { - expect(screen.getByTestId('common-create-modal')).toBeInTheDocument() - expect(screen.getByTestId('common-create-modal')).toHaveAttribute('data-has-builder', 'true') + expect(screen.getByTestId('common-create-modal'))!.toBeInTheDocument() + expect(screen.getByTestId('common-create-modal'))!.toHaveAttribute('data-has-builder', 'true') }) }) @@ -956,6 +1090,37 @@ describe('CreateSubscriptionButton', () => { // Act const { container } = render() + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert // Assert expect(container).not.toBeEmptyDOMElement() }) @@ -972,7 +1137,8 @@ describe('CreateSubscriptionButton', () => { render() // Assert - component should still render - expect(screen.getByTestId('custom-select')).toBeInTheDocument() + // Assert - component should still render + expect(screen.getByTestId('custom-select'))!.toBeInTheDocument() }) it('should handle empty oauthConfig gracefully', () => { @@ -990,7 +1156,8 @@ describe('CreateSubscriptionButton', () => { render() // Assert - expect(screen.getByTestId('custom-select')).toBeInTheDocument() + // Assert + expect(screen.getByTestId('custom-select'))!.toBeInTheDocument() }) it('should show max count tooltip when subscriptions reach limit', () => { @@ -1010,7 +1177,8 @@ describe('CreateSubscriptionButton', () => { render() // Assert - ActionButton should be in disabled state - expect(screen.getByTestId('custom-trigger')).toBeInTheDocument() + // Assert - ActionButton should be in disabled state + expect(screen.getByTestId('custom-trigger'))!.toBeInTheDocument() }) it('should handle showOAuthCreateModal callback from OAuthClientSettingsModal', async () => { @@ -1032,7 +1200,7 @@ describe('CreateSubscriptionButton', () => { fireEvent.click(oauthOption) await waitFor(() => { - expect(screen.getByTestId('oauth-client-modal')).toBeInTheDocument() + expect(screen.getByTestId('oauth-client-modal'))!.toBeInTheDocument() }) // Click show create modal button @@ -1040,9 +1208,9 @@ describe('CreateSubscriptionButton', () => { // Assert - CommonCreateModal should be shown with OAuth type and builder await waitFor(() => { - expect(screen.getByTestId('common-create-modal')).toBeInTheDocument() - expect(screen.getByTestId('common-create-modal')).toHaveAttribute('data-create-type', SupportedCreationMethods.OAUTH) - expect(screen.getByTestId('common-create-modal')).toHaveAttribute('data-has-builder', 'true') + expect(screen.getByTestId('common-create-modal'))!.toBeInTheDocument() + expect(screen.getByTestId('common-create-modal'))!.toHaveAttribute('data-create-type', SupportedCreationMethods.OAUTH) + expect(screen.getByTestId('common-create-modal'))!.toHaveAttribute('data-has-builder', 'true') }) }) }) @@ -1066,8 +1234,8 @@ describe('CreateSubscriptionButton', () => { // Assert - settings icon should be present in button, OAuth mode has multiple buttons const buttons = screen.getAllByRole('button') const primaryButton = buttons[0] - const settingsDiv = primaryButton.querySelector('.p-2') - expect(settingsDiv).toBeInTheDocument() + const settingsDiv = primaryButton!.querySelector('.p-2') + expect(settingsDiv)!.toBeInTheDocument() }) it('should not render settings icon for non-OAuth methods', () => { @@ -1106,7 +1274,8 @@ describe('CreateSubscriptionButton', () => { render() // Assert - icon button should exist - expect(screen.getByTestId('custom-trigger')).toBeInTheDocument() + // Assert - icon button should exist + expect(screen.getByTestId('custom-trigger'))!.toBeInTheDocument() }) it('should apply circle shape class when shape is circle', () => { @@ -1123,7 +1292,8 @@ describe('CreateSubscriptionButton', () => { render() // Assert - expect(screen.getByTestId('custom-trigger')).toBeInTheDocument() + // Assert + expect(screen.getByTestId('custom-trigger'))!.toBeInTheDocument() }) }) @@ -1181,7 +1351,7 @@ describe('CreateSubscriptionButton', () => { // Assert - for single non-OAuth, dropdown should be disabled (open = false) const customSelect = screen.getByTestId('custom-select') - expect(customSelect).toHaveAttribute('data-container-open', 'false') + expect(customSelect)!.toHaveAttribute('data-container-open', 'false') }) }) @@ -1202,7 +1372,7 @@ describe('CreateSubscriptionButton', () => { // Assert const button = screen.getByRole('button') - expect(button).toHaveClass('w-full') + expect(button)!.toHaveClass('w-full') }) it('should render icon button with float-right class', () => { @@ -1219,7 +1389,8 @@ describe('CreateSubscriptionButton', () => { render() // Assert - expect(screen.getByTestId('custom-trigger')).toBeInTheDocument() + // Assert + expect(screen.getByTestId('custom-trigger'))!.toBeInTheDocument() }) }) @@ -1265,7 +1436,7 @@ describe('CreateSubscriptionButton', () => { // Assert await waitFor(() => { const modal = screen.getByTestId('common-create-modal') - expect(modal).toHaveAttribute('data-create-type', SupportedCreationMethods.MANUAL) + expect(modal)!.toHaveAttribute('data-create-type', SupportedCreationMethods.MANUAL) }) }) @@ -1289,7 +1460,7 @@ describe('CreateSubscriptionButton', () => { // Assert await waitFor(() => { const modal = screen.getByTestId('common-create-modal') - expect(modal).toHaveAttribute('data-create-type', SupportedCreationMethods.APIKEY) + expect(modal)!.toHaveAttribute('data-create-type', SupportedCreationMethods.APIKEY) }) }) @@ -1339,7 +1510,7 @@ describe('CreateSubscriptionButton', () => { // Assert await waitFor(() => { const modal = screen.getByTestId('common-create-modal') - expect(modal).toHaveAttribute('data-has-builder', 'true') + expect(modal)!.toHaveAttribute('data-has-builder', 'true') }) }) }) @@ -1368,7 +1539,7 @@ describe('CreateSubscriptionButton', () => { // Assert await waitFor(() => { const modal = screen.getByTestId('oauth-client-modal') - expect(modal).toHaveAttribute('data-has-config', 'true') + expect(modal)!.toHaveAttribute('data-has-config', 'true') }) }) @@ -1397,7 +1568,7 @@ describe('CreateSubscriptionButton', () => { fireEvent.click(oauthOption) await waitFor(() => { - expect(screen.getByTestId('oauth-client-modal')).toBeInTheDocument() + expect(screen.getByTestId('oauth-client-modal'))!.toBeInTheDocument() }) // Close modal @@ -1428,7 +1599,7 @@ describe('CreateSubscriptionButton', () => { fireEvent.click(oauthOption) await waitFor(() => { - expect(screen.getByTestId('oauth-client-modal')).toBeInTheDocument() + expect(screen.getByTestId('oauth-client-modal'))!.toBeInTheDocument() }) // Click showOAuthCreateModal button @@ -1436,9 +1607,9 @@ describe('CreateSubscriptionButton', () => { // Assert - CommonCreateModal should appear with OAuth type and builder await waitFor(() => { - expect(screen.getByTestId('common-create-modal')).toBeInTheDocument() - expect(screen.getByTestId('common-create-modal')).toHaveAttribute('data-create-type', SupportedCreationMethods.OAUTH) - expect(screen.getByTestId('common-create-modal')).toHaveAttribute('data-has-builder', 'true') + expect(screen.getByTestId('common-create-modal'))!.toBeInTheDocument() + expect(screen.getByTestId('common-create-modal'))!.toHaveAttribute('data-create-type', SupportedCreationMethods.OAUTH) + expect(screen.getByTestId('common-create-modal'))!.toHaveAttribute('data-has-builder', 'true') }) }) }) @@ -1513,7 +1684,9 @@ describe('CreateSubscriptionButton', () => { // The methodType will be DEFAULT_METHOD since multiple methods // This verifies the render doesn't crash with multiple methods - expect(screen.getByTestId('custom-select')).toHaveAttribute('data-value', 'default') + // The methodType will be DEFAULT_METHOD since multiple methods + // This verifies the render doesn't crash with multiple methods + expect(screen.getByTestId('custom-select'))!.toHaveAttribute('data-value', 'default') }) }) @@ -1534,7 +1707,8 @@ describe('CreateSubscriptionButton', () => { render() // Assert - tooltip should be enabled (disabled prop = false for single method) - expect(screen.getByTestId('custom-trigger')).toBeInTheDocument() + // Assert - tooltip should be enabled (disabled prop = false for single method) + expect(screen.getByTestId('custom-trigger'))!.toBeInTheDocument() }) it('should disable tooltip when multiple methods and not at max count', () => { @@ -1552,7 +1726,8 @@ describe('CreateSubscriptionButton', () => { render() // Assert - tooltip should be disabled (neither single method nor at max) - expect(screen.getByTestId('custom-trigger')).toBeInTheDocument() + // Assert - tooltip should be disabled (neither single method nor at max) + expect(screen.getByTestId('custom-trigger'))!.toBeInTheDocument() }) }) @@ -1574,7 +1749,8 @@ describe('CreateSubscriptionButton', () => { render() // Assert - component renders with max subscriptions - expect(screen.getByTestId('custom-trigger')).toBeInTheDocument() + // Assert - component renders with max subscriptions + expect(screen.getByTestId('custom-trigger'))!.toBeInTheDocument() }) it('should show method description when not at max', () => { @@ -1592,7 +1768,8 @@ describe('CreateSubscriptionButton', () => { render() // Assert - component renders without max subscriptions - expect(screen.getByTestId('custom-trigger')).toBeInTheDocument() + // Assert - component renders without max subscriptions + expect(screen.getByTestId('custom-trigger'))!.toBeInTheDocument() }) }) @@ -1613,7 +1790,8 @@ describe('CreateSubscriptionButton', () => { const { container } = render() // Assert - should render null when supported methods fallback to empty - expect(container).toBeEmptyDOMElement() + // Assert - should render null when supported methods fallback to empty + expect(container)!.toBeEmptyDOMElement() }) it('should handle providerInfo with null supported_creation_methods', () => { @@ -1627,7 +1805,8 @@ describe('CreateSubscriptionButton', () => { const { container } = render() // Assert - should render null - expect(container).toBeEmptyDOMElement() + // Assert - should render null + expect(container)!.toBeEmptyDOMElement() }) }) @@ -1648,7 +1827,7 @@ describe('CreateSubscriptionButton', () => { // Assert const customSelect = screen.getByTestId('custom-select') - expect(customSelect).toHaveAttribute('data-value', SupportedCreationMethods.APIKEY) + expect(customSelect)!.toHaveAttribute('data-value', SupportedCreationMethods.APIKEY) }) }) }) diff --git a/web/app/components/plugins/plugin-detail-panel/subscription-list/create/hooks/__tests__/use-oauth-client-state.spec.ts b/web/app/components/plugins/plugin-detail-panel/subscription-list/create/hooks/__tests__/use-oauth-client-state.spec.ts index 68864b0b80..cebfc947e7 100644 --- a/web/app/components/plugins/plugin-detail-panel/subscription-list/create/hooks/__tests__/use-oauth-client-state.spec.ts +++ b/web/app/components/plugins/plugin-detail-panel/subscription-list/create/hooks/__tests__/use-oauth-client-state.spec.ts @@ -194,8 +194,8 @@ describe('useOAuthClientState', () => { })) expect(result.current.oauthClientSchema).toHaveLength(2) - expect(result.current.oauthClientSchema[0].default).toBe('my-client-id') - expect(result.current.oauthClientSchema[1].default).toBe('my-secret') + expect(result.current.oauthClientSchema[0]!.default).toBe('my-client-id') + expect(result.current.oauthClientSchema[1]!.default).toBe('my-secret') }) it('should return empty array when oauth_client_schema is empty', () => { @@ -239,9 +239,9 @@ describe('useOAuthClientState', () => { })) // client_id should be overridden - expect(result.current.oauthClientSchema[0].default).toBe('only-client-id') + expect(result.current.oauthClientSchema[0]!.default).toBe('only-client-id') // extra_field should keep original default since key not in params - expect(result.current.oauthClientSchema[1].default).toBe('extra-default') + expect(result.current.oauthClientSchema[1]!.default).toBe('extra-default') }) }) diff --git a/web/app/components/plugins/plugin-detail-panel/subscription-list/create/hooks/use-common-modal-state.ts b/web/app/components/plugins/plugin-detail-panel/subscription-list/create/hooks/use-common-modal-state.ts index e55f9525fe..29613c6f4f 100644 --- a/web/app/components/plugins/plugin-detail-panel/subscription-list/create/hooks/use-common-modal-state.ts +++ b/web/app/components/plugins/plugin-detail-panel/subscription-list/create/hooks/use-common-modal-state.ts @@ -202,7 +202,7 @@ export const useCommonModalState = ({ if (!apiKeyCredentialsSchema.length) return apiKeyCredentialsFormRef.current?.setFields([{ - name: apiKeyCredentialsSchema[0].name, + name: apiKeyCredentialsSchema[0]!.name, errors: [], }]) }, [apiKeyCredentialsSchema]) diff --git a/web/app/components/plugins/plugin-detail-panel/subscription-list/create/index.tsx b/web/app/components/plugins/plugin-detail-panel/subscription-list/create/index.tsx index 73b28bd60e..4861f30934 100644 --- a/web/app/components/plugins/plugin-detail-panel/subscription-list/create/index.tsx +++ b/web/app/components/plugins/plugin-detail-panel/subscription-list/create/index.tsx @@ -137,7 +137,7 @@ export const CreateSubscriptionButton = ({ buttonType = CreateButtonType.FULL_BU e.stopPropagation() e.preventDefault() - onChooseCreateType(methodType) + onChooseCreateType(methodType!) } if (!supportedMethods.length) @@ -172,7 +172,7 @@ export const CreateSubscriptionButton = ({ buttonType = CreateButtonType.FULL_BU >
- {buttonTextMap[methodType]} + {buttonTextMap[methodType!]} {methodType === SupportedCreationMethods.OAUTH && oauthConfig?.custom_enabled && oauthConfig?.custom_configured && ( = MAX_COUNT ? t('subscription.maxCount', { ns: 'pluginTrigger', num: MAX_COUNT }) : t(`subscription.addType.options.${methodType.toLowerCase() as Lowercase}.description`, { ns: 'pluginTrigger' })} + popupContent={subscriptionCount >= MAX_COUNT ? t('subscription.maxCount', { ns: 'pluginTrigger', num: MAX_COUNT }) : t(`subscription.addType.options.${methodType!.toLowerCase() as Lowercase}.description`, { ns: 'pluginTrigger' })} disabled={!(supportedMethods?.length === 1 || subscriptionCount >= MAX_COUNT)} > { return produce(value, (draft) => { - draft[variable].value = { + draft[variable]!.value = { type: newType, value: newType === VarKindType.variable ? '' : defaultValue, } @@ -126,7 +126,7 @@ export const updateReasoningValue = ( newValue: unknown, ) => { return produce(value, (draft) => { - draft[variable].value = { + draft[variable]!.value = { type: getVarKindType(type), value: newValue, } @@ -139,8 +139,8 @@ export const mergeReasoningValue = ( newValue: Record, ) => { return produce(value, (draft) => { - const currentValue = draft[variable].value as Record | undefined - draft[variable].value = { + const currentValue = draft[variable]!.value as Record | undefined + draft[variable]!.value = { ...currentValue, ...newValue, } @@ -153,7 +153,7 @@ export const updateVariableSelectorValue = ( newValue: ValueSelector | string, ) => { return produce(value, (draft) => { - draft[variable].value = { + draft[variable]!.value = { type: VarKindType.variable, value: newValue, } diff --git a/web/app/components/plugins/plugin-detail-panel/tool-selector/components/reasoning-config-form.tsx b/web/app/components/plugins/plugin-detail-panel/tool-selector/components/reasoning-config-form.tsx index 8a3642d043..4e6be7d81c 100644 --- a/web/app/components/plugins/plugin-detail-panel/tool-selector/components/reasoning-config-form.tsx +++ b/web/app/components/plugins/plugin-detail-panel/tool-selector/components/reasoning-config-form.tsx @@ -137,7 +137,7 @@ const ReasoningConfigForm: React.FC = ({ asChild={false} /> )) - const varInput = value[variable].value + const varInput = value[variable]!.value const { isString, isNumber, @@ -179,7 +179,7 @@ const ReasoningConfigForm: React.FC = ({ >
showSchema(input_schema as SchemaRoot, fieldTitle)} + onClick={() => showSchema(input_schema as SchemaRoot, fieldTitle!)} >
diff --git a/web/app/components/plugins/plugin-detail-panel/tool-selector/hooks/__tests__/use-tool-selector-state.spec.ts b/web/app/components/plugins/plugin-detail-panel/tool-selector/hooks/__tests__/use-tool-selector-state.spec.ts index 5af624649c..dff369c969 100644 --- a/web/app/components/plugins/plugin-detail-panel/tool-selector/hooks/__tests__/use-tool-selector-state.spec.ts +++ b/web/app/components/plugins/plugin-detail-panel/tool-selector/hooks/__tests__/use-tool-selector-state.spec.ts @@ -112,11 +112,11 @@ describe('useToolSelectorState', () => { // param2 has form='form' (not 'llm'), so it goes to settings expect(result.current.currentToolSettings).toHaveLength(1) - expect(result.current.currentToolSettings[0].name).toBe('param2') + expect(result.current.currentToolSettings[0]!.name).toBe('param2') // param1 has form='llm', so it goes to params expect(result.current.currentToolParams).toHaveLength(1) - expect(result.current.currentToolParams[0].name).toBe('param1') + expect(result.current.currentToolParams[0]!.name).toBe('param1') }) it('should show tab slider when both settings and params exist', () => { diff --git a/web/app/components/plugins/plugin-detail-panel/trigger/__tests__/event-detail-drawer.spec.tsx b/web/app/components/plugins/plugin-detail-panel/trigger/__tests__/event-detail-drawer.spec.tsx index a04aba3e57..77b77c0302 100644 --- a/web/app/components/plugins/plugin-detail-panel/trigger/__tests__/event-detail-drawer.spec.tsx +++ b/web/app/components/plugins/plugin-detail-panel/trigger/__tests__/event-detail-drawer.spec.tsx @@ -91,45 +91,45 @@ describe('EventDetailDrawer', () => { it('should render drawer', () => { render() - expect(screen.getByRole('dialog')).toBeInTheDocument() + expect(screen.getByRole('dialog'))!.toBeInTheDocument() }) it('should render event title', () => { render() - expect(screen.getByText('Test Event')).toBeInTheDocument() + expect(screen.getByText('Test Event'))!.toBeInTheDocument() }) it('should render event description', () => { render() - expect(screen.getByTestId('description')).toHaveTextContent('Test event description') + expect(screen.getByTestId('description'))!.toHaveTextContent('Test event description') }) it('should render org info', () => { render() - expect(screen.getByTestId('org-info')).toBeInTheDocument() + expect(screen.getByTestId('org-info'))!.toBeInTheDocument() }) it('should render parameters section', () => { render() - expect(screen.getByText('tools.setBuiltInTools.parameters')).toBeInTheDocument() - expect(screen.getByText('Parameter 1')).toBeInTheDocument() + expect(screen.getByText('tools.setBuiltInTools.parameters'))!.toBeInTheDocument() + expect(screen.getByText('Parameter 1'))!.toBeInTheDocument() }) it('should render output section', () => { render() - expect(screen.getByText('pluginTrigger.events.output')).toBeInTheDocument() - expect(screen.getByTestId('output-field')).toHaveTextContent('result') + expect(screen.getByText('pluginTrigger.events.output'))!.toBeInTheDocument() + expect(screen.getByTestId('output-field'))!.toHaveTextContent('result') }) it('should render back button', () => { render() - expect(screen.getByText('plugin.detailPanel.operation.back')).toBeInTheDocument() + expect(screen.getByText('plugin.detailPanel.operation.back'))!.toBeInTheDocument() }) }) @@ -159,14 +159,14 @@ describe('EventDetailDrawer', () => { const eventWithNoParams = { ...mockEventInfo, parameters: [] } render() - expect(screen.getByText('pluginTrigger.events.item.noParameters')).toBeInTheDocument() + expect(screen.getByText('pluginTrigger.events.item.noParameters'))!.toBeInTheDocument() }) it('should handle no output schema', () => { const eventWithNoOutput = { ...mockEventInfo, output_schema: {} } render() - expect(screen.getByText('pluginTrigger.events.output')).toBeInTheDocument() + expect(screen.getByText('pluginTrigger.events.output'))!.toBeInTheDocument() expect(screen.queryByTestId('output-field')).not.toBeInTheDocument() }) }) @@ -175,41 +175,41 @@ describe('EventDetailDrawer', () => { it('should display correct type for number-input', () => { const eventWithNumber = { ...mockEventInfo, - parameters: [{ ...mockEventInfo.parameters[0], type: 'number-input' }], + parameters: [{ ...mockEventInfo.parameters[0]!, type: 'number-input' }], } render() - expect(screen.getByText('tools.setBuiltInTools.number')).toBeInTheDocument() + expect(screen.getByText('tools.setBuiltInTools.number'))!.toBeInTheDocument() }) it('should display correct type for checkbox', () => { const eventWithCheckbox = { ...mockEventInfo, - parameters: [{ ...mockEventInfo.parameters[0], type: 'checkbox' }], + parameters: [{ ...mockEventInfo.parameters[0]!, type: 'checkbox' }], } render() - expect(screen.getByText('boolean')).toBeInTheDocument() + expect(screen.getByText('boolean'))!.toBeInTheDocument() }) it('should display correct type for file', () => { const eventWithFile = { ...mockEventInfo, - parameters: [{ ...mockEventInfo.parameters[0], type: 'file' }], + parameters: [{ ...mockEventInfo.parameters[0]!, type: 'file' }], } render() - expect(screen.getByText('tools.setBuiltInTools.file')).toBeInTheDocument() + expect(screen.getByText('tools.setBuiltInTools.file'))!.toBeInTheDocument() }) it('should display original type for unknown types', () => { const eventWithUnknown = { ...mockEventInfo, - parameters: [{ ...mockEventInfo.parameters[0], type: 'custom-type' }], + parameters: [{ ...mockEventInfo.parameters[0]!, type: 'custom-type' }], } render() - expect(screen.getByText('custom-type')).toBeInTheDocument() + expect(screen.getByText('custom-type'))!.toBeInTheDocument() }) }) @@ -226,7 +226,7 @@ describe('EventDetailDrawer', () => { } render() - expect(screen.getByText('pluginTrigger.events.output')).toBeInTheDocument() + expect(screen.getByText('pluginTrigger.events.output'))!.toBeInTheDocument() }) it('should handle nested properties in output schema', () => { @@ -245,7 +245,7 @@ describe('EventDetailDrawer', () => { } render() - expect(screen.getByText('pluginTrigger.events.output')).toBeInTheDocument() + expect(screen.getByText('pluginTrigger.events.output'))!.toBeInTheDocument() }) it('should handle enum in output schema', () => { @@ -260,7 +260,7 @@ describe('EventDetailDrawer', () => { } render() - expect(screen.getByText('pluginTrigger.events.output')).toBeInTheDocument() + expect(screen.getByText('pluginTrigger.events.output'))!.toBeInTheDocument() }) it('should handle array type schema', () => { @@ -275,7 +275,7 @@ describe('EventDetailDrawer', () => { } render() - expect(screen.getByText('pluginTrigger.events.output')).toBeInTheDocument() + expect(screen.getByText('pluginTrigger.events.output'))!.toBeInTheDocument() }) }) }) diff --git a/web/app/components/plugins/plugin-detail-panel/trigger/event-detail-drawer.tsx b/web/app/components/plugins/plugin-detail-panel/trigger/event-detail-drawer.tsx index bacb7bbd66..eb56a0178c 100644 --- a/web/app/components/plugins/plugin-detail-panel/trigger/event-detail-drawer.tsx +++ b/web/app/components/plugins/plugin-detail-panel/trigger/event-detail-drawer.tsx @@ -112,7 +112,7 @@ export const EventDetailDrawer: FC = (props) => { />
{eventInfo?.identity?.label[language]}
- +
{t('setBuiltInTools.parameters', { ns: 'tools' })}
diff --git a/web/app/components/plugins/plugin-item/__tests__/action.spec.tsx b/web/app/components/plugins/plugin-item/__tests__/action.spec.tsx index 82c4d69d1b..0b0d9c7fc8 100644 --- a/web/app/components/plugins/plugin-item/__tests__/action.spec.tsx +++ b/web/app/components/plugins/plugin-item/__tests__/action.spec.tsx @@ -253,10 +253,11 @@ describe('Action Component', () => { // Act render() - fireEvent.click(getActionButtons()[0]) + fireEvent.click(getActionButtons()[0]!) // Assert - expect(screen.getByText('plugin.action.delete')).toBeInTheDocument() + // Assert + expect(screen.getByText('plugin.action.delete'))!.toBeInTheDocument() }) it('should display plugin name in delete confirm content', () => { @@ -270,10 +271,11 @@ describe('Action Component', () => { // Act render() - fireEvent.click(getActionButtons()[0]) + fireEvent.click(getActionButtons()[0]!) // Assert - expect(screen.getByText('my-awesome-plugin')).toBeInTheDocument() + // Assert + expect(screen.getByText('my-awesome-plugin'))!.toBeInTheDocument() }) it('should hide confirm modal when cancel is clicked', () => { @@ -286,8 +288,8 @@ describe('Action Component', () => { // Act render() - fireEvent.click(getActionButtons()[0]) - expect(screen.getByText('plugin.action.delete')).toBeInTheDocument() + fireEvent.click(getActionButtons()[0]!) + expect(screen.getByText('plugin.action.delete'))!.toBeInTheDocument() fireEvent.click(getDeleteCancelButton()) @@ -308,7 +310,7 @@ describe('Action Component', () => { // Act render() - fireEvent.click(getActionButtons()[0]) + fireEvent.click(getActionButtons()[0]!) fireEvent.click(getDeleteConfirmButton()) // Assert @@ -330,7 +332,7 @@ describe('Action Component', () => { // Act render() - fireEvent.click(getActionButtons()[0]) + fireEvent.click(getActionButtons()[0]!) fireEvent.click(getDeleteConfirmButton()) // Assert @@ -352,7 +354,7 @@ describe('Action Component', () => { // Act render() - fireEvent.click(getActionButtons()[0]) + fireEvent.click(getActionButtons()[0]!) fireEvent.click(getDeleteConfirmButton()) // Assert @@ -374,7 +376,7 @@ describe('Action Component', () => { // Act render() - fireEvent.click(getActionButtons()[0]) + fireEvent.click(getActionButtons()[0]!) fireEvent.click(getDeleteConfirmButton()) // Assert @@ -401,12 +403,12 @@ describe('Action Component', () => { // Act render() - fireEvent.click(getActionButtons()[0]) + fireEvent.click(getActionButtons()[0]!) fireEvent.click(getDeleteConfirmButton()) // Assert - Loading state await waitFor(() => { - expect(getDeleteConfirmButton()).toBeDisabled() + expect(getDeleteConfirmButton())!.toBeDisabled() }) // Resolve and check modal closes @@ -434,13 +436,14 @@ describe('Action Component', () => { // Act render() - fireEvent.click(getActionButtons()[0]) + fireEvent.click(getActionButtons()[0]!) // Assert - expect(screen.getByTestId('plugin-info-modal')).toBeInTheDocument() - expect(screen.getByTestId('plugin-info-modal')).toHaveAttribute('data-repo', 'owner/repo-name') - expect(screen.getByTestId('plugin-info-modal')).toHaveAttribute('data-release', '2.0.0') - expect(screen.getByTestId('plugin-info-modal')).toHaveAttribute('data-package', 'my-package.difypkg') + // Assert + expect(screen.getByTestId('plugin-info-modal'))!.toBeInTheDocument() + expect(screen.getByTestId('plugin-info-modal'))!.toHaveAttribute('data-repo', 'owner/repo-name') + expect(screen.getByTestId('plugin-info-modal'))!.toHaveAttribute('data-release', '2.0.0') + expect(screen.getByTestId('plugin-info-modal'))!.toHaveAttribute('data-package', 'my-package.difypkg') }) it('should hide plugin info modal when close is clicked', () => { @@ -453,11 +456,42 @@ describe('Action Component', () => { // Act render() - fireEvent.click(getActionButtons()[0]) - expect(screen.getByTestId('plugin-info-modal')).toBeInTheDocument() + fireEvent.click(getActionButtons()[0]!) + expect(screen.getByTestId('plugin-info-modal'))!.toBeInTheDocument() fireEvent.click(screen.getByTestId('close-plugin-info')) + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert // Assert expect(screen.queryByTestId('plugin-info-modal')).not.toBeInTheDocument() }) @@ -481,7 +515,7 @@ describe('Action Component', () => { // Act render() - fireEvent.click(getActionButtons()[0]) + fireEvent.click(getActionButtons()[0]!) // Assert await waitFor(() => { @@ -507,7 +541,7 @@ describe('Action Component', () => { // Act render() - fireEvent.click(getActionButtons()[0]) + fireEvent.click(getActionButtons()[0]!) // Assert await waitFor(() => { @@ -526,7 +560,7 @@ describe('Action Component', () => { // Act render() - fireEvent.click(getActionButtons()[0]) + fireEvent.click(getActionButtons()[0]!) // Assert await waitFor(() => { @@ -550,7 +584,7 @@ describe('Action Component', () => { // Act render() - fireEvent.click(getActionButtons()[0]) + fireEvent.click(getActionButtons()[0]!) // Assert - toast is called with the translated payload await waitFor(() => { @@ -581,7 +615,7 @@ describe('Action Component', () => { // Act render() - fireEvent.click(getActionButtons()[0]) + fireEvent.click(getActionButtons()[0]!) // Assert await waitFor(() => { @@ -621,7 +655,7 @@ describe('Action Component', () => { // Act render() - fireEvent.click(getActionButtons()[0]) + fireEvent.click(getActionButtons()[0]!) // Wait for modal to be called await waitFor(() => { @@ -629,7 +663,7 @@ describe('Action Component', () => { }) // Invoke the callback - const call = mockSetShowUpdatePluginModal.mock.calls[0][0] + const call = mockSetShowUpdatePluginModal.mock.calls[0]![0] call.onSaveCallback() // Assert @@ -653,7 +687,7 @@ describe('Action Component', () => { // Act render() - fireEvent.click(getActionButtons()[0]) + fireEvent.click(getActionButtons()[0]!) // Assert await waitFor(() => { @@ -678,7 +712,7 @@ describe('Action Component', () => { // Act - First render and delete const { rerender } = render() - fireEvent.click(getActionButtons()[0]) + fireEvent.click(getActionButtons()[0]!) fireEvent.click(getDeleteConfirmButton()) await waitFor(() => { @@ -688,7 +722,7 @@ describe('Action Component', () => { // Re-render with same props mockUninstallPlugin.mockClear() rerender() - fireEvent.click(getActionButtons()[0]) + fireEvent.click(getActionButtons()[0]!) fireEvent.click(getDeleteConfirmButton()) await waitFor(() => { @@ -714,7 +748,7 @@ describe('Action Component', () => { // Act const { rerender } = render() - fireEvent.click(getActionButtons()[0]) + fireEvent.click(getActionButtons()[0]!) fireEvent.click(getDeleteConfirmButton()) await waitFor(() => { @@ -723,7 +757,7 @@ describe('Action Component', () => { mockUninstallPlugin.mockClear() rerender() - fireEvent.click(getActionButtons()[0]) + fireEvent.click(getActionButtons()[0]!) fireEvent.click(getDeleteConfirmButton()) await waitFor(() => { @@ -751,7 +785,7 @@ describe('Action Component', () => { // Act const { rerender } = render() - fireEvent.click(getActionButtons()[0]) + fireEvent.click(getActionButtons()[0]!) fireEvent.click(getDeleteConfirmButton()) await waitFor(() => { @@ -760,7 +794,7 @@ describe('Action Component', () => { expect(onDelete2).not.toHaveBeenCalled() rerender() - fireEvent.click(getActionButtons()[0]) + fireEvent.click(getActionButtons()[0]!) fireEvent.click(getDeleteConfirmButton()) await waitFor(() => { @@ -802,7 +836,7 @@ describe('Action Component', () => { // Act render() - fireEvent.click(getActionButtons()[0]) + fireEvent.click(getActionButtons()[0]!) // Assert - Should use author and pluginName as fallback await waitFor(() => { @@ -826,11 +860,12 @@ describe('Action Component', () => { // Act render() - fireEvent.click(getActionButtons()[0]) + fireEvent.click(getActionButtons()[0]!) fireEvent.click(getDeleteConfirmButton()) // The confirm button should be disabled during deletion - expect(getDeleteConfirmButton()).toBeDisabled() + // The confirm button should be disabled during deletion + expect(getDeleteConfirmButton())!.toBeDisabled() // Resolve the deletion resolveFirst!({ success: true }) @@ -851,10 +886,11 @@ describe('Action Component', () => { // Act render() - fireEvent.click(getActionButtons()[0]) + fireEvent.click(getActionButtons()[0]!) // Assert - expect(screen.getByText('plugin-with-special@chars#123')).toBeInTheDocument() + // Assert + expect(screen.getByText('plugin-with-special@chars#123'))!.toBeInTheDocument() }) }) diff --git a/web/app/components/plugins/plugin-item/index.tsx b/web/app/components/plugins/plugin-item/index.tsx index ad03c002d8..0f19db81e6 100644 --- a/web/app/components/plugins/plugin-item/index.tsx +++ b/web/app/components/plugins/plugin-item/index.tsx @@ -105,7 +105,7 @@ const PluginItem: FC = ({ }} >
- + {/* Header */}
diff --git a/web/app/components/plugins/plugin-page/filter-management/__tests__/category-filter.spec.tsx b/web/app/components/plugins/plugin-page/filter-management/__tests__/category-filter.spec.tsx index 051d16792a..e4b698a5f8 100644 --- a/web/app/components/plugins/plugin-page/filter-management/__tests__/category-filter.spec.tsx +++ b/web/app/components/plugins/plugin-page/filter-management/__tests__/category-filter.spec.tsx @@ -47,7 +47,7 @@ describe('CategoriesFilter', () => { it('should show "allCategories" when no categories selected', () => { render() - expect(screen.getByText('plugin.allCategories')).toBeInTheDocument() + expect(screen.getByText('plugin.allCategories'))!.toBeInTheDocument() }) it('should show selected category labels', () => { @@ -60,7 +60,7 @@ describe('CategoriesFilter', () => { it('should show +N when more than 2 selected', () => { render() - expect(screen.getByText('+1')).toBeInTheDocument() + expect(screen.getByText('+1'))!.toBeInTheDocument() }) it('should clear all selections when clear button clicked', () => { @@ -76,9 +76,9 @@ describe('CategoriesFilter', () => { it('should render category options in dropdown', () => { render() - expect(screen.getByText('Tool')).toBeInTheDocument() - expect(screen.getByText('Model')).toBeInTheDocument() - expect(screen.getByText('Extension')).toBeInTheDocument() + expect(screen.getByText('Tool'))!.toBeInTheDocument() + expect(screen.getByText('Model'))!.toBeInTheDocument() + expect(screen.getByText('Extension'))!.toBeInTheDocument() }) it('should toggle category on option click', () => { @@ -94,7 +94,7 @@ describe('CategoriesFilter', () => { render() const toolElements = screen.getAllByText('Tool') - fireEvent.click(toolElements[toolElements.length - 1]) + fireEvent.click(toolElements[toolElements.length - 1]!) expect(mockOnChange).toHaveBeenCalledWith([]) }) }) diff --git a/web/app/components/plugins/plugin-page/filter-management/__tests__/index.spec.tsx b/web/app/components/plugins/plugin-page/filter-management/__tests__/index.spec.tsx index 95f0c5c120..f30b5fb5fa 100644 --- a/web/app/components/plugins/plugin-page/filter-management/__tests__/index.spec.tsx +++ b/web/app/components/plugins/plugin-page/filter-management/__tests__/index.spec.tsx @@ -338,7 +338,8 @@ describe('SearchBox Component', () => { render() // Assert - expect(screen.getByPlaceholderText('plugin.search')).toBeInTheDocument() + // Assert + expect(screen.getByPlaceholderText('plugin.search'))!.toBeInTheDocument() }) it('should render with provided searchQuery value', () => { @@ -346,7 +347,8 @@ describe('SearchBox Component', () => { render() // Assert - expect(screen.getByDisplayValue('test query')).toBeInTheDocument() + // Assert + expect(screen.getByDisplayValue('test query'))!.toBeInTheDocument() }) it('should render search icon', () => { @@ -355,7 +357,7 @@ describe('SearchBox Component', () => { // Assert - Input should have showLeftIcon which renders search icon const wrapper = container.querySelector('.w-\\[200px\\]') - expect(wrapper).toBeInTheDocument() + expect(wrapper)!.toBeInTheDocument() }) }) @@ -464,7 +466,8 @@ describe('CategoriesFilter Component', () => { render() // Assert - expect(screen.getByText('plugin.allCategories')).toBeInTheDocument() + // Assert + expect(screen.getByText('plugin.allCategories'))!.toBeInTheDocument() }) it('should render dropdown arrow when no selection', () => { @@ -473,7 +476,7 @@ describe('CategoriesFilter Component', () => { // Assert - Arrow icon should be visible const arrowIcon = container.querySelector('svg') - expect(arrowIcon).toBeInTheDocument() + expect(arrowIcon)!.toBeInTheDocument() }) it('should render selected category labels', () => { @@ -481,7 +484,8 @@ describe('CategoriesFilter Component', () => { render() // Assert - expect(screen.getByText('Models')).toBeInTheDocument() + // Assert + expect(screen.getByText('Models'))!.toBeInTheDocument() }) it('should show clear button when categories are selected', () => { @@ -490,7 +494,7 @@ describe('CategoriesFilter Component', () => { // Assert - Close icon should be visible const closeIcon = container.querySelector('[class*="cursor-pointer"]') - expect(closeIcon).toBeInTheDocument() + expect(closeIcon)!.toBeInTheDocument() }) it('should show count badge for more than 2 selections', () => { @@ -498,7 +502,8 @@ describe('CategoriesFilter Component', () => { render() // Assert - expect(screen.getByText('+1')).toBeInTheDocument() + // Assert + expect(screen.getByText('+1'))!.toBeInTheDocument() }) }) @@ -512,7 +517,7 @@ describe('CategoriesFilter Component', () => { // Assert await waitFor(() => { - expect(screen.getByTestId('portal-content')).toBeInTheDocument() + expect(screen.getByTestId('portal-content'))!.toBeInTheDocument() }) }) @@ -525,10 +530,10 @@ describe('CategoriesFilter Component', () => { // Assert await waitFor(() => { - expect(screen.getByText('Models')).toBeInTheDocument() - expect(screen.getByText('Tools')).toBeInTheDocument() - expect(screen.getByText('Extensions')).toBeInTheDocument() - expect(screen.getByText('Agents')).toBeInTheDocument() + expect(screen.getByText('Models'))!.toBeInTheDocument() + expect(screen.getByText('Tools'))!.toBeInTheDocument() + expect(screen.getByText('Extensions'))!.toBeInTheDocument() + expect(screen.getByText('Agents'))!.toBeInTheDocument() }) }) @@ -541,7 +546,7 @@ describe('CategoriesFilter Component', () => { // Assert await waitFor(() => { - expect(screen.getByPlaceholderText('plugin.searchCategories')).toBeInTheDocument() + expect(screen.getByPlaceholderText('plugin.searchCategories'))!.toBeInTheDocument() }) }) }) @@ -555,7 +560,7 @@ describe('CategoriesFilter Component', () => { // Act - Open dropdown and click category fireEvent.click(screen.getByTestId('portal-trigger')) await waitFor(() => { - expect(screen.getByText('Models')).toBeInTheDocument() + expect(screen.getByText('Models'))!.toBeInTheDocument() }) fireEvent.click(screen.getByText('Models')) @@ -592,7 +597,7 @@ describe('CategoriesFilter Component', () => { // Act fireEvent.click(screen.getByTestId('portal-trigger')) await waitFor(() => { - expect(screen.getByText('Tools')).toBeInTheDocument() + expect(screen.getByText('Tools'))!.toBeInTheDocument() }) fireEvent.click(screen.getByText('Tools')) @@ -607,7 +612,7 @@ describe('CategoriesFilter Component', () => { // Act - Find and click the close icon const closeIcon = container.querySelector('.text-text-quaternary') - expect(closeIcon).toBeInTheDocument() + expect(closeIcon)!.toBeInTheDocument() fireEvent.click(closeIcon!) // Assert @@ -623,14 +628,15 @@ describe('CategoriesFilter Component', () => { // Act fireEvent.click(screen.getByTestId('portal-trigger')) await waitFor(() => { - expect(screen.getByPlaceholderText('plugin.searchCategories')).toBeInTheDocument() + expect(screen.getByPlaceholderText('plugin.searchCategories'))!.toBeInTheDocument() }) fireEvent.change(screen.getByPlaceholderText('plugin.searchCategories'), { target: { value: 'mod' }, }) // Assert - expect(screen.getByText('Models')).toBeInTheDocument() + // Assert + expect(screen.getByText('Models'))!.toBeInTheDocument() expect(screen.queryByText('Extensions')).not.toBeInTheDocument() }) @@ -641,14 +647,15 @@ describe('CategoriesFilter Component', () => { // Act fireEvent.click(screen.getByTestId('portal-trigger')) await waitFor(() => { - expect(screen.getByPlaceholderText('plugin.searchCategories')).toBeInTheDocument() + expect(screen.getByPlaceholderText('plugin.searchCategories'))!.toBeInTheDocument() }) fireEvent.change(screen.getByPlaceholderText('plugin.searchCategories'), { target: { value: 'MOD' }, }) // Assert - expect(screen.getByText('Models')).toBeInTheDocument() + // Assert + expect(screen.getByText('Models'))!.toBeInTheDocument() }) }) @@ -696,7 +703,8 @@ describe('TagFilter Component', () => { render() // Assert - expect(screen.getByText('pluginTags.allTags')).toBeInTheDocument() + // Assert + expect(screen.getByText('pluginTags.allTags'))!.toBeInTheDocument() }) it('should render selected tag labels', () => { @@ -704,7 +712,8 @@ describe('TagFilter Component', () => { render() // Assert - expect(screen.getByText('Agent')).toBeInTheDocument() + // Assert + expect(screen.getByText('Agent'))!.toBeInTheDocument() }) it('should show count badge for more than 2 selections', () => { @@ -712,7 +721,8 @@ describe('TagFilter Component', () => { render() // Assert - expect(screen.getByText('+1')).toBeInTheDocument() + // Assert + expect(screen.getByText('+1'))!.toBeInTheDocument() }) it('should show clear button when tags are selected', () => { @@ -721,7 +731,7 @@ describe('TagFilter Component', () => { // Assert const closeIcon = container.querySelector('.text-text-quaternary') - expect(closeIcon).toBeInTheDocument() + expect(closeIcon)!.toBeInTheDocument() }) }) @@ -735,7 +745,7 @@ describe('TagFilter Component', () => { // Assert await waitFor(() => { - expect(screen.getByTestId('portal-content')).toBeInTheDocument() + expect(screen.getByTestId('portal-content'))!.toBeInTheDocument() }) }) @@ -748,10 +758,10 @@ describe('TagFilter Component', () => { // Assert await waitFor(() => { - expect(screen.getByText('Agent')).toBeInTheDocument() - expect(screen.getByText('RAG')).toBeInTheDocument() - expect(screen.getByText('Search')).toBeInTheDocument() - expect(screen.getByText('Image')).toBeInTheDocument() + expect(screen.getByText('Agent'))!.toBeInTheDocument() + expect(screen.getByText('RAG'))!.toBeInTheDocument() + expect(screen.getByText('Search'))!.toBeInTheDocument() + expect(screen.getByText('Image'))!.toBeInTheDocument() }) }) }) @@ -765,7 +775,7 @@ describe('TagFilter Component', () => { // Act fireEvent.click(screen.getByTestId('portal-trigger')) await waitFor(() => { - expect(screen.getByText('Agent')).toBeInTheDocument() + expect(screen.getByText('Agent'))!.toBeInTheDocument() }) fireEvent.click(screen.getByText('Agent')) @@ -783,7 +793,7 @@ describe('TagFilter Component', () => { await waitFor(() => { // Find the Agent option in dropdown const agentOptions = screen.getAllByText('Agent') - fireEvent.click(agentOptions[agentOptions.length - 1]) + fireEvent.click(agentOptions[agentOptions.length - 1]!) }) // Assert @@ -798,7 +808,7 @@ describe('TagFilter Component', () => { // Act fireEvent.click(screen.getByTestId('portal-trigger')) await waitFor(() => { - expect(screen.getByText('RAG')).toBeInTheDocument() + expect(screen.getByText('RAG'))!.toBeInTheDocument() }) fireEvent.click(screen.getByText('RAG')) @@ -828,14 +838,15 @@ describe('TagFilter Component', () => { // Act fireEvent.click(screen.getByTestId('portal-trigger')) await waitFor(() => { - expect(screen.getByPlaceholderText('pluginTags.searchTags')).toBeInTheDocument() + expect(screen.getByPlaceholderText('pluginTags.searchTags'))!.toBeInTheDocument() }) fireEvent.change(screen.getByPlaceholderText('pluginTags.searchTags'), { target: { value: 'rag' }, }) // Assert - expect(screen.getByText('RAG')).toBeInTheDocument() + // Assert + expect(screen.getByText('RAG'))!.toBeInTheDocument() expect(screen.queryByText('Image')).not.toBeInTheDocument() }) }) @@ -855,9 +866,10 @@ describe('FilterManagement Component', () => { renderFilterManagement() // Assert - All three filters should be present - expect(screen.getByText('plugin.allCategories')).toBeInTheDocument() - expect(screen.getByText('pluginTags.allTags')).toBeInTheDocument() - expect(screen.getByPlaceholderText('plugin.search')).toBeInTheDocument() + // Assert - All three filters should be present + expect(screen.getByText('plugin.allCategories'))!.toBeInTheDocument() + expect(screen.getByText('pluginTags.allTags'))!.toBeInTheDocument() + expect(screen.getByPlaceholderText('plugin.search'))!.toBeInTheDocument() }) it('should render with correct container classes', () => { @@ -866,7 +878,7 @@ describe('FilterManagement Component', () => { // Assert const wrapper = container.firstChild as HTMLElement - expect(wrapper).toHaveClass('flex', 'items-center', 'gap-2', 'self-stretch') + expect(wrapper)!.toHaveClass('flex', 'items-center', 'gap-2', 'self-stretch') }) }) @@ -879,9 +891,10 @@ describe('FilterManagement Component', () => { renderFilterManagement() // Assert - expect(screen.getByText('plugin.allCategories')).toBeInTheDocument() - expect(screen.getByText('pluginTags.allTags')).toBeInTheDocument() - expect(screen.getByPlaceholderText('plugin.search')).toHaveValue('') + // Assert + expect(screen.getByText('plugin.allCategories'))!.toBeInTheDocument() + expect(screen.getByText('pluginTags.allTags'))!.toBeInTheDocument() + expect(screen.getByPlaceholderText('plugin.search'))!.toHaveValue('') }) it('should initialize with pre-selected categories', () => { @@ -892,7 +905,8 @@ describe('FilterManagement Component', () => { renderFilterManagement() // Assert - expect(screen.getByText('Models')).toBeInTheDocument() + // Assert + expect(screen.getByText('Models'))!.toBeInTheDocument() }) it('should initialize with pre-selected tags', () => { @@ -903,7 +917,8 @@ describe('FilterManagement Component', () => { renderFilterManagement() // Assert - expect(screen.getByText('Agent')).toBeInTheDocument() + // Assert + expect(screen.getByText('Agent'))!.toBeInTheDocument() }) it('should initialize with search query', () => { @@ -914,7 +929,8 @@ describe('FilterManagement Component', () => { renderFilterManagement() // Assert - expect(screen.getByDisplayValue('initial search')).toBeInTheDocument() + // Assert + expect(screen.getByDisplayValue('initial search'))!.toBeInTheDocument() }) }) @@ -926,10 +942,10 @@ describe('FilterManagement Component', () => { // Act - Open categories dropdown and select const triggers = screen.getAllByTestId('portal-trigger') - fireEvent.click(triggers[0]) // Categories filter trigger + fireEvent.click(triggers[0]!) // Categories filter trigger await waitFor(() => { - expect(screen.getByText('Models')).toBeInTheDocument() + expect(screen.getByText('Models'))!.toBeInTheDocument() }) fireEvent.click(screen.getByText('Models')) @@ -948,10 +964,10 @@ describe('FilterManagement Component', () => { // Act - Open tags dropdown and select const triggers = screen.getAllByTestId('portal-trigger') - fireEvent.click(triggers[1]) // Tags filter trigger + fireEvent.click(triggers[1]!) // Tags filter trigger await waitFor(() => { - expect(screen.getByText('Agent')).toBeInTheDocument() + expect(screen.getByText('Agent'))!.toBeInTheDocument() }) fireEvent.click(screen.getByText('Agent')) @@ -990,9 +1006,9 @@ describe('FilterManagement Component', () => { // Act 1 - Select a category const triggers = screen.getAllByTestId('portal-trigger') - fireEvent.click(triggers[0]) + fireEvent.click(triggers[0]!) await waitFor(() => { - expect(screen.getByText('Models')).toBeInTheDocument() + expect(screen.getByText('Models'))!.toBeInTheDocument() }) fireEvent.click(screen.getByText('Models')) @@ -1003,12 +1019,12 @@ describe('FilterManagement Component', () => { }) // Close dropdown by clicking trigger again - fireEvent.click(triggers[0]) + fireEvent.click(triggers[0]!) // Act 2 - Select a tag (state should include previous category) - fireEvent.click(triggers[1]) + fireEvent.click(triggers[1]!) await waitFor(() => { - expect(screen.getByText('Agent')).toBeInTheDocument() + expect(screen.getByText('Agent'))!.toBeInTheDocument() }) fireEvent.click(screen.getByText('Agent')) @@ -1051,20 +1067,20 @@ describe('FilterManagement Component', () => { // Act 1 - Select categories const triggers = screen.getAllByTestId('portal-trigger') - fireEvent.click(triggers[0]) + fireEvent.click(triggers[0]!) await waitFor(() => { - expect(screen.getByText('Models')).toBeInTheDocument() + expect(screen.getByText('Models'))!.toBeInTheDocument() }) fireEvent.click(screen.getByText('Models')) - fireEvent.click(triggers[0]) // Close + fireEvent.click(triggers[0]!) // Close // Act 2 - Select tags - fireEvent.click(triggers[1]) + fireEvent.click(triggers[1]!) await waitFor(() => { - expect(screen.getByText('RAG')).toBeInTheDocument() + expect(screen.getByText('RAG'))!.toBeInTheDocument() }) fireEvent.click(screen.getByText('RAG')) - fireEvent.click(triggers[1]) // Close + fireEvent.click(triggers[1]!) // Close // Act 3 - Enter search fireEvent.change(screen.getByPlaceholderText('plugin.search'), { @@ -1103,7 +1119,7 @@ describe('FilterManagement Component', () => { // Act - Clear categories (click clear button) const closeIcons = container.querySelectorAll('.text-text-quaternary') - fireEvent.click(closeIcons[0]) // First close icon is for categories + fireEvent.click(closeIcons[0]!) // First close icon is for categories // Assert expect(onFilterChange).toHaveBeenLastCalledWith({ @@ -1124,7 +1140,8 @@ describe('FilterManagement Component', () => { render() // Assert - Should render without errors - expect(screen.getByText('plugin.allCategories')).toBeInTheDocument() + // Assert - Should render without errors + expect(screen.getByText('plugin.allCategories'))!.toBeInTheDocument() }) it('should handle multiple rapid filter changes', () => { diff --git a/web/app/components/plugins/plugin-page/filter-management/category-filter.tsx b/web/app/components/plugins/plugin-page/filter-management/category-filter.tsx index f6ace774e8..8dbef5395d 100644 --- a/web/app/components/plugins/plugin-page/filter-management/category-filter.tsx +++ b/web/app/components/plugins/plugin-page/filter-management/category-filter.tsx @@ -61,7 +61,7 @@ const CategoriesFilter = ({ !selectedTagsLength && t('allCategories', { ns: 'plugin' }) } { - !!selectedTagsLength && value.map(val => categoriesMap[val].label).slice(0, 2).join(',') + !!selectedTagsLength && value.map(val => categoriesMap[val]!.label).slice(0, 2).join(',') } { selectedTagsLength > 2 && ( diff --git a/web/app/components/plugins/plugin-page/list/__tests__/index.spec.tsx b/web/app/components/plugins/plugin-page/list/__tests__/index.spec.tsx index c6326461d4..6537237839 100644 --- a/web/app/components/plugins/plugin-page/list/__tests__/index.spec.tsx +++ b/web/app/components/plugins/plugin-page/list/__tests__/index.spec.tsx @@ -113,7 +113,8 @@ describe('PluginList', () => { const { container } = render() // Assert - expect(container).toBeInTheDocument() + // Assert + expect(container)!.toBeInTheDocument() }) it('should render container with correct structure', () => { @@ -125,10 +126,10 @@ describe('PluginList', () => { // Assert const outerDiv = container.firstChild as HTMLElement - expect(outerDiv).toHaveClass('pb-3') + expect(outerDiv)!.toHaveClass('pb-3') const gridDiv = outerDiv.firstChild as HTMLElement - expect(gridDiv).toHaveClass('grid', 'grid-cols-2', 'gap-3') + expect(gridDiv)!.toHaveClass('grid', 'grid-cols-2', 'gap-3') }) it('should render single plugin correctly', () => { @@ -141,7 +142,7 @@ describe('PluginList', () => { // Assert const pluginItems = screen.getAllByTestId('plugin-item') expect(pluginItems).toHaveLength(1) - expect(pluginItems[0]).toHaveAttribute('data-plugin-name', 'single-plugin') + expect(pluginItems[0])!.toHaveAttribute('data-plugin-name', 'single-plugin') }) it('should render multiple plugins correctly', () => { @@ -169,9 +170,9 @@ describe('PluginList', () => { // Assert const pluginItems = screen.getAllByTestId('plugin-item') - expect(pluginItems[0]).toHaveAttribute('data-plugin-id', 'first') - expect(pluginItems[1]).toHaveAttribute('data-plugin-id', 'second') - expect(pluginItems[2]).toHaveAttribute('data-plugin-id', 'third') + expect(pluginItems[0])!.toHaveAttribute('data-plugin-id', 'first') + expect(pluginItems[1])!.toHaveAttribute('data-plugin-id', 'second') + expect(pluginItems[2])!.toHaveAttribute('data-plugin-id', 'third') }) it('should pass plugin prop to each PluginItem', () => { @@ -185,8 +186,9 @@ describe('PluginList', () => { render() // Assert - expect(screen.getByText('Plugin A')).toBeInTheDocument() - expect(screen.getByText('Plugin B')).toBeInTheDocument() + // Assert + expect(screen.getByText('Plugin A'))!.toBeInTheDocument() + expect(screen.getByText('Plugin B'))!.toBeInTheDocument() }) }) @@ -198,7 +200,7 @@ describe('PluginList', () => { // Assert const gridDiv = container.querySelector('.grid') - expect(gridDiv).toBeEmptyDOMElement() + expect(gridDiv)!.toBeEmptyDOMElement() }) it('should handle pluginList with various categories', () => { @@ -250,6 +252,37 @@ describe('PluginList', () => { // Arrange & Act render() + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert // Assert expect(screen.queryByTestId('plugin-item')).not.toBeInTheDocument() }) @@ -303,7 +336,8 @@ describe('PluginList', () => { render() // Assert - expect(screen.getByTestId('plugin-item')).toBeInTheDocument() + // Assert + expect(screen.getByTestId('plugin-item'))!.toBeInTheDocument() }) it('should handle plugin with minimal data', () => { @@ -317,7 +351,8 @@ describe('PluginList', () => { render() // Assert - expect(screen.getByTestId('plugin-item')).toBeInTheDocument() + // Assert + expect(screen.getByTestId('plugin-item'))!.toBeInTheDocument() }) it('should handle plugins with undefined optional fields', () => { @@ -333,7 +368,8 @@ describe('PluginList', () => { render() // Assert - expect(screen.getByTestId('plugin-item')).toBeInTheDocument() + // Assert + expect(screen.getByTestId('plugin-item'))!.toBeInTheDocument() }) }) @@ -348,7 +384,7 @@ describe('PluginList', () => { // Assert const gridDiv = container.querySelector('.grid') - expect(gridDiv).toHaveClass('grid-cols-2') + expect(gridDiv)!.toHaveClass('grid-cols-2') }) it('should have proper gap between items', () => { @@ -360,7 +396,7 @@ describe('PluginList', () => { // Assert const gridDiv = container.querySelector('.grid') - expect(gridDiv).toHaveClass('gap-3') + expect(gridDiv)!.toHaveClass('gap-3') }) it('should have bottom padding on container', () => { @@ -372,7 +408,7 @@ describe('PluginList', () => { // Assert const outerDiv = container.firstChild as HTMLElement - expect(outerDiv).toHaveClass('pb-3') + expect(outerDiv)!.toHaveClass('pb-3') }) }) @@ -404,6 +440,37 @@ describe('PluginList', () => { rerender() + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert // Assert expect(screen.queryByTestId('plugin-item')).not.toBeInTheDocument() }) @@ -430,12 +497,13 @@ describe('PluginList', () => { // Act const { rerender } = render() - expect(screen.getByText('Original Name')).toBeInTheDocument() + expect(screen.getByText('Original Name'))!.toBeInTheDocument() rerender() // Assert - expect(screen.getByText('Updated Name')).toBeInTheDocument() + // Assert + expect(screen.getByText('Updated Name'))!.toBeInTheDocument() expect(screen.queryByText('Original Name')).not.toBeInTheDocument() }) }) @@ -454,15 +522,15 @@ describe('PluginList', () => { const { rerender } = render() // Reorder the list - const reorderedList = [pluginList[2], pluginList[0], pluginList[1]] + const reorderedList = [pluginList[2]!, pluginList[0]!, pluginList[1]!] rerender() // Assert - All items should still be present const items = screen.getAllByTestId('plugin-item') expect(items).toHaveLength(3) - expect(items[0]).toHaveAttribute('data-plugin-id', 'stable-key-3') - expect(items[1]).toHaveAttribute('data-plugin-id', 'stable-key-1') - expect(items[2]).toHaveAttribute('data-plugin-id', 'stable-key-2') + expect(items[0])!.toHaveAttribute('data-plugin-id', 'stable-key-3') + expect(items[1])!.toHaveAttribute('data-plugin-id', 'stable-key-1') + expect(items[2])!.toHaveAttribute('data-plugin-id', 'stable-key-2') }) }) @@ -476,7 +544,8 @@ describe('PluginList', () => { render() // Assert - expect(screen.getByTestId('plugin-item')).toBeInTheDocument() + // Assert + expect(screen.getByTestId('plugin-item'))!.toBeInTheDocument() }) it('should render deleted/deprecated plugins', () => { @@ -492,7 +561,8 @@ describe('PluginList', () => { render() // Assert - expect(screen.getByTestId('plugin-item')).toBeInTheDocument() + // Assert + expect(screen.getByTestId('plugin-item'))!.toBeInTheDocument() }) it('should render mixed status plugins', () => { @@ -529,7 +599,8 @@ describe('PluginList', () => { render() // Assert - expect(screen.getByTestId('plugin-item')).toBeInTheDocument() + // Assert + expect(screen.getByTestId('plugin-item'))!.toBeInTheDocument() }) it('should render plugins with outdated version', () => { @@ -545,7 +616,8 @@ describe('PluginList', () => { render() // Assert - expect(screen.getByTestId('plugin-item')).toBeInTheDocument() + // Assert + expect(screen.getByTestId('plugin-item'))!.toBeInTheDocument() }) }) @@ -607,9 +679,10 @@ describe('PluginList', () => { render() // Assert - expect(screen.getByText('Marketplace Plugin')).toBeInTheDocument() - expect(screen.getByText('GitHub Plugin')).toBeInTheDocument() - expect(screen.getByText('Local Plugin')).toBeInTheDocument() + // Assert + expect(screen.getByText('Marketplace Plugin'))!.toBeInTheDocument() + expect(screen.getByText('GitHub Plugin'))!.toBeInTheDocument() + expect(screen.getByText('Local Plugin'))!.toBeInTheDocument() }) it('should render plugins of different categories together', () => { @@ -636,9 +709,10 @@ describe('PluginList', () => { render() // Assert - expect(screen.getByText('Tool Plugin')).toBeInTheDocument() - expect(screen.getByText('Model Plugin')).toBeInTheDocument() - expect(screen.getByText('Agent Plugin')).toBeInTheDocument() + // Assert + expect(screen.getByText('Tool Plugin'))!.toBeInTheDocument() + expect(screen.getByText('Model Plugin'))!.toBeInTheDocument() + expect(screen.getByText('Agent Plugin'))!.toBeInTheDocument() }) }) diff --git a/web/app/components/plugins/plugin-page/plugin-tasks/__tests__/index.spec.tsx b/web/app/components/plugins/plugin-page/plugin-tasks/__tests__/index.spec.tsx index 56992b377f..15b4429de8 100644 --- a/web/app/components/plugins/plugin-page/plugin-tasks/__tests__/index.spec.tsx +++ b/web/app/components/plugins/plugin-page/plugin-tasks/__tests__/index.spec.tsx @@ -83,8 +83,8 @@ describe('usePluginTaskStatus Hook', () => { render() - expect(screen.getByTestId('running-count')).toHaveTextContent('1') - expect(screen.getByTestId('running-id')).toHaveTextContent(runningPlugin.plugin_unique_identifier) + expect(screen.getByTestId('running-count'))!.toHaveTextContent('1') + expect(screen.getByTestId('running-id'))!.toHaveTextContent(runningPlugin.plugin_unique_identifier) }) it('should categorize success plugins correctly', () => { @@ -103,8 +103,8 @@ describe('usePluginTaskStatus Hook', () => { render() - expect(screen.getByTestId('success-count')).toHaveTextContent('1') - expect(screen.getByTestId('success-id')).toHaveTextContent(successPlugin.plugin_unique_identifier) + expect(screen.getByTestId('success-count'))!.toHaveTextContent('1') + expect(screen.getByTestId('success-id'))!.toHaveTextContent(successPlugin.plugin_unique_identifier) }) it('should categorize error plugins correctly', () => { @@ -123,8 +123,8 @@ describe('usePluginTaskStatus Hook', () => { render() - expect(screen.getByTestId('error-count')).toHaveTextContent('1') - expect(screen.getByTestId('error-id')).toHaveTextContent(errorPlugin.plugin_unique_identifier) + expect(screen.getByTestId('error-count'))!.toHaveTextContent('1') + expect(screen.getByTestId('error-id'))!.toHaveTextContent(errorPlugin.plugin_unique_identifier) }) it('should categorize mixed plugins correctly', () => { @@ -149,10 +149,10 @@ describe('usePluginTaskStatus Hook', () => { render() - expect(screen.getByTestId('running')).toHaveTextContent('1') - expect(screen.getByTestId('success')).toHaveTextContent('1') - expect(screen.getByTestId('error')).toHaveTextContent('1') - expect(screen.getByTestId('total')).toHaveTextContent('3') + expect(screen.getByTestId('running'))!.toHaveTextContent('1') + expect(screen.getByTestId('success'))!.toHaveTextContent('1') + expect(screen.getByTestId('error'))!.toHaveTextContent('1') + expect(screen.getByTestId('total'))!.toHaveTextContent('3') }) }) @@ -175,11 +175,11 @@ describe('usePluginTaskStatus Hook', () => { render() - expect(screen.getByTestId('isInstalling')).toHaveTextContent('true') - expect(screen.getByTestId('isInstallingWithSuccess')).toHaveTextContent('false') - expect(screen.getByTestId('isInstallingWithError')).toHaveTextContent('false') - expect(screen.getByTestId('isSuccess')).toHaveTextContent('false') - expect(screen.getByTestId('isFailed')).toHaveTextContent('false') + expect(screen.getByTestId('isInstalling'))!.toHaveTextContent('true') + expect(screen.getByTestId('isInstallingWithSuccess'))!.toHaveTextContent('false') + expect(screen.getByTestId('isInstallingWithError'))!.toHaveTextContent('false') + expect(screen.getByTestId('isSuccess'))!.toHaveTextContent('false') + expect(screen.getByTestId('isFailed'))!.toHaveTextContent('false') }) it('should set isInstallingWithSuccess when running and success plugins exist', () => { @@ -194,7 +194,7 @@ describe('usePluginTaskStatus Hook', () => { } render() - expect(screen.getByTestId('flag')).toHaveTextContent('true') + expect(screen.getByTestId('flag'))!.toHaveTextContent('true') }) it('should set isInstallingWithError when running and error plugins exist', () => { @@ -209,7 +209,7 @@ describe('usePluginTaskStatus Hook', () => { } render() - expect(screen.getByTestId('flag')).toHaveTextContent('true') + expect(screen.getByTestId('flag'))!.toHaveTextContent('true') }) it('should set isSuccess when all plugins succeeded', () => { @@ -224,7 +224,7 @@ describe('usePluginTaskStatus Hook', () => { } render() - expect(screen.getByTestId('flag')).toHaveTextContent('true') + expect(screen.getByTestId('flag'))!.toHaveTextContent('true') }) it('should set isFailed when no running plugins and some failed', () => { @@ -239,7 +239,7 @@ describe('usePluginTaskStatus Hook', () => { } render() - expect(screen.getByTestId('flag')).toHaveTextContent('true') + expect(screen.getByTestId('flag'))!.toHaveTextContent('true') }) }) @@ -296,12 +296,12 @@ describe('TaskStatusIndicator Component', () => { describe('Rendering', () => { it('should render without crashing', () => { render() - expect(document.getElementById('plugin-task-trigger')).toBeInTheDocument() + expect(document.getElementById('plugin-task-trigger'))!.toBeInTheDocument() }) it('should render with correct id', () => { render() - expect(document.getElementById('plugin-task-trigger')).toBeInTheDocument() + expect(document.getElementById('plugin-task-trigger'))!.toBeInTheDocument() }) }) @@ -309,17 +309,18 @@ describe('TaskStatusIndicator Component', () => { it('should show downloading icon when installing', () => { render() // DownloadingIcon is rendered when isInstalling is true - expect(document.getElementById('plugin-task-trigger')).toBeInTheDocument() + // DownloadingIcon is rendered when isInstalling is true + expect(document.getElementById('plugin-task-trigger'))!.toBeInTheDocument() }) it('should show downloading icon when installing with error', () => { render() - expect(document.getElementById('plugin-task-trigger')).toBeInTheDocument() + expect(document.getElementById('plugin-task-trigger'))!.toBeInTheDocument() }) it('should show install icon when not installing', () => { render() - expect(document.getElementById('plugin-task-trigger')).toBeInTheDocument() + expect(document.getElementById('plugin-task-trigger'))!.toBeInTheDocument() }) }) @@ -333,7 +334,7 @@ describe('TaskStatusIndicator Component', () => { totalPluginsLength={3} />, ) - expect(document.getElementById('plugin-task-trigger')).toBeInTheDocument() + expect(document.getElementById('plugin-task-trigger'))!.toBeInTheDocument() }) it('should show progress circle when installing with success', () => { @@ -345,7 +346,7 @@ describe('TaskStatusIndicator Component', () => { totalPluginsLength={3} />, ) - expect(document.getElementById('plugin-task-trigger')).toBeInTheDocument() + expect(document.getElementById('plugin-task-trigger'))!.toBeInTheDocument() }) it('should show error progress circle when installing with error', () => { @@ -357,7 +358,7 @@ describe('TaskStatusIndicator Component', () => { totalPluginsLength={3} />, ) - expect(document.getElementById('plugin-task-trigger')).toBeInTheDocument() + expect(document.getElementById('plugin-task-trigger'))!.toBeInTheDocument() }) it('should show success icon when all completed successfully', () => { @@ -370,12 +371,12 @@ describe('TaskStatusIndicator Component', () => { totalPluginsLength={3} />, ) - expect(document.getElementById('plugin-task-trigger')).toBeInTheDocument() + expect(document.getElementById('plugin-task-trigger'))!.toBeInTheDocument() }) it('should show error icon when failed', () => { render() - expect(document.getElementById('plugin-task-trigger')).toBeInTheDocument() + expect(document.getElementById('plugin-task-trigger'))!.toBeInTheDocument() }) }) @@ -383,19 +384,19 @@ describe('TaskStatusIndicator Component', () => { it('should apply error styles when installing with error', () => { render() const trigger = document.getElementById('plugin-task-trigger') - expect(trigger).toHaveClass('bg-state-destructive-hover') + expect(trigger)!.toHaveClass('bg-state-destructive-hover') }) it('should apply error styles when failed', () => { render() const trigger = document.getElementById('plugin-task-trigger') - expect(trigger).toHaveClass('bg-state-destructive-hover') + expect(trigger)!.toHaveClass('bg-state-destructive-hover') }) it('should apply cursor-pointer when clickable', () => { render() const trigger = document.getElementById('plugin-task-trigger') - expect(trigger).toHaveClass('cursor-pointer') + expect(trigger)!.toHaveClass('cursor-pointer') }) }) @@ -429,7 +430,7 @@ describe('PluginTaskList Component', () => { describe('Rendering', () => { it('should render without crashing with empty lists', () => { render() - expect(document.querySelector('.w-\\[360px\\]')).toBeInTheDocument() + expect(document.querySelector('.w-\\[360px\\]'))!.toBeInTheDocument() }) it('should render running plugins section when plugins exist', () => { @@ -439,7 +440,8 @@ describe('PluginTaskList Component', () => { // Translation key is returned as text in tests, multiple matches expected (title + status) expect(screen.getAllByText(/task\.installing/i).length).toBeGreaterThan(0) // Verify section container is rendered - expect(document.querySelector('.max-h-\\[300px\\]')).toBeInTheDocument() + // Verify section container is rendered + expect(document.querySelector('.max-h-\\[300px\\]'))!.toBeInTheDocument() }) it('should render success plugins section when plugins exist', () => { @@ -454,7 +456,7 @@ describe('PluginTaskList Component', () => { const errorPlugins = [createMockPlugin({ status: TaskStatus.failed, message: 'Error occurred' })] render() - expect(screen.getByText('Error occurred')).toBeInTheDocument() + expect(screen.getByText('Error occurred'))!.toBeInTheDocument() }) it('should render all sections when all types exist', () => { @@ -541,7 +543,7 @@ describe('PluginTaskList Component', () => { render() - expect(screen.getByText('My Test Plugin')).toBeInTheDocument() + expect(screen.getByText('My Test Plugin'))!.toBeInTheDocument() }) it('should display plugin message when available', () => { @@ -552,7 +554,7 @@ describe('PluginTaskList Component', () => { render() - expect(screen.getByText('Successfully installed!')).toBeInTheDocument() + expect(screen.getByText('Successfully installed!'))!.toBeInTheDocument() }) it('should display multiple plugins in each section', () => { @@ -563,8 +565,8 @@ describe('PluginTaskList Component', () => { render() - expect(screen.getByText('Plugin A')).toBeInTheDocument() - expect(screen.getByText('Plugin B')).toBeInTheDocument() + expect(screen.getByText('Plugin A'))!.toBeInTheDocument() + expect(screen.getByText('Plugin B'))!.toBeInTheDocument() // Count is rendered, verify multiple items are in list expect(document.querySelectorAll('.hover\\:bg-state-base-hover').length).toBe(2) }) @@ -593,7 +595,7 @@ describe('PluginTasks Component', () => { render() - expect(document.getElementById('plugin-task-trigger')).toBeInTheDocument() + expect(document.getElementById('plugin-task-trigger'))!.toBeInTheDocument() }) }) @@ -604,7 +606,8 @@ describe('PluginTasks Component', () => { render() // The component renders with a tooltip, we verify it exists - expect(document.getElementById('plugin-task-trigger')).toBeInTheDocument() + // The component renders with a tooltip, we verify it exists + expect(document.getElementById('plugin-task-trigger'))!.toBeInTheDocument() }) it('should show success tip when all succeeded', () => { @@ -612,7 +615,7 @@ describe('PluginTasks Component', () => { render() - expect(document.getElementById('plugin-task-trigger')).toBeInTheDocument() + expect(document.getElementById('plugin-task-trigger'))!.toBeInTheDocument() }) it('should show error tip when some failed', () => { @@ -623,7 +626,7 @@ describe('PluginTasks Component', () => { render() - expect(document.getElementById('plugin-task-trigger')).toBeInTheDocument() + expect(document.getElementById('plugin-task-trigger'))!.toBeInTheDocument() }) }) @@ -637,7 +640,8 @@ describe('PluginTasks Component', () => { fireEvent.click(document.getElementById('plugin-task-trigger')!) // The popover content should be visible (PluginTaskList) - expect(document.querySelector('.w-\\[360px\\]')).toBeInTheDocument() + // The popover content should be visible (PluginTaskList) + expect(document.querySelector('.w-\\[360px\\]'))!.toBeInTheDocument() }) it('should not toggle when status does not allow', () => { @@ -647,7 +651,8 @@ describe('PluginTasks Component', () => { render() // Component should still render - expect(document.getElementById('plugin-task-trigger')).toBeInTheDocument() + // Component should still render + expect(document.getElementById('plugin-task-trigger'))!.toBeInTheDocument() }) }) @@ -665,7 +670,7 @@ describe('PluginTasks Component', () => { // Wait for popover content to render await waitFor(() => { - expect(document.querySelector('.w-\\[360px\\]')).toBeInTheDocument() + expect(document.querySelector('.w-\\[360px\\]'))!.toBeInTheDocument() }) // Find and click clear all button @@ -691,13 +696,13 @@ describe('PluginTasks Component', () => { fireEvent.click(document.getElementById('plugin-task-trigger')!) await waitFor(() => { - expect(document.querySelector('.w-\\[360px\\]')).toBeInTheDocument() + expect(document.querySelector('.w-\\[360px\\]'))!.toBeInTheDocument() }) // Find and click the clear all button in error section const clearButtons = screen.getAllByRole('button') if (clearButtons.length > 0) - fireEvent.click(clearButtons[0]) + fireEvent.click(clearButtons[0]!) await waitFor(() => { expect(mockMutateAsync).toHaveBeenCalled() @@ -719,13 +724,13 @@ describe('PluginTasks Component', () => { fireEvent.click(document.getElementById('plugin-task-trigger')!) await waitFor(() => { - expect(document.querySelector('.w-\\[360px\\]')).toBeInTheDocument() + expect(document.querySelector('.w-\\[360px\\]'))!.toBeInTheDocument() }) // Find and click individual clear button (usually the last one) const clearButtons = screen.getAllByRole('button') const individualClearButton = clearButtons[clearButtons.length - 1] - fireEvent.click(individualClearButton) + fireEvent.click(individualClearButton!) await waitFor(() => { expect(mockMutateAsync).toHaveBeenCalledWith({ @@ -750,7 +755,7 @@ describe('PluginTasks Component', () => { render() - expect(document.getElementById('plugin-task-trigger')).toBeInTheDocument() + expect(document.getElementById('plugin-task-trigger'))!.toBeInTheDocument() }) it('should handle many plugins', () => { @@ -763,7 +768,7 @@ describe('PluginTasks Component', () => { render() - expect(document.getElementById('plugin-task-trigger')).toBeInTheDocument() + expect(document.getElementById('plugin-task-trigger'))!.toBeInTheDocument() }) it('should handle plugins with empty labels', () => { @@ -775,7 +780,7 @@ describe('PluginTasks Component', () => { render() - expect(document.getElementById('plugin-task-trigger')).toBeInTheDocument() + expect(document.getElementById('plugin-task-trigger'))!.toBeInTheDocument() }) it('should handle plugins with long messages', () => { @@ -790,7 +795,7 @@ describe('PluginTasks Component', () => { // Open popover fireEvent.click(document.getElementById('plugin-task-trigger')!) - expect(document.querySelector('.w-\\[360px\\]')).toBeInTheDocument() + expect(document.querySelector('.w-\\[360px\\]'))!.toBeInTheDocument() }) }) }) @@ -809,13 +814,13 @@ describe('PluginTasks Integration', () => { const { rerender } = render() - expect(document.getElementById('plugin-task-trigger')).toBeInTheDocument() + expect(document.getElementById('plugin-task-trigger'))!.toBeInTheDocument() // Simulate completion by re-rendering with success setupMocks([createMockPlugin({ status: TaskStatus.success })]) rerender() - expect(document.getElementById('plugin-task-trigger')).toBeInTheDocument() + expect(document.getElementById('plugin-task-trigger'))!.toBeInTheDocument() }) it('should show correct UI flow from installing to failure', async () => { @@ -824,13 +829,13 @@ describe('PluginTasks Integration', () => { const { rerender } = render() - expect(document.getElementById('plugin-task-trigger')).toBeInTheDocument() + expect(document.getElementById('plugin-task-trigger'))!.toBeInTheDocument() // Simulate failure by re-rendering with failed setupMocks([createMockPlugin({ status: TaskStatus.failed, message: 'Network error' })]) rerender() - expect(document.getElementById('plugin-task-trigger')).toBeInTheDocument() + expect(document.getElementById('plugin-task-trigger'))!.toBeInTheDocument() }) it('should handle mixed status during installation', () => { diff --git a/web/app/components/plugins/plugin-page/plugin-tasks/components/__tests__/plugin-section.spec.tsx b/web/app/components/plugins/plugin-page/plugin-tasks/components/__tests__/plugin-section.spec.tsx index fb7429e9be..0c724000dd 100644 --- a/web/app/components/plugins/plugin-page/plugin-tasks/components/__tests__/plugin-section.spec.tsx +++ b/web/app/components/plugins/plugin-page/plugin-tasks/components/__tests__/plugin-section.spec.tsx @@ -41,15 +41,15 @@ describe('PluginSection', () => { it('should render title and count', () => { render() - expect(screen.getByText(/installing plugins/i)).toBeInTheDocument() + expect(screen.getByText(/installing plugins/i))!.toBeInTheDocument() expect(screen.getByText(/installing plugins/i).textContent).toContain('2') }) it('should render all plugin items', () => { render() - expect(screen.getByText('PluginA')).toBeInTheDocument() - expect(screen.getByText('PluginB')).toBeInTheDocument() + expect(screen.getByText('PluginA'))!.toBeInTheDocument() + expect(screen.getByText('PluginB'))!.toBeInTheDocument() }) it('should render status icons for each plugin', () => { @@ -72,14 +72,14 @@ describe('PluginSection', () => { const plugins = [createPlugin('p1', 'PluginA', 'Custom message')] render() - expect(screen.getByText('Custom message')).toBeInTheDocument() + expect(screen.getByText('Custom message'))!.toBeInTheDocument() }) it('should use defaultStatusText when plugin has no message', () => { const plugins = [createPlugin('p1', 'PluginA', '')] render() - expect(screen.getByText('Default status')).toBeInTheDocument() + expect(screen.getByText('Default status'))!.toBeInTheDocument() }) it('should apply statusClassName to items', () => { @@ -104,7 +104,7 @@ describe('PluginSection', () => { />, ) - expect(screen.getByRole('button', { name: /clear all/i })).toBeInTheDocument() + expect(screen.getByRole('button', { name: /clear all/i }))!.toBeInTheDocument() }) it('should not render headerAction when not provided', () => { @@ -123,8 +123,8 @@ describe('PluginSection', () => { />, ) - expect(screen.getByRole('button', { name: /action plugina/i })).toBeInTheDocument() - expect(screen.getByRole('button', { name: /action pluginb/i })).toBeInTheDocument() + expect(screen.getByRole('button', { name: /action plugina/i }))!.toBeInTheDocument() + expect(screen.getByRole('button', { name: /action pluginb/i }))!.toBeInTheDocument() }) }) @@ -140,7 +140,7 @@ describe('PluginSection', () => { // Clear buttons are rendered when onClearSingle is provided const clearButtons = screen.getAllByRole('button') - fireEvent.click(clearButtons[0]) + fireEvent.click(clearButtons[0]!) expect(onClearSingle).toHaveBeenCalledWith('task-1', 'p1') }) @@ -157,8 +157,8 @@ describe('PluginSection', () => { const plugins = [createPlugin('p1', 'Solo')] render() - expect(screen.getByText('Solo')).toBeInTheDocument() - expect(screen.getByText(/solo/i).closest('.max-h-\\[300px\\]')).toBeInTheDocument() + expect(screen.getByText('Solo'))!.toBeInTheDocument() + expect(screen.getByText(/solo/i).closest('.max-h-\\[300px\\]'))!.toBeInTheDocument() }) }) }) diff --git a/web/app/components/plugins/plugin-page/plugin-tasks/components/__tests__/plugin-task-list.spec.tsx b/web/app/components/plugins/plugin-page/plugin-tasks/components/__tests__/plugin-task-list.spec.tsx index 2d0d3f7fd6..07a651e2c5 100644 --- a/web/app/components/plugins/plugin-page/plugin-tasks/components/__tests__/plugin-task-list.spec.tsx +++ b/web/app/components/plugins/plugin-page/plugin-tasks/components/__tests__/plugin-task-list.spec.tsx @@ -67,7 +67,7 @@ describe('PluginTaskList', () => { it('should render empty container when no plugins', () => { const { container } = render() const wrapper = container.firstElementChild! - expect(wrapper).toBeInTheDocument() + expect(wrapper)!.toBeInTheDocument() expect(wrapper.children).toHaveLength(0) }) @@ -77,9 +77,9 @@ describe('PluginTaskList', () => { // Header contains the title text const headers = container.querySelectorAll('.system-sm-semibold-uppercase') expect(headers).toHaveLength(1) - expect(headers[0].textContent).toContain('plugin.task.installing') - expect(screen.getByText('OpenAI')).toBeInTheDocument() - expect(screen.getByText('Anthropic')).toBeInTheDocument() + expect(headers[0]!.textContent).toContain('plugin.task.installing') + expect(screen.getByText('OpenAI'))!.toBeInTheDocument() + expect(screen.getByText('Anthropic'))!.toBeInTheDocument() }) it('should render success section when success plugins exist', () => { @@ -87,8 +87,8 @@ describe('PluginTaskList', () => { const headers = container.querySelectorAll('.system-sm-semibold-uppercase') expect(headers).toHaveLength(1) - expect(headers[0].textContent).toContain('plugin.task.installed') - expect(screen.getByText('Google')).toBeInTheDocument() + expect(headers[0]!.textContent).toContain('plugin.task.installed') + expect(screen.getByText('Google'))!.toBeInTheDocument() }) it('should render error section when error plugins exist', () => { @@ -96,8 +96,8 @@ describe('PluginTaskList', () => { const headers = container.querySelectorAll('.system-sm-semibold-uppercase') expect(headers).toHaveLength(1) - expect(headers[0].textContent).toContain('plugin.task.installedError') - expect(screen.getByText('DALLE')).toBeInTheDocument() + expect(headers[0]!.textContent).toContain('plugin.task.installedError') + expect(screen.getByText('DALLE'))!.toBeInTheDocument() }) it('should render all three sections simultaneously', () => { @@ -110,9 +110,9 @@ describe('PluginTaskList', () => { />, ) - expect(screen.getByText('OpenAI')).toBeInTheDocument() - expect(screen.getByText('Google')).toBeInTheDocument() - expect(screen.getByText('DALLE')).toBeInTheDocument() + expect(screen.getByText('OpenAI'))!.toBeInTheDocument() + expect(screen.getByText('Google'))!.toBeInTheDocument() + expect(screen.getByText('DALLE'))!.toBeInTheDocument() }) }) @@ -183,6 +183,37 @@ describe('PluginTaskList', () => { it('should not render clear buttons for running plugins', () => { render() + // Running section has no headerAction and no onClearSingle + // Running section has no headerAction and no onClearSingle + // Running section has no headerAction and no onClearSingle + // Running section has no headerAction and no onClearSingle + // Running section has no headerAction and no onClearSingle + // Running section has no headerAction and no onClearSingle + // Running section has no headerAction and no onClearSingle + // Running section has no headerAction and no onClearSingle + // Running section has no headerAction and no onClearSingle + // Running section has no headerAction and no onClearSingle + // Running section has no headerAction and no onClearSingle + // Running section has no headerAction and no onClearSingle + // Running section has no headerAction and no onClearSingle + // Running section has no headerAction and no onClearSingle + // Running section has no headerAction and no onClearSingle + // Running section has no headerAction and no onClearSingle + // Running section has no headerAction and no onClearSingle + // Running section has no headerAction and no onClearSingle + // Running section has no headerAction and no onClearSingle + // Running section has no headerAction and no onClearSingle + // Running section has no headerAction and no onClearSingle + // Running section has no headerAction and no onClearSingle + // Running section has no headerAction and no onClearSingle + // Running section has no headerAction and no onClearSingle + // Running section has no headerAction and no onClearSingle + // Running section has no headerAction and no onClearSingle + // Running section has no headerAction and no onClearSingle + // Running section has no headerAction and no onClearSingle + // Running section has no headerAction and no onClearSingle + // Running section has no headerAction and no onClearSingle + // Running section has no headerAction and no onClearSingle // Running section has no headerAction and no onClearSingle expect(screen.queryByText(/plugin\.task\.clearAll/)).not.toBeInTheDocument() }) @@ -243,9 +274,9 @@ describe('PluginTaskList', () => { render() - expect(screen.getByText('PluginA')).toBeInTheDocument() - expect(screen.getByText('PluginB')).toBeInTheDocument() - expect(screen.getByText('PluginC')).toBeInTheDocument() + expect(screen.getByText('PluginA'))!.toBeInTheDocument() + expect(screen.getByText('PluginB'))!.toBeInTheDocument() + expect(screen.getByText('PluginC'))!.toBeInTheDocument() }) }) }) diff --git a/web/app/components/plugins/plugin-page/plugin-tasks/components/error-plugin-item.tsx b/web/app/components/plugins/plugin-page/plugin-tasks/components/error-plugin-item.tsx index 81f559c9aa..ef857cb6c6 100644 --- a/web/app/components/plugins/plugin-page/plugin-tasks/components/error-plugin-item.tsx +++ b/web/app/components/plugins/plugin-page/plugin-tasks/components/error-plugin-item.tsx @@ -30,14 +30,14 @@ const ErrorPluginItem: FC = ({ plugin, getIconUrl, languag const [org, name] = parts setIsFetching(true) try { - const response = await fetchPluginInfoFromMarketPlace({ org, name }) + const response = await fetchPluginInfoFromMarketPlace({ org: org!, name: name! }) const info = response.data.plugin const manifest: Plugin = { plugin_id: plugin.plugin_id, type: info.category as Plugin['type'], category: info.category, - name, - org, + name: name!, + org: org!, version: info.latest_version, latest_version: info.latest_version, latest_package_identifier: info.latest_package_identifier, diff --git a/web/app/components/plugins/plugin-page/use-uploader.ts b/web/app/components/plugins/plugin-page/use-uploader.ts index 7df1cb95e3..17699e7b9d 100644 --- a/web/app/components/plugins/plugin-page/use-uploader.ts +++ b/web/app/components/plugins/plugin-page/use-uploader.ts @@ -38,7 +38,7 @@ export const useUploader = ({ onFileChange, containerRef, enabled = true }: Uplo return const files = Array.from(e.dataTransfer.files) if (files.length > 0) - onFileChange(files[0]) + onFileChange(files[0]!) } const fileChangeHandle = enabled diff --git a/web/app/components/plugins/reference-setting-modal/__tests__/index.spec.tsx b/web/app/components/plugins/reference-setting-modal/__tests__/index.spec.tsx index 26eeb7fe03..f71780e2df 100644 --- a/web/app/components/plugins/reference-setting-modal/__tests__/index.spec.tsx +++ b/web/app/components/plugins/reference-setting-modal/__tests__/index.spec.tsx @@ -148,7 +148,8 @@ describe('reference-setting-modal', () => { render() // Assert - expect(screen.getByText('plugin.privilege.title')).toBeInTheDocument() + // Assert + expect(screen.getByText('plugin.privilege.title'))!.toBeInTheDocument() }) it('should render install permission section', () => { @@ -156,7 +157,8 @@ describe('reference-setting-modal', () => { render() // Assert - expect(screen.getByText('plugin.privilege.whoCanInstall')).toBeInTheDocument() + // Assert + expect(screen.getByText('plugin.privilege.whoCanInstall'))!.toBeInTheDocument() }) it('should render debug permission section', () => { @@ -164,7 +166,8 @@ describe('reference-setting-modal', () => { render() // Assert - expect(screen.getByText('plugin.privilege.whoCanDebug')).toBeInTheDocument() + // Assert + expect(screen.getByText('plugin.privilege.whoCanDebug'))!.toBeInTheDocument() }) it('should render all permission options for install', () => { @@ -180,8 +183,9 @@ describe('reference-setting-modal', () => { render() // Assert - expect(screen.getByText('common.operation.cancel')).toBeInTheDocument() - expect(screen.getByText('common.operation.save')).toBeInTheDocument() + // Assert + expect(screen.getByText('common.operation.cancel'))!.toBeInTheDocument() + expect(screen.getByText('common.operation.save'))!.toBeInTheDocument() }) it('should render AutoUpdateSetting when marketplace is enabled', () => { @@ -192,7 +196,8 @@ describe('reference-setting-modal', () => { render() // Assert - expect(screen.getByTestId('auto-update-setting')).toBeInTheDocument() + // Assert + expect(screen.getByTestId('auto-update-setting'))!.toBeInTheDocument() }) it('should not render AutoUpdateSetting when marketplace is disabled', () => { @@ -202,6 +207,37 @@ describe('reference-setting-modal', () => { // Act render() + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert + // Assert // Assert expect(screen.queryByTestId('auto-update-setting')).not.toBeInTheDocument() }) @@ -211,7 +247,8 @@ describe('reference-setting-modal', () => { render() // Assert - expect(screen.getByTestId('modal-close')).toBeInTheDocument() + // Assert + expect(screen.getByTestId('modal-close'))!.toBeInTheDocument() }) }) @@ -230,11 +267,11 @@ describe('reference-setting-modal', () => { // Assert - admin option should be selected for install (first one) const adminOptions = screen.getAllByTestId('option-card-plugin.privilege.admins') - expect(adminOptions[0]).toHaveAttribute('aria-pressed', 'true') // Install permission + expect(adminOptions[0])!.toHaveAttribute('aria-pressed', 'true') // Install permission // Assert - noOne option should be selected for debug (second one) const noOneOptions = screen.getAllByTestId('option-card-plugin.privilege.noone') - expect(noOneOptions[1]).toHaveAttribute('aria-pressed', 'true') // Debug permission + expect(noOneOptions[1])!.toHaveAttribute('aria-pressed', 'true') // Debug permission }) it('should update tempPrivilege when permission option is clicked', () => { @@ -243,10 +280,11 @@ describe('reference-setting-modal', () => { // Act - click on "No One" for install permission const noOneOptions = screen.getAllByTestId('option-card-plugin.privilege.noone') - fireEvent.click(noOneOptions[0]) // First one is for install permission + fireEvent.click(noOneOptions[0]!) // First one is for install permission // Assert - the option should now be selected - expect(noOneOptions[0]).toHaveAttribute('aria-pressed', 'true') + // Assert - the option should now be selected + expect(noOneOptions[0])!.toHaveAttribute('aria-pressed', 'true') }) it('should initialize with payload auto_upgrade values', () => { @@ -261,7 +299,8 @@ describe('reference-setting-modal', () => { render() // Assert - expect(screen.getByTestId('auto-update-strategy')).toHaveTextContent('latest') + // Assert + expect(screen.getByTestId('auto-update-strategy'))!.toHaveTextContent('latest') }) it('should use default auto_upgrade when payload.auto_upgrade is undefined', () => { @@ -275,7 +314,8 @@ describe('reference-setting-modal', () => { render() // Assert - should use default value (disabled) - expect(screen.getByTestId('auto-update-strategy')).toHaveTextContent('disabled') + // Assert - should use default value (disabled) + expect(screen.getByTestId('auto-update-strategy'))!.toHaveTextContent('disabled') }) }) @@ -351,10 +391,11 @@ describe('reference-setting-modal', () => { // Click Everyone for install permission const everyoneOptions = screen.getAllByTestId('option-card-plugin.privilege.everyone') - fireEvent.click(everyoneOptions[0]) + fireEvent.click(everyoneOptions[0]!) // Assert - expect(everyoneOptions[0]).toHaveAttribute('aria-pressed', 'true') + // Assert + expect(everyoneOptions[0])!.toHaveAttribute('aria-pressed', 'true') }) it('should update debug permission when Admins Only option is clicked', () => { @@ -371,10 +412,11 @@ describe('reference-setting-modal', () => { // Click Admins Only for debug permission (second set of options) const adminOptions = screen.getAllByTestId('option-card-plugin.privilege.admins') - fireEvent.click(adminOptions[1]) // Second one is for debug permission + fireEvent.click(adminOptions[1]!) // Second one is for debug permission // Assert - expect(adminOptions[1]).toHaveAttribute('aria-pressed', 'true') + // Assert + expect(adminOptions[1])!.toHaveAttribute('aria-pressed', 'true') }) it('should update auto_upgrade config when changed in AutoUpdateSetting', async () => { @@ -410,7 +452,8 @@ describe('reference-setting-modal', () => { rerender() // Assert - component should render without issues - expect(screen.getByText('plugin.privilege.title')).toBeInTheDocument() + // Assert - component should render without issues + expect(screen.getByText('plugin.privilege.title'))!.toBeInTheDocument() }) it('handleSave should be memoized with useCallback', async () => { @@ -434,10 +477,11 @@ describe('reference-setting-modal', () => { // Act - click install permission option const everyoneOptions = screen.getAllByTestId('option-card-plugin.privilege.everyone') - fireEvent.click(everyoneOptions[0]) + fireEvent.click(everyoneOptions[0]!) // Assert - install permission should be updated - expect(everyoneOptions[0]).toHaveAttribute('aria-pressed', 'true') + // Assert - install permission should be updated + expect(everyoneOptions[0])!.toHaveAttribute('aria-pressed', 'true') }) }) @@ -456,7 +500,7 @@ describe('reference-setting-modal', () => { // Act & Assert - should not crash render() - expect(screen.getByText('plugin.privilege.title')).toBeInTheDocument() + expect(screen.getByText('plugin.privilege.title'))!.toBeInTheDocument() }) it('should handle undefined permission values', () => { @@ -471,7 +515,7 @@ describe('reference-setting-modal', () => { // Assert - should use default PermissionType.noOne const noOneOptions = screen.getAllByTestId('option-card-plugin.privilege.noone') - expect(noOneOptions[0]).toHaveAttribute('aria-pressed', 'true') + expect(noOneOptions[0])!.toHaveAttribute('aria-pressed', 'true') }) it('should handle missing install_permission', () => { @@ -487,7 +531,8 @@ describe('reference-setting-modal', () => { render() // Assert - should fall back to PermissionType.noOne - expect(screen.getByText('plugin.privilege.title')).toBeInTheDocument() + // Assert - should fall back to PermissionType.noOne + expect(screen.getByText('plugin.privilege.title'))!.toBeInTheDocument() }) it('should handle missing debug_permission', () => { @@ -503,7 +548,8 @@ describe('reference-setting-modal', () => { render() // Assert - should fall back to PermissionType.noOne - expect(screen.getByText('plugin.privilege.title')).toBeInTheDocument() + // Assert - should fall back to PermissionType.noOne + expect(screen.getByText('plugin.privilege.title'))!.toBeInTheDocument() }) it('should handle slow async onSave gracefully', async () => { @@ -555,7 +601,8 @@ describe('reference-setting-modal', () => { const { unmount } = render() // Assert - should render without crashing - expect(screen.getByText('plugin.privilege.title')).toBeInTheDocument() + // Assert - should render without crashing + expect(screen.getByText('plugin.privilege.title'))!.toBeInTheDocument() unmount() }) @@ -582,7 +629,8 @@ describe('reference-setting-modal', () => { const { unmount } = render() // Assert - expect(screen.getByTestId('auto-update-strategy')).toHaveTextContent(strategy) + // Assert + expect(screen.getByTestId('auto-update-strategy'))!.toHaveTextContent(strategy) unmount() }) @@ -608,7 +656,8 @@ describe('reference-setting-modal', () => { const { unmount } = render() // Assert - expect(screen.getByTestId('auto-update-mode')).toHaveTextContent(mode) + // Assert + expect(screen.getByTestId('auto-update-mode'))!.toHaveTextContent(mode) unmount() }) @@ -631,7 +680,7 @@ describe('reference-setting-modal', () => { // Change install permission to noOne const noOneOptions = screen.getAllByTestId('option-card-plugin.privilege.noone') - fireEvent.click(noOneOptions[0]) + fireEvent.click(noOneOptions[0]!) // Save fireEvent.click(screen.getByText('common.operation.save')) @@ -662,7 +711,7 @@ describe('reference-setting-modal', () => { // Change debug permission to noOne const noOneOptions = screen.getAllByTestId('option-card-plugin.privilege.noone') - fireEvent.click(noOneOptions[1]) // Second one is for debug + fireEvent.click(noOneOptions[1]!) // Second one is for debug // Save fireEvent.click(screen.getByText('common.operation.save')) @@ -691,7 +740,7 @@ describe('reference-setting-modal', () => { // Change install permission const everyoneOptions = screen.getAllByTestId('option-card-plugin.privilege.everyone') - fireEvent.click(everyoneOptions[0]) + fireEvent.click(everyoneOptions[0]!) // Save fireEvent.click(screen.getByText('common.operation.save')) @@ -717,7 +766,7 @@ describe('reference-setting-modal', () => { // Assert const modal = screen.getByTestId('modal') - expect(modal).toHaveClass('w-[620px]', 'max-w-[620px]', 'p-0!') + expect(modal)!.toHaveClass('w-[620px]', 'max-w-[620px]', 'p-0!') }) it('should pass isShow=true to Modal', () => { @@ -725,7 +774,8 @@ describe('reference-setting-modal', () => { render() // Assert - modal should be visible - expect(screen.getByTestId('modal')).toBeInTheDocument() + // Assert - modal should be visible + expect(screen.getByTestId('modal'))!.toBeInTheDocument() }) }) @@ -736,8 +786,8 @@ describe('reference-setting-modal', () => { // Assert - check order by getting all section labels const labels = screen.getAllByText(/plugin\.privilege\.whoCan/) - expect(labels[0]).toHaveTextContent('plugin.privilege.whoCanInstall') - expect(labels[1]).toHaveTextContent('plugin.privilege.whoCanDebug') + expect(labels[0])!.toHaveTextContent('plugin.privilege.whoCanInstall') + expect(labels[1])!.toHaveTextContent('plugin.privilege.whoCanDebug') }) it('should render three options per permission section', () => { @@ -762,8 +812,8 @@ describe('reference-setting-modal', () => { const cancelButton = screen.getByText('common.operation.cancel') const saveButton = screen.getByText('common.operation.save') - expect(cancelButton).toBeInTheDocument() - expect(saveButton).toBeInTheDocument() + expect(cancelButton)!.toBeInTheDocument() + expect(saveButton)!.toBeInTheDocument() }) }) }) @@ -797,11 +847,11 @@ describe('reference-setting-modal', () => { // Change install permission to Everyone const everyoneOptions = screen.getAllByTestId('option-card-plugin.privilege.everyone') - fireEvent.click(everyoneOptions[0]) + fireEvent.click(everyoneOptions[0]!) // Change debug permission to Admins Only const adminOptions = screen.getAllByTestId('option-card-plugin.privilege.admins') - fireEvent.click(adminOptions[1]) + fireEvent.click(adminOptions[1]!) // Change auto-update strategy fireEvent.click(screen.getByTestId('auto-update-change')) @@ -841,7 +891,7 @@ describe('reference-setting-modal', () => { // Make some changes const noOneOptions = screen.getAllByTestId('option-card-plugin.privilege.noone') - fireEvent.click(noOneOptions[0]) + fireEvent.click(noOneOptions[0]!) // Cancel fireEvent.click(screen.getByText('common.operation.cancel')) @@ -863,8 +913,9 @@ describe('reference-setting-modal', () => { render() // Assert - Labels are rendered correctly - expect(screen.getByText('plugin.privilege.whoCanInstall')).toBeInTheDocument() - expect(screen.getByText('plugin.privilege.whoCanDebug')).toBeInTheDocument() + // Assert - Labels are rendered correctly + expect(screen.getByText('plugin.privilege.whoCanInstall'))!.toBeInTheDocument() + expect(screen.getByText('plugin.privilege.whoCanDebug'))!.toBeInTheDocument() }) }) }) diff --git a/web/app/components/plugins/update-plugin/plugin-version-picker.tsx b/web/app/components/plugins/update-plugin/plugin-version-picker.tsx index 0266c05069..d590f0c9eb 100644 --- a/web/app/components/plugins/update-plugin/plugin-version-picker.tsx +++ b/web/app/components/plugins/update-plugin/plugin-version-picker.tsx @@ -107,7 +107,7 @@ const PluginVersionPicker: FC = ({
{version.version}
{currentVersion === version.version && }
-
{formatDate(version.created_at, format)}
+
{formatDate(version.created_at, format!)}
))}
diff --git a/web/app/components/rag-pipeline/components/chunk-card-list/__tests__/index.spec.tsx b/web/app/components/rag-pipeline/components/chunk-card-list/__tests__/index.spec.tsx index 2fab56f0ea..4827481579 100644 --- a/web/app/components/rag-pipeline/components/chunk-card-list/__tests__/index.spec.tsx +++ b/web/app/components/rag-pipeline/components/chunk-card-list/__tests__/index.spec.tsx @@ -61,15 +61,15 @@ describe('QAItem', () => { it('should render question type with Q prefix', () => { render() - expect(screen.getByText('Q')).toBeInTheDocument() - expect(screen.getByText('What is this?')).toBeInTheDocument() + expect(screen.getByText('Q'))!.toBeInTheDocument() + expect(screen.getByText('What is this?'))!.toBeInTheDocument() }) it('should render answer type with A prefix', () => { render() - expect(screen.getByText('A')).toBeInTheDocument() - expect(screen.getByText('This is the answer.')).toBeInTheDocument() + expect(screen.getByText('A'))!.toBeInTheDocument() + expect(screen.getByText('This is the answer.'))!.toBeInTheDocument() }) }) @@ -77,7 +77,7 @@ describe('QAItem', () => { it('should render with empty text', () => { render() - expect(screen.getByText('Q')).toBeInTheDocument() + expect(screen.getByText('Q'))!.toBeInTheDocument() }) it('should render with long text content', () => { @@ -85,7 +85,7 @@ describe('QAItem', () => { render() - expect(screen.getByText(longText)).toBeInTheDocument() + expect(screen.getByText(longText))!.toBeInTheDocument() }) it('should render with special characters in text', () => { @@ -93,7 +93,7 @@ describe('QAItem', () => { render() - expect(screen.getByText(specialText)).toBeInTheDocument() + expect(screen.getByText(specialText))!.toBeInTheDocument() }) }) @@ -101,11 +101,11 @@ describe('QAItem', () => { it('should be memoized with React.memo', () => { const { rerender } = render() - expect(screen.getByText('Q')).toBeInTheDocument() - expect(screen.getByText('Test')).toBeInTheDocument() + expect(screen.getByText('Q'))!.toBeInTheDocument() + expect(screen.getByText('Test'))!.toBeInTheDocument() rerender() - expect(screen.getByText('Q')).toBeInTheDocument() + expect(screen.getByText('Q'))!.toBeInTheDocument() }) }) }) @@ -120,14 +120,14 @@ describe('ChunkCard', () => { render( , ) - expect(screen.getByText('This is the first chunk of text content.')).toBeInTheDocument() - expect(screen.getByText(/Chunk-01/)).toBeInTheDocument() + expect(screen.getByText('This is the first chunk of text content.'))!.toBeInTheDocument() + expect(screen.getByText(/Chunk-01/))!.toBeInTheDocument() }) it('should render QA chunk type with question and answer', () => { @@ -145,10 +145,10 @@ describe('ChunkCard', () => { />, ) - expect(screen.getByText('Q')).toBeInTheDocument() - expect(screen.getByText('What is React?')).toBeInTheDocument() - expect(screen.getByText('A')).toBeInTheDocument() - expect(screen.getByText('React is a JavaScript library.')).toBeInTheDocument() + expect(screen.getByText('Q'))!.toBeInTheDocument() + expect(screen.getByText('What is React?'))!.toBeInTheDocument() + expect(screen.getByText('A'))!.toBeInTheDocument() + expect(screen.getByText('React is a JavaScript library.'))!.toBeInTheDocument() }) it('should render parent-child chunk type with child contents', () => { @@ -164,10 +164,10 @@ describe('ChunkCard', () => { />, ) - expect(screen.getByText('Child 1 content')).toBeInTheDocument() - expect(screen.getByText('Child 2 content')).toBeInTheDocument() - expect(screen.getByText('C-1')).toBeInTheDocument() - expect(screen.getByText('C-2')).toBeInTheDocument() + expect(screen.getByText('Child 1 content'))!.toBeInTheDocument() + expect(screen.getByText('Child 2 content'))!.toBeInTheDocument() + expect(screen.getByText('C-1'))!.toBeInTheDocument() + expect(screen.getByText('C-2'))!.toBeInTheDocument() }) }) @@ -183,7 +183,7 @@ describe('ChunkCard', () => { />, ) - expect(screen.getByText(/Parent-Chunk-01/)).toBeInTheDocument() + expect(screen.getByText(/Parent-Chunk-01/))!.toBeInTheDocument() }) it('should hide segment index tag for full-doc mode', () => { @@ -205,13 +205,13 @@ describe('ChunkCard', () => { render( , ) - expect(screen.getByText(/Chunk-05/)).toBeInTheDocument() + expect(screen.getByText(/Chunk-05/))!.toBeInTheDocument() }) }) @@ -220,26 +220,26 @@ describe('ChunkCard', () => { render( , ) - expect(screen.getByText(/1,234/)).toBeInTheDocument() + expect(screen.getByText(/1,234/))!.toBeInTheDocument() }) it('should display word count with character translation key', () => { render( , ) - expect(screen.getByText(/100\s+(?:\S.*)?characters/)).toBeInTheDocument() + expect(screen.getByText(/100\s+(?:\S.*)?characters/))!.toBeInTheDocument() }) it('should not display word count info for full-doc mode', () => { @@ -262,39 +262,39 @@ describe('ChunkCard', () => { render( , ) - expect(screen.getByText(/Chunk-42/)).toBeInTheDocument() + expect(screen.getByText(/Chunk-42/))!.toBeInTheDocument() }) it('should handle string position ID', () => { render( , ) - expect(screen.getByText(/Chunk-99/)).toBeInTheDocument() + expect(screen.getByText(/Chunk-99/))!.toBeInTheDocument() }) it('should pad single digit position ID', () => { render( , ) - expect(screen.getByText(/Chunk-03/)).toBeInTheDocument() + expect(screen.getByText(/Chunk-03/))!.toBeInTheDocument() }) }) @@ -310,7 +310,7 @@ describe('ChunkCard', () => { />, ) - expect(screen.getByText(/Parent-Chunk/)).toBeInTheDocument() + expect(screen.getByText(/Parent-Chunk/))!.toBeInTheDocument() rerender( { />, ) - expect(screen.getByText('Initial content')).toBeInTheDocument() + expect(screen.getByText('Initial content'))!.toBeInTheDocument() rerender( { />, ) - expect(screen.getByText('Updated content')).toBeInTheDocument() + expect(screen.getByText('Updated content'))!.toBeInTheDocument() expect(screen.queryByText('Initial content')).not.toBeInTheDocument() }) @@ -364,7 +364,7 @@ describe('ChunkCard', () => { />, ) - expect(screen.getByText('Text content')).toBeInTheDocument() + expect(screen.getByText('Text content'))!.toBeInTheDocument() const qaContent: QAChunk = { question: 'Q?', answer: 'A.' } rerender( @@ -376,8 +376,8 @@ describe('ChunkCard', () => { />, ) - expect(screen.getByText('Q')).toBeInTheDocument() - expect(screen.getByText('Q?')).toBeInTheDocument() + expect(screen.getByText('Q'))!.toBeInTheDocument() + expect(screen.getByText('Q?'))!.toBeInTheDocument() }) }) @@ -393,7 +393,7 @@ describe('ChunkCard', () => { />, ) - expect(screen.getByText(/Parent-Chunk-01/)).toBeInTheDocument() + expect(screen.getByText(/Parent-Chunk-01/))!.toBeInTheDocument() }) it('should handle QA chunk with empty strings', () => { @@ -408,8 +408,8 @@ describe('ChunkCard', () => { />, ) - expect(screen.getByText('Q')).toBeInTheDocument() - expect(screen.getByText('A')).toBeInTheDocument() + expect(screen.getByText('Q'))!.toBeInTheDocument() + expect(screen.getByText('A'))!.toBeInTheDocument() }) it('should handle very long content', () => { @@ -425,20 +425,20 @@ describe('ChunkCard', () => { />, ) - expect(screen.getByText(longContent)).toBeInTheDocument() + expect(screen.getByText(longContent))!.toBeInTheDocument() }) it('should handle zero word count', () => { render( , ) - expect(screen.getByText(/0\s+(?:\S.*)?characters/)).toBeInTheDocument() + expect(screen.getByText(/0\s+(?:\S.*)?characters/))!.toBeInTheDocument() }) }) }) @@ -459,9 +459,9 @@ describe('ChunkCardList', () => { />, ) - expect(screen.getByText(chunks[0].content)).toBeInTheDocument() - expect(screen.getByText(chunks[1].content)).toBeInTheDocument() - expect(screen.getByText(chunks[2].content)).toBeInTheDocument() + expect(screen.getByText(chunks[0]!.content))!.toBeInTheDocument() + expect(screen.getByText(chunks[1]!.content))!.toBeInTheDocument() + expect(screen.getByText(chunks[2]!.content))!.toBeInTheDocument() }) it('should render parent-child chunks correctly', () => { @@ -475,9 +475,9 @@ describe('ChunkCardList', () => { />, ) - expect(screen.getByText('Child content 1')).toBeInTheDocument() - expect(screen.getByText('Child content 2')).toBeInTheDocument() - expect(screen.getByText('Another child 1')).toBeInTheDocument() + expect(screen.getByText('Child content 1'))!.toBeInTheDocument() + expect(screen.getByText('Child content 2'))!.toBeInTheDocument() + expect(screen.getByText('Another child 1'))!.toBeInTheDocument() }) it('should render QA chunks correctly', () => { @@ -490,10 +490,10 @@ describe('ChunkCardList', () => { />, ) - expect(screen.getByText('What is the answer to life?')).toBeInTheDocument() - expect(screen.getByText('The answer is 42.')).toBeInTheDocument() - expect(screen.getByText('How does this work?')).toBeInTheDocument() - expect(screen.getByText('It works by processing data.')).toBeInTheDocument() + expect(screen.getByText('What is the answer to life?'))!.toBeInTheDocument() + expect(screen.getByText('The answer is 42.'))!.toBeInTheDocument() + expect(screen.getByText('How does this work?'))!.toBeInTheDocument() + expect(screen.getByText('It works by processing data.'))!.toBeInTheDocument() }) }) @@ -511,8 +511,8 @@ describe('ChunkCardList', () => { />, ) - expect(screen.getByText('Chunk 1')).toBeInTheDocument() - expect(screen.getByText('Chunk 2')).toBeInTheDocument() + expect(screen.getByText('Chunk 1'))!.toBeInTheDocument() + expect(screen.getByText('Chunk 2'))!.toBeInTheDocument() }) it('should extract parent_child_chunks from ParentChildChunks for parentChild mode', () => { @@ -530,7 +530,7 @@ describe('ChunkCardList', () => { />, ) - expect(screen.getByText('Specific child')).toBeInTheDocument() + expect(screen.getByText('Specific child'))!.toBeInTheDocument() }) it('should extract qa_chunks from QAChunks for qa mode', () => { @@ -547,8 +547,8 @@ describe('ChunkCardList', () => { />, ) - expect(screen.getByText('Specific Q')).toBeInTheDocument() - expect(screen.getByText('Specific A')).toBeInTheDocument() + expect(screen.getByText('Specific Q'))!.toBeInTheDocument() + expect(screen.getByText('Specific A'))!.toBeInTheDocument() }) it('should update chunkList when chunkInfo changes', () => { @@ -561,7 +561,7 @@ describe('ChunkCardList', () => { />, ) - expect(screen.getByText('Initial chunk')).toBeInTheDocument() + expect(screen.getByText('Initial chunk'))!.toBeInTheDocument() const updatedChunks = createGeneralChunks([{ content: 'Updated chunk' }]) rerender( @@ -571,7 +571,7 @@ describe('ChunkCardList', () => { />, ) - expect(screen.getByText('Updated chunk')).toBeInTheDocument() + expect(screen.getByText('Updated chunk'))!.toBeInTheDocument() expect(screen.queryByText('Initial chunk')).not.toBeInTheDocument() }) }) @@ -587,7 +587,7 @@ describe('ChunkCardList', () => { />, ) - expect(screen.getByText(/5\s+(?:\S.*)?characters/)).toBeInTheDocument() + expect(screen.getByText(/5\s+(?:\S.*)?characters/))!.toBeInTheDocument() }) it('should calculate word count for parent-child chunks using parent_content length', () => { @@ -608,7 +608,7 @@ describe('ChunkCardList', () => { />, ) - expect(screen.getByText(/6\s+(?:\S.*)?characters/)).toBeInTheDocument() + expect(screen.getByText(/6\s+(?:\S.*)?characters/))!.toBeInTheDocument() }) it('should calculate word count for QA chunks using question + answer length', () => { @@ -625,7 +625,7 @@ describe('ChunkCardList', () => { />, ) - expect(screen.getByText(/5\s+(?:\S.*)?characters/)).toBeInTheDocument() + expect(screen.getByText(/5\s+(?:\S.*)?characters/))!.toBeInTheDocument() }) }) @@ -644,9 +644,9 @@ describe('ChunkCardList', () => { />, ) - expect(screen.getByText(/Chunk-01/)).toBeInTheDocument() - expect(screen.getByText(/Chunk-02/)).toBeInTheDocument() - expect(screen.getByText(/Chunk-03/)).toBeInTheDocument() + expect(screen.getByText(/Chunk-01/))!.toBeInTheDocument() + expect(screen.getByText(/Chunk-02/))!.toBeInTheDocument() + expect(screen.getByText(/Chunk-03/))!.toBeInTheDocument() }) }) @@ -662,7 +662,7 @@ describe('ChunkCardList', () => { />, ) - expect(container.firstChild).toHaveClass('custom-class') + expect(container.firstChild)!.toHaveClass('custom-class') }) it('should merge custom className with default classes', () => { @@ -676,10 +676,10 @@ describe('ChunkCardList', () => { />, ) - expect(container.firstChild).toHaveClass('flex') - expect(container.firstChild).toHaveClass('w-full') - expect(container.firstChild).toHaveClass('flex-col') - expect(container.firstChild).toHaveClass('my-custom-class') + expect(container.firstChild)!.toHaveClass('flex') + expect(container.firstChild)!.toHaveClass('w-full') + expect(container.firstChild)!.toHaveClass('flex-col') + expect(container.firstChild)!.toHaveClass('my-custom-class') }) it('should render without className prop', () => { @@ -692,8 +692,8 @@ describe('ChunkCardList', () => { />, ) - expect(container.firstChild).toHaveClass('flex') - expect(container.firstChild).toHaveClass('w-full') + expect(container.firstChild)!.toHaveClass('flex') + expect(container.firstChild)!.toHaveClass('w-full') }) }) @@ -738,7 +738,7 @@ describe('ChunkCardList', () => { />, ) - expect(screen.getByText(/Chunk-01/)).toBeInTheDocument() + expect(screen.getByText(/Chunk-01/))!.toBeInTheDocument() }) }) @@ -753,7 +753,7 @@ describe('ChunkCardList', () => { />, ) - expect(container.firstChild).toBeInTheDocument() + expect(container.firstChild)!.toBeInTheDocument() expect(container.firstChild?.childNodes.length).toBe(0) }) @@ -771,7 +771,7 @@ describe('ChunkCardList', () => { />, ) - expect(container.firstChild).toBeInTheDocument() + expect(container.firstChild)!.toBeInTheDocument() expect(container.firstChild?.childNodes.length).toBe(0) }) @@ -787,7 +787,7 @@ describe('ChunkCardList', () => { />, ) - expect(container.firstChild).toBeInTheDocument() + expect(container.firstChild)!.toBeInTheDocument() expect(container.firstChild?.childNodes.length).toBe(0) }) @@ -801,8 +801,8 @@ describe('ChunkCardList', () => { />, ) - expect(screen.getByText('Single chunk')).toBeInTheDocument() - expect(screen.getByText(/Chunk-01/)).toBeInTheDocument() + expect(screen.getByText('Single chunk'))!.toBeInTheDocument() + expect(screen.getByText(/Chunk-01/))!.toBeInTheDocument() }) it('should handle large number of chunks', () => { @@ -815,9 +815,9 @@ describe('ChunkCardList', () => { />, ) - expect(screen.getByText('Chunk number 1')).toBeInTheDocument() - expect(screen.getByText('Chunk number 100')).toBeInTheDocument() - expect(screen.getByText(/Chunk-100/)).toBeInTheDocument() + expect(screen.getByText('Chunk number 1'))!.toBeInTheDocument() + expect(screen.getByText('Chunk number 100'))!.toBeInTheDocument() + expect(screen.getByText(/Chunk-100/))!.toBeInTheDocument() }) }) @@ -861,15 +861,15 @@ describe('ChunkCardList Integration', () => { />, ) - expect(screen.getByText('First paragraph of the document.')).toBeInTheDocument() - expect(screen.getByText(/Chunk-01/)).toBeInTheDocument() - expect(screen.getByText(/32\s+(?:\S.*)?characters/)).toBeInTheDocument() + expect(screen.getByText('First paragraph of the document.'))!.toBeInTheDocument() + expect(screen.getByText(/Chunk-01/))!.toBeInTheDocument() + expect(screen.getByText(/32\s+(?:\S.*)?characters/))!.toBeInTheDocument() - expect(screen.getByText('Second paragraph with more information.')).toBeInTheDocument() - expect(screen.getByText(/Chunk-02/)).toBeInTheDocument() + expect(screen.getByText('Second paragraph with more information.'))!.toBeInTheDocument() + expect(screen.getByText(/Chunk-02/))!.toBeInTheDocument() - expect(screen.getByText('Final paragraph concluding the content.')).toBeInTheDocument() - expect(screen.getByText(/Chunk-03/)).toBeInTheDocument() + expect(screen.getByText('Final paragraph concluding the content.'))!.toBeInTheDocument() + expect(screen.getByText(/Chunk-03/))!.toBeInTheDocument() }) it('should render complete parent-child chunking workflow', () => { @@ -894,11 +894,11 @@ describe('ChunkCardList Integration', () => { />, ) - expect(screen.getByText('React components are building blocks.')).toBeInTheDocument() - expect(screen.getByText('Lifecycle methods control component behavior.')).toBeInTheDocument() - expect(screen.getByText('C-1')).toBeInTheDocument() - expect(screen.getByText('C-2')).toBeInTheDocument() - expect(screen.getByText(/Parent-Chunk-01/)).toBeInTheDocument() + expect(screen.getByText('React components are building blocks.'))!.toBeInTheDocument() + expect(screen.getByText('Lifecycle methods control component behavior.'))!.toBeInTheDocument() + expect(screen.getByText('C-1'))!.toBeInTheDocument() + expect(screen.getByText('C-2'))!.toBeInTheDocument() + expect(screen.getByText(/Parent-Chunk-01/))!.toBeInTheDocument() }) it('should render complete QA chunking workflow', () => { @@ -927,10 +927,10 @@ describe('ChunkCardList Integration', () => { expect(qLabels.length).toBe(2) expect(aLabels.length).toBe(2) - expect(screen.getByText('What is Dify?')).toBeInTheDocument() - expect(screen.getByText('Dify is an open-source LLM application development platform.')).toBeInTheDocument() - expect(screen.getByText('How do I get started?')).toBeInTheDocument() - expect(screen.getByText('You can start by installing the platform using Docker.')).toBeInTheDocument() + expect(screen.getByText('What is Dify?'))!.toBeInTheDocument() + expect(screen.getByText('Dify is an open-source LLM application development platform.'))!.toBeInTheDocument() + expect(screen.getByText('How do I get started?'))!.toBeInTheDocument() + expect(screen.getByText('You can start by installing the platform using Docker.'))!.toBeInTheDocument() }) }) @@ -946,7 +946,7 @@ describe('ChunkCardList Integration', () => { />, ) - expect(screen.getByText('Text content')).toBeInTheDocument() + expect(screen.getByText('Text content'))!.toBeInTheDocument() rerender( { ) expect(screen.queryByText('Text content')).not.toBeInTheDocument() - expect(screen.getByText('What is the answer to life?')).toBeInTheDocument() + expect(screen.getByText('What is the answer to life?'))!.toBeInTheDocument() }) it('should handle switching from text to parent-child type', () => { @@ -970,8 +970,8 @@ describe('ChunkCardList Integration', () => { />, ) - expect(screen.getByText('Simple text')).toBeInTheDocument() - expect(screen.getByText(/Chunk-01/)).toBeInTheDocument() + expect(screen.getByText('Simple text'))!.toBeInTheDocument() + expect(screen.getByText(/Chunk-01/))!.toBeInTheDocument() rerender( { it('should render panel without crashing', () => { render() - expect( - screen.getByText('datasetPipeline.inputFieldPanel.title'), - ).toBeInTheDocument() + expect(screen.getByText('datasetPipeline.inputFieldPanel.title'))!.toBeInTheDocument() }) it('should render panel title correctly', () => { render() - expect( - screen.getByText('datasetPipeline.inputFieldPanel.title'), - ).toBeInTheDocument() + expect(screen.getByText('datasetPipeline.inputFieldPanel.title'))!.toBeInTheDocument() }) it('should render panel description', () => { render() - expect( - screen.getByText('datasetPipeline.inputFieldPanel.description'), - ).toBeInTheDocument() + expect(screen.getByText('datasetPipeline.inputFieldPanel.description'))!.toBeInTheDocument() }) it('should render preview button', () => { render() - expect( - screen.getByText('datasetPipeline.operations.preview'), - ).toBeInTheDocument() + expect(screen.getByText('datasetPipeline.operations.preview'))!.toBeInTheDocument() }) it('should render close button', () => { render() const closeButton = screen.getByRole('button', { name: '' }) - expect(closeButton).toBeInTheDocument() + expect(closeButton)!.toBeInTheDocument() }) it('should render footer tip component', () => { render() - expect(screen.getByTestId('footer-tip')).toBeInTheDocument() + expect(screen.getByTestId('footer-tip'))!.toBeInTheDocument() }) it('should render unique inputs section title', () => { render() - expect( - screen.getByText('datasetPipeline.inputFieldPanel.uniqueInputs.title'), - ).toBeInTheDocument() + expect(screen.getByText('datasetPipeline.inputFieldPanel.uniqueInputs.title'))!.toBeInTheDocument() }) it('should render global inputs field list', () => { render() - expect(screen.getByTestId('field-list-shared')).toBeInTheDocument() - expect(screen.getByTestId('global-inputs-label')).toBeInTheDocument() + expect(screen.getByTestId('field-list-shared'))!.toBeInTheDocument() + expect(screen.getByTestId('global-inputs-label'))!.toBeInTheDocument() }) }) @@ -263,8 +253,8 @@ describe('InputFieldPanel', () => { render() - expect(screen.getByTestId('field-list-node-1')).toBeInTheDocument() - expect(screen.getByTestId('field-list-node-2')).toBeInTheDocument() + expect(screen.getByTestId('field-list-node-1'))!.toBeInTheDocument() + expect(screen.getByTestId('field-list-node-2'))!.toBeInTheDocument() }) it('should render datasource label for each node', () => { @@ -273,9 +263,7 @@ describe('InputFieldPanel', () => { render() - expect( - screen.getByTestId('datasource-label-My DataSource'), - ).toBeInTheDocument() + expect(screen.getByTestId('datasource-label-My DataSource'))!.toBeInTheDocument() }) it('should not render any datasource field lists when no nodes exist', () => { @@ -284,7 +272,7 @@ describe('InputFieldPanel', () => { render() expect(screen.queryByTestId('field-list-node-1')).not.toBeInTheDocument() - expect(screen.getByTestId('field-list-shared')).toBeInTheDocument() + expect(screen.getByTestId('field-list-shared'))!.toBeInTheDocument() }) it('should filter only DataSource type nodes', () => { @@ -303,10 +291,8 @@ describe('InputFieldPanel', () => { render() - expect(screen.getByTestId('field-list-ds-node')).toBeInTheDocument() - expect( - screen.queryByTestId('field-list-other-node'), - ).not.toBeInTheDocument() + expect(screen.getByTestId('field-list-ds-node'))!.toBeInTheDocument() + expect(screen.queryByTestId('field-list-other-node')).not.toBeInTheDocument() }) }) @@ -322,8 +308,8 @@ describe('InputFieldPanel', () => { render() - expect(screen.getByTestId('field-list-fields-count-node-1')).toHaveTextContent('2') - expect(screen.getByTestId('field-list-fields-count-shared')).toHaveTextContent('1') + expect(screen.getByTestId('field-list-fields-count-node-1'))!.toHaveTextContent('2') + expect(screen.getByTestId('field-list-fields-count-shared'))!.toHaveTextContent('1') }) it('should show zero fields for nodes without variables', () => { @@ -332,7 +318,7 @@ describe('InputFieldPanel', () => { render() - expect(screen.getByTestId('field-list-fields-count-node-1')).toHaveTextContent('0') + expect(screen.getByTestId('field-list-fields-count-node-1'))!.toHaveTextContent('0') }) it('should pass all variable names to field lists', () => { @@ -345,10 +331,10 @@ describe('InputFieldPanel', () => { render() - expect(screen.getByTestId('field-list-all-vars-node-1')).toHaveTextContent( + expect(screen.getByTestId('field-list-all-vars-node-1'))!.toHaveTextContent( 'var1,var2', ) - expect(screen.getByTestId('field-list-all-vars-shared')).toHaveTextContent( + expect(screen.getByTestId('field-list-all-vars-shared'))!.toHaveTextContent( 'var1,var2', ) }) @@ -386,7 +372,7 @@ describe('InputFieldPanel', () => { const previewButton = screen .getByText('datasetPipeline.operations.preview') .closest('button') - expect(previewButton).toBeDisabled() + expect(previewButton)!.toBeDisabled() }) it('should not disable preview button when not editing', () => { @@ -410,8 +396,8 @@ describe('InputFieldPanel', () => { const previewButton = screen .getByText('datasetPipeline.operations.preview') .closest('button') - expect(previewButton).toHaveClass('bg-state-accent-active') - expect(previewButton).toHaveClass('text-text-accent') + expect(previewButton)!.toHaveClass('bg-state-accent-active') + expect(previewButton)!.toHaveClass('text-text-accent') }) it('should set readonly to true when previewing', () => { @@ -419,7 +405,7 @@ describe('InputFieldPanel', () => { render() - expect(screen.getByTestId('field-list-readonly-shared')).toHaveTextContent( + expect(screen.getByTestId('field-list-readonly-shared'))!.toHaveTextContent( 'true', ) }) @@ -429,7 +415,7 @@ describe('InputFieldPanel', () => { render() - expect(screen.getByTestId('field-list-readonly-shared')).toHaveTextContent( + expect(screen.getByTestId('field-list-readonly-shared'))!.toHaveTextContent( 'true', ) }) @@ -439,7 +425,7 @@ describe('InputFieldPanel', () => { render() - expect(screen.getByTestId('field-list-readonly-shared')).toHaveTextContent( + expect(screen.getByTestId('field-list-readonly-shared'))!.toHaveTextContent( 'false', ) }) @@ -484,15 +470,15 @@ describe('InputFieldPanel', () => { expect(mockSetRagPipelineVariables).toHaveBeenCalled() }) - const setVarsCall = mockSetRagPipelineVariables.mock.calls[0][0] as RAGPipelineVariables + const setVarsCall = mockSetRagPipelineVariables.mock.calls[0]![0] as RAGPipelineVariables const isNotShared = (v: RAGPipelineVariable) => v.belong_to_node_id !== 'shared' const isShared = (v: RAGPipelineVariable) => v.belong_to_node_id === 'shared' const dsFields = setVarsCall.filter(isNotShared) const sharedFields = setVarsCall.filter(isShared) if (dsFields.length > 0 && sharedFields.length > 0) { - const firstDsIndex = setVarsCall.indexOf(dsFields[0]) - const firstSharedIndex = setVarsCall.indexOf(sharedFields[0]) + const firstDsIndex = setVarsCall.indexOf(dsFields[0]!) + const firstSharedIndex = setVarsCall.indexOf(sharedFields[0]!) expect(firstDsIndex).toBeLessThan(firstSharedIndex) } }) @@ -523,7 +509,7 @@ describe('InputFieldPanel', () => { expect(mockSetRagPipelineVariables).toHaveBeenCalled() }) - const setVarsCall = mockSetRagPipelineVariables.mock.calls[0][0] as RAGPipelineVariables + const setVarsCall = mockSetRagPipelineVariables.mock.calls[0]![0] as RAGPipelineVariables const isSharedField = (v: RAGPipelineVariable) => v.belong_to_node_id === 'shared' const hasSharedField = setVarsCall.some(isSharedField) expect(hasSharedField).toBe(true) @@ -537,15 +523,13 @@ describe('InputFieldPanel', () => { render() - expect( - screen.getByTestId('field-list-classname-node-1'), - ).toHaveTextContent('pt-1 pb-1') + expect(screen.getByTestId('field-list-classname-node-1'))!.toHaveTextContent('pt-1 pb-1') }) it('should pass correct className to global inputs field list', () => { render() - expect(screen.getByTestId('field-list-classname-shared')).toHaveTextContent( + expect(screen.getByTestId('field-list-classname-shared'))!.toHaveTextContent( 'pt-2 pb-1', ) }) @@ -559,7 +543,7 @@ describe('InputFieldPanel', () => { rerender() - expect(screen.getByTestId('field-list-node-1')).toBeInTheDocument() + expect(screen.getByTestId('field-list-node-1'))!.toBeInTheDocument() }) it('should compute allVariableNames correctly', () => { @@ -572,7 +556,7 @@ describe('InputFieldPanel', () => { render() - expect(screen.getByTestId('field-list-all-vars-shared')).toHaveTextContent( + expect(screen.getByTestId('field-list-all-vars-shared'))!.toHaveTextContent( 'alpha,beta,gamma', ) }) @@ -621,7 +605,7 @@ describe('InputFieldPanel', () => { render() - expect(screen.getByTestId('field-list-all-vars-shared')).toHaveTextContent( + expect(screen.getByTestId('field-list-all-vars-shared'))!.toHaveTextContent( '', ) }) @@ -632,7 +616,7 @@ describe('InputFieldPanel', () => { render() - expect(screen.getByTestId('field-list-shared')).toBeInTheDocument() + expect(screen.getByTestId('field-list-shared'))!.toBeInTheDocument() }) it('should handle null variable names in allVariableNames', () => { @@ -644,7 +628,7 @@ describe('InputFieldPanel', () => { render() - expect(screen.getByTestId('field-list-shared')).toBeInTheDocument() + expect(screen.getByTestId('field-list-shared'))!.toBeInTheDocument() }) it('should handle large number of datasource nodes', () => { @@ -655,7 +639,7 @@ describe('InputFieldPanel', () => { render() nodes.forEach((_, i) => { - expect(screen.getByTestId(`field-list-node-${i}`)).toBeInTheDocument() + expect(screen.getByTestId(`field-list-node-${i}`))!.toBeInTheDocument() }) }) @@ -666,7 +650,7 @@ describe('InputFieldPanel', () => { render() - expect(screen.getByTestId('field-list-fields-count-shared')).toHaveTextContent( + expect(screen.getByTestId('field-list-fields-count-shared'))!.toHaveTextContent( '100', ) }) @@ -680,7 +664,7 @@ describe('InputFieldPanel', () => { render() - expect(screen.getByTestId('field-list-all-vars-shared')).toHaveTextContent( + expect(screen.getByTestId('field-list-all-vars-shared'))!.toHaveTextContent( 'var_with_underscore,varWithCamelCase', ) }) @@ -717,8 +701,8 @@ describe('InputFieldPanel', () => { render() - expect(screen.getByTestId('field-list-fields-count-node-1')).toHaveTextContent('1') - expect(screen.getByTestId('field-list-fields-count-node-2')).toHaveTextContent('2') + expect(screen.getByTestId('field-list-fields-count-node-1'))!.toHaveTextContent('1') + expect(screen.getByTestId('field-list-fields-count-node-2'))!.toHaveTextContent('2') }) }) @@ -727,25 +711,21 @@ describe('InputFieldPanel', () => { const { container } = render() const panel = container.firstChild as HTMLElement - expect(panel).toHaveClass('w-[400px]') + expect(panel)!.toHaveClass('w-[400px]') }) it('should have overflow scroll on content area', () => { const { container } = render() const scrollContainer = container.querySelector('.overflow-y-auto') - expect(scrollContainer).toBeInTheDocument() + expect(scrollContainer)!.toBeInTheDocument() }) it('should render header section with proper spacing', () => { render() - expect( - screen.getByText('datasetPipeline.inputFieldPanel.title'), - ).toBeInTheDocument() - expect( - screen.getByText('datasetPipeline.inputFieldPanel.description'), - ).toBeInTheDocument() + expect(screen.getByText('datasetPipeline.inputFieldPanel.title'))!.toBeInTheDocument() + expect(screen.getByText('datasetPipeline.inputFieldPanel.description'))!.toBeInTheDocument() }) }) @@ -763,9 +743,9 @@ describe('InputFieldPanel', () => { render() - expect(screen.getByTestId('field-list-node-1')).toBeInTheDocument() - expect(screen.getByTestId('field-list-readonly-node-1')).toHaveTextContent('true') - expect(screen.getByTestId('field-list-fields-count-node-1')).toHaveTextContent('1') + expect(screen.getByTestId('field-list-node-1'))!.toBeInTheDocument() + expect(screen.getByTestId('field-list-readonly-node-1'))!.toHaveTextContent('true') + expect(screen.getByTestId('field-list-fields-count-node-1'))!.toHaveTextContent('1') }) it('should pass correct props to FieldList for shared node', () => { @@ -776,9 +756,9 @@ describe('InputFieldPanel', () => { render() - expect(screen.getByTestId('field-list-shared')).toBeInTheDocument() - expect(screen.getByTestId('field-list-readonly-shared')).toHaveTextContent('true') - expect(screen.getByTestId('field-list-fields-count-shared')).toHaveTextContent('1') + expect(screen.getByTestId('field-list-shared'))!.toBeInTheDocument() + expect(screen.getByTestId('field-list-readonly-shared'))!.toHaveTextContent('true') + expect(screen.getByTestId('field-list-fields-count-shared'))!.toHaveTextContent('1') }) }) @@ -793,7 +773,7 @@ describe('InputFieldPanel', () => { render() - expect(screen.getByTestId('field-list-all-vars-shared')).toHaveTextContent( + expect(screen.getByTestId('field-list-all-vars-shared'))!.toHaveTextContent( 'first,second,third', ) }) @@ -810,7 +790,7 @@ describe('useFloatingRight Hook Integration', () => { it('should render panel correctly with default floating state', () => { render() - expect(screen.getByTestId('field-list-shared')).toBeInTheDocument() + expect(screen.getByTestId('field-list-shared'))!.toBeInTheDocument() }) }) @@ -823,7 +803,7 @@ describe('FooterTip Integration', () => { it('should render footer tip at the bottom of the panel', () => { render() - expect(screen.getByTestId('footer-tip')).toBeInTheDocument() + expect(screen.getByTestId('footer-tip'))!.toBeInTheDocument() }) }) @@ -836,7 +816,7 @@ describe('Label Components Integration', () => { it('should render GlobalInputs label for shared field list', () => { render() - expect(screen.getByTestId('global-inputs-label')).toBeInTheDocument() + expect(screen.getByTestId('global-inputs-label'))!.toBeInTheDocument() }) it('should render Datasource label for each datasource node', () => { @@ -848,12 +828,8 @@ describe('Label Components Integration', () => { render() - expect( - screen.getByTestId('datasource-label-First DataSource'), - ).toBeInTheDocument() - expect( - screen.getByTestId('datasource-label-Second DataSource'), - ).toBeInTheDocument() + expect(screen.getByTestId('datasource-label-First DataSource'))!.toBeInTheDocument() + expect(screen.getByTestId('datasource-label-Second DataSource'))!.toBeInTheDocument() }) }) @@ -868,10 +844,8 @@ describe('Component Memo Behavior', () => { rerender() - expect(screen.getByTestId('field-list-shared')).toBeInTheDocument() - expect( - screen.getByText('datasetPipeline.inputFieldPanel.title'), - ).toBeInTheDocument() + expect(screen.getByTestId('field-list-shared'))!.toBeInTheDocument() + expect(screen.getByText('datasetPipeline.inputFieldPanel.title'))!.toBeInTheDocument() }) it('should handle state updates correctly with memo', async () => { diff --git a/web/app/components/rag-pipeline/components/panel/input-field/editor/form/index.tsx b/web/app/components/rag-pipeline/components/panel/input-field/editor/form/index.tsx index de003e78ba..3854b6561a 100644 --- a/web/app/components/rag-pipeline/components/panel/input-field/editor/form/index.tsx +++ b/web/app/components/rag-pipeline/components/panel/input-field/editor/form/index.tsx @@ -28,7 +28,7 @@ const InputFieldForm = ({ initialData, supportFile = false, onCancel, onSubmit, if (!result.success) { const issues = result.error.issues const firstIssue = issues[0] - const errorMessage = `"${firstIssue.path.join('.')}" ${firstIssue.message}` + const errorMessage = `"${firstIssue!.path.join('.')}" ${firstIssue!.message}` toast.error(errorMessage) return errorMessage } diff --git a/web/app/components/rag-pipeline/components/panel/input-field/field-list/__tests__/field-item.spec.tsx b/web/app/components/rag-pipeline/components/panel/input-field/field-list/__tests__/field-item.spec.tsx index 4a738761d0..8f5d9b5386 100644 --- a/web/app/components/rag-pipeline/components/panel/input-field/field-list/__tests__/field-item.spec.tsx +++ b/web/app/components/rag-pipeline/components/panel/input-field/field-list/__tests__/field-item.spec.tsx @@ -35,9 +35,9 @@ describe('FieldItem', () => { />, ) - expect(screen.getByText('field_name')).toBeInTheDocument() - expect(screen.getByText('Field Label')).toBeInTheDocument() - expect(screen.getByText('workflow.nodes.start.required')).toBeInTheDocument() + expect(screen.getByText('field_name'))!.toBeInTheDocument() + expect(screen.getByText('Field Label'))!.toBeInTheDocument() + expect(screen.getByText('workflow.nodes.start.required'))!.toBeInTheDocument() }) it('should show edit and delete controls on hover and trigger both callbacks', () => { @@ -54,8 +54,8 @@ describe('FieldItem', () => { fireEvent.mouseEnter(container.firstChild!) const buttons = screen.getAllByRole('button') - fireEvent.click(buttons[0]) - fireEvent.click(buttons[1]) + fireEvent.click(buttons[0]!) + fireEvent.click(buttons[1]!) expect(onClickEdit).toHaveBeenCalledWith('custom_field') expect(onRemove).toHaveBeenCalledWith(2) diff --git a/web/app/components/rag-pipeline/components/panel/input-field/field-list/__tests__/hooks.spec.ts b/web/app/components/rag-pipeline/components/panel/input-field/field-list/__tests__/hooks.spec.ts index c7f5a11ce3..376751e58f 100644 --- a/web/app/components/rag-pipeline/components/panel/input-field/field-list/__tests__/hooks.spec.ts +++ b/web/app/components/rag-pipeline/components/panel/input-field/field-list/__tests__/hooks.spec.ts @@ -141,7 +141,7 @@ describe('useFieldList', () => { ]) }) - const updatedFields = onInputFieldsChange.mock.calls[0][0] + const updatedFields = onInputFieldsChange.mock.calls[0]![0] expect(updatedFields[0]).not.toHaveProperty('id') expect(updatedFields[0]).not.toHaveProperty('chosen') expect(updatedFields[0]).not.toHaveProperty('selected') @@ -282,7 +282,7 @@ describe('useFieldList', () => { result.current.handleOpenInputFieldEditor() }) - const editorProps = mockToggleInputFieldEditPanel.mock.calls[0][0] + const editorProps = mockToggleInputFieldEditPanel.mock.calls[0]![0] const newField = createInputVar({ variable: 'new_var', label: 'New' }) act(() => { @@ -307,7 +307,7 @@ describe('useFieldList', () => { result.current.handleOpenInputFieldEditor() }) - const editorProps = mockToggleInputFieldEditPanel.mock.calls[0][0] + const editorProps = mockToggleInputFieldEditPanel.mock.calls[0]![0] const duplicateField = createInputVar({ variable: 'existing_var' }) act(() => { @@ -336,7 +336,7 @@ describe('useFieldList', () => { result.current.handleOpenInputFieldEditor('old_name') }) - const editorProps = mockToggleInputFieldEditPanel.mock.calls[0][0] + const editorProps = mockToggleInputFieldEditPanel.mock.calls[0]![0] const updatedField = createInputVar({ variable: 'new_name' }) act(() => { diff --git a/web/app/components/rag-pipeline/components/panel/input-field/field-list/__tests__/index.spec.tsx b/web/app/components/rag-pipeline/components/panel/input-field/field-list/__tests__/index.spec.tsx index e44301e252..b2dbc35601 100644 --- a/web/app/components/rag-pipeline/components/panel/input-field/field-list/__tests__/index.spec.tsx +++ b/web/app/components/rag-pipeline/components/panel/input-field/field-list/__tests__/index.spec.tsx @@ -110,7 +110,7 @@ describe('FieldItem', () => { />, ) - expect(screen.getByText('my_field')).toBeInTheDocument() + expect(screen.getByText('my_field'))!.toBeInTheDocument() }) it('should render field item with label when provided', () => { @@ -125,7 +125,7 @@ describe('FieldItem', () => { />, ) - expect(screen.getByText('Field Label')).toBeInTheDocument() + expect(screen.getByText('Field Label'))!.toBeInTheDocument() }) it('should not render label when empty', () => { @@ -155,7 +155,7 @@ describe('FieldItem', () => { />, ) - expect(screen.getByText(/required/i)).toBeInTheDocument() + expect(screen.getByText(/required/i))!.toBeInTheDocument() }) it('should not render required badge when required is false', () => { @@ -259,7 +259,7 @@ describe('FieldItem', () => { ) fireEvent.mouseEnter(container.firstChild!) const buttons = screen.getAllByRole('button') - fireEvent.click(buttons[0]) // Edit button + fireEvent.click(buttons[0]!) // Edit button expect(onClickEdit).toHaveBeenCalledWith('test_var') }) @@ -278,7 +278,7 @@ describe('FieldItem', () => { ) fireEvent.mouseEnter(container.firstChild!) const buttons = screen.getAllByRole('button') - fireEvent.click(buttons[1]) // Delete button + fireEvent.click(buttons[1]!) // Delete button expect(onRemove).toHaveBeenCalledWith(5) }) @@ -328,7 +328,7 @@ describe('FieldItem', () => { ) fireEvent.mouseEnter(container.querySelector('.handle')!) const buttons = screen.getAllByRole('button') - fireEvent.click(buttons[0]) + fireEvent.click(buttons[0]!) expect(onClickEdit).toHaveBeenCalled() expect(parentClick).not.toHaveBeenCalled() @@ -351,7 +351,7 @@ describe('FieldItem', () => { ) fireEvent.mouseEnter(container.querySelector('.handle')!) const buttons = screen.getAllByRole('button') - fireEvent.click(buttons[1]) + fireEvent.click(buttons[1]!) expect(onRemove).toHaveBeenCalled() expect(parentClick).not.toHaveBeenCalled() @@ -373,7 +373,7 @@ describe('FieldItem', () => { ) fireEvent.mouseEnter(container.firstChild!) const buttons = screen.getAllByRole('button') - fireEvent.click(buttons[0]) + fireEvent.click(buttons[0]!) rerender( { ) fireEvent.mouseEnter(container.firstChild!) const buttonsAfterRerender = screen.getAllByRole('button') - fireEvent.click(buttonsAfterRerender[0]) + fireEvent.click(buttonsAfterRerender[0]!) expect(onClickEdit).toHaveBeenCalledTimes(2) }) @@ -406,7 +406,7 @@ describe('FieldItem', () => { ) const varElement = screen.getByTitle(longVariable) - expect(varElement).toHaveClass('truncate') + expect(varElement)!.toHaveClass('truncate') }) it('should handle very long label names with truncation', () => { @@ -423,7 +423,7 @@ describe('FieldItem', () => { ) const labelElement = screen.getByTitle(longLabel) - expect(labelElement).toHaveClass('truncate') + expect(labelElement)!.toHaveClass('truncate') }) it('should handle special characters in variable and label', () => { @@ -441,8 +441,8 @@ describe('FieldItem', () => { />, ) - expect(screen.getByText('&"var\'')).toBeInTheDocument() - expect(screen.getByText('