|
@@ -0,0 +1,210 @@
|
|
|
+// Copyright (C) 2012-2025 Zammad Foundation, https://zammad-foundation.org/
|
|
|
+
|
|
|
+import '#tests/graphql/builders/mocks.ts'
|
|
|
+import { createPinia, setActivePinia } from 'pinia'
|
|
|
+
|
|
|
+import renderComponent from '#tests/support/components/renderComponent.ts'
|
|
|
+import { waitForNextTick } from '#tests/support/utils.ts'
|
|
|
+
|
|
|
+import {
|
|
|
+ EnumTicketStateColorCode,
|
|
|
+ type Organization,
|
|
|
+} from '#shared/graphql/types.ts'
|
|
|
+import { convertToGraphQLId } from '#shared/graphql/utils.ts'
|
|
|
+
|
|
|
+import QuickSearch from '#desktop/components/QuickSearch/QuickSearch.vue'
|
|
|
+import { useRecentSearches } from '#desktop/composables/useRecentSearches.ts'
|
|
|
+import { mockUserCurrentRecentViewResetMutation } from '#desktop/entities/user/current/graphql/mutations/userCurrentRecentViewReset.mocks.ts'
|
|
|
+import { mockUserCurrentRecentViewListQuery } from '#desktop/entities/user/current/graphql/queries/userCurrentRecentViewList.mocks.ts'
|
|
|
+import { getUserCurrentRecentViewUpdatesSubscriptionHandler } from '#desktop/entities/user/current/graphql/subscriptions/userCurrentRecentViewUpdates.mocks.ts'
|
|
|
+
|
|
|
+const renderQuickSearch = async () => {
|
|
|
+ const wrapper = renderComponent(QuickSearch, {
|
|
|
+ props: {
|
|
|
+ collapsed: false,
|
|
|
+ search: '',
|
|
|
+ },
|
|
|
+ router: true,
|
|
|
+ store: true,
|
|
|
+ dialog: true,
|
|
|
+ })
|
|
|
+
|
|
|
+ await waitForNextTick()
|
|
|
+
|
|
|
+ return wrapper
|
|
|
+}
|
|
|
+
|
|
|
+setActivePinia(createPinia()) // Because we haven't set up the store before useRecentSearches is called
|
|
|
+
|
|
|
+describe('QuickSearch', () => {
|
|
|
+ const { addSearch, removeSearch, clearSearches } = useRecentSearches()
|
|
|
+
|
|
|
+ beforeEach(() => {
|
|
|
+ clearSearches()
|
|
|
+ })
|
|
|
+
|
|
|
+ describe('default state', () => {
|
|
|
+ it('shows empty state message when no searches or recently viewed items exist', async () => {
|
|
|
+ mockUserCurrentRecentViewListQuery({ userCurrentRecentViewList: [] })
|
|
|
+
|
|
|
+ const wrapper = await renderQuickSearch()
|
|
|
+
|
|
|
+ expect(
|
|
|
+ wrapper.getByText(
|
|
|
+ 'Start typing i.e. the name of a ticket, an organization or a user.',
|
|
|
+ ),
|
|
|
+ ).toBeInTheDocument()
|
|
|
+
|
|
|
+ expect(
|
|
|
+ wrapper.queryByRole('button', { name: 'Clear All' }),
|
|
|
+ ).not.toBeInTheDocument()
|
|
|
+ })
|
|
|
+ })
|
|
|
+
|
|
|
+ describe('recent searches', () => {
|
|
|
+ beforeEach(() => {
|
|
|
+ mockUserCurrentRecentViewListQuery({ userCurrentRecentViewList: [] })
|
|
|
+ })
|
|
|
+
|
|
|
+ it('displays recent searches when added', async () => {
|
|
|
+ const wrapper = await renderQuickSearch()
|
|
|
+
|
|
|
+ addSearch('Foobar')
|
|
|
+ addSearch('Dummy')
|
|
|
+ await waitForNextTick()
|
|
|
+
|
|
|
+ expect(
|
|
|
+ wrapper.getByRole('heading', { level: 3, name: 'Recent searches' }),
|
|
|
+ ).toBeInTheDocument()
|
|
|
+
|
|
|
+ expect(wrapper.getByText('Foobar')).toBeInTheDocument()
|
|
|
+ expect(wrapper.getByText('Dummy')).toBeInTheDocument()
|
|
|
+
|
|
|
+ expect(
|
|
|
+ wrapper.getByRole('link', { name: 'Clear recent searches' }),
|
|
|
+ ).toBeInTheDocument()
|
|
|
+ })
|
|
|
+
|
|
|
+ it('allows clearing all recent searches', async () => {
|
|
|
+ const wrapper = await renderQuickSearch()
|
|
|
+
|
|
|
+ addSearch('Foobar')
|
|
|
+ addSearch('Dummy')
|
|
|
+ await waitForNextTick()
|
|
|
+
|
|
|
+ expect(
|
|
|
+ wrapper.getByRole('link', { name: 'Clear recent searches' }),
|
|
|
+ ).toBeInTheDocument()
|
|
|
+
|
|
|
+ clearSearches()
|
|
|
+ await waitForNextTick()
|
|
|
+
|
|
|
+ expect(
|
|
|
+ wrapper.getByText(
|
|
|
+ 'Start typing i.e. the name of a ticket, an organization or a user.',
|
|
|
+ ),
|
|
|
+ ).toBeInTheDocument()
|
|
|
+
|
|
|
+ expect(
|
|
|
+ wrapper.queryByRole('link', { name: 'Clear recent searches' }),
|
|
|
+ ).not.toBeInTheDocument()
|
|
|
+ })
|
|
|
+
|
|
|
+ it('allows removing individual recent searches', async () => {
|
|
|
+ const wrapper = await renderQuickSearch()
|
|
|
+
|
|
|
+ addSearch('Foobar')
|
|
|
+ addSearch('Dummy')
|
|
|
+ await waitForNextTick()
|
|
|
+
|
|
|
+ let removeIcons = wrapper.getAllByIconName('x-lg')
|
|
|
+ expect(removeIcons.length).toBe(2)
|
|
|
+
|
|
|
+ removeSearch('Foobar')
|
|
|
+ await waitForNextTick()
|
|
|
+
|
|
|
+ removeIcons = wrapper.getAllByIconName('x-lg')
|
|
|
+ expect(removeIcons.length).toBe(1)
|
|
|
+ })
|
|
|
+ })
|
|
|
+
|
|
|
+ describe('recently viewed items', () => {
|
|
|
+ const recentlyViewedItems = [
|
|
|
+ {
|
|
|
+ __typename: 'Ticket',
|
|
|
+ id: convertToGraphQLId('Ticket', 1),
|
|
|
+ title: 'Ticket 1',
|
|
|
+ number: '1',
|
|
|
+ stateColorCode: EnumTicketStateColorCode.Open,
|
|
|
+ },
|
|
|
+ {
|
|
|
+ __typename: 'User',
|
|
|
+ id: convertToGraphQLId('User', 1),
|
|
|
+ internalId: 1,
|
|
|
+ fullname: 'User 1',
|
|
|
+ },
|
|
|
+ {
|
|
|
+ __typename: 'Organization',
|
|
|
+ id: convertToGraphQLId('Organization', 1),
|
|
|
+ internalId: 1,
|
|
|
+ name: 'Organization 1',
|
|
|
+ },
|
|
|
+ ]
|
|
|
+
|
|
|
+ it('displays recently viewed items', async () => {
|
|
|
+ mockUserCurrentRecentViewListQuery({
|
|
|
+ userCurrentRecentViewList: recentlyViewedItems as Organization[],
|
|
|
+ })
|
|
|
+
|
|
|
+ const wrapper = await renderQuickSearch()
|
|
|
+
|
|
|
+ expect(
|
|
|
+ wrapper.getByRole('heading', { level: 3, name: 'Recently viewed' }),
|
|
|
+ ).toBeInTheDocument()
|
|
|
+
|
|
|
+ expect(
|
|
|
+ wrapper.getByRole('link', { name: 'check-circle-no Ticket 1' }),
|
|
|
+ ).toBeInTheDocument()
|
|
|
+ expect(wrapper.getByRole('link', { name: 'User 1' })).toBeInTheDocument()
|
|
|
+ expect(
|
|
|
+ wrapper.getByRole('link', { name: 'Organization 1' }),
|
|
|
+ ).toBeInTheDocument()
|
|
|
+ })
|
|
|
+
|
|
|
+ it('allows clearing all recently viewed items', async () => {
|
|
|
+ mockUserCurrentRecentViewListQuery({
|
|
|
+ userCurrentRecentViewList: recentlyViewedItems as Organization[],
|
|
|
+ })
|
|
|
+
|
|
|
+ mockUserCurrentRecentViewResetMutation({
|
|
|
+ userCurrentRecentViewReset: { success: true },
|
|
|
+ })
|
|
|
+
|
|
|
+ const wrapper = await renderQuickSearch()
|
|
|
+
|
|
|
+ expect(
|
|
|
+ wrapper.getByRole('link', { name: 'Clear recently viewed' }),
|
|
|
+ ).toBeInTheDocument()
|
|
|
+
|
|
|
+ mockUserCurrentRecentViewListQuery({ userCurrentRecentViewList: [] })
|
|
|
+
|
|
|
+ await getUserCurrentRecentViewUpdatesSubscriptionHandler().trigger({
|
|
|
+ userCurrentRecentViewUpdates: {
|
|
|
+ recentViewsUpdated: true,
|
|
|
+ },
|
|
|
+ })
|
|
|
+
|
|
|
+ await waitForNextTick()
|
|
|
+
|
|
|
+ expect(
|
|
|
+ wrapper.getByText(
|
|
|
+ 'Start typing i.e. the name of a ticket, an organization or a user.',
|
|
|
+ ),
|
|
|
+ ).toBeInTheDocument()
|
|
|
+
|
|
|
+ expect(
|
|
|
+ wrapper.queryByRole('link', { name: 'Clear recently viewed' }),
|
|
|
+ ).not.toBeInTheDocument()
|
|
|
+ })
|
|
|
+ })
|
|
|
+})
|