summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--app/assets/javascripts/awards_handler.js2
-rw-r--r--app/assets/javascripts/blob/balsamiq_viewer.js5
-rw-r--r--app/assets/javascripts/blob/file_template_mediator.js3
-rw-r--r--app/assets/javascripts/blob/viewer/index.js2
-rw-r--r--app/assets/javascripts/boards/boards_bundle.js2
-rw-r--r--app/assets/javascripts/boards/components/board_sidebar.js2
-rw-r--r--app/assets/javascripts/boards/components/modal/footer.js2
-rw-r--r--app/assets/javascripts/boards/components/sidebar/remove_issue.js2
-rw-r--r--app/assets/javascripts/build_artifacts.js34
-rw-r--r--app/assets/javascripts/build_variables.js16
-rw-r--r--app/assets/javascripts/ci_lint_editor.js7
-rw-r--r--app/assets/javascripts/create_merge_request_dropdown.js2
-rw-r--r--app/assets/javascripts/cycle_analytics/cycle_analytics_bundle.js3
-rw-r--r--app/assets/javascripts/deploy_keys/components/app.vue2
-rw-r--r--app/assets/javascripts/diff_notes/components/resolve_btn.js2
-rw-r--r--app/assets/javascripts/diff_notes/services/resolve.js2
-rw-r--r--app/assets/javascripts/dispatcher.js16
-rw-r--r--app/assets/javascripts/dropzone_input.js5
-rw-r--r--app/assets/javascripts/environments/components/environment.vue2
-rw-r--r--app/assets/javascripts/environments/folder/environments_folder_view.vue2
-rw-r--r--app/assets/javascripts/filtered_search/dropdown_emoji.js7
-rw-r--r--app/assets/javascripts/filtered_search/dropdown_non_user.js7
-rw-r--r--app/assets/javascripts/filtered_search/dropdown_user.js5
-rw-r--r--app/assets/javascripts/filtered_search/filtered_search_manager.js3
-rw-r--r--app/assets/javascripts/filtered_search/filtered_search_visual_tokens.js2
-rw-r--r--app/assets/javascripts/flash.js145
-rw-r--r--app/assets/javascripts/groups/index.js3
-rw-r--r--app/assets/javascripts/integrations/integration_settings_form.js4
-rw-r--r--app/assets/javascripts/issuable_bulk_update_actions.js2
-rw-r--r--app/assets/javascripts/issue.js4
-rw-r--r--app/assets/javascripts/issue_show/components/app.vue5
-rw-r--r--app/assets/javascripts/issue_show/components/fields/description.vue1
-rw-r--r--app/assets/javascripts/job.js (renamed from app/assets/javascripts/build.js)106
-rw-r--r--app/assets/javascripts/jobs/job_details_bundle.js2
-rw-r--r--app/assets/javascripts/jobs/job_details_mediator.js8
-rw-r--r--app/assets/javascripts/label_manager.js3
-rw-r--r--app/assets/javascripts/main.js21
-rw-r--r--app/assets/javascripts/merge_conflicts/components/diff_file_editor.js2
-rw-r--r--app/assets/javascripts/merge_conflicts/merge_conflicts_bundle.js2
-rw-r--r--app/assets/javascripts/merge_request_tabs.js3
-rw-r--r--app/assets/javascripts/milestone.js3
-rw-r--r--app/assets/javascripts/mini_pipeline_graph_dropdown.js2
-rw-r--r--app/assets/javascripts/monitoring/components/dashboard.vue2
-rw-r--r--app/assets/javascripts/notes.js14
-rw-r--r--app/assets/javascripts/notes/components/issue_comment_form.vue7
-rw-r--r--app/assets/javascripts/notes/components/issue_discussion.vue4
-rw-r--r--app/assets/javascripts/notes/components/issue_note.vue5
-rw-r--r--app/assets/javascripts/notes/components/issue_note_awards_list.vue3
-rw-r--r--app/assets/javascripts/notes/components/issue_notes_app.vue2
-rw-r--r--app/assets/javascripts/notes/stores/actions.js10
-rw-r--r--app/assets/javascripts/notifications_dropdown.js2
-rw-r--r--app/assets/javascripts/pipelines/components/pipelines_actions.vue2
-rw-r--r--app/assets/javascripts/pipelines/components/stage.vue2
-rw-r--r--app/assets/javascripts/pipelines/mixins/pipelines.js3
-rw-r--r--app/assets/javascripts/pipelines/pipeline_details_bundle.js3
-rw-r--r--app/assets/javascripts/pipelines/pipeline_details_mediatior.js3
-rw-r--r--app/assets/javascripts/profile/profile.js2
-rw-r--r--app/assets/javascripts/protected_branches/protected_branch_edit.js5
-rw-r--r--app/assets/javascripts/protected_tags/protected_tag_edit.js5
-rw-r--r--app/assets/javascripts/repo/components/repo_commit_section.vue2
-rw-r--r--app/assets/javascripts/repo/helpers/repo_helper.js3
-rw-r--r--app/assets/javascripts/repo/services/repo_service.js1
-rw-r--r--app/assets/javascripts/repo/stores/repo_store.js1
-rw-r--r--app/assets/javascripts/search.js2
-rw-r--r--app/assets/javascripts/sidebar/components/assignees/sidebar_assignees.js3
-rw-r--r--app/assets/javascripts/sidebar/components/confidential/confidential_issue_sidebar.vue2
-rw-r--r--app/assets/javascripts/sidebar/lib/sidebar_move_issue.js6
-rw-r--r--app/assets/javascripts/sidebar/sidebar_mediator.js3
-rw-r--r--app/assets/javascripts/star.js3
-rw-r--r--app/assets/javascripts/task_list.js3
-rw-r--r--app/assets/javascripts/two_factor_auth.js3
-rw-r--r--app/assets/javascripts/u2f/authenticate.js188
-rw-r--r--app/assets/javascripts/u2f/error.js43
-rw-r--r--app/assets/javascripts/u2f/register.js151
-rw-r--r--app/assets/javascripts/u2f/util.js15
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/mr_widget_deployment.js3
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_merge_when_pipeline_succeeds.js2
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_merged.js3
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_ready_to_merge.js2
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_wip.js5
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/mr_widget_options.js3
-rw-r--r--app/assets/javascripts/vue_shared/components/markdown/field.vue2
-rw-r--r--app/helpers/compare_helper.rb4
-rw-r--r--app/helpers/groups_helper.rb9
-rw-r--r--app/helpers/lazy_image_tag_helper.rb1
-rw-r--r--app/models/concerns/avatarable.rb2
-rw-r--r--app/models/merge_request.rb4
-rw-r--r--app/serializers/group_entity.rb2
-rw-r--r--app/services/quick_actions/interpret_service.rb2
-rw-r--r--app/services/system_note_service.rb2
-rw-r--r--app/views/admin/groups/_group.html.haml2
-rw-r--r--app/views/admin/groups/show.html.haml2
-rw-r--r--app/views/groups/_home_panel.html.haml2
-rw-r--r--app/views/groups/edit.html.haml2
-rw-r--r--app/views/layouts/nav/sidebar/_group.html.haml2
-rw-r--r--app/views/projects/jobs/_sidebar.html.haml2
-rw-r--r--app/views/shared/groups/_group.html.haml2
-rw-r--r--app/views/shared/members/_group.html.haml2
-rw-r--r--app/views/users/_groups.html.haml2
-rw-r--r--changelogs/unreleased/an-use-branch-exists-over-branch-names-include.yml5
-rw-r--r--db/migrate/20160518200441_add_artifacts_expire_date_to_ci_builds.rb1
-rw-r--r--db/migrate/20160716115711_add_queued_at_to_ci_builds.rb1
-rw-r--r--db/migrate/20170503021915_add_last_edited_at_and_last_edited_by_id_to_issues.rb1
-rw-r--r--db/migrate/20170503022548_add_last_edited_at_and_last_edited_by_id_to_merge_requests.rb1
-rw-r--r--rubocop/cop/migration/datetime.rb20
-rw-r--r--spec/controllers/projects/branches_controller_spec.rb2
-rw-r--r--spec/features/projects/issuable_templates_spec.rb2
-rw-r--r--spec/features/projects/jobs_spec.rb4
-rw-r--r--spec/features/projects/merge_requests/user_views_open_merge_request_spec.rb2
-rw-r--r--spec/features/task_lists_spec.rb2
-rw-r--r--spec/helpers/application_helper_spec.rb8
-rw-r--r--spec/helpers/groups_helper_spec.rb45
-rw-r--r--spec/javascripts/flash_spec.js269
-rw-r--r--spec/javascripts/integrations/integration_settings_form_spec.js8
-rw-r--r--spec/javascripts/job_spec.js (renamed from spec/javascripts/build_spec.js)43
-rw-r--r--spec/javascripts/notes_spec.js4
-rw-r--r--spec/javascripts/u2f/authenticate_spec.js109
-rw-r--r--spec/javascripts/u2f/mock_u2f_device.js53
-rw-r--r--spec/javascripts/u2f/register_spec.js120
-rw-r--r--spec/models/project_spec.rb2
-rw-r--r--spec/rubocop/cop/migration/datetime_spec.rb26
-rw-r--r--spec/serializers/merge_request_entity_spec.rb2
-rw-r--r--spec/services/merge_requests/update_service_spec.rb4
-rw-r--r--spec/services/system_note_service_spec.rb38
124 files changed, 1055 insertions, 737 deletions
diff --git a/app/assets/javascripts/awards_handler.js b/app/assets/javascripts/awards_handler.js
index 4f01345ee3b..622764107ad 100644
--- a/app/assets/javascripts/awards_handler.js
+++ b/app/assets/javascripts/awards_handler.js
@@ -1,8 +1,8 @@
/* eslint-disable class-methods-use-this */
-/* global Flash */
import _ from 'underscore';
import Cookies from 'js-cookie';
import { isInIssuePage, updateTooltipTitle } from './lib/utils/common_utils';
+import Flash from './flash';
const animationEndEventString = 'animationend webkitAnimationEnd MSAnimationEnd oAnimationEnd';
const transitionEndEventString = 'transitionend webkitTransitionEnd oTransitionEnd MSTransitionEnd';
diff --git a/app/assets/javascripts/blob/balsamiq_viewer.js b/app/assets/javascripts/blob/balsamiq_viewer.js
index 8641a6fdae6..062577af385 100644
--- a/app/assets/javascripts/blob/balsamiq_viewer.js
+++ b/app/assets/javascripts/blob/balsamiq_viewer.js
@@ -1,9 +1,8 @@
-/* global Flash */
-
+import Flash from '../flash';
import BalsamiqViewer from './balsamiq/balsamiq_viewer';
function onError() {
- const flash = new window.Flash('Balsamiq file could not be loaded.');
+ const flash = new Flash('Balsamiq file could not be loaded.');
return flash;
}
diff --git a/app/assets/javascripts/blob/file_template_mediator.js b/app/assets/javascripts/blob/file_template_mediator.js
index a20c6ca7a21..583e5faa506 100644
--- a/app/assets/javascripts/blob/file_template_mediator.js
+++ b/app/assets/javascripts/blob/file_template_mediator.js
@@ -1,6 +1,5 @@
/* eslint-disable class-methods-use-this */
-/* global Flash */
-
+import Flash from '../flash';
import FileTemplateTypeSelector from './template_selectors/type_selector';
import BlobCiYamlSelector from './template_selectors/ci_yaml_selector';
import DockerfileSelector from './template_selectors/dockerfile_selector';
diff --git a/app/assets/javascripts/blob/viewer/index.js b/app/assets/javascripts/blob/viewer/index.js
index e0b73f13d36..54132e8537b 100644
--- a/app/assets/javascripts/blob/viewer/index.js
+++ b/app/assets/javascripts/blob/viewer/index.js
@@ -1,4 +1,4 @@
-/* global Flash */
+import Flash from '../../flash';
import { handleLocationHash } from '../../lib/utils/common_utils';
export default class BlobViewer {
diff --git a/app/assets/javascripts/boards/boards_bundle.js b/app/assets/javascripts/boards/boards_bundle.js
index 815248f38ee..ef4093b59e3 100644
--- a/app/assets/javascripts/boards/boards_bundle.js
+++ b/app/assets/javascripts/boards/boards_bundle.js
@@ -1,10 +1,10 @@
/* eslint-disable one-var, quote-props, comma-dangle, space-before-function-paren */
/* global BoardService */
-/* global Flash */
import _ from 'underscore';
import Vue from 'vue';
import VueResource from 'vue-resource';
+import Flash from '../flash';
import FilteredSearchBoards from './filtered_search_boards';
import eventHub from './eventhub';
import './models/issue';
diff --git a/app/assets/javascripts/boards/components/board_sidebar.js b/app/assets/javascripts/boards/components/board_sidebar.js
index 590b7be36e3..7f3afefc9cc 100644
--- a/app/assets/javascripts/boards/components/board_sidebar.js
+++ b/app/assets/javascripts/boards/components/board_sidebar.js
@@ -3,9 +3,9 @@
/* global MilestoneSelect */
/* global LabelsSelect */
/* global Sidebar */
-/* global Flash */
import Vue from 'vue';
+import Flash from '../../flash';
import eventHub from '../../sidebar/event_hub';
import AssigneeTitle from '../../sidebar/components/assignees/assignee_title';
import Assignees from '../../sidebar/components/assignees/assignees';
diff --git a/app/assets/javascripts/boards/components/modal/footer.js b/app/assets/javascripts/boards/components/modal/footer.js
index a656f0546c0..de9e44cef35 100644
--- a/app/assets/javascripts/boards/components/modal/footer.js
+++ b/app/assets/javascripts/boards/components/modal/footer.js
@@ -1,7 +1,7 @@
/* eslint-disable no-new */
-/* global Flash */
import Vue from 'vue';
+import Flash from '../../../flash';
import './lists_dropdown';
const ModalStore = gl.issueBoards.ModalStore;
diff --git a/app/assets/javascripts/boards/components/sidebar/remove_issue.js b/app/assets/javascripts/boards/components/sidebar/remove_issue.js
index 1e623cf58b7..1ad97211934 100644
--- a/app/assets/javascripts/boards/components/sidebar/remove_issue.js
+++ b/app/assets/javascripts/boards/components/sidebar/remove_issue.js
@@ -1,7 +1,7 @@
/* eslint-disable no-new */
-/* global Flash */
import Vue from 'vue';
+import Flash from '../../../flash';
const Store = gl.issueBoards.BoardsStore;
diff --git a/app/assets/javascripts/build_artifacts.js b/app/assets/javascripts/build_artifacts.js
index 19388f1f9ae..ace89398943 100644
--- a/app/assets/javascripts/build_artifacts.js
+++ b/app/assets/javascripts/build_artifacts.js
@@ -1,30 +1,30 @@
-/* eslint-disable func-names, space-before-function-paren, wrap-iife, prefer-arrow-callback, no-unused-vars, no-return-assign, max-len */
+/* eslint-disable func-names, prefer-arrow-callback, no-return-assign */
import { visitUrl } from './lib/utils/url_utility';
import { convertPermissionToBoolean } from './lib/utils/common_utils';
-window.BuildArtifacts = (function() {
- function BuildArtifacts() {
+export default class BuildArtifacts {
+ constructor() {
this.disablePropagation();
this.setupEntryClick();
this.setupTooltips();
}
-
- BuildArtifacts.prototype.disablePropagation = function() {
- $('.top-block').on('click', '.download', function(e) {
+ // eslint-disable-next-line class-methods-use-this
+ disablePropagation() {
+ $('.top-block').on('click', '.download', function (e) {
return e.stopPropagation();
});
- return $('.tree-holder').on('click', 'tr[data-link] a', function(e) {
+ return $('.tree-holder').on('click', 'tr[data-link] a', function (e) {
return e.stopImmediatePropagation();
});
- };
-
- BuildArtifacts.prototype.setupEntryClick = function() {
- return $('.tree-holder').on('click', 'tr[data-link]', function(e) {
+ }
+ // eslint-disable-next-line class-methods-use-this
+ setupEntryClick() {
+ return $('.tree-holder').on('click', 'tr[data-link]', function () {
visitUrl(this.dataset.link, convertPermissionToBoolean(this.dataset.externalLink));
});
- };
-
- BuildArtifacts.prototype.setupTooltips = function() {
+ }
+ // eslint-disable-next-line class-methods-use-this
+ setupTooltips() {
$('.js-artifact-tree-tooltip').tooltip({
placement: 'bottom',
// Stop the tooltip from hiding when we stop hovering the element directly
@@ -41,7 +41,5 @@ window.BuildArtifacts = (function() {
.on('mouseleave', (e) => {
$(e.currentTarget).find('.js-artifact-tree-tooltip').tooltip('hide');
});
- };
-
- return BuildArtifacts;
-})();
+ }
+}
diff --git a/app/assets/javascripts/build_variables.js b/app/assets/javascripts/build_variables.js
index c955a9ac2ea..35edf3e0017 100644
--- a/app/assets/javascripts/build_variables.js
+++ b/app/assets/javascripts/build_variables.js
@@ -1,8 +1,10 @@
-/* eslint-disable func-names, prefer-arrow-callback, space-before-function-paren */
+/* eslint-disable func-names*/
-$(function() {
- $('.reveal-variables').off('click').on('click', function() {
- $('.js-build-variables').toggle();
- $(this).hide();
- });
-});
+export default function handleRevealVariables() {
+ $('.js-reveal-variables')
+ .off('click')
+ .on('click', function () {
+ $('.js-build-variables').toggle();
+ $(this).hide();
+ });
+}
diff --git a/app/assets/javascripts/ci_lint_editor.js b/app/assets/javascripts/ci_lint_editor.js
index dd4a08a2f31..b9469e5b7cb 100644
--- a/app/assets/javascripts/ci_lint_editor.js
+++ b/app/assets/javascripts/ci_lint_editor.js
@@ -1,7 +1,4 @@
-
-window.gl = window.gl || {};
-
-class CILintEditor {
+export default class CILintEditor {
constructor() {
this.editor = window.ace.edit('ci-editor');
this.textarea = document.querySelector('#content');
@@ -13,5 +10,3 @@ class CILintEditor {
});
}
}
-
-gl.CILintEditor = CILintEditor;
diff --git a/app/assets/javascripts/create_merge_request_dropdown.js b/app/assets/javascripts/create_merge_request_dropdown.js
index ff2f2c81971..bf40eb3ee11 100644
--- a/app/assets/javascripts/create_merge_request_dropdown.js
+++ b/app/assets/javascripts/create_merge_request_dropdown.js
@@ -1,5 +1,5 @@
/* eslint-disable no-new */
-/* global Flash */
+import Flash from './flash';
import DropLab from './droplab/drop_lab';
import ISetter from './droplab/plugins/input_setter';
diff --git a/app/assets/javascripts/cycle_analytics/cycle_analytics_bundle.js b/app/assets/javascripts/cycle_analytics/cycle_analytics_bundle.js
index cdf5e3c0290..49bb6c52180 100644
--- a/app/assets/javascripts/cycle_analytics/cycle_analytics_bundle.js
+++ b/app/assets/javascripts/cycle_analytics/cycle_analytics_bundle.js
@@ -1,7 +1,6 @@
-/* global Flash */
-
import Vue from 'vue';
import Cookies from 'js-cookie';
+import Flash from '../flash';
import Translate from '../vue_shared/translate';
import banner from './components/banner.vue';
import stageCodeComponent from './components/stage_code_component.vue';
diff --git a/app/assets/javascripts/deploy_keys/components/app.vue b/app/assets/javascripts/deploy_keys/components/app.vue
index a663e30dfd0..54e13b79a4f 100644
--- a/app/assets/javascripts/deploy_keys/components/app.vue
+++ b/app/assets/javascripts/deploy_keys/components/app.vue
@@ -1,5 +1,5 @@
<script>
- /* global Flash */
+ import Flash from '../../flash';
import eventHub from '../eventhub';
import DeployKeysService from '../service';
import DeployKeysStore from '../store';
diff --git a/app/assets/javascripts/diff_notes/components/resolve_btn.js b/app/assets/javascripts/diff_notes/components/resolve_btn.js
index efb6ced9f46..20ddcbfb8bd 100644
--- a/app/assets/javascripts/diff_notes/components/resolve_btn.js
+++ b/app/assets/javascripts/diff_notes/components/resolve_btn.js
@@ -1,9 +1,9 @@
/* eslint-disable comma-dangle, object-shorthand, func-names, quote-props, no-else-return, camelcase, max-len */
/* global CommentsStore */
/* global ResolveService */
-/* global Flash */
import Vue from 'vue';
+import Flash from '../../flash';
const ResolveBtn = Vue.extend({
props: {
diff --git a/app/assets/javascripts/diff_notes/services/resolve.js b/app/assets/javascripts/diff_notes/services/resolve.js
index 2f063f6fe1f..6eae54f830b 100644
--- a/app/assets/javascripts/diff_notes/services/resolve.js
+++ b/app/assets/javascripts/diff_notes/services/resolve.js
@@ -1,7 +1,7 @@
-/* global Flash */
/* global CommentsStore */
import Vue from 'vue';
+import Flash from '../../flash';
import '../../vue_shared/vue_resource_interceptor';
window.gl = window.gl || {};
diff --git a/app/assets/javascripts/dispatcher.js b/app/assets/javascripts/dispatcher.js
index 1edd460f380..d43eae79730 100644
--- a/app/assets/javascripts/dispatcher.js
+++ b/app/assets/javascripts/dispatcher.js
@@ -12,7 +12,8 @@
/* global NotificationsDropdown */
/* global GroupAvatar */
/* global LineHighlighter */
-/* global BuildArtifacts */
+import BuildArtifacts from './build_artifacts';
+import CILintEditor from './ci_lint_editor';
/* global GroupsSelect */
/* global Search */
/* global Admin */
@@ -78,6 +79,7 @@ import initChangesDropdown from './init_changes_dropdown';
import AbuseReports from './abuse_reports';
import { ajaxGet, convertPermissionToBoolean } from './lib/utils/common_utils';
import AjaxLoadingSpinner from './ajax_loading_spinner';
+import U2FAuthenticate from './u2f/authenticate';
(function() {
var Dispatcher;
@@ -90,8 +92,8 @@ import AjaxLoadingSpinner from './ajax_loading_spinner';
}
Dispatcher.prototype.initPageScripts = function() {
- var page, path, shortcut_handler, fileBlobPermalinkUrlElement, fileBlobPermalinkUrl;
- page = $('body').attr('data-page');
+ var path, shortcut_handler, fileBlobPermalinkUrlElement, fileBlobPermalinkUrl;
+ const page = $('body').attr('data-page');
if (!page) {
return false;
}
@@ -505,7 +507,7 @@ import AjaxLoadingSpinner from './ajax_loading_spinner';
break;
case 'ci:lints:create':
case 'ci:lints:show':
- new gl.CILintEditor();
+ new CILintEditor();
break;
case 'users:show':
new UserCallout();
@@ -535,14 +537,16 @@ import AjaxLoadingSpinner from './ajax_loading_spinner';
case 'sessions':
case 'omniauth_callbacks':
if (!gon.u2f) break;
- gl.u2fAuthenticate = new gl.U2FAuthenticate(
+ const u2fAuthenticate = new U2FAuthenticate(
$('#js-authenticate-u2f'),
'#js-login-u2f-form',
gon.u2f,
document.querySelector('#js-login-2fa-device'),
document.querySelector('.js-2fa-form'),
);
- gl.u2fAuthenticate.start();
+ u2fAuthenticate.start();
+ // needed in rspec
+ gl.u2fAuthenticate = u2fAuthenticate;
case 'admin':
new Admin();
switch (path[1]) {
diff --git a/app/assets/javascripts/dropzone_input.js b/app/assets/javascripts/dropzone_input.js
index 1cba65d17cd..bd45da8c422 100644
--- a/app/assets/javascripts/dropzone_input.js
+++ b/app/assets/javascripts/dropzone_input.js
@@ -237,9 +237,12 @@ window.DropzoneInput = (function() {
};
const insertToTextArea = function(filename, url) {
- return $(child).val(function(index, val) {
+ const $child = $(child);
+ $child.val(function(index, val) {
return val.replace(`{{${filename}}}`, url);
});
+
+ $child.trigger('change');
};
const appendToTextArea = function(url) {
diff --git a/app/assets/javascripts/environments/components/environment.vue b/app/assets/javascripts/environments/components/environment.vue
index ce5f6219a3e..c039ae85cfb 100644
--- a/app/assets/javascripts/environments/components/environment.vue
+++ b/app/assets/javascripts/environments/components/environment.vue
@@ -1,6 +1,6 @@
<script>
-/* global Flash */
import Visibility from 'visibilityjs';
+import Flash from '../../flash';
import EnvironmentsService from '../services/environments_service';
import environmentTable from './environments_table.vue';
import EnvironmentsStore from '../stores/environments_store';
diff --git a/app/assets/javascripts/environments/folder/environments_folder_view.vue b/app/assets/javascripts/environments/folder/environments_folder_view.vue
index 01e70c0bbb7..b155560df9d 100644
--- a/app/assets/javascripts/environments/folder/environments_folder_view.vue
+++ b/app/assets/javascripts/environments/folder/environments_folder_view.vue
@@ -1,6 +1,6 @@
<script>
-/* global Flash */
import Visibility from 'visibilityjs';
+import Flash from '../../flash';
import EnvironmentsService from '../services/environments_service';
import environmentTable from '../components/environments_table.vue';
import EnvironmentsStore from '../stores/environments_store';
diff --git a/app/assets/javascripts/filtered_search/dropdown_emoji.js b/app/assets/javascripts/filtered_search/dropdown_emoji.js
index ada14d2053c..a6cc079d720 100644
--- a/app/assets/javascripts/filtered_search/dropdown_emoji.js
+++ b/app/assets/javascripts/filtered_search/dropdown_emoji.js
@@ -1,7 +1,6 @@
-/* global Flash */
-
-import Ajax from '~/droplab/plugins/ajax';
-import Filter from '~/droplab/plugins/filter';
+import Flash from '../flash';
+import Ajax from '../droplab/plugins/ajax';
+import Filter from '../droplab/plugins/filter';
import './filtered_search_dropdown';
class DropdownEmoji extends gl.FilteredSearchDropdown {
diff --git a/app/assets/javascripts/filtered_search/dropdown_non_user.js b/app/assets/javascripts/filtered_search/dropdown_non_user.js
index b32d589481d..788fb1dc614 100644
--- a/app/assets/javascripts/filtered_search/dropdown_non_user.js
+++ b/app/assets/javascripts/filtered_search/dropdown_non_user.js
@@ -1,7 +1,6 @@
-/* global Flash */
-
-import Ajax from '~/droplab/plugins/ajax';
-import Filter from '~/droplab/plugins/filter';
+import Flash from '../flash';
+import Ajax from '../droplab/plugins/ajax';
+import Filter from '../droplab/plugins/filter';
import './filtered_search_dropdown';
class DropdownNonUser extends gl.FilteredSearchDropdown {
diff --git a/app/assets/javascripts/filtered_search/dropdown_user.js b/app/assets/javascripts/filtered_search/dropdown_user.js
index ce8817b1b2e..a9e2b65def0 100644
--- a/app/assets/javascripts/filtered_search/dropdown_user.js
+++ b/app/assets/javascripts/filtered_search/dropdown_user.js
@@ -1,6 +1,5 @@
-/* global Flash */
-
-import AjaxFilter from '~/droplab/plugins/ajax_filter';
+import Flash from '../flash';
+import AjaxFilter from '../droplab/plugins/ajax_filter';
import './filtered_search_dropdown';
import { addClassIfElementExists } from '../lib/utils/dom_utils';
diff --git a/app/assets/javascripts/filtered_search/filtered_search_manager.js b/app/assets/javascripts/filtered_search/filtered_search_manager.js
index a44dc279a6f..7b233842d5a 100644
--- a/app/assets/javascripts/filtered_search/filtered_search_manager.js
+++ b/app/assets/javascripts/filtered_search/filtered_search_manager.js
@@ -1,3 +1,4 @@
+import Flash from '../flash';
import FilteredSearchContainer from './container';
import RecentSearchesRoot from './recent_searches_root';
import RecentSearchesStore from './stores/recent_searches_store';
@@ -36,7 +37,7 @@ class FilteredSearchManager {
.catch((error) => {
if (error.name === 'RecentSearchesServiceError') return undefined;
// eslint-disable-next-line no-new
- new window.Flash('An error occurred while parsing recent searches');
+ new Flash('An error occurred while parsing recent searches');
// Gracefully fail to empty array
return [];
})
diff --git a/app/assets/javascripts/filtered_search/filtered_search_visual_tokens.js b/app/assets/javascripts/filtered_search/filtered_search_visual_tokens.js
index 28e8240169d..dd24fc44d2a 100644
--- a/app/assets/javascripts/filtered_search/filtered_search_visual_tokens.js
+++ b/app/assets/javascripts/filtered_search/filtered_search_visual_tokens.js
@@ -1,5 +1,5 @@
import AjaxCache from '../lib/utils/ajax_cache';
-import '../flash'; /* global Flash */
+import Flash from '../flash';
import FilteredSearchContainer from './container';
import UsersCache from '../lib/utils/users_cache';
diff --git a/app/assets/javascripts/flash.js b/app/assets/javascripts/flash.js
index ccff8f0ace7..bc5cd818e1c 100644
--- a/app/assets/javascripts/flash.js
+++ b/app/assets/javascripts/flash.js
@@ -1,71 +1,94 @@
-/* eslint-disable func-names, space-before-function-paren, wrap-iife, no-var, one-var, one-var-declaration-per-line, no-param-reassign, quotes, quote-props, prefer-template, comma-dangle, max-len */
+import _ from 'underscore';
-window.Flash = (function() {
- var hideFlash;
+const hideFlash = (flashEl, fadeTransition = true) => {
+ if (fadeTransition) {
+ Object.assign(flashEl.style, {
+ transition: 'opacity .3s',
+ opacity: '0',
+ });
+ }
- hideFlash = function() {
- return $(this).fadeOut();
- };
+ flashEl.addEventListener('transitionend', () => {
+ flashEl.remove();
+ }, {
+ once: true,
+ passive: true,
+ });
- /**
- * Flash banner supports different types of Flash configurations
- * along with ability to provide actionConfig which can be used to show
- * additional action or link on banner next to message
- *
- * @param {String} message Flash message
- * @param {String} type Type of Flash, it can be `notice` or `alert` (default)
- * @param {Object} parent Reference to Parent element under which Flash needs to appear
- * @param {Object} actionConfig Map of config to show action on banner
- * @param {String} href URL to which action link should point (default '#')
- * @param {String} title Title of action
- * @param {Function} clickHandler Method to call when action is clicked on
- */
- function Flash(message, type, parent, actionConfig) {
- var flash, textDiv, actionLink;
- if (type == null) {
- type = 'alert';
- }
- if (parent == null) {
- parent = null;
- }
- if (parent) {
- this.flashContainer = parent.find('.flash-container');
- } else {
- this.flashContainer = $('.flash-container-page');
- }
- this.flashContainer.html('');
- flash = $('<div/>', {
- "class": "flash-" + type
- });
- flash.on('click', hideFlash);
- textDiv = $('<div/>', {
- "class": 'flash-text',
- text: message
- });
- textDiv.appendTo(flash);
+ if (!fadeTransition) flashEl.dispatchEvent(new Event('transitionend'));
+};
- if (actionConfig) {
- const actionLinkConfig = {
- class: 'flash-action',
- href: actionConfig.href || '#',
- text: actionConfig.title
- };
+const createAction = config => `
+ <a
+ href="${config.href || '#'}"
+ class="flash-action"
+ ${config.href ? '' : 'role="button"'}
+ >
+ ${_.escape(config.title)}
+ </a>
+`;
- if (!actionConfig.href) {
- actionLinkConfig.role = 'button';
- }
+const createFlashEl = (message, type, isInContentWrapper = false) => `
+ <div
+ class="flash-${type}"
+ >
+ <div
+ class="flash-text ${isInContentWrapper ? 'container-fluid container-limited' : ''}"
+ >
+ ${_.escape(message)}
+ </div>
+ </div>
+`;
- actionLink = $('<a/>', actionLinkConfig);
+/*
+ * Flash banner supports different types of Flash configurations
+ * along with ability to provide actionConfig which can be used to show
+ * additional action or link on banner next to message
+ *
+ * @param {String} message Flash message text
+ * @param {String} type Type of Flash, it can be `notice` or `alert` (default)
+ * @param {Object} parent Reference to parent element under which Flash needs to appear
+ * @param {Object} actonConfig Map of config to show action on banner
+ * @param {String} href URL to which action config should point to (default: '#')
+ * @param {String} title Title of action
+ * @param {Function} clickHandler Method to call when action is clicked on
+ * @param {Boolean} fadeTransition Boolean to determine whether to fade the alert out
+ */
+const createFlash = function createFlash(
+ message,
+ type = 'alert',
+ parent = document,
+ actionConfig = null,
+ fadeTransition = true,
+) {
+ const flashContainer = parent.querySelector('.flash-container');
- actionLink.appendTo(flash);
- this.flashContainer.on('click', '.flash-action', actionConfig.clickHandler);
- }
- if (this.flashContainer.parent().hasClass('content-wrapper')) {
- textDiv.addClass('container-fluid container-limited');
+ if (!flashContainer) return null;
+
+ const isInContentWrapper = flashContainer.parentNode.classList.contains('content-wrapper');
+
+ flashContainer.innerHTML = createFlashEl(message, type, isInContentWrapper);
+
+ const flashEl = flashContainer.querySelector(`.flash-${type}`);
+ flashEl.addEventListener('click', () => hideFlash(flashEl, fadeTransition));
+
+ if (actionConfig) {
+ flashEl.innerHTML += createAction(actionConfig);
+
+ if (actionConfig.clickHandler) {
+ flashEl.querySelector('.flash-action').addEventListener('click', e => actionConfig.clickHandler(e));
}
- flash.appendTo(this.flashContainer);
- this.flashContainer.show();
}
- return Flash;
-})();
+ flashContainer.style.display = 'block';
+
+ return flashContainer;
+};
+
+export {
+ createFlash as default,
+ createFlashEl,
+ createAction,
+ hideFlash,
+};
+window.Flash = createFlash;
diff --git a/app/assets/javascripts/groups/index.js b/app/assets/javascripts/groups/index.js
index 9ad8e5c6052..600bae24b52 100644
--- a/app/assets/javascripts/groups/index.js
+++ b/app/assets/javascripts/groups/index.js
@@ -1,6 +1,5 @@
-/* global Flash */
-
import Vue from 'vue';
+import Flash from '../flash';
import GroupFilterableList from './groups_filterable_list';
import GroupsComponent from './components/groups.vue';
import GroupFolder from './components/group_folder.vue';
diff --git a/app/assets/javascripts/integrations/integration_settings_form.js b/app/assets/javascripts/integrations/integration_settings_form.js
index cf1e6a14725..32415a8791f 100644
--- a/app/assets/javascripts/integrations/integration_settings_form.js
+++ b/app/assets/javascripts/integrations/integration_settings_form.js
@@ -1,4 +1,4 @@
-/* global Flash */
+import Flash from '../flash';
export default class IntegrationSettingsForm {
constructor(formSelector) {
@@ -102,7 +102,7 @@ export default class IntegrationSettingsForm {
})
.done((res) => {
if (res.error) {
- new Flash(`${res.message} ${res.service_response}`, null, null, {
+ new Flash(`${res.message} ${res.service_response}`, 'alert', document, {
title: 'Save anyway',
clickHandler: (e) => {
e.preventDefault();
diff --git a/app/assets/javascripts/issuable_bulk_update_actions.js b/app/assets/javascripts/issuable_bulk_update_actions.js
index c39ffdb2e0f..eb15949603f 100644
--- a/app/assets/javascripts/issuable_bulk_update_actions.js
+++ b/app/assets/javascripts/issuable_bulk_update_actions.js
@@ -1,7 +1,7 @@
/* eslint-disable comma-dangle, quotes, consistent-return, func-names, array-callback-return, space-before-function-paren, prefer-arrow-callback, max-len, no-unused-expressions, no-sequences, no-underscore-dangle, no-unused-vars, no-param-reassign */
/* global IssuableIndex */
-/* global Flash */
import _ from 'underscore';
+import Flash from './flash';
export default {
init({ container, form, issues, prefixId } = {}) {
diff --git a/app/assets/javascripts/issue.js b/app/assets/javascripts/issue.js
index c0bd64814ca..3fc29f9a661 100644
--- a/app/assets/javascripts/issue.js
+++ b/app/assets/javascripts/issue.js
@@ -1,9 +1,7 @@
/* eslint-disable func-names, space-before-function-paren, no-var, prefer-rest-params, wrap-iife, one-var, no-underscore-dangle, one-var-declaration-per-line, object-shorthand, no-unused-vars, no-new, comma-dangle, consistent-return, quotes, dot-notation, quote-props, prefer-arrow-callback, max-len */
-/* global Flash */
-
import 'vendor/jquery.waitforimages';
import '~/lib/utils/text_utility';
-import './flash';
+import Flash from './flash';
import TaskList from './task_list';
import CreateMergeRequestDropdown from './create_merge_request_dropdown';
import IssuablesHelper from './helpers/issuables_helper';
diff --git a/app/assets/javascripts/issue_show/components/app.vue b/app/assets/javascripts/issue_show/components/app.vue
index 06f6ec241f4..eecb56cb185 100644
--- a/app/assets/javascripts/issue_show/components/app.vue
+++ b/app/assets/javascripts/issue_show/components/app.vue
@@ -1,5 +1,4 @@
<script>
-/* global Flash */
import Visibility from 'visibilityjs';
import Poll from '../../lib/utils/poll';
import eventHub from '../event_hub';
@@ -153,7 +152,7 @@ export default {
})
.catch(() => {
eventHub.$emit('close.form');
- return new Flash('Error updating issue');
+ window.Flash('Error updating issue');
});
},
deleteIssuable() {
@@ -167,7 +166,7 @@ export default {
})
.catch(() => {
eventHub.$emit('close.form');
- return new Flash('Error deleting issue');
+ window.Flash('Error deleting issue');
});
},
},
diff --git a/app/assets/javascripts/issue_show/components/fields/description.vue b/app/assets/javascripts/issue_show/components/fields/description.vue
index dc902eefc5f..0aa1b2c2e31 100644
--- a/app/assets/javascripts/issue_show/components/fields/description.vue
+++ b/app/assets/javascripts/issue_show/components/fields/description.vue
@@ -1,5 +1,4 @@
<script>
- /* global Flash */
import updateMixin from '../../mixins/update';
import markdownField from '../../../vue_shared/components/markdown/field.vue';
diff --git a/app/assets/javascripts/build.js b/app/assets/javascripts/job.js
index 3d27a3544eb..c6b5844dff6 100644
--- a/app/assets/javascripts/build.js
+++ b/app/assets/javascripts/job.js
@@ -1,15 +1,12 @@
-/* eslint-disable func-names, wrap-iife, no-use-before-define,
-consistent-return, prefer-rest-params */
import _ from 'underscore';
import bp from './breakpoints';
import { bytesToKiB } from './lib/utils/number_utils';
import { setCiStatusFavicon } from './lib/utils/common_utils';
-window.Build = (function () {
- Build.timeout = null;
- Build.state = null;
-
- function Build(options) {
+export default class Job {
+ constructor(options) {
+ this.timeout = null;
+ this.state = null;
this.options = options || $('.js-build-options').data();
this.pageUrl = this.options.pageUrl;
@@ -19,9 +16,7 @@ window.Build = (function () {
this.$document = $(document);
this.logBytes = 0;
this.hasBeenScrolled = false;
-
this.updateDropdown = this.updateDropdown.bind(this);
- this.getBuildTrace = this.getBuildTrace.bind(this);
this.$buildTrace = $('#build-trace');
this.$buildRefreshAnimation = $('.js-build-refresh');
@@ -33,7 +28,7 @@ window.Build = (function () {
this.$scrollTopBtn = $('.js-scroll-up');
this.$scrollBottomBtn = $('.js-scroll-down');
- clearTimeout(Build.timeout);
+ clearTimeout(this.timeout);
this.initSidebar();
this.populateJobs(this.buildStage);
@@ -85,7 +80,7 @@ window.Build = (function () {
this.getBuildTrace();
}
- Build.prototype.initAffixTopArea = function () {
+ initAffixTopArea() {
/**
If the browser does not support position sticky, it returns the position as static.
If the browser does support sticky, then we allow the browser to handle it, if not
@@ -100,13 +95,14 @@ window.Build = (function () {
top: offsetTop,
},
});
- };
+ }
- Build.prototype.canScroll = function () {
+ // eslint-disable-next-line class-methods-use-this
+ canScroll() {
return $(document).height() > $(window).height();
- };
+ }
- Build.prototype.toggleScroll = function () {
+ toggleScroll() {
const currentPosition = $(document).scrollTop();
const scrollHeight = $(document).height();
@@ -119,7 +115,7 @@ window.Build = (function () {
this.toggleDisableButton(this.$scrollTopBtn, false);
this.toggleDisableButton(this.$scrollBottomBtn, false);
} else if (currentPosition === 0) {
- // User is at Top of Build Log
+ // User is at Top of Log
this.toggleDisableButton(this.$scrollTopBtn, true);
this.toggleDisableButton(this.$scrollBottomBtn, false);
@@ -133,38 +129,40 @@ window.Build = (function () {
this.toggleDisableButton(this.$scrollTopBtn, true);
this.toggleDisableButton(this.$scrollBottomBtn, true);
}
- };
+ }
- Build.prototype.scrollDown = function () {
+ // eslint-disable-next-line class-methods-use-this
+ scrollDown() {
$(document).scrollTop($(document).height());
- };
+ }
- Build.prototype.scrollToBottom = function () {
+ scrollToBottom() {
this.scrollDown();
this.hasBeenScrolled = true;
this.toggleScroll();
- };
+ }
- Build.prototype.scrollToTop = function () {
+ scrollToTop() {
$(document).scrollTop(0);
this.hasBeenScrolled = true;
this.toggleScroll();
- };
+ }
- Build.prototype.toggleDisableButton = function ($button, disable) {
+ // eslint-disable-next-line class-methods-use-this
+ toggleDisableButton($button, disable) {
if (disable && $button.prop('disabled')) return;
$button.prop('disabled', disable);
- };
+ }
- Build.prototype.toggleScrollAnimation = function (toggle) {
+ toggleScrollAnimation(toggle) {
this.$scrollBottomBtn.toggleClass('animate', toggle);
- };
+ }
- Build.prototype.initSidebar = function () {
+ initSidebar() {
this.$sidebar = $('.js-build-sidebar');
- };
+ }
- Build.prototype.getBuildTrace = function () {
+ getBuildTrace() {
return $.ajax({
url: `${this.pageUrl}/trace.json`,
data: { state: this.state },
@@ -204,7 +202,7 @@ window.Build = (function () {
this.toggleScrollAnimation(false);
}
- Build.timeout = setTimeout(() => {
+ this.timeout = setTimeout(() => {
this.getBuildTrace();
}, 4000);
} else {
@@ -225,14 +223,14 @@ window.Build = (function () {
}
})
.then(() => this.toggleScroll());
- };
-
- Build.prototype.shouldHideSidebarForViewport = function () {
+ }
+ // eslint-disable-next-line class-methods-use-this
+ shouldHideSidebarForViewport() {
const bootstrapBreakpoint = bp.getBreakpointSize();
return bootstrapBreakpoint === 'xs' || bootstrapBreakpoint === 'sm';
- };
+ }
- Build.prototype.toggleSidebar = function (shouldHide) {
+ toggleSidebar(shouldHide) {
const shouldShow = typeof shouldHide === 'boolean' ? !shouldHide : undefined;
const $toggleButton = $('.js-sidebar-build-toggle-header');
@@ -249,17 +247,17 @@ window.Build = (function () {
} else {
$toggleButton.removeClass('hidden');
}
- };
+ }
- Build.prototype.sidebarOnResize = function () {
+ sidebarOnResize() {
this.toggleSidebar(this.shouldHideSidebarForViewport());
- };
+ }
- Build.prototype.sidebarOnClick = function () {
+ sidebarOnClick() {
if (this.shouldHideSidebarForViewport()) this.toggleSidebar();
- };
-
- Build.prototype.updateArtifactRemoveDate = function () {
+ }
+ // eslint-disable-next-line class-methods-use-this, consistent-return
+ updateArtifactRemoveDate() {
const $date = $('.js-artifacts-remove');
if ($date.length) {
const date = $date.text();
@@ -267,23 +265,21 @@ window.Build = (function () {
gl.utils.timeFor(new Date(date.replace(/([0-9]+)-([0-9]+)-([0-9]+)/g, '$1/$2/$3')), ' '),
);
}
- };
-
- Build.prototype.populateJobs = function (stage) {
+ }
+ // eslint-disable-next-line class-methods-use-this
+ populateJobs(stage) {
$('.build-job').hide();
$(`.build-job[data-stage="${stage}"]`).show();
- };
-
- Build.prototype.updateStageDropdownText = function (stage) {
+ }
+ // eslint-disable-next-line class-methods-use-this
+ updateStageDropdownText(stage) {
$('.stage-selection').text(stage);
- };
+ }
- Build.prototype.updateDropdown = function (e) {
+ updateDropdown(e) {
e.preventDefault();
const stage = e.currentTarget.text;
this.updateStageDropdownText(stage);
this.populateJobs(stage);
- };
-
- return Build;
-})();
+ }
+}
diff --git a/app/assets/javascripts/jobs/job_details_bundle.js b/app/assets/javascripts/jobs/job_details_bundle.js
index f92e669414a..baaf5641200 100644
--- a/app/assets/javascripts/jobs/job_details_bundle.js
+++ b/app/assets/javascripts/jobs/job_details_bundle.js
@@ -1,5 +1,3 @@
-/* global Flash */
-
import Vue from 'vue';
import JobMediator from './job_details_mediator';
import jobHeader from './components/header.vue';
diff --git a/app/assets/javascripts/jobs/job_details_mediator.js b/app/assets/javascripts/jobs/job_details_mediator.js
index cc014b815c4..3e2658f9fc1 100644
--- a/app/assets/javascripts/jobs/job_details_mediator.js
+++ b/app/assets/javascripts/jobs/job_details_mediator.js
@@ -1,11 +1,12 @@
-/* global Flash */
/* global Build */
import Visibility from 'visibilityjs';
+import Flash from '../flash';
import Poll from '../lib/utils/poll';
import JobStore from './stores/job_store';
import JobService from './services/job_service';
-import '../build';
+import Job from '../job';
+import handleRevealVariables from '../build_variables';
export default class JobMediator {
constructor(options = {}) {
@@ -20,7 +21,8 @@ export default class JobMediator {
}
initBuildClass() {
- this.build = new Build();
+ this.build = new Job();
+ handleRevealVariables();
}
fetchJob() {
diff --git a/app/assets/javascripts/label_manager.js b/app/assets/javascripts/label_manager.js
index d8814802d9e..a8f613c6cf2 100644
--- a/app/assets/javascripts/label_manager.js
+++ b/app/assets/javascripts/label_manager.js
@@ -1,7 +1,8 @@
/* eslint-disable comma-dangle, class-methods-use-this, no-underscore-dangle, no-param-reassign, no-unused-vars, consistent-return, func-names, space-before-function-paren, max-len */
-/* global Flash */
/* global Sortable */
+import Flash from './flash';
+
((global) => {
class LabelManager {
constructor({ togglePriorityButton, prioritizedLabels, otherLabels } = {}) {
diff --git a/app/assets/javascripts/main.js b/app/assets/javascripts/main.js
index 974121412bd..50aa445e9e7 100644
--- a/app/assets/javascripts/main.js
+++ b/app/assets/javascripts/main.js
@@ -1,5 +1,4 @@
/* eslint-disable func-names, space-before-function-paren, no-var, quotes, consistent-return, prefer-arrow-callback, comma-dangle, object-shorthand, no-new, max-len, no-multi-spaces, import/newline-after-import, import/first */
-/* global Flash */
/* global ConfirmDangerModal */
/* global Aside */
@@ -47,12 +46,6 @@ import './lib/utils/url_utility';
// behaviors
import './behaviors/';
-// u2f
-import './u2f/authenticate';
-import './u2f/error';
-import './u2f/register';
-import './u2f/util';
-
// everything else
import './activities';
import './admin';
@@ -61,10 +54,6 @@ import './autosave';
import loadAwardsHandler from './awards_handler';
import bp from './breakpoints';
import './broadcast_message';
-import './build';
-import './build_artifacts';
-import './build_variables';
-import './ci_lint_editor';
import './commits';
import './compare';
import './compare_autocomplete';
@@ -75,7 +64,7 @@ import './diff';
import './dropzone_input';
import './due_date_select';
import './files_comment_button';
-import './flash';
+import Flash from './flash';
import './gl_dropdown';
import './gl_field_error';
import './gl_field_errors';
@@ -168,7 +157,6 @@ $(function () {
var $document = $(document);
var $window = $(window);
var $sidebarGutterToggle = $('.js-sidebar-toggle');
- var $flash = $('.flash-container');
var bootstrapBreakpoint = bp.getBreakpointSize();
var fitSidebarForSize;
@@ -253,13 +241,6 @@ $(function () {
// Form submitter
});
gl.utils.localTimeAgo($('abbr.timeago, .js-timeago'), true);
- // Flash
- if ($flash.length > 0) {
- $flash.click(function () {
- return $(this).fadeOut();
- });
- $flash.show();
- }
// Disable form buttons while a form is submitting
$body.on('ajax:complete, ajax:beforeSend, submit', 'form', function (e) {
var buttons;
diff --git a/app/assets/javascripts/merge_conflicts/components/diff_file_editor.js b/app/assets/javascripts/merge_conflicts/components/diff_file_editor.js
index 645045fea88..93f8f6ee926 100644
--- a/app/assets/javascripts/merge_conflicts/components/diff_file_editor.js
+++ b/app/assets/javascripts/merge_conflicts/components/diff_file_editor.js
@@ -1,8 +1,8 @@
/* eslint-disable comma-dangle, quote-props, no-useless-computed-key, object-shorthand, no-new, no-param-reassign, max-len */
/* global ace */
-/* global Flash */
import Vue from 'vue';
+import Flash from '../../flash';
((global) => {
global.mergeConflicts = global.mergeConflicts || {};
diff --git a/app/assets/javascripts/merge_conflicts/merge_conflicts_bundle.js b/app/assets/javascripts/merge_conflicts/merge_conflicts_bundle.js
index d74cf5328ad..17591829b76 100644
--- a/app/assets/javascripts/merge_conflicts/merge_conflicts_bundle.js
+++ b/app/assets/javascripts/merge_conflicts/merge_conflicts_bundle.js
@@ -1,7 +1,7 @@
/* eslint-disable new-cap, comma-dangle, no-new */
-/* global Flash */
import Vue from 'vue';
+import Flash from '../flash';
import initIssuableSidebar from '../init_issuable_sidebar';
import './merge_conflict_store';
import './merge_conflict_service';
diff --git a/app/assets/javascripts/merge_request_tabs.js b/app/assets/javascripts/merge_request_tabs.js
index c042b22d1fd..df042c7baff 100644
--- a/app/assets/javascripts/merge_request_tabs.js
+++ b/app/assets/javascripts/merge_request_tabs.js
@@ -1,9 +1,8 @@
/* eslint-disable no-new, class-methods-use-this */
-/* global Flash */
/* global notes */
import Cookies from 'js-cookie';
-import './flash';
+import Flash from './flash';
import BlobForkSuggestion from './blob/blob_fork_suggestion';
import initChangesDropdown from './init_changes_dropdown';
import bp from './breakpoints';
diff --git a/app/assets/javascripts/milestone.js b/app/assets/javascripts/milestone.js
index 3e07ec4d0aa..8f3f1986763 100644
--- a/app/assets/javascripts/milestone.js
+++ b/app/assets/javascripts/milestone.js
@@ -1,7 +1,8 @@
/* eslint-disable func-names, space-before-function-paren, wrap-iife, no-use-before-define, camelcase, quotes, object-shorthand, no-shadow, no-unused-vars, comma-dangle, no-var, prefer-template, no-underscore-dangle, consistent-return, one-var, one-var-declaration-per-line, default-case, prefer-arrow-callback, max-len */
-/* global Flash */
/* global Sortable */
+import Flash from './flash';
+
(function() {
this.Milestone = (function() {
function Milestone() {
diff --git a/app/assets/javascripts/mini_pipeline_graph_dropdown.js b/app/assets/javascripts/mini_pipeline_graph_dropdown.js
index 64c1447f427..ca3d271663b 100644
--- a/app/assets/javascripts/mini_pipeline_graph_dropdown.js
+++ b/app/assets/javascripts/mini_pipeline_graph_dropdown.js
@@ -1,5 +1,5 @@
/* eslint-disable no-new */
-/* global Flash */
+import Flash from './flash';
/**
* In each pipelines table we have a mini pipeline graph for each pipeline.
diff --git a/app/assets/javascripts/monitoring/components/dashboard.vue b/app/assets/javascripts/monitoring/components/dashboard.vue
index 442ed86d50c..cbe24c0915b 100644
--- a/app/assets/javascripts/monitoring/components/dashboard.vue
+++ b/app/assets/javascripts/monitoring/components/dashboard.vue
@@ -1,6 +1,6 @@
<script>
- /* global Flash */
import _ from 'underscore';
+ import Flash from '../../flash';
import MonitoringService from '../services/monitoring_service';
import GraphGroup from './graph_group.vue';
import Graph from './graph.vue';
diff --git a/app/assets/javascripts/notes.js b/app/assets/javascripts/notes.js
index 24de21f2ce2..cf7322ba1da 100644
--- a/app/assets/javascripts/notes.js
+++ b/app/assets/javascripts/notes.js
@@ -5,7 +5,6 @@ default-case, prefer-template, consistent-return, no-alert, no-return-assign,
no-param-reassign, prefer-arrow-callback, no-else-return, comma-dangle, no-new,
brace-style, no-lonely-if, vars-on-top, no-unused-vars, no-sequences, no-shadow,
newline-per-chained-call, no-useless-escape, class-methods-use-this */
-/* global Flash */
/* global Autosave */
/* global ResolveService */
/* global mrRefreshWidgetUrl */
@@ -18,6 +17,7 @@ import Dropzone from 'dropzone';
import 'vendor/jquery.caret'; // required by jquery.atwho
import 'vendor/jquery.atwho';
import AjaxCache from '~/lib/utils/ajax_cache';
+import Flash from './flash';
import CommentTypeToggle from './comment_type_toggle';
import loadAwardsHandler from './awards_handler';
import './autosave';
@@ -354,7 +354,7 @@ export default class Notes {
Object.keys(noteEntity.commands_changes).length > 0) {
$notesList.find('.system-note.being-posted').remove();
}
- this.addFlash(noteEntity.errors.commands_only, 'notice', this.parentTimeline);
+ this.addFlash(noteEntity.errors.commands_only, 'notice', this.parentTimeline.get(0));
this.refresh();
}
return;
@@ -593,7 +593,7 @@ export default class Notes {
} else if ($form.hasClass('js-discussion-note-form')) {
formParentTimeline = $form.closest('.discussion-notes').find('.notes');
}
- return this.addFlash('Your comment could not be submitted! Please check your network connection and try again.', 'alert', formParentTimeline);
+ return this.addFlash('Your comment could not be submitted! Please check your network connection and try again.', 'alert', formParentTimeline.get(0));
}
updateNoteError($parentTimeline) {
@@ -1213,13 +1213,13 @@ export default class Notes {
}
addFlash(...flashParams) {
- this.flashInstance = new Flash(...flashParams);
+ this.flashContainer = new Flash(...flashParams);
}
clearFlash() {
- if (this.flashInstance && this.flashInstance.flashContainer) {
- this.flashInstance.flashContainer.hide();
- this.flashInstance = null;
+ if (this.flashContainer) {
+ this.flashContainer.style.display = 'none';
+ this.flashContainer = null;
}
}
diff --git a/app/assets/javascripts/notes/components/issue_comment_form.vue b/app/assets/javascripts/notes/components/issue_comment_form.vue
index ab8516296a8..2ce52e4538a 100644
--- a/app/assets/javascripts/notes/components/issue_comment_form.vue
+++ b/app/assets/javascripts/notes/components/issue_comment_form.vue
@@ -1,8 +1,9 @@
<script>
- /* global Flash, Autosave */
+ /* global Autosave */
import { mapActions, mapGetters } from 'vuex';
import _ from 'underscore';
import autosize from 'vendor/autosize';
+ import Flash from '../../flash';
import '../../autosave';
import TaskList from '../../task_list';
import * as constants from '../constants';
@@ -145,7 +146,7 @@
Flash(
'Something went wrong while adding your comment. Please try again.',
'alert',
- $(this.$refs.commentForm),
+ this.$refs.commentForm,
);
}
} else {
@@ -160,7 +161,7 @@
this.isSubmitting = false;
this.discard(false);
const msg = 'Your comment could not be submitted! Please check your network connection and try again.';
- Flash(msg, 'alert', $(this.$el));
+ Flash(msg, 'alert', this.$el);
this.note = noteData.data.note.note; // Restore textarea content.
this.removePlaceholderNotes();
});
diff --git a/app/assets/javascripts/notes/components/issue_discussion.vue b/app/assets/javascripts/notes/components/issue_discussion.vue
index b131ef4b182..baf43190d9e 100644
--- a/app/assets/javascripts/notes/components/issue_discussion.vue
+++ b/app/assets/javascripts/notes/components/issue_discussion.vue
@@ -1,6 +1,6 @@
<script>
- /* global Flash */
import { mapActions, mapGetters } from 'vuex';
+ import Flash from '../../flash';
import { SYSTEM_NOTE } from '../constants';
import issueNote from './issue_note.vue';
import userAvatarLink from '../../vue_shared/components/user_avatar/user_avatar_link.vue';
@@ -133,7 +133,7 @@
this.isReplying = true;
this.$nextTick(() => {
const msg = 'Your comment could not be submitted! Please check your network connection and try again.';
- Flash(msg, 'alert', $(this.$el));
+ Flash(msg, 'alert', this.$el);
this.$refs.noteForm.note = noteText;
callback(err);
});
diff --git a/app/assets/javascripts/notes/components/issue_note.vue b/app/assets/javascripts/notes/components/issue_note.vue
index 1f43b8a16ad..0ddbd672bed 100644
--- a/app/assets/javascripts/notes/components/issue_note.vue
+++ b/app/assets/javascripts/notes/components/issue_note.vue
@@ -1,7 +1,6 @@
<script>
- /* global Flash */
-
import { mapGetters, mapActions } from 'vuex';
+ import Flash from '../../flash';
import userAvatarLink from '../../vue_shared/components/user_avatar/user_avatar_link.vue';
import issueNoteHeader from './issue_note_header.vue';
import issueNoteActions from './issue_note_actions.vue';
@@ -101,7 +100,7 @@
this.isEditing = true;
this.$nextTick(() => {
const msg = 'Something went wrong while editing your comment. Please try again.';
- Flash(msg, 'alert', $(this.$el));
+ Flash(msg, 'alert', this.$el);
this.recoverNoteContent(noteText);
callback();
});
diff --git a/app/assets/javascripts/notes/components/issue_note_awards_list.vue b/app/assets/javascripts/notes/components/issue_note_awards_list.vue
index d42e61e3899..c3a340139e7 100644
--- a/app/assets/javascripts/notes/components/issue_note_awards_list.vue
+++ b/app/assets/javascripts/notes/components/issue_note_awards_list.vue
@@ -1,10 +1,9 @@
<script>
- /* global Flash */
-
import { mapActions, mapGetters } from 'vuex';
import emojiSmiling from 'icons/_emoji_slightly_smiling_face.svg';
import emojiSmile from 'icons/_emoji_smile.svg';
import emojiSmiley from 'icons/_emoji_smiley.svg';
+ import Flash from '../../flash';
import { glEmojiTag } from '../../emoji';
import tooltip from '../../vue_shared/directives/tooltip';
diff --git a/app/assets/javascripts/notes/components/issue_notes_app.vue b/app/assets/javascripts/notes/components/issue_notes_app.vue
index b6fc5e5036f..aecd1f957e5 100644
--- a/app/assets/javascripts/notes/components/issue_notes_app.vue
+++ b/app/assets/javascripts/notes/components/issue_notes_app.vue
@@ -1,6 +1,6 @@
<script>
- /* global Flash */
import { mapGetters, mapActions } from 'vuex';
+ import Flash from '../../flash';
import store from '../stores/';
import * as constants from '../constants';
import issueNote from './issue_note.vue';
diff --git a/app/assets/javascripts/notes/stores/actions.js b/app/assets/javascripts/notes/stores/actions.js
index 1a791039909..6f04aecc9b7 100644
--- a/app/assets/javascripts/notes/stores/actions.js
+++ b/app/assets/javascripts/notes/stores/actions.js
@@ -1,5 +1,5 @@
-/* global Flash */
import Visibility from 'visibilityjs';
+import Flash from '../../flash';
import Poll from '../../lib/utils/poll';
import * as types from './mutation_types';
import * as utils from './utils';
@@ -99,7 +99,7 @@ export const saveNote = ({ commit, dispatch }, noteData) => {
eTagPoll.makeRequest();
$('.js-gfm-input').trigger('clear-commands-cache.atwho');
- Flash('Commands applied', 'notice', $(noteData.flashContainer));
+ Flash('Commands applied', 'notice', noteData.flashContainer);
}
if (commandsChanges) {
@@ -114,8 +114,8 @@ export const saveNote = ({ commit, dispatch }, noteData) => {
.catch(() => {
Flash(
'Something went wrong while adding your award. Please try again.',
- null,
- $(noteData.flashContainer),
+ 'alert',
+ noteData.flashContainer,
);
});
}
@@ -126,7 +126,7 @@ export const saveNote = ({ commit, dispatch }, noteData) => {
}
if (errors && errors.commands_only) {
- Flash(errors.commands_only, 'notice', $(noteData.flashContainer));
+ Flash(errors.commands_only, 'notice', noteData.flashContainer);
}
commit(types.REMOVE_PLACEHOLDER_NOTES);
diff --git a/app/assets/javascripts/notifications_dropdown.js b/app/assets/javascripts/notifications_dropdown.js
index 838356133cd..f90ac2d9f71 100644
--- a/app/assets/javascripts/notifications_dropdown.js
+++ b/app/assets/javascripts/notifications_dropdown.js
@@ -1,5 +1,5 @@
/* eslint-disable func-names, space-before-function-paren, wrap-iife, one-var, no-var, one-var-declaration-per-line, no-unused-vars, consistent-return, prefer-arrow-callback, no-else-return, max-len */
-/* global Flash */
+import Flash from './flash';
(function() {
this.NotificationsDropdown = (function() {
diff --git a/app/assets/javascripts/pipelines/components/pipelines_actions.vue b/app/assets/javascripts/pipelines/components/pipelines_actions.vue
index c4c63a52358..f3c0aca17ba 100644
--- a/app/assets/javascripts/pipelines/components/pipelines_actions.vue
+++ b/app/assets/javascripts/pipelines/components/pipelines_actions.vue
@@ -1,6 +1,4 @@
<script>
- /* global Flash */
- import '~/flash';
import playIconSvg from 'icons/_icon_play.svg';
import eventHub from '../event_hub';
import loadingIcon from '../../vue_shared/components/loading_icon.vue';
diff --git a/app/assets/javascripts/pipelines/components/stage.vue b/app/assets/javascripts/pipelines/components/stage.vue
index a4a27247406..1a7a5c2a415 100644
--- a/app/assets/javascripts/pipelines/components/stage.vue
+++ b/app/assets/javascripts/pipelines/components/stage.vue
@@ -13,7 +13,7 @@
* 4. Commit widget
*/
-/* global Flash */
+import Flash from '../../flash';
import { borderlessStatusIconEntityMap } from '../../vue_shared/ci_status_icons';
import loadingIcon from '../../vue_shared/components/loading_icon.vue';
import tooltip from '../../vue_shared/directives/tooltip';
diff --git a/app/assets/javascripts/pipelines/mixins/pipelines.js b/app/assets/javascripts/pipelines/mixins/pipelines.js
index e97f5632dc8..50bdf80c3e3 100644
--- a/app/assets/javascripts/pipelines/mixins/pipelines.js
+++ b/app/assets/javascripts/pipelines/mixins/pipelines.js
@@ -1,6 +1,5 @@
-/* global Flash */
-import '~/flash';
import Visibility from 'visibilityjs';
+import Flash from '../../flash';
import Poll from '../../lib/utils/poll';
import emptyState from '../components/empty_state.vue';
import errorState from '../components/error_state.vue';
diff --git a/app/assets/javascripts/pipelines/pipeline_details_bundle.js b/app/assets/javascripts/pipelines/pipeline_details_bundle.js
index bfc416da50b..206023d4ddb 100644
--- a/app/assets/javascripts/pipelines/pipeline_details_bundle.js
+++ b/app/assets/javascripts/pipelines/pipeline_details_bundle.js
@@ -1,6 +1,5 @@
-/* global Flash */
-
import Vue from 'vue';
+import Flash from '../flash';
import PipelinesMediator from './pipeline_details_mediatior';
import pipelineGraph from './components/graph/graph_component.vue';
import pipelineHeader from './components/header_component.vue';
diff --git a/app/assets/javascripts/pipelines/pipeline_details_mediatior.js b/app/assets/javascripts/pipelines/pipeline_details_mediatior.js
index 385e7430a7d..823ccd849f4 100644
--- a/app/assets/javascripts/pipelines/pipeline_details_mediatior.js
+++ b/app/assets/javascripts/pipelines/pipeline_details_mediatior.js
@@ -1,6 +1,5 @@
-/* global Flash */
-
import Visibility from 'visibilityjs';
+import Flash from '../flash';
import Poll from '../lib/utils/poll';
import PipelineStore from './stores/pipeline_store';
import PipelineService from './services/pipeline_service';
diff --git a/app/assets/javascripts/profile/profile.js b/app/assets/javascripts/profile/profile.js
index 3deb242bc1f..0dc02f012e4 100644
--- a/app/assets/javascripts/profile/profile.js
+++ b/app/assets/javascripts/profile/profile.js
@@ -1,5 +1,5 @@
/* eslint-disable comma-dangle, no-unused-vars, class-methods-use-this, quotes, consistent-return, func-names, prefer-arrow-callback, space-before-function-paren, max-len */
-/* global Flash */
+import Flash from '../flash';
import { getPagePath } from '../lib/utils/common_utils';
((global) => {
diff --git a/app/assets/javascripts/protected_branches/protected_branch_edit.js b/app/assets/javascripts/protected_branches/protected_branch_edit.js
index 3b920942a3f..632625da8e7 100644
--- a/app/assets/javascripts/protected_branches/protected_branch_edit.js
+++ b/app/assets/javascripts/protected_branches/protected_branch_edit.js
@@ -1,6 +1,5 @@
/* eslint-disable no-new */
-/* global Flash */
-
+import Flash from '../flash';
import ProtectedBranchAccessDropdown from './protected_branch_access_dropdown';
export default class ProtectedBranchEdit {
@@ -57,7 +56,7 @@ export default class ProtectedBranchEdit {
},
},
error() {
- new Flash('Failed to update branch!', null, $('.js-protected-branches-list'));
+ new Flash('Failed to update branch!', 'alert', document.querySelector('.js-protected-branches-list'));
},
}).always(() => {
this.$allowedToMergeDropdown.enable();
diff --git a/app/assets/javascripts/protected_tags/protected_tag_edit.js b/app/assets/javascripts/protected_tags/protected_tag_edit.js
index 09a387c0f9e..dad0ad25b65 100644
--- a/app/assets/javascripts/protected_tags/protected_tag_edit.js
+++ b/app/assets/javascripts/protected_tags/protected_tag_edit.js
@@ -1,6 +1,5 @@
/* eslint-disable no-new */
-/* global Flash */
-
+import Flash from '../flash';
import ProtectedTagAccessDropdown from './protected_tag_access_dropdown';
export default class ProtectedTagEdit {
@@ -43,7 +42,7 @@ export default class ProtectedTagEdit {
},
},
error() {
- new Flash('Failed to update tag!', null, $('.js-protected-tags-list'));
+ new Flash('Failed to update tag!', 'alert', document.querySelector('.js-protected-tags-list'));
},
}).always(() => {
this.$allowedToCreateDropdownButton.enable();
diff --git a/app/assets/javascripts/repo/components/repo_commit_section.vue b/app/assets/javascripts/repo/components/repo_commit_section.vue
index 119e38c583d..6d8cc964eb2 100644
--- a/app/assets/javascripts/repo/components/repo_commit_section.vue
+++ b/app/assets/javascripts/repo/components/repo_commit_section.vue
@@ -1,5 +1,5 @@
<script>
-/* global Flash */
+import Flash from '../../flash';
import Store from '../stores/repo_store';
import RepoMixin from '../mixins/repo_mixin';
import Service from '../services/repo_service';
diff --git a/app/assets/javascripts/repo/helpers/repo_helper.js b/app/assets/javascripts/repo/helpers/repo_helper.js
index 7483f8bc305..46204598e1d 100644
--- a/app/assets/javascripts/repo/helpers/repo_helper.js
+++ b/app/assets/javascripts/repo/helpers/repo_helper.js
@@ -1,7 +1,6 @@
-/* global Flash */
import Service from '../services/repo_service';
import Store from '../stores/repo_store';
-import '../../flash';
+import Flash from '../../flash';
const RepoHelper = {
monacoInstance: null,
diff --git a/app/assets/javascripts/repo/services/repo_service.js b/app/assets/javascripts/repo/services/repo_service.js
index af83497fa39..830685f7e6e 100644
--- a/app/assets/javascripts/repo/services/repo_service.js
+++ b/app/assets/javascripts/repo/services/repo_service.js
@@ -1,4 +1,3 @@
-/* global Flash */
import axios from 'axios';
import Store from '../stores/repo_store';
import Api from '../../api';
diff --git a/app/assets/javascripts/repo/stores/repo_store.js b/app/assets/javascripts/repo/stores/repo_store.js
index 93b39cff27e..c633f538c1b 100644
--- a/app/assets/javascripts/repo/stores/repo_store.js
+++ b/app/assets/javascripts/repo/stores/repo_store.js
@@ -1,4 +1,3 @@
-/* global Flash */
import Helper from '../helpers/repo_helper';
import Service from '../services/repo_service';
diff --git a/app/assets/javascripts/search.js b/app/assets/javascripts/search.js
index 05caf177aec..07fee53d814 100644
--- a/app/assets/javascripts/search.js
+++ b/app/assets/javascripts/search.js
@@ -1,5 +1,5 @@
/* eslint-disable func-names, space-before-function-paren, wrap-iife, no-var, one-var, one-var-declaration-per-line, object-shorthand, prefer-arrow-callback, comma-dangle, prefer-template, quotes, no-else-return, max-len */
-/* global Flash */
+import Flash from './flash';
import Api from './api';
(function() {
diff --git a/app/assets/javascripts/sidebar/components/assignees/sidebar_assignees.js b/app/assets/javascripts/sidebar/components/assignees/sidebar_assignees.js
index f83c3b037ed..74c17bc14a2 100644
--- a/app/assets/javascripts/sidebar/components/assignees/sidebar_assignees.js
+++ b/app/assets/javascripts/sidebar/components/assignees/sidebar_assignees.js
@@ -1,5 +1,4 @@
-/* global Flash */
-
+import Flash from '../../../flash';
import AssigneeTitle from './assignee_title';
import Assignees from './assignees';
diff --git a/app/assets/javascripts/sidebar/components/confidential/confidential_issue_sidebar.vue b/app/assets/javascripts/sidebar/components/confidential/confidential_issue_sidebar.vue
index f2b1099a678..22a9a34dda3 100644
--- a/app/assets/javascripts/sidebar/components/confidential/confidential_issue_sidebar.vue
+++ b/app/assets/javascripts/sidebar/components/confidential/confidential_issue_sidebar.vue
@@ -1,5 +1,5 @@
<script>
-/* global Flash */
+import Flash from '../../../flash';
import editForm from './edit_form.vue';
export default {
diff --git a/app/assets/javascripts/sidebar/lib/sidebar_move_issue.js b/app/assets/javascripts/sidebar/lib/sidebar_move_issue.js
index 3c9de02407e..977dd83a7ea 100644
--- a/app/assets/javascripts/sidebar/lib/sidebar_move_issue.js
+++ b/app/assets/javascripts/sidebar/lib/sidebar_move_issue.js
@@ -1,5 +1,3 @@
-/* global Flash */
-
function isValidProjectId(id) {
return id > 0;
}
@@ -38,7 +36,7 @@ class SidebarMoveIssue {
data: (searchTerm, callback) => {
this.mediator.fetchAutocompleteProjects(searchTerm)
.then(callback)
- .catch(() => new Flash('An error occurred while fetching projects autocomplete.'));
+ .catch(() => new window.Flash('An error occurred while fetching projects autocomplete.'));
},
renderRow: project => `
<li>
@@ -73,7 +71,7 @@ class SidebarMoveIssue {
this.mediator.moveIssue()
.catch(() => {
- Flash('An error occurred while moving the issue.');
+ window.Flash('An error occurred while moving the issue.');
this.$confirmButton
.enable()
.removeClass('is-loading');
diff --git a/app/assets/javascripts/sidebar/sidebar_mediator.js b/app/assets/javascripts/sidebar/sidebar_mediator.js
index 2fe6e5b31f0..ede3a0de144 100644
--- a/app/assets/javascripts/sidebar/sidebar_mediator.js
+++ b/app/assets/javascripts/sidebar/sidebar_mediator.js
@@ -1,5 +1,4 @@
-/* global Flash */
-
+import Flash from '../flash';
import Service from './services/sidebar_service';
import Store from './stores/sidebar_store';
diff --git a/app/assets/javascripts/star.js b/app/assets/javascripts/star.js
index 3a06b477d7c..77db075d1ef 100644
--- a/app/assets/javascripts/star.js
+++ b/app/assets/javascripts/star.js
@@ -1,6 +1,5 @@
/* eslint-disable func-names, space-before-function-paren, wrap-iife, no-unused-vars, one-var, no-var, one-var-declaration-per-line, prefer-arrow-callback, no-new, max-len */
-/* global Flash */
-
+import Flash from './flash';
import { __, s__ } from './locale';
export default class Star {
diff --git a/app/assets/javascripts/task_list.js b/app/assets/javascripts/task_list.js
index c39f569da5e..dcbec40c79e 100644
--- a/app/assets/javascripts/task_list.js
+++ b/app/assets/javascripts/task_list.js
@@ -1,6 +1,5 @@
-/* global Flash */
-
import 'deckar01-task_list';
+import Flash from './flash';
export default class TaskList {
constructor(options = {}) {
diff --git a/app/assets/javascripts/two_factor_auth.js b/app/assets/javascripts/two_factor_auth.js
index d26f61562a5..e3414d9afff 100644
--- a/app/assets/javascripts/two_factor_auth.js
+++ b/app/assets/javascripts/two_factor_auth.js
@@ -1,4 +1,5 @@
-/* global U2FRegister */
+import U2FRegister from './u2f/register';
+
document.addEventListener('DOMContentLoaded', () => {
const twoFactorNode = document.querySelector('.js-two-factor-auth');
const skippable = twoFactorNode.dataset.twoFactorSkippable === 'true';
diff --git a/app/assets/javascripts/u2f/authenticate.js b/app/assets/javascripts/u2f/authenticate.js
index 8821b22477f..a3cc04e35fe 100644
--- a/app/assets/javascripts/u2f/authenticate.js
+++ b/app/assets/javascripts/u2f/authenticate.js
@@ -1,118 +1,108 @@
-/* eslint-disable func-names, space-before-function-paren, no-var, prefer-rest-params, wrap-iife, prefer-arrow-callback, no-else-return, quotes, quote-props, comma-dangle, one-var, one-var-declaration-per-line, max-len */
+/* eslint-disable func-names, wrap-iife */
/* global u2f */
-/* global U2FError */
-/* global U2FUtil */
-
import _ from 'underscore';
+import isU2FSupported from './util';
+import U2FError from './error';
// Authenticate U2F (universal 2nd factor) devices for users to authenticate with.
//
// State Flow #1: setup -> in_progress -> authenticated -> POST to server
// State Flow #2: setup -> in_progress -> error -> setup
-(function() {
- const global = window.gl || (window.gl = {});
-
- global.U2FAuthenticate = (function() {
- function U2FAuthenticate(container, form, u2fParams, fallbackButton, fallbackUI) {
- this.container = container;
- this.renderNotSupported = this.renderNotSupported.bind(this);
- this.renderAuthenticated = this.renderAuthenticated.bind(this);
- this.renderError = this.renderError.bind(this);
- this.renderInProgress = this.renderInProgress.bind(this);
- this.renderTemplate = this.renderTemplate.bind(this);
- this.authenticate = this.authenticate.bind(this);
- this.start = this.start.bind(this);
- this.appId = u2fParams.app_id;
- this.challenge = u2fParams.challenge;
- this.form = form;
- this.fallbackButton = fallbackButton;
- this.fallbackUI = fallbackUI;
- if (this.fallbackButton) this.fallbackButton.addEventListener('click', this.switchToFallbackUI.bind(this));
- this.signRequests = u2fParams.sign_requests.map(function(request) {
- // The U2F Javascript API v1.1 requires a single challenge, with
- // _no challenges per-request_. The U2F Javascript API v1.0 requires a
- // challenge per-request, which is done by copying the single challenge
- // into every request.
- //
- // In either case, we don't need the per-request challenges that the server
- // has generated, so we can remove them.
- //
- // Note: The server library fixes this behaviour in (unreleased) version 1.0.0.
- // This can be removed once we upgrade.
- // https://github.com/castle/ruby-u2f/commit/103f428071a81cd3d5f80c2e77d522d5029946a4
- return _(request).omit('challenge');
- });
+export default class U2FAuthenticate {
+ constructor(container, form, u2fParams, fallbackButton, fallbackUI) {
+ this.container = container;
+ this.renderNotSupported = this.renderNotSupported.bind(this);
+ this.renderAuthenticated = this.renderAuthenticated.bind(this);
+ this.renderError = this.renderError.bind(this);
+ this.renderInProgress = this.renderInProgress.bind(this);
+ this.renderTemplate = this.renderTemplate.bind(this);
+ this.authenticate = this.authenticate.bind(this);
+ this.start = this.start.bind(this);
+ this.appId = u2fParams.app_id;
+ this.challenge = u2fParams.challenge;
+ this.form = form;
+ this.fallbackButton = fallbackButton;
+ this.fallbackUI = fallbackUI;
+ if (this.fallbackButton) {
+ this.fallbackButton.addEventListener('click', this.switchToFallbackUI.bind(this));
}
- U2FAuthenticate.prototype.start = function() {
- if (U2FUtil.isU2FSupported()) {
- return this.renderInProgress();
- } else {
- return this.renderNotSupported();
- }
- };
+ // The U2F Javascript API v1.1 requires a single challenge, with
+ // _no challenges per-request_. The U2F Javascript API v1.0 requires a
+ // challenge per-request, which is done by copying the single challenge
+ // into every request.
+ //
+ // In either case, we don't need the per-request challenges that the server
+ // has generated, so we can remove them.
+ //
+ // Note: The server library fixes this behaviour in (unreleased) version 1.0.0.
+ // This can be removed once we upgrade.
+ // https://github.com/castle/ruby-u2f/commit/103f428071a81cd3d5f80c2e77d522d5029946a4
+ this.signRequests = u2fParams.sign_requests.map(request => _(request).omit('challenge'));
- U2FAuthenticate.prototype.authenticate = function() {
- return u2f.sign(this.appId, this.challenge, this.signRequests, (function(_this) {
- return function(response) {
- var error;
- if (response.errorCode) {
- error = new U2FError(response.errorCode, 'authenticate');
- return _this.renderError(error);
- } else {
- return _this.renderAuthenticated(JSON.stringify(response));
- }
- };
- })(this), 10);
+ this.templates = {
+ notSupported: '#js-authenticate-u2f-not-supported',
+ setup: '#js-authenticate-u2f-setup',
+ inProgress: '#js-authenticate-u2f-in-progress',
+ error: '#js-authenticate-u2f-error',
+ authenticated: '#js-authenticate-u2f-authenticated',
};
+ }
- // Rendering #
- U2FAuthenticate.prototype.templates = {
- "notSupported": "#js-authenticate-u2f-not-supported",
- "setup": '#js-authenticate-u2f-setup',
- "inProgress": '#js-authenticate-u2f-in-progress',
- "error": '#js-authenticate-u2f-error',
- "authenticated": '#js-authenticate-u2f-authenticated'
- };
+ start() {
+ if (isU2FSupported()) {
+ return this.renderInProgress();
+ }
+ return this.renderNotSupported();
+ }
- U2FAuthenticate.prototype.renderTemplate = function(name, params) {
- var template, templateString;
- templateString = $(this.templates[name]).html();
- template = _.template(templateString);
- return this.container.html(template(params));
- };
+ authenticate() {
+ return u2f.sign(this.appId, this.challenge, this.signRequests, (function (_this) {
+ return function (response) {
+ if (response.errorCode) {
+ const error = new U2FError(response.errorCode, 'authenticate');
+ return _this.renderError(error);
+ }
+ return _this.renderAuthenticated(JSON.stringify(response));
+ };
+ })(this), 10);
+ }
- U2FAuthenticate.prototype.renderInProgress = function() {
- this.renderTemplate('inProgress');
- return this.authenticate();
- };
+ renderTemplate(name, params) {
+ const templateString = $(this.templates[name]).html();
+ const template = _.template(templateString);
+ return this.container.html(template(params));
+ }
- U2FAuthenticate.prototype.renderError = function(error) {
- this.renderTemplate('error', {
- error_message: error.message(),
- error_code: error.errorCode
- });
- return this.container.find('#js-u2f-try-again').on('click', this.renderInProgress);
- };
+ renderInProgress() {
+ this.renderTemplate('inProgress');
+ return this.authenticate();
+ }
- U2FAuthenticate.prototype.renderAuthenticated = function(deviceResponse) {
- this.renderTemplate('authenticated');
- const container = this.container[0];
- container.querySelector('#js-device-response').value = deviceResponse;
- container.querySelector(this.form).submit();
- this.fallbackButton.classList.add('hidden');
- };
+ renderError(error) {
+ this.renderTemplate('error', {
+ error_message: error.message(),
+ error_code: error.errorCode,
+ });
+ return this.container.find('#js-u2f-try-again').on('click', this.renderInProgress);
+ }
- U2FAuthenticate.prototype.renderNotSupported = function() {
- return this.renderTemplate('notSupported');
- };
+ renderAuthenticated(deviceResponse) {
+ this.renderTemplate('authenticated');
+ const container = this.container[0];
+ container.querySelector('#js-device-response').value = deviceResponse;
+ container.querySelector(this.form).submit();
+ this.fallbackButton.classList.add('hidden');
+ }
- U2FAuthenticate.prototype.switchToFallbackUI = function() {
- this.fallbackButton.classList.add('hidden');
- this.container[0].classList.add('hidden');
- this.fallbackUI.classList.remove('hidden');
- };
+ renderNotSupported() {
+ return this.renderTemplate('notSupported');
+ }
+
+ switchToFallbackUI() {
+ this.fallbackButton.classList.add('hidden');
+ this.container[0].classList.add('hidden');
+ this.fallbackUI.classList.remove('hidden');
+ }
- return U2FAuthenticate;
- })();
-})();
+}
diff --git a/app/assets/javascripts/u2f/error.js b/app/assets/javascripts/u2f/error.js
index 3119b3480c3..1a98564ff55 100644
--- a/app/assets/javascripts/u2f/error.js
+++ b/app/assets/javascripts/u2f/error.js
@@ -1,25 +1,22 @@
-/* eslint-disable func-names, space-before-function-paren, no-var, prefer-rest-params, wrap-iife, no-console, quotes, prefer-template, max-len */
-/* global u2f */
+export default class U2FError {
+ constructor(errorCode, u2fFlowType) {
+ this.errorCode = errorCode;
+ this.message = this.message.bind(this);
+ this.httpsDisabled = window.location.protocol !== 'https:';
+ this.u2fFlowType = u2fFlowType;
+ }
-(function() {
- this.U2FError = (function() {
- function U2FError(errorCode, u2fFlowType) {
- this.errorCode = errorCode;
- this.message = this.message.bind(this);
- this.httpsDisabled = window.location.protocol !== 'https:';
- this.u2fFlowType = u2fFlowType;
- }
-
- U2FError.prototype.message = function() {
- if (this.errorCode === u2f.ErrorCodes.BAD_REQUEST && this.httpsDisabled) {
- return 'U2F only works with HTTPS-enabled websites. Contact your administrator for more details.';
- } else if (this.errorCode === u2f.ErrorCodes.DEVICE_INELIGIBLE) {
- if (this.u2fFlowType === 'authenticate') return 'This device has not been registered with us.';
- if (this.u2fFlowType === 'register') return 'This device has already been registered with us.';
+ message() {
+ if (this.errorCode === window.u2f.ErrorCodes.BAD_REQUEST && this.httpsDisabled) {
+ return 'U2F only works with HTTPS-enabled websites. Contact your administrator for more details.';
+ } else if (this.errorCode === window.u2f.ErrorCodes.DEVICE_INELIGIBLE) {
+ if (this.u2fFlowType === 'authenticate') {
+ return 'This device has not been registered with us.';
}
- return "There was a problem communicating with your device.";
- };
-
- return U2FError;
- })();
-}).call(window);
+ if (this.u2fFlowType === 'register') {
+ return 'This device has already been registered with us.';
+ }
+ }
+ return 'There was a problem communicating with your device.';
+ }
+}
diff --git a/app/assets/javascripts/u2f/register.js b/app/assets/javascripts/u2f/register.js
index 3a2534d553b..cc3f02e75f6 100644
--- a/app/assets/javascripts/u2f/register.js
+++ b/app/assets/javascripts/u2f/register.js
@@ -1,98 +1,89 @@
-/* eslint-disable func-names, space-before-function-paren, no-var, prefer-rest-params, wrap-iife, no-else-return, quotes, quote-props, comma-dangle, one-var, one-var-declaration-per-line, max-len */
+/* eslint-disable func-names, wrap-iife */
/* global u2f */
-/* global U2FError */
-/* global U2FUtil */
import _ from 'underscore';
+import isU2FSupported from './util';
+import U2FError from './error';
// Register U2F (universal 2nd factor) devices for users to authenticate with.
//
// State Flow #1: setup -> in_progress -> registered -> POST to server
// State Flow #2: setup -> in_progress -> error -> setup
-(function() {
- this.U2FRegister = (function() {
- function U2FRegister(container, u2fParams) {
- this.container = container;
- this.renderNotSupported = this.renderNotSupported.bind(this);
- this.renderRegistered = this.renderRegistered.bind(this);
- this.renderError = this.renderError.bind(this);
- this.renderInProgress = this.renderInProgress.bind(this);
- this.renderSetup = this.renderSetup.bind(this);
- this.renderTemplate = this.renderTemplate.bind(this);
- this.register = this.register.bind(this);
- this.start = this.start.bind(this);
- this.appId = u2fParams.app_id;
- this.registerRequests = u2fParams.register_requests;
- this.signRequests = u2fParams.sign_requests;
- }
+export default class U2FRegister {
+ constructor(container, u2fParams) {
+ this.container = container;
+ this.renderNotSupported = this.renderNotSupported.bind(this);
+ this.renderRegistered = this.renderRegistered.bind(this);
+ this.renderError = this.renderError.bind(this);
+ this.renderInProgress = this.renderInProgress.bind(this);
+ this.renderSetup = this.renderSetup.bind(this);
+ this.renderTemplate = this.renderTemplate.bind(this);
+ this.register = this.register.bind(this);
+ this.start = this.start.bind(this);
+ this.appId = u2fParams.app_id;
+ this.registerRequests = u2fParams.register_requests;
+ this.signRequests = u2fParams.sign_requests;
- U2FRegister.prototype.start = function() {
- if (U2FUtil.isU2FSupported()) {
- return this.renderSetup();
- } else {
- return this.renderNotSupported();
- }
+ this.templates = {
+ notSupported: '#js-register-u2f-not-supported',
+ setup: '#js-register-u2f-setup',
+ inProgress: '#js-register-u2f-in-progress',
+ error: '#js-register-u2f-error',
+ registered: '#js-register-u2f-registered',
};
+ }
- U2FRegister.prototype.register = function() {
- return u2f.register(this.appId, this.registerRequests, this.signRequests, (function(_this) {
- return function(response) {
- var error;
- if (response.errorCode) {
- error = new U2FError(response.errorCode, 'register');
- return _this.renderError(error);
- } else {
- return _this.renderRegistered(JSON.stringify(response));
- }
- };
- })(this), 10);
- };
+ start() {
+ if (isU2FSupported()) {
+ return this.renderSetup();
+ }
+ return this.renderNotSupported();
+ }
- // Rendering #
- U2FRegister.prototype.templates = {
- "notSupported": "#js-register-u2f-not-supported",
- "setup": '#js-register-u2f-setup',
- "inProgress": '#js-register-u2f-in-progress',
- "error": '#js-register-u2f-error',
- "registered": '#js-register-u2f-registered'
- };
+ register() {
+ return u2f.register(this.appId, this.registerRequests, this.signRequests, (function (_this) {
+ return function (response) {
+ if (response.errorCode) {
+ const error = new U2FError(response.errorCode, 'register');
+ return _this.renderError(error);
+ }
+ return _this.renderRegistered(JSON.stringify(response));
+ };
+ })(this), 10);
+ }
- U2FRegister.prototype.renderTemplate = function(name, params) {
- var template, templateString;
- templateString = $(this.templates[name]).html();
- template = _.template(templateString);
- return this.container.html(template(params));
- };
+ renderTemplate(name, params) {
+ const templateString = $(this.templates[name]).html();
+ const template = _.template(templateString);
+ return this.container.html(template(params));
+ }
- U2FRegister.prototype.renderSetup = function() {
- this.renderTemplate('setup');
- return this.container.find('#js-setup-u2f-device').on('click', this.renderInProgress);
- };
+ renderSetup() {
+ this.renderTemplate('setup');
+ return this.container.find('#js-setup-u2f-device').on('click', this.renderInProgress);
+ }
- U2FRegister.prototype.renderInProgress = function() {
- this.renderTemplate('inProgress');
- return this.register();
- };
+ renderInProgress() {
+ this.renderTemplate('inProgress');
+ return this.register();
+ }
- U2FRegister.prototype.renderError = function(error) {
- this.renderTemplate('error', {
- error_message: error.message(),
- error_code: error.errorCode
- });
- return this.container.find('#js-u2f-try-again').on('click', this.renderSetup);
- };
+ renderError(error) {
+ this.renderTemplate('error', {
+ error_message: error.message(),
+ error_code: error.errorCode,
+ });
+ return this.container.find('#js-u2f-try-again').on('click', this.renderSetup);
+ }
- U2FRegister.prototype.renderRegistered = function(deviceResponse) {
- this.renderTemplate('registered');
- // Prefer to do this instead of interpolating using Underscore templates
- // because of JSON escaping issues.
- return this.container.find("#js-device-response").val(deviceResponse);
- };
-
- U2FRegister.prototype.renderNotSupported = function() {
- return this.renderTemplate('notSupported');
- };
+ renderRegistered(deviceResponse) {
+ this.renderTemplate('registered');
+ // Prefer to do this instead of interpolating using Underscore templates
+ // because of JSON escaping issues.
+ return this.container.find('#js-device-response').val(deviceResponse);
+ }
- return U2FRegister;
- })();
-}).call(window);
+ renderNotSupported() {
+ return this.renderTemplate('notSupported');
+ }
+}
diff --git a/app/assets/javascripts/u2f/util.js b/app/assets/javascripts/u2f/util.js
index 813d363db00..9771ff935c2 100644
--- a/app/assets/javascripts/u2f/util.js
+++ b/app/assets/javascripts/u2f/util.js
@@ -1,12 +1,3 @@
-/* eslint-disable func-names, space-before-function-paren, wrap-iife */
-(function() {
- this.U2FUtil = (function() {
- function U2FUtil() {}
-
- U2FUtil.isU2FSupported = function() {
- return window.u2f;
- };
-
- return U2FUtil;
- })();
-}).call(window);
+export default function isU2FSupported() {
+ return window.u2f;
+}
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_deployment.js b/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_deployment.js
index e98d147733c..e86a0f7e749 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_deployment.js
+++ b/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_deployment.js
@@ -1,6 +1,5 @@
-/* global Flash */
-
import '~/lib/utils/datetime_utility';
+import Flash from '../../flash';
import MemoryUsage from './mr_widget_memory_usage';
import StatusIcon from './mr_widget_status_icon';
import MRWidgetService from '../services/mr_widget_service';
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_merge_when_pipeline_succeeds.js b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_merge_when_pipeline_succeeds.js
index bdfd4d9667c..05c4a28be88 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_merge_when_pipeline_succeeds.js
+++ b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_merge_when_pipeline_succeeds.js
@@ -1,4 +1,4 @@
-/* global Flash */
+import Flash from '../../../flash';
import statusIcon from '../mr_widget_status_icon';
import MRWidgetAuthor from '../../components/mr_widget_author';
import eventHub from '../../event_hub';
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_merged.js b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_merged.js
index 74fc52796a0..2dfd87ed904 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_merged.js
+++ b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_merged.js
@@ -1,5 +1,4 @@
-/* global Flash */
-
+import Flash from '../../../flash';
import mrWidgetAuthorTime from '../../components/mr_widget_author_time';
import tooltip from '../../../vue_shared/directives/tooltip';
import loadingIcon from '../../../vue_shared/components/loading_icon.vue';
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_ready_to_merge.js b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_ready_to_merge.js
index 61734163b6e..b8a96b23012 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_ready_to_merge.js
+++ b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_ready_to_merge.js
@@ -1,7 +1,7 @@
-/* global Flash */
import successSvg from 'icons/_icon_status_success.svg';
import warningSvg from 'icons/_icon_status_warning.svg';
import simplePoll from '~/lib/utils/simple_poll';
+import Flash from '../../../flash';
import statusIcon from '../mr_widget_status_icon';
import eventHub from '../../event_hub';
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_wip.js b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_wip.js
index 54be1fbe675..4f83350e07c 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_wip.js
+++ b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_wip.js
@@ -1,4 +1,3 @@
-/* global Flash */
import statusIcon from '../mr_widget_status_icon';
import tooltip from '../../../vue_shared/directives/tooltip';
import eventHub from '../../event_hub';
@@ -27,12 +26,12 @@ export default {
.then(res => res.json())
.then((res) => {
eventHub.$emit('UpdateWidgetData', res);
- new Flash('The merge request can now be merged.', 'notice'); // eslint-disable-line
+ new window.Flash('The merge request can now be merged.', 'notice'); // eslint-disable-line
$('.merge-request .detail-page-description .title').text(this.mr.title);
})
.catch(() => {
this.isMakingRequest = false;
- new Flash('Something went wrong. Please try again.'); // eslint-disable-line
+ new window.Flash('Something went wrong. Please try again.'); // eslint-disable-line
});
},
},
diff --git a/app/assets/javascripts/vue_merge_request_widget/mr_widget_options.js b/app/assets/javascripts/vue_merge_request_widget/mr_widget_options.js
index 044b664484b..4f497b204a3 100644
--- a/app/assets/javascripts/vue_merge_request_widget/mr_widget_options.js
+++ b/app/assets/javascripts/vue_merge_request_widget/mr_widget_options.js
@@ -1,5 +1,4 @@
-/* global Flash */
-
+import Flash from '../flash';
import {
WidgetHeader,
WidgetMergeHelp,
diff --git a/app/assets/javascripts/vue_shared/components/markdown/field.vue b/app/assets/javascripts/vue_shared/components/markdown/field.vue
index 759d30c9c7c..af4187fab46 100644
--- a/app/assets/javascripts/vue_shared/components/markdown/field.vue
+++ b/app/assets/javascripts/vue_shared/components/markdown/field.vue
@@ -1,5 +1,5 @@
<script>
- /* global Flash */
+ import Flash from '../../../flash';
import markdownHeader from './header.vue';
import markdownToolbar from './toolbar.vue';
diff --git a/app/helpers/compare_helper.rb b/app/helpers/compare_helper.rb
index 2c28dd81c87..8bf96c0905f 100644
--- a/app/helpers/compare_helper.rb
+++ b/app/helpers/compare_helper.rb
@@ -4,8 +4,8 @@ module CompareHelper
to.present? &&
from != to &&
can?(current_user, :create_merge_request, project) &&
- project.repository.branch_names.include?(from) &&
- project.repository.branch_names.include?(to)
+ project.repository.branch_exists?(from) &&
+ project.repository.branch_exists?(to)
end
def create_mr_path(from = params[:from], to = params[:to], project = @project)
diff --git a/app/helpers/groups_helper.rb b/app/helpers/groups_helper.rb
index 82bceddf1f0..676c1d1988b 100644
--- a/app/helpers/groups_helper.rb
+++ b/app/helpers/groups_helper.rb
@@ -7,7 +7,12 @@ module GroupsHelper
can?(current_user, :change_share_with_group_lock, group)
end
- def group_icon(group)
+ def group_icon(group, options = {})
+ img_path = group_icon_url(group, options)
+ image_tag img_path, options
+ end
+
+ def group_icon_url(group, options = {})
if group.is_a?(String)
group = Group.find_by_full_path(group)
end
@@ -89,7 +94,7 @@ module GroupsHelper
link_to(group_path(group), class: "group-path #{'breadcrumb-item-text' unless for_dropdown} js-breadcrumb-item-text #{'hidable' if hidable}") do
output =
if (group.try(:avatar_url) || show_avatar) && !Rails.env.test?
- image_tag(group_icon(group), class: "avatar-tile", width: 15, height: 15)
+ group_icon(group, class: "avatar-tile", width: 15, height: 15)
else
""
end
diff --git a/app/helpers/lazy_image_tag_helper.rb b/app/helpers/lazy_image_tag_helper.rb
index 2c5619ac41b..603b9438e35 100644
--- a/app/helpers/lazy_image_tag_helper.rb
+++ b/app/helpers/lazy_image_tag_helper.rb
@@ -10,6 +10,7 @@ module LazyImageTagHelper
unless options.delete(:lazy) == false
options[:data] ||= {}
options[:data][:src] = path_to_image(source)
+
options[:class] ||= ""
options[:class] << " lazy"
diff --git a/app/models/concerns/avatarable.rb b/app/models/concerns/avatarable.rb
index 8fbfed11bdf..2ec70203710 100644
--- a/app/models/concerns/avatarable.rb
+++ b/app/models/concerns/avatarable.rb
@@ -11,7 +11,7 @@ module Avatarable
# If asset_host is set then it is expected that assets are handled by a standalone host.
# That means we do not want to get GitLab's relative_url_root option anymore.
- host = asset_host.present? ? asset_host : gitlab_host
+ host = (asset_host.present? && (!respond_to?(:public?) || public?)) ? asset_host : gitlab_host
[host, avatar.url].join
end
diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb
index 75e9bdaaa45..972a35dde4d 100644
--- a/app/models/merge_request.rb
+++ b/app/models/merge_request.rb
@@ -675,13 +675,13 @@ class MergeRequest < ActiveRecord::Base
def source_branch_exists?
return false unless self.source_project
- self.source_project.repository.branch_names.include?(self.source_branch)
+ self.source_project.repository.branch_exists?(self.source_branch)
end
def target_branch_exists?
return false unless self.target_project
- self.target_project.repository.branch_names.include?(self.target_branch)
+ self.target_project.repository.branch_exists?(self.target_branch)
end
def merge_commit_message(include_description: false)
diff --git a/app/serializers/group_entity.rb b/app/serializers/group_entity.rb
index 7c872a3e986..6d8466da902 100644
--- a/app/serializers/group_entity.rb
+++ b/app/serializers/group_entity.rb
@@ -45,6 +45,6 @@ class GroupEntity < Grape::Entity
end
expose :avatar_url do |group|
- group_icon(group)
+ group_icon_url(group)
end
end
diff --git a/app/services/quick_actions/interpret_service.rb b/app/services/quick_actions/interpret_service.rb
index a077b3584b0..955d934838b 100644
--- a/app/services/quick_actions/interpret_service.rb
+++ b/app/services/quick_actions/interpret_service.rb
@@ -458,7 +458,7 @@ module QuickActions
target_branch_param.strip
end
command :target_branch do |branch_name|
- @updates[:target_branch] = branch_name if project.repository.branch_names.include?(branch_name)
+ @updates[:target_branch] = branch_name if project.repository.branch_exists?(branch_name)
end
desc 'Move issue from one column of the board to another'
diff --git a/app/services/system_note_service.rb b/app/services/system_note_service.rb
index 7b32e215c7f..a52dce6cb4b 100644
--- a/app/services/system_note_service.rb
+++ b/app/services/system_note_service.rb
@@ -593,7 +593,7 @@ module SystemNoteService
def discussion_lock(issuable, author)
action = issuable.discussion_locked? ? 'locked' : 'unlocked'
- body = "#{action} this issue"
+ body = "#{action} this #{issuable.class.to_s.titleize.downcase}"
create_note(NoteSummary.new(issuable, issuable.project, author, body, action: action))
end
diff --git a/app/views/admin/groups/_group.html.haml b/app/views/admin/groups/_group.html.haml
index e3a77dfdf10..47cc2d4d27e 100644
--- a/app/views/admin/groups/_group.html.haml
+++ b/app/views/admin/groups/_group.html.haml
@@ -20,7 +20,7 @@
= visibility_level_icon(group.visibility_level, fw: false)
.avatar-container.s40
- = image_tag group_icon(group), class: "avatar s40 hidden-xs"
+ = group_icon(group, class: "avatar s40 hidden-xs")
.title
= link_to [:admin, group], class: 'group-name' do
= group.full_name
diff --git a/app/views/admin/groups/show.html.haml b/app/views/admin/groups/show.html.haml
index 3e02f7b1e16..2545cecc721 100644
--- a/app/views/admin/groups/show.html.haml
+++ b/app/views/admin/groups/show.html.haml
@@ -16,7 +16,7 @@
%ul.well-list
%li
.avatar-container.s60
- = image_tag group_icon(@group), class: "avatar s60"
+ = group_icon(@group, class: "avatar s60")
%li
%span.light Name:
%strong= @group.name
diff --git a/app/views/groups/_home_panel.html.haml b/app/views/groups/_home_panel.html.haml
index 181c7bee702..a0760c2073b 100644
--- a/app/views/groups/_home_panel.html.haml
+++ b/app/views/groups/_home_panel.html.haml
@@ -1,7 +1,7 @@
.group-home-panel.text-center
%div{ class: container_class }
.avatar-container.s70.group-avatar
- = image_tag group_icon(@group), class: "avatar s70 avatar-tile"
+ = group_icon(@group, class: "avatar s70 avatar-tile")
%h1.group-title
= @group.name
%span.visibility-icon.has-tooltip{ data: { container: 'body' }, title: visibility_icon_description(@group) }
diff --git a/app/views/groups/edit.html.haml b/app/views/groups/edit.html.haml
index 15606dd30fd..16038ef2f79 100644
--- a/app/views/groups/edit.html.haml
+++ b/app/views/groups/edit.html.haml
@@ -10,7 +10,7 @@
.form-group
.col-sm-offset-2.col-sm-10
.avatar-container.s160
- = image_tag group_icon(@group), alt: '', class: 'avatar group-avatar s160'
+ = group_icon(@group, alt: '', class: 'avatar group-avatar s160')
%p.light
- if @group.avatar?
You can change your group avatar here
diff --git a/app/views/layouts/nav/sidebar/_group.html.haml b/app/views/layouts/nav/sidebar/_group.html.haml
index 8cba495f7e4..0bf318b0b66 100644
--- a/app/views/layouts/nav/sidebar/_group.html.haml
+++ b/app/views/layouts/nav/sidebar/_group.html.haml
@@ -6,7 +6,7 @@
.context-header
= link_to group_path(@group), title: @group.name do
.avatar-container.s40.group-avatar
- = image_tag group_icon(@group), class: "avatar s40 avatar-tile"
+ = group_icon(@group, class: "avatar s40 avatar-tile")
.sidebar-context-title
= @group.name
%ul.sidebar-top-level-items
diff --git a/app/views/projects/jobs/_sidebar.html.haml b/app/views/projects/jobs/_sidebar.html.haml
index 43e23bb2200..d5c6d329ce4 100644
--- a/app/views/projects/jobs/_sidebar.html.haml
+++ b/app/views/projects/jobs/_sidebar.html.haml
@@ -48,7 +48,7 @@
- if @build.trigger_variables.any?
%p
- %button.btn.group.btn-group-justified.reveal-variables Reveal Variables
+ %button.btn.group.btn-group-justified.js-reveal-variables Reveal Variables
%dl.js-build-variables.trigger-build-variables.hide
- @build.trigger_variables.each do |trigger_variable|
diff --git a/app/views/shared/groups/_group.html.haml b/app/views/shared/groups/_group.html.haml
index b361ec86ced..63f62eb476e 100644
--- a/app/views/shared/groups/_group.html.haml
+++ b/app/views/shared/groups/_group.html.haml
@@ -28,7 +28,7 @@
.avatar-container.s40
= link_to group do
- = image_tag group_icon(group), class: "avatar s40 hidden-xs"
+ = group_icon(group, class: "avatar s40 hidden-xs")
.title
= link_to group_name, group, class: 'group-name'
diff --git a/app/views/shared/members/_group.html.haml b/app/views/shared/members/_group.html.haml
index bcdad3c153a..5868c52566d 100644
--- a/app/views/shared/members/_group.html.haml
+++ b/app/views/shared/members/_group.html.haml
@@ -4,7 +4,7 @@
- dom_id = "group_member_#{group_link.id}"
%li.member.group_member{ id: dom_id }
%span.list-item-name
- = image_tag group_icon(group), class: "avatar s40", alt: ''
+ = group_icon(group, class: "avatar s40", alt: '')
%strong
= link_to group.full_name, group_path(group)
.cgray
diff --git a/app/views/users/_groups.html.haml b/app/views/users/_groups.html.haml
index eff6c80d144..55799e10a46 100644
--- a/app/views/users/_groups.html.haml
+++ b/app/views/users/_groups.html.haml
@@ -2,4 +2,4 @@
- groups.each do |group|
= link_to group, class: 'profile-groups-avatars inline', title: group.name do
.avatar-container.s40
- = image_tag group_icon(group), class: 'avatar group-avatar s40'
+ = group_icon(group, class: 'avatar group-avatar s40')
diff --git a/changelogs/unreleased/an-use-branch-exists-over-branch-names-include.yml b/changelogs/unreleased/an-use-branch-exists-over-branch-names-include.yml
new file mode 100644
index 00000000000..19d950b48d6
--- /dev/null
+++ b/changelogs/unreleased/an-use-branch-exists-over-branch-names-include.yml
@@ -0,0 +1,5 @@
+---
+title: Avoid fetching all branches for branch existence checks
+merge_request: 14778
+author:
+type: changed
diff --git a/db/migrate/20160518200441_add_artifacts_expire_date_to_ci_builds.rb b/db/migrate/20160518200441_add_artifacts_expire_date_to_ci_builds.rb
index 915167b038d..8e9ab3f8acc 100644
--- a/db/migrate/20160518200441_add_artifacts_expire_date_to_ci_builds.rb
+++ b/db/migrate/20160518200441_add_artifacts_expire_date_to_ci_builds.rb
@@ -1,3 +1,4 @@
+# rubocop:disable Migration/Datetime
class AddArtifactsExpireDateToCiBuilds < ActiveRecord::Migration
def change
add_column :ci_builds, :artifacts_expire_at, :timestamp
diff --git a/db/migrate/20160716115711_add_queued_at_to_ci_builds.rb b/db/migrate/20160716115711_add_queued_at_to_ci_builds.rb
index 756910a1fa0..fd7a48d881e 100644
--- a/db/migrate/20160716115711_add_queued_at_to_ci_builds.rb
+++ b/db/migrate/20160716115711_add_queued_at_to_ci_builds.rb
@@ -1,3 +1,4 @@
+# rubocop:disable Migration/Datetime
class AddQueuedAtToCiBuilds < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
diff --git a/db/migrate/20170503021915_add_last_edited_at_and_last_edited_by_id_to_issues.rb b/db/migrate/20170503021915_add_last_edited_at_and_last_edited_by_id_to_issues.rb
index 6ac10723c82..a5d1eca82bb 100644
--- a/db/migrate/20170503021915_add_last_edited_at_and_last_edited_by_id_to_issues.rb
+++ b/db/migrate/20170503021915_add_last_edited_at_and_last_edited_by_id_to_issues.rb
@@ -1,3 +1,4 @@
+# rubocop:disable Migration/Datetime
# See http://doc.gitlab.com/ce/development/migration_style_guide.html
# for more information on how to write migrations for GitLab.
diff --git a/db/migrate/20170503022548_add_last_edited_at_and_last_edited_by_id_to_merge_requests.rb b/db/migrate/20170503022548_add_last_edited_at_and_last_edited_by_id_to_merge_requests.rb
index 7a1acdcbf69..47ba6bde856 100644
--- a/db/migrate/20170503022548_add_last_edited_at_and_last_edited_by_id_to_merge_requests.rb
+++ b/db/migrate/20170503022548_add_last_edited_at_and_last_edited_by_id_to_merge_requests.rb
@@ -1,3 +1,4 @@
+# rubocop:disable Migration/Datetime
# See http://doc.gitlab.com/ce/development/migration_style_guide.html
# for more information on how to write migrations for GitLab.
diff --git a/rubocop/cop/migration/datetime.rb b/rubocop/cop/migration/datetime.rb
index 651935dd53e..9cba3c35b26 100644
--- a/rubocop/cop/migration/datetime.rb
+++ b/rubocop/cop/migration/datetime.rb
@@ -7,14 +7,18 @@ module RuboCop
class Datetime < RuboCop::Cop::Cop
include MigrationHelpers
- MSG = 'Do not use the `datetime` data type, use `datetime_with_timezone` instead'.freeze
+ MSG = 'Do not use the `%s` data type, use `datetime_with_timezone` instead'.freeze
# Check methods in table creation.
def on_def(node)
return unless in_migration?(node)
node.each_descendant(:send) do |send_node|
- add_offense(send_node, :selector) if method_name(send_node) == :datetime
+ method_name = node.children[1]
+
+ if method_name == :datetime || method_name == :timestamp
+ add_offense(send_node, :selector, format(MSG, method_name))
+ end
end
end
@@ -23,12 +27,14 @@ module RuboCop
return unless in_migration?(node)
node.each_descendant do |descendant|
- add_offense(node, :expression) if descendant.type == :sym && descendant.children.last == :datetime
- end
- end
+ next unless descendant.type == :sym
- def method_name(node)
- node.children[1]
+ last_argument = descendant.children.last
+
+ if last_argument == :datetime || last_argument == :timestamp
+ add_offense(node, :expression, format(MSG, last_argument))
+ end
+ end
end
end
end
diff --git a/spec/controllers/projects/branches_controller_spec.rb b/spec/controllers/projects/branches_controller_spec.rb
index 5e0b57e9b2e..3b3b63444c7 100644
--- a/spec/controllers/projects/branches_controller_spec.rb
+++ b/spec/controllers/projects/branches_controller_spec.rb
@@ -62,7 +62,7 @@ describe Projects::BranchesController do
let(:branch) { "feature%2Ftest" }
let(:ref) { "<script>alert('ref');</script>" }
it { is_expected.to render_template('new') }
- it { project.repository.branch_names.include?('feature/test') }
+ it { project.repository.branch_exists?('feature/test') }
end
end
diff --git a/spec/features/projects/issuable_templates_spec.rb b/spec/features/projects/issuable_templates_spec.rb
index 62b23121c5a..9f67216705d 100644
--- a/spec/features/projects/issuable_templates_spec.rb
+++ b/spec/features/projects/issuable_templates_spec.rb
@@ -118,7 +118,7 @@ feature 'issuable templates', :js do
context 'user creates a merge request from a forked project using templates' do
let(:template_content) { 'this is a test "feature-proposal" template' }
let(:fork_user) { create(:user) }
- let(:forked_project) { fork_project(project, fork_user) }
+ let(:forked_project) { fork_project(project, fork_user, repository: true) }
let(:merge_request) { create(:merge_request, :with_diffs, source_project: forked_project, target_project: project) }
background do
diff --git a/spec/features/projects/jobs_spec.rb b/spec/features/projects/jobs_spec.rb
index 71702db860c..576870ea0f3 100644
--- a/spec/features/projects/jobs_spec.rb
+++ b/spec/features/projects/jobs_spec.rb
@@ -300,13 +300,13 @@ feature 'Jobs' do
shared_examples 'expected variables behavior' do
it 'shows variable key and value after click', :js do
- expect(page).to have_css('.reveal-variables')
+ expect(page).to have_css('.js-reveal-variables')
expect(page).not_to have_css('.js-build-variable')
expect(page).not_to have_css('.js-build-value')
click_button 'Reveal Variables'
- expect(page).not_to have_css('.reveal-variables')
+ expect(page).not_to have_css('.js-reveal-variables')
expect(page).to have_selector('.js-build-variable', text: 'TRIGGER_KEY_1')
expect(page).to have_selector('.js-build-value', text: 'TRIGGER_VALUE_1')
end
diff --git a/spec/features/projects/merge_requests/user_views_open_merge_request_spec.rb b/spec/features/projects/merge_requests/user_views_open_merge_request_spec.rb
index 8970cf54457..3aac93eaf7c 100644
--- a/spec/features/projects/merge_requests/user_views_open_merge_request_spec.rb
+++ b/spec/features/projects/merge_requests/user_views_open_merge_request_spec.rb
@@ -6,7 +6,7 @@ describe 'User views an open merge request' do
end
context 'when a merge request does not have repository' do
- let(:project) { create(:project, :public) }
+ let(:project) { create(:project, :public, :repository) }
before do
visit(merge_request_path(merge_request))
diff --git a/spec/features/task_lists_spec.rb b/spec/features/task_lists_spec.rb
index 485b0b287ad..2dc3c5e3927 100644
--- a/spec/features/task_lists_spec.rb
+++ b/spec/features/task_lists_spec.rb
@@ -3,7 +3,7 @@ require 'spec_helper'
feature 'Task Lists' do
include Warden::Test::Helpers
- let(:project) { create(:project) }
+ let(:project) { create(:project, :repository) }
let(:user) { create(:user) }
let(:user2) { create(:user) }
diff --git a/spec/helpers/application_helper_spec.rb b/spec/helpers/application_helper_spec.rb
index 10bc5f2ecd2..87ae1fa5660 100644
--- a/spec/helpers/application_helper_spec.rb
+++ b/spec/helpers/application_helper_spec.rb
@@ -57,15 +57,17 @@ describe ApplicationHelper do
end
describe 'project_icon' do
+ let(:asset_host) { 'http://assets' }
+
it 'returns an url for the avatar' do
- project = create(:project, avatar: File.open(uploaded_image_temp_path))
+ project = create(:project, :public, avatar: File.open(uploaded_image_temp_path))
avatar_url = "/uploads/-/system/project/avatar/#{project.id}/banana_sample.gif"
expect(helper.project_icon(project.full_path).to_s)
.to eq "<img data-src=\"#{avatar_url}\" class=\" lazy\" src=\"#{LazyImageTagHelper.placeholder_image}\" />"
- allow(ActionController::Base).to receive(:asset_host).and_return(gitlab_host)
- avatar_url = "#{gitlab_host}/uploads/-/system/project/avatar/#{project.id}/banana_sample.gif"
+ allow(ActionController::Base).to receive(:asset_host).and_return(asset_host)
+ avatar_url = "#{asset_host}/uploads/-/system/project/avatar/#{project.id}/banana_sample.gif"
expect(helper.project_icon(project.full_path).to_s)
.to eq "<img data-src=\"#{avatar_url}\" class=\" lazy\" src=\"#{LazyImageTagHelper.placeholder_image}\" />"
diff --git a/spec/helpers/groups_helper_spec.rb b/spec/helpers/groups_helper_spec.rb
index 76e5964ccf7..97f0ed4904e 100644
--- a/spec/helpers/groups_helper_spec.rb
+++ b/spec/helpers/groups_helper_spec.rb
@@ -3,6 +3,8 @@ require 'spec_helper'
describe GroupsHelper do
include ApplicationHelper
+ let(:asset_host) { 'http://assets' }
+
describe 'group_icon' do
avatar_file_path = File.join(Rails.root, 'spec', 'fixtures', 'banana_sample.gif')
@@ -10,14 +12,53 @@ describe GroupsHelper do
group = create(:group)
group.avatar = fixture_file_upload(avatar_file_path)
group.save!
- expect(group_icon(group.path).to_s)
+
+ avatar_url = "/uploads/-/system/group/avatar/#{group.id}/banana_sample.gif"
+
+ expect(helper.group_icon(group).to_s)
+ .to eq "<img data-src=\"#{avatar_url}\" class=\" lazy\" src=\"#{LazyImageTagHelper.placeholder_image}\" />"
+
+ allow(ActionController::Base).to receive(:asset_host).and_return(asset_host)
+ avatar_url = "#{asset_host}/uploads/-/system/group/avatar/#{group.id}/banana_sample.gif"
+
+ expect(helper.group_icon(group).to_s)
+ .to eq "<img data-src=\"#{avatar_url}\" class=\" lazy\" src=\"#{LazyImageTagHelper.placeholder_image}\" />"
+ end
+ end
+
+ describe 'group_icon_url' do
+ avatar_file_path = File.join(Rails.root, 'spec', 'fixtures', 'banana_sample.gif')
+
+ it 'returns an url for the avatar' do
+ group = create(:group)
+ group.avatar = fixture_file_upload(avatar_file_path)
+ group.save!
+ expect(group_icon_url(group.path).to_s)
+ .to match("/uploads/-/system/group/avatar/#{group.id}/banana_sample.gif")
+ end
+
+ it 'returns an CDN url for the avatar' do
+ allow(ActionController::Base).to receive(:asset_host).and_return(asset_host)
+ group = create(:group)
+ group.avatar = fixture_file_upload(avatar_file_path)
+ group.save!
+ expect(group_icon_url(group.path).to_s)
+ .to match("#{asset_host}/uploads/-/system/group/avatar/#{group.id}/banana_sample.gif")
+ end
+
+ it 'returns an based url for the avatar if private' do
+ allow(ActionController::Base).to receive(:asset_host).and_return(asset_host)
+ group = create(:group, :private)
+ group.avatar = fixture_file_upload(avatar_file_path)
+ group.save!
+ expect(group_icon_url(group.path).to_s)
.to match("/uploads/-/system/group/avatar/#{group.id}/banana_sample.gif")
end
it 'gives default avatar_icon when no avatar is present' do
group = create(:group)
group.save!
- expect(group_icon(group.path)).to match_asset_path('group_avatar.png')
+ expect(group_icon_url(group.path)).to match_asset_path('group_avatar.png')
end
end
diff --git a/spec/javascripts/flash_spec.js b/spec/javascripts/flash_spec.js
new file mode 100644
index 00000000000..060ffaa339b
--- /dev/null
+++ b/spec/javascripts/flash_spec.js
@@ -0,0 +1,269 @@
+import flash, {
+ createFlashEl,
+ createAction,
+ hideFlash,
+} from '~/flash';
+
+describe('Flash', () => {
+ describe('createFlashEl', () => {
+ let el;
+
+ beforeEach(() => {
+ el = document.createElement('div');
+ });
+
+ afterEach(() => {
+ el.innerHTML = '';
+ });
+
+ it('creates flash element with type', () => {
+ el.innerHTML = createFlashEl('testing', 'alert');
+
+ expect(
+ el.querySelector('.flash-alert'),
+ ).not.toBeNull();
+ });
+
+ it('escapes text', () => {
+ el.innerHTML = createFlashEl('<script>alert("a");</script>', 'alert');
+
+ expect(
+ el.querySelector('.flash-text').textContent.trim(),
+ ).toBe('<script>alert("a");</script>');
+ });
+
+ it('adds container classes when inside content wrapper', () => {
+ el.innerHTML = createFlashEl('testing', 'alert', true);
+
+ expect(
+ el.querySelector('.flash-text').classList.contains('container-fluid'),
+ ).toBeTruthy();
+ expect(
+ el.querySelector('.flash-text').classList.contains('container-limited'),
+ ).toBeTruthy();
+ });
+ });
+
+ describe('hideFlash', () => {
+ let el;
+
+ beforeEach(() => {
+ el = document.createElement('div');
+ el.className = 'js-testing';
+ });
+
+ it('sets transition style', () => {
+ hideFlash(el);
+
+ expect(
+ el.style.transition,
+ ).toBe('opacity 0.3s');
+ });
+
+ it('sets opacity style', () => {
+ hideFlash(el);
+
+ expect(
+ el.style.opacity,
+ ).toBe('0');
+ });
+
+ it('does not set styles when fadeTransition is false', () => {
+ hideFlash(el, false);
+
+ expect(
+ el.style.opacity,
+ ).toBe('');
+ expect(
+ el.style.transition,
+ ).toBe('');
+ });
+
+ it('removes element after transitionend', () => {
+ document.body.appendChild(el);
+
+ hideFlash(el);
+ el.dispatchEvent(new Event('transitionend'));
+
+ expect(
+ document.querySelector('.js-testing'),
+ ).toBeNull();
+ });
+
+ it('calls event listener callback once', () => {
+ spyOn(el, 'remove').and.callThrough();
+ document.body.appendChild(el);
+
+ hideFlash(el);
+
+ el.dispatchEvent(new Event('transitionend'));
+ el.dispatchEvent(new Event('transitionend'));
+
+ expect(
+ el.remove.calls.count(),
+ ).toBe(1);
+ });
+ });
+
+ describe('createAction', () => {
+ let el;
+
+ beforeEach(() => {
+ el = document.createElement('div');
+ });
+
+ it('creates link with href', () => {
+ el.innerHTML = createAction({
+ href: 'testing',
+ title: 'test',
+ });
+
+ expect(
+ el.querySelector('.flash-action').href,
+ ).toContain('testing');
+ });
+
+ it('uses hash as href when no href is present', () => {
+ el.innerHTML = createAction({
+ title: 'test',
+ });
+
+ expect(
+ el.querySelector('.flash-action').href,
+ ).toContain('#');
+ });
+
+ it('adds role when no href is present', () => {
+ el.innerHTML = createAction({
+ title: 'test',
+ });
+
+ expect(
+ el.querySelector('.flash-action').getAttribute('role'),
+ ).toBe('button');
+ });
+
+ it('escapes the title text', () => {
+ el.innerHTML = createAction({
+ title: '<script>alert("a")</script>',
+ });
+
+ expect(
+ el.querySelector('.flash-action').textContent.trim(),
+ ).toBe('<script>alert("a")</script>');
+ });
+ });
+
+ describe('createFlash', () => {
+ describe('no flash-container', () => {
+ it('does not add to the DOM', () => {
+ const flashEl = flash('testing');
+
+ expect(
+ flashEl,
+ ).toBeNull();
+ expect(
+ document.querySelector('.flash-alert'),
+ ).toBeNull();
+ });
+ });
+
+ describe('with flash-container', () => {
+ beforeEach(() => {
+ document.body.innerHTML += `
+ <div class="content-wrapper js-content-wrapper">
+ <div class="flash-container"></div>
+ </div>
+ `;
+ });
+
+ afterEach(() => {
+ document.querySelector('.js-content-wrapper').remove();
+ });
+
+ it('adds flash element into container', () => {
+ flash('test');
+
+ expect(
+ document.querySelector('.flash-alert'),
+ ).not.toBeNull();
+ });
+
+ it('adds flash into specified parent', () => {
+ flash(
+ 'test',
+ 'alert',
+ document.querySelector('.content-wrapper'),
+ );
+
+ expect(
+ document.querySelector('.content-wrapper .flash-alert'),
+ ).not.toBeNull();
+ });
+
+ it('adds container classes when inside content-wrapper', () => {
+ flash('test');
+
+ expect(
+ document.querySelector('.flash-text').className,
+ ).toBe('flash-text container-fluid container-limited');
+ });
+
+ it('does not add container when outside of content-wrapper', () => {
+ document.querySelector('.content-wrapper').className = 'js-content-wrapper';
+ flash('test');
+
+ expect(
+ document.querySelector('.flash-text').className.trim(),
+ ).toBe('flash-text');
+ });
+
+ it('removes element after clicking', () => {
+ flash('test', 'alert', document, null, false);
+
+ document.querySelector('.flash-alert').click();
+
+ expect(
+ document.querySelector('.flash-alert'),
+ ).toBeNull();
+ });
+
+ describe('with actionConfig', () => {
+ it('adds action link', () => {
+ flash(
+ 'test',
+ 'alert',
+ document,
+ {
+ title: 'test',
+ },
+ );
+
+ expect(
+ document.querySelector('.flash-action'),
+ ).not.toBeNull();
+ });
+
+ it('calls actionConfig clickHandler on click', () => {
+ const actionConfig = {
+ title: 'test',
+ clickHandler: jasmine.createSpy('actionConfig'),
+ };
+
+ flash(
+ 'test',
+ 'alert',
+ document,
+ actionConfig,
+ );
+
+ document.querySelector('.flash-action').click();
+
+ expect(
+ actionConfig.clickHandler,
+ ).toHaveBeenCalled();
+ });
+ });
+ });
+ });
+});
diff --git a/spec/javascripts/integrations/integration_settings_form_spec.js b/spec/javascripts/integrations/integration_settings_form_spec.js
index 3daeb91b1e2..9033eb9ce02 100644
--- a/spec/javascripts/integrations/integration_settings_form_spec.js
+++ b/spec/javascripts/integrations/integration_settings_form_spec.js
@@ -138,9 +138,9 @@ describe('IntegrationSettingsForm', () => {
deferred.resolve({ error: true, message: errorMessage, service_response: 'some error' });
const $flashContainer = $('.flash-container');
- expect($flashContainer.find('.flash-text').text()).toEqual('Test failed. some error');
+ expect($flashContainer.find('.flash-text').text().trim()).toEqual('Test failed. some error');
expect($flashContainer.find('.flash-action')).toBeDefined();
- expect($flashContainer.find('.flash-action').text()).toEqual('Save anyway');
+ expect($flashContainer.find('.flash-action').text().trim()).toEqual('Save anyway');
});
it('should submit form if ajax request responds without any error in test', () => {
@@ -168,7 +168,7 @@ describe('IntegrationSettingsForm', () => {
expect($flashAction).toBeDefined();
spyOn(integrationSettingsForm.$form, 'submit');
- $flashAction.trigger('click');
+ $flashAction.get(0).click();
expect(integrationSettingsForm.$form.submit).toHaveBeenCalled();
});
@@ -181,7 +181,7 @@ describe('IntegrationSettingsForm', () => {
deferred.reject();
- expect($('.flash-container .flash-text').text()).toEqual(errorMessage);
+ expect($('.flash-container .flash-text').text().trim()).toEqual(errorMessage);
});
it('should always call `toggleSubmitBtnState` with `false` once request is completed', () => {
diff --git a/spec/javascripts/build_spec.js b/spec/javascripts/job_spec.js
index d5b0f23e7b7..5e67911d338 100644
--- a/spec/javascripts/build_spec.js
+++ b/spec/javascripts/job_spec.js
@@ -1,13 +1,11 @@
-/* eslint-disable no-new */
-/* global Build */
import { bytesToKiB } from '~/lib/utils/number_utils';
import '~/lib/utils/datetime_utility';
import '~/lib/utils/url_utility';
-import '~/build';
+import Job from '~/job';
import '~/breakpoints';
-describe('Build', () => {
- const BUILD_URL = `${gl.TEST_HOST}/frontend-fixtures/builds-project/-/jobs/1`;
+describe('Job', () => {
+ const JOB_URL = `${gl.TEST_HOST}/frontend-fixtures/builds-project/-/jobs/1`;
preloadFixtures('builds/build-with-artifacts.html.raw');
@@ -26,14 +24,14 @@ describe('Build', () => {
describe('setup', () => {
beforeEach(function () {
- this.build = new Build();
+ this.job = new Job();
});
it('copies build options', function () {
- expect(this.build.pageUrl).toBe(BUILD_URL);
- expect(this.build.buildStatus).toBe('success');
- expect(this.build.buildStage).toBe('test');
- expect(this.build.state).toBe('');
+ expect(this.job.pageUrl).toBe(JOB_URL);
+ expect(this.job.buildStatus).toBe('success');
+ expect(this.job.buildStage).toBe('test');
+ expect(this.job.state).toBe('');
});
it('only shows the jobs matching the current stage', () => {
@@ -87,15 +85,15 @@ describe('Build', () => {
complete: true,
});
- this.build = new Build();
+ this.job = new Job();
expect($('#build-trace .js-build-output').text()).toMatch(/Update/);
- expect(this.build.state).toBe('newstate');
+ expect(this.job.state).toBe('newstate');
jasmine.clock().tick(4001);
expect($('#build-trace .js-build-output').text()).toMatch(/UpdateMore/);
- expect(this.build.state).toBe('finalstate');
+ expect(this.job.state).toBe('finalstate');
});
it('replaces the entire build trace', () => {
@@ -122,7 +120,7 @@ describe('Build', () => {
append: false,
});
- this.build = new Build();
+ this.job = new Job();
expect($('#build-trace .js-build-output').text()).toMatch(/Update/);
@@ -148,7 +146,7 @@ describe('Build', () => {
total: 100,
});
- this.build = new Build();
+ this.job = new Job();
expect(document.querySelector('.js-truncated-info').classList).not.toContain('hidden');
});
@@ -167,7 +165,7 @@ describe('Build', () => {
total: 100,
});
- this.build = new Build();
+ this.job = new Job();
expect(
document.querySelector('.js-truncated-info-size').textContent.trim(),
@@ -193,7 +191,7 @@ describe('Build', () => {
deferred2.resolve();
- this.build = new Build();
+ this.job = new Job();
expect(
document.querySelector('.js-truncated-info-size').textContent.trim(),
@@ -227,7 +225,7 @@ describe('Build', () => {
total: 100,
});
- this.build = new Build();
+ this.job = new Job();
expect(
document.querySelector('.js-raw-link').textContent.trim(),
@@ -249,7 +247,7 @@ describe('Build', () => {
total: 100,
});
- this.build = new Build();
+ this.job = new Job();
expect(document.querySelector('.js-truncated-info').classList).toContain('hidden');
});
@@ -270,7 +268,7 @@ describe('Build', () => {
total: 100,
});
- this.build = new Build();
+ this.job = new Job();
});
it('should render trace controls', () => {
@@ -293,11 +291,12 @@ describe('Build', () => {
describe('getBuildTrace', () => {
it('should request build trace with state parameter', (done) => {
spyOn(jQuery, 'ajax').and.callThrough();
- new Build();
+ // eslint-disable-next-line no-new
+ new Job();
setTimeout(() => {
expect(jQuery.ajax).toHaveBeenCalledWith(
- { url: `${BUILD_URL}/trace.json`, data: { state: '' } },
+ { url: `${JOB_URL}/trace.json`, data: { state: '' } },
);
done();
}, 0);
diff --git a/spec/javascripts/notes_spec.js b/spec/javascripts/notes_spec.js
index 3e791a31604..65d2e8fd9fb 100644
--- a/spec/javascripts/notes_spec.js
+++ b/spec/javascripts/notes_spec.js
@@ -815,7 +815,7 @@ import '~/notes';
});
it('shows a flash message', () => {
- this.notes.addFlash('Error message', FLASH_TYPE_ALERT, this.notes.parentTimeline);
+ this.notes.addFlash('Error message', FLASH_TYPE_ALERT, this.notes.parentTimeline.get(0));
expect($('.flash-alert').is(':visible')).toBeTruthy();
});
@@ -828,7 +828,7 @@ import '~/notes';
});
it('hides visible flash message', () => {
- this.notes.addFlash('Error message 1', FLASH_TYPE_ALERT, this.notes.parentTimeline);
+ this.notes.addFlash('Error message 1', FLASH_TYPE_ALERT, this.notes.parentTimeline.get(0));
this.notes.clearFlash();
diff --git a/spec/javascripts/u2f/authenticate_spec.js b/spec/javascripts/u2f/authenticate_spec.js
index a160c86308d..29b15f3a782 100644
--- a/spec/javascripts/u2f/authenticate_spec.js
+++ b/spec/javascripts/u2f/authenticate_spec.js
@@ -1,72 +1,63 @@
-/* eslint-disable space-before-function-paren, new-parens, quotes, comma-dangle, no-var, one-var, one-var-declaration-per-line, max-len */
-/* global MockU2FDevice */
-/* global U2FAuthenticate */
-
-import '~/u2f/authenticate';
-import '~/u2f/util';
-import '~/u2f/error';
+import U2FAuthenticate from '~/u2f/authenticate';
import 'vendor/u2f';
-import './mock_u2f_device';
+import MockU2FDevice from './mock_u2f_device';
+
+describe('U2FAuthenticate', () => {
+ preloadFixtures('u2f/authenticate.html.raw');
-(function() {
- describe('U2FAuthenticate', function() {
- preloadFixtures('u2f/authenticate.html.raw');
+ beforeEach(() => {
+ loadFixtures('u2f/authenticate.html.raw');
+ this.u2fDevice = new MockU2FDevice();
+ this.container = $('#js-authenticate-u2f');
+ this.component = new U2FAuthenticate(
+ this.container,
+ '#js-login-u2f-form',
+ {
+ sign_requests: [],
+ },
+ document.querySelector('#js-login-2fa-device'),
+ document.querySelector('.js-2fa-form'),
+ );
- beforeEach(function() {
- loadFixtures('u2f/authenticate.html.raw');
- this.u2fDevice = new MockU2FDevice;
- this.container = $("#js-authenticate-u2f");
- this.component = new window.gl.U2FAuthenticate(
- this.container,
- '#js-login-u2f-form',
- {
- sign_requests: []
- },
- document.querySelector('#js-login-2fa-device'),
- document.querySelector('.js-2fa-form')
- );
+ // bypass automatic form submission within renderAuthenticated
+ spyOn(this.component, 'renderAuthenticated').and.returnValue(true);
- // bypass automatic form submission within renderAuthenticated
- spyOn(this.component, 'renderAuthenticated').and.returnValue(true);
+ return this.component.start();
+ });
- return this.component.start();
+ it('allows authenticating via a U2F device', () => {
+ const inProgressMessage = this.container.find('p');
+ expect(inProgressMessage.text()).toContain('Trying to communicate with your device');
+ this.u2fDevice.respondToAuthenticateRequest({
+ deviceData: 'this is data from the device',
});
- it('allows authenticating via a U2F device', function() {
- var inProgressMessage;
- inProgressMessage = this.container.find("p");
- expect(inProgressMessage.text()).toContain("Trying to communicate with your device");
+ expect(this.component.renderAuthenticated).toHaveBeenCalledWith('{"deviceData":"this is data from the device"}');
+ });
+
+ return describe('errors', () => {
+ it('displays an error message', () => {
+ const setupButton = this.container.find('#js-login-u2f-device');
+ setupButton.trigger('click');
this.u2fDevice.respondToAuthenticateRequest({
- deviceData: "this is data from the device"
+ errorCode: 'error!',
});
- expect(this.component.renderAuthenticated).toHaveBeenCalledWith('{"deviceData":"this is data from the device"}');
+ const errorMessage = this.container.find('p');
+ return expect(errorMessage.text()).toContain('There was a problem communicating with your device');
});
- return describe("errors", function() {
- it("displays an error message", function() {
- var errorMessage, setupButton;
- setupButton = this.container.find("#js-login-u2f-device");
- setupButton.trigger('click');
- this.u2fDevice.respondToAuthenticateRequest({
- errorCode: "error!"
- });
- errorMessage = this.container.find("p");
- return expect(errorMessage.text()).toContain("There was a problem communicating with your device");
+ return it('allows retrying authentication after an error', () => {
+ let setupButton = this.container.find('#js-login-u2f-device');
+ setupButton.trigger('click');
+ this.u2fDevice.respondToAuthenticateRequest({
+ errorCode: 'error!',
});
- return it("allows retrying authentication after an error", function() {
- var retryButton, setupButton;
- setupButton = this.container.find("#js-login-u2f-device");
- setupButton.trigger('click');
- this.u2fDevice.respondToAuthenticateRequest({
- errorCode: "error!"
- });
- retryButton = this.container.find("#js-u2f-try-again");
- retryButton.trigger('click');
- setupButton = this.container.find("#js-login-u2f-device");
- setupButton.trigger('click');
- this.u2fDevice.respondToAuthenticateRequest({
- deviceData: "this is data from the device"
- });
- expect(this.component.renderAuthenticated).toHaveBeenCalledWith('{"deviceData":"this is data from the device"}');
+ const retryButton = this.container.find('#js-u2f-try-again');
+ retryButton.trigger('click');
+ setupButton = this.container.find('#js-login-u2f-device');
+ setupButton.trigger('click');
+ this.u2fDevice.respondToAuthenticateRequest({
+ deviceData: 'this is data from the device',
});
+ expect(this.component.renderAuthenticated).toHaveBeenCalledWith('{"deviceData":"this is data from the device"}');
});
});
-}).call(window);
+});
diff --git a/spec/javascripts/u2f/mock_u2f_device.js b/spec/javascripts/u2f/mock_u2f_device.js
index 4eb8ad3d9e4..5a1ace2b4d6 100644
--- a/spec/javascripts/u2f/mock_u2f_device.js
+++ b/spec/javascripts/u2f/mock_u2f_device.js
@@ -1,31 +1,28 @@
-/* eslint-disable space-before-function-paren, no-var, prefer-rest-params, wrap-iife, no-unused-expressions, no-return-assign, no-param-reassign, max-len */
+/* eslint-disable prefer-rest-params, wrap-iife,
+no-unused-expressions, no-return-assign, no-param-reassign*/
-(function() {
- this.MockU2FDevice = (function() {
- function MockU2FDevice() {
- this.respondToAuthenticateRequest = this.respondToAuthenticateRequest.bind(this);
- this.respondToRegisterRequest = this.respondToRegisterRequest.bind(this);
- window.u2f || (window.u2f = {});
- window.u2f.register = (function(_this) {
- return function(appId, registerRequests, signRequests, callback) {
- return _this.registerCallback = callback;
- };
- })(this);
- window.u2f.sign = (function(_this) {
- return function(appId, challenges, signRequests, callback) {
- return _this.authenticateCallback = callback;
- };
- })(this);
- }
+export default class MockU2FDevice {
+ constructor() {
+ this.respondToAuthenticateRequest = this.respondToAuthenticateRequest.bind(this);
+ this.respondToRegisterRequest = this.respondToRegisterRequest.bind(this);
+ window.u2f || (window.u2f = {});
+ window.u2f.register = (function (_this) {
+ return function (appId, registerRequests, signRequests, callback) {
+ return _this.registerCallback = callback;
+ };
+ })(this);
+ window.u2f.sign = (function (_this) {
+ return function (appId, challenges, signRequests, callback) {
+ return _this.authenticateCallback = callback;
+ };
+ })(this);
+ }
- MockU2FDevice.prototype.respondToRegisterRequest = function(params) {
- return this.registerCallback(params);
- };
+ respondToRegisterRequest(params) {
+ return this.registerCallback(params);
+ }
- MockU2FDevice.prototype.respondToAuthenticateRequest = function(params) {
- return this.authenticateCallback(params);
- };
-
- return MockU2FDevice;
- })();
-}).call(window);
+ respondToAuthenticateRequest(params) {
+ return this.authenticateCallback(params);
+ }
+}
diff --git a/spec/javascripts/u2f/register_spec.js b/spec/javascripts/u2f/register_spec.js
index a445c80f2af..b0051f11362 100644
--- a/spec/javascripts/u2f/register_spec.js
+++ b/spec/javascripts/u2f/register_spec.js
@@ -1,77 +1,69 @@
-/* eslint-disable space-before-function-paren, new-parens, quotes, no-var, one-var, one-var-declaration-per-line, comma-dangle, max-len */
-/* global MockU2FDevice */
-/* global U2FRegister */
-
-import '~/u2f/register';
-import '~/u2f/util';
-import '~/u2f/error';
+import U2FRegister from '~/u2f/register';
import 'vendor/u2f';
-import './mock_u2f_device';
+import MockU2FDevice from './mock_u2f_device';
+
+describe('U2FRegister', () => {
+ preloadFixtures('u2f/register.html.raw');
-(function() {
- describe('U2FRegister', function() {
- preloadFixtures('u2f/register.html.raw');
+ beforeEach(() => {
+ loadFixtures('u2f/register.html.raw');
+ this.u2fDevice = new MockU2FDevice();
+ this.container = $('#js-register-u2f');
+ this.component = new U2FRegister(this.container, $('#js-register-u2f-templates'), {}, 'token');
+ return this.component.start();
+ });
- beforeEach(function() {
- loadFixtures('u2f/register.html.raw');
- this.u2fDevice = new MockU2FDevice;
- this.container = $("#js-register-u2f");
- this.component = new U2FRegister(this.container, $("#js-register-u2f-templates"), {}, "token");
- return this.component.start();
+ it('allows registering a U2F device', () => {
+ const setupButton = this.container.find('#js-setup-u2f-device');
+ expect(setupButton.text()).toBe('Setup new U2F device');
+ setupButton.trigger('click');
+ const inProgressMessage = this.container.children('p');
+ expect(inProgressMessage.text()).toContain('Trying to communicate with your device');
+ this.u2fDevice.respondToRegisterRequest({
+ deviceData: 'this is data from the device',
});
- it('allows registering a U2F device', function() {
- var deviceResponse, inProgressMessage, registeredMessage, setupButton;
- setupButton = this.container.find("#js-setup-u2f-device");
- expect(setupButton.text()).toBe('Setup new U2F device');
+ const registeredMessage = this.container.find('p');
+ const deviceResponse = this.container.find('#js-device-response');
+ expect(registeredMessage.text()).toContain('Your device was successfully set up!');
+ return expect(deviceResponse.val()).toBe('{"deviceData":"this is data from the device"}');
+ });
+
+ return describe('errors', () => {
+ it('doesn\'t allow the same device to be registered twice (for the same user', () => {
+ const setupButton = this.container.find('#js-setup-u2f-device');
setupButton.trigger('click');
- inProgressMessage = this.container.children("p");
- expect(inProgressMessage.text()).toContain("Trying to communicate with your device");
this.u2fDevice.respondToRegisterRequest({
- deviceData: "this is data from the device"
+ errorCode: 4,
});
- registeredMessage = this.container.find('p');
- deviceResponse = this.container.find('#js-device-response');
- expect(registeredMessage.text()).toContain("Your device was successfully set up!");
- return expect(deviceResponse.val()).toBe('{"deviceData":"this is data from the device"}');
+ const errorMessage = this.container.find('p');
+ return expect(errorMessage.text()).toContain('already been registered with us');
});
- return describe("errors", function() {
- it("doesn't allow the same device to be registered twice (for the same user", function() {
- var errorMessage, setupButton;
- setupButton = this.container.find("#js-setup-u2f-device");
- setupButton.trigger('click');
- this.u2fDevice.respondToRegisterRequest({
- errorCode: 4
- });
- errorMessage = this.container.find("p");
- return expect(errorMessage.text()).toContain("already been registered with us");
+
+ it('displays an error message for other errors', () => {
+ const setupButton = this.container.find('#js-setup-u2f-device');
+ setupButton.trigger('click');
+ this.u2fDevice.respondToRegisterRequest({
+ errorCode: 'error!',
});
- it("displays an error message for other errors", function() {
- var errorMessage, setupButton;
- setupButton = this.container.find("#js-setup-u2f-device");
- setupButton.trigger('click');
- this.u2fDevice.respondToRegisterRequest({
- errorCode: "error!"
- });
- errorMessage = this.container.find("p");
- return expect(errorMessage.text()).toContain("There was a problem communicating with your device");
+ const errorMessage = this.container.find('p');
+ return expect(errorMessage.text()).toContain('There was a problem communicating with your device');
+ });
+
+ return it('allows retrying registration after an error', () => {
+ let setupButton = this.container.find('#js-setup-u2f-device');
+ setupButton.trigger('click');
+ this.u2fDevice.respondToRegisterRequest({
+ errorCode: 'error!',
});
- return it("allows retrying registration after an error", function() {
- var registeredMessage, retryButton, setupButton;
- setupButton = this.container.find("#js-setup-u2f-device");
- setupButton.trigger('click');
- this.u2fDevice.respondToRegisterRequest({
- errorCode: "error!"
- });
- retryButton = this.container.find("#U2FTryAgain");
- retryButton.trigger('click');
- setupButton = this.container.find("#js-setup-u2f-device");
- setupButton.trigger('click');
- this.u2fDevice.respondToRegisterRequest({
- deviceData: "this is data from the device"
- });
- registeredMessage = this.container.find("p");
- return expect(registeredMessage.text()).toContain("Your device was successfully set up!");
+ const retryButton = this.container.find('#U2FTryAgain');
+ retryButton.trigger('click');
+ setupButton = this.container.find('#js-setup-u2f-device');
+ setupButton.trigger('click');
+ this.u2fDevice.respondToRegisterRequest({
+ deviceData: 'this is data from the device',
});
+ const registeredMessage = this.container.find('p');
+ return expect(registeredMessage.text()).toContain('Your device was successfully set up!');
});
});
-}).call(window);
+});
diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb
index a26c71e5155..cf26dbfea49 100644
--- a/spec/models/project_spec.rb
+++ b/spec/models/project_spec.rb
@@ -874,7 +874,7 @@ describe Project do
let(:project) { create(:project) }
context 'when avatar file is uploaded' do
- let(:project) { create(:project, :with_avatar) }
+ let(:project) { create(:project, :public, :with_avatar) }
let(:avatar_path) { "/uploads/-/system/project/avatar/#{project.id}/dk.png" }
let(:gitlab_host) { "http://#{Gitlab.config.gitlab.host}" }
diff --git a/spec/rubocop/cop/migration/datetime_spec.rb b/spec/rubocop/cop/migration/datetime_spec.rb
index 388b086ce6a..b1dfcf1b048 100644
--- a/spec/rubocop/cop/migration/datetime_spec.rb
+++ b/spec/rubocop/cop/migration/datetime_spec.rb
@@ -9,6 +9,7 @@ describe RuboCop::Cop::Migration::Datetime do
include CopHelper
subject(:cop) { described_class.new }
+
let(:migration_with_datetime) do
%q(
class Users < ActiveRecord::Migration
@@ -22,6 +23,19 @@ describe RuboCop::Cop::Migration::Datetime do
)
end
+ let(:migration_with_timestamp) do
+ %q(
+ class Users < ActiveRecord::Migration
+ DOWNTIME = false
+
+ def change
+ add_column(:users, :username, :text)
+ add_column(:users, :last_sign_in, :timestamp)
+ end
+ end
+ )
+ end
+
let(:migration_without_datetime) do
%q(
class Users < ActiveRecord::Migration
@@ -58,6 +72,17 @@ describe RuboCop::Cop::Migration::Datetime do
aggregate_failures do
expect(cop.offenses.size).to eq(1)
expect(cop.offenses.map(&:line)).to eq([7])
+ expect(cop.offenses.first.message).to include('datetime')
+ end
+ end
+
+ it 'registers an offense when the ":timestamp" data type is used' do
+ inspect_source(cop, migration_with_timestamp)
+
+ aggregate_failures do
+ expect(cop.offenses.size).to eq(1)
+ expect(cop.offenses.map(&:line)).to eq([7])
+ expect(cop.offenses.first.message).to include('timestamp')
end
end
@@ -81,6 +106,7 @@ describe RuboCop::Cop::Migration::Datetime do
context 'outside of migration' do
it 'registers no offense' do
inspect_source(cop, migration_with_datetime)
+ inspect_source(cop, migration_with_timestamp)
inspect_source(cop, migration_without_datetime)
inspect_source(cop, migration_with_datetime_with_timezone)
diff --git a/spec/serializers/merge_request_entity_spec.rb b/spec/serializers/merge_request_entity_spec.rb
index 4aeb593da44..87832b3dca1 100644
--- a/spec/serializers/merge_request_entity_spec.rb
+++ b/spec/serializers/merge_request_entity_spec.rb
@@ -1,7 +1,7 @@
require 'spec_helper'
describe MergeRequestEntity do
- let(:project) { create :project }
+ let(:project) { create :project, :repository }
let(:resource) { create(:merge_request, source_project: project, target_project: project) }
let(:user) { create(:user) }
diff --git a/spec/services/merge_requests/update_service_spec.rb b/spec/services/merge_requests/update_service_spec.rb
index 7257c359a7e..98409be4236 100644
--- a/spec/services/merge_requests/update_service_spec.rb
+++ b/spec/services/merge_requests/update_service_spec.rb
@@ -127,10 +127,10 @@ describe MergeRequests::UpdateService, :mailer do
end
it 'creates system note about discussion lock' do
- note = find_note('locked this issue')
+ note = find_note('locked this merge request')
expect(note).not_to be_nil
- expect(note.note).to eq 'locked this issue'
+ expect(note.note).to eq 'locked this merge request'
end
context 'when not including source branch removal options' do
diff --git a/spec/services/system_note_service_spec.rb b/spec/services/system_note_service_spec.rb
index b1241cd8d0b..cd473c1f388 100644
--- a/spec/services/system_note_service_spec.rb
+++ b/spec/services/system_note_service_spec.rb
@@ -1145,4 +1145,42 @@ describe SystemNoteService do
it { expect(subject.note).to eq "marked #{duplicate_issue.to_reference(project)} as a duplicate of this issue" }
end
end
+
+ describe '.discussion_lock' do
+ subject { described_class.discussion_lock(noteable, author) }
+
+ context 'discussion unlocked' do
+ it_behaves_like 'a system note' do
+ let(:action) { 'unlocked' }
+ end
+
+ it 'creates the note text correctly' do
+ [:issue, :merge_request].each do |type|
+ issuable = create(type)
+
+ expect(described_class.discussion_lock(issuable, author).note)
+ .to eq("unlocked this #{type.to_s.titleize.downcase}")
+ end
+ end
+ end
+
+ context 'discussion locked' do
+ before do
+ noteable.update_attribute(:discussion_locked, true)
+ end
+
+ it_behaves_like 'a system note' do
+ let(:action) { 'locked' }
+ end
+
+ it 'creates the note text correctly' do
+ [:issue, :merge_request].each do |type|
+ issuable = create(type, discussion_locked: true)
+
+ expect(described_class.discussion_lock(issuable, author).note)
+ .to eq("locked this #{type.to_s.titleize.downcase}")
+ end
+ end
+ end
+ end
end