summaryrefslogtreecommitdiff
path: root/spec/frontend/usage_quotas/storage/components
diff options
context:
space:
mode:
Diffstat (limited to 'spec/frontend/usage_quotas/storage/components')
-rw-r--r--spec/frontend/usage_quotas/storage/components/project_storage_app_spec.js150
-rw-r--r--spec/frontend/usage_quotas/storage/components/project_storage_detail_spec.js129
-rw-r--r--spec/frontend/usage_quotas/storage/components/storage_type_icon_spec.js41
-rw-r--r--spec/frontend/usage_quotas/storage/components/usage_graph_spec.js144
4 files changed, 464 insertions, 0 deletions
diff --git a/spec/frontend/usage_quotas/storage/components/project_storage_app_spec.js b/spec/frontend/usage_quotas/storage/components/project_storage_app_spec.js
new file mode 100644
index 00000000000..3379af3f41c
--- /dev/null
+++ b/spec/frontend/usage_quotas/storage/components/project_storage_app_spec.js
@@ -0,0 +1,150 @@
+import { GlAlert, GlLoadingIcon } from '@gitlab/ui';
+import { shallowMount } from '@vue/test-utils';
+import Vue from 'vue';
+import VueApollo from 'vue-apollo';
+import createMockApollo from 'helpers/mock_apollo_helper';
+import { extendedWrapper } from 'helpers/vue_test_utils_helper';
+import waitForPromises from 'helpers/wait_for_promises';
+import ProjectStorageApp from '~/usage_quotas/storage/components/project_storage_app.vue';
+import UsageGraph from '~/usage_quotas/storage/components/usage_graph.vue';
+import { TOTAL_USAGE_DEFAULT_TEXT } from '~/usage_quotas/storage/constants';
+import getProjectStorageStatistics from '~/usage_quotas/storage/queries/project_storage.query.graphql';
+import {
+ projectData,
+ mockGetProjectStorageStatisticsGraphQLResponse,
+ mockEmptyResponse,
+ defaultProjectProvideValues,
+} from '../mock_data';
+
+Vue.use(VueApollo);
+
+describe('ProjectStorageApp', () => {
+ let wrapper;
+
+ const createMockApolloProvider = ({ reject = false, mockedValue } = {}) => {
+ let response;
+
+ if (reject) {
+ response = jest.fn().mockRejectedValue(mockedValue || new Error('GraphQL error'));
+ } else {
+ response = jest.fn().mockResolvedValue(mockedValue);
+ }
+
+ const requestHandlers = [[getProjectStorageStatistics, response]];
+
+ return createMockApollo(requestHandlers);
+ };
+
+ const createComponent = ({ provide = {}, mockApollo } = {}) => {
+ wrapper = extendedWrapper(
+ shallowMount(ProjectStorageApp, {
+ apolloProvider: mockApollo,
+ provide: {
+ ...defaultProjectProvideValues,
+ ...provide,
+ },
+ }),
+ );
+ };
+
+ const findAlert = () => wrapper.findComponent(GlAlert);
+ const findLoadingIcon = () => wrapper.findComponent(GlLoadingIcon);
+ const findUsagePercentage = () => wrapper.findByTestId('total-usage');
+ const findUsageQuotasHelpLink = () => wrapper.findByTestId('usage-quotas-help-link');
+ const findUsageGraph = () => wrapper.findComponent(UsageGraph);
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ describe('with apollo fetching successful', () => {
+ let mockApollo;
+
+ beforeEach(async () => {
+ mockApollo = createMockApolloProvider({
+ mockedValue: mockGetProjectStorageStatisticsGraphQLResponse,
+ });
+ createComponent({ mockApollo });
+ await waitForPromises();
+ });
+
+ it('renders correct total usage', () => {
+ expect(findUsagePercentage().text()).toBe(projectData.storage.totalUsage);
+ });
+
+ it('renders correct usage quotas help link', () => {
+ expect(findUsageQuotasHelpLink().attributes('href')).toBe(
+ defaultProjectProvideValues.helpLinks.usageQuotas,
+ );
+ });
+ });
+
+ describe('with apollo loading', () => {
+ let mockApollo;
+
+ beforeEach(() => {
+ mockApollo = createMockApolloProvider({
+ mockedValue: new Promise(() => {}),
+ });
+ createComponent({ mockApollo });
+ });
+
+ it('should show loading icon', () => {
+ expect(findLoadingIcon().exists()).toBe(true);
+ });
+ });
+
+ describe('with apollo returning empty data', () => {
+ let mockApollo;
+
+ beforeEach(async () => {
+ mockApollo = createMockApolloProvider({
+ mockedValue: mockEmptyResponse,
+ });
+ createComponent({ mockApollo });
+ await waitForPromises();
+ });
+
+ it('shows default text for total usage', () => {
+ expect(findUsagePercentage().text()).toBe(TOTAL_USAGE_DEFAULT_TEXT);
+ });
+ });
+
+ describe('with apollo fetching error', () => {
+ let mockApollo;
+
+ beforeEach(async () => {
+ mockApollo = createMockApolloProvider();
+ createComponent({ mockApollo, reject: true });
+ await waitForPromises();
+ });
+
+ it('renders gl-alert', () => {
+ expect(findAlert().exists()).toBe(true);
+ });
+ });
+
+ describe('rendering <usage-graph />', () => {
+ let mockApollo;
+
+ beforeEach(async () => {
+ mockApollo = createMockApolloProvider({
+ mockedValue: mockGetProjectStorageStatisticsGraphQLResponse,
+ });
+ createComponent({ mockApollo });
+ await waitForPromises();
+ });
+
+ it('renders usage-graph component if project.statistics exists', () => {
+ expect(findUsageGraph().exists()).toBe(true);
+ });
+
+ it('passes project.statistics to usage-graph component', () => {
+ const {
+ __typename,
+ ...statistics
+ } = mockGetProjectStorageStatisticsGraphQLResponse.data.project.statistics;
+ expect(findUsageGraph().props('rootStorageStatistics')).toMatchObject(statistics);
+ });
+ });
+});
diff --git a/spec/frontend/usage_quotas/storage/components/project_storage_detail_spec.js b/spec/frontend/usage_quotas/storage/components/project_storage_detail_spec.js
new file mode 100644
index 00000000000..ce489f69cad
--- /dev/null
+++ b/spec/frontend/usage_quotas/storage/components/project_storage_detail_spec.js
@@ -0,0 +1,129 @@
+import { GlTableLite, GlPopover } from '@gitlab/ui';
+import { mount } from '@vue/test-utils';
+import { extendedWrapper } from 'helpers/vue_test_utils_helper';
+import ProjectStorageDetail from '~/usage_quotas/storage/components/project_storage_detail.vue';
+import {
+ containerRegistryPopoverId,
+ containerRegistryId,
+ uploadsPopoverId,
+ uploadsId,
+} from '~/usage_quotas/storage/constants';
+import { numberToHumanSize } from '~/lib/utils/number_utils';
+import { projectData, projectHelpLinks } from '../mock_data';
+
+describe('ProjectStorageDetail', () => {
+ let wrapper;
+
+ const { storageTypes } = projectData.storage;
+ const defaultProps = { storageTypes };
+
+ const createComponent = (props = {}) => {
+ wrapper = extendedWrapper(
+ mount(ProjectStorageDetail, {
+ propsData: {
+ ...defaultProps,
+ ...props,
+ },
+ provide: {
+ containerRegistryPopoverContent: 'Sample popover message',
+ },
+ }),
+ );
+ };
+
+ const generateStorageType = (id = 'buildArtifactsSize') => {
+ return {
+ storageType: {
+ id,
+ name: 'Test Name',
+ description: 'Test Description',
+ helpPath: '/test-type',
+ },
+ value: 400000,
+ };
+ };
+
+ const findTable = () => wrapper.findComponent(GlTableLite);
+ const findPopoverById = (id) =>
+ wrapper.findAllComponents(GlPopover).filter((p) => p.attributes('data-testid') === id);
+ const findContainerRegistryPopover = () => findPopoverById(containerRegistryPopoverId);
+ const findUploadsPopover = () => findPopoverById(uploadsPopoverId);
+ const findContainerRegistryWarningIcon = () => wrapper.find(`#${containerRegistryPopoverId}`);
+ const findUploadsWarningIcon = () => wrapper.find(`#${uploadsPopoverId}`);
+
+ beforeEach(() => {
+ createComponent();
+ });
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ describe('with storage types', () => {
+ it.each(storageTypes)(
+ 'renders table row correctly %o',
+ ({ storageType: { id, name, description } }) => {
+ expect(wrapper.findByTestId(`${id}-name`).text()).toBe(name);
+ expect(wrapper.findByTestId(`${id}-description`).text()).toBe(description);
+ expect(wrapper.findByTestId(`${id}-icon`).props('name')).toBe(id);
+ expect(wrapper.findByTestId(`${id}-help-link`).attributes('href')).toBe(
+ projectHelpLinks[id.replace(`Size`, ``)],
+ );
+ },
+ );
+
+ it('should render items in order from the biggest usage size to the smallest', () => {
+ const rows = findTable().find('tbody').findAll('tr');
+ // Cloning array not to mutate the source
+ const sortedStorageTypes = [...storageTypes].sort((a, b) => b.value - a.value);
+
+ sortedStorageTypes.forEach((storageType, i) => {
+ const rowUsageAmount = rows.wrappers[i].find('td:last-child').text();
+ const expectedUsageAmount = numberToHumanSize(storageType.value, 1);
+ expect(rowUsageAmount).toBe(expectedUsageAmount);
+ });
+ });
+ });
+
+ describe('without storage types', () => {
+ beforeEach(() => {
+ createComponent({ storageTypes: [] });
+ });
+
+ it('should render the table header <th>', () => {
+ expect(findTable().find('th').exists()).toBe(true);
+ });
+
+ it('should not render any table data <td>', () => {
+ expect(findTable().find('td').exists()).toBe(false);
+ });
+ });
+
+ describe.each`
+ description | mockStorageTypes | rendersContainerRegistryPopover | rendersUploadsPopover
+ ${'without any storage type that has popover'} | ${[generateStorageType()]} | ${false} | ${false}
+ ${'with container registry storage type'} | ${[generateStorageType(containerRegistryId)]} | ${true} | ${false}
+ ${'with uploads storage type'} | ${[generateStorageType(uploadsId)]} | ${false} | ${true}
+ ${'with container registry and uploads storage types'} | ${[generateStorageType(containerRegistryId), generateStorageType(uploadsId)]} | ${true} | ${true}
+ `(
+ '$description',
+ ({ mockStorageTypes, rendersContainerRegistryPopover, rendersUploadsPopover }) => {
+ beforeEach(() => {
+ createComponent({ storageTypes: mockStorageTypes });
+ });
+
+ it(`does ${
+ rendersContainerRegistryPopover ? '' : ' not'
+ } render container registry warning icon and popover`, () => {
+ expect(findContainerRegistryWarningIcon().exists()).toBe(rendersContainerRegistryPopover);
+ expect(findContainerRegistryPopover().exists()).toBe(rendersContainerRegistryPopover);
+ });
+
+ it(`does ${
+ rendersUploadsPopover ? '' : ' not'
+ } render container registry warning icon and popover`, () => {
+ expect(findUploadsWarningIcon().exists()).toBe(rendersUploadsPopover);
+ expect(findUploadsPopover().exists()).toBe(rendersUploadsPopover);
+ });
+ },
+ );
+});
diff --git a/spec/frontend/usage_quotas/storage/components/storage_type_icon_spec.js b/spec/frontend/usage_quotas/storage/components/storage_type_icon_spec.js
new file mode 100644
index 00000000000..1eb3386bfb8
--- /dev/null
+++ b/spec/frontend/usage_quotas/storage/components/storage_type_icon_spec.js
@@ -0,0 +1,41 @@
+import { mount } from '@vue/test-utils';
+import { GlIcon } from '@gitlab/ui';
+import StorageTypeIcon from '~/usage_quotas/storage/components/storage_type_icon.vue';
+
+describe('StorageTypeIcon', () => {
+ let wrapper;
+
+ const createComponent = (props = {}) => {
+ wrapper = mount(StorageTypeIcon, {
+ propsData: {
+ ...props,
+ },
+ });
+ };
+
+ const findGlIcon = () => wrapper.findComponent(GlIcon);
+
+ describe('rendering icon', () => {
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ it.each`
+ expected | provided
+ ${'doc-image'} | ${'lfsObjectsSize'}
+ ${'snippet'} | ${'snippetsSize'}
+ ${'infrastructure-registry'} | ${'repositorySize'}
+ ${'package'} | ${'packagesSize'}
+ ${'upload'} | ${'uploadsSize'}
+ ${'disk'} | ${'wikiSize'}
+ ${'disk'} | ${'anything-else'}
+ `(
+ 'renders icon with name of $expected when name prop is $provided',
+ ({ expected, provided }) => {
+ createComponent({ name: provided });
+
+ expect(findGlIcon().props('name')).toBe(expected);
+ },
+ );
+ });
+});
diff --git a/spec/frontend/usage_quotas/storage/components/usage_graph_spec.js b/spec/frontend/usage_quotas/storage/components/usage_graph_spec.js
new file mode 100644
index 00000000000..75b970d937a
--- /dev/null
+++ b/spec/frontend/usage_quotas/storage/components/usage_graph_spec.js
@@ -0,0 +1,144 @@
+import { shallowMount } from '@vue/test-utils';
+import { numberToHumanSize } from '~/lib/utils/number_utils';
+import UsageGraph from '~/usage_quotas/storage/components/usage_graph.vue';
+
+let data;
+let wrapper;
+
+function mountComponent({ rootStorageStatistics, limit }) {
+ wrapper = shallowMount(UsageGraph, {
+ propsData: {
+ rootStorageStatistics,
+ limit,
+ },
+ });
+}
+function findStorageTypeUsagesSerialized() {
+ return wrapper
+ .findAll('[data-testid="storage-type-usage"]')
+ .wrappers.map((wp) => wp.element.style.flex);
+}
+
+describe('Storage Counter usage graph component', () => {
+ beforeEach(() => {
+ data = {
+ rootStorageStatistics: {
+ wikiSize: 5000,
+ repositorySize: 4000,
+ packagesSize: 3000,
+ containerRegistrySize: 2500,
+ lfsObjectsSize: 2000,
+ buildArtifactsSize: 500,
+ pipelineArtifactsSize: 500,
+ snippetsSize: 2000,
+ storageSize: 17000,
+ uploadsSize: 1000,
+ },
+ limit: 2000,
+ };
+ mountComponent(data);
+ });
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ it('renders the legend in order', () => {
+ const types = wrapper.findAll('[data-testid="storage-type-legend"]');
+
+ const {
+ buildArtifactsSize,
+ pipelineArtifactsSize,
+ lfsObjectsSize,
+ packagesSize,
+ containerRegistrySize,
+ repositorySize,
+ wikiSize,
+ snippetsSize,
+ uploadsSize,
+ } = data.rootStorageStatistics;
+
+ expect(types.at(0).text()).toMatchInterpolatedText(`Wiki ${numberToHumanSize(wikiSize)}`);
+ expect(types.at(1).text()).toMatchInterpolatedText(
+ `Repository ${numberToHumanSize(repositorySize)}`,
+ );
+ expect(types.at(2).text()).toMatchInterpolatedText(
+ `Packages ${numberToHumanSize(packagesSize)}`,
+ );
+ expect(types.at(3).text()).toMatchInterpolatedText(
+ `Container Registry ${numberToHumanSize(containerRegistrySize)}`,
+ );
+ expect(types.at(4).text()).toMatchInterpolatedText(
+ `LFS storage ${numberToHumanSize(lfsObjectsSize)}`,
+ );
+ expect(types.at(5).text()).toMatchInterpolatedText(
+ `Snippets ${numberToHumanSize(snippetsSize)}`,
+ );
+ expect(types.at(6).text()).toMatchInterpolatedText(
+ `Artifacts ${numberToHumanSize(buildArtifactsSize + pipelineArtifactsSize)}`,
+ );
+ expect(types.at(7).text()).toMatchInterpolatedText(`Uploads ${numberToHumanSize(uploadsSize)}`);
+ });
+
+ describe('when storage type is not used', () => {
+ beforeEach(() => {
+ data.rootStorageStatistics.wikiSize = 0;
+ mountComponent(data);
+ });
+
+ it('filters the storage type', () => {
+ expect(wrapper.text()).not.toContain('Wikis');
+ });
+ });
+
+ describe('when there is no storage usage', () => {
+ beforeEach(() => {
+ data.rootStorageStatistics.storageSize = 0;
+ mountComponent(data);
+ });
+
+ it('does not render', () => {
+ expect(wrapper.html()).toEqual('');
+ });
+ });
+
+ describe('when limit is 0', () => {
+ beforeEach(() => {
+ data.limit = 0;
+ mountComponent(data);
+ });
+
+ it('sets correct flex values', () => {
+ expect(findStorageTypeUsagesSerialized()).toStrictEqual([
+ '0.29411764705882354',
+ '0.23529411764705882',
+ '0.17647058823529413',
+ '0.14705882352941177',
+ '0.11764705882352941',
+ '0.11764705882352941',
+ '0.058823529411764705',
+ '0.058823529411764705',
+ ]);
+ });
+ });
+
+ describe('when storage exceeds limit', () => {
+ beforeEach(() => {
+ data.limit = data.rootStorageStatistics.storageSize - 1;
+ mountComponent(data);
+ });
+
+ it('does render correclty', () => {
+ expect(findStorageTypeUsagesSerialized()).toStrictEqual([
+ '0.29411764705882354',
+ '0.23529411764705882',
+ '0.17647058823529413',
+ '0.14705882352941177',
+ '0.11764705882352941',
+ '0.11764705882352941',
+ '0.058823529411764705',
+ '0.058823529411764705',
+ ]);
+ });
+ });
+});