diff options
Diffstat (limited to 'spec/frontend/snippets/components/snippet_blob_actions_edit_spec.js')
-rw-r--r-- | spec/frontend/snippets/components/snippet_blob_actions_edit_spec.js | 301 |
1 files changed, 301 insertions, 0 deletions
diff --git a/spec/frontend/snippets/components/snippet_blob_actions_edit_spec.js b/spec/frontend/snippets/components/snippet_blob_actions_edit_spec.js new file mode 100644 index 00000000000..8b2051008d7 --- /dev/null +++ b/spec/frontend/snippets/components/snippet_blob_actions_edit_spec.js @@ -0,0 +1,301 @@ +import { times } from 'lodash'; +import { shallowMount } from '@vue/test-utils'; +import SnippetBlobActionsEdit from '~/snippets/components/snippet_blob_actions_edit.vue'; +import SnippetBlobEdit from '~/snippets/components/snippet_blob_edit.vue'; +import { + SNIPPET_MAX_BLOBS, + SNIPPET_BLOB_ACTION_CREATE, + SNIPPET_BLOB_ACTION_MOVE, +} from '~/snippets/constants'; +import { testEntries, createBlobFromTestEntry } from '../test_utils'; + +const TEST_BLOBS = [ + createBlobFromTestEntry(testEntries.updated), + createBlobFromTestEntry(testEntries.deleted), +]; + +const TEST_BLOBS_UNLOADED = TEST_BLOBS.map(blob => ({ ...blob, content: '', isLoaded: false })); + +describe('snippets/components/snippet_blob_actions_edit', () => { + let wrapper; + + const createComponent = (props = {}, snippetMultipleFiles = true) => { + wrapper = shallowMount(SnippetBlobActionsEdit, { + propsData: { + initBlobs: TEST_BLOBS, + ...props, + }, + provide: { + glFeatures: { + snippetMultipleFiles, + }, + }, + }); + }; + + const findLabel = () => wrapper.find('label'); + const findBlobEdits = () => wrapper.findAll(SnippetBlobEdit); + const findBlobsData = () => + findBlobEdits().wrappers.map(x => ({ + blob: x.props('blob'), + classes: x.classes(), + })); + const findFirstBlobEdit = () => findBlobEdits().at(0); + const findAddButton = () => wrapper.find('[data-testid="add_button"]'); + const getLastActions = () => { + const events = wrapper.emitted().actions; + + return events[events.length - 1]?.[0]; + }; + const buildBlobsDataExpectation = blobs => + blobs.map((blob, index) => ({ + blob: { + ...blob, + id: expect.stringMatching('blob_local_'), + }, + classes: index > 0 ? ['gl-mt-3'] : [], + })); + const triggerBlobDelete = idx => + findBlobEdits() + .at(idx) + .vm.$emit('delete'); + const triggerBlobUpdate = (idx, props) => + findBlobEdits() + .at(idx) + .vm.$emit('blob-updated', props); + + afterEach(() => { + wrapper.destroy(); + wrapper = null; + }); + + describe.each` + featureFlag | label | showDelete | showAdd + ${true} | ${'Files'} | ${true} | ${true} + ${false} | ${'File'} | ${false} | ${false} + `('with feature flag = $featureFlag', ({ featureFlag, label, showDelete, showAdd }) => { + beforeEach(() => { + createComponent({}, featureFlag); + }); + + it('renders label', () => { + expect(findLabel().text()).toBe(label); + }); + + it(`renders delete button (show=${showDelete})`, () => { + expect(findFirstBlobEdit().props()).toMatchObject({ + showDelete, + canDelete: true, + }); + }); + + it(`renders add button (show=${showAdd})`, () => { + expect(findAddButton().exists()).toBe(showAdd); + }); + }); + + describe('with default', () => { + beforeEach(() => { + createComponent(); + }); + + it('emits no actions', () => { + expect(getLastActions()).toEqual([]); + }); + + it('shows blobs', () => { + expect(findBlobsData()).toEqual(buildBlobsDataExpectation(TEST_BLOBS_UNLOADED)); + }); + + it('shows add button', () => { + const button = findAddButton(); + + expect(button.text()).toBe(`Add another file ${TEST_BLOBS.length}/${SNIPPET_MAX_BLOBS}`); + expect(button.props('disabled')).toBe(false); + }); + + describe('when add is clicked', () => { + beforeEach(() => { + findAddButton().vm.$emit('click'); + }); + + it('adds blob with empty content', () => { + expect(findBlobsData()).toEqual( + buildBlobsDataExpectation([ + ...TEST_BLOBS_UNLOADED, + { + content: '', + isLoaded: true, + path: '', + }, + ]), + ); + }); + + it('emits action', () => { + expect(getLastActions()).toEqual([ + expect.objectContaining({ + action: SNIPPET_BLOB_ACTION_CREATE, + }), + ]); + }); + }); + + describe('when blob is deleted', () => { + beforeEach(() => { + triggerBlobDelete(1); + }); + + it('removes blob', () => { + expect(findBlobsData()).toEqual(buildBlobsDataExpectation(TEST_BLOBS_UNLOADED.slice(0, 1))); + }); + + it('emits action', () => { + expect(getLastActions()).toEqual([ + expect.objectContaining({ + ...testEntries.deleted.diff, + content: '', + }), + ]); + }); + }); + + describe('when blob changes path', () => { + beforeEach(() => { + triggerBlobUpdate(0, { path: 'new/path' }); + }); + + it('renames blob', () => { + expect(findBlobsData()[0]).toMatchObject({ + blob: { + path: 'new/path', + }, + }); + }); + + it('emits action', () => { + expect(getLastActions()).toMatchObject([ + { + action: SNIPPET_BLOB_ACTION_MOVE, + filePath: 'new/path', + previousPath: testEntries.updated.diff.filePath, + }, + ]); + }); + }); + + describe('when blob emits new content', () => { + const { content } = testEntries.updated.diff; + const originalContent = `${content}\noriginal content\n`; + + beforeEach(() => { + triggerBlobUpdate(0, { content: originalContent }); + }); + + it('loads new content', () => { + expect(findBlobsData()[0]).toMatchObject({ + blob: { + content: originalContent, + isLoaded: true, + }, + }); + }); + + it('does not emit an action', () => { + expect(getLastActions()).toEqual([]); + }); + + it('emits an action when content changes again', async () => { + triggerBlobUpdate(0, { content }); + + await wrapper.vm.$nextTick(); + + expect(getLastActions()).toEqual([testEntries.updated.diff]); + }); + }); + }); + + describe('with 1 blob', () => { + beforeEach(() => { + createComponent({ initBlobs: [createBlobFromTestEntry(testEntries.created)] }); + }); + + it('disables delete button', () => { + expect(findBlobEdits()).toHaveLength(1); + expect( + findBlobEdits() + .at(0) + .props(), + ).toMatchObject({ + showDelete: true, + canDelete: false, + }); + }); + + describe(`when added ${SNIPPET_MAX_BLOBS} files`, () => { + let addButton; + + beforeEach(() => { + addButton = findAddButton(); + + times(SNIPPET_MAX_BLOBS - 1, () => addButton.vm.$emit('click')); + }); + + it('should have blobs', () => { + expect(findBlobsData()).toHaveLength(SNIPPET_MAX_BLOBS); + }); + + it('should disable add button', () => { + expect(addButton.props('disabled')).toBe(true); + }); + }); + }); + + describe('with 0 init blob', () => { + beforeEach(() => { + createComponent({ initBlobs: [] }); + }); + + it('shows 1 blob by default', () => { + expect(findBlobsData()).toEqual([ + expect.objectContaining({ + blob: { + id: expect.stringMatching('blob_local_'), + content: '', + path: '', + isLoaded: true, + }, + }), + ]); + }); + + it('emits create action', () => { + expect(getLastActions()).toEqual([ + { + action: SNIPPET_BLOB_ACTION_CREATE, + content: '', + filePath: '', + previousPath: '', + }, + ]); + }); + }); + + describe(`with ${SNIPPET_MAX_BLOBS} files`, () => { + beforeEach(() => { + const initBlobs = Array(SNIPPET_MAX_BLOBS) + .fill(1) + .map(() => createBlobFromTestEntry(testEntries.created)); + + createComponent({ initBlobs }); + }); + + it('should have blobs', () => { + expect(findBlobsData()).toHaveLength(SNIPPET_MAX_BLOBS); + }); + + it('should disable add button', () => { + expect(findAddButton().props('disabled')).toBe(true); + }); + }); +}); |