summaryrefslogtreecommitdiff
path: root/spec/javascripts/diffs/components
diff options
context:
space:
mode:
Diffstat (limited to 'spec/javascripts/diffs/components')
-rw-r--r--spec/javascripts/diffs/components/app_spec.js1
-rw-r--r--spec/javascripts/diffs/components/changed_files_dropdown_spec.js1
-rw-r--r--spec/javascripts/diffs/components/changed_files_spec.js100
-rw-r--r--spec/javascripts/diffs/components/compare_versions_dropdown_spec.js1
-rw-r--r--spec/javascripts/diffs/components/compare_versions_spec.js1
-rw-r--r--spec/javascripts/diffs/components/diff_content_spec.js1
-rw-r--r--spec/javascripts/diffs/components/diff_discussions_spec.js24
-rw-r--r--spec/javascripts/diffs/components/diff_file_header_spec.js433
-rw-r--r--spec/javascripts/diffs/components/diff_file_spec.js88
-rw-r--r--spec/javascripts/diffs/components/diff_gutter_avatars_spec.js115
-rw-r--r--spec/javascripts/diffs/components/diff_line_gutter_content_spec.js153
-rw-r--r--spec/javascripts/diffs/components/diff_line_note_form_spec.js68
-rw-r--r--spec/javascripts/diffs/components/edit_button_spec.js1
-rw-r--r--spec/javascripts/diffs/components/hidden_files_warning_spec.js1
-rw-r--r--spec/javascripts/diffs/components/inline_diff_view_spec.js111
-rw-r--r--spec/javascripts/diffs/components/no_changes_spec.js1
-rw-r--r--spec/javascripts/diffs/components/parallel_diff_view_spec.js224
17 files changed, 1324 insertions, 0 deletions
diff --git a/spec/javascripts/diffs/components/app_spec.js b/spec/javascripts/diffs/components/app_spec.js
new file mode 100644
index 00000000000..7237274eb43
--- /dev/null
+++ b/spec/javascripts/diffs/components/app_spec.js
@@ -0,0 +1 @@
+// TODO: https://gitlab.com/gitlab-org/gitlab-ce/issues/48034
diff --git a/spec/javascripts/diffs/components/changed_files_dropdown_spec.js b/spec/javascripts/diffs/components/changed_files_dropdown_spec.js
new file mode 100644
index 00000000000..7237274eb43
--- /dev/null
+++ b/spec/javascripts/diffs/components/changed_files_dropdown_spec.js
@@ -0,0 +1 @@
+// TODO: https://gitlab.com/gitlab-org/gitlab-ce/issues/48034
diff --git a/spec/javascripts/diffs/components/changed_files_spec.js b/spec/javascripts/diffs/components/changed_files_spec.js
new file mode 100644
index 00000000000..2d57af6137c
--- /dev/null
+++ b/spec/javascripts/diffs/components/changed_files_spec.js
@@ -0,0 +1,100 @@
+import Vue from 'vue';
+import $ from 'jquery';
+import { mountComponentWithStore } from 'spec/helpers';
+import store from '~/diffs/store';
+import ChangedFiles from '~/diffs/components/changed_files.vue';
+
+describe('ChangedFiles', () => {
+ const Component = Vue.extend(ChangedFiles);
+ const createComponent = props => mountComponentWithStore(Component, { props, store });
+ let vm;
+
+ beforeEach(() => {
+ setFixtures(`
+ <div id="dummy-element"></div>
+ <div class="js-tabs-affix"></div>
+ `);
+ const props = {
+ diffFiles: [
+ {
+ addedLines: 10,
+ removedLines: 20,
+ blob: {
+ path: 'some/code.txt',
+ },
+ filePath: 'some/code.txt',
+ },
+ ],
+ };
+ vm = createComponent(props);
+ });
+
+ describe('with single file added', () => {
+ it('shows files changes', () => {
+ expect(vm.$el).toContainText('1 changed file');
+ });
+
+ it('shows file additions and deletions', () => {
+ expect(vm.$el).toContainText('10 additions');
+ expect(vm.$el).toContainText('20 deletions');
+ });
+ });
+
+ describe('template', () => {
+ describe('diff view mode buttons', () => {
+ let inlineButton;
+ let parallelButton;
+
+ beforeEach(() => {
+ inlineButton = vm.$el.querySelector('.js-inline-diff-button');
+ parallelButton = vm.$el.querySelector('.js-parallel-diff-button');
+ });
+
+ it('should have Inline and Side-by-side buttons', () => {
+ expect(inlineButton).toBeDefined();
+ expect(parallelButton).toBeDefined();
+ });
+
+ it('should add active class to Inline button', done => {
+ vm.$store.state.diffs.diffViewType = 'inline';
+
+ vm.$nextTick(() => {
+ expect(inlineButton.classList.contains('active')).toEqual(true);
+ expect(parallelButton.classList.contains('active')).toEqual(false);
+
+ done();
+ });
+ });
+
+ it('should toggle active state of buttons when diff view type changed', done => {
+ vm.$store.state.diffs.diffViewType = 'parallel';
+
+ vm.$nextTick(() => {
+ expect(inlineButton.classList.contains('active')).toEqual(false);
+ expect(parallelButton.classList.contains('active')).toEqual(true);
+
+ done();
+ });
+ });
+
+ describe('clicking them', () => {
+ it('should toggle the diff view type', done => {
+ $(parallelButton).click();
+
+ vm.$nextTick(() => {
+ expect(inlineButton.classList.contains('active')).toEqual(false);
+ expect(parallelButton.classList.contains('active')).toEqual(true);
+
+ $(inlineButton).click();
+
+ vm.$nextTick(() => {
+ expect(inlineButton.classList.contains('active')).toEqual(true);
+ expect(parallelButton.classList.contains('active')).toEqual(false);
+ done();
+ });
+ });
+ });
+ });
+ });
+ });
+});
diff --git a/spec/javascripts/diffs/components/compare_versions_dropdown_spec.js b/spec/javascripts/diffs/components/compare_versions_dropdown_spec.js
new file mode 100644
index 00000000000..7237274eb43
--- /dev/null
+++ b/spec/javascripts/diffs/components/compare_versions_dropdown_spec.js
@@ -0,0 +1 @@
+// TODO: https://gitlab.com/gitlab-org/gitlab-ce/issues/48034
diff --git a/spec/javascripts/diffs/components/compare_versions_spec.js b/spec/javascripts/diffs/components/compare_versions_spec.js
new file mode 100644
index 00000000000..7237274eb43
--- /dev/null
+++ b/spec/javascripts/diffs/components/compare_versions_spec.js
@@ -0,0 +1 @@
+// TODO: https://gitlab.com/gitlab-org/gitlab-ce/issues/48034
diff --git a/spec/javascripts/diffs/components/diff_content_spec.js b/spec/javascripts/diffs/components/diff_content_spec.js
new file mode 100644
index 00000000000..7237274eb43
--- /dev/null
+++ b/spec/javascripts/diffs/components/diff_content_spec.js
@@ -0,0 +1 @@
+// TODO: https://gitlab.com/gitlab-org/gitlab-ce/issues/48034
diff --git a/spec/javascripts/diffs/components/diff_discussions_spec.js b/spec/javascripts/diffs/components/diff_discussions_spec.js
new file mode 100644
index 00000000000..270f363825f
--- /dev/null
+++ b/spec/javascripts/diffs/components/diff_discussions_spec.js
@@ -0,0 +1,24 @@
+import Vue from 'vue';
+import DiffDiscussions from '~/diffs/components/diff_discussions.vue';
+import store from '~/mr_notes/stores';
+import { createComponentWithStore } from 'spec/helpers/vue_mount_component_helper';
+import discussionsMockData from '../mock_data/diff_discussions';
+
+describe('DiffDiscussions', () => {
+ let component;
+ const getDiscussionsMockData = () => [Object.assign({}, discussionsMockData)];
+
+ beforeEach(() => {
+ component = createComponentWithStore(Vue.extend(DiffDiscussions), store, {
+ discussions: getDiscussionsMockData(),
+ }).$mount();
+ });
+
+ describe('template', () => {
+ it('should have notes list', () => {
+ const { $el } = component;
+
+ expect($el.querySelectorAll('.discussion .note.timeline-entry').length).toEqual(5);
+ });
+ });
+});
diff --git a/spec/javascripts/diffs/components/diff_file_header_spec.js b/spec/javascripts/diffs/components/diff_file_header_spec.js
new file mode 100644
index 00000000000..d0f1700bee6
--- /dev/null
+++ b/spec/javascripts/diffs/components/diff_file_header_spec.js
@@ -0,0 +1,433 @@
+import Vue from 'vue';
+import DiffFileHeader from '~/diffs/components/diff_file_header.vue';
+import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils';
+import mountComponent from 'spec/helpers/vue_mount_component_helper';
+
+const discussionFixture = 'merge_requests/diff_discussion.json';
+
+describe('diff_file_header', () => {
+ let vm;
+ let props;
+ const Component = Vue.extend(DiffFileHeader);
+
+ beforeEach(() => {
+ const diffDiscussionMock = getJSONFixture(discussionFixture)[0];
+ const diffFile = convertObjectPropsToCamelCase(diffDiscussionMock.diff_file, { deep: true });
+ props = {
+ diffFile,
+ currentUser: {},
+ };
+ });
+
+ afterEach(() => {
+ vm.$destroy();
+ });
+
+ describe('computed', () => {
+ describe('icon', () => {
+ beforeEach(() => {
+ props.diffFile.blob.icon = 'dummy icon';
+ });
+
+ it('returns the blob icon for files', () => {
+ props.diffFile.submodule = false;
+
+ vm = mountComponent(Component, props);
+
+ expect(vm.icon).toBe(props.diffFile.blob.icon);
+ });
+
+ it('returns the archive icon for submodules', () => {
+ props.diffFile.submodule = true;
+
+ vm = mountComponent(Component, props);
+
+ expect(vm.icon).toBe('archive');
+ });
+ });
+
+ describe('titleLink', () => {
+ beforeEach(() => {
+ Object.assign(props.diffFile, {
+ fileHash: 'badc0ffee',
+ submoduleLink: 'link://to/submodule',
+ submoduleTreeUrl: 'some://tree/url',
+ });
+ });
+
+ it('returns the fileHash for files', () => {
+ props.diffFile.submodule = false;
+
+ vm = mountComponent(Component, props);
+
+ expect(vm.titleLink).toBe(`#${props.diffFile.fileHash}`);
+ });
+
+ it('returns the submoduleTreeUrl for submodules', () => {
+ props.diffFile.submodule = true;
+
+ vm = mountComponent(Component, props);
+
+ expect(vm.titleLink).toBe(props.diffFile.submoduleTreeUrl);
+ });
+
+ it('returns the submoduleLink for submodules without submoduleTreeUrl', () => {
+ Object.assign(props.diffFile, {
+ submodule: true,
+ submoduleTreeUrl: null,
+ });
+
+ vm = mountComponent(Component, props);
+
+ expect(vm.titleLink).toBe(props.diffFile.submoduleLink);
+ });
+ });
+
+ describe('filePath', () => {
+ beforeEach(() => {
+ Object.assign(props.diffFile, {
+ blob: { id: 'b10b1db10b1d' },
+ filePath: 'path/to/file',
+ });
+ });
+
+ it('returns the filePath for files', () => {
+ props.diffFile.submodule = false;
+
+ vm = mountComponent(Component, props);
+
+ expect(vm.filePath).toBe(props.diffFile.filePath);
+ });
+
+ it('appends the truncated blob id for submodules', () => {
+ props.diffFile.submodule = true;
+
+ vm = mountComponent(Component, props);
+
+ expect(vm.filePath).toBe(
+ `${props.diffFile.filePath} @ ${props.diffFile.blob.id.substr(0, 8)}`,
+ );
+ });
+ });
+
+ describe('titleTag', () => {
+ it('returns a link tag if fileHash is set', () => {
+ props.diffFile.fileHash = 'some hash';
+
+ vm = mountComponent(Component, props);
+
+ expect(vm.titleTag).toBe('a');
+ });
+
+ it('returns a span tag if fileHash is not set', () => {
+ props.diffFile.fileHash = null;
+
+ vm = mountComponent(Component, props);
+
+ expect(vm.titleTag).toBe('span');
+ });
+ });
+
+ describe('isUsingLfs', () => {
+ beforeEach(() => {
+ Object.assign(props.diffFile, {
+ storedExternally: true,
+ externalStorage: 'lfs',
+ });
+ });
+
+ it('returns true if file is stored in LFS', () => {
+ vm = mountComponent(Component, props);
+
+ expect(vm.isUsingLfs).toBe(true);
+ });
+
+ it('returns false if file is not stored externally', () => {
+ props.diffFile.storedExternally = false;
+
+ vm = mountComponent(Component, props);
+
+ expect(vm.isUsingLfs).toBe(false);
+ });
+
+ it('returns false if file is not stored in LFS', () => {
+ props.diffFile.externalStorage = 'not lfs';
+
+ vm = mountComponent(Component, props);
+
+ expect(vm.isUsingLfs).toBe(false);
+ });
+ });
+
+ describe('collapseIcon', () => {
+ it('returns chevron-down if the diff is expanded', () => {
+ props.expanded = true;
+
+ vm = mountComponent(Component, props);
+
+ expect(vm.collapseIcon).toBe('chevron-down');
+ });
+
+ it('returns chevron-right if the diff is collapsed', () => {
+ props.expanded = false;
+
+ vm = mountComponent(Component, props);
+
+ expect(vm.collapseIcon).toBe('chevron-right');
+ });
+ });
+
+ describe('isDiscussionsExpanded', () => {
+ beforeEach(() => {
+ Object.assign(props, {
+ discussionsExpanded: true,
+ expanded: true,
+ });
+ });
+
+ it('returns true if diff and discussion are expanded', () => {
+ vm = mountComponent(Component, props);
+
+ expect(vm.isDiscussionsExpanded).toBe(true);
+ });
+
+ it('returns false if discussion is collapsed', () => {
+ props.discussionsExpanded = false;
+
+ vm = mountComponent(Component, props);
+
+ expect(vm.isDiscussionsExpanded).toBe(false);
+ });
+
+ it('returns false if diff is collapsed', () => {
+ props.expanded = false;
+
+ vm = mountComponent(Component, props);
+
+ expect(vm.isDiscussionsExpanded).toBe(false);
+ });
+ });
+
+ describe('viewFileButtonText', () => {
+ it('contains the truncated content SHA', () => {
+ const dummySha = 'deebd00f is no SHA';
+ props.diffFile.contentSha = dummySha;
+
+ vm = mountComponent(Component, props);
+
+ expect(vm.viewFileButtonText).not.toContain(dummySha);
+ expect(vm.viewFileButtonText).toContain(dummySha.substr(0, 8));
+ });
+ });
+
+ describe('viewReplacedFileButtonText', () => {
+ it('contains the truncated base SHA', () => {
+ const dummySha = 'deadabba sings no more';
+ props.diffFile.diffRefs.baseSha = dummySha;
+
+ vm = mountComponent(Component, props);
+
+ expect(vm.viewReplacedFileButtonText).not.toContain(dummySha);
+ expect(vm.viewReplacedFileButtonText).toContain(dummySha.substr(0, 8));
+ });
+ });
+ });
+
+ describe('methods', () => {
+ describe('handleToggle', () => {
+ beforeEach(() => {
+ spyOn(vm, '$emit').and.stub();
+ });
+
+ it('emits toggleFile if checkTarget is false', () => {
+ vm.handleToggle(null, false);
+
+ expect(vm.$emit).toHaveBeenCalledWith('toggleFile');
+ });
+
+ it('emits toggleFile if checkTarget is true and event target is header', () => {
+ vm.handleToggle({ target: vm.$refs.header }, true);
+
+ expect(vm.$emit).toHaveBeenCalledWith('toggleFile');
+ });
+
+ it('does not emit toggleFile if checkTarget is true and event target is not header', () => {
+ vm.handleToggle({ target: 'not header' }, true);
+
+ expect(vm.$emit).not.toHaveBeenCalled();
+ });
+ });
+ });
+
+ describe('template', () => {
+ describe('collapse toggle', () => {
+ const collapseToggle = () => vm.$el.querySelector('.diff-toggle-caret');
+
+ it('is visible if collapsible is true', () => {
+ props.collapsible = true;
+
+ vm = mountComponent(Component, props);
+
+ expect(collapseToggle()).not.toBe(null);
+ });
+
+ it('is hidden if collapsible is false', () => {
+ props.collapsible = false;
+
+ vm = mountComponent(Component, props);
+
+ expect(collapseToggle()).toBe(null);
+ });
+ });
+
+ it('displays an icon in the title', () => {
+ vm = mountComponent(Component, props);
+
+ const icon = vm.$el.querySelector(`i[class="fa fa-fw fa-${vm.icon}"]`);
+ expect(icon).not.toBe(null);
+ });
+
+ describe('file paths', () => {
+ const filePaths = () => vm.$el.querySelectorAll('.file-title-name');
+
+ it('displays the path of a added file', () => {
+ props.diffFile.renamedFile = false;
+
+ vm = mountComponent(Component, props);
+
+ expect(filePaths()).toHaveLength(1);
+ expect(filePaths()[0]).toHaveText(props.diffFile.filePath);
+ });
+
+ it('displays path for deleted file', () => {
+ props.diffFile.renamedFile = false;
+ props.diffFile.deletedFile = true;
+
+ vm = mountComponent(Component, props);
+
+ expect(filePaths()).toHaveLength(1);
+ expect(filePaths()[0]).toHaveText(`${props.diffFile.filePath} deleted`);
+ });
+
+ it('displays old and new path if the file was renamed', () => {
+ props.diffFile.renamedFile = true;
+
+ vm = mountComponent(Component, props);
+
+ expect(filePaths()).toHaveLength(2);
+ expect(filePaths()[0]).toHaveText(props.diffFile.oldPath);
+ expect(filePaths()[1]).toHaveText(props.diffFile.newPath);
+ });
+ });
+
+ it('displays a copy to clipboard button', () => {
+ vm = mountComponent(Component, props);
+
+ const button = vm.$el.querySelector('.btn-clipboard');
+ expect(button).not.toBe(null);
+ expect(button.dataset.clipboardText).toBe(props.diffFile.filePath);
+ });
+
+ describe('file mode', () => {
+ it('it displays old and new file mode if it changed', () => {
+ props.diffFile.modeChanged = true;
+
+ vm = mountComponent(Component, props);
+
+ const { fileMode } = vm.$refs;
+ expect(fileMode).not.toBe(undefined);
+ expect(fileMode).toContainText(props.diffFile.aMode);
+ expect(fileMode).toContainText(props.diffFile.bMode);
+ });
+
+ it('does not display the file mode if it has not changed', () => {
+ props.diffFile.modeChanged = false;
+
+ vm = mountComponent(Component, props);
+
+ const { fileMode } = vm.$refs;
+ expect(fileMode).toBe(undefined);
+ });
+ });
+
+ describe('LFS label', () => {
+ const lfsLabel = () => vm.$el.querySelector('.label-lfs');
+
+ it('displays the LFS label for files stored in LFS', () => {
+ Object.assign(props.diffFile, {
+ storedExternally: true,
+ externalStorage: 'lfs',
+ });
+
+ vm = mountComponent(Component, props);
+
+ expect(lfsLabel()).not.toBe(null);
+ expect(lfsLabel()).toHaveText('LFS');
+ });
+
+ it('does not display the LFS label for files stored in repository', () => {
+ props.diffFile.storedExternally = false;
+
+ vm = mountComponent(Component, props);
+
+ expect(lfsLabel()).toBe(null);
+ });
+ });
+
+ describe('edit button', () => {
+ it('should not render edit button if addMergeRequestButtons is not true', () => {
+ vm = mountComponent(Component, props);
+
+ expect(vm.$el.querySelector('.js-edit-blob')).toEqual(null);
+ });
+
+ it('should show edit button when file is editable', () => {
+ props.addMergeRequestButtons = true;
+ props.diffFile.editPath = '/';
+ vm = mountComponent(Component, props);
+
+ expect(vm.$el.querySelector('.js-edit-blob')).toContainText('Edit');
+ });
+
+ it('should not show edit button when file is deleted', () => {
+ props.addMergeRequestButtons = true;
+ props.diffFile.deletedFile = true;
+ props.diffFile.editPath = '/';
+ vm = mountComponent(Component, props);
+
+ expect(vm.$el.querySelector('.js-edit-blob')).toEqual(null);
+ });
+ });
+
+ describe('addMergeRequestButtons', () => {
+ beforeEach(() => {
+ props.addMergeRequestButtons = true;
+ props.diffFile.editPath = '';
+ });
+
+ describe('view on environment button', () => {
+ const url = 'some.external.url/';
+ const title = 'url.title';
+
+ it('displays link to external url', () => {
+ props.diffFile.externalUrl = url;
+ props.diffFile.formattedExternalUrl = title;
+
+ vm = mountComponent(Component, props);
+
+ expect(vm.$el.querySelector(`a[href="${url}"]`)).not.toBe(null);
+ expect(vm.$el.querySelector(`a[data-original-title="View on ${title}"]`)).not.toBe(null);
+ });
+
+ it('hides link if no external url', () => {
+ props.diffFile.externalUrl = '';
+ props.diffFile.formattedExternalUrl = title;
+
+ vm = mountComponent(Component, props);
+
+ expect(vm.$el.querySelector(`a[data-original-title="View on ${title}"]`)).toBe(null);
+ });
+ });
+ });
+ });
+});
diff --git a/spec/javascripts/diffs/components/diff_file_spec.js b/spec/javascripts/diffs/components/diff_file_spec.js
new file mode 100644
index 00000000000..1c1edfac68c
--- /dev/null
+++ b/spec/javascripts/diffs/components/diff_file_spec.js
@@ -0,0 +1,88 @@
+import Vue from 'vue';
+import DiffFileComponent from '~/diffs/components/diff_file.vue';
+import store from '~/mr_notes/stores';
+import { createComponentWithStore } from 'spec/helpers/vue_mount_component_helper';
+import diffFileMockData from '../mock_data/diff_file';
+
+describe('DiffFile', () => {
+ let vm;
+ const getDiffFileMock = () => Object.assign({}, diffFileMockData);
+
+ beforeEach(() => {
+ vm = createComponentWithStore(Vue.extend(DiffFileComponent), store, {
+ file: getDiffFileMock(),
+ currentUser: {},
+ }).$mount();
+ });
+
+ describe('template', () => {
+ it('should render component with file header, file content components', () => {
+ const el = vm.$el;
+ const { fileHash, filePath } = diffFileMockData;
+
+ expect(el.id).toEqual(fileHash);
+ expect(el.classList.contains('diff-file')).toEqual(true);
+ expect(el.querySelectorAll('.diff-content.hidden').length).toEqual(0);
+ expect(el.querySelector('.js-file-title')).toBeDefined();
+ expect(el.querySelector('.file-title-name').innerText.indexOf(filePath) > -1).toEqual(true);
+ expect(el.querySelector('.js-syntax-highlight')).toBeDefined();
+ expect(el.querySelectorAll('.line_content').length > 5).toEqual(true);
+ });
+
+ describe('collapsed', () => {
+ it('should not have file content', done => {
+ expect(vm.$el.querySelectorAll('.diff-content.hidden').length).toEqual(0);
+ expect(vm.file.collapsed).toEqual(false);
+ vm.file.collapsed = true;
+
+ vm.$nextTick(() => {
+ expect(vm.$el.querySelectorAll('.diff-content.hidden').length).toEqual(1);
+
+ done();
+ });
+ });
+
+ it('should have collapsed text and link', done => {
+ vm.file.collapsed = true;
+
+ vm.$nextTick(() => {
+ expect(vm.$el.innerText).toContain('This diff is collapsed');
+ expect(vm.$el.querySelectorAll('.js-click-to-expand').length).toEqual(1);
+
+ done();
+ });
+ });
+
+ it('should have loading icon while loading a collapsed diffs', done => {
+ vm.file.collapsed = true;
+ vm.isLoadingCollapsedDiff = true;
+
+ vm.$nextTick(() => {
+ expect(vm.$el.querySelectorAll('.diff-content.loading').length).toEqual(1);
+
+ done();
+ });
+ });
+ });
+ });
+
+ describe('too large diff', () => {
+ it('should have too large warning and blob link', done => {
+ const BLOB_LINK = '/file/view/path';
+ vm.file.tooLarge = true;
+ vm.file.viewPath = BLOB_LINK;
+
+ vm.$nextTick(() => {
+ expect(vm.$el.innerText).toContain(
+ 'This source diff could not be displayed because it is too large',
+ );
+ expect(vm.$el.querySelector('.js-too-large-diff')).toBeDefined();
+ expect(vm.$el.querySelector('.js-too-large-diff a').href.indexOf(BLOB_LINK) > -1).toEqual(
+ true,
+ );
+
+ done();
+ });
+ });
+ });
+});
diff --git a/spec/javascripts/diffs/components/diff_gutter_avatars_spec.js b/spec/javascripts/diffs/components/diff_gutter_avatars_spec.js
new file mode 100644
index 00000000000..0085a16815a
--- /dev/null
+++ b/spec/javascripts/diffs/components/diff_gutter_avatars_spec.js
@@ -0,0 +1,115 @@
+import Vue from 'vue';
+import DiffGutterAvatarsComponent from '~/diffs/components/diff_gutter_avatars.vue';
+import { COUNT_OF_AVATARS_IN_GUTTER } from '~/diffs/constants';
+import store from '~/mr_notes/stores';
+import { createComponentWithStore } from 'spec/helpers/vue_mount_component_helper';
+import discussionsMockData from '../mock_data/diff_discussions';
+
+describe('DiffGutterAvatars', () => {
+ let component;
+ const getDiscussionsMockData = () => [Object.assign({}, discussionsMockData)];
+
+ beforeEach(() => {
+ component = createComponentWithStore(Vue.extend(DiffGutterAvatarsComponent), store, {
+ discussions: getDiscussionsMockData(),
+ }).$mount();
+ });
+
+ describe('computed', () => {
+ describe('discussionsExpanded', () => {
+ it('should return true when all discussions are expanded', () => {
+ expect(component.discussionsExpanded).toEqual(true);
+ });
+
+ it('should return false when all discussions are not expanded', () => {
+ component.discussions[0].expanded = false;
+ expect(component.discussionsExpanded).toEqual(false);
+ });
+ });
+
+ describe('allDiscussions', () => {
+ it('should return an array of notes', () => {
+ expect(component.allDiscussions).toEqual([...component.discussions[0].notes]);
+ });
+ });
+
+ describe('notesInGutter', () => {
+ it('should return a subset of discussions to show in gutter', () => {
+ expect(component.notesInGutter.length).toEqual(COUNT_OF_AVATARS_IN_GUTTER);
+ expect(component.notesInGutter[0]).toEqual({
+ note: component.discussions[0].notes[0].note,
+ author: component.discussions[0].notes[0].author,
+ });
+ });
+ });
+
+ describe('moreCount', () => {
+ it('should return count of remaining discussions from gutter', () => {
+ expect(component.moreCount).toEqual(2);
+ });
+ });
+
+ describe('moreText', () => {
+ it('should return proper text if moreCount > 0', () => {
+ expect(component.moreText).toEqual('2 more comments');
+ });
+
+ it('should return empty string if there is no discussion', () => {
+ component.discussions = [];
+ expect(component.moreText).toEqual('');
+ });
+ });
+ });
+
+ describe('methods', () => {
+ describe('getTooltipText', () => {
+ it('should return original comment if it is shorter than max length', () => {
+ const note = component.discussions[0].notes[0];
+
+ expect(component.getTooltipText(note)).toEqual('Administrator: comment 1');
+ });
+
+ it('should return truncated version of comment', () => {
+ const note = component.discussions[0].notes[1];
+
+ expect(component.getTooltipText(note)).toEqual('Fatih Acet: comment 2 is r...');
+ });
+ });
+
+ describe('toggleDiscussions', () => {
+ it('should toggle all discussions', () => {
+ expect(component.discussions[0].expanded).toEqual(true);
+
+ component.$store.dispatch('setInitialNotes', getDiscussionsMockData());
+ component.discussions = component.$store.state.notes.discussions;
+ component.toggleDiscussions();
+
+ expect(component.discussions[0].expanded).toEqual(false);
+ component.$store.dispatch('setInitialNotes', []);
+ });
+ });
+ });
+
+ describe('template', () => {
+ const buttonSelector = '.js-diff-comment-button';
+ const svgSelector = `${buttonSelector} svg`;
+ const avatarSelector = '.js-diff-comment-avatar';
+ const plusCountSelector = '.js-diff-comment-plus';
+
+ it('should have button to collapse discussions when the discussions expanded', () => {
+ expect(component.$el.querySelector(buttonSelector)).toBeDefined();
+ expect(component.$el.querySelector(svgSelector)).toBeDefined();
+ });
+
+ it('should have user avatars when discussions collapsed', () => {
+ component.discussions[0].expanded = false;
+
+ Vue.nextTick(() => {
+ expect(component.$el.querySelector(buttonSelector)).toBeNull();
+ expect(component.$el.querySelectorAll(avatarSelector).length).toEqual(4);
+ expect(component.$el.querySelector(plusCountSelector)).toBeDefined();
+ expect(component.$el.querySelector(plusCountSelector).textContent).toEqual('+2');
+ });
+ });
+ });
+});
diff --git a/spec/javascripts/diffs/components/diff_line_gutter_content_spec.js b/spec/javascripts/diffs/components/diff_line_gutter_content_spec.js
new file mode 100644
index 00000000000..312a684f4d2
--- /dev/null
+++ b/spec/javascripts/diffs/components/diff_line_gutter_content_spec.js
@@ -0,0 +1,153 @@
+import Vue from 'vue';
+import DiffLineGutterContent from '~/diffs/components/diff_line_gutter_content.vue';
+import store from '~/mr_notes/stores';
+import { createComponentWithStore } from 'spec/helpers/vue_mount_component_helper';
+import {
+ MATCH_LINE_TYPE,
+ CONTEXT_LINE_TYPE,
+ OLD_NO_NEW_LINE_TYPE,
+ NEW_NO_NEW_LINE_TYPE,
+} from '~/diffs/constants';
+import discussionsMockData from '../mock_data/diff_discussions';
+import diffFileMockData from '../mock_data/diff_file';
+
+describe('DiffLineGutterContent', () => {
+ const getDiscussionsMockData = () => [Object.assign({}, discussionsMockData)];
+ const getDiffFileMock = () => Object.assign({}, diffFileMockData);
+ const createComponent = (options = {}) => {
+ const cmp = Vue.extend(DiffLineGutterContent);
+ const props = Object.assign({}, options);
+ props.fileHash = getDiffFileMock().fileHash;
+ props.contextLinesPath = '/context/lines/path';
+
+ return createComponentWithStore(cmp, store, props).$mount();
+ };
+ const setDiscussions = component => {
+ component.$store.dispatch('setInitialNotes', getDiscussionsMockData());
+ };
+
+ const resetDiscussions = component => {
+ component.$store.dispatch('setInitialNotes', []);
+ };
+
+ describe('computed', () => {
+ describe('isMatchLine', () => {
+ it('should return true for match line type', () => {
+ const component = createComponent({ lineType: MATCH_LINE_TYPE });
+ expect(component.isMatchLine).toEqual(true);
+ });
+
+ it('should return false for non-match line type', () => {
+ const component = createComponent({ lineType: CONTEXT_LINE_TYPE });
+ expect(component.isMatchLine).toEqual(false);
+ });
+ });
+
+ describe('isContextLine', () => {
+ it('should return true for context line type', () => {
+ const component = createComponent({ lineType: CONTEXT_LINE_TYPE });
+ expect(component.isContextLine).toEqual(true);
+ });
+
+ it('should return false for non-context line type', () => {
+ const component = createComponent({ lineType: MATCH_LINE_TYPE });
+ expect(component.isContextLine).toEqual(false);
+ });
+ });
+
+ describe('isMetaLine', () => {
+ it('should return true for meta line type', () => {
+ const component = createComponent({ lineType: NEW_NO_NEW_LINE_TYPE });
+ expect(component.isMetaLine).toEqual(true);
+
+ const component2 = createComponent({ lineType: OLD_NO_NEW_LINE_TYPE });
+ expect(component2.isMetaLine).toEqual(true);
+ });
+
+ it('should return false for non-meta line type', () => {
+ const component = createComponent({ lineType: MATCH_LINE_TYPE });
+ expect(component.isMetaLine).toEqual(false);
+ });
+ });
+
+ describe('lineHref', () => {
+ it('should prepend # to lineCode', () => {
+ const lineCode = 'LC_42';
+ const component = createComponent({ lineCode });
+ expect(component.lineHref).toEqual(`#${lineCode}`);
+ });
+
+ it('should return # if there is no lineCode', () => {
+ const component = createComponent({ lineCode: null });
+ expect(component.lineHref).toEqual('#');
+ });
+ });
+
+ describe('discussions, hasDiscussions, shouldShowAvatarsOnGutter', () => {
+ it('should return empty array when there is no discussion', () => {
+ const component = createComponent({ lineCode: 'LC_42' });
+ expect(component.discussions).toEqual([]);
+ expect(component.hasDiscussions).toEqual(false);
+ expect(component.shouldShowAvatarsOnGutter).toEqual(false);
+ });
+
+ it('should return discussions for the given lineCode', () => {
+ const lineCode = getDiffFileMock().highlightedDiffLines[1].lineCode;
+ const component = createComponent({ lineCode, showCommentButton: true });
+
+ setDiscussions(component);
+
+ expect(component.discussions).toEqual(getDiscussionsMockData());
+ expect(component.hasDiscussions).toEqual(true);
+ expect(component.shouldShowAvatarsOnGutter).toEqual(true);
+
+ resetDiscussions(component);
+ });
+ });
+ });
+
+ describe('template', () => {
+ it('should render three dots for context lines', () => {
+ const component = createComponent({
+ lineType: MATCH_LINE_TYPE,
+ });
+
+ expect(component.$el.querySelector('span').classList.contains('context-cell')).toEqual(true);
+ expect(component.$el.innerText).toEqual('...');
+ });
+
+ it('should render comment button', () => {
+ const component = createComponent({
+ showCommentButton: true,
+ });
+ Object.defineProperty(component, 'isLoggedIn', {
+ get() {
+ return true;
+ },
+ });
+
+ expect(component.$el.querySelector('.js-add-diff-note-button')).toBeDefined();
+ });
+
+ it('should render line link', () => {
+ const lineNumber = 42;
+ const lineCode = `LC_${lineNumber}`;
+ const component = createComponent({ lineNumber, lineCode });
+ const link = component.$el.querySelector('a');
+
+ expect(link.href.indexOf(`#${lineCode}`) > -1).toEqual(true);
+ expect(link.dataset.linenumber).toEqual(lineNumber.toString());
+ });
+
+ it('should render user avatars', () => {
+ const component = createComponent({
+ showCommentButton: true,
+ lineCode: getDiffFileMock().highlightedDiffLines[1].lineCode,
+ });
+
+ setDiscussions(component);
+ expect(component.$el.querySelector('.diff-comment-avatar-holders')).toBeDefined();
+ resetDiscussions(component);
+ });
+ });
+});
diff --git a/spec/javascripts/diffs/components/diff_line_note_form_spec.js b/spec/javascripts/diffs/components/diff_line_note_form_spec.js
new file mode 100644
index 00000000000..724d1948214
--- /dev/null
+++ b/spec/javascripts/diffs/components/diff_line_note_form_spec.js
@@ -0,0 +1,68 @@
+import Vue from 'vue';
+import DiffLineNoteForm from '~/diffs/components/diff_line_note_form.vue';
+import store from '~/mr_notes/stores';
+import { createComponentWithStore } from 'spec/helpers/vue_mount_component_helper';
+import diffFileMockData from '../mock_data/diff_file';
+
+describe('DiffLineNoteForm', () => {
+ let component;
+ let diffFile;
+ let diffLines;
+ const getDiffFileMock = () => Object.assign({}, diffFileMockData);
+
+ beforeEach(() => {
+ diffFile = getDiffFileMock();
+ diffLines = diffFile.highlightedDiffLines;
+
+ component = createComponentWithStore(Vue.extend(DiffLineNoteForm), store, {
+ diffFile,
+ diffLines,
+ line: diffLines[0],
+ noteTargetLine: diffLines[0],
+ }).$mount();
+ });
+
+ describe('methods', () => {
+ describe('handleCancelCommentForm', () => {
+ it('should call cancelCommentForm with lineCode', () => {
+ spyOn(component, 'cancelCommentForm');
+ component.handleCancelCommentForm();
+
+ expect(component.cancelCommentForm).toHaveBeenCalledWith({
+ lineCode: diffLines[0].lineCode,
+ });
+ });
+ });
+
+ describe('saveNoteForm', () => {
+ it('should call saveNote action with proper params', done => {
+ let isPromiseCalled = false;
+ const formDataSpy = spyOnDependency(DiffLineNoteForm, 'getNoteFormData').and.returnValue({
+ postData: 1,
+ });
+ const saveNoteSpy = spyOn(component, 'saveNote').and.returnValue(
+ new Promise(() => {
+ isPromiseCalled = true;
+ done();
+ }),
+ );
+
+ component.handleSaveNote('note body');
+
+ expect(formDataSpy).toHaveBeenCalled();
+ expect(saveNoteSpy).toHaveBeenCalled();
+ expect(isPromiseCalled).toEqual(true);
+ });
+ });
+ });
+
+ describe('template', () => {
+ it('should have note form', () => {
+ const { $el } = component;
+
+ expect($el.querySelector('.js-vue-textarea')).toBeDefined();
+ expect($el.querySelector('.js-vue-issue-save')).toBeDefined();
+ expect($el.querySelector('.js-vue-markdown-field')).toBeDefined();
+ });
+ });
+});
diff --git a/spec/javascripts/diffs/components/edit_button_spec.js b/spec/javascripts/diffs/components/edit_button_spec.js
new file mode 100644
index 00000000000..7237274eb43
--- /dev/null
+++ b/spec/javascripts/diffs/components/edit_button_spec.js
@@ -0,0 +1 @@
+// TODO: https://gitlab.com/gitlab-org/gitlab-ce/issues/48034
diff --git a/spec/javascripts/diffs/components/hidden_files_warning_spec.js b/spec/javascripts/diffs/components/hidden_files_warning_spec.js
new file mode 100644
index 00000000000..7237274eb43
--- /dev/null
+++ b/spec/javascripts/diffs/components/hidden_files_warning_spec.js
@@ -0,0 +1 @@
+// TODO: https://gitlab.com/gitlab-org/gitlab-ce/issues/48034
diff --git a/spec/javascripts/diffs/components/inline_diff_view_spec.js b/spec/javascripts/diffs/components/inline_diff_view_spec.js
new file mode 100644
index 00000000000..0d5a3576204
--- /dev/null
+++ b/spec/javascripts/diffs/components/inline_diff_view_spec.js
@@ -0,0 +1,111 @@
+import Vue from 'vue';
+import InlineDiffView from '~/diffs/components/inline_diff_view.vue';
+import store from '~/mr_notes/stores';
+import * as constants from '~/diffs/constants';
+import { createComponentWithStore } from 'spec/helpers/vue_mount_component_helper';
+import diffFileMockData from '../mock_data/diff_file';
+import discussionsMockData from '../mock_data/diff_discussions';
+
+describe('InlineDiffView', () => {
+ let component;
+ const getDiffFileMock = () => Object.assign({}, diffFileMockData);
+ const getDiscussionsMockData = () => [Object.assign({}, discussionsMockData)];
+
+ beforeEach(() => {
+ const diffFile = getDiffFileMock();
+
+ component = createComponentWithStore(Vue.extend(InlineDiffView), store, {
+ diffFile,
+ diffLines: diffFile.highlightedDiffLines,
+ }).$mount();
+ });
+
+ describe('methods', () => {
+ describe('handleMouse', () => {
+ it('should set hoveredLineCode', () => {
+ expect(component.hoveredLineCode).toEqual(null);
+
+ component.handleMouse('lineCode1', true);
+ expect(component.hoveredLineCode).toEqual('lineCode1');
+
+ component.handleMouse('lineCode1', false);
+ expect(component.hoveredLineCode).toEqual(null);
+ });
+ });
+
+ describe('getLineClass', () => {
+ it('should return line class object', () => {
+ const { LINE_HOVER_CLASS_NAME, LINE_UNFOLD_CLASS_NAME } = constants;
+ const { MATCH_LINE_TYPE, NEW_LINE_TYPE } = constants;
+
+ expect(component.getLineClass(component.diffLines[0])).toEqual({
+ [NEW_LINE_TYPE]: NEW_LINE_TYPE,
+ [LINE_UNFOLD_CLASS_NAME]: false,
+ [LINE_HOVER_CLASS_NAME]: false,
+ });
+
+ component.handleMouse(component.diffLines[0].lineCode, true);
+ Object.defineProperty(component, 'isLoggedIn', {
+ get() {
+ return true;
+ },
+ });
+
+ expect(component.getLineClass(component.diffLines[0])).toEqual({
+ [NEW_LINE_TYPE]: NEW_LINE_TYPE,
+ [LINE_UNFOLD_CLASS_NAME]: false,
+ [LINE_HOVER_CLASS_NAME]: true,
+ });
+
+ expect(component.getLineClass(component.diffLines[5])).toEqual({
+ [MATCH_LINE_TYPE]: MATCH_LINE_TYPE,
+ [LINE_UNFOLD_CLASS_NAME]: true,
+ [LINE_HOVER_CLASS_NAME]: false,
+ });
+ });
+ });
+ });
+
+ describe('template', () => {
+ it('should have rendered diff lines', () => {
+ const el = component.$el;
+
+ expect(el.querySelectorAll('tr.line_holder').length).toEqual(6);
+ expect(el.querySelectorAll('tr.line_holder.new').length).toEqual(2);
+ expect(el.querySelectorAll('tr.line_holder.match').length).toEqual(1);
+ expect(el.textContent.indexOf('Bad dates') > -1).toEqual(true);
+ });
+
+ it('should render discussions', done => {
+ const el = component.$el;
+ component.$store.dispatch('setInitialNotes', getDiscussionsMockData());
+
+ Vue.nextTick(() => {
+ expect(el.querySelectorAll('.notes_holder').length).toEqual(1);
+ expect(el.querySelectorAll('.notes_holder .note-discussion li').length).toEqual(5);
+ expect(el.innerText.indexOf('comment 5') > -1).toEqual(true);
+ component.$store.dispatch('setInitialNotes', []);
+
+ done();
+ });
+ });
+
+ it('should render new discussion forms', done => {
+ const el = component.$el;
+ const lines = getDiffFileMock().highlightedDiffLines;
+
+ component.handleShowCommentForm({ lineCode: lines[0].lineCode });
+ component.handleShowCommentForm({ lineCode: lines[1].lineCode });
+
+ Vue.nextTick(() => {
+ expect(el.querySelectorAll('.js-vue-markdown-field').length).toEqual(2);
+ expect(el.querySelectorAll('tr')[1].classList.contains('notes_holder')).toEqual(true);
+ expect(el.querySelectorAll('tr')[3].classList.contains('notes_holder')).toEqual(true);
+
+ store.state.diffs.diffLineCommentForms = {};
+
+ done();
+ });
+ });
+ });
+});
diff --git a/spec/javascripts/diffs/components/no_changes_spec.js b/spec/javascripts/diffs/components/no_changes_spec.js
new file mode 100644
index 00000000000..7237274eb43
--- /dev/null
+++ b/spec/javascripts/diffs/components/no_changes_spec.js
@@ -0,0 +1 @@
+// TODO: https://gitlab.com/gitlab-org/gitlab-ce/issues/48034
diff --git a/spec/javascripts/diffs/components/parallel_diff_view_spec.js b/spec/javascripts/diffs/components/parallel_diff_view_spec.js
new file mode 100644
index 00000000000..cab533217c0
--- /dev/null
+++ b/spec/javascripts/diffs/components/parallel_diff_view_spec.js
@@ -0,0 +1,224 @@
+import Vue from 'vue';
+import ParallelDiffView from '~/diffs/components/parallel_diff_view.vue';
+import store from '~/mr_notes/stores';
+import * as constants from '~/diffs/constants';
+import { createComponentWithStore } from 'spec/helpers/vue_mount_component_helper';
+import diffFileMockData from '../mock_data/diff_file';
+import discussionsMockData from '../mock_data/diff_discussions';
+
+describe('ParallelDiffView', () => {
+ let component;
+ const getDiffFileMock = () => Object.assign({}, diffFileMockData);
+ const getDiscussionsMockData = () => [Object.assign({}, discussionsMockData)];
+
+ beforeEach(() => {
+ const diffFile = getDiffFileMock();
+
+ component = createComponentWithStore(Vue.extend(ParallelDiffView), store, {
+ diffFile,
+ diffLines: diffFile.parallelDiffLines,
+ }).$mount();
+ });
+
+ describe('computed', () => {
+ describe('parallelDiffLines', () => {
+ it('should normalize lines for empty cells', () => {
+ expect(component.parallelDiffLines[0].left.type).toEqual(constants.EMPTY_CELL_TYPE);
+ expect(component.parallelDiffLines[1].left.type).toEqual(constants.EMPTY_CELL_TYPE);
+ });
+ });
+ });
+
+ describe('methods', () => {
+ describe('hasDiscussion', () => {
+ it('it should return true if there is a discussion either for left or right section', () => {
+ Object.defineProperty(component, 'discussionsByLineCode', {
+ get() {
+ return { line_42: true };
+ },
+ });
+
+ expect(component.hasDiscussion({ left: {}, right: {} })).toEqual(undefined);
+ expect(component.hasDiscussion({ left: { lineCode: 'line_42' }, right: {} })).toEqual(true);
+ expect(component.hasDiscussion({ left: {}, right: { lineCode: 'line_42' } })).toEqual(true);
+ });
+ });
+
+ describe('getClassName', () => {
+ it('should return line class object', () => {
+ const { LINE_HOVER_CLASS_NAME, LINE_UNFOLD_CLASS_NAME } = constants;
+ const { MATCH_LINE_TYPE, NEW_LINE_TYPE, LINE_POSITION_RIGHT } = constants;
+
+ expect(component.getClassName(component.diffLines[1], LINE_POSITION_RIGHT)).toEqual({
+ [NEW_LINE_TYPE]: NEW_LINE_TYPE,
+ [LINE_UNFOLD_CLASS_NAME]: false,
+ [LINE_HOVER_CLASS_NAME]: false,
+ });
+
+ const eventMock = {
+ target: component.$refs.rightLines[1],
+ };
+
+ component.handleMouse(eventMock, component.diffLines[1], true);
+ Object.defineProperty(component, 'isLoggedIn', {
+ get() {
+ return true;
+ },
+ });
+
+ expect(component.getClassName(component.diffLines[1], LINE_POSITION_RIGHT)).toEqual({
+ [NEW_LINE_TYPE]: NEW_LINE_TYPE,
+ [LINE_UNFOLD_CLASS_NAME]: false,
+ [LINE_HOVER_CLASS_NAME]: true,
+ });
+
+ expect(component.getClassName(component.diffLines[5], LINE_POSITION_RIGHT)).toEqual({
+ [MATCH_LINE_TYPE]: MATCH_LINE_TYPE,
+ [LINE_UNFOLD_CLASS_NAME]: true,
+ [LINE_HOVER_CLASS_NAME]: false,
+ });
+ });
+ });
+
+ describe('handleMouse', () => {
+ it('should set hovered line code and line section to null when isHover is false', () => {
+ const rightLineEventMock = { target: component.$refs.rightLines[1] };
+ expect(component.hoveredLineCode).toEqual(null);
+ expect(component.hoveredSection).toEqual(null);
+
+ component.handleMouse(rightLineEventMock, null, false);
+ expect(component.hoveredLineCode).toEqual(null);
+ expect(component.hoveredSection).toEqual(null);
+ });
+
+ it('should set hovered line code and line section for right section', () => {
+ const rightLineEventMock = { target: component.$refs.rightLines[1] };
+ component.handleMouse(rightLineEventMock, component.diffLines[1], true);
+ expect(component.hoveredLineCode).toEqual(component.diffLines[1].right.lineCode);
+ expect(component.hoveredSection).toEqual(constants.LINE_POSITION_RIGHT);
+ });
+
+ it('should set hovered line code and line section for left section', () => {
+ const leftLineEventMock = { target: component.$refs.leftLines[2] };
+ component.handleMouse(leftLineEventMock, component.diffLines[2], true);
+ expect(component.hoveredLineCode).toEqual(component.diffLines[2].left.lineCode);
+ expect(component.hoveredSection).toEqual(constants.LINE_POSITION_LEFT);
+ });
+ });
+
+ describe('shouldRenderDiscussions', () => {
+ it('should return true if there is a discussion on left side and it is expanded', () => {
+ const line = { left: { lineCode: 'lineCode1' } };
+ spyOn(component, 'isDiscussionExpanded').and.returnValue(true);
+ Object.defineProperty(component, 'discussionsByLineCode', {
+ get() {
+ return {
+ [line.left.lineCode]: true,
+ };
+ },
+ });
+
+ expect(component.shouldRenderDiscussions(line, constants.LINE_POSITION_LEFT)).toEqual(true);
+ expect(component.isDiscussionExpanded).toHaveBeenCalledWith(line.left.lineCode);
+ });
+
+ it('should return false if there is a discussion on left side but it is collapsed', () => {
+ const line = { left: { lineCode: 'lineCode1' } };
+ spyOn(component, 'isDiscussionExpanded').and.returnValue(false);
+ Object.defineProperty(component, 'discussionsByLineCode', {
+ get() {
+ return {
+ [line.left.lineCode]: true,
+ };
+ },
+ });
+
+ expect(component.shouldRenderDiscussions(line, constants.LINE_POSITION_LEFT)).toEqual(
+ false,
+ );
+ });
+
+ it('should return false for discussions on the right side if there is no line type', () => {
+ const CUSTOM_RIGHT_LINE_TYPE = 'CUSTOM_RIGHT_LINE_TYPE';
+ const line = { right: { lineCode: 'lineCode1', type: CUSTOM_RIGHT_LINE_TYPE } };
+ spyOn(component, 'isDiscussionExpanded').and.returnValue(true);
+ Object.defineProperty(component, 'discussionsByLineCode', {
+ get() {
+ return {
+ [line.right.lineCode]: true,
+ };
+ },
+ });
+
+ expect(component.shouldRenderDiscussions(line, constants.LINE_POSITION_RIGHT)).toEqual(
+ CUSTOM_RIGHT_LINE_TYPE,
+ );
+ });
+ });
+
+ describe('hasAnyExpandedDiscussion', () => {
+ const LINE_CODE_LEFT = 'LINE_CODE_LEFT';
+ const LINE_CODE_RIGHT = 'LINE_CODE_RIGHT';
+
+ it('should return true if there is a discussion either on the left or the right side', () => {
+ const mockLineOne = {
+ right: { lineCode: LINE_CODE_RIGHT },
+ left: {},
+ };
+ const mockLineTwo = {
+ left: { lineCode: LINE_CODE_LEFT },
+ right: {},
+ };
+
+ spyOn(component, 'isDiscussionExpanded').and.callFake(lc => lc === LINE_CODE_RIGHT);
+ expect(component.hasAnyExpandedDiscussion(mockLineOne)).toEqual(true);
+ expect(component.hasAnyExpandedDiscussion(mockLineTwo)).toEqual(false);
+ });
+ });
+ });
+
+ describe('template', () => {
+ it('should have rendered diff lines', () => {
+ const el = component.$el;
+
+ expect(el.querySelectorAll('tr.line_holder.parallel').length).toEqual(6);
+ expect(el.querySelectorAll('td.empty-cell').length).toEqual(4);
+ expect(el.querySelectorAll('td.line_content.parallel.right-side').length).toEqual(6);
+ expect(el.querySelectorAll('td.line_content.parallel.left-side').length).toEqual(6);
+ expect(el.querySelectorAll('td.match').length).toEqual(4);
+ expect(el.textContent.indexOf('Bad dates') > -1).toEqual(true);
+ });
+
+ it('should render discussions', done => {
+ const el = component.$el;
+ component.$store.dispatch('setInitialNotes', getDiscussionsMockData());
+
+ Vue.nextTick(() => {
+ expect(el.querySelectorAll('.notes_holder').length).toEqual(1);
+ expect(el.querySelectorAll('.notes_holder .note-discussion li').length).toEqual(5);
+ expect(el.innerText.indexOf('comment 5') > -1).toEqual(true);
+ component.$store.dispatch('setInitialNotes', []);
+
+ done();
+ });
+ });
+
+ it('should render new discussion forms', done => {
+ const el = component.$el;
+ const lines = getDiffFileMock().parallelDiffLines;
+
+ component.handleShowCommentForm({ lineCode: lines[0].lineCode });
+ component.handleShowCommentForm({ lineCode: lines[1].lineCode });
+
+ Vue.nextTick(() => {
+ expect(el.querySelectorAll('.js-vue-markdown-field').length).toEqual(2);
+ expect(el.querySelectorAll('tr')[1].classList.contains('notes_holder')).toEqual(true);
+ expect(el.querySelectorAll('tr')[3].classList.contains('notes_holder')).toEqual(true);
+
+ store.state.diffs.diffLineCommentForms = {};
+
+ done();
+ });
+ });
+ });
+});