summaryrefslogtreecommitdiff
path: root/spec/frontend/projects
diff options
context:
space:
mode:
Diffstat (limited to 'spec/frontend/projects')
-rw-r--r--spec/frontend/projects/pipelines/charts/components/pipeline_charts_spec.js2
-rw-r--r--spec/frontend/projects/settings_service_desk/components/service_desk_setting_spec.js23
-rw-r--r--spec/frontend/projects/storage_counter/components/app_spec.js150
-rw-r--r--spec/frontend/projects/storage_counter/components/storage_table_spec.js62
-rw-r--r--spec/frontend/projects/storage_counter/mock_data.js109
-rw-r--r--spec/frontend/projects/storage_counter/utils_spec.js17
-rw-r--r--spec/frontend/projects/terraform_notification/terraform_notification_spec.js70
7 files changed, 417 insertions, 16 deletions
diff --git a/spec/frontend/projects/pipelines/charts/components/pipeline_charts_spec.js b/spec/frontend/projects/pipelines/charts/components/pipeline_charts_spec.js
index b5ee62f2042..6ef49390c47 100644
--- a/spec/frontend/projects/pipelines/charts/components/pipeline_charts_spec.js
+++ b/spec/frontend/projects/pipelines/charts/components/pipeline_charts_spec.js
@@ -60,7 +60,7 @@ describe('~/projects/pipelines/charts/components/pipeline_charts.vue', () => {
expect(chart.props('yAxisTitle')).toBe('Minutes');
expect(chart.props('xAxisTitle')).toBe('Commit');
expect(chart.props('bars')).toBe(wrapper.vm.timesChartTransformedData);
- expect(chart.props('option')).toBe(wrapper.vm.$options.timesChartOptions);
+ expect(chart.props('option')).toBe(wrapper.vm.chartOptions);
});
});
diff --git a/spec/frontend/projects/settings_service_desk/components/service_desk_setting_spec.js b/spec/frontend/projects/settings_service_desk/components/service_desk_setting_spec.js
index 5323c1afbb5..eacf858f22c 100644
--- a/spec/frontend/projects/settings_service_desk/components/service_desk_setting_spec.js
+++ b/spec/frontend/projects/settings_service_desk/components/service_desk_setting_spec.js
@@ -107,6 +107,29 @@ describe('ServiceDeskSetting', () => {
});
});
+ describe('project suffix', () => {
+ it('input is hidden', () => {
+ wrapper = createComponent({
+ props: { customEmailEnabled: false },
+ });
+
+ const input = wrapper.findByTestId('project-suffix');
+
+ expect(input.exists()).toBe(false);
+ });
+
+ it('input is enabled', () => {
+ wrapper = createComponent({
+ props: { customEmailEnabled: true },
+ });
+
+ const input = wrapper.findByTestId('project-suffix');
+
+ expect(input.exists()).toBe(true);
+ expect(input.attributes('disabled')).toBeUndefined();
+ });
+ });
+
describe('customEmail is the same as incomingEmail', () => {
const email = 'foo@bar.com';
diff --git a/spec/frontend/projects/storage_counter/components/app_spec.js b/spec/frontend/projects/storage_counter/components/app_spec.js
new file mode 100644
index 00000000000..f3da01e0602
--- /dev/null
+++ b/spec/frontend/projects/storage_counter/components/app_spec.js
@@ -0,0 +1,150 @@
+import { GlAlert, GlLoadingIcon } from '@gitlab/ui';
+import { shallowMount, createLocalVue } from '@vue/test-utils';
+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 StorageCounterApp from '~/projects/storage_counter/components/app.vue';
+import { TOTAL_USAGE_DEFAULT_TEXT } from '~/projects/storage_counter/constants';
+import getProjectStorageCount from '~/projects/storage_counter/queries/project_storage.query.graphql';
+import UsageGraph from '~/vue_shared/components/storage_counter/usage_graph.vue';
+import {
+ mockGetProjectStorageCountGraphQLResponse,
+ mockEmptyResponse,
+ projectData,
+ defaultProvideValues,
+} from '../mock_data';
+
+const localVue = createLocalVue();
+localVue.use(VueApollo);
+
+describe('Storage counter app', () => {
+ 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 = [[getProjectStorageCount, response]];
+
+ return createMockApollo(requestHandlers);
+ };
+
+ const createComponent = ({ provide = {}, mockApollo } = {}) => {
+ wrapper = extendedWrapper(
+ shallowMount(StorageCounterApp, {
+ localVue,
+ apolloProvider: mockApollo,
+ provide: {
+ ...defaultProvideValues,
+ ...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: mockGetProjectStorageCountGraphQLResponse,
+ });
+ 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(
+ defaultProvideValues.helpLinks.usageQuotasHelpPagePath,
+ );
+ });
+ });
+
+ 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(() => {
+ mockApollo = createMockApolloProvider();
+ createComponent({ mockApollo, reject: true });
+ });
+
+ it('renders gl-alert', () => {
+ expect(findAlert().exists()).toBe(true);
+ });
+ });
+
+ describe('rendering <usage-graph />', () => {
+ let mockApollo;
+
+ beforeEach(async () => {
+ mockApollo = createMockApolloProvider({
+ mockedValue: mockGetProjectStorageCountGraphQLResponse,
+ });
+ 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
+ } = mockGetProjectStorageCountGraphQLResponse.data.project.statistics;
+ expect(findUsageGraph().props('rootStorageStatistics')).toMatchObject(statistics);
+ });
+ });
+});
diff --git a/spec/frontend/projects/storage_counter/components/storage_table_spec.js b/spec/frontend/projects/storage_counter/components/storage_table_spec.js
new file mode 100644
index 00000000000..14298318fff
--- /dev/null
+++ b/spec/frontend/projects/storage_counter/components/storage_table_spec.js
@@ -0,0 +1,62 @@
+import { GlTable } from '@gitlab/ui';
+import { mount } from '@vue/test-utils';
+import { extendedWrapper } from 'helpers/vue_test_utils_helper';
+import StorageTable from '~/projects/storage_counter/components/storage_table.vue';
+import { projectData, defaultProvideValues } from '../mock_data';
+
+describe('StorageTable', () => {
+ let wrapper;
+
+ const defaultProps = {
+ storageTypes: projectData.storage.storageTypes,
+ };
+
+ const createComponent = (props = {}) => {
+ wrapper = extendedWrapper(
+ mount(StorageTable, {
+ propsData: {
+ ...defaultProps,
+ ...props,
+ },
+ }),
+ );
+ };
+
+ const findTable = () => wrapper.findComponent(GlTable);
+
+ beforeEach(() => {
+ createComponent();
+ });
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ describe('with storage types', () => {
+ it.each(projectData.storage.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}-help-link`).attributes('href')).toBe(
+ defaultProvideValues.helpLinks[id.replace(`Size`, `HelpPagePath`)]
+ .replace(`Size`, ``)
+ .replace(/[A-Z]/g, (m) => `-${m.toLowerCase()}`),
+ );
+ },
+ );
+ });
+
+ 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);
+ });
+ });
+});
diff --git a/spec/frontend/projects/storage_counter/mock_data.js b/spec/frontend/projects/storage_counter/mock_data.js
new file mode 100644
index 00000000000..b9fa68b3ec7
--- /dev/null
+++ b/spec/frontend/projects/storage_counter/mock_data.js
@@ -0,0 +1,109 @@
+export const mockGetProjectStorageCountGraphQLResponse = {
+ data: {
+ project: {
+ id: 'gid://gitlab/Project/20',
+ statistics: {
+ buildArtifactsSize: 400000.0,
+ pipelineArtifactsSize: 25000.0,
+ lfsObjectsSize: 4800000.0,
+ packagesSize: 3800000.0,
+ repositorySize: 3900000.0,
+ snippetsSize: 1200000.0,
+ storageSize: 15300000.0,
+ uploadsSize: 900000.0,
+ wikiSize: 300000.0,
+ __typename: 'ProjectStatistics',
+ },
+ __typename: 'Project',
+ },
+ },
+};
+
+export const mockEmptyResponse = { data: { project: null } };
+
+export const defaultProvideValues = {
+ projectPath: '/project-path',
+ helpLinks: {
+ usageQuotasHelpPagePath: '/usage-quotas',
+ buildArtifactsHelpPagePath: '/build-artifacts',
+ lfsObjectsHelpPagePath: '/lsf-objects',
+ packagesHelpPagePath: '/packages',
+ repositoryHelpPagePath: '/repository',
+ snippetsHelpPagePath: '/snippets',
+ uploadsHelpPagePath: '/uploads',
+ wikiHelpPagePath: '/wiki',
+ },
+};
+
+export const projectData = {
+ storage: {
+ totalUsage: '14.6 MiB',
+ storageTypes: [
+ {
+ storageType: {
+ id: 'buildArtifactsSize',
+ name: 'Artifacts',
+ description: 'Pipeline artifacts and job artifacts, created with CI/CD.',
+ warningMessage:
+ 'There is a known issue with Artifact storage where the total could be incorrect for some projects. More details and progress are available in %{warningLinkStart}the epic%{warningLinkEnd}.',
+ helpPath: '/build-artifacts',
+ },
+ value: 400000,
+ },
+ {
+ storageType: {
+ id: 'lfsObjectsSize',
+ name: 'LFS Storage',
+ description: 'Audio samples, videos, datasets, and graphics.',
+ helpPath: '/lsf-objects',
+ },
+ value: 4800000,
+ },
+ {
+ storageType: {
+ id: 'packagesSize',
+ name: 'Packages',
+ description: 'Code packages and container images.',
+ helpPath: '/packages',
+ },
+ value: 3800000,
+ },
+ {
+ storageType: {
+ id: 'repositorySize',
+ name: 'Repository',
+ description: 'Git repository, managed by the Gitaly service.',
+ helpPath: '/repository',
+ },
+ value: 3900000,
+ },
+ {
+ storageType: {
+ id: 'snippetsSize',
+ name: 'Snippets',
+ description: 'Shared bits of code and text.',
+ helpPath: '/snippets',
+ },
+ value: 1200000,
+ },
+ {
+ storageType: {
+ id: 'uploadsSize',
+ name: 'Uploads',
+ description: 'File attachments and smaller design graphics.',
+ helpPath: '/uploads',
+ },
+ value: 900000,
+ },
+ {
+ storageType: {
+ id: 'wikiSize',
+ name: 'Wiki',
+ description: 'Wiki content.',
+ helpPath: '/wiki',
+ },
+ value: 300000,
+ },
+ ],
+ },
+};
diff --git a/spec/frontend/projects/storage_counter/utils_spec.js b/spec/frontend/projects/storage_counter/utils_spec.js
new file mode 100644
index 00000000000..57c755266a0
--- /dev/null
+++ b/spec/frontend/projects/storage_counter/utils_spec.js
@@ -0,0 +1,17 @@
+import { parseGetProjectStorageResults } from '~/projects/storage_counter/utils';
+import {
+ mockGetProjectStorageCountGraphQLResponse,
+ projectData,
+ defaultProvideValues,
+} from './mock_data';
+
+describe('parseGetProjectStorageResults', () => {
+ it('parses project statistics correctly', () => {
+ expect(
+ parseGetProjectStorageResults(
+ mockGetProjectStorageCountGraphQLResponse.data,
+ defaultProvideValues.helpLinks,
+ ),
+ ).toMatchObject(projectData);
+ });
+});
diff --git a/spec/frontend/projects/terraform_notification/terraform_notification_spec.js b/spec/frontend/projects/terraform_notification/terraform_notification_spec.js
index 71c22998b08..6576ce70d60 100644
--- a/spec/frontend/projects/terraform_notification/terraform_notification_spec.js
+++ b/spec/frontend/projects/terraform_notification/terraform_notification_spec.js
@@ -1,51 +1,91 @@
import { GlBanner } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils';
-import { setCookie, parseBoolean } from '~/lib/utils/common_utils';
+import { makeMockUserCalloutDismisser } from 'helpers/mock_user_callout_dismisser';
+import { mockTracking } from 'helpers/tracking_helper';
import TerraformNotification from '~/projects/terraform_notification/components/terraform_notification.vue';
-
-jest.mock('~/lib/utils/common_utils');
+import {
+ EVENT_LABEL,
+ DISMISS_EVENT,
+ CLICK_EVENT,
+} from '~/projects/terraform_notification/constants';
const terraformImagePath = '/path/to/image';
-const bannerDismissedKey = 'terraform_notification_dismissed';
describe('TerraformNotificationBanner', () => {
let wrapper;
+ let trackingSpy;
+ let userCalloutDismissSpy;
const provideData = {
terraformImagePath,
- bannerDismissedKey,
};
const findBanner = () => wrapper.findComponent(GlBanner);
- beforeEach(() => {
+ const createComponent = ({ shouldShowCallout = true } = {}) => {
+ userCalloutDismissSpy = jest.fn();
+
wrapper = shallowMount(TerraformNotification, {
provide: provideData,
- stubs: { GlBanner },
+ stubs: {
+ GlBanner,
+ UserCalloutDismisser: makeMockUserCalloutDismisser({
+ dismiss: userCalloutDismissSpy,
+ shouldShowCallout,
+ }),
+ },
});
+ };
+
+ beforeEach(() => {
+ createComponent();
+ trackingSpy = mockTracking(undefined, wrapper.element, jest.spyOn);
});
afterEach(() => {
wrapper.destroy();
- parseBoolean.mockReturnValue(false);
});
- describe('when the dismiss cookie is not set', () => {
+ describe('when user has already dismissed the banner', () => {
+ beforeEach(() => {
+ createComponent({
+ shouldShowCallout: false,
+ });
+ });
+ it('should not render the banner', () => {
+ expect(findBanner().exists()).toBe(false);
+ });
+ });
+
+ describe("when user hasn't yet dismissed the banner", () => {
it('should render the banner', () => {
expect(findBanner().exists()).toBe(true);
});
});
describe('when close button is clicked', () => {
- beforeEach(async () => {
- await findBanner().vm.$emit('close');
+ beforeEach(() => {
+ wrapper.vm.$refs.calloutDismisser.dismiss = userCalloutDismissSpy;
+ findBanner().vm.$emit('close');
+ });
+ it('should send the dismiss event', () => {
+ expect(trackingSpy).toHaveBeenCalledWith(undefined, DISMISS_EVENT, {
+ label: EVENT_LABEL,
+ });
});
+ it('should call the dismiss callback', () => {
+ expect(userCalloutDismissSpy).toHaveBeenCalledTimes(1);
+ });
+ });
- it('should set the cookie with the bannerDismissedKey', () => {
- expect(setCookie).toHaveBeenCalledWith(bannerDismissedKey, true);
+ describe('when docs link is clicked', () => {
+ beforeEach(() => {
+ findBanner().vm.$emit('primary');
});
- it('should remove the banner', () => {
- expect(findBanner().exists()).toBe(false);
+ it('should send button click event', () => {
+ expect(trackingSpy).toHaveBeenCalledWith(undefined, CLICK_EVENT, {
+ label: EVENT_LABEL,
+ });
});
});
});