diff options
Diffstat (limited to 'spec/frontend/issues/dashboard')
-rw-r--r-- | spec/frontend/issues/dashboard/components/issues_dashboard_app_spec.js | 173 | ||||
-rw-r--r-- | spec/frontend/issues/dashboard/utils_spec.js | 88 |
2 files changed, 204 insertions, 57 deletions
diff --git a/spec/frontend/issues/dashboard/components/issues_dashboard_app_spec.js b/spec/frontend/issues/dashboard/components/issues_dashboard_app_spec.js index 3f40772f7fc..841cea28ffc 100644 --- a/spec/frontend/issues/dashboard/components/issues_dashboard_app_spec.js +++ b/spec/frontend/issues/dashboard/components/issues_dashboard_app_spec.js @@ -27,6 +27,9 @@ import { scrollUp } from '~/lib/utils/scroll_utils'; import { TOKEN_TYPE_ASSIGNEE, TOKEN_TYPE_AUTHOR, + TOKEN_TYPE_LABEL, + TOKEN_TYPE_MILESTONE, + TOKEN_TYPE_MY_REACTION, } from '~/vue_shared/components/filtered_search_bar/constants'; import IssuableList from '~/vue_shared/issuable/list/components/issuable_list_root.vue'; import { IssuableStates } from '~/vue_shared/issuable/list/constants'; @@ -42,8 +45,12 @@ describe('IssuesDashboardApp component', () => { Vue.use(VueApollo); const defaultProvide = { + autocompleteAwardEmojisPath: 'autocomplete/award/emojis/path', calendarPath: 'calendar/path', - emptyStateSvgPath: 'empty-state.svg', + dashboardLabelsPath: 'dashboard/labels/path', + dashboardMilestonesPath: 'dashboard/milestones/path', + emptyStateWithFilterSvgPath: 'empty/state/with/filter/svg/path.svg', + emptyStateWithoutFilterSvgPath: 'empty/state/with/filter/svg/path.svg', hasBlockedIssuesFeature: true, hasIssuableHealthStatusFeature: true, hasIssueWeightsFeature: true, @@ -97,74 +104,122 @@ describe('IssuesDashboardApp component', () => { axiosMock.reset(); }); - it('renders IssuableList component', async () => { - mountComponent(); - jest.runOnlyPendingTimers(); - await waitForPromises(); - - expect(findIssuableList().props()).toMatchObject({ - currentTab: IssuableStates.Opened, - hasNextPage: true, - hasPreviousPage: false, - hasScopedLabelsFeature: defaultProvide.hasScopedLabelsFeature, - initialSortBy: CREATED_DESC, - issuables: issuesQueryResponse.data.issues.nodes, - issuablesLoading: false, - namespace: 'dashboard', - recentSearchesStorageKey: 'issues', - searchInputPlaceholder: IssuesDashboardApp.i18n.searchInputPlaceholder, - showPaginationControls: true, - sortOptions: getSortOptions({ - hasBlockedIssuesFeature: defaultProvide.hasBlockedIssuesFeature, - hasIssuableHealthStatusFeature: defaultProvide.hasIssuableHealthStatusFeature, - hasIssueWeightsFeature: defaultProvide.hasIssueWeightsFeature, - }), - tabs: IssuesDashboardApp.IssuableListTabs, - urlParams: { - sort: urlSortParams[CREATED_DESC], - state: IssuableStates.Opened, - }, - useKeysetPagination: true, + describe('UI components', () => { + beforeEach(() => { + setWindowLocation(locationSearch); + mountComponent(); + jest.runOnlyPendingTimers(); + return waitForPromises(); }); - }); - it('renders RSS button link', () => { - mountComponent(); + it('renders IssuableList component', () => { + expect(findIssuableList().props()).toMatchObject({ + currentTab: IssuableStates.Opened, + hasNextPage: true, + hasPreviousPage: false, + hasScopedLabelsFeature: defaultProvide.hasScopedLabelsFeature, + initialSortBy: CREATED_DESC, + issuables: issuesQueryResponse.data.issues.nodes, + issuablesLoading: false, + namespace: 'dashboard', + recentSearchesStorageKey: 'issues', + searchInputPlaceholder: IssuesDashboardApp.i18n.searchInputPlaceholder, + showPaginationControls: true, + sortOptions: getSortOptions({ + hasBlockedIssuesFeature: defaultProvide.hasBlockedIssuesFeature, + hasIssuableHealthStatusFeature: defaultProvide.hasIssuableHealthStatusFeature, + hasIssueWeightsFeature: defaultProvide.hasIssueWeightsFeature, + }), + tabs: IssuesDashboardApp.IssuableListTabs, + urlParams: { + sort: urlSortParams[CREATED_DESC], + state: IssuableStates.Opened, + }, + useKeysetPagination: true, + }); + }); - expect(findRssButton().attributes('href')).toBe(defaultProvide.rssPath); - expect(findRssButton().props('icon')).toBe('rss'); - }); + it('renders RSS button link', () => { + expect(findRssButton().attributes('href')).toBe(defaultProvide.rssPath); + }); - it('renders calendar button link', () => { - mountComponent(); + it('renders calendar button link', () => { + expect(findCalendarButton().attributes('href')).toBe(defaultProvide.calendarPath); + }); + + it('renders issue time information', () => { + expect(findIssueCardTimeInfo().exists()).toBe(true); + }); - expect(findCalendarButton().attributes('href')).toBe(defaultProvide.calendarPath); - expect(findCalendarButton().props('icon')).toBe('calendar'); + it('renders issue statistics', () => { + expect(findIssueCardStatistics().exists()).toBe(true); + }); }); - it('renders issue time information', async () => { - mountComponent(); - jest.runOnlyPendingTimers(); - await waitForPromises(); + describe('fetching issues', () => { + describe('with a search query', () => { + describe('when there are issues returned', () => { + beforeEach(() => { + setWindowLocation(locationSearch); + mountComponent(); + jest.runOnlyPendingTimers(); + return waitForPromises(); + }); - expect(findIssueCardTimeInfo().exists()).toBe(true); - }); + it('renders the issues', () => { + expect(findIssuableList().props('issuables')).toEqual( + defaultQueryResponse.data.issues.nodes, + ); + }); - it('renders issue statistics', async () => { - mountComponent(); - jest.runOnlyPendingTimers(); - await waitForPromises(); + it('does not render empty state', () => { + expect(findEmptyState().exists()).toBe(false); + }); + }); - expect(findIssueCardStatistics().exists()).toBe(true); - }); + describe('when there are no issues returned', () => { + beforeEach(() => { + setWindowLocation(locationSearch); + mountComponent({ + issuesQueryHandler: jest.fn().mockResolvedValue(emptyIssuesQueryResponse), + }); + return waitForPromises(); + }); + + it('renders no issues', () => { + expect(findIssuableList().props('issuables')).toEqual([]); + }); + + it('renders empty state', () => { + expect(findEmptyState().props()).toMatchObject({ + description: IssuesDashboardApp.i18n.emptyStateWithFilterDescription, + svgPath: defaultProvide.emptyStateWithFilterSvgPath, + title: IssuesDashboardApp.i18n.emptyStateWithFilterTitle, + }); + }); + }); + }); + + describe('with no search query', () => { + let issuesQueryHandler; + + beforeEach(() => { + issuesQueryHandler = jest.fn().mockResolvedValue(defaultQueryResponse); + mountComponent({ issuesQueryHandler }); + return waitForPromises(); + }); - it('renders empty state', async () => { - mountComponent({ issuesQueryHandler: jest.fn().mockResolvedValue(emptyIssuesQueryResponse) }); - await waitForPromises(); + it('does not call issues query', () => { + expect(issuesQueryHandler).not.toHaveBeenCalled(); + }); - expect(findEmptyState().props()).toMatchObject({ - svgPath: defaultProvide.emptyStateSvgPath, - title: IssuesDashboardApp.i18n.emptyStateTitle, + it('renders empty state', () => { + expect(findEmptyState().props()).toMatchObject({ + description: null, + svgPath: defaultProvide.emptyStateWithoutFilterSvgPath, + title: IssuesDashboardApp.i18n.emptyStateWithoutFilterTitle, + }); + }); }); }); @@ -233,6 +288,7 @@ describe('IssuesDashboardApp component', () => { describe('when there is an error fetching issues', () => { beforeEach(() => { + setWindowLocation(locationSearch); mountComponent({ issuesQueryHandler: jest.fn().mockRejectedValue(new Error('ERROR')) }); jest.runOnlyPendingTimers(); return waitForPromises(); @@ -281,6 +337,9 @@ describe('IssuesDashboardApp component', () => { expect(findIssuableList().props('searchTokens')).toMatchObject([ { type: TOKEN_TYPE_ASSIGNEE, preloadedUsers }, { type: TOKEN_TYPE_AUTHOR, preloadedUsers }, + { type: TOKEN_TYPE_LABEL }, + { type: TOKEN_TYPE_MILESTONE }, + { type: TOKEN_TYPE_MY_REACTION }, ]); }); }); diff --git a/spec/frontend/issues/dashboard/utils_spec.js b/spec/frontend/issues/dashboard/utils_spec.js new file mode 100644 index 00000000000..08d00eee3e3 --- /dev/null +++ b/spec/frontend/issues/dashboard/utils_spec.js @@ -0,0 +1,88 @@ +import AxiosMockAdapter from 'axios-mock-adapter'; +import fuzzaldrinPlus from 'fuzzaldrin-plus'; +import { AutocompleteCache } from '~/issues/dashboard/utils'; +import { MAX_LIST_SIZE } from '~/issues/list/constants'; +import axios from '~/lib/utils/axios_utils'; + +describe('AutocompleteCache', () => { + let autocompleteCache; + let axiosMock; + const cacheName = 'name'; + const searchProperty = 'property'; + const url = 'url'; + + const data = [ + { [searchProperty]: 'one' }, + { [searchProperty]: 'two' }, + { [searchProperty]: 'three' }, + { [searchProperty]: 'four' }, + { [searchProperty]: 'five' }, + { [searchProperty]: 'six' }, + { [searchProperty]: 'seven' }, + { [searchProperty]: 'eight' }, + { [searchProperty]: 'nine' }, + { [searchProperty]: 'ten' }, + { [searchProperty]: 'eleven' }, + { [searchProperty]: 'twelve' }, + { [searchProperty]: 'thirteen' }, + { [searchProperty]: 'fourteen' }, + { [searchProperty]: 'fifteen' }, + ]; + + beforeEach(() => { + autocompleteCache = new AutocompleteCache(); + axiosMock = new AxiosMockAdapter(axios); + }); + + afterEach(() => { + axiosMock.reset(); + }); + + describe('when there is no cached data', () => { + let response; + + beforeEach(async () => { + axiosMock.onGet(url).replyOnce(200, data); + response = await autocompleteCache.fetch({ url, cacheName, searchProperty }); + }); + + it('fetches items via the API', () => { + expect(axiosMock.history.get[0].url).toBe(url); + }); + + it('returns a maximum of 10 items', () => { + expect(response).toHaveLength(MAX_LIST_SIZE); + }); + }); + + describe('when there is cached data', () => { + let response; + + beforeEach(async () => { + axiosMock.onGet(url).replyOnce(200, data); + jest.spyOn(fuzzaldrinPlus, 'filter'); + // Populate cache + await autocompleteCache.fetch({ url, cacheName, searchProperty }); + // Execute filtering on cache data + response = await autocompleteCache.fetch({ url, cacheName, searchProperty, search: 'een' }); + }); + + it('returns filtered items based on search characters', () => { + expect(response).toEqual([ + { [searchProperty]: 'fifteen' }, + { [searchProperty]: 'thirteen' }, + { [searchProperty]: 'fourteen' }, + { [searchProperty]: 'eleven' }, + { [searchProperty]: 'seven' }, + ]); + }); + + it('filters using fuzzaldrinPlus', () => { + expect(fuzzaldrinPlus.filter).toHaveBeenCalled(); + }); + + it('does not call the API', () => { + expect(axiosMock.history.get[1]).toBeUndefined(); + }); + }); +}); |