summaryrefslogtreecommitdiff
path: root/spec/javascripts
diff options
context:
space:
mode:
authorKamil Trzciński <ayufan@ayufan.eu>2017-12-06 09:39:13 +0000
committerKamil Trzciński <ayufan@ayufan.eu>2017-12-06 09:39:13 +0000
commit3b9102b200d25bf1164b15675db65db567d26b7b (patch)
tree445aaef220b399a3d88204fec130cbd270334990 /spec/javascripts
parentcc0c61155561e5baa1b28319ceb6633858a7d9f8 (diff)
parent1bc1c2d0b2cc5c4c1de5ebdf4229edf6d13d6636 (diff)
downloadgitlab-ce-list-multiple-clusters.tar.gz
Merge branch 'multiple-clusters-single-list' into 'list-multiple-clusters'list-multiple-clusters
Use single list for multiple clusters See merge request gitlab-org/gitlab-ce!15669
Diffstat (limited to 'spec/javascripts')
-rw-r--r--spec/javascripts/behaviors/autosize_spec.js31
-rw-r--r--spec/javascripts/behaviors/requires_input_spec.js76
-rw-r--r--spec/javascripts/boards/board_card_spec.js29
-rw-r--r--spec/javascripts/boards/issue_spec.js19
-rw-r--r--spec/javascripts/clusters/clusters_bundle_spec.js18
-rw-r--r--spec/javascripts/clusters/clusters_index_spec.js58
-rw-r--r--spec/javascripts/datetime_utility_spec.js22
-rw-r--r--spec/javascripts/droplab/drop_down_spec.js8
-rw-r--r--spec/javascripts/droplab/hook_spec.js2
-rw-r--r--spec/javascripts/environments/emtpy_state_spec.js57
-rw-r--r--spec/javascripts/environments/environment_table_spec.js31
-rw-r--r--spec/javascripts/environments/environments_app_spec.js (renamed from spec/javascripts/environments/environment_spec.js)132
-rw-r--r--spec/javascripts/environments/folder/environments_folder_view_spec.js157
-rw-r--r--spec/javascripts/fixtures/clusters.rb15
-rw-r--r--spec/javascripts/fixtures/environments/element.html.haml1
-rw-r--r--spec/javascripts/fixtures/environments/environments.html.haml9
-rw-r--r--spec/javascripts/fixtures/environments/environments_folder_view.html.haml7
-rw-r--r--spec/javascripts/flash_spec.js2
-rw-r--r--spec/javascripts/issuable_spec.js2
-rw-r--r--spec/javascripts/issue_show/components/app_spec.js5
-rw-r--r--spec/javascripts/issue_show/components/description_spec.js99
-rw-r--r--spec/javascripts/issue_show/components/title_spec.js8
-rw-r--r--spec/javascripts/jobs/job_details_mediator_spec.js20
-rw-r--r--spec/javascripts/lib/utils/common_utils_spec.js41
-rw-r--r--spec/javascripts/lib/utils/text_utility_spec.js8
-rw-r--r--spec/javascripts/monitoring/dashboard_spec.js12
-rw-r--r--spec/javascripts/monitoring/mock_data.js15
-rw-r--r--spec/javascripts/new_branch_spec.js3
-rw-r--r--spec/javascripts/notes/components/issue_comment_form_spec.js8
-rw-r--r--spec/javascripts/notes/components/issue_discussion_spec.js4
-rw-r--r--spec/javascripts/notes/components/issue_note_app_spec.js6
-rw-r--r--spec/javascripts/notes/components/issue_note_body_spec.js4
-rw-r--r--spec/javascripts/notes/components/issue_note_form_spec.js4
-rw-r--r--spec/javascripts/notes/components/issue_note_spec.js4
-rw-r--r--spec/javascripts/notes/components/note_actions_spec.js (renamed from spec/javascripts/notes/components/issue_note_actions_spec.js)4
-rw-r--r--spec/javascripts/notes/components/note_attachment_spec.js (renamed from spec/javascripts/notes/components/issue_note_attachment_spec.js)4
-rw-r--r--spec/javascripts/notes/components/note_awards_list_spec.js (renamed from spec/javascripts/notes/components/issue_note_awards_list_spec.js)8
-rw-r--r--spec/javascripts/notes/components/note_edited_text_spec.js (renamed from spec/javascripts/notes/components/issue_note_edited_text_spec.js)6
-rw-r--r--spec/javascripts/notes/components/note_header_spec.js (renamed from spec/javascripts/notes/components/issue_note_header_spec.js)6
-rw-r--r--spec/javascripts/notes/components/note_signed_out_widget_spec.js (renamed from spec/javascripts/notes/components/issue_note_signed_out_widget_spec.js)6
-rw-r--r--spec/javascripts/notes/mock_data.js4
-rw-r--r--spec/javascripts/notes/stores/actions_spec.js8
-rw-r--r--spec/javascripts/notes/stores/getters_spec.js10
-rw-r--r--spec/javascripts/notes/stores/mutation_spec.js10
-rw-r--r--spec/javascripts/notes_spec.js1
-rw-r--r--spec/javascripts/pipelines/graph/job_component_spec.js65
-rw-r--r--spec/javascripts/pipelines/pipelines_spec.js62
-rw-r--r--spec/javascripts/projects_dropdown/components/projects_list_item_spec.js12
-rw-r--r--spec/javascripts/repo/components/commit_sidebar/list_collapsed_spec.js33
-rw-r--r--spec/javascripts/repo/components/commit_sidebar/list_item_spec.js53
-rw-r--r--spec/javascripts/repo/components/commit_sidebar/list_spec.js72
-rw-r--r--spec/javascripts/repo/components/repo_commit_section_spec.js22
-rw-r--r--spec/javascripts/repo/components/repo_editor_spec.js10
-rw-r--r--spec/javascripts/repo/components/repo_sidebar_spec.js13
-rw-r--r--spec/javascripts/repo/components/repo_tab_spec.js12
-rw-r--r--spec/javascripts/repo/components/repo_tabs_spec.js5
-rw-r--r--spec/javascripts/repo/lib/common/disposable_spec.js44
-rw-r--r--spec/javascripts/repo/lib/common/model_manager_spec.js81
-rw-r--r--spec/javascripts/repo/lib/common/model_spec.js84
-rw-r--r--spec/javascripts/repo/lib/decorations/controller_spec.js120
-rw-r--r--spec/javascripts/repo/lib/diff/controller_spec.js176
-rw-r--r--spec/javascripts/repo/lib/diff/diff_spec.js80
-rw-r--r--spec/javascripts/repo/lib/editor_options_spec.js7
-rw-r--r--spec/javascripts/repo/lib/editor_spec.js128
-rw-r--r--spec/javascripts/repo/stores/getters_spec.js27
-rw-r--r--spec/javascripts/sidebar/mock_data.js82
-rw-r--r--spec/javascripts/sidebar/sidebar_mediator_spec.js27
-rw-r--r--spec/javascripts/sidebar/sidebar_store_spec.js6
-rw-r--r--spec/javascripts/vue_mr_widget/mock_data.js1
-rw-r--r--spec/javascripts/vue_shared/components/icon_spec.js12
-rw-r--r--spec/javascripts/vue_shared/components/issue/issue_warning_spec.js6
-rw-r--r--spec/javascripts/vue_shared/components/loading_button_spec.js1
-rw-r--r--spec/javascripts/vue_shared/components/markdown/toolbar_spec.js37
-rw-r--r--spec/javascripts/vue_shared/components/navigation_tabs_spec.js (renamed from spec/javascripts/pipelines/navigation_tabs_spec.js)14
-rw-r--r--spec/javascripts/vue_shared/components/pikaday_spec.js29
-rw-r--r--spec/javascripts/vue_shared/components/sidebar/collapsed_calendar_icon_spec.js35
-rw-r--r--spec/javascripts/vue_shared/components/sidebar/collapsed_grouped_date_picker_spec.js91
-rw-r--r--spec/javascripts/vue_shared/components/sidebar/date_picker_spec.js117
-rw-r--r--spec/javascripts/vue_shared/components/sidebar/toggle_sidebar_spec.js32
-rw-r--r--spec/javascripts/vue_shared/components/toggle_button_spec.js91
-rw-r--r--spec/javascripts/zen_mode_spec.js146
81 files changed, 2236 insertions, 581 deletions
diff --git a/spec/javascripts/behaviors/autosize_spec.js b/spec/javascripts/behaviors/autosize_spec.js
index 67afba19190..960b731892a 100644
--- a/spec/javascripts/behaviors/autosize_spec.js
+++ b/spec/javascripts/behaviors/autosize_spec.js
@@ -1,21 +1,18 @@
-/* eslint-disable space-before-function-paren, no-var, comma-dangle, no-return-assign, max-len */
-
import '~/behaviors/autosize';
-(function() {
- describe('Autosize behavior', function() {
- var load;
- beforeEach(function() {
- return setFixtures('<textarea class="js-autosize" style="resize: vertical"></textarea>');
- });
- it('does not overwrite the resize property', function() {
- load();
- return expect($('textarea')).toHaveCss({
- resize: 'vertical'
- });
+function load() {
+ $(document).trigger('load');
+}
+
+describe('Autosize behavior', () => {
+ beforeEach(() => {
+ setFixtures('<textarea class="js-autosize" style="resize: vertical"></textarea>');
+ });
+
+ it('does not overwrite the resize property', () => {
+ load();
+ expect($('textarea')).toHaveCss({
+ resize: 'vertical',
});
- return load = function() {
- return $(document).trigger('load');
- };
});
-}).call(window);
+});
diff --git a/spec/javascripts/behaviors/requires_input_spec.js b/spec/javascripts/behaviors/requires_input_spec.js
index f9fa814b801..8287c58ac5a 100644
--- a/spec/javascripts/behaviors/requires_input_spec.js
+++ b/spec/javascripts/behaviors/requires_input_spec.js
@@ -1,39 +1,43 @@
-/* eslint-disable space-before-function-paren, no-var */
-
import '~/behaviors/requires_input';
-(function() {
- describe('requiresInput', function() {
- preloadFixtures('branches/new_branch.html.raw');
- beforeEach(function() {
- loadFixtures('branches/new_branch.html.raw');
- this.submitButton = $('button[type="submit"]');
- });
- it('disables submit when any field is required', function() {
- $('.js-requires-input').requiresInput();
- return expect(this.submitButton).toBeDisabled();
- });
- it('enables submit when no field is required', function() {
- $('*[required=required]').removeAttr('required');
- $('.js-requires-input').requiresInput();
- return expect(this.submitButton).not.toBeDisabled();
- });
- it('enables submit when all required fields are pre-filled', function() {
- $('*[required=required]').remove();
- $('.js-requires-input').requiresInput();
- return expect($('.submit')).not.toBeDisabled();
- });
- it('enables submit when all required fields receive input', function() {
- $('.js-requires-input').requiresInput();
- $('#required1').val('input1').change();
- expect(this.submitButton).toBeDisabled();
- $('#optional1').val('input1').change();
- expect(this.submitButton).toBeDisabled();
- $('#required2').val('input2').change();
- $('#required3').val('input3').change();
- $('#required4').val('input4').change();
- $('#required5').val('1').change();
- return expect($('.submit')).not.toBeDisabled();
- });
+describe('requiresInput', () => {
+ let submitButton;
+ preloadFixtures('branches/new_branch.html.raw');
+
+ beforeEach(() => {
+ loadFixtures('branches/new_branch.html.raw');
+ submitButton = $('button[type="submit"]');
+ });
+
+ it('disables submit when any field is required', () => {
+ $('.js-requires-input').requiresInput();
+ expect(submitButton).toBeDisabled();
+ });
+
+ it('enables submit when no field is required', () => {
+ $('*[required=required]').removeAttr('required');
+ $('.js-requires-input').requiresInput();
+ expect(submitButton).not.toBeDisabled();
+ });
+
+ it('enables submit when all required fields are pre-filled', () => {
+ $('*[required=required]').remove();
+ $('.js-requires-input').requiresInput();
+ expect($('.submit')).not.toBeDisabled();
+ });
+
+ it('enables submit when all required fields receive input', () => {
+ $('.js-requires-input').requiresInput();
+ $('#required1').val('input1').change();
+ expect(submitButton).toBeDisabled();
+
+ $('#optional1').val('input1').change();
+ expect(submitButton).toBeDisabled();
+
+ $('#required2').val('input2').change();
+ $('#required3').val('input3').change();
+ $('#required4').val('input4').change();
+ $('#required5').val('1').change();
+ expect($('.submit')).not.toBeDisabled();
});
-}).call(window);
+});
diff --git a/spec/javascripts/boards/board_card_spec.js b/spec/javascripts/boards/board_card_spec.js
index 83b13b06dc1..8f607899b20 100644
--- a/spec/javascripts/boards/board_card_spec.js
+++ b/spec/javascripts/boards/board_card_spec.js
@@ -9,10 +9,11 @@
import Vue from 'vue';
import '~/boards/models/assignee';
+import eventHub from '~/boards/eventhub';
import '~/boards/models/list';
import '~/boards/models/label';
import '~/boards/stores/boards_store';
-import boardCard from '~/boards/components/board_card';
+import boardCard from '~/boards/components/board_card.vue';
import './mock_data';
describe('Board card', () => {
@@ -157,33 +158,35 @@ describe('Board card', () => {
});
it('sets detail issue to card issue on mouse up', () => {
+ spyOn(eventHub, '$emit');
+
triggerEvent('mousedown');
triggerEvent('mouseup');
- expect(gl.issueBoards.BoardsStore.detail.issue).toEqual(vm.issue);
+ expect(eventHub.$emit).toHaveBeenCalledWith('newDetailIssue', vm.issue);
expect(gl.issueBoards.BoardsStore.detail.list).toEqual(vm.list);
});
it('adds active class if detail issue is set', (done) => {
- triggerEvent('mousedown');
- triggerEvent('mouseup');
-
- setTimeout(() => {
- expect(vm.$el.classList.contains('is-active')).toBe(true);
- done();
- }, 0);
+ vm.detailIssue.issue = vm.issue;
+
+ Vue.nextTick()
+ .then(() => {
+ expect(vm.$el.classList.contains('is-active')).toBe(true);
+ })
+ .then(done)
+ .catch(done.fail);
});
it('resets detail issue to empty if already set', () => {
- triggerEvent('mousedown');
- triggerEvent('mouseup');
+ spyOn(eventHub, '$emit');
- expect(gl.issueBoards.BoardsStore.detail.issue).toEqual(vm.issue);
+ gl.issueBoards.BoardsStore.detail.issue = vm.issue;
triggerEvent('mousedown');
triggerEvent('mouseup');
- expect(gl.issueBoards.BoardsStore.detail.issue).toEqual({});
+ expect(eventHub.$emit).toHaveBeenCalledWith('clearDetailIssue');
});
});
});
diff --git a/spec/javascripts/boards/issue_spec.js b/spec/javascripts/boards/issue_spec.js
index 022d286d5df..10b88878c2a 100644
--- a/spec/javascripts/boards/issue_spec.js
+++ b/spec/javascripts/boards/issue_spec.js
@@ -133,6 +133,25 @@ describe('Issue model', () => {
expect(relativePositionIssue.position).toBe(1);
});
+ it('updates data', () => {
+ issue.updateData({ subscribed: true });
+ expect(issue.subscribed).toBe(true);
+ });
+
+ it('sets fetching state', () => {
+ expect(issue.isFetching.subscriptions).toBe(true);
+
+ issue.setFetchingState('subscriptions', false);
+
+ expect(issue.isFetching.subscriptions).toBe(false);
+ });
+
+ it('sets loading state', () => {
+ issue.setLoadingState('foo', true);
+
+ expect(issue.isLoading.foo).toBe(true);
+ });
+
describe('update', () => {
it('passes assignee ids when there are assignees', (done) => {
spyOn(Vue.http, 'patch').and.callFake((url, data) => {
diff --git a/spec/javascripts/clusters/clusters_bundle_spec.js b/spec/javascripts/clusters/clusters_bundle_spec.js
index 027e8001053..f5be9ea0fb2 100644
--- a/spec/javascripts/clusters/clusters_bundle_spec.js
+++ b/spec/javascripts/clusters/clusters_bundle_spec.js
@@ -28,7 +28,7 @@ describe('Clusters', () => {
expect(
cluster.toggleButton.classList,
- ).not.toContain('checked');
+ ).not.toContain('is-checked');
expect(
cluster.toggleInput.getAttribute('value'),
@@ -36,6 +36,20 @@ describe('Clusters', () => {
});
});
+ describe('showToken', () => {
+ it('should update tye field type', () => {
+ cluster.showTokenButton.click();
+ expect(
+ cluster.tokenField.getAttribute('type'),
+ ).toEqual('text');
+
+ cluster.showTokenButton.click();
+ expect(
+ cluster.tokenField.getAttribute('type'),
+ ).toEqual('password');
+ });
+ });
+
describe('checkForNewInstalls', () => {
const INITIAL_APP_MAP = {
helm: { status: null, title: 'Helm Tiller' },
@@ -113,7 +127,7 @@ describe('Clusters', () => {
});
describe('when cluster is created', () => {
- it('should show the success container', () => {
+ it('should show the success container and fresh the page', () => {
cluster.updateContainer(null, 'created');
expect(
diff --git a/spec/javascripts/clusters/clusters_index_spec.js b/spec/javascripts/clusters/clusters_index_spec.js
new file mode 100644
index 00000000000..0a8b63ed5b4
--- /dev/null
+++ b/spec/javascripts/clusters/clusters_index_spec.js
@@ -0,0 +1,58 @@
+import MockAdapter from 'axios-mock-adapter';
+import axios from '~/lib/utils/axios_utils';
+import setClusterTableToggles from '~/clusters/clusters_index';
+import { setTimeout } from 'core-js/library/web/timers';
+
+describe('Clusters table', () => {
+ preloadFixtures('clusters/index_cluster.html.raw');
+ let mock;
+
+ beforeEach(() => {
+ loadFixtures('clusters/index_cluster.html.raw');
+ mock = new MockAdapter(axios);
+ setClusterTableToggles();
+ });
+
+ describe('update cluster', () => {
+ it('renders loading state while request is made', () => {
+ const button = document.querySelector('.js-toggle-cluster-list');
+
+ button.click();
+
+ expect(button.classList).toContain('is-loading');
+ expect(button.getAttribute('disabled')).toEqual('true');
+ });
+
+ afterEach(() => {
+ mock.restore();
+ });
+
+ it('shows updated state after sucessfull request', (done) => {
+ mock.onPut().reply(200, {}, {});
+ const button = document.querySelector('.js-toggle-cluster-list');
+ button.click();
+
+ expect(button.classList).toContain('is-loading');
+
+ setTimeout(() => {
+ expect(button.classList).not.toContain('is-loading');
+ expect(button.classList).not.toContain('is-checked');
+ done();
+ }, 0);
+ });
+
+ it('shows inital state after failed request', (done) => {
+ mock.onPut().reply(500, {}, {});
+ const button = document.querySelector('.js-toggle-cluster-list');
+
+ button.click();
+ expect(button.classList).toContain('is-loading');
+
+ setTimeout(() => {
+ expect(button.classList).not.toContain('is-loading');
+ expect(button.classList).toContain('is-checked');
+ done();
+ }, 0);
+ });
+ });
+});
diff --git a/spec/javascripts/datetime_utility_spec.js b/spec/javascripts/datetime_utility_spec.js
index 3391cade541..0f7bf9ec712 100644
--- a/spec/javascripts/datetime_utility_spec.js
+++ b/spec/javascripts/datetime_utility_spec.js
@@ -1,4 +1,4 @@
-import { timeIntervalInWords } from '~/lib/utils/datetime_utility';
+import * as datetimeUtility from '~/lib/utils/datetime_utility';
(() => {
describe('Date time utils', () => {
@@ -89,10 +89,22 @@ import { timeIntervalInWords } from '~/lib/utils/datetime_utility';
describe('timeIntervalInWords', () => {
it('should return string with number of minutes and seconds', () => {
- expect(timeIntervalInWords(9.54)).toEqual('9 seconds');
- expect(timeIntervalInWords(1)).toEqual('1 second');
- expect(timeIntervalInWords(200)).toEqual('3 minutes 20 seconds');
- expect(timeIntervalInWords(6008)).toEqual('100 minutes 8 seconds');
+ expect(datetimeUtility.timeIntervalInWords(9.54)).toEqual('9 seconds');
+ expect(datetimeUtility.timeIntervalInWords(1)).toEqual('1 second');
+ expect(datetimeUtility.timeIntervalInWords(200)).toEqual('3 minutes 20 seconds');
+ expect(datetimeUtility.timeIntervalInWords(6008)).toEqual('100 minutes 8 seconds');
+ });
+ });
+
+ describe('dateInWords', () => {
+ const date = new Date('07/01/2016');
+
+ it('should return date in words', () => {
+ expect(datetimeUtility.dateInWords(date)).toEqual('July 1, 2016');
+ });
+
+ it('should return abbreviated month name', () => {
+ expect(datetimeUtility.dateInWords(date, true)).toEqual('Jul 1, 2016');
});
});
})();
diff --git a/spec/javascripts/droplab/drop_down_spec.js b/spec/javascripts/droplab/drop_down_spec.js
index 1ef494a00b8..1225fe2cb66 100644
--- a/spec/javascripts/droplab/drop_down_spec.js
+++ b/spec/javascripts/droplab/drop_down_spec.js
@@ -279,7 +279,12 @@ describe('DropDown', function () {
describe('addEvents', function () {
beforeEach(function () {
this.list = { addEventListener: () => {} };
- this.dropdown = { list: this.list, clickEvent: () => {}, eventWrapper: {} };
+ this.dropdown = {
+ list: this.list,
+ clickEvent: () => {},
+ closeDropdown: () => {},
+ eventWrapper: {},
+ };
spyOn(this.list, 'addEventListener');
@@ -288,6 +293,7 @@ describe('DropDown', function () {
it('should call .addEventListener', function () {
expect(this.list.addEventListener).toHaveBeenCalledWith('click', jasmine.any(Function));
+ expect(this.list.addEventListener).toHaveBeenCalledWith('keyup', jasmine.any(Function));
});
});
diff --git a/spec/javascripts/droplab/hook_spec.js b/spec/javascripts/droplab/hook_spec.js
index 75bf5f3d611..3d39bd0812b 100644
--- a/spec/javascripts/droplab/hook_spec.js
+++ b/spec/javascripts/droplab/hook_spec.js
@@ -24,7 +24,7 @@ describe('Hook', function () {
});
it('should call DropDown constructor', function () {
- expect(dropdownSrc.default).toHaveBeenCalledWith(this.list);
+ expect(dropdownSrc.default).toHaveBeenCalledWith(this.list, this.config);
});
it('should set .type', function () {
diff --git a/spec/javascripts/environments/emtpy_state_spec.js b/spec/javascripts/environments/emtpy_state_spec.js
new file mode 100644
index 00000000000..82de35933f5
--- /dev/null
+++ b/spec/javascripts/environments/emtpy_state_spec.js
@@ -0,0 +1,57 @@
+
+import Vue from 'vue';
+import emptyState from '~/environments/components/empty_state.vue';
+import mountComponent from '../helpers/vue_mount_component_helper';
+
+describe('environments empty state', () => {
+ let vm;
+ let Component;
+
+ beforeEach(() => {
+ Component = Vue.extend(emptyState);
+ });
+
+ afterEach(() => {
+ vm.$destroy();
+ });
+
+ describe('With permissions', () => {
+ beforeEach(() => {
+ vm = mountComponent(Component, {
+ newPath: 'foo',
+ canCreateEnvironment: true,
+ helpPath: 'bar',
+ });
+ });
+
+ it('renders empty state and new environment button', () => {
+ expect(
+ vm.$el.querySelector('.js-blank-state-title').textContent.trim(),
+ ).toEqual('You don\'t have any environments right now.');
+
+ expect(
+ vm.$el.querySelector('.js-new-environment-button').getAttribute('href'),
+ ).toEqual('foo');
+ });
+ });
+
+ describe('Without permission', () => {
+ beforeEach(() => {
+ vm = mountComponent(Component, {
+ newPath: 'foo',
+ canCreateEnvironment: false,
+ helpPath: 'bar',
+ });
+ });
+
+ it('renders empty state without new button', () => {
+ expect(
+ vm.$el.querySelector('.js-blank-state-title').textContent.trim(),
+ ).toEqual('You don\'t have any environments right now.');
+
+ expect(
+ vm.$el.querySelector('.js-new-environment-button'),
+ ).toBeNull();
+ });
+ });
+});
diff --git a/spec/javascripts/environments/environment_table_spec.js b/spec/javascripts/environments/environment_table_spec.js
index 2862971bec4..9bd42863759 100644
--- a/spec/javascripts/environments/environment_table_spec.js
+++ b/spec/javascripts/environments/environment_table_spec.js
@@ -1,10 +1,17 @@
import Vue from 'vue';
import environmentTableComp from '~/environments/components/environments_table.vue';
+import mountComponent from '../helpers/vue_mount_component_helper';
+
+describe('Environment table', () => {
+ let Component;
+ let vm;
-describe('Environment item', () => {
- preloadFixtures('static/environments/element.html.raw');
beforeEach(() => {
- loadFixtures('static/environments/element.html.raw');
+ Component = Vue.extend(environmentTableComp);
+ });
+
+ afterEach(() => {
+ vm.$destroy();
});
it('Should render a table', () => {
@@ -17,18 +24,12 @@ describe('Environment item', () => {
},
};
- const EnvironmentTable = Vue.extend(environmentTableComp);
-
- const component = new EnvironmentTable({
- el: document.querySelector('.test-dom-element'),
- propsData: {
- environments: [{ mockItem }],
- canCreateDeployment: false,
- canReadEnvironment: true,
- service: {},
- },
- }).$mount();
+ vm = mountComponent(Component, {
+ environments: [mockItem],
+ canCreateDeployment: false,
+ canReadEnvironment: true,
+ });
- expect(component.$el.getAttribute('class')).toContain('ci-table');
+ expect(vm.$el.getAttribute('class')).toContain('ci-table');
});
});
diff --git a/spec/javascripts/environments/environment_spec.js b/spec/javascripts/environments/environments_app_spec.js
index 0c8817a8148..d02adb25b4e 100644
--- a/spec/javascripts/environments/environment_spec.js
+++ b/spec/javascripts/environments/environments_app_spec.js
@@ -1,18 +1,24 @@
import Vue from 'vue';
-import '~/flash';
-import environmentsComponent from '~/environments/components/environment.vue';
+import environmentsComponent from '~/environments/components/environments_app.vue';
import { environment, folder } from './mock_data';
import { headersInterceptor } from '../helpers/vue_resource_helper';
+import mountComponent from '../helpers/vue_mount_component_helper';
describe('Environment', () => {
- preloadFixtures('static/environments/environments.html.raw');
+ const mockData = {
+ endpoint: 'environments.json',
+ canCreateEnvironment: true,
+ canCreateDeployment: true,
+ canReadEnvironment: true,
+ cssContainerClass: 'container',
+ newEnvironmentPath: 'environments/new',
+ helpPagePath: 'help',
+ };
let EnvironmentsComponent;
let component;
beforeEach(() => {
- loadFixtures('static/environments/environments.html.raw');
-
EnvironmentsComponent = Vue.extend(environmentsComponent);
});
@@ -37,9 +43,7 @@ describe('Environment', () => {
});
it('should render the empty state', (done) => {
- component = new EnvironmentsComponent({
- el: document.querySelector('#environments-list-view'),
- });
+ component = mountComponent(EnvironmentsComponent, mockData);
setTimeout(() => {
expect(
@@ -81,9 +85,7 @@ describe('Environment', () => {
beforeEach(() => {
Vue.http.interceptors.push(environmentsResponseInterceptor);
Vue.http.interceptors.push(headersInterceptor);
- component = new EnvironmentsComponent({
- el: document.querySelector('#environments-list-view'),
- });
+ component = mountComponent(EnvironmentsComponent, mockData);
});
afterEach(() => {
@@ -95,7 +97,7 @@ describe('Environment', () => {
it('should render a table with environments', (done) => {
setTimeout(() => {
- expect(component.$el.querySelectorAll('table')).toBeDefined();
+ expect(component.$el.querySelectorAll('table')).not.toBeNull();
expect(
component.$el.querySelector('.environment-name').textContent.trim(),
).toEqual(environment.name);
@@ -104,10 +106,6 @@ describe('Environment', () => {
});
describe('pagination', () => {
- afterEach(() => {
- window.history.pushState({}, null, '');
- });
-
it('should render pagination', (done) => {
setTimeout(() => {
expect(
@@ -117,46 +115,23 @@ describe('Environment', () => {
}, 0);
});
- it('should update url when no search params are present', (done) => {
- spyOn(gl.utils, 'visitUrl');
+ it('should make an API request when page is clicked', (done) => {
+ spyOn(component, 'updateContent');
setTimeout(() => {
component.$el.querySelector('.gl-pagination li:nth-child(5) a').click();
- expect(gl.utils.visitUrl).toHaveBeenCalledWith('?page=2');
+ expect(component.updateContent).toHaveBeenCalledWith({ scope: 'available', page: '2' });
done();
}, 0);
});
- it('should update url when page is already present', (done) => {
- spyOn(gl.utils, 'visitUrl');
- window.history.pushState({}, null, '?page=1');
-
- setTimeout(() => {
- component.$el.querySelector('.gl-pagination li:nth-child(5) a').click();
- expect(gl.utils.visitUrl).toHaveBeenCalledWith('?page=2');
- done();
- }, 0);
- });
-
- it('should update url when page and scope are already present', (done) => {
- spyOn(gl.utils, 'visitUrl');
- window.history.pushState({}, null, '?scope=all&page=1');
-
+ it('should make an API request when using tabs', (done) => {
setTimeout(() => {
- component.$el.querySelector('.gl-pagination li:nth-child(5) a').click();
- expect(gl.utils.visitUrl).toHaveBeenCalledWith('?scope=all&page=2');
- done();
- }, 0);
- });
+ spyOn(component, 'updateContent');
+ component.$el.querySelector('.js-environments-tab-stopped').click();
- it('should update url when page and scope are already present and page is first param', (done) => {
- spyOn(gl.utils, 'visitUrl');
- window.history.pushState({}, null, '?page=1&scope=all');
-
- setTimeout(() => {
- component.$el.querySelector('.gl-pagination li:nth-child(5) a').click();
- expect(gl.utils.visitUrl).toHaveBeenCalledWith('?page=2&scope=all');
+ expect(component.updateContent).toHaveBeenCalledWith({ scope: 'stopped', page: '1' });
done();
- }, 0);
+ });
});
});
});
@@ -180,9 +155,7 @@ describe('Environment', () => {
});
it('should render empty state', (done) => {
- component = new EnvironmentsComponent({
- el: document.querySelector('#environments-list-view'),
- });
+ component = mountComponent(EnvironmentsComponent, mockData);
setTimeout(() => {
expect(
@@ -214,9 +187,7 @@ describe('Environment', () => {
beforeEach(() => {
Vue.http.interceptors.push(environmentsResponseInterceptor);
- component = new EnvironmentsComponent({
- el: document.querySelector('#environments-list-view'),
- });
+ component = mountComponent(EnvironmentsComponent, mockData);
});
afterEach(() => {
@@ -289,4 +260,59 @@ describe('Environment', () => {
});
});
});
+
+ describe('methods', () => {
+ const environmentsEmptyResponseInterceptor = (request, next) => {
+ next(request.respondWith(JSON.stringify([]), {
+ status: 200,
+ }));
+ };
+
+ beforeEach(() => {
+ Vue.http.interceptors.push(environmentsEmptyResponseInterceptor);
+ Vue.http.interceptors.push(headersInterceptor);
+
+ component = mountComponent(EnvironmentsComponent, mockData);
+ spyOn(history, 'pushState').and.stub();
+ });
+
+ afterEach(() => {
+ Vue.http.interceptors = _.without(
+ Vue.http.interceptors, environmentsEmptyResponseInterceptor,
+ );
+ Vue.http.interceptors = _.without(Vue.http.interceptors, headersInterceptor);
+ });
+
+ describe('updateContent', () => {
+ it('should set given parameters', (done) => {
+ component.updateContent({ scope: 'stopped', page: '3' })
+ .then(() => {
+ expect(component.page).toEqual('3');
+ expect(component.scope).toEqual('stopped');
+ expect(component.requestData.scope).toEqual('stopped');
+ expect(component.requestData.page).toEqual('3');
+ done();
+ })
+ .catch(done.fail);
+ });
+ });
+
+ describe('onChangeTab', () => {
+ it('should set page to 1', () => {
+ spyOn(component, 'updateContent');
+ component.onChangeTab('stopped');
+
+ expect(component.updateContent).toHaveBeenCalledWith({ scope: 'stopped', page: '1' });
+ });
+ });
+
+ describe('onChangePage', () => {
+ it('should update page and keep scope', () => {
+ spyOn(component, 'updateContent');
+ component.onChangePage(4);
+
+ expect(component.updateContent).toHaveBeenCalledWith({ scope: component.scope, page: '4' });
+ });
+ });
+ });
});
diff --git a/spec/javascripts/environments/folder/environments_folder_view_spec.js b/spec/javascripts/environments/folder/environments_folder_view_spec.js
index 7e62d356bd2..4ea4d9d7499 100644
--- a/spec/javascripts/environments/folder/environments_folder_view_spec.js
+++ b/spec/javascripts/environments/folder/environments_folder_view_spec.js
@@ -1,25 +1,28 @@
import Vue from 'vue';
-import '~/flash';
import environmentsFolderViewComponent from '~/environments/folder/environments_folder_view.vue';
import { environmentsList } from '../mock_data';
import { headersInterceptor } from '../../helpers/vue_resource_helper';
+import mountComponent from '../../helpers/vue_mount_component_helper';
describe('Environments Folder View', () => {
- preloadFixtures('static/environments/environments_folder_view.html.raw');
- let EnvironmentsFolderViewComponent;
+ let Component;
+ let component;
+ const mockData = {
+ endpoint: 'environments.json',
+ folderName: 'review',
+ canCreateDeployment: true,
+ canReadEnvironment: true,
+ cssContainerClass: 'container',
+ };
beforeEach(() => {
- loadFixtures('static/environments/environments_folder_view.html.raw');
- EnvironmentsFolderViewComponent = Vue.extend(environmentsFolderViewComponent);
- window.history.pushState({}, null, 'environments/folders/build');
+ Component = Vue.extend(environmentsFolderViewComponent);
});
afterEach(() => {
- window.history.pushState({}, null, '/');
+ component.$destroy();
});
- let component;
-
describe('successfull request', () => {
const environmentsResponseInterceptor = (request, next) => {
next(request.respondWith(JSON.stringify({
@@ -31,10 +34,10 @@ describe('Environments Folder View', () => {
headers: {
'X-nExt-pAge': '2',
'x-page': '1',
- 'X-Per-Page': '1',
+ 'X-Per-Page': '2',
'X-Prev-Page': '',
- 'X-TOTAL': '37',
- 'X-Total-Pages': '2',
+ 'X-TOTAL': '20',
+ 'X-Total-Pages': '10',
},
}));
};
@@ -43,9 +46,7 @@ describe('Environments Folder View', () => {
Vue.http.interceptors.push(environmentsResponseInterceptor);
Vue.http.interceptors.push(headersInterceptor);
- component = new EnvironmentsFolderViewComponent({
- el: document.querySelector('#environments-folder-list-view'),
- });
+ component = mountComponent(Component, mockData);
});
afterEach(() => {
@@ -57,7 +58,7 @@ describe('Environments Folder View', () => {
it('should render a table with environments', (done) => {
setTimeout(() => {
- expect(component.$el.querySelectorAll('table')).toBeDefined();
+ expect(component.$el.querySelectorAll('table')).not.toBeNull();
expect(
component.$el.querySelector('.environment-name').textContent.trim(),
).toEqual(environmentsList[0].name);
@@ -68,11 +69,11 @@ describe('Environments Folder View', () => {
it('should render available tab with count', (done) => {
setTimeout(() => {
expect(
- component.$el.querySelector('.js-available-environments-folder-tab').textContent,
+ component.$el.querySelector('.js-environments-tab-available').textContent,
).toContain('Available');
expect(
- component.$el.querySelector('.js-available-environments-folder-tab .js-available-environments-count').textContent,
+ component.$el.querySelector('.js-environments-tab-available .badge').textContent,
).toContain('0');
done();
}, 0);
@@ -81,11 +82,11 @@ describe('Environments Folder View', () => {
it('should render stopped tab with count', (done) => {
setTimeout(() => {
expect(
- component.$el.querySelector('.js-stopped-environments-folder-tab').textContent,
+ component.$el.querySelector('.js-environments-tab-stopped').textContent,
).toContain('Stopped');
expect(
- component.$el.querySelector('.js-stopped-environments-folder-tab .js-stopped-environments-count').textContent,
+ component.$el.querySelector('.js-environments-tab-stopped .badge').textContent,
).toContain('1');
done();
}, 0);
@@ -94,8 +95,8 @@ describe('Environments Folder View', () => {
it('should render parent folder name', (done) => {
setTimeout(() => {
expect(
- component.$el.querySelector('.js-folder-name').textContent,
- ).toContain('Environments / build');
+ component.$el.querySelector('.js-folder-name').textContent.trim(),
+ ).toContain('Environments / review');
done();
}, 0);
});
@@ -104,52 +105,30 @@ describe('Environments Folder View', () => {
it('should render pagination', (done) => {
setTimeout(() => {
expect(
- component.$el.querySelectorAll('.gl-pagination li').length,
- ).toEqual(5);
+ component.$el.querySelectorAll('.gl-pagination'),
+ ).not.toBeNull();
done();
}, 0);
});
- it('should update url when no search params are present', (done) => {
- spyOn(gl.utils, 'visitUrl');
+ it('should make an API request when changing page', (done) => {
+ spyOn(component, 'updateContent');
setTimeout(() => {
- component.$el.querySelector('.gl-pagination li:nth-child(5) a').click();
- expect(gl.utils.visitUrl).toHaveBeenCalledWith('?page=2');
- done();
- }, 0);
- });
-
- it('should update url when page is already present', (done) => {
- spyOn(gl.utils, 'visitUrl');
- window.history.pushState({}, null, '?page=1');
+ component.$el.querySelector('.gl-pagination .js-last-button a').click();
- setTimeout(() => {
- component.$el.querySelector('.gl-pagination li:nth-child(5) a').click();
- expect(gl.utils.visitUrl).toHaveBeenCalledWith('?page=2');
+ expect(component.updateContent).toHaveBeenCalledWith({ scope: component.scope, page: '10' });
done();
}, 0);
});
- it('should update url when page and scope are already present', (done) => {
- spyOn(gl.utils, 'visitUrl');
- window.history.pushState({}, null, '?scope=all&page=1');
-
+ it('should make an API request when using tabs', (done) => {
setTimeout(() => {
- component.$el.querySelector('.gl-pagination li:nth-child(5) a').click();
- expect(gl.utils.visitUrl).toHaveBeenCalledWith('?scope=all&page=2');
- done();
- }, 0);
- });
-
- it('should update url when page and scope are already present and page is first param', (done) => {
- spyOn(gl.utils, 'visitUrl');
- window.history.pushState({}, null, '?page=1&scope=all');
+ spyOn(component, 'updateContent');
+ component.$el.querySelector('.js-environments-tab-stopped').click();
- setTimeout(() => {
- component.$el.querySelector('.gl-pagination li:nth-child(5) a').click();
- expect(gl.utils.visitUrl).toHaveBeenCalledWith('?page=2&scope=all');
+ expect(component.updateContent).toHaveBeenCalledWith({ scope: 'stopped', page: '1' });
done();
- }, 0);
+ });
});
});
});
@@ -172,9 +151,7 @@ describe('Environments Folder View', () => {
});
it('should not render a table', (done) => {
- component = new EnvironmentsFolderViewComponent({
- el: document.querySelector('#environments-folder-list-view'),
- });
+ component = mountComponent(Component, mockData);
setTimeout(() => {
expect(
@@ -187,11 +164,11 @@ describe('Environments Folder View', () => {
it('should render available tab with count 0', (done) => {
setTimeout(() => {
expect(
- component.$el.querySelector('.js-available-environments-folder-tab').textContent,
+ component.$el.querySelector('.js-environments-tab-available').textContent,
).toContain('Available');
expect(
- component.$el.querySelector('.js-available-environments-folder-tab .js-available-environments-count').textContent,
+ component.$el.querySelector('.js-environments-tab-available .badge').textContent,
).toContain('0');
done();
}, 0);
@@ -200,14 +177,70 @@ describe('Environments Folder View', () => {
it('should render stopped tab with count 0', (done) => {
setTimeout(() => {
expect(
- component.$el.querySelector('.js-stopped-environments-folder-tab').textContent,
+ component.$el.querySelector('.js-environments-tab-stopped').textContent,
).toContain('Stopped');
expect(
- component.$el.querySelector('.js-stopped-environments-folder-tab .js-stopped-environments-count').textContent,
+ component.$el.querySelector('.js-environments-tab-stopped .badge').textContent,
).toContain('0');
done();
}, 0);
});
});
+
+ describe('methods', () => {
+ const environmentsEmptyResponseInterceptor = (request, next) => {
+ next(request.respondWith(JSON.stringify([]), {
+ status: 200,
+ }));
+ };
+
+ beforeEach(() => {
+ Vue.http.interceptors.push(environmentsEmptyResponseInterceptor);
+ Vue.http.interceptors.push(headersInterceptor);
+
+ component = mountComponent(Component, mockData);
+ spyOn(history, 'pushState').and.stub();
+ });
+
+ afterEach(() => {
+ Vue.http.interceptors = _.without(
+ Vue.http.interceptors, environmentsEmptyResponseInterceptor,
+ );
+ Vue.http.interceptors = _.without(Vue.http.interceptors, headersInterceptor);
+ });
+
+ describe('updateContent', () => {
+ it('should set given parameters', (done) => {
+ component.updateContent({ scope: 'stopped', page: '4' })
+ .then(() => {
+ expect(component.page).toEqual('4');
+ expect(component.scope).toEqual('stopped');
+ expect(component.requestData.scope).toEqual('stopped');
+ expect(component.requestData.page).toEqual('4');
+ done();
+ })
+ .catch(done.fail);
+ });
+ });
+
+ describe('onChangeTab', () => {
+ it('should set page to 1', () => {
+ spyOn(component, 'updateContent');
+ component.onChangeTab('stopped');
+
+ expect(component.updateContent).toHaveBeenCalledWith({ scope: 'stopped', page: '1' });
+ });
+ });
+
+ describe('onChangePage', () => {
+ it('should update page and keep scope', () => {
+ spyOn(component, 'updateContent');
+
+ component.onChangePage(4);
+
+ expect(component.updateContent).toHaveBeenCalledWith({ scope: component.scope, page: '4' });
+ });
+ });
+ });
});
diff --git a/spec/javascripts/fixtures/clusters.rb b/spec/javascripts/fixtures/clusters.rb
index 8e74c4f859c..d26ea3febe8 100644
--- a/spec/javascripts/fixtures/clusters.rb
+++ b/spec/javascripts/fixtures/clusters.rb
@@ -31,4 +31,19 @@ describe Projects::ClustersController, '(JavaScript fixtures)', type: :controlle
expect(response).to be_success
store_frontend_fixture(response, example.description)
end
+
+ context 'rendering non-empty state' do
+ before do
+ cluster
+ end
+
+ it 'clusters/index_cluster.html.raw' do |example|
+ get :index,
+ namespace_id: namespace,
+ project_id: project
+
+ expect(response).to be_success
+ store_frontend_fixture(response, example.description)
+ end
+ end
end
diff --git a/spec/javascripts/fixtures/environments/element.html.haml b/spec/javascripts/fixtures/environments/element.html.haml
deleted file mode 100644
index 8d7aeb23356..00000000000
--- a/spec/javascripts/fixtures/environments/element.html.haml
+++ /dev/null
@@ -1 +0,0 @@
-.test-dom-element
diff --git a/spec/javascripts/fixtures/environments/environments.html.haml b/spec/javascripts/fixtures/environments/environments.html.haml
deleted file mode 100644
index e6000fbb553..00000000000
--- a/spec/javascripts/fixtures/environments/environments.html.haml
+++ /dev/null
@@ -1,9 +0,0 @@
-%div
- #environments-list-view{ data: { environments_data: "foo/environments",
- "can-create-deployment" => "true",
- "can-read-environment" => "true",
- "can-create-environment" => "true",
- "project-environments-path" => "https://gitlab.com/foo/environments",
- "project-stopped-environments-path" => "https://gitlab.com/foo/environments?scope=stopped",
- "new-environment-path" => "https://gitlab.com/foo/environments/new",
- "help-page-path" => "https://gitlab.com/help_page"}}
diff --git a/spec/javascripts/fixtures/environments/environments_folder_view.html.haml b/spec/javascripts/fixtures/environments/environments_folder_view.html.haml
deleted file mode 100644
index aceec139730..00000000000
--- a/spec/javascripts/fixtures/environments/environments_folder_view.html.haml
+++ /dev/null
@@ -1,7 +0,0 @@
-%div
- #environments-folder-list-view{ data: { "can-create-deployment" => "true",
- "can-read-environment" => "true",
- "css-class" => "",
- "commit-icon-svg" => custom_icon("icon_commit"),
- "terminal-icon-svg" => custom_icon("icon_terminal"),
- "play-icon-svg" => custom_icon("icon_play") } }
diff --git a/spec/javascripts/flash_spec.js b/spec/javascripts/flash_spec.js
index b669aabcee4..97e3ab682c5 100644
--- a/spec/javascripts/flash_spec.js
+++ b/spec/javascripts/flash_spec.js
@@ -278,7 +278,7 @@ describe('Flash', () => {
removeFlashClickListener(flashEl, false);
- flashEl.parentNode.click();
+ flashEl.click();
setTimeout(() => {
expect(document.querySelector('.flash')).toBeNull();
diff --git a/spec/javascripts/issuable_spec.js b/spec/javascripts/issuable_spec.js
index ceee08d47c5..5a9112716f4 100644
--- a/spec/javascripts/issuable_spec.js
+++ b/spec/javascripts/issuable_spec.js
@@ -26,7 +26,7 @@ describe('Issuable', () => {
document.body.appendChild(element);
const input = document.createElement('input');
- input.setAttribute('id', 'issue_email');
+ input.setAttribute('id', 'issuable_email');
document.body.appendChild(input);
Issuable = new IssuableIndex('issue_');
diff --git a/spec/javascripts/issue_show/components/app_spec.js b/spec/javascripts/issue_show/components/app_spec.js
index 5662c7387fb..b47a8bf705f 100644
--- a/spec/javascripts/issue_show/components/app_spec.js
+++ b/spec/javascripts/issue_show/components/app_spec.js
@@ -35,11 +35,12 @@ describe('Issuable output', () => {
canUpdate: true,
canDestroy: true,
endpoint: '/gitlab-org/gitlab-shell/issues/9/realtime_changes',
+ updateEndpoint: gl.TEST_HOST,
issuableRef: '#1',
initialTitleHtml: '',
initialTitleText: '',
- initialDescriptionHtml: '',
- initialDescriptionText: '',
+ initialDescriptionHtml: 'test',
+ initialDescriptionText: 'test',
markdownPreviewPath: '/',
markdownDocsPath: '/',
projectNamespace: '/',
diff --git a/spec/javascripts/issue_show/components/description_spec.js b/spec/javascripts/issue_show/components/description_spec.js
index 360691a3546..163e5cdd062 100644
--- a/spec/javascripts/issue_show/components/description_spec.js
+++ b/spec/javascripts/issue_show/components/description_spec.js
@@ -1,11 +1,22 @@
import Vue from 'vue';
import descriptionComponent from '~/issue_show/components/description.vue';
+import * as taskList from '~/task_list';
+import mountComponent from '../../helpers/vue_mount_component_helper';
describe('Description component', () => {
let vm;
+ let DescriptionComponent;
+ const props = {
+ canUpdate: true,
+ descriptionHtml: 'test',
+ descriptionText: 'test',
+ updatedAt: new Date().toString(),
+ taskStatus: '',
+ updateUrl: gl.TEST_HOST,
+ };
beforeEach(() => {
- const Component = Vue.extend(descriptionComponent);
+ DescriptionComponent = Vue.extend(descriptionComponent);
if (!document.querySelector('.issuable-meta')) {
const metaData = document.createElement('div');
@@ -15,15 +26,11 @@ describe('Description component', () => {
document.body.appendChild(metaData);
}
- vm = new Component({
- propsData: {
- canUpdate: true,
- descriptionHtml: 'test',
- descriptionText: 'test',
- updatedAt: new Date().toString(),
- taskStatus: '',
- },
- }).$mount();
+ vm = mountComponent(DescriptionComponent, props);
+ });
+
+ afterEach(() => {
+ vm.$destroy();
});
it('animates description changes', (done) => {
@@ -44,34 +51,46 @@ describe('Description component', () => {
});
});
- // TODO: gl.TaskList no longer exists. rewrite these tests once we have a way to rewire ES modules
-
- // it('re-inits the TaskList when description changed', (done) => {
- // spyOn(gl, 'TaskList');
- // vm.descriptionHtml = 'changed';
- //
- // setTimeout(() => {
- // expect(
- // gl.TaskList,
- // ).toHaveBeenCalled();
- //
- // done();
- // });
- // });
-
- // it('does not re-init the TaskList when canUpdate is false', (done) => {
- // spyOn(gl, 'TaskList');
- // vm.canUpdate = false;
- // vm.descriptionHtml = 'changed';
- //
- // setTimeout(() => {
- // expect(
- // gl.TaskList,
- // ).not.toHaveBeenCalled();
- //
- // done();
- // });
- // });
+ describe('TaskList', () => {
+ beforeEach(() => {
+ vm = mountComponent(DescriptionComponent, Object.assign({}, props, {
+ issuableType: 'issuableType',
+ }));
+ spyOn(taskList, 'default');
+ });
+
+ it('re-inits the TaskList when description changed', (done) => {
+ vm.descriptionHtml = 'changed';
+
+ setTimeout(() => {
+ expect(taskList.default).toHaveBeenCalled();
+ done();
+ });
+ });
+
+ it('does not re-init the TaskList when canUpdate is false', (done) => {
+ vm.canUpdate = false;
+ vm.descriptionHtml = 'changed';
+
+ setTimeout(() => {
+ expect(taskList.default).not.toHaveBeenCalled();
+ done();
+ });
+ });
+
+ it('calls with issuableType dataType', (done) => {
+ vm.descriptionHtml = 'changed';
+
+ setTimeout(() => {
+ expect(taskList.default).toHaveBeenCalledWith({
+ dataType: 'issuableType',
+ fieldName: 'description',
+ selector: '.detail-page-description',
+ });
+ done();
+ });
+ });
+ });
describe('taskStatus', () => {
it('adds full taskStatus', (done) => {
@@ -126,4 +145,8 @@ describe('Description component', () => {
});
});
});
+
+ it('sets data-update-url', () => {
+ expect(vm.$el.querySelector('textarea').dataset.updateUrl).toEqual(gl.TEST_HOST);
+ });
});
diff --git a/spec/javascripts/issue_show/components/title_spec.js b/spec/javascripts/issue_show/components/title_spec.js
index c1edc785d0f..5370f4e1fea 100644
--- a/spec/javascripts/issue_show/components/title_spec.js
+++ b/spec/javascripts/issue_show/components/title_spec.js
@@ -80,19 +80,19 @@ describe('Title component', () => {
});
it('should not show by default', () => {
- expect(vm.$el.querySelector('.note-action-button')).toBeNull();
+ expect(vm.$el.querySelector('.btn-edit')).toBeNull();
});
it('should not show if canUpdate is false', () => {
vm.showInlineEditButton = true;
vm.canUpdate = false;
- expect(vm.$el.querySelector('.note-action-button')).toBeNull();
+ expect(vm.$el.querySelector('.btn-edit')).toBeNull();
});
it('should show if showInlineEditButton and canUpdate', () => {
vm.showInlineEditButton = true;
vm.canUpdate = true;
- expect(vm.$el.querySelector('.note-action-button')).toBeDefined();
+ expect(vm.$el.querySelector('.btn-edit')).toBeDefined();
});
it('should trigger open.form event when clicked', () => {
@@ -100,7 +100,7 @@ describe('Title component', () => {
vm.canUpdate = true;
Vue.nextTick(() => {
- vm.$el.querySelector('.note-action-button').click();
+ vm.$el.querySelector('.btn-edit').click();
expect(eventHub.$emit).toHaveBeenCalledWith('open.form');
});
});
diff --git a/spec/javascripts/jobs/job_details_mediator_spec.js b/spec/javascripts/jobs/job_details_mediator_spec.js
index 1d7fa7e12fc..3069a0cd60e 100644
--- a/spec/javascripts/jobs/job_details_mediator_spec.js
+++ b/spec/javascripts/jobs/job_details_mediator_spec.js
@@ -1,39 +1,35 @@
-import Vue from 'vue';
+import MockAdapter from 'axios-mock-adapter';
+import axios from '~/lib/utils/axios_utils';
import JobMediator from '~/jobs/job_details_mediator';
import job from './mock_data';
describe('JobMediator', () => {
let mediator;
+ let mock;
beforeEach(() => {
- mediator = new JobMediator({ endpoint: 'foo' });
+ mediator = new JobMediator({ endpoint: 'jobs/40291672.json' });
+ mock = new MockAdapter(axios);
});
it('should set defaults', () => {
expect(mediator.store).toBeDefined();
expect(mediator.service).toBeDefined();
- expect(mediator.options).toEqual({ endpoint: 'foo' });
+ expect(mediator.options).toEqual({ endpoint: 'jobs/40291672.json' });
expect(mediator.state.isLoading).toEqual(false);
});
describe('request and store data', () => {
- const interceptor = (request, next) => {
- next(request.respondWith(JSON.stringify(job), {
- status: 200,
- }));
- };
-
beforeEach(() => {
- Vue.http.interceptors.push(interceptor);
+ mock.onGet().reply(200, job, {});
});
afterEach(() => {
- Vue.http.interceptors = _.without(Vue.http.interceptor, interceptor);
+ mock.restore();
});
it('should store received data', (done) => {
mediator.fetchJob();
-
setTimeout(() => {
expect(mediator.store.state.job).toEqual(job);
done();
diff --git a/spec/javascripts/lib/utils/common_utils_spec.js b/spec/javascripts/lib/utils/common_utils_spec.js
index 6dad5d6b6bd..0a9d815f469 100644
--- a/spec/javascripts/lib/utils/common_utils_spec.js
+++ b/spec/javascripts/lib/utils/common_utils_spec.js
@@ -142,47 +142,6 @@ describe('common_utils', () => {
});
});
- describe('setParamInURL', () => {
- afterEach(() => {
- window.history.pushState({}, null, '');
- });
-
- it('should return the parameter', () => {
- window.history.replaceState({}, null, '');
-
- expect(commonUtils.setParamInURL('page', 156)).toBe('?page=156');
- expect(commonUtils.setParamInURL('page', '156')).toBe('?page=156');
- });
-
- it('should update the existing parameter when its a number', () => {
- window.history.pushState({}, null, '?page=15');
-
- expect(commonUtils.setParamInURL('page', 16)).toBe('?page=16');
- expect(commonUtils.setParamInURL('page', '16')).toBe('?page=16');
- expect(commonUtils.setParamInURL('page', true)).toBe('?page=true');
- });
-
- it('should update the existing parameter when its a string', () => {
- window.history.pushState({}, null, '?scope=all');
-
- expect(commonUtils.setParamInURL('scope', 'finished')).toBe('?scope=finished');
- });
-
- it('should update the existing parameter when more than one parameter exists', () => {
- window.history.pushState({}, null, '?scope=all&page=15');
-
- expect(commonUtils.setParamInURL('scope', 'finished')).toBe('?scope=finished&page=15');
- });
-
- it('should add a new parameter to the end of the existing ones', () => {
- window.history.pushState({}, null, '?scope=all');
-
- expect(commonUtils.setParamInURL('page', 16)).toBe('?scope=all&page=16');
- expect(commonUtils.setParamInURL('page', '16')).toBe('?scope=all&page=16');
- expect(commonUtils.setParamInURL('page', true)).toBe('?scope=all&page=true');
- });
- });
-
describe('historyPushState', () => {
afterEach(() => {
window.history.replaceState({}, null, null);
diff --git a/spec/javascripts/lib/utils/text_utility_spec.js b/spec/javascripts/lib/utils/text_utility_spec.js
index b21bd958f90..1f46c225071 100644
--- a/spec/javascripts/lib/utils/text_utility_spec.js
+++ b/spec/javascripts/lib/utils/text_utility_spec.js
@@ -23,6 +23,14 @@ describe('text_utility', () => {
});
});
+ describe('capitalizeFirstCharacter', () => {
+ it('returns string with first letter capitalized', () => {
+ expect(textUtils.capitalizeFirstCharacter('gitlab')).toEqual('Gitlab');
+ expect(textUtils.highCountTrim(105)).toBe('99+');
+ expect(textUtils.highCountTrim(100)).toBe('99+');
+ });
+ });
+
describe('humanize', () => {
it('should remove underscores and uppercase the first letter', () => {
expect(textUtils.humanize('foo_bar')).toEqual('Foo bar');
diff --git a/spec/javascripts/monitoring/dashboard_spec.js b/spec/javascripts/monitoring/dashboard_spec.js
index 752fdfb4614..9885b8a790f 100644
--- a/spec/javascripts/monitoring/dashboard_spec.js
+++ b/spec/javascripts/monitoring/dashboard_spec.js
@@ -1,6 +1,8 @@
import Vue from 'vue';
+import MockAdapter from 'axios-mock-adapter';
import Dashboard from '~/monitoring/components/dashboard.vue';
-import { MonitorMockInterceptor } from './mock_data';
+import axios from '~/lib/utils/axios_utils';
+import { metricsGroupsAPIResponse, mockApiEndpoint } from './mock_data';
describe('Dashboard', () => {
const fixtureName = 'environments/metrics/metrics.html.raw';
@@ -26,13 +28,17 @@ describe('Dashboard', () => {
});
describe('requests information to the server', () => {
+ let mock;
beforeEach(() => {
document.querySelector('#prometheus-graphs').setAttribute('data-has-metrics', 'true');
- Vue.http.interceptors.push(MonitorMockInterceptor);
+ mock = new MockAdapter(axios);
+ mock.onGet(mockApiEndpoint).reply(200, {
+ metricsGroupsAPIResponse,
+ });
});
afterEach(() => {
- Vue.http.interceptors = _.without(Vue.http.interceptors, MonitorMockInterceptor);
+ mock.reset();
});
it('shows up a loading state', (done) => {
diff --git a/spec/javascripts/monitoring/mock_data.js b/spec/javascripts/monitoring/mock_data.js
index 7ceab657464..6b34855b8b2 100644
--- a/spec/javascripts/monitoring/mock_data.js
+++ b/spec/javascripts/monitoring/mock_data.js
@@ -2425,13 +2425,6 @@ const metricsGroupsAPIResponse = {
export default metricsGroupsAPIResponse;
-const responseMockData = {
- 'GET': {
- '/root/hello-prometheus/environments/30/additional_metrics.json': metricsGroupsAPIResponse,
- 'http://test.host/frontend-fixtures/environments-project/environments/1/additional_metrics.json': metricsGroupsAPIResponse, // TODO: MAke sure this works in the monitoring_bundle_spec
- },
-};
-
export const deploymentData = [
{
id: 111,
@@ -8320,11 +8313,3 @@ export function convertDatesMultipleSeries(multipleSeries) {
});
return convertedMultiple;
}
-
-export function MonitorMockInterceptor(request, next) {
- const body = responseMockData[request.method.toUpperCase()][request.url];
-
- next(request.respondWith(JSON.stringify(body), {
- status: 200,
- }));
-}
diff --git a/spec/javascripts/new_branch_spec.js b/spec/javascripts/new_branch_spec.js
index c57f44dae17..50a5e4ff056 100644
--- a/spec/javascripts/new_branch_spec.js
+++ b/spec/javascripts/new_branch_spec.js
@@ -1,7 +1,6 @@
/* eslint-disable space-before-function-paren, one-var, no-var, one-var-declaration-per-line, no-return-assign, quotes, max-len */
-/* global NewBranchForm */
-import '~/new_branch_form';
+import NewBranchForm from '~/new_branch_form';
(function() {
describe('Branch', function() {
diff --git a/spec/javascripts/notes/components/issue_comment_form_spec.js b/spec/javascripts/notes/components/issue_comment_form_spec.js
index db75262b562..04a7f8e32f1 100644
--- a/spec/javascripts/notes/components/issue_comment_form_spec.js
+++ b/spec/javascripts/notes/components/issue_comment_form_spec.js
@@ -2,7 +2,7 @@ import Vue from 'vue';
import Autosize from 'autosize';
import store from '~/notes/stores';
import issueCommentForm from '~/notes/components/issue_comment_form.vue';
-import { loggedOutIssueData, notesDataMock, userDataMock, issueDataMock } from '../mock_data';
+import { loggedOutnoteableData, notesDataMock, userDataMock, noteableDataMock } from '../mock_data';
import { keyboardDownEvent } from '../../issue_show/helpers';
describe('issue_comment_form component', () => {
@@ -23,7 +23,7 @@ describe('issue_comment_form component', () => {
describe('user is logged in', () => {
beforeEach(() => {
store.dispatch('setUserData', userDataMock);
- store.dispatch('setIssueData', issueDataMock);
+ store.dispatch('setNoteableData', noteableDataMock);
store.dispatch('setNotesData', notesDataMock);
vm = mountComponent();
@@ -178,7 +178,7 @@ describe('issue_comment_form component', () => {
describe('issue is confidential', () => {
it('shows information warning', (done) => {
- store.dispatch('setIssueData', Object.assign(issueDataMock, { confidential: true }));
+ store.dispatch('setNoteableData', Object.assign(noteableDataMock, { confidential: true }));
Vue.nextTick(() => {
expect(vm.$el.querySelector('.confidential-issue-warning')).toBeDefined();
done();
@@ -190,7 +190,7 @@ describe('issue_comment_form component', () => {
describe('user is not logged in', () => {
beforeEach(() => {
store.dispatch('setUserData', null);
- store.dispatch('setIssueData', loggedOutIssueData);
+ store.dispatch('setNoteableData', loggedOutnoteableData);
store.dispatch('setNotesData', notesDataMock);
vm = mountComponent();
diff --git a/spec/javascripts/notes/components/issue_discussion_spec.js b/spec/javascripts/notes/components/issue_discussion_spec.js
index 05c6b57f93e..b6ae55d44f5 100644
--- a/spec/javascripts/notes/components/issue_discussion_spec.js
+++ b/spec/javascripts/notes/components/issue_discussion_spec.js
@@ -1,7 +1,7 @@
import Vue from 'vue';
import store from '~/notes/stores';
import issueDiscussion from '~/notes/components/issue_discussion.vue';
-import { issueDataMock, discussionMock, notesDataMock } from '../mock_data';
+import { noteableDataMock, discussionMock, notesDataMock } from '../mock_data';
describe('issue_discussion component', () => {
let vm;
@@ -9,7 +9,7 @@ describe('issue_discussion component', () => {
beforeEach(() => {
const Component = Vue.extend(issueDiscussion);
- store.dispatch('setIssueData', issueDataMock);
+ store.dispatch('setNoteableData', noteableDataMock);
store.dispatch('setNotesData', notesDataMock);
vm = new Component({
diff --git a/spec/javascripts/notes/components/issue_note_app_spec.js b/spec/javascripts/notes/components/issue_note_app_spec.js
index 22e91c4c40f..8e43037f356 100644
--- a/spec/javascripts/notes/components/issue_note_app_spec.js
+++ b/spec/javascripts/notes/components/issue_note_app_spec.js
@@ -1,6 +1,6 @@
import Vue from 'vue';
import issueNotesApp from '~/notes/components/issue_notes_app.vue';
-import service from '~/notes/services/issue_notes_service';
+import service from '~/notes/services/notes_service';
import * as mockData from '../mock_data';
describe('issue_note_app', () => {
@@ -24,7 +24,7 @@ describe('issue_note_app', () => {
mountComponent = (data) => {
const props = data || {
- issueData: mockData.issueDataMock,
+ noteableData: mockData.noteableDataMock,
notesData: mockData.notesDataMock,
userData: mockData.userDataMock,
};
@@ -60,7 +60,7 @@ describe('issue_note_app', () => {
});
it('should set issue data', () => {
- expect(vm.$store.state.issueData).toEqual(mockData.issueDataMock);
+ expect(vm.$store.state.noteableData).toEqual(mockData.noteableDataMock);
});
it('should set user data', () => {
diff --git a/spec/javascripts/notes/components/issue_note_body_spec.js b/spec/javascripts/notes/components/issue_note_body_spec.js
index 81f07ed47cc..37aad50737b 100644
--- a/spec/javascripts/notes/components/issue_note_body_spec.js
+++ b/spec/javascripts/notes/components/issue_note_body_spec.js
@@ -2,7 +2,7 @@
import Vue from 'vue';
import store from '~/notes/stores';
import noteBody from '~/notes/components/issue_note_body.vue';
-import { issueDataMock, notesDataMock, note } from '../mock_data';
+import { noteableDataMock, notesDataMock, note } from '../mock_data';
describe('issue_note_body component', () => {
let vm;
@@ -10,7 +10,7 @@ describe('issue_note_body component', () => {
beforeEach(() => {
const Component = Vue.extend(noteBody);
- store.dispatch('setIssueData', issueDataMock);
+ store.dispatch('setNoteableData', noteableDataMock);
store.dispatch('setNotesData', notesDataMock);
vm = new Component({
diff --git a/spec/javascripts/notes/components/issue_note_form_spec.js b/spec/javascripts/notes/components/issue_note_form_spec.js
index a90dbcb72b5..d42ef239711 100644
--- a/spec/javascripts/notes/components/issue_note_form_spec.js
+++ b/spec/javascripts/notes/components/issue_note_form_spec.js
@@ -1,7 +1,7 @@
import Vue from 'vue';
import store from '~/notes/stores';
import issueNoteForm from '~/notes/components/issue_note_form.vue';
-import { issueDataMock, notesDataMock } from '../mock_data';
+import { noteableDataMock, notesDataMock } from '../mock_data';
import { keyboardDownEvent } from '../../issue_show/helpers';
describe('issue_note_form component', () => {
@@ -11,7 +11,7 @@ describe('issue_note_form component', () => {
beforeEach(() => {
const Component = Vue.extend(issueNoteForm);
- store.dispatch('setIssueData', issueDataMock);
+ store.dispatch('setNoteableData', noteableDataMock);
store.dispatch('setNotesData', notesDataMock);
props = {
diff --git a/spec/javascripts/notes/components/issue_note_spec.js b/spec/javascripts/notes/components/issue_note_spec.js
index 7ef85d5b4f0..73fd188dbe5 100644
--- a/spec/javascripts/notes/components/issue_note_spec.js
+++ b/spec/javascripts/notes/components/issue_note_spec.js
@@ -2,7 +2,7 @@
import Vue from 'vue';
import store from '~/notes/stores';
import issueNote from '~/notes/components/issue_note.vue';
-import { issueDataMock, notesDataMock, note } from '../mock_data';
+import { noteableDataMock, notesDataMock, note } from '../mock_data';
describe('issue_note', () => {
let vm;
@@ -10,7 +10,7 @@ describe('issue_note', () => {
beforeEach(() => {
const Component = Vue.extend(issueNote);
- store.dispatch('setIssueData', issueDataMock);
+ store.dispatch('setNoteableData', noteableDataMock);
store.dispatch('setNotesData', notesDataMock);
vm = new Component({
diff --git a/spec/javascripts/notes/components/issue_note_actions_spec.js b/spec/javascripts/notes/components/note_actions_spec.js
index 7bcc061f167..ab81aabb992 100644
--- a/spec/javascripts/notes/components/issue_note_actions_spec.js
+++ b/spec/javascripts/notes/components/note_actions_spec.js
@@ -1,6 +1,6 @@
import Vue from 'vue';
import store from '~/notes/stores';
-import issueActions from '~/notes/components/issue_note_actions.vue';
+import noteActions from '~/notes/components/note_actions.vue';
import { userDataMock } from '../mock_data';
describe('issse_note_actions component', () => {
@@ -8,7 +8,7 @@ describe('issse_note_actions component', () => {
let Component;
beforeEach(() => {
- Component = Vue.extend(issueActions);
+ Component = Vue.extend(noteActions);
});
afterEach(() => {
diff --git a/spec/javascripts/notes/components/issue_note_attachment_spec.js b/spec/javascripts/notes/components/note_attachment_spec.js
index 8f33b874ad6..b14a518b622 100644
--- a/spec/javascripts/notes/components/issue_note_attachment_spec.js
+++ b/spec/javascripts/notes/components/note_attachment_spec.js
@@ -1,5 +1,5 @@
import Vue from 'vue';
-import issueNoteAttachment from '~/notes/components/issue_note_attachment.vue';
+import noteAttachment from '~/notes/components/note_attachment.vue';
describe('issue note attachment', () => {
it('should render properly', () => {
@@ -11,7 +11,7 @@ describe('issue note attachment', () => {
},
};
- const Component = Vue.extend(issueNoteAttachment);
+ const Component = Vue.extend(noteAttachment);
const vm = new Component({
propsData: props,
}).$mount();
diff --git a/spec/javascripts/notes/components/issue_note_awards_list_spec.js b/spec/javascripts/notes/components/note_awards_list_spec.js
index 3b6c34f1494..15995ec5a05 100644
--- a/spec/javascripts/notes/components/issue_note_awards_list_spec.js
+++ b/spec/javascripts/notes/components/note_awards_list_spec.js
@@ -1,16 +1,16 @@
import Vue from 'vue';
import store from '~/notes/stores';
-import awardsNote from '~/notes/components/issue_note_awards_list.vue';
-import { issueDataMock, notesDataMock } from '../mock_data';
+import awardsNote from '~/notes/components/note_awards_list.vue';
+import { noteableDataMock, notesDataMock } from '../mock_data';
-describe('issue_note_awards_list component', () => {
+describe('note_awards_list component', () => {
let vm;
let awardsMock;
beforeEach(() => {
const Component = Vue.extend(awardsNote);
- store.dispatch('setIssueData', issueDataMock);
+ store.dispatch('setNoteableData', noteableDataMock);
store.dispatch('setNotesData', notesDataMock);
awardsMock = [
{
diff --git a/spec/javascripts/notes/components/issue_note_edited_text_spec.js b/spec/javascripts/notes/components/note_edited_text_spec.js
index 6603241eb64..e0b991c32ec 100644
--- a/spec/javascripts/notes/components/issue_note_edited_text_spec.js
+++ b/spec/javascripts/notes/components/note_edited_text_spec.js
@@ -1,12 +1,12 @@
import Vue from 'vue';
-import issueNoteEditedText from '~/notes/components/issue_note_edited_text.vue';
+import noteEditedText from '~/notes/components/note_edited_text.vue';
-describe('issue_note_edited_text', () => {
+describe('note_edited_text', () => {
let vm;
let props;
beforeEach(() => {
- const Component = Vue.extend(issueNoteEditedText);
+ const Component = Vue.extend(noteEditedText);
props = {
actionText: 'Edited',
className: 'foo-bar',
diff --git a/spec/javascripts/notes/components/issue_note_header_spec.js b/spec/javascripts/notes/components/note_header_spec.js
index 83ea18508ae..16a76b11321 100644
--- a/spec/javascripts/notes/components/issue_note_header_spec.js
+++ b/spec/javascripts/notes/components/note_header_spec.js
@@ -1,13 +1,13 @@
import Vue from 'vue';
-import issueNoteHeader from '~/notes/components/issue_note_header.vue';
+import noteHeader from '~/notes/components/note_header.vue';
import store from '~/notes/stores';
-describe('issue_note_header component', () => {
+describe('note_header component', () => {
let vm;
let Component;
beforeEach(() => {
- Component = Vue.extend(issueNoteHeader);
+ Component = Vue.extend(noteHeader);
});
afterEach(() => {
diff --git a/spec/javascripts/notes/components/issue_note_signed_out_widget_spec.js b/spec/javascripts/notes/components/note_signed_out_widget_spec.js
index f20d9ce9268..6cba8053888 100644
--- a/spec/javascripts/notes/components/issue_note_signed_out_widget_spec.js
+++ b/spec/javascripts/notes/components/note_signed_out_widget_spec.js
@@ -1,13 +1,13 @@
import Vue from 'vue';
-import issueNoteSignedOut from '~/notes/components/issue_note_signed_out_widget.vue';
+import noteSignedOut from '~/notes/components/note_signed_out_widget.vue';
import store from '~/notes/stores';
import { notesDataMock } from '../mock_data';
-describe('issue_note_signed_out_widget component', () => {
+describe('note_signed_out_widget component', () => {
let vm;
beforeEach(() => {
- const Component = Vue.extend(issueNoteSignedOut);
+ const Component = Vue.extend(noteSignedOut);
store.dispatch('setNotesData', notesDataMock);
vm = new Component({
diff --git a/spec/javascripts/notes/mock_data.js b/spec/javascripts/notes/mock_data.js
index 89ba3a002b7..42497de3c55 100644
--- a/spec/javascripts/notes/mock_data.js
+++ b/spec/javascripts/notes/mock_data.js
@@ -18,7 +18,7 @@ export const userDataMock = {
username: 'root',
};
-export const issueDataMock = {
+export const noteableDataMock = {
assignees: [],
author_id: 1,
branch_name: null,
@@ -271,7 +271,7 @@ export const discussionMock = {
individual_note: false,
};
-export const loggedOutIssueData = {
+export const loggedOutnoteableData = {
"id": 98,
"iid": 26,
"author_id": 1,
diff --git a/spec/javascripts/notes/stores/actions_spec.js b/spec/javascripts/notes/stores/actions_spec.js
index 3d1ca870ca4..e092320f9a3 100644
--- a/spec/javascripts/notes/stores/actions_spec.js
+++ b/spec/javascripts/notes/stores/actions_spec.js
@@ -1,6 +1,6 @@
import * as actions from '~/notes/stores/actions';
import testAction from '../../helpers/vuex_action_helper';
-import { discussionMock, notesDataMock, userDataMock, issueDataMock, individualNote } from '../mock_data';
+import { discussionMock, notesDataMock, userDataMock, noteableDataMock, individualNote } from '../mock_data';
describe('Actions Notes Store', () => {
describe('setNotesData', () => {
@@ -11,10 +11,10 @@ describe('Actions Notes Store', () => {
});
});
- describe('setIssueData', () => {
+ describe('setNoteableData', () => {
it('should set received issue data', (done) => {
- testAction(actions.setIssueData, null, { issueData: {} }, [
- { type: 'SET_ISSUE_DATA', payload: issueDataMock },
+ testAction(actions.setNoteableData, null, { noteableData: {} }, [
+ { type: 'SET_NOTEABLE_DATA', payload: noteableDataMock },
], done);
});
});
diff --git a/spec/javascripts/notes/stores/getters_spec.js b/spec/javascripts/notes/stores/getters_spec.js
index 48ee1bf9a52..c5a84b71788 100644
--- a/spec/javascripts/notes/stores/getters_spec.js
+++ b/spec/javascripts/notes/stores/getters_spec.js
@@ -1,5 +1,5 @@
import * as getters from '~/notes/stores/getters';
-import { notesDataMock, userDataMock, issueDataMock, individualNote } from '../mock_data';
+import { notesDataMock, userDataMock, noteableDataMock, individualNote } from '../mock_data';
describe('Getters Notes Store', () => {
let state;
@@ -11,7 +11,7 @@ describe('Getters Notes Store', () => {
notesData: notesDataMock,
userData: userDataMock,
- issueData: issueDataMock,
+ noteableData: noteableDataMock,
};
});
describe('notes', () => {
@@ -32,9 +32,9 @@ describe('Getters Notes Store', () => {
});
});
- describe('getIssueData', () => {
- it('should return all data in `issueData`', () => {
- expect(getters.getIssueData(state)).toEqual(issueDataMock);
+ describe('getNoteableData', () => {
+ it('should return all data in `noteableData`', () => {
+ expect(getters.getNoteableData(state)).toEqual(noteableDataMock);
});
});
diff --git a/spec/javascripts/notes/stores/mutation_spec.js b/spec/javascripts/notes/stores/mutation_spec.js
index 1e22e03e178..22d99998a7d 100644
--- a/spec/javascripts/notes/stores/mutation_spec.js
+++ b/spec/javascripts/notes/stores/mutation_spec.js
@@ -1,5 +1,5 @@
import mutations from '~/notes/stores/mutations';
-import { note, discussionMock, notesDataMock, userDataMock, issueDataMock, individualNote } from '../mock_data';
+import { note, discussionMock, notesDataMock, userDataMock, noteableDataMock, individualNote } from '../mock_data';
describe('Mutation Notes Store', () => {
describe('ADD_NEW_NOTE', () => {
@@ -74,14 +74,14 @@ describe('Mutation Notes Store', () => {
});
});
- describe('SET_ISSUE_DATA', () => {
+ describe('SET_NOTEABLE_DATA', () => {
it('should set the issue data', () => {
const state = {
- issueData: {},
+ noteableData: {},
};
- mutations.SET_ISSUE_DATA(state, issueDataMock);
- expect(state.issueData).toEqual(issueDataMock);
+ mutations.SET_NOTEABLE_DATA(state, noteableDataMock);
+ expect(state.noteableData).toEqual(noteableDataMock);
});
});
diff --git a/spec/javascripts/notes_spec.js b/spec/javascripts/notes_spec.js
index 928a4b461cc..677a389b88f 100644
--- a/spec/javascripts/notes_spec.js
+++ b/spec/javascripts/notes_spec.js
@@ -5,7 +5,6 @@ import 'autosize';
import '~/gl_form';
import '~/lib/utils/text_utility';
import '~/render_gfm';
-import '~/render_math';
import '~/notes';
(function() {
diff --git a/spec/javascripts/pipelines/graph/job_component_spec.js b/spec/javascripts/pipelines/graph/job_component_spec.js
index 342ee6c1242..23c87610d83 100644
--- a/spec/javascripts/pipelines/graph/job_component_spec.js
+++ b/spec/javascripts/pipelines/graph/job_component_spec.js
@@ -1,8 +1,10 @@
import Vue from 'vue';
import jobComponent from '~/pipelines/components/graph/job_component.vue';
+import mountComponent from '../../helpers/vue_mount_component_helper';
describe('pipeline graph job component', () => {
let JobComponent;
+ let component;
const mockJob = {
id: 4256,
@@ -13,6 +15,7 @@ describe('pipeline graph job component', () => {
label: 'passed',
group: 'success',
details_path: '/root/ci-mock/builds/4256',
+ has_details: true,
action: {
icon: 'retry',
title: 'Retry',
@@ -26,13 +29,13 @@ describe('pipeline graph job component', () => {
JobComponent = Vue.extend(jobComponent);
});
+ afterEach(() => {
+ component.$destroy();
+ });
+
describe('name with link', () => {
it('should render the job name and status with a link', (done) => {
- const component = new JobComponent({
- propsData: {
- job: mockJob,
- },
- }).$mount();
+ component = mountComponent(JobComponent, { job: mockJob });
Vue.nextTick(() => {
const link = component.$el.querySelector('a');
@@ -56,23 +59,23 @@ describe('pipeline graph job component', () => {
describe('name without link', () => {
it('it should render status and name', () => {
- const component = new JobComponent({
- propsData: {
- job: {
- id: 4256,
- name: 'test',
- status: {
- icon: 'icon_status_success',
- text: 'passed',
- label: 'passed',
- group: 'success',
- details_path: '/root/ci-mock/builds/4256',
- },
+ component = mountComponent(JobComponent, {
+ job: {
+ id: 4256,
+ name: 'test',
+ status: {
+ icon: 'icon_status_success',
+ text: 'passed',
+ label: 'passed',
+ group: 'success',
+ details_path: '/root/ci-mock/builds/4256',
+ has_details: false,
},
},
- }).$mount();
+ });
expect(component.$el.querySelector('.js-status-icon-success')).toBeDefined();
+ expect(component.$el.querySelector('a')).toBeNull();
expect(
component.$el.querySelector('.ci-status-text').textContent.trim(),
@@ -82,11 +85,7 @@ describe('pipeline graph job component', () => {
describe('action icon', () => {
it('it should render the action icon', () => {
- const component = new JobComponent({
- propsData: {
- job: mockJob,
- },
- }).$mount();
+ component = mountComponent(JobComponent, { job: mockJob });
expect(component.$el.querySelector('a.ci-action-icon-container')).toBeDefined();
expect(component.$el.querySelector('i.ci-action-icon-wrapper')).toBeDefined();
@@ -95,24 +94,20 @@ describe('pipeline graph job component', () => {
describe('dropdown', () => {
it('should render the dropdown action icon', () => {
- const component = new JobComponent({
- propsData: {
- job: mockJob,
- isDropdown: true,
- },
- }).$mount();
+ component = mountComponent(JobComponent, {
+ job: mockJob,
+ isDropdown: true,
+ });
expect(component.$el.querySelector('a.ci-action-icon-wrapper')).toBeDefined();
});
});
it('should render provided class name', () => {
- const component = new JobComponent({
- propsData: {
- job: mockJob,
- cssClassJobName: 'css-class-job-name',
- },
- }).$mount();
+ component = mountComponent(JobComponent, {
+ job: mockJob,
+ cssClassJobName: 'css-class-job-name',
+ });
expect(
component.$el.querySelector('a').classList.contains('css-class-job-name'),
diff --git a/spec/javascripts/pipelines/pipelines_spec.js b/spec/javascripts/pipelines/pipelines_spec.js
index ff38bc1974d..367b42cefb0 100644
--- a/spec/javascripts/pipelines/pipelines_spec.js
+++ b/spec/javascripts/pipelines/pipelines_spec.js
@@ -176,45 +176,49 @@ describe('Pipelines', () => {
});
});
- describe('updateContent', () => {
- it('should set given parameters', () => {
- component = mountComponent(PipelinesComponent, {
- store: new Store(),
- });
- component.updateContent({ scope: 'finished', page: '4' });
-
- expect(component.page).toEqual('4');
- expect(component.scope).toEqual('finished');
- expect(component.requestData.scope).toEqual('finished');
- expect(component.requestData.page).toEqual('4');
+ describe('methods', () => {
+ beforeEach(() => {
+ spyOn(history, 'pushState').and.stub();
});
- });
- describe('onChangeTab', () => {
- it('should set page to 1', () => {
- component = mountComponent(PipelinesComponent, {
- store: new Store(),
- });
+ describe('updateContent', () => {
+ it('should set given parameters', () => {
+ component = mountComponent(PipelinesComponent, {
+ store: new Store(),
+ });
+ component.updateContent({ scope: 'finished', page: '4' });
- spyOn(component, 'updateContent');
+ expect(component.page).toEqual('4');
+ expect(component.scope).toEqual('finished');
+ expect(component.requestData.scope).toEqual('finished');
+ expect(component.requestData.page).toEqual('4');
+ });
+ });
- component.onChangeTab('running');
+ describe('onChangeTab', () => {
+ it('should set page to 1', () => {
+ component = mountComponent(PipelinesComponent, {
+ store: new Store(),
+ });
+ spyOn(component, 'updateContent');
- expect(component.updateContent).toHaveBeenCalledWith({ scope: 'running', page: '1' });
- });
- });
+ component.onChangeTab('running');
- describe('onChangePage', () => {
- it('should update page and keep scope', () => {
- component = mountComponent(PipelinesComponent, {
- store: new Store(),
+ expect(component.updateContent).toHaveBeenCalledWith({ scope: 'running', page: '1' });
});
+ });
- spyOn(component, 'updateContent');
+ describe('onChangePage', () => {
+ it('should update page and keep scope', () => {
+ component = mountComponent(PipelinesComponent, {
+ store: new Store(),
+ });
+ spyOn(component, 'updateContent');
- component.onChangePage(4);
+ component.onChangePage(4);
- expect(component.updateContent).toHaveBeenCalledWith({ scope: component.scope, page: '4' });
+ expect(component.updateContent).toHaveBeenCalledWith({ scope: component.scope, page: '4' });
+ });
});
});
});
diff --git a/spec/javascripts/projects_dropdown/components/projects_list_item_spec.js b/spec/javascripts/projects_dropdown/components/projects_list_item_spec.js
index 171629fcd6b..edef150dd1e 100644
--- a/spec/javascripts/projects_dropdown/components/projects_list_item_spec.js
+++ b/spec/javascripts/projects_dropdown/components/projects_list_item_spec.js
@@ -50,6 +50,18 @@ describe('ProjectsListItemComponent', () => {
expect(vm.highlightedProjectName).toBe(mockProject.name);
});
});
+
+ describe('truncatedNamespace', () => {
+ it('should truncate project name from namespace string', () => {
+ vm.namespace = 'platform / nokia-3310';
+ expect(vm.truncatedNamespace).toBe('platform');
+ });
+
+ it('should truncate namespace string from the middle if it includes more than two groups in path', () => {
+ vm.namespace = 'platform / hardware / broadcom / Wifi Group / Mobile Chipset / nokia-3310';
+ expect(vm.truncatedNamespace).toBe('platform / ... / Mobile Chipset');
+ });
+ });
});
describe('template', () => {
diff --git a/spec/javascripts/repo/components/commit_sidebar/list_collapsed_spec.js b/spec/javascripts/repo/components/commit_sidebar/list_collapsed_spec.js
new file mode 100644
index 00000000000..f750061a6a1
--- /dev/null
+++ b/spec/javascripts/repo/components/commit_sidebar/list_collapsed_spec.js
@@ -0,0 +1,33 @@
+import Vue from 'vue';
+import store from '~/repo/stores';
+import listCollapsed from '~/repo/components/commit_sidebar/list_collapsed.vue';
+import { createComponentWithStore } from '../../../helpers/vue_mount_component_helper';
+import { file } from '../../helpers';
+
+describe('Multi-file editor commit sidebar list collapsed', () => {
+ let vm;
+
+ beforeEach(() => {
+ const Component = Vue.extend(listCollapsed);
+
+ vm = createComponentWithStore(Component, store);
+
+ vm.$store.state.openFiles.push(file(), file());
+ vm.$store.state.openFiles[0].tempFile = true;
+ vm.$store.state.openFiles.forEach((f) => {
+ Object.assign(f, {
+ changed: true,
+ });
+ });
+
+ vm.$mount();
+ });
+
+ afterEach(() => {
+ vm.$destroy();
+ });
+
+ it('renders added & modified files count', () => {
+ expect(vm.$el.textContent.replace(/\s+/g, ' ').trim()).toBe('1 1');
+ });
+});
diff --git a/spec/javascripts/repo/components/commit_sidebar/list_item_spec.js b/spec/javascripts/repo/components/commit_sidebar/list_item_spec.js
new file mode 100644
index 00000000000..18c9b46fcd9
--- /dev/null
+++ b/spec/javascripts/repo/components/commit_sidebar/list_item_spec.js
@@ -0,0 +1,53 @@
+import Vue from 'vue';
+import listItem from '~/repo/components/commit_sidebar/list_item.vue';
+import mountComponent from '../../../helpers/vue_mount_component_helper';
+import { file } from '../../helpers';
+
+describe('Multi-file editor commit sidebar list item', () => {
+ let vm;
+ let f;
+
+ beforeEach(() => {
+ const Component = Vue.extend(listItem);
+
+ f = file();
+
+ vm = mountComponent(Component, {
+ file: f,
+ });
+ });
+
+ afterEach(() => {
+ vm.$destroy();
+ });
+
+ it('renders file path', () => {
+ expect(vm.$el.querySelector('.multi-file-commit-list-path').textContent.trim()).toBe(f.path);
+ });
+
+ describe('computed', () => {
+ describe('iconName', () => {
+ it('returns modified when not a tempFile', () => {
+ expect(vm.iconName).toBe('file-modified');
+ });
+
+ it('returns addition when not a tempFile', () => {
+ f.tempFile = true;
+
+ expect(vm.iconName).toBe('file-addition');
+ });
+ });
+
+ describe('iconClass', () => {
+ it('returns modified when not a tempFile', () => {
+ expect(vm.iconClass).toContain('multi-file-modified');
+ });
+
+ it('returns addition when not a tempFile', () => {
+ f.tempFile = true;
+
+ expect(vm.iconClass).toContain('multi-file-addition');
+ });
+ });
+ });
+});
diff --git a/spec/javascripts/repo/components/commit_sidebar/list_spec.js b/spec/javascripts/repo/components/commit_sidebar/list_spec.js
new file mode 100644
index 00000000000..df7e3c5de21
--- /dev/null
+++ b/spec/javascripts/repo/components/commit_sidebar/list_spec.js
@@ -0,0 +1,72 @@
+import Vue from 'vue';
+import store from '~/repo/stores';
+import commitSidebarList from '~/repo/components/commit_sidebar/list.vue';
+import { createComponentWithStore } from '../../../helpers/vue_mount_component_helper';
+import { file } from '../../helpers';
+
+describe('Multi-file editor commit sidebar list', () => {
+ let vm;
+
+ beforeEach(() => {
+ const Component = Vue.extend(commitSidebarList);
+
+ vm = createComponentWithStore(Component, store, {
+ title: 'Staged',
+ fileList: [],
+ collapsed: false,
+ }).$mount();
+ });
+
+ afterEach(() => {
+ vm.$destroy();
+ });
+
+ describe('empty file list', () => {
+ it('renders no changes text', () => {
+ expect(vm.$el.querySelector('.help-block').textContent.trim()).toBe('No changes');
+ });
+ });
+
+ describe('with a list of files', () => {
+ beforeEach((done) => {
+ const f = file('file name');
+ f.changed = true;
+ vm.fileList.push(f);
+
+ Vue.nextTick(done);
+ });
+
+ it('renders list', () => {
+ expect(vm.$el.querySelectorAll('li').length).toBe(1);
+ });
+ });
+
+ describe('collapsed', () => {
+ beforeEach((done) => {
+ vm.collapsed = true;
+
+ Vue.nextTick(done);
+ });
+
+ it('adds collapsed class', () => {
+ expect(vm.$el.querySelector('.is-collapsed')).not.toBeNull();
+ });
+
+ it('hides list', () => {
+ expect(vm.$el.querySelector('.list-unstyled')).toBeNull();
+ expect(vm.$el.querySelector('.help-block')).toBeNull();
+ });
+
+ it('hides collapse button', () => {
+ expect(vm.$el.querySelector('.multi-file-commit-panel-collapse-btn')).toBeNull();
+ });
+ });
+
+ it('clicking toggle collapse button emits toggle event', () => {
+ spyOn(vm, '$emit');
+
+ vm.$el.querySelector('.multi-file-commit-panel-collapse-btn').click();
+
+ expect(vm.$emit).toHaveBeenCalledWith('toggleCollapsed');
+ });
+});
diff --git a/spec/javascripts/repo/components/repo_commit_section_spec.js b/spec/javascripts/repo/components/repo_commit_section_spec.js
index 0f991e1b727..1c794123095 100644
--- a/spec/javascripts/repo/components/repo_commit_section_spec.js
+++ b/spec/javascripts/repo/components/repo_commit_section_spec.js
@@ -25,8 +25,12 @@ describe('RepoCommitSection', () => {
return comp.$mount();
}
- beforeEach(() => {
+ beforeEach((done) => {
vm = createComponent();
+
+ vm.collapsed = false;
+
+ Vue.nextTick(done);
});
afterEach(() => {
@@ -36,12 +40,11 @@ describe('RepoCommitSection', () => {
});
it('renders a commit section', () => {
- const changedFileElements = [...vm.$el.querySelectorAll('.changed-files > li')];
- const submitCommit = vm.$el.querySelector('.btn');
- const targetBranch = vm.$el.querySelector('.target-branch');
+ const changedFileElements = [...vm.$el.querySelectorAll('.multi-file-commit-list li')];
+ const submitCommit = vm.$el.querySelector('form .btn');
- expect(vm.$el.querySelector(':scope > form')).toBeTruthy();
- expect(vm.$el.querySelector('.staged-files').textContent.trim()).toEqual('Staged files (2)');
+ expect(vm.$el.querySelector('.multi-file-commit-form')).not.toBeNull();
+ expect(vm.$el.querySelector('.multi-file-commit-panel-section header').textContent.trim()).toEqual('Staged');
expect(changedFileElements.length).toEqual(2);
changedFileElements.forEach((changedFile, i) => {
@@ -49,10 +52,7 @@ describe('RepoCommitSection', () => {
});
expect(submitCommit.disabled).toBeTruthy();
- expect(submitCommit.querySelector('.fa-spinner.fa-spin')).toBeFalsy();
- expect(vm.$el.querySelector('.commit-summary').textContent.trim()).toEqual('Commit 2 files');
- expect(targetBranch.querySelector(':scope > label').textContent.trim()).toEqual('Target branch');
- expect(targetBranch.querySelector('.help-block').textContent.trim()).toEqual('master');
+ expect(submitCommit.querySelector('.fa-spinner.fa-spin')).toBeNull();
});
describe('when submitting', () => {
@@ -69,7 +69,7 @@ describe('RepoCommitSection', () => {
});
it('allows you to submit', () => {
- expect(vm.$el.querySelector('.btn').disabled).toBeTruthy();
+ expect(vm.$el.querySelector('form .btn').disabled).toBeTruthy();
});
it('submits commit', (done) => {
diff --git a/spec/javascripts/repo/components/repo_editor_spec.js b/spec/javascripts/repo/components/repo_editor_spec.js
index 979d2185076..81158cad639 100644
--- a/spec/javascripts/repo/components/repo_editor_spec.js
+++ b/spec/javascripts/repo/components/repo_editor_spec.js
@@ -1,12 +1,13 @@
import Vue from 'vue';
import store from '~/repo/stores';
import repoEditor from '~/repo/components/repo_editor.vue';
+import monacoLoader from '~/repo/monaco_loader';
import { file, resetStore } from '../helpers';
describe('RepoEditor', () => {
let vm;
- beforeEach(() => {
+ beforeEach((done) => {
const f = file();
const RepoEditor = Vue.extend(repoEditor);
@@ -21,6 +22,10 @@ describe('RepoEditor', () => {
vm.monaco = true;
vm.$mount();
+
+ monacoLoader(['vs/editor/editor.main'], () => {
+ setTimeout(done, 0);
+ });
});
afterEach(() => {
@@ -32,7 +37,6 @@ describe('RepoEditor', () => {
it('renders an ide container', (done) => {
Vue.nextTick(() => {
expect(vm.shouldHideEditor).toBeFalsy();
- expect(vm.$el.textContent.trim()).toBe('');
done();
});
@@ -50,7 +54,7 @@ describe('RepoEditor', () => {
});
it('shows activeFile html', () => {
- expect(vm.$el.textContent.trim()).toBe('testing');
+ expect(vm.$el.textContent).toContain('testing');
});
});
});
diff --git a/spec/javascripts/repo/components/repo_sidebar_spec.js b/spec/javascripts/repo/components/repo_sidebar_spec.js
index 7cb4dace491..df7cf8aabbb 100644
--- a/spec/javascripts/repo/components/repo_sidebar_spec.js
+++ b/spec/javascripts/repo/components/repo_sidebar_spec.js
@@ -29,7 +29,6 @@ describe('RepoSidebar', () => {
const thead = vm.$el.querySelector('thead');
const tbody = vm.$el.querySelector('tbody');
- expect(vm.$el.id).toEqual('sidebar');
expect(vm.$el.classList.contains('sidebar-mini')).toBeFalsy();
expect(thead.querySelector('.name').textContent.trim()).toEqual('Name');
expect(thead.querySelector('.last-commit').textContent.trim()).toEqual('Last commit');
@@ -40,18 +39,6 @@ describe('RepoSidebar', () => {
expect(tbody.querySelector('.file')).toBeTruthy();
});
- it('does not render a thead, renders repo-file-options and sets sidebar-mini class if isMini', (done) => {
- vm.$store.state.openFiles.push(vm.$store.state.tree[0]);
-
- Vue.nextTick(() => {
- expect(vm.$el.classList.contains('sidebar-mini')).toBeTruthy();
- expect(vm.$el.querySelector('thead')).toBeTruthy();
- expect(vm.$el.querySelector('thead .repo-file-options')).toBeTruthy();
-
- done();
- });
- });
-
it('renders 5 loading files if tree is loading', (done) => {
vm.$store.state.tree = [];
vm.$store.state.loading = true;
diff --git a/spec/javascripts/repo/components/repo_tab_spec.js b/spec/javascripts/repo/components/repo_tab_spec.js
index df0ca55aafc..7d2174196c9 100644
--- a/spec/javascripts/repo/components/repo_tab_spec.js
+++ b/spec/javascripts/repo/components/repo_tab_spec.js
@@ -24,8 +24,8 @@ describe('RepoTab', () => {
tab: file(),
});
vm.$store.state.openFiles.push(vm.tab);
- const close = vm.$el.querySelector('.close-btn');
- const name = vm.$el.querySelector(`a[title="${vm.tab.url}"]`);
+ const close = vm.$el.querySelector('.multi-file-tab-close');
+ const name = vm.$el.querySelector(`[title="${vm.tab.url}"]`);
expect(close.querySelector('.fa-times')).toBeTruthy();
expect(name.textContent.trim()).toEqual(vm.tab.name);
@@ -50,7 +50,7 @@ describe('RepoTab', () => {
spyOn(vm, 'closeFile');
- vm.$el.querySelector('.close-btn').click();
+ vm.$el.querySelector('.multi-file-tab-close').click();
expect(vm.closeFile).toHaveBeenCalledWith({ file: vm.tab });
});
@@ -62,7 +62,7 @@ describe('RepoTab', () => {
tab,
});
- expect(vm.$el.querySelector('.close-btn .fa-circle')).toBeTruthy();
+ expect(vm.$el.querySelector('.multi-file-tab-close .fa-circle')).not.toBeNull();
});
describe('methods', () => {
@@ -77,7 +77,7 @@ describe('RepoTab', () => {
vm.$store.state.openFiles.push(tab);
vm.$store.dispatch('setFileActive', tab);
- vm.$el.querySelector('.close-btn').click();
+ vm.$el.querySelector('.multi-file-tab-close').click();
vm.$nextTick(() => {
expect(tab.opened).toBeTruthy();
@@ -95,7 +95,7 @@ describe('RepoTab', () => {
vm.$store.state.openFiles.push(tab);
vm.$store.dispatch('setFileActive', tab);
- vm.$el.querySelector('.close-btn').click();
+ vm.$el.querySelector('.multi-file-tab-close').click();
vm.$nextTick(() => {
expect(tab.opened).toBeFalsy();
diff --git a/spec/javascripts/repo/components/repo_tabs_spec.js b/spec/javascripts/repo/components/repo_tabs_spec.js
index d0246cc72e6..1fb2242c051 100644
--- a/spec/javascripts/repo/components/repo_tabs_spec.js
+++ b/spec/javascripts/repo/components/repo_tabs_spec.js
@@ -25,12 +25,11 @@ describe('RepoTabs', () => {
vm.$store.state.openFiles = openedFiles;
vm.$nextTick(() => {
- const tabs = [...vm.$el.querySelectorAll(':scope > li')];
+ const tabs = [...vm.$el.querySelectorAll('.multi-file-tab')];
- expect(tabs.length).toEqual(3);
+ expect(tabs.length).toEqual(2);
expect(tabs[0].classList.contains('active')).toBeTruthy();
expect(tabs[1].classList.contains('active')).toBeFalsy();
- expect(tabs[2].classList.contains('tabs-divider')).toBeTruthy();
done();
});
diff --git a/spec/javascripts/repo/lib/common/disposable_spec.js b/spec/javascripts/repo/lib/common/disposable_spec.js
new file mode 100644
index 00000000000..62c3913bf4d
--- /dev/null
+++ b/spec/javascripts/repo/lib/common/disposable_spec.js
@@ -0,0 +1,44 @@
+import Disposable from '~/repo/lib/common/disposable';
+
+describe('Multi-file editor library disposable class', () => {
+ let instance;
+ let disposableClass;
+
+ beforeEach(() => {
+ instance = new Disposable();
+
+ disposableClass = {
+ dispose: jasmine.createSpy('dispose'),
+ };
+ });
+
+ afterEach(() => {
+ instance.dispose();
+ });
+
+ describe('add', () => {
+ it('adds disposable classes', () => {
+ instance.add(disposableClass);
+
+ expect(instance.disposers.size).toBe(1);
+ });
+ });
+
+ describe('dispose', () => {
+ beforeEach(() => {
+ instance.add(disposableClass);
+ });
+
+ it('calls dispose on all cached disposers', () => {
+ instance.dispose();
+
+ expect(disposableClass.dispose).toHaveBeenCalled();
+ });
+
+ it('clears cached disposers', () => {
+ instance.dispose();
+
+ expect(instance.disposers.size).toBe(0);
+ });
+ });
+});
diff --git a/spec/javascripts/repo/lib/common/model_manager_spec.js b/spec/javascripts/repo/lib/common/model_manager_spec.js
new file mode 100644
index 00000000000..8c134f178c0
--- /dev/null
+++ b/spec/javascripts/repo/lib/common/model_manager_spec.js
@@ -0,0 +1,81 @@
+/* global monaco */
+import monacoLoader from '~/repo/monaco_loader';
+import ModelManager from '~/repo/lib/common/model_manager';
+import { file } from '../../helpers';
+
+describe('Multi-file editor library model manager', () => {
+ let instance;
+
+ beforeEach((done) => {
+ monacoLoader(['vs/editor/editor.main'], () => {
+ instance = new ModelManager(monaco);
+
+ done();
+ });
+ });
+
+ afterEach(() => {
+ instance.dispose();
+ });
+
+ describe('addModel', () => {
+ it('caches model', () => {
+ instance.addModel(file());
+
+ expect(instance.models.size).toBe(1);
+ });
+
+ it('caches model by file path', () => {
+ instance.addModel(file('path-name'));
+
+ expect(instance.models.keys().next().value).toBe('path-name');
+ });
+
+ it('adds model into disposable', () => {
+ spyOn(instance.disposable, 'add').and.callThrough();
+
+ instance.addModel(file());
+
+ expect(instance.disposable.add).toHaveBeenCalled();
+ });
+
+ it('returns cached model', () => {
+ spyOn(instance.models, 'get').and.callThrough();
+
+ instance.addModel(file());
+ instance.addModel(file());
+
+ expect(instance.models.get).toHaveBeenCalled();
+ });
+ });
+
+ describe('hasCachedModel', () => {
+ it('returns false when no models exist', () => {
+ expect(instance.hasCachedModel('path')).toBeFalsy();
+ });
+
+ it('returns true when model exists', () => {
+ instance.addModel(file('path-name'));
+
+ expect(instance.hasCachedModel('path-name')).toBeTruthy();
+ });
+ });
+
+ describe('dispose', () => {
+ it('clears cached models', () => {
+ instance.addModel(file());
+
+ instance.dispose();
+
+ expect(instance.models.size).toBe(0);
+ });
+
+ it('calls disposable dispose', () => {
+ spyOn(instance.disposable, 'dispose').and.callThrough();
+
+ instance.dispose();
+
+ expect(instance.disposable.dispose).toHaveBeenCalled();
+ });
+ });
+});
diff --git a/spec/javascripts/repo/lib/common/model_spec.js b/spec/javascripts/repo/lib/common/model_spec.js
new file mode 100644
index 00000000000..d41ade237ca
--- /dev/null
+++ b/spec/javascripts/repo/lib/common/model_spec.js
@@ -0,0 +1,84 @@
+/* global monaco */
+import monacoLoader from '~/repo/monaco_loader';
+import Model from '~/repo/lib/common/model';
+import { file } from '../../helpers';
+
+describe('Multi-file editor library model', () => {
+ let model;
+
+ beforeEach((done) => {
+ monacoLoader(['vs/editor/editor.main'], () => {
+ model = new Model(monaco, file('path'));
+
+ done();
+ });
+ });
+
+ afterEach(() => {
+ model.dispose();
+ });
+
+ it('creates original model & new model', () => {
+ expect(model.originalModel).not.toBeNull();
+ expect(model.model).not.toBeNull();
+ });
+
+ describe('path', () => {
+ it('returns file path', () => {
+ expect(model.path).toBe('path');
+ });
+ });
+
+ describe('getModel', () => {
+ it('returns model', () => {
+ expect(model.getModel()).toBe(model.model);
+ });
+ });
+
+ describe('getOriginalModel', () => {
+ it('returns original model', () => {
+ expect(model.getOriginalModel()).toBe(model.originalModel);
+ });
+ });
+
+ describe('onChange', () => {
+ it('caches event by path', () => {
+ model.onChange(() => {});
+
+ expect(model.events.size).toBe(1);
+ expect(model.events.keys().next().value).toBe('path');
+ });
+
+ it('calls callback on change', (done) => {
+ const spy = jasmine.createSpy();
+ model.onChange(spy);
+
+ model.getModel().setValue('123');
+
+ setTimeout(() => {
+ expect(spy).toHaveBeenCalledWith(model.getModel(), jasmine.anything());
+ done();
+ });
+ });
+ });
+
+ describe('dispose', () => {
+ it('calls disposable dispose', () => {
+ spyOn(model.disposable, 'dispose').and.callThrough();
+
+ model.dispose();
+
+ expect(model.disposable.dispose).toHaveBeenCalled();
+ });
+
+ it('clears events', () => {
+ model.onChange(() => {});
+
+ expect(model.events.size).toBe(1);
+
+ model.dispose();
+
+ expect(model.events.size).toBe(0);
+ });
+ });
+});
diff --git a/spec/javascripts/repo/lib/decorations/controller_spec.js b/spec/javascripts/repo/lib/decorations/controller_spec.js
new file mode 100644
index 00000000000..2e32e8fa0bd
--- /dev/null
+++ b/spec/javascripts/repo/lib/decorations/controller_spec.js
@@ -0,0 +1,120 @@
+/* global monaco */
+import monacoLoader from '~/repo/monaco_loader';
+import editor from '~/repo/lib/editor';
+import DecorationsController from '~/repo/lib/decorations/controller';
+import Model from '~/repo/lib/common/model';
+import { file } from '../../helpers';
+
+describe('Multi-file editor library decorations controller', () => {
+ let editorInstance;
+ let controller;
+ let model;
+
+ beforeEach((done) => {
+ monacoLoader(['vs/editor/editor.main'], () => {
+ editorInstance = editor.create(monaco);
+ editorInstance.createInstance(document.createElement('div'));
+
+ controller = new DecorationsController(editorInstance);
+ model = new Model(monaco, file('path'));
+
+ done();
+ });
+ });
+
+ afterEach(() => {
+ model.dispose();
+ editorInstance.dispose();
+ controller.dispose();
+ });
+
+ describe('getAllDecorationsForModel', () => {
+ it('returns empty array when no decorations exist for model', () => {
+ const decorations = controller.getAllDecorationsForModel(model);
+
+ expect(decorations).toEqual([]);
+ });
+
+ it('returns decorations by model URL', () => {
+ controller.addDecorations(model, 'key', [{ decoration: 'decorationValue' }]);
+
+ const decorations = controller.getAllDecorationsForModel(model);
+
+ expect(decorations[0]).toEqual({ decoration: 'decorationValue' });
+ });
+ });
+
+ describe('addDecorations', () => {
+ it('caches decorations in a new map', () => {
+ controller.addDecorations(model, 'key', [{ decoration: 'decorationValue' }]);
+
+ expect(controller.decorations.size).toBe(1);
+ });
+
+ it('does not create new cache model', () => {
+ controller.addDecorations(model, 'key', [{ decoration: 'decorationValue' }]);
+ controller.addDecorations(model, 'key', [{ decoration: 'decorationValue2' }]);
+
+ expect(controller.decorations.size).toBe(1);
+ });
+
+ it('caches decorations by model URL', () => {
+ controller.addDecorations(model, 'key', [{ decoration: 'decorationValue' }]);
+
+ expect(controller.decorations.size).toBe(1);
+ expect(controller.decorations.keys().next().value).toBe('path');
+ });
+
+ it('calls decorate method', () => {
+ spyOn(controller, 'decorate');
+
+ controller.addDecorations(model, 'key', [{ decoration: 'decorationValue' }]);
+
+ expect(controller.decorate).toHaveBeenCalled();
+ });
+ });
+
+ describe('decorate', () => {
+ it('sets decorations on editor instance', () => {
+ spyOn(controller.editor.instance, 'deltaDecorations');
+
+ controller.decorate(model);
+
+ expect(controller.editor.instance.deltaDecorations).toHaveBeenCalledWith([], []);
+ });
+
+ it('caches decorations', () => {
+ spyOn(controller.editor.instance, 'deltaDecorations').and.returnValue([]);
+
+ controller.decorate(model);
+
+ expect(controller.editorDecorations.size).toBe(1);
+ });
+
+ it('caches decorations by model URL', () => {
+ spyOn(controller.editor.instance, 'deltaDecorations').and.returnValue([]);
+
+ controller.decorate(model);
+
+ expect(controller.editorDecorations.keys().next().value).toBe('path');
+ });
+ });
+
+ describe('dispose', () => {
+ it('clears cached decorations', () => {
+ controller.addDecorations(model, 'key', [{ decoration: 'decorationValue' }]);
+
+ controller.dispose();
+
+ expect(controller.decorations.size).toBe(0);
+ });
+
+ it('clears cached editorDecorations', () => {
+ controller.addDecorations(model, 'key', [{ decoration: 'decorationValue' }]);
+
+ controller.dispose();
+
+ expect(controller.editorDecorations.size).toBe(0);
+ });
+ });
+});
diff --git a/spec/javascripts/repo/lib/diff/controller_spec.js b/spec/javascripts/repo/lib/diff/controller_spec.js
new file mode 100644
index 00000000000..ed62e28d3a3
--- /dev/null
+++ b/spec/javascripts/repo/lib/diff/controller_spec.js
@@ -0,0 +1,176 @@
+/* global monaco */
+import monacoLoader from '~/repo/monaco_loader';
+import editor from '~/repo/lib/editor';
+import ModelManager from '~/repo/lib/common/model_manager';
+import DecorationsController from '~/repo/lib/decorations/controller';
+import DirtyDiffController, { getDiffChangeType, getDecorator } from '~/repo/lib/diff/controller';
+import { computeDiff } from '~/repo/lib/diff/diff';
+import { file } from '../../helpers';
+
+describe('Multi-file editor library dirty diff controller', () => {
+ let editorInstance;
+ let controller;
+ let modelManager;
+ let decorationsController;
+ let model;
+
+ beforeEach((done) => {
+ monacoLoader(['vs/editor/editor.main'], () => {
+ editorInstance = editor.create(monaco);
+ editorInstance.createInstance(document.createElement('div'));
+
+ modelManager = new ModelManager(monaco);
+ decorationsController = new DecorationsController(editorInstance);
+
+ model = modelManager.addModel(file());
+
+ controller = new DirtyDiffController(modelManager, decorationsController);
+
+ done();
+ });
+ });
+
+ afterEach(() => {
+ controller.dispose();
+ model.dispose();
+ decorationsController.dispose();
+ editorInstance.dispose();
+ });
+
+ describe('getDiffChangeType', () => {
+ ['added', 'removed', 'modified'].forEach((type) => {
+ it(`returns ${type}`, () => {
+ const change = {
+ [type]: true,
+ };
+
+ expect(getDiffChangeType(change)).toBe(type);
+ });
+ });
+ });
+
+ describe('getDecorator', () => {
+ ['added', 'removed', 'modified'].forEach((type) => {
+ it(`returns with linesDecorationsClassName for ${type}`, () => {
+ const change = {
+ [type]: true,
+ };
+
+ expect(
+ getDecorator(change).options.linesDecorationsClassName,
+ ).toBe(`dirty-diff dirty-diff-${type}`);
+ });
+
+ it('returns with line numbers', () => {
+ const change = {
+ lineNumber: 1,
+ endLineNumber: 2,
+ [type]: true,
+ };
+
+ const range = getDecorator(change).range;
+
+ expect(range.startLineNumber).toBe(1);
+ expect(range.endLineNumber).toBe(2);
+ expect(range.startColumn).toBe(1);
+ expect(range.endColumn).toBe(1);
+ });
+ });
+ });
+
+ describe('attachModel', () => {
+ it('adds change event callback', () => {
+ spyOn(model, 'onChange');
+
+ controller.attachModel(model);
+
+ expect(model.onChange).toHaveBeenCalled();
+ });
+
+ it('calls throttledComputeDiff on change', () => {
+ spyOn(controller, 'throttledComputeDiff');
+
+ controller.attachModel(model);
+
+ model.getModel().setValue('123');
+
+ expect(controller.throttledComputeDiff).toHaveBeenCalled();
+ });
+ });
+
+ describe('computeDiff', () => {
+ it('posts to worker', () => {
+ spyOn(controller.dirtyDiffWorker, 'postMessage');
+
+ controller.computeDiff(model);
+
+ expect(controller.dirtyDiffWorker.postMessage).toHaveBeenCalledWith({
+ path: model.path,
+ originalContent: '',
+ newContent: '',
+ });
+ });
+ });
+
+ describe('reDecorate', () => {
+ it('calls decorations controller decorate', () => {
+ spyOn(controller.decorationsController, 'decorate');
+
+ controller.reDecorate(model);
+
+ expect(controller.decorationsController.decorate).toHaveBeenCalledWith(model);
+ });
+ });
+
+ describe('decorate', () => {
+ it('adds decorations into decorations controller', () => {
+ spyOn(controller.decorationsController, 'addDecorations');
+
+ controller.decorate({ data: { changes: [], path: 'path' } });
+
+ expect(controller.decorationsController.addDecorations).toHaveBeenCalledWith('path', 'dirtyDiff', jasmine.anything());
+ });
+
+ it('adds decorations into editor', () => {
+ const spy = spyOn(controller.decorationsController.editor.instance, 'deltaDecorations');
+
+ controller.decorate({ data: { changes: computeDiff('123', '1234'), path: 'path' } });
+
+ expect(spy).toHaveBeenCalledWith([], [{
+ range: new monaco.Range(
+ 1, 1, 1, 1,
+ ),
+ options: {
+ isWholeLine: true,
+ linesDecorationsClassName: 'dirty-diff dirty-diff-modified',
+ },
+ }]);
+ });
+ });
+
+ describe('dispose', () => {
+ it('calls disposable dispose', () => {
+ spyOn(controller.disposable, 'dispose').and.callThrough();
+
+ controller.dispose();
+
+ expect(controller.disposable.dispose).toHaveBeenCalled();
+ });
+
+ it('terminates worker', () => {
+ spyOn(controller.dirtyDiffWorker, 'terminate').and.callThrough();
+
+ controller.dispose();
+
+ expect(controller.dirtyDiffWorker.terminate).toHaveBeenCalled();
+ });
+
+ it('removes worker event listener', () => {
+ spyOn(controller.dirtyDiffWorker, 'removeEventListener').and.callThrough();
+
+ controller.dispose();
+
+ expect(controller.dirtyDiffWorker.removeEventListener).toHaveBeenCalledWith('message', jasmine.anything());
+ });
+ });
+});
diff --git a/spec/javascripts/repo/lib/diff/diff_spec.js b/spec/javascripts/repo/lib/diff/diff_spec.js
new file mode 100644
index 00000000000..3269ec5d2c9
--- /dev/null
+++ b/spec/javascripts/repo/lib/diff/diff_spec.js
@@ -0,0 +1,80 @@
+import { computeDiff } from '~/repo/lib/diff/diff';
+
+describe('Multi-file editor library diff calculator', () => {
+ describe('computeDiff', () => {
+ it('returns empty array if no changes', () => {
+ const diff = computeDiff('123', '123');
+
+ expect(diff).toEqual([]);
+ });
+
+ describe('modified', () => {
+ it('', () => {
+ const diff = computeDiff('123', '1234')[0];
+
+ expect(diff.added).toBeTruthy();
+ expect(diff.modified).toBeTruthy();
+ expect(diff.removed).toBeUndefined();
+ });
+
+ it('', () => {
+ const diff = computeDiff('123\n123\n123', '123\n1234\n123')[0];
+
+ expect(diff.added).toBeTruthy();
+ expect(diff.modified).toBeTruthy();
+ expect(diff.removed).toBeUndefined();
+ expect(diff.lineNumber).toBe(2);
+ });
+ });
+
+ describe('added', () => {
+ it('', () => {
+ const diff = computeDiff('123', '123\n123')[0];
+
+ expect(diff.added).toBeTruthy();
+ expect(diff.modified).toBeUndefined();
+ expect(diff.removed).toBeUndefined();
+ });
+
+ it('', () => {
+ const diff = computeDiff('123\n123\n123', '123\n123\n1234\n123')[0];
+
+ expect(diff.added).toBeTruthy();
+ expect(diff.modified).toBeUndefined();
+ expect(diff.removed).toBeUndefined();
+ expect(diff.lineNumber).toBe(3);
+ });
+ });
+
+ describe('removed', () => {
+ it('', () => {
+ const diff = computeDiff('123', '')[0];
+
+ expect(diff.added).toBeUndefined();
+ expect(diff.modified).toBeUndefined();
+ expect(diff.removed).toBeTruthy();
+ });
+
+ it('', () => {
+ const diff = computeDiff('123\n123\n123', '123\n123')[0];
+
+ expect(diff.added).toBeUndefined();
+ expect(diff.modified).toBeTruthy();
+ expect(diff.removed).toBeTruthy();
+ expect(diff.lineNumber).toBe(2);
+ });
+ });
+
+ it('includes line number of change', () => {
+ const diff = computeDiff('123', '')[0];
+
+ expect(diff.lineNumber).toBe(1);
+ });
+
+ it('includes end line number of change', () => {
+ const diff = computeDiff('123', '')[0];
+
+ expect(diff.endLineNumber).toBe(1);
+ });
+ });
+});
diff --git a/spec/javascripts/repo/lib/editor_options_spec.js b/spec/javascripts/repo/lib/editor_options_spec.js
new file mode 100644
index 00000000000..b4887d063ed
--- /dev/null
+++ b/spec/javascripts/repo/lib/editor_options_spec.js
@@ -0,0 +1,7 @@
+import editorOptions from '~/repo/lib/editor_options';
+
+describe('Multi-file editor library editor options', () => {
+ it('returns an array', () => {
+ expect(editorOptions).toEqual(jasmine.any(Array));
+ });
+});
diff --git a/spec/javascripts/repo/lib/editor_spec.js b/spec/javascripts/repo/lib/editor_spec.js
new file mode 100644
index 00000000000..cd32832a232
--- /dev/null
+++ b/spec/javascripts/repo/lib/editor_spec.js
@@ -0,0 +1,128 @@
+/* global monaco */
+import monacoLoader from '~/repo/monaco_loader';
+import editor from '~/repo/lib/editor';
+import { file } from '../helpers';
+
+describe('Multi-file editor library', () => {
+ let instance;
+
+ beforeEach((done) => {
+ monacoLoader(['vs/editor/editor.main'], () => {
+ instance = editor.create(monaco);
+
+ done();
+ });
+ });
+
+ afterEach(() => {
+ instance.dispose();
+ });
+
+ it('creates instance of editor', () => {
+ expect(editor.editorInstance).not.toBeNull();
+ });
+
+ describe('createInstance', () => {
+ let el;
+
+ beforeEach(() => {
+ el = document.createElement('div');
+ });
+
+ it('creates editor instance', () => {
+ spyOn(instance.monaco.editor, 'create').and.callThrough();
+
+ instance.createInstance(el);
+
+ expect(instance.monaco.editor.create).toHaveBeenCalled();
+ });
+
+ it('creates dirty diff controller', () => {
+ instance.createInstance(el);
+
+ expect(instance.dirtyDiffController).not.toBeNull();
+ });
+ });
+
+ describe('createModel', () => {
+ it('calls model manager addModel', () => {
+ spyOn(instance.modelManager, 'addModel');
+
+ instance.createModel('FILE');
+
+ expect(instance.modelManager.addModel).toHaveBeenCalledWith('FILE');
+ });
+ });
+
+ describe('attachModel', () => {
+ let model;
+
+ beforeEach(() => {
+ instance.createInstance(document.createElement('div'));
+
+ model = instance.createModel(file());
+ });
+
+ it('sets the current model on the instance', () => {
+ instance.attachModel(model);
+
+ expect(instance.currentModel).toBe(model);
+ });
+
+ it('attaches the model to the current instance', () => {
+ spyOn(instance.instance, 'setModel');
+
+ instance.attachModel(model);
+
+ expect(instance.instance.setModel).toHaveBeenCalledWith(model.getModel());
+ });
+
+ it('attaches the model to the dirty diff controller', () => {
+ spyOn(instance.dirtyDiffController, 'attachModel');
+
+ instance.attachModel(model);
+
+ expect(instance.dirtyDiffController.attachModel).toHaveBeenCalledWith(model);
+ });
+
+ it('re-decorates with the dirty diff controller', () => {
+ spyOn(instance.dirtyDiffController, 'reDecorate');
+
+ instance.attachModel(model);
+
+ expect(instance.dirtyDiffController.reDecorate).toHaveBeenCalledWith(model);
+ });
+ });
+
+ describe('clearEditor', () => {
+ it('resets the editor model', () => {
+ instance.createInstance(document.createElement('div'));
+
+ spyOn(instance.instance, 'setModel');
+
+ instance.clearEditor();
+
+ expect(instance.instance.setModel).toHaveBeenCalledWith(null);
+ });
+ });
+
+ describe('dispose', () => {
+ it('calls disposble dispose method', () => {
+ spyOn(instance.disposable, 'dispose').and.callThrough();
+
+ instance.dispose();
+
+ expect(instance.disposable.dispose).toHaveBeenCalled();
+ });
+
+ it('resets instance', () => {
+ instance.createInstance(document.createElement('div'));
+
+ expect(instance.instance).not.toBeNull();
+
+ instance.dispose();
+
+ expect(instance.instance).toBeNull();
+ });
+ });
+});
diff --git a/spec/javascripts/repo/stores/getters_spec.js b/spec/javascripts/repo/stores/getters_spec.js
index a204b2386cd..952b8ec3a59 100644
--- a/spec/javascripts/repo/stores/getters_spec.js
+++ b/spec/javascripts/repo/stores/getters_spec.js
@@ -116,4 +116,31 @@ describe('Multi-file store getters', () => {
expect(getters.canEditFile(localState)).toBeFalsy();
});
});
+
+ describe('modifiedFiles', () => {
+ it('returns a list of modified files', () => {
+ localState.openFiles.push(file());
+ localState.openFiles.push(file('changed'));
+ localState.openFiles[1].changed = true;
+
+ const modifiedFiles = getters.modifiedFiles(localState);
+
+ expect(modifiedFiles.length).toBe(1);
+ expect(modifiedFiles[0].name).toBe('changed');
+ });
+ });
+
+ describe('addedFiles', () => {
+ it('returns a list of added files', () => {
+ localState.openFiles.push(file());
+ localState.openFiles.push(file('added'));
+ localState.openFiles[1].changed = true;
+ localState.openFiles[1].tempFile = true;
+
+ const modifiedFiles = getters.addedFiles(localState);
+
+ expect(modifiedFiles.length).toBe(1);
+ expect(modifiedFiles[0].name).toBe('added');
+ });
+ });
});
diff --git a/spec/javascripts/sidebar/mock_data.js b/spec/javascripts/sidebar/mock_data.js
index 0682b463043..3b094d20838 100644
--- a/spec/javascripts/sidebar/mock_data.js
+++ b/spec/javascripts/sidebar/mock_data.js
@@ -1,6 +1,6 @@
/* eslint-disable quote-props*/
-const sidebarMockData = {
+const RESPONSE_MAP = {
'GET': {
'/gitlab-org/gitlab-shell/issues/5.json': {
id: 45,
@@ -66,6 +66,65 @@ const sidebarMockData = {
},
labels: [],
},
+ '/gitlab-org/gitlab-shell/issues/5.json?serializer=sidebar': {
+ assignees: [
+ {
+ name: 'User 0',
+ username: 'user0',
+ id: 22,
+ state: 'active',
+ avatar_url: 'http: //www.gravatar.com/avatar/52e4ce24a915fb7e51e1ad3b57f4b00a?s=80\u0026d=identicon',
+ web_url: 'http: //localhost:3001/user0',
+ },
+ {
+ name: 'Marguerite Bartell',
+ username: 'tajuana',
+ id: 18,
+ state: 'active',
+ avatar_url: 'http: //www.gravatar.com/avatar/4852a41fb41616bf8f140d3701673f53?s=80\u0026d=identicon',
+ web_url: 'http: //localhost:3001/tajuana',
+ },
+ {
+ name: 'Laureen Ritchie',
+ username: 'michaele.will',
+ id: 16,
+ state: 'active',
+ avatar_url: 'http: //www.gravatar.com/avatar/e301827eb03be955c9c172cb9a8e4e8a?s=80\u0026d=identicon',
+ web_url: 'http: //localhost:3001/michaele.will',
+ },
+ ],
+ human_time_estimate: null,
+ human_total_time_spent: null,
+ participants: [
+ {
+ name: 'User 0',
+ username: 'user0',
+ id: 22,
+ state: 'active',
+ avatar_url: 'http: //www.gravatar.com/avatar/52e4ce24a915fb7e51e1ad3b57f4b00a?s=80\u0026d=identicon',
+ web_url: 'http: //localhost:3001/user0',
+ },
+ {
+ name: 'Marguerite Bartell',
+ username: 'tajuana',
+ id: 18,
+ state: 'active',
+ avatar_url: 'http: //www.gravatar.com/avatar/4852a41fb41616bf8f140d3701673f53?s=80\u0026d=identicon',
+ web_url: 'http: //localhost:3001/tajuana',
+ },
+ {
+ name: 'Laureen Ritchie',
+ username: 'michaele.will',
+ id: 16,
+ state: 'active',
+ avatar_url: 'http: //www.gravatar.com/avatar/e301827eb03be955c9c172cb9a8e4e8a?s=80\u0026d=identicon',
+ web_url: 'http: //localhost:3001/michaele.will',
+ },
+ ],
+ subscribed: true,
+ time_estimate: 0,
+ total_time_spent: 0,
+ },
'/autocomplete/projects?project_id=15': [
{
'id': 0,
@@ -113,9 +172,10 @@ const sidebarMockData = {
},
};
-export default {
+const mockData = {
+ responseMap: RESPONSE_MAP,
mediator: {
- endpoint: '/gitlab-org/gitlab-shell/issues/5.json',
+ endpoint: '/gitlab-org/gitlab-shell/issues/5.json?serializer=sidebar',
toggleSubscriptionEndpoint: '/gitlab-org/gitlab-shell/issues/5/toggle_subscription',
moveIssueEndpoint: '/gitlab-org/gitlab-shell/issues/5/move',
projectsAutocompleteEndpoint: '/autocomplete/projects?project_id=15',
@@ -141,12 +201,14 @@ export default {
name: 'Administrator',
username: 'root',
},
+};
- sidebarMockInterceptor(request, next) {
- const body = sidebarMockData[request.method.toUpperCase()][request.url];
+mockData.sidebarMockInterceptor = function (request, next) {
+ const body = this.responseMap[request.method.toUpperCase()][request.url];
- next(request.respondWith(JSON.stringify(body), {
- status: 200,
- }));
- },
-};
+ next(request.respondWith(JSON.stringify(body), {
+ status: 200,
+ }));
+}.bind(mockData);
+
+export default mockData;
diff --git a/spec/javascripts/sidebar/sidebar_mediator_spec.js b/spec/javascripts/sidebar/sidebar_mediator_spec.js
index 7deb1fd2118..14c34d5a78c 100644
--- a/spec/javascripts/sidebar/sidebar_mediator_spec.js
+++ b/spec/javascripts/sidebar/sidebar_mediator_spec.js
@@ -33,10 +33,29 @@ describe('Sidebar mediator', () => {
.catch(done.fail);
});
- it('fetches the data', () => {
- spyOn(this.mediator.service, 'get').and.callThrough();
- this.mediator.fetch();
- expect(this.mediator.service.get).toHaveBeenCalled();
+ it('fetches the data', (done) => {
+ const mockData = Mock.responseMap.GET['/gitlab-org/gitlab-shell/issues/5.json?serializer=sidebar'];
+ spyOn(this.mediator, 'processFetchedData').and.callThrough();
+
+ this.mediator.fetch()
+ .then(() => {
+ expect(this.mediator.processFetchedData).toHaveBeenCalledWith(mockData);
+ })
+ .then(done)
+ .catch(done.fail);
+ });
+
+ it('processes fetched data', () => {
+ const mockData = Mock.responseMap.GET['/gitlab-org/gitlab-shell/issues/5.json?serializer=sidebar'];
+ this.mediator.processFetchedData(mockData);
+
+ expect(this.mediator.store.assignees).toEqual(mockData.assignees);
+ expect(this.mediator.store.humanTimeEstimate).toEqual(mockData.human_time_estimate);
+ expect(this.mediator.store.humanTotalTimeSpent).toEqual(mockData.human_total_time_spent);
+ expect(this.mediator.store.participants).toEqual(mockData.participants);
+ expect(this.mediator.store.subscribed).toEqual(mockData.subscribed);
+ expect(this.mediator.store.timeEstimate).toEqual(mockData.time_estimate);
+ expect(this.mediator.store.totalTimeSpent).toEqual(mockData.total_time_spent);
});
it('sets moveToProjectId', () => {
diff --git a/spec/javascripts/sidebar/sidebar_store_spec.js b/spec/javascripts/sidebar/sidebar_store_spec.js
index 51dee64fb93..ea4eae1e23f 100644
--- a/spec/javascripts/sidebar/sidebar_store_spec.js
+++ b/spec/javascripts/sidebar/sidebar_store_spec.js
@@ -120,6 +120,12 @@ describe('Sidebar store', () => {
expect(this.store.isFetching.participants).toEqual(false);
});
+ it('sets loading state', () => {
+ this.store.setLoadingState('assignees', true);
+
+ expect(this.store.isLoading.assignees).toEqual(true);
+ });
+
it('set time tracking data', () => {
this.store.setTimeTrackingData(Mock.time);
expect(this.store.timeEstimate).toEqual(Mock.time.time_estimate);
diff --git a/spec/javascripts/vue_mr_widget/mock_data.js b/spec/javascripts/vue_mr_widget/mock_data.js
index 0795d0aaa82..1ad7c2d8efa 100644
--- a/spec/javascripts/vue_mr_widget/mock_data.js
+++ b/spec/javascripts/vue_mr_widget/mock_data.js
@@ -202,7 +202,6 @@ export default {
"revert_in_fork_path": "/root/acets-app/forks?continue%5Bnotice%5D=You%27re+not+allowed+to+make+changes+to+this+project+directly.+A+fork+of+this+project+has+been+created+that+you+can+make+changes+in%2C+so+you+can+submit+a+merge+request.+Try+to+cherry-pick+this+commit+again.&continue%5Bnotice_now%5D=You%27re+not+allowed+to+make+changes+to+this+project+directly.+A+fork+of+this+project+is+being+created+that+you+can+make+changes+in%2C+so+you+can+submit+a+merge+request.&continue%5Bto%5D=%2Froot%2Facets-app%2Fmerge_requests%2F22&namespace_key=1",
"email_patches_path": "/root/acets-app/merge_requests/22.patch",
"plain_diff_path": "/root/acets-app/merge_requests/22.diff",
- "ci_status_path": "/root/acets-app/merge_requests/22/ci_status",
"status_path": "/root/acets-app/merge_requests/22.json",
"merge_check_path": "/root/acets-app/merge_requests/22/merge_check",
"ci_environments_status_url": "/root/acets-app/merge_requests/22/ci_environments_status",
diff --git a/spec/javascripts/vue_shared/components/icon_spec.js b/spec/javascripts/vue_shared/components/icon_spec.js
index 104da4473ce..a22b6bd3a67 100644
--- a/spec/javascripts/vue_shared/components/icon_spec.js
+++ b/spec/javascripts/vue_shared/components/icon_spec.js
@@ -11,7 +11,7 @@ describe('Sprite Icon Component', function () {
icon = mountComponent(IconComponent, {
name: 'test',
- size: 99,
+ size: 32,
cssClasses: 'extraclasses',
});
});
@@ -34,12 +34,18 @@ describe('Sprite Icon Component', function () {
});
it('should properly compute iconSizeClass', function () {
- expect(icon.iconSizeClass).toBe('s99');
+ expect(icon.iconSizeClass).toBe('s32');
+ });
+
+ it('forbids invalid size prop', () => {
+ expect(icon.$options.props.size.validator(NaN)).toBeFalsy();
+ expect(icon.$options.props.size.validator(0)).toBeFalsy();
+ expect(icon.$options.props.size.validator(9001)).toBeFalsy();
});
it('should properly render img css', function () {
const classList = icon.$el.classList;
- const containsSizeClass = classList.contains('s99');
+ const containsSizeClass = classList.contains('s32');
const containsCustomClass = classList.contains('extraclasses');
expect(containsSizeClass).toBe(true);
expect(containsCustomClass).toBe(true);
diff --git a/spec/javascripts/vue_shared/components/issue/issue_warning_spec.js b/spec/javascripts/vue_shared/components/issue/issue_warning_spec.js
index 2cf4d8e00ed..24484796bf1 100644
--- a/spec/javascripts/vue_shared/components/issue/issue_warning_spec.js
+++ b/spec/javascripts/vue_shared/components/issue/issue_warning_spec.js
@@ -16,7 +16,7 @@ describe('Issue Warning Component', () => {
isLocked: true,
});
- expect(vm.$el.querySelector('i').className).toEqual('fa icon fa-lock');
+ expect(vm.$el.querySelector('.icon use').href.baseVal).toMatch(/lock$/);
expect(formatWarning(vm.$el.querySelector('span').textContent)).toEqual('This issue is locked. Only project members can comment.');
});
});
@@ -27,7 +27,7 @@ describe('Issue Warning Component', () => {
isConfidential: true,
});
- expect(vm.$el.querySelector('i').className).toEqual('fa icon fa-eye-slash');
+ expect(vm.$el.querySelector('.icon use').href.baseVal).toMatch(/eye-slash$/);
expect(formatWarning(vm.$el.querySelector('span').textContent)).toEqual('This is a confidential issue. Your comment will not be visible to the public.');
});
});
@@ -39,7 +39,7 @@ describe('Issue Warning Component', () => {
isConfidential: true,
});
- expect(vm.$el.querySelector('i')).toBeFalsy();
+ expect(vm.$el.querySelector('.icon')).toBeFalsy();
expect(formatWarning(vm.$el.querySelector('span').textContent)).toEqual('This issue is confidential and locked. People without permission will never get a notification and won\'t be able to comment.');
});
});
diff --git a/spec/javascripts/vue_shared/components/loading_button_spec.js b/spec/javascripts/vue_shared/components/loading_button_spec.js
index c1eabdede00..49bf8ee6f7c 100644
--- a/spec/javascripts/vue_shared/components/loading_button_spec.js
+++ b/spec/javascripts/vue_shared/components/loading_button_spec.js
@@ -98,7 +98,6 @@ describe('LoadingButton', function () {
it('does not call given callback when disabled because of loading', () => {
vm = mountComponent(LoadingButton, {
loading: true,
- indeterminate: true,
});
spyOn(vm, '$emit');
diff --git a/spec/javascripts/vue_shared/components/markdown/toolbar_spec.js b/spec/javascripts/vue_shared/components/markdown/toolbar_spec.js
new file mode 100644
index 00000000000..818ef0af3c2
--- /dev/null
+++ b/spec/javascripts/vue_shared/components/markdown/toolbar_spec.js
@@ -0,0 +1,37 @@
+import Vue from 'vue';
+import toolbar from '~/vue_shared/components/markdown/toolbar.vue';
+import mountComponent from '../../../helpers/vue_mount_component_helper';
+
+describe('toolbar', () => {
+ let vm;
+ const Toolbar = Vue.extend(toolbar);
+ const props = {
+ markdownDocsPath: '',
+ };
+
+ afterEach(() => {
+ vm.$destroy();
+ });
+
+ describe('user can attach file', () => {
+ beforeEach(() => {
+ vm = mountComponent(Toolbar, props);
+ });
+
+ it('should render uploading-container', () => {
+ expect(vm.$el.querySelector('.uploading-container')).not.toBeNull();
+ });
+ });
+
+ describe('user cannot attach file', () => {
+ beforeEach(() => {
+ vm = mountComponent(Toolbar, Object.assign({}, props, {
+ canAttachFile: false,
+ }));
+ });
+
+ it('should not render uploading-container', () => {
+ expect(vm.$el.querySelector('.uploading-container')).toBeNull();
+ });
+ });
+});
diff --git a/spec/javascripts/pipelines/navigation_tabs_spec.js b/spec/javascripts/vue_shared/components/navigation_tabs_spec.js
index f125a2fa189..78e7d747b92 100644
--- a/spec/javascripts/pipelines/navigation_tabs_spec.js
+++ b/spec/javascripts/vue_shared/components/navigation_tabs_spec.js
@@ -1,8 +1,8 @@
import Vue from 'vue';
-import navigationTabs from '~/pipelines/components/navigation_tabs.vue';
-import mountComponent from '../helpers/vue_mount_component_helper';
+import navigationTabs from '~/vue_shared/components/navigation_tabs.vue';
+import mountComponent from '../../helpers/vue_mount_component_helper';
-describe('navigation tabs pipeline component', () => {
+describe('navigation tabs component', () => {
let vm;
let Component;
let data;
@@ -29,7 +29,7 @@ describe('navigation tabs pipeline component', () => {
];
Component = Vue.extend(navigationTabs);
- vm = mountComponent(Component, { tabs: data });
+ vm = mountComponent(Component, { tabs: data, scope: 'pipelines' });
});
afterEach(() => {
@@ -52,4 +52,10 @@ describe('navigation tabs pipeline component', () => {
it('should not render badge', () => {
expect(vm.$el.querySelector('.js-pipelines-tab-running .badge')).toEqual(null);
});
+
+ it('should trigger onTabClick', () => {
+ spyOn(vm, '$emit');
+ vm.$el.querySelector('.js-pipelines-tab-pending').click();
+ expect(vm.$emit).toHaveBeenCalledWith('onChangeTab', 'pending');
+ });
});
diff --git a/spec/javascripts/vue_shared/components/pikaday_spec.js b/spec/javascripts/vue_shared/components/pikaday_spec.js
new file mode 100644
index 00000000000..47af9534737
--- /dev/null
+++ b/spec/javascripts/vue_shared/components/pikaday_spec.js
@@ -0,0 +1,29 @@
+import Vue from 'vue';
+import datePicker from '~/vue_shared/components/pikaday.vue';
+import mountComponent from '../../helpers/vue_mount_component_helper';
+
+describe('datePicker', () => {
+ let vm;
+ beforeEach(() => {
+ const DatePicker = Vue.extend(datePicker);
+ vm = mountComponent(DatePicker, {
+ label: 'label',
+ });
+ });
+
+ it('should render label text', () => {
+ expect(vm.$el.querySelector('.dropdown-toggle-text').innerText.trim()).toEqual('label');
+ });
+
+ it('should show calendar', () => {
+ expect(vm.$el.querySelector('.pika-single')).toBeDefined();
+ });
+
+ it('should toggle when dropdown is clicked', () => {
+ const hidePicker = jasmine.createSpy();
+ vm.$on('hidePicker', hidePicker);
+
+ vm.$el.querySelector('.dropdown-menu-toggle').click();
+ expect(hidePicker).toHaveBeenCalled();
+ });
+});
diff --git a/spec/javascripts/vue_shared/components/sidebar/collapsed_calendar_icon_spec.js b/spec/javascripts/vue_shared/components/sidebar/collapsed_calendar_icon_spec.js
new file mode 100644
index 00000000000..cce53193870
--- /dev/null
+++ b/spec/javascripts/vue_shared/components/sidebar/collapsed_calendar_icon_spec.js
@@ -0,0 +1,35 @@
+import Vue from 'vue';
+import collapsedCalendarIcon from '~/vue_shared/components/sidebar/collapsed_calendar_icon.vue';
+import mountComponent from '../../../helpers/vue_mount_component_helper';
+
+describe('collapsedCalendarIcon', () => {
+ let vm;
+ beforeEach(() => {
+ const CollapsedCalendarIcon = Vue.extend(collapsedCalendarIcon);
+ vm = mountComponent(CollapsedCalendarIcon, {
+ containerClass: 'test-class',
+ text: 'text',
+ showIcon: false,
+ });
+ });
+
+ it('should add class to container', () => {
+ expect(vm.$el.classList.contains('test-class')).toEqual(true);
+ });
+
+ it('should hide calendar icon if showIcon', () => {
+ expect(vm.$el.querySelector('.fa-calendar')).toBeNull();
+ });
+
+ it('should render text', () => {
+ expect(vm.$el.querySelector('span').innerText.trim()).toEqual('text');
+ });
+
+ it('should emit click event when container is clicked', () => {
+ const click = jasmine.createSpy();
+ vm.$on('click', click);
+
+ vm.$el.click();
+ expect(click).toHaveBeenCalled();
+ });
+});
diff --git a/spec/javascripts/vue_shared/components/sidebar/collapsed_grouped_date_picker_spec.js b/spec/javascripts/vue_shared/components/sidebar/collapsed_grouped_date_picker_spec.js
new file mode 100644
index 00000000000..20363e78094
--- /dev/null
+++ b/spec/javascripts/vue_shared/components/sidebar/collapsed_grouped_date_picker_spec.js
@@ -0,0 +1,91 @@
+import Vue from 'vue';
+import collapsedGroupedDatePicker from '~/vue_shared/components/sidebar/collapsed_grouped_date_picker.vue';
+import mountComponent from '../../../helpers/vue_mount_component_helper';
+
+describe('collapsedGroupedDatePicker', () => {
+ let vm;
+ beforeEach(() => {
+ const CollapsedGroupedDatePicker = Vue.extend(collapsedGroupedDatePicker);
+ vm = mountComponent(CollapsedGroupedDatePicker, {
+ showToggleSidebar: true,
+ });
+ });
+
+ it('should render toggle sidebar if showToggleSidebar', (done) => {
+ expect(vm.$el.querySelector('.issuable-sidebar-header')).toBeDefined();
+
+ vm.showToggleSidebar = false;
+ Vue.nextTick(() => {
+ expect(vm.$el.querySelector('.issuable-sidebar-header')).toBeNull();
+ done();
+ });
+ });
+
+ it('toggleCollapse events', () => {
+ const toggleCollapse = jasmine.createSpy();
+
+ beforeEach((done) => {
+ vm.minDate = new Date('07/17/2016');
+ Vue.nextTick(done);
+ });
+
+ it('should emit when sidebar is toggled', () => {
+ vm.$el.querySelector('.gutter-toggle').click();
+ expect(toggleCollapse).toHaveBeenCalled();
+ });
+
+ it('should emit when collapsed-calendar-icon is clicked', () => {
+ vm.$el.querySelector('.sidebar-collapsed-icon').click();
+ expect(toggleCollapse).toHaveBeenCalled();
+ });
+ });
+
+ describe('minDate and maxDate', () => {
+ beforeEach((done) => {
+ vm.minDate = new Date('07/17/2016');
+ vm.maxDate = new Date('07/17/2017');
+ Vue.nextTick(done);
+ });
+
+ it('should render both collapsed-calendar-icon', () => {
+ const icons = vm.$el.querySelectorAll('.sidebar-collapsed-icon');
+ expect(icons.length).toEqual(2);
+ expect(icons[0].innerText.trim()).toEqual('Jul 17 2016');
+ expect(icons[1].innerText.trim()).toEqual('Jul 17 2017');
+ });
+ });
+
+ describe('minDate', () => {
+ beforeEach((done) => {
+ vm.minDate = new Date('07/17/2016');
+ Vue.nextTick(done);
+ });
+
+ it('should render minDate in collapsed-calendar-icon', () => {
+ const icons = vm.$el.querySelectorAll('.sidebar-collapsed-icon');
+ expect(icons.length).toEqual(1);
+ expect(icons[0].innerText.trim()).toEqual('From Jul 17 2016');
+ });
+ });
+
+ describe('maxDate', () => {
+ beforeEach((done) => {
+ vm.maxDate = new Date('07/17/2017');
+ Vue.nextTick(done);
+ });
+
+ it('should render maxDate in collapsed-calendar-icon', () => {
+ const icons = vm.$el.querySelectorAll('.sidebar-collapsed-icon');
+ expect(icons.length).toEqual(1);
+ expect(icons[0].innerText.trim()).toEqual('Until Jul 17 2017');
+ });
+ });
+
+ describe('no dates', () => {
+ it('should render None', () => {
+ const icons = vm.$el.querySelectorAll('.sidebar-collapsed-icon');
+ expect(icons.length).toEqual(1);
+ expect(icons[0].innerText.trim()).toEqual('None');
+ });
+ });
+});
diff --git a/spec/javascripts/vue_shared/components/sidebar/date_picker_spec.js b/spec/javascripts/vue_shared/components/sidebar/date_picker_spec.js
new file mode 100644
index 00000000000..926e11b4d30
--- /dev/null
+++ b/spec/javascripts/vue_shared/components/sidebar/date_picker_spec.js
@@ -0,0 +1,117 @@
+import Vue from 'vue';
+import sidebarDatePicker from '~/vue_shared/components/sidebar/date_picker.vue';
+import mountComponent from '../../../helpers/vue_mount_component_helper';
+
+describe('sidebarDatePicker', () => {
+ let vm;
+ beforeEach(() => {
+ const SidebarDatePicker = Vue.extend(sidebarDatePicker);
+ vm = mountComponent(SidebarDatePicker, {
+ label: 'label',
+ isLoading: true,
+ });
+ });
+
+ it('should emit toggleCollapse when collapsed toggle sidebar is clicked', () => {
+ const toggleCollapse = jasmine.createSpy();
+ vm.$on('toggleCollapse', toggleCollapse);
+
+ vm.$el.querySelector('.issuable-sidebar-header .gutter-toggle').click();
+ expect(toggleCollapse).toHaveBeenCalled();
+ });
+
+ it('should render collapsed-calendar-icon', () => {
+ expect(vm.$el.querySelector('.sidebar-collapsed-icon')).toBeDefined();
+ });
+
+ it('should render label', () => {
+ expect(vm.$el.querySelector('.title').innerText.trim()).toEqual('label');
+ });
+
+ it('should render loading-icon when isLoading', () => {
+ expect(vm.$el.querySelector('.fa-spin')).toBeDefined();
+ });
+
+ it('should render value when not editing', () => {
+ expect(vm.$el.querySelector('.value-content')).toBeDefined();
+ });
+
+ it('should render None if there is no selectedDate', () => {
+ expect(vm.$el.querySelector('.value-content span').innerText.trim()).toEqual('None');
+ });
+
+ it('should render date-picker when editing', (done) => {
+ vm.editing = true;
+ Vue.nextTick(() => {
+ expect(vm.$el.querySelector('.pika-label')).toBeDefined();
+ done();
+ });
+ });
+
+ describe('editable', () => {
+ beforeEach((done) => {
+ vm.editable = true;
+ Vue.nextTick(done);
+ });
+
+ it('should render edit button', () => {
+ expect(vm.$el.querySelector('.title .btn-blank').innerText.trim()).toEqual('Edit');
+ });
+
+ it('should enable editing when edit button is clicked', (done) => {
+ vm.isLoading = false;
+ Vue.nextTick(() => {
+ vm.$el.querySelector('.title .btn-blank').click();
+ expect(vm.editing).toEqual(true);
+ done();
+ });
+ });
+ });
+
+ it('should render date if selectedDate', (done) => {
+ vm.selectedDate = new Date('07/07/2017');
+ Vue.nextTick(() => {
+ expect(vm.$el.querySelector('.value-content strong').innerText.trim()).toEqual('Jul 7, 2017');
+ done();
+ });
+ });
+
+ describe('selectedDate and editable', () => {
+ beforeEach((done) => {
+ vm.selectedDate = new Date('07/07/2017');
+ vm.editable = true;
+ Vue.nextTick(done);
+ });
+
+ it('should render remove button if selectedDate and editable', () => {
+ expect(vm.$el.querySelector('.value-content .btn-blank').innerText.trim()).toEqual('remove');
+ });
+
+ it('should emit saveDate when remove button is clicked', () => {
+ const saveDate = jasmine.createSpy();
+ vm.$on('saveDate', saveDate);
+
+ vm.$el.querySelector('.value-content .btn-blank').click();
+ expect(saveDate).toHaveBeenCalled();
+ });
+ });
+
+ describe('showToggleSidebar', () => {
+ beforeEach((done) => {
+ vm.showToggleSidebar = true;
+ Vue.nextTick(done);
+ });
+
+ it('should render toggle-sidebar when showToggleSidebar', () => {
+ expect(vm.$el.querySelector('.title .gutter-toggle')).toBeDefined();
+ });
+
+ it('should emit toggleCollapse when toggle sidebar is clicked', () => {
+ const toggleCollapse = jasmine.createSpy();
+ vm.$on('toggleCollapse', toggleCollapse);
+
+ vm.$el.querySelector('.title .gutter-toggle').click();
+ expect(toggleCollapse).toHaveBeenCalled();
+ });
+ });
+});
diff --git a/spec/javascripts/vue_shared/components/sidebar/toggle_sidebar_spec.js b/spec/javascripts/vue_shared/components/sidebar/toggle_sidebar_spec.js
new file mode 100644
index 00000000000..752a9e89d50
--- /dev/null
+++ b/spec/javascripts/vue_shared/components/sidebar/toggle_sidebar_spec.js
@@ -0,0 +1,32 @@
+import Vue from 'vue';
+import toggleSidebar from '~/vue_shared/components/sidebar/toggle_sidebar.vue';
+import mountComponent from '../../../helpers/vue_mount_component_helper';
+
+describe('toggleSidebar', () => {
+ let vm;
+ beforeEach(() => {
+ const ToggleSidebar = Vue.extend(toggleSidebar);
+ vm = mountComponent(ToggleSidebar, {
+ collapsed: true,
+ });
+ });
+
+ it('should render << when collapsed', () => {
+ expect(vm.$el.querySelector('.fa').classList.contains('fa-angle-double-left')).toEqual(true);
+ });
+
+ it('should render >> when collapsed', () => {
+ vm.collapsed = false;
+ Vue.nextTick(() => {
+ expect(vm.$el.querySelector('.fa').classList.contains('fa-angle-double-right')).toEqual(true);
+ });
+ });
+
+ it('should emit toggle event when button clicked', () => {
+ const toggle = jasmine.createSpy();
+ vm.$on('toggle', toggle);
+ vm.$el.click();
+
+ expect(toggle).toHaveBeenCalled();
+ });
+});
diff --git a/spec/javascripts/vue_shared/components/toggle_button_spec.js b/spec/javascripts/vue_shared/components/toggle_button_spec.js
new file mode 100644
index 00000000000..447d74d4e08
--- /dev/null
+++ b/spec/javascripts/vue_shared/components/toggle_button_spec.js
@@ -0,0 +1,91 @@
+import Vue from 'vue';
+import toggleButton from '~/vue_shared/components/toggle_button.vue';
+import mountComponent from '../../helpers/vue_mount_component_helper';
+
+describe('Toggle Button', () => {
+ let vm;
+ let Component;
+
+ beforeEach(() => {
+ Component = Vue.extend(toggleButton);
+ });
+
+ afterEach(() => {
+ vm.$destroy();
+ });
+
+ describe('render output', () => {
+ beforeEach(() => {
+ vm = mountComponent(Component, {
+ value: true,
+ name: 'foo',
+ });
+ });
+
+ it('renders input with provided name', () => {
+ expect(vm.$el.querySelector('input').getAttribute('name')).toEqual('foo');
+ });
+
+ it('renders input with provided value', () => {
+ expect(vm.$el.querySelector('input').getAttribute('value')).toEqual('true');
+ });
+
+ it('renders Enabled and Disabled text data attributes', () => {
+ expect(vm.$el.querySelector('button').getAttribute('data-enabled-text')).toEqual('Enabled');
+ expect(vm.$el.querySelector('button').getAttribute('data-disabled-text')).toEqual('Disabled');
+ });
+ });
+
+ describe('is-checked', () => {
+ beforeEach(() => {
+ vm = mountComponent(Component, {
+ value: true,
+ });
+
+ spyOn(vm, '$emit');
+ });
+
+ it('renders is checked class', () => {
+ expect(vm.$el.querySelector('button').classList.contains('is-checked')).toEqual(true);
+ });
+
+ it('emits change event when clicked', () => {
+ vm.$el.querySelector('button').click();
+
+ expect(vm.$emit).toHaveBeenCalledWith('change', false);
+ });
+ });
+
+ describe('is-disabled', () => {
+ beforeEach(() => {
+ vm = mountComponent(Component, {
+ value: true,
+ disabledInput: true,
+ });
+ spyOn(vm, '$emit');
+ });
+
+ it('renders disabled button', () => {
+ expect(vm.$el.querySelector('button').classList.contains('is-disabled')).toEqual(true);
+ });
+
+ it('does not emit change event when clicked', () => {
+ vm.$el.querySelector('button').click();
+
+ expect(vm.$emit).not.toHaveBeenCalled();
+ });
+ });
+
+ describe('is-loading', () => {
+ beforeEach(() => {
+ vm = mountComponent(Component, {
+ value: true,
+ isLoading: true,
+ });
+ });
+
+ it('renders loading class', () => {
+ expect(vm.$el.querySelector('button').classList.contains('is-loading')).toEqual(true);
+ });
+ });
+});
diff --git a/spec/javascripts/zen_mode_spec.js b/spec/javascripts/zen_mode_spec.js
index 7047053d131..45a0bb0650f 100644
--- a/spec/javascripts/zen_mode_spec.js
+++ b/spec/javascripts/zen_mode_spec.js
@@ -1,77 +1,93 @@
-/* eslint-disable space-before-function-paren, no-var, one-var, one-var-declaration-per-line, object-shorthand, comma-dangle, no-return-assign, new-cap, max-len */
/* global Mousetrap */
import Dropzone from 'dropzone';
import ZenMode from '~/zen_mode';
-(function() {
- var enterZen, escapeKeydown, exitZen;
-
- describe('ZenMode', function() {
- var fixtureName = 'merge_requests/merge_request_with_comment.html.raw';
- preloadFixtures(fixtureName);
- beforeEach(function() {
- loadFixtures(fixtureName);
- spyOn(Dropzone, 'forElement').and.callFake(function() {
- return {
- enable: function() {
- return true;
- }
- };
- // Stub Dropzone.forElement(...).enable()
- });
- this.zen = new ZenMode();
- // Set this manually because we can't actually scroll the window
- return this.zen.scroll_position = 456;
+describe('ZenMode', () => {
+ let zen;
+ const fixtureName = 'merge_requests/merge_request_with_comment.html.raw';
+
+ preloadFixtures(fixtureName);
+
+ function enterZen() {
+ $('.notes-form .js-zen-enter').click();
+ }
+
+ function exitZen() {
+ $('.notes-form .js-zen-leave').click();
+ }
+
+ function escapeKeydown() {
+ $('.notes-form textarea').trigger($.Event('keydown', {
+ keyCode: 27,
+ }));
+ }
+
+ beforeEach(() => {
+ loadFixtures(fixtureName);
+
+ spyOn(Dropzone, 'forElement').and.callFake(() => ({
+ enable: () => true,
+ }));
+ zen = new ZenMode();
+
+ // Set this manually because we can't actually scroll the window
+ zen.scroll_position = 456;
+ });
+
+ describe('on enter', () => {
+ it('pauses Mousetrap', () => {
+ spyOn(Mousetrap, 'pause');
+ enterZen();
+ expect(Mousetrap.pause).toHaveBeenCalled();
});
- describe('on enter', function() {
- it('pauses Mousetrap', function() {
- spyOn(Mousetrap, 'pause');
- enterZen();
- return expect(Mousetrap.pause).toHaveBeenCalled();
- });
- return it('removes textarea styling', function() {
- $('.notes-form textarea').attr('style', 'height: 400px');
- enterZen();
- return expect($('.notes-form textarea')).not.toHaveAttr('style');
- });
+
+ it('removes textarea styling', () => {
+ $('.notes-form textarea').attr('style', 'height: 400px');
+ enterZen();
+ expect($('.notes-form textarea')).not.toHaveAttr('style');
});
- describe('in use', function() {
- beforeEach(function() {
- return enterZen();
- });
- return it('exits on Escape', function() {
- escapeKeydown();
- return expect($('.notes-form .zen-backdrop')).not.toHaveClass('fullscreen');
- });
+ });
+
+ describe('in use', () => {
+ beforeEach(enterZen);
+
+ it('exits on Escape', () => {
+ escapeKeydown();
+ expect($('.notes-form .zen-backdrop')).not.toHaveClass('fullscreen');
+ });
+ });
+
+ describe('on exit', () => {
+ beforeEach(enterZen);
+
+ it('unpauses Mousetrap', () => {
+ spyOn(Mousetrap, 'unpause');
+ exitZen();
+ expect(Mousetrap.unpause).toHaveBeenCalled();
});
- return describe('on exit', function() {
- beforeEach(function() {
- return enterZen();
- });
- it('unpauses Mousetrap', function() {
- spyOn(Mousetrap, 'unpause');
- exitZen();
- return expect(Mousetrap.unpause).toHaveBeenCalled();
- });
- return it('restores the scroll position', function() {
- spyOn(this.zen, 'scrollTo');
- exitZen();
- return expect(this.zen.scrollTo).toHaveBeenCalled();
- });
+
+ it('restores the scroll position', () => {
+ spyOn(zen, 'scrollTo');
+ exitZen();
+ expect(zen.scrollTo).toHaveBeenCalled();
});
});
- enterZen = function() {
- return $('.notes-form .js-zen-enter').click();
- };
+ describe('enabling dropzone', () => {
+ beforeEach(() => {
+ enterZen();
+ });
- exitZen = function() {
- return $('.notes-form .js-zen-leave').click();
- };
+ it('should not call dropzone if element is not dropzone valid', () => {
+ $('.div-dropzone').addClass('js-invalid-dropzone');
+ exitZen();
+ expect(Dropzone.forElement).not.toHaveBeenCalled();
+ });
- escapeKeydown = function() {
- return $('.notes-form textarea').trigger($.Event('keydown', {
- keyCode: 27
- }));
- };
-}).call(window);
+ it('should call dropzone if element is dropzone valid', () => {
+ $('.div-dropzone').removeClass('js-invalid-dropzone');
+ exitZen();
+ expect(Dropzone.forElement).toHaveBeenCalled();
+ });
+ });
+});