summaryrefslogtreecommitdiff
path: root/spec/frontend/projects/settings
diff options
context:
space:
mode:
Diffstat (limited to 'spec/frontend/projects/settings')
-rw-r--r--spec/frontend/projects/settings/branch_rules/components/edit/branch_dropdown_spec.js (renamed from spec/frontend/projects/settings/branch_rules/branch_dropdown_spec.js)2
-rw-r--r--spec/frontend/projects/settings/branch_rules/components/edit/index_spec.js (renamed from spec/frontend/projects/settings/branch_rules/rule_edit_spec.js)6
-rw-r--r--spec/frontend/projects/settings/branch_rules/components/edit/protections/index_spec.js (renamed from spec/frontend/projects/settings/branch_rules/components/protections/index_spec.js)8
-rw-r--r--spec/frontend/projects/settings/branch_rules/components/edit/protections/merge_protections_spec.js (renamed from spec/frontend/projects/settings/branch_rules/components/protections/merge_protections_spec.js)4
-rw-r--r--spec/frontend/projects/settings/branch_rules/components/edit/protections/push_protections_spec.js (renamed from spec/frontend/projects/settings/branch_rules/components/protections/push_protections_spec.js)4
-rw-r--r--spec/frontend/projects/settings/branch_rules/components/view/index_spec.js113
-rw-r--r--spec/frontend/projects/settings/branch_rules/components/view/mock_data.js141
-rw-r--r--spec/frontend/projects/settings/branch_rules/components/view/protection_row_spec.js71
-rw-r--r--spec/frontend/projects/settings/branch_rules/components/view/protection_spec.js68
-rw-r--r--spec/frontend/projects/settings/components/default_branch_selector_spec.js46
-rw-r--r--spec/frontend/projects/settings/components/transfer_project_form_spec.js273
-rw-r--r--spec/frontend/projects/settings/repository/branch_rules/app_spec.js18
-rw-r--r--spec/frontend/projects/settings/repository/branch_rules/components/branch_rule_spec.js30
-rw-r--r--spec/frontend/projects/settings/repository/branch_rules/mock_data.js13
14 files changed, 676 insertions, 121 deletions
diff --git a/spec/frontend/projects/settings/branch_rules/branch_dropdown_spec.js b/spec/frontend/projects/settings/branch_rules/components/edit/branch_dropdown_spec.js
index 79bce5a4b3f..11f219c1f90 100644
--- a/spec/frontend/projects/settings/branch_rules/branch_dropdown_spec.js
+++ b/spec/frontend/projects/settings/branch_rules/components/edit/branch_dropdown_spec.js
@@ -4,7 +4,7 @@ import { GlDropdown, GlSearchBoxByType, GlDropdownItem, GlSprintf } from '@gitla
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
import BranchDropdown, {
i18n,
-} from '~/projects/settings/branch_rules/components/branch_dropdown.vue';
+} from '~/projects/settings/branch_rules/components/edit/branch_dropdown.vue';
import createMockApollo from 'helpers/mock_apollo_helper';
import branchesQuery from '~/projects/settings/branch_rules/queries/branches.query.graphql';
import waitForPromises from 'helpers/wait_for_promises';
diff --git a/spec/frontend/projects/settings/branch_rules/rule_edit_spec.js b/spec/frontend/projects/settings/branch_rules/components/edit/index_spec.js
index b0b2b9191d4..21e63fdb24d 100644
--- a/spec/frontend/projects/settings/branch_rules/rule_edit_spec.js
+++ b/spec/frontend/projects/settings/branch_rules/components/edit/index_spec.js
@@ -1,9 +1,9 @@
import { nextTick } from 'vue';
import { getParameterByName } from '~/lib/utils/url_utility';
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
-import RuleEdit from '~/projects/settings/branch_rules/components/rule_edit.vue';
-import BranchDropdown from '~/projects/settings/branch_rules/components/branch_dropdown.vue';
-import Protections from '~/projects/settings/branch_rules/components/protections/index.vue';
+import RuleEdit from '~/projects/settings/branch_rules/components/edit/index.vue';
+import BranchDropdown from '~/projects/settings/branch_rules/components/edit/branch_dropdown.vue';
+import Protections from '~/projects/settings/branch_rules/components/edit/protections/index.vue';
jest.mock('~/lib/utils/url_utility', () => ({
getParameterByName: jest.fn().mockImplementation(() => 'main'),
diff --git a/spec/frontend/projects/settings/branch_rules/components/protections/index_spec.js b/spec/frontend/projects/settings/branch_rules/components/edit/protections/index_spec.js
index 3592fa50622..ee90ff8318f 100644
--- a/spec/frontend/projects/settings/branch_rules/components/protections/index_spec.js
+++ b/spec/frontend/projects/settings/branch_rules/components/edit/protections/index_spec.js
@@ -3,10 +3,10 @@ import { GlLink } from '@gitlab/ui';
import { mountExtended } from 'helpers/vue_test_utils_helper';
import Protections, {
i18n,
-} from '~/projects/settings/branch_rules/components/protections/index.vue';
-import PushProtections from '~/projects/settings/branch_rules/components/protections/push_protections.vue';
-import MergeProtections from '~/projects/settings/branch_rules/components/protections/merge_protections.vue';
-import { protections } from '../../mock_data';
+} from '~/projects/settings/branch_rules/components/edit/protections/index.vue';
+import PushProtections from '~/projects/settings/branch_rules/components/edit/protections/push_protections.vue';
+import MergeProtections from '~/projects/settings/branch_rules/components/edit/protections/merge_protections.vue';
+import { protections } from '../../../mock_data';
describe('Branch Protections', () => {
let wrapper;
diff --git a/spec/frontend/projects/settings/branch_rules/components/protections/merge_protections_spec.js b/spec/frontend/projects/settings/branch_rules/components/edit/protections/merge_protections_spec.js
index 0e168a2ad78..b5fdc46d600 100644
--- a/spec/frontend/projects/settings/branch_rules/components/protections/merge_protections_spec.js
+++ b/spec/frontend/projects/settings/branch_rules/components/edit/protections/merge_protections_spec.js
@@ -2,8 +2,8 @@ import { GlFormGroup, GlFormCheckbox } from '@gitlab/ui';
import { mountExtended } from 'helpers/vue_test_utils_helper';
import MergeProtections, {
i18n,
-} from '~/projects/settings/branch_rules/components/protections/merge_protections.vue';
-import { membersAllowedToMerge, requireCodeOwnersApproval } from '../../mock_data';
+} from '~/projects/settings/branch_rules/components/edit/protections/merge_protections.vue';
+import { membersAllowedToMerge, requireCodeOwnersApproval } from '../../../mock_data';
describe('Merge Protections', () => {
let wrapper;
diff --git a/spec/frontend/projects/settings/branch_rules/components/protections/push_protections_spec.js b/spec/frontend/projects/settings/branch_rules/components/edit/protections/push_protections_spec.js
index d54dad08338..60bb7a51dcb 100644
--- a/spec/frontend/projects/settings/branch_rules/components/protections/push_protections_spec.js
+++ b/spec/frontend/projects/settings/branch_rules/components/edit/protections/push_protections_spec.js
@@ -2,8 +2,8 @@ import { GlFormGroup, GlSprintf, GlFormCheckbox } from '@gitlab/ui';
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
import PushProtections, {
i18n,
-} from '~/projects/settings/branch_rules/components/protections/push_protections.vue';
-import { membersAllowedToPush, allowForcePush } from '../../mock_data';
+} from '~/projects/settings/branch_rules/components/edit/protections/push_protections.vue';
+import { membersAllowedToPush, allowForcePush } from '../../../mock_data';
describe('Push Protections', () => {
let wrapper;
diff --git a/spec/frontend/projects/settings/branch_rules/components/view/index_spec.js b/spec/frontend/projects/settings/branch_rules/components/view/index_spec.js
new file mode 100644
index 00000000000..bf4026b65db
--- /dev/null
+++ b/spec/frontend/projects/settings/branch_rules/components/view/index_spec.js
@@ -0,0 +1,113 @@
+import Vue from 'vue';
+import VueApollo from 'vue-apollo';
+import * as util from '~/lib/utils/url_utility';
+import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
+import createMockApollo from 'helpers/mock_apollo_helper';
+import waitForPromises from 'helpers/wait_for_promises';
+import RuleView from '~/projects/settings/branch_rules/components/view/index.vue';
+import {
+ I18N,
+ ALL_BRANCHES_WILDCARD,
+} from '~/projects/settings/branch_rules/components/view/constants';
+import Protection from '~/projects/settings/branch_rules/components/view/protection.vue';
+import branchRulesQuery from '~/projects/settings/branch_rules/queries/branch_rules_details.query.graphql';
+import { sprintf } from '~/locale';
+import { branchProtectionsMockResponse } from './mock_data';
+
+jest.mock('~/lib/utils/url_utility', () => ({
+ getParameterByName: jest.fn().mockReturnValue('main'),
+ joinPaths: jest.fn(),
+}));
+
+Vue.use(VueApollo);
+
+const protectionMockProps = {
+ headerLinkHref: 'protected/branches',
+ headerLinkTitle: 'Manage in Protected Branches',
+ roles: [{ accessLevelDescription: 'Maintainers' }],
+ users: [{ avatarUrl: 'test.com/user.png', name: 'peter', webUrl: 'test.com' }],
+};
+
+describe('View branch rules', () => {
+ let wrapper;
+ let fakeApollo;
+ const projectPath = 'test/testing';
+ const protectedBranchesPath = 'protected/branches';
+ const approvalRulesPath = 'approval/rules';
+ const branchProtectionsMockRequestHandler = jest
+ .fn()
+ .mockResolvedValue(branchProtectionsMockResponse);
+
+ const createComponent = async () => {
+ fakeApollo = createMockApollo([[branchRulesQuery, branchProtectionsMockRequestHandler]]);
+
+ wrapper = shallowMountExtended(RuleView, {
+ apolloProvider: fakeApollo,
+ provide: { projectPath, protectedBranchesPath, approvalRulesPath },
+ });
+
+ await waitForPromises();
+ };
+
+ beforeEach(() => createComponent());
+
+ afterEach(() => wrapper.destroy());
+
+ const findBranchName = () => wrapper.findByTestId('branch');
+ const findBranchTitle = () => wrapper.findByTestId('branch-title');
+ const findBranchProtectionTitle = () => wrapper.findByText(I18N.protectBranchTitle);
+ const findBranchProtections = () => wrapper.findAllComponents(Protection);
+ const findForcePushTitle = () => wrapper.findByText(I18N.allowForcePushDescription);
+ const findApprovalsTitle = () => wrapper.findByText(I18N.approvalsTitle);
+
+ it('gets the branch param from url and renders it in the view', () => {
+ expect(util.getParameterByName).toHaveBeenCalledWith('branch');
+ expect(findBranchName().text()).toBe('main');
+ expect(findBranchTitle().text()).toBe(I18N.branchNameOrPattern);
+ });
+
+ it('renders the correct label if all branches are targeted', async () => {
+ jest.spyOn(util, 'getParameterByName').mockReturnValueOnce(ALL_BRANCHES_WILDCARD);
+ await createComponent();
+
+ expect(findBranchName().text()).toBe(I18N.allBranches);
+ expect(findBranchTitle().text()).toBe(I18N.targetBranch);
+ jest.restoreAllMocks();
+ });
+
+ it('renders the correct branch title', () => {
+ expect(findBranchTitle().exists()).toBe(true);
+ });
+
+ it('renders a branch protection title', () => {
+ expect(findBranchProtectionTitle().exists()).toBe(true);
+ });
+
+ it('renders a branch protection component for push rules', () => {
+ expect(findBranchProtections().at(0).props()).toMatchObject({
+ header: sprintf(I18N.allowedToPushHeader, { total: 2 }),
+ ...protectionMockProps,
+ });
+ });
+
+ it('renders force push protection', () => {
+ expect(findForcePushTitle().exists()).toBe(true);
+ });
+
+ it('renders a branch protection component for merge rules', () => {
+ expect(findBranchProtections().at(1).props()).toMatchObject({
+ header: sprintf(I18N.allowedToMergeHeader, { total: 2 }),
+ ...protectionMockProps,
+ });
+ });
+
+ it('renders a branch protection component for approvals', () => {
+ expect(findApprovalsTitle().exists()).toBe(true);
+
+ expect(findBranchProtections().at(2).props()).toMatchObject({
+ header: sprintf(I18N.approvalsHeader, { total: 0 }),
+ headerLinkHref: approvalRulesPath,
+ headerLinkTitle: I18N.manageApprovalsLinkTitle,
+ });
+ });
+});
diff --git a/spec/frontend/projects/settings/branch_rules/components/view/mock_data.js b/spec/frontend/projects/settings/branch_rules/components/view/mock_data.js
new file mode 100644
index 00000000000..c3f573061da
--- /dev/null
+++ b/spec/frontend/projects/settings/branch_rules/components/view/mock_data.js
@@ -0,0 +1,141 @@
+const usersMock = [
+ {
+ username: 'usr1',
+ webUrl: 'http://test.test/usr1',
+ name: 'User 1',
+ avatarUrl: 'http://test.test/avt1.png',
+ },
+ {
+ username: 'usr2',
+ webUrl: 'http://test.test/usr2',
+ name: 'User 2',
+ avatarUrl: 'http://test.test/avt2.png',
+ },
+ {
+ username: 'usr3',
+ webUrl: 'http://test.test/usr3',
+ name: 'User 3',
+ avatarUrl: 'http://test.test/avt3.png',
+ },
+ {
+ username: 'usr4',
+ webUrl: 'http://test.test/usr4',
+ name: 'User 4',
+ avatarUrl: 'http://test.test/avt4.png',
+ },
+ {
+ username: 'usr5',
+ webUrl: 'http://test.test/usr5',
+ name: 'User 5',
+ avatarUrl: 'http://test.test/avt5.png',
+ },
+];
+
+const accessLevelsMock = [
+ { accessLevelDescription: 'Administrator' },
+ { accessLevelDescription: 'Maintainer' },
+];
+
+const approvalsRequired = 3;
+
+const groupsMock = [{ name: 'test_group_1' }, { name: 'test_group_2' }];
+
+export const protectionPropsMock = {
+ header: 'Test protection',
+ headerLinkTitle: 'Test link title',
+ headerLinkHref: 'Test link href',
+ roles: accessLevelsMock,
+ users: usersMock,
+ groups: groupsMock,
+ approvals: [
+ {
+ name: 'test',
+ eligibleApprovers: { nodes: usersMock },
+ approvalsRequired,
+ },
+ ],
+};
+
+export const protectionRowPropsMock = {
+ title: 'Test title',
+ users: usersMock,
+ accessLevels: accessLevelsMock,
+ approvalsRequired,
+};
+
+export const accessLevelsMockResponse = [
+ {
+ __typename: 'PushAccessLevelEdge',
+ node: {
+ __typename: 'PushAccessLevel',
+ accessLevel: 40,
+ accessLevelDescription: 'Jona Langworth',
+ group: null,
+ user: {
+ __typename: 'UserCore',
+ id: '123',
+ webUrl: 'test.com',
+ name: 'peter',
+ avatarUrl: 'test.com/user.png',
+ },
+ },
+ },
+ {
+ __typename: 'PushAccessLevelEdge',
+ node: {
+ __typename: 'PushAccessLevel',
+ accessLevel: 40,
+ accessLevelDescription: 'Maintainers',
+ group: null,
+ user: null,
+ },
+ },
+];
+
+export const branchProtectionsMockResponse = {
+ data: {
+ project: {
+ id: 'gid://gitlab/Project/6',
+ __typename: 'Project',
+ branchRules: {
+ __typename: 'BranchRuleConnection',
+ nodes: [
+ {
+ __typename: 'BranchRule',
+ name: 'main',
+ branchProtection: {
+ __typename: 'BranchProtection',
+ allowForcePush: true,
+ codeOwnerApprovalRequired: true,
+ mergeAccessLevels: {
+ __typename: 'MergeAccessLevelConnection',
+ edges: accessLevelsMockResponse,
+ },
+ pushAccessLevels: {
+ __typename: 'PushAccessLevelConnection',
+ edges: accessLevelsMockResponse,
+ },
+ },
+ },
+ {
+ __typename: 'BranchRule',
+ name: '*',
+ branchProtection: {
+ __typename: 'BranchProtection',
+ allowForcePush: true,
+ codeOwnerApprovalRequired: true,
+ mergeAccessLevels: {
+ __typename: 'MergeAccessLevelConnection',
+ edges: [],
+ },
+ pushAccessLevels: {
+ __typename: 'PushAccessLevelConnection',
+ edges: [],
+ },
+ },
+ },
+ ],
+ },
+ },
+ },
+};
diff --git a/spec/frontend/projects/settings/branch_rules/components/view/protection_row_spec.js b/spec/frontend/projects/settings/branch_rules/components/view/protection_row_spec.js
new file mode 100644
index 00000000000..b0a69bedd3e
--- /dev/null
+++ b/spec/frontend/projects/settings/branch_rules/components/view/protection_row_spec.js
@@ -0,0 +1,71 @@
+import { GlAvatarsInline, GlAvatar, GlAvatarLink } from '@gitlab/ui';
+import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
+import ProtectionRow, {
+ MAX_VISIBLE_AVATARS,
+ AVATAR_SIZE,
+} from '~/projects/settings/branch_rules/components/view/protection_row.vue';
+import { protectionRowPropsMock } from './mock_data';
+
+describe('Branch rule protection row', () => {
+ let wrapper;
+
+ const createComponent = () => {
+ wrapper = shallowMountExtended(ProtectionRow, {
+ propsData: protectionRowPropsMock,
+ stubs: { GlAvatarsInline },
+ });
+ };
+
+ beforeEach(() => createComponent());
+
+ afterEach(() => wrapper.destroy());
+
+ const findTitle = () => wrapper.findByText(protectionRowPropsMock.title);
+ const findAvatarsInline = () => wrapper.findComponent(GlAvatarsInline);
+ const findAvatarLinks = () => wrapper.findAllComponents(GlAvatarLink);
+ const findAvatars = () => wrapper.findAllComponents(GlAvatar);
+ const findAccessLevels = () => wrapper.findAllByTestId('access-level');
+ const findApprovalsRequired = () =>
+ wrapper.findByText(`${protectionRowPropsMock.approvalsRequired} approvals required`);
+
+ it('renders a title', () => {
+ expect(findTitle().exists()).toBe(true);
+ });
+
+ it('renders an avatars-inline component', () => {
+ expect(findAvatarsInline().props('avatars')).toMatchObject(protectionRowPropsMock.users);
+ expect(findAvatarsInline().props('badgeSrOnlyText')).toBe('1 additional user');
+ });
+
+ it('renders avatar-link components', () => {
+ expect(findAvatarLinks().length).toBe(MAX_VISIBLE_AVATARS);
+
+ expect(findAvatarLinks().at(1).attributes('href')).toBe(protectionRowPropsMock.users[1].webUrl);
+ expect(findAvatarLinks().at(1).attributes('title')).toBe(protectionRowPropsMock.users[1].name);
+ });
+
+ it('renders avatar components', () => {
+ expect(findAvatars().length).toBe(MAX_VISIBLE_AVATARS);
+
+ expect(findAvatars().at(1).attributes('src')).toBe(protectionRowPropsMock.users[1].avatarUrl);
+ expect(findAvatars().at(1).attributes('label')).toBe(protectionRowPropsMock.users[1].name);
+ expect(findAvatars().at(1).props('size')).toBe(AVATAR_SIZE);
+ });
+
+ it('renders access level descriptions', () => {
+ expect(findAccessLevels().length).toBe(protectionRowPropsMock.accessLevels.length);
+
+ expect(findAccessLevels().at(0).text()).toBe(
+ protectionRowPropsMock.accessLevels[0].accessLevelDescription,
+ );
+ expect(findAccessLevels().at(1).text()).toContain(',');
+
+ expect(findAccessLevels().at(1).text()).toContain(
+ protectionRowPropsMock.accessLevels[1].accessLevelDescription,
+ );
+ });
+
+ it('renders the number of approvals required', () => {
+ expect(findApprovalsRequired().exists()).toBe(true);
+ });
+});
diff --git a/spec/frontend/projects/settings/branch_rules/components/view/protection_spec.js b/spec/frontend/projects/settings/branch_rules/components/view/protection_spec.js
new file mode 100644
index 00000000000..e2fbb4f5bbb
--- /dev/null
+++ b/spec/frontend/projects/settings/branch_rules/components/view/protection_spec.js
@@ -0,0 +1,68 @@
+import { GlCard, GlLink } from '@gitlab/ui';
+import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
+import Protection, { i18n } from '~/projects/settings/branch_rules/components/view/protection.vue';
+import ProtectionRow from '~/projects/settings/branch_rules/components/view/protection_row.vue';
+import { protectionPropsMock } from './mock_data';
+
+describe('Branch rule protection', () => {
+ let wrapper;
+
+ const createComponent = () => {
+ wrapper = shallowMountExtended(Protection, {
+ propsData: protectionPropsMock,
+ stubs: { GlCard },
+ });
+ };
+
+ beforeEach(() => createComponent());
+
+ afterEach(() => wrapper.destroy());
+
+ const findCard = () => wrapper.findComponent(GlCard);
+ const findHeader = () => wrapper.findByText(protectionPropsMock.header);
+ const findLink = () => wrapper.findComponent(GlLink);
+ const findProtectionRows = () => wrapper.findAllComponents(ProtectionRow);
+
+ it('renders a card component', () => {
+ expect(findCard().exists()).toBe(true);
+ });
+
+ it('renders a header with a link', () => {
+ expect(findHeader().exists()).toBe(true);
+ expect(findLink().text()).toBe(protectionPropsMock.headerLinkTitle);
+ expect(findLink().attributes('href')).toBe(protectionPropsMock.headerLinkHref);
+ });
+
+ it('renders a protection row for roles', () => {
+ expect(findProtectionRows().at(0).props()).toMatchObject({
+ accessLevels: protectionPropsMock.roles,
+ showDivider: false,
+ title: i18n.rolesTitle,
+ });
+ });
+
+ it('renders a protection row for users', () => {
+ expect(findProtectionRows().at(1).props()).toMatchObject({
+ users: protectionPropsMock.users,
+ showDivider: true,
+ title: i18n.usersTitle,
+ });
+ });
+
+ it('renders a protection row for groups', () => {
+ expect(findProtectionRows().at(2).props()).toMatchObject({
+ accessLevels: protectionPropsMock.groups,
+ showDivider: true,
+ title: i18n.groupsTitle,
+ });
+ });
+
+ it('renders a protection row for approvals', () => {
+ const approval = protectionPropsMock.approvals[0];
+ expect(findProtectionRows().at(3).props()).toMatchObject({
+ title: approval.name,
+ users: approval.eligibleApprovers.nodes,
+ approvalsRequired: approval.approvalsRequired,
+ });
+ });
+});
diff --git a/spec/frontend/projects/settings/components/default_branch_selector_spec.js b/spec/frontend/projects/settings/components/default_branch_selector_spec.js
new file mode 100644
index 00000000000..94648d87524
--- /dev/null
+++ b/spec/frontend/projects/settings/components/default_branch_selector_spec.js
@@ -0,0 +1,46 @@
+import { shallowMount } from '@vue/test-utils';
+import DefaultBranchSelector from '~/projects/settings/components/default_branch_selector.vue';
+import RefSelector from '~/ref/components/ref_selector.vue';
+import { REF_TYPE_BRANCHES } from '~/ref/constants';
+
+describe('projects/settings/components/default_branch_selector', () => {
+ const persistedDefaultBranch = 'main';
+ const projectId = '123';
+ let wrapper;
+
+ const findRefSelector = () => wrapper.findComponent(RefSelector);
+
+ const buildWrapper = () => {
+ wrapper = shallowMount(DefaultBranchSelector, {
+ propsData: {
+ persistedDefaultBranch,
+ projectId,
+ },
+ });
+ };
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ beforeEach(() => {
+ buildWrapper();
+ });
+
+ it('displays a RefSelector component', () => {
+ expect(findRefSelector().props()).toEqual({
+ value: persistedDefaultBranch,
+ enabledRefTypes: [REF_TYPE_BRANCHES],
+ projectId,
+ state: true,
+ translations: {
+ dropdownHeader: expect.any(String),
+ searchPlaceholder: expect.any(String),
+ },
+ useSymbolicRefNames: false,
+ name: 'project[default_branch]',
+ });
+
+ expect(findRefSelector().classes()).toContain('gl-w-full');
+ });
+});
diff --git a/spec/frontend/projects/settings/components/transfer_project_form_spec.js b/spec/frontend/projects/settings/components/transfer_project_form_spec.js
index bde7148078d..6e639f895a8 100644
--- a/spec/frontend/projects/settings/components/transfer_project_form_spec.js
+++ b/spec/frontend/projects/settings/components/transfer_project_form_spec.js
@@ -1,41 +1,65 @@
import Vue, { nextTick } from 'vue';
+import { GlAlert } from '@gitlab/ui';
import VueApollo from 'vue-apollo';
-import searchNamespacesWhereUserCanTransferProjectsQueryResponsePage1 from 'test_fixtures/graphql/projects/settings/search_namespaces_where_user_can_transfer_projects_page_1.query.graphql.json';
-import searchNamespacesWhereUserCanTransferProjectsQueryResponsePage2 from 'test_fixtures/graphql/projects/settings/search_namespaces_where_user_can_transfer_projects_page_2.query.graphql.json';
-import {
- groupNamespaces,
- userNamespaces,
-} from 'jest/vue_shared/components/namespace_select/mock_data';
+import currentUserNamespaceQueryResponse from 'test_fixtures/graphql/projects/settings/current_user_namespace.query.graphql.json';
+import transferLocationsResponsePage1 from 'test_fixtures/api/projects/transfer_locations_page_1.json';
+import transferLocationsResponsePage2 from 'test_fixtures/api/projects/transfer_locations_page_2.json';
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
import createMockApollo from 'helpers/mock_apollo_helper';
import { getIdFromGraphQLId } from '~/graphql_shared/utils';
import TransferProjectForm from '~/projects/settings/components/transfer_project_form.vue';
-import NamespaceSelect from '~/vue_shared/components/namespace_select/namespace_select.vue';
+import NamespaceSelect from '~/vue_shared/components/namespace_select/namespace_select_deprecated.vue';
import ConfirmDanger from '~/vue_shared/components/confirm_danger/confirm_danger.vue';
-import searchNamespacesWhereUserCanTransferProjectsQuery from '~/projects/settings/graphql/queries/search_namespaces_where_user_can_transfer_projects.query.graphql';
+import currentUserNamespaceQuery from '~/projects/settings/graphql/queries/current_user_namespace.query.graphql';
+import { getTransferLocations } from '~/api/projects_api';
import waitForPromises from 'helpers/wait_for_promises';
+jest.mock('~/api/projects_api', () => ({
+ getTransferLocations: jest.fn(),
+}));
+
describe('Transfer project form', () => {
let wrapper;
+ const projectId = '1';
const confirmButtonText = 'Confirm';
const confirmationPhrase = 'You must construct additional pylons!';
- const runDebounce = () => jest.runAllTimers();
-
Vue.use(VueApollo);
- const defaultQueryHandler = jest
- .fn()
- .mockResolvedValue(searchNamespacesWhereUserCanTransferProjectsQueryResponsePage1);
+ const defaultQueryHandler = jest.fn().mockResolvedValue(currentUserNamespaceQueryResponse);
+ const mockResolvedGetTransferLocations = ({
+ data = transferLocationsResponsePage1,
+ page = '1',
+ nextPage = '2',
+ prevPage = null,
+ } = {}) => {
+ getTransferLocations.mockResolvedValueOnce({
+ data,
+ headers: {
+ 'x-per-page': '2',
+ 'x-page': page,
+ 'x-total': '4',
+ 'x-total-pages': '2',
+ 'x-next-page': nextPage,
+ 'x-prev-page': prevPage,
+ },
+ });
+ };
+ const mockRejectedGetTransferLocations = () => {
+ const error = new Error();
+
+ getTransferLocations.mockRejectedValueOnce(error);
+ };
const createComponent = ({
- requestHandlers = [[searchNamespacesWhereUserCanTransferProjectsQuery, defaultQueryHandler]],
+ requestHandlers = [[currentUserNamespaceQuery, defaultQueryHandler]],
} = {}) => {
wrapper = shallowMountExtended(TransferProjectForm, {
+ provide: {
+ projectId,
+ },
propsData: {
- userNamespaces,
- groupNamespaces,
confirmButtonText,
confirmationPhrase,
},
@@ -44,7 +68,12 @@ describe('Transfer project form', () => {
};
const findNamespaceSelect = () => wrapper.findComponent(NamespaceSelect);
+ const showNamespaceSelect = async () => {
+ findNamespaceSelect().vm.$emit('show');
+ await waitForPromises();
+ };
const findConfirmDanger = () => wrapper.findComponent(ConfirmDanger);
+ const findAlert = () => wrapper.findComponent(GlAlert);
afterEach(() => {
wrapper.destroy();
@@ -69,66 +98,113 @@ describe('Transfer project form', () => {
});
describe('with a selected namespace', () => {
- const [selectedItem] = groupNamespaces;
+ const [selectedItem] = transferLocationsResponsePage1;
- beforeEach(() => {
+ const arrange = async () => {
+ mockResolvedGetTransferLocations();
createComponent();
-
+ await showNamespaceSelect();
findNamespaceSelect().vm.$emit('select', selectedItem);
- });
+ };
+
+ it('emits the `selectNamespace` event when a namespace is selected', async () => {
+ await arrange();
- it('emits the `selectNamespace` event when a namespace is selected', () => {
const args = [selectedItem.id];
expect(wrapper.emitted('selectNamespace')).toEqual([args]);
});
- it('enables the confirm button', () => {
+ it('enables the confirm button', async () => {
+ await arrange();
+
expect(findConfirmDanger().attributes('disabled')).toBeUndefined();
});
- it('clicking the confirm button emits the `confirm` event', () => {
+ it('clicking the confirm button emits the `confirm` event', async () => {
+ await arrange();
+
findConfirmDanger().vm.$emit('confirm');
expect(wrapper.emitted('confirm')).toBeDefined();
});
});
- it('passes correct props to `NamespaceSelect` component', async () => {
- createComponent();
+ describe('when `NamespaceSelect` is opened', () => {
+ it('fetches user and group namespaces and passes correct props to `NamespaceSelect` component', async () => {
+ mockResolvedGetTransferLocations();
+ createComponent();
+ await showNamespaceSelect();
+
+ const { namespace } = currentUserNamespaceQueryResponse.data.currentUser;
+
+ expect(findNamespaceSelect().props()).toMatchObject({
+ userNamespaces: [
+ {
+ id: getIdFromGraphQLId(namespace.id),
+ humanName: namespace.fullName,
+ },
+ ],
+ groupNamespaces: transferLocationsResponsePage1.map(({ id, full_name: humanName }) => ({
+ id,
+ humanName,
+ })),
+ hasNextPageOfGroups: true,
+ isLoading: false,
+ isSearchLoading: false,
+ shouldFilterNamespaces: false,
+ });
+ });
- runDebounce();
- await waitForPromises();
+ describe('when namespaces have already been fetched', () => {
+ beforeEach(async () => {
+ mockResolvedGetTransferLocations();
+ createComponent();
+ await showNamespaceSelect();
+ });
+
+ it('does not fetch namespaces', async () => {
+ getTransferLocations.mockClear();
+ defaultQueryHandler.mockClear();
+
+ await showNamespaceSelect();
- const {
- namespace,
- groups,
- } = searchNamespacesWhereUserCanTransferProjectsQueryResponsePage1.data.currentUser;
-
- expect(findNamespaceSelect().props()).toMatchObject({
- userNamespaces: [
- {
- id: getIdFromGraphQLId(namespace.id),
- humanName: namespace.fullName,
- },
- ],
- groupNamespaces: groups.nodes.map((node) => ({
- id: getIdFromGraphQLId(node.id),
- humanName: node.fullName,
- })),
- hasNextPageOfGroups: true,
- isLoadingMoreGroups: false,
- isSearchLoading: false,
- shouldFilterNamespaces: false,
+ expect(getTransferLocations).not.toHaveBeenCalled();
+ expect(defaultQueryHandler).not.toHaveBeenCalled();
+ });
+ });
+
+ describe('when `getTransferLocations` API call fails', () => {
+ it('displays error alert', async () => {
+ mockRejectedGetTransferLocations();
+ createComponent();
+ await showNamespaceSelect();
+
+ expect(findAlert().exists()).toBe(true);
+ });
+ });
+
+ describe('when `currentUser` GraphQL query fails', () => {
+ it('displays error alert', async () => {
+ mockResolvedGetTransferLocations();
+ const error = new Error();
+ createComponent({
+ requestHandlers: [[currentUserNamespaceQuery, jest.fn().mockRejectedValueOnce(error)]],
+ });
+ await showNamespaceSelect();
+
+ expect(findAlert().exists()).toBe(true);
+ });
});
});
describe('when `search` event is fired', () => {
const arrange = async () => {
+ mockResolvedGetTransferLocations();
createComponent();
-
+ await showNamespaceSelect();
+ mockResolvedGetTransferLocations();
findNamespaceSelect().vm.$emit('search', 'foo');
-
await nextTick();
};
@@ -138,87 +214,106 @@ describe('Transfer project form', () => {
expect(findNamespaceSelect().props('isSearchLoading')).toBe(true);
});
- it('passes `search` variable to query', async () => {
+ it('passes `search` param to API call', async () => {
await arrange();
- runDebounce();
await waitForPromises();
- expect(defaultQueryHandler).toHaveBeenCalledWith(expect.objectContaining({ search: 'foo' }));
+ expect(getTransferLocations).toHaveBeenCalledWith(
+ projectId,
+ expect.objectContaining({ search: 'foo' }),
+ );
+ });
+
+ describe('when `getTransferLocations` API call fails', () => {
+ it('displays dismissible error alert', async () => {
+ mockResolvedGetTransferLocations();
+ createComponent();
+ await showNamespaceSelect();
+ mockRejectedGetTransferLocations();
+ findNamespaceSelect().vm.$emit('search', 'foo');
+ await waitForPromises();
+
+ const alert = findAlert();
+
+ expect(alert.exists()).toBe(true);
+
+ alert.vm.$emit('dismiss');
+ await nextTick();
+
+ expect(alert.exists()).toBe(false);
+ });
});
});
describe('when `load-more-groups` event is fired', () => {
- let queryHandler;
-
const arrange = async () => {
- queryHandler = jest.fn();
- queryHandler.mockResolvedValueOnce(
- searchNamespacesWhereUserCanTransferProjectsQueryResponsePage1,
- );
- queryHandler.mockResolvedValueOnce(
- searchNamespacesWhereUserCanTransferProjectsQueryResponsePage2,
- );
+ mockResolvedGetTransferLocations();
+ createComponent();
+ await showNamespaceSelect();
- createComponent({
- requestHandlers: [[searchNamespacesWhereUserCanTransferProjectsQuery, queryHandler]],
+ mockResolvedGetTransferLocations({
+ data: transferLocationsResponsePage2,
+ page: '2',
+ nextPage: null,
+ prevPage: '1',
});
- runDebounce();
- await waitForPromises();
-
findNamespaceSelect().vm.$emit('load-more-groups');
await nextTick();
};
- it('sets `isLoadingMoreGroups` prop to `true`', async () => {
+ it('sets `isLoading` prop to `true`', async () => {
await arrange();
- expect(findNamespaceSelect().props('isLoadingMoreGroups')).toBe(true);
+ expect(findNamespaceSelect().props('isLoading')).toBe(true);
});
- it('passes `after` and `first` variables to query', async () => {
+ it('passes `page` param to API call', async () => {
await arrange();
- runDebounce();
await waitForPromises();
- expect(queryHandler).toHaveBeenCalledWith(
- expect.objectContaining({
- first: 25,
- after:
- searchNamespacesWhereUserCanTransferProjectsQueryResponsePage1.data.currentUser.groups
- .pageInfo.endCursor,
- }),
+ expect(getTransferLocations).toHaveBeenCalledWith(
+ projectId,
+ expect.objectContaining({ page: 2 }),
);
});
it('updates `groupNamespaces` prop with new groups', async () => {
await arrange();
- runDebounce();
await waitForPromises();
- expect(findNamespaceSelect().props('groupNamespaces')).toEqual(
- [
- ...searchNamespacesWhereUserCanTransferProjectsQueryResponsePage1.data.currentUser.groups
- .nodes,
- ...searchNamespacesWhereUserCanTransferProjectsQueryResponsePage2.data.currentUser.groups
- .nodes,
- ].map((node) => ({
- id: getIdFromGraphQLId(node.id),
- humanName: node.fullName,
- })),
+ expect(findNamespaceSelect().props('groupNamespaces')).toMatchObject(
+ [...transferLocationsResponsePage1, ...transferLocationsResponsePage2].map(
+ ({ id, full_name: humanName }) => ({
+ id,
+ humanName,
+ }),
+ ),
);
});
it('updates `hasNextPageOfGroups` prop', async () => {
await arrange();
- runDebounce();
await waitForPromises();
expect(findNamespaceSelect().props('hasNextPageOfGroups')).toBe(false);
});
+
+ describe('when `getTransferLocations` API call fails', () => {
+ it('displays error alert', async () => {
+ mockResolvedGetTransferLocations();
+ createComponent();
+ await showNamespaceSelect();
+ mockRejectedGetTransferLocations();
+ findNamespaceSelect().vm.$emit('load-more-groups');
+ await waitForPromises();
+
+ expect(findAlert().exists()).toBe(true);
+ });
+ });
});
});
diff --git a/spec/frontend/projects/settings/repository/branch_rules/app_spec.js b/spec/frontend/projects/settings/repository/branch_rules/app_spec.js
index e920cd48163..4603436c40a 100644
--- a/spec/frontend/projects/settings/repository/branch_rules/app_spec.js
+++ b/spec/frontend/projects/settings/repository/branch_rules/app_spec.js
@@ -6,8 +6,8 @@ import { mountExtended } from 'helpers/vue_test_utils_helper';
import BranchRules, { i18n } from '~/projects/settings/repository/branch_rules/app.vue';
import BranchRule from '~/projects/settings/repository/branch_rules/components/branch_rule.vue';
import branchRulesQuery from '~/projects/settings/repository/branch_rules/graphql/queries/branch_rules.query.graphql';
-import createFlash from '~/flash';
-import { branchRulesMockResponse, propsDataMock } from './mock_data';
+import { createAlert } from '~/flash';
+import { branchRulesMockResponse, appProvideMock } from './mock_data';
jest.mock('~/flash');
@@ -24,9 +24,7 @@ describe('Branch rules app', () => {
wrapper = mountExtended(BranchRules, {
apolloProvider: fakeApollo,
- propsData: {
- ...propsDataMock,
- },
+ provide: appProvideMock,
});
await waitForPromises();
@@ -39,7 +37,7 @@ describe('Branch rules app', () => {
it('displays an error if branch rules query fails', async () => {
await createComponent({ queryHandler: jest.fn().mockRejectedValue() });
- expect(createFlash).toHaveBeenCalledWith({ message: i18n.queryError });
+ expect(createAlert).toHaveBeenCalledWith({ message: i18n.queryError });
});
it('displays an empty state if no branch rules are present', async () => {
@@ -49,7 +47,11 @@ describe('Branch rules app', () => {
it('renders branch rules', () => {
const { nodes } = branchRulesMockResponse.data.project.branchRules;
- expect(findAllBranchRules().at(0).text()).toBe(nodes[0].name);
- expect(findAllBranchRules().at(1).text()).toBe(nodes[1].name);
+
+ expect(findAllBranchRules().length).toBe(nodes.length);
+
+ expect(findAllBranchRules().at(0).props('name')).toBe(nodes[0].name);
+
+ expect(findAllBranchRules().at(1).props('name')).toBe(nodes[1].name);
});
});
diff --git a/spec/frontend/projects/settings/repository/branch_rules/components/branch_rule_spec.js b/spec/frontend/projects/settings/repository/branch_rules/components/branch_rule_spec.js
index 924dab60704..2bc705f538b 100644
--- a/spec/frontend/projects/settings/repository/branch_rules/components/branch_rule_spec.js
+++ b/spec/frontend/projects/settings/repository/branch_rules/components/branch_rule_spec.js
@@ -2,26 +2,24 @@ import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
import BranchRule, {
i18n,
} from '~/projects/settings/repository/branch_rules/components/branch_rule.vue';
-
-const defaultProps = {
- name: 'main',
- isDefault: true,
- isProtected: true,
- approvalDetails: ['requires approval from TEST', '2 status checks'],
-};
+import { branchRuleProvideMock, branchRulePropsMock } from '../mock_data';
describe('Branch rule', () => {
let wrapper;
const createComponent = (props = {}) => {
- wrapper = shallowMountExtended(BranchRule, { propsData: { ...defaultProps, ...props } });
+ wrapper = shallowMountExtended(BranchRule, {
+ provide: branchRuleProvideMock,
+ propsData: { ...branchRulePropsMock, ...props },
+ });
};
const findDefaultBadge = () => wrapper.findByText(i18n.defaultLabel);
const findProtectedBadge = () => wrapper.findByText(i18n.protectedLabel);
- const findBranchName = () => wrapper.findByText(defaultProps.name);
+ const findBranchName = () => wrapper.findByText(branchRulePropsMock.name);
const findProtectionDetailsList = () => wrapper.findByRole('list');
const findProtectionDetailsListItems = () => wrapper.findAllByRole('listitem');
+ const findDetailsButton = () => wrapper.findByText(i18n.detailsButtonLabel);
beforeEach(() => createComponent());
@@ -52,7 +50,17 @@ describe('Branch rule', () => {
});
it('renders the protection details list items', () => {
- expect(findProtectionDetailsListItems().at(0).text()).toBe(defaultProps.approvalDetails[0]);
- expect(findProtectionDetailsListItems().at(1).text()).toBe(defaultProps.approvalDetails[1]);
+ expect(findProtectionDetailsListItems().at(0).text()).toBe(
+ branchRulePropsMock.approvalDetails[0],
+ );
+ expect(findProtectionDetailsListItems().at(1).text()).toBe(
+ branchRulePropsMock.approvalDetails[1],
+ );
+ });
+
+ it('renders a detail button with the correct href', () => {
+ expect(findDetailsButton().attributes('href')).toBe(
+ `${branchRuleProvideMock.branchRulesPath}?branch=${branchRulePropsMock.name}`,
+ );
});
});
diff --git a/spec/frontend/projects/settings/repository/branch_rules/mock_data.js b/spec/frontend/projects/settings/repository/branch_rules/mock_data.js
index 14ed35f047d..bac82992c4d 100644
--- a/spec/frontend/projects/settings/repository/branch_rules/mock_data.js
+++ b/spec/frontend/projects/settings/repository/branch_rules/mock_data.js
@@ -20,6 +20,17 @@ export const branchRulesMockResponse = {
},
};
-export const propsDataMock = {
+export const appProvideMock = {
projectPath: 'some/project/path',
};
+
+export const branchRuleProvideMock = {
+ branchRulesPath: 'settings/repository/branch_rules',
+};
+
+export const branchRulePropsMock = {
+ name: 'main',
+ isDefault: true,
+ isProtected: true,
+ approvalDetails: ['requires approval from TEST', '2 status checks'],
+};