diff options
Diffstat (limited to 'spec/frontend/snippets')
8 files changed, 211 insertions, 51 deletions
diff --git a/spec/frontend/snippets/components/__snapshots__/snippet_description_edit_spec.js.snap b/spec/frontend/snippets/components/__snapshots__/snippet_description_edit_spec.js.snap index 6020d595e3f..3b101e9e815 100644 --- a/spec/frontend/snippets/components/__snapshots__/snippet_description_edit_spec.js.snap +++ b/spec/frontend/snippets/components/__snapshots__/snippet_description_edit_spec.js.snap @@ -41,7 +41,7 @@ exports[`Snippet Description Edit component rendering matches the snapshot 1`] = > <textarea aria-label="Description" - class="note-textarea js-gfm-input js-autosize markdown-area" + class="note-textarea js-gfm-input js-autosize markdown-area js-gfm-input-initialized" data-qa-selector="snippet_description_field" data-supports-quick-actions="false" dir="auto" @@ -63,8 +63,8 @@ exports[`Snippet Description Edit component rendering matches the snapshot 1`] = class="zen-control zen-control-leave js-zen-leave gl-text-gray-500" href="#" > - <icon-stub - name="screen-normal" + <gl-icon-stub + name="minimize" size="16" /> </a> diff --git a/spec/frontend/snippets/components/__snapshots__/snippet_visibility_edit_spec.js.snap b/spec/frontend/snippets/components/__snapshots__/snippet_visibility_edit_spec.js.snap index be75a5bfbdc..8446f0f50c4 100644 --- a/spec/frontend/snippets/components/__snapshots__/snippet_visibility_edit_spec.js.snap +++ b/spec/frontend/snippets/components/__snapshots__/snippet_visibility_edit_spec.js.snap @@ -20,6 +20,7 @@ exports[`Snippet Visibility Edit component rendering matches the snapshot 1`] = </label> <gl-form-group-stub + class="gl-mb-0" id="visibility-level-setting" > <gl-form-radio-group-stub @@ -90,5 +91,12 @@ exports[`Snippet Visibility Edit component rendering matches the snapshot 1`] = </gl-form-radio-stub> </gl-form-radio-group-stub> </gl-form-group-stub> + + <div + class="text-muted" + data-testid="restricted-levels-info" + > + <!----> + </div> </div> `; diff --git a/spec/frontend/snippets/components/edit_spec.js b/spec/frontend/snippets/components/edit_spec.js index ebab6aa84f6..b6abb9f389a 100644 --- a/spec/frontend/snippets/components/edit_spec.js +++ b/spec/frontend/snippets/components/edit_spec.js @@ -47,6 +47,8 @@ const createTestSnippet = () => ({ describe('Snippet Edit app', () => { let wrapper; + const relativeUrlRoot = '/foo/'; + const originalRelativeUrlRoot = gon.relative_url_root; const mutationTypes = { RESOLVE: jest.fn().mockResolvedValue({ @@ -100,16 +102,25 @@ describe('Snippet Edit app', () => { markdownDocsPath: 'http://docs.foo.bar', ...props, }, + data() { + return { + snippet: { + visibilityLevel: SNIPPET_VISIBILITY_PRIVATE, + }, + }; + }, }); } beforeEach(() => { + gon.relative_url_root = relativeUrlRoot; jest.spyOn(urlUtils, 'redirectTo').mockImplementation(); }); afterEach(() => { wrapper.destroy(); wrapper = null; + gon.relative_url_root = originalRelativeUrlRoot; }); const findBlobActions = () => wrapper.find(SnippetBlobActionsEdit); @@ -164,10 +175,10 @@ describe('Snippet Edit app', () => { props => { createComponent(props); - expect(wrapper.contains(TitleField)).toBe(true); - expect(wrapper.contains(SnippetDescriptionEdit)).toBe(true); - expect(wrapper.contains(SnippetVisibilityEdit)).toBe(true); - expect(wrapper.contains(FormFooterActions)).toBe(true); + expect(wrapper.find(TitleField).exists()).toBe(true); + expect(wrapper.find(SnippetDescriptionEdit).exists()).toBe(true); + expect(wrapper.find(SnippetVisibilityEdit).exists()).toBe(true); + expect(wrapper.find(FormFooterActions).exists()).toBe(true); expect(findBlobActions().exists()).toBe(true); }, ); @@ -196,8 +207,8 @@ describe('Snippet Edit app', () => { it.each` projectPath | snippetArg | expectation - ${''} | ${[]} | ${'/-/snippets'} - ${'project/path'} | ${[]} | ${'/project/path/-/snippets'} + ${''} | ${[]} | ${urlUtils.joinPaths('/', relativeUrlRoot, '-', 'snippets')} + ${'project/path'} | ${[]} | ${urlUtils.joinPaths('/', relativeUrlRoot, 'project/path/-', 'snippets')} ${''} | ${[createTestSnippet()]} | ${TEST_WEB_URL} ${'project/path'} | ${[createTestSnippet()]} | ${TEST_WEB_URL} `( diff --git a/spec/frontend/snippets/components/embed_dropdown_spec.js b/spec/frontend/snippets/components/embed_dropdown_spec.js new file mode 100644 index 00000000000..8eb44965692 --- /dev/null +++ b/spec/frontend/snippets/components/embed_dropdown_spec.js @@ -0,0 +1,70 @@ +import { escape as esc } from 'lodash'; +import { mount } from '@vue/test-utils'; +import { GlFormInputGroup } from '@gitlab/ui'; +import { TEST_HOST } from 'helpers/test_constants'; +import EmbedDropdown from '~/snippets/components/embed_dropdown.vue'; + +const TEST_URL = `${TEST_HOST}/test/no">'xss`; + +describe('snippets/components/embed_dropdown', () => { + let wrapper; + + const createComponent = () => { + wrapper = mount(EmbedDropdown, { + propsData: { + url: TEST_URL, + }, + }); + }; + + afterEach(() => { + wrapper.destroy(); + wrapper = null; + }); + + const findSectionsData = () => { + const sections = []; + let current = {}; + + wrapper.findAll('[data-testid="header"],[data-testid="input"]').wrappers.forEach(x => { + const type = x.attributes('data-testid'); + + if (type === 'header') { + current = { + header: x.text(), + }; + + sections.push(current); + } else { + const value = x.find(GlFormInputGroup).props('value'); + const copyValue = x.find('button[title="Copy"]').attributes('data-clipboard-text'); + + Object.assign(current, { + value, + copyValue, + }); + } + }); + + return sections; + }; + + it('renders dropdown items', () => { + createComponent(); + + const embedValue = `<script src="${esc(TEST_URL)}.js"></script>`; + + expect(findSectionsData()).toEqual([ + { + header: 'Embed', + value: embedValue, + copyValue: embedValue, + }, + { + header: 'Share', + value: TEST_URL, + copyValue: TEST_URL, + }, + ]); + }); +}); diff --git a/spec/frontend/snippets/components/show_spec.js b/spec/frontend/snippets/components/show_spec.js index 8cccbb83d54..b5ab7def753 100644 --- a/spec/frontend/snippets/components/show_spec.js +++ b/spec/frontend/snippets/components/show_spec.js @@ -2,7 +2,7 @@ import { GlLoadingIcon } from '@gitlab/ui'; import { Blob, BinaryBlob } from 'jest/blob/components/mock_data'; import { shallowMount } from '@vue/test-utils'; import SnippetApp from '~/snippets/components/show.vue'; -import BlobEmbeddable from '~/blob/components/blob_embeddable.vue'; +import EmbedDropdown from '~/snippets/components/embed_dropdown.vue'; import SnippetHeader from '~/snippets/components/snippet_header.vue'; import SnippetTitle from '~/snippets/components/snippet_title.vue'; import SnippetBlob from '~/snippets/components/snippet_blob_view.vue'; @@ -57,7 +57,7 @@ describe('Snippet view app', () => { expect(wrapper.find(SnippetTitle).exists()).toBe(true); }); - it('renders embeddable component if visibility allows', () => { + it('renders embed dropdown component if visibility allows', () => { createComponent({ data: { snippet: { @@ -66,7 +66,7 @@ describe('Snippet view app', () => { }, }, }); - expect(wrapper.contains(BlobEmbeddable)).toBe(true); + expect(wrapper.find(EmbedDropdown).exists()).toBe(true); }); it('renders correct snippet-blob components', () => { @@ -88,7 +88,7 @@ describe('Snippet view app', () => { ${SNIPPET_VISIBILITY_PRIVATE} | ${'not render'} | ${false} ${'foo'} | ${'not render'} | ${false} ${SNIPPET_VISIBILITY_PUBLIC} | ${'render'} | ${true} - `('does $condition blob-embeddable by default', ({ visibilityLevel, isRendered }) => { + `('does $condition embed-dropdown by default', ({ visibilityLevel, isRendered }) => { createComponent({ data: { snippet: { @@ -97,7 +97,7 @@ describe('Snippet view app', () => { }, }, }); - expect(wrapper.contains(BlobEmbeddable)).toBe(isRendered); + expect(wrapper.find(EmbedDropdown).exists()).toBe(isRendered); }); }); @@ -119,7 +119,7 @@ describe('Snippet view app', () => { }, }, }); - expect(wrapper.contains(CloneDropdownButton)).toBe(isRendered); + expect(wrapper.find(CloneDropdownButton).exists()).toBe(isRendered); }, ); }); diff --git a/spec/frontend/snippets/components/snippet_blob_edit_spec.js b/spec/frontend/snippets/components/snippet_blob_edit_spec.js index 188f9ae5cf1..fc4da46d722 100644 --- a/spec/frontend/snippets/components/snippet_blob_edit_spec.js +++ b/spec/frontend/snippets/components/snippet_blob_edit_spec.js @@ -17,6 +17,7 @@ const TEST_PATH = 'foo/bar/test.md'; const TEST_RAW_PATH = '/gitlab/raw/path/to/blob/7'; const TEST_FULL_PATH = joinPaths(TEST_HOST, TEST_RAW_PATH); const TEST_CONTENT = 'Lorem ipsum dolar sit amet,\nconsectetur adipiscing elit.'; +const TEST_JSON_CONTENT = '{"abc":"lorem ipsum"}'; const TEST_BLOB = { id: TEST_ID, @@ -66,7 +67,7 @@ describe('Snippet Blob Edit component', () => { }); describe('with not loaded blob', () => { - beforeEach(async () => { + beforeEach(() => { createComponent(); }); @@ -100,6 +101,20 @@ describe('Snippet Blob Edit component', () => { }); }); + describe('with unloaded blob and JSON content', () => { + beforeEach(() => { + axiosMock.onGet(TEST_FULL_PATH).reply(200, TEST_JSON_CONTENT); + createComponent(); + }); + + // This checks against this issue https://gitlab.com/gitlab-org/gitlab/-/issues/241199 + it('emits raw content', async () => { + await waitForPromises(); + + expect(getLastUpdatedArgs()).toEqual({ content: TEST_JSON_CONTENT }); + }); + }); + describe('with error', () => { beforeEach(() => { axiosMock.reset(); diff --git a/spec/frontend/snippets/components/snippet_header_spec.js b/spec/frontend/snippets/components/snippet_header_spec.js index da8cb2e6a8d..5836de1fdbe 100644 --- a/spec/frontend/snippets/components/snippet_header_spec.js +++ b/spec/frontend/snippets/components/snippet_header_spec.js @@ -5,6 +5,7 @@ import { Blob, BinaryBlob } from 'jest/blob/components/mock_data'; import waitForPromises from 'helpers/wait_for_promises'; import DeleteSnippetMutation from '~/snippets/mutations/deleteSnippet.mutation.graphql'; import SnippetHeader from '~/snippets/components/snippet_header.vue'; +import { differenceInMilliseconds } from '~/lib/utils/datetime_utility'; describe('Snippet header component', () => { let wrapper; @@ -14,6 +15,7 @@ describe('Snippet header component', () => { let errorMsg; let err; + const originalRelativeUrlRoot = gon.relative_url_root; function createComponent({ loading = false, @@ -50,6 +52,7 @@ describe('Snippet header component', () => { } beforeEach(() => { + gon.relative_url_root = '/foo/'; snippet = { id: 'gid://gitlab/PersonalSnippet/50', title: 'The property of Thor', @@ -65,7 +68,7 @@ describe('Snippet header component', () => { name: 'Thor Odinson', }, blobs: [Blob], - createdAt: new Date(Date.now() - 32 * 24 * 3600 * 1000).toISOString(), + createdAt: new Date(differenceInMilliseconds(32 * 24 * 3600 * 1000)).toISOString(), }; mutationVariables = { @@ -86,6 +89,7 @@ describe('Snippet header component', () => { afterEach(() => { wrapper.destroy(); + gon.relative_url_root = originalRelativeUrlRoot; }); it('renders itself', () => { @@ -213,7 +217,7 @@ describe('Snippet header component', () => { it('redirects to dashboard/snippets for personal snippet', () => { return createDeleteSnippet().then(() => { expect(wrapper.vm.closeDeleteModal).toHaveBeenCalled(); - expect(window.location.pathname).toBe('dashboard/snippets'); + expect(window.location.pathname).toBe(`${gon.relative_url_root}dashboard/snippets`); }); }); diff --git a/spec/frontend/snippets/components/snippet_visibility_edit_spec.js b/spec/frontend/snippets/components/snippet_visibility_edit_spec.js index a8df13787a5..3919e4d7993 100644 --- a/spec/frontend/snippets/components/snippet_visibility_edit_spec.js +++ b/spec/frontend/snippets/components/snippet_visibility_edit_spec.js @@ -1,31 +1,55 @@ 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, SNIPPET_VISIBILITY_INTERNAL, SNIPPET_VISIBILITY_PUBLIC, + SNIPPET_LEVELS_RESTRICTED, + SNIPPET_LEVELS_DISABLED, } from '~/snippets/constants'; describe('Snippet Visibility Edit component', () => { let wrapper; const defaultHelpLink = '/foo/bar'; const defaultVisibilityLevel = 'private'; - - function createComponent(propsData = {}, deep = false) { + const defaultVisibility = defaultSnippetVisibilityLevels([0, 10, 20]); + + function createComponent({ + propsData = {}, + visibilityLevels = defaultVisibility, + 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, + }; + }, }); } - const findLabel = () => wrapper.find('label'); + const findLink = () => wrapper.find('label').find(GlLink); const findRadios = () => wrapper.find(GlFormRadioGroup).findAll(GlFormRadio); const findRadiosData = () => findRadios().wrappers.map(x => { @@ -47,56 +71,84 @@ describe('Snippet Visibility Edit component', () => { expect(wrapper.element).toMatchSnapshot(); }); - it('renders visibility options', () => { - createComponent({}, true); + it('renders label help link', () => { + createComponent(); + + expect(findLink().attributes('href')).toBe(defaultHelpLink); + }); + + it('when helpLink is not defined, does not render label help link', () => { + createComponent({ propsData: { helpLink: null } }); - expect(findRadiosData()).toEqual([ - { + expect(findLink().exists()).toBe(false); + }); + + describe('Visibility options', () => { + const findRestrictedInfo = () => wrapper.find('[data-testid="restricted-levels-info"]'); + const RESULTING_OPTIONS = { + 0: { value: SNIPPET_VISIBILITY_PRIVATE, icon: SNIPPET_VISIBILITY.private.icon, text: SNIPPET_VISIBILITY.private.label, description: SNIPPET_VISIBILITY.private.description, }, - { + 10: { value: SNIPPET_VISIBILITY_INTERNAL, icon: SNIPPET_VISIBILITY.internal.icon, text: SNIPPET_VISIBILITY.internal.label, description: SNIPPET_VISIBILITY.internal.description, }, - { + 20: { value: SNIPPET_VISIBILITY_PUBLIC, icon: SNIPPET_VISIBILITY.public.icon, text: SNIPPET_VISIBILITY.public.label, description: SNIPPET_VISIBILITY.public.description, }, - ]); - }); - - it('when project snippet, renders special private description', () => { - createComponent({ isProjectSnippet: true }, true); + }; - expect(findRadiosData()[0]).toEqual({ - value: SNIPPET_VISIBILITY_PRIVATE, - icon: SNIPPET_VISIBILITY.private.icon, - text: SNIPPET_VISIBILITY.private.label, - description: SNIPPET_VISIBILITY.private.description_project, + it.each` + levels | resultOptions + ${undefined} | ${[]} + ${''} | ${[]} + ${[]} | ${[]} + ${[0]} | ${[RESULTING_OPTIONS[0]]} + ${[0, 10]} | ${[RESULTING_OPTIONS[0], RESULTING_OPTIONS[10]]} + ${[0, 10, 20]} | ${[RESULTING_OPTIONS[0], RESULTING_OPTIONS[10], RESULTING_OPTIONS[20]]} + ${[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 }); + expect(findRadiosData()).toEqual(resultOptions); }); - }); - it('renders label help link', () => { - createComponent(); - - expect( - findLabel() - .find(GlLink) - .attributes('href'), - ).toBe(defaultHelpLink); - }); + it.each` + levels | levelsRestricted | resultText + ${[]} | ${false} | ${SNIPPET_LEVELS_DISABLED} + ${[]} | ${true} | ${SNIPPET_LEVELS_DISABLED} + ${[0]} | ${true} | ${SNIPPET_LEVELS_RESTRICTED} + ${[0]} | ${false} | ${''} + ${[0, 10, 20]} | ${false} | ${''} + `( + 'renders correct information about restricted visibility levels for $levels', + ({ levels, levelsRestricted, resultText }) => { + createComponent({ + visibilityLevels: defaultSnippetVisibilityLevels(levels), + multipleLevelsRestricted: levelsRestricted, + }); + expect(findRestrictedInfo().text()).toBe(resultText); + }, + ); - it('when helpLink is not defined, does not render label help link', () => { - createComponent({ helpLink: null }); + it('when project snippet, renders special private description', () => { + createComponent({ propsData: { isProjectSnippet: true }, deep: true }); - expect(findLabel().contains(GlLink)).toBe(false); + expect(findRadiosData()[0]).toEqual({ + value: SNIPPET_VISIBILITY_PRIVATE, + icon: SNIPPET_VISIBILITY.private.icon, + text: SNIPPET_VISIBILITY.private.label, + description: SNIPPET_VISIBILITY.private.description_project, + }); + }); }); }); @@ -104,7 +156,7 @@ describe('Snippet Visibility Edit component', () => { it('pre-selects correct option in the list', () => { const value = SNIPPET_VISIBILITY_INTERNAL; - createComponent({ value }); + createComponent({ propsData: { value } }); expect(wrapper.find(GlFormRadioGroup).attributes('checked')).toBe(value); }); |