summaryrefslogtreecommitdiff
path: root/spec/frontend/admin
diff options
context:
space:
mode:
Diffstat (limited to 'spec/frontend/admin')
-rw-r--r--spec/frontend/admin/analytics/devops_score/components/devops_score_spec.js134
-rw-r--r--spec/frontend/admin/analytics/devops_score/mock_data.js46
-rw-r--r--spec/frontend/admin/users/components/actions/actions_spec.js5
-rw-r--r--spec/frontend/admin/users/components/user_actions_spec.js12
-rw-r--r--spec/frontend/admin/users/components/users_table_spec.js87
-rw-r--r--spec/frontend/admin/users/mock_data.js15
-rw-r--r--spec/frontend/admin/users/tabs_spec.js37
7 files changed, 286 insertions, 50 deletions
diff --git a/spec/frontend/admin/analytics/devops_score/components/devops_score_spec.js b/spec/frontend/admin/analytics/devops_score/components/devops_score_spec.js
new file mode 100644
index 00000000000..7c20bbe21c8
--- /dev/null
+++ b/spec/frontend/admin/analytics/devops_score/components/devops_score_spec.js
@@ -0,0 +1,134 @@
+import { GlTable, GlBadge, GlEmptyState, GlLink } from '@gitlab/ui';
+import { GlSingleStat } from '@gitlab/ui/dist/charts';
+import { mount } from '@vue/test-utils';
+import { extendedWrapper } from 'helpers/vue_test_utils_helper';
+import DevopsScore from '~/analytics/devops_report/components/devops_score.vue';
+import {
+ devopsScoreMetricsData,
+ devopsReportDocsPath,
+ noDataImagePath,
+ devopsScoreTableHeaders,
+} from '../mock_data';
+
+describe('DevopsScore', () => {
+ let wrapper;
+
+ const createComponent = ({ devopsScoreMetrics = devopsScoreMetricsData } = {}) => {
+ wrapper = extendedWrapper(
+ mount(DevopsScore, {
+ provide: {
+ devopsScoreMetrics,
+ devopsReportDocsPath,
+ noDataImagePath,
+ },
+ }),
+ );
+ };
+
+ const findTable = () => wrapper.findComponent(GlTable);
+ const findEmptyState = () => wrapper.findComponent(GlEmptyState);
+ const findCol = (testId) => findTable().find(`[data-testid="${testId}"]`);
+ const findUsageCol = () => findCol('usageCol');
+ const findDevopsScoreApp = () => wrapper.findByTestId('devops-score-app');
+
+ describe('with no data', () => {
+ beforeEach(() => {
+ createComponent({ devopsScoreMetrics: {} });
+ });
+
+ describe('empty state', () => {
+ it('displays the empty state', () => {
+ expect(findEmptyState().exists()).toBe(true);
+ });
+
+ it('displays the correct message', () => {
+ expect(findEmptyState().text()).toBe(
+ 'Data is still calculating... It may be several days before you see feature usage data. See example DevOps Score page in our documentation.',
+ );
+ });
+
+ it('contains a link to the feature documentation', () => {
+ expect(wrapper.findComponent(GlLink).exists()).toBe(true);
+ });
+ });
+
+ it('does not display the devops score app', () => {
+ expect(findDevopsScoreApp().exists()).toBe(false);
+ });
+ });
+
+ describe('with data', () => {
+ beforeEach(() => {
+ createComponent();
+ });
+
+ it('does not display the empty state', () => {
+ expect(findEmptyState().exists()).toBe(false);
+ });
+
+ it('displays the devops score app', () => {
+ expect(findDevopsScoreApp().exists()).toBe(true);
+ });
+
+ describe('devops score app', () => {
+ it('displays the title note', () => {
+ expect(wrapper.findByTestId('devops-score-note-text').text()).toBe(
+ 'DevOps score metrics are based on usage over the last 30 days. Last updated: 2020-06-29 08:16.',
+ );
+ });
+
+ it('displays the single stat section', () => {
+ const component = wrapper.findComponent(GlSingleStat);
+
+ expect(component.exists()).toBe(true);
+ expect(component.props('value')).toBe(devopsScoreMetricsData.averageScore.value);
+ });
+
+ describe('devops score table', () => {
+ it('displays the table', () => {
+ expect(findTable().exists()).toBe(true);
+ });
+
+ describe('table headings', () => {
+ let headers;
+
+ beforeEach(() => {
+ headers = findTable().findAll("[data-testid='header']");
+ });
+
+ it('displays the correct number of headings', () => {
+ expect(headers).toHaveLength(devopsScoreTableHeaders.length);
+ });
+
+ describe.each(devopsScoreTableHeaders)('header fields', ({ label, index }) => {
+ let headerWrapper;
+
+ beforeEach(() => {
+ headerWrapper = headers.at(index);
+ });
+
+ it(`displays the correct table heading text for "${label}"`, () => {
+ expect(headerWrapper.text()).toContain(label);
+ });
+ });
+ });
+
+ describe('table columns', () => {
+ describe('Your usage', () => {
+ it('displays the corrrect value', () => {
+ expect(findUsageCol().text()).toContain('3.2');
+ });
+
+ it('displays the corrrect badge', () => {
+ const badge = findUsageCol().find(GlBadge);
+
+ expect(badge.exists()).toBe(true);
+ expect(badge.props('variant')).toBe('muted');
+ expect(badge.text()).toBe('Low');
+ });
+ });
+ });
+ });
+ });
+ });
+});
diff --git a/spec/frontend/admin/analytics/devops_score/mock_data.js b/spec/frontend/admin/analytics/devops_score/mock_data.js
new file mode 100644
index 00000000000..ae0c01a2661
--- /dev/null
+++ b/spec/frontend/admin/analytics/devops_score/mock_data.js
@@ -0,0 +1,46 @@
+export const devopsScoreTableHeaders = [
+ {
+ index: 0,
+ label: '',
+ },
+ {
+ index: 1,
+ label: 'Your usage',
+ },
+ {
+ index: 2,
+ label: 'Leader usage',
+ },
+ {
+ index: 3,
+ label: 'Score',
+ },
+];
+
+export const devopsScoreMetricsData = {
+ createdAt: '2020-06-29 08:16',
+ cards: [
+ {
+ title: 'Issues created per active user',
+ usage: '3.2',
+ leadInstance: '10.2',
+ score: '0',
+ scoreLevel: {
+ label: 'Low',
+ variant: 'muted',
+ },
+ },
+ ],
+ averageScore: {
+ value: '10',
+ scoreLevel: {
+ label: 'High',
+ icon: 'check-circle',
+ variant: 'success',
+ },
+ },
+};
+
+export const devopsReportDocsPath = 'docs-path';
+
+export const noDataImagePath = 'image-path';
diff --git a/spec/frontend/admin/users/components/actions/actions_spec.js b/spec/frontend/admin/users/components/actions/actions_spec.js
index 5e232f34311..5db5b8a90a9 100644
--- a/spec/frontend/admin/users/components/actions/actions_spec.js
+++ b/spec/frontend/admin/users/components/actions/actions_spec.js
@@ -71,6 +71,7 @@ describe('Action components', () => {
});
describe('DELETE_ACTION_COMPONENTS', () => {
+ const oncallSchedules = [{ name: 'schedule1' }, { name: 'schedule2' }];
it.each(DELETE_ACTIONS)('renders a dropdown item for "%s"', async (action) => {
initComponent({
component: Actions[capitalizeFirstCharacter(action)],
@@ -80,6 +81,7 @@ describe('Action components', () => {
delete: '/delete',
block: '/block',
},
+ oncallSchedules,
},
stubs: { SharedDeleteAction },
});
@@ -92,6 +94,9 @@ describe('Action components', () => {
expect(sharedAction.attributes('data-delete-user-url')).toBe('/delete');
expect(sharedAction.attributes('data-gl-modal-action')).toBe(kebabCase(action));
expect(sharedAction.attributes('data-username')).toBe('John Doe');
+ expect(sharedAction.attributes('data-oncall-schedules')).toBe(
+ JSON.stringify(oncallSchedules),
+ );
expect(findDropdownItem().exists()).toBe(true);
});
});
diff --git a/spec/frontend/admin/users/components/user_actions_spec.js b/spec/frontend/admin/users/components/user_actions_spec.js
index 0745d961f25..debe964e7aa 100644
--- a/spec/frontend/admin/users/components/user_actions_spec.js
+++ b/spec/frontend/admin/users/components/user_actions_spec.js
@@ -1,5 +1,5 @@
import { GlDropdownDivider } from '@gitlab/ui';
-import { shallowMount } from '@vue/test-utils';
+import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
import Actions from '~/admin/users/components/actions';
import AdminUserActions from '~/admin/users/components/user_actions.vue';
import { I18N_USER_ACTIONS } from '~/admin/users/constants';
@@ -14,12 +14,14 @@ describe('AdminUserActions component', () => {
const user = users[0];
const userPaths = generateUserPaths(paths, user.username);
- const findEditButton = () => wrapper.find('[data-testid="edit"]');
- const findActionsDropdown = () => wrapper.find('[data-testid="actions"');
- const findDropdownDivider = () => wrapper.find(GlDropdownDivider);
+ const findUserActions = (id) => wrapper.findByTestId(`user-actions-${id}`);
+ const findEditButton = (id = user.id) => findUserActions(id).find('[data-testid="edit"]');
+ const findActionsDropdown = (id = user.id) =>
+ findUserActions(id).find('[data-testid="dropdown-toggle"]');
+ const findDropdownDivider = () => wrapper.findComponent(GlDropdownDivider);
const initComponent = ({ actions = [] } = {}) => {
- wrapper = shallowMount(AdminUserActions, {
+ wrapper = shallowMountExtended(AdminUserActions, {
propsData: {
user: {
...user,
diff --git a/spec/frontend/admin/users/components/users_table_spec.js b/spec/frontend/admin/users/components/users_table_spec.js
index 424b0deebd3..708c9e1979e 100644
--- a/spec/frontend/admin/users/components/users_table_spec.js
+++ b/spec/frontend/admin/users/components/users_table_spec.js
@@ -1,16 +1,36 @@
-import { GlTable } from '@gitlab/ui';
-import { mount } from '@vue/test-utils';
+import { GlTable, GlSkeletonLoader } from '@gitlab/ui';
+import { createLocalVue } from '@vue/test-utils';
+import VueApollo from 'vue-apollo';
+
+import createMockApollo from 'helpers/mock_apollo_helper';
+import { mountExtended } from 'helpers/vue_test_utils_helper';
import AdminUserActions from '~/admin/users/components/user_actions.vue';
import AdminUserAvatar from '~/admin/users/components/user_avatar.vue';
import AdminUsersTable from '~/admin/users/components/users_table.vue';
+import getUsersGroupCountsQuery from '~/admin/users/graphql/queries/get_users_group_counts.query.graphql';
+import createFlash from '~/flash';
import AdminUserDate from '~/vue_shared/components/user_date.vue';
-import { users, paths } from '../mock_data';
+import { users, paths, createGroupCountResponse } from '../mock_data';
+
+jest.mock('~/flash');
+
+const localVue = createLocalVue();
+localVue.use(VueApollo);
describe('AdminUsersTable component', () => {
let wrapper;
+ const user = users[0];
+ const createFetchGroupCount = (data) =>
+ jest.fn().mockResolvedValue(createGroupCountResponse(data));
+ const fetchGroupCountsLoading = jest.fn().mockResolvedValue(new Promise(() => {}));
+ const fetchGroupCountsError = jest.fn().mockRejectedValue(new Error('Network error'));
+ const fetchGroupCountsResponse = createFetchGroupCount([{ id: user.id, groupCount: 5 }]);
+
+ const findUserGroupCount = (id) => wrapper.findByTestId(`user-group-count-${id}`);
+ const findUserGroupCountLoader = (id) => findUserGroupCount(id).find(GlSkeletonLoader);
const getCellByLabel = (trIdx, label) => {
return wrapper
.find(GlTable)
@@ -20,8 +40,16 @@ describe('AdminUsersTable component', () => {
.find(`[data-label="${label}"][role="cell"]`);
};
- const initComponent = (props = {}) => {
- wrapper = mount(AdminUsersTable, {
+ function createMockApolloProvider(resolverMock) {
+ const requestHandlers = [[getUsersGroupCountsQuery, resolverMock]];
+
+ return createMockApollo(requestHandlers);
+ }
+
+ const initComponent = (props = {}, resolverMock = fetchGroupCountsResponse) => {
+ wrapper = mountExtended(AdminUsersTable, {
+ localVue,
+ apolloProvider: createMockApolloProvider(resolverMock),
propsData: {
users,
paths,
@@ -36,8 +64,6 @@ describe('AdminUsersTable component', () => {
});
describe('when there are users', () => {
- const user = users[0];
-
beforeEach(() => {
initComponent();
});
@@ -69,4 +95,51 @@ describe('AdminUsersTable component', () => {
expect(wrapper.text()).toContain('No users found');
});
});
+
+ describe('group counts', () => {
+ describe('when fetching the data', () => {
+ beforeEach(() => {
+ initComponent({}, fetchGroupCountsLoading);
+ });
+
+ it('renders a loader for each user', () => {
+ expect(findUserGroupCountLoader(user.id).exists()).toBe(true);
+ });
+ });
+
+ describe('when the data has been fetched', () => {
+ beforeEach(() => {
+ initComponent();
+ });
+
+ it("renders the user's group count", () => {
+ expect(findUserGroupCount(user.id).text()).toBe('5');
+ });
+
+ describe("and a user's group count is null", () => {
+ beforeEach(() => {
+ initComponent({}, createFetchGroupCount([{ id: user.id, groupCount: null }]));
+ });
+
+ it("renders the user's group count as 0", () => {
+ expect(findUserGroupCount(user.id).text()).toBe('0');
+ });
+ });
+ });
+
+ describe('when there is an error while fetching the data', () => {
+ beforeEach(() => {
+ initComponent({}, fetchGroupCountsError);
+ });
+
+ it('creates a flash message and captures the error', () => {
+ expect(createFlash).toHaveBeenCalledTimes(1);
+ expect(createFlash).toHaveBeenCalledWith({
+ message: 'Could not load user group counts. Please refresh the page to try again.',
+ captureError: true,
+ error: expect.any(Error),
+ });
+ });
+ });
+ });
});
diff --git a/spec/frontend/admin/users/mock_data.js b/spec/frontend/admin/users/mock_data.js
index c3918ef5173..4689ab36773 100644
--- a/spec/frontend/admin/users/mock_data.js
+++ b/spec/frontend/admin/users/mock_data.js
@@ -10,7 +10,7 @@ export const users = [
'https://secure.gravatar.com/avatar/054f062d8b1a42b123f17e13a173cda8?s=80\\u0026d=identicon',
badges: [
{ text: 'Admin', variant: 'success' },
- { text: "It's you!", variant: null },
+ { text: "It's you!", variant: 'muted' },
],
projectsCount: 0,
actions: [],
@@ -31,3 +31,16 @@ export const paths = {
deleteWithContributions: '/admin/users/id',
adminUser: '/admin/users/id',
};
+
+export const createGroupCountResponse = (groupCounts) => ({
+ data: {
+ users: {
+ nodes: groupCounts.map(({ id, groupCount }) => ({
+ id: `gid://gitlab/User/${id}`,
+ groupCount,
+ __typename: 'UserCore',
+ })),
+ __typename: 'UserCoreConnection',
+ },
+ },
+});
diff --git a/spec/frontend/admin/users/tabs_spec.js b/spec/frontend/admin/users/tabs_spec.js
deleted file mode 100644
index 39ba8618486..00000000000
--- a/spec/frontend/admin/users/tabs_spec.js
+++ /dev/null
@@ -1,37 +0,0 @@
-import initTabs from '~/admin/users/tabs';
-import Api from '~/api';
-
-jest.mock('~/api.js');
-jest.mock('~/lib/utils/common_utils');
-
-describe('tabs', () => {
- beforeEach(() => {
- setFixtures(`
- <div>
- <div class="js-users-tab-item">
- <a href="#users" data-testid='users-tab'>Users</a>
- </div>
- <div class="js-users-tab-item">
- <a href="#cohorts" data-testid='cohorts-tab'>Cohorts</a>
- </div>
- </div`);
-
- initTabs();
- });
-
- afterEach(() => {});
-
- describe('tracking', () => {
- it('tracks event when cohorts tab is clicked', () => {
- document.querySelector('[data-testid="cohorts-tab"]').click();
-
- expect(Api.trackRedisHllUserEvent).toHaveBeenCalledWith('i_analytics_cohorts');
- });
-
- it('does not track an event when users tab is clicked', () => {
- document.querySelector('[data-testid="users-tab"]').click();
-
- expect(Api.trackRedisHllUserEvent).not.toHaveBeenCalled();
- });
- });
-});