diff --git a/src/components/sidebar/tabs/NodeLibrarySidebarTabV2.test.ts b/src/components/sidebar/tabs/NodeLibrarySidebarTabV2.test.ts
index 2fcf1d9ef7..77360d4454 100644
--- a/src/components/sidebar/tabs/NodeLibrarySidebarTabV2.test.ts
+++ b/src/components/sidebar/tabs/NodeLibrarySidebarTabV2.test.ts
@@ -3,10 +3,20 @@ import { ref } from 'vue'
import { beforeEach, describe, expect, it, vi } from 'vitest'
import { createI18n } from 'vue-i18n'
-import { render, screen } from '@testing-library/vue'
+import { fireEvent, render, screen } from '@testing-library/vue'
import NodeLibrarySidebarTabV2 from './NodeLibrarySidebarTabV2.vue'
+const hoisted = vi.hoisted(() => ({
+ mockSearchNode: vi.fn<(query: string) => unknown[]>(() => [])
+}))
+
+vi.mock('@/services/nodeSearchService', () => ({
+ NodeSearchService: class {
+ searchNode = hoisted.mockSearchNode
+ }
+}))
+
vi.mock('@vueuse/core', async () => {
const actual = await vi.importActual('@vueuse/core')
return {
@@ -72,8 +82,10 @@ vi.mock('./nodeLibrary/NodeDragPreview.vue', () => ({
vi.mock('@/components/ui/search-input/SearchInput.vue', () => ({
default: {
name: 'SearchBox',
- template: '',
+ template:
+ '',
props: ['modelValue', 'placeholder'],
+ emits: ['update:modelValue', 'search'],
setup() {
return { focus: vi.fn() }
},
@@ -84,12 +96,22 @@ vi.mock('@/components/ui/search-input/SearchInput.vue', () => ({
const i18n = createI18n({
legacy: false,
locale: 'en',
- messages: { en: {} }
+ messages: {
+ en: {
+ sideToolbar: {
+ nodeLibraryTab: {
+ noMatchingNodes: 'No nodes match "{query}"'
+ }
+ }
+ }
+ }
})
describe('NodeLibrarySidebarTabV2', () => {
beforeEach(() => {
vi.clearAllMocks()
+ hoisted.mockSearchNode.mockReset()
+ hoisted.mockSearchNode.mockReturnValue([])
})
function renderComponent() {
@@ -123,4 +145,49 @@ describe('NodeLibrarySidebarTabV2', () => {
expect(screen.queryByTestId('all-panel')).not.toBeInTheDocument()
expect(screen.queryByTestId('blueprints-panel')).not.toBeInTheDocument()
})
+
+ describe('search empty state', () => {
+ it('does not render the empty state when search query is empty', () => {
+ renderComponent()
+
+ expect(screen.queryByText(/No nodes match/)).not.toBeInTheDocument()
+ expect(screen.getByTestId('essential-panel')).toBeInTheDocument()
+ })
+
+ it('renders the empty state with the query when search has no matches', async () => {
+ hoisted.mockSearchNode.mockReturnValue([])
+ renderComponent()
+
+ await fireEvent.update(screen.getByTestId('search-box'), 'gibberish')
+
+ expect(screen.getByText('No nodes match "gibberish"')).toBeInTheDocument()
+ expect(screen.queryByTestId('essential-panel')).not.toBeInTheDocument()
+ expect(screen.queryByTestId('all-panel')).not.toBeInTheDocument()
+ expect(screen.queryByTestId('blueprints-panel')).not.toBeInTheDocument()
+ })
+
+ it('hides the empty state when the search has matches', async () => {
+ hoisted.mockSearchNode.mockReturnValue([{ name: 'KSampler' }])
+ renderComponent()
+
+ await fireEvent.update(screen.getByTestId('search-box'), 'ksampler')
+
+ expect(screen.queryByText(/No nodes match/)).not.toBeInTheDocument()
+ expect(screen.getByTestId('essential-panel')).toBeInTheDocument()
+ })
+
+ it('hides the empty state once the query is cleared', async () => {
+ hoisted.mockSearchNode.mockReturnValue([])
+ renderComponent()
+
+ const input = screen.getByTestId('search-box')
+ await fireEvent.update(input, 'gibberish')
+ expect(screen.getByText('No nodes match "gibberish"')).toBeInTheDocument()
+
+ await fireEvent.update(input, '')
+
+ expect(screen.queryByText(/No nodes match/)).not.toBeInTheDocument()
+ expect(screen.getByTestId('essential-panel')).toBeInTheDocument()
+ })
+ })
})