diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2023-05-03 03:09:11 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2023-05-03 03:09:11 +0300 |
commit | a0754ad291e60e9411897ae4e05e01a600037ee9 (patch) | |
tree | d23b71bd0d4db9245aaa12d2fc81da20bcf0f105 /spec/frontend/super_sidebar | |
parent | 90693cc231ba6e1645dc57f2a9111a7b5a5ceae0 (diff) |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'spec/frontend/super_sidebar')
5 files changed, 211 insertions, 16 deletions
diff --git a/spec/frontend/super_sidebar/components/counter_spec.js b/spec/frontend/super_sidebar/components/counter_spec.js index 8f514540413..77f77eae1c2 100644 --- a/spec/frontend/super_sidebar/components/counter_spec.js +++ b/spec/frontend/super_sidebar/components/counter_spec.js @@ -49,4 +49,15 @@ describe('Counter component', () => { expect(findButton().exists()).toBe(false); }); }); + + it.each([ + ['99+', '99+'], + ['110%', '110%'], + [100, '99+'], + [10, '10'], + [0, ''], + ])('formats count %p as %p', (count, result) => { + createWrapper({ count }); + expect(findButton().text()).toBe(result); + }); }); diff --git a/spec/frontend/super_sidebar/components/merge_request_menu_spec.js b/spec/frontend/super_sidebar/components/merge_request_menu_spec.js index 9c8fd0556f1..53d47397eb3 100644 --- a/spec/frontend/super_sidebar/components/merge_request_menu_spec.js +++ b/spec/frontend/super_sidebar/components/merge_request_menu_spec.js @@ -1,6 +1,7 @@ import { GlBadge, GlDisclosureDropdown } from '@gitlab/ui'; import { mountExtended } from 'helpers/vue_test_utils_helper'; import MergeRequestMenu from '~/super_sidebar/components/merge_request_menu.vue'; +import { userCounts } from '~/super_sidebar/user_counts_manager'; import { mergeRequestMenuGroup } from '../mock_data'; describe('MergeRequestMenu component', () => { @@ -10,17 +11,17 @@ describe('MergeRequestMenu component', () => { const findGlDisclosureDropdown = () => wrapper.findComponent(GlDisclosureDropdown); const findLink = (name) => wrapper.findByRole('link', { name }); - const createWrapper = () => { + const createWrapper = (items) => { wrapper = mountExtended(MergeRequestMenu, { propsData: { - items: mergeRequestMenuGroup, + items, }, }); }; describe('default', () => { beforeEach(() => { - createWrapper(); + createWrapper(mergeRequestMenuGroup); }); it('passes the items to the disclosure dropdown', () => { @@ -49,5 +50,21 @@ describe('MergeRequestMenu component', () => { it('renders 0 string when count is empty', () => { expect(findGlBadge(1).text()).toBe(String(0)); }); + + it('renders value from userCounts if `userCount` prop is defined', () => { + userCounts.assigned_merge_requests = 5; + mergeRequestMenuGroup[0].items[0].userCount = 'assigned_merge_requests'; + createWrapper(mergeRequestMenuGroup); + + expect(findGlBadge(0).text()).toBe(String(userCounts.assigned_merge_requests)); + }); + + it('renders item count if unknown `userCount` prop is defined', () => { + const { count } = mergeRequestMenuGroup[0].items[0]; + mergeRequestMenuGroup[0].items[0].userCount = 'foobar'; + createWrapper(mergeRequestMenuGroup); + + expect(findGlBadge(0).text()).toBe(String(count)); + }); }); }); diff --git a/spec/frontend/super_sidebar/components/user_bar_spec.js b/spec/frontend/super_sidebar/components/user_bar_spec.js index 7abd64ca108..25f699bddb5 100644 --- a/spec/frontend/super_sidebar/components/user_bar_spec.js +++ b/spec/frontend/super_sidebar/components/user_bar_spec.js @@ -10,14 +10,10 @@ import Counter from '~/super_sidebar/components/counter.vue'; import UserBar from '~/super_sidebar/components/user_bar.vue'; import { createMockDirective, getBinding } from 'helpers/vue_mock_directive'; import waitForPromises from 'helpers/wait_for_promises'; -import { highCountTrim } from '~/lib/utils/text_utility'; +import { userCounts } from '~/super_sidebar/user_counts_manager'; import { sidebarData } from '../mock_data'; import { MOCK_DEFAULT_SEARCH_OPTIONS } from './global_search/mock_data'; -jest.mock('~/lib/utils/text_utility', () => ({ - highCountTrim: jest.fn().mockReturnValue('99+'), -})); - describe('UserBar component', () => { let wrapper; @@ -78,7 +74,7 @@ describe('UserBar component', () => { it('renders issues counter', () => { const isuesCounter = findIssuesCounter(); - expect(isuesCounter.props('count')).toBe(sidebarData.assigned_open_issues_count); + expect(isuesCounter.props('count')).toBe(userCounts.assigned_issues); expect(isuesCounter.props('href')).toBe(sidebarData.issues_dashboard_path); expect(isuesCounter.props('label')).toBe(__('Issues')); expect(isuesCounter.attributes('data-track-action')).toBe('click_link'); @@ -89,7 +85,9 @@ describe('UserBar component', () => { it('renders merge requests counter', () => { const mrsCounter = findMRsCounter(); - expect(mrsCounter.props('count')).toBe(sidebarData.total_merge_requests_count); + expect(mrsCounter.props('count')).toBe( + userCounts.assigned_merge_requests + userCounts.review_requested_merge_requests, + ); expect(mrsCounter.props('label')).toBe(__('Merge requests')); expect(mrsCounter.attributes('data-track-action')).toBe('click_dropdown'); expect(mrsCounter.attributes('data-track-label')).toBe('merge_requests_menu'); @@ -107,13 +105,12 @@ describe('UserBar component', () => { expect(todosCounter.attributes('class')).toContain('shortcuts-todos'); }); - it('should format and update todo counter when event is emitted', async () => { + it('should update todo counter when event is emitted', async () => { createWrapper(); const count = 100; document.dispatchEvent(new CustomEvent('todo:toggle', { detail: { count } })); await nextTick(); - expect(highCountTrim).toHaveBeenCalledWith(count); - expect(findTodosCounter().props('count')).toBe('99+'); + expect(findTodosCounter().props('count')).toBe(count); }); }); diff --git a/spec/frontend/super_sidebar/mock_data.js b/spec/frontend/super_sidebar/mock_data.js index ecb662be481..d42bc24098d 100644 --- a/spec/frontend/super_sidebar/mock_data.js +++ b/spec/frontend/super_sidebar/mock_data.js @@ -81,10 +81,14 @@ export const sidebarData = { username: 'root', avatar_url: 'path/to/img_administrator', logo_url: 'path/to/logo', - assigned_open_issues_count: 1, - todos_pending_count: 3, + user_counts: { + last_update: Date.now(), + todos: 3, + assigned_issues: 1, + assigned_merge_requests: 3, + review_requested_merge_requests: 1, + }, issues_dashboard_path: 'path/to/issues', - total_merge_requests_count: 4, create_new_menu_groups: createNewMenuGroups, merge_request_menu: mergeRequestMenuGroup, projects_path: 'path/to/projects', diff --git a/spec/frontend/super_sidebar/user_counts_manager_spec.js b/spec/frontend/super_sidebar/user_counts_manager_spec.js new file mode 100644 index 00000000000..b5074620195 --- /dev/null +++ b/spec/frontend/super_sidebar/user_counts_manager_spec.js @@ -0,0 +1,166 @@ +import waitForPromises from 'helpers/wait_for_promises'; + +import * as UserApi from '~/api/user_api'; +import { + createUserCountsManager, + userCounts, + destroyUserCountsManager, +} from '~/super_sidebar/user_counts_manager'; + +jest.mock('~/api'); + +const USER_ID = 123; +const userCountDefaults = { + todos: 1, + assigned_issues: 2, + assigned_merge_requests: 3, + review_requested_merge_requests: 4, +}; + +const userCountUpdate = { + todos: 123, + assigned_issues: 456, + assigned_merge_requests: 789, + review_requested_merge_requests: 101112, +}; + +describe('User Merge Requests', () => { + let channelMock; + let newBroadcastChannelMock; + + beforeEach(() => { + jest.spyOn(document, 'removeEventListener'); + jest.spyOn(document, 'addEventListener'); + + global.gon.current_user_id = USER_ID; + + channelMock = { + postMessage: jest.fn(), + close: jest.fn(), + }; + newBroadcastChannelMock = jest.fn().mockImplementation(() => channelMock); + + Object.assign(userCounts, userCountDefaults, { last_update: 0 }); + + global.BroadcastChannel = newBroadcastChannelMock; + }); + + describe('createUserCountsManager', () => { + beforeEach(() => { + createUserCountsManager(); + }); + + it('creates BroadcastChannel which updates counts on message received', () => { + expect(newBroadcastChannelMock).toHaveBeenCalledWith(`user_counts_${USER_ID}`); + }); + + it('closes BroadCastchannel if called while already open', () => { + expect(channelMock.close).not.toHaveBeenCalled(); + + createUserCountsManager(); + + expect(channelMock.close).toHaveBeenCalled(); + }); + + describe('BroadcastChannel onmessage handler', () => { + it('updates counts on message received', () => { + expect(userCounts).toMatchObject(userCountDefaults); + + channelMock.onmessage({ data: { ...userCountUpdate, last_update: Date.now() } }); + + expect(userCounts).toMatchObject(userCountUpdate); + }); + + it('ignores updates with older data', () => { + expect(userCounts).toMatchObject(userCountDefaults); + userCounts.last_update = Date.now(); + + channelMock.onmessage({ + data: { ...userCountUpdate, last_update: userCounts.last_update - 1000 }, + }); + + expect(userCounts).toMatchObject(userCountDefaults); + }); + + it('ignores unknown fields', () => { + expect(userCounts).toMatchObject(userCountDefaults); + + channelMock.onmessage({ data: { ...userCountUpdate, i_am_unknown: 5 } }); + + expect(userCounts).toMatchObject(userCountUpdate); + expect(userCounts.i_am_unknown).toBeUndefined(); + }); + }); + + it('broadcasts user counts during initialization', () => { + expect(channelMock.postMessage).toHaveBeenCalledWith( + expect.objectContaining(userCountDefaults), + ); + }); + + it('setups event listener without leaking them', () => { + expect(document.removeEventListener).toHaveBeenCalledWith( + 'userCounts:fetch', + expect.any(Function), + ); + expect(document.addEventListener).toHaveBeenCalledWith( + 'userCounts:fetch', + expect.any(Function), + ); + }); + }); + + describe('Event listener userCounts:fetch', () => { + beforeEach(() => { + jest.spyOn(UserApi, 'getUserCounts').mockResolvedValue({ + data: { ...userCountUpdate, merge_requests: 'FOO' }, + }); + createUserCountsManager(); + }); + + it('fetches counts from API, stores and rebroadcasts them', async () => { + expect(userCounts).toMatchObject(userCountDefaults); + + document.dispatchEvent(new CustomEvent('userCounts:fetch')); + await waitForPromises(); + + expect(UserApi.getUserCounts).toHaveBeenCalled(); + expect(userCounts).toMatchObject(userCountUpdate); + expect(channelMock.postMessage).toHaveBeenLastCalledWith(userCounts); + }); + }); + + describe('destroyUserCountsManager', () => { + it('unregisters event handler', () => { + expect(document.removeEventListener).not.toHaveBeenCalledWith(); + + destroyUserCountsManager(); + + expect(document.removeEventListener).toHaveBeenCalledWith( + 'userCounts:fetch', + expect.any(Function), + ); + }); + + describe('when BroadcastChannel is not opened', () => { + it('does nothing', () => { + destroyUserCountsManager(); + expect(channelMock.close).not.toHaveBeenCalled(); + }); + }); + + describe('when BroadcastChannel is opened', () => { + beforeEach(() => { + createUserCountsManager(); + }); + + it('closes BroadcastChannel', () => { + expect(channelMock.close).not.toHaveBeenCalled(); + + destroyUserCountsManager(); + + expect(channelMock.close).toHaveBeenCalled(); + }); + }); + }); +}); |