diff options
Diffstat (limited to 'spec/frontend/blob/filepath_form/components')
3 files changed, 294 insertions, 0 deletions
diff --git a/spec/frontend/blob/filepath_form/components/filepath_form_spec.js b/spec/frontend/blob/filepath_form/components/filepath_form_spec.js new file mode 100644 index 00000000000..8a890cdc75a --- /dev/null +++ b/spec/frontend/blob/filepath_form/components/filepath_form_spec.js @@ -0,0 +1,70 @@ +import { shallowMount } from '@vue/test-utils'; +import { nextTick } from 'vue'; +import { GlFormInput } from '@gitlab/ui'; +import { setHTMLFixture, resetHTMLFixture } from 'helpers/fixtures'; +import FilepathForm from '~/blob/filepath_form/components/filepath_form.vue'; +import TemplateSelector from '~/blob/filepath_form/components/template_selector.vue'; +import { Templates as TemplatesMock } from './mock_data'; + +describe('Filepath Form component', () => { + let wrapper; + + const findNavLinks = () => document.querySelector('.nav-links'); + const findNavLinkWrite = () => findNavLinks().querySelector('#edit'); + const findNavLinkPreview = () => findNavLinks().querySelector('#preview'); + + const findInput = () => wrapper.findComponent(GlFormInput); + const findTemplateSelector = () => wrapper.findComponent(TemplateSelector); + + const createComponent = (props = {}) => { + wrapper = shallowMount(FilepathForm, { + propsData: { + templates: TemplatesMock, + inputOptions: {}, + ...props, + }, + }); + }; + + beforeEach(() => { + setHTMLFixture(` + <div class="file-editor"> + <ul class="nav-links"> + <a class="nav-link" id="edit" href="#editor">Write</a> + <a class="nav-link" id="preview" href="#preview">Preview</a> + </ul> + </div> + `); + createComponent(); + }); + + afterEach(() => { + resetHTMLFixture(); + }); + + it('renders input with correct attributes', () => { + createComponent({ inputOptions: { name: 'foo', value: 'bar' } }); + expect(findInput().attributes()).toMatchObject({ + name: 'foo', + value: 'bar', + }); + }); + + describe('when write button is clicked', () => { + it('renders template selector', async () => { + findNavLinkWrite().click(); + await nextTick(); + + expect(findTemplateSelector().exists()).toBe(true); + }); + }); + + describe('when preview button is clicked', () => { + it('hides template selector', async () => { + findNavLinkPreview().click(); + await nextTick(); + + expect(findTemplateSelector().exists()).toBe(false); + }); + }); +}); diff --git a/spec/frontend/blob/filepath_form/components/mock_data.js b/spec/frontend/blob/filepath_form/components/mock_data.js new file mode 100644 index 00000000000..fc19d6f1887 --- /dev/null +++ b/spec/frontend/blob/filepath_form/components/mock_data.js @@ -0,0 +1,57 @@ +export const SuggestCiYmlData = { + trackLabel: 'suggest_gitlab_ci_yml', + dismissKey: '10', + mergeRequestPath: 'mr_path', + humanAccess: 'owner', +}; + +export const Templates = { + licenses: { + Other: [ + { + name: 'GNU Affero General Public License v3.0', + id: 'agpl-3.0', + key: 'agpl-3.0', + project_id: 10, + }, + ], + Popular: [ + { + name: 'Apache License 2.0', + id: 'apache-2.0', + key: 'apache-2.0', + project_id: 10, + }, + ], + }, + gitignore_names: { + Languages: [ + { + name: 'Actionscript', + id: 'Actionscript', + key: 'Actionscript', + project_id: 10, + }, + ], + }, + gitlab_ci_ymls: { + General: [ + { + name: '5-Minute-Production-App', + id: '5-Minute-Production-App', + key: '5-Minute-Production-App', + project_id: 10, + }, + ], + }, + dockerfile_names: { + General: [ + { + name: 'Binary', + id: 'Binary', + key: 'Binary', + project_id: 10, + }, + ], + }, +}; diff --git a/spec/frontend/blob/filepath_form/components/template_selector_spec.js b/spec/frontend/blob/filepath_form/components/template_selector_spec.js new file mode 100644 index 00000000000..b1419320e1e --- /dev/null +++ b/spec/frontend/blob/filepath_form/components/template_selector_spec.js @@ -0,0 +1,167 @@ +import { GlCollapsibleListbox } from '@gitlab/ui'; +import { shallowMount } from '@vue/test-utils'; +import { nextTick } from 'vue'; +import TemplateSelector from '~/blob/filepath_form/components/template_selector.vue'; +import SuggestGitlabCiYml from '~/blob/suggest_gitlab_ci_yml/components/popover.vue'; +import { Templates as TemplatesMock, SuggestCiYmlData as SuggestCiYmlDataMock } from './mock_data'; + +describe('Template Selector component', () => { + let wrapper; + + const findListbox = () => wrapper.findComponent(GlCollapsibleListbox); + const findSuggestCiYmlPopover = () => wrapper.findComponent(SuggestGitlabCiYml); + const findDisplayedTemplates = () => + findListbox() + .props('items') + .reduce((acc, item) => [...acc, ...item.options], []) + .map((template) => template.value); + + const getTemplateKeysFromMock = (key) => + Object.values(TemplatesMock[key]) + .reduce((acc, items) => [...acc, ...items], []) + .map((template) => template.key); + + const createComponent = (props = {}) => { + wrapper = shallowMount(TemplateSelector, { + propsData: { + filename: '', + templates: TemplatesMock, + ...props, + }, + }); + }; + + describe('when filename input is empty', () => { + beforeEach(() => { + createComponent(); + }); + + it('does not render listbox', () => { + expect(findListbox().exists()).toBe(false); + }); + + it('does not render suggest-ci-yml popover', () => { + expect(findSuggestCiYmlPopover().exists()).toBe(false); + }); + }); + + describe.each` + filename | key + ${'LICENSE'} | ${'licenses'} + ${'Dockerfile'} | ${'dockerfile_names'} + ${'.gitignore'} | ${'gitignore_names'} + ${'.gitlab-ci.yml'} | ${'gitlab_ci_ymls'} + `('when filename is $filename', ({ filename, key }) => { + beforeEach(() => { + createComponent({ filename }); + }); + + it('renders listbox with correct props', () => { + expect(findListbox().exists()).toBe(true); + expect(findListbox().props('toggleText')).toBe('Apply a template'); + expect(findListbox().props('searchPlaceholder')).toBe('Filter'); + expect(findDisplayedTemplates()).toEqual(getTemplateKeysFromMock(key)); + }); + + it('does not render suggest-ci-yml popover', () => { + expect(findSuggestCiYmlPopover().exists()).toBe(false); + }); + }); + + describe('when filename input is .gitlab-ci.yml with suggestCiYmlData prop', () => { + beforeEach(() => { + createComponent({ filename: '.gitlab-ci.yml', suggestCiYmlData: SuggestCiYmlDataMock }); + }); + + it('renders listbox with correct props', () => { + expect(findListbox().exists()).toBe(true); + expect(findListbox().props('toggleText')).toBe('Apply a template'); + expect(findListbox().props('searchPlaceholder')).toBe('Filter'); + }); + + it('renders suggest-ci-yml popover', () => { + expect(findSuggestCiYmlPopover().exists()).toBe(true); + }); + }); + + describe('has filename that matches template pattern', () => { + const filename = 'LICENSE'; + const templates = TemplatesMock.licenses.Other; + + describe('has initial template prop', () => { + const initialTemplate = TemplatesMock.licenses.Other[0]; + + beforeEach(() => { + createComponent({ filename, initialTemplate: initialTemplate.key }); + }); + + it('renders listbox toggle button with selected template name', () => { + expect(findListbox().props('toggleText')).toBe(initialTemplate.name); + }); + + it('selected template is checked', () => { + expect(findListbox().props('selected')).toBe(initialTemplate.key); + }); + }); + + describe('when template is selected', () => { + beforeEach(() => { + createComponent({ filename }); + findListbox().vm.$emit('select', templates[0].key); + }); + + it('emit `selected` event with selected template', () => { + const licenseSelectorType = { + key: 'licenses', + name: 'LICENSE', + pattern: /^(.+\/)?(licen[sc]e|copying)($|\.)/i, + type: 'licenses', + }; + + const { template, type } = wrapper.emitted('selected')[0][0]; + expect(template).toBe(templates[0]); + expect(type).toMatchObject(licenseSelectorType); + }); + + it('set loading state to true', () => { + expect(findListbox().props('loading')).toBe(true); + }); + + describe('when stopLoading callback from `selected` event is called', () => { + it('set loading state to false', async () => { + const { stopLoading } = wrapper.emitted('selected')[0][0]; + + stopLoading(); + await nextTick(); + + expect(findListbox().props('loading')).toBe(false); + }); + }); + }); + + describe('when searching for filter', () => { + const searchTerm = 'GNU'; + + beforeEach(() => { + createComponent({ filename: 'LICENSE' }); + findListbox().vm.$emit('search', searchTerm); + }); + + it('shows matching templates', () => { + const displayedTemplates = findDisplayedTemplates(); + const matchingTemplate = templates.find((template) => + template.name.toLowerCase().includes(searchTerm.toLowerCase()), + ); + expect(displayedTemplates).toContain(matchingTemplate?.key); + }); + + it('hides non-matching templates', () => { + const displayedTemplates = findDisplayedTemplates(); + const nonMatchingTemplate = templates.find( + (template) => !template.name.includes(searchTerm), + ); + expect(displayedTemplates).not.toContain(nonMatchingTemplate?.key); + }); + }); + }); +}); |