diff options
Diffstat (limited to 'spec/frontend/issuable_list')
3 files changed, 163 insertions, 4 deletions
diff --git a/spec/frontend/issuable_list/components/issuable_item_spec.js b/spec/frontend/issuable_list/components/issuable_item_spec.js index a96a4e15e6c..5c8b679e4e1 100644 --- a/spec/frontend/issuable_list/components/issuable_item_spec.js +++ b/spec/frontend/issuable_list/components/issuable_item_spec.js @@ -2,28 +2,34 @@ import { shallowMount } from '@vue/test-utils'; import { GlLink, GlLabel } from '@gitlab/ui'; import IssuableItem from '~/issuable_list/components/issuable_item.vue'; +import IssuableAssignees from '~/vue_shared/components/issue/issue_assignees.vue'; import { mockIssuable, mockRegularLabel, mockScopedLabel } from '../mock_data'; -const createComponent = ({ issuableSymbol = '#', issuable = mockIssuable } = {}) => +const createComponent = ({ issuableSymbol = '#', issuable = mockIssuable, slots = {} } = {}) => shallowMount(IssuableItem, { propsData: { issuableSymbol, issuable, + enableLabelPermalinks: true, }, + slots, }); describe('IssuableItem', () => { const mockLabels = mockIssuable.labels.nodes; const mockAuthor = mockIssuable.author; + const originalUrl = gon.gitlab_url; let wrapper; beforeEach(() => { + gon.gitlab_url = 'http://0.0.0.0:3000'; wrapper = createComponent(); }); afterEach(() => { wrapper.destroy(); + gon.gitlab_url = originalUrl; }); describe('computed', () => { @@ -38,8 +44,8 @@ describe('IssuableItem', () => { authorId | returnValue ${1} | ${1} ${'1'} | ${1} - ${'gid://gitlab/User/1'} | ${'1'} - ${'foo'} | ${''} + ${'gid://gitlab/User/1'} | ${1} + ${'foo'} | ${null} `( 'returns $returnValue when value of `issuable.author.id` is $authorId', async ({ authorId, returnValue }) => { @@ -60,6 +66,30 @@ describe('IssuableItem', () => { ); }); + describe('isIssuableUrlExternal', () => { + it.each` + issuableWebUrl | urlType | returnValue + ${'/gitlab-org/gitlab-test/-/issues/2'} | ${'relative'} | ${false} + ${'http://0.0.0.0:3000/gitlab-org/gitlab-test/-/issues/1'} | ${'absolute and internal'} | ${false} + ${'http://jira.atlassian.net/browse/IG-1'} | ${'external'} | ${true} + ${'https://github.com/gitlabhq/gitlabhq/issues/1'} | ${'external'} | ${true} + `( + 'returns $returnValue when `issuable.webUrl` is $urlType', + async ({ issuableWebUrl, returnValue }) => { + wrapper.setProps({ + issuable: { + ...mockIssuable, + webUrl: issuableWebUrl, + }, + }); + + await wrapper.vm.$nextTick(); + + expect(wrapper.vm.isIssuableUrlExternal).toBe(returnValue); + }, + ); + }); + describe('labels', () => { it('returns `issuable.labels.nodes` reference when it is available', () => { expect(wrapper.vm.labels).toEqual(mockLabels); @@ -92,6 +122,12 @@ describe('IssuableItem', () => { }); }); + describe('assignees', () => { + it('returns `issuable.assignees` reference when it is available', () => { + expect(wrapper.vm.assignees).toBe(mockIssuable.assignees); + }); + }); + describe('createdAt', () => { it('returns string containing timeago string based on `issuable.createdAt`', () => { expect(wrapper.vm.createdAt).toContain('created'); @@ -120,6 +156,34 @@ describe('IssuableItem', () => { }, ); }); + + describe('labelTitle', () => { + it.each` + label | propWithTitle | returnValue + ${{ title: 'foo' }} | ${'title'} | ${'foo'} + ${{ name: 'foo' }} | ${'name'} | ${'foo'} + `('returns string value of `label.$propWithTitle`', ({ label, returnValue }) => { + expect(wrapper.vm.labelTitle(label)).toBe(returnValue); + }); + }); + + describe('labelTarget', () => { + it('returns target string for a provided label param when `enableLabelPermalinks` is true', () => { + expect(wrapper.vm.labelTarget(mockRegularLabel)).toBe( + '?label_name%5B%5D=Documentation%20Update', + ); + }); + + it('returns string "#" for a provided label param when `enableLabelPermalinks` is false', async () => { + wrapper.setProps({ + enableLabelPermalinks: false, + }); + + await wrapper.vm.$nextTick(); + + expect(wrapper.vm.labelTarget(mockRegularLabel)).toBe('#'); + }); + }); }); describe('template', () => { @@ -128,9 +192,28 @@ describe('IssuableItem', () => { expect(titleEl.exists()).toBe(true); expect(titleEl.find(GlLink).attributes('href')).toBe(mockIssuable.webUrl); + expect(titleEl.find(GlLink).attributes('target')).not.toBeDefined(); expect(titleEl.find(GlLink).text()).toBe(mockIssuable.title); }); + it('renders issuable title with `target` set as "_blank" when issuable.webUrl is external', async () => { + wrapper.setProps({ + issuable: { + ...mockIssuable, + webUrl: 'http://jira.atlassian.net/browse/IG-1', + }, + }); + + await wrapper.vm.$nextTick(); + + expect( + wrapper + .find('[data-testid="issuable-title"]') + .find(GlLink) + .attributes('target'), + ).toBe('_blank'); + }); + it('renders issuable reference', () => { const referenceEl = wrapper.find('[data-testid="issuable-reference"]'); @@ -138,6 +221,24 @@ describe('IssuableItem', () => { expect(referenceEl.text()).toBe(`#${mockIssuable.iid}`); }); + it('renders issuable reference via slot', () => { + const wrapperWithRefSlot = createComponent({ + issuableSymbol: '#', + issuable: mockIssuable, + slots: { + reference: ` + <b class="js-reference">${mockIssuable.iid}</b> + `, + }, + }); + const referenceEl = wrapperWithRefSlot.find('.js-reference'); + + expect(referenceEl.exists()).toBe(true); + expect(referenceEl.text()).toBe(`${mockIssuable.iid}`); + + wrapperWithRefSlot.destroy(); + }); + it('renders issuable createdAt info', () => { const createdAtEl = wrapper.find('[data-testid="issuable-created-at"]'); @@ -151,7 +252,7 @@ describe('IssuableItem', () => { expect(authorEl.exists()).toBe(true); expect(authorEl.attributes()).toMatchObject({ - 'data-user-id': wrapper.vm.authorId, + 'data-user-id': `${wrapper.vm.authorId}`, 'data-username': mockAuthor.username, 'data-name': mockAuthor.name, 'data-avatar-url': mockAuthor.avatarUrl, @@ -170,10 +271,40 @@ describe('IssuableItem', () => { title: mockLabels[0].title, description: mockLabels[0].description, scoped: false, + target: wrapper.vm.labelTarget(mockLabels[0]), size: 'sm', }); }); + it('renders issuable status via slot', () => { + const wrapperWithStatusSlot = createComponent({ + issuableSymbol: '#', + issuable: mockIssuable, + slots: { + status: ` + <b class="js-status">${mockIssuable.state}</b> + `, + }, + }); + const statusEl = wrapperWithStatusSlot.find('.js-status'); + + expect(statusEl.exists()).toBe(true); + expect(statusEl.text()).toBe(`${mockIssuable.state}`); + + wrapperWithStatusSlot.destroy(); + }); + + it('renders issuable-assignees component', () => { + const assigneesEl = wrapper.find(IssuableAssignees); + + expect(assigneesEl.exists()).toBe(true); + expect(assigneesEl.props()).toMatchObject({ + assignees: mockIssuable.assignees, + iconSize: 16, + maxVisible: 4, + }); + }); + it('renders issuable updatedAt info', () => { const updatedAtEl = wrapper.find('[data-testid="issuable-updated-at"]'); diff --git a/spec/frontend/issuable_list/components/issuable_list_root_spec.js b/spec/frontend/issuable_list/components/issuable_list_root_spec.js index 34184522b55..e560b0915bc 100644 --- a/spec/frontend/issuable_list/components/issuable_list_root_spec.js +++ b/spec/frontend/issuable_list/components/issuable_list_root_spec.js @@ -1,6 +1,8 @@ import { mount } from '@vue/test-utils'; import { GlLoadingIcon, GlPagination } from '@gitlab/ui'; +import { TEST_HOST } from 'helpers/test_constants'; + import IssuableListRoot from '~/issuable_list/components/issuable_list_root.vue'; import IssuableTabs from '~/issuable_list/components/issuable_tabs.vue'; import IssuableItem from '~/issuable_list/components/issuable_item.vue'; @@ -32,6 +34,29 @@ describe('IssuableListRoot', () => { wrapper.destroy(); }); + describe('watch', () => { + describe('urlParams', () => { + it('updates window URL reflecting props within `urlParams`', async () => { + const urlParams = { + state: 'closed', + sort: 'updated_asc', + page: 1, + search: 'foo', + }; + + wrapper.setProps({ + urlParams, + }); + + await wrapper.vm.$nextTick(); + + expect(global.window.location.href).toBe( + `${TEST_HOST}/?state=${urlParams.state}&sort=${urlParams.sort}&page=${urlParams.page}&search=${urlParams.search}`, + ); + }); + }); + }); + describe('template', () => { it('renders component container element with class "issuable-list-container"', () => { expect(wrapper.classes()).toContain('issuable-list-container'); @@ -114,6 +139,7 @@ describe('IssuableListRoot', () => { it('renders gl-pagination when `showPaginationControls` prop is true', async () => { wrapper.setProps({ showPaginationControls: true, + totalPages: 10, }); await wrapper.vm.$nextTick(); @@ -125,6 +151,7 @@ describe('IssuableListRoot', () => { value: 1, prevPage: 0, nextPage: 2, + totalItems: 10, align: 'center', }); }); diff --git a/spec/frontend/issuable_list/mock_data.js b/spec/frontend/issuable_list/mock_data.js index 8eab2ca6f94..b18e49e2102 100644 --- a/spec/frontend/issuable_list/mock_data.js +++ b/spec/frontend/issuable_list/mock_data.js @@ -51,6 +51,7 @@ export const mockIssuable = { labels: { nodes: mockLabels, }, + assignees: [mockAuthor], }; export const mockIssuables = [ |