diff options
Diffstat (limited to 'spec/frontend/analytics/shared/components')
4 files changed, 215 insertions, 40 deletions
diff --git a/spec/frontend/analytics/shared/components/daterange_spec.js b/spec/frontend/analytics/shared/components/daterange_spec.js index 854582abb82..a38df274243 100644 --- a/spec/frontend/analytics/shared/components/daterange_spec.js +++ b/spec/frontend/analytics/shared/components/daterange_spec.js @@ -1,7 +1,6 @@ -import { GlDaterangePicker } from '@gitlab/ui'; -import { mount } from '@vue/test-utils'; +import { GlDaterangePicker, GlSprintf } from '@gitlab/ui'; +import { shallowMount, mount } from '@vue/test-utils'; import { useFakeDate } from 'helpers/fake_date'; -import { createMockDirective, getBinding } from 'helpers/vue_mock_directive'; import Daterange from '~/analytics/shared/components/daterange.vue'; const defaultProps = { @@ -14,13 +13,13 @@ describe('Daterange component', () => { let wrapper; - const factory = (props = defaultProps) => { - wrapper = mount(Daterange, { + const factory = (props = defaultProps, mountFn = shallowMount) => { + wrapper = mountFn(Daterange, { propsData: { ...defaultProps, ...props, }, - directives: { GlTooltip: createMockDirective() }, + stubs: { GlSprintf }, }); }; @@ -28,9 +27,8 @@ describe('Daterange component', () => { wrapper.destroy(); }); - const findDaterangePicker = () => wrapper.find(GlDaterangePicker); - - const findDateRangeIndicator = () => wrapper.find('.daterange-indicator'); + const findDaterangePicker = () => wrapper.findComponent(GlDaterangePicker); + const findDateRangeIndicator = () => wrapper.findComponent(GlSprintf); describe('template', () => { describe('when show is false', () => { @@ -43,26 +41,24 @@ describe('Daterange component', () => { describe('when show is true', () => { it('renders the daterange picker', () => { factory({ show: true }); + expect(findDaterangePicker().exists()).toBe(true); }); }); describe('with a minDate being set', () => { - it('emits the change event with the minDate when the user enters a start date before the minDate', () => { + it('emits the change event with the minDate when the user enters a start date before the minDate', async () => { const startDate = new Date('2019-09-01'); const endDate = new Date('2019-09-30'); const minDate = new Date('2019-06-01'); - factory({ show: true, startDate, endDate, minDate }); - + factory({ show: true, startDate, endDate, minDate }, mount); const input = findDaterangePicker().find('input'); input.setValue('2019-01-01'); - input.trigger('change'); + await input.trigger('change'); - return wrapper.vm.$nextTick().then(() => { - expect(wrapper.emitted().change).toEqual([[{ startDate: minDate, endDate }]]); - }); + expect(wrapper.emitted().change).toEqual([[{ startDate: minDate, endDate }]]); }); }); @@ -76,16 +72,13 @@ describe('Daterange component', () => { }); it('displays the correct number of selected days in the indicator', () => { - expect(findDateRangeIndicator().find('span').text()).toBe('10 days selected'); + expect(findDateRangeIndicator().text()).toMatchInterpolatedText('10 days selected'); }); - it('displays a tooltip', () => { - const icon = wrapper.find('[data-testid="helper-icon"]'); - const tooltip = getBinding(icon.element, 'gl-tooltip'); - - expect(tooltip).toBeDefined(); - expect(icon.attributes('title')).toBe( - 'Showing data for workflow items created in this date range. Date range cannot exceed 30 days.', + it('sets the tooltip', () => { + const tooltip = findDaterangePicker().props('tooltip'); + expect(tooltip).toBe( + 'Showing data for workflow items created in this date range. Date range limited to 30 days.', ); }); }); diff --git a/spec/frontend/analytics/shared/components/metric_popover_spec.js b/spec/frontend/analytics/shared/components/metric_popover_spec.js new file mode 100644 index 00000000000..b799c911488 --- /dev/null +++ b/spec/frontend/analytics/shared/components/metric_popover_spec.js @@ -0,0 +1,102 @@ +import { GlLink, GlIcon } from '@gitlab/ui'; +import { shallowMountExtended } from 'helpers/vue_test_utils_helper'; +import MetricPopover from '~/analytics/shared/components/metric_popover.vue'; + +const MOCK_METRIC = { + key: 'deployment-frequency', + label: 'Deployment Frequency', + value: '10.0', + unit: 'per day', + description: 'Average number of deployments to production per day.', + links: [], +}; + +describe('MetricPopover', () => { + let wrapper; + + const createComponent = (props = {}) => { + return shallowMountExtended(MetricPopover, { + propsData: { + target: 'deployment-frequency', + ...props, + }, + stubs: { + 'gl-popover': { template: '<div><slot name="title"></slot><slot></slot></div>' }, + }, + }); + }; + + const findMetricLabel = () => wrapper.findByTestId('metric-label'); + const findAllMetricLinks = () => wrapper.findAll('[data-testid="metric-link"]'); + const findMetricDescription = () => wrapper.findByTestId('metric-description'); + const findMetricDocsLink = () => wrapper.findByTestId('metric-docs-link'); + const findMetricDocsLinkIcon = () => findMetricDocsLink().find(GlIcon); + + afterEach(() => { + wrapper.destroy(); + }); + + it('renders the metric label', () => { + wrapper = createComponent({ metric: MOCK_METRIC }); + expect(findMetricLabel().text()).toBe(MOCK_METRIC.label); + }); + + it('renders the metric description', () => { + wrapper = createComponent({ metric: MOCK_METRIC }); + expect(findMetricDescription().text()).toBe(MOCK_METRIC.description); + }); + + describe('with links', () => { + const links = [ + { + name: 'Deployment frequency', + url: '/groups/gitlab-org/-/analytics/ci_cd?tab=deployment-frequency', + label: 'Dashboard', + }, + { + name: 'Another link', + url: '/groups/gitlab-org/-/analytics/another-link', + label: 'Another link', + }, + ]; + const docsLink = { + name: 'Deployment frequency', + url: '/help/user/analytics/index#definitions', + label: 'Go to docs', + docs_link: true, + }; + const linksWithDocs = [...links, docsLink]; + + describe.each` + hasDocsLink | allLinks | displayedMetricLinks + ${true} | ${linksWithDocs} | ${links} + ${false} | ${links} | ${links} + `( + 'when one link has docs_link=$hasDocsLink', + ({ hasDocsLink, allLinks, displayedMetricLinks }) => { + beforeEach(() => { + wrapper = createComponent({ metric: { ...MOCK_METRIC, links: allLinks } }); + }); + + displayedMetricLinks.forEach((link, idx) => { + it(`renders a link for "${link.name}"`, () => { + const allLinkContainers = findAllMetricLinks(); + + expect(allLinkContainers.at(idx).text()).toContain(link.name); + expect(allLinkContainers.at(idx).find(GlLink).attributes('href')).toBe(link.url); + }); + }); + + it(`${hasDocsLink ? 'renders' : "doesn't render"} a docs link`, () => { + expect(findMetricDocsLink().exists()).toBe(hasDocsLink); + + if (hasDocsLink) { + expect(findMetricDocsLink().attributes('href')).toBe(docsLink.url); + expect(findMetricDocsLink().text()).toBe(docsLink.label); + expect(findMetricDocsLinkIcon().attributes('name')).toBe('external-link'); + } + }); + }, + ); + }); +}); diff --git a/spec/frontend/analytics/shared/components/metric_tile_spec.js b/spec/frontend/analytics/shared/components/metric_tile_spec.js new file mode 100644 index 00000000000..980dfad9eb0 --- /dev/null +++ b/spec/frontend/analytics/shared/components/metric_tile_spec.js @@ -0,0 +1,81 @@ +import { GlSingleStat } from '@gitlab/ui/dist/charts'; +import { shallowMount } from '@vue/test-utils'; +import MetricTile from '~/analytics/shared/components/metric_tile.vue'; +import MetricPopover from '~/analytics/shared/components/metric_popover.vue'; +import { redirectTo } from '~/lib/utils/url_utility'; + +jest.mock('~/lib/utils/url_utility'); + +describe('MetricTile', () => { + let wrapper; + + const createComponent = (props = {}) => { + return shallowMount(MetricTile, { + propsData: { + metric: {}, + ...props, + }, + }); + }; + + const findSingleStat = () => wrapper.findComponent(GlSingleStat); + const findPopover = () => wrapper.findComponent(MetricPopover); + + afterEach(() => { + wrapper.destroy(); + }); + + describe('template', () => { + describe('links', () => { + it('when the metric has links, it redirects the user on click', () => { + const metric = { + identifier: 'deploys', + value: '10', + label: 'Deploys', + links: [{ url: 'foo/bar' }], + }; + wrapper = createComponent({ metric }); + + const singleStat = findSingleStat(); + singleStat.vm.$emit('click'); + expect(redirectTo).toHaveBeenCalledWith('foo/bar'); + }); + + it("when the metric doesn't have links, it won't the user on click", () => { + const metric = { identifier: 'deploys', value: '10', label: 'Deploys' }; + wrapper = createComponent({ metric }); + + const singleStat = findSingleStat(); + singleStat.vm.$emit('click'); + expect(redirectTo).not.toHaveBeenCalled(); + }); + }); + + describe('decimal places', () => { + it(`will render 0 decimal places for an integer value`, () => { + const metric = { identifier: 'deploys', value: '10', label: 'Deploys' }; + wrapper = createComponent({ metric }); + + const singleStat = findSingleStat(); + expect(singleStat.props('animationDecimalPlaces')).toBe(0); + }); + + it(`will render 1 decimal place for a float value`, () => { + const metric = { identifier: 'deploys', value: '10.5', label: 'Deploys' }; + wrapper = createComponent({ metric }); + + const singleStat = findSingleStat(); + expect(singleStat.props('animationDecimalPlaces')).toBe(1); + }); + }); + + it('renders a metric popover', () => { + const metric = { identifier: 'deploys', value: '10', label: 'Deploys' }; + wrapper = createComponent({ metric }); + + const popover = findPopover(); + expect(popover.exists()).toBe(true); + expect(popover.props()).toMatchObject({ metric, target: metric.identifier }); + }); + }); +}); diff --git a/spec/frontend/analytics/shared/components/projects_dropdown_filter_spec.js b/spec/frontend/analytics/shared/components/projects_dropdown_filter_spec.js index 28d7ebe28df..386fb4eb616 100644 --- a/spec/frontend/analytics/shared/components/projects_dropdown_filter_spec.js +++ b/spec/frontend/analytics/shared/components/projects_dropdown_filter_spec.js @@ -1,4 +1,5 @@ import { GlDropdown, GlDropdownItem } from '@gitlab/ui'; +import { nextTick } from 'vue'; import { mountExtended } from 'helpers/vue_test_utils_helper'; import { stubComponent } from 'helpers/stub_component'; import { TEST_HOST } from 'helpers/test_constants'; @@ -99,9 +100,9 @@ describe('ProjectsDropdownFilter component', () => { const findDropdownFullPathAtIndex = (index) => findDropdownAtIndex(index).find('[data-testid="project-full-path"]'); - const selectDropdownItemAtIndex = (index) => { + const selectDropdownItemAtIndex = async (index) => { findDropdownAtIndex(index).find('button').trigger('click'); - return wrapper.vm.$nextTick(); + await nextTick(); }; // NOTE: Selected items are now visually separated from unselected items @@ -132,16 +133,15 @@ describe('ProjectsDropdownFilter component', () => { expect(spyQuery).toHaveBeenCalledTimes(1); - await wrapper.vm.$nextTick(() => { - expect(spyQuery).toHaveBeenCalledWith({ - query: getProjects, - variables: { - search: 'gitlab', - groupFullPath: wrapper.vm.groupNamespace, - first: 50, - includeSubgroups: true, - }, - }); + await nextTick(); + expect(spyQuery).toHaveBeenCalledWith({ + query: getProjects, + variables: { + search: 'gitlab', + groupFullPath: wrapper.vm.groupNamespace, + first: 50, + includeSubgroups: true, + }, }); }); }); @@ -193,7 +193,7 @@ describe('ProjectsDropdownFilter component', () => { expect(wrapper.text()).toContain('2 projects selected'); findClearAllButton().trigger('click'); - await wrapper.vm.$nextTick(); + await nextTick(); expect(wrapper.text()).not.toContain('2 projects selected'); expect(wrapper.text()).toContain('Select projects'); @@ -366,9 +366,8 @@ describe('ProjectsDropdownFilter component', () => { selectDropdownItemAtIndex(0); selectDropdownItemAtIndex(1); - await wrapper.vm.$nextTick().then(() => { - expect(findDropdownButton().text()).toBe('2 projects selected'); - }); + await nextTick(); + expect(findDropdownButton().text()).toBe('2 projects selected'); }); }); }); |