diff options
Diffstat (limited to 'spec/frontend/cycle_analytics')
-rw-r--r-- | spec/frontend/cycle_analytics/base_spec.js | 2 | ||||
-rw-r--r-- | spec/frontend/cycle_analytics/filter_bar_spec.js | 9 | ||||
-rw-r--r-- | spec/frontend/cycle_analytics/metric_popover_spec.js | 102 | ||||
-rw-r--r-- | spec/frontend/cycle_analytics/stage_table_spec.js | 3 | ||||
-rw-r--r-- | spec/frontend/cycle_analytics/utils_spec.js | 31 | ||||
-rw-r--r-- | spec/frontend/cycle_analytics/value_stream_metrics_spec.js | 80 |
6 files changed, 57 insertions, 170 deletions
diff --git a/spec/frontend/cycle_analytics/base_spec.js b/spec/frontend/cycle_analytics/base_spec.js index 9a9415cc12a..7b1ef71da63 100644 --- a/spec/frontend/cycle_analytics/base_spec.js +++ b/spec/frontend/cycle_analytics/base_spec.js @@ -3,11 +3,11 @@ import { shallowMount } from '@vue/test-utils'; import Vue from 'vue'; import Vuex from 'vuex'; import { extendedWrapper } from 'helpers/vue_test_utils_helper'; +import ValueStreamMetrics from '~/analytics/shared/components/value_stream_metrics.vue'; import BaseComponent from '~/cycle_analytics/components/base.vue'; import PathNavigation from '~/cycle_analytics/components/path_navigation.vue'; import StageTable from '~/cycle_analytics/components/stage_table.vue'; import ValueStreamFilters from '~/cycle_analytics/components/value_stream_filters.vue'; -import ValueStreamMetrics from '~/cycle_analytics/components/value_stream_metrics.vue'; import { NOT_ENOUGH_DATA_ERROR } from '~/cycle_analytics/constants'; import initState from '~/cycle_analytics/store/state'; import { diff --git a/spec/frontend/cycle_analytics/filter_bar_spec.js b/spec/frontend/cycle_analytics/filter_bar_spec.js index 407f21bd956..36933790cf7 100644 --- a/spec/frontend/cycle_analytics/filter_bar_spec.js +++ b/spec/frontend/cycle_analytics/filter_bar_spec.js @@ -1,4 +1,5 @@ -import { createLocalVue, shallowMount } from '@vue/test-utils'; +import { shallowMount } from '@vue/test-utils'; +import Vue, { nextTick } from 'vue'; import axios from 'axios'; import MockAdapter from 'axios-mock-adapter'; import Vuex from 'vuex'; @@ -15,8 +16,7 @@ import * as utils from '~/vue_shared/components/filtered_search_bar/filtered_sea import initialFiltersState from '~/vue_shared/components/filtered_search_bar/store/modules/filters/state'; import UrlSync from '~/vue_shared/components/url_sync.vue'; -const localVue = createLocalVue(); -localVue.use(Vuex); +Vue.use(Vuex); const milestoneTokenType = 'milestone'; const labelsTokenType = 'labels'; @@ -42,7 +42,7 @@ const defaultParams = { }; async function shouldMergeUrlParams(wrapper, result) { - await wrapper.vm.$nextTick(); + await nextTick(); expect(urlUtils.mergeUrlParams).toHaveBeenCalledWith(result, window.location.href, { spreadArrays: true, }); @@ -77,7 +77,6 @@ describe('Filter bar', () => { const createComponent = (initialStore) => { return shallowMount(FilterBar, { - localVue, store: initialStore, propsData: { groupPath: 'foo', diff --git a/spec/frontend/cycle_analytics/metric_popover_spec.js b/spec/frontend/cycle_analytics/metric_popover_spec.js deleted file mode 100644 index 5a622fcacd5..00000000000 --- a/spec/frontend/cycle_analytics/metric_popover_spec.js +++ /dev/null @@ -1,102 +0,0 @@ -import { GlLink, GlIcon } from '@gitlab/ui'; -import { shallowMountExtended } from 'helpers/vue_test_utils_helper'; -import MetricPopover from '~/cycle_analytics/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/cycle_analytics/stage_table_spec.js b/spec/frontend/cycle_analytics/stage_table_spec.js index 9605dce2668..107fe5fc865 100644 --- a/spec/frontend/cycle_analytics/stage_table_spec.js +++ b/spec/frontend/cycle_analytics/stage_table_spec.js @@ -1,5 +1,6 @@ import { GlEmptyState, GlLoadingIcon, GlTable } from '@gitlab/ui'; import { shallowMount, mount } from '@vue/test-utils'; +import { nextTick } from 'vue'; import { mockTracking, unmockTracking } from 'helpers/tracking_helper'; import { extendedWrapper } from 'helpers/vue_test_utils_helper'; import StageTable from '~/cycle_analytics/components/stage_table.vue'; @@ -263,7 +264,7 @@ describe('StageTable', () => { expect(wrapper.emitted('handleUpdatePagination')).toBeUndefined(); findPagination().vm.$emit('input', 2); - await wrapper.vm.$nextTick(); + await nextTick(); expect(wrapper.emitted('handleUpdatePagination')[0]).toEqual([{ page: 2 }]); }); diff --git a/spec/frontend/cycle_analytics/utils_spec.js b/spec/frontend/cycle_analytics/utils_spec.js index a6d6d022781..51405a1ba4d 100644 --- a/spec/frontend/cycle_analytics/utils_spec.js +++ b/spec/frontend/cycle_analytics/utils_spec.js @@ -1,13 +1,10 @@ -import metricsData from 'test_fixtures/projects/analytics/value_stream_analytics/summary.json'; import { transformStagesForPathNavigation, medianTimeToParsedSeconds, formatMedianValues, filterStagesByHiddenStatus, - prepareTimeMetricsData, buildCycleAnalyticsInitialData, } from '~/cycle_analytics/utils'; -import { slugify } from '~/lib/utils/text_utility'; import { selectedStage, allowedStages, @@ -89,34 +86,6 @@ describe('Value stream analytics utils', () => { }); }); - describe('prepareTimeMetricsData', () => { - let prepared; - const [first, second] = metricsData; - const firstKey = slugify(first.title); - const secondKey = slugify(second.title); - - beforeEach(() => { - prepared = prepareTimeMetricsData([first, second], { - [firstKey]: { description: 'Is a value that is good' }, - }); - }); - - it('will add a `key` based on the title', () => { - expect(prepared).toMatchObject([{ key: firstKey }, { key: secondKey }]); - }); - - it('will add a `label` key', () => { - expect(prepared).toMatchObject([{ label: 'New Issues' }, { label: 'Commits' }]); - }); - - it('will add a popover description using the key if it is provided', () => { - expect(prepared).toMatchObject([ - { description: 'Is a value that is good' }, - { description: '' }, - ]); - }); - }); - describe('buildCycleAnalyticsInitialData', () => { let res = null; const projectId = '5'; diff --git a/spec/frontend/cycle_analytics/value_stream_metrics_spec.js b/spec/frontend/cycle_analytics/value_stream_metrics_spec.js index 082db2cc312..7a539b262fc 100644 --- a/spec/frontend/cycle_analytics/value_stream_metrics_spec.js +++ b/spec/frontend/cycle_analytics/value_stream_metrics_spec.js @@ -1,20 +1,22 @@ import { GlDeprecatedSkeletonLoading as GlSkeletonLoading } from '@gitlab/ui'; -import { GlSingleStat } from '@gitlab/ui/dist/charts'; import { shallowMount } from '@vue/test-utils'; +import { nextTick } from 'vue'; import metricsData from 'test_fixtures/projects/analytics/value_stream_analytics/summary.json'; import waitForPromises from 'helpers/wait_for_promises'; +import ValueStreamMetrics from '~/analytics/shared/components/value_stream_metrics.vue'; import { METRIC_TYPE_SUMMARY } from '~/api/analytics_api'; -import ValueStreamMetrics from '~/cycle_analytics/components/value_stream_metrics.vue'; +import { METRICS_POPOVER_CONTENT } from '~/analytics/shared/constants'; +import { prepareTimeMetricsData } from '~/analytics/shared/utils'; +import MetricTile from '~/analytics/shared/components/metric_tile.vue'; import createFlash from '~/flash'; -import { redirectTo } from '~/lib/utils/url_utility'; import { group } from './mock_data'; jest.mock('~/flash'); -jest.mock('~/lib/utils/url_utility'); describe('ValueStreamMetrics', () => { let wrapper; let mockGetValueStreamSummaryMetrics; + let mockFilterFn; const { full_path: requestPath } = group; const fakeReqName = 'Mock metrics'; @@ -24,17 +26,18 @@ describe('ValueStreamMetrics', () => { name: fakeReqName, }); - const createComponent = ({ requestParams = {} } = {}) => { + const createComponent = (props = {}) => { return shallowMount(ValueStreamMetrics, { propsData: { requestPath, - requestParams, + requestParams: {}, requests: [metricsRequestFactory()], + ...props, }, }); }; - const findMetrics = () => wrapper.findAllComponents(GlSingleStat); + const findMetrics = () => wrapper.findAllComponents(MetricTile); const expectToHaveRequest = (fields) => { expect(mockGetValueStreamSummaryMetrics).toHaveBeenCalledWith({ @@ -55,19 +58,19 @@ describe('ValueStreamMetrics', () => { }); it('will display a loader with pending requests', async () => { - await wrapper.vm.$nextTick(); + await nextTick(); expect(wrapper.findComponent(GlSkeletonLoading).exists()).toBe(true); }); - it('renders hidden GlSingleStat components for each metric', async () => { + it('renders hidden MetricTile components for each metric', async () => { await waitForPromises(); // setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details // eslint-disable-next-line no-restricted-syntax wrapper.setData({ isLoading: true }); - await wrapper.vm.$nextTick(); + await nextTick(); const components = findMetrics(); @@ -88,35 +91,52 @@ describe('ValueStreamMetrics', () => { }); describe.each` - index | value | title | unit | clickable - ${0} | ${metricsData[0].value} | ${metricsData[0].title} | ${metricsData[0].unit} | ${false} - ${1} | ${metricsData[1].value} | ${metricsData[1].title} | ${metricsData[1].unit} | ${false} - ${2} | ${metricsData[2].value} | ${metricsData[2].title} | ${metricsData[2].unit} | ${false} - ${3} | ${metricsData[3].value} | ${metricsData[3].title} | ${metricsData[3].unit} | ${true} - `('metric tiles', ({ index, value, title, unit, clickable }) => { - it(`renders a single stat component for "${title}" with value and unit`, () => { + index | identifier | value | label + ${0} | ${metricsData[0].identifier} | ${metricsData[0].value} | ${metricsData[0].title} + ${1} | ${metricsData[1].identifier} | ${metricsData[1].value} | ${metricsData[1].title} + ${2} | ${metricsData[2].identifier} | ${metricsData[2].value} | ${metricsData[2].title} + ${3} | ${metricsData[3].identifier} | ${metricsData[3].value} | ${metricsData[3].title} + `('metric tiles', ({ identifier, index, value, label }) => { + it(`renders a metric tile component for "${label}"`, () => { const metric = findMetrics().at(index); - expect(metric.props()).toMatchObject({ value, title, unit: unit ?? '' }); + expect(metric.props('metric')).toMatchObject({ identifier, value, label }); expect(metric.isVisible()).toBe(true); }); - - it(`${ - clickable ? 'redirects' : "doesn't redirect" - } when the user clicks the "${title}" metric`, () => { - const metric = findMetrics().at(index); - metric.vm.$emit('click'); - if (clickable) { - expect(redirectTo).toHaveBeenCalledWith(metricsData[index].links[0].url); - } else { - expect(redirectTo).not.toHaveBeenCalled(); - } - }); }); it('will not display a loading icon', () => { expect(wrapper.find(GlSkeletonLoading).exists()).toBe(false); }); + describe('filterFn', () => { + const transferedMetricsData = prepareTimeMetricsData(metricsData, METRICS_POPOVER_CONTENT); + + it('with a filter function, will call the function with the metrics data', async () => { + const filteredData = [ + { identifier: 'issues', value: '3', title: 'New Issues', description: 'foo' }, + ]; + mockFilterFn = jest.fn(() => filteredData); + + wrapper = createComponent({ + filterFn: mockFilterFn, + }); + + await waitForPromises(); + + expect(mockFilterFn).toHaveBeenCalledWith(transferedMetricsData); + expect(wrapper.vm.metrics).toEqual(filteredData); + }); + + it('without a filter function, it will only update the metrics', async () => { + wrapper = createComponent(); + + await waitForPromises(); + + expect(mockFilterFn).not.toHaveBeenCalled(); + expect(wrapper.vm.metrics).toEqual(transferedMetricsData); + }); + }); + describe('with additional params', () => { beforeEach(async () => { wrapper = createComponent({ |