313 lines
8.3 KiB
JavaScript
313 lines
8.3 KiB
JavaScript
import { times } from 'lodash';
|
|
import { nextTick } from 'vue';
|
|
import { GlFormGroup } from '@gitlab/ui';
|
|
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 { s__ } from '~/locale';
|
|
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 = {}) => {
|
|
wrapper = shallowMount(SnippetBlobActionsEdit, {
|
|
propsData: {
|
|
initBlobs: TEST_BLOBS,
|
|
...props,
|
|
},
|
|
});
|
|
};
|
|
|
|
const findLabel = () => wrapper.findComponent(GlFormGroup);
|
|
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('multi-file snippets rendering', () => {
|
|
beforeEach(() => {
|
|
createComponent();
|
|
});
|
|
|
|
it('renders label', () => {
|
|
expect(findLabel().attributes('label')).toBe('Files');
|
|
});
|
|
|
|
it(`renders delete button (show=true)`, () => {
|
|
expect(findFirstBlobEdit().props()).toMatchObject({
|
|
showDelete: true,
|
|
canDelete: true,
|
|
});
|
|
});
|
|
|
|
it(`renders add button (show=true)`, () => {
|
|
expect(findAddButton().exists()).toBe(true);
|
|
});
|
|
});
|
|
|
|
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 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);
|
|
});
|
|
});
|
|
|
|
describe('isValid prop', () => {
|
|
const validationMessage = s__(
|
|
"Snippets|Snippets can't contain empty files. Ensure all files have content, or delete them.",
|
|
);
|
|
|
|
describe('when not present', () => {
|
|
it('sets the label validation state to true', () => {
|
|
createComponent();
|
|
|
|
const label = findLabel();
|
|
|
|
expect(Boolean(label.attributes('state'))).toEqual(true);
|
|
expect(label.attributes('invalid-feedback')).toEqual(validationMessage);
|
|
});
|
|
});
|
|
|
|
describe('when present', () => {
|
|
it('sets the label validation state to the value', () => {
|
|
createComponent({ isValid: false });
|
|
|
|
const label = findLabel();
|
|
|
|
expect(Boolean(label.attributes('state'))).toEqual(false);
|
|
expect(label.attributes('invalid-feedback')).toEqual(validationMessage);
|
|
});
|
|
});
|
|
});
|
|
});
|