summaryrefslogtreecommitdiff
path: root/spec/javascripts
diff options
context:
space:
mode:
authorJose Ivan Vargas <jvargas@gitlab.com>2018-02-12 12:48:31 -0600
committerJose Ivan Vargas <jvargas@gitlab.com>2018-02-12 12:48:31 -0600
commitc833a09d9156e26851746250460ddd2f091e73cd (patch)
tree01791c2a8461b53c797b3d5b91858f98b07eb477 /spec/javascripts
parent46ae03628de47d1bef2683a3a5fe4963b3df7d52 (diff)
parent0a22ff267b2d3d787d3da44d927caac7bd442832 (diff)
downloadgitlab-ce-c833a09d9156e26851746250460ddd2f091e73cd.tar.gz
Merge branch 'master' into jivl-update-katex
Diffstat (limited to 'spec/javascripts')
-rw-r--r--spec/javascripts/boards/board_list_spec.js2
-rw-r--r--spec/javascripts/ci_variable_list/ajax_variable_list_spec.js189
-rw-r--r--spec/javascripts/ci_variable_list/ci_variable_list_spec.js83
-rw-r--r--spec/javascripts/clusters/clusters_bundle_spec.js13
-rw-r--r--spec/javascripts/clusters/stores/clusters_store_spec.js1
-rw-r--r--spec/javascripts/datetime_utility_spec.js64
-rw-r--r--spec/javascripts/feature_highlight/feature_highlight_helper_spec.js231
-rw-r--r--spec/javascripts/feature_highlight/feature_highlight_options_spec.js30
-rw-r--r--spec/javascripts/feature_highlight/feature_highlight_spec.js131
-rw-r--r--spec/javascripts/filtered_search/components/recent_searches_dropdown_content_spec.js6
-rw-r--r--spec/javascripts/filtered_search/dropdown_user_spec.js4
-rw-r--r--spec/javascripts/filtered_search/dropdown_utils_spec.js3
-rw-r--r--spec/javascripts/filtered_search/filtered_search_manager_spec.js11
-rw-r--r--spec/javascripts/filtered_search/filtered_search_token_keys_spec.js40
-rw-r--r--spec/javascripts/filtered_search/filtered_search_tokenizer_spec.js4
-rw-r--r--spec/javascripts/fixtures/groups.rb29
-rw-r--r--spec/javascripts/fixtures/jobs.rb2
-rw-r--r--spec/javascripts/fixtures/projects.rb51
-rw-r--r--spec/javascripts/gl_form_spec.js20
-rw-r--r--spec/javascripts/gpg_badges_spec.js50
-rw-r--r--spec/javascripts/groups/components/app_spec.js12
-rw-r--r--spec/javascripts/importer_status_spec.js47
-rw-r--r--spec/javascripts/issuable_time_tracker_spec.js2
-rw-r--r--spec/javascripts/job_spec.js45
-rw-r--r--spec/javascripts/lib/utils/common_utils_spec.js29
-rw-r--r--spec/javascripts/lib/utils/text_utility_spec.js6
-rw-r--r--spec/javascripts/monitoring/dashboard_state_spec.js29
-rw-r--r--spec/javascripts/monitoring/mock_data.js1
-rw-r--r--spec/javascripts/notes/components/note_app_spec.js92
-rw-r--r--spec/javascripts/notes/components/noteable_note_spec.js21
-rw-r--r--spec/javascripts/pipelines/async_button_spec.js8
-rw-r--r--spec/javascripts/vue_mr_widget/components/states/mr_widget_missing_branch_spec.js34
-rw-r--r--spec/javascripts/vue_shared/components/confirmation_input_spec.js63
33 files changed, 1048 insertions, 305 deletions
diff --git a/spec/javascripts/boards/board_list_spec.js b/spec/javascripts/boards/board_list_spec.js
index a5fcb10b9dd..03df6c06691 100644
--- a/spec/javascripts/boards/board_list_spec.js
+++ b/spec/javascripts/boards/board_list_spec.js
@@ -5,7 +5,7 @@ import Vue from 'vue';
import MockAdapter from 'axios-mock-adapter';
import axios from '~/lib/utils/axios_utils';
import Sortable from 'vendor/Sortable';
-import BoardList from '~/boards/components/board_list';
+import BoardList from '~/boards/components/board_list.vue';
import eventHub from '~/boards/eventhub';
import '~/boards/mixins/sortable_default_options';
import '~/boards/models/issue';
diff --git a/spec/javascripts/ci_variable_list/ajax_variable_list_spec.js b/spec/javascripts/ci_variable_list/ajax_variable_list_spec.js
new file mode 100644
index 00000000000..5b9cdceee71
--- /dev/null
+++ b/spec/javascripts/ci_variable_list/ajax_variable_list_spec.js
@@ -0,0 +1,189 @@
+import MockAdapter from 'axios-mock-adapter';
+import axios from '~/lib/utils/axios_utils';
+import AjaxFormVariableList from '~/ci_variable_list/ajax_variable_list';
+
+const VARIABLE_PATCH_ENDPOINT = 'http://test.host/frontend-fixtures/builds-project/variables';
+
+describe('AjaxFormVariableList', () => {
+ preloadFixtures('projects/ci_cd_settings.html.raw');
+ preloadFixtures('projects/ci_cd_settings_with_variables.html.raw');
+
+ let container;
+ let saveButton;
+ let errorBox;
+
+ let mock;
+ let ajaxVariableList;
+
+ beforeEach(() => {
+ loadFixtures('projects/ci_cd_settings.html.raw');
+ container = document.querySelector('.js-ci-variable-list-section');
+
+ mock = new MockAdapter(axios);
+
+ const ajaxVariableListEl = document.querySelector('.js-ci-variable-list-section');
+ saveButton = ajaxVariableListEl.querySelector('.js-secret-variables-save-button');
+ errorBox = container.querySelector('.js-ci-variable-error-box');
+ ajaxVariableList = new AjaxFormVariableList({
+ container,
+ formField: 'variables',
+ saveButton,
+ errorBox,
+ saveEndpoint: container.dataset.saveEndpoint,
+ });
+
+ spyOn(ajaxVariableList, 'updateRowsWithPersistedVariables').and.callThrough();
+ spyOn(ajaxVariableList.variableList, 'toggleEnableRow').and.callThrough();
+ });
+
+ afterEach(() => {
+ mock.restore();
+ });
+
+ describe('onSaveClicked', () => {
+ it('shows loading spinner while waiting for the request', (done) => {
+ const loadingIcon = saveButton.querySelector('.js-secret-variables-save-loading-icon');
+
+ mock.onPatch(VARIABLE_PATCH_ENDPOINT).reply(() => {
+ expect(loadingIcon.classList.contains('hide')).toEqual(false);
+
+ return [200, {}];
+ });
+
+ expect(loadingIcon.classList.contains('hide')).toEqual(true);
+
+ ajaxVariableList.onSaveClicked()
+ .then(() => {
+ expect(loadingIcon.classList.contains('hide')).toEqual(true);
+ })
+ .then(done)
+ .catch(done.fail);
+ });
+
+ it('calls `updateRowsWithPersistedVariables` with the persisted variables', (done) => {
+ const variablesResponse = [{ id: 1, key: 'foo', value: 'bar' }];
+ mock.onPatch(VARIABLE_PATCH_ENDPOINT).reply(200, {
+ variables: variablesResponse,
+ });
+
+ ajaxVariableList.onSaveClicked()
+ .then(() => {
+ expect(ajaxVariableList.updateRowsWithPersistedVariables)
+ .toHaveBeenCalledWith(variablesResponse);
+ })
+ .then(done)
+ .catch(done.fail);
+ });
+
+ it('hides any previous error box', (done) => {
+ mock.onPatch(VARIABLE_PATCH_ENDPOINT).reply(200);
+
+ expect(errorBox.classList.contains('hide')).toEqual(true);
+
+ ajaxVariableList.onSaveClicked()
+ .then(() => {
+ expect(errorBox.classList.contains('hide')).toEqual(true);
+ })
+ .then(done)
+ .catch(done.fail);
+ });
+
+ it('disables remove buttons while waiting for the request', (done) => {
+ mock.onPatch(VARIABLE_PATCH_ENDPOINT).reply(() => {
+ expect(ajaxVariableList.variableList.toggleEnableRow).toHaveBeenCalledWith(false);
+
+ return [200, {}];
+ });
+
+ ajaxVariableList.onSaveClicked()
+ .then(() => {
+ expect(ajaxVariableList.variableList.toggleEnableRow).toHaveBeenCalledWith(true);
+ })
+ .then(done)
+ .catch(done.fail);
+ });
+
+ it('shows error box with validation errors', (done) => {
+ const validationError = 'some validation error';
+ mock.onPatch(VARIABLE_PATCH_ENDPOINT).reply(400, [
+ validationError,
+ ]);
+
+ expect(errorBox.classList.contains('hide')).toEqual(true);
+
+ ajaxVariableList.onSaveClicked()
+ .then(() => {
+ expect(errorBox.classList.contains('hide')).toEqual(false);
+ expect(errorBox.textContent.trim().replace(/\n+\s+/m, ' ')).toEqual(`Validation failed ${validationError}`);
+ })
+ .then(done)
+ .catch(done.fail);
+ });
+
+ it('shows flash message when request fails', (done) => {
+ mock.onPatch(VARIABLE_PATCH_ENDPOINT).reply(500);
+
+ expect(errorBox.classList.contains('hide')).toEqual(true);
+
+ ajaxVariableList.onSaveClicked()
+ .then(() => {
+ expect(errorBox.classList.contains('hide')).toEqual(true);
+ })
+ .then(done)
+ .catch(done.fail);
+ });
+ });
+
+ describe('updateRowsWithPersistedVariables', () => {
+ beforeEach(() => {
+ loadFixtures('projects/ci_cd_settings_with_variables.html.raw');
+ container = document.querySelector('.js-ci-variable-list-section');
+
+ const ajaxVariableListEl = document.querySelector('.js-ci-variable-list-section');
+ saveButton = ajaxVariableListEl.querySelector('.js-secret-variables-save-button');
+ errorBox = container.querySelector('.js-ci-variable-error-box');
+ ajaxVariableList = new AjaxFormVariableList({
+ container,
+ formField: 'variables',
+ saveButton,
+ errorBox,
+ saveEndpoint: container.dataset.saveEndpoint,
+ });
+ });
+
+ it('removes variable that was removed', () => {
+ expect(container.querySelectorAll('.js-row').length).toBe(3);
+
+ container.querySelector('.js-row-remove-button').click();
+
+ expect(container.querySelectorAll('.js-row').length).toBe(3);
+
+ ajaxVariableList.updateRowsWithPersistedVariables([]);
+
+ expect(container.querySelectorAll('.js-row').length).toBe(2);
+ });
+
+ it('updates new variable row with persisted ID', () => {
+ const row = container.querySelector('.js-row:last-child');
+ const idInput = row.querySelector('.js-ci-variable-input-id');
+ const keyInput = row.querySelector('.js-ci-variable-input-key');
+ const valueInput = row.querySelector('.js-ci-variable-input-value');
+
+ keyInput.value = 'foo';
+ keyInput.dispatchEvent(new Event('input'));
+ valueInput.value = 'bar';
+ valueInput.dispatchEvent(new Event('input'));
+
+ expect(idInput.value).toEqual('');
+
+ ajaxVariableList.updateRowsWithPersistedVariables([{
+ id: 3,
+ key: 'foo',
+ value: 'bar',
+ }]);
+
+ expect(idInput.value).toEqual('3');
+ expect(row.dataset.isPersisted).toEqual('true');
+ });
+ });
+});
diff --git a/spec/javascripts/ci_variable_list/ci_variable_list_spec.js b/spec/javascripts/ci_variable_list/ci_variable_list_spec.js
index 0170ab458d4..8acb346901f 100644
--- a/spec/javascripts/ci_variable_list/ci_variable_list_spec.js
+++ b/spec/javascripts/ci_variable_list/ci_variable_list_spec.js
@@ -4,6 +4,7 @@ import getSetTimeoutPromise from '../helpers/set_timeout_promise_helper';
describe('VariableList', () => {
preloadFixtures('pipeline_schedules/edit.html.raw');
preloadFixtures('pipeline_schedules/edit_with_variables.html.raw');
+ preloadFixtures('projects/ci_cd_settings.html.raw');
let $wrapper;
let variableList;
@@ -105,37 +106,8 @@ describe('VariableList', () => {
describe('with all inputs(key, value, protected)', () => {
beforeEach(() => {
- // This markup will be replaced with a fixture when we can render the
- // CI/CD settings page with the new dynamic variable list in https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/4110
- $wrapper = $(`<form class="js-variable-list">
- <ul>
- <li class="js-row">
- <div class="ci-variable-body-item">
- <input class="js-ci-variable-input-key" name="variables[variables_attributes][][key]">
- </div>
-
- <div class="ci-variable-body-item">
- <textarea class="js-ci-variable-input-value" name="variables[variables_attributes][][value]"></textarea>
- </div>
-
- <div class="ci-variable-body-item ci-variable-protected-item">
- <button type="button" class="js-project-feature-toggle project-feature-toggle">
- <input
- type="hidden"
- class="js-ci-variable-input-protected js-project-feature-toggle-input"
- name="variables[variables_attributes][][protected]"
- value="true"
- />
- </button>
- </div>
-
- <button type="button" class="js-row-remove-button"></button>
- </li>
- </ul>
- <button type="button" class="js-secret-value-reveal-button">
- Reveal values
- </button>
- </form>`);
+ loadFixtures('projects/ci_cd_settings.html.raw');
+ $wrapper = $('.js-ci-variable-list-section');
variableList = new VariableList({
container: $wrapper,
@@ -154,10 +126,57 @@ describe('VariableList', () => {
// Check for the correct default in the new row
const $protectedInput = $wrapper.find('.js-row:last-child').find('.js-ci-variable-input-protected');
- expect($protectedInput.val()).toBe('true');
+ expect($protectedInput.val()).toBe('false');
})
.then(done)
.catch(done.fail);
});
});
+
+ describe('toggleEnableRow method', () => {
+ beforeEach(() => {
+ loadFixtures('pipeline_schedules/edit_with_variables.html.raw');
+ $wrapper = $('.js-ci-variable-list-section');
+
+ variableList = new VariableList({
+ container: $wrapper,
+ formField: 'variables',
+ });
+ variableList.init();
+ });
+
+ it('should disable all key inputs', () => {
+ expect($wrapper.find('.js-ci-variable-input-key:not([disabled])').length).toBe(3);
+
+ variableList.toggleEnableRow(false);
+
+ expect($wrapper.find('.js-ci-variable-input-key[disabled]').length).toBe(3);
+ });
+
+ it('should disable all remove buttons', () => {
+ expect($wrapper.find('.js-row-remove-button:not([disabled])').length).toBe(3);
+
+ variableList.toggleEnableRow(false);
+
+ expect($wrapper.find('.js-row-remove-button[disabled]').length).toBe(3);
+ });
+
+ it('should enable all remove buttons', () => {
+ variableList.toggleEnableRow(false);
+ expect($wrapper.find('.js-row-remove-button[disabled]').length).toBe(3);
+
+ variableList.toggleEnableRow(true);
+
+ expect($wrapper.find('.js-row-remove-button:not([disabled])').length).toBe(3);
+ });
+
+ it('should enable all key inputs', () => {
+ variableList.toggleEnableRow(false);
+ expect($wrapper.find('.js-ci-variable-input-key[disabled]').length).toBe(3);
+
+ variableList.toggleEnableRow(true);
+
+ expect($wrapper.find('.js-ci-variable-input-key:not([disabled])').length).toBe(3);
+ });
+ });
});
diff --git a/spec/javascripts/clusters/clusters_bundle_spec.js b/spec/javascripts/clusters/clusters_bundle_spec.js
index 7b38f6b7855..a9e244e523d 100644
--- a/spec/javascripts/clusters/clusters_bundle_spec.js
+++ b/spec/javascripts/clusters/clusters_bundle_spec.js
@@ -71,7 +71,8 @@ describe('Clusters', () => {
helm: { status: APPLICATION_INSTALLABLE, title: 'Helm Tiller' },
});
- expect(document.querySelector('.js-cluster-application-notice .flash-text')).toBeNull();
+ const flashMessage = document.querySelector('.js-cluster-application-notice .flash-text');
+ expect(flashMessage).toBeNull();
});
it('shows an alert when something gets newly installed', () => {
@@ -83,8 +84,9 @@ describe('Clusters', () => {
helm: { status: APPLICATION_INSTALLED, title: 'Helm Tiller' },
});
- expect(document.querySelector('.js-cluster-application-notice .flash-text')).toBeDefined();
- expect(document.querySelector('.js-cluster-application-notice .flash-text').textContent.trim()).toEqual('Helm Tiller was successfully installed on your cluster');
+ const flashMessage = document.querySelector('.js-cluster-application-notice .flash-text');
+ expect(flashMessage).not.toBeNull();
+ expect(flashMessage.textContent.trim()).toEqual('Helm Tiller was successfully installed on your Kubernetes cluster');
});
it('shows an alert when multiple things gets newly installed', () => {
@@ -98,8 +100,9 @@ describe('Clusters', () => {
ingress: { status: APPLICATION_INSTALLED, title: 'Ingress' },
});
- expect(document.querySelector('.js-cluster-application-notice .flash-text')).toBeDefined();
- expect(document.querySelector('.js-cluster-application-notice .flash-text').textContent.trim()).toEqual('Helm Tiller, Ingress was successfully installed on your cluster');
+ const flashMessage = document.querySelector('.js-cluster-application-notice .flash-text');
+ expect(flashMessage).not.toBeNull();
+ expect(flashMessage.textContent.trim()).toEqual('Helm Tiller, Ingress was successfully installed on your Kubernetes cluster');
});
});
diff --git a/spec/javascripts/clusters/stores/clusters_store_spec.js b/spec/javascripts/clusters/stores/clusters_store_spec.js
index ec2889355e6..726a4ed30de 100644
--- a/spec/javascripts/clusters/stores/clusters_store_spec.js
+++ b/spec/javascripts/clusters/stores/clusters_store_spec.js
@@ -58,6 +58,7 @@ describe('Clusters Store', () => {
expect(store.state).toEqual({
helpPath: null,
+ ingressHelpPath: null,
status: mockResponseData.status,
statusReason: mockResponseData.status_reason,
applications: {
diff --git a/spec/javascripts/datetime_utility_spec.js b/spec/javascripts/datetime_utility_spec.js
index 2e5b65f5610..a8d09202154 100644
--- a/spec/javascripts/datetime_utility_spec.js
+++ b/spec/javascripts/datetime_utility_spec.js
@@ -105,4 +105,68 @@ describe('dateInWords', () => {
it('should return abbreviated month name', () => {
expect(datetimeUtility.dateInWords(date, true)).toEqual('Jul 1, 2016');
});
+
+ it('should return date in words without year', () => {
+ expect(datetimeUtility.dateInWords(date, true, true)).toEqual('Jul 1');
+ });
+});
+
+describe('monthInWords', () => {
+ const date = new Date('2017-01-20');
+
+ it('returns month name from provided date', () => {
+ expect(datetimeUtility.monthInWords(date)).toBe('January');
+ });
+
+ it('returns abbreviated month name from provided date', () => {
+ expect(datetimeUtility.monthInWords(date, true)).toBe('Jan');
+ });
+});
+
+describe('totalDaysInMonth', () => {
+ it('returns number of days in a month for given date', () => {
+ // 1st Feb, 2016 (leap year)
+ expect(datetimeUtility.totalDaysInMonth(new Date(2016, 1, 1))).toBe(29);
+
+ // 1st Feb, 2017
+ expect(datetimeUtility.totalDaysInMonth(new Date(2017, 1, 1))).toBe(28);
+
+ // 1st Jan, 2017
+ expect(datetimeUtility.totalDaysInMonth(new Date(2017, 0, 1))).toBe(31);
+ });
+});
+
+describe('getSundays', () => {
+ it('returns array of dates representing all Sundays of the month', () => {
+ // December, 2017 (it has 5 Sundays)
+ const dateOfSundays = [3, 10, 17, 24, 31];
+ const sundays = datetimeUtility.getSundays(new Date(2017, 11, 1));
+
+ expect(sundays.length).toBe(5);
+ sundays.forEach((sunday, index) => {
+ expect(sunday.getDate()).toBe(dateOfSundays[index]);
+ });
+ });
+});
+
+describe('getTimeframeWindow', () => {
+ it('returns array of dates representing a timeframe based on provided length and date', () => {
+ const date = new Date(2018, 0, 1);
+ const mockTimeframe = [
+ new Date(2017, 9, 1),
+ new Date(2017, 10, 1),
+ new Date(2017, 11, 1),
+ new Date(2018, 0, 1),
+ new Date(2018, 1, 1),
+ new Date(2018, 2, 31),
+ ];
+ const timeframe = datetimeUtility.getTimeframeWindow(6, date);
+
+ expect(timeframe.length).toBe(6);
+ timeframe.forEach((timeframeItem, index) => {
+ expect(timeframeItem.getFullYear() === mockTimeframe[index].getFullYear()).toBeTruthy();
+ expect(timeframeItem.getMonth() === mockTimeframe[index].getMonth()).toBeTruthy();
+ expect(timeframeItem.getDate() === mockTimeframe[index].getDate()).toBeTruthy();
+ });
+ });
});
diff --git a/spec/javascripts/feature_highlight/feature_highlight_helper_spec.js b/spec/javascripts/feature_highlight/feature_highlight_helper_spec.js
new file mode 100644
index 00000000000..34ffc7b1016
--- /dev/null
+++ b/spec/javascripts/feature_highlight/feature_highlight_helper_spec.js
@@ -0,0 +1,231 @@
+import MockAdapter from 'axios-mock-adapter';
+import axios from '~/lib/utils/axios_utils';
+import {
+ getSelector,
+ togglePopover,
+ dismiss,
+ mouseleave,
+ mouseenter,
+ inserted,
+} from '~/feature_highlight/feature_highlight_helper';
+import getSetTimeoutPromise from '../helpers/set_timeout_promise_helper';
+
+describe('feature highlight helper', () => {
+ describe('getSelector', () => {
+ it('returns js-feature-highlight selector', () => {
+ const highlightId = 'highlightId';
+ expect(getSelector(highlightId)).toEqual(`.js-feature-highlight[data-highlight=${highlightId}]`);
+ });
+ });
+
+ describe('togglePopover', () => {
+ describe('togglePopover(true)', () => {
+ it('returns true when popover is shown', () => {
+ const context = {
+ hasClass: () => false,
+ popover: () => {},
+ toggleClass: () => {},
+ };
+
+ expect(togglePopover.call(context, true)).toEqual(true);
+ });
+
+ it('returns false when popover is already shown', () => {
+ const context = {
+ hasClass: () => true,
+ };
+
+ expect(togglePopover.call(context, true)).toEqual(false);
+ });
+
+ it('shows popover', (done) => {
+ const context = {
+ hasClass: () => false,
+ popover: () => {},
+ toggleClass: () => {},
+ };
+
+ spyOn(context, 'popover').and.callFake((method) => {
+ expect(method).toEqual('show');
+ done();
+ });
+
+ togglePopover.call(context, true);
+ });
+
+ it('adds disable-animation and js-popover-show class', (done) => {
+ const context = {
+ hasClass: () => false,
+ popover: () => {},
+ toggleClass: () => {},
+ };
+
+ spyOn(context, 'toggleClass').and.callFake((classNames, show) => {
+ expect(classNames).toEqual('disable-animation js-popover-show');
+ expect(show).toEqual(true);
+ done();
+ });
+
+ togglePopover.call(context, true);
+ });
+ });
+
+ describe('togglePopover(false)', () => {
+ it('returns true when popover is hidden', () => {
+ const context = {
+ hasClass: () => true,
+ popover: () => {},
+ toggleClass: () => {},
+ };
+
+ expect(togglePopover.call(context, false)).toEqual(true);
+ });
+
+ it('returns false when popover is already hidden', () => {
+ const context = {
+ hasClass: () => false,
+ };
+
+ expect(togglePopover.call(context, false)).toEqual(false);
+ });
+
+ it('hides popover', (done) => {
+ const context = {
+ hasClass: () => true,
+ popover: () => {},
+ toggleClass: () => {},
+ };
+
+ spyOn(context, 'popover').and.callFake((method) => {
+ expect(method).toEqual('hide');
+ done();
+ });
+
+ togglePopover.call(context, false);
+ });
+
+ it('removes disable-animation and js-popover-show class', (done) => {
+ const context = {
+ hasClass: () => true,
+ popover: () => {},
+ toggleClass: () => {},
+ };
+
+ spyOn(context, 'toggleClass').and.callFake((classNames, show) => {
+ expect(classNames).toEqual('disable-animation js-popover-show');
+ expect(show).toEqual(false);
+ done();
+ });
+
+ togglePopover.call(context, false);
+ });
+ });
+ });
+
+ describe('dismiss', () => {
+ let mock;
+ const context = {
+ hide: () => {},
+ attr: () => '/-/callouts/dismiss',
+ };
+
+ beforeEach(() => {
+ mock = new MockAdapter(axios);
+
+ spyOn(togglePopover, 'call').and.callFake(() => {});
+ spyOn(context, 'hide').and.callFake(() => {});
+ dismiss.call(context);
+ });
+
+ afterEach(() => {
+ mock.restore();
+ });
+
+ it('calls persistent dismissal endpoint', (done) => {
+ const spy = jasmine.createSpy('dismiss-endpoint-hit');
+ mock.onPost('/-/callouts/dismiss').reply(spy);
+
+ getSetTimeoutPromise()
+ .then(() => {
+ expect(spy).toHaveBeenCalled();
+ })
+ .then(done)
+ .catch(done.fail);
+ });
+
+ it('calls hide popover', () => {
+ expect(togglePopover.call).toHaveBeenCalledWith(context, false);
+ });
+
+ it('calls hide', () => {
+ expect(context.hide).toHaveBeenCalled();
+ });
+ });
+
+ describe('mouseleave', () => {
+ it('calls hide popover if .popover:hover is false', () => {
+ const fakeJquery = {
+ length: 0,
+ };
+
+ spyOn($.fn, 'init').and.callFake(selector => (selector === '.popover:hover' ? fakeJquery : $.fn));
+ spyOn(togglePopover, 'call');
+ mouseleave();
+ expect(togglePopover.call).toHaveBeenCalledWith(jasmine.any(Object), false);
+ });
+
+ it('does not call hide popover if .popover:hover is true', () => {
+ const fakeJquery = {
+ length: 1,
+ };
+
+ spyOn($.fn, 'init').and.callFake(selector => (selector === '.popover:hover' ? fakeJquery : $.fn));
+ spyOn(togglePopover, 'call');
+ mouseleave();
+ expect(togglePopover.call).not.toHaveBeenCalledWith(false);
+ });
+ });
+
+ describe('mouseenter', () => {
+ const context = {};
+
+ it('shows popover', () => {
+ spyOn(togglePopover, 'call').and.returnValue(false);
+ mouseenter.call(context);
+ expect(togglePopover.call).toHaveBeenCalledWith(jasmine.any(Object), true);
+ });
+
+ it('registers mouseleave event if popover is showed', (done) => {
+ spyOn(togglePopover, 'call').and.returnValue(true);
+ spyOn($.fn, 'on').and.callFake((eventName) => {
+ expect(eventName).toEqual('mouseleave');
+ done();
+ });
+ mouseenter.call(context);
+ });
+
+ it('does not register mouseleave event if popover is not showed', () => {
+ spyOn(togglePopover, 'call').and.returnValue(false);
+ const spy = spyOn($.fn, 'on').and.callFake(() => {});
+ mouseenter.call(context);
+ expect(spy).not.toHaveBeenCalled();
+ });
+ });
+
+ describe('inserted', () => {
+ it('registers click event callback', (done) => {
+ const context = {
+ getAttribute: () => 'popoverId',
+ dataset: {
+ highlight: 'some-feature',
+ },
+ };
+
+ spyOn($.fn, 'on').and.callFake((event) => {
+ expect(event).toEqual('click');
+ done();
+ });
+ inserted.call(context);
+ });
+ });
+});
diff --git a/spec/javascripts/feature_highlight/feature_highlight_options_spec.js b/spec/javascripts/feature_highlight/feature_highlight_options_spec.js
new file mode 100644
index 00000000000..7f9425d8abe
--- /dev/null
+++ b/spec/javascripts/feature_highlight/feature_highlight_options_spec.js
@@ -0,0 +1,30 @@
+import domContentLoaded from '~/feature_highlight/feature_highlight_options';
+import bp from '~/breakpoints';
+
+describe('feature highlight options', () => {
+ describe('domContentLoaded', () => {
+ it('should not call highlightFeatures when breakpoint is xs', () => {
+ spyOn(bp, 'getBreakpointSize').and.returnValue('xs');
+
+ expect(domContentLoaded()).toBe(false);
+ });
+
+ it('should not call highlightFeatures when breakpoint is sm', () => {
+ spyOn(bp, 'getBreakpointSize').and.returnValue('sm');
+
+ expect(domContentLoaded()).toBe(false);
+ });
+
+ it('should not call highlightFeatures when breakpoint is md', () => {
+ spyOn(bp, 'getBreakpointSize').and.returnValue('md');
+
+ expect(domContentLoaded()).toBe(false);
+ });
+
+ it('should call highlightFeatures when breakpoint is lg', () => {
+ spyOn(bp, 'getBreakpointSize').and.returnValue('lg');
+
+ expect(domContentLoaded()).toBe(true);
+ });
+ });
+});
diff --git a/spec/javascripts/feature_highlight/feature_highlight_spec.js b/spec/javascripts/feature_highlight/feature_highlight_spec.js
new file mode 100644
index 00000000000..6e1b0429ab7
--- /dev/null
+++ b/spec/javascripts/feature_highlight/feature_highlight_spec.js
@@ -0,0 +1,131 @@
+import * as featureHighlightHelper from '~/feature_highlight/feature_highlight_helper';
+import * as featureHighlight from '~/feature_highlight/feature_highlight';
+
+describe('feature highlight', () => {
+ beforeEach(() => {
+ setFixtures(`
+ <div>
+ <div class="js-feature-highlight" data-highlight="test" data-highlight-priority="10" disabled>
+ Trigger
+ </div>
+ </div>
+ <div class="feature-highlight-popover-content">
+ Content
+ <div class="dismiss-feature-highlight">
+ Dismiss
+ </div>
+ </div>
+ `);
+ });
+
+ describe('setupFeatureHighlightPopover', () => {
+ const selector = '.js-feature-highlight[data-highlight=test]';
+ beforeEach(() => {
+ spyOn(window, 'addEventListener');
+ spyOn(window, 'removeEventListener');
+ featureHighlight.setupFeatureHighlightPopover('test', 0);
+ });
+
+ it('setup popover content', () => {
+ const $popoverContent = $('.feature-highlight-popover-content');
+ const outerHTML = $popoverContent.prop('outerHTML');
+
+ expect($(selector).data('content')).toEqual(outerHTML);
+ });
+
+ it('setup mouseenter', () => {
+ const toggleSpy = spyOn(featureHighlightHelper.togglePopover, 'call');
+ $(selector).trigger('mouseenter');
+
+ expect(toggleSpy).toHaveBeenCalledWith(jasmine.any(Object), true);
+ });
+
+ it('setup debounced mouseleave', (done) => {
+ const toggleSpy = spyOn(featureHighlightHelper.togglePopover, 'call');
+ $(selector).trigger('mouseleave');
+
+ // Even though we've set the debounce to 0ms, setTimeout is needed for the debounce
+ setTimeout(() => {
+ expect(toggleSpy).toHaveBeenCalledWith(jasmine.any(Object), false);
+ done();
+ }, 0);
+ });
+
+ it('setup inserted.bs.popover', () => {
+ $(selector).trigger('mouseenter');
+ const popoverId = $(selector).attr('aria-describedby');
+ const spyEvent = spyOnEvent(`#${popoverId} .dismiss-feature-highlight`, 'click');
+
+ $(`#${popoverId} .dismiss-feature-highlight`).click();
+ expect(spyEvent).toHaveBeenTriggered();
+ });
+
+ it('setup show.bs.popover', () => {
+ $(selector).trigger('show.bs.popover');
+ expect(window.addEventListener).toHaveBeenCalledWith('scroll', jasmine.any(Function));
+ });
+
+ it('setup hide.bs.popover', () => {
+ $(selector).trigger('hide.bs.popover');
+ expect(window.removeEventListener).toHaveBeenCalledWith('scroll', jasmine.any(Function));
+ });
+
+ it('removes disabled attribute', () => {
+ expect($('.js-feature-highlight').is(':disabled')).toEqual(false);
+ });
+
+ it('displays popover', () => {
+ expect($(selector).attr('aria-describedby')).toBeFalsy();
+ $(selector).trigger('mouseenter');
+ expect($(selector).attr('aria-describedby')).toBeTruthy();
+ });
+ });
+
+ describe('findHighestPriorityFeature', () => {
+ beforeEach(() => {
+ setFixtures(`
+ <div class="js-feature-highlight" data-highlight="test" data-highlight-priority="10" disabled></div>
+ <div class="js-feature-highlight" data-highlight="test-high-priority" data-highlight-priority="20" disabled></div>
+ <div class="js-feature-highlight" data-highlight="test-low-priority" data-highlight-priority="0" disabled></div>
+ `);
+ });
+
+ it('should pick the highest priority feature highlight', () => {
+ setFixtures(`
+ <div class="js-feature-highlight" data-highlight="test" data-highlight-priority="10" disabled></div>
+ <div class="js-feature-highlight" data-highlight="test-high-priority" data-highlight-priority="20" disabled></div>
+ <div class="js-feature-highlight" data-highlight="test-low-priority" data-highlight-priority="0" disabled></div>
+ `);
+
+ expect($('.js-feature-highlight').length).toBeGreaterThan(1);
+ expect(featureHighlight.findHighestPriorityFeature()).toEqual('test-high-priority');
+ });
+
+ it('should work when no priority is set', () => {
+ setFixtures(`
+ <div class="js-feature-highlight" data-highlight="test" disabled></div>
+ `);
+
+ expect(featureHighlight.findHighestPriorityFeature()).toEqual('test');
+ });
+
+ it('should pick the highest priority feature highlight when some have no priority set', () => {
+ setFixtures(`
+ <div class="js-feature-highlight" data-highlight="test-no-priority1" disabled></div>
+ <div class="js-feature-highlight" data-highlight="test" data-highlight-priority="10" disabled></div>
+ <div class="js-feature-highlight" data-highlight="test-no-priority2" disabled></div>
+ <div class="js-feature-highlight" data-highlight="test-high-priority" data-highlight-priority="20" disabled></div>
+ <div class="js-feature-highlight" data-highlight="test-low-priority" data-highlight-priority="0" disabled></div>
+ `);
+
+ expect($('.js-feature-highlight').length).toBeGreaterThan(1);
+ expect(featureHighlight.findHighestPriorityFeature()).toEqual('test-high-priority');
+ });
+ });
+
+ describe('highlightFeatures', () => {
+ it('calls setupFeatureHighlightPopover', () => {
+ expect(featureHighlight.highlightFeatures()).toEqual('test');
+ });
+ });
+});
diff --git a/spec/javascripts/filtered_search/components/recent_searches_dropdown_content_spec.js b/spec/javascripts/filtered_search/components/recent_searches_dropdown_content_spec.js
index 79447787fc9..4a516c517ef 100644
--- a/spec/javascripts/filtered_search/components/recent_searches_dropdown_content_spec.js
+++ b/spec/javascripts/filtered_search/components/recent_searches_dropdown_content_spec.js
@@ -2,7 +2,7 @@ import Vue from 'vue';
import eventHub from '~/filtered_search/event_hub';
import RecentSearchesDropdownContent from '~/filtered_search/components/recent_searches_dropdown_content';
-import '~/filtered_search/filtered_search_token_keys';
+import FilteredSearchTokenKeys from '~/filtered_search/filtered_search_token_keys';
const createComponent = (propsData) => {
const Component = Vue.extend(RecentSearchesDropdownContent);
@@ -19,14 +19,14 @@ const trimMarkupWhitespace = text => text.replace(/(\n|\s)+/gm, ' ').trim();
describe('RecentSearchesDropdownContent', () => {
const propsDataWithoutItems = {
items: [],
- allowedKeys: gl.FilteredSearchTokenKeys.getKeys(),
+ allowedKeys: FilteredSearchTokenKeys.getKeys(),
};
const propsDataWithItems = {
items: [
'foo',
'author:@root label:~foo bar',
],
- allowedKeys: gl.FilteredSearchTokenKeys.getKeys(),
+ allowedKeys: FilteredSearchTokenKeys.getKeys(),
};
let vm;
diff --git a/spec/javascripts/filtered_search/dropdown_user_spec.js b/spec/javascripts/filtered_search/dropdown_user_spec.js
index 02415485d19..f1e6119253e 100644
--- a/spec/javascripts/filtered_search/dropdown_user_spec.js
+++ b/spec/javascripts/filtered_search/dropdown_user_spec.js
@@ -3,6 +3,8 @@ import '~/filtered_search/filtered_search_tokenizer';
import '~/filtered_search/filtered_search_dropdown';
import '~/filtered_search/dropdown_user';
+import FilteredSearchTokenKeys from '~/filtered_search/filtered_search_token_keys';
+
describe('Dropdown User', () => {
describe('getSearchInput', () => {
let dropdownUser;
@@ -14,7 +16,7 @@ describe('Dropdown User', () => {
spyOn(gl.DropdownUtils, 'getSearchInput').and.callFake(() => {});
dropdownUser = new gl.DropdownUser({
- tokenKeys: gl.FilteredSearchTokenKeys,
+ tokenKeys: FilteredSearchTokenKeys,
});
});
diff --git a/spec/javascripts/filtered_search/dropdown_utils_spec.js b/spec/javascripts/filtered_search/dropdown_utils_spec.js
index b1b3d43f241..d6e1af105f1 100644
--- a/spec/javascripts/filtered_search/dropdown_utils_spec.js
+++ b/spec/javascripts/filtered_search/dropdown_utils_spec.js
@@ -1,6 +1,7 @@
import '~/filtered_search/dropdown_utils';
import '~/filtered_search/filtered_search_tokenizer';
import '~/filtered_search/filtered_search_dropdown_manager';
+import FilteredSearchTokenKeys from '~/filtered_search/filtered_search_token_keys';
import FilteredSearchSpecHelper from '../helpers/filtered_search_spec_helper';
describe('Dropdown Utils', () => {
@@ -137,7 +138,7 @@ describe('Dropdown Utils', () => {
`);
input = document.getElementById('test');
- allowedKeys = gl.FilteredSearchTokenKeys.getKeys();
+ allowedKeys = FilteredSearchTokenKeys.getKeys();
});
function config() {
diff --git a/spec/javascripts/filtered_search/filtered_search_manager_spec.js b/spec/javascripts/filtered_search/filtered_search_manager_spec.js
index b8890e4cda1..0ed9a587dc1 100644
--- a/spec/javascripts/filtered_search/filtered_search_manager_spec.js
+++ b/spec/javascripts/filtered_search/filtered_search_manager_spec.js
@@ -3,8 +3,8 @@ import * as recentSearchesStoreSrc from '~/filtered_search/stores/recent_searche
import RecentSearchesService from '~/filtered_search/services/recent_searches_service';
import RecentSearchesServiceError from '~/filtered_search/services/recent_searches_service_error';
import RecentSearchesRoot from '~/filtered_search/recent_searches_root';
+import FilteredSearchTokenKeys from '~/filtered_search/filtered_search_token_keys';
import '~/lib/utils/common_utils';
-import '~/filtered_search/filtered_search_token_keys';
import '~/filtered_search/filtered_search_tokenizer';
import '~/filtered_search/filtered_search_dropdown_manager';
import '~/filtered_search/filtered_search_manager';
@@ -14,6 +14,7 @@ describe('Filtered Search Manager', () => {
let input;
let manager;
let tokensContainer;
+ const page = 'issues';
const placeholder = 'Search or filter results...';
function dispatchBackspaceEvent(element, eventType) {
@@ -62,7 +63,7 @@ describe('Filtered Search Manager', () => {
input = document.querySelector('.filtered-search');
tokensContainer = document.querySelector('.tokens-container');
- manager = new gl.FilteredSearchManager();
+ manager = new gl.FilteredSearchManager({ page });
manager.setup();
};
@@ -80,19 +81,19 @@ describe('Filtered Search Manager', () => {
});
it('should instantiate RecentSearchesStore with isLocalStorageAvailable', () => {
- manager = new gl.FilteredSearchManager();
+ manager = new gl.FilteredSearchManager({ page });
expect(RecentSearchesService.isAvailable).toHaveBeenCalled();
expect(recentSearchesStoreSrc.default).toHaveBeenCalledWith({
isLocalStorageAvailable,
- allowedKeys: gl.FilteredSearchTokenKeys.getKeys(),
+ allowedKeys: FilteredSearchTokenKeys.getKeys(),
});
});
});
describe('setup', () => {
beforeEach(() => {
- manager = new gl.FilteredSearchManager();
+ manager = new gl.FilteredSearchManager({ page });
});
it('should not instantiate Flash if an RecentSearchesServiceError is caught', () => {
diff --git a/spec/javascripts/filtered_search/filtered_search_token_keys_spec.js b/spec/javascripts/filtered_search/filtered_search_token_keys_spec.js
index 69b424c3af5..fbc3926d332 100644
--- a/spec/javascripts/filtered_search/filtered_search_token_keys_spec.js
+++ b/spec/javascripts/filtered_search/filtered_search_token_keys_spec.js
@@ -1,11 +1,11 @@
-import '~/filtered_search/filtered_search_token_keys';
+import FilteredSearchTokenKeys from '~/filtered_search/filtered_search_token_keys';
describe('Filtered Search Token Keys', () => {
describe('get', () => {
let tokenKeys;
beforeEach(() => {
- tokenKeys = gl.FilteredSearchTokenKeys.get();
+ tokenKeys = FilteredSearchTokenKeys.get();
});
it('should return tokenKeys', () => {
@@ -21,7 +21,7 @@ describe('Filtered Search Token Keys', () => {
let conditions;
beforeEach(() => {
- conditions = gl.FilteredSearchTokenKeys.getConditions();
+ conditions = FilteredSearchTokenKeys.getConditions();
});
it('should return conditions', () => {
@@ -35,71 +35,71 @@ describe('Filtered Search Token Keys', () => {
describe('searchByKey', () => {
it('should return null when key not found', () => {
- const tokenKey = gl.FilteredSearchTokenKeys.searchByKey('notakey');
+ const tokenKey = FilteredSearchTokenKeys.searchByKey('notakey');
expect(tokenKey === null).toBe(true);
});
it('should return tokenKey when found by key', () => {
- const tokenKeys = gl.FilteredSearchTokenKeys.get();
- const result = gl.FilteredSearchTokenKeys.searchByKey(tokenKeys[0].key);
+ const tokenKeys = FilteredSearchTokenKeys.get();
+ const result = FilteredSearchTokenKeys.searchByKey(tokenKeys[0].key);
expect(result).toEqual(tokenKeys[0]);
});
});
describe('searchBySymbol', () => {
it('should return null when symbol not found', () => {
- const tokenKey = gl.FilteredSearchTokenKeys.searchBySymbol('notasymbol');
+ const tokenKey = FilteredSearchTokenKeys.searchBySymbol('notasymbol');
expect(tokenKey === null).toBe(true);
});
it('should return tokenKey when found by symbol', () => {
- const tokenKeys = gl.FilteredSearchTokenKeys.get();
- const result = gl.FilteredSearchTokenKeys.searchBySymbol(tokenKeys[0].symbol);
+ const tokenKeys = FilteredSearchTokenKeys.get();
+ const result = FilteredSearchTokenKeys.searchBySymbol(tokenKeys[0].symbol);
expect(result).toEqual(tokenKeys[0]);
});
});
describe('searchByKeyParam', () => {
it('should return null when key param not found', () => {
- const tokenKey = gl.FilteredSearchTokenKeys.searchByKeyParam('notakeyparam');
+ const tokenKey = FilteredSearchTokenKeys.searchByKeyParam('notakeyparam');
expect(tokenKey === null).toBe(true);
});
it('should return tokenKey when found by key param', () => {
- const tokenKeys = gl.FilteredSearchTokenKeys.get();
- const result = gl.FilteredSearchTokenKeys.searchByKeyParam(`${tokenKeys[0].key}_${tokenKeys[0].param}`);
+ const tokenKeys = FilteredSearchTokenKeys.get();
+ const result = FilteredSearchTokenKeys.searchByKeyParam(`${tokenKeys[0].key}_${tokenKeys[0].param}`);
expect(result).toEqual(tokenKeys[0]);
});
it('should return alternative tokenKey when found by key param', () => {
- const tokenKeys = gl.FilteredSearchTokenKeys.getAlternatives();
- const result = gl.FilteredSearchTokenKeys.searchByKeyParam(`${tokenKeys[0].key}_${tokenKeys[0].param}`);
+ const tokenKeys = FilteredSearchTokenKeys.getAlternatives();
+ const result = FilteredSearchTokenKeys.searchByKeyParam(`${tokenKeys[0].key}_${tokenKeys[0].param}`);
expect(result).toEqual(tokenKeys[0]);
});
});
describe('searchByConditionUrl', () => {
it('should return null when condition url not found', () => {
- const condition = gl.FilteredSearchTokenKeys.searchByConditionUrl(null);
+ const condition = FilteredSearchTokenKeys.searchByConditionUrl(null);
expect(condition === null).toBe(true);
});
it('should return condition when found by url', () => {
- const conditions = gl.FilteredSearchTokenKeys.getConditions();
- const result = gl.FilteredSearchTokenKeys.searchByConditionUrl(conditions[0].url);
+ const conditions = FilteredSearchTokenKeys.getConditions();
+ const result = FilteredSearchTokenKeys.searchByConditionUrl(conditions[0].url);
expect(result).toBe(conditions[0]);
});
});
describe('searchByConditionKeyValue', () => {
it('should return null when condition tokenKey and value not found', () => {
- const condition = gl.FilteredSearchTokenKeys.searchByConditionKeyValue(null, null);
+ const condition = FilteredSearchTokenKeys.searchByConditionKeyValue(null, null);
expect(condition === null).toBe(true);
});
it('should return condition when found by tokenKey and value', () => {
- const conditions = gl.FilteredSearchTokenKeys.getConditions();
- const result = gl.FilteredSearchTokenKeys
+ const conditions = FilteredSearchTokenKeys.getConditions();
+ const result = FilteredSearchTokenKeys
.searchByConditionKeyValue(conditions[0].tokenKey, conditions[0].value);
expect(result).toEqual(conditions[0]);
});
diff --git a/spec/javascripts/filtered_search/filtered_search_tokenizer_spec.js b/spec/javascripts/filtered_search/filtered_search_tokenizer_spec.js
index 585bea9b499..bf8b66f1110 100644
--- a/spec/javascripts/filtered_search/filtered_search_tokenizer_spec.js
+++ b/spec/javascripts/filtered_search/filtered_search_tokenizer_spec.js
@@ -1,8 +1,8 @@
-import '~/filtered_search/filtered_search_token_keys';
+import FilteredSearchTokenKeys from '~/filtered_search/filtered_search_token_keys';
import '~/filtered_search/filtered_search_tokenizer';
describe('Filtered Search Tokenizer', () => {
- const allowedKeys = gl.FilteredSearchTokenKeys.getKeys();
+ const allowedKeys = FilteredSearchTokenKeys.getKeys();
describe('processTokens', () => {
it('returns for input containing only search value', () => {
diff --git a/spec/javascripts/fixtures/groups.rb b/spec/javascripts/fixtures/groups.rb
new file mode 100644
index 00000000000..35be52fbf97
--- /dev/null
+++ b/spec/javascripts/fixtures/groups.rb
@@ -0,0 +1,29 @@
+require 'spec_helper'
+
+describe 'Groups (JavaScript fixtures)', type: :controller do
+ include JavaScriptFixturesHelpers
+
+ let(:admin) { create(:admin) }
+ let(:group) { create(:group, name: 'frontend-fixtures-group' )}
+
+ render_views
+
+ before(:all) do
+ clean_frontend_fixtures('groups/')
+ end
+
+ before do
+ group.add_master(admin)
+ sign_in(admin)
+ end
+
+ describe Groups::Settings::CiCdController, '(JavaScript fixtures)', type: :controller do
+ it 'groups/ci_cd_settings.html.raw' do |example|
+ get :show,
+ group_id: group
+
+ expect(response).to be_success
+ store_frontend_fixture(response, example.description)
+ end
+ end
+end
diff --git a/spec/javascripts/fixtures/jobs.rb b/spec/javascripts/fixtures/jobs.rb
index 87d131dfe28..6d5c6d5334f 100644
--- a/spec/javascripts/fixtures/jobs.rb
+++ b/spec/javascripts/fixtures/jobs.rb
@@ -7,7 +7,7 @@ describe Projects::JobsController, '(JavaScript fixtures)', type: :controller do
let(:namespace) { create(:namespace, name: 'frontend-fixtures' )}
let(:project) { create(:project_empty_repo, namespace: namespace, path: 'builds-project') }
let(:pipeline) { create(:ci_empty_pipeline, project: project) }
- let!(:build_with_artifacts) { create(:ci_build, :success, :artifacts, :trace, pipeline: pipeline, stage: 'test', artifacts_expire_at: Time.now + 18.months) }
+ let!(:build_with_artifacts) { create(:ci_build, :success, :artifacts, :trace_artifact, pipeline: pipeline, stage: 'test', artifacts_expire_at: Time.now + 18.months) }
let!(:failed_build) { create(:ci_build, :failed, pipeline: pipeline, stage: 'build') }
let!(:pending_build) { create(:ci_build, :pending, pipeline: pipeline, stage: 'deploy') }
diff --git a/spec/javascripts/fixtures/projects.rb b/spec/javascripts/fixtures/projects.rb
index 2a100e7fab5..b344b389241 100644
--- a/spec/javascripts/fixtures/projects.rb
+++ b/spec/javascripts/fixtures/projects.rb
@@ -1,11 +1,14 @@
require 'spec_helper'
-describe ProjectsController, '(JavaScript fixtures)', type: :controller do
+describe 'Projects (JavaScript fixtures)', type: :controller do
include JavaScriptFixturesHelpers
let(:admin) { create(:admin) }
let(:namespace) { create(:namespace, name: 'frontend-fixtures' )}
let(:project) { create(:project, namespace: namespace, path: 'builds-project') }
+ let(:project_variable_populated) { create(:project, namespace: namespace, path: 'builds-project2') }
+ let!(:variable1) { create(:ci_variable, project: project_variable_populated) }
+ let!(:variable2) { create(:ci_variable, project: project_variable_populated) }
render_views
@@ -14,6 +17,9 @@ describe ProjectsController, '(JavaScript fixtures)', type: :controller do
end
before do
+ # EE-specific start
+ # EE specific end
+ project.add_master(admin)
sign_in(admin)
end
@@ -21,12 +27,43 @@ describe ProjectsController, '(JavaScript fixtures)', type: :controller do
remove_repository(project)
end
- it 'projects/dashboard.html.raw' do |example|
- get :show,
- namespace_id: project.namespace.to_param,
- id: project
+ describe ProjectsController, '(JavaScript fixtures)', type: :controller do
+ it 'projects/dashboard.html.raw' do |example|
+ get :show,
+ namespace_id: project.namespace.to_param,
+ id: project
- expect(response).to be_success
- store_frontend_fixture(response, example.description)
+ expect(response).to be_success
+ store_frontend_fixture(response, example.description)
+ end
+
+ it 'projects/edit.html.raw' do |example|
+ get :edit,
+ namespace_id: project.namespace.to_param,
+ id: project
+
+ expect(response).to be_success
+ store_frontend_fixture(response, example.description)
+ end
+ end
+
+ describe Projects::Settings::CiCdController, '(JavaScript fixtures)', type: :controller do
+ it 'projects/ci_cd_settings.html.raw' do |example|
+ get :show,
+ namespace_id: project.namespace.to_param,
+ project_id: project
+
+ expect(response).to be_success
+ store_frontend_fixture(response, example.description)
+ end
+
+ it 'projects/ci_cd_settings_with_variables.html.raw' do |example|
+ get :show,
+ namespace_id: project_variable_populated.namespace.to_param,
+ project_id: project_variable_populated
+
+ expect(response).to be_success
+ store_frontend_fixture(response, example.description)
+ end
end
end
diff --git a/spec/javascripts/gl_form_spec.js b/spec/javascripts/gl_form_spec.js
index 5a8009e57fd..9c1fc0fda9e 100644
--- a/spec/javascripts/gl_form_spec.js
+++ b/spec/javascripts/gl_form_spec.js
@@ -1,10 +1,8 @@
-import Autosize from 'autosize';
+import autosize from 'autosize';
import GLForm from '~/gl_form';
import '~/lib/utils/text_utility';
import '~/lib/utils/common_utils';
-window.autosize = Autosize;
-
describe('GLForm', () => {
describe('when instantiated', function () {
beforeEach((done) => {
@@ -13,14 +11,12 @@ describe('GLForm', () => {
spyOn($.prototype, 'off').and.returnValue(this.textarea);
spyOn($.prototype, 'on').and.returnValue(this.textarea);
spyOn($.prototype, 'css');
- spyOn(window, 'autosize');
- this.glForm = new GLForm(this.form);
+ this.glForm = new GLForm(this.form, false);
setTimeout(() => {
$.prototype.off.calls.reset();
$.prototype.on.calls.reset();
$.prototype.css.calls.reset();
- window.autosize.calls.reset();
done();
});
});
@@ -43,10 +39,6 @@ describe('GLForm', () => {
expect($.prototype.on).toHaveBeenCalledWith('mouseup.autosize', jasmine.any(Function));
});
- it('should autosize the textarea', () => {
- expect(window.autosize).toHaveBeenCalledWith(jasmine.any(Object));
- });
-
it('should set the resize css property to vertical', () => {
expect($.prototype.css).toHaveBeenCalledWith('resize', 'vertical');
});
@@ -74,7 +66,7 @@ describe('GLForm', () => {
spyOn($.prototype, 'data');
spyOn($.prototype, 'outerHeight').and.returnValue(200);
spyOn(window, 'outerHeight').and.returnValue(400);
- spyOn(window.autosize, 'destroy');
+ spyOn(autosize, 'destroy');
this.glForm.destroyAutosize();
});
@@ -88,7 +80,7 @@ describe('GLForm', () => {
});
it('should call autosize destroy', () => {
- expect(window.autosize.destroy).toHaveBeenCalledWith(this.textarea);
+ expect(autosize.destroy).toHaveBeenCalledWith(this.textarea);
});
it('should set the data-height attribute', () => {
@@ -107,9 +99,9 @@ describe('GLForm', () => {
it('should return undefined if the data-height equals the outerHeight', () => {
spyOn($.prototype, 'outerHeight').and.returnValue(200);
spyOn($.prototype, 'data').and.returnValue(200);
- spyOn(window.autosize, 'destroy');
+ spyOn(autosize, 'destroy');
expect(this.glForm.destroyAutosize()).toBeUndefined();
- expect(window.autosize.destroy).not.toHaveBeenCalled();
+ expect(autosize.destroy).not.toHaveBeenCalled();
});
});
});
diff --git a/spec/javascripts/gpg_badges_spec.js b/spec/javascripts/gpg_badges_spec.js
index 7a826487bf9..5decb5e6bbd 100644
--- a/spec/javascripts/gpg_badges_spec.js
+++ b/spec/javascripts/gpg_badges_spec.js
@@ -1,6 +1,9 @@
+import MockAdapter from 'axios-mock-adapter';
+import axios from '~/lib/utils/axios_utils';
import GpgBadges from '~/gpg_badges';
describe('GpgBadges', () => {
+ let mock;
const dummyCommitSha = 'n0m0rec0ffee';
const dummyBadgeHtml = 'dummy html';
const dummyResponse = {
@@ -11,38 +14,43 @@ describe('GpgBadges', () => {
};
beforeEach(() => {
+ mock = new MockAdapter(axios);
setFixtures(`
+ <form
+ class="commits-search-form" data-signatures-path="/hello" action="/hello"
+ method="get">
+ <input name="utf8" type="hidden" value="✓">
+ <input type="search" name="search" id="commits-search"class="form-control search-text-input input-short">
+ </form>
<div class="parent-container">
<div class="js-loading-gpg-badge" data-commit-sha="${dummyCommitSha}"></div>
</div>
`);
});
- it('displays a loading spinner', () => {
- spyOn($, 'get').and.returnValue({
- done() {
- // intentionally left blank
- },
- });
+ afterEach(() => {
+ mock.restore();
+ });
- GpgBadges.fetch();
+ it('displays a loading spinner', (done) => {
+ mock.onGet('/hello').reply(200);
- expect(document.querySelector('.js-loading-gpg-badge:empty')).toBe(null);
- const spinners = document.querySelectorAll('.js-loading-gpg-badge i.fa.fa-spinner.fa-spin');
- expect(spinners.length).toBe(1);
+ GpgBadges.fetch().then(() => {
+ expect(document.querySelector('.js-loading-gpg-badge:empty')).toBe(null);
+ const spinners = document.querySelectorAll('.js-loading-gpg-badge i.fa.fa-spinner.fa-spin');
+ expect(spinners.length).toBe(1);
+ done();
+ }).catch(done.fail);
});
- it('replaces the loading spinner', () => {
- spyOn($, 'get').and.returnValue({
- done(callback) {
- callback(dummyResponse);
- },
- });
-
- GpgBadges.fetch();
+ it('replaces the loading spinner', (done) => {
+ mock.onGet('/hello').reply(200, dummyResponse);
- expect(document.querySelector('.js-loading-gpg-badge')).toBe(null);
- const parentContainer = document.querySelector('.parent-container');
- expect(parentContainer.innerHTML.trim()).toEqual(dummyBadgeHtml);
+ GpgBadges.fetch().then(() => {
+ expect(document.querySelector('.js-loading-gpg-badge')).toBe(null);
+ const parentContainer = document.querySelector('.parent-container');
+ expect(parentContainer.innerHTML.trim()).toEqual(dummyBadgeHtml);
+ done();
+ }).catch(done.fail);
});
});
diff --git a/spec/javascripts/groups/components/app_spec.js b/spec/javascripts/groups/components/app_spec.js
index 8338efe915b..3adc29262f3 100644
--- a/spec/javascripts/groups/components/app_spec.js
+++ b/spec/javascripts/groups/components/app_spec.js
@@ -268,10 +268,10 @@ describe('AppComponent', () => {
it('updates props which show modal confirmation dialog', () => {
const group = Object.assign({}, mockParentGroupItem);
- expect(vm.showModal).toBeFalsy();
+ expect(vm.updateModal).toBeFalsy();
expect(vm.groupLeaveConfirmationMessage).toBe('');
vm.showLeaveGroupModal(group, mockParentGroupItem);
- expect(vm.showModal).toBeTruthy();
+ expect(vm.updateModal).toBeTruthy();
expect(vm.groupLeaveConfirmationMessage).toBe(`Are you sure you want to leave the "${group.fullName}" group?`);
});
});
@@ -280,9 +280,9 @@ describe('AppComponent', () => {
it('hides modal confirmation which is shown before leaving the group', () => {
const group = Object.assign({}, mockParentGroupItem);
vm.showLeaveGroupModal(group, mockParentGroupItem);
- expect(vm.showModal).toBeTruthy();
+ expect(vm.updateModal).toBeTruthy();
vm.hideLeaveGroupModal();
- expect(vm.showModal).toBeFalsy();
+ expect(vm.updateModal).toBeFalsy();
});
});
@@ -307,7 +307,7 @@ describe('AppComponent', () => {
spyOn($, 'scrollTo');
vm.leaveGroup();
- expect(vm.showModal).toBeFalsy();
+ expect(vm.updateModal).toBeFalsy();
expect(vm.targetGroup.isBeingRemoved).toBeTruthy();
expect(vm.service.leaveGroup).toHaveBeenCalledWith(vm.targetGroup.leavePath);
setTimeout(() => {
@@ -475,7 +475,7 @@ describe('AppComponent', () => {
it('renders modal confirmation dialog', () => {
vm.groupLeaveConfirmationMessage = 'Are you sure you want to leave the "foo" group?';
- vm.showModal = true;
+ vm.updateModal = true;
const modalDialogEl = vm.$el.querySelector('.modal');
expect(modalDialogEl).not.toBe(null);
expect(modalDialogEl.querySelector('.modal-title').innerText.trim()).toBe('Are you sure?');
diff --git a/spec/javascripts/importer_status_spec.js b/spec/javascripts/importer_status_spec.js
new file mode 100644
index 00000000000..bb49c576e91
--- /dev/null
+++ b/spec/javascripts/importer_status_spec.js
@@ -0,0 +1,47 @@
+import { ImporterStatus } from '~/importer_status';
+import axios from '~/lib/utils/axios_utils';
+import MockAdapter from 'axios-mock-adapter';
+
+describe('Importer Status', () => {
+ describe('addToImport', () => {
+ let instance;
+ let mock;
+ const importUrl = '/import_url';
+
+ beforeEach(() => {
+ setFixtures(`
+ <tr id="repo_123">
+ <td class="import-target"></td>
+ <td class="import-actions job-status">
+ <button name="button" type="submit" class="btn btn-import js-add-to-import">
+ </button>
+ </td>
+ </tr>
+ `);
+ spyOn(ImporterStatus.prototype, 'initStatusPage').and.callFake(() => {});
+ spyOn(ImporterStatus.prototype, 'setAutoUpdate').and.callFake(() => {});
+ instance = new ImporterStatus('', importUrl);
+ mock = new MockAdapter(axios);
+ });
+
+ afterEach(() => {
+ mock.restore();
+ });
+
+ it('sets table row to active after post request', (done) => {
+ mock.onPost(importUrl).reply(200, {
+ id: 1,
+ full_path: '/full_path',
+ });
+
+ instance.addToImport({
+ currentTarget: document.querySelector('.js-add-to-import'),
+ })
+ .then(() => {
+ expect(document.querySelector('tr').classList.contains('active')).toEqual(true);
+ done();
+ })
+ .catch(done.fail);
+ });
+ });
+});
diff --git a/spec/javascripts/issuable_time_tracker_spec.js b/spec/javascripts/issuable_time_tracker_spec.js
index 8ff93c4f918..365e9fe6a4b 100644
--- a/spec/javascripts/issuable_time_tracker_spec.js
+++ b/spec/javascripts/issuable_time_tracker_spec.js
@@ -2,7 +2,7 @@
import Vue from 'vue';
-import timeTracker from '~/sidebar/components/time_tracking/time_tracker';
+import timeTracker from '~/sidebar/components/time_tracking/time_tracker.vue';
function initTimeTrackingComponent(opts) {
setFixtures(`
diff --git a/spec/javascripts/job_spec.js b/spec/javascripts/job_spec.js
index 03b58e9c1d0..b4599688c6d 100644
--- a/spec/javascripts/job_spec.js
+++ b/spec/javascripts/job_spec.js
@@ -10,6 +10,7 @@ describe('Job', () => {
const JOB_URL = `${gl.TEST_HOST}/frontend-fixtures/builds-project/-/jobs/1`;
let mock;
let response;
+ let job;
function waitForPromise() {
return new Promise(resolve => requestAnimationFrame(resolve));
@@ -22,6 +23,8 @@ describe('Job', () => {
spyOn(urlUtils, 'visitUrl');
+ response = {};
+
mock = new MockAdapter(axios);
mock.onGet(new RegExp(`${JOB_URL}/trace.json?(.*)`)).reply(() => [200, response]);
@@ -30,7 +33,7 @@ describe('Job', () => {
afterEach(() => {
mock.restore();
- response = {};
+ clearTimeout(job.timeout);
});
describe('class constructor', () => {
@@ -43,15 +46,19 @@ describe('Job', () => {
});
describe('setup', () => {
- beforeEach(function () {
- this.job = new Job();
+ beforeEach(function (done) {
+ job = new Job();
+
+ waitForPromise()
+ .then(done)
+ .catch(done.fail);
});
it('copies build options', function () {
- expect(this.job.pagePath).toBe(JOB_URL);
- expect(this.job.buildStatus).toBe('success');
- expect(this.job.buildStage).toBe('test');
- expect(this.job.state).toBe('');
+ expect(job.pagePath).toBe(JOB_URL);
+ expect(job.buildStatus).toBe('success');
+ expect(job.buildStage).toBe('test');
+ expect(job.state).toBe('');
});
it('only shows the jobs matching the current stage', () => {
@@ -84,12 +91,12 @@ describe('Job', () => {
complete: false,
};
- this.job = new Job();
+ job = new Job();
waitForPromise()
.then(() => {
expect($('#build-trace .js-build-output').text()).toMatch(/Update/);
- expect(this.job.state).toBe('newstate');
+ expect(job.state).toBe('newstate');
response = {
html: '<span>More</span>',
@@ -103,7 +110,7 @@ describe('Job', () => {
.then(waitForPromise)
.then(() => {
expect($('#build-trace .js-build-output').text()).toMatch(/UpdateMore/);
- expect(this.job.state).toBe('finalstate');
+ expect(job.state).toBe('finalstate');
})
.then(done)
.catch(done.fail);
@@ -117,7 +124,7 @@ describe('Job', () => {
complete: false,
};
- this.job = new Job();
+ job = new Job();
waitForPromise()
.then(() => {
@@ -151,7 +158,7 @@ describe('Job', () => {
total: 100,
};
- this.job = new Job();
+ job = new Job();
waitForPromise()
.then(() => {
@@ -172,7 +179,7 @@ describe('Job', () => {
total: 100,
};
- this.job = new Job();
+ job = new Job();
waitForPromise()
.then(() => {
@@ -191,9 +198,10 @@ describe('Job', () => {
append: false,
size: 50,
total: 100,
+ complete: false,
};
- this.job = new Job();
+ job = new Job();
waitForPromise()
.then(() => {
@@ -207,6 +215,7 @@ describe('Job', () => {
append: true,
size: 10,
total: 100,
+ complete: true,
};
})
.then(() => jasmine.clock().tick(4001))
@@ -229,7 +238,7 @@ describe('Job', () => {
total: 100,
};
- this.job = new Job();
+ job = new Job();
expect(
document.querySelector('.js-raw-link').textContent.trim(),
@@ -247,7 +256,7 @@ describe('Job', () => {
total: 100,
};
- this.job = new Job();
+ job = new Job();
waitForPromise()
.then(() => {
@@ -269,7 +278,7 @@ describe('Job', () => {
total: 100,
};
- this.job = new Job();
+ job = new Job();
waitForPromise()
.then(done)
@@ -296,7 +305,7 @@ describe('Job', () => {
it('should request build trace with state parameter', (done) => {
spyOn(axios, 'get').and.callThrough();
// eslint-disable-next-line no-new
- new Job();
+ job = new Job();
setTimeout(() => {
expect(axios.get).toHaveBeenCalledWith(
diff --git a/spec/javascripts/lib/utils/common_utils_spec.js b/spec/javascripts/lib/utils/common_utils_spec.js
index 80430011aed..49799c31995 100644
--- a/spec/javascripts/lib/utils/common_utils_spec.js
+++ b/spec/javascripts/lib/utils/common_utils_spec.js
@@ -480,4 +480,33 @@ describe('common_utils', () => {
expect(commonUtils.spriteIcon('test', 'fa fa-test')).toEqual('<svg class="fa fa-test"><use xlink:href="icons.svg#test" /></svg>');
});
});
+
+ describe('convertObjectPropsToCamelCase', () => {
+ it('returns new object with camelCase property names by converting object with snake_case names', () => {
+ const snakeRegEx = /(_\w)/g;
+ const mockObj = {
+ id: 1,
+ group_name: 'GitLab.org',
+ absolute_web_url: 'https://gitlab.com/gitlab-org/',
+ };
+ const mappings = {
+ id: 'id',
+ groupName: 'group_name',
+ absoluteWebUrl: 'absolute_web_url',
+ };
+
+ const convertedObj = commonUtils.convertObjectPropsToCamelCase(mockObj);
+
+ Object.keys(convertedObj).forEach((prop) => {
+ expect(snakeRegEx.test(prop)).toBeFalsy();
+ expect(convertedObj[prop]).toBe(mockObj[mappings[prop]]);
+ });
+ });
+
+ it('return empty object if method is called with null or undefined', () => {
+ expect(Object.keys(commonUtils.convertObjectPropsToCamelCase(null)).length).toBe(0);
+ expect(Object.keys(commonUtils.convertObjectPropsToCamelCase()).length).toBe(0);
+ expect(Object.keys(commonUtils.convertObjectPropsToCamelCase({})).length).toBe(0);
+ });
+ });
});
diff --git a/spec/javascripts/lib/utils/text_utility_spec.js b/spec/javascripts/lib/utils/text_utility_spec.js
index 69a23d7b2f3..e57a55fa71a 100644
--- a/spec/javascripts/lib/utils/text_utility_spec.js
+++ b/spec/javascripts/lib/utils/text_utility_spec.js
@@ -72,4 +72,10 @@ describe('text_utility', () => {
expect(textUtils.stripHtml('This is a text with <p>html</p>.', ' ')).toEqual('This is a text with html .');
});
});
+
+ describe('convertToCamelCase', () => {
+ it('converts snake_case string to camelCase string', () => {
+ expect(textUtils.convertToCamelCase('snake_case')).toBe('snakeCase');
+ });
+ });
});
diff --git a/spec/javascripts/monitoring/dashboard_state_spec.js b/spec/javascripts/monitoring/dashboard_state_spec.js
index 3319eeb3f31..df3198dd3e2 100644
--- a/spec/javascripts/monitoring/dashboard_state_spec.js
+++ b/spec/javascripts/monitoring/dashboard_state_spec.js
@@ -29,34 +29,6 @@ describe('EmptyState', () => {
expect(component.currentState).toBe(component.states.gettingStarted);
});
- it('buttonPath returns settings path for the state "gettingStarted"', () => {
- const component = createComponent({
- selectedState: 'gettingStarted',
- settingsPath: statePaths.settingsPath,
- documentationPath: statePaths.documentationPath,
- emptyGettingStartedSvgPath: 'foo',
- emptyLoadingSvgPath: 'foo',
- emptyUnableToConnectSvgPath: 'foo',
- });
-
- expect(component.buttonPath).toEqual(statePaths.settingsPath);
- expect(component.buttonPath).not.toEqual(statePaths.documentationPath);
- });
-
- it('buttonPath returns documentation path for any of the other states', () => {
- const component = createComponent({
- selectedState: 'loading',
- settingsPath: statePaths.settingsPath,
- documentationPath: statePaths.documentationPath,
- emptyGettingStartedSvgPath: 'foo',
- emptyLoadingSvgPath: 'foo',
- emptyUnableToConnectSvgPath: 'foo',
- });
-
- expect(component.buttonPath).toEqual(statePaths.documentationPath);
- expect(component.buttonPath).not.toEqual(statePaths.settingsPath);
- });
-
it('showButtonDescription returns a description with a link for the unableToConnect state', () => {
const component = createComponent({
selectedState: 'unableToConnect',
@@ -88,6 +60,7 @@ describe('EmptyState', () => {
const component = createComponent({
selectedState: 'gettingStarted',
settingsPath: statePaths.settingsPath,
+ clustersPath: statePaths.clustersPath,
documentationPath: statePaths.documentationPath,
emptyGettingStartedSvgPath: 'foo',
emptyLoadingSvgPath: 'foo',
diff --git a/spec/javascripts/monitoring/mock_data.js b/spec/javascripts/monitoring/mock_data.js
index 2bbe963e393..f30208b27b6 100644
--- a/spec/javascripts/monitoring/mock_data.js
+++ b/spec/javascripts/monitoring/mock_data.js
@@ -2471,6 +2471,7 @@ export const deploymentData = [
export const statePaths = {
settingsPath: '/root/hello-prometheus/services/prometheus/edit',
+ clustersPath: '/root/hello-prometheus/clusters',
documentationPath: '/help/administration/monitoring/prometheus/index.md',
};
diff --git a/spec/javascripts/notes/components/note_app_spec.js b/spec/javascripts/notes/components/note_app_spec.js
index 36c56cd3862..12d180137a0 100644
--- a/spec/javascripts/notes/components/note_app_spec.js
+++ b/spec/javascripts/notes/components/note_app_spec.js
@@ -2,14 +2,29 @@ import _ from 'underscore';
import Vue from 'vue';
import notesApp from '~/notes/components/notes_app.vue';
import service from '~/notes/services/notes_service';
+import '~/render_gfm';
import * as mockData from '../mock_data';
-import getSetTimeoutPromise from '../../helpers/set_timeout_promise_helper';
+
+const vueMatchers = {
+ toIncludeElement() {
+ return {
+ compare(vm, selector) {
+ const result = {
+ pass: vm.$el.querySelector(selector) !== null,
+ };
+ return result;
+ },
+ };
+ },
+};
describe('note_app', () => {
let mountComponent;
let vm;
beforeEach(() => {
+ jasmine.addMatchers(vueMatchers);
+
const IssueNotesApp = Vue.extend(notesApp);
mountComponent = (data) => {
@@ -105,7 +120,7 @@ describe('note_app', () => {
});
it('should render loading icon', () => {
- expect(vm.$el.querySelector('.js-loading')).toBeDefined();
+ expect(vm).toIncludeElement('.js-loading');
});
it('should render form', () => {
@@ -118,10 +133,14 @@ describe('note_app', () => {
describe('update note', () => {
describe('individual note', () => {
- beforeEach(() => {
+ beforeEach((done) => {
Vue.http.interceptors.push(mockData.individualNoteInterceptor);
spyOn(service, 'updateNote').and.callThrough();
vm = mountComponent();
+ setTimeout(() => {
+ vm.$el.querySelector('.js-note-edit').click();
+ Vue.nextTick(done);
+ }, 0);
});
afterEach(() => {
@@ -131,40 +150,32 @@ describe('note_app', () => {
);
});
- it('renders edit form', (done) => {
- setTimeout(() => {
- vm.$el.querySelector('.js-note-edit').click();
- Vue.nextTick(() => {
- expect(vm.$el.querySelector('.js-vue-issue-note-form')).toBeDefined();
- done();
- });
- }, 0);
+ it('renders edit form', () => {
+ expect(vm).toIncludeElement('.js-vue-issue-note-form');
});
it('calls the service to update the note', (done) => {
- getSetTimeoutPromise()
- .then(() => {
- vm.$el.querySelector('.js-note-edit').click();
- })
- .then(Vue.nextTick)
- .then(() => {
- vm.$el.querySelector('.js-vue-issue-note-form').value = 'this is a note';
- vm.$el.querySelector('.js-vue-issue-save').click();
-
- expect(service.updateNote).toHaveBeenCalled();
- })
- // Wait for the requests to finish before destroying
- .then(Vue.nextTick)
+ vm.$el.querySelector('.js-vue-issue-note-form').value = 'this is a note';
+ vm.$el.querySelector('.js-vue-issue-save').click();
+
+ expect(service.updateNote).toHaveBeenCalled();
+ // Wait for the requests to finish before destroying
+ Vue.nextTick()
.then(done)
.catch(done.fail);
});
});
- describe('dicussion note', () => {
- beforeEach(() => {
+ describe('discussion note', () => {
+ beforeEach((done) => {
Vue.http.interceptors.push(mockData.discussionNoteInterceptor);
spyOn(service, 'updateNote').and.callThrough();
vm = mountComponent();
+
+ setTimeout(() => {
+ vm.$el.querySelector('.js-note-edit').click();
+ Vue.nextTick(done);
+ }, 0);
});
afterEach(() => {
@@ -174,30 +185,17 @@ describe('note_app', () => {
);
});
- it('renders edit form', (done) => {
- setTimeout(() => {
- vm.$el.querySelector('.js-note-edit').click();
- Vue.nextTick(() => {
- expect(vm.$el.querySelector('.js-vue-issue-note-form')).toBeDefined();
- done();
- });
- }, 0);
+ it('renders edit form', () => {
+ expect(vm).toIncludeElement('.js-vue-issue-note-form');
});
it('updates the note and resets the edit form', (done) => {
- getSetTimeoutPromise()
- .then(() => {
- vm.$el.querySelector('.js-note-edit').click();
- })
- .then(Vue.nextTick)
- .then(() => {
- vm.$el.querySelector('.js-vue-issue-note-form').value = 'this is a note';
- vm.$el.querySelector('.js-vue-issue-save').click();
-
- expect(service.updateNote).toHaveBeenCalled();
- })
- // Wait for the requests to finish before destroying
- .then(Vue.nextTick)
+ vm.$el.querySelector('.js-vue-issue-note-form').value = 'this is a note';
+ vm.$el.querySelector('.js-vue-issue-save').click();
+
+ expect(service.updateNote).toHaveBeenCalled();
+ // Wait for the requests to finish before destroying
+ Vue.nextTick()
.then(done)
.catch(done.fail);
});
diff --git a/spec/javascripts/notes/components/noteable_note_spec.js b/spec/javascripts/notes/components/noteable_note_spec.js
index cb63b64724d..88a7ffb0b9c 100644
--- a/spec/javascripts/notes/components/noteable_note_spec.js
+++ b/spec/javascripts/notes/components/noteable_note_spec.js
@@ -56,4 +56,25 @@ describe('issue_note', () => {
done();
}, 0);
});
+
+ describe('cancel edit', () => {
+ it('restores content of updated note', (done) => {
+ const noteBody = 'updated note text';
+ vm.updateNote = () => Promise.resolve();
+
+ vm.formUpdateHandler(noteBody, null, $.noop);
+
+ setTimeout(() => {
+ expect(vm.note.note_html).toEqual(noteBody);
+
+ vm.formCancelHandler();
+
+ setTimeout(() => {
+ expect(vm.note.note_html).toEqual(noteBody);
+
+ done();
+ });
+ });
+ });
+ });
});
diff --git a/spec/javascripts/pipelines/async_button_spec.js b/spec/javascripts/pipelines/async_button_spec.js
index d010d897642..8ce33d410a7 100644
--- a/spec/javascripts/pipelines/async_button_spec.js
+++ b/spec/javascripts/pipelines/async_button_spec.js
@@ -15,6 +15,7 @@ describe('Pipelines Async Button', () => {
title: 'Foo',
icon: 'repeat',
cssClass: 'bar',
+ id: 123,
},
}).$mount();
});
@@ -38,9 +39,8 @@ describe('Pipelines Async Button', () => {
describe('With confirm dialog', () => {
it('should call the service when confimation is positive', () => {
- spyOn(window, 'confirm').and.returnValue(true);
- eventHub.$on('postAction', (endpoint) => {
- expect(endpoint).toEqual('/foo');
+ eventHub.$on('actionConfirmationModal', (data) => {
+ expect(data.id).toEqual(123);
});
component = new AsyncButtonComponent({
@@ -49,7 +49,7 @@ describe('Pipelines Async Button', () => {
title: 'Foo',
icon: 'fa fa-foo',
cssClass: 'bar',
- confirmActionMessage: 'bar',
+ id: 123,
},
}).$mount();
diff --git a/spec/javascripts/vue_mr_widget/components/states/mr_widget_missing_branch_spec.js b/spec/javascripts/vue_mr_widget/components/states/mr_widget_missing_branch_spec.js
index 720effb5c1c..3d7f4abd420 100644
--- a/spec/javascripts/vue_mr_widget/components/states/mr_widget_missing_branch_spec.js
+++ b/spec/javascripts/vue_mr_widget/components/states/mr_widget_missing_branch_spec.js
@@ -1,38 +1,22 @@
import Vue from 'vue';
-import missingBranchComponent from '~/vue_merge_request_widget/components/states/mr_widget_missing_branch';
-
-const createComponent = () => {
- const Component = Vue.extend(missingBranchComponent);
- const mr = {
- sourceBranchRemoved: true,
- };
-
- return new Component({
- el: document.createElement('div'),
- propsData: { mr },
- });
-};
+import missingBranchComponent from '~/vue_merge_request_widget/components/states/mr_widget_missing_branch.vue';
+import mountComponent from '../../../helpers/vue_mount_component_helper';
describe('MRWidgetMissingBranch', () => {
- describe('props', () => {
- it('should have props', () => {
- const mrProp = missingBranchComponent.props.mr;
+ let vm;
- expect(mrProp.type instanceof Object).toBeTruthy();
- expect(mrProp.required).toBeTruthy();
- });
+ beforeEach(() => {
+ const Component = Vue.extend(missingBranchComponent);
+ vm = mountComponent(Component, { mr: { sourceBranchRemoved: true } });
});
- describe('components', () => {
- it('should have components added', () => {
- expect(missingBranchComponent.components['mr-widget-merge-help']).toBeDefined();
- });
+ afterEach(() => {
+ vm.$destroy();
});
describe('computed', () => {
describe('missingBranchName', () => {
it('should return proper branch name', () => {
- const vm = createComponent();
expect(vm.missingBranchName).toEqual('source');
vm.mr.sourceBranchRemoved = false;
@@ -43,7 +27,7 @@ describe('MRWidgetMissingBranch', () => {
describe('template', () => {
it('should have correct elements', () => {
- const el = createComponent().$el;
+ const el = vm.$el;
const content = el.textContent.replace(/\n(\s)+/g, ' ').trim();
expect(el.classList.contains('mr-widget-body')).toBeTruthy();
diff --git a/spec/javascripts/vue_shared/components/confirmation_input_spec.js b/spec/javascripts/vue_shared/components/confirmation_input_spec.js
deleted file mode 100644
index a6a12614e77..00000000000
--- a/spec/javascripts/vue_shared/components/confirmation_input_spec.js
+++ /dev/null
@@ -1,63 +0,0 @@
-import Vue from 'vue';
-import confirmationInput from '~/vue_shared/components/confirmation_input.vue';
-import mountComponent from '../../helpers/vue_mount_component_helper';
-
-describe('Confirmation input component', () => {
- const Component = Vue.extend(confirmationInput);
- const props = {
- inputId: 'dummy-id',
- confirmationKey: 'confirmation-key',
- confirmationValue: 'confirmation-value',
- };
- let vm;
-
- afterEach(() => {
- vm.$destroy();
- });
-
- describe('props', () => {
- beforeEach(() => {
- vm = mountComponent(Component, props);
- });
-
- it('sets id of the input field to inputId', () => {
- expect(vm.$refs.enteredValue.id).toBe(props.inputId);
- });
-
- it('sets name of the input field to confirmationKey', () => {
- expect(vm.$refs.enteredValue.name).toBe(props.confirmationKey);
- });
- });
-
- describe('computed', () => {
- describe('inputLabel', () => {
- it('escapes confirmationValue by default', () => {
- vm = mountComponent(Component, { ...props, confirmationValue: 'n<e></e>ds escap"ng' });
- expect(vm.inputLabel).toBe('Type <code>n&lt;e&gt;&lt;/e&gt;ds escap&quot;ng</code> to confirm:');
- });
-
- it('does not escape confirmationValue if escapeValue is false', () => {
- vm = mountComponent(Component, { ...props, confirmationValue: 'n<e></e>ds escap"ng', shouldEscapeConfirmationValue: false });
- expect(vm.inputLabel).toBe('Type <code>n<e></e>ds escap"ng</code> to confirm:');
- });
- });
- });
-
- describe('methods', () => {
- describe('hasCorrectValue', () => {
- beforeEach(() => {
- vm = mountComponent(Component, props);
- });
-
- it('returns false if entered value is incorrect', () => {
- vm.$refs.enteredValue.value = 'incorrect';
- expect(vm.hasCorrectValue()).toBe(false);
- });
-
- it('returns true if entered value is correct', () => {
- vm.$refs.enteredValue.value = props.confirmationValue;
- expect(vm.hasCorrectValue()).toBe(true);
- });
- });
- });
-});