summaryrefslogtreecommitdiff
path: root/spec/frontend
diff options
context:
space:
mode:
Diffstat (limited to 'spec/frontend')
-rw-r--r--spec/frontend/design_management/mock_data/apollo_mock.js23
-rw-r--r--spec/frontend/design_management/pages/index_spec.js4
-rw-r--r--spec/frontend/fixtures/issues.rb2
-rw-r--r--spec/frontend/helpers/mock_apollo_helper.js4
-rw-r--r--spec/frontend/issue_show/components/header_actions_spec.js218
-rw-r--r--spec/frontend/issue_show/issue_spec.js2
-rw-r--r--spec/frontend/pipeline_editor/components/text_editor_spec.js6
-rw-r--r--spec/frontend/pipeline_editor/pipeline_editor_app_spec.js81
-rw-r--r--spec/frontend/snippets/components/edit_spec.js85
-rw-r--r--spec/frontend/snippets/components/snippet_visibility_edit_spec.js25
10 files changed, 380 insertions, 70 deletions
diff --git a/spec/frontend/design_management/mock_data/apollo_mock.js b/spec/frontend/design_management/mock_data/apollo_mock.js
index 5e41210221b..e53ad2e6afe 100644
--- a/spec/frontend/design_management/mock_data/apollo_mock.js
+++ b/spec/frontend/design_management/mock_data/apollo_mock.js
@@ -1,13 +1,18 @@
export const designListQueryResponse = {
data: {
project: {
+ __typename: 'Project',
id: '1',
issue: {
+ __typename: 'Issue',
designCollection: {
+ __typename: 'DesignCollection',
copyState: 'READY',
designs: {
+ __typename: 'DesignConnection',
nodes: [
{
+ __typename: 'Design',
id: '1',
event: 'NONE',
filename: 'fox_1.jpg',
@@ -15,10 +20,12 @@ export const designListQueryResponse = {
image: 'image-1',
imageV432x230: 'image-1',
currentUserTodos: {
+ __typename: 'ToDo',
nodes: [],
},
},
{
+ __typename: 'Design',
id: '2',
event: 'NONE',
filename: 'fox_2.jpg',
@@ -26,10 +33,12 @@ export const designListQueryResponse = {
image: 'image-2',
imageV432x230: 'image-2',
currentUserTodos: {
+ __typename: 'ToDo',
nodes: [],
},
},
{
+ __typename: 'Design',
id: '3',
event: 'NONE',
filename: 'fox_3.jpg',
@@ -37,12 +46,14 @@ export const designListQueryResponse = {
image: 'image-3',
imageV432x230: 'image-3',
currentUserTodos: {
+ __typename: 'ToDo',
nodes: [],
},
},
],
},
versions: {
+ __typename: 'DesignVersion',
nodes: [],
},
},
@@ -82,9 +93,11 @@ export const designUploadMutationUpdatedResponse = {
export const permissionsQueryResponse = {
data: {
project: {
+ __typename: 'Project',
id: '1',
issue: {
- userPermissions: { createDesign: true },
+ __typename: 'Issue',
+ userPermissions: { __typename: 'UserPermissions', createDesign: true },
},
},
},
@@ -92,6 +105,7 @@ export const permissionsQueryResponse = {
export const reorderedDesigns = [
{
+ __typename: 'Design',
id: '2',
event: 'NONE',
filename: 'fox_2.jpg',
@@ -99,10 +113,12 @@ export const reorderedDesigns = [
image: 'image-2',
imageV432x230: 'image-2',
currentUserTodos: {
+ __typename: 'ToDo',
nodes: [],
},
},
{
+ __typename: 'Design',
id: '1',
event: 'NONE',
filename: 'fox_1.jpg',
@@ -110,10 +126,12 @@ export const reorderedDesigns = [
image: 'image-1',
imageV432x230: 'image-1',
currentUserTodos: {
+ __typename: 'ToDo',
nodes: [],
},
},
{
+ __typename: 'Design',
id: '3',
event: 'NONE',
filename: 'fox_3.jpg',
@@ -121,6 +139,7 @@ export const reorderedDesigns = [
image: 'image-3',
imageV432x230: 'image-3',
currentUserTodos: {
+ __typename: 'ToDo',
nodes: [],
},
},
@@ -130,7 +149,9 @@ export const moveDesignMutationResponse = {
data: {
designManagementMove: {
designCollection: {
+ __typename: 'DesignCollection',
designs: {
+ __typename: 'DesignConnection',
nodes: [...reorderedDesigns],
},
},
diff --git a/spec/frontend/design_management/pages/index_spec.js b/spec/frontend/design_management/pages/index_spec.js
index 87d45196d54..63de96a1b4e 100644
--- a/spec/frontend/design_management/pages/index_spec.js
+++ b/spec/frontend/design_management/pages/index_spec.js
@@ -5,6 +5,8 @@ import VueRouter from 'vue-router';
import { GlEmptyState } from '@gitlab/ui';
import createMockApollo from 'jest/helpers/mock_apollo_helper';
import { mockTracking, unmockTracking } from 'helpers/tracking_helper';
+import getDesignListQuery from 'shared_queries/design_management/get_design_list.query.graphql';
+import permissionsQuery from 'shared_queries/design_management/design_permissions.query.graphql';
import Index from '~/design_management/pages/index.vue';
import uploadDesignQuery from '~/design_management/graphql/mutations/upload_design.mutation.graphql';
import DesignDestroyer from '~/design_management/components/design_destroyer.vue';
@@ -28,8 +30,6 @@ import {
reorderedDesigns,
moveDesignMutationResponseWithErrors,
} from '../mock_data/apollo_mock';
-import getDesignListQuery from '~/design_management/graphql/queries/get_design_list.query.graphql';
-import permissionsQuery from '~/design_management/graphql/queries/design_permissions.query.graphql';
import moveDesignMutation from '~/design_management/graphql/mutations/move_design.mutation.graphql';
import { DESIGN_TRACKING_PAGE_NAME } from '~/design_management/utils/tracking';
diff --git a/spec/frontend/fixtures/issues.rb b/spec/frontend/fixtures/issues.rb
index 2c380ba6a96..baea87be45f 100644
--- a/spec/frontend/fixtures/issues.rb
+++ b/spec/frontend/fixtures/issues.rb
@@ -16,6 +16,8 @@ RSpec.describe Projects::IssuesController, '(JavaScript fixtures)', type: :contr
end
before do
+ stub_feature_flags(vue_issue_header: false)
+
sign_in(admin)
end
diff --git a/spec/frontend/helpers/mock_apollo_helper.js b/spec/frontend/helpers/mock_apollo_helper.js
index 8a5a160231c..914cce1d662 100644
--- a/spec/frontend/helpers/mock_apollo_helper.js
+++ b/spec/frontend/helpers/mock_apollo_helper.js
@@ -2,14 +2,14 @@ import { InMemoryCache } from 'apollo-cache-inmemory';
import { createMockClient } from 'mock-apollo-client';
import VueApollo from 'vue-apollo';
-export default (handlers = []) => {
+export default (handlers = [], resolvers = {}) => {
const fragmentMatcher = { match: () => true };
const cache = new InMemoryCache({
fragmentMatcher,
addTypename: false,
});
- const mockClient = createMockClient({ cache });
+ const mockClient = createMockClient({ cache, resolvers });
if (Array.isArray(handlers)) {
handlers.forEach(([query, value]) => mockClient.setRequestHandler(query, value));
diff --git a/spec/frontend/issue_show/components/header_actions_spec.js b/spec/frontend/issue_show/components/header_actions_spec.js
new file mode 100644
index 00000000000..7f4a9c8cdc3
--- /dev/null
+++ b/spec/frontend/issue_show/components/header_actions_spec.js
@@ -0,0 +1,218 @@
+import { GlButton, GlDropdown, GlDropdownItem, GlLink, GlModal } from '@gitlab/ui';
+import { createLocalVue, shallowMount } from '@vue/test-utils';
+import Vuex from 'vuex';
+import HeaderActions from '~/issue_show/components/header_actions.vue';
+import { IssuableStatus, IssueStateEvent } from '~/issue_show/constants';
+import createStore from '~/notes/stores';
+
+describe('HeaderActions component', () => {
+ let dispatchEventSpy;
+ let wrapper;
+
+ const localVue = createLocalVue();
+ localVue.use(Vuex);
+ const store = createStore();
+
+ const defaultProps = {
+ canCreateIssue: true,
+ canReopenIssue: true,
+ canReportSpam: true,
+ canUpdateIssue: true,
+ iid: '32',
+ isIssueAuthor: true,
+ newIssuePath: 'gitlab-org/gitlab-test/-/issues/new',
+ projectPath: 'gitlab-org/gitlab-test',
+ reportAbusePath:
+ '-/abuse_reports/new?ref_url=http%3A%2F%2Flocalhost%2Fgitlab-org%2Fgitlab-test%2F-%2Fissues%2F32&user_id=1',
+ submitAsSpamPath: 'gitlab-org/gitlab-test/-/issues/32/submit_as_spam',
+ };
+
+ const mutate = jest.fn().mockResolvedValue({ data: { updateIssue: { errors: [] } } });
+
+ const findToggleIssueStateButton = () => wrapper.find(GlButton);
+
+ const findDropdownAt = index => wrapper.findAll(GlDropdown).at(index);
+
+ const findMobileDropdownItems = () => findDropdownAt(0).findAll(GlDropdownItem);
+
+ const findDesktopDropdownItems = () => findDropdownAt(1).findAll(GlDropdownItem);
+
+ const findModal = () => wrapper.find(GlModal);
+
+ const findModalLinkAt = index =>
+ findModal()
+ .findAll(GlLink)
+ .at(index);
+
+ const mountComponent = ({
+ props = {},
+ issueState = IssuableStatus.Open,
+ blockedByIssues = [],
+ } = {}) => {
+ store.getters.getNoteableData.state = issueState;
+ store.getters.getNoteableData.blocked_by_issues = blockedByIssues;
+
+ return shallowMount(HeaderActions, {
+ localVue,
+ store,
+ provide: {
+ ...defaultProps,
+ ...props,
+ },
+ mocks: {
+ $apollo: {
+ mutate,
+ },
+ },
+ });
+ };
+
+ afterEach(() => {
+ if (dispatchEventSpy) {
+ dispatchEventSpy.mockRestore();
+ }
+ wrapper.destroy();
+ });
+
+ describe('close/reopen button', () => {
+ describe.each`
+ description | issueState | buttonText | newIssueState
+ ${'when the issue is open'} | ${IssuableStatus.Open} | ${'Close issue'} | ${IssueStateEvent.Close}
+ ${'when the issue is closed'} | ${IssuableStatus.Closed} | ${'Reopen issue'} | ${IssueStateEvent.Reopen}
+ `('$description', ({ issueState, buttonText, newIssueState }) => {
+ beforeEach(() => {
+ dispatchEventSpy = jest.spyOn(document, 'dispatchEvent');
+
+ wrapper = mountComponent({ issueState });
+ });
+
+ it(`has text "${buttonText}"`, () => {
+ expect(findToggleIssueStateButton().text()).toBe(buttonText);
+ });
+
+ it('calls apollo mutation', () => {
+ findToggleIssueStateButton().vm.$emit('click');
+
+ expect(mutate).toHaveBeenCalledWith(
+ expect.objectContaining({
+ variables: {
+ input: {
+ iid: defaultProps.iid.toString(),
+ projectPath: defaultProps.projectPath,
+ stateEvent: newIssueState,
+ },
+ },
+ }),
+ );
+ });
+
+ it('dispatches a custom event to update the issue page', async () => {
+ findToggleIssueStateButton().vm.$emit('click');
+
+ await wrapper.vm.$nextTick();
+
+ expect(dispatchEventSpy).toHaveBeenCalledTimes(1);
+ });
+ });
+ });
+
+ describe.each`
+ description | isCloseIssueItemVisible | findDropdownItems
+ ${'mobile dropdown'} | ${true} | ${findMobileDropdownItems}
+ ${'desktop dropdown'} | ${false} | ${findDesktopDropdownItems}
+ `('$description', ({ isCloseIssueItemVisible, findDropdownItems }) => {
+ describe.each`
+ description | itemText | isItemVisible | canUpdateIssue | canCreateIssue | isIssueAuthor | canReportSpam
+ ${'when user can update issue'} | ${'Close issue'} | ${isCloseIssueItemVisible} | ${true} | ${true} | ${true} | ${true}
+ ${'when user cannot update issue'} | ${'Close issue'} | ${false} | ${false} | ${true} | ${true} | ${true}
+ ${'when user can create issue'} | ${'New issue'} | ${true} | ${true} | ${true} | ${true} | ${true}
+ ${'when user cannot create issue'} | ${'New issue'} | ${false} | ${true} | ${false} | ${true} | ${true}
+ ${'when user can report abuse'} | ${'Report abuse'} | ${true} | ${true} | ${true} | ${false} | ${true}
+ ${'when user cannot report abuse'} | ${'Report abuse'} | ${false} | ${true} | ${true} | ${true} | ${true}
+ ${'when user can submit as spam'} | ${'Submit as spam'} | ${true} | ${true} | ${true} | ${true} | ${true}
+ ${'when user cannot submit as spam'} | ${'Submit as spam'} | ${false} | ${true} | ${true} | ${true} | ${false}
+ `(
+ '$description',
+ ({
+ itemText,
+ isItemVisible,
+ canUpdateIssue,
+ canCreateIssue,
+ isIssueAuthor,
+ canReportSpam,
+ }) => {
+ beforeEach(() => {
+ wrapper = mountComponent({
+ props: {
+ canUpdateIssue,
+ canCreateIssue,
+ isIssueAuthor,
+ canReportSpam,
+ },
+ });
+ });
+
+ it(`${isItemVisible ? 'shows' : 'hides'} "${itemText}" item`, () => {
+ expect(
+ findDropdownItems()
+ .filter(item => item.text() === itemText)
+ .exists(),
+ ).toBe(isItemVisible);
+ });
+ },
+ );
+ });
+
+ describe('modal', () => {
+ const blockedByIssues = [
+ { iid: 13, web_url: 'gitlab-org/gitlab-test/-/issues/13' },
+ { iid: 79, web_url: 'gitlab-org/gitlab-test/-/issues/79' },
+ ];
+
+ beforeEach(() => {
+ wrapper = mountComponent({ blockedByIssues });
+ });
+
+ it('has title text', () => {
+ expect(findModal().attributes('title')).toBe(
+ 'Are you sure you want to close this blocked issue?',
+ );
+ });
+
+ it('has body text', () => {
+ expect(findModal().text()).toContain(
+ 'This issue is currently blocked by the following issues:',
+ );
+ });
+
+ it('calls apollo mutation when primary button is clicked', () => {
+ findModal().vm.$emit('primary');
+
+ expect(mutate).toHaveBeenCalledWith(
+ expect.objectContaining({
+ variables: {
+ input: {
+ iid: defaultProps.iid.toString(),
+ projectPath: defaultProps.projectPath,
+ stateEvent: IssueStateEvent.Close,
+ },
+ },
+ }),
+ );
+ });
+
+ describe.each`
+ ordinal | index
+ ${'first'} | ${0}
+ ${'second'} | ${1}
+ `('$ordinal blocked-by issue link', ({ index }) => {
+ it('has link text', () => {
+ expect(findModalLinkAt(index).text()).toBe(`#${blockedByIssues[index].iid}`);
+ });
+
+ it('has url', () => {
+ expect(findModalLinkAt(index).attributes('href')).toBe(blockedByIssues[index].web_url);
+ });
+ });
+ });
+});
diff --git a/spec/frontend/issue_show/issue_spec.js b/spec/frontend/issue_show/issue_spec.js
index 91cacb4601c..7a48353af94 100644
--- a/spec/frontend/issue_show/issue_spec.js
+++ b/spec/frontend/issue_show/issue_spec.js
@@ -2,7 +2,7 @@ import MockAdapter from 'axios-mock-adapter';
import { useMockIntersectionObserver } from 'helpers/mock_dom_observer';
import waitForPromises from 'helpers/wait_for_promises';
import axios from '~/lib/utils/axios_utils';
-import initIssuableApp from '~/issue_show/issue';
+import { initIssuableApp } from '~/issue_show/issue';
import * as parseData from '~/issue_show/utils/parse_data';
import { appProps } from './mock_data';
import createStore from '~/notes/stores';
diff --git a/spec/frontend/pipeline_editor/components/text_editor_spec.js b/spec/frontend/pipeline_editor/components/text_editor_spec.js
index e6e91034194..39d205839f4 100644
--- a/spec/frontend/pipeline_editor/components/text_editor_spec.js
+++ b/spec/frontend/pipeline_editor/components/text_editor_spec.js
@@ -32,4 +32,10 @@ describe('~/pipeline_editor/components/text_editor.vue', () => {
expect(findEditor().props('editorOptions')).toEqual({ readOnly: true });
expect(findEditor().props('fileName')).toBe('*.yml');
});
+
+ it('bubbles up editor-ready event', () => {
+ findEditor().vm.$emit('editor-ready');
+
+ expect(wrapper.emitted('editor-ready')).toHaveLength(1);
+ });
});
diff --git a/spec/frontend/pipeline_editor/pipeline_editor_app_spec.js b/spec/frontend/pipeline_editor/pipeline_editor_app_spec.js
index b7a8585a686..46523baadf3 100644
--- a/spec/frontend/pipeline_editor/pipeline_editor_app_spec.js
+++ b/spec/frontend/pipeline_editor/pipeline_editor_app_spec.js
@@ -1,16 +1,20 @@
import { nextTick } from 'vue';
-import { mount, shallowMount } from '@vue/test-utils';
-import { GlAlert, GlLoadingIcon } from '@gitlab/ui';
+import { shallowMount } from '@vue/test-utils';
+import { GlAlert, GlLoadingIcon, GlTabs, GlTab } from '@gitlab/ui';
import { mockProjectPath, mockDefaultBranch, mockCiConfigPath, mockCiYml } from './mock_data';
import TextEditor from '~/pipeline_editor/components/text_editor.vue';
import EditorLite from '~/vue_shared/components/editor_lite.vue';
+import PipelineGraph from '~/pipelines/components/pipeline_graph/pipeline_graph.vue';
import PipelineEditorApp from '~/pipeline_editor/pipeline_editor_app.vue';
describe('~/pipeline_editor/pipeline_editor_app.vue', () => {
let wrapper;
- const createComponent = ({ props = {}, loading = false } = {}, mountFn = shallowMount) => {
+ const createComponent = (
+ { props = {}, data = {}, loading = false } = {},
+ mountFn = shallowMount,
+ ) => {
wrapper = mountFn(PipelineEditorApp, {
propsData: {
projectPath: mockProjectPath,
@@ -18,7 +22,11 @@ describe('~/pipeline_editor/pipeline_editor_app.vue', () => {
ciConfigPath: mockCiConfigPath,
...props,
},
+ data() {
+ return data;
+ },
stubs: {
+ GlTabs,
TextEditor,
},
mocks: {
@@ -35,23 +43,59 @@ describe('~/pipeline_editor/pipeline_editor_app.vue', () => {
const findLoadingIcon = () => wrapper.find(GlLoadingIcon);
const findAlert = () => wrapper.find(GlAlert);
- const findEditor = () => wrapper.find(EditorLite);
+ const findTabAt = i => wrapper.findAll(GlTab).at(i);
+ const findEditorLite = () => wrapper.find(EditorLite);
- it('displays content', async () => {
+ beforeEach(() => {
createComponent();
- wrapper.setData({ content: mockCiYml });
- await nextTick();
+ });
+
+ afterEach(() => {
+ wrapper.destroy();
+ wrapper = null;
+ });
+
+ it('displays content', () => {
+ createComponent({ data: { content: mockCiYml } });
expect(findLoadingIcon().exists()).toBe(false);
- expect(findEditor().props('value')).toBe(mockCiYml);
+ expect(findEditorLite().props('value')).toBe(mockCiYml);
});
- it('displays a loading icon if the query is loading', async () => {
+ it('displays a loading icon if the query is loading', () => {
createComponent({ loading: true });
expect(findLoadingIcon().exists()).toBe(true);
});
+ describe('tabs', () => {
+ it('displays tabs and their content', () => {
+ createComponent({ data: { content: mockCiYml } });
+
+ expect(
+ findTabAt(0)
+ .find(EditorLite)
+ .exists(),
+ ).toBe(true);
+ expect(
+ findTabAt(1)
+ .find(PipelineGraph)
+ .exists(),
+ ).toBe(true);
+ });
+
+ it('displays editor tab lazily, until editor is ready', async () => {
+ createComponent({ data: { content: mockCiYml } });
+
+ expect(findTabAt(0).attributes('lazy')).toBe('true');
+
+ findEditorLite().vm.$emit('editor-ready');
+ await nextTick();
+
+ expect(findTabAt(0).attributes('lazy')).toBe(undefined);
+ });
+ });
+
describe('when in error state', () => {
class MockError extends Error {
constructor(message, data) {
@@ -64,24 +108,18 @@ describe('~/pipeline_editor/pipeline_editor_app.vue', () => {
}
}
- beforeEach(() => {
- createComponent(mount);
- });
-
- it('shows a generic error', async () => {
- wrapper.setData({ error: new MockError('An error message') });
- await nextTick();
+ it('shows a generic error', () => {
+ const error = new MockError('An error message');
+ createComponent({ data: { error } });
expect(findAlert().text()).toBe('CI file could not be loaded: An error message');
});
- it('shows a ref missing error state', async () => {
+ it('shows a ref missing error state', () => {
const error = new MockError('Ref missing!', {
error: 'ref is missing, ref is empty',
});
-
- wrapper.setData({ error });
- await nextTick();
+ createComponent({ data: { error } });
expect(findAlert().text()).toMatch(
'CI file could not be loaded: ref is missing, ref is empty',
@@ -93,8 +131,7 @@ describe('~/pipeline_editor/pipeline_editor_app.vue', () => {
message: 'file not found',
});
- wrapper.setData({ error });
- await nextTick();
+ await wrapper.setData({ error });
expect(findAlert().text()).toMatch('CI file could not be loaded: file not found');
});
diff --git a/spec/frontend/snippets/components/edit_spec.js b/spec/frontend/snippets/components/edit_spec.js
index c1fad8cebe6..3521733ee5e 100644
--- a/spec/frontend/snippets/components/edit_spec.js
+++ b/spec/frontend/snippets/components/edit_spec.js
@@ -1,7 +1,9 @@
-import { ApolloMutation } from 'vue-apollo';
+import VueApollo, { ApolloMutation } from 'vue-apollo';
import { GlLoadingIcon } from '@gitlab/ui';
-import { shallowMount } from '@vue/test-utils';
+import { shallowMount, createLocalVue } from '@vue/test-utils';
import waitForPromises from 'helpers/wait_for_promises';
+import createMockApollo from 'jest/helpers/mock_apollo_helper';
+import GetSnippetQuery from 'shared_queries/snippet/snippet.query.graphql';
import { deprecatedCreateFlash as Flash } from '~/flash';
import * as urlUtils from '~/lib/utils/url_utility';
import SnippetEditApp from '~/snippets/components/edit.vue';
@@ -10,7 +12,11 @@ import SnippetVisibilityEdit from '~/snippets/components/snippet_visibility_edit
import SnippetBlobActionsEdit from '~/snippets/components/snippet_blob_actions_edit.vue';
import TitleField from '~/vue_shared/components/form/title.vue';
import FormFooterActions from '~/vue_shared/components/form/form_footer_actions.vue';
-import { SNIPPET_VISIBILITY_PRIVATE } from '~/snippets/constants';
+import {
+ SNIPPET_VISIBILITY_PRIVATE,
+ SNIPPET_VISIBILITY_INTERNAL,
+ SNIPPET_VISIBILITY_PUBLIC,
+} from '~/snippets/constants';
import UpdateSnippetMutation from '~/snippets/mutations/updateSnippet.mutation.graphql';
import CreateSnippetMutation from '~/snippets/mutations/createSnippet.mutation.graphql';
import { testEntries } from '../test_utils';
@@ -47,8 +53,12 @@ const createTestSnippet = () => ({
describe('Snippet Edit app', () => {
let wrapper;
+ let fakeApollo;
const relativeUrlRoot = '/foo/';
const originalRelativeUrlRoot = gon.relative_url_root;
+ const GetSnippetQuerySpy = jest.fn().mockResolvedValue({
+ data: { snippets: { nodes: [createTestSnippet()] } },
+ });
const mutationTypes = {
RESOLVE: jest.fn().mockResolvedValue({
@@ -78,12 +88,10 @@ describe('Snippet Edit app', () => {
props = {},
loading = false,
mutationRes = mutationTypes.RESOLVE,
+ selectedLevel = SNIPPET_VISIBILITY_PRIVATE,
+ withApollo = false,
} = {}) {
- if (wrapper) {
- throw new Error('wrapper already exists');
- }
-
- wrapper = shallowMount(SnippetEditApp, {
+ let componentData = {
mocks: {
$apollo: {
queries: {
@@ -92,23 +100,35 @@ describe('Snippet Edit app', () => {
mutate: mutationRes,
},
},
+ };
+
+ if (withApollo) {
+ const localVue = createLocalVue();
+ localVue.use(VueApollo);
+
+ const requestHandlers = [[GetSnippetQuery, GetSnippetQuerySpy]];
+ fakeApollo = createMockApollo(requestHandlers);
+ componentData = {
+ localVue,
+ apolloProvider: fakeApollo,
+ };
+ }
+
+ wrapper = shallowMount(SnippetEditApp, {
+ ...componentData,
stubs: {
ApolloMutation,
FormFooterActions,
},
+ provide: {
+ selectedLevel,
+ },
propsData: {
snippetGid: 'gid://gitlab/PersonalSnippet/42',
markdownPreviewPath: 'http://preview.foo.bar',
markdownDocsPath: 'http://docs.foo.bar',
...props,
},
- data() {
- return {
- snippet: {
- visibilityLevel: SNIPPET_VISIBILITY_PRIVATE,
- },
- };
- },
});
}
@@ -152,16 +172,13 @@ describe('Snippet Edit app', () => {
if (nodes.length) {
wrapper.setData({
snippet: nodes[0],
+ newSnippet: false,
+ });
+ } else {
+ wrapper.setData({
+ newSnippet: true,
});
}
-
- wrapper.vm.onSnippetFetch({
- data: {
- snippets: {
- nodes,
- },
- },
- });
};
describe('rendering', () => {
@@ -228,6 +245,28 @@ describe('Snippet Edit app', () => {
});
describe('functionality', () => {
+ it('does not fetch snippet when create a new snippet', async () => {
+ createComponent({ props: { snippetGid: '' }, withApollo: true });
+
+ jest.runOnlyPendingTimers();
+ await wrapper.vm.$nextTick();
+
+ expect(GetSnippetQuerySpy).not.toHaveBeenCalled();
+ });
+
+ describe('default visibility', () => {
+ it.each([SNIPPET_VISIBILITY_PRIVATE, SNIPPET_VISIBILITY_INTERNAL, SNIPPET_VISIBILITY_PUBLIC])(
+ 'marks %s visibility by default',
+ async visibility => {
+ createComponent({
+ props: { snippetGid: '' },
+ selectedLevel: visibility,
+ });
+ expect(wrapper.vm.snippet.visibilityLevel).toEqual(visibility);
+ },
+ );
+ });
+
describe('form submission handling', () => {
it.each`
snippetArg | projectPath | uploadedFiles | input | mutation
diff --git a/spec/frontend/snippets/components/snippet_visibility_edit_spec.js b/spec/frontend/snippets/components/snippet_visibility_edit_spec.js
index 3919e4d7993..3151090f388 100644
--- a/spec/frontend/snippets/components/snippet_visibility_edit_spec.js
+++ b/spec/frontend/snippets/components/snippet_visibility_edit_spec.js
@@ -1,7 +1,6 @@
import { GlFormRadio, GlIcon, GlFormRadioGroup, GlLink } from '@gitlab/ui';
import { mount, shallowMount } from '@vue/test-utils';
import SnippetVisibilityEdit from '~/snippets/components/snippet_visibility_edit.vue';
-import { defaultSnippetVisibilityLevels } from '~/snippets/utils/blob';
import {
SNIPPET_VISIBILITY,
SNIPPET_VISIBILITY_PRIVATE,
@@ -15,36 +14,25 @@ describe('Snippet Visibility Edit component', () => {
let wrapper;
const defaultHelpLink = '/foo/bar';
const defaultVisibilityLevel = 'private';
- const defaultVisibility = defaultSnippetVisibilityLevels([0, 10, 20]);
function createComponent({
propsData = {},
- visibilityLevels = defaultVisibility,
+ visibilityLevels = [0, 10, 20],
multipleLevelsRestricted = false,
deep = false,
} = {}) {
const method = deep ? mount : shallowMount;
- const $apollo = {
- queries: {
- defaultVisibility: {
- loading: false,
- },
- },
- };
wrapper = method.call(this, SnippetVisibilityEdit, {
- mock: { $apollo },
propsData: {
helpLink: defaultHelpLink,
isProjectSnippet: false,
value: defaultVisibilityLevel,
...propsData,
},
- data() {
- return {
- visibilityLevels,
- multipleLevelsRestricted,
- };
+ provide: {
+ visibilityLevels,
+ multipleLevelsRestricted,
},
});
}
@@ -108,7 +96,6 @@ describe('Snippet Visibility Edit component', () => {
it.each`
levels | resultOptions
- ${undefined} | ${[]}
${''} | ${[]}
${[]} | ${[]}
${[0]} | ${[RESULTING_OPTIONS[0]]}
@@ -117,7 +104,7 @@ describe('Snippet Visibility Edit component', () => {
${[0, 20]} | ${[RESULTING_OPTIONS[0], RESULTING_OPTIONS[20]]}
${[10, 20]} | ${[RESULTING_OPTIONS[10], RESULTING_OPTIONS[20]]}
`('renders correct visibility options for $levels', ({ levels, resultOptions }) => {
- createComponent({ visibilityLevels: defaultSnippetVisibilityLevels(levels), deep: true });
+ createComponent({ visibilityLevels: levels, deep: true });
expect(findRadiosData()).toEqual(resultOptions);
});
@@ -132,7 +119,7 @@ describe('Snippet Visibility Edit component', () => {
'renders correct information about restricted visibility levels for $levels',
({ levels, levelsRestricted, resultText }) => {
createComponent({
- visibilityLevels: defaultSnippetVisibilityLevels(levels),
+ visibilityLevels: levels,
multipleLevelsRestricted: levelsRestricted,
});
expect(findRestrictedInfo().text()).toBe(resultText);