Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'spec/frontend/ref')
-rw-r--r--spec/frontend/ref/components/__snapshots__/ref_selector_spec.js.snap70
-rw-r--r--spec/frontend/ref/components/ref_selector_spec.js211
-rw-r--r--spec/frontend/ref/stores/actions_spec.js22
-rw-r--r--spec/frontend/ref/stores/mutations_spec.js11
4 files changed, 287 insertions, 27 deletions
diff --git a/spec/frontend/ref/components/__snapshots__/ref_selector_spec.js.snap b/spec/frontend/ref/components/__snapshots__/ref_selector_spec.js.snap
new file mode 100644
index 00000000000..5f05b7fc68b
--- /dev/null
+++ b/spec/frontend/ref/components/__snapshots__/ref_selector_spec.js.snap
@@ -0,0 +1,70 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`Ref selector component footer slot passes the expected slot props 1`] = `
+Object {
+ "isLoading": false,
+ "matches": Object {
+ "branches": Object {
+ "error": null,
+ "list": Array [
+ Object {
+ "default": false,
+ "name": "add_images_and_changes",
+ },
+ Object {
+ "default": false,
+ "name": "conflict-contains-conflict-markers",
+ },
+ Object {
+ "default": false,
+ "name": "deleted-image-test",
+ },
+ Object {
+ "default": false,
+ "name": "diff-files-image-to-symlink",
+ },
+ Object {
+ "default": false,
+ "name": "diff-files-symlink-to-image",
+ },
+ Object {
+ "default": false,
+ "name": "markdown",
+ },
+ Object {
+ "default": true,
+ "name": "master",
+ },
+ ],
+ "totalCount": 123,
+ },
+ "commits": Object {
+ "error": null,
+ "list": Array [
+ Object {
+ "name": "b83d6e39",
+ "subtitle": "Merge branch 'branch-merged' into 'master'",
+ "value": "b83d6e391c22777fca1ed3012fce84f633d7fed0",
+ },
+ ],
+ "totalCount": 1,
+ },
+ "tags": Object {
+ "error": null,
+ "list": Array [
+ Object {
+ "name": "v1.1.1",
+ },
+ Object {
+ "name": "v1.1.0",
+ },
+ Object {
+ "name": "v1.0.0",
+ },
+ ],
+ "totalCount": 456,
+ },
+ },
+ "query": "abcd1234",
+}
+`;
diff --git a/spec/frontend/ref/components/ref_selector_spec.js b/spec/frontend/ref/components/ref_selector_spec.js
index 27ada131ed6..a642a8cf8c2 100644
--- a/spec/frontend/ref/components/ref_selector_spec.js
+++ b/spec/frontend/ref/components/ref_selector_spec.js
@@ -1,13 +1,20 @@
-import { GlLoadingIcon, GlSearchBoxByType, GlDropdownItem, GlIcon } from '@gitlab/ui';
+import { GlLoadingIcon, GlSearchBoxByType, GlDropdownItem, GlDropdown, GlIcon } from '@gitlab/ui';
import { mount, createLocalVue } from '@vue/test-utils';
import axios from 'axios';
import MockAdapter from 'axios-mock-adapter';
+import { merge, last } from 'lodash';
import Vuex from 'vuex';
import { trimText } from 'helpers/text_helper';
import { ENTER_KEY } from '~/lib/utils/keys';
import { sprintf } from '~/locale';
import RefSelector from '~/ref/components/ref_selector.vue';
-import { X_TOTAL_HEADER, DEFAULT_I18N } from '~/ref/constants';
+import {
+ X_TOTAL_HEADER,
+ DEFAULT_I18N,
+ REF_TYPE_BRANCHES,
+ REF_TYPE_TAGS,
+ REF_TYPE_COMMITS,
+} from '~/ref/constants';
import createStore from '~/ref/stores/';
const localVue = createLocalVue();
@@ -26,27 +33,32 @@ describe('Ref selector component', () => {
let branchesApiCallSpy;
let tagsApiCallSpy;
let commitApiCallSpy;
-
- const createComponent = (props = {}, attrs = {}) => {
- wrapper = mount(RefSelector, {
- propsData: {
- projectId,
- value: '',
- ...props,
- },
- attrs,
- listeners: {
- // simulate a parent component v-model binding
- input: (selectedRef) => {
- wrapper.setProps({ value: selectedRef });
+ let requestSpies;
+
+ const createComponent = (mountOverrides = {}) => {
+ wrapper = mount(
+ RefSelector,
+ merge(
+ {
+ propsData: {
+ projectId,
+ value: '',
+ },
+ listeners: {
+ // simulate a parent component v-model binding
+ input: (selectedRef) => {
+ wrapper.setProps({ value: selectedRef });
+ },
+ },
+ stubs: {
+ GlSearchBoxByType: true,
+ },
+ localVue,
+ store: createStore(),
},
- },
- stubs: {
- GlSearchBoxByType: true,
- },
- localVue,
- store: createStore(),
- });
+ mountOverrides,
+ ),
+ );
};
beforeEach(() => {
@@ -58,6 +70,7 @@ describe('Ref selector component', () => {
.mockReturnValue([200, fixtures.branches, { [X_TOTAL_HEADER]: '123' }]);
tagsApiCallSpy = jest.fn().mockReturnValue([200, fixtures.tags, { [X_TOTAL_HEADER]: '456' }]);
commitApiCallSpy = jest.fn().mockReturnValue([200, fixtures.commit]);
+ requestSpies = { branchesApiCallSpy, tagsApiCallSpy, commitApiCallSpy };
mock
.onGet(`/api/v4/projects/${projectId}/repository/branches`)
@@ -78,7 +91,7 @@ describe('Ref selector component', () => {
//
// Finders
//
- const findButtonContent = () => wrapper.find('[data-testid="button-content"]');
+ const findButtonContent = () => wrapper.find('button');
const findNoResults = () => wrapper.find('[data-testid="no-results"]');
@@ -175,7 +188,7 @@ describe('Ref selector component', () => {
const id = 'git-ref';
beforeEach(() => {
- createComponent({}, { id });
+ createComponent({ attrs: { id } });
return waitForRequests();
});
@@ -189,7 +202,7 @@ describe('Ref selector component', () => {
const preselectedRef = fixtures.branches[0].name;
beforeEach(() => {
- createComponent({ value: preselectedRef });
+ createComponent({ propsData: { value: preselectedRef } });
return waitForRequests();
});
@@ -592,4 +605,152 @@ describe('Ref selector component', () => {
});
});
});
+
+ describe('with non-default ref types', () => {
+ it.each`
+ enabledRefTypes | reqsCalled | reqsNotCalled
+ ${[REF_TYPE_BRANCHES]} | ${['branchesApiCallSpy']} | ${['tagsApiCallSpy', 'commitApiCallSpy']}
+ ${[REF_TYPE_TAGS]} | ${['tagsApiCallSpy']} | ${['branchesApiCallSpy', 'commitApiCallSpy']}
+ ${[REF_TYPE_COMMITS]} | ${[]} | ${['branchesApiCallSpy', 'tagsApiCallSpy', 'commitApiCallSpy']}
+ ${[REF_TYPE_TAGS, REF_TYPE_COMMITS]} | ${['tagsApiCallSpy']} | ${['branchesApiCallSpy', 'commitApiCallSpy']}
+ `(
+ 'only calls $reqsCalled requests when $enabledRefTypes are enabled',
+ async ({ enabledRefTypes, reqsCalled, reqsNotCalled }) => {
+ createComponent({ propsData: { enabledRefTypes } });
+
+ await waitForRequests();
+
+ reqsCalled.forEach((req) => expect(requestSpies[req]).toHaveBeenCalledTimes(1));
+ reqsNotCalled.forEach((req) => expect(requestSpies[req]).not.toHaveBeenCalled());
+ },
+ );
+
+ it('only calls commitApiCallSpy when REF_TYPE_COMMITS is enabled', async () => {
+ createComponent({ propsData: { enabledRefTypes: [REF_TYPE_COMMITS] } });
+ updateQuery('abcd1234');
+
+ await waitForRequests();
+
+ expect(commitApiCallSpy).toHaveBeenCalledTimes(1);
+ expect(branchesApiCallSpy).not.toHaveBeenCalled();
+ expect(tagsApiCallSpy).not.toHaveBeenCalled();
+ });
+
+ it('triggers another search if enabled ref types change', async () => {
+ createComponent({ propsData: { enabledRefTypes: [REF_TYPE_BRANCHES] } });
+ await waitForRequests();
+
+ expect(branchesApiCallSpy).toHaveBeenCalledTimes(1);
+ expect(tagsApiCallSpy).not.toHaveBeenCalled();
+
+ wrapper.setProps({
+ enabledRefTypes: [REF_TYPE_BRANCHES, REF_TYPE_TAGS],
+ });
+ await waitForRequests();
+
+ expect(branchesApiCallSpy).toHaveBeenCalledTimes(2);
+ expect(tagsApiCallSpy).toHaveBeenCalledTimes(1);
+ });
+
+ it('if a ref type becomes disabled, its section is hidden, even if it had some results in store', async () => {
+ createComponent({ propsData: { enabledRefTypes: [REF_TYPE_BRANCHES, REF_TYPE_COMMITS] } });
+ updateQuery('abcd1234');
+ await waitForRequests();
+
+ expect(findBranchesSection().exists()).toBe(true);
+ expect(findCommitsSection().exists()).toBe(true);
+
+ wrapper.setProps({ enabledRefTypes: [REF_TYPE_COMMITS] });
+ await waitForRequests();
+
+ expect(findBranchesSection().exists()).toBe(false);
+ expect(findCommitsSection().exists()).toBe(true);
+ });
+
+ it.each`
+ enabledRefType | findVisibleSection | findHiddenSections
+ ${REF_TYPE_BRANCHES} | ${findBranchesSection} | ${[findTagsSection, findCommitsSection]}
+ ${REF_TYPE_TAGS} | ${findTagsSection} | ${[findBranchesSection, findCommitsSection]}
+ ${REF_TYPE_COMMITS} | ${findCommitsSection} | ${[findBranchesSection, findTagsSection]}
+ `(
+ 'hides section headers if a single ref type is enabled',
+ async ({ enabledRefType, findVisibleSection, findHiddenSections }) => {
+ createComponent({ propsData: { enabledRefTypes: [enabledRefType] } });
+ updateQuery('abcd1234');
+ await waitForRequests();
+
+ expect(findVisibleSection().exists()).toBe(true);
+ expect(findVisibleSection().find('[data-testid="section-header"]').exists()).toBe(false);
+ findHiddenSections.forEach((findHiddenSection) =>
+ expect(findHiddenSection().exists()).toBe(false),
+ );
+ },
+ );
+ });
+
+ describe('validation state', () => {
+ const invalidClass = 'gl-inset-border-1-red-500!';
+ const isInvalidClassApplied = () => wrapper.find(GlDropdown).props('toggleClass')[invalidClass];
+
+ describe('valid state', () => {
+ describe('when the state prop is not provided', () => {
+ it('does not render a red border', () => {
+ createComponent();
+
+ expect(isInvalidClassApplied()).toBe(false);
+ });
+ });
+
+ describe('when the state prop is true', () => {
+ it('does not render a red border', () => {
+ createComponent({ propsData: { state: true } });
+
+ expect(isInvalidClassApplied()).toBe(false);
+ });
+ });
+ });
+
+ describe('invalid state', () => {
+ it('renders the dropdown with a red border if the state prop is false', () => {
+ createComponent({ propsData: { state: false } });
+
+ expect(isInvalidClassApplied()).toBe(true);
+ });
+ });
+ });
+
+ describe('footer slot', () => {
+ const footerContent = 'This is the footer content';
+ const createFooter = jest.fn().mockImplementation(function createMockFooter() {
+ return this.$createElement('div', { attrs: { 'data-testid': 'footer-content' } }, [
+ footerContent,
+ ]);
+ });
+
+ beforeEach(() => {
+ createComponent({
+ scopedSlots: { footer: createFooter },
+ });
+
+ updateQuery('abcd1234');
+
+ return waitForRequests();
+ });
+
+ afterEach(() => {
+ createFooter.mockClear();
+ });
+
+ it('allows custom content to be shown at the bottom of the dropdown using the footer slot', () => {
+ expect(wrapper.find(`[data-testid="footer-content"]`).text()).toBe(footerContent);
+ });
+
+ it('passes the expected slot props', () => {
+ // The createFooter function gets called every time one of the scoped properties
+ // is updated. For the sake of this test, we'll just test the last call, which
+ // represents the final state of the slot props.
+ const lastCallProps = last(createFooter.mock.calls)[0];
+ expect(lastCallProps).toMatchSnapshot();
+ });
+ });
});
diff --git a/spec/frontend/ref/stores/actions_spec.js b/spec/frontend/ref/stores/actions_spec.js
index 11acec27165..099ce062a3a 100644
--- a/spec/frontend/ref/stores/actions_spec.js
+++ b/spec/frontend/ref/stores/actions_spec.js
@@ -1,4 +1,5 @@
import testAction from 'helpers/vuex_action_helper';
+import { ALL_REF_TYPES, REF_TYPE_BRANCHES, REF_TYPE_TAGS, REF_TYPE_COMMITS } from '~/ref/constants';
import * as actions from '~/ref/stores/actions';
import * as types from '~/ref/stores/mutation_types';
import createState from '~/ref/stores/state';
@@ -25,6 +26,14 @@ describe('Ref selector Vuex store actions', () => {
state = createState();
});
+ describe('setEnabledRefTypes', () => {
+ it(`commits ${types.SET_ENABLED_REF_TYPES} with the enabled ref types`, () => {
+ testAction(actions.setProjectId, ALL_REF_TYPES, state, [
+ { type: types.SET_PROJECT_ID, payload: ALL_REF_TYPES },
+ ]);
+ });
+ });
+
describe('setProjectId', () => {
it(`commits ${types.SET_PROJECT_ID} with the new project ID`, () => {
const projectId = '4';
@@ -46,12 +55,23 @@ describe('Ref selector Vuex store actions', () => {
describe('search', () => {
it(`commits ${types.SET_QUERY} with the new search query`, () => {
const query = 'hello';
+ testAction(actions.search, query, state, [{ type: types.SET_QUERY, payload: query }]);
+ });
+
+ it.each`
+ enabledRefTypes | expectedActions
+ ${[REF_TYPE_BRANCHES]} | ${['searchBranches']}
+ ${[REF_TYPE_COMMITS]} | ${['searchCommits']}
+ ${[REF_TYPE_BRANCHES, REF_TYPE_TAGS, REF_TYPE_COMMITS]} | ${['searchBranches', 'searchTags', 'searchCommits']}
+ `(`dispatches fetch actions for enabled ref types`, ({ enabledRefTypes, expectedActions }) => {
+ const query = 'hello';
+ state.enabledRefTypes = enabledRefTypes;
testAction(
actions.search,
query,
state,
[{ type: types.SET_QUERY, payload: query }],
- [{ type: 'searchBranches' }, { type: 'searchTags' }, { type: 'searchCommits' }],
+ expectedActions.map((type) => ({ type })),
);
});
});
diff --git a/spec/frontend/ref/stores/mutations_spec.js b/spec/frontend/ref/stores/mutations_spec.js
index cda13089766..11d4fe0e206 100644
--- a/spec/frontend/ref/stores/mutations_spec.js
+++ b/spec/frontend/ref/stores/mutations_spec.js
@@ -1,4 +1,4 @@
-import { X_TOTAL_HEADER } from '~/ref/constants';
+import { X_TOTAL_HEADER, ALL_REF_TYPES } from '~/ref/constants';
import * as types from '~/ref/stores/mutation_types';
import mutations from '~/ref/stores/mutations';
import createState from '~/ref/stores/state';
@@ -13,6 +13,7 @@ describe('Ref selector Vuex store mutations', () => {
describe('initial state', () => {
it('is created with the correct structure and initial values', () => {
expect(state).toEqual({
+ enabledRefTypes: [],
projectId: null,
query: '',
@@ -39,6 +40,14 @@ describe('Ref selector Vuex store mutations', () => {
});
});
+ describe(`${types.SET_ENABLED_REF_TYPES}`, () => {
+ it('sets the enabled ref types', () => {
+ mutations[types.SET_ENABLED_REF_TYPES](state, ALL_REF_TYPES);
+
+ expect(state.enabledRefTypes).toBe(ALL_REF_TYPES);
+ });
+ });
+
describe(`${types.SET_PROJECT_ID}`, () => {
it('updates the project ID', () => {
const newProjectId = '4';