summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorClement Ho <clemmakesapps@gmail.com>2018-11-01 15:20:54 +0000
committerClement Ho <clemmakesapps@gmail.com>2018-11-01 15:20:54 +0000
commit3c99d8308a481b3f6edb002912ffc4d760b7fb64 (patch)
tree73555ed44c63103ae57836dd00587e45d084dff2
parent0336dc438b6faab575df996364076b57e388cc27 (diff)
parentd3a407d66fa553f3ff8afd1b9e42f01b9912e14a (diff)
downloadgitlab-ce-gl-ui-progress-bar.tar.gz
Merge branch 'master' into 'gl-ui-progress-bar'gl-ui-progress-bar
# Conflicts: # app/assets/javascripts/commons/gitlab_ui.js
-rw-r--r--.gitlab-ci.yml8
-rw-r--r--Dockerfile.assets4
-rw-r--r--app/assets/javascripts/blob_edit/blob_bundle.js2
-rw-r--r--app/assets/javascripts/boards/components/board.js27
-rw-r--r--app/assets/javascripts/boards/components/board_blank_state.vue15
-rw-r--r--app/assets/javascripts/boards/components/board_card.vue128
-rw-r--r--app/assets/javascripts/boards/components/board_new_issue.vue3
-rw-r--r--app/assets/javascripts/boards/components/board_sidebar.js41
-rw-r--r--app/assets/javascripts/boards/components/issue_card_inner.vue256
-rw-r--r--app/assets/javascripts/boards/components/modal/empty_state.vue4
-rw-r--r--app/assets/javascripts/boards/components/modal/footer.vue16
-rw-r--r--app/assets/javascripts/boards/components/modal/header.vue80
-rw-r--r--app/assets/javascripts/boards/components/modal/index.vue229
-rw-r--r--app/assets/javascripts/boards/components/modal/list.vue198
-rw-r--r--app/assets/javascripts/boards/components/modal/tabs.vue30
-rw-r--r--app/assets/javascripts/boards/components/new_list_dropdown.js43
-rw-r--r--app/assets/javascripts/boards/components/project_select.vue6
-rw-r--r--app/assets/javascripts/boards/components/sidebar/remove_issue.vue122
-rw-r--r--app/assets/javascripts/boards/filtered_search_boards.js9
-rw-r--r--app/assets/javascripts/boards/index.js6
-rw-r--r--app/assets/javascripts/boards/mixins/sortable_default_options.js10
-rw-r--r--app/assets/javascripts/boards/models/issue.js30
-rw-r--r--app/assets/javascripts/boards/models/list.js4
-rw-r--r--app/assets/javascripts/boards/services/board_service.js12
-rw-r--r--app/assets/javascripts/boards/stores/boards_store.js57
-rw-r--r--app/assets/javascripts/boards/stores/modal_store.js12
-rw-r--r--app/assets/javascripts/clusters/components/applications.vue2
-rw-r--r--app/assets/javascripts/commons/gitlab_ui.js5
-rw-r--r--app/assets/javascripts/diffs/components/tree_list.vue4
-rw-r--r--app/assets/javascripts/environments/components/environment_item.vue4
-rw-r--r--app/assets/javascripts/environments/components/environments_app.vue152
-rw-r--r--app/assets/javascripts/environments/stores/environments_store.js30
-rw-r--r--app/assets/javascripts/filtered_search/dropdown_user.js7
-rw-r--r--app/assets/javascripts/filtered_search/filtered_search_dropdown.js2
-rw-r--r--app/assets/javascripts/filtered_search/filtered_search_dropdown_manager.js27
-rw-r--r--app/assets/javascripts/filtered_search/filtered_search_manager.js113
-rw-r--r--app/assets/javascripts/filtered_search/issuable_filtered_search_token_keys.js14
-rw-r--r--app/assets/javascripts/flash.js8
-rw-r--r--app/assets/javascripts/gfm_auto_complete.js63
-rw-r--r--app/assets/javascripts/gl_field_errors.js2
-rw-r--r--app/assets/javascripts/jobs/components/job_app.vue290
-rw-r--r--app/assets/javascripts/jobs/components/job_log.vue74
-rw-r--r--app/assets/javascripts/jobs/index.js1
-rw-r--r--app/assets/javascripts/jobs/store/getters.js9
-rw-r--r--app/assets/javascripts/labels_select.js246
-rw-r--r--app/assets/javascripts/lib/utils/ace_utils.js4
-rw-r--r--app/assets/javascripts/lib/utils/number_utils.js2
-rw-r--r--app/assets/javascripts/members.js10
-rw-r--r--app/assets/javascripts/milestone_select.js5
-rw-r--r--app/assets/javascripts/notes/components/note_awards_list.vue2
-rw-r--r--app/assets/javascripts/notes/components/notes_app.vue8
-rw-r--r--app/assets/javascripts/notes/discussion_filters.js8
-rw-r--r--app/assets/javascripts/notes/stores/collapse_utils.js2
-rw-r--r--app/assets/javascripts/pages/milestones/shared/components/promote_milestone_modal.vue99
-rw-r--r--app/assets/javascripts/pages/projects/project.js4
-rw-r--r--app/assets/javascripts/pages/projects/shared/permissions/components/settings_panel.vue348
-rw-r--r--app/assets/javascripts/projects/project_new.js44
-rw-r--r--app/assets/javascripts/right_sidebar.js57
-rw-r--r--app/assets/javascripts/search_autocomplete.js4
-rw-r--r--app/assets/javascripts/sidebar/components/assignees/assignees.vue5
-rw-r--r--app/assets/javascripts/sidebar/components/subscriptions/subscriptions.vue124
-rw-r--r--app/assets/javascripts/sidebar/sidebar_mediator.js20
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/mr_widget_pipeline.vue13
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/states/ready_to_merge.vue32
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/mr_widget_options.vue12
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/services/mr_widget_service.js4
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/stores/mr_widget_store.js3
-rw-r--r--app/assets/javascripts/vue_shared/components/commit.vue2
-rw-r--r--app/assets/javascripts/vue_shared/components/filtered_search_dropdown.vue6
-rw-r--r--app/assets/javascripts/vue_shared/components/icon.vue2
-rw-r--r--app/assets/javascripts/vue_shared/components/sidebar/collapsed_calendar_icon.vue62
-rw-r--r--app/assets/javascripts/vue_shared/components/sidebar/collapsed_grouped_date_picker.vue141
-rw-r--r--app/assets/javascripts/vue_shared/components/sidebar/labels_select/dropdown_value_collapsed.vue5
-rw-r--r--app/assets/stylesheets/framework/dropdowns.scss4
-rw-r--r--app/assets/stylesheets/framework/secondary_navigation_elements.scss4
-rw-r--r--app/controllers/admin/appearances_controller.rb6
-rw-r--r--app/controllers/import/gitea_controller.rb2
-rw-r--r--app/controllers/import/github_controller.rb2
-rw-r--r--app/controllers/oauth/authorizations_controller.rb2
-rw-r--r--app/controllers/projects_controller.rb2
-rw-r--r--app/finders/autocomplete/users_finder.rb2
-rw-r--r--app/finders/concerns/finder_with_cross_project_access.rb2
-rw-r--r--app/finders/group_descendants_finder.rb2
-rw-r--r--app/finders/groups_finder.rb4
-rw-r--r--app/finders/issuable_finder.rb73
-rw-r--r--app/finders/issues_finder.rb2
-rw-r--r--app/finders/labels_finder.rb2
-rw-r--r--app/finders/milestones_finder.rb2
-rw-r--r--app/finders/pipelines_finder.rb2
-rw-r--r--app/finders/projects_finder.rb2
-rw-r--r--app/finders/snippets_finder.rb2
-rw-r--r--app/helpers/icons_helper.rb2
-rw-r--r--app/helpers/page_layout_helper.rb2
-rw-r--r--app/models/commit_status.rb4
-rw-r--r--app/models/concerns/awardable.rb18
-rw-r--r--app/models/concerns/cacheable_attributes.rb4
-rw-r--r--app/models/concerns/fast_destroy_all.rb2
-rw-r--r--app/models/concerns/issuable.rb2
-rw-r--r--app/models/concerns/noteable.rb2
-rw-r--r--app/models/concerns/storage/legacy_namespace.rb2
-rw-r--r--app/models/concerns/with_uploads.rb2
-rw-r--r--app/models/diff_note.rb6
-rw-r--r--app/models/discussion_note.rb6
-rw-r--r--app/models/namespace.rb2
-rw-r--r--app/models/project.rb4
-rw-r--r--app/models/project_services/issue_tracker_service.rb2
-rw-r--r--app/presenters/project_presenter.rb2
-rw-r--r--app/serializers/job_entity.rb16
-rw-r--r--app/services/auth/container_registry_authentication_service.rb2
-rw-r--r--app/services/keys/destroy_service.rb2
-rw-r--r--app/services/members/base_service.rb2
-rw-r--r--app/services/projects/move_project_authorizations_service.rb2
-rw-r--r--app/services/projects/move_project_group_links_service.rb2
-rw-r--r--app/services/projects/move_project_members_service.rb2
-rw-r--r--app/services/quick_actions/interpret_service.rb4
-rw-r--r--app/services/search/group_service.rb2
-rw-r--r--app/views/projects/edit.html.haml2
-rw-r--r--app/views/projects/empty.html.haml2
-rw-r--r--app/views/projects/show.html.haml2
-rw-r--r--app/views/shared/issuable/_search_bar.html.haml8
-rw-r--r--app/workers/gitlab/github_import/advance_stage_worker.rb2
-rw-r--r--changelogs/unreleased/44012-filter-reactions-none-any.yml5
-rw-r--r--changelogs/unreleased/52382-filter-milestone-api-none-any.yml5
-rw-r--r--changelogs/unreleased/drop-gcp-cluster-table.yml5
-rw-r--r--changelogs/unreleased/gt-fix-quick-links-button-styles.yml5
-rw-r--r--changelogs/unreleased/related_mrs.yml5
-rw-r--r--changelogs/unreleased/sh-optimize-merge-request-project-lookup.yml5
-rw-r--r--config/application.rb2
-rw-r--r--config/dependency_decisions.yml2
-rw-r--r--config/gitlab.yml.example2
-rw-r--r--db/migrate/20181031190559_drop_gcp_clusters_table.rb53
-rw-r--r--db/post_migrate/20170717111152_cleanup_move_system_upload_folder_symlink.rb2
-rw-r--r--db/schema.rb34
-rw-r--r--doc/api/issues.md99
-rw-r--r--doc/api/merge_requests.md34
-rw-r--r--doc/ci/examples/artifactory_and_gitlab/index.md4
-rw-r--r--doc/development/api_graphql_styleguide.md2
-rw-r--r--doc/development/fe_guide/icons.md4
-rw-r--r--doc/development/ux_guide/tips.md18
-rw-r--r--doc/install/digitaloceandocker.md102
-rw-r--r--doc/ssh/README.md243
-rw-r--r--doc/university/training/user_training.md267
-rw-r--r--doc/user/markdown.md111
-rw-r--r--lib/api/issues.rb24
-rw-r--r--lib/banzai/filter/issuable_state_filter.rb11
-rw-r--r--lib/banzai/issuable_extractor.rb41
-rw-r--r--lib/extracts_path.rb2
-rw-r--r--lib/gitlab/auth/o_auth/auth_hash.rb2
-rw-r--r--lib/gitlab/background_migration/set_confidential_note_events_on_services.rb4
-rw-r--r--lib/gitlab/background_migration/set_confidential_note_events_on_webhooks.rb4
-rw-r--r--lib/gitlab/ci/templates/Maven.gitlab-ci.yml2
-rw-r--r--lib/gitlab/database/migration_helpers.rb2
-rw-r--r--lib/gitlab/diff/position_tracer.rb2
-rw-r--r--lib/gitlab/gitaly_client/repository_service.rb2
-rw-r--r--lib/gitlab/import/merge_request_helpers.rb2
-rw-r--r--lib/gitlab/import_export/project_tree_restorer.rb2
-rw-r--r--lib/gitlab/path_regex.rb2
-rw-r--r--lib/gitlab/proxy_http_connection_adapter.rb2
-rw-r--r--lib/gitlab/slash_commands/issue_new.rb2
-rw-r--r--lib/gitlab/user_extractor.rb4
-rw-r--r--lib/google_api/auth.rb2
-rw-r--r--package.json2
-rw-r--r--rubocop/cop/code_reuse/active_record.rb1
-rwxr-xr-xscripts/build_assets_image21
-rwxr-xr-xscripts/static-analysis1
-rwxr-xr-xscripts/trigger-build12
-rw-r--r--spec/controllers/groups/milestones_controller_spec.rb4
-rw-r--r--spec/controllers/groups_controller_spec.rb2
-rw-r--r--spec/controllers/profiles/keys_controller_spec.rb2
-rw-r--r--spec/controllers/projects/environments_controller_spec.rb2
-rw-r--r--spec/controllers/projects/jobs_controller_spec.rb34
-rw-r--r--spec/controllers/projects/merge_requests_controller_spec.rb2
-rw-r--r--spec/features/dashboard/archived_projects_spec.rb2
-rw-r--r--spec/features/import/manifest_import_spec.rb2
-rw-r--r--spec/features/issues/filtered_search/dropdown_emoji_spec.rb20
-rw-r--r--spec/features/issues/gfm_autocomplete_spec.rb2
-rw-r--r--spec/features/projects/badges/pipeline_badge_spec.rb2
-rw-r--r--spec/features/projects/clusters_spec.rb2
-rw-r--r--spec/features/projects/files/user_creates_files_spec.rb4
-rw-r--r--spec/features/projects/jobs_spec.rb18
-rw-r--r--spec/finders/issues_finder_spec.rb32
-rw-r--r--spec/javascripts/awards_handler_spec.js4
-rw-r--r--spec/javascripts/boards/board_list_spec.js86
-rw-r--r--spec/javascripts/boards/components/board_spec.js37
-rw-r--r--spec/javascripts/commit/commit_pipeline_status_component_spec.js8
-rw-r--r--spec/javascripts/environments/emtpy_state_spec.js23
-rw-r--r--spec/javascripts/environments/environment_item_spec.js56
-rw-r--r--spec/javascripts/environments/environments_app_spec.js89
-rw-r--r--spec/javascripts/environments/folder/environments_folder_view_spec.js104
-rw-r--r--spec/javascripts/issue_show/components/title_spec.js18
-rw-r--r--spec/javascripts/jobs/components/job_app_spec.js4
-rw-r--r--spec/javascripts/jobs/store/actions_spec.js36
-rw-r--r--spec/javascripts/jobs/store/getters_spec.js6
-rw-r--r--spec/javascripts/lib/utils/common_utils_spec.js221
-rw-r--r--spec/javascripts/lib/utils/datetime_utility_spec.js4
-rw-r--r--spec/javascripts/lib/utils/number_utility_spec.js8
-rw-r--r--spec/javascripts/lib/utils/text_utility_spec.js2
-rw-r--r--spec/javascripts/monitoring/graph/flag_spec.js33
-rw-r--r--spec/javascripts/notes/components/discussion_filter_spec.js20
-rw-r--r--spec/javascripts/notes/components/note_app_spec.js3
-rw-r--r--spec/javascripts/notes/stores/mutation_spec.js2
-rw-r--r--spec/javascripts/pipelines/empty_state_spec.js2
-rw-r--r--spec/javascripts/pipelines/graph/action_component_spec.js2
-rw-r--r--spec/javascripts/pipelines/graph/graph_component_spec.js8
-rw-r--r--spec/javascripts/pipelines/pipelines_spec.js4
-rw-r--r--spec/javascripts/pipelines/stage_spec.js2
-rw-r--r--spec/javascripts/sidebar/assignees_spec.js84
-rw-r--r--spec/javascripts/vue_mr_widget/components/mr_widget_pipeline_spec.js72
-rw-r--r--spec/javascripts/vue_mr_widget/components/states/mr_widget_merged_spec.js2
-rw-r--r--spec/javascripts/vue_mr_widget/mock_data.js5
-rw-r--r--spec/javascripts/vue_mr_widget/mr_widget_options_spec.js59
-rw-r--r--spec/javascripts/vue_shared/components/filtered_search_dropdown_spec.js2
-rw-r--r--spec/javascripts/vue_shared/components/sidebar/collapsed_grouped_date_picker_spec.js8
-rw-r--r--spec/javascripts/vue_shared/components/sidebar/date_picker_spec.js12
-rw-r--r--spec/javascripts/vue_shared/components/sidebar/labels_select/dropdown_value_collapsed_spec.js4
-rw-r--r--spec/javascripts/vue_shared/components/user_avatar/user_avatar_link_spec.js2
-rw-r--r--spec/lib/banzai/filter/autolink_filter_spec.rb2
-rw-r--r--spec/lib/banzai/filter/relative_link_filter_spec.rb2
-rw-r--r--spec/lib/container_registry/blob_spec.rb2
-rw-r--r--spec/lib/gitlab/background_migration/create_fork_network_memberships_range_spec.rb2
-rw-r--r--spec/lib/gitlab/checks/lfs_integrity_spec.rb2
-rw-r--r--spec/lib/gitlab/ci/ansi2html_spec.rb2
-rw-r--r--spec/lib/gitlab/ci/build/policy/variables_spec.rb2
-rw-r--r--spec/lib/gitlab/ci/config/entry/global_spec.rb2
-rw-r--r--spec/lib/gitlab/cross_project_access/check_info_spec.rb4
-rw-r--r--spec/lib/gitlab/database/rename_reserved_paths_migration/v1/rename_base_spec.rb2
-rw-r--r--spec/lib/gitlab/diff/position_spec.rb8
-rw-r--r--spec/lib/gitlab/diff/position_tracer_spec.rb2
-rw-r--r--spec/lib/gitlab/git/attributes_at_ref_parser_spec.rb2
-rw-r--r--spec/lib/gitlab/gpg_spec.rb4
-rw-r--r--spec/lib/gitlab/import_export/project_tree_restorer_spec.rb2
-rw-r--r--spec/lib/gitlab/view/presenter/base_spec.rb2
-rw-r--r--spec/lib/microsoft_teams/notifier_spec.rb2
-rw-r--r--spec/migrations/migrate_old_artifacts_spec.rb2
-rw-r--r--spec/models/concerns/awardable_spec.rb18
-rw-r--r--spec/models/concerns/cacheable_attributes_spec.rb4
-rw-r--r--spec/models/concerns/issuable_spec.rb4
-rw-r--r--spec/models/lfs_object_spec.rb2
-rw-r--r--spec/models/merge_request_spec.rb14
-rw-r--r--spec/models/project_spec.rb8
-rw-r--r--spec/models/project_wiki_spec.rb4
-rw-r--r--spec/models/upload_spec.rb2
-rw-r--r--spec/presenters/project_presenter_spec.rb2
-rw-r--r--spec/requests/api/commits_spec.rb2
-rw-r--r--spec/requests/api/issues_spec.rb92
-rw-r--r--spec/services/clusters/gcp/fetch_operation_service_spec.rb2
-rw-r--r--spec/services/clusters/gcp/finalize_creation_service_spec.rb2
-rw-r--r--spec/services/clusters/gcp/provision_service_spec.rb2
-rw-r--r--spec/services/groups/transfer_service_spec.rb2
-rw-r--r--spec/services/projects/import_service_spec.rb2
-rw-r--r--spec/services/projects/transfer_service_spec.rb2
-rw-r--r--spec/services/quick_actions/interpret_service_spec.rb4
-rw-r--r--spec/support/capybara.rb2
-rw-r--r--spec/support/helpers/filtered_search_helpers.rb8
-rw-r--r--spec/support/helpers/project_forks_helper.rb2
-rw-r--r--spec/support/shared_examples/services/boards/lists_move_service.rb12
-rw-r--r--spec/tasks/gitlab/backup_rake_spec.rb2
-rw-r--r--vendor/licenses.csv2
-rw-r--r--yarn.lock10
259 files changed, 3468 insertions, 2766 deletions
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 0d70eae0d1e..0e7a67f9cc1 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -694,7 +694,10 @@ gitlab:setup-mysql:
# Frontend-related jobs
gitlab:assets:compile:
<<: *dedicated-no-docs-and-no-qa-pull-cache-job
+ image: dev.gitlab.org:5005/gitlab/gitlab-build-images:ruby-2.4.4-git-2.18-chrome-69.0-node-8.x-yarn-1.2-graphicsmagick-1.3.29-docker-18.06.1
dependencies: []
+ services:
+ - docker:stable-dind
variables:
NODE_ENV: "production"
RAILS_ENV: "production"
@@ -703,18 +706,23 @@ gitlab:assets:compile:
WEBPACK_REPORT: "true"
# we override the max_old_space_size to prevent OOM errors
NODE_OPTIONS: --max_old_space_size=3584
+ DOCKER_DRIVER: overlay2
+ DOCKER_HOST: tcp://docker:2375
script:
- date
- yarn install --frozen-lockfile --production --cache-folder .yarn-cache
- date
- free -m
- bundle exec rake gitlab:assets:compile
+ - scripts/build_assets_image
artifacts:
name: webpack-report
expire_in: 31d
paths:
- webpack-report/
- public/assets/
+ tags:
+ - docker
karma:
<<: *dedicated-no-docs-pull-cache-job
diff --git a/Dockerfile.assets b/Dockerfile.assets
new file mode 100644
index 00000000000..403d16cc4ab
--- /dev/null
+++ b/Dockerfile.assets
@@ -0,0 +1,4 @@
+# Simple container to store assets for later use
+FROM scratch
+ADD public/assets /assets/
+CMD /bin/true
diff --git a/app/assets/javascripts/blob_edit/blob_bundle.js b/app/assets/javascripts/blob_edit/blob_bundle.js
index 3cc89ff1955..ec27ae8c291 100644
--- a/app/assets/javascripts/blob_edit/blob_bundle.js
+++ b/app/assets/javascripts/blob_edit/blob_bundle.js
@@ -13,7 +13,7 @@ export default () => {
if (editBlobForm.length) {
const urlRoot = editBlobForm.data('relativeUrlRoot');
const assetsPath = editBlobForm.data('assetsPrefix');
- const filePath = editBlobForm.data('blobFilename')
+ const filePath = editBlobForm.data('blobFilename');
const currentAction = $('.js-file-title').data('currentAction');
const projectId = editBlobForm.data('project-id');
diff --git a/app/assets/javascripts/boards/components/board.js b/app/assets/javascripts/boards/components/board.js
index 623cda5679a..fb6e5291a61 100644
--- a/app/assets/javascripts/boards/components/board.js
+++ b/app/assets/javascripts/boards/components/board.js
@@ -42,7 +42,7 @@ export default Vue.extend({
required: true,
},
},
- data () {
+ data() {
return {
detailIssue: boardsStore.detail,
filter: boardsStore.filter,
@@ -55,27 +55,26 @@ export default Vue.extend({
},
isNewIssueShown() {
return this.list.type === 'backlog' || (!this.disabled && this.list.type !== 'closed');
- }
+ },
},
watch: {
filter: {
handler() {
this.list.page = 1;
- this.list.getIssues(true)
- .catch(() => {
- // TODO: handle request error
- });
+ this.list.getIssues(true).catch(() => {
+ // TODO: handle request error
+ });
},
deep: true,
- }
+ },
},
- mounted () {
+ mounted() {
this.sortableOptions = getBoardSortableDefaultOptions({
disabled: this.disabled,
group: 'boards',
draggable: '.is-draggable',
handle: '.js-board-handle',
- onEnd: (e) => {
+ onEnd: e => {
sortableEnd();
if (e.newIndex !== undefined && e.oldIndex !== e.newIndex) {
@@ -86,14 +85,15 @@ export default Vue.extend({
boardsStore.moveList(list, order);
});
}
- }
+ },
});
this.sortable = Sortable.create(this.$el.parentNode, this.sortableOptions);
},
created() {
if (this.list.isExpandable && AccessorUtilities.isLocalStorageAccessSafe()) {
- const isCollapsed = localStorage.getItem(`boards.${this.boardId}.${this.list.type}.expanded`) === 'false';
+ const isCollapsed =
+ localStorage.getItem(`boards.${this.boardId}.${this.list.type}.expanded`) === 'false';
this.list.isExpanded = !isCollapsed;
}
@@ -107,7 +107,10 @@ export default Vue.extend({
this.list.isExpanded = !this.list.isExpanded;
if (AccessorUtilities.isLocalStorageAccessSafe()) {
- localStorage.setItem(`boards.${this.boardId}.${this.list.type}.expanded`, this.list.isExpanded);
+ localStorage.setItem(
+ `boards.${this.boardId}.${this.list.type}.expanded`,
+ this.list.isExpanded,
+ );
}
}
},
diff --git a/app/assets/javascripts/boards/components/board_blank_state.vue b/app/assets/javascripts/boards/components/board_blank_state.vue
index 38aaec73d7d..561a4636ef5 100644
--- a/app/assets/javascripts/boards/components/board_blank_state.vue
+++ b/app/assets/javascripts/boards/components/board_blank_state.vue
@@ -32,18 +32,18 @@ export default {
boardsStore.state.lists = _.sortBy(boardsStore.state.lists, 'position');
// Save the labels
- gl.boardService.generateDefaultLists()
+ gl.boardService
+ .generateDefaultLists()
.then(res => res.data)
- .then((data) => {
- data.forEach((listObj) => {
+ .then(data => {
+ data.forEach(listObj => {
const list = boardsStore.findList('title', listObj.title);
list.id = listObj.id;
list.label.id = listObj.label.id;
- list.getIssues()
- .catch(() => {
- // TODO: handle request error
- });
+ list.getIssues().catch(() => {
+ // TODO: handle request error
+ });
});
})
.catch(() => {
@@ -57,7 +57,6 @@ export default {
clearBlankState: boardsStore.removeBlankState.bind(boardsStore),
},
};
-
</script>
<template>
diff --git a/app/assets/javascripts/boards/components/board_card.vue b/app/assets/javascripts/boards/components/board_card.vue
index 843498f0d06..2f31316aa76 100644
--- a/app/assets/javascripts/boards/components/board_card.vue
+++ b/app/assets/javascripts/boards/components/board_card.vue
@@ -1,77 +1,77 @@
<script>
- /* eslint-disable vue/require-default-prop */
- import IssueCardInner from './issue_card_inner.vue';
- import eventHub from '../eventhub';
- import boardsStore from '../stores/boards_store';
+/* eslint-disable vue/require-default-prop */
+import IssueCardInner from './issue_card_inner.vue';
+import eventHub from '../eventhub';
+import boardsStore from '../stores/boards_store';
- export default {
- name: 'BoardsIssueCard',
- components: {
- IssueCardInner,
+export default {
+ name: 'BoardsIssueCard',
+ components: {
+ IssueCardInner,
+ },
+ props: {
+ list: {
+ type: Object,
+ default: () => ({}),
},
- props: {
- list: {
- type: Object,
- default: () => ({}),
- },
- issue: {
- type: Object,
- default: () => ({}),
- },
- issueLinkBase: {
- type: String,
- default: '',
- },
- disabled: {
- type: Boolean,
- default: false,
- },
- index: {
- type: Number,
- default: 0,
- },
- rootPath: {
- type: String,
- default: '',
- },
- groupId: {
- type: Number,
- },
+ issue: {
+ type: Object,
+ default: () => ({}),
},
- data() {
- return {
- showDetail: false,
- detailIssue: boardsStore.detail,
- };
+ issueLinkBase: {
+ type: String,
+ default: '',
},
- computed: {
- issueDetailVisible() {
- return this.detailIssue.issue && this.detailIssue.issue.id === this.issue.id;
- },
+ disabled: {
+ type: Boolean,
+ default: false,
},
- methods: {
- mouseDown() {
- this.showDetail = true;
- },
- mouseMove() {
- this.showDetail = false;
- },
- showIssue(e) {
- if (e.target.classList.contains('js-no-trigger')) return;
+ index: {
+ type: Number,
+ default: 0,
+ },
+ rootPath: {
+ type: String,
+ default: '',
+ },
+ groupId: {
+ type: Number,
+ },
+ },
+ data() {
+ return {
+ showDetail: false,
+ detailIssue: boardsStore.detail,
+ };
+ },
+ computed: {
+ issueDetailVisible() {
+ return this.detailIssue.issue && this.detailIssue.issue.id === this.issue.id;
+ },
+ },
+ methods: {
+ mouseDown() {
+ this.showDetail = true;
+ },
+ mouseMove() {
+ this.showDetail = false;
+ },
+ showIssue(e) {
+ if (e.target.classList.contains('js-no-trigger')) return;
- if (this.showDetail) {
- this.showDetail = false;
+ if (this.showDetail) {
+ this.showDetail = false;
- if (boardsStore.detail.issue && boardsStore.detail.issue.id === this.issue.id) {
- eventHub.$emit('clearDetailIssue');
- } else {
- eventHub.$emit('newDetailIssue', this.issue);
- boardsStore.detail.list = this.list;
- }
+ if (boardsStore.detail.issue && boardsStore.detail.issue.id === this.issue.id) {
+ eventHub.$emit('clearDetailIssue');
+ } else {
+ eventHub.$emit('newDetailIssue', this.issue);
+ boardsStore.detail.list = this.list;
}
- },
+ }
},
- };
+ },
+};
</script>
<template>
diff --git a/app/assets/javascripts/boards/components/board_new_issue.vue b/app/assets/javascripts/boards/components/board_new_issue.vue
index ae2d1ee3c6e..ee3dc38bca6 100644
--- a/app/assets/javascripts/boards/components/board_new_issue.vue
+++ b/app/assets/javascripts/boards/components/board_new_issue.vue
@@ -62,7 +62,8 @@ export default {
eventHub.$emit(`scroll-board-list-${this.list.id}`);
this.cancel();
- return this.list.newIssue(issue)
+ return this.list
+ .newIssue(issue)
.then(() => {
// Need this because our jQuery very kindly disables buttons on ALL form submissions
$(this.$refs.submitButton).enable();
diff --git a/app/assets/javascripts/boards/components/board_sidebar.js b/app/assets/javascripts/boards/components/board_sidebar.js
index 62666954de0..e637e1f1223 100644
--- a/app/assets/javascripts/boards/components/board_sidebar.js
+++ b/app/assets/javascripts/boards/components/board_sidebar.js
@@ -38,7 +38,7 @@ export default Vue.extend({
};
},
computed: {
- showSidebar () {
+ showSidebar() {
return Object.keys(this.issue).length;
},
milestoneTitle() {
@@ -51,18 +51,20 @@ export default Vue.extend({
return this.issue.labels && this.issue.labels.length;
},
labelDropdownTitle() {
- return this.hasLabels ? sprintf(__('%{firstLabel} +%{labelCount} more'), {
- firstLabel: this.issue.labels[0].title,
- labelCount: this.issue.labels.length - 1
- }) : __('Label');
+ return this.hasLabels
+ ? sprintf(__('%{firstLabel} +%{labelCount} more'), {
+ firstLabel: this.issue.labels[0].title,
+ labelCount: this.issue.labels.length - 1,
+ })
+ : __('Label');
},
selectedLabels() {
return this.hasLabels ? this.issue.labels.map(l => l.title).join(',') : '';
- }
+ },
},
watch: {
detail: {
- handler () {
+ handler() {
if (this.issue.id !== this.detail.issue.id) {
$('.block.assignee')
.find('input:not(.js-vue)[name="issue[assignee_ids][]"]')
@@ -71,17 +73,19 @@ export default Vue.extend({
});
$('.js-issue-board-sidebar', this.$el).each((i, el) => {
- $(el).data('glDropdown').clearMenu();
+ $(el)
+ .data('glDropdown')
+ .clearMenu();
});
}
this.issue = this.detail.issue;
this.list = this.detail.list;
},
- deep: true
+ deep: true,
},
},
- created () {
+ created() {
// Get events from glDropdown
eventHub.$on('sidebar.removeAssignee', this.removeAssignee);
eventHub.$on('sidebar.addAssignee', this.addAssignee);
@@ -94,7 +98,7 @@ export default Vue.extend({
eventHub.$off('sidebar.removeAllAssignees', this.removeAllAssignees);
eventHub.$off('sidebar.saveAssignees', this.saveAssignees);
},
- mounted () {
+ mounted() {
new IssuableContext(this.currentUser);
new MilestoneSelect();
new DueDateSelectors();
@@ -102,29 +106,30 @@ export default Vue.extend({
new Sidebar();
},
methods: {
- closeSidebar () {
+ closeSidebar() {
this.detail.issue = {};
},
- assignSelf () {
+ assignSelf() {
// Notify gl dropdown that we are now assigning to current user
this.$refs.assigneeBlock.dispatchEvent(new Event('assignYourself'));
this.addAssignee(this.currentUser);
this.saveAssignees();
},
- removeAssignee (a) {
+ removeAssignee(a) {
boardsStore.detail.issue.removeAssignee(a);
},
- addAssignee (a) {
+ addAssignee(a) {
boardsStore.detail.issue.addAssignee(a);
},
- removeAllAssignees () {
+ removeAllAssignees() {
boardsStore.detail.issue.removeAllAssignees();
},
- saveAssignees () {
+ saveAssignees() {
this.loadingAssignees = true;
- boardsStore.detail.issue.update()
+ boardsStore.detail.issue
+ .update()
.then(() => {
this.loadingAssignees = false;
})
diff --git a/app/assets/javascripts/boards/components/issue_card_inner.vue b/app/assets/javascripts/boards/components/issue_card_inner.vue
index aa98f35786e..d956777a86b 100644
--- a/app/assets/javascripts/boards/components/issue_card_inner.vue
+++ b/app/assets/javascripts/boards/components/issue_card_inner.vue
@@ -1,142 +1,142 @@
<script>
- import $ from 'jquery';
- import Icon from '~/vue_shared/components/icon.vue';
- import UserAvatarLink from '../../vue_shared/components/user_avatar/user_avatar_link.vue';
- import eventHub from '../eventhub';
- import tooltip from '../../vue_shared/directives/tooltip';
- import boardsStore from '../stores/boards_store';
+import $ from 'jquery';
+import Icon from '~/vue_shared/components/icon.vue';
+import UserAvatarLink from '../../vue_shared/components/user_avatar/user_avatar_link.vue';
+import eventHub from '../eventhub';
+import tooltip from '../../vue_shared/directives/tooltip';
+import boardsStore from '../stores/boards_store';
- export default {
- components: {
- UserAvatarLink,
- Icon,
- },
- directives: {
- tooltip,
- },
- props: {
- issue: {
- type: Object,
- required: true,
- },
- issueLinkBase: {
- type: String,
- required: true,
- },
- list: {
- type: Object,
- required: false,
- default: () => ({}),
- },
- rootPath: {
- type: String,
- required: true,
- },
- updateFilters: {
- type: Boolean,
- required: false,
- default: false,
- },
- groupId: {
- type: Number,
- required: false,
- default: null,
- },
- },
- data() {
- return {
- limitBeforeCounter: 3,
- maxRender: 4,
- maxCounter: 99,
- };
+export default {
+ components: {
+ UserAvatarLink,
+ Icon,
+ },
+ directives: {
+ tooltip,
+ },
+ props: {
+ issue: {
+ type: Object,
+ required: true,
},
- computed: {
- numberOverLimit() {
- return this.issue.assignees.length - this.limitBeforeCounter;
- },
- assigneeCounterTooltip() {
- return `${this.assigneeCounterLabel} more`;
- },
- assigneeCounterLabel() {
- if (this.numberOverLimit > this.maxCounter) {
- return `${this.maxCounter}+`;
- }
-
- return `+${this.numberOverLimit}`;
- },
- shouldRenderCounter() {
- if (this.issue.assignees.length <= this.maxRender) {
- return false;
- }
+ issueLinkBase: {
+ type: String,
+ required: true,
+ },
+ list: {
+ type: Object,
+ required: false,
+ default: () => ({}),
+ },
+ rootPath: {
+ type: String,
+ required: true,
+ },
+ updateFilters: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
+ groupId: {
+ type: Number,
+ required: false,
+ default: null,
+ },
+ },
+ data() {
+ return {
+ limitBeforeCounter: 3,
+ maxRender: 4,
+ maxCounter: 99,
+ };
+ },
+ computed: {
+ numberOverLimit() {
+ return this.issue.assignees.length - this.limitBeforeCounter;
+ },
+ assigneeCounterTooltip() {
+ return `${this.assigneeCounterLabel} more`;
+ },
+ assigneeCounterLabel() {
+ if (this.numberOverLimit > this.maxCounter) {
+ return `${this.maxCounter}+`;
+ }
- return this.issue.assignees.length > this.numberOverLimit;
- },
- issueId() {
- if (this.issue.iid) {
- return `#${this.issue.iid}`;
- }
+ return `+${this.numberOverLimit}`;
+ },
+ shouldRenderCounter() {
+ if (this.issue.assignees.length <= this.maxRender) {
return false;
- },
- showLabelFooter() {
- return this.issue.labels.find(l => this.showLabel(l)) !== undefined;
- },
- },
- methods: {
- isIndexLessThanlimit(index) {
- return index < this.limitBeforeCounter;
- },
- shouldRenderAssignee(index) {
- // Eg. maxRender is 4,
- // Render up to all 4 assignees if there are only 4 assigness
- // Otherwise render up to the limitBeforeCounter
- if (this.issue.assignees.length <= this.maxRender) {
- return index < this.maxRender;
- }
+ }
- return index < this.limitBeforeCounter;
- },
- assigneeUrl(assignee) {
- return `${this.rootPath}${assignee.username}`;
- },
- assigneeUrlTitle(assignee) {
- return `Assigned to ${assignee.name}`;
- },
- avatarUrlTitle(assignee) {
- return `Avatar for ${assignee.name}`;
- },
- showLabel(label) {
- if (!label.id) return false;
- return true;
- },
- filterByLabel(label, e) {
- if (!this.updateFilters) return;
+ return this.issue.assignees.length > this.numberOverLimit;
+ },
+ issueId() {
+ if (this.issue.iid) {
+ return `#${this.issue.iid}`;
+ }
+ return false;
+ },
+ showLabelFooter() {
+ return this.issue.labels.find(l => this.showLabel(l)) !== undefined;
+ },
+ },
+ methods: {
+ isIndexLessThanlimit(index) {
+ return index < this.limitBeforeCounter;
+ },
+ shouldRenderAssignee(index) {
+ // Eg. maxRender is 4,
+ // Render up to all 4 assignees if there are only 4 assigness
+ // Otherwise render up to the limitBeforeCounter
+ if (this.issue.assignees.length <= this.maxRender) {
+ return index < this.maxRender;
+ }
+
+ return index < this.limitBeforeCounter;
+ },
+ assigneeUrl(assignee) {
+ return `${this.rootPath}${assignee.username}`;
+ },
+ assigneeUrlTitle(assignee) {
+ return `Assigned to ${assignee.name}`;
+ },
+ avatarUrlTitle(assignee) {
+ return `Avatar for ${assignee.name}`;
+ },
+ showLabel(label) {
+ if (!label.id) return false;
+ return true;
+ },
+ filterByLabel(label, e) {
+ if (!this.updateFilters) return;
- const filterPath = boardsStore.filter.path.split('&');
- const labelTitle = encodeURIComponent(label.title);
- const param = `label_name[]=${labelTitle}`;
- const labelIndex = filterPath.indexOf(param);
- $(e.currentTarget).tooltip('hide');
+ const filterPath = boardsStore.filter.path.split('&');
+ const labelTitle = encodeURIComponent(label.title);
+ const param = `label_name[]=${labelTitle}`;
+ const labelIndex = filterPath.indexOf(param);
+ $(e.currentTarget).tooltip('hide');
- if (labelIndex === -1) {
- filterPath.push(param);
- } else {
- filterPath.splice(labelIndex, 1);
- }
+ if (labelIndex === -1) {
+ filterPath.push(param);
+ } else {
+ filterPath.splice(labelIndex, 1);
+ }
- boardsStore.filter.path = filterPath.join('&');
+ boardsStore.filter.path = filterPath.join('&');
- boardsStore.updateFiltersUrl();
+ boardsStore.updateFiltersUrl();
- eventHub.$emit('updateTokens');
- },
- labelStyle(label) {
- return {
- backgroundColor: label.color,
- color: label.textColor,
- };
- },
- },
- };
+ eventHub.$emit('updateTokens');
+ },
+ labelStyle(label) {
+ return {
+ backgroundColor: label.color,
+ color: label.textColor,
+ };
+ },
+ },
+};
</script>
<template>
<div>
diff --git a/app/assets/javascripts/boards/components/modal/empty_state.vue b/app/assets/javascripts/boards/components/modal/empty_state.vue
index dbd69f84526..795ba864545 100644
--- a/app/assets/javascripts/boards/components/modal/empty_state.vue
+++ b/app/assets/javascripts/boards/components/modal/empty_state.vue
@@ -20,7 +20,7 @@ export default {
computed: {
contents() {
const obj = {
- title: 'You haven\'t added any issues to your project yet',
+ title: "You haven't added any issues to your project yet",
content: `
An issue can be a bug, a todo or a feature request that needs to be
discussed in a project. Besides, issues are searchable and filterable.
@@ -28,7 +28,7 @@ export default {
};
if (this.activeTab === 'selected') {
- obj.title = 'You haven\'t selected any issues yet';
+ obj.title = "You haven't selected any issues yet";
obj.content = `
Go back to <strong>Open issues</strong> and select some issues
to add to your board.
diff --git a/app/assets/javascripts/boards/components/modal/footer.vue b/app/assets/javascripts/boards/components/modal/footer.vue
index 268ca6bca13..d51597ed22d 100644
--- a/app/assets/javascripts/boards/components/modal/footer.vue
+++ b/app/assets/javascripts/boards/components/modal/footer.vue
@@ -42,19 +42,17 @@ export default {
const req = this.buildUpdateRequest(list);
// Post the data to the backend
- gl.boardService
- .bulkUpdate(issueIds, req)
- .catch(() => {
- Flash(__('Failed to update issues, please try again.'));
+ gl.boardService.bulkUpdate(issueIds, req).catch(() => {
+ Flash(__('Failed to update issues, please try again.'));
- selectedIssues.forEach((issue) => {
- list.removeIssue(issue);
- list.issuesSize -= 1;
- });
+ selectedIssues.forEach(issue => {
+ list.removeIssue(issue);
+ list.issuesSize -= 1;
});
+ });
// Add the issues on the frontend
- selectedIssues.forEach((issue) => {
+ selectedIssues.forEach(issue => {
list.addIssue(issue);
list.issuesSize += 1;
});
diff --git a/app/assets/javascripts/boards/components/modal/header.vue b/app/assets/javascripts/boards/components/modal/header.vue
index 979fb4d7199..fc6cefa89a9 100644
--- a/app/assets/javascripts/boards/components/modal/header.vue
+++ b/app/assets/javascripts/boards/components/modal/header.vue
@@ -1,52 +1,52 @@
<script>
- import ModalFilters from './filters';
- import ModalTabs from './tabs.vue';
- import ModalStore from '../../stores/modal_store';
- import modalMixin from '../../mixins/modal_mixins';
+import ModalFilters from './filters';
+import ModalTabs from './tabs.vue';
+import ModalStore from '../../stores/modal_store';
+import modalMixin from '../../mixins/modal_mixins';
- export default {
- components: {
- ModalTabs,
- ModalFilters,
+export default {
+ components: {
+ ModalTabs,
+ ModalFilters,
+ },
+ mixins: [modalMixin],
+ props: {
+ projectId: {
+ type: Number,
+ required: true,
},
- mixins: [modalMixin],
- props: {
- projectId: {
- type: Number,
- required: true,
- },
- milestonePath: {
- type: String,
- required: true,
- },
- labelPath: {
- type: String,
- required: true,
- },
+ milestonePath: {
+ type: String,
+ required: true,
},
- data() {
- return ModalStore.store;
+ labelPath: {
+ type: String,
+ required: true,
},
- computed: {
- selectAllText() {
- if (ModalStore.selectedCount() !== this.issues.length || this.issues.length === 0) {
- return 'Select all';
- }
+ },
+ data() {
+ return ModalStore.store;
+ },
+ computed: {
+ selectAllText() {
+ if (ModalStore.selectedCount() !== this.issues.length || this.issues.length === 0) {
+ return 'Select all';
+ }
- return 'Deselect all';
- },
- showSearch() {
- return this.activeTab === 'all' && !this.loading && this.issuesCount > 0;
- },
+ return 'Deselect all';
},
- methods: {
- toggleAll() {
- this.$refs.selectAllBtn.blur();
+ showSearch() {
+ return this.activeTab === 'all' && !this.loading && this.issuesCount > 0;
+ },
+ },
+ methods: {
+ toggleAll() {
+ this.$refs.selectAllBtn.blur();
- ModalStore.toggleAll();
- },
+ ModalStore.toggleAll();
},
- };
+ },
+};
</script>
<template>
<div>
diff --git a/app/assets/javascripts/boards/components/modal/index.vue b/app/assets/javascripts/boards/components/modal/index.vue
index 0c4c709324d..40949cc0656 100644
--- a/app/assets/javascripts/boards/components/modal/index.vue
+++ b/app/assets/javascripts/boards/components/modal/index.vue
@@ -1,143 +1,144 @@
<script>
- /* global ListIssue */
- import { urlParamsToObject } from '~/lib/utils/common_utils';
- import ModalHeader from './header.vue';
- import ModalList from './list.vue';
- import ModalFooter from './footer.vue';
- import EmptyState from './empty_state.vue';
- import ModalStore from '../../stores/modal_store';
+/* global ListIssue */
+import { urlParamsToObject } from '~/lib/utils/common_utils';
+import ModalHeader from './header.vue';
+import ModalList from './list.vue';
+import ModalFooter from './footer.vue';
+import EmptyState from './empty_state.vue';
+import ModalStore from '../../stores/modal_store';
- export default {
- components: {
- EmptyState,
- ModalHeader,
- ModalList,
- ModalFooter,
+export default {
+ components: {
+ EmptyState,
+ ModalHeader,
+ ModalList,
+ ModalFooter,
+ },
+ props: {
+ newIssuePath: {
+ type: String,
+ required: true,
},
- props: {
- newIssuePath: {
- type: String,
- required: true,
- },
- emptyStateSvg: {
- type: String,
- required: true,
- },
- issueLinkBase: {
- type: String,
- required: true,
- },
- rootPath: {
- type: String,
- required: true,
- },
- projectId: {
- type: Number,
- required: true,
- },
- milestonePath: {
- type: String,
- required: true,
- },
- labelPath: {
- type: String,
- required: true,
- },
+ emptyStateSvg: {
+ type: String,
+ required: true,
},
- data() {
- return ModalStore.store;
+ issueLinkBase: {
+ type: String,
+ required: true,
},
- computed: {
- showList() {
- if (this.activeTab === 'selected') {
- return this.selectedIssues.length > 0;
- }
+ rootPath: {
+ type: String,
+ required: true,
+ },
+ projectId: {
+ type: Number,
+ required: true,
+ },
+ milestonePath: {
+ type: String,
+ required: true,
+ },
+ labelPath: {
+ type: String,
+ required: true,
+ },
+ },
+ data() {
+ return ModalStore.store;
+ },
+ computed: {
+ showList() {
+ if (this.activeTab === 'selected') {
+ return this.selectedIssues.length > 0;
+ }
- return this.issuesCount > 0;
- },
- showEmptyState() {
- if (!this.loading && this.issuesCount === 0) {
- return true;
- }
+ return this.issuesCount > 0;
+ },
+ showEmptyState() {
+ if (!this.loading && this.issuesCount === 0) {
+ return true;
+ }
- return this.activeTab === 'selected' && this.selectedIssues.length === 0;
- },
+ return this.activeTab === 'selected' && this.selectedIssues.length === 0;
},
- watch: {
- page() {
- this.loadIssues();
- },
- showAddIssuesModal() {
- if (this.showAddIssuesModal && !this.issues.length) {
- this.loading = true;
+ },
+ watch: {
+ page() {
+ this.loadIssues();
+ },
+ showAddIssuesModal() {
+ if (this.showAddIssuesModal && !this.issues.length) {
+ this.loading = true;
+ const loadingDone = () => {
+ this.loading = false;
+ };
+
+ this.loadIssues()
+ .then(loadingDone)
+ .catch(loadingDone);
+ } else if (!this.showAddIssuesModal) {
+ this.issues = [];
+ this.selectedIssues = [];
+ this.issuesCount = false;
+ }
+ },
+ filter: {
+ handler() {
+ if (this.$el.tagName) {
+ this.page = 1;
+ this.filterLoading = true;
const loadingDone = () => {
- this.loading = false;
+ this.filterLoading = false;
};
- this.loadIssues()
+ this.loadIssues(true)
.then(loadingDone)
.catch(loadingDone);
- } else if (!this.showAddIssuesModal) {
- this.issues = [];
- this.selectedIssues = [];
- this.issuesCount = false;
}
},
- filter: {
- handler() {
- if (this.$el.tagName) {
- this.page = 1;
- this.filterLoading = true;
- const loadingDone = () => {
- this.filterLoading = false;
- };
-
- this.loadIssues(true)
- .then(loadingDone)
- .catch(loadingDone);
- }
- },
- deep: true,
- },
+ deep: true,
},
- created() {
- this.page = 1;
- },
- methods: {
- loadIssues(clearIssues = false) {
- if (!this.showAddIssuesModal) return false;
+ },
+ created() {
+ this.page = 1;
+ },
+ methods: {
+ loadIssues(clearIssues = false) {
+ if (!this.showAddIssuesModal) return false;
- return gl.boardService.getBacklog({
+ return gl.boardService
+ .getBacklog({
...urlParamsToObject(this.filter.path),
page: this.page,
per: this.perPage,
})
- .then(res => res.data)
- .then(data => {
- if (clearIssues) {
- this.issues = [];
- }
+ .then(res => res.data)
+ .then(data => {
+ if (clearIssues) {
+ this.issues = [];
+ }
- data.issues.forEach(issueObj => {
- const issue = new ListIssue(issueObj);
- const foundSelectedIssue = ModalStore.findSelectedIssue(issue);
- issue.selected = !!foundSelectedIssue;
+ data.issues.forEach(issueObj => {
+ const issue = new ListIssue(issueObj);
+ const foundSelectedIssue = ModalStore.findSelectedIssue(issue);
+ issue.selected = !!foundSelectedIssue;
- this.issues.push(issue);
- });
+ this.issues.push(issue);
+ });
- this.loadingNewPage = false;
+ this.loadingNewPage = false;
- if (!this.issuesCount) {
- this.issuesCount = data.size;
- }
- })
- .catch(() => {
- // TODO: handle request error
- });
- },
+ if (!this.issuesCount) {
+ this.issuesCount = data.size;
+ }
+ })
+ .catch(() => {
+ // TODO: handle request error
+ });
},
- };
+ },
+};
</script>
<template>
<div
diff --git a/app/assets/javascripts/boards/components/modal/list.vue b/app/assets/javascripts/boards/components/modal/list.vue
index c93fd9f415c..e11f398e70d 100644
--- a/app/assets/javascripts/boards/components/modal/list.vue
+++ b/app/assets/javascripts/boards/components/modal/list.vue
@@ -1,120 +1,120 @@
<script>
- import Icon from '~/vue_shared/components/icon.vue';
- import bp from '../../../breakpoints';
- import ModalStore from '../../stores/modal_store';
- import IssueCardInner from '../issue_card_inner.vue';
+import Icon from '~/vue_shared/components/icon.vue';
+import bp from '../../../breakpoints';
+import ModalStore from '../../stores/modal_store';
+import IssueCardInner from '../issue_card_inner.vue';
- export default {
- components: {
- IssueCardInner,
- Icon,
+export default {
+ components: {
+ IssueCardInner,
+ Icon,
+ },
+ props: {
+ issueLinkBase: {
+ type: String,
+ required: true,
},
- props: {
- issueLinkBase: {
- type: String,
- required: true,
- },
- rootPath: {
- type: String,
- required: true,
- },
- emptyStateSvg: {
- type: String,
- required: true,
- },
+ rootPath: {
+ type: String,
+ required: true,
},
- data() {
- return ModalStore.store;
+ emptyStateSvg: {
+ type: String,
+ required: true,
},
- computed: {
- loopIssues() {
- if (this.activeTab === 'all') {
- return this.issues;
- }
+ },
+ data() {
+ return ModalStore.store;
+ },
+ computed: {
+ loopIssues() {
+ if (this.activeTab === 'all') {
+ return this.issues;
+ }
- return this.selectedIssues;
- },
- groupedIssues() {
- const groups = [];
- this.loopIssues.forEach((issue, i) => {
- const index = i % this.columns;
+ return this.selectedIssues;
+ },
+ groupedIssues() {
+ const groups = [];
+ this.loopIssues.forEach((issue, i) => {
+ const index = i % this.columns;
- if (!groups[index]) {
- groups.push([]);
- }
+ if (!groups[index]) {
+ groups.push([]);
+ }
- groups[index].push(issue);
- });
+ groups[index].push(issue);
+ });
- return groups;
- },
+ return groups;
},
- watch: {
- activeTab() {
- if (this.activeTab === 'all') {
- ModalStore.purgeUnselectedIssues();
- }
- },
+ },
+ watch: {
+ activeTab() {
+ if (this.activeTab === 'all') {
+ ModalStore.purgeUnselectedIssues();
+ }
},
- mounted() {
- this.scrollHandlerWrapper = this.scrollHandler.bind(this);
- this.setColumnCountWrapper = this.setColumnCount.bind(this);
- this.setColumnCount();
+ },
+ mounted() {
+ this.scrollHandlerWrapper = this.scrollHandler.bind(this);
+ this.setColumnCountWrapper = this.setColumnCount.bind(this);
+ this.setColumnCount();
- this.$refs.list.addEventListener('scroll', this.scrollHandlerWrapper);
- window.addEventListener('resize', this.setColumnCountWrapper);
+ this.$refs.list.addEventListener('scroll', this.scrollHandlerWrapper);
+ window.addEventListener('resize', this.setColumnCountWrapper);
+ },
+ beforeDestroy() {
+ this.$refs.list.removeEventListener('scroll', this.scrollHandlerWrapper);
+ window.removeEventListener('resize', this.setColumnCountWrapper);
+ },
+ methods: {
+ scrollHandler() {
+ const currentPage = Math.floor(this.issues.length / this.perPage);
+
+ if (
+ this.scrollTop() > this.scrollHeight() - 100 &&
+ !this.loadingNewPage &&
+ currentPage === this.page
+ ) {
+ this.loadingNewPage = true;
+ this.page += 1;
+ }
},
- beforeDestroy() {
- this.$refs.list.removeEventListener('scroll', this.scrollHandlerWrapper);
- window.removeEventListener('resize', this.setColumnCountWrapper);
+ toggleIssue(e, issue) {
+ if (e.target.tagName !== 'A') {
+ ModalStore.toggleIssue(issue);
+ }
},
- methods: {
- scrollHandler() {
- const currentPage = Math.floor(this.issues.length / this.perPage);
-
- if (
- this.scrollTop() > this.scrollHeight() - 100 &&
- !this.loadingNewPage &&
- currentPage === this.page
- ) {
- this.loadingNewPage = true;
- this.page += 1;
- }
- },
- toggleIssue(e, issue) {
- if (e.target.tagName !== 'A') {
- ModalStore.toggleIssue(issue);
- }
- },
- listHeight() {
- return this.$refs.list.getBoundingClientRect().height;
- },
- scrollHeight() {
- return this.$refs.list.scrollHeight;
- },
- scrollTop() {
- return this.$refs.list.scrollTop + this.listHeight();
- },
- showIssue(issue) {
- if (this.activeTab === 'all') return true;
+ listHeight() {
+ return this.$refs.list.getBoundingClientRect().height;
+ },
+ scrollHeight() {
+ return this.$refs.list.scrollHeight;
+ },
+ scrollTop() {
+ return this.$refs.list.scrollTop + this.listHeight();
+ },
+ showIssue(issue) {
+ if (this.activeTab === 'all') return true;
- const index = ModalStore.selectedIssueIndex(issue);
+ const index = ModalStore.selectedIssueIndex(issue);
- return index !== -1;
- },
- setColumnCount() {
- const breakpoint = bp.getBreakpointSize();
+ return index !== -1;
+ },
+ setColumnCount() {
+ const breakpoint = bp.getBreakpointSize();
- if (breakpoint === 'lg' || breakpoint === 'md') {
- this.columns = 3;
- } else if (breakpoint === 'sm') {
- this.columns = 2;
- } else {
- this.columns = 1;
- }
- },
+ if (breakpoint === 'lg' || breakpoint === 'md') {
+ this.columns = 3;
+ } else if (breakpoint === 'sm') {
+ this.columns = 2;
+ } else {
+ this.columns = 1;
+ }
},
- };
+ },
+};
</script>
<template>
<section
diff --git a/app/assets/javascripts/boards/components/modal/tabs.vue b/app/assets/javascripts/boards/components/modal/tabs.vue
index d926b080094..5d661590e8e 100644
--- a/app/assets/javascripts/boards/components/modal/tabs.vue
+++ b/app/assets/javascripts/boards/components/modal/tabs.vue
@@ -1,21 +1,21 @@
<script>
- import ModalStore from '../../stores/modal_store';
- import modalMixin from '../../mixins/modal_mixins';
+import ModalStore from '../../stores/modal_store';
+import modalMixin from '../../mixins/modal_mixins';
- export default {
- mixins: [modalMixin],
- data() {
- return ModalStore.store;
+export default {
+ mixins: [modalMixin],
+ data() {
+ return ModalStore.store;
+ },
+ computed: {
+ selectedCount() {
+ return ModalStore.selectedCount();
},
- computed: {
- selectedCount() {
- return ModalStore.selectedCount();
- },
- },
- destroyed() {
- this.activeTab = 'all';
- },
- };
+ },
+ destroyed() {
+ this.activeTab = 'all';
+ },
+};
</script>
<template>
<div class="top-area prepend-top-10 append-bottom-10">
diff --git a/app/assets/javascripts/boards/components/new_list_dropdown.js b/app/assets/javascripts/boards/components/new_list_dropdown.js
index 2c2045f8901..f7016561f93 100644
--- a/app/assets/javascripts/boards/components/new_list_dropdown.js
+++ b/app/assets/javascripts/boards/components/new_list_dropdown.js
@@ -6,36 +6,41 @@ import _ from 'underscore';
import CreateLabelDropdown from '../../create_label';
import boardsStore from '../stores/boards_store';
-$(document).off('created.label').on('created.label', (e, label) => {
- boardsStore.new({
- title: label.title,
- position: boardsStore.state.lists.length - 2,
- list_type: 'label',
- label: {
- id: label.id,
+$(document)
+ .off('created.label')
+ .on('created.label', (e, label) => {
+ boardsStore.new({
title: label.title,
- color: label.color,
- },
+ position: boardsStore.state.lists.length - 2,
+ list_type: 'label',
+ label: {
+ id: label.id,
+ title: label.title,
+ color: label.color,
+ },
+ });
});
-});
export default function initNewListDropdown() {
- $('.js-new-board-list').each(function () {
+ $('.js-new-board-list').each(function() {
const $this = $(this);
- new CreateLabelDropdown($this.closest('.dropdown').find('.dropdown-new-label'), $this.data('namespacePath'), $this.data('projectPath'));
+ new CreateLabelDropdown(
+ $this.closest('.dropdown').find('.dropdown-new-label'),
+ $this.data('namespacePath'),
+ $this.data('projectPath'),
+ );
$this.glDropdown({
data(term, callback) {
- axios.get($this.attr('data-list-labels-path'))
- .then(({ data }) => {
- callback(data);
- });
+ axios.get($this.attr('data-list-labels-path')).then(({ data }) => {
+ callback(data);
+ });
},
- renderRow (label) {
+ renderRow(label) {
const active = boardsStore.findList('title', label.title);
const $li = $('<li />');
const $a = $('<a />', {
- class: (active ? `is-active js-board-list-${active.id}` : ''),
+ class: active ? `is-active js-board-list-${active.id}` : '',
text: label.title,
href: '#',
});
@@ -53,7 +58,7 @@ export default function initNewListDropdown() {
selectable: true,
multiSelect: true,
containerSelector: '.js-tab-container-labels .dropdown-page-one .dropdown-content',
- clicked (options) {
+ clicked(options) {
const { e } = options;
const label = options.selectedObj;
e.preventDefault();
diff --git a/app/assets/javascripts/boards/components/project_select.vue b/app/assets/javascripts/boards/components/project_select.vue
index 427a0868b0c..0f01a2a6c09 100644
--- a/app/assets/javascripts/boards/components/project_select.vue
+++ b/app/assets/javascripts/boards/components/project_select.vue
@@ -46,7 +46,7 @@ export default {
selectable: true,
data: (term, callback) => {
this.loading = true;
- return Api.groupProjects(this.groupId, term, {with_issues_enabled: true}, projects => {
+ return Api.groupProjects(this.groupId, term, { with_issues_enabled: true }, projects => {
this.loading = false;
callback(projects);
});
@@ -54,7 +54,9 @@ export default {
renderRow(project) {
return `
<li>
- <a href='#' class='dropdown-menu-link' data-project-id="${project.id}" data-project-name="${project.name}">
+ <a href='#' class='dropdown-menu-link' data-project-id="${
+ project.id
+ }" data-project-name="${project.name}">
${_.escape(project.name)}
</a>
</li>
diff --git a/app/assets/javascripts/boards/components/sidebar/remove_issue.vue b/app/assets/javascripts/boards/components/sidebar/remove_issue.vue
index b8f2e324d43..d681e6a431c 100644
--- a/app/assets/javascripts/boards/components/sidebar/remove_issue.vue
+++ b/app/assets/javascripts/boards/components/sidebar/remove_issue.vue
@@ -1,79 +1,77 @@
<script>
- import Vue from 'vue';
- import Flash from '../../../flash';
- import { __ } from '../../../locale';
- import boardsStore from '../../stores/boards_store';
+import Vue from 'vue';
+import Flash from '../../../flash';
+import { __ } from '../../../locale';
+import boardsStore from '../../stores/boards_store';
- export default Vue.extend({
- props: {
- issue: {
- type: Object,
- required: true,
- },
- list: {
- type: Object,
- required: true,
- },
+export default Vue.extend({
+ props: {
+ issue: {
+ type: Object,
+ required: true,
},
- computed: {
- updateUrl() {
- return this.issue.path;
- },
+ list: {
+ type: Object,
+ required: true,
},
- methods: {
- removeIssue() {
- const { issue } = this;
- const lists = issue.getLists();
- const req = this.buildPatchRequest(issue, lists);
-
- const data = {
- issue: this.seedPatchRequest(issue, req),
- };
+ },
+ computed: {
+ updateUrl() {
+ return this.issue.path;
+ },
+ },
+ methods: {
+ removeIssue() {
+ const { issue } = this;
+ const lists = issue.getLists();
+ const req = this.buildPatchRequest(issue, lists);
- if (data.issue.label_ids.length === 0) {
- data.issue.label_ids = [''];
- }
+ const data = {
+ issue: this.seedPatchRequest(issue, req),
+ };
- // Post the remove data
- Vue.http.patch(this.updateUrl, data).catch(() => {
- Flash(__('Failed to remove issue from board, please try again.'));
+ if (data.issue.label_ids.length === 0) {
+ data.issue.label_ids = [''];
+ }
- lists.forEach(list => {
- list.addIssue(issue);
- });
- });
+ // Post the remove data
+ Vue.http.patch(this.updateUrl, data).catch(() => {
+ Flash(__('Failed to remove issue from board, please try again.'));
- // Remove from the frontend store
lists.forEach(list => {
- list.removeIssue(issue);
+ list.addIssue(issue);
});
+ });
- boardsStore.detail.issue = {};
- },
- /**
- * Build the default patch request.
- */
- buildPatchRequest(issue, lists) {
- const listLabelIds = lists.map(list => list.label.id);
+ // Remove from the frontend store
+ lists.forEach(list => {
+ list.removeIssue(issue);
+ });
- const labelIds = issue.labels
- .map(label => label.id)
- .filter(id => !listLabelIds.includes(id));
+ boardsStore.detail.issue = {};
+ },
+ /**
+ * Build the default patch request.
+ */
+ buildPatchRequest(issue, lists) {
+ const listLabelIds = lists.map(list => list.label.id);
+
+ const labelIds = issue.labels.map(label => label.id).filter(id => !listLabelIds.includes(id));
- return {
- label_ids: labelIds,
- };
- },
- /**
- * Seed the given patch request.
- *
- * (This is overridden in EE)
- */
- seedPatchRequest(issue, req) {
- return req;
- },
+ return {
+ label_ids: labelIds,
+ };
+ },
+ /**
+ * Seed the given patch request.
+ *
+ * (This is overridden in EE)
+ */
+ seedPatchRequest(issue, req) {
+ return req;
},
- });
+ },
+});
</script>
<template>
<div
diff --git a/app/assets/javascripts/boards/filtered_search_boards.js b/app/assets/javascripts/boards/filtered_search_boards.js
index acf41e5689e..c14d69c5d18 100644
--- a/app/assets/javascripts/boards/filtered_search_boards.js
+++ b/app/assets/javascripts/boards/filtered_search_boards.js
@@ -32,7 +32,7 @@ export default class FilteredSearchBoards extends FilteredSearchManager {
const tokens = FilteredSearchContainer.container.querySelectorAll('.js-visual-token');
// Remove all the tokens as they will be replaced by the search manager
- [].forEach.call(tokens, (el) => {
+ [].forEach.call(tokens, el => {
el.parentNode.removeChild(el);
});
@@ -50,7 +50,10 @@ export default class FilteredSearchBoards extends FilteredSearchManager {
canEdit(tokenName, tokenValue) {
if (this.cantEdit.includes(tokenName)) return false;
- return this.cantEditWithValue.findIndex(token => token.name === tokenName &&
- token.value === tokenValue) === -1;
+ return (
+ this.cantEditWithValue.findIndex(
+ token => token.name === tokenName && token.value === tokenValue,
+ ) === -1
+ );
}
}
diff --git a/app/assets/javascripts/boards/index.js b/app/assets/javascripts/boards/index.js
index 91861f2f9ee..61a3072ac27 100644
--- a/app/assets/javascripts/boards/index.js
+++ b/app/assets/javascripts/boards/index.js
@@ -32,9 +32,9 @@ export default () => {
const $boardApp = document.getElementById('board-app');
// check for browser back and trigger a hard reload to circumvent browser caching.
- window.addEventListener('pageshow', (event) => {
- const isNavTypeBackForward = window.performance &&
- window.performance.navigation.type === NavigationType.TYPE_BACK_FORWARD;
+ window.addEventListener('pageshow', event => {
+ const isNavTypeBackForward =
+ window.performance && window.performance.navigation.type === NavigationType.TYPE_BACK_FORWARD;
if (event.persisted || isNavTypeBackForward) {
window.location.reload();
diff --git a/app/assets/javascripts/boards/mixins/sortable_default_options.js b/app/assets/javascripts/boards/mixins/sortable_default_options.js
index c9cde4effb9..983b28d2e67 100644
--- a/app/assets/javascripts/boards/mixins/sortable_default_options.js
+++ b/app/assets/javascripts/boards/mixins/sortable_default_options.js
@@ -4,7 +4,8 @@ import $ from 'jquery';
import sortableConfig from '../../sortable/sortable_config';
export function sortableStart() {
- $('.has-tooltip').tooltip('hide')
+ $('.has-tooltip')
+ .tooltip('hide')
.tooltip('disable');
document.body.classList.add('is-dragging');
}
@@ -15,7 +16,8 @@ export function sortableEnd() {
}
export function getBoardSortableDefaultOptions(obj) {
- const touchEnabled = ('ontouchstart' in window) || window.DocumentTouch && document instanceof DocumentTouch;
+ const touchEnabled =
+ 'ontouchstart' in window || (window.DocumentTouch && document instanceof DocumentTouch);
const defaultSortOptions = Object.assign({}, sortableConfig, {
filter: '.board-delete, .btn',
@@ -26,6 +28,8 @@ export function getBoardSortableDefaultOptions(obj) {
onEnd: sortableEnd,
});
- Object.keys(obj).forEach((key) => { defaultSortOptions[key] = obj[key]; });
+ Object.keys(obj).forEach(key => {
+ defaultSortOptions[key] = obj[key];
+ });
return defaultSortOptions;
}
diff --git a/app/assets/javascripts/boards/models/issue.js b/app/assets/javascripts/boards/models/issue.js
index 52d04389b88..bb3b2865934 100644
--- a/app/assets/javascripts/boards/models/issue.js
+++ b/app/assets/javascripts/boards/models/issue.js
@@ -9,7 +9,7 @@ import IssueProject from './project';
import boardsStore from '../stores/boards_store';
class ListIssue {
- constructor (obj, defaultAvatar) {
+ constructor(obj, defaultAvatar) {
this.id = obj.id;
this.iid = obj.iid;
this.title = obj.title;
@@ -39,54 +39,54 @@ class ListIssue {
this.milestone = new ListMilestone(obj.milestone);
}
- obj.labels.forEach((label) => {
+ obj.labels.forEach(label => {
this.labels.push(new ListLabel(label));
});
this.assignees = obj.assignees.map(a => new ListAssignee(a, defaultAvatar));
}
- addLabel (label) {
+ addLabel(label) {
if (!this.findLabel(label)) {
this.labels.push(new ListLabel(label));
}
}
- findLabel (findLabel) {
+ findLabel(findLabel) {
return this.labels.filter(label => label.title === findLabel.title)[0];
}
- removeLabel (removeLabel) {
+ removeLabel(removeLabel) {
if (removeLabel) {
this.labels = this.labels.filter(label => removeLabel.title !== label.title);
}
}
- removeLabels (labels) {
+ removeLabels(labels) {
labels.forEach(this.removeLabel.bind(this));
}
- addAssignee (assignee) {
+ addAssignee(assignee) {
if (!this.findAssignee(assignee)) {
this.assignees.push(new ListAssignee(assignee));
}
}
- findAssignee (findAssignee) {
+ findAssignee(findAssignee) {
return this.assignees.filter(assignee => assignee.id === findAssignee.id)[0];
}
- removeAssignee (removeAssignee) {
+ removeAssignee(removeAssignee) {
if (removeAssignee) {
this.assignees = this.assignees.filter(assignee => assignee.id !== removeAssignee.id);
}
}
- removeAllAssignees () {
+ removeAllAssignees() {
this.assignees = [];
}
- getLists () {
+ getLists() {
return boardsStore.state.lists.filter(list => list.findIssue(this.id));
}
@@ -102,14 +102,14 @@ class ListIssue {
this.isLoading[key] = value;
}
- update () {
+ update() {
const data = {
issue: {
milestone_id: this.milestone ? this.milestone.id : null,
due_date: this.dueDate,
- assignee_ids: this.assignees.length > 0 ? this.assignees.map((u) => u.id) : [0],
- label_ids: this.labels.map((label) => label.id)
- }
+ assignee_ids: this.assignees.length > 0 ? this.assignees.map(u => u.id) : [0],
+ label_ids: this.labels.map(label => label.id),
+ },
};
if (!data.issue.label_ids.length) {
diff --git a/app/assets/javascripts/boards/models/list.js b/app/assets/javascripts/boards/models/list.js
index 3161f1da8c9..dd3feedbc0e 100644
--- a/app/assets/javascripts/boards/models/list.js
+++ b/app/assets/javascripts/boards/models/list.js
@@ -234,11 +234,11 @@ class List {
});
}
- getTypeInfo (type) {
+ getTypeInfo(type) {
return TYPES[type] || {};
}
- onNewIssueResponse (issue, data) {
+ onNewIssueResponse(issue, data) {
issue.id = data.id;
issue.iid = data.iid;
issue.project = data.project;
diff --git a/app/assets/javascripts/boards/services/board_service.js b/app/assets/javascripts/boards/services/board_service.js
index 029b0971f2c..3de6eb056c2 100644
--- a/app/assets/javascripts/boards/services/board_service.js
+++ b/app/assets/javascripts/boards/services/board_service.js
@@ -19,7 +19,9 @@ export default class BoardService {
}
static generateIssuePath(boardId, id) {
- return `${gon.relative_url_root}/-/boards/${boardId ? `${boardId}` : ''}/issues${id ? `/${id}` : ''}`;
+ return `${gon.relative_url_root}/-/boards/${boardId ? `${boardId}` : ''}/issues${
+ id ? `/${id}` : ''
+ }`;
}
all() {
@@ -54,7 +56,9 @@ export default class BoardService {
getIssuesForList(id, filter = {}) {
const data = { id };
- Object.keys(filter).forEach((key) => { data[key] = filter[key]; });
+ Object.keys(filter).forEach(key => {
+ data[key] = filter[key];
+ });
return axios.get(mergeUrlParams(data, this.generateIssuesPath(id)));
}
@@ -75,7 +79,9 @@ export default class BoardService {
}
getBacklog(data) {
- return axios.get(mergeUrlParams(data, `${gon.relative_url_root}/-/boards/${this.boardId}/issues.json`));
+ return axios.get(
+ mergeUrlParams(data, `${gon.relative_url_root}/-/boards/${this.boardId}/issues.json`),
+ );
}
bulkUpdate(issueIds, extraData = {}) {
diff --git a/app/assets/javascripts/boards/stores/boards_store.js b/app/assets/javascripts/boards/stores/boards_store.js
index 471955747fd..eefe14a1d79 100644
--- a/app/assets/javascripts/boards/stores/boards_store.js
+++ b/app/assets/javascripts/boards/stores/boards_store.js
@@ -20,20 +20,20 @@ const boardsStore = {
issue: {},
list: {},
},
- create () {
+ create() {
this.state.lists = [];
this.filter.path = getUrlParamsArray().join('&');
this.detail = {
issue: {},
};
},
- addList (listObj, defaultAvatar) {
+ addList(listObj, defaultAvatar) {
const list = new List(listObj, defaultAvatar);
this.state.lists.push(list);
return list;
},
- new (listObj) {
+ new(listObj) {
const list = this.addList(listObj);
const backlogList = this.findList('type', 'backlog', 'backlog');
@@ -50,44 +50,44 @@ const boardsStore = {
});
this.removeBlankState();
},
- updateNewListDropdown (listId) {
+ updateNewListDropdown(listId) {
$(`.js-board-list-${listId}`).removeClass('is-active');
},
- shouldAddBlankState () {
+ shouldAddBlankState() {
// Decide whether to add the blank state
- return !(this.state.lists.filter(list => list.type !== 'backlog' && list.type !== 'closed')[0]);
+ return !this.state.lists.filter(list => list.type !== 'backlog' && list.type !== 'closed')[0];
},
- addBlankState () {
+ addBlankState() {
if (!this.shouldAddBlankState() || this.welcomeIsHidden() || this.disabled) return;
this.addList({
id: 'blank',
list_type: 'blank',
title: 'Welcome to your Issue Board!',
- position: 0
+ position: 0,
});
this.state.lists = _.sortBy(this.state.lists, 'position');
},
- removeBlankState () {
+ removeBlankState() {
this.removeList('blank');
Cookies.set('issue_board_welcome_hidden', 'true', {
expires: 365 * 10,
- path: ''
+ path: '',
});
},
- welcomeIsHidden () {
+ welcomeIsHidden() {
return Cookies.get('issue_board_welcome_hidden') === 'true';
},
- removeList (id, type = 'blank') {
+ removeList(id, type = 'blank') {
const list = this.findList('id', id, type);
if (!list) return;
this.state.lists = this.state.lists.filter(list => list.id !== id);
},
- moveList (listFrom, orderLists) {
+ moveList(listFrom, orderLists) {
orderLists.forEach((id, i) => {
const list = this.findList('id', parseInt(id, 10));
@@ -95,22 +95,25 @@ const boardsStore = {
});
listFrom.update();
},
- moveIssueToList (listFrom, listTo, issue, newIndex) {
+ moveIssueToList(listFrom, listTo, issue, newIndex) {
const issueTo = listTo.findIssue(issue.id);
const issueLists = issue.getLists();
const listLabels = issueLists.map(listIssue => listIssue.label);
if (!issueTo) {
// Check if target list assignee is already present in this issue
- if ((listTo.type === 'assignee' && listFrom.type === 'assignee') &&
- issue.findAssignee(listTo.assignee)) {
+ if (
+ listTo.type === 'assignee' &&
+ listFrom.type === 'assignee' &&
+ issue.findAssignee(listTo.assignee)
+ ) {
const targetIssue = listTo.findIssue(issue.id);
targetIssue.removeAssignee(listFrom.assignee);
} else if (listTo.type === 'milestone') {
const currentMilestone = issue.milestone;
const currentLists = this.state.lists
- .filter(list => (list.type === 'milestone' && list.id !== listTo.id))
- .filter(list => list.issues.some(listIssue => issue.id === listIssue.id));
+ .filter(list => list.type === 'milestone' && list.id !== listTo.id)
+ .filter(list => list.issues.some(listIssue => issue.id === listIssue.id));
issue.removeMilestone(currentMilestone);
issue.addMilestone(listTo.milestone);
@@ -126,7 +129,7 @@ const boardsStore = {
}
if (listTo.type === 'closed' && listFrom.type !== 'backlog') {
- issueLists.forEach((list) => {
+ issueLists.forEach(list => {
list.removeIssue(issue);
});
issue.removeLabels(listLabels);
@@ -144,26 +147,28 @@ const boardsStore = {
return (
(listTo.type !== 'label' && listFrom.type === 'assignee') ||
(listTo.type !== 'assignee' && listFrom.type === 'label') ||
- (listFrom.type === 'backlog')
+ listFrom.type === 'backlog'
);
},
- moveIssueInList (list, issue, oldIndex, newIndex, idArray) {
+ moveIssueInList(list, issue, oldIndex, newIndex, idArray) {
const beforeId = parseInt(idArray[newIndex - 1], 10) || null;
const afterId = parseInt(idArray[newIndex + 1], 10) || null;
list.moveIssue(issue, oldIndex, newIndex, beforeId, afterId);
},
- findList (key, val, type = 'label') {
- const filteredList = this.state.lists.filter((list) => {
- const byType = type ? (list.type === type) || (list.type === 'assignee') || (list.type === 'milestone') : true;
+ findList(key, val, type = 'label') {
+ const filteredList = this.state.lists.filter(list => {
+ const byType = type
+ ? list.type === type || list.type === 'assignee' || list.type === 'milestone'
+ : true;
return list[key] === val && byType;
});
return filteredList[0];
},
- updateFiltersUrl () {
+ updateFiltersUrl() {
window.history.pushState(null, null, `?${this.filter.path}`);
- }
+ },
};
// hacks added in order to allow milestone_select to function properly
diff --git a/app/assets/javascripts/boards/stores/modal_store.js b/app/assets/javascripts/boards/stores/modal_store.js
index 0d9ac367a70..b7228bf7bf5 100644
--- a/app/assets/javascripts/boards/stores/modal_store.js
+++ b/app/assets/javascripts/boards/stores/modal_store.js
@@ -40,7 +40,7 @@ class ModalStore {
toggleAll() {
const select = this.selectedCount() !== this.store.issues.length;
- this.store.issues.forEach((issue) => {
+ this.store.issues.forEach(issue => {
const issueUpdate = issue;
if (issueUpdate.selected !== select) {
@@ -69,13 +69,14 @@ class ModalStore {
removeSelectedIssue(issue, forcePurge = false) {
if (this.store.activeTab === 'all' || forcePurge) {
- this.store.selectedIssues = this.store.selectedIssues
- .filter(fIssue => fIssue.id !== issue.id);
+ this.store.selectedIssues = this.store.selectedIssues.filter(
+ fIssue => fIssue.id !== issue.id,
+ );
}
}
purgeUnselectedIssues() {
- this.store.selectedIssues.forEach((issue) => {
+ this.store.selectedIssues.forEach(issue => {
if (!issue.selected) {
this.removeSelectedIssue(issue, true);
}
@@ -87,8 +88,7 @@ class ModalStore {
}
findSelectedIssue(issue) {
- return this.store.selectedIssues
- .filter(filteredIssue => filteredIssue.id === issue.id)[0];
+ return this.store.selectedIssues.filter(filteredIssue => filteredIssue.id === issue.id)[0];
}
}
diff --git a/app/assets/javascripts/clusters/components/applications.vue b/app/assets/javascripts/clusters/components/applications.vue
index 6e7b5eb5526..6d7f45a35d8 100644
--- a/app/assets/javascripts/clusters/components/applications.vue
+++ b/app/assets/javascripts/clusters/components/applications.vue
@@ -1,6 +1,6 @@
<script>
import _ from 'underscore';
-import helmInstallIllustration from '@gitlab-org/gitlab-svgs/dist/illustrations/kubernetes-installation.svg';
+import helmInstallIllustration from '@gitlab/svgs/dist/illustrations/kubernetes-installation.svg';
import elasticsearchLogo from 'images/cluster_app_logos/elasticsearch.png';
import gitlabLogo from 'images/cluster_app_logos/gitlab.png';
import helmLogo from 'images/cluster_app_logos/helm.png';
diff --git a/app/assets/javascripts/commons/gitlab_ui.js b/app/assets/javascripts/commons/gitlab_ui.js
index ba43832aa55..82a191d056b 100644
--- a/app/assets/javascripts/commons/gitlab_ui.js
+++ b/app/assets/javascripts/commons/gitlab_ui.js
@@ -1,8 +1,5 @@
import Vue from 'vue';
-import {
- GlLoadingIcon,
- GlTooltipDirective,
-} from '@gitlab-org/gitlab-ui';
+import { GlLoadingIcon, GlTooltipDirective } from '@gitlab-org/gitlab-ui';
Vue.component('gl-loading-icon', GlLoadingIcon);
diff --git a/app/assets/javascripts/diffs/components/tree_list.vue b/app/assets/javascripts/diffs/components/tree_list.vue
index 96e7bd63183..91052b303a6 100644
--- a/app/assets/javascripts/diffs/components/tree_list.vue
+++ b/app/assets/javascripts/diffs/components/tree_list.vue
@@ -18,8 +18,8 @@ export default {
},
data() {
const treeListStored = localStorage.getItem(treeListStorageKey);
- const renderTreeList = treeListStored !== null ?
- convertPermissionToBoolean(treeListStored) : true;
+ const renderTreeList =
+ treeListStored !== null ? convertPermissionToBoolean(treeListStored) : true;
return {
search: '',
diff --git a/app/assets/javascripts/environments/components/environment_item.vue b/app/assets/javascripts/environments/components/environment_item.vue
index bb9c139727e..b62a5bb1940 100644
--- a/app/assets/javascripts/environments/components/environment_item.vue
+++ b/app/assets/javascripts/environments/components/environment_item.vue
@@ -15,7 +15,7 @@ import CommitComponent from '../../vue_shared/components/commit.vue';
import eventHub from '../event_hub';
/**
- * Envrionment Item Component
+ * Environment Item Component
*
* Renders a table row for each environment.
*/
@@ -60,7 +60,7 @@ export default {
computed: {
/**
- * Verifies if `last_deployment` key exists in the current Envrionment.
+ * Verifies if `last_deployment` key exists in the current Environment.
* This key is required to render most of the html - this method works has
* an helper.
*
diff --git a/app/assets/javascripts/environments/components/environments_app.vue b/app/assets/javascripts/environments/components/environments_app.vue
index e2ecf426e64..557b2062c64 100644
--- a/app/assets/javascripts/environments/components/environments_app.vue
+++ b/app/assets/javascripts/environments/components/environments_app.vue
@@ -1,94 +1,92 @@
<script>
- import Flash from '../../flash';
- import { s__ } from '../../locale';
- import emptyState from './empty_state.vue';
- import eventHub from '../event_hub';
- import environmentsMixin from '../mixins/environments_mixin';
- import CIPaginationMixin from '../../vue_shared/mixins/ci_pagination_api_mixin';
- import StopEnvironmentModal from './stop_environment_modal.vue';
+import Flash from '../../flash';
+import { s__ } from '../../locale';
+import emptyState from './empty_state.vue';
+import eventHub from '../event_hub';
+import environmentsMixin from '../mixins/environments_mixin';
+import CIPaginationMixin from '../../vue_shared/mixins/ci_pagination_api_mixin';
+import StopEnvironmentModal from './stop_environment_modal.vue';
- export default {
- components: {
- emptyState,
- StopEnvironmentModal,
- },
+export default {
+ components: {
+ emptyState,
+ StopEnvironmentModal,
+ },
- mixins: [
- CIPaginationMixin,
- environmentsMixin,
- ],
+ mixins: [CIPaginationMixin, environmentsMixin],
- props: {
- endpoint: {
- type: String,
- required: true,
- },
- canCreateEnvironment: {
- type: Boolean,
- required: true,
- },
- canCreateDeployment: {
- type: Boolean,
- required: true,
- },
- canReadEnvironment: {
- type: Boolean,
- required: true,
- },
- cssContainerClass: {
- type: String,
- required: true,
- },
- newEnvironmentPath: {
- type: String,
- required: true,
- },
- helpPagePath: {
- type: String,
- required: true,
- },
+ props: {
+ endpoint: {
+ type: String,
+ required: true,
},
-
- created() {
- eventHub.$on('toggleFolder', this.toggleFolder);
+ canCreateEnvironment: {
+ type: Boolean,
+ required: true,
},
-
- beforeDestroy() {
- eventHub.$off('toggleFolder');
+ canCreateDeployment: {
+ type: Boolean,
+ required: true,
+ },
+ canReadEnvironment: {
+ type: Boolean,
+ required: true,
+ },
+ cssContainerClass: {
+ type: String,
+ required: true,
+ },
+ newEnvironmentPath: {
+ type: String,
+ required: true,
},
+ helpPagePath: {
+ type: String,
+ required: true,
+ },
+ },
+
+ created() {
+ eventHub.$on('toggleFolder', this.toggleFolder);
+ },
- methods: {
- toggleFolder(folder) {
- this.store.toggleFolder(folder);
+ beforeDestroy() {
+ eventHub.$off('toggleFolder');
+ },
- if (!folder.isOpen) {
- this.fetchChildEnvironments(folder, true);
- }
- },
+ methods: {
+ toggleFolder(folder) {
+ this.store.toggleFolder(folder);
- fetchChildEnvironments(folder, showLoader = false) {
- this.store.updateEnvironmentProp(folder, 'isLoadingFolderContent', showLoader);
+ if (!folder.isOpen) {
+ this.fetchChildEnvironments(folder, true);
+ }
+ },
- this.service.getFolderContent(folder.folder_path)
- .then(response => this.store.setfolderContent(folder, response.data.environments))
- .then(() => this.store.updateEnvironmentProp(folder, 'isLoadingFolderContent', false))
- .catch(() => {
- Flash(s__('Environments|An error occurred while fetching the environments.'));
- this.store.updateEnvironmentProp(folder, 'isLoadingFolderContent', false);
- });
- },
+ fetchChildEnvironments(folder, showLoader = false) {
+ this.store.updateEnvironmentProp(folder, 'isLoadingFolderContent', showLoader);
+
+ this.service
+ .getFolderContent(folder.folder_path)
+ .then(response => this.store.setfolderContent(folder, response.data.environments))
+ .then(() => this.store.updateEnvironmentProp(folder, 'isLoadingFolderContent', false))
+ .catch(() => {
+ Flash(s__('Environments|An error occurred while fetching the environments.'));
+ this.store.updateEnvironmentProp(folder, 'isLoadingFolderContent', false);
+ });
+ },
- successCallback(resp) {
- this.saveData(resp);
+ successCallback(resp) {
+ this.saveData(resp);
- // We need to verify if any folder is open to also update it
- const openFolders = this.store.getOpenFolders();
- if (openFolders.length) {
- openFolders.forEach(folder => this.fetchChildEnvironments(folder));
- }
- },
+ // We need to verify if any folder is open to also update it
+ const openFolders = this.store.getOpenFolders();
+ if (openFolders.length) {
+ openFolders.forEach(folder => this.fetchChildEnvironments(folder));
+ }
},
- };
+ },
+};
</script>
<template>
<div :class="cssContainerClass">
diff --git a/app/assets/javascripts/environments/stores/environments_store.js b/app/assets/javascripts/environments/stores/environments_store.js
index 5ce9225a4bb..5808a2d4afa 100644
--- a/app/assets/javascripts/environments/stores/environments_store.js
+++ b/app/assets/javascripts/environments/stores/environments_store.js
@@ -34,14 +34,14 @@ export default class EnvironmentsStore {
* @returns {Array}
*/
storeEnvironments(environments = []) {
- const filteredEnvironments = environments.map((env) => {
- const oldEnvironmentState = this.state.environments
- .find((element) => {
- if (env.latest) {
- return element.id === env.latest.id;
- }
- return element.id === env.id;
- }) || {};
+ const filteredEnvironments = environments.map(env => {
+ const oldEnvironmentState =
+ this.state.environments.find(element => {
+ if (env.latest) {
+ return element.id === env.latest.id;
+ }
+ return element.id === env.id;
+ }) || {};
let filtered = {};
@@ -101,11 +101,11 @@ export default class EnvironmentsStore {
}
/**
- * Toggles folder open property for the given folder.
- *
- * @param {Object} folder
- * @return {Array}
- */
+ * Toggles folder open property for the given folder.
+ *
+ * @param {Object} folder
+ * @return {Array}
+ */
toggleFolder(folder) {
return this.updateEnvironmentProp(folder, 'isOpen', !folder.isOpen);
}
@@ -119,7 +119,7 @@ export default class EnvironmentsStore {
* @return {Object}
*/
setfolderContent(folder, environments) {
- const updatedEnvironments = environments.map((env) => {
+ const updatedEnvironments = environments.map(env => {
let updated = env;
if (env.latest) {
@@ -148,7 +148,7 @@ export default class EnvironmentsStore {
updateEnvironmentProp(environment, prop, newValue) {
const { environments } = this.state;
- const updatedEnvironments = environments.map((env) => {
+ const updatedEnvironments = environments.map(env => {
const updateEnv = Object.assign({}, env);
if (env.id === environment.id) {
updateEnv[prop] = newValue;
diff --git a/app/assets/javascripts/filtered_search/dropdown_user.js b/app/assets/javascripts/filtered_search/dropdown_user.js
index d36f38a70b5..d5027590bb7 100644
--- a/app/assets/javascripts/filtered_search/dropdown_user.js
+++ b/app/assets/javascripts/filtered_search/dropdown_user.js
@@ -39,8 +39,9 @@ export default class DropdownUser extends FilteredSearchDropdown {
}
itemClicked(e) {
- super.itemClicked(e,
- selected => selected.querySelector('.dropdown-light-content').innerText.trim());
+ super.itemClicked(e, selected =>
+ selected.querySelector('.dropdown-light-content').innerText.trim(),
+ );
}
renderContent(forceShowList = false) {
@@ -68,7 +69,7 @@ export default class DropdownUser extends FilteredSearchDropdown {
// Removes the first character if it is a quotation so that we can search
// with multiple words
- if (value[0] === '"' || value[0] === '\'') {
+ if (value[0] === '"' || value[0] === "'") {
value = value.slice(1);
}
diff --git a/app/assets/javascripts/filtered_search/filtered_search_dropdown.js b/app/assets/javascripts/filtered_search/filtered_search_dropdown.js
index 4eb67ff7649..146d3ba963c 100644
--- a/app/assets/javascripts/filtered_search/filtered_search_dropdown.js
+++ b/app/assets/javascripts/filtered_search/filtered_search_dropdown.js
@@ -85,7 +85,7 @@ export default class FilteredSearchDropdown {
}
dispatchInputEvent() {
- // Propogate input change to FilteredSearchDropdownManager
+ // Propagate input change to FilteredSearchDropdownManager
// so that it can determine which dropdowns to open
this.input.dispatchEvent(
new CustomEvent('input', {
diff --git a/app/assets/javascripts/filtered_search/filtered_search_dropdown_manager.js b/app/assets/javascripts/filtered_search/filtered_search_dropdown_manager.js
index cd3d532c958..57ec6603d80 100644
--- a/app/assets/javascripts/filtered_search/filtered_search_dropdown_manager.js
+++ b/app/assets/javascripts/filtered_search/filtered_search_dropdown_manager.js
@@ -108,7 +108,7 @@ export default class FilteredSearchDropdownManager {
},
};
- supportedTokens.forEach((type) => {
+ supportedTokens.forEach(type => {
if (availableMappings[type]) {
allowedMappings[type] = availableMappings[type];
}
@@ -142,10 +142,7 @@ export default class FilteredSearchDropdownManager {
}
static addWordToInput(tokenName, tokenValue = '', clicked = false, options = {}) {
- const {
- uppercaseTokenName = false,
- capitalizeTokenValue = false,
- } = options;
+ const { uppercaseTokenName = false, capitalizeTokenValue = false } = options;
const input = FilteredSearchContainer.container.querySelector('.filtered-search');
FilteredSearchVisualTokens.addFilterVisualToken(tokenName, tokenValue, {
uppercaseTokenName,
@@ -164,13 +161,16 @@ export default class FilteredSearchDropdownManager {
updateDropdownOffset(key) {
// Always align dropdown with the input field
- let offset = this.filteredSearchInput.getBoundingClientRect().left - this.container.querySelector('.scroll-container').getBoundingClientRect().left;
+ let offset =
+ this.filteredSearchInput.getBoundingClientRect().left -
+ this.container.querySelector('.scroll-container').getBoundingClientRect().left;
const maxInputWidth = 240;
const currentDropdownWidth = this.mapping[key].element.clientWidth || maxInputWidth;
// Make sure offset never exceeds the input container
- const offsetMaxWidth = this.container.querySelector('.scroll-container').clientWidth - currentDropdownWidth;
+ const offsetMaxWidth =
+ this.container.querySelector('.scroll-container').clientWidth - currentDropdownWidth;
if (offsetMaxWidth < offset) {
offset = offsetMaxWidth;
}
@@ -196,8 +196,7 @@ export default class FilteredSearchDropdownManager {
const glArguments = Object.assign({}, defaultArguments, extraArguments);
// Passing glArguments to `new glClass(<arguments>)`
- mappingKey.reference =
- new (Function.prototype.bind.apply(glClass, [null, glArguments]))();
+ mappingKey.reference = new (Function.prototype.bind.apply(glClass, [null, glArguments]))();
}
if (firstLoad) {
@@ -224,8 +223,8 @@ export default class FilteredSearchDropdownManager {
}
const match = this.filteredSearchTokenKeys.searchByKey(dropdownName.toLowerCase());
- const shouldOpenFilterDropdown = match && this.currentDropdown !== match.key
- && this.mapping[match.key];
+ const shouldOpenFilterDropdown =
+ match && this.currentDropdown !== match.key && this.mapping[match.key];
const shouldOpenHintDropdown = !match && this.currentDropdown !== 'hint';
if (shouldOpenFilterDropdown || shouldOpenHintDropdown) {
@@ -236,8 +235,10 @@ export default class FilteredSearchDropdownManager {
setDropdown() {
const query = DropdownUtils.getSearchQuery(true);
- const { lastToken, searchToken } =
- this.tokenizer.processTokens(query, this.filteredSearchTokenKeys.getKeys());
+ const { lastToken, searchToken } = this.tokenizer.processTokens(
+ query,
+ this.filteredSearchTokenKeys.getKeys(),
+ );
if (this.currentDropdown) {
this.updateCurrentDropdownOffset();
diff --git a/app/assets/javascripts/filtered_search/filtered_search_manager.js b/app/assets/javascripts/filtered_search/filtered_search_manager.js
index 54533ebb70d..4a2af02b40a 100644
--- a/app/assets/javascripts/filtered_search/filtered_search_manager.js
+++ b/app/assets/javascripts/filtered_search/filtered_search_manager.js
@@ -1,8 +1,5 @@
import _ from 'underscore';
-import {
- getParameterByName,
- getUrlParamsArray,
-} from '~/lib/utils/common_utils';
+import { getParameterByName, getUrlParamsArray } from '~/lib/utils/common_utils';
import IssuableFilteredSearchTokenKeys from '~/filtered_search/issuable_filtered_search_token_keys';
import { visitUrl } from '../lib/utils/url_utility';
import Flash from '../flash';
@@ -48,24 +45,28 @@ export default class FilteredSearchManager {
isLocalStorageAvailable: RecentSearchesService.isAvailable(),
allowedKeys: this.filteredSearchTokenKeys.getKeys(),
});
- this.searchHistoryDropdownElement = document.querySelector('.js-filtered-search-history-dropdown');
- const fullPath = this.searchHistoryDropdownElement ?
- this.searchHistoryDropdownElement.dataset.fullPath : 'project';
+ this.searchHistoryDropdownElement = document.querySelector(
+ '.js-filtered-search-history-dropdown',
+ );
+ const fullPath = this.searchHistoryDropdownElement
+ ? this.searchHistoryDropdownElement.dataset.fullPath
+ : 'project';
const recentSearchesKey = `${fullPath}-${this.recentsStorageKeyNames[this.page]}`;
this.recentSearchesService = new RecentSearchesService(recentSearchesKey);
}
setup() {
// Fetch recent searches from localStorage
- this.fetchingRecentSearchesPromise = this.recentSearchesService.fetch()
- .catch((error) => {
+ this.fetchingRecentSearchesPromise = this.recentSearchesService
+ .fetch()
+ .catch(error => {
if (error.name === 'RecentSearchesServiceError') return undefined;
// eslint-disable-next-line no-new
new Flash('An error occurred while parsing recent searches');
// Gracefully fail to empty array
return [];
})
- .then((searches) => {
+ .then(searches => {
if (!searches) {
return;
}
@@ -120,7 +121,7 @@ export default class FilteredSearchManager {
if (this.stateFilters) {
this.searchStateWrapper = this.searchState.bind(this);
- this.applyToStateFilters((filterEl) => {
+ this.applyToStateFilters(filterEl => {
filterEl.addEventListener('click', this.searchStateWrapper);
});
}
@@ -128,14 +129,14 @@ export default class FilteredSearchManager {
unbindStateEvents() {
if (this.stateFilters) {
- this.applyToStateFilters((filterEl) => {
+ this.applyToStateFilters(filterEl => {
filterEl.removeEventListener('click', this.searchStateWrapper);
});
}
}
applyToStateFilters(callback) {
- this.stateFilters.querySelectorAll('a[data-state]').forEach((filterEl) => {
+ this.stateFilters.querySelectorAll('a[data-state]').forEach(filterEl => {
if (this.states.indexOf(filterEl.dataset.state) > -1) {
callback(filterEl);
}
@@ -207,7 +208,7 @@ export default class FilteredSearchManager {
let backspaceCount = 0;
// closure for keeping track of the number of backspace keystrokes
- return (e) => {
+ return e => {
// 8 = Backspace Key
// 46 = Delete Key
if (e.keyCode === 8 || e.keyCode === 46) {
@@ -274,8 +275,12 @@ export default class FilteredSearchManager {
const isElementInDynamicFilterDropdown = e.target.closest('.filter-dropdown') !== null;
const isElementInStaticFilterDropdown = e.target.closest('ul[data-dropdown]') !== null;
- if (!isElementInFilteredSearch && !isElementInDynamicFilterDropdown &&
- !isElementInStaticFilterDropdown && inputContainer) {
+ if (
+ !isElementInFilteredSearch &&
+ !isElementInDynamicFilterDropdown &&
+ !isElementInStaticFilterDropdown &&
+ inputContainer
+ ) {
inputContainer.classList.remove('focus');
}
}
@@ -368,7 +373,7 @@ export default class FilteredSearchManager {
const removeElements = [];
- [].forEach.call(this.tokensContainer.children, (t) => {
+ [].forEach.call(this.tokensContainer.children, t => {
let canClearToken = t.classList.contains('js-visual-token');
if (canClearToken) {
@@ -381,7 +386,7 @@ export default class FilteredSearchManager {
}
});
- removeElements.forEach((el) => {
+ removeElements.forEach(el => {
el.parentElement.removeChild(el);
});
@@ -397,13 +402,14 @@ export default class FilteredSearchManager {
handleInputVisualToken() {
const input = this.filteredSearchInput;
- const { tokens, searchToken }
- = this.tokenizer.processTokens(input.value, this.filteredSearchTokenKeys.getKeys());
- const { isLastVisualTokenValid }
- = FilteredSearchVisualTokens.getLastVisualTokenBeforeInput();
+ const { tokens, searchToken } = this.tokenizer.processTokens(
+ input.value,
+ this.filteredSearchTokenKeys.getKeys(),
+ );
+ const { isLastVisualTokenValid } = FilteredSearchVisualTokens.getLastVisualTokenBeforeInput();
if (isLastVisualTokenValid) {
- tokens.forEach((t) => {
+ tokens.forEach(t => {
input.value = input.value.replace(`${t.key}:${t.symbol}${t.value}`, '');
FilteredSearchVisualTokens.addFilterVisualToken(t.key, `${t.symbol}${t.value}`, {
uppercaseTokenName: this.filteredSearchTokenKeys.shouldUppercaseTokenName(t.key),
@@ -453,15 +459,17 @@ export default class FilteredSearchManager {
saveCurrentSearchQuery() {
// Don't save before we have fetched the already saved searches
- this.fetchingRecentSearchesPromise.then(() => {
- const searchQuery = DropdownUtils.getSearchQuery();
- if (searchQuery.length > 0) {
- const resultantSearches = this.recentSearchesStore.addRecentSearch(searchQuery);
- this.recentSearchesService.save(resultantSearches);
- }
- }).catch(() => {
- // https://gitlab.com/gitlab-org/gitlab-ce/issues/30821
- });
+ this.fetchingRecentSearchesPromise
+ .then(() => {
+ const searchQuery = DropdownUtils.getSearchQuery();
+ if (searchQuery.length > 0) {
+ const resultantSearches = this.recentSearchesStore.addRecentSearch(searchQuery);
+ this.recentSearchesService.save(resultantSearches);
+ }
+ })
+ .catch(() => {
+ // https://gitlab.com/gitlab-org/gitlab-ce/issues/30821
+ });
}
// allows for modifying params array when a param can't be included in the URL (e.g. Service Desk)
@@ -475,7 +483,7 @@ export default class FilteredSearchManager {
const usernameParams = this.getUsernameParams();
let hasFilteredSearch = false;
- params.forEach((p) => {
+ params.forEach(p => {
const split = p.split('=');
const keyParam = decodeURIComponent(split[0]);
const value = split[1];
@@ -486,11 +494,9 @@ export default class FilteredSearchManager {
if (condition) {
hasFilteredSearch = true;
const canEdit = this.canEdit && this.canEdit(condition.tokenKey);
- FilteredSearchVisualTokens.addFilterVisualToken(
- condition.tokenKey,
- condition.value,
- { canEdit },
- );
+ FilteredSearchVisualTokens.addFilterVisualToken(condition.tokenKey, condition.value, {
+ canEdit,
+ });
} else {
// Sanitize value since URL converts spaces into +
// Replace before decode so that we know what was originally + versus the encoded +
@@ -510,7 +516,7 @@ export default class FilteredSearchManager {
if (sanitizedValue.indexOf(' ') !== -1) {
// Prefer ", but use ' if required
- quotationsToUse = sanitizedValue.indexOf('"') === -1 ? '"' : '\'';
+ quotationsToUse = sanitizedValue.indexOf('"') === -1 ? '"' : "'";
}
hasFilteredSearch = true;
@@ -531,7 +537,9 @@ export default class FilteredSearchManager {
hasFilteredSearch = true;
const tokenName = 'assignee';
const canEdit = this.canEdit && this.canEdit(tokenName);
- FilteredSearchVisualTokens.addFilterVisualToken(tokenName, `@${usernameParams[id]}`, { canEdit });
+ FilteredSearchVisualTokens.addFilterVisualToken(tokenName, `@${usernameParams[id]}`, {
+ canEdit,
+ });
}
} else if (!match && keyParam === 'author_id') {
const id = parseInt(value, 10);
@@ -539,7 +547,9 @@ export default class FilteredSearchManager {
hasFilteredSearch = true;
const tokenName = 'author';
const canEdit = this.canEdit && this.canEdit(tokenName);
- FilteredSearchVisualTokens.addFilterVisualToken(tokenName, `@${usernameParams[id]}`, { canEdit });
+ FilteredSearchVisualTokens.addFilterVisualToken(tokenName, `@${usernameParams[id]}`, {
+ canEdit,
+ });
}
} else if (!match && keyParam === 'search') {
hasFilteredSearch = true;
@@ -580,9 +590,11 @@ export default class FilteredSearchManager {
const currentState = state || getParameterByName('state') || 'opened';
paths.push(`state=${currentState}`);
- tokens.forEach((token) => {
- const condition = this.filteredSearchTokenKeys
- .searchByConditionKeyValue(token.key, token.value.toLowerCase());
+ tokens.forEach(token => {
+ const condition = this.filteredSearchTokenKeys.searchByConditionKeyValue(
+ token.key,
+ token.value.toLowerCase(),
+ );
const tokenConfig = this.filteredSearchTokenKeys.searchByKey(token.key) || {};
const { param } = tokenConfig;
@@ -601,8 +613,10 @@ export default class FilteredSearchManager {
tokenValue = tokenValue.toLowerCase();
}
- if ((tokenValue[0] === '\'' && tokenValue[tokenValue.length - 1] === '\'') ||
- (tokenValue[0] === '"' && tokenValue[tokenValue.length - 1] === '"')) {
+ if (
+ (tokenValue[0] === "'" && tokenValue[tokenValue.length - 1] === "'") ||
+ (tokenValue[0] === '"' && tokenValue[tokenValue.length - 1] === '"')
+ ) {
tokenValue = tokenValue.slice(1, tokenValue.length - 1);
}
@@ -613,7 +627,10 @@ export default class FilteredSearchManager {
});
if (searchToken) {
- const sanitized = searchToken.split(' ').map(t => encodeURIComponent(t)).join('+');
+ const sanitized = searchToken
+ .split(' ')
+ .map(t => encodeURIComponent(t))
+ .join('+');
paths.push(`search=${sanitized}`);
}
@@ -630,7 +647,7 @@ export default class FilteredSearchManager {
const usernamesById = {};
try {
const attribute = this.filteredSearchInput.getAttribute('data-username-params');
- JSON.parse(attribute).forEach((user) => {
+ JSON.parse(attribute).forEach(user => {
usernamesById[user.id] = user.username;
});
} catch (e) {
diff --git a/app/assets/javascripts/filtered_search/issuable_filtered_search_token_keys.js b/app/assets/javascripts/filtered_search/issuable_filtered_search_token_keys.js
index e22f542b7bf..bb0ecb8efe7 100644
--- a/app/assets/javascripts/filtered_search/issuable_filtered_search_token_keys.js
+++ b/app/assets/javascripts/filtered_search/issuable_filtered_search_token_keys.js
@@ -68,12 +68,12 @@ export const conditions = [
value: 'any',
},
{
- url: 'milestone_title=No+Milestone',
+ url: 'milestone_title=None',
tokenKey: 'milestone',
value: 'none',
},
{
- url: 'milestone_title=Any+Milestone',
+ url: 'milestone_title=Any',
tokenKey: 'milestone',
value: 'any',
},
@@ -92,6 +92,16 @@ export const conditions = [
tokenKey: 'label',
value: 'none',
},
+ {
+ url: 'my_reaction_emoji=None',
+ tokenKey: 'my-reaction',
+ value: 'none',
+ },
+ {
+ url: 'my_reaction_emoji=Any',
+ tokenKey: 'my-reaction',
+ value: 'any',
+ },
];
const IssuableFilteredSearchTokenKeys = new FilteredSearchTokenKeys(
diff --git a/app/assets/javascripts/flash.js b/app/assets/javascripts/flash.js
index 749c09f897c..c2397842125 100644
--- a/app/assets/javascripts/flash.js
+++ b/app/assets/javascripts/flash.js
@@ -40,7 +40,9 @@ const createFlashEl = (message, type, isFixedLayout = false) => `
class="flash-${type}"
>
<div
- class="flash-text ${isFixedLayout ? 'container-fluid container-limited limit-container-width' : ''}"
+ class="flash-text ${
+ isFixedLayout ? 'container-fluid container-limited limit-container-width' : ''
+ }"
>
${_.escape(message)}
</div>
@@ -78,7 +80,9 @@ const createFlash = function createFlash(
if (!flashContainer) return null;
- const isFixedLayout = navigation ? navigation.parentNode.classList.contains('container-limited') : true;
+ const isFixedLayout = navigation
+ ? navigation.parentNode.classList.contains('container-limited')
+ : true;
flashContainer.innerHTML = createFlashEl(message, type, isFixedLayout);
diff --git a/app/assets/javascripts/gfm_auto_complete.js b/app/assets/javascripts/gfm_auto_complete.js
index 7dd0efd622d..00b3d283570 100644
--- a/app/assets/javascripts/gfm_auto_complete.js
+++ b/app/assets/javascripts/gfm_auto_complete.js
@@ -94,7 +94,7 @@ class GfmAutoComplete {
...this.getDefaultCallbacks(),
beforeSave(commands) {
if (GfmAutoComplete.isLoading(commands)) return commands;
- return $.map(commands, (c) => {
+ return $.map(commands, c => {
let search = c.name;
if (c.aliases.length > 0) {
search = `${search} ${c.aliases.join(' ')}`;
@@ -167,7 +167,7 @@ class GfmAutoComplete {
callbacks: {
...this.getDefaultCallbacks(),
beforeSave(members) {
- return $.map(members, (m) => {
+ return $.map(members, m => {
let title = '';
if (m.username == null) {
return m;
@@ -178,7 +178,9 @@ class GfmAutoComplete {
}
const autoCompleteAvatar = m.avatar_url || m.username.charAt(0).toUpperCase();
- const imgAvatar = `<img src="${m.avatar_url}" alt="${m.username}" class="avatar avatar-inline center s26"/>`;
+ const imgAvatar = `<img src="${m.avatar_url}" alt="${
+ m.username
+ }" class="avatar avatar-inline center s26"/>`;
const txtAvatar = `<div class="avatar center avatar-inline s26">${autoCompleteAvatar}</div>`;
return {
@@ -211,7 +213,7 @@ class GfmAutoComplete {
callbacks: {
...this.getDefaultCallbacks(),
beforeSave(issues) {
- return $.map(issues, (i) => {
+ return $.map(issues, i => {
if (i.title == null) {
return i;
}
@@ -244,7 +246,7 @@ class GfmAutoComplete {
callbacks: {
...this.getDefaultCallbacks(),
beforeSave(milestones) {
- return $.map(milestones, (m) => {
+ return $.map(milestones, m => {
if (m.title == null) {
return m;
}
@@ -277,7 +279,7 @@ class GfmAutoComplete {
callbacks: {
...this.getDefaultCallbacks(),
beforeSave(merges) {
- return $.map(merges, (m) => {
+ return $.map(merges, m => {
if (m.title == null) {
return m;
}
@@ -324,13 +326,20 @@ class GfmAutoComplete {
},
matcher(flag, subtext) {
const match = GfmAutoComplete.defaultMatcher(flag, subtext, this.app.controllers);
- const subtextNodes = subtext.split(/\n+/g).pop().split(GfmAutoComplete.regexSubtext);
+ const subtextNodes = subtext
+ .split(/\n+/g)
+ .pop()
+ .split(GfmAutoComplete.regexSubtext);
// Check if ~ is followed by '/label', '/relabel' or '/unlabel' commands.
- command = subtextNodes.find((node) => {
- if (node === LABEL_COMMAND.LABEL ||
- node === LABEL_COMMAND.RELABEL ||
- node === LABEL_COMMAND.UNLABEL) { return node; }
+ command = subtextNodes.find(node => {
+ if (
+ node === LABEL_COMMAND.LABEL ||
+ node === LABEL_COMMAND.RELABEL ||
+ node === LABEL_COMMAND.UNLABEL
+ ) {
+ return node;
+ }
return null;
});
@@ -380,7 +389,7 @@ class GfmAutoComplete {
callbacks: {
...this.getDefaultCallbacks(),
beforeSave(snippets) {
- return $.map(snippets, (m) => {
+ return $.map(snippets, m => {
if (m.title == null) {
return m;
}
@@ -458,13 +467,17 @@ class GfmAutoComplete {
this.loadData($input, at, validEmojiNames);
GfmAutoComplete.glEmojiTag = glEmojiTag;
})
- .catch(() => { this.isLoadingData[at] = false; });
+ .catch(() => {
+ this.isLoadingData[at] = false;
+ });
} else if (dataSource) {
AjaxCache.retrieve(dataSource, true)
- .then((data) => {
+ .then(data => {
this.loadData($input, at, data);
})
- .catch(() => { this.isLoadingData[at] = false; });
+ .catch(() => {
+ this.isLoadingData[at] = false;
+ });
} else {
this.isLoadingData[at] = false;
}
@@ -497,15 +510,16 @@ class GfmAutoComplete {
}
const loadingState = GfmAutoComplete.defaultLoadingData[0];
- return dataToInspect &&
- (dataToInspect === loadingState || dataToInspect.name === loadingState);
+ return dataToInspect && (dataToInspect === loadingState || dataToInspect.name === loadingState);
}
static defaultMatcher(flag, subtext, controllers) {
// The below is taken from At.js source
// Tweaked to commands to start without a space only if char before is a non-word character
// https://github.com/ichord/At.js
- const atSymbolsWithBar = Object.keys(controllers).join('|').replace(/[$]/, '\\$&');
+ const atSymbolsWithBar = Object.keys(controllers)
+ .join('|')
+ .replace(/[$]/, '\\$&');
const atSymbolsWithoutBar = Object.keys(controllers).join('');
const targetSubtext = subtext.split(GfmAutoComplete.regexSubtext).pop();
const resultantFlag = flag.replace(/[-[\]/{}()*+?.\\^$|]/g, '\\$&');
@@ -513,7 +527,10 @@ class GfmAutoComplete {
const accentAChar = decodeURI('%C3%80');
const accentYChar = decodeURI('%C3%BF');
- const regexp = new RegExp(`^(?:\\B|[^a-zA-Z0-9_\`${atSymbolsWithoutBar}]|\\s)${resultantFlag}(?!${atSymbolsWithBar})((?:[A-Za-z${accentAChar}-${accentYChar}0-9_'.+-]|[^\\x00-\\x7a])*)$`, 'gi');
+ const regexp = new RegExp(
+ `^(?:\\B|[^a-zA-Z0-9_\`${atSymbolsWithoutBar}]|\\s)${resultantFlag}(?!${atSymbolsWithBar})((?:[A-Za-z${accentAChar}-${accentYChar}0-9_'.+-]|[^\\x00-\\x7a])*)$`,
+ 'gi',
+ );
return regexp.exec(targetSubtext);
}
@@ -552,8 +569,9 @@ GfmAutoComplete.Members = {
template: '<li>${avatarTag} ${username} <small>${title}</small></li>',
};
GfmAutoComplete.Labels = {
- // eslint-disable-next-line no-template-curly-in-string
- template: '<li><span class="dropdown-label-box" style="background: ${color}"></span> ${title}</li>',
+ template:
+ // eslint-disable-next-line no-template-curly-in-string
+ '<li><span class="dropdown-label-box" style="background: ${color}"></span> ${title}</li>',
};
// Issues, MergeRequests and Snippets
GfmAutoComplete.Issues = {
@@ -567,7 +585,8 @@ GfmAutoComplete.Milestones = {
template: '<li>${title}</li>',
};
GfmAutoComplete.Loading = {
- template: '<li style="pointer-events: none;"><i class="fa fa-spinner fa-spin"></i> Loading...</li>',
+ template:
+ '<li style="pointer-events: none;"><i class="fa fa-spinner fa-spin"></i> Loading...</li>',
};
export default GfmAutoComplete;
diff --git a/app/assets/javascripts/gl_field_errors.js b/app/assets/javascripts/gl_field_errors.js
index 3764e7ab422..d5d5954ce6a 100644
--- a/app/assets/javascripts/gl_field_errors.js
+++ b/app/assets/javascripts/gl_field_errors.js
@@ -28,7 +28,7 @@ export default class GlFieldErrors {
this.form.on('submit', GlFieldErrors.catchInvalidFormSubmit);
}
- /* Neccessary to prevent intercept and override invalid form submit
+ /* Necessary to prevent intercept and override invalid form submit
* because Safari & iOS quietly allow form submission when form is invalid
* and prevents disabling of invalid submit button by application.js */
diff --git a/app/assets/javascripts/jobs/components/job_app.vue b/app/assets/javascripts/jobs/components/job_app.vue
index ac19034f69d..3cabbfc6e27 100644
--- a/app/assets/javascripts/jobs/components/job_app.vue
+++ b/app/assets/javascripts/jobs/components/job_app.vue
@@ -1,164 +1,164 @@
<script>
- import _ from 'underscore';
- import { mapGetters, mapState, mapActions } from 'vuex';
- import { isScrolledToBottom } from '~/lib/utils/scroll_utils';
- import bp from '~/breakpoints';
- import CiHeader from '~/vue_shared/components/header_ci_component.vue';
- import Callout from '~/vue_shared/components/callout.vue';
- import createStore from '../store';
- import EmptyState from './empty_state.vue';
- import EnvironmentsBlock from './environments_block.vue';
- import ErasedBlock from './erased_block.vue';
- import Log from './job_log.vue';
- import LogTopBar from './job_log_controllers.vue';
- import StuckBlock from './stuck_block.vue';
- import Sidebar from './sidebar.vue';
+import _ from 'underscore';
+import { mapGetters, mapState, mapActions } from 'vuex';
+import { isScrolledToBottom } from '~/lib/utils/scroll_utils';
+import bp from '~/breakpoints';
+import CiHeader from '~/vue_shared/components/header_ci_component.vue';
+import Callout from '~/vue_shared/components/callout.vue';
+import createStore from '../store';
+import EmptyState from './empty_state.vue';
+import EnvironmentsBlock from './environments_block.vue';
+import ErasedBlock from './erased_block.vue';
+import Log from './job_log.vue';
+import LogTopBar from './job_log_controllers.vue';
+import StuckBlock from './stuck_block.vue';
+import Sidebar from './sidebar.vue';
- export default {
- name: 'JobPageApp',
- store: createStore(),
- components: {
- CiHeader,
- Callout,
- EmptyState,
- EnvironmentsBlock,
- ErasedBlock,
- Log,
- LogTopBar,
- StuckBlock,
- Sidebar,
+export default {
+ name: 'JobPageApp',
+ store: createStore(),
+ components: {
+ CiHeader,
+ Callout,
+ EmptyState,
+ EnvironmentsBlock,
+ ErasedBlock,
+ Log,
+ LogTopBar,
+ StuckBlock,
+ Sidebar,
+ },
+ props: {
+ runnerSettingsUrl: {
+ type: String,
+ required: false,
+ default: null,
},
- props: {
- runnerSettingsUrl: {
- type: String,
- required: false,
- default: null,
- },
- runnerHelpUrl: {
- type: String,
- required: false,
- default: null,
- },
- endpoint: {
- type: String,
- required: true,
- },
- terminalPath: {
- type: String,
- required: false,
- default: null,
- },
- pagePath: {
- type: String,
- required: true,
- },
- logState: {
- type: String,
- required: true,
- },
+ runnerHelpUrl: {
+ type: String,
+ required: false,
+ default: null,
},
- computed: {
- ...mapState([
- 'isLoading',
- 'job',
- 'isSidebarOpen',
- 'trace',
- 'isTraceComplete',
- 'traceSize',
- 'isTraceSizeVisible',
- 'isScrollBottomDisabled',
- 'isScrollTopDisabled',
- 'isScrolledToBottomBeforeReceivingTrace',
- 'hasError',
- ]),
- ...mapGetters([
- 'headerActions',
- 'headerTime',
- 'shouldRenderCalloutMessage',
- 'shouldRenderTriggeredLabel',
- 'hasEnvironment',
- 'hasTrace',
- 'emptyStateIllustration',
- 'isScrollingDown',
- 'emptyStateAction',
- 'hasRunnersForProject',
- ]),
+ endpoint: {
+ type: String,
+ required: true,
+ },
+ terminalPath: {
+ type: String,
+ required: false,
+ default: null,
+ },
+ pagePath: {
+ type: String,
+ required: true,
+ },
+ logState: {
+ type: String,
+ required: true,
+ },
+ },
+ computed: {
+ ...mapState([
+ 'isLoading',
+ 'job',
+ 'isSidebarOpen',
+ 'trace',
+ 'isTraceComplete',
+ 'traceSize',
+ 'isTraceSizeVisible',
+ 'isScrollBottomDisabled',
+ 'isScrollTopDisabled',
+ 'isScrolledToBottomBeforeReceivingTrace',
+ 'hasError',
+ ]),
+ ...mapGetters([
+ 'headerActions',
+ 'headerTime',
+ 'shouldRenderCalloutMessage',
+ 'shouldRenderTriggeredLabel',
+ 'hasEnvironment',
+ 'hasTrace',
+ 'emptyStateIllustration',
+ 'isScrollingDown',
+ 'emptyStateAction',
+ 'hasRunnersForProject',
+ ]),
- shouldRenderContent() {
- return !this.isLoading && !this.hasError;
- }
+ shouldRenderContent() {
+ return !this.isLoading && !this.hasError;
},
- watch: {
- // Once the job log is loaded,
- // fetch the stages for the dropdown on the sidebar
- job(newVal, oldVal) {
- if (_.isEmpty(oldVal) && !_.isEmpty(newVal.pipeline)) {
- this.fetchStages();
- }
- },
+ },
+ watch: {
+ // Once the job log is loaded,
+ // fetch the stages for the dropdown on the sidebar
+ job(newVal, oldVal) {
+ if (_.isEmpty(oldVal) && !_.isEmpty(newVal.pipeline)) {
+ this.fetchStages();
+ }
},
- created() {
- this.throttled = _.throttle(this.toggleScrollButtons, 100);
+ },
+ created() {
+ this.throttled = _.throttle(this.toggleScrollButtons, 100);
- this.setJobEndpoint(this.endpoint);
- this.setTraceOptions({
- logState: this.logState,
- pagePath: this.pagePath,
- });
+ this.setJobEndpoint(this.endpoint);
+ this.setTraceOptions({
+ logState: this.logState,
+ pagePath: this.pagePath,
+ });
- this.fetchJob();
- this.fetchTrace();
+ this.fetchJob();
+ this.fetchTrace();
- window.addEventListener('resize', this.onResize);
- window.addEventListener('scroll', this.updateScroll);
- },
+ window.addEventListener('resize', this.onResize);
+ window.addEventListener('scroll', this.updateScroll);
+ },
- mounted() {
+ mounted() {
+ this.updateSidebar();
+ },
+
+ destroyed() {
+ window.removeEventListener('resize', this.onResize);
+ window.removeEventListener('scroll', this.updateScroll);
+ },
+
+ methods: {
+ ...mapActions([
+ 'setJobEndpoint',
+ 'setTraceOptions',
+ 'fetchJob',
+ 'fetchStages',
+ 'hideSidebar',
+ 'showSidebar',
+ 'toggleSidebar',
+ 'fetchTrace',
+ 'scrollBottom',
+ 'scrollTop',
+ 'toggleScrollButtons',
+ 'toggleScrollAnimation',
+ ]),
+ onResize() {
this.updateSidebar();
+ this.updateScroll();
},
-
- destroyed() {
- window.removeEventListener('resize', this.onResize);
- window.removeEventListener('scroll', this.updateScroll);
+ updateSidebar() {
+ if (bp.getBreakpointSize() === 'xs') {
+ this.hideSidebar();
+ } else if (!this.isSidebarOpen) {
+ this.showSidebar();
+ }
},
+ updateScroll() {
+ if (!isScrolledToBottom()) {
+ this.toggleScrollAnimation(false);
+ } else if (this.isScrollingDown) {
+ this.toggleScrollAnimation(true);
+ }
- methods: {
- ...mapActions([
- 'setJobEndpoint',
- 'setTraceOptions',
- 'fetchJob',
- 'fetchStages',
- 'hideSidebar',
- 'showSidebar',
- 'toggleSidebar',
- 'fetchTrace',
- 'scrollBottom',
- 'scrollTop',
- 'toggleScrollButtons',
- 'toggleScrollAnimation',
- ]),
- onResize() {
- this.updateSidebar();
- this.updateScroll();
- },
- updateSidebar() {
- if (bp.getBreakpointSize() === 'xs') {
- this.hideSidebar();
- } else if (!this.isSidebarOpen) {
- this.showSidebar();
- }
- },
- updateScroll() {
- if (!isScrolledToBottom()) {
- this.toggleScrollAnimation(false);
- } else if (this.isScrollingDown) {
- this.toggleScrollAnimation(true);
- }
-
- this.throttled();
- },
+ this.throttled();
},
- };
+ },
+};
</script>
<template>
<div>
diff --git a/app/assets/javascripts/jobs/components/job_log.vue b/app/assets/javascripts/jobs/components/job_log.vue
index ffa6ada3e28..92e20e92d66 100644
--- a/app/assets/javascripts/jobs/components/job_log.vue
+++ b/app/assets/javascripts/jobs/components/job_log.vue
@@ -1,45 +1,45 @@
<script>
- import { mapState, mapActions } from 'vuex';
+import { mapState, mapActions } from 'vuex';
- export default {
- name: 'JobLog',
- props: {
- trace: {
- type: String,
- required: true,
- },
- isComplete: {
- type: Boolean,
- required: true,
- },
+export default {
+ name: 'JobLog',
+ props: {
+ trace: {
+ type: String,
+ required: true,
},
- computed: {
- ...mapState(['isScrolledToBottomBeforeReceivingTrace']),
+ isComplete: {
+ type: Boolean,
+ required: true,
},
- updated() {
- this.$nextTick(() => this.handleScrollDown());
+ },
+ computed: {
+ ...mapState(['isScrolledToBottomBeforeReceivingTrace']),
+ },
+ updated() {
+ this.$nextTick(() => this.handleScrollDown());
+ },
+ mounted() {
+ this.$nextTick(() => this.handleScrollDown());
+ },
+ methods: {
+ ...mapActions(['scrollBottom']),
+ /**
+ * The job log is sent in HTML, which means we need to use `v-html` to render it
+ * Using the updated hook with $nextTick is not enough to wait for the DOM to be updated
+ * in this case because it runs before `v-html` has finished running, since there's no
+ * Vue binding.
+ * In order to scroll the page down after `v-html` has finished, we need to use setTimeout
+ */
+ handleScrollDown() {
+ if (this.isScrolledToBottomBeforeReceivingTrace) {
+ setTimeout(() => {
+ this.scrollBottom();
+ }, 0);
+ }
},
- mounted() {
- this.$nextTick(() => this.handleScrollDown());
- },
- methods: {
- ...mapActions(['scrollBottom']),
- /**
- * The job log is sent in HTML, which means we need to use `v-html` to render it
- * Using the updated hook with $nextTick is not enough to wait for the DOM to be updated
- * in this case because it runs before `v-html` has finished running, since there's no
- * Vue binding.
- * In order to scroll the page down after `v-html` has finished, we need to use setTimeout
- */
- handleScrollDown() {
- if (this.isScrolledToBottomBeforeReceivingTrace) {
- setTimeout(() => {
- this.scrollBottom();
- }, 0);
- }
- },
- },
- };
+ },
+};
</script>
<template>
<pre class="js-build-trace build-trace qa-build-trace">
diff --git a/app/assets/javascripts/jobs/index.js b/app/assets/javascripts/jobs/index.js
index ccd096a1da5..a32e945627c 100644
--- a/app/assets/javascripts/jobs/index.js
+++ b/app/assets/javascripts/jobs/index.js
@@ -23,4 +23,3 @@ export default () => {
},
});
};
-
diff --git a/app/assets/javascripts/jobs/store/getters.js b/app/assets/javascripts/jobs/store/getters.js
index 4de01f8e532..d440b2c9ef1 100644
--- a/app/assets/javascripts/jobs/store/getters.js
+++ b/app/assets/javascripts/jobs/store/getters.js
@@ -35,16 +35,19 @@ export const hasEnvironment = state => !_.isEmpty(state.job.deployment_status);
* Used to check if it should render the job log or the empty state
* @returns {Boolean}
*/
-export const hasTrace = state => state.job.has_trace || (!_.isEmpty(state.job.status) && state.job.status.group === 'running');
+export const hasTrace = state =>
+ state.job.has_trace || (!_.isEmpty(state.job.status) && state.job.status.group === 'running');
export const emptyStateIllustration = state =>
(state.job && state.job.status && state.job.status.illustration) || {};
-export const emptyStateAction = state => (state.job && state.job.status && state.job.status.action) || {};
+export const emptyStateAction = state =>
+ (state.job && state.job.status && state.job.status.action) || {};
export const isScrollingDown = state => isScrolledToBottom() && !state.isTraceComplete;
-export const hasRunnersForProject = state => state.job.runners.available && !state.job.runners.online;
+export const hasRunnersForProject = state =>
+ state.job.runners.available && !state.job.runners.online;
// prevent babel-plugin-rewire from generating an invalid default during karma tests
export default () => {};
diff --git a/app/assets/javascripts/labels_select.js b/app/assets/javascripts/labels_select.js
index 3c38d998b6c..5457604b3b9 100644
--- a/app/assets/javascripts/labels_select.js
+++ b/app/assets/javascripts/labels_select.js
@@ -25,7 +25,35 @@ export default class LabelsSelect {
}
$els.each(function(i, dropdown) {
- var $block, $colorPreview, $dropdown, $form, $loading, $selectbox, $sidebarCollapsedValue, $value, abilityName, defaultLabel, enableLabelCreateButton, issueURLSplit, issueUpdateURL, labelUrl, namespacePath, projectPath, saveLabelData, selectedLabel, showAny, showNo, $sidebarLabelTooltip, initialSelected, $toggleText, fieldName, useId, propertyName, showMenuAbove, $container, $dropdownContainer;
+ var $block,
+ $colorPreview,
+ $dropdown,
+ $form,
+ $loading,
+ $selectbox,
+ $sidebarCollapsedValue,
+ $value,
+ abilityName,
+ defaultLabel,
+ enableLabelCreateButton,
+ issueURLSplit,
+ issueUpdateURL,
+ labelUrl,
+ namespacePath,
+ projectPath,
+ saveLabelData,
+ selectedLabel,
+ showAny,
+ showNo,
+ $sidebarLabelTooltip,
+ initialSelected,
+ $toggleText,
+ fieldName,
+ useId,
+ propertyName,
+ showMenuAbove,
+ $container,
+ $dropdownContainer;
$dropdown = $(dropdown);
$dropdownContainer = $dropdown.closest('.labels-filter');
$toggleText = $dropdown.find('.dropdown-toggle-text');
@@ -34,7 +62,7 @@ export default class LabelsSelect {
labelUrl = $dropdown.data('labels');
issueUpdateURL = $dropdown.data('issueUpdate');
selectedLabel = $dropdown.data('selected');
- if ((selectedLabel != null) && !$dropdown.hasClass('js-multiselect')) {
+ if (selectedLabel != null && !$dropdown.hasClass('js-multiselect')) {
selectedLabel = selectedLabel.split(',');
}
showNo = $dropdown.data('showNo');
@@ -50,26 +78,37 @@ export default class LabelsSelect {
$value = $block.find('.value');
$loading = $block.find('.block-loading').fadeOut();
fieldName = $dropdown.data('fieldName');
- useId = $dropdown.is('.js-issuable-form-dropdown, .js-filter-bulk-update, .js-label-sidebar-dropdown');
+ useId = $dropdown.is(
+ '.js-issuable-form-dropdown, .js-filter-bulk-update, .js-label-sidebar-dropdown',
+ );
propertyName = useId ? 'id' : 'title';
initialSelected = $selectbox
.find('input[name="' + $dropdown.data('fieldName') + '"]')
- .map(function () {
+ .map(function() {
return this.value;
- }).get();
+ })
+ .get();
const { handleClick } = options;
$sidebarLabelTooltip.tooltip();
if ($dropdown.closest('.dropdown').find('.dropdown-new-label').length) {
- new CreateLabelDropdown($dropdown.closest('.dropdown').find('.dropdown-new-label'), namespacePath, projectPath);
+ new CreateLabelDropdown(
+ $dropdown.closest('.dropdown').find('.dropdown-new-label'),
+ namespacePath,
+ projectPath,
+ );
}
saveLabelData = function() {
var data, selected;
- selected = $dropdown.closest('.selectbox').find("input[name='" + fieldName + "']").map(function() {
- return this.value;
- }).get();
+ selected = $dropdown
+ .closest('.selectbox')
+ .find("input[name='" + fieldName + "']")
+ .map(function() {
+ return this.value;
+ })
+ .get();
if (_.isEqual(initialSelected, selected)) return;
initialSelected = selected;
@@ -82,7 +121,8 @@ export default class LabelsSelect {
}
$loading.removeClass('hidden').fadeIn();
$dropdown.trigger('loading.gl.dropdown');
- axios.put(issueUpdateURL, data)
+ axios
+ .put(issueUpdateURL, data)
.then(({ data }) => {
var labelCount, template, labelTooltipTitle, labelTitles, formattedLabels;
$loading.fadeOut();
@@ -96,8 +136,7 @@ export default class LabelsSelect {
issueUpdateURL,
});
labelCount = data.labels.length;
- }
- else {
+ } else {
template = '<span class="no-value">None</span>';
}
$value.removeAttr('style').html(template);
@@ -114,17 +153,14 @@ export default class LabelsSelect {
}
labelTooltipTitle = labelTitles.join(', ');
- }
- else {
+ } else {
labelTooltipTitle = __('Labels');
}
- $sidebarLabelTooltip
- .attr('title', labelTooltipTitle)
- .tooltip('_fixTitle');
+ $sidebarLabelTooltip.attr('title', labelTooltipTitle).tooltip('_fixTitle');
$('.has-tooltip', $value).tooltip({
- container: 'body'
+ container: 'body',
});
})
.catch(() => flash(__('Error saving label update.')));
@@ -132,34 +168,38 @@ export default class LabelsSelect {
$dropdown.glDropdown({
showMenuAbove: showMenuAbove,
data: function(term, callback) {
- axios.get(labelUrl)
- .then((res) => {
- let data = _.chain(res.data).groupBy(function(label) {
- return label.title;
- }).map(function(label) {
- var color;
- color = _.map(label, function(dup) {
- return dup.color;
- });
- return {
- id: label[0].id,
- title: label[0].title,
- color: color,
- duplicate: color.length > 1
- };
- }).value();
+ axios
+ .get(labelUrl)
+ .then(res => {
+ let data = _.chain(res.data)
+ .groupBy(function(label) {
+ return label.title;
+ })
+ .map(function(label) {
+ var color;
+ color = _.map(label, function(dup) {
+ return dup.color;
+ });
+ return {
+ id: label[0].id,
+ title: label[0].title,
+ color: color,
+ duplicate: color.length > 1,
+ };
+ })
+ .value();
if ($dropdown.hasClass('js-extra-options')) {
var extraData = [];
if (showNo) {
extraData.unshift({
id: 0,
- title: 'No Label'
+ title: 'No Label',
});
}
if (showAny) {
extraData.unshift({
isAny: true,
- title: 'Any Label'
+ title: 'Any Label',
});
}
if (extraData.length) {
@@ -176,11 +216,22 @@ export default class LabelsSelect {
.catch(() => flash(__('Error fetching labels.')));
},
renderRow: function(label, instance) {
- var $a, $li, color, colorEl, indeterminate, removesAll, selectedClass, spacing, i, marked, dropdownName, dropdownValue;
+ var $a,
+ $li,
+ color,
+ colorEl,
+ indeterminate,
+ removesAll,
+ selectedClass,
+ spacing,
+ i,
+ marked,
+ dropdownName,
+ dropdownValue;
$li = $('<li>');
$a = $('<a href="#">');
selectedClass = [];
- removesAll = label.id <= 0 || (label.id == null);
+ removesAll = label.id <= 0 || label.id == null;
if ($dropdown.hasClass('js-filter-bulk-update')) {
indeterminate = $dropdown.data('indeterminate') || [];
marked = $dropdown.data('marked') || [];
@@ -200,9 +251,19 @@ export default class LabelsSelect {
} else {
if (this.id(label)) {
dropdownName = $dropdown.data('fieldName');
- dropdownValue = this.id(label).toString().replace(/'/g, '\\\'');
-
- if ($form.find("input[type='hidden'][name='" + dropdownName + "'][value='" + dropdownValue + "']").length) {
+ dropdownValue = this.id(label)
+ .toString()
+ .replace(/'/g, "\\'");
+
+ if (
+ $form.find(
+ "input[type='hidden'][name='" +
+ dropdownName +
+ "'][value='" +
+ dropdownValue +
+ "']",
+ ).length
+ ) {
selectedClass.push('is-active');
}
}
@@ -213,16 +274,14 @@ export default class LabelsSelect {
}
if (label.duplicate) {
color = DropdownUtils.duplicateLabelColor(label.color);
- }
- else {
+ } else {
if (label.color != null) {
[color] = label.color;
}
}
if (color) {
colorEl = "<span class='dropdown-label-box' style='background: " + color + "'></span>";
- }
- else {
+ } else {
colorEl = '';
}
// We need to identify which items are actually labels
@@ -235,7 +294,7 @@ export default class LabelsSelect {
return $li.html($a).prop('outerHTML');
},
search: {
- fields: ['title']
+ fields: ['title'],
},
selectable: true,
filterable: true,
@@ -255,25 +314,21 @@ export default class LabelsSelect {
if (selected && selected.id === 0) {
this.selected = [];
return 'No Label';
- }
- else if (isSelected) {
+ } else if (isSelected) {
this.selected.push(title);
- }
- else if (!isSelected && title) {
+ } else if (!isSelected && title) {
var index = this.selected.indexOf(title);
this.selected.splice(index, 1);
}
if (selectedLabels.length === 1) {
return selectedLabels;
- }
- else if (selectedLabels.length) {
+ } else if (selectedLabels.length) {
return sprintf(__('%{firstLabel} +%{labelCount} more'), {
firstLabel: selectedLabels[0],
- labelCount: selectedLabels.length - 1
+ labelCount: selectedLabels.length - 1,
});
- }
- else {
+ } else {
return defaultLabel;
}
},
@@ -285,10 +340,9 @@ export default class LabelsSelect {
return label.id;
}
- if ($dropdown.hasClass("js-filter-submit") && (label.isAny == null)) {
+ if ($dropdown.hasClass('js-filter-submit') && label.isAny == null) {
return label.title;
- }
- else {
+ } else {
return label.id;
}
},
@@ -310,13 +364,13 @@ export default class LabelsSelect {
}
if ($dropdown.hasClass('js-multiselect')) {
if ($dropdown.hasClass('js-filter-submit') && (isIssueIndex || isMRIndex)) {
- selectedLabels = $dropdown.closest('form').find("input:hidden[name='" + ($dropdown.data('fieldName')) + "']");
+ selectedLabels = $dropdown
+ .closest('form')
+ .find("input:hidden[name='" + $dropdown.data('fieldName') + "']");
Issuable.filterResults($dropdown.closest('form'));
- }
- else if ($dropdown.hasClass('js-filter-submit')) {
+ } else if ($dropdown.hasClass('js-filter-submit')) {
$dropdown.closest('form').submit();
- }
- else {
+ } else {
if (!$dropdown.hasClass('js-filter-bulk-update')) {
saveLabelData();
}
@@ -325,7 +379,7 @@ export default class LabelsSelect {
},
multiSelect: $dropdown.hasClass('js-multiselect'),
vue: $dropdown.hasClass('js-issue-board-sidebar'),
- clicked: function (clickEvent) {
+ clicked: function(clickEvent) {
const { $el, e, isMarking } = clickEvent;
const label = clickEvent.selectedObj;
@@ -339,7 +393,8 @@ export default class LabelsSelect {
isMRIndex = page === 'projects:merge_requests:index';
if ($dropdown.parent().find('.is-active:not(.dropdown-clear-active)').length) {
- $dropdown.parent()
+ $dropdown
+ .parent()
.find('.dropdown-clear-active')
.removeClass('is-active');
}
@@ -367,28 +422,26 @@ export default class LabelsSelect {
e.preventDefault();
return;
- }
- else if ($dropdown.hasClass('js-filter-submit') && (isIssueIndex || isMRIndex)) {
+ } else if ($dropdown.hasClass('js-filter-submit') && (isIssueIndex || isMRIndex)) {
if (!$dropdown.hasClass('js-multiselect')) {
selectedLabel = label.title;
return Issuable.filterResults($dropdown.closest('form'));
}
- }
- else if ($dropdown.hasClass('js-filter-submit')) {
+ } else if ($dropdown.hasClass('js-filter-submit')) {
return $dropdown.closest('form').submit();
- }
- else if ($dropdown.hasClass('js-issue-board-sidebar')) {
+ } else if ($dropdown.hasClass('js-issue-board-sidebar')) {
if ($el.hasClass('is-active')) {
- boardsStore.detail.issue.labels.push(new ListLabel({
- id: label.id,
- title: label.title,
- color: label.color[0],
- textColor: '#fff'
- }));
- }
- else {
+ boardsStore.detail.issue.labels.push(
+ new ListLabel({
+ id: label.id,
+ title: label.title,
+ color: label.color[0],
+ textColor: '#fff',
+ }),
+ );
+ } else {
var { labels } = boardsStore.detail.issue;
- labels = labels.filter(function (selectedLabel) {
+ labels = labels.filter(function(selectedLabel) {
return selectedLabel.id !== label.id;
});
boardsStore.detail.issue.labels = labels;
@@ -396,19 +449,16 @@ export default class LabelsSelect {
$loading.fadeIn();
- boardsStore.detail.issue.update($dropdown.attr('data-issue-update'))
+ boardsStore.detail.issue
+ .update($dropdown.attr('data-issue-update'))
.then(fadeOutLoader)
.catch(fadeOutLoader);
- }
- else if (handleClick) {
+ } else if (handleClick) {
e.preventDefault();
handleClick(label);
- }
- else {
+ } else {
if ($dropdown.hasClass('js-multiselect')) {
-
- }
- else {
+ } else {
return saveLabelData();
}
}
@@ -436,15 +486,17 @@ export default class LabelsSelect {
// so best approach is to use traditional way of
// concatenation
// see: http://2ality.com/2016/05/template-literal-whitespace.html#joining-arrays
- const tpl = _.template([
- '<% _.each(labels, function(label){ %>',
- '<a href="<%- issueUpdateURL.slice(0, issueUpdateURL.lastIndexOf("/")) %>?label_name[]=<%- encodeURIComponent(label.title) %>">',
- '<span class="badge label has-tooltip color-label" title="<%- label.description %>" style="background-color: <%- label.color %>; color: <%- label.text_color %>;">',
- '<%- label.title %>',
- '</span>',
- '</a>',
- '<% }); %>',
- ].join(''));
+ const tpl = _.template(
+ [
+ '<% _.each(labels, function(label){ %>',
+ '<a href="<%- issueUpdateURL.slice(0, issueUpdateURL.lastIndexOf("/")) %>?label_name[]=<%- encodeURIComponent(label.title) %>">',
+ '<span class="badge label has-tooltip color-label" title="<%- label.description %>" style="background-color: <%- label.color %>; color: <%- label.text_color %>;">',
+ '<%- label.title %>',
+ '</span>',
+ '</a>',
+ '<% }); %>',
+ ].join(''),
+ );
return tpl(tplData);
}
diff --git a/app/assets/javascripts/lib/utils/ace_utils.js b/app/assets/javascripts/lib/utils/ace_utils.js
index efc4b2a8d94..ee71ae0e61a 100644
--- a/app/assets/javascripts/lib/utils/ace_utils.js
+++ b/app/assets/javascripts/lib/utils/ace_utils.js
@@ -1,6 +1,6 @@
/* global ace */
export default function getModeByFileExtension(path) {
- const modelist = ace.require("ace/ext/modelist");
+ const modelist = ace.require('ace/ext/modelist');
return modelist.getModeForPath(path).mode;
-};
+}
diff --git a/app/assets/javascripts/lib/utils/number_utils.js b/app/assets/javascripts/lib/utils/number_utils.js
index afbab59055b..2ccc51c35f7 100644
--- a/app/assets/javascripts/lib/utils/number_utils.js
+++ b/app/assets/javascripts/lib/utils/number_utils.js
@@ -7,7 +7,7 @@ import { BYTES_IN_KIB } from './constants';
* * * Show 3 digits to the right
* * For 2 digits to the left of the decimal point and X digits to the right of it
* * * Show 2 digits to the right
-*/
+ */
export function formatRelevantDigits(number) {
let digitsLeft = '';
let relevantDigits = 0;
diff --git a/app/assets/javascripts/members.js b/app/assets/javascripts/members.js
index 7d0c701fd70..bd263c75a3d 100644
--- a/app/assets/javascripts/members.js
+++ b/app/assets/javascripts/members.js
@@ -7,8 +7,12 @@ export default class Members {
}
addListeners() {
- $('.js-member-update-control').off('change').on('change', this.formSubmit.bind(this));
- $('.js-edit-member-form').off('ajax:success').on('ajax:success', this.formSuccess.bind(this));
+ $('.js-member-update-control')
+ .off('change')
+ .on('change', this.formSubmit.bind(this));
+ $('.js-edit-member-form')
+ .off('ajax:success')
+ .on('ajax:success', this.formSuccess.bind(this));
gl.utils.disableButtonIfEmptyField('#user_ids', 'input[name=commit]', 'change');
}
@@ -28,7 +32,7 @@ export default class Members {
toggleLabel(selected, $el) {
return $el.text();
},
- clicked: (options) => {
+ clicked: options => {
this.formSubmit(null, options.$el);
},
});
diff --git a/app/assets/javascripts/milestone_select.js b/app/assets/javascripts/milestone_select.js
index 42fb5c7177a..d32f39881dd 100644
--- a/app/assets/javascripts/milestone_select.js
+++ b/app/assets/javascripts/milestone_select.js
@@ -9,7 +9,10 @@ import '~/gl_dropdown';
import axios from './lib/utils/axios_utils';
import { timeFor } from './lib/utils/datetime_utility';
import ModalStore from './boards/stores/modal_store';
-import boardsStore, { boardStoreIssueSet, boardStoreIssueDelete } from './boards/stores/boards_store';
+import boardsStore, {
+ boardStoreIssueSet,
+ boardStoreIssueDelete,
+} from './boards/stores/boards_store';
export default class MilestoneSelect {
constructor(currentProject, els, options = {}) {
diff --git a/app/assets/javascripts/notes/components/note_awards_list.vue b/app/assets/javascripts/notes/components/note_awards_list.vue
index e707f44bf5a..df7ab4502a6 100644
--- a/app/assets/javascripts/notes/components/note_awards_list.vue
+++ b/app/assets/javascripts/notes/components/note_awards_list.vue
@@ -110,7 +110,7 @@ export default {
// Get the remaining list to use in `and x more` text.
const remainingAwardList = awardList.slice(TOOLTIP_NAME_COUNT, awardList.length);
- // Add myself to the begining of the list so title will start with You.
+ // Add myself to the beginning of the list so title will start with You.
if (hasReactionByCurrentUser) {
namesToShow.unshift('You');
}
diff --git a/app/assets/javascripts/notes/components/notes_app.vue b/app/assets/javascripts/notes/components/notes_app.vue
index b0faa443a18..7514ce8a1eb 100644
--- a/app/assets/javascripts/notes/components/notes_app.vue
+++ b/app/assets/javascripts/notes/components/notes_app.vue
@@ -54,7 +54,13 @@ export default {
};
},
computed: {
- ...mapGetters(['isNotesFetched', 'discussions', 'getNotesDataByProp', 'discussionCount', 'isLoading']),
+ ...mapGetters([
+ 'isNotesFetched',
+ 'discussions',
+ 'getNotesDataByProp',
+ 'discussionCount',
+ 'isLoading',
+ ]),
noteableType() {
return this.noteableData.noteableType;
},
diff --git a/app/assets/javascripts/notes/discussion_filters.js b/app/assets/javascripts/notes/discussion_filters.js
index 012ffc4093e..06eadaeea0e 100644
--- a/app/assets/javascripts/notes/discussion_filters.js
+++ b/app/assets/javascripts/notes/discussion_filters.js
@@ -1,15 +1,17 @@
import Vue from 'vue';
import DiscussionFilter from './components/discussion_filter.vue';
-export default (store) => {
+export default store => {
const discussionFilterEl = document.getElementById('js-vue-discussion-filter');
if (discussionFilterEl) {
const { defaultFilter, notesFilters } = discussionFilterEl.dataset;
const defaultValue = defaultFilter ? parseInt(defaultFilter, 10) : null;
const filterValues = notesFilters ? JSON.parse(notesFilters) : {};
- const filters = Object.keys(filterValues).map(entry =>
- ({ title: entry, value: filterValues[entry] }));
+ const filters = Object.keys(filterValues).map(entry => ({
+ title: entry,
+ value: filterValues[entry],
+ }));
return new Vue({
el: discussionFilterEl,
diff --git a/app/assets/javascripts/notes/stores/collapse_utils.js b/app/assets/javascripts/notes/stores/collapse_utils.js
index 4532226aa07..bee6d4f0329 100644
--- a/app/assets/javascripts/notes/stores/collapse_utils.js
+++ b/app/assets/javascripts/notes/stores/collapse_utils.js
@@ -70,7 +70,7 @@ export const collapseSystemNotes = notes => {
} else if (lastDescriptionSystemNote) {
const timeDifferenceMinutes = getTimeDifferenceMinutes(lastDescriptionSystemNote, note);
- // are they less than 10 minutes appart?
+ // are they less than 10 minutes apart?
if (timeDifferenceMinutes > 10) {
// reset counter
descriptionChangedTimes = 1;
diff --git a/app/assets/javascripts/pages/milestones/shared/components/promote_milestone_modal.vue b/app/assets/javascripts/pages/milestones/shared/components/promote_milestone_modal.vue
index 2c683a39f42..9d19e4a095d 100644
--- a/app/assets/javascripts/pages/milestones/shared/components/promote_milestone_modal.vue
+++ b/app/assets/javascripts/pages/milestones/shared/components/promote_milestone_modal.vue
@@ -1,54 +1,66 @@
<script>
- import axios from '~/lib/utils/axios_utils';
- import createFlash from '~/flash';
- import GlModal from '~/vue_shared/components/gl_modal.vue';
- import { s__, sprintf } from '~/locale';
- import { visitUrl } from '~/lib/utils/url_utility';
- import eventHub from '../event_hub';
+import axios from '~/lib/utils/axios_utils';
+import createFlash from '~/flash';
+import GlModal from '~/vue_shared/components/gl_modal.vue';
+import { s__, sprintf } from '~/locale';
+import { visitUrl } from '~/lib/utils/url_utility';
+import eventHub from '../event_hub';
- export default {
- components: {
- GlModal,
+export default {
+ components: {
+ GlModal,
+ },
+ props: {
+ milestoneTitle: {
+ type: String,
+ required: true,
},
- props: {
- milestoneTitle: {
- type: String,
- required: true,
- },
- url: {
- type: String,
- required: true,
- },
- groupName: {
- type: String,
- required: true,
- },
+ url: {
+ type: String,
+ required: true,
},
- computed: {
- title() {
- return sprintf(s__('Milestones|Promote %{milestoneTitle} to group milestone?'), { milestoneTitle: this.milestoneTitle });
- },
- text() {
- return sprintf(s__(`Milestones|Promoting %{milestoneTitle} will make it available for all projects inside %{groupName}.
+ groupName: {
+ type: String,
+ required: true,
+ },
+ },
+ computed: {
+ title() {
+ return sprintf(s__('Milestones|Promote %{milestoneTitle} to group milestone?'), {
+ milestoneTitle: this.milestoneTitle,
+ });
+ },
+ text() {
+ return sprintf(
+ s__(`Milestones|Promoting %{milestoneTitle} will make it available for all projects inside %{groupName}.
Existing project milestones with the same title will be merged.
- This action cannot be reversed.`), { milestoneTitle: this.milestoneTitle, groupName: this.groupName });
- },
+ This action cannot be reversed.`),
+ { milestoneTitle: this.milestoneTitle, groupName: this.groupName },
+ );
},
- methods: {
- onSubmit() {
- eventHub.$emit('promoteMilestoneModal.requestStarted', this.url);
- return axios.post(this.url, { params: { format: 'json' } })
- .then((response) => {
- eventHub.$emit('promoteMilestoneModal.requestFinished', { milestoneUrl: this.url, successful: true });
- visitUrl(response.data.url);
- })
- .catch((error) => {
- eventHub.$emit('promoteMilestoneModal.requestFinished', { milestoneUrl: this.url, successful: false });
- createFlash(error);
+ },
+ methods: {
+ onSubmit() {
+ eventHub.$emit('promoteMilestoneModal.requestStarted', this.url);
+ return axios
+ .post(this.url, { params: { format: 'json' } })
+ .then(response => {
+ eventHub.$emit('promoteMilestoneModal.requestFinished', {
+ milestoneUrl: this.url,
+ successful: true,
});
- },
+ visitUrl(response.data.url);
+ })
+ .catch(error => {
+ eventHub.$emit('promoteMilestoneModal.requestFinished', {
+ milestoneUrl: this.url,
+ successful: false,
+ });
+ createFlash(error);
+ });
},
- };
+ },
+};
</script>
<template>
<gl-modal
@@ -65,4 +77,3 @@
{{ text }}
</gl-modal>
</template>
-
diff --git a/app/assets/javascripts/pages/projects/project.js b/app/assets/javascripts/pages/projects/project.js
index 52d66beefc9..a6bee49a6b1 100644
--- a/app/assets/javascripts/pages/projects/project.js
+++ b/app/assets/javascripts/pages/projects/project.js
@@ -64,7 +64,9 @@ export default class Project {
const projectId = $(this).data('project-id');
const cookieKey = `hide_auto_devops_implicitly_enabled_banner_${projectId}`;
Cookies.set(cookieKey, 'false');
- $(this).parents('.auto-devops-implicitly-enabled-banner').remove();
+ $(this)
+ .parents('.auto-devops-implicitly-enabled-banner')
+ .remove();
return e.preventDefault();
});
Project.projectSelectDropdown();
diff --git a/app/assets/javascripts/pages/projects/shared/permissions/components/settings_panel.vue b/app/assets/javascripts/pages/projects/shared/permissions/components/settings_panel.vue
index a16f7e6b77c..c0ec7a5dc94 100644
--- a/app/assets/javascripts/pages/projects/shared/permissions/components/settings_panel.vue
+++ b/app/assets/javascripts/pages/projects/shared/permissions/components/settings_panel.vue
@@ -1,202 +1,200 @@
<script>
- import projectFeatureSetting from './project_feature_setting.vue';
- import projectFeatureToggle from '../../../../../vue_shared/components/toggle_button.vue';
- import projectSettingRow from './project_setting_row.vue';
- import { visibilityOptions, visibilityLevelDescriptions } from '../constants';
- import { toggleHiddenClassBySelector } from '../external';
+import projectFeatureSetting from './project_feature_setting.vue';
+import projectFeatureToggle from '../../../../../vue_shared/components/toggle_button.vue';
+import projectSettingRow from './project_setting_row.vue';
+import { visibilityOptions, visibilityLevelDescriptions } from '../constants';
+import { toggleHiddenClassBySelector } from '../external';
- export default {
- components: {
- projectFeatureSetting,
- projectFeatureToggle,
- projectSettingRow,
- },
+export default {
+ components: {
+ projectFeatureSetting,
+ projectFeatureToggle,
+ projectSettingRow,
+ },
- props: {
- currentSettings: {
- type: Object,
- required: true,
- },
- canChangeVisibilityLevel: {
- type: Boolean,
- required: false,
- default: false,
- },
- allowedVisibilityOptions: {
- type: Array,
- required: false,
- default: () => [0, 10, 20],
- },
- lfsAvailable: {
- type: Boolean,
- required: false,
- default: false,
- },
- registryAvailable: {
- type: Boolean,
- required: false,
- default: false,
- },
- visibilityHelpPath: {
- type: String,
- required: false,
- default: '',
- },
- lfsHelpPath: {
- type: String,
- required: false,
- default: '',
- },
- registryHelpPath: {
- type: String,
- required: false,
- default: '',
- },
- pagesAvailable: {
- type: Boolean,
- required: false,
- default: false,
- },
- pagesAccessControlEnabled: {
- type: Boolean,
- required: false,
- default: false,
- },
- pagesHelpPath: {
- type: String,
- required: false,
- default: '',
- },
+ props: {
+ currentSettings: {
+ type: Object,
+ required: true,
+ },
+ canChangeVisibilityLevel: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
+ allowedVisibilityOptions: {
+ type: Array,
+ required: false,
+ default: () => [0, 10, 20],
+ },
+ lfsAvailable: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
+ registryAvailable: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
+ visibilityHelpPath: {
+ type: String,
+ required: false,
+ default: '',
},
+ lfsHelpPath: {
+ type: String,
+ required: false,
+ default: '',
+ },
+ registryHelpPath: {
+ type: String,
+ required: false,
+ default: '',
+ },
+ pagesAvailable: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
+ pagesAccessControlEnabled: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
+ pagesHelpPath: {
+ type: String,
+ required: false,
+ default: '',
+ },
+ },
- data() {
- const defaults = {
- visibilityOptions,
- visibilityLevel: visibilityOptions.PUBLIC,
- issuesAccessLevel: 20,
- repositoryAccessLevel: 20,
- mergeRequestsAccessLevel: 20,
- buildsAccessLevel: 20,
- wikiAccessLevel: 20,
- snippetsAccessLevel: 20,
- pagesAccessLevel: 20,
- containerRegistryEnabled: true,
- lfsEnabled: true,
- requestAccessEnabled: true,
- highlightChangesClass: false,
- };
+ data() {
+ const defaults = {
+ visibilityOptions,
+ visibilityLevel: visibilityOptions.PUBLIC,
+ issuesAccessLevel: 20,
+ repositoryAccessLevel: 20,
+ mergeRequestsAccessLevel: 20,
+ buildsAccessLevel: 20,
+ wikiAccessLevel: 20,
+ snippetsAccessLevel: 20,
+ pagesAccessLevel: 20,
+ containerRegistryEnabled: true,
+ lfsEnabled: true,
+ requestAccessEnabled: true,
+ highlightChangesClass: false,
+ };
- return { ...defaults, ...this.currentSettings };
- },
+ return { ...defaults, ...this.currentSettings };
+ },
- computed: {
- featureAccessLevelOptions() {
- const options = [
- [10, 'Only Project Members'],
- ];
- if (this.visibilityLevel !== visibilityOptions.PRIVATE) {
- options.push([20, 'Everyone With Access']);
- }
- return options;
- },
+ computed: {
+ featureAccessLevelOptions() {
+ const options = [[10, 'Only Project Members']];
+ if (this.visibilityLevel !== visibilityOptions.PRIVATE) {
+ options.push([20, 'Everyone With Access']);
+ }
+ return options;
+ },
- repoFeatureAccessLevelOptions() {
- return this.featureAccessLevelOptions.filter(
- ([value]) => value <= this.repositoryAccessLevel,
- );
- },
+ repoFeatureAccessLevelOptions() {
+ return this.featureAccessLevelOptions.filter(
+ ([value]) => value <= this.repositoryAccessLevel,
+ );
+ },
- pagesFeatureAccessLevelOptions() {
- if (this.visibilityLevel !== visibilityOptions.PUBLIC) {
- return this.featureAccessLevelOptions.concat([[30, 'Everyone']]);
- }
- return this.featureAccessLevelOptions;
- },
+ pagesFeatureAccessLevelOptions() {
+ if (this.visibilityLevel !== visibilityOptions.PUBLIC) {
+ return this.featureAccessLevelOptions.concat([[30, 'Everyone']]);
+ }
+ return this.featureAccessLevelOptions;
+ },
- repositoryEnabled() {
- return this.repositoryAccessLevel > 0;
- },
+ repositoryEnabled() {
+ return this.repositoryAccessLevel > 0;
+ },
- visibilityLevelDescription() {
- return visibilityLevelDescriptions[this.visibilityLevel];
- },
+ visibilityLevelDescription() {
+ return visibilityLevelDescriptions[this.visibilityLevel];
},
+ },
- watch: {
- visibilityLevel(value, oldValue) {
- if (value === visibilityOptions.PRIVATE) {
- // when private, features are restricted to "only team members"
- this.issuesAccessLevel = Math.min(10, this.issuesAccessLevel);
- this.repositoryAccessLevel = Math.min(10, this.repositoryAccessLevel);
- this.mergeRequestsAccessLevel = Math.min(10, this.mergeRequestsAccessLevel);
- this.buildsAccessLevel = Math.min(10, this.buildsAccessLevel);
- this.wikiAccessLevel = Math.min(10, this.wikiAccessLevel);
- this.snippetsAccessLevel = Math.min(10, this.snippetsAccessLevel);
- if (this.pagesAccessLevel === 20) {
- // When from Internal->Private narrow access for only members
- this.pagesAccessLevel = 10;
- }
- this.highlightChanges();
- } else if (oldValue === visibilityOptions.PRIVATE) {
- // if changing away from private, make enabled features more permissive
- if (this.issuesAccessLevel > 0) this.issuesAccessLevel = 20;
- if (this.repositoryAccessLevel > 0) this.repositoryAccessLevel = 20;
- if (this.mergeRequestsAccessLevel > 0) this.mergeRequestsAccessLevel = 20;
- if (this.buildsAccessLevel > 0) this.buildsAccessLevel = 20;
- if (this.wikiAccessLevel > 0) this.wikiAccessLevel = 20;
- if (this.snippetsAccessLevel > 0) this.snippetsAccessLevel = 20;
- if (this.pagesAccessLevel === 10) this.pagesAccessLevel = 20;
- this.highlightChanges();
+ watch: {
+ visibilityLevel(value, oldValue) {
+ if (value === visibilityOptions.PRIVATE) {
+ // when private, features are restricted to "only team members"
+ this.issuesAccessLevel = Math.min(10, this.issuesAccessLevel);
+ this.repositoryAccessLevel = Math.min(10, this.repositoryAccessLevel);
+ this.mergeRequestsAccessLevel = Math.min(10, this.mergeRequestsAccessLevel);
+ this.buildsAccessLevel = Math.min(10, this.buildsAccessLevel);
+ this.wikiAccessLevel = Math.min(10, this.wikiAccessLevel);
+ this.snippetsAccessLevel = Math.min(10, this.snippetsAccessLevel);
+ if (this.pagesAccessLevel === 20) {
+ // When from Internal->Private narrow access for only members
+ this.pagesAccessLevel = 10;
}
- },
+ this.highlightChanges();
+ } else if (oldValue === visibilityOptions.PRIVATE) {
+ // if changing away from private, make enabled features more permissive
+ if (this.issuesAccessLevel > 0) this.issuesAccessLevel = 20;
+ if (this.repositoryAccessLevel > 0) this.repositoryAccessLevel = 20;
+ if (this.mergeRequestsAccessLevel > 0) this.mergeRequestsAccessLevel = 20;
+ if (this.buildsAccessLevel > 0) this.buildsAccessLevel = 20;
+ if (this.wikiAccessLevel > 0) this.wikiAccessLevel = 20;
+ if (this.snippetsAccessLevel > 0) this.snippetsAccessLevel = 20;
+ if (this.pagesAccessLevel === 10) this.pagesAccessLevel = 20;
+ this.highlightChanges();
+ }
+ },
- repositoryAccessLevel(value, oldValue) {
- if (value < oldValue) {
- // sub-features cannot have more premissive access level
- this.mergeRequestsAccessLevel = Math.min(this.mergeRequestsAccessLevel, value);
- this.buildsAccessLevel = Math.min(this.buildsAccessLevel, value);
+ repositoryAccessLevel(value, oldValue) {
+ if (value < oldValue) {
+ // sub-features cannot have more premissive access level
+ this.mergeRequestsAccessLevel = Math.min(this.mergeRequestsAccessLevel, value);
+ this.buildsAccessLevel = Math.min(this.buildsAccessLevel, value);
- if (value === 0) {
- this.containerRegistryEnabled = false;
- this.lfsEnabled = false;
- }
- } else if (oldValue === 0) {
- this.mergeRequestsAccessLevel = value;
- this.buildsAccessLevel = value;
- this.containerRegistryEnabled = true;
- this.lfsEnabled = true;
+ if (value === 0) {
+ this.containerRegistryEnabled = false;
+ this.lfsEnabled = false;
}
- },
+ } else if (oldValue === 0) {
+ this.mergeRequestsAccessLevel = value;
+ this.buildsAccessLevel = value;
+ this.containerRegistryEnabled = true;
+ this.lfsEnabled = true;
+ }
+ },
- issuesAccessLevel(value, oldValue) {
- if (value === 0) toggleHiddenClassBySelector('.issues-feature', true);
- else if (oldValue === 0) toggleHiddenClassBySelector('.issues-feature', false);
- },
+ issuesAccessLevel(value, oldValue) {
+ if (value === 0) toggleHiddenClassBySelector('.issues-feature', true);
+ else if (oldValue === 0) toggleHiddenClassBySelector('.issues-feature', false);
+ },
- mergeRequestsAccessLevel(value, oldValue) {
- if (value === 0) toggleHiddenClassBySelector('.merge-requests-feature', true);
- else if (oldValue === 0) toggleHiddenClassBySelector('.merge-requests-feature', false);
- },
+ mergeRequestsAccessLevel(value, oldValue) {
+ if (value === 0) toggleHiddenClassBySelector('.merge-requests-feature', true);
+ else if (oldValue === 0) toggleHiddenClassBySelector('.merge-requests-feature', false);
+ },
- buildsAccessLevel(value, oldValue) {
- if (value === 0) toggleHiddenClassBySelector('.builds-feature', true);
- else if (oldValue === 0) toggleHiddenClassBySelector('.builds-feature', false);
- },
+ buildsAccessLevel(value, oldValue) {
+ if (value === 0) toggleHiddenClassBySelector('.builds-feature', true);
+ else if (oldValue === 0) toggleHiddenClassBySelector('.builds-feature', false);
},
+ },
- methods: {
- highlightChanges() {
- this.highlightChangesClass = true;
- this.$nextTick(() => {
- this.highlightChangesClass = false;
- });
- },
+ methods: {
+ highlightChanges() {
+ this.highlightChangesClass = true;
+ this.$nextTick(() => {
+ this.highlightChangesClass = false;
+ });
+ },
- visibilityAllowed(option) {
- return this.allowedVisibilityOptions.includes(option);
- },
+ visibilityAllowed(option) {
+ return this.allowedVisibilityOptions.includes(option);
},
- };
+ },
+};
</script>
<template>
diff --git a/app/assets/javascripts/projects/project_new.js b/app/assets/javascripts/projects/project_new.js
index ebe18b47e4e..998554d1be5 100644
--- a/app/assets/javascripts/projects/project_new.js
+++ b/app/assets/javascripts/projects/project_new.js
@@ -4,8 +4,10 @@ import { slugifyWithHyphens } from '../lib/utils/text_utility';
let hasUserDefinedProjectPath = false;
-const deriveProjectPathFromUrl = ($projectImportUrl) => {
- const $currentProjectPath = $projectImportUrl.parents('.toggle-import-form').find('#project_path');
+const deriveProjectPathFromUrl = $projectImportUrl => {
+ const $currentProjectPath = $projectImportUrl
+ .parents('.toggle-import-form')
+ .find('#project_path');
if (hasUserDefinedProjectPath) {
return;
}
@@ -52,9 +54,11 @@ const bindEvents = () => {
return;
}
- $('.how_to_import_link').on('click', (e) => {
+ $('.how_to_import_link').on('click', e => {
e.preventDefault();
- $(e.currentTarget).next('.modal').show();
+ $(e.currentTarget)
+ .next('.modal')
+ .show();
});
$('.modal-header .close').on('click', () => {
@@ -63,15 +67,21 @@ const bindEvents = () => {
$('.btn_import_gitlab_project').on('click', () => {
const importHref = $('a.btn_import_gitlab_project').attr('href');
- $('.btn_import_gitlab_project')
- .attr('href', `${importHref}?namespace_id=${$('#project_namespace_id').val()}&name=${$projectName.val()}&path=${$projectPath.val()}`);
+ $('.btn_import_gitlab_project').attr(
+ 'href',
+ `${importHref}?namespace_id=${$(
+ '#project_namespace_id',
+ ).val()}&name=${$projectName.val()}&path=${$projectPath.val()}`,
+ );
});
if ($pushNewProjectTipTrigger) {
$pushNewProjectTipTrigger
.removeAttr('rel')
.removeAttr('target')
- .on('click', (e) => { e.preventDefault(); })
+ .on('click', e => {
+ e.preventDefault();
+ })
.popover({
title: $pushNewProjectTipTrigger.data('title'),
placement: 'bottom',
@@ -79,13 +89,15 @@ const bindEvents = () => {
content: $('.push-new-project-tip-template').html(),
})
.on('shown.bs.popover', () => {
- $(document).on('click.popover touchstart.popover', (event) => {
+ $(document).on('click.popover touchstart.popover', event => {
if ($(event.target).closest('.popover').length === 0) {
$pushNewProjectTipTrigger.trigger('click');
}
});
- const target = $(`#${$pushNewProjectTipTrigger.attr('aria-describedby')}`).find('.js-select-on-focus');
+ const target = $(`#${$pushNewProjectTipTrigger.attr('aria-describedby')}`).find(
+ '.js-select-on-focus',
+ );
addSelectOnFocusBehaviour(target);
target.focus();
@@ -117,16 +129,18 @@ const bindEvents = () => {
const selectedTemplate = templates[value];
$selectedTemplateText.text(selectedTemplate.text);
- $(selectedTemplate.icon).clone().addClass('d-block').appendTo($selectedIcon);
+ $(selectedTemplate.icon)
+ .clone()
+ .addClass('d-block')
+ .appendTo($selectedIcon);
const $activeTabProjectName = $('.tab-pane.active #project_name');
const $activeTabProjectPath = $('.tab-pane.active #project_path');
$activeTabProjectName.focus();
- $activeTabProjectName
- .keyup(() => {
- onProjectNameChange($activeTabProjectName, $activeTabProjectPath);
- hasUserDefinedProjectPath = $activeTabProjectPath.val().trim().length > 0;
- });
+ $activeTabProjectName.keyup(() => {
+ onProjectNameChange($activeTabProjectName, $activeTabProjectPath);
+ hasUserDefinedProjectPath = $activeTabProjectPath.val().trim().length > 0;
+ });
}
$useTemplateBtn.on('change', chooseTemplate);
diff --git a/app/assets/javascripts/right_sidebar.js b/app/assets/javascripts/right_sidebar.js
index 6b3753f7966..225e21ad322 100644
--- a/app/assets/javascripts/right_sidebar.js
+++ b/app/assets/javascripts/right_sidebar.js
@@ -21,7 +21,7 @@ Sidebar.initialize = function(currentUser) {
}
};
-Sidebar.prototype.removeListeners = function () {
+Sidebar.prototype.removeListeners = function() {
this.sidebar.off('click', '.sidebar-collapsed-icon');
this.sidebar.off('hidden.gl.dropdown');
$('.dropdown').off('loading.gl.dropdown');
@@ -38,10 +38,12 @@ Sidebar.prototype.addEventListeners = function() {
$('.dropdown').on('loaded.gl.dropdown', this.sidebarDropdownLoaded);
$document.on('click', '.js-sidebar-toggle', this.sidebarToggleClicked);
- return $(document).off('click', '.js-issuable-todo').on('click', '.js-issuable-todo', this.toggleTodo);
+ return $(document)
+ .off('click', '.js-issuable-todo')
+ .on('click', '.js-issuable-todo', this.toggleTodo);
};
-Sidebar.prototype.sidebarToggleClicked = function (e, triggered) {
+Sidebar.prototype.sidebarToggleClicked = function(e, triggered) {
var $allGutterToggleIcons, $this, isExpanded, tooltipLabel;
e.preventDefault();
$this = $(this);
@@ -51,18 +53,26 @@ Sidebar.prototype.sidebarToggleClicked = function (e, triggered) {
if (isExpanded) {
$allGutterToggleIcons.removeClass('fa-angle-double-right').addClass('fa-angle-double-left');
- $('aside.right-sidebar').removeClass('right-sidebar-expanded').addClass('right-sidebar-collapsed');
- $('.layout-page').removeClass('right-sidebar-expanded').addClass('right-sidebar-collapsed');
+ $('aside.right-sidebar')
+ .removeClass('right-sidebar-expanded')
+ .addClass('right-sidebar-collapsed');
+ $('.layout-page')
+ .removeClass('right-sidebar-expanded')
+ .addClass('right-sidebar-collapsed');
} else {
$allGutterToggleIcons.removeClass('fa-angle-double-left').addClass('fa-angle-double-right');
- $('aside.right-sidebar').removeClass('right-sidebar-collapsed').addClass('right-sidebar-expanded');
- $('.layout-page').removeClass('right-sidebar-collapsed').addClass('right-sidebar-expanded');
+ $('aside.right-sidebar')
+ .removeClass('right-sidebar-collapsed')
+ .addClass('right-sidebar-expanded');
+ $('.layout-page')
+ .removeClass('right-sidebar-collapsed')
+ .addClass('right-sidebar-expanded');
}
$this.attr('data-original-title', tooltipLabel);
if (!triggered) {
- Cookies.set("collapsed_gutter", $('.right-sidebar').hasClass('right-sidebar-collapsed'));
+ Cookies.set('collapsed_gutter', $('.right-sidebar').hasClass('right-sidebar-collapsed'));
}
};
@@ -71,21 +81,27 @@ Sidebar.prototype.toggleTodo = function(e) {
$this = $(e.currentTarget);
ajaxType = $this.attr('data-delete-path') ? 'delete' : 'post';
if ($this.attr('data-delete-path')) {
- url = "" + ($this.attr('data-delete-path'));
+ url = '' + $this.attr('data-delete-path');
} else {
- url = "" + ($this.data('url'));
+ url = '' + $this.data('url');
}
$this.tooltip('hide');
- $('.js-issuable-todo').disable().addClass('is-loading');
+ $('.js-issuable-todo')
+ .disable()
+ .addClass('is-loading');
axios[ajaxType](url, {
issuable_id: $this.data('issuableId'),
issuable_type: $this.data('issuableType'),
- }).then(({ data }) => {
- this.todoUpdateDone(data);
- }).catch(() => flash(`There was an error ${ajaxType === 'post' ? 'adding a' : 'deleting the'} todo.`));
+ })
+ .then(({ data }) => {
+ this.todoUpdateDone(data);
+ })
+ .catch(() =>
+ flash(`There was an error ${ajaxType === 'post' ? 'adding a' : 'deleting the'} todo.`),
+ );
};
Sidebar.prototype.todoUpdateDone = function(data) {
@@ -99,7 +115,8 @@ Sidebar.prototype.todoUpdateDone = function(data) {
const $el = $(el);
const $elText = $el.find('.js-issuable-todo-inner');
- $el.removeClass('is-loading')
+ $el
+ .removeClass('is-loading')
.enable()
.attr('aria-label', $el.data(`${attrPrefix}Text`))
.attr('data-delete-path', deletePath)
@@ -119,7 +136,9 @@ Sidebar.prototype.todoUpdateDone = function(data) {
Sidebar.prototype.sidebarDropdownLoading = function(e) {
var $loading, $sidebarCollapsedIcon, i, img;
- $sidebarCollapsedIcon = $(this).closest('.block').find('.sidebar-collapsed-icon');
+ $sidebarCollapsedIcon = $(this)
+ .closest('.block')
+ .find('.sidebar-collapsed-icon');
img = $sidebarCollapsedIcon.find('img');
i = $sidebarCollapsedIcon.find('i');
$loading = $('<i class="fa fa-spinner fa-spin"></i>');
@@ -134,7 +153,9 @@ Sidebar.prototype.sidebarDropdownLoading = function(e) {
Sidebar.prototype.sidebarDropdownLoaded = function(e) {
var $sidebarCollapsedIcon, i, img;
- $sidebarCollapsedIcon = $(this).closest('.block').find('.sidebar-collapsed-icon');
+ $sidebarCollapsedIcon = $(this)
+ .closest('.block')
+ .find('.sidebar-collapsed-icon');
img = $sidebarCollapsedIcon.find('img');
$sidebarCollapsedIcon.find('i.fa-spin').remove();
i = $sidebarCollapsedIcon.find('i');
@@ -220,7 +241,7 @@ Sidebar.prototype.isOpen = function() {
};
Sidebar.prototype.getBlock = function(name) {
- return this.sidebar.find(".block." + name);
+ return this.sidebar.find('.block.' + name);
};
export default Sidebar;
diff --git a/app/assets/javascripts/search_autocomplete.js b/app/assets/javascripts/search_autocomplete.js
index ad4f5320ff8..17def77b2d7 100644
--- a/app/assets/javascripts/search_autocomplete.js
+++ b/app/assets/javascripts/search_autocomplete.js
@@ -234,7 +234,9 @@ export class SearchAutocomplete {
icon,
text: term,
template,
- url: `${gon.relative_url_root}/search?search=${term}&project_id=${this.projectInputEl.val()}&group_id=${this.groupInputEl.val()}`,
+ url: `${
+ gon.relative_url_root
+ }/search?search=${term}&project_id=${this.projectInputEl.val()}&group_id=${this.groupInputEl.val()}`,
});
}
}
diff --git a/app/assets/javascripts/sidebar/components/assignees/assignees.vue b/app/assets/javascripts/sidebar/components/assignees/assignees.vue
index dd155c133ce..f1ea6aacdb2 100644
--- a/app/assets/javascripts/sidebar/components/assignees/assignees.vue
+++ b/app/assets/javascripts/sidebar/components/assignees/assignees.vue
@@ -74,8 +74,8 @@ export default {
}
if (!this.users.length) {
- const emptyTooltipLabel = this.issuableType === 'issue' ?
- __('Assignee(s)') : __('Assignee');
+ const emptyTooltipLabel =
+ this.issuableType === 'issue' ? __('Assignee(s)') : __('Assignee');
names.push(emptyTooltipLabel);
}
@@ -248,4 +248,3 @@ export default {
</div>
</div>
</template>
-
diff --git a/app/assets/javascripts/sidebar/components/subscriptions/subscriptions.vue b/app/assets/javascripts/sidebar/components/subscriptions/subscriptions.vue
index 448c8fc3602..b6151aa6c64 100644
--- a/app/assets/javascripts/sidebar/components/subscriptions/subscriptions.vue
+++ b/app/assets/javascripts/sidebar/components/subscriptions/subscriptions.vue
@@ -1,74 +1,74 @@
<script>
- import { __ } from '~/locale';
- import icon from '~/vue_shared/components/icon.vue';
- import toggleButton from '~/vue_shared/components/toggle_button.vue';
- import tooltip from '~/vue_shared/directives/tooltip';
- import eventHub from '../../event_hub';
+import { __ } from '~/locale';
+import icon from '~/vue_shared/components/icon.vue';
+import toggleButton from '~/vue_shared/components/toggle_button.vue';
+import tooltip from '~/vue_shared/directives/tooltip';
+import eventHub from '../../event_hub';
- const ICON_ON = 'notifications';
- const ICON_OFF = 'notifications-off';
- const LABEL_ON = __('Notifications on');
- const LABEL_OFF = __('Notifications off');
+const ICON_ON = 'notifications';
+const ICON_OFF = 'notifications-off';
+const LABEL_ON = __('Notifications on');
+const LABEL_OFF = __('Notifications off');
- export default {
- directives: {
- tooltip,
+export default {
+ directives: {
+ tooltip,
+ },
+ components: {
+ icon,
+ toggleButton,
+ },
+ props: {
+ loading: {
+ type: Boolean,
+ required: false,
+ default: false,
},
- components: {
- icon,
- toggleButton,
+ subscribed: {
+ type: Boolean,
+ required: false,
+ default: null,
},
- props: {
- loading: {
- type: Boolean,
- required: false,
- default: false,
- },
- subscribed: {
- type: Boolean,
- required: false,
- default: null,
- },
- id: {
- type: Number,
- required: false,
- default: null,
- },
+ id: {
+ type: Number,
+ required: false,
+ default: null,
},
- computed: {
- showLoadingState() {
- return this.subscribed === null;
- },
- notificationIcon() {
- return this.subscribed ? ICON_ON : ICON_OFF;
- },
- notificationTooltip() {
- return this.subscribed ? LABEL_ON : LABEL_OFF;
- },
+ },
+ computed: {
+ showLoadingState() {
+ return this.subscribed === null;
},
- methods: {
- /**
- * We need to emit this event on both component & eventHub
- * for 2 dependencies;
- *
- * 1. eventHub: This component is used in Issue Boards sidebar
- * where component template is part of HAML
- * and event listeners are tied to app's eventHub.
- * 2. Component: This compone is also used in Epics in EE
- * where listeners are tied to component event.
- */
- toggleSubscription() {
- // App's eventHub event emission.
- eventHub.$emit('toggleSubscription', this.id);
+ notificationIcon() {
+ return this.subscribed ? ICON_ON : ICON_OFF;
+ },
+ notificationTooltip() {
+ return this.subscribed ? LABEL_ON : LABEL_OFF;
+ },
+ },
+ methods: {
+ /**
+ * We need to emit this event on both component & eventHub
+ * for 2 dependencies;
+ *
+ * 1. eventHub: This component is used in Issue Boards sidebar
+ * where component template is part of HAML
+ * and event listeners are tied to app's eventHub.
+ * 2. Component: This compone is also used in Epics in EE
+ * where listeners are tied to component event.
+ */
+ toggleSubscription() {
+ // App's eventHub event emission.
+ eventHub.$emit('toggleSubscription', this.id);
- // Component event emission.
- this.$emit('toggleSubscription', this.id);
- },
- onClickCollapsedIcon() {
- this.$emit('toggleSidebar');
- },
+ // Component event emission.
+ this.$emit('toggleSubscription', this.id);
+ },
+ onClickCollapsedIcon() {
+ this.$emit('toggleSidebar');
},
- };
+ },
+};
</script>
<template>
diff --git a/app/assets/javascripts/sidebar/sidebar_mediator.js b/app/assets/javascripts/sidebar/sidebar_mediator.js
index d9ca5e46770..3e040ec8428 100644
--- a/app/assets/javascripts/sidebar/sidebar_mediator.js
+++ b/app/assets/javascripts/sidebar/sidebar_mediator.js
@@ -39,9 +39,10 @@ export default class SidebarMediator {
}
fetch() {
- return this.service.get()
+ return this.service
+ .get()
.then(response => response.json())
- .then((data) => {
+ .then(data => {
this.processFetchedData(data);
})
.catch(() => new Flash('Error occurred when fetching sidebar data'));
@@ -56,30 +57,33 @@ export default class SidebarMediator {
toggleSubscription() {
this.store.setFetchingState('subscriptions', true);
- return this.service.toggleSubscription()
+ return this.service
+ .toggleSubscription()
.then(() => {
this.store.setSubscribedState(!this.store.subscribed);
this.store.setFetchingState('subscriptions', false);
})
- .catch((err) => {
+ .catch(err => {
this.store.setFetchingState('subscriptions', false);
throw err;
});
}
fetchAutocompleteProjects(searchTerm) {
- return this.service.getProjectsAutocomplete(searchTerm)
+ return this.service
+ .getProjectsAutocomplete(searchTerm)
.then(response => response.json())
- .then((data) => {
+ .then(data => {
this.store.setAutocompleteProjects(data);
return this.store.autocompleteProjects;
});
}
moveIssue() {
- return this.service.moveIssue(this.store.moveToProjectId)
+ return this.service
+ .moveIssue(this.store.moveToProjectId)
.then(response => response.json())
- .then((data) => {
+ .then(data => {
if (window.location.pathname !== data.web_url) {
visitUrl(data.web_url);
}
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_pipeline.vue b/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_pipeline.vue
index bd1946f337e..8bcabc10225 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_pipeline.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_pipeline.vue
@@ -63,10 +63,15 @@ export default {
return this.pipeline.commit && Object.keys(this.pipeline.commit).length > 0;
},
errorText() {
- return sprintf(__('Could not retrieve the pipeline status. For troubleshooting steps, read the %{linkStart}documentation.%{linkEnd}'), {
- linkStart: `<a href="${this.troubleshootingDocsPath}">`,
- linkEnd: '</a>',
- });
+ return sprintf(
+ __(
+ 'Could not retrieve the pipeline status. For troubleshooting steps, read the %{linkStart}documentation.%{linkEnd}',
+ ),
+ {
+ linkStart: `<a href="${this.troubleshootingDocsPath}">`,
+ linkEnd: '</a>',
+ },
+ );
},
},
};
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/ready_to_merge.vue b/app/assets/javascripts/vue_merge_request_widget/components/states/ready_to_merge.vue
index c8ad2aa30a6..e7baecbcde4 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/states/ready_to_merge.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/components/states/ready_to_merge.vue
@@ -71,7 +71,12 @@ export default {
return defaultClass;
},
iconClass() {
- if (this.status === 'failed' || !this.commitMessage.length || !this.mr.isMergeAllowed || this.mr.preventMerge) {
+ if (
+ this.status === 'failed' ||
+ !this.commitMessage.length ||
+ !this.mr.isMergeAllowed ||
+ this.mr.preventMerge
+ ) {
return 'warning';
}
return 'success';
@@ -90,10 +95,12 @@ export default {
},
isMergeButtonDisabled() {
const { commitMessage } = this;
- return Boolean(!commitMessage.length
- || !this.shouldShowMergeControls()
- || this.isMakingRequest
- || this.mr.preventMerge);
+ return Boolean(
+ !commitMessage.length ||
+ !this.shouldShowMergeControls() ||
+ this.isMakingRequest ||
+ this.mr.preventMerge,
+ );
},
isRemoveSourceBranchButtonDisabled() {
return this.isMergeButtonDisabled;
@@ -140,9 +147,10 @@ export default {
};
this.isMakingRequest = true;
- this.service.merge(options)
+ this.service
+ .merge(options)
.then(res => res.data)
- .then((data) => {
+ .then(data => {
const hasError = data.status === 'failed' || data.status === 'hook_validation_error';
if (data.status === 'merge_when_pipeline_succeeds') {
@@ -167,9 +175,10 @@ export default {
});
},
handleMergePolling(continuePolling, stopPolling) {
- this.service.poll()
+ this.service
+ .poll()
.then(res => res.data)
- .then((data) => {
+ .then(data => {
if (data.state === 'merged') {
// If state is merged we should update the widget and stop the polling
eventHub.$emit('MRWidgetUpdateRequested');
@@ -205,9 +214,10 @@ export default {
});
},
handleRemoveBranchPolling(continuePolling, stopPolling) {
- this.service.poll()
+ this.service
+ .poll()
.then(res => res.data)
- .then((data) => {
+ .then(data => {
// If source branch exists then we should continue polling
// because removing a source branch is a background task and takes time
if (data.source_branch_exists) {
diff --git a/app/assets/javascripts/vue_merge_request_widget/mr_widget_options.vue b/app/assets/javascripts/vue_merge_request_widget/mr_widget_options.vue
index 33064f82ef7..063d1e15544 100644
--- a/app/assets/javascripts/vue_merge_request_widget/mr_widget_options.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/mr_widget_options.vue
@@ -116,7 +116,7 @@ export default {
// init polling
this.initPostMergeDeploymentsPolling();
}
- }
+ },
},
created() {
this.initPolling();
@@ -213,17 +213,21 @@ export default {
})
.catch(() => this.throwDeploymentsError());
},
- fetchPostMergeDeployments(){
+ fetchPostMergeDeployments() {
return this.fetchDeployments('merge_commit')
.then(({ data }) => {
if (data.length) {
this.mr.postMergeDeployments = data;
}
})
- .catch(() => this.throwDeploymentsError());
+ .catch(() => this.throwDeploymentsError());
},
throwDeploymentsError() {
- createFlash(__('Something went wrong while fetching the environments for this merge request. Please try again.'));
+ createFlash(
+ __(
+ 'Something went wrong while fetching the environments for this merge request. Please try again.',
+ ),
+ );
},
fetchActionsContent() {
this.service
diff --git a/app/assets/javascripts/vue_merge_request_widget/services/mr_widget_service.js b/app/assets/javascripts/vue_merge_request_widget/services/mr_widget_service.js
index bf5b85b2ae6..0bb70bfd658 100644
--- a/app/assets/javascripts/vue_merge_request_widget/services/mr_widget_service.js
+++ b/app/assets/javascripts/vue_merge_request_widget/services/mr_widget_service.js
@@ -24,8 +24,8 @@ export default class MRWidgetService {
fetchDeployments(targetParam) {
return axios.get(this.endpoints.ciEnvironmentsStatusPath, {
params: {
- environment_target: targetParam
- }
+ environment_target: targetParam,
+ },
});
}
diff --git a/app/assets/javascripts/vue_merge_request_widget/stores/mr_widget_store.js b/app/assets/javascripts/vue_merge_request_widget/stores/mr_widget_store.js
index c17e9c9e4d6..5c9a7133a6e 100644
--- a/app/assets/javascripts/vue_merge_request_widget/stores/mr_widget_store.js
+++ b/app/assets/javascripts/vue_merge_request_widget/stores/mr_widget_store.js
@@ -18,8 +18,7 @@ export default class MergeRequestStore {
this.squash = data.squash;
this.squashBeforeMergeHelpPath =
this.squashBeforeMergeHelpPath || data.squash_before_merge_help_path;
- this.troubleshootingDocsPath =
- this.troubleshootingDocsPath || data.troubleshooting_docs_path;
+ this.troubleshootingDocsPath = this.troubleshootingDocsPath || data.troubleshooting_docs_path;
this.enableSquashBeforeMerge = this.enableSquashBeforeMerge || true;
this.iid = data.iid;
diff --git a/app/assets/javascripts/vue_shared/components/commit.vue b/app/assets/javascripts/vue_shared/components/commit.vue
index 13bca99dcb3..151eee75d44 100644
--- a/app/assets/javascripts/vue_shared/components/commit.vue
+++ b/app/assets/javascripts/vue_shared/components/commit.vue
@@ -13,7 +13,7 @@ export default {
},
props: {
/**
- * Indicates the existance of a tag.
+ * Indicates the existence of a tag.
* Used to render the correct icon, if true will render `fa-tag` icon,
* if false will render a svg sprite fork icon
*/
diff --git a/app/assets/javascripts/vue_shared/components/filtered_search_dropdown.vue b/app/assets/javascripts/vue_shared/components/filtered_search_dropdown.vue
index 460fa6ad72e..388a2f4ca36 100644
--- a/app/assets/javascripts/vue_shared/components/filtered_search_dropdown.vue
+++ b/app/assets/javascripts/vue_shared/components/filtered_search_dropdown.vue
@@ -56,12 +56,14 @@ export default {
filteredResults() {
if (this.filter !== '') {
return this.items.filter(
- item => item[this.filterKey] && item[this.filterKey].toLowerCase().includes(this.filter.toLowerCase()),
+ item =>
+ item[this.filterKey] &&
+ item[this.filterKey].toLowerCase().includes(this.filter.toLowerCase()),
);
}
return this.items.slice(0, this.visibleItems);
- }
+ },
},
mounted() {
/**
diff --git a/app/assets/javascripts/vue_shared/components/icon.vue b/app/assets/javascripts/vue_shared/components/icon.vue
index 26f9d5ddc91..cddebfae115 100644
--- a/app/assets/javascripts/vue_shared/components/icon.vue
+++ b/app/assets/javascripts/vue_shared/components/icon.vue
@@ -8,7 +8,7 @@ let iconValidator = () => true;
*/
if (process.env.NODE_ENV !== 'production') {
// eslint-disable-next-line global-require
- const data = require('@gitlab-org/gitlab-svgs/dist/icons.json');
+ const data = require('@gitlab/svgs/dist/icons.json');
const { icons } = data;
iconValidator = value => {
if (icons.includes(value)) {
diff --git a/app/assets/javascripts/vue_shared/components/sidebar/collapsed_calendar_icon.vue b/app/assets/javascripts/vue_shared/components/sidebar/collapsed_calendar_icon.vue
index 11fac3bb12c..5841db52704 100644
--- a/app/assets/javascripts/vue_shared/components/sidebar/collapsed_calendar_icon.vue
+++ b/app/assets/javascripts/vue_shared/components/sidebar/collapsed_calendar_icon.vue
@@ -1,39 +1,39 @@
<script>
- import tooltip from '~/vue_shared/directives/tooltip';
+import tooltip from '~/vue_shared/directives/tooltip';
- export default {
- name: 'CollapsedCalendarIcon',
- directives: {
- tooltip,
+export default {
+ name: 'CollapsedCalendarIcon',
+ directives: {
+ tooltip,
+ },
+ props: {
+ containerClass: {
+ type: String,
+ required: false,
+ default: '',
},
- props: {
- containerClass: {
- type: String,
- required: false,
- default: '',
- },
- text: {
- type: String,
- required: false,
- default: '',
- },
- showIcon: {
- type: Boolean,
- required: false,
- default: true,
- },
- tooltipText: {
- type: String,
- required: false,
- default: '',
- },
+ text: {
+ type: String,
+ required: false,
+ default: '',
},
- methods: {
- click() {
- this.$emit('click');
- },
+ showIcon: {
+ type: Boolean,
+ required: false,
+ default: true,
},
- };
+ tooltipText: {
+ type: String,
+ required: false,
+ default: '',
+ },
+ },
+ methods: {
+ click() {
+ this.$emit('click');
+ },
+ },
+};
</script>
<template>
diff --git a/app/assets/javascripts/vue_shared/components/sidebar/collapsed_grouped_date_picker.vue b/app/assets/javascripts/vue_shared/components/sidebar/collapsed_grouped_date_picker.vue
index 6e7194ccc9e..174c29809ac 100644
--- a/app/assets/javascripts/vue_shared/components/sidebar/collapsed_grouped_date_picker.vue
+++ b/app/assets/javascripts/vue_shared/components/sidebar/collapsed_grouped_date_picker.vue
@@ -1,84 +1,79 @@
<script>
- import { __ } from '~/locale';
- import timeagoMixin from '~/vue_shared/mixins/timeago';
- import { dateInWords, timeFor } from '~/lib/utils/datetime_utility';
- import collapsedCalendarIcon from './collapsed_calendar_icon.vue';
+import { __ } from '~/locale';
+import timeagoMixin from '~/vue_shared/mixins/timeago';
+import { dateInWords, timeFor } from '~/lib/utils/datetime_utility';
+import collapsedCalendarIcon from './collapsed_calendar_icon.vue';
- export default {
- name: 'SidebarCollapsedGroupedDatePicker',
- components: {
- collapsedCalendarIcon,
+export default {
+ name: 'SidebarCollapsedGroupedDatePicker',
+ components: {
+ collapsedCalendarIcon,
+ },
+ mixins: [timeagoMixin],
+ props: {
+ collapsed: {
+ type: Boolean,
+ required: false,
+ default: true,
},
- mixins: [
- timeagoMixin,
- ],
- props: {
- collapsed: {
- type: Boolean,
- required: false,
- default: true,
- },
- minDate: {
- type: Date,
- required: false,
- default: null,
- },
- maxDate: {
- type: Date,
- required: false,
- default: null,
- },
- disableClickableIcons: {
- type: Boolean,
- required: false,
- default: false,
- },
+ minDate: {
+ type: Date,
+ required: false,
+ default: null,
},
- computed: {
- hasMinAndMaxDates() {
- return this.minDate && this.maxDate;
- },
- hasNoMinAndMaxDates() {
- return !this.minDate && !this.maxDate;
- },
- showMinDateBlock() {
- return this.minDate || this.hasNoMinAndMaxDates;
- },
- showFromText() {
- return !this.maxDate && this.minDate;
- },
- iconClass() {
- const disabledClass = this.disableClickableIcons ? 'disabled' : '';
- return `sidebar-collapsed-icon calendar-icon ${disabledClass}`;
- },
+ maxDate: {
+ type: Date,
+ required: false,
+ default: null,
},
- methods: {
- toggleSidebar() {
- this.$emit('toggleCollapse');
- },
- dateText(dateType = 'min') {
- const date = this[`${dateType}Date`];
- const dateWords = dateInWords(date, true);
- const parsedDateWords = dateWords ? dateWords.replace(',', '') : dateWords;
+ disableClickableIcons: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
+ },
+ computed: {
+ hasMinAndMaxDates() {
+ return this.minDate && this.maxDate;
+ },
+ hasNoMinAndMaxDates() {
+ return !this.minDate && !this.maxDate;
+ },
+ showMinDateBlock() {
+ return this.minDate || this.hasNoMinAndMaxDates;
+ },
+ showFromText() {
+ return !this.maxDate && this.minDate;
+ },
+ iconClass() {
+ const disabledClass = this.disableClickableIcons ? 'disabled' : '';
+ return `sidebar-collapsed-icon calendar-icon ${disabledClass}`;
+ },
+ },
+ methods: {
+ toggleSidebar() {
+ this.$emit('toggleCollapse');
+ },
+ dateText(dateType = 'min') {
+ const date = this[`${dateType}Date`];
+ const dateWords = dateInWords(date, true);
+ const parsedDateWords = dateWords ? dateWords.replace(',', '') : dateWords;
- return date ? parsedDateWords : __('None');
- },
- tooltipText(dateType = 'min') {
- const defaultText = dateType === 'min' ? __('Start date') : __('Due date');
- const date = this[`${dateType}Date`];
- const timeAgo = dateType === 'min' ? this.timeFormated(date) : timeFor(date);
- const dateText = date ? [
- this.dateText(dateType),
- `(${timeAgo})`,
- ].join(' ') : '';
+ return date ? parsedDateWords : __('None');
+ },
+ tooltipText(dateType = 'min') {
+ const defaultText = dateType === 'min' ? __('Start date') : __('Due date');
+ const date = this[`${dateType}Date`];
+ const timeAgo = dateType === 'min' ? this.timeFormated(date) : timeFor(date);
+ const dateText = date ? [this.dateText(dateType), `(${timeAgo})`].join(' ') : '';
- if (date) {
- return [defaultText, dateText].join('<br />');
- }
- return __('Start and due date');
- },
+ if (date) {
+ return [defaultText, dateText].join('<br />');
+ }
+ return __('Start and due date');
},
- };
+ },
+};
</script>
<template>
diff --git a/app/assets/javascripts/vue_shared/components/sidebar/labels_select/dropdown_value_collapsed.vue b/app/assets/javascripts/vue_shared/components/sidebar/labels_select/dropdown_value_collapsed.vue
index af297f3c408..0d5fc07e6e3 100644
--- a/app/assets/javascripts/vue_shared/components/sidebar/labels_select/dropdown_value_collapsed.vue
+++ b/app/assets/javascripts/vue_shared/components/sidebar/labels_select/dropdown_value_collapsed.vue
@@ -14,7 +14,10 @@ export default {
},
computed: {
labelsList() {
- const labelsString = this.labels.slice(0, 5).map(label => label.title).join(', ');
+ const labelsString = this.labels
+ .slice(0, 5)
+ .map(label => label.title)
+ .join(', ');
if (this.labels.length > 5) {
return sprintf(s__('LabelSelect|%{labelsString}, and %{remainingLabelCount} more'), {
diff --git a/app/assets/stylesheets/framework/dropdowns.scss b/app/assets/stylesheets/framework/dropdowns.scss
index cdfad30e7ca..dca89981d81 100644
--- a/app/assets/stylesheets/framework/dropdowns.scss
+++ b/app/assets/stylesheets/framework/dropdowns.scss
@@ -158,7 +158,7 @@
color: $gl-text-color;
outline: 0;
- // make sure the text color is not overriden
+ // make sure the text color is not overridden
&.text-danger {
color: $brand-danger;
}
@@ -184,7 +184,7 @@
text-align: left;
width: 100%;
- // make sure the text color is not overriden
+ // make sure the text color is not overridden
&.text-danger {
color: $brand-danger;
}
diff --git a/app/assets/stylesheets/framework/secondary_navigation_elements.scss b/app/assets/stylesheets/framework/secondary_navigation_elements.scss
index f47dfe1b563..de9e7c37695 100644
--- a/app/assets/stylesheets/framework/secondary_navigation_elements.scss
+++ b/app/assets/stylesheets/framework/secondary_navigation_elements.scss
@@ -1,6 +1,6 @@
// For tabbed navigation links, scrolling tabs, etc. For all top/main navigation,
// please check nav.scss
-.nav-links {
+.nav-links:not(.quick-links) {
display: flex;
padding: 0;
margin: 0;
@@ -106,7 +106,7 @@
display: inline-block;
float: right;
text-align: right;
- padding: 11px 0;
+ padding: $gl-padding-8 0;
margin-bottom: 0;
> .btn,
diff --git a/app/controllers/admin/appearances_controller.rb b/app/controllers/admin/appearances_controller.rb
index fdd3b4126ff..e3226c86b0b 100644
--- a/app/controllers/admin/appearances_controller.rb
+++ b/app/controllers/admin/appearances_controller.rb
@@ -33,21 +33,21 @@ class Admin::AppearancesController < Admin::ApplicationController
@appearance.save
- redirect_to admin_appearances_path, notice: 'Logo was succesfully removed.'
+ redirect_to admin_appearances_path, notice: 'Logo was successfully removed.'
end
def header_logos
@appearance.remove_header_logo!
@appearance.save
- redirect_to admin_appearances_path, notice: 'Header logo was succesfully removed.'
+ redirect_to admin_appearances_path, notice: 'Header logo was successfully removed.'
end
def favicon
@appearance.remove_favicon!
@appearance.save
- redirect_to admin_appearances_path, notice: 'Favicon was succesfully removed.'
+ redirect_to admin_appearances_path, notice: 'Favicon was successfully removed.'
end
private
diff --git a/app/controllers/import/gitea_controller.rb b/app/controllers/import/gitea_controller.rb
index 382c684a408..f067ef625aa 100644
--- a/app/controllers/import/gitea_controller.rb
+++ b/app/controllers/import/gitea_controller.rb
@@ -23,7 +23,7 @@ class Import::GiteaController < Import::GithubController
:"#{provider}_host_url"
end
- # Overriden methods
+ # Overridden methods
def provider
:gitea
end
diff --git a/app/controllers/import/github_controller.rb b/app/controllers/import/github_controller.rb
index e3eec5a020d..58565aaf8c9 100644
--- a/app/controllers/import/github_controller.rb
+++ b/app/controllers/import/github_controller.rb
@@ -103,7 +103,7 @@ class Import::GithubController < Import::BaseController
{ github_access_token: session[access_token_key] }
end
- # The following methods are overriden in subclasses
+ # The following methods are overridden in subclasses
def provider
:github
end
diff --git a/app/controllers/oauth/authorizations_controller.rb b/app/controllers/oauth/authorizations_controller.rb
index 894a6a431e3..705389749d8 100644
--- a/app/controllers/oauth/authorizations_controller.rb
+++ b/app/controllers/oauth/authorizations_controller.rb
@@ -3,7 +3,7 @@
class Oauth::AuthorizationsController < Doorkeeper::AuthorizationsController
layout 'profile'
- # Overriden from Doorkeeper::AuthorizationsController to
+ # Overridden from Doorkeeper::AuthorizationsController to
# include the call to session.delete
def new
if pre_auth.authorizable?
diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb
index ee438e160f2..7f4a9f5151b 100644
--- a/app/controllers/projects_controller.rb
+++ b/app/controllers/projects_controller.rb
@@ -276,7 +276,7 @@ class ProjectsController < Projects::ApplicationController
# rubocop: enable CodeReuse/ActiveRecord
# Render project landing depending of which features are available
- # So if page is not availble in the list it renders the next page
+ # So if page is not available in the list it renders the next page
#
# pages list order: repository readme, wiki home, issues list, customize workflow
def render_landing_page
diff --git a/app/finders/autocomplete/users_finder.rb b/app/finders/autocomplete/users_finder.rb
index e2283f3266e..45955783be9 100644
--- a/app/finders/autocomplete/users_finder.rb
+++ b/app/finders/autocomplete/users_finder.rb
@@ -72,7 +72,6 @@ module Autocomplete
author_id.present? && current_user
end
- # rubocop: disable CodeReuse/ActiveRecord
def find_users
if project
project.authorized_users.union_with_user(author_id)
@@ -84,6 +83,5 @@ module Autocomplete
User.none
end
end
- # rubocop: enable CodeReuse/ActiveRecord
end
end
diff --git a/app/finders/concerns/finder_with_cross_project_access.rb b/app/finders/concerns/finder_with_cross_project_access.rb
index e038636f0c4..220f62bcc7f 100644
--- a/app/finders/concerns/finder_with_cross_project_access.rb
+++ b/app/finders/concerns/finder_with_cross_project_access.rb
@@ -16,7 +16,6 @@ module FinderWithCrossProjectAccess
end
override :execute
- # rubocop: disable CodeReuse/ActiveRecord
def execute(*args)
check = Gitlab::CrossProjectAccess.find_check(self)
original = super
@@ -30,7 +29,6 @@ module FinderWithCrossProjectAccess
original
end
end
- # rubocop: enable CodeReuse/ActiveRecord
# We can skip the cross project check for finding indivitual records.
# this would be handled by the `can?(:read_*, result)` call in `FinderMethods`
diff --git a/app/finders/group_descendants_finder.rb b/app/finders/group_descendants_finder.rb
index 9d57d2d3bc9..c96979619fd 100644
--- a/app/finders/group_descendants_finder.rb
+++ b/app/finders/group_descendants_finder.rb
@@ -131,7 +131,6 @@ class GroupDescendantsFinder
.with_selects_for_list(archived: params[:archived])
end
- # rubocop: disable CodeReuse/ActiveRecord
def subgroups
return Group.none unless Group.supports_nested_groups?
@@ -145,7 +144,6 @@ class GroupDescendantsFinder
groups.with_selects_for_list(archived: params[:archived]).order_by(sort)
end
- # rubocop: enable CodeReuse/ActiveRecord
# rubocop: disable CodeReuse/Finder
def direct_child_projects
diff --git a/app/finders/groups_finder.rb b/app/finders/groups_finder.rb
index a35a3ed6142..ea954f98220 100644
--- a/app/finders/groups_finder.rb
+++ b/app/finders/groups_finder.rb
@@ -40,7 +40,6 @@ class GroupsFinder < UnionFinder
attr_reader :current_user, :params
- # rubocop: disable CodeReuse/ActiveRecord
def all_groups
return [owned_groups] if params[:owned]
return [groups_with_min_access_level] if min_access_level?
@@ -52,7 +51,6 @@ class GroupsFinder < UnionFinder
groups << Group.none if groups.empty?
groups
end
- # rubocop: enable CodeReuse/ActiveRecord
def groups_for_ancestors
current_user.authorized_groups
@@ -82,11 +80,9 @@ class GroupsFinder < UnionFinder
end
# rubocop: enable CodeReuse/ActiveRecord
- # rubocop: disable CodeReuse/ActiveRecord
def owned_groups
current_user&.owned_groups || Group.none
end
- # rubocop: enable CodeReuse/ActiveRecord
def include_public_groups?
current_user.nil? || all_available?
diff --git a/app/finders/issuable_finder.rb b/app/finders/issuable_finder.rb
index eb3d2498830..93bef592c65 100644
--- a/app/finders/issuable_finder.rb
+++ b/app/finders/issuable_finder.rb
@@ -192,11 +192,6 @@ class IssuableFinder
params[:milestone_title].present?
end
- def filter_by_no_milestone?
- milestones? && params[:milestone_title] == Milestone::None.title
- end
-
- # rubocop: disable CodeReuse/ActiveRecord
def milestones
return @milestones if defined?(@milestones)
@@ -217,7 +212,6 @@ class IssuableFinder
Milestone.none
end
end
- # rubocop: enable CodeReuse/ActiveRecord
def labels?
params[:label_name].present?
@@ -227,7 +221,6 @@ class IssuableFinder
labels? && params[:label_name].include?(Label::None.title)
end
- # rubocop: disable CodeReuse/ActiveRecord
def labels
return @labels if defined?(@labels)
@@ -238,7 +231,6 @@ class IssuableFinder
Label.none
end
end
- # rubocop: enable CodeReuse/ActiveRecord
def assignee_id?
params[:assignee_id].present?
@@ -248,15 +240,6 @@ class IssuableFinder
params[:assignee_username].present?
end
- def filter_by_no_assignee?
- # Assignee_id takes precedence over assignee_username
- [NONE, FILTER_NONE].include?(params[:assignee_id].to_s.downcase) || params[:assignee_username].to_s == NONE
- end
-
- def filter_by_any_assignee?
- params[:assignee_id].to_s.downcase == FILTER_ANY
- end
-
# rubocop: disable CodeReuse/ActiveRecord
def assignee
return @assignee if defined?(@assignee)
@@ -422,6 +405,15 @@ class IssuableFinder
end
# rubocop: enable CodeReuse/ActiveRecord
+ def filter_by_no_assignee?
+ # Assignee_id takes precedence over assignee_username
+ [NONE, FILTER_NONE].include?(params[:assignee_id].to_s.downcase) || params[:assignee_username].to_s == NONE
+ end
+
+ def filter_by_any_assignee?
+ params[:assignee_id].to_s.downcase == FILTER_ANY
+ end
+
# rubocop: disable CodeReuse/ActiveRecord
def by_author(items)
if author
@@ -436,18 +428,6 @@ class IssuableFinder
end
# rubocop: enable CodeReuse/ActiveRecord
- def filter_by_upcoming_milestone?
- params[:milestone_title] == Milestone::Upcoming.name
- end
-
- def filter_by_any_milestone?
- params[:milestone_title] == Milestone::Any.title
- end
-
- def filter_by_started_milestone?
- params[:milestone_title] == Milestone::Started.name
- end
-
# rubocop: disable CodeReuse/ActiveRecord
def by_milestone(items)
if milestones?
@@ -469,6 +449,24 @@ class IssuableFinder
end
# rubocop: enable CodeReuse/ActiveRecord
+ def filter_by_no_milestone?
+ # Accepts `No Milestone` for compatibility
+ params[:milestone_title].to_s.downcase == FILTER_NONE || params[:milestone_title] == Milestone::None.title
+ end
+
+ def filter_by_any_milestone?
+ # Accepts `Any Milestone` for compatibility
+ params[:milestone_title].to_s.downcase == FILTER_ANY || params[:milestone_title] == Milestone::Any.title
+ end
+
+ def filter_by_upcoming_milestone?
+ params[:milestone_title] == Milestone::Upcoming.name
+ end
+
+ def filter_by_started_milestone?
+ params[:milestone_title] == Milestone::Started.name
+ end
+
def by_label(items)
return items unless labels?
@@ -484,12 +482,27 @@ class IssuableFinder
def by_my_reaction_emoji(items)
if params[:my_reaction_emoji].present? && current_user
- items = items.awarded(current_user, params[:my_reaction_emoji])
+ items =
+ if filter_by_no_reaction?
+ items.not_awarded(current_user)
+ elsif filter_by_any_reaction?
+ items.awarded(current_user)
+ else
+ items.awarded(current_user, params[:my_reaction_emoji])
+ end
end
items
end
+ def filter_by_no_reaction?
+ params[:my_reaction_emoji].to_s.downcase == FILTER_NONE
+ end
+
+ def filter_by_any_reaction?
+ params[:my_reaction_emoji].to_s.downcase == FILTER_ANY
+ end
+
def label_names
if labels?
params[:label_name].is_a?(String) ? params[:label_name].split(',') : params[:label_name]
diff --git a/app/finders/issues_finder.rb b/app/finders/issues_finder.rb
index cee57a83df4..45e494725d7 100644
--- a/app/finders/issues_finder.rb
+++ b/app/finders/issues_finder.rb
@@ -135,7 +135,6 @@ class IssuesFinder < IssuableFinder
current_user.blank?
end
- # rubocop: disable CodeReuse/ActiveRecord
def by_assignee(items)
if filter_by_no_assignee?
items.unassigned
@@ -149,5 +148,4 @@ class IssuesFinder < IssuableFinder
items
end
end
- # rubocop: enable CodeReuse/ActiveRecord
end
diff --git a/app/finders/labels_finder.rb b/app/finders/labels_finder.rb
index d000af21be3..e523942ea4c 100644
--- a/app/finders/labels_finder.rb
+++ b/app/finders/labels_finder.rb
@@ -12,7 +12,6 @@ class LabelsFinder < UnionFinder
@params = params
end
- # rubocop: disable CodeReuse/ActiveRecord
def execute(skip_authorization: false)
@skip_authorization = skip_authorization
items = find_union(label_ids, Label) || Label.none
@@ -21,7 +20,6 @@ class LabelsFinder < UnionFinder
items = by_search(items)
sort(items)
end
- # rubocop: enable CodeReuse/ActiveRecord
private
diff --git a/app/finders/milestones_finder.rb b/app/finders/milestones_finder.rb
index 47231ea80c7..9c477978f60 100644
--- a/app/finders/milestones_finder.rb
+++ b/app/finders/milestones_finder.rb
@@ -20,7 +20,6 @@ class MilestonesFinder
@params = params
end
- # rubocop: disable CodeReuse/ActiveRecord
def execute
return Milestone.none if project_ids.empty? && group_ids.empty?
@@ -31,7 +30,6 @@ class MilestonesFinder
order(items)
end
- # rubocop: enable CodeReuse/ActiveRecord
private
diff --git a/app/finders/pipelines_finder.rb b/app/finders/pipelines_finder.rb
index 3d0d3219a94..35d0e1acce5 100644
--- a/app/finders/pipelines_finder.rb
+++ b/app/finders/pipelines_finder.rb
@@ -12,7 +12,6 @@ class PipelinesFinder
@params = params
end
- # rubocop: disable CodeReuse/ActiveRecord
def execute
unless Ability.allowed?(current_user, :read_pipeline, project)
return Ci::Pipeline.none
@@ -28,7 +27,6 @@ class PipelinesFinder
items = by_yaml_errors(items)
sort_items(items)
end
- # rubocop: enable CodeReuse/ActiveRecord
private
diff --git a/app/finders/projects_finder.rb b/app/finders/projects_finder.rb
index 6ececcd4152..93d3c991846 100644
--- a/app/finders/projects_finder.rb
+++ b/app/finders/projects_finder.rb
@@ -88,7 +88,6 @@ class ProjectsFinder < UnionFinder
# rubocop: enable CodeReuse/ActiveRecord
# Builds a collection for an anonymous user.
- # rubocop: disable CodeReuse/ActiveRecord
def collection_without_user
if private_only? || owned_projects? || min_access_level?
Project.none
@@ -96,7 +95,6 @@ class ProjectsFinder < UnionFinder
Project.public_to_user
end
end
- # rubocop: enable CodeReuse/ActiveRecord
def owned_projects?
params[:owned].present?
diff --git a/app/finders/snippets_finder.rb b/app/finders/snippets_finder.rb
index 3528e4228b2..f90971bb9f6 100644
--- a/app/finders/snippets_finder.rb
+++ b/app/finders/snippets_finder.rb
@@ -43,7 +43,6 @@ class SnippetsFinder < UnionFinder
end
end
- # rubocop: disable CodeReuse/ActiveRecord
def authorized_snippets_from_project
if can?(current_user, :read_project_snippet, project)
if project.team.member?(current_user)
@@ -55,7 +54,6 @@ class SnippetsFinder < UnionFinder
Snippet.none
end
end
- # rubocop: enable CodeReuse/ActiveRecord
# rubocop: disable CodeReuse/ActiveRecord
def authorized_snippets
diff --git a/app/helpers/icons_helper.rb b/app/helpers/icons_helper.rb
index 037004327b9..910c9e9446f 100644
--- a/app/helpers/icons_helper.rb
+++ b/app/helpers/icons_helper.rb
@@ -153,6 +153,6 @@ module IconsHelper
private
def known_sprites
- @known_sprites ||= JSON.parse(File.read(Rails.root.join('node_modules/@gitlab-org/gitlab-svgs/dist/icons.json')))['icons']
+ @known_sprites ||= JSON.parse(File.read(Rails.root.join('node_modules/@gitlab/svgs/dist/icons.json')))['icons']
end
end
diff --git a/app/helpers/page_layout_helper.rb b/app/helpers/page_layout_helper.rb
index b33c074d1af..5038dcf9746 100644
--- a/app/helpers/page_layout_helper.rb
+++ b/app/helpers/page_layout_helper.rb
@@ -10,7 +10,7 @@ module PageLayoutHelper
@breadcrumb_title = @page_title.last
end
- # Segments are seperated by middot
+ # Segments are separated by middot
@page_title.join(" · ")
end
diff --git a/app/models/commit_status.rb b/app/models/commit_status.rb
index 7d36f9982ab..95c88e11a6e 100644
--- a/app/models/commit_status.rb
+++ b/app/models/commit_status.rb
@@ -167,12 +167,12 @@ class CommitStatus < ActiveRecord::Base
false
end
- # To be overriden when inherrited from
+ # To be overridden when inherrited from
def retryable?
false
end
- # To be overriden when inherrited from
+ # To be overridden when inherrited from
def cancelable?
false
end
diff --git a/app/models/concerns/awardable.rb b/app/models/concerns/awardable.rb
index 6f29c92d176..60b7ec2815c 100644
--- a/app/models/concerns/awardable.rb
+++ b/app/models/concerns/awardable.rb
@@ -13,13 +13,13 @@ module Awardable
end
class_methods do
- def awarded(user, name)
+ def awarded(user, name = nil)
sql = <<~EOL
EXISTS (
SELECT TRUE
FROM award_emoji
WHERE user_id = :user_id AND
- name = :name AND
+ #{"name = :name AND" if name.present?}
awardable_type = :awardable_type AND
awardable_id = #{self.arel_table.name}.id
)
@@ -28,6 +28,20 @@ module Awardable
where(sql, user_id: user.id, name: name, awardable_type: self.name)
end
+ def not_awarded(user)
+ sql = <<~EOL
+ NOT EXISTS (
+ SELECT TRUE
+ FROM award_emoji
+ WHERE user_id = :user_id AND
+ awardable_type = :awardable_type AND
+ awardable_id = #{self.arel_table.name}.id
+ )
+ EOL
+
+ where(sql, user_id: user.id, awardable_type: self.name)
+ end
+
def order_upvotes_desc
order_votes_desc(AwardEmoji::UPVOTE_NAME)
end
diff --git a/app/models/concerns/cacheable_attributes.rb b/app/models/concerns/cacheable_attributes.rb
index f8034be8376..75592bb63e2 100644
--- a/app/models/concerns/cacheable_attributes.rb
+++ b/app/models/concerns/cacheable_attributes.rb
@@ -12,12 +12,12 @@ module CacheableAttributes
"#{name}:#{Gitlab::VERSION}:#{Rails.version}".freeze
end
- # Can be overriden
+ # Can be overridden
def current_without_cache
last
end
- # Can be overriden
+ # Can be overridden
def defaults
{}
end
diff --git a/app/models/concerns/fast_destroy_all.rb b/app/models/concerns/fast_destroy_all.rb
index c342d01243e..2bfa7da6c1c 100644
--- a/app/models/concerns/fast_destroy_all.rb
+++ b/app/models/concerns/fast_destroy_all.rb
@@ -7,7 +7,7 @@
# `delete_all` is efficient as it deletes all rows with a single `DELETE` query.
#
# It's better to use `delete_all` as our best practice, however,
-# if external data (e.g. ObjectStorage, FileStorage or Redis) are assosiated with database records,
+# if external data (e.g. ObjectStorage, FileStorage or Redis) are associated with database records,
# it is difficult to accomplish it.
#
# This module defines a format to use `delete_all` and delete associated external data.
diff --git a/app/models/concerns/issuable.rb b/app/models/concerns/issuable.rb
index a808f9ad4ad..69c5affe142 100644
--- a/app/models/concerns/issuable.rb
+++ b/app/models/concerns/issuable.rb
@@ -363,7 +363,7 @@ module Issuable
end
##
- # Overriden in MergeRequest
+ # Overridden in MergeRequest
#
def wipless_title_changed(old_title)
old_title != title
diff --git a/app/models/concerns/noteable.rb b/app/models/concerns/noteable.rb
index 098eed137ba..eb315058c3a 100644
--- a/app/models/concerns/noteable.rb
+++ b/app/models/concerns/noteable.rb
@@ -23,7 +23,7 @@ module Noteable
end
def supports_discussions?
- DiscussionNote::NOTEABLE_TYPES.include?(base_class_name)
+ DiscussionNote.noteable_types.include?(base_class_name)
end
def discussions_rendered_on_frontend?
diff --git a/app/models/concerns/storage/legacy_namespace.rb b/app/models/concerns/storage/legacy_namespace.rb
index 7723c07279d..af699eeebce 100644
--- a/app/models/concerns/storage/legacy_namespace.rb
+++ b/app/models/concerns/storage/legacy_namespace.rb
@@ -94,7 +94,7 @@ module Storage
if gitlab_shell.mv_namespace(repository_storage, full_path, new_path)
Gitlab::AppLogger.info %Q(Namespace directory "#{full_path}" moved to "#{new_path}")
- # Remove namespace directroy async with delay so
+ # Remove namespace directory async with delay so
# GitLab has time to remove all projects first
run_after_commit do
GitlabShellWorker.perform_in(5.minutes, :rm_namespace, repository_storage, new_path)
diff --git a/app/models/concerns/with_uploads.rb b/app/models/concerns/with_uploads.rb
index e231af5368d..2bdef2a40e4 100644
--- a/app/models/concerns/with_uploads.rb
+++ b/app/models/concerns/with_uploads.rb
@@ -2,7 +2,7 @@
# Mounted uploaders are destroyed by carrierwave's after_commit
# hook. This hook fetches upload location (local vs remote) from
-# Upload model. So it's neccessary to make sure that during that
+# Upload model. So it's necessary to make sure that during that
# after_commit hook model's associated uploads are not deleted yet.
# IOW we can not use dependent: :destroy :
# has_many :uploads, as: :model, dependent: :destroy
diff --git a/app/models/diff_note.rb b/app/models/diff_note.rb
index 95694377fe3..5f59e4832db 100644
--- a/app/models/diff_note.rb
+++ b/app/models/diff_note.rb
@@ -8,12 +8,14 @@ class DiffNote < Note
include DiffPositionableNote
include Gitlab::Utils::StrongMemoize
- NOTEABLE_TYPES = %w(MergeRequest Commit).freeze
+ def self.noteable_types
+ %w(MergeRequest Commit)
+ end
validates :original_position, presence: true
validates :position, presence: true
validates :line_code, presence: true, line_code: true, if: :on_text?
- validates :noteable_type, inclusion: { in: NOTEABLE_TYPES }
+ validates :noteable_type, inclusion: { in: noteable_types }
validate :positions_complete
validate :verify_supported
validate :diff_refs_match_commit, if: :for_commit?
diff --git a/app/models/discussion_note.rb b/app/models/discussion_note.rb
index 89d86aaed66..142cbdcdfa6 100644
--- a/app/models/discussion_note.rb
+++ b/app/models/discussion_note.rb
@@ -5,9 +5,11 @@
# A note of this type can be resolvable.
class DiscussionNote < Note
# Names of all implementers of `Noteable` that support discussions.
- NOTEABLE_TYPES = %w(MergeRequest Issue Commit Snippet).freeze
+ def self.noteable_types
+ %w(MergeRequest Issue Commit Snippet)
+ end
- validates :noteable_type, inclusion: { in: NOTEABLE_TYPES }
+ validates :noteable_type, inclusion: { in: noteable_types }
def discussion_class(*)
Discussion
diff --git a/app/models/namespace.rb b/app/models/namespace.rb
index 599bedde27d..74d48d0a9af 100644
--- a/app/models/namespace.rb
+++ b/app/models/namespace.rb
@@ -83,7 +83,7 @@ class Namespace < ActiveRecord::Base
find_by('lower(path) = :value', value: path.downcase)
end
- # Case insensetive search for namespace by path or name
+ # Case insensitive search for namespace by path or name
def find_by_path_or_name(path)
find_by("lower(path) = :path OR lower(name) = :path", path: path.downcase)
end
diff --git a/app/models/project.rb b/app/models/project.rb
index d593cbb223a..e2e309e8496 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -181,7 +181,7 @@ class Project < ActiveRecord::Base
has_one :import_export_upload, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent
# Merge Requests for target project should be removed with it
- has_many :merge_requests, foreign_key: 'target_project_id'
+ has_many :merge_requests, foreign_key: 'target_project_id', inverse_of: :target_project
has_many :source_of_merge_requests, foreign_key: 'source_project_id', class_name: 'MergeRequest'
has_many :issues
has_many :labels, class_name: 'ProjectLabel'
@@ -665,7 +665,7 @@ class Project < ActiveRecord::Base
remove_import_data
end
- # This method is overriden in EE::Project model
+ # This method is overridden in EE::Project model
def remove_import_data
import_data&.destroy
end
diff --git a/app/models/project_services/issue_tracker_service.rb b/app/models/project_services/issue_tracker_service.rb
index e1d342be188..7cff0e30e8d 100644
--- a/app/models/project_services/issue_tracker_service.rb
+++ b/app/models/project_services/issue_tracker_service.rb
@@ -9,7 +9,7 @@ class IssueTrackerService < Service
# Override this method on services that uses different patterns
# This pattern does not support cross-project references
# The other code assumes that this pattern is a superset of all
- # overriden patterns. See ReferenceRegexes::EXTERNAL_PATTERN
+ # overridden patterns. See ReferenceRegexes::EXTERNAL_PATTERN
def self.reference_pattern(only_long: false)
if only_long
/(\b[A-Z][A-Z0-9_]+-)(?<issue>\d+)/
diff --git a/app/presenters/project_presenter.rb b/app/presenters/project_presenter.rb
index 79cd3606aec..d61124fa787 100644
--- a/app/presenters/project_presenter.rb
+++ b/app/presenters/project_presenter.rb
@@ -176,7 +176,7 @@ class ProjectPresenter < Gitlab::View::Presenter::Delegated
AnchorData.new(false,
_('New file'),
project_new_blob_path(project, default_branch || 'master'),
- 'new')
+ 'success')
end
end
diff --git a/app/serializers/job_entity.rb b/app/serializers/job_entity.rb
index 0b19cb16955..a0a66511b7b 100644
--- a/app/serializers/job_entity.rb
+++ b/app/serializers/job_entity.rb
@@ -9,7 +9,7 @@ class JobEntity < Grape::Entity
expose :started?, as: :started
expose :build_path do |build|
- build.target_url || path_to(:namespace_project_job, build)
+ build_path(build)
end
expose :retry_path, if: -> (*) { retryable? } do |build|
@@ -17,7 +17,11 @@ class JobEntity < Grape::Entity
end
expose :cancel_path, if: -> (*) { cancelable? } do |build|
- path_to(:cancel_namespace_project_job, build)
+ path_to(
+ :cancel_namespace_project_job,
+ build,
+ { continue: { to: build_path(build) } }
+ )
end
expose :play_path, if: -> (*) { playable? } do |build|
@@ -60,8 +64,12 @@ class JobEntity < Grape::Entity
build.detailed_status(request.current_user)
end
- def path_to(route, build)
- send("#{route}_path", build.project.namespace, build.project, build) # rubocop:disable GitlabSecurity/PublicSend
+ def path_to(route, build, params = {})
+ send("#{route}_path", build.project.namespace, build.project, build, params) # rubocop:disable GitlabSecurity/PublicSend
+ end
+
+ def build_path(build)
+ build.target_url || path_to(:namespace_project_job, build)
end
def failed?
diff --git a/app/services/auth/container_registry_authentication_service.rb b/app/services/auth/container_registry_authentication_service.rb
index 893b37b831a..f764536e762 100644
--- a/app/services/auth/container_registry_authentication_service.rb
+++ b/app/services/auth/container_registry_authentication_service.rb
@@ -99,7 +99,7 @@ module Auth
##
# Because we do not have two way communication with registry yet,
# we create a container repository image resource when push to the
- # registry is successfuly authorized.
+ # registry is successfully authorized.
#
def ensure_container_repository!(path, actions)
return if path.has_repository?
diff --git a/app/services/keys/destroy_service.rb b/app/services/keys/destroy_service.rb
index e2ae4047941..159455f80f3 100644
--- a/app/services/keys/destroy_service.rb
+++ b/app/services/keys/destroy_service.rb
@@ -6,7 +6,7 @@ module Keys
key.destroy if destroy_possible?(key)
end
- # overriden in EE::Keys::DestroyService
+ # overridden in EE::Keys::DestroyService
def destroy_possible?(key)
true
end
diff --git a/app/services/members/base_service.rb b/app/services/members/base_service.rb
index 8248f1441d7..d734571f835 100644
--- a/app/services/members/base_service.rb
+++ b/app/services/members/base_service.rb
@@ -10,7 +10,7 @@ module Members
end
def after_execute(args)
- # overriden in EE::Members modules
+ # overridden in EE::Members modules
end
private
diff --git a/app/services/projects/move_project_authorizations_service.rb b/app/services/projects/move_project_authorizations_service.rb
index 2060a263751..2985ba89014 100644
--- a/app/services/projects/move_project_authorizations_service.rb
+++ b/app/services/projects/move_project_authorizations_service.rb
@@ -3,7 +3,7 @@
# NOTE: This service cannot be used directly because it is part of a
# a bigger process. Instead, use the service MoveAccessService which moves
# project memberships, project group links, authorizations and refreshes
-# the authorizations if neccessary
+# the authorizations if necessary
module Projects
class MoveProjectAuthorizationsService < BaseMoveRelationsService
def execute(source_project, remove_remaining_elements: true)
diff --git a/app/services/projects/move_project_group_links_service.rb b/app/services/projects/move_project_group_links_service.rb
index fb395ecb9a1..36afcd0c503 100644
--- a/app/services/projects/move_project_group_links_service.rb
+++ b/app/services/projects/move_project_group_links_service.rb
@@ -3,7 +3,7 @@
# NOTE: This service cannot be used directly because it is part of a
# a bigger process. Instead, use the service MoveAccessService which moves
# project memberships, project group links, authorizations and refreshes
-# the authorizations if neccessary
+# the authorizations if necessary
module Projects
class MoveProjectGroupLinksService < BaseMoveRelationsService
def execute(source_project, remove_remaining_elements: true)
diff --git a/app/services/projects/move_project_members_service.rb b/app/services/projects/move_project_members_service.rb
index f28f44adc03..faf389241d2 100644
--- a/app/services/projects/move_project_members_service.rb
+++ b/app/services/projects/move_project_members_service.rb
@@ -3,7 +3,7 @@
# NOTE: This service cannot be used directly because it is part of a
# a bigger process. Instead, use the service MoveAccessService which moves
# project memberships, project group links, authorizations and refreshes
-# the authorizations if neccessary
+# the authorizations if necessary
module Projects
class MoveProjectMembersService < BaseMoveRelationsService
def execute(source_project, remove_remaining_elements: true)
diff --git a/app/services/quick_actions/interpret_service.rb b/app/services/quick_actions/interpret_service.rb
index 751aae2696d..eb431c36807 100644
--- a/app/services/quick_actions/interpret_service.rb
+++ b/app/services/quick_actions/interpret_service.rb
@@ -433,14 +433,14 @@ module QuickActions
end
end
- desc 'Add or substract spent time'
+ desc 'Add or subtract spent time'
explanation do |time_spent, time_spent_date|
if time_spent
if time_spent > 0
verb = 'Adds'
value = time_spent
else
- verb = 'Substracts'
+ verb = 'Subtracts'
value = -time_spent
end
diff --git a/app/services/search/group_service.rb b/app/services/search/group_service.rb
index 00372887985..34803d005e3 100644
--- a/app/services/search/group_service.rb
+++ b/app/services/search/group_service.rb
@@ -11,13 +11,11 @@ module Search
@group = group
end
- # rubocop: disable CodeReuse/ActiveRecord
def projects
return Project.none unless group
return @projects if defined? @projects
@projects = super.inside_path(group.full_path)
end
- # rubocop: enable CodeReuse/ActiveRecord
end
end
diff --git a/app/views/projects/edit.html.haml b/app/views/projects/edit.html.haml
index 07fc9e1c682..3aff5538813 100644
--- a/app/views/projects/edit.html.haml
+++ b/app/views/projects/edit.html.haml
@@ -148,7 +148,7 @@
= link_to 'Archive project', archive_project_path(@project),
data: { confirm: "Are you sure that you want to archive this project?" },
method: :post, class: "btn btn-warning"
- .sub-section.rename-respository
+ .sub-section.rename-repository
%h4.warning-title
Rename repository
= render 'projects/errors'
diff --git a/app/views/projects/empty.html.haml b/app/views/projects/empty.html.haml
index 75f35360e5e..936900a0087 100644
--- a/app/views/projects/empty.html.haml
+++ b/app/views/projects/empty.html.haml
@@ -36,7 +36,7 @@
.scrolling-tabs-container.inner-page-scroll-tabs.is-smaller
.fade-left= icon('angle-left')
.fade-right= icon('angle-right')
- .nav-links.scrolling-tabs
+ .nav-links.scrolling-tabs.quick-links
= render 'stat_anchor_list', anchors: @project.empty_repo_statistics_anchors
= render 'stat_anchor_list', anchors: @project.empty_repo_statistics_buttons
diff --git a/app/views/projects/show.html.haml b/app/views/projects/show.html.haml
index 283031b06da..f29ce4f5c06 100644
--- a/app/views/projects/show.html.haml
+++ b/app/views/projects/show.html.haml
@@ -22,7 +22,7 @@
.scrolling-tabs-container.inner-page-scroll-tabs.is-smaller
.fade-left= icon('angle-left')
.fade-right= icon('angle-right')
- .nav-links.scrolling-tabs
+ .nav-links.scrolling-tabs.quick-links
= render 'stat_anchor_list', anchors: @project.statistics_anchors(show_auto_devops_callout: show_auto_devops_callout)
= render 'stat_anchor_list', anchors: @project.statistics_buttons(show_auto_devops_callout: show_auto_devops_callout)
diff --git a/app/views/shared/issuable/_search_bar.html.haml b/app/views/shared/issuable/_search_bar.html.haml
index 1d876cc4a5d..d27f79dc404 100644
--- a/app/views/shared/issuable/_search_bar.html.haml
+++ b/app/views/shared/issuable/_search_bar.html.haml
@@ -105,6 +105,14 @@
%span.label-title.js-data-value
{{title}}
#js-dropdown-my-reaction.filtered-search-input-dropdown-menu.dropdown-menu
+ %ul{ data: { dropdown: true } }
+ %li.filter-dropdown-item{ data: { value: 'none' } }
+ %button.btn.btn-link{ type: 'button' }
+ = _('None')
+ %li.filter-dropdown-item{ data: { value: 'any' } }
+ %button.btn.btn-link{ type: 'button' }
+ = _('Any')
+ %li.divider.droplab-item-ignore
%ul.filter-dropdown{ data: { dynamic: true, dropdown: true } }
%li.filter-dropdown-item
%button.btn.btn-link{ type: 'button' }
diff --git a/app/workers/gitlab/github_import/advance_stage_worker.rb b/app/workers/gitlab/github_import/advance_stage_worker.rb
index cd2ceb8dcdf..2b49860025a 100644
--- a/app/workers/gitlab/github_import/advance_stage_worker.rb
+++ b/app/workers/gitlab/github_import/advance_stage_worker.rb
@@ -14,7 +14,7 @@ module Gitlab
INTERVAL = 30.seconds.to_i
# The number of seconds to wait (while blocking the thread) before
- # continueing to the next waiter.
+ # continuing to the next waiter.
BLOCKING_WAIT_TIME = 5
# The known importer stages and their corresponding Sidekiq workers.
diff --git a/changelogs/unreleased/44012-filter-reactions-none-any.yml b/changelogs/unreleased/44012-filter-reactions-none-any.yml
new file mode 100644
index 00000000000..5d685010f8a
--- /dev/null
+++ b/changelogs/unreleased/44012-filter-reactions-none-any.yml
@@ -0,0 +1,5 @@
+---
+title: Add None / Any options to reactions filter
+merge_request: 22638
+author: Heinrich Lee Yu
+type: added
diff --git a/changelogs/unreleased/52382-filter-milestone-api-none-any.yml b/changelogs/unreleased/52382-filter-milestone-api-none-any.yml
new file mode 100644
index 00000000000..a7559a25645
--- /dev/null
+++ b/changelogs/unreleased/52382-filter-milestone-api-none-any.yml
@@ -0,0 +1,5 @@
+---
+title: Standardize milestones filter in APIs to None / Any
+merge_request: 22637
+author: Heinrich Lee Yu
+type: changed
diff --git a/changelogs/unreleased/drop-gcp-cluster-table.yml b/changelogs/unreleased/drop-gcp-cluster-table.yml
new file mode 100644
index 00000000000..15964ec2eaf
--- /dev/null
+++ b/changelogs/unreleased/drop-gcp-cluster-table.yml
@@ -0,0 +1,5 @@
+---
+title: Drop gcp_clusters table
+merge_request: 22713
+author:
+type: other
diff --git a/changelogs/unreleased/gt-fix-quick-links-button-styles.yml b/changelogs/unreleased/gt-fix-quick-links-button-styles.yml
new file mode 100644
index 00000000000..4c1150631f8
--- /dev/null
+++ b/changelogs/unreleased/gt-fix-quick-links-button-styles.yml
@@ -0,0 +1,5 @@
+---
+title: Fix quick links button styles
+merge_request: 22657
+author: George Tsiolis
+type: fixed
diff --git a/changelogs/unreleased/related_mrs.yml b/changelogs/unreleased/related_mrs.yml
new file mode 100644
index 00000000000..cc89e9d0cdb
--- /dev/null
+++ b/changelogs/unreleased/related_mrs.yml
@@ -0,0 +1,5 @@
+---
+title: Add API endpoint to list issue related merge requests
+merge_request: 21806
+author: Helmut Januschka
+type: added
diff --git a/changelogs/unreleased/sh-optimize-merge-request-project-lookup.yml b/changelogs/unreleased/sh-optimize-merge-request-project-lookup.yml
new file mode 100644
index 00000000000..241b89c4633
--- /dev/null
+++ b/changelogs/unreleased/sh-optimize-merge-request-project-lookup.yml
@@ -0,0 +1,5 @@
+---
+title: Reduce SQL queries needed to load open merge requests
+merge_request: 22709
+author:
+type: performance
diff --git a/config/application.rb b/config/application.rb
index 9074cf02c46..95b0f74a5a3 100644
--- a/config/application.rb
+++ b/config/application.rb
@@ -144,7 +144,7 @@ module Gitlab
config.assets.precompile << "errors.css"
# Import gitlab-svgs directly from vendored directory
- config.assets.paths << "#{config.root}/node_modules/@gitlab-org/gitlab-svgs/dist"
+ config.assets.paths << "#{config.root}/node_modules/@gitlab/svgs/dist"
config.assets.precompile << "icons.svg"
config.assets.precompile << "icons.json"
config.assets.precompile << "illustrations/*.svg"
diff --git a/config/dependency_decisions.yml b/config/dependency_decisions.yml
index 62760ffee3a..488728e26ab 100644
--- a/config/dependency_decisions.yml
+++ b/config/dependency_decisions.yml
@@ -461,7 +461,7 @@
:versions: []
:when: 2017-09-13 17:31:16.425819400 Z
- - :license
- - "@gitlab-org/gitlab-svgs"
+ - "@gitlab/svgs"
- MIT
- :who: Tim Zallmann
:why: Our own library - GitLab License https://gitlab.com/gitlab-org/gitlab-svgs
diff --git a/config/gitlab.yml.example b/config/gitlab.yml.example
index a4db125f831..d37775e772d 100644
--- a/config/gitlab.yml.example
+++ b/config/gitlab.yml.example
@@ -587,7 +587,7 @@ production: &base
gitaly:
# Path to the directory containing Gitaly client executables.
client_path: /home/git/gitaly/bin
- # Default Gitaly authentication token. Can be overriden per storage. Can
+ # Default Gitaly authentication token. Can be overridden per storage. Can
# be left blank when Gitaly is running locally on a Unix socket, which
# is the normal way to deploy Gitaly.
token:
diff --git a/db/migrate/20181031190559_drop_gcp_clusters_table.rb b/db/migrate/20181031190559_drop_gcp_clusters_table.rb
new file mode 100644
index 00000000000..808d474b4fc
--- /dev/null
+++ b/db/migrate/20181031190559_drop_gcp_clusters_table.rb
@@ -0,0 +1,53 @@
+# frozen_string_literal: true
+
+class DropGcpClustersTable < ActiveRecord::Migration
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+
+ def up
+ drop_table :gcp_clusters
+ end
+
+ def down
+ create_table :gcp_clusters do |t|
+ # Order columns by best align scheme
+ t.references :project, null: false, index: { unique: true }, foreign_key: { on_delete: :cascade }
+ t.references :user, foreign_key: { on_delete: :nullify }
+ t.references :service, foreign_key: { on_delete: :nullify }
+ t.integer :status
+ t.integer :gcp_cluster_size, null: false
+
+ # Timestamps
+ t.datetime_with_timezone :created_at, null: false
+ t.datetime_with_timezone :updated_at, null: false
+
+ # Enable/disable
+ t.boolean :enabled, default: true
+
+ # General
+ t.text :status_reason
+
+ # k8s integration specific
+ t.string :project_namespace
+
+ # Cluster details
+ t.string :endpoint
+ t.text :ca_cert
+ t.text :encrypted_kubernetes_token
+ t.string :encrypted_kubernetes_token_iv
+ t.string :username
+ t.text :encrypted_password
+ t.string :encrypted_password_iv
+
+ # GKE
+ t.string :gcp_project_id, null: false
+ t.string :gcp_cluster_zone, null: false
+ t.string :gcp_cluster_name, null: false
+ t.string :gcp_machine_type
+ t.string :gcp_operation_id
+ t.text :encrypted_gcp_token
+ t.string :encrypted_gcp_token_iv
+ end
+ end
+end
diff --git a/db/post_migrate/20170717111152_cleanup_move_system_upload_folder_symlink.rb b/db/post_migrate/20170717111152_cleanup_move_system_upload_folder_symlink.rb
index c48f1c938d0..3ae4406ff96 100644
--- a/db/post_migrate/20170717111152_cleanup_move_system_upload_folder_symlink.rb
+++ b/db/post_migrate/20170717111152_cleanup_move_system_upload_folder_symlink.rb
@@ -13,7 +13,7 @@ class CleanupMoveSystemUploadFolderSymlink < ActiveRecord::Migration
say "Removing #{old_directory} -> #{new_directory} symlink"
FileUtils.rm(old_directory)
else
- say "Symlink #{old_directory} non existant, nothing to do."
+ say "Symlink #{old_directory} non existent, nothing to do."
end
end
diff --git a/db/schema.rb b/db/schema.rb
index 6b0bb33f969..22474916034 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -11,7 +11,7 @@
#
# It's strongly recommended that you check this file into your version control system.
-ActiveRecord::Schema.define(version: 20181017001059) do
+ActiveRecord::Schema.define(version: 20181031190559) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
@@ -918,35 +918,6 @@ ActiveRecord::Schema.define(version: 20181017001059) do
add_index "forked_project_links", ["forked_to_project_id"], name: "index_forked_project_links_on_forked_to_project_id", unique: true, using: :btree
- create_table "gcp_clusters", force: :cascade do |t|
- t.integer "project_id", null: false
- t.integer "user_id"
- t.integer "service_id"
- t.integer "status"
- t.integer "gcp_cluster_size", null: false
- t.datetime_with_timezone "created_at", null: false
- t.datetime_with_timezone "updated_at", null: false
- t.boolean "enabled", default: true
- t.text "status_reason"
- t.string "project_namespace"
- t.string "endpoint"
- t.text "ca_cert"
- t.text "encrypted_kubernetes_token"
- t.string "encrypted_kubernetes_token_iv"
- t.string "username"
- t.text "encrypted_password"
- t.string "encrypted_password_iv"
- t.string "gcp_project_id", null: false
- t.string "gcp_cluster_zone", null: false
- t.string "gcp_cluster_name", null: false
- t.string "gcp_machine_type"
- t.string "gcp_operation_id"
- t.text "encrypted_gcp_token"
- t.string "encrypted_gcp_token_iv"
- end
-
- add_index "gcp_clusters", ["project_id"], name: "index_gcp_clusters_on_project_id", unique: true, using: :btree
-
create_table "gpg_key_subkeys", force: :cascade do |t|
t.integer "gpg_key_id", null: false
t.binary "keyid"
@@ -2412,9 +2383,6 @@ ActiveRecord::Schema.define(version: 20181017001059) do
add_foreign_key "fork_network_members", "projects", on_delete: :cascade
add_foreign_key "fork_networks", "projects", column: "root_project_id", name: "fk_e7b436b2b5", on_delete: :nullify
add_foreign_key "forked_project_links", "projects", column: "forked_to_project_id", name: "fk_434510edb0", on_delete: :cascade
- add_foreign_key "gcp_clusters", "projects", on_delete: :cascade
- add_foreign_key "gcp_clusters", "services", on_delete: :nullify
- add_foreign_key "gcp_clusters", "users", on_delete: :nullify
add_foreign_key "gpg_key_subkeys", "gpg_keys", on_delete: :cascade
add_foreign_key "gpg_keys", "users", on_delete: :cascade
add_foreign_key "gpg_signatures", "gpg_key_subkeys", on_delete: :nullify
diff --git a/doc/api/issues.md b/doc/api/issues.md
index 57e861bc62e..0dc9d706120 100644
--- a/doc/api/issues.md
+++ b/doc/api/issues.md
@@ -37,11 +37,11 @@ GET /issues?my_reaction_emoji=star
| ------------------- | ---------------- | ---------- | --------------------------------------------------------------------------------------------------------------------------------------------------- |
| `state` | string | no | Return all issues or just those that are `opened` or `closed` |
| `labels` | string | no | Comma-separated list of label names, issues must have all labels to be returned. `No+Label` lists all issues with no labels |
-| `milestone` | string | no | The milestone title. `No+Milestone` lists all issues with no milestone. `Any+Milestone` lists all issues that have an assigned milestone |
+| `milestone` | string | no | The milestone title. `None` lists all issues with no milestone. `Any` lists all issues that have an assigned milestone. |
| `scope` | string | no | Return issues for the given scope: `created_by_me`, `assigned_to_me` or `all`. Defaults to `created_by_me`<br> For versions before 11.0, use the now deprecated `created-by-me` or `assigned-to-me` scopes instead.<br> _([Introduced][ce-13004] in GitLab 9.5. [Changed to snake_case][ce-18935] in GitLab 11.0)_ |
| `author_id` | integer | no | Return issues created by the given user `id`. Combine with `scope=all` or `scope=assigned_to_me`. _([Introduced][ce-13004] in GitLab 9.5)_ |
| `assignee_id` | integer | no | Return issues assigned to the given user `id`. `None` returns unassigned issues. `Any` returns issues with an assignee. _([Introduced][ce-13004] in GitLab 9.5)_ |
-| `my_reaction_emoji` | string | no | Return issues reacted by the authenticated user by the given `emoji` _([Introduced][ce-14016] in GitLab 10.0)_ |
+| `my_reaction_emoji` | string | no | Return issues reacted by the authenticated user by the given `emoji`. `None` returns issues not given a reaction. `Any` returns issues given at least one reaction. _([Introduced][ce-14016] in GitLab 10.0)_ |
| `iids[]` | Array[integer] | no | Return only the issues having the given `iid` |
| `order_by` | string | no | Return issues ordered by `created_at` or `updated_at` fields. Default is `created_at` |
| `sort` | string | no | Return issues sorted in `asc` or `desc` order. Default is `desc` |
@@ -151,11 +151,11 @@ GET /groups/:id/issues?my_reaction_emoji=star
| `state` | string | no | Return all issues or just those that are `opened` or `closed` |
| `labels` | string | no | Comma-separated list of label names, issues must have all labels to be returned. `No+Label` lists all issues with no labels |
| `iids[]` | Array[integer] | no | Return only the issues having the given `iid` |
-| `milestone` | string | no | The milestone title. `No+Milestone` lists all issues with no milestone |
+| `milestone` | string | no | The milestone title. `None` lists all issues with no milestone. `Any` lists all issues that have an assigned milestone. |
| `scope` | string | no | Return issues for the given scope: `created_by_me`, `assigned_to_me` or `all`.<br> For versions before 11.0, use the now deprecated `created-by-me` or `assigned-to-me` scopes instead.<br> _([Introduced][ce-13004] in GitLab 9.5. [Changed to snake_case][ce-18935] in GitLab 11.0)_ |
| `author_id` | integer | no | Return issues created by the given user `id` _([Introduced][ce-13004] in GitLab 9.5)_ |
| `assignee_id` | integer | no | Return issues assigned to the given user `id`. `None` returns unassigned issues. `Any` returns issues with an assignee. _([Introduced][ce-13004] in GitLab 9.5)_ |
-| `my_reaction_emoji` | string | no | Return issues reacted by the authenticated user by the given `emoji` _([Introduced][ce-14016] in GitLab 10.0)_ |
+| `my_reaction_emoji` | string | no | Return issues reacted by the authenticated user by the given `emoji`. `None` returns issues not given a reaction. `Any` returns issues given at least one reaction. _([Introduced][ce-14016] in GitLab 10.0)_ |
| `order_by` | string | no | Return issues ordered by `created_at` or `updated_at` fields. Default is `created_at` |
| `sort` | string | no | Return issues sorted in `asc` or `desc` order. Default is `desc` |
| `search` | string | no | Search group issues against their `title` and `description` |
@@ -265,11 +265,11 @@ GET /projects/:id/issues?my_reaction_emoji=star
| `iids[]` | Array[integer] | no | Return only the milestone having the given `iid` |
| `state` | string | no | Return all issues or just those that are `opened` or `closed` |
| `labels` | string | no | Comma-separated list of label names, issues must have all labels to be returned. `No+Label` lists all issues with no labels |
-| `milestone` | string | no | The milestone title. `No+Milestone` lists all issues with no milestone |
+| `milestone` | string | no | The milestone title. `None` lists all issues with no milestone. `Any` lists all issues that have an assigned milestone. |
| `scope` | string | no | Return issues for the given scope: `created_by_me`, `assigned_to_me` or `all`.<br> For versions before 11.0, use the now deprecated `created-by-me` or `assigned-to-me` scopes instead.<br> _([Introduced][ce-13004] in GitLab 9.5. [Changed to snake_case][ce-18935] in GitLab 11.0)_ |
| `author_id` | integer | no | Return issues created by the given user `id` _([Introduced][ce-13004] in GitLab 9.5)_ |
| `assignee_id` | integer | no | Return issues assigned to the given user `id`. `None` returns unassigned issues. `Any` returns issues with an assignee. _([Introduced][ce-13004] in GitLab 9.5)_ |
-| `my_reaction_emoji` | string | no | Return issues reacted by the authenticated user by the given `emoji` _([Introduced][ce-14016] in GitLab 10.0)_ |
+| `my_reaction_emoji` | string | no | Return issues reacted by the authenticated user by the given `emoji`. `None` returns issues not given a reaction. `Any` returns issues given at least one reaction. _([Introduced][ce-14016] in GitLab 10.0)_ |
| `order_by` | string | no | Return issues ordered by `created_at` or `updated_at` fields. Default is `created_at` |
| `sort` | string | no | Return issues sorted in `asc` or `desc` order. Default is `desc` |
| `search` | string | no | Search project issues against their `title` and `description` |
@@ -1113,6 +1113,93 @@ Example response:
}
```
+## List merge requests related to issue
+
+Get all the merge requests that are related to the issue.
+
+```
+GET /projects/:id/issues/:issue_id/related_merge_requests
+```
+
+| Attribute | Type | Required | Description |
+|-------------|---------|----------|--------------------------------------|
+| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user |
+| `issue_iid` | integer | yes | The internal ID of a project's issue |
+
+```sh
+curl --request GET --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/projects/1/issues/11/related_merge_requests
+```
+
+Example response:
+
+```json
+[
+ {
+ "id": 29,
+ "iid": 11,
+ "project_id": 1,
+ "title": "Provident eius eos blanditiis consequatur neque odit.",
+ "description": "Ut consequatur ipsa aspernatur quisquam voluptatum fugit. Qui harum corporis quo fuga ut incidunt veritatis. Autem necessitatibus et harum occaecati nihil ea.\r\n\r\ntwitter/flight#8",
+ "state": "opened",
+ "created_at": "2018-09-18T14:36:15.510Z",
+ "updated_at": "2018-09-19T07:45:13.089Z",
+ "target_branch": "v2.x",
+ "source_branch": "so_long_jquery",
+ "upvotes": 0,
+ "downvotes": 0,
+ "author": {
+ "id": 14,
+ "name": "Verna Hills",
+ "username": "lawanda_reinger",
+ "state": "active",
+ "avatar_url": "https://www.gravatar.com/avatar/de68a91aeab1cff563795fb98a0c2cc0?s=80&d=identicon",
+ "web_url": "https://gitlab.example.com/lawanda_reinger"
+ },
+ "assignee": {
+ "id": 19,
+ "name": "Jody Baumbach",
+ "username": "felipa.kuvalis",
+ "state": "active",
+ "avatar_url": "https://www.gravatar.com/avatar/6541fc75fc4e87e203529bd275fafd07?s=80&d=identicon",
+ "web_url": "https://gitlab.example.com/felipa.kuvalis"
+ },
+ "source_project_id": 1,
+ "target_project_id": 1,
+ "labels": [],
+ "work_in_progress": false,
+ "milestone": {
+ "id": 27,
+ "iid": 2,
+ "project_id": 1,
+ "title": "v1.0",
+ "description": "Et tenetur voluptatem minima doloribus vero dignissimos vitae.",
+ "state": "active",
+ "created_at": "2018-09-18T14:35:44.353Z",
+ "updated_at": "2018-09-18T14:35:44.353Z",
+ "due_date": null,
+ "start_date": null,
+ "web_url": "https://gitlab.example.com/twitter/flight/milestones/2"
+ },
+ "merge_when_pipeline_succeeds": false,
+ "merge_status": "cannot_be_merged",
+ "sha": "3b7b528e9353295c1c125dad281ac5b5deae5f12",
+ "merge_commit_sha": null,
+ "user_notes_count": 9,
+ "discussion_locked": null,
+ "should_remove_source_branch": null,
+ "force_remove_source_branch": false,
+ "web_url": "https://gitlab.example.com/twitter/flight/merge_requests/4",
+ "time_stats": {
+ "time_estimate": 0,
+ "total_time_spent": 0,
+ "human_time_estimate": null,
+ "human_total_time_spent": null
+ },
+ "squash": false
+ }
+]
+```
+
## List merge requests that will close issue on merge
Get all the merge requests that will close issue when merged.
diff --git a/doc/api/merge_requests.md b/doc/api/merge_requests.md
index 0291b7e00c2..f3cfe0ad218 100644
--- a/doc/api/merge_requests.md
+++ b/doc/api/merge_requests.md
@@ -30,10 +30,10 @@ Parameters:
| Attribute | Type | Required | Description |
| ------------------- | -------- | -------- | ---------------------------------------------------------------------------------------------------------------------- |
-| `state` | string | no | Return all merge requests or just those that are `opened`, `closed`, `locked`, or `merged` |
+| `state` | string | no | Return all merge requests or just those that are `opened`, `closed`, `locked`, or `merged` |
| `order_by` | string | no | Return requests ordered by `created_at` or `updated_at` fields. Default is `created_at` |
| `sort` | string | no | Return requests sorted in `asc` or `desc` order. Default is `desc` |
-| `milestone` | string | no | Return merge requests for a specific milestone |
+| `milestone` | string | no | Return merge requests for a specific milestone. `None` returns merge requests with no milestone. `Any` returns merge requests that have an assigned milestone. |
| `view` | string | no | If `simple`, returns the `iid`, URL, title, description, and basic state of merge request |
| `labels` | string | no | Return merge requests matching a comma separated list of labels |
| `created_after` | datetime | no | Return merge requests created on or after the given time |
@@ -43,11 +43,11 @@ Parameters:
| `scope` | string | no | Return merge requests for the given scope: `created_by_me`, `assigned_to_me` or `all`. Defaults to `created_by_me`<br> For versions before 11.0, use the now deprecated `created-by-me` or `assigned-to-me` scopes instead. |
| `author_id` | integer | no | Returns merge requests created by the given user `id`. Combine with `scope=all` or `scope=assigned_to_me` |
| `assignee_id` | integer | no | Returns merge requests assigned to the given user `id`. `None` returns unassigned merge requests. `Any` returns merge requests with an assignee. |
-| `my_reaction_emoji` | string | no | Return merge requests reacted by the authenticated user by the given `emoji` _([Introduced][ce-14016] in GitLab 10.0)_ |
+| `my_reaction_emoji` | string | no | Return merge requests reacted by the authenticated user by the given `emoji`. `None` returns issues not given a reaction. `Any` returns issues given at least one reaction. _([Introduced][ce-14016] in GitLab 10.0)_ |
| `source_branch` | string | no | Return merge requests with the given source branch |
| `target_branch` | string | no | Return merge requests with the given target branch |
| `search` | string | no | Search merge requests against their `title` and `description` |
-| `wip` | string | no | Filter merge requests against their `wip` status. `yes` to return *only* WIP merge requests, `no` to return *non* WIP merge requests |
+| `wip` | string | no | Filter merge requests against their `wip` status. `yes` to return *only* WIP merge requests, `no` to return *non* WIP merge requests |
```json
[
@@ -154,10 +154,10 @@ Parameters:
| ------------------- | -------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------ |
| `id` | integer | yes | The ID of a project |
| `iids[]` | Array[integer] | no | Return the request having the given `iid` |
-| `state` | string | no | Return all merge requests or just those that are `opened`, `closed`, `locked`, or `merged` |
+| `state` | string | no | Return all merge requests or just those that are `opened`, `closed`, `locked`, or `merged` |
| `order_by` | string | no | Return requests ordered by `created_at` or `updated_at` fields. Default is `created_at` |
| `sort` | string | no | Return requests sorted in `asc` or `desc` order. Default is `desc` |
-| `milestone` | string | no | Return merge requests for a specific milestone |
+| `milestone` | string | no | Return merge requests for a specific milestone. `None` returns merge requests with no milestone. `Any` returns merge requests that have an assigned milestone. |
| `view` | string | no | If `simple`, returns the `iid`, URL, title, description, and basic state of merge request |
| `labels` | string | no | Return merge requests matching a comma separated list of labels |
| `created_after` | datetime | no | Return merge requests created on or after the given time |
@@ -167,9 +167,9 @@ Parameters:
| `scope` | string | no | Return merge requests for the given scope: `created_by_me`, `assigned_to_me` or `all`.<br> For versions before 11.0, use the now deprecated `created-by-me` or `assigned-to-me` scopes instead.<br> _([Introduced][ce-13060] in GitLab 9.5. [Changed to snake_case][ce-18935] in GitLab 11.0)_ |
| `author_id` | integer | no | Returns merge requests created by the given user `id` _([Introduced][ce-13060] in GitLab 9.5)_ |
| `assignee_id` | integer | no | Returns merge requests assigned to the given user `id`. `None` returns unassigned merge requests. `Any` returns merge requests with an assignee. _([Introduced][ce-13060] in GitLab 9.5)_ |
-| `my_reaction_emoji` | string | no | Return merge requests reacted by the authenticated user by the given `emoji` _([Introduced][ce-14016] in GitLab 10.0)_ |
-| `source_branch` | string | no | Return merge requests with the given source branch |
-| `target_branch` | string | no | Return merge requests with the given target branch |
+| `my_reaction_emoji` | string | no | Return merge requests reacted by the authenticated user by the given `emoji`. `None` returns issues not given a reaction. `Any` returns issues given at least one reaction. _([Introduced][ce-14016] in GitLab 10.0)_ |
+| `source_branch` | string | no | Return merge requests with the given source branch |
+| `target_branch` | string | no | Return merge requests with the given target branch |
| `search` | string | no | Search merge requests against their `title` and `description` |
```json
@@ -266,11 +266,11 @@ Parameters:
| Attribute | Type | Required | Description |
| ------------------- | -------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------ |
-| `id` | integer | yes | The ID of a group |
-| `state` | string | no | Return all merge requests or just those that are `opened`, `closed`, `locked`, or `merged` |
-| `order_by` | string | no | Return merge requests ordered by `created_at` or `updated_at` fields. Default is `created_at` |
-| `sort` | string | no | Return merge requests sorted in `asc` or `desc` order. Default is `desc` |
-| `milestone` | string | no | Return merge requests for a specific milestone |
+| `id` | integer | yes | The ID of a group |
+| `state` | string | no | Return all merge requests or just those that are `opened`, `closed`, `locked`, or `merged` |
+| `order_by` | string | no | Return merge requests ordered by `created_at` or `updated_at` fields. Default is `created_at` |
+| `sort` | string | no | Return merge requests sorted in `asc` or `desc` order. Default is `desc` |
+| `milestone` | string | no | Return merge requests for a specific milestone. `None` returns merge requests with no milestone. `Any` returns merge requests that have an assigned milestone. |
| `view` | string | no | If `simple`, returns the `iid`, URL, title, description, and basic state of merge request |
| `labels` | string | no | Return merge requests matching a comma separated list of labels |
| `created_after` | datetime | no | Return merge requests created on or after the given time |
@@ -280,9 +280,9 @@ Parameters:
| `scope` | string | no | Return merge requests for the given scope: `created_by_me`, `assigned_to_me` or `all`.<br> |
| `author_id` | integer | no | Returns merge requests created by the given user `id` _([Introduced][ce-13060] in GitLab 9.5)_ |
| `assignee_id` | integer | no | Returns merge requests assigned to the given user `id`. `None` returns unassigned merge requests. `Any` returns merge requests with an assignee. _([Introduced][ce-13060] in GitLab 9.5)_ |
-| `my_reaction_emoji` | string | no | Return merge requests reacted by the authenticated user by the given `emoji` _([Introduced][ce-14016] in GitLab 10.0)_ |
-| `source_branch` | string | no | Return merge requests with the given source branch |
-| `target_branch` | string | no | Return merge requests with the given target branch |
+| `my_reaction_emoji` | string | no | Return merge requests reacted by the authenticated user by the given `emoji`. `None` returns issues not given a reaction. `Any` returns issues given at least one reaction. _([Introduced][ce-14016] in GitLab 10.0)_ |
+| `source_branch` | string | no | Return merge requests with the given source branch |
+| `target_branch` | string | no | Return merge requests with the given target branch |
| `search` | string | no | Search merge requests against their `title` and `description` |
```json
diff --git a/doc/ci/examples/artifactory_and_gitlab/index.md b/doc/ci/examples/artifactory_and_gitlab/index.md
index 9657f52159e..6aa0edd87b4 100644
--- a/doc/ci/examples/artifactory_and_gitlab/index.md
+++ b/doc/ci/examples/artifactory_and_gitlab/index.md
@@ -16,8 +16,8 @@ to build a [Maven](https://maven.apache.org/) project, deploy it to [Artifactory
You'll create two different projects:
-- `simple-maven-dep`: the app built and deployed to Artifactory (available at https://gitlab.com/gitlab-examples/maven/simple-maven-dep)
-- `simple-maven-app`: the app using the previous one as a dependency (available at https://gitlab.com/gitlab-examples/maven/simple-maven-app)
+- `simple-maven-dep`: the app built and deployed to Artifactory (available at https://gitlab.com/gitlab-examples/maven/simple-maven-dep )
+- `simple-maven-app`: the app using the previous one as a dependency (available at https://gitlab.com/gitlab-examples/maven/simple-maven-app )
We assume that you already have a GitLab account on [GitLab.com](https://gitlab.com/), and that you know the basic usage of Git and [GitLab CI/CD](https://about.gitlab.com/features/gitlab-ci-cd/).
We also assume that an Artifactory instance is available and reachable from the internet, and that you have valid credentials to deploy on it.
diff --git a/doc/development/api_graphql_styleguide.md b/doc/development/api_graphql_styleguide.md
index 6c6e198a7c3..95722c027ba 100644
--- a/doc/development/api_graphql_styleguide.md
+++ b/doc/development/api_graphql_styleguide.md
@@ -379,7 +379,7 @@ let(:mutation) do
)
end
-it 'returns a successfull response' do
+it 'returns a successful response' do
post_graphql_mutation(mutation, current_user: user)
expect(response).to have_gitlab_http_status(:success)
diff --git a/doc/development/fe_guide/icons.md b/doc/development/fe_guide/icons.md
index 3d8da6accc1..533e2001300 100644
--- a/doc/development/fe_guide/icons.md
+++ b/doc/development/fe_guide/icons.md
@@ -3,7 +3,7 @@
We manage our own Icon and Illustration library in the [gitlab-svgs][gitlab-svgs] repository.
This repository is published on [npm][npm] and managed as a dependency via yarn.
You can browse all available Icons and Illustrations [here][svg-preview].
-To upgrade to a new version run `yarn upgrade @gitlab-org/gitlab-svgs`.
+To upgrade to a new version run `yarn upgrade @gitlab/svgs`.
## Icons
@@ -111,6 +111,6 @@ export default {
</template>
```
-[npm]: https://www.npmjs.com/package/@gitlab-org/gitlab-svgs
+[npm]: https://www.npmjs.com/package/@gitlab/svgs
[gitlab-svgs]: https://gitlab.com/gitlab-org/gitlab-svgs
[svg-preview]: https://gitlab-org.gitlab.io/gitlab-svgs
diff --git a/doc/development/ux_guide/tips.md b/doc/development/ux_guide/tips.md
index 8348de4f8a2..ceb9ed56ac4 100644
--- a/doc/development/ux_guide/tips.md
+++ b/doc/development/ux_guide/tips.md
@@ -1,20 +1,16 @@
# Tips
-## Contents
-* [SVGs](#svgs)
-
----
-
## SVGs
When exporting SVGs, be sure to follow the following guidelines:
1. Convert all strokes to outlines.
-2. Use pathfinder tools to combine overlapping paths and create compound paths.
-3. SVGs that are limited to one color should be exported without a fill color so the color can be set using CSS.
-4. Ensure that exported SVGs have been run through an [SVG cleaner](https://github.com/RazrFalcon/SVGCleaner) to remove unused elements and attributes.
+1. Use pathfinder tools to combine overlapping paths and create compound paths.
+1. SVGs that are limited to one color should be exported without a fill color so the color can be set using CSS.
+1. Ensure that exported SVGs have been run through an [SVG cleaner](https://github.com/RazrFalcon/SVGCleaner) to remove unused elements and attributes.
+
+You can open your SVG in a text editor to ensure that it is clean.
-You can open your svg in a text editor to ensure that it is clean.
Incorrect files will look like this:
```xml
@@ -35,10 +31,10 @@ Incorrect files will look like this:
</svg>
```
-Correct file will look like this:
+Correct files will look like this:
```xml
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 17" enable-background="new 0 0 16 17"><path d="m15.1 1h-2.1v-1h-2v1h-6v-1h-2v1h-2.1c-.5 0-.9.5-.9 1v14c0 .6.4 1 .9 1h14.2c.5 0 .9-.4.9-1v-14c0-.5-.4-1-.9-1m-1.1 14h-12v-9h12v9m0-11h-12v-1h12v1"/><path d="m5.4 11.6l1.5 1.2c.4.3 1.1.3 1.4-.1l2.5-3c.3-.4.3-1.1-.1-1.4-.5-.4-1.1-.3-1.5.1l-1.8 2.2-.8-.6c-.4-.3-1.1-.3-1.4.2-.3.4-.3 1 .2 1.4"/></svg>
```
-> TODO: Checkout [https://github.com/svg/svgo](https://github.com/svg/svgo)
+> TODO: Checkout <https://github.com/svg/svgo>.
diff --git a/doc/install/digitaloceandocker.md b/doc/install/digitaloceandocker.md
index 676392eacf2..d67695d75b4 100644
--- a/doc/install/digitaloceandocker.md
+++ b/doc/install/digitaloceandocker.md
@@ -1,7 +1,8 @@
# Digital Ocean and Docker Machine test environment
-## Warning. This guide is for quickly testing different versions of GitLab and
-## not recommended for ease of future upgrades or keeping the data you create.
+CAUTION: **Caution:**
+This guide is for quickly testing different versions of GitLab and not recommended for ease of
+future upgrades or keeping the data you create.
## Initial setup
@@ -12,92 +13,88 @@ locally on either macOS or Linux.
#### Install Docker Toolbox
-1. [https://www.docker.com/products/docker-toolbox](https://www.docker.com/products/docker-toolbox)
+- <https://www.docker.com/products/docker-toolbox>
### On Linux
#### Install Docker Engine
-1. [https://docs.docker.com/engine/installation/linux](https://docs.docker.com/engine/installation/linux/)
+- <https://docs.docker.com/engine/installation/linux/>
#### Install Docker Machine
-1. [https://docs.docker.com/machine/install-machine](https://docs.docker.com/machine/install-machine/)
+- <https://docs.docker.com/machine/install-machine/>
-_The rest of the steps are identical for macOS and Linux_
+NOTE: **Note:**
+The rest of the steps are identical for macOS and Linux.
### Create new docker host
-1. Login to Digital Ocean
-1. Generate a new API token at https://cloud.digitalocean.com/settings/api/tokens
+1. Login to Digital Ocean.
+1. Generate a new API token at <https://cloud.digitalocean.com/settings/api/tokens>.
+ This command will create a new DO droplet called `gitlab-test-env-do` that will act as a docker host.
-This command will create a new DO droplet called `gitlab-test-env-do` that will act as a docker host.
+ NOTE: **Note:**
+ 4GB is the minimum requirement for a Docker host that will run more than one GitLab instance.
-**Note: 4GB is the minimum requirement for a Docker host that will run more then one GitLab instance**
+ - RAM: 4GB
+ - Name: `gitlab-test-env-do`
+ - Driver: `digitalocean`
-+ RAM: 4GB
-+ Name: `gitlab-test-env-do`
-+ Driver: `digitalocean`
+1. Set the DO token:
+ ```sh
+ export DOTOKEN=<your generated token>
+ ```
-**Set the DO token** - Replace the string below with your generated token
+1. Create the machine:
-```
-export DOTOKEN=cf3dfd0662933203005c4a73396214b7879d70aabc6352573fe178d340a80248
-```
-
-**Create the machine**
-
-```
-docker-machine create \
- --driver digitalocean \
- --digitalocean-access-token=$DOTOKEN \
- --digitalocean-size "4gb" \
- gitlab-test-env-do
-```
-
-+ Resource: https://docs.docker.com/machine/drivers/digital-ocean/
+ ```sh
+ docker-machine create \
+ --driver digitalocean \
+ --digitalocean-access-token=$DOTOKEN \
+ --digitalocean-size "4gb" \
+ gitlab-test-env-do
+ ```
+Resource: <https://docs.docker.com/machine/drivers/digital-ocean/>.
### Creating GitLab test instance
-
#### Connect your shell to the new machine
-
In this example we'll create a GitLab EE 8.10.8 instance.
-
First connect the docker client to the docker host you created previously.
-```
+```sh
eval "$(docker-machine env gitlab-test-env-do)"
```
You can add this to your `~/.bash_profile` file to ensure the `docker` client uses the `gitlab-test-env-do` docker host
-
#### Create new GitLab container
-+ HTTP port: `8888`
-+ SSH port: `2222`
- + Set `gitlab_shell_ssh_port` using `--env GITLAB_OMNIBUS_CONFIG `
-+ Hostname: IP of docker host
-+ Container name: `gitlab-test-8.10`
-+ GitLab version: **EE** `8.10.8-ee.0`
+- HTTP port: `8888`
+- SSH port: `2222`
+ - Set `gitlab_shell_ssh_port` using `--env GITLAB_OMNIBUS_CONFIG`
+- Hostname: IP of docker host
+- Container name: `gitlab-test-8.10`
+- GitLab version: **EE** `8.10.8-ee.0`
-##### Set up container settings
+##### Set up container settings
-```
+```sh
export SSH_PORT=2222
export HTTP_PORT=8888
export VERSION=8.10.8-ee.0
export NAME=gitlab-test-8.10
```
-##### Create container
-```
+##### Create container
+
+```sh
docker run --detach \
--env GITLAB_OMNIBUS_CONFIG="external_url 'http://$(docker-machine ip gitlab-test-env-do):$HTTP_PORT'; gitlab_rails['gitlab_shell_ssh_port'] = $SSH_PORT;" \
--hostname $(docker-machine ip gitlab-test-env-do) \
@@ -110,23 +107,20 @@ gitlab/gitlab-ee:$VERSION
##### Retrieve the docker host IP
-```
+```sh
docker-machine ip gitlab-test-env-do
# example output: 192.168.151.134
```
-
-+ Browse to: http://192.168.151.134:8888/
-
+Browse to: <http://192.168.151.134:8888/>.
##### Execute interactive shell/edit configuration
-
-```
+```sh
docker exec -it $NAME /bin/bash
```
-```
+```sh
# example commands
root@192:/# vi /etc/gitlab/gitlab.rb
root@192:/# gitlab-ctl reconfigure
@@ -134,6 +128,6 @@ root@192:/# gitlab-ctl reconfigure
#### Resources
-+ [https://docs.gitlab.com/omnibus/docker/](https://docs.gitlab.com/omnibus/docker/)
-+ [https://docs.docker.com/machine/get-started/](https://docs.docker.com/machine/get-started/)
-+ [https://docs.docker.com/machine/reference/ip/](https://docs.docker.com/machine/reference/ip/)+
+- <https://docs.gitlab.com/omnibus/docker/>.
+- <https://docs.docker.com/machine/get-started/>.
+- <https://docs.docker.com/machine/reference/ip/>.
diff --git a/doc/ssh/README.md b/doc/ssh/README.md
index 5db042326f3..c5b7813b285 100644
--- a/doc/ssh/README.md
+++ b/doc/ssh/README.md
@@ -8,163 +8,224 @@ you need a secure communication channel for sharing information.
The SSH protocol provides this security and allows you to authenticate to the
GitLab remote server without supplying your username or password each time.
-For a more detailed explanation of how the SSH protocol works, we advise you to
-read [this nice tutorial by DigitalOcean](https://www.digitalocean.com/community/tutorials/understanding-the-ssh-encryption-and-connection-process).
+For a more detailed explanation of how the SSH protocol works, read
+[this nice tutorial by DigitalOcean](https://www.digitalocean.com/community/tutorials/understanding-the-ssh-encryption-and-connection-process).
-## Locating an existing SSH key pair
+## Requirements
-Before generating a new SSH key pair check if your system already has one
-at the default location by opening a shell, or Command Prompt on Windows,
-and running the following command:
+The only requirement is to have the OpenSSH client installed on your system. This
+comes pre-installed on GNU/Linux and macOS, but not on Windows.
-**Windows Command Prompt:**
+Depending on your Windows version, there are different methods to work with
+SSH keys.
-```bash
-type %userprofile%\.ssh\id_rsa.pub
-```
+### Installing the SSH client for Windows 10
-**Git Bash on Windows / GNU/Linux / macOS / PowerShell:**
+Starting with Windows 10, you can
+[install the Windows Subsystem for Linux (WSL)](https://docs.microsoft.com/en-us/windows/wsl/install-win10)
+where you can run Linux distributions directly on Windows, without the overhead
+of a virtual machine. Once installed and set up, you'll have the Git and SSH
+clients at your disposal.
-```bash
-cat ~/.ssh/id_rsa.pub
-```
+### Installing the SSH client for Windows 8.1 and Windows 7
+
+The easiest way to install Git and the SSH client on Windows 8.1 and Windows 7
+is [Git for Windows](https://gitforwindows.com). It provides a BASH
+emulation (Git Bash) used for running Git from the command line and the
+`ssh-keygen` command that is useful to create SSH keys as you'll learn below.
+
+NOTE: **Alternative tools:**
+Although not explored in this page, you can use some alternative tools.
+[Cygwin](https://www.cygwin.com) is a large collection of GNU and open source
+tools which provide functionality similar to a Unix distribution.
+[PuttyGen](https://www.chiark.greenend.org.uk/~sgtatham/putty/latest.html)
+provides a graphical user interface to [create SSH keys](https://tartarus.org/~simon/putty-snapshots/htmldoc/Chapter8.html#pubkey-puttygen).
+
+## Types of SSH keys and which to choose
+
+GitLab supports RSA, DSA, ECDSA, and ED25519 keys. Their difference lies on
+the signing algorithm, and some of them have advantages over the others. For
+more information, you can read this
+[nice article on ArchWiki](https://wiki.archlinux.org/index.php/SSH_keys#Choosing_the_authentication_key_type).
+We'll focus on ED25519 and RSA and here.
+
+NOTE: **Note:**
+As an admin, you can restrict
+[which keys should be permitted and their minimum length](../security/ssh_keys_restrictions.md).
+By default, all keys are permitted, which is also the case for
+[GitLab.com](../user/gitlab_com/index.md#ssh-host-keys-fingerprints).
-If you see a string starting with `ssh-rsa` you already have an SSH key pair
-and you can skip the generate portion of the next section and skip to the copy
-to clipboard step.
-If you don't see the string or would like to generate a SSH key pair with a
-custom name continue onto the next step.
+## ED25519 SSH keys
-Note that Public SSH key may also be named as follows:
+Following [best practices](https://linux-audit.com/using-ed25519-openssh-keys-instead-of-dsa-rsa-ecdsa/),
+you should always favor [ED25519](https://ed25519.cr.yp.to/) SSH keys, since they
+are more secure and have better performance over the other types.
-- `id_dsa.pub`
-- `id_ecdsa.pub`
-- `id_ed25519.pub`
+They were introduced in OpenSSH 6.5, so any modern OS should include the
+option to create them. If for any reason your OS or the GitLab instance you
+interact with doesn't support this, you can fallback to RSA.
+
+## RSA SSH keys
+
+RSA keys are the most common ones and therefore the most compatible with
+servers that may have an old OpenSSH version. Use them if the GitLab server
+doesn't work with ED25519 keys.
+
+The minimum key size is 1024 bits, defaulting to 2048. If you wish to generate a
+stronger RSA key pair, specify the `-b` flag with a higher bit value than the
+default.
+
+The old, default password encoding for SSH private keys keys is
+[insecure](https://latacora.singles/2018/08/03/the-default-openssh.html);
+it's only a single round of an MD5 hash. Since OpenSSH version 6.5, you should
+use the `-o` option to `ssh-keygen` to encode your private key in a new, more
+secure format.
+
+If you already have an RSA SSH key pair to use with GitLab, consider upgrading it
+to use the more secure password encryption format by using the following command
+on the private key:
+
+```bash
+ssh-keygen -o -f ~/.ssh/id_rsa
+```
## Generating a new SSH key pair
-1. To generate a new SSH key pair, use the following command:
+Before creating an SSH key pair, make sure to read about the
+[different types of keys](#types-of-ssh-keys-and-which-to-choose) to understand
+their differences.
+
+To create a new SSH key pair:
- **Git Bash on Windows / GNU/Linux / macOS:**
+1. Open a terminal on Linux or macOS, or Git Bash / WSL on Windows.
+1. Generate a new ED25519 SSH key pair:
```bash
- ssh-keygen -o -t rsa -C "your.email@example.com" -b 4096
+ ssh-keygen -t ed25519 -C "email@example.com"
```
- (Note: the `-o` option was introduced in 2014; if this command does not work for you, simply remove the `-o` option and try again)
+ Or, if you want to use RSA:
- **Windows:**
+ ```bash
+ ssh-keygen -o -t rsa -b 4096 -C "email@example.com"
+ ```
- Alternatively on Windows you can download
- [PuttyGen](http://www.chiark.greenend.org.uk/~sgtatham/putty/download.html)
- and follow [this documentation article][winputty] to generate a SSH key pair.
+ The `-C` flag adds a comment in the key in case you have multiple of them
+ and want to tell which is which. It is optional.
1. Next, you will be prompted to input a file path to save your SSH key pair to.
+ If you don't already have an SSH key pair, use the suggested path by pressing
+ <kbd>Enter</kbd>. Using the suggested path will normally allow your SSH client
+ to automatically use the SSH key pair with no additional configuration.
- If you don't already have an SSH key pair use the suggested path by pressing
- enter. Using the suggested path will normally allow your SSH client
- to automatically use the SSH key pair with no additional configuration.
+ If you already have an SSH key pair with the suggested file path, you will need
+ to input a new file path and [declare what host](#working-with-non-default-ssh-key-pair-paths)
+ this SSH key pair will be used for in your `~/.ssh/config` file.
- If you already have a SSH key pair with the suggested file path, you will need
- to input a new file path and declare what host this SSH key pair will be used
- for in your `.ssh/config` file, see [**Working with non-default SSH key pair paths**](#working-with-non-default-ssh-key-pair-paths)
- for more information.
+1. Once the path is decided, you will be prompted to input a password to
+ secure your new SSH key pair. It's a best practice to use a password,
+ but it's not required and you can skip creating it by pressing
+ <kbd>Enter</kbd> twice.
-1. Once you have input a file path you will be prompted to input a password to
- secure your SSH key pair. It is a best practice to use a password for an SSH
- key pair, but it is not required and you can skip creating a password by
- pressing enter.
+ If, in any case, you want to add or change the password of your SSH key pair,
+ you can use the `-p`flag:
- NOTE: **Note:**
- If you want to change the password of your SSH key pair, you can use
- `ssh-keygen -p -o -f <keyname>`.
- The `-o` option was added in 2014, so if this command does not work for you,
- simply remove the `-o` option and try again.
+ ```
+ ssh-keygen -p -o -f <keyname>
+ ```
-## Adding a SSH key to your GitLab account
+Now, it's time to add the newly created public key to your GitLab account.
-1. The next step is to copy the public SSH key as we will need it afterwards.
+## Adding an SSH key to your GitLab account
- To copy your public SSH key to the clipboard, use the appropriate code below:
+1. Copy your **public** SSH key to the clipboard by using one of the commands below
+ depending on your Operating System:
**macOS:**
```bash
- pbcopy < ~/.ssh/id_rsa.pub
+ pbcopy < ~/.ssh/id_ed25519.pub
```
- **GNU/Linux (requires the xclip package):**
+ **WSL / GNU/Linux (requires the xclip package):**
```bash
- xclip -sel clip < ~/.ssh/id_rsa.pub
+ xclip -sel clip < ~/.ssh/id_ed25519.pub
```
- **Windows Command Line:**
+ **Git Bash on Windows:**
```bash
- type %userprofile%\.ssh\id_rsa.pub | clip
+ cat ~/.ssh/id_ed25519.pub | clip
```
- **Git Bash on Windows / Windows PowerShell:**
+ You can also open the key in a graphical editor and copy it from there,
+ but be careful not to accidentally change anything.
- ```bash
- cat ~/.ssh/id_rsa.pub | clip
- ```
-
-1. The final step is to add your public SSH key to GitLab.
+ NOTE: **Note:**
+ If you opted to create an RSA key, the name might differ.
- Navigate to the 'SSH Keys' tab in your 'Profile Settings'.
- Paste your key in the 'Key' section and give it a relevant 'Title'.
- Use an identifiable title like 'Work Laptop - Windows 7' or
- 'Home MacBook Pro 15'.
+1. Add your public SSH key to your GitLab account by clicking your avatar
+ in the upper right corner and selecting **Settings**. From there on,
+ navigate to **SSH Keys** and paste your public key in the "Key" section.
+ If you created the key with a comment, this will appear under "Title".
+ If not, give your key an identifiable title like _Work Laptop_ or
+ _Home Workstation_, and click **Add key**.
+ NOTE: **Note:**
If you manually copied your public SSH key make sure you copied the entire
- key starting with `ssh-rsa` and ending with your email.
+ key starting with `ssh-ed25519` (or `ssh-rsa`) and ending with your email.
+
+## Testing that everything is set up correctly
+
+To test whether your SSH key was added correctly, run the following command in
+your terminal (replacing `gitlab.com` with your GitLab's instance domain):
-1. Optionally you can test your setup by running `ssh -T git@example.com`
- (replacing `example.com` with your GitLab domain) and verifying that you
- receive a `Welcome to GitLab` message.
+```bash
+ssh -T git@gitlab.com
+```
+
+You should receive a _Welcome to GitLab, `@username`!_ message.
+
+If the welcome message doesn't appear, run SSH's verbose mode by replacing `-T`
+with `-vvvT` to understand where the error is.
## Working with non-default SSH key pair paths
If you used a non-default file path for your GitLab SSH key pair,
you must configure your SSH client to find your GitLab private SSH key
-for connections to your GitLab server (perhaps `gitlab.com`).
+for connections to GitLab.
-For your current terminal session you can do so using the following commands
+Open a terminal and use the following commands
(replacing `other_id_rsa` with your private SSH key):
-**Git Bash on Windows / GNU/Linux / macOS:**
-
```bash
eval $(ssh-agent -s)
ssh-add ~/.ssh/other_id_rsa
```
-To retain these settings you'll need to save them to a configuration file.
-For OpenSSH clients this is configured in the `~/.ssh/config` file for some
-operating systems.
+To retain these settings, you'll need to save them to a configuration file.
+For OpenSSH clients this is configured in the `~/.ssh/config` file. In this
+file you can set up configurations for multiple hosts, like GitLab.com, your
+own GitLab instance, GitHub, Bitbucket, etc.
+
Below are two example host configurations using their own SSH key:
-```
-# GitLab.com server
+```conf
+# GitLab.com
Host gitlab.com
-RSAAuthentication yes
-IdentityFile ~/.ssh/config/private-key-filename-01
+ Preferredauthentications publickey
+ IdentityFile ~/.ssh/gitlab_com_rsa
-# Private GitLab server
+# Private GitLab instance
Host gitlab.company.com
-RSAAuthentication yes
-IdentityFile ~/.ssh/config/private-key-filename
+ Preferredauthentications publickey
+ IdentityFile ~/.ssh/example_com_rsa
```
-Due to the wide variety of SSH clients and their very large number of
-configuration options, further explanation of these topics is beyond the scope
-of this document.
-
-Public SSH keys need to be unique, as they will bind to your account.
-Your SSH key is the only identifier you'll have when pushing code via SSH.
-That's why it needs to uniquely map to a single user.
+Public SSH keys need to be unique to GitLab, as they will bind to your account.
+Your SSH key is the only identifier you'll have when pushing code via SSH,
+that's why it needs to uniquely map to a single user.
## Deploy keys
@@ -240,8 +301,6 @@ not implicitly give any access just by setting them up.
How to add your SSH key to Eclipse: https://wiki.eclipse.org/EGit/User_Guide#Eclipse_SSH_Configuration
-[winputty]: https://the.earth.li/~sgtatham/putty/0.67/htmldoc/Chapter8.html#pubkey-puttygen
-
## SSH on the GitLab server
GitLab integrates with the system-installed SSH daemon, designating a user
diff --git a/doc/university/training/user_training.md b/doc/university/training/user_training.md
index dccb6cbf071..f3a4d766522 100644
--- a/doc/university/training/user_training.md
+++ b/doc/university/training/user_training.md
@@ -6,91 +6,90 @@ comments: false
---
-# Agenda
+## Agenda
-1. Brief history of Git
-1. GitLab walkthrough
-1. Configure your environment
-1. Workshop
+1. Brief history of Git.
+1. GitLab walkthrough.
+1. Configure your environment.
+1. Workshop.
---
-# Git introduction
+## Git introduction
-https://git-scm.com/about
+<https://git-scm.com/about>
-- Distributed version control
- - Does not rely on connection to a central server
- - Many copies of the complete history
-- Powerful branching and merging
-- Adapts to nearly any workflow
-- Fast, reliable and stable file format
+- Distributed version control.
+ - Does not rely on connection to a central server.
+ - Many copies of the complete history.
+- Powerful branching and merging.
+- Adapts to nearly any workflow.
+- Fast, reliable and stable file format.
---
-# Help!
+## Help!
Use the tools at your disposal when you get stuck.
-- Use '`git help <command>`' command
-- Use Google
-- Read documentation at https://git-scm.com
+- Use '`git help <command>`' command.
+- Use Google.
+- Read documentation at <https://git-scm.com>.
---
-# GitLab Walkthrough
+## GitLab Walkthrough
![fit](logo.png)
---
-# Configure your environment
+## Configure your environment
- Windows: Install 'Git for Windows'
-> https://git-for-windows.github.io
+> <https://git-for-windows.github.io>
- Mac: Type '`git`' in the Terminal application.
> If it's not installed, it will prompt you to install it.
-- Debian: '`sudo apt-get install git-all`'
-or Red Hat '`sudo yum install git-all`'
+- Debian: '`sudo apt-get install git-all`' or Red Hat '`sudo yum install git-all`'
---
-# Git Workshop
+## Git Workshop
-## Overview
+### Overview
-1. Configure Git
-1. Configure SSH Key
-1. Create a project
-1. Committing
-1. Feature branching
-1. Merge requests
-1. Feedback and Collaboration
+1. Configure Git.
+1. Configure SSH Key.
+1. Create a project.
+1. Committing.
+1. Feature branching.
+1. Merge requests.
+1. Feedback and Collaboration.
---
-# Configure Git
+## Configure Git
-One-time configuration of the Git client
+One-time configuration of the Git client:
-```bash
+```sh
git config --global user.name "Your Name"
git config --global user.email you@example.com
```
---
-# Configure SSH Key
+## Configure SSH Key
-```bash
+```sh
ssh-keygen -t rsa -b 4096 -C "you@computer-name"
```
-```bash
+```sh
# You will be prompted for the following information. Press enter to accept the defaults. Defaults appear in parentheses.
Generating public/private rsa key pair.
Enter file in which to save the key (/Users/you/.ssh/id_rsa):
@@ -102,31 +101,30 @@ The key fingerprint is:
39:fc:ce:94:f4:09:13:95:64:9a:65:c1:de:05:4d:01 you@computer-name
```
-Copy your public key and add it to your GitLab profile
+Copy your public key and add it to your GitLab profile:
-```bash
+```sh
cat ~/.ssh/id_rsa.pub
```
-```bash
+```sh
ssh-rsa AAAAB3NzaC1yc2EAAAADAQEL17Ufacg8cDhlQMS5NhV8z3GHZdhCrZbl4gz you@example.com
```
---
-# Create a project
+## Create a project
-- Create a project in your user namespace
- - Choose to import from 'Any Repo by URL' and use
- https://gitlab.com/gitlab-org/training-examples.git
+- Create a project in your user namespace.
+ - Choose to import from 'Any Repo by URL' and use <https://gitlab.com/gitlab-org/training-examples.git>.
- Create a '`development`' or '`workspace`' directory in your home directory.
-- Clone the '`training-examples`' project
+- Clone the '`training-examples`' project.
---
-# Commands
+## Commands (project)
-```
+```sh
mkdir ~/development
cd ~/development
@@ -141,37 +139,37 @@ cd training-examples
---
-# Git concepts
+## Git concepts
-**Untracked files**
+### Untracked files
New files that Git has not been told to track previously.
-**Working area**
+### Working area
Files that have been modified but are not committed.
-**Staging area**
+### Staging area
Modified files that have been marked to go in the next commit.
---
-# Committing
+## Committing
-1. Edit '`edit_this_file.rb`' in '`training-examples`'
-1. See it listed as a changed file (working area)
-1. View the differences
-1. Stage the file
-1. Commit
-1. Push the commit to the remote
-1. View the git log
+1. Edit '`edit_this_file.rb`' in '`training-examples`'.
+1. See it listed as a changed file (working area).
+1. View the differences.
+1. Stage the file.
+1. Commit.
+1. Push the commit to the remote.
+1. View the git log.
---
-# Commands
+## Commands (committing)
-```
+```sh
# Edit `edit_this_file.rb`
git status
git diff
@@ -183,29 +181,29 @@ git log
---
-# Feature branching
+## Feature branching
-- Efficient parallel workflow for teams
-- Develop each feature in a branch
-- Keeps changes isolated
-- Consider a 1-to-1 link to issues
-- Push branches to the server frequently
- - Hint: This is a cheap backup for your work-in-progress code
+- Efficient parallel workflow for teams.
+- Develop each feature in a branch.
+- Keeps changes isolated.
+- Consider a 1-to-1 link to issues.
+- Push branches to the server frequently.
+ - Hint: This is a cheap backup for your work-in-progress code.
---
-# Feature branching
+## Feature branching steps
-1. Create a new feature branch called 'squash_some_bugs'
+1. Create a new feature branch called 'squash_some_bugs'.
1. Edit '`bugs.rb`' and remove all the bugs.
-1. Commit
-1. Push
+1. Commit.
+1. Push.
---
-# Commands
+## Commands (feature branching)
-```
+```sh
git checkout -b squash_some_bugs
# Edit `bugs.rb`
git status
@@ -216,51 +214,50 @@ git push origin squash_some_bugs
---
-# Merge requests
+## Merge requests
-- When you want feedback create a merge request
-- Target is the ‘default’ branch (usually master)
-- Assign or mention the person you would like to review
-- Add 'WIP' to the title if it's a work in progress
-- When accepting, always delete the branch
-- Anyone can comment, not just the assignee
-- Push corrections to the same branch
+- When you want feedback create a merge request.
+- Target is the ‘default’ branch (usually master).
+- Assign or mention the person you would like to review.
+- Add 'WIP' to the title if it's a work in progress.
+- When accepting, always delete the branch.
+- Anyone can comment, not just the assignee.
+- Push corrections to the same branch.
---
-# Merge requests
+## Merge requests steps
-**Create your first merge request**
+Create your first merge request:
-1. Use the blue button in the activity feed
-1. View the diff (changes) and leave a comment
-1. Push a new commit to the same branch
-1. Review the changes again and notice the update
+1. Use the blue button in the activity feed.
+1. View the diff (changes) and leave a comment.
+1. Push a new commit to the same branch.
+1. Review the changes again and notice the update.
---
-# Feedback and Collaboration
+## Feedback and Collaboration
-- Merge requests are a time for feedback and collaboration
-- Giving feedback is hard
-- Be as kind as possible
-- Receiving feedback is hard
-- Be as receptive as possible
-- Feedback is about the best code, not the person. You are not your code
+- Merge requests are a time for feedback and collaboration.
+- Giving feedback is hard.
+- Be as kind as possible.
+- Receiving feedback is hard.
+- Be as receptive as possible.
+- Feedback is about the best code, not the person. You are not your code.
---
-# Feedback and Collaboration
+## Feedback and Collaboration resources
Review the Thoughtbot code-review guide for suggestions to follow when reviewing merge requests:
-[https://github.com/thoughtbot/guides/tree/master/code-review](https://github.com/thoughtbot/guides/tree/master/code-review)
+<https://github.com/thoughtbot/guides/tree/master/code-review>.
-See GitLab merge requests for examples:
-[https://gitlab.com/gitlab-org/gitlab-ce/merge_requests](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests)
+See GitLab merge requests for examples: <https://gitlab.com/gitlab-org/gitlab-ce/merge_requests>.
---
-# Explore GitLab projects
+## Explore GitLab projects
![fit](logo.png)
@@ -274,31 +271,29 @@ See GitLab merge requests for examples:
---
-# Tags
+## Tags
-- Useful for marking deployments and releases
-- Annotated tags are an unchangeable part of Git history
-- Soft/lightweight tags can be set and removed at will
-- Many projects combine an annotated release tag with a stable branch
-- Consider setting deployment/release tags automatically
+- Useful for marking deployments and releases.
+- Annotated tags are an unchangeable part of Git history.
+- Soft/lightweight tags can be set and removed at will.
+- Many projects combine an annotated release tag with a stable branch.
+- Consider setting deployment/release tags automatically.
---
-# Tags
-
-- Create a lightweight tag
-- Create an annotated tag
-- Push the tags to the remote repository
+## Tags steps
-**Additional resources**
+1. Create a lightweight tag.
+1. Create an annotated tag.
+1. Push the tags to the remote repository.
-[http://git-scm.com/book/en/Git-Basics-Tagging](http://git-scm.com/book/en/Git-Basics-Tagging)
+Additional resources: <http://git-scm.com/book/en/Git-Basics-Tagging>.
---
-# Commands
+## Commands (tags)
-```
+```sh
git checkout master
# Lightweight tag
@@ -313,31 +308,31 @@ git push origin --tags
---
-# Merge conflicts
+## Merge conflicts
-- Happen often
-- Learning to fix conflicts is hard
-- Practice makes perfect
+- Happen often.
+- Learning to fix conflicts is hard.
+- Practice makes perfect.
- Force push after fixing conflicts. Be careful!
---
-# Merge conflicts
+## Merge conflicts steps
1. Checkout a new branch and edit `conflicts.rb`. Add 'Line4' and 'Line5'.
-1. Commit and push
+1. Commit and push.
1. Checkout master and edit `conflicts.rb`. Add 'Line6' and 'Line7' below 'Line3'.
-1. Commit and push to master
-1. Create a merge request
+1. Commit and push to master.
+1. Create a merge request.
---
-# Merge conflicts
+## Merge conflicts commands
After creating a merge request you should notice that conflicts exist. Resolve
the conflicts locally by rebasing.
-```
+```sh
git rebase master
# Fix conflicts by editing the files.
@@ -350,7 +345,7 @@ git push origin <branch> -f
---
-# Rebase with squash
+## Rebase with squash
You may end up with a commit log that looks like this:
@@ -368,11 +363,11 @@ Squash these in to meaningful commits using an interactive rebase.
---
-# Rebase with squash
+## Rebase with squash commands
Squash the commits on the same branch we used for the merge conflicts step.
-```
+```sh
git rebase -i master
```
@@ -380,17 +375,17 @@ In the editor, leave the first commit as 'pick' and set others to 'fixup'.
---
-# Questions?
+## Questions?
![fit](logo.png)
Thank you for your hard work!
-**Additional Resources**
+## Additional Resources
-GitLab Documentation [http://docs.gitlab.com](http://docs.gitlab.com/)
-GUI Clients [http://git-scm.com/downloads/guis](http://git-scm.com/downloads/guis)
-Pro git book [http://git-scm.com/book](http://git-scm.com/book)
-Platzi Course [https://courses.platzi.com/courses/git-gitlab/](https://courses.platzi.com/courses/git-gitlab/)
-Code School tutorial [http://try.github.io/](http://try.github.io/)
-Contact Us at `subscribers@gitlab.com`
+- GitLab Documentation: <http://docs.gitlab.com/>.
+- GUI Clients: <http://git-scm.com/downloads/guis>.
+- Pro git book: <http://git-scm.com/book>.
+- Platzi Course: <https://courses.platzi.com/courses/git-gitlab/>.
+- Code School tutorial: <http://try.github.io/>.
+- Contact us at `subscribers@gitlab.com`.
diff --git a/doc/user/markdown.md b/doc/user/markdown.md
index f9bdaea185b..96a509c4b21 100644
--- a/doc/user/markdown.md
+++ b/doc/user/markdown.md
@@ -1,17 +1,8 @@
# Markdown
-## GitLab Flavored Markdown (GFM)
-
-> **Note:**
-> Not all of the GitLab-specific extensions to Markdown that are described in
-> this document currently work on our documentation website.
->
-> For the best result, we encourage you to check this document out as rendered
-> by GitLab: [markdown.md]
-
-_GitLab uses (as of 11.1) the [CommonMark Ruby Library][commonmarker] for Markdown processing of all new issues, merge requests, comments, and other Markdown content in the GitLab system. As of 11.3, wiki pages and Markdown files (`.md`) in the repositories are also processed with CommonMark. Older content in issues/comments are still processed using the [Redcarpet Ruby library][redcarpet]._
+This markdown guide is valid for GitLab's system markdown entries and files.
-_Where there are significant differences, we will try to call them out in this document._
+## GitLab Flavored Markdown (GFM)
GitLab uses "GitLab Flavored Markdown" (GFM). It extends the [CommonMark specification][commonmark-spec] (which is based on standard Markdown) in a few significant ways to add some useful functionality. It was inspired by [GitHub Flavored Markdown](https://help.github.com/articles/basic-writing-and-formatting-syntax/).
@@ -26,11 +17,28 @@ You can use GFM in the following areas:
- markdown documents inside the repository
You can also use other rich text files in GitLab. You might have to install a
-dependency to do so. Please see the [github-markup gem readme](https://github.com/gitlabhq/markup#markups) for more information.
+dependency to do so. Please see the [`github-markup` gem readme](https://github.com/gitlabhq/markup#markups) for more information.
+
+> **Notes:**
+>
+> For the best result, we encourage you to check this document out as rendered
+> by GitLab itself: [markdown.md]
+>
+> As of 11.1, GitLab uses the [CommonMark Ruby Library][commonmarker] for Markdown
+processing of all new issues, merge requests, comments, and other Markdown content
+in the GitLab system. As of 11.3, wiki pages and Markdown files (`.md`) in the
+repositories are also processed with CommonMark. Older content in issues/comments
+are still processed using the [Redcarpet Ruby library][redcarpet].
+>
+> _Where there are significant differences, we will try to call them out in this document._
### Transitioning to CommonMark
-You may have Markdown documents in your repository that were written using some of the nuances of RedCarpet's version of Markdown. Since CommonMark uses a slightly stricter syntax, these documents may now display a little strangely since we've transitioned to CommonMark. Numbered lists with nested lists in particular can be displayed incorrectly.
+You may have Markdown documents in your repository that were written using some
+of the nuances of RedCarpet's version of Markdown. Since CommonMark uses a
+slightly stricter syntax, these documents may now display a little strangely
+since we've transitioned to CommonMark. Numbered lists with nested lists in
+particular can be displayed incorrectly.
It is usually quite easy to fix. In the case of a nested list such as this:
@@ -50,11 +58,18 @@ simply add a space to each nested item:
In the documentation below, we try to highlight some of the differences.
-If you have a need to view a document using RedCarpet, you can add the token `legacy_render=1` to the end of the url, like this:
+If you have a need to view a document using RedCarpet, you can add the token
+`legacy_render=1` to the end of the url, like this:
https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/user/markdown.md?legacy_render=1
-If you have a large volume of Markdown files, it can be tedious to determine if they will be displayed correctly or not. You can use the [diff_redcarpet_cmark](https://gitlab.com/digitalmoksha/diff_redcarpet_cmark) tool (not an officially supported product) to generate a list of files and differences between how RedCarpet and CommonMark render the files. It can give you a great idea if anything needs to be changed - many times nothing will need to changed.
+If you have a large volume of Markdown files, it can be tedious to determine
+if they will be displayed correctly or not. You can use the
+[diff_redcarpet_cmark](https://gitlab.com/digitalmoksha/diff_redcarpet_cmark)
+tool (not an officially supported product) to generate a list of files and
+differences between how RedCarpet and CommonMark render the files. It can give
+you a great idea if anything needs to be changed - many times nothing will need
+to changed.
### Newlines
@@ -63,7 +78,8 @@ https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/user/markdown.md#newline
GFM honors the markdown specification in how [paragraphs and line breaks are handled][commonmark-spec].
-A paragraph is simply one or more consecutive lines of text, separated by one or more blank lines.
+A paragraph is simply one or more consecutive lines of text, separated by one or
+more blank lines.
Line-breaks, or soft returns, are rendered if you end a line with two or more spaces:
<!-- (Do *NOT* remove the two ending whitespaces in the following line.) -->
@@ -85,7 +101,9 @@ Sugar is sweet
> If this is not rendered correctly, see
https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/user/markdown.md#multiple-underscores-in-words
-It is not reasonable to italicize just _part_ of a word, especially when you're dealing with code and names that often appear with multiple underscores. Therefore, GFM ignores multiple underscores in words:
+It is not reasonable to italicize just _part_ of a word, especially when you're
+dealing with code and names that often appear with multiple underscores.
+Therefore, GFM ignores multiple underscores in words:
perform_complicated_task
@@ -124,7 +142,7 @@ https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/user/markdown.md#multili
On top of standard Markdown [blockquotes](#blockquotes), which require prepending `>` to quoted lines,
GFM supports multiline blockquotes fenced by <code>>>></code>:
-```no-highlight
+```
>>>
If you paste a message from somewhere else
@@ -158,7 +176,7 @@ Blocks of code are either fenced by lines with three back-ticks <code>```</code>
or are indented with four spaces. Only the fenced code blocks support syntax
highlighting:
-```no-highlight
+```
Inline `code` has `back-ticks around` it.
```
@@ -248,21 +266,23 @@ However the wrapping tags cannot be mixed as such:
> If this is not rendered correctly, see
https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/user/markdown.md#emoji
- Sometimes you want to :monkey: around a bit and add some :star2: to your :speech_balloon:. Well we have a gift for you:
+```
+Sometimes you want to :monkey: around a bit and add some :star2: to your :speech_balloon:. Well we have a gift for you:
- :zap: You can use emoji anywhere GFM is supported. :v:
+:zap: You can use emoji anywhere GFM is supported. :v:
- You can use it to point out a :bug: or warn about :speak_no_evil: patches. And if someone improves your really :snail: code, send them some :birthday:. People will :heart: you for that.
+You can use it to point out a :bug: or warn about :speak_no_evil: patches. And if someone improves your really :snail: code, send them some :birthday:. People will :heart: you for that.
- If you are new to this, don't be :fearful:. You can easily join the emoji :family:. All you need to do is to look up one of the supported codes.
+If you are new to this, don't be :fearful:. You can easily join the emoji :family:. All you need to do is to look up one of the supported codes.
- Consult the [Emoji Cheat Sheet](https://www.emojicopy.com) for a list of all supported emoji codes. :thumbsup:
+Consult the [Emoji Cheat Sheet](https://www.emojicopy.com) for a list of all supported emoji codes. :thumbsup:
- Most emoji are natively supported on macOS, Windows, iOS, Android and will fallback to image-based emoji where there is lack of support.
+Most emoji are natively supported on macOS, Windows, iOS, Android and will fallback to image-based emoji where there is lack of support.
- On Linux, you can download [Noto Color Emoji](https://www.google.com/get/noto/help/emoji/) to get full native emoji support.
+On Linux, you can download [Noto Color Emoji](https://www.google.com/get/noto/help/emoji/) to get full native emoji support.
- Ubuntu 18.04 (like many modern Linux distros) has this font installed by default.
+Ubuntu 18.04 (like many modern Linux distros) has this font installed by default.
+```
Sometimes you want to <img src="https://gitlab.com/gitlab-org/gitlab-ce/raw/master/app/assets/images/emoji/monkey.png" width="20px" height="20px"> around a bit and add some <img src="https://gitlab.com/gitlab-org/gitlab-ce/raw/master/app/assets/images/emoji/star2.png" width="20px" height="20px"> to your <img src="https://gitlab.com/gitlab-org/gitlab-ce/raw/master/app/assets/images/emoji/speech_balloon.png" width="20px" height="20px">. Well we have a gift for you:
@@ -281,7 +301,6 @@ On Linux, you can download [Noto Color Emoji](https://www.google.com/get/noto/he
Ubuntu 18.04 (like many modern Linux distros) has this font installed by default.
-
### Special GitLab References
GFM recognizes special references.
@@ -343,7 +362,7 @@ https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/user/markdown.md#task-li
You can add task lists to issues, merge requests and comments. To create a task list, add a specially-formatted Markdown list, like so:
-```no-highlight
+```
- [x] Completed task
- [ ] Incomplete task
- [ ] Sub-task 1
@@ -355,7 +374,7 @@ You can add task lists to issues, merge requests and comments. To create a task
Tasks formatted as ordered lists are supported as well:
-```no-highlight
+```
1. [x] Completed task
1. [ ] Incomplete task
1. [ ] Sub-task 1
@@ -412,7 +431,7 @@ This math is inline ![alt text](img/math_inline_sup_render_gfm.png).
This is on a separate line
-<div align="center"><img src="./img/math_inline_sup_render_gfm.png" ></div>
+<img src="./img/math_inline_sup_render_gfm.png" >
_Be advised that KaTeX only supports a [subset][katex-subset] of LaTeX._
@@ -440,7 +459,7 @@ Examples:
`HSL(540,70%,50%)`
`HSLA(540,70%,50%,0.7)`
-Become:
+Becomes:
![alt color-inline-colorchip-render-gfm](img/color_inline_colorchip_render_gfm.png)
@@ -482,7 +501,7 @@ For details see the [Mermaid official page][mermaid].
### Headers
-```no-highlight
+```
# H1
## H2
### H3
@@ -540,7 +559,7 @@ Note that the Emoji processing happens before the header IDs are generated, so t
Examples:
-```no-highlight
+```
Emphasis, aka italics, with *asterisks* or _underscores_.
Strong emphasis, aka bold, with **asterisks** or __underscores__.
@@ -550,7 +569,7 @@ Combined emphasis with **asterisks and _underscores_**.
Strikethrough uses two tildes. ~~Scratch this.~~
```
-Become:
+Becomes:
Emphasis, aka italics, with *asterisks* or _underscores_.
@@ -564,7 +583,7 @@ Strikethrough uses two tildes. ~~Scratch this.~~
Examples:
-```no-highlight
+```
1. First ordered list item
2. Another item
* Unordered sub-list.
@@ -577,7 +596,7 @@ Examples:
+ Or pluses
```
-Become:
+Becomes:
1. First ordered list item
2. Another item
@@ -595,7 +614,7 @@ each subsequent paragraph should be indented to the same level as the start of t
Example:
-```no-highlight
+```
1. First ordered list item
Second paragraph of first item.
@@ -616,7 +635,7 @@ the paragraph will appear outside the list, instead of properly indented under t
Example:
-```no-highlight
+```
1. First ordered list item
Paragraph of first item.
@@ -676,7 +695,7 @@ Examples:
[logo]: img/markdown_logo.png
-Become:
+Becomes:
Here's our logo:
@@ -694,7 +713,7 @@ Reference-style:
Examples:
-```no-highlight
+```
> Blockquotes are very handy in email to emulate reply text.
> This line is part of the same quote.
@@ -703,7 +722,7 @@ Quote break.
> This is a very long line that will still be quoted properly when it wraps. Oh boy let's keep writing to make sure this is long enough to actually wrap for everyone. Oh, you can *put* **Markdown** into a blockquote.
```
-Become:
+Becomes:
> Blockquotes are very handy in email to emulate reply text.
> This line is part of the same quote.
@@ -720,7 +739,7 @@ See the documentation for HTML::Pipeline's [SanitizationFilter](http://www.rubyd
Examples:
-```no-highlight
+```
<dl>
<dt>Definition list</dt>
<dd>Is something people use sometimes.</dd>
@@ -730,7 +749,7 @@ Examples:
</dl>
```
-Become:
+Becomes:
<dl>
<dt>Definition list</dt>
@@ -788,7 +807,7 @@ ___
Underscores
```
-Become:
+Becomes:
Three or more...
@@ -826,7 +845,7 @@ This line is *on its own line*, because the previous line ends with two spaces.
spaces.
```
-Become:
+Becomes:
Here's a line for us to start with.
diff --git a/lib/api/issues.rb b/lib/api/issues.rb
index e37083165f5..7909f9c7a00 100644
--- a/lib/api/issues.rb
+++ b/lib/api/issues.rb
@@ -294,6 +294,30 @@ module API
end
# rubocop: enable CodeReuse/ActiveRecord
+ desc 'List merge requests that are related to the issue' do
+ success Entities::MergeRequestBasic
+ end
+ params do
+ requires :issue_iid, type: Integer, desc: 'The internal ID of a project issue'
+ end
+ get ':id/issues/:issue_iid/related_merge_requests' do
+ issue = find_project_issue(params[:issue_iid])
+
+ merge_request_iids = ::Issues::ReferencedMergeRequestsService.new(user_project, current_user)
+ .execute(issue)
+ .flatten
+ .map(&:iid)
+
+ merge_requests =
+ if merge_request_iids.present?
+ MergeRequestsFinder.new(current_user, project_id: user_project.id, iids: merge_request_iids).execute
+ else
+ MergeRequest.none
+ end
+
+ present paginate(merge_requests), with: Entities::MergeRequestBasic, current_user: current_user, project: user_project
+ end
+
desc 'List merge requests closing issue' do
success Entities::MergeRequestBasic
end
diff --git a/lib/banzai/filter/issuable_state_filter.rb b/lib/banzai/filter/issuable_state_filter.rb
index d7fe012883d..8e2358694d4 100644
--- a/lib/banzai/filter/issuable_state_filter.rb
+++ b/lib/banzai/filter/issuable_state_filter.rb
@@ -18,7 +18,7 @@ module Banzai
issuables = extractor.extract([doc])
issuables.each do |node, issuable|
- next if !can_read_cross_project? && issuable.project != project
+ next if !can_read_cross_project? && cross_reference?(issuable)
if VISIBLE_STATES.include?(issuable.state) && issuable_reference?(node.inner_html, issuable)
node.content += " (#{issuable.state})"
@@ -31,7 +31,14 @@ module Banzai
private
def issuable_reference?(text, issuable)
- text == issuable.reference_link_text(project || group)
+ CGI.unescapeHTML(text) == issuable.reference_link_text(project || group)
+ end
+
+ def cross_reference?(issuable)
+ return true if issuable.project != project
+ return true if issuable.respond_to?(:group) && issuable.group != group
+
+ false
end
def can_read_cross_project?
diff --git a/lib/banzai/issuable_extractor.rb b/lib/banzai/issuable_extractor.rb
index 0a05d46db4c..341dbb74fe0 100644
--- a/lib/banzai/issuable_extractor.rb
+++ b/lib/banzai/issuable_extractor.rb
@@ -9,13 +9,11 @@ module Banzai
# so we can avoid N+1 queries problem
class IssuableExtractor
- QUERY = %q(
- descendant-or-self::a[contains(concat(" ", @class, " "), " gfm ")]
- [@data-reference-type="issue" or @data-reference-type="merge_request"]
- ).freeze
-
attr_reader :context
+ ISSUE_REFERENCE_TYPE = '@data-reference-type="issue"'.freeze
+ MERGE_REQUEST_REFERENCE_TYPE = '@data-reference-type="merge_request"'.freeze
+
# context - An instance of Banzai::RenderContext.
def initialize(context)
@context = context
@@ -24,21 +22,38 @@ module Banzai
# Returns Hash in the form { node => issuable_instance }
def extract(documents)
nodes = documents.flat_map do |document|
- document.xpath(QUERY)
+ document.xpath(query)
end
- issue_parser = Banzai::ReferenceParser::IssueParser.new(context)
+ # The project or group for the issuable might be pending for deletion!
+ # Filter them out because we don't care about them.
+ issuables_for_nodes(nodes).select { |node, issuable| issuable.project || issuable.group }
+ end
+
+ private
- merge_request_parser =
+ def issuables_for_nodes(nodes)
+ parsers.each_with_object({}) do |parser, result|
+ result.merge!(parser.records_for_nodes(nodes))
+ end
+ end
+
+ def parsers
+ [
+ Banzai::ReferenceParser::IssueParser.new(context),
Banzai::ReferenceParser::MergeRequestParser.new(context)
+ ]
+ end
- issuables_for_nodes = issue_parser.records_for_nodes(nodes).merge(
- merge_request_parser.records_for_nodes(nodes)
+ def query
+ %Q(
+ descendant-or-self::a[contains(concat(" ", @class, " "), " gfm ")]
+ [#{reference_types.join(' or ')}]
)
+ end
- # The project for the issue/MR might be pending for deletion!
- # Filter them out because we don't care about them.
- issuables_for_nodes.select { |node, issuable| issuable.project }
+ def reference_types
+ [ISSUE_REFERENCE_TYPE, MERGE_REQUEST_REFERENCE_TYPE]
end
end
end
diff --git a/lib/extracts_path.rb b/lib/extracts_path.rb
index a340a276640..655278da711 100644
--- a/lib/extracts_path.rb
+++ b/lib/extracts_path.rb
@@ -153,7 +153,7 @@ module ExtractsPath
private
- # overriden in subclasses, do not remove
+ # overridden in subclasses, do not remove
def get_id
id = [params[:id] || params[:ref]]
id << "/" + params[:path] unless params[:path].blank?
diff --git a/lib/gitlab/auth/o_auth/auth_hash.rb b/lib/gitlab/auth/o_auth/auth_hash.rb
index 4a5f9d2839d..36fc8061d92 100644
--- a/lib/gitlab/auth/o_auth/auth_hash.rb
+++ b/lib/gitlab/auth/o_auth/auth_hash.rb
@@ -80,7 +80,7 @@ module Gitlab
end
# Get the first part of the email address (before @)
- # In addtion in removes illegal characters
+ # In addition in removes illegal characters
def generate_username(email)
email.match(/^[^@]*/)[0].mb_chars.normalize(:kd).gsub(/[^\x00-\x7F]/, '').to_s
end
diff --git a/lib/gitlab/background_migration/set_confidential_note_events_on_services.rb b/lib/gitlab/background_migration/set_confidential_note_events_on_services.rb
index e5e8837221e..bc434b0cb64 100644
--- a/lib/gitlab/background_migration/set_confidential_note_events_on_services.rb
+++ b/lib/gitlab/background_migration/set_confidential_note_events_on_services.rb
@@ -3,8 +3,8 @@
module Gitlab
module BackgroundMigration
- # Ensures services which previously recieved all notes events continue
- # to recieve confidential ones.
+ # Ensures services which previously received all notes events continue
+ # to receive confidential ones.
class SetConfidentialNoteEventsOnServices
class Service < ActiveRecord::Base
self.table_name = 'services'
diff --git a/lib/gitlab/background_migration/set_confidential_note_events_on_webhooks.rb b/lib/gitlab/background_migration/set_confidential_note_events_on_webhooks.rb
index 171c8ef21b7..28d8d2c640b 100644
--- a/lib/gitlab/background_migration/set_confidential_note_events_on_webhooks.rb
+++ b/lib/gitlab/background_migration/set_confidential_note_events_on_webhooks.rb
@@ -3,8 +3,8 @@
module Gitlab
module BackgroundMigration
- # Ensures hooks which previously recieved all notes events continue
- # to recieve confidential ones.
+ # Ensures hooks which previously received all notes events continue
+ # to receive confidential ones.
class SetConfidentialNoteEventsOnWebhooks
class WebHook < ActiveRecord::Base
self.table_name = 'web_hooks'
diff --git a/lib/gitlab/ci/templates/Maven.gitlab-ci.yml b/lib/gitlab/ci/templates/Maven.gitlab-ci.yml
index d61ff239e13..492b3d03db2 100644
--- a/lib/gitlab/ci/templates/Maven.gitlab-ci.yml
+++ b/lib/gitlab/ci/templates/Maven.gitlab-ci.yml
@@ -15,7 +15,7 @@
# * Publishes the documentation for `master` branch.
variables:
- # This will supress any download for dependencies and plugins or upload messages which would clutter the console log.
+ # This will suppress any download for dependencies and plugins or upload messages which would clutter the console log.
# `showDateTime` will show the passed time in milliseconds. You need to specify `--batch-mode` to make this work.
MAVEN_OPTS: "-Dhttps.protocols=TLSv1.2 -Dmaven.repo.local=$CI_PROJECT_DIR/.m2/repository -Dorg.slf4j.simpleLogger.log.org.apache.maven.cli.transfer.Slf4jMavenTransferListener=WARN -Dorg.slf4j.simpleLogger.showDateTime=true -Djava.awt.headless=true"
# As of Maven 3.3.0 instead of this you may define these options in `.mvn/maven.config` so the same config is used
diff --git a/lib/gitlab/database/migration_helpers.rb b/lib/gitlab/database/migration_helpers.rb
index a17f27a3147..f98d6dbd46f 100644
--- a/lib/gitlab/database/migration_helpers.rb
+++ b/lib/gitlab/database/migration_helpers.rb
@@ -879,7 +879,7 @@ module Gitlab
columns(table).find { |column| column.name == name }
end
- # This will replace the first occurance of a string in a column with
+ # This will replace the first occurrence of a string in a column with
# the replacement
# On postgresql we can use `regexp_replace` for that.
# On mysql we find the location of the pattern, and overwrite it
diff --git a/lib/gitlab/diff/position_tracer.rb b/lib/gitlab/diff/position_tracer.rb
index b68a1636814..8457e0c4cb6 100644
--- a/lib/gitlab/diff/position_tracer.rb
+++ b/lib/gitlab/diff/position_tracer.rb
@@ -24,7 +24,7 @@ module Gitlab
# head of `feature` was commit B, resulting in the original diff A->B.
# Since creation, `master` was updated to C.
# Now `feature` is being updated to D, and the newly generated MR diff is C->D.
- # It is possible that C and D are direct decendants of A and B respectively,
+ # It is possible that C and D are direct descendants of A and B respectively,
# but this isn't necessarily the case as rebases and merges come into play.
#
# Suppose we have a diff note on the original diff A->B. Now that the MR
diff --git a/lib/gitlab/gitaly_client/repository_service.rb b/lib/gitlab/gitaly_client/repository_service.rb
index 2956ed4b911..d7b36946b65 100644
--- a/lib/gitlab/gitaly_client/repository_service.rb
+++ b/lib/gitlab/gitaly_client/repository_service.rb
@@ -349,7 +349,7 @@ module Gitlab
f.write(message.data)
end
end
- # If the file is empty means that we recieved an empty stream, we delete the file
+ # If the file is empty means that we received an empty stream, we delete the file
FileUtils.rm(save_path) if File.zero?(save_path)
end
diff --git a/lib/gitlab/import/merge_request_helpers.rb b/lib/gitlab/import/merge_request_helpers.rb
index 97dc1a987c4..9215067d973 100644
--- a/lib/gitlab/import/merge_request_helpers.rb
+++ b/lib/gitlab/import/merge_request_helpers.rb
@@ -22,7 +22,7 @@ module Gitlab
# additional work that is strictly necessary.
merge_request_id = insert_and_return_id(attributes, project.merge_requests)
- merge_request = project.merge_requests.find(merge_request_id)
+ merge_request = project.merge_requests.reload.find(merge_request_id)
# We use .insert_and_return_id which effectively disables all callbacks.
# Trigger iid logic here to make sure we track internal id values consistently.
diff --git a/lib/gitlab/import_export/project_tree_restorer.rb b/lib/gitlab/import_export/project_tree_restorer.rb
index 3d693d23c99..99581eb0416 100644
--- a/lib/gitlab/import_export/project_tree_restorer.rb
+++ b/lib/gitlab/import_export/project_tree_restorer.rb
@@ -154,7 +154,7 @@ module Gitlab
Project.transaction do
process_sub_relation(relation, relation_item)
- # For every subrelation that hangs from Project, save the associated records alltogether
+ # For every subrelation that hangs from Project, save the associated records altogether
# This effectively batches all records per subrelation item, only keeping those in memory
# We have to keep in mind that more batch granularity << Memory, but >> Slowness
if save
diff --git a/lib/gitlab/path_regex.rb b/lib/gitlab/path_regex.rb
index 44025650de0..fa68dead80b 100644
--- a/lib/gitlab/path_regex.rb
+++ b/lib/gitlab/path_regex.rb
@@ -236,7 +236,7 @@ module Gitlab
def single_line_regexp(regex)
# Turns a multiline extended regexp into a single line one,
- # beacuse `rake routes` breaks on multiline regexes.
+ # because `rake routes` breaks on multiline regexes.
Regexp.new(regex.source.gsub(/\(\?#.+?\)/, '').gsub(/\s*/, ''), regex.options ^ Regexp::EXTENDED).freeze
end
end
diff --git a/lib/gitlab/proxy_http_connection_adapter.rb b/lib/gitlab/proxy_http_connection_adapter.rb
index 82213098672..a64cb47e77e 100644
--- a/lib/gitlab/proxy_http_connection_adapter.rb
+++ b/lib/gitlab/proxy_http_connection_adapter.rb
@@ -4,7 +4,7 @@
# of the global setting allow_local_requests_from_hooks_and_services this adapter
# will allow/block connection to internal IPs and/or urls.
#
-# This functionality can be overriden by providing the setting the option
+# This functionality can be overridden by providing the setting the option
# allow_local_requests = true in the request. For example:
# Gitlab::HTTP.get('http://www.gitlab.com', allow_local_requests: true)
#
diff --git a/lib/gitlab/slash_commands/issue_new.rb b/lib/gitlab/slash_commands/issue_new.rb
index 25f965e843d..6396b828dc7 100644
--- a/lib/gitlab/slash_commands/issue_new.rb
+++ b/lib/gitlab/slash_commands/issue_new.rb
@@ -3,7 +3,7 @@ module Gitlab
class IssueNew < IssueCommand
def self.match(text)
# we can not match \n with the dot by passing the m modifier as than
- # the title and description are not seperated
+ # the title and description are not separated
/\Aissue\s+(new|create)\s+(?<title>[^\n]*)\n*(?<description>(.|\n)*)/.match(text)
end
diff --git a/lib/gitlab/user_extractor.rb b/lib/gitlab/user_extractor.rb
index bd0d24e4369..874599688bb 100644
--- a/lib/gitlab/user_extractor.rb
+++ b/lib/gitlab/user_extractor.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
# This class extracts all users found in a piece of text by the username or the
-# email adress
+# email address
module Gitlab
class UserExtractor
@@ -14,13 +14,11 @@ module Gitlab
@text = text
end
- # rubocop: disable CodeReuse/ActiveRecord
def users
return User.none unless @text.present?
@users ||= User.from_union(union_relations)
end
- # rubocop: enable CodeReuse/ActiveRecord
def usernames
matches[:usernames]
diff --git a/lib/google_api/auth.rb b/lib/google_api/auth.rb
index e724e58e9ca..56f056fd869 100644
--- a/lib/google_api/auth.rb
+++ b/lib/google_api/auth.rb
@@ -16,7 +16,7 @@ module GoogleApi
client.auth_code.authorize_url(
redirect_uri: redirect_uri,
scope: scope,
- state: state # This is used for arbitary redirection
+ state: state # This is used for arbitrary redirection
)
end
diff --git a/package.json b/package.json
index 608d4e58dd3..2d6479fea3f 100644
--- a/package.json
+++ b/package.json
@@ -24,8 +24,8 @@
"@babel/plugin-syntax-dynamic-import": "^7.0.0",
"@babel/plugin-syntax-import-meta": "^7.0.0",
"@babel/preset-env": "^7.1.0",
- "@gitlab-org/gitlab-svgs": "^1.33.0",
"@gitlab-org/gitlab-ui": "^1.10.0",
+ "@gitlab/svgs": "^1.35.0",
"autosize": "^4.0.0",
"axios": "^0.17.1",
"babel-loader": "^8.0.4",
diff --git a/rubocop/cop/code_reuse/active_record.rb b/rubocop/cop/code_reuse/active_record.rb
index d25e8548fd0..2be8f7c11aa 100644
--- a/rubocop/cop/code_reuse/active_record.rb
+++ b/rubocop/cop/code_reuse/active_record.rb
@@ -49,7 +49,6 @@ module RuboCop
limit: true,
lock: false,
many?: false,
- none: false,
offset: true,
order: true,
pluck: true,
diff --git a/scripts/build_assets_image b/scripts/build_assets_image
new file mode 100755
index 00000000000..218606b9a40
--- /dev/null
+++ b/scripts/build_assets_image
@@ -0,0 +1,21 @@
+#!/bin/bash
+
+# Generate the image name based on the project this is being run in
+ASSETS_IMAGE_NAME=$(echo ${CI_PROJECT_NAME} |
+ awk '{
+ split($1, p, "-");
+ interim = sprintf("%s-assets-%s", p[1], p[2]);
+ sub(/-$/, "", interim);
+ print interim
+ }'
+)
+
+ASSETS_IMAGE_PATH=${CI_REGISTRY}/${CI_PROJECT_PATH}/${ASSETS_IMAGE_NAME}
+
+mkdir -p assets_container.build/public
+cp -r public/assets assets_container.build/public/
+cp Dockerfile.assets assets_container.build/
+docker build -t ${ASSETS_IMAGE_PATH}:${CI_COMMIT_REF_NAME} -f assets_container.build/Dockerfile.assets assets_container.build/
+docker login -u gitlab-ci-token -p ${CI_JOB_TOKEN} ${CI_REGISTRY}
+docker push ${ASSETS_IMAGE_PATH}
+
diff --git a/scripts/static-analysis b/scripts/static-analysis
index 0e67eabfec1..25ba7ec6c8e 100755
--- a/scripts/static-analysis
+++ b/scripts/static-analysis
@@ -29,6 +29,7 @@ tasks = [
%w[bin/rake lint:all],
%w[bundle exec license_finder],
%w[yarn run eslint],
+ %w[yarn run prettier-all],
%w[bundle exec rubocop --parallel],
%w[scripts/lint-conflicts.sh],
%w[scripts/lint-rugged]
diff --git a/scripts/trigger-build b/scripts/trigger-build
index b76cd5dd6f0..dd0425b6472 100755
--- a/scripts/trigger-build
+++ b/scripts/trigger-build
@@ -32,32 +32,32 @@ module Trigger
private
- # Must be overriden
+ # Must be overridden
def downstream_project_path
raise NotImplementedError
end
- # Must be overriden
+ # Must be overridden
def ref
raise NotImplementedError
end
- # Must be overriden
+ # Must be overridden
def trigger_token
raise NotImplementedError
end
- # Must be overriden
+ # Must be overridden
def access_token
raise NotImplementedError
end
- # Can be overriden
+ # Can be overridden
def extra_variables
{}
end
- # Can be overriden
+ # Can be overridden
def version_param_value(version_file)
File.read(version_file).strip
end
diff --git a/spec/controllers/groups/milestones_controller_spec.rb b/spec/controllers/groups/milestones_controller_spec.rb
index 465f3499703..42723bb3820 100644
--- a/spec/controllers/groups/milestones_controller_spec.rb
+++ b/spec/controllers/groups/milestones_controller_spec.rb
@@ -63,7 +63,7 @@ describe Groups::MilestonesController do
let(:group_milestone) { create(:milestone, group: group) }
context 'when there is a title parameter' do
- it 'searchs for a legacy group milestone' do
+ it 'searches for a legacy group milestone' do
expect(GlobalMilestone).to receive(:build)
expect(Milestone).not_to receive(:find_by_iid)
@@ -72,7 +72,7 @@ describe Groups::MilestonesController do
end
context 'when there is not a title parameter' do
- it 'searchs for a group milestone' do
+ it 'searches for a group milestone' do
expect(GlobalMilestone).not_to receive(:build)
expect(Milestone).to receive(:find_by_iid)
diff --git a/spec/controllers/groups_controller_spec.rb b/spec/controllers/groups_controller_spec.rb
index a099cdafa58..4de61b65f71 100644
--- a/spec/controllers/groups_controller_spec.rb
+++ b/spec/controllers/groups_controller_spec.rb
@@ -529,7 +529,7 @@ describe GroupsController do
sign_in(user)
end
- context 'when transfering to a subgroup goes right' do
+ context 'when transferring to a subgroup goes right' do
let(:new_parent_group) { create(:group, :public) }
let!(:group_member) { create(:group_member, :owner, group: group, user: user) }
let!(:new_parent_group_member) { create(:group_member, :owner, group: new_parent_group, user: user) }
diff --git a/spec/controllers/profiles/keys_controller_spec.rb b/spec/controllers/profiles/keys_controller_spec.rb
index 363ed410bc0..ea26bc83353 100644
--- a/spec/controllers/profiles/keys_controller_spec.rb
+++ b/spec/controllers/profiles/keys_controller_spec.rb
@@ -4,7 +4,7 @@ describe Profiles::KeysController do
let(:user) { create(:user) }
describe "#get_keys" do
- describe "non existant user" do
+ describe "non existent user" do
it "does not generally work" do
get :get_keys, username: 'not-existent'
diff --git a/spec/controllers/projects/environments_controller_spec.rb b/spec/controllers/projects/environments_controller_spec.rb
index b86029a4baf..bc17331f531 100644
--- a/spec/controllers/projects/environments_controller_spec.rb
+++ b/spec/controllers/projects/environments_controller_spec.rb
@@ -216,7 +216,7 @@ describe Projects::EnvironmentsController do
expect(response).to have_gitlab_http_status(200)
end
- it 'loads the terminals for the enviroment' do
+ it 'loads the terminals for the environment' do
expect_any_instance_of(Environment).to receive(:terminals)
get :terminal, environment_params
diff --git a/spec/controllers/projects/jobs_controller_spec.rb b/spec/controllers/projects/jobs_controller_spec.rb
index 2023d4b0bd0..8eb01145ed5 100644
--- a/spec/controllers/projects/jobs_controller_spec.rb
+++ b/spec/controllers/projects/jobs_controller_spec.rb
@@ -152,11 +152,33 @@ describe Projects::JobsController, :clean_gitlab_redis_shared_state do
expect(response).to have_gitlab_http_status(:ok)
expect(response).to match_response_schema('job/job_details')
expect(json_response['raw_path']).to match(%r{jobs/\d+/raw\z})
- expect(json_response.dig('merge_request', 'path')).to match(%r{merge_requests/\d+\z})
+ expect(json_response['merge_request']['path']).to match(%r{merge_requests/\d+\z})
expect(json_response['new_issue_path']).to include('/issues/new')
end
end
+ context 'when job is running' do
+ context 'job is cancelable' do
+ let(:job) { create(:ci_build, :running, pipeline: pipeline) }
+
+ it 'cancel_path is present with correct redirect' do
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(response).to match_response_schema('job/job_details')
+ expect(json_response['cancel_path']).to include(CGI.escape(json_response['build_path']))
+ end
+ end
+
+ context 'with web terminal' do
+ let(:job) { create(:ci_build, :running, :with_runner_session, pipeline: pipeline) }
+
+ it 'exposes the terminal path' do
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(response).to match_response_schema('job/job_details')
+ expect(json_response['terminal_path']).to match(%r{/terminal})
+ end
+ end
+ end
+
context 'when job has artifacts' do
context 'with not expiry date' do
let(:job) { create(:ci_build, :success, :artifacts, pipeline: pipeline) }
@@ -185,16 +207,6 @@ describe Projects::JobsController, :clean_gitlab_redis_shared_state do
end
end
- context 'when job has terminal' do
- let(:job) { create(:ci_build, :running, :with_runner_session, pipeline: pipeline) }
-
- it 'exposes the terminal path' do
- expect(response).to have_gitlab_http_status(:ok)
- expect(response).to match_response_schema('job/job_details')
- expect(json_response['terminal_path']).to match(%r{/terminal})
- end
- end
-
context 'when job passed with no trace' do
let(:job) { create(:ci_build, :success, :artifacts, pipeline: pipeline) }
diff --git a/spec/controllers/projects/merge_requests_controller_spec.rb b/spec/controllers/projects/merge_requests_controller_spec.rb
index 7b0459e0325..7463586621c 100644
--- a/spec/controllers/projects/merge_requests_controller_spec.rb
+++ b/spec/controllers/projects/merge_requests_controller_spec.rb
@@ -786,7 +786,7 @@ describe Projects::MergeRequestsController do
merge_request.mark_as_merged!
end
- it 'returns the enviroment on the source project' do
+ it 'returns the environment on the source project' do
get_ci_environments_status(environment_target: 'merge_commit')
expect(response).to have_gitlab_http_status(:ok)
diff --git a/spec/features/dashboard/archived_projects_spec.rb b/spec/features/dashboard/archived_projects_spec.rb
index 6a0cd848345..d31df322d10 100644
--- a/spec/features/dashboard/archived_projects_spec.rb
+++ b/spec/features/dashboard/archived_projects_spec.rb
@@ -33,7 +33,7 @@ RSpec.describe 'Dashboard Archived Project' do
expect(page).not_to have_content(project.name)
end
- it 'searchs archived projects', :js do
+ it 'searches archived projects', :js do
click_button 'Last updated'
click_link 'Show archived projects'
diff --git a/spec/features/import/manifest_import_spec.rb b/spec/features/import/manifest_import_spec.rb
index e381d073804..a90cdd8d920 100644
--- a/spec/features/import/manifest_import_spec.rb
+++ b/spec/features/import/manifest_import_spec.rb
@@ -22,7 +22,7 @@ describe 'Import multiple repositories by uploading a manifest file', :js, :post
expect(page).to have_content('https://android-review.googlesource.com/platform/build/blueprint')
end
- it 'imports succesfully imports a project' do
+ it 'imports successfully imports a project' do
visit new_import_manifest_path
attach_file('manifest', Rails.root.join('spec/fixtures/aosp_manifest.xml'))
diff --git a/spec/features/issues/filtered_search/dropdown_emoji_spec.rb b/spec/features/issues/filtered_search/dropdown_emoji_spec.rb
index be229e8aa7d..c42fcd92a36 100644
--- a/spec/features/issues/filtered_search/dropdown_emoji_spec.rb
+++ b/spec/features/issues/filtered_search/dropdown_emoji_spec.rb
@@ -92,7 +92,7 @@ describe 'Dropdown emoji', :js do
it 'shows the most populated emoji at top of dropdown' do
send_keys_to_filtered_search('my-reaction:')
- expect(first('#js-dropdown-my-reaction li')).to have_content(award_emoji_star.name)
+ expect(first('#js-dropdown-my-reaction .filter-dropdown li')).to have_content(award_emoji_star.name)
end
end
@@ -121,13 +121,29 @@ describe 'Dropdown emoji', :js do
send_keys_to_filtered_search(':')
end
+ it 'selects `None`' do
+ find('#js-dropdown-my-reaction .filter-dropdown-item', text: 'None').click
+
+ expect(page).to have_css(js_dropdown_emoji, visible: false)
+ expect_tokens([reaction_token('none', false)])
+ expect_filtered_search_input_empty
+ end
+
+ it 'selects `Any`' do
+ find('#js-dropdown-my-reaction .filter-dropdown-item', text: 'Any').click
+
+ expect(page).to have_css(js_dropdown_emoji, visible: false)
+ expect_tokens([reaction_token('any', false)])
+ expect_filtered_search_input_empty
+ end
+
it 'fills in the my-reaction name' do
click_emoji('thumbsup')
wait_for_requests
expect(page).to have_css(js_dropdown_emoji, visible: false)
- expect_tokens([emoji_token('thumbsup')])
+ expect_tokens([reaction_token('thumbsup')])
expect_filtered_search_input_empty
end
end
diff --git a/spec/features/issues/gfm_autocomplete_spec.rb b/spec/features/issues/gfm_autocomplete_spec.rb
index 593dc6b6690..605860b90cd 100644
--- a/spec/features/issues/gfm_autocomplete_spec.rb
+++ b/spec/features/issues/gfm_autocomplete_spec.rb
@@ -15,7 +15,7 @@ describe 'GFM autocomplete', :js do
wait_for_requests
end
- it 'updates issue descripton with GFM reference' do
+ it 'updates issue description with GFM reference' do
find('.js-issuable-edit').click
simulate_input('#issue-description', "@#{user.name[0...3]}")
diff --git a/spec/features/projects/badges/pipeline_badge_spec.rb b/spec/features/projects/badges/pipeline_badge_spec.rb
index 8c4488b2ca6..dee81898928 100644
--- a/spec/features/projects/badges/pipeline_badge_spec.rb
+++ b/spec/features/projects/badges/pipeline_badge_spec.rb
@@ -19,7 +19,7 @@ describe 'Pipeline Badge' do
let!(:pipeline) { create(:ci_empty_pipeline, project: project, ref: ref, sha: project.commit(ref).sha) }
let!(:job) { create(:ci_build, pipeline: pipeline) }
- context 'when the pipeline was successfull' do
+ context 'when the pipeline was successful' do
it 'displays so on the badge' do
job.success
diff --git a/spec/features/projects/clusters_spec.rb b/spec/features/projects/clusters_spec.rb
index 91eac9c8278..f13c35c00d3 100644
--- a/spec/features/projects/clusters_spec.rb
+++ b/spec/features/projects/clusters_spec.rb
@@ -40,7 +40,7 @@ describe 'Clusters', :js do
expect(page).to have_selector('.js-project-feature-toggle')
end
- context 'with sucessfull request' do
+ context 'with successful request' do
it 'user sees updated cluster' do
expect do
page.find('.js-project-feature-toggle').click
diff --git a/spec/features/projects/files/user_creates_files_spec.rb b/spec/features/projects/files/user_creates_files_spec.rb
index d9df4b50621..14b5bd58bd1 100644
--- a/spec/features/projects/files/user_creates_files_spec.rb
+++ b/spec/features/projects/files/user_creates_files_spec.rb
@@ -16,7 +16,7 @@ describe 'Projects > Files > User creates files' do
sign_in(user)
end
- context 'without commiting a new file' do
+ context 'without committing a new file' do
context 'when an user has write access' do
before do
visit(project_tree_path_root_ref)
@@ -49,7 +49,7 @@ describe 'Projects > Files > User creates files' do
end
end
- context 'with commiting a new file' do
+ context 'with committing a new file' do
context 'when an user has write access' do
before do
visit(project_tree_path_root_ref)
diff --git a/spec/features/projects/jobs_spec.rb b/spec/features/projects/jobs_spec.rb
index b3bea92e635..5cb3f7c732f 100644
--- a/spec/features/projects/jobs_spec.rb
+++ b/spec/features/projects/jobs_spec.rb
@@ -198,6 +198,24 @@ describe 'Jobs', :clean_gitlab_redis_shared_state do
end
end
+ context 'when job is running', :js do
+ let(:job) { create(:ci_build, :running, pipeline: pipeline) }
+ let(:job_url) { project_job_path(project, job) }
+
+ before do
+ visit job_url
+ wait_for_requests
+ end
+
+ context 'job is cancelable' do
+ it 'shows cancel button' do
+ click_link 'Cancel'
+
+ expect(page.current_path).to eq(job_url)
+ end
+ end
+ end
+
context "Job from other project" do
before do
visit project_job_path(project, job2)
diff --git a/spec/finders/issues_finder_spec.rb b/spec/finders/issues_finder_spec.rb
index 2f164ffa8b0..c0488c83bd8 100644
--- a/spec/finders/issues_finder_spec.rb
+++ b/spec/finders/issues_finder_spec.rb
@@ -144,19 +144,31 @@ describe IssuesFinder do
end
context 'filtering by no milestone' do
- let(:params) { { milestone_title: Milestone::None.title } }
+ let(:params) { { milestone_title: 'None' } }
it 'returns issues with no milestone' do
expect(issues).to contain_exactly(issue2, issue3, issue4)
end
+
+ it 'returns issues with no milestone (deprecated)' do
+ params[:milestone_title] = Milestone::None.title
+
+ expect(issues).to contain_exactly(issue2, issue3, issue4)
+ end
end
context 'filtering by any milestone' do
- let(:params) { { milestone_title: Milestone::Any.title } }
+ let(:params) { { milestone_title: 'Any' } }
it 'returns issues with any assigned milestone' do
expect(issues).to contain_exactly(issue1)
end
+
+ it 'returns issues with any assigned milestone (deprecated)' do
+ params[:milestone_title] = Milestone::Any.title
+
+ expect(issues).to contain_exactly(issue1)
+ end
end
context 'filtering by upcoming milestone' do
@@ -360,6 +372,22 @@ describe IssuesFinder do
end
context 'filtering by reaction name' do
+ context 'user searches by no reaction' do
+ let(:params) { { my_reaction_emoji: 'None' } }
+
+ it 'returns issues that the user did not react to' do
+ expect(issues).to contain_exactly(issue2, issue4)
+ end
+ end
+
+ context 'user searches by any reaction' do
+ let(:params) { { my_reaction_emoji: 'Any' } }
+
+ it 'returns issues that the user reacted to' do
+ expect(issues).to contain_exactly(issue1, issue3)
+ end
+ end
+
context 'user searches by "thumbsup" reaction' do
let(:params) { { my_reaction_emoji: 'thumbsup' } }
diff --git a/spec/javascripts/awards_handler_spec.js b/spec/javascripts/awards_handler_spec.js
index b0689fc7cfe..ce5d2022441 100644
--- a/spec/javascripts/awards_handler_spec.js
+++ b/spec/javascripts/awards_handler_spec.js
@@ -219,7 +219,7 @@ describe('AwardsHandler', function() {
expect($thumbsUpEmoji.data('originalTitle')).toBe('You, sam, jerry, max, and andy');
});
- it('handles the special case where "You" is not cleanly comma seperated', function() {
+ it('handles the special case where "You" is not cleanly comma separated', function() {
const awardUrl = awardsHandler.getAwardUrl();
const $votesBlock = $('.js-awards-block').eq(0);
const $thumbsUpEmoji = $votesBlock.find('[data-name=thumbsup]').parent();
@@ -244,7 +244,7 @@ describe('AwardsHandler', function() {
expect($thumbsUpEmoji.data('originalTitle')).toBe('sam, jerry, max, and andy');
});
- it('handles the special case where "You" is not cleanly comma seperated', function() {
+ it('handles the special case where "You" is not cleanly comma separated', function() {
const awardUrl = awardsHandler.getAwardUrl();
const $votesBlock = $('.js-awards-block').eq(0);
const $thumbsUpEmoji = $votesBlock.find('[data-name=thumbsup]').parent();
diff --git a/spec/javascripts/boards/board_list_spec.js b/spec/javascripts/boards/board_list_spec.js
index 037e06cf3b2..2642c8b1bdb 100644
--- a/spec/javascripts/boards/board_list_spec.js
+++ b/spec/javascripts/boards/board_list_spec.js
@@ -18,7 +18,7 @@ describe('Board list component', () => {
let mock;
let component;
- beforeEach((done) => {
+ beforeEach(done => {
const el = document.createElement('div');
document.body.appendChild(el);
@@ -62,122 +62,102 @@ describe('Board list component', () => {
});
it('renders component', () => {
- expect(
- component.$el.classList.contains('board-list-component'),
- ).toBe(true);
+ expect(component.$el.classList.contains('board-list-component')).toBe(true);
});
- it('renders loading icon', (done) => {
+ it('renders loading icon', done => {
component.loading = true;
Vue.nextTick(() => {
- expect(
- component.$el.querySelector('.board-list-loading'),
- ).not.toBeNull();
+ expect(component.$el.querySelector('.board-list-loading')).not.toBeNull();
done();
});
});
it('renders issues', () => {
- expect(
- component.$el.querySelectorAll('.board-card').length,
- ).toBe(1);
+ expect(component.$el.querySelectorAll('.board-card').length).toBe(1);
});
it('sets data attribute with issue id', () => {
- expect(
- component.$el.querySelector('.board-card').getAttribute('data-issue-id'),
- ).toBe('1');
+ expect(component.$el.querySelector('.board-card').getAttribute('data-issue-id')).toBe('1');
});
- it('shows new issue form', (done) => {
+ it('shows new issue form', done => {
component.toggleForm();
Vue.nextTick(() => {
- expect(
- component.$el.querySelector('.board-new-issue-form'),
- ).not.toBeNull();
+ expect(component.$el.querySelector('.board-new-issue-form')).not.toBeNull();
- expect(
- component.$el.querySelector('.is-smaller'),
- ).not.toBeNull();
+ expect(component.$el.querySelector('.is-smaller')).not.toBeNull();
done();
});
});
- it('shows new issue form after eventhub event', (done) => {
+ it('shows new issue form after eventhub event', done => {
eventHub.$emit(`hide-issue-form-${component.list.id}`);
Vue.nextTick(() => {
- expect(
- component.$el.querySelector('.board-new-issue-form'),
- ).not.toBeNull();
+ expect(component.$el.querySelector('.board-new-issue-form')).not.toBeNull();
- expect(
- component.$el.querySelector('.is-smaller'),
- ).not.toBeNull();
+ expect(component.$el.querySelector('.is-smaller')).not.toBeNull();
done();
});
});
- it('does not show new issue form for closed list', (done) => {
+ it('does not show new issue form for closed list', done => {
component.list.type = 'closed';
component.toggleForm();
Vue.nextTick(() => {
- expect(
- component.$el.querySelector('.board-new-issue-form'),
- ).toBeNull();
+ expect(component.$el.querySelector('.board-new-issue-form')).toBeNull();
done();
});
});
- it('shows count list item', (done) => {
+ it('shows count list item', done => {
component.showCount = true;
Vue.nextTick(() => {
- expect(
- component.$el.querySelector('.board-list-count'),
- ).not.toBeNull();
+ expect(component.$el.querySelector('.board-list-count')).not.toBeNull();
- expect(
- component.$el.querySelector('.board-list-count').textContent.trim(),
- ).toBe('Showing all issues');
+ expect(component.$el.querySelector('.board-list-count').textContent.trim()).toBe(
+ 'Showing all issues',
+ );
done();
});
});
- it('sets data attribute with invalid id', (done) => {
+ it('sets data attribute with invalid id', done => {
component.showCount = true;
Vue.nextTick(() => {
- expect(
- component.$el.querySelector('.board-list-count').getAttribute('data-issue-id'),
- ).toBe('-1');
+ expect(component.$el.querySelector('.board-list-count').getAttribute('data-issue-id')).toBe(
+ '-1',
+ );
done();
});
});
- it('shows how many more issues to load', (done) => {
+ it('shows how many more issues to load', done => {
component.showCount = true;
component.list.issuesSize = 20;
Vue.nextTick(() => {
- expect(
- component.$el.querySelector('.board-list-count').textContent.trim(),
- ).toBe('Showing 1 of 20 issues');
+ expect(component.$el.querySelector('.board-list-count').textContent.trim()).toBe(
+ 'Showing 1 of 20 issues',
+ );
done();
});
});
- it('loads more issues after scrolling', (done) => {
+ it('loads more issues after scrolling', done => {
spyOn(component.list, 'nextPage');
component.$refs.list.style.height = '100px';
component.$refs.list.style.overflow = 'scroll';
@@ -200,7 +180,9 @@ describe('Board list component', () => {
});
it('does not load issues if already loading', () => {
- component.list.nextPage = spyOn(component.list, 'nextPage').and.returnValue(new Promise(() => {}));
+ component.list.nextPage = spyOn(component.list, 'nextPage').and.returnValue(
+ new Promise(() => {}),
+ );
component.onScroll();
component.onScroll();
@@ -208,14 +190,12 @@ describe('Board list component', () => {
expect(component.list.nextPage).toHaveBeenCalledTimes(1);
});
- it('shows loading more spinner', (done) => {
+ it('shows loading more spinner', done => {
component.showCount = true;
component.list.loadingMore = true;
Vue.nextTick(() => {
- expect(
- component.$el.querySelector('.board-list-count .fa-spinner'),
- ).not.toBeNull();
+ expect(component.$el.querySelector('.board-list-count .fa-spinner')).not.toBeNull();
done();
});
diff --git a/spec/javascripts/boards/components/board_spec.js b/spec/javascripts/boards/components/board_spec.js
index d4c53bd5a7d..dee7841c088 100644
--- a/spec/javascripts/boards/components/board_spec.js
+++ b/spec/javascripts/boards/components/board_spec.js
@@ -8,7 +8,7 @@ describe('Board component', () => {
let vm;
let el;
- beforeEach((done) => {
+ beforeEach(done => {
loadFixtures('boards/show.html.raw');
el = document.createElement('div');
@@ -50,56 +50,46 @@ describe('Board component', () => {
});
it('board is expandable when list type is backlog', () => {
- expect(
- vm.$el.classList.contains('is-expandable'),
- ).toBe(true);
+ expect(vm.$el.classList.contains('is-expandable')).toBe(true);
});
- it('board is expandable when list type is closed', (done) => {
+ it('board is expandable when list type is closed', done => {
vm.list.type = 'closed';
Vue.nextTick(() => {
- expect(
- vm.$el.classList.contains('is-expandable'),
- ).toBe(true);
+ expect(vm.$el.classList.contains('is-expandable')).toBe(true);
done();
});
});
- it('board is not expandable when list type is label', (done) => {
+ it('board is not expandable when list type is label', done => {
vm.list.type = 'label';
vm.list.isExpandable = false;
Vue.nextTick(() => {
- expect(
- vm.$el.classList.contains('is-expandable'),
- ).toBe(false);
+ expect(vm.$el.classList.contains('is-expandable')).toBe(false);
done();
});
});
- it('collapses when clicking header', (done) => {
+ it('collapses when clicking header', done => {
vm.$el.querySelector('.board-header').click();
Vue.nextTick(() => {
- expect(
- vm.$el.classList.contains('is-collapsed'),
- ).toBe(true);
+ expect(vm.$el.classList.contains('is-collapsed')).toBe(true);
done();
});
});
- it('created sets isExpanded to true from localStorage', (done) => {
+ it('created sets isExpanded to true from localStorage', done => {
vm.$el.querySelector('.board-header').click();
return Vue.nextTick()
.then(() => {
- expect(
- vm.$el.classList.contains('is-collapsed'),
- ).toBe(true);
+ expect(vm.$el.classList.contains('is-collapsed')).toBe(true);
// call created manually
vm.$options.created[0].call(vm);
@@ -107,11 +97,10 @@ describe('Board component', () => {
return Vue.nextTick();
})
.then(() => {
- expect(
- vm.$el.classList.contains('is-collapsed'),
- ).toBe(true);
+ expect(vm.$el.classList.contains('is-collapsed')).toBe(true);
done();
- }).catch(done.fail);
+ })
+ .catch(done.fail);
});
});
diff --git a/spec/javascripts/commit/commit_pipeline_status_component_spec.js b/spec/javascripts/commit/commit_pipeline_status_component_spec.js
index 4fc56fd9a27..f6b36e88a5f 100644
--- a/spec/javascripts/commit/commit_pipeline_status_component_spec.js
+++ b/spec/javascripts/commit/commit_pipeline_status_component_spec.js
@@ -22,7 +22,7 @@ describe('Commit pipeline status component', () => {
Component = Vue.extend(commitPipelineStatus);
});
- describe('While polling pipeline data succesfully', () => {
+ describe('While polling pipeline data successfully', () => {
beforeEach(() => {
mock = new MockAdapter(axios);
mock.onGet('/dummy/endpoint').reply(() => {
@@ -59,14 +59,14 @@ describe('Commit pipeline status component', () => {
});
});
- it('contains a ciStatus when the polling is succesful ', done => {
+ it('contains a ciStatus when the polling is successful ', done => {
setTimeout(() => {
expect(vm.ciStatus).toEqual(mockCiStatus);
done();
});
});
- it('contains a ci-status icon when polling is succesful', done => {
+ it('contains a ci-status icon when polling is successful', done => {
setTimeout(() => {
expect(vm.$el.querySelector('.ci-status-icon')).not.toBe(null);
expect(vm.$el.querySelector('.ci-status-icon').classList).toContain(
@@ -77,7 +77,7 @@ describe('Commit pipeline status component', () => {
});
});
- describe('When polling data was not succesful', () => {
+ describe('When polling data was not successful', () => {
beforeEach(() => {
mock = new MockAdapter(axios);
mock.onGet('/dummy/endpoint').reply(502, {});
diff --git a/spec/javascripts/environments/emtpy_state_spec.js b/spec/javascripts/environments/emtpy_state_spec.js
index d71dfe8197e..1f986d49bc7 100644
--- a/spec/javascripts/environments/emtpy_state_spec.js
+++ b/spec/javascripts/environments/emtpy_state_spec.js
@@ -1,4 +1,3 @@
-
import Vue from 'vue';
import emptyState from '~/environments/components/empty_state.vue';
import mountComponent from 'spec/helpers/vue_mount_component_helper';
@@ -25,13 +24,13 @@ describe('environments empty state', () => {
});
it('renders empty state and new environment button', () => {
- expect(
- vm.$el.querySelector('.js-blank-state-title').textContent.trim(),
- ).toEqual('You don\'t have any environments right now');
+ expect(vm.$el.querySelector('.js-blank-state-title').textContent.trim()).toEqual(
+ "You don't have any environments right now",
+ );
- expect(
- vm.$el.querySelector('.js-new-environment-button').getAttribute('href'),
- ).toEqual('foo');
+ expect(vm.$el.querySelector('.js-new-environment-button').getAttribute('href')).toEqual(
+ 'foo',
+ );
});
});
@@ -45,13 +44,11 @@ describe('environments empty state', () => {
});
it('renders empty state without new button', () => {
- expect(
- vm.$el.querySelector('.js-blank-state-title').textContent.trim(),
- ).toEqual('You don\'t have any environments right now');
+ expect(vm.$el.querySelector('.js-blank-state-title').textContent.trim()).toEqual(
+ "You don't have any environments right now",
+ );
- expect(
- vm.$el.querySelector('.js-new-environment-button'),
- ).toBeNull();
+ expect(vm.$el.querySelector('.js-new-environment-button')).toBeNull();
});
});
});
diff --git a/spec/javascripts/environments/environment_item_spec.js b/spec/javascripts/environments/environment_item_spec.js
index 0b933dda431..7618c2f50ce 100644
--- a/spec/javascripts/environments/environment_item_spec.js
+++ b/spec/javascripts/environments/environment_item_spec.js
@@ -38,7 +38,9 @@ describe('Environment item', () => {
});
it('Should render the number of children in a badge', () => {
- expect(component.$el.querySelector('.folder-name .badge').textContent).toContain(mockItem.size);
+ expect(component.$el.querySelector('.folder-name .badge').textContent).toContain(
+ mockItem.size,
+ );
});
});
@@ -68,7 +70,8 @@ describe('Environment item', () => {
username: 'root',
id: 1,
state: 'active',
- avatar_url: 'https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80\u0026d=identicon',
+ avatar_url:
+ 'https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80\u0026d=identicon',
web_url: 'http://localhost:3000/root',
},
commit: {
@@ -84,7 +87,8 @@ describe('Environment item', () => {
username: 'root',
id: 1,
state: 'active',
- avatar_url: 'https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80\u0026d=identicon',
+ avatar_url:
+ 'https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80\u0026d=identicon',
web_url: 'http://localhost:3000/root',
},
commit_path: '/root/ci-folders/tree/500aabcb17c97bdcf2d0c410b70cb8556f0362dd',
@@ -121,18 +125,18 @@ describe('Environment item', () => {
});
it('should render environment name', () => {
- expect(component.$el.querySelector('.environment-name').textContent).toContain(environment.name);
+ expect(component.$el.querySelector('.environment-name').textContent).toContain(
+ environment.name,
+ );
});
describe('With deployment', () => {
it('should render deployment internal id', () => {
- expect(
- component.$el.querySelector('.deployment-column span').textContent,
- ).toContain(environment.last_deployment.iid);
+ expect(component.$el.querySelector('.deployment-column span').textContent).toContain(
+ environment.last_deployment.iid,
+ );
- expect(
- component.$el.querySelector('.deployment-column span').textContent,
- ).toContain('#');
+ expect(component.$el.querySelector('.deployment-column span').textContent).toContain('#');
});
it('should render last deployment date', () => {
@@ -156,56 +160,46 @@ describe('Environment item', () => {
describe('With build url', () => {
it('Should link to build url provided', () => {
- expect(
- component.$el.querySelector('.build-link').getAttribute('href'),
- ).toEqual(environment.last_deployment.deployable.build_path);
+ expect(component.$el.querySelector('.build-link').getAttribute('href')).toEqual(
+ environment.last_deployment.deployable.build_path,
+ );
});
it('Should render deployable name and id', () => {
- expect(
- component.$el.querySelector('.build-link').getAttribute('href'),
- ).toEqual(environment.last_deployment.deployable.build_path);
+ expect(component.$el.querySelector('.build-link').getAttribute('href')).toEqual(
+ environment.last_deployment.deployable.build_path,
+ );
});
});
describe('With commit information', () => {
it('should render commit component', () => {
- expect(
- component.$el.querySelector('.js-commit-component'),
- ).toBeDefined();
+ expect(component.$el.querySelector('.js-commit-component')).toBeDefined();
});
});
});
describe('With manual actions', () => {
it('Should render actions component', () => {
- expect(
- component.$el.querySelector('.js-manual-actions-container'),
- ).toBeDefined();
+ expect(component.$el.querySelector('.js-manual-actions-container')).toBeDefined();
});
});
describe('With external URL', () => {
it('should render external url component', () => {
- expect(
- component.$el.querySelector('.js-external-url-container'),
- ).toBeDefined();
+ expect(component.$el.querySelector('.js-external-url-container')).toBeDefined();
});
});
describe('With stop action', () => {
it('Should render stop action component', () => {
- expect(
- component.$el.querySelector('.js-stop-component-container'),
- ).toBeDefined();
+ expect(component.$el.querySelector('.js-stop-component-container')).toBeDefined();
});
});
describe('With retry action', () => {
it('Should render rollback component', () => {
- expect(
- component.$el.querySelector('.js-rollback-component-container'),
- ).toBeDefined();
+ expect(component.$el.querySelector('.js-rollback-component-container')).toBeDefined();
});
});
});
diff --git a/spec/javascripts/environments/environments_app_spec.js b/spec/javascripts/environments/environments_app_spec.js
index 7edc0ccac0b..e2d81eb454a 100644
--- a/spec/javascripts/environments/environments_app_spec.js
+++ b/spec/javascripts/environments/environments_app_spec.js
@@ -31,9 +31,9 @@ describe('Environment', () => {
mock.restore();
});
- describe('successfull request', () => {
+ describe('successful request', () => {
describe('without environments', () => {
- beforeEach((done) => {
+ beforeEach(done => {
mock.onGet(mockData.endpoint).reply(200, { environments: [] });
component = mountComponent(EnvironmentsComponent, mockData);
@@ -44,30 +44,34 @@ describe('Environment', () => {
});
it('should render the empty state', () => {
- expect(
- component.$el.querySelector('.js-new-environment-button').textContent,
- ).toContain('New environment');
+ expect(component.$el.querySelector('.js-new-environment-button').textContent).toContain(
+ 'New environment',
+ );
- expect(
- component.$el.querySelector('.js-blank-state-title').textContent,
- ).toContain('You don\'t have any environments right now');
+ expect(component.$el.querySelector('.js-blank-state-title').textContent).toContain(
+ "You don't have any environments right now",
+ );
});
});
describe('with paginated environments', () => {
- beforeEach((done) => {
- mock.onGet(mockData.endpoint).reply(200, {
- environments: [environment],
- stopped_count: 1,
- available_count: 0,
- }, {
- 'X-nExt-pAge': '2',
- 'x-page': '1',
- 'X-Per-Page': '1',
- 'X-Prev-Page': '',
- 'X-TOTAL': '37',
- 'X-Total-Pages': '2',
- });
+ beforeEach(done => {
+ mock.onGet(mockData.endpoint).reply(
+ 200,
+ {
+ environments: [environment],
+ stopped_count: 1,
+ available_count: 0,
+ },
+ {
+ 'X-nExt-pAge': '2',
+ 'x-page': '1',
+ 'X-Per-Page': '1',
+ 'X-Prev-Page': '',
+ 'X-TOTAL': '37',
+ 'X-Total-Pages': '2',
+ },
+ );
component = mountComponent(EnvironmentsComponent, mockData);
@@ -78,19 +82,17 @@ describe('Environment', () => {
it('should render a table with environments', () => {
expect(component.$el.querySelectorAll('table')).not.toBeNull();
- expect(
- component.$el.querySelector('.environment-name').textContent.trim(),
- ).toEqual(environment.name);
+ expect(component.$el.querySelector('.environment-name').textContent.trim()).toEqual(
+ environment.name,
+ );
});
describe('pagination', () => {
it('should render pagination', () => {
- expect(
- component.$el.querySelectorAll('.gl-pagination li').length,
- ).toEqual(5);
+ expect(component.$el.querySelectorAll('.gl-pagination li').length).toEqual(5);
});
- it('should make an API request when page is clicked', (done) => {
+ it('should make an API request when page is clicked', done => {
spyOn(component, 'updateContent');
setTimeout(() => {
component.$el.querySelector('.gl-pagination li:nth-child(5) a').click();
@@ -100,7 +102,7 @@ describe('Environment', () => {
}, 0);
});
- it('should make an API request when using tabs', (done) => {
+ it('should make an API request when using tabs', done => {
setTimeout(() => {
spyOn(component, 'updateContent');
component.$el.querySelector('.js-environments-tab-stopped').click();
@@ -114,7 +116,7 @@ describe('Environment', () => {
});
describe('unsuccessfull request', () => {
- beforeEach((done) => {
+ beforeEach(done => {
mock.onGet(mockData.endpoint).reply(500, {});
component = mountComponent(EnvironmentsComponent, mockData);
@@ -125,15 +127,16 @@ describe('Environment', () => {
});
it('should render empty state', () => {
- expect(
- component.$el.querySelector('.js-blank-state-title').textContent,
- ).toContain('You don\'t have any environments right now');
+ expect(component.$el.querySelector('.js-blank-state-title').textContent).toContain(
+ "You don't have any environments right now",
+ );
});
});
describe('expandable folders', () => {
beforeEach(() => {
- mock.onGet(mockData.endpoint).reply(200,
+ mock.onGet(mockData.endpoint).reply(
+ 200,
{
environments: [folder],
stopped_count: 0,
@@ -154,7 +157,7 @@ describe('Environment', () => {
component = mountComponent(EnvironmentsComponent, mockData);
});
- it('should open a closed folder', (done) => {
+ it('should open a closed folder', done => {
setTimeout(() => {
component.$el.querySelector('.folder-name').click();
@@ -165,7 +168,7 @@ describe('Environment', () => {
}, 0);
});
- it('should close an opened folder', (done) => {
+ it('should close an opened folder', done => {
setTimeout(() => {
// open folder
component.$el.querySelector('.folder-name').click();
@@ -182,7 +185,7 @@ describe('Environment', () => {
}, 0);
});
- it('should show children environments and a button to show all environments', (done) => {
+ it('should show children environments and a button to show all environments', done => {
setTimeout(() => {
// open folder
component.$el.querySelector('.folder-name').click();
@@ -191,7 +194,9 @@ describe('Environment', () => {
// wait for next async request
setTimeout(() => {
expect(component.$el.querySelectorAll('.js-child-row').length).toEqual(1);
- expect(component.$el.querySelector('.text-center > a.btn').textContent).toContain('Show all');
+ expect(component.$el.querySelector('.text-center > a.btn').textContent).toContain(
+ 'Show all',
+ );
done();
});
});
@@ -201,7 +206,8 @@ describe('Environment', () => {
describe('methods', () => {
beforeEach(() => {
- mock.onGet(mockData.endpoint).reply(200,
+ mock.onGet(mockData.endpoint).reply(
+ 200,
{
environments: [],
stopped_count: 0,
@@ -215,8 +221,9 @@ describe('Environment', () => {
});
describe('updateContent', () => {
- it('should set given parameters', (done) => {
- component.updateContent({ scope: 'stopped', page: '3' })
+ it('should set given parameters', done => {
+ component
+ .updateContent({ scope: 'stopped', page: '3' })
.then(() => {
expect(component.page).toEqual('3');
expect(component.scope).toEqual('stopped');
diff --git a/spec/javascripts/environments/folder/environments_folder_view_spec.js b/spec/javascripts/environments/folder/environments_folder_view_spec.js
index 51d4213c38f..7f0a9475d5f 100644
--- a/spec/javascripts/environments/folder/environments_folder_view_spec.js
+++ b/spec/javascripts/environments/folder/environments_folder_view_spec.js
@@ -30,39 +30,43 @@ describe('Environments Folder View', () => {
component.$destroy();
});
- describe('successfull request', () => {
+ describe('successful request', () => {
beforeEach(() => {
- mock.onGet(mockData.endpoint).reply(200, {
- environments: environmentsList,
- stopped_count: 1,
- available_count: 0,
- }, {
- 'X-nExt-pAge': '2',
- 'x-page': '1',
- 'X-Per-Page': '2',
- 'X-Prev-Page': '',
- 'X-TOTAL': '20',
- 'X-Total-Pages': '10',
- });
+ mock.onGet(mockData.endpoint).reply(
+ 200,
+ {
+ environments: environmentsList,
+ stopped_count: 1,
+ available_count: 0,
+ },
+ {
+ 'X-nExt-pAge': '2',
+ 'x-page': '1',
+ 'X-Per-Page': '2',
+ 'X-Prev-Page': '',
+ 'X-TOTAL': '20',
+ 'X-Total-Pages': '10',
+ },
+ );
component = mountComponent(Component, mockData);
});
- it('should render a table with environments', (done) => {
+ it('should render a table with environments', done => {
setTimeout(() => {
expect(component.$el.querySelectorAll('table')).not.toBeNull();
- expect(
- component.$el.querySelector('.environment-name').textContent.trim(),
- ).toEqual(environmentsList[0].name);
+ expect(component.$el.querySelector('.environment-name').textContent.trim()).toEqual(
+ environmentsList[0].name,
+ );
done();
}, 0);
});
- it('should render available tab with count', (done) => {
+ it('should render available tab with count', done => {
setTimeout(() => {
- expect(
- component.$el.querySelector('.js-environments-tab-available').textContent,
- ).toContain('Available');
+ expect(component.$el.querySelector('.js-environments-tab-available').textContent).toContain(
+ 'Available',
+ );
expect(
component.$el.querySelector('.js-environments-tab-available .badge').textContent,
@@ -71,11 +75,11 @@ describe('Environments Folder View', () => {
}, 0);
});
- it('should render stopped tab with count', (done) => {
+ it('should render stopped tab with count', done => {
setTimeout(() => {
- expect(
- component.$el.querySelector('.js-environments-tab-stopped').textContent,
- ).toContain('Stopped');
+ expect(component.$el.querySelector('.js-environments-tab-stopped').textContent).toContain(
+ 'Stopped',
+ );
expect(
component.$el.querySelector('.js-environments-tab-stopped .badge').textContent,
@@ -84,36 +88,37 @@ describe('Environments Folder View', () => {
}, 0);
});
- it('should render parent folder name', (done) => {
+ it('should render parent folder name', done => {
setTimeout(() => {
- expect(
- component.$el.querySelector('.js-folder-name').textContent.trim(),
- ).toContain('Environments / review');
+ expect(component.$el.querySelector('.js-folder-name').textContent.trim()).toContain(
+ 'Environments / review',
+ );
done();
}, 0);
});
describe('pagination', () => {
- it('should render pagination', (done) => {
+ it('should render pagination', done => {
setTimeout(() => {
- expect(
- component.$el.querySelectorAll('.gl-pagination'),
- ).not.toBeNull();
+ expect(component.$el.querySelectorAll('.gl-pagination')).not.toBeNull();
done();
}, 0);
});
- it('should make an API request when changing page', (done) => {
+ it('should make an API request when changing page', done => {
spyOn(component, 'updateContent');
setTimeout(() => {
component.$el.querySelector('.gl-pagination .js-last-button a').click();
- expect(component.updateContent).toHaveBeenCalledWith({ scope: component.scope, page: '10' });
+ expect(component.updateContent).toHaveBeenCalledWith({
+ scope: component.scope,
+ page: '10',
+ });
done();
}, 0);
});
- it('should make an API request when using tabs', (done) => {
+ it('should make an API request when using tabs', done => {
setTimeout(() => {
spyOn(component, 'updateContent');
component.$el.querySelector('.js-environments-tab-stopped').click();
@@ -134,20 +139,18 @@ describe('Environments Folder View', () => {
component = mountComponent(Component, mockData);
});
- it('should not render a table', (done) => {
+ it('should not render a table', done => {
setTimeout(() => {
- expect(
- component.$el.querySelector('table'),
- ).toBe(null);
+ expect(component.$el.querySelector('table')).toBe(null);
done();
}, 0);
});
- it('should render available tab with count 0', (done) => {
+ it('should render available tab with count 0', done => {
setTimeout(() => {
- expect(
- component.$el.querySelector('.js-environments-tab-available').textContent,
- ).toContain('Available');
+ expect(component.$el.querySelector('.js-environments-tab-available').textContent).toContain(
+ 'Available',
+ );
expect(
component.$el.querySelector('.js-environments-tab-available .badge').textContent,
@@ -156,11 +159,11 @@ describe('Environments Folder View', () => {
}, 0);
});
- it('should render stopped tab with count 0', (done) => {
+ it('should render stopped tab with count 0', done => {
setTimeout(() => {
- expect(
- component.$el.querySelector('.js-environments-tab-stopped').textContent,
- ).toContain('Stopped');
+ expect(component.$el.querySelector('.js-environments-tab-stopped').textContent).toContain(
+ 'Stopped',
+ );
expect(
component.$el.querySelector('.js-environments-tab-stopped .badge').textContent,
@@ -181,8 +184,9 @@ describe('Environments Folder View', () => {
});
describe('updateContent', () => {
- it('should set given parameters', (done) => {
- component.updateContent({ scope: 'stopped', page: '4' })
+ it('should set given parameters', done => {
+ component
+ .updateContent({ scope: 'stopped', page: '4' })
.then(() => {
expect(component.page).toEqual('4');
expect(component.scope).toEqual('stopped');
diff --git a/spec/javascripts/issue_show/components/title_spec.js b/spec/javascripts/issue_show/components/title_spec.js
index 9e6f236b687..9754c8a6755 100644
--- a/spec/javascripts/issue_show/components/title_spec.js
+++ b/spec/javascripts/issue_show/components/title_spec.js
@@ -25,25 +25,21 @@ describe('Title component', () => {
});
it('renders title HTML', () => {
- expect(
- vm.$el.querySelector('.title').innerHTML.trim(),
- ).toBe('Testing <img>');
+ expect(vm.$el.querySelector('.title').innerHTML.trim()).toBe('Testing <img>');
});
- it('updates page title when changing titleHtml', (done) => {
+ it('updates page title when changing titleHtml', done => {
spyOn(vm, 'setPageTitle');
vm.titleHtml = 'test';
Vue.nextTick(() => {
- expect(
- vm.setPageTitle,
- ).toHaveBeenCalled();
+ expect(vm.setPageTitle).toHaveBeenCalled();
done();
});
});
- it('animates title changes', (done) => {
+ it('animates title changes', done => {
vm.titleHtml = 'test';
Vue.nextTick(() => {
@@ -61,14 +57,12 @@ describe('Title component', () => {
});
});
- it('updates page title after changing title', (done) => {
+ it('updates page title after changing title', done => {
vm.titleHtml = 'changed';
vm.titleText = 'changed';
Vue.nextTick(() => {
- expect(
- document.querySelector('title').textContent.trim(),
- ).toContain('changed');
+ expect(document.querySelector('title').textContent.trim()).toContain('changed');
done();
});
diff --git a/spec/javascripts/jobs/components/job_app_spec.js b/spec/javascripts/jobs/components/job_app_spec.js
index 288c06d6615..ba1889c4dcd 100644
--- a/spec/javascripts/jobs/components/job_app_spec.js
+++ b/spec/javascripts/jobs/components/job_app_spec.js
@@ -52,7 +52,7 @@ describe('Job App ', () => {
});
});
- describe('with successfull request', () => {
+ describe('with successful request', () => {
beforeEach(() => {
mock.onGet(`${props.pagePath}/trace.json`).replyOnce(200, {});
});
@@ -234,7 +234,7 @@ describe('Job App ', () => {
);
done();
}, 0);
- })
+ });
});
it('does not renders stuck block when there are no runners', done => {
diff --git a/spec/javascripts/jobs/store/actions_spec.js b/spec/javascripts/jobs/store/actions_spec.js
index 45130b983e7..77b44995b12 100644
--- a/spec/javascripts/jobs/store/actions_spec.js
+++ b/spec/javascripts/jobs/store/actions_spec.js
@@ -68,41 +68,20 @@ describe('Job State actions', () => {
describe('hideSidebar', () => {
it('should commit HIDE_SIDEBAR mutation', done => {
- testAction(
- hideSidebar,
- null,
- mockedState,
- [{ type: types.HIDE_SIDEBAR }],
- [],
- done,
- );
+ testAction(hideSidebar, null, mockedState, [{ type: types.HIDE_SIDEBAR }], [], done);
});
});
describe('showSidebar', () => {
it('should commit HIDE_SIDEBAR mutation', done => {
- testAction(
- showSidebar,
- null,
- mockedState,
- [{ type: types.SHOW_SIDEBAR }],
- [],
- done,
- );
+ testAction(showSidebar, null, mockedState, [{ type: types.SHOW_SIDEBAR }], [], done);
});
});
describe('toggleSidebar', () => {
describe('when isSidebarOpen is true', () => {
it('should dispatch hideSidebar', done => {
- testAction(
- toggleSidebar,
- null,
- mockedState,
- [],
- [{ type: 'hideSidebar' }],
- done,
- );
+ testAction(toggleSidebar, null, mockedState, [], [{ type: 'hideSidebar' }], done);
});
});
@@ -110,14 +89,7 @@ describe('Job State actions', () => {
it('should dispatch showSidebar', done => {
mockedState.isSidebarOpen = false;
- testAction(
- toggleSidebar,
- null,
- mockedState,
- [],
- [{ type: 'showSidebar' }],
- done,
- );
+ testAction(toggleSidebar, null, mockedState, [], [{ type: 'showSidebar' }], done);
});
});
});
diff --git a/spec/javascripts/jobs/store/getters_spec.js b/spec/javascripts/jobs/store/getters_spec.js
index 34e9707eadd..4195d9d3680 100644
--- a/spec/javascripts/jobs/store/getters_spec.js
+++ b/spec/javascripts/jobs/store/getters_spec.js
@@ -180,7 +180,7 @@ describe('Job Store Getters', () => {
it('returns true', () => {
localState.job.runners = {
available: true,
- online: false
+ online: false,
};
expect(getters.hasRunnersForProject(localState)).toEqual(true);
@@ -191,7 +191,7 @@ describe('Job Store Getters', () => {
it('returns false', () => {
localState.job.runners = {
available: false,
- online: false
+ online: false,
};
expect(getters.hasRunnersForProject(localState)).toEqual(false);
@@ -202,7 +202,7 @@ describe('Job Store Getters', () => {
it('returns false', () => {
localState.job.runners = {
available: false,
- online: true
+ online: true,
};
expect(getters.hasRunnersForProject(localState)).toEqual(false);
diff --git a/spec/javascripts/lib/utils/common_utils_spec.js b/spec/javascripts/lib/utils/common_utils_spec.js
index 514d6ddeae5..0fb90c3b78c 100644
--- a/spec/javascripts/lib/utils/common_utils_spec.js
+++ b/spec/javascripts/lib/utils/common_utils_spec.js
@@ -35,9 +35,7 @@ describe('common_utils', () => {
});
it('should decode params', () => {
- expect(
- commonUtils.urlParamsToArray('?label_name%5B%5D=test')[0],
- ).toBe('label_name[]=test');
+ expect(commonUtils.urlParamsToArray('?label_name%5B%5D=test')[0]).toBe('label_name[]=test');
});
it('should remove the question mark from the search params', () => {
@@ -49,25 +47,19 @@ describe('common_utils', () => {
describe('urlParamsToObject', () => {
it('parses path for label with trailing +', () => {
- expect(
- commonUtils.urlParamsToObject('label_name[]=label%2B', {}),
- ).toEqual({
+ expect(commonUtils.urlParamsToObject('label_name[]=label%2B', {})).toEqual({
label_name: ['label+'],
});
});
it('parses path for milestone with trailing +', () => {
- expect(
- commonUtils.urlParamsToObject('milestone_title=A%2B', {}),
- ).toEqual({
+ expect(commonUtils.urlParamsToObject('milestone_title=A%2B', {})).toEqual({
milestone_title: 'A+',
});
});
it('parses path for search terms with spaces', () => {
- expect(
- commonUtils.urlParamsToObject('search=two+words', {}),
- ).toEqual({
+ expect(commonUtils.urlParamsToObject('search=two+words', {})).toEqual({
search: 'two words',
});
});
@@ -187,7 +179,11 @@ describe('common_utils', () => {
describe('parseQueryStringIntoObject', () => {
it('should return object with query parameters', () => {
- expect(commonUtils.parseQueryStringIntoObject('scope=all&page=2')).toEqual({ scope: 'all', page: '2' });
+ expect(commonUtils.parseQueryStringIntoObject('scope=all&page=2')).toEqual({
+ scope: 'all',
+ page: '2',
+ });
+
expect(commonUtils.parseQueryStringIntoObject('scope=all')).toEqual({ scope: 'all' });
expect(commonUtils.parseQueryStringIntoObject()).toEqual({});
});
@@ -211,7 +207,9 @@ describe('common_utils', () => {
describe('buildUrlWithCurrentLocation', () => {
it('should build an url with current location and given parameters', () => {
expect(commonUtils.buildUrlWithCurrentLocation()).toEqual(window.location.pathname);
- expect(commonUtils.buildUrlWithCurrentLocation('?page=2')).toEqual(`${window.location.pathname}?page=2`);
+ expect(commonUtils.buildUrlWithCurrentLocation('?page=2')).toEqual(
+ `${window.location.pathname}?page=2`,
+ );
});
});
@@ -266,21 +264,24 @@ describe('common_utils', () => {
});
describe('normalizeCRLFHeaders', () => {
- beforeEach(function () {
- this.CLRFHeaders = 'a-header: a-value\nAnother-Header: ANOTHER-VALUE\nLaSt-HeAdEr: last-VALUE';
+ beforeEach(function() {
+ this.CLRFHeaders =
+ 'a-header: a-value\nAnother-Header: ANOTHER-VALUE\nLaSt-HeAdEr: last-VALUE';
spyOn(String.prototype, 'split').and.callThrough();
this.normalizeCRLFHeaders = commonUtils.normalizeCRLFHeaders(this.CLRFHeaders);
});
- it('should split by newline', function () {
+ it('should split by newline', function() {
expect(String.prototype.split).toHaveBeenCalledWith('\n');
});
- it('should split by colon+space for each header', function () {
- expect(String.prototype.split.calls.allArgs().filter(args => args[0] === ': ').length).toBe(3);
+ it('should split by colon+space for each header', function() {
+ expect(String.prototype.split.calls.allArgs().filter(args => args[0] === ': ').length).toBe(
+ 3,
+ );
});
- it('should return a normalized headers object', function () {
+ it('should return a normalized headers object', function() {
expect(this.normalizeCRLFHeaders).toEqual({
'A-HEADER': 'a-value',
'ANOTHER-HEADER': 'ANOTHER-VALUE',
@@ -359,67 +360,79 @@ describe('common_utils', () => {
spyOn(window, 'setTimeout').and.callFake(cb => origSetTimeout(cb, 0));
});
- it('solves the promise from the callback', (done) => {
+ it('solves the promise from the callback', done => {
const expectedResponseValue = 'Success!';
- commonUtils.backOff((next, stop) => (
- new Promise((resolve) => {
- resolve(expectedResponseValue);
- }).then((resp) => {
- stop(resp);
+ commonUtils
+ .backOff((next, stop) =>
+ new Promise(resolve => {
+ resolve(expectedResponseValue);
+ })
+ .then(resp => {
+ stop(resp);
+ })
+ .catch(done.fail),
+ )
+ .then(respBackoff => {
+ expect(respBackoff).toBe(expectedResponseValue);
+ done();
})
- ).catch(done.fail)).then((respBackoff) => {
- expect(respBackoff).toBe(expectedResponseValue);
- done();
- }).catch(done.fail);
+ .catch(done.fail);
});
- it('catches the rejected promise from the callback ', (done) => {
+ it('catches the rejected promise from the callback ', done => {
const errorMessage = 'Mistakes were made!';
- commonUtils.backOff((next, stop) => {
- new Promise((resolve, reject) => {
- reject(new Error(errorMessage));
- }).then((resp) => {
- stop(resp);
- }).catch(err => stop(err));
- }).catch((errBackoffResp) => {
- expect(errBackoffResp instanceof Error).toBe(true);
- expect(errBackoffResp.message).toBe(errorMessage);
- done();
- });
+ commonUtils
+ .backOff((next, stop) => {
+ new Promise((resolve, reject) => {
+ reject(new Error(errorMessage));
+ })
+ .then(resp => {
+ stop(resp);
+ })
+ .catch(err => stop(err));
+ })
+ .catch(errBackoffResp => {
+ expect(errBackoffResp instanceof Error).toBe(true);
+ expect(errBackoffResp.message).toBe(errorMessage);
+ done();
+ });
});
- it('solves the promise correctly after retrying a third time', (done) => {
+ it('solves the promise correctly after retrying a third time', done => {
let numberOfCalls = 1;
const expectedResponseValue = 'Success!';
- commonUtils.backOff((next, stop) => (
- Promise.resolve(expectedResponseValue)
- .then((resp) => {
- if (numberOfCalls < 3) {
- numberOfCalls += 1;
- next();
- } else {
- stop(resp);
- }
- })
- ).catch(done.fail)).then((respBackoff) => {
- const timeouts = window.setTimeout.calls.allArgs().map(([, timeout]) => timeout);
+ commonUtils
+ .backOff((next, stop) =>
+ Promise.resolve(expectedResponseValue)
+ .then(resp => {
+ if (numberOfCalls < 3) {
+ numberOfCalls += 1;
+ next();
+ } else {
+ stop(resp);
+ }
+ })
+ .catch(done.fail),
+ )
+ .then(respBackoff => {
+ const timeouts = window.setTimeout.calls.allArgs().map(([, timeout]) => timeout);
- expect(timeouts).toEqual([2000, 4000]);
- expect(respBackoff).toBe(expectedResponseValue);
- done();
- }).catch(done.fail);
+ expect(timeouts).toEqual([2000, 4000]);
+ expect(respBackoff).toBe(expectedResponseValue);
+ done();
+ })
+ .catch(done.fail);
});
- it('rejects the backOff promise after timing out', (done) => {
- commonUtils.backOff(next => next(), 64000)
- .catch((errBackoffResp) => {
- const timeouts = window.setTimeout.calls.allArgs().map(([, timeout]) => timeout);
+ it('rejects the backOff promise after timing out', done => {
+ commonUtils.backOff(next => next(), 64000).catch(errBackoffResp => {
+ const timeouts = window.setTimeout.calls.allArgs().map(([, timeout]) => timeout);
- expect(timeouts).toEqual([2000, 4000, 8000, 16000, 32000, 32000]);
- expect(errBackoffResp instanceof Error).toBe(true);
- expect(errBackoffResp.message).toBe('BACKOFF_TIMEOUT');
- done();
- });
+ expect(timeouts).toEqual([2000, 4000, 8000, 16000, 32000, 32000]);
+ expect(errBackoffResp instanceof Error).toBe(true);
+ expect(errBackoffResp.message).toBe('BACKOFF_TIMEOUT');
+ done();
+ });
});
});
@@ -466,11 +479,14 @@ describe('common_utils', () => {
});
describe('createOverlayIcon', () => {
- it('should return the favicon with the overlay', (done) => {
- commonUtils.createOverlayIcon(faviconDataUrl, overlayDataUrl).then((url) => {
- expect(url).toEqual(faviconWithOverlayDataUrl);
- done();
- }).catch(done.fail);
+ it('should return the favicon with the overlay', done => {
+ commonUtils
+ .createOverlayIcon(faviconDataUrl, overlayDataUrl)
+ .then(url => {
+ expect(url).toEqual(faviconWithOverlayDataUrl);
+ done();
+ })
+ .catch(done.fail);
});
});
@@ -486,11 +502,16 @@ describe('common_utils', () => {
document.body.removeChild(document.getElementById('favicon'));
});
- it('should set page favicon to provided favicon overlay', (done) => {
- commonUtils.setFaviconOverlay(overlayDataUrl).then(() => {
- expect(document.getElementById('favicon').getAttribute('href')).toEqual(faviconWithOverlayDataUrl);
- done();
- }).catch(done.fail);
+ it('should set page favicon to provided favicon overlay', done => {
+ commonUtils
+ .setFaviconOverlay(overlayDataUrl)
+ .then(() => {
+ expect(document.getElementById('favicon').getAttribute('href')).toEqual(
+ faviconWithOverlayDataUrl,
+ );
+ done();
+ })
+ .catch(done.fail);
});
});
@@ -512,24 +533,24 @@ describe('common_utils', () => {
document.body.removeChild(document.getElementById('favicon'));
});
- it('should reset favicon in case of error', (done) => {
+ it('should reset favicon in case of error', done => {
mock.onGet(BUILD_URL).replyOnce(500);
- commonUtils.setCiStatusFavicon(BUILD_URL)
- .catch(() => {
- const favicon = document.getElementById('favicon');
+ commonUtils.setCiStatusFavicon(BUILD_URL).catch(() => {
+ const favicon = document.getElementById('favicon');
- expect(favicon.getAttribute('href')).toEqual(faviconDataUrl);
- done();
- });
+ expect(favicon.getAttribute('href')).toEqual(faviconDataUrl);
+ done();
+ });
});
- it('should set page favicon to CI status favicon based on provided status', (done) => {
+ it('should set page favicon to CI status favicon based on provided status', done => {
mock.onGet(BUILD_URL).reply(200, {
favicon: overlayDataUrl,
});
- commonUtils.setCiStatusFavicon(BUILD_URL)
+ commonUtils
+ .setCiStatusFavicon(BUILD_URL)
.then(() => {
const favicon = document.getElementById('favicon');
@@ -554,11 +575,15 @@ describe('common_utils', () => {
});
it('should return the svg for a linked icon', () => {
- expect(commonUtils.spriteIcon('test')).toEqual('<svg ><use xlink:href="icons.svg#test" /></svg>');
+ expect(commonUtils.spriteIcon('test')).toEqual(
+ '<svg ><use xlink:href="icons.svg#test" /></svg>',
+ );
});
it('should set svg className when passed', () => {
- expect(commonUtils.spriteIcon('test', 'fa fa-test')).toEqual('<svg class="fa fa-test"><use xlink:href="icons.svg#test" /></svg>');
+ expect(commonUtils.spriteIcon('test', 'fa fa-test')).toEqual(
+ '<svg class="fa fa-test"><use xlink:href="icons.svg#test" /></svg>',
+ );
});
});
@@ -578,7 +603,7 @@ describe('common_utils', () => {
const convertedObj = commonUtils.convertObjectPropsToCamelCase(mockObj);
- Object.keys(convertedObj).forEach((prop) => {
+ Object.keys(convertedObj).forEach(prop => {
expect(snakeRegEx.test(prop)).toBeFalsy();
expect(convertedObj[prop]).toBe(mockObj[mappings[prop]]);
});
@@ -597,9 +622,7 @@ describe('common_utils', () => {
},
};
- expect(
- commonUtils.convertObjectPropsToCamelCase(obj),
- ).toEqual({
+ expect(commonUtils.convertObjectPropsToCamelCase(obj)).toEqual({
snakeKey: {
child_snake_key: 'value',
},
@@ -614,9 +637,7 @@ describe('common_utils', () => {
},
};
- expect(
- commonUtils.convertObjectPropsToCamelCase(obj, { deep: true }),
- ).toEqual({
+ expect(commonUtils.convertObjectPropsToCamelCase(obj, { deep: true })).toEqual({
snakeKey: {
childSnakeKey: 'value',
},
@@ -630,9 +651,7 @@ describe('common_utils', () => {
},
];
- expect(
- commonUtils.convertObjectPropsToCamelCase(arr, { deep: true }),
- ).toEqual([
+ expect(commonUtils.convertObjectPropsToCamelCase(arr, { deep: true })).toEqual([
{
childSnakeKey: 'value',
},
@@ -648,9 +667,7 @@ describe('common_utils', () => {
],
];
- expect(
- commonUtils.convertObjectPropsToCamelCase(arr, { deep: true }),
- ).toEqual([
+ expect(commonUtils.convertObjectPropsToCamelCase(arr, { deep: true })).toEqual([
[
{
childSnakeKey: 'value',
diff --git a/spec/javascripts/lib/utils/datetime_utility_spec.js b/spec/javascripts/lib/utils/datetime_utility_spec.js
index de6b96aab57..d699e66b8ca 100644
--- a/spec/javascripts/lib/utils/datetime_utility_spec.js
+++ b/spec/javascripts/lib/utils/datetime_utility_spec.js
@@ -199,11 +199,11 @@ describe('datefix', () => {
expect(datetimeUtility.pad(2)).toEqual('02');
});
- it('should not add a zero when lenght matches the default', () => {
+ it('should not add a zero when length matches the default', () => {
expect(datetimeUtility.pad(12)).toEqual('12');
});
- it('should add a 0 when lenght is smaller than the provided', () => {
+ it('should add a 0 when length is smaller than the provided', () => {
expect(datetimeUtility.pad(12, 3)).toEqual('012');
});
});
diff --git a/spec/javascripts/lib/utils/number_utility_spec.js b/spec/javascripts/lib/utils/number_utility_spec.js
index a5099a2a3b8..94c6214c86a 100644
--- a/spec/javascripts/lib/utils/number_utility_spec.js
+++ b/spec/javascripts/lib/utils/number_utility_spec.js
@@ -1,4 +1,10 @@
-import { formatRelevantDigits, bytesToKiB, bytesToMiB, bytesToGiB, numberToHumanSize } from '~/lib/utils/number_utils';
+import {
+ formatRelevantDigits,
+ bytesToKiB,
+ bytesToMiB,
+ bytesToGiB,
+ numberToHumanSize,
+} from '~/lib/utils/number_utils';
describe('Number Utils', () => {
describe('formatRelevantDigits', () => {
diff --git a/spec/javascripts/lib/utils/text_utility_spec.js b/spec/javascripts/lib/utils/text_utility_spec.js
index ac3270baef5..92ebfc38722 100644
--- a/spec/javascripts/lib/utils/text_utility_spec.js
+++ b/spec/javascripts/lib/utils/text_utility_spec.js
@@ -120,7 +120,7 @@ describe('text_utility', () => {
});
describe('getFirstCharacterCapitalized', () => {
- it('returns the first character captialized, if first character is alphabetic', () => {
+ it('returns the first character capitalized, if first character is alphabetic', () => {
expect(textUtils.getFirstCharacterCapitalized('loremIpsumDolar')).toEqual('L');
expect(textUtils.getFirstCharacterCapitalized('Sit amit !')).toEqual('S');
});
diff --git a/spec/javascripts/monitoring/graph/flag_spec.js b/spec/javascripts/monitoring/graph/flag_spec.js
index a837b71db0b..038bfffd44f 100644
--- a/spec/javascripts/monitoring/graph/flag_spec.js
+++ b/spec/javascripts/monitoring/graph/flag_spec.js
@@ -2,7 +2,7 @@ import Vue from 'vue';
import GraphFlag from '~/monitoring/components/graph/flag.vue';
import { deploymentData } from '../mock_data';
-const createComponent = (propsData) => {
+const createComponent = propsData => {
const Component = Vue.extend(GraphFlag);
return new Component({
@@ -51,8 +51,7 @@ describe('GraphFlag', () => {
it('has a line at the currentXCoordinate', () => {
component = createComponent(defaultValuesComponent);
- expect(component.$el.style.left)
- .toEqual(`${70 + component.currentXCoordinate}px`);
+ expect(component.$el.style.left).toEqual(`${70 + component.currentXCoordinate}px`);
});
describe('Deployment flag', () => {
@@ -62,9 +61,7 @@ describe('GraphFlag', () => {
deploymentFlagData,
});
- expect(
- deploymentFlagComponent.$el.querySelector('.popover-title'),
- ).toContainText('Deployed');
+ expect(deploymentFlagComponent.$el.querySelector('.popover-title')).toContainText('Deployed');
});
it('contains the ref when a tag is available', () => {
@@ -78,13 +75,13 @@ describe('GraphFlag', () => {
},
});
- expect(
- deploymentFlagComponent.$el.querySelector('.deploy-meta-content'),
- ).toContainText('f5bcd1d9');
+ expect(deploymentFlagComponent.$el.querySelector('.deploy-meta-content')).toContainText(
+ 'f5bcd1d9',
+ );
- expect(
- deploymentFlagComponent.$el.querySelector('.deploy-meta-content'),
- ).toContainText('1.0');
+ expect(deploymentFlagComponent.$el.querySelector('.deploy-meta-content')).toContainText(
+ '1.0',
+ );
});
it('does not contain the ref when a tag is unavailable', () => {
@@ -98,13 +95,13 @@ describe('GraphFlag', () => {
},
});
- expect(
- deploymentFlagComponent.$el.querySelector('.deploy-meta-content'),
- ).toContainText('f5bcd1d9');
+ expect(deploymentFlagComponent.$el.querySelector('.deploy-meta-content')).toContainText(
+ 'f5bcd1d9',
+ );
- expect(
- deploymentFlagComponent.$el.querySelector('.deploy-meta-content'),
- ).not.toContainText('1.0');
+ expect(deploymentFlagComponent.$el.querySelector('.deploy-meta-content')).not.toContainText(
+ '1.0',
+ );
});
});
diff --git a/spec/javascripts/notes/components/discussion_filter_spec.js b/spec/javascripts/notes/components/discussion_filter_spec.js
index 70dd5bb3be5..a81bdf618a3 100644
--- a/spec/javascripts/notes/components/discussion_filter_spec.js
+++ b/spec/javascripts/notes/components/discussion_filter_spec.js
@@ -11,11 +11,13 @@ describe('DiscussionFilter component', () => {
beforeEach(() => {
store = createStore();
- const discussions = [{
- ...discussionMock,
- id: discussionMock.id,
- notes: [{ ...discussionMock.notes[0], resolvable: true, resolved: true }],
- }];
+ const discussions = [
+ {
+ ...discussionMock,
+ id: discussionMock.id,
+ notes: [{ ...discussionMock.notes[0], resolvable: true, resolved: true }],
+ },
+ ];
const Component = Vue.extend(DiscussionFilter);
const defaultValue = discussionFiltersMock[0].value;
@@ -35,11 +37,15 @@ describe('DiscussionFilter component', () => {
});
it('renders the all filters', () => {
- expect(vm.$el.querySelectorAll('.dropdown-menu li').length).toEqual(discussionFiltersMock.length);
+ expect(vm.$el.querySelectorAll('.dropdown-menu li').length).toEqual(
+ discussionFiltersMock.length,
+ );
});
it('renders the default selected item', () => {
- expect(vm.$el.querySelector('#discussion-filter-dropdown').textContent.trim()).toEqual(discussionFiltersMock[0].title);
+ expect(vm.$el.querySelector('#discussion-filter-dropdown').textContent.trim()).toEqual(
+ discussionFiltersMock[0].title,
+ );
});
it('updates to the selected item', () => {
diff --git a/spec/javascripts/notes/components/note_app_spec.js b/spec/javascripts/notes/components/note_app_spec.js
index 06b30375306..3e289a6b8e6 100644
--- a/spec/javascripts/notes/components/note_app_spec.js
+++ b/spec/javascripts/notes/components/note_app_spec.js
@@ -97,7 +97,8 @@ describe('note_app', () => {
});
it('should render list of notes', done => {
- const note = mockData.INDIVIDUAL_NOTE_RESPONSE_MAP.GET[
+ const note =
+ mockData.INDIVIDUAL_NOTE_RESPONSE_MAP.GET[
'/gitlab-org/gitlab-ce/issues/26/discussions.json'
][0].notes[0];
diff --git a/spec/javascripts/notes/stores/mutation_spec.js b/spec/javascripts/notes/stores/mutation_spec.js
index 9d652ba9f1e..380ab59099d 100644
--- a/spec/javascripts/notes/stores/mutation_spec.js
+++ b/spec/javascripts/notes/stores/mutation_spec.js
@@ -78,7 +78,7 @@ describe('Notes Store mutations', () => {
});
describe('COLLAPSE_DISCUSSION', () => {
- it('should collpase an expanded discussion', () => {
+ it('should collapse an expanded discussion', () => {
const discussion = Object.assign({}, discussionMock, { expanded: true });
const state = {
diff --git a/spec/javascripts/pipelines/empty_state_spec.js b/spec/javascripts/pipelines/empty_state_spec.js
index e21dca45fa1..f12950b8fce 100644
--- a/spec/javascripts/pipelines/empty_state_spec.js
+++ b/spec/javascripts/pipelines/empty_state_spec.js
@@ -24,7 +24,7 @@ describe('Pipelines Empty State', () => {
expect(component.$el.querySelector('.svg-content svg')).toBeDefined();
});
- it('should render emtpy state information', () => {
+ it('should render empty state information', () => {
expect(component.$el.querySelector('h4').textContent).toContain('Build with confidence');
expect(
diff --git a/spec/javascripts/pipelines/graph/action_component_spec.js b/spec/javascripts/pipelines/graph/action_component_spec.js
index 027066e1d4d..3d2232ff239 100644
--- a/spec/javascripts/pipelines/graph/action_component_spec.js
+++ b/spec/javascripts/pipelines/graph/action_component_spec.js
@@ -50,7 +50,7 @@ describe('pipeline graph action component', () => {
});
describe('on click', () => {
- it('emits `pipelineActionRequestComplete` after a successfull request', done => {
+ it('emits `pipelineActionRequestComplete` after a successful request', done => {
spyOn(component, '$emit');
component.$el.click();
diff --git a/spec/javascripts/pipelines/graph/graph_component_spec.js b/spec/javascripts/pipelines/graph/graph_component_spec.js
index b6fa4272c8b..96a2d5f62fa 100644
--- a/spec/javascripts/pipelines/graph/graph_component_spec.js
+++ b/spec/javascripts/pipelines/graph/graph_component_spec.js
@@ -40,7 +40,9 @@ describe('graph component', () => {
).toEqual(true);
expect(
- component.$el.querySelector('.stage-column:nth-child(2) .build:nth-child(1)').classList.contains('left-connector'),
+ component.$el
+ .querySelector('.stage-column:nth-child(2) .build:nth-child(1)')
+ .classList.contains('left-connector'),
).toEqual(true);
expect(component.$el.querySelector('loading-icon')).toBe(null);
@@ -56,7 +58,9 @@ describe('graph component', () => {
pipeline: graphJSON,
});
- expect(component.$el.querySelector('.stage-column:nth-child(2) .stage-name').textContent.trim()).toEqual('Deploy &lt;img src=x onerror=alert(document.domain)&gt;');
+ expect(
+ component.$el.querySelector('.stage-column:nth-child(2) .stage-name').textContent.trim(),
+ ).toEqual('Deploy &lt;img src=x onerror=alert(document.domain)&gt;');
});
});
});
diff --git a/spec/javascripts/pipelines/pipelines_spec.js b/spec/javascripts/pipelines/pipelines_spec.js
index 37908153e0e..97ded16db69 100644
--- a/spec/javascripts/pipelines/pipelines_spec.js
+++ b/spec/javascripts/pipelines/pipelines_spec.js
@@ -372,7 +372,7 @@ describe('Pipelines', () => {
});
});
- describe('successfull request', () => {
+ describe('successful request', () => {
describe('with pipelines', () => {
beforeEach(() => {
mock.onGet('twitter/flight/pipelines.json').reply(200, pipelines);
@@ -667,7 +667,7 @@ describe('Pipelines', () => {
});
});
- it('returns false when state is emtpy state', done => {
+ it('returns false when state is empty state', done => {
vm.isLoading = false;
vm.hasMadeRequest = true;
vm.hasGitlabCi = false;
diff --git a/spec/javascripts/pipelines/stage_spec.js b/spec/javascripts/pipelines/stage_spec.js
index a3caaeb44dc..3c8b8032de8 100644
--- a/spec/javascripts/pipelines/stage_spec.js
+++ b/spec/javascripts/pipelines/stage_spec.js
@@ -40,7 +40,7 @@ describe('Pipelines stage component', () => {
expect(component.$el.querySelector('button').getAttribute('data-toggle')).toEqual('dropdown');
});
- describe('with successfull request', () => {
+ describe('with successful request', () => {
beforeEach(() => {
mock.onGet('path.json').reply(200, stageReply);
});
diff --git a/spec/javascripts/sidebar/assignees_spec.js b/spec/javascripts/sidebar/assignees_spec.js
index e7f8f4f9936..eced4925489 100644
--- a/spec/javascripts/sidebar/assignees_spec.js
+++ b/spec/javascripts/sidebar/assignees_spec.js
@@ -78,9 +78,7 @@ describe('Assignee component', () => {
component = new AssigneeComponent({
propsData: {
rootPath: 'http://localhost:3000',
- users: [
- UsersMock.user,
- ],
+ users: [UsersMock.user],
editable: false,
},
}).$mount();
@@ -90,7 +88,10 @@ describe('Assignee component', () => {
expect(collapsed.childElementCount).toEqual(1);
expect(assignee.querySelector('.avatar').getAttribute('src')).toEqual(UsersMock.user.avatar);
- expect(assignee.querySelector('.avatar').getAttribute('alt')).toEqual(`${UsersMock.user.name}'s avatar`);
+ expect(assignee.querySelector('.avatar').getAttribute('alt')).toEqual(
+ `${UsersMock.user.name}'s avatar`,
+ );
+
expect(assignee.querySelector('.author').innerText.trim()).toEqual(UsersMock.user.name);
});
@@ -98,34 +99,38 @@ describe('Assignee component', () => {
component = new AssigneeComponent({
propsData: {
rootPath: 'http://localhost:3000/',
- users: [
- UsersMock.user,
- ],
+ users: [UsersMock.user],
editable: true,
},
}).$mount();
expect(component.$el.querySelector('.author-link')).not.toBeNull();
// The image
- expect(component.$el.querySelector('.author-link img').getAttribute('src')).toEqual(UsersMock.user.avatar);
+ expect(component.$el.querySelector('.author-link img').getAttribute('src')).toEqual(
+ UsersMock.user.avatar,
+ );
// Author name
- expect(component.$el.querySelector('.author-link .author').innerText.trim()).toEqual(UsersMock.user.name);
+ expect(component.$el.querySelector('.author-link .author').innerText.trim()).toEqual(
+ UsersMock.user.name,
+ );
// Username
- expect(component.$el.querySelector('.author-link .username').innerText.trim()).toEqual(`@${UsersMock.user.username}`);
+ expect(component.$el.querySelector('.author-link .username').innerText.trim()).toEqual(
+ `@${UsersMock.user.username}`,
+ );
});
it('has the root url present in the assigneeUrl method', () => {
component = new AssigneeComponent({
propsData: {
rootPath: 'http://localhost:3000/',
- users: [
- UsersMock.user,
- ],
+ users: [UsersMock.user],
editable: true,
},
}).$mount();
- expect(component.assigneeUrl(UsersMock.user).indexOf('http://localhost:3000/')).not.toEqual(-1);
+ expect(component.assigneeUrl(UsersMock.user).indexOf('http://localhost:3000/')).not.toEqual(
+ -1,
+ );
});
});
@@ -147,13 +152,19 @@ describe('Assignee component', () => {
const first = collapsed.children[0];
expect(first.querySelector('.avatar').getAttribute('src')).toEqual(users[0].avatar);
- expect(first.querySelector('.avatar').getAttribute('alt')).toEqual(`${users[0].name}'s avatar`);
+ expect(first.querySelector('.avatar').getAttribute('alt')).toEqual(
+ `${users[0].name}'s avatar`,
+ );
+
expect(first.querySelector('.author').innerText.trim()).toEqual(users[0].name);
const second = collapsed.children[1];
expect(second.querySelector('.avatar').getAttribute('src')).toEqual(users[1].avatar);
- expect(second.querySelector('.avatar').getAttribute('alt')).toEqual(`${users[1].name}'s avatar`);
+ expect(second.querySelector('.avatar').getAttribute('alt')).toEqual(
+ `${users[1].name}'s avatar`,
+ );
+
expect(second.querySelector('.author').innerText.trim()).toEqual(users[1].name);
});
@@ -174,7 +185,10 @@ describe('Assignee component', () => {
const first = collapsed.children[0];
expect(first.querySelector('.avatar').getAttribute('src')).toEqual(users[0].avatar);
- expect(first.querySelector('.avatar').getAttribute('alt')).toEqual(`${users[0].name}'s avatar`);
+ expect(first.querySelector('.avatar').getAttribute('alt')).toEqual(
+ `${users[0].name}'s avatar`,
+ );
+
expect(first.querySelector('.author').innerText.trim()).toEqual(users[0].name);
const second = collapsed.children[1];
@@ -196,7 +210,7 @@ describe('Assignee component', () => {
expect(component.$el.querySelector('.user-list-more')).toBe(null);
});
- it('Shows the "show-less" assignees label', (done) => {
+ it('Shows the "show-less" assignees label', done => {
const users = UsersMockHelper.createNumberRandomUsers(6);
component = new AssigneeComponent({
propsData: {
@@ -206,21 +220,26 @@ describe('Assignee component', () => {
},
}).$mount();
- expect(component.$el.querySelectorAll('.user-item').length).toEqual(component.defaultRenderCount);
+ expect(component.$el.querySelectorAll('.user-item').length).toEqual(
+ component.defaultRenderCount,
+ );
+
expect(component.$el.querySelector('.user-list-more')).not.toBe(null);
const usersLabelExpectation = users.length - component.defaultRenderCount;
- expect(component.$el.querySelector('.user-list-more .btn-link').innerText.trim())
- .not.toBe(`+${usersLabelExpectation} more`);
+ expect(component.$el.querySelector('.user-list-more .btn-link').innerText.trim()).not.toBe(
+ `+${usersLabelExpectation} more`,
+ );
component.toggleShowLess();
Vue.nextTick(() => {
- expect(component.$el.querySelector('.user-list-more .btn-link').innerText.trim())
- .toBe('- show less');
+ expect(component.$el.querySelector('.user-list-more .btn-link').innerText.trim()).toBe(
+ '- show less',
+ );
done();
});
});
- it('Shows the "show-less" when "n+ more " label is clicked', (done) => {
+ it('Shows the "show-less" when "n+ more " label is clicked', done => {
const users = UsersMockHelper.createNumberRandomUsers(6);
component = new AssigneeComponent({
propsData: {
@@ -232,8 +251,9 @@ describe('Assignee component', () => {
component.$el.querySelector('.user-list-more .btn-link').click();
Vue.nextTick(() => {
- expect(component.$el.querySelector('.user-list-more .btn-link').innerText.trim())
- .toBe('- show less');
+ expect(component.$el.querySelector('.user-list-more .btn-link').innerText.trim()).toBe(
+ '- show less',
+ );
done();
});
});
@@ -264,16 +284,18 @@ describe('Assignee component', () => {
});
it('shows "+1 more" label', () => {
- expect(component.$el.querySelector('.user-list-more .btn-link').innerText.trim())
- .toBe('+ 1 more');
+ expect(component.$el.querySelector('.user-list-more .btn-link').innerText.trim()).toBe(
+ '+ 1 more',
+ );
});
- it('shows "show less" label', (done) => {
+ it('shows "show less" label', done => {
component.toggleShowLess();
Vue.nextTick(() => {
- expect(component.$el.querySelector('.user-list-more .btn-link').innerText.trim())
- .toBe('- show less');
+ expect(component.$el.querySelector('.user-list-more .btn-link').innerText.trim()).toBe(
+ '- show less',
+ );
done();
});
});
diff --git a/spec/javascripts/vue_mr_widget/components/mr_widget_pipeline_spec.js b/spec/javascripts/vue_mr_widget/components/mr_widget_pipeline_spec.js
index 689580e6b91..6c7637eed13 100644
--- a/spec/javascripts/vue_mr_widget/components/mr_widget_pipeline_spec.js
+++ b/spec/javascripts/vue_mr_widget/components/mr_widget_pipeline_spec.js
@@ -69,12 +69,12 @@ describe('MRWidgetPipeline', () => {
pipeline: mockData.pipeline,
hasCi: true,
ciStatus: null,
- troubleshootingDocsPath: 'help',
+ troubleshootingDocsPath: 'help',
});
- expect(
- vm.$el.querySelector('.media-body').textContent.trim(),
- ).toContain('Could not retrieve the pipeline status. For troubleshooting steps, read the <a href="help">documentation.</a>');
+ expect(vm.$el.querySelector('.media-body').textContent.trim()).toContain(
+ 'Could not retrieve the pipeline status. For troubleshooting steps, read the <a href="help">documentation.</a>',
+ );
});
describe('with a pipeline', () => {
@@ -88,34 +88,36 @@ describe('MRWidgetPipeline', () => {
});
it('should render pipeline ID', () => {
- expect(
- vm.$el.querySelector('.pipeline-id').textContent.trim(),
- ).toEqual(`#${mockData.pipeline.id}`);
+ expect(vm.$el.querySelector('.pipeline-id').textContent.trim()).toEqual(
+ `#${mockData.pipeline.id}`,
+ );
});
it('should render pipeline status and commit id', () => {
- expect(
- vm.$el.querySelector('.media-body').textContent.trim(),
- ).toContain(mockData.pipeline.details.status.label);
+ expect(vm.$el.querySelector('.media-body').textContent.trim()).toContain(
+ mockData.pipeline.details.status.label,
+ );
- expect(
- vm.$el.querySelector('.js-commit-link').textContent.trim(),
- ).toEqual(mockData.pipeline.commit.short_id);
+ expect(vm.$el.querySelector('.js-commit-link').textContent.trim()).toEqual(
+ mockData.pipeline.commit.short_id,
+ );
- expect(
- vm.$el.querySelector('.js-commit-link').getAttribute('href'),
- ).toEqual(mockData.pipeline.commit.commit_path);
+ expect(vm.$el.querySelector('.js-commit-link').getAttribute('href')).toEqual(
+ mockData.pipeline.commit.commit_path,
+ );
});
it('should render pipeline graph', () => {
expect(vm.$el.querySelector('.mr-widget-pipeline-graph')).toBeDefined();
- expect(vm.$el.querySelectorAll('.stage-container').length).toEqual(mockData.pipeline.details.stages.length);
+ expect(vm.$el.querySelectorAll('.stage-container').length).toEqual(
+ mockData.pipeline.details.stages.length,
+ );
});
it('should render coverage information', () => {
- expect(
- vm.$el.querySelector('.media-body').textContent,
- ).toContain(`Coverage ${mockData.pipeline.coverage}`);
+ expect(vm.$el.querySelector('.media-body').textContent).toContain(
+ `Coverage ${mockData.pipeline.coverage}`,
+ );
});
});
@@ -133,30 +135,30 @@ describe('MRWidgetPipeline', () => {
});
it('should render pipeline ID', () => {
- expect(
- vm.$el.querySelector('.pipeline-id').textContent.trim(),
- ).toEqual(`#${mockData.pipeline.id}`);
+ expect(vm.$el.querySelector('.pipeline-id').textContent.trim()).toEqual(
+ `#${mockData.pipeline.id}`,
+ );
});
it('should render pipeline status', () => {
- expect(
- vm.$el.querySelector('.media-body').textContent.trim(),
- ).toContain(mockData.pipeline.details.status.label);
+ expect(vm.$el.querySelector('.media-body').textContent.trim()).toContain(
+ mockData.pipeline.details.status.label,
+ );
- expect(
- vm.$el.querySelector('.js-commit-link'),
- ).toBeNull();
+ expect(vm.$el.querySelector('.js-commit-link')).toBeNull();
});
it('should render pipeline graph', () => {
expect(vm.$el.querySelector('.mr-widget-pipeline-graph')).toBeDefined();
- expect(vm.$el.querySelectorAll('.stage-container').length).toEqual(mockData.pipeline.details.stages.length);
+ expect(vm.$el.querySelectorAll('.stage-container').length).toEqual(
+ mockData.pipeline.details.stages.length,
+ );
});
it('should render coverage information', () => {
- expect(
- vm.$el.querySelector('.media-body').textContent,
- ).toContain(`Coverage ${mockData.pipeline.coverage}`);
+ expect(vm.$el.querySelector('.media-body').textContent).toContain(
+ `Coverage ${mockData.pipeline.coverage}`,
+ );
});
});
@@ -172,9 +174,7 @@ describe('MRWidgetPipeline', () => {
troubleshootingDocsPath: 'help',
});
- expect(
- vm.$el.querySelector('.media-body').textContent,
- ).not.toContain('Coverage');
+ expect(vm.$el.querySelector('.media-body').textContent).not.toContain('Coverage');
});
});
diff --git a/spec/javascripts/vue_mr_widget/components/states/mr_widget_merged_spec.js b/spec/javascripts/vue_mr_widget/components/states/mr_widget_merged_spec.js
index d68342635ef..da5cb752c6f 100644
--- a/spec/javascripts/vue_mr_widget/components/states/mr_widget_merged_spec.js
+++ b/spec/javascripts/vue_mr_widget/components/states/mr_widget_merged_spec.js
@@ -69,7 +69,7 @@ describe('MRWidgetMerged', () => {
expect(vm.shouldShowRemoveSourceBranch).toEqual(true);
});
- it('returns false wehn sourceBranchRemoved is true', () => {
+ it('returns false when sourceBranchRemoved is true', () => {
vm.mr.sourceBranchRemoved = true;
expect(vm.shouldShowRemoveSourceBranch).toEqual(false);
diff --git a/spec/javascripts/vue_mr_widget/mock_data.js b/spec/javascripts/vue_mr_widget/mock_data.js
index c5e30a730cb..17554c4fe42 100644
--- a/spec/javascripts/vue_mr_widget/mock_data.js
+++ b/spec/javascripts/vue_mr_widget/mock_data.js
@@ -218,6 +218,7 @@ export default {
diverged_commits_count: 0,
only_allow_merge_if_pipeline_succeeds: false,
commit_change_content_path: '/root/acets-app/merge_requests/22/commit_change_content',
- merge_commit_path: 'http://localhost:3000/root/acets-app/commit/53027d060246c8f47e4a9310fb332aa52f221775',
- troubleshooting_docs_path: 'help'
+ merge_commit_path:
+ 'http://localhost:3000/root/acets-app/commit/53027d060246c8f47e4a9310fb332aa52f221775',
+ troubleshooting_docs_path: 'help',
};
diff --git a/spec/javascripts/vue_mr_widget/mr_widget_options_spec.js b/spec/javascripts/vue_mr_widget/mr_widget_options_spec.js
index 27b6c91e154..09fbe87b27e 100644
--- a/spec/javascripts/vue_mr_widget/mr_widget_options_spec.js
+++ b/spec/javascripts/vue_mr_widget/mr_widget_options_spec.js
@@ -454,7 +454,7 @@ describe('mrWidgetOptions', () => {
deployed_at: '2017-03-22T22:44:42.258Z',
deployed_at_formatted: 'Mar 22, 2017 10:44pm',
changes,
- status: 'success'
+ status: 'success',
};
beforeEach(done => {
@@ -607,33 +607,36 @@ describe('mrWidgetOptions', () => {
describe('with post merge deployments', () => {
beforeEach(done => {
- vm.mr.postMergeDeployments = [{
- id: 15,
- name: 'review/diplo',
- url: '/root/acets-review-apps/environments/15',
- stop_url: '/root/acets-review-apps/environments/15/stop',
- metrics_url: '/root/acets-review-apps/environments/15/deployments/1/metrics',
- metrics_monitoring_url: '/root/acets-review-apps/environments/15/metrics',
- external_url: 'http://diplo.',
- external_url_formatted: 'diplo.',
- deployed_at: '2017-03-22T22:44:42.258Z',
- deployed_at_formatted: 'Mar 22, 2017 10:44pm',
- changes: [
- {
- path: 'index.html',
- external_url: 'http://root-master-patch-91341.volatile-watch.surge.sh/index.html',
- },
- {
- path: 'imgs/gallery.html',
- external_url: 'http://root-master-patch-91341.volatile-watch.surge.sh/imgs/gallery.html',
- },
- {
- path: 'about/',
- external_url: 'http://root-master-patch-91341.volatile-watch.surge.sh/about/',
- },
- ],
- status: 'success'
- }];
+ vm.mr.postMergeDeployments = [
+ {
+ id: 15,
+ name: 'review/diplo',
+ url: '/root/acets-review-apps/environments/15',
+ stop_url: '/root/acets-review-apps/environments/15/stop',
+ metrics_url: '/root/acets-review-apps/environments/15/deployments/1/metrics',
+ metrics_monitoring_url: '/root/acets-review-apps/environments/15/metrics',
+ external_url: 'http://diplo.',
+ external_url_formatted: 'diplo.',
+ deployed_at: '2017-03-22T22:44:42.258Z',
+ deployed_at_formatted: 'Mar 22, 2017 10:44pm',
+ changes: [
+ {
+ path: 'index.html',
+ external_url: 'http://root-master-patch-91341.volatile-watch.surge.sh/index.html',
+ },
+ {
+ path: 'imgs/gallery.html',
+ external_url:
+ 'http://root-master-patch-91341.volatile-watch.surge.sh/imgs/gallery.html',
+ },
+ {
+ path: 'about/',
+ external_url: 'http://root-master-patch-91341.volatile-watch.surge.sh/about/',
+ },
+ ],
+ status: 'success',
+ },
+ ];
vm.$nextTick(done);
});
diff --git a/spec/javascripts/vue_shared/components/filtered_search_dropdown_spec.js b/spec/javascripts/vue_shared/components/filtered_search_dropdown_spec.js
index b71cb36ecf6..b84b5ae67a8 100644
--- a/spec/javascripts/vue_shared/components/filtered_search_dropdown_spec.js
+++ b/spec/javascripts/vue_shared/components/filtered_search_dropdown_spec.js
@@ -41,7 +41,7 @@ describe('Filtered search dropdown', () => {
});
});
- describe('when visible number is bigger than the items lenght', () => {
+ describe('when visible number is bigger than the items length', () => {
beforeEach(() => {
vm = mountComponent(Component, {
items: [{ title: 'One' }, { title: 'Two' }, { title: 'Three' }],
diff --git a/spec/javascripts/vue_shared/components/sidebar/collapsed_grouped_date_picker_spec.js b/spec/javascripts/vue_shared/components/sidebar/collapsed_grouped_date_picker_spec.js
index 3483b7d387d..c507a97d37e 100644
--- a/spec/javascripts/vue_shared/components/sidebar/collapsed_grouped_date_picker_spec.js
+++ b/spec/javascripts/vue_shared/components/sidebar/collapsed_grouped_date_picker_spec.js
@@ -12,7 +12,7 @@ describe('collapsedGroupedDatePicker', () => {
});
describe('toggleCollapse events', () => {
- beforeEach((done) => {
+ beforeEach(done => {
spyOn(vm, 'toggleSidebar');
vm.minDate = new Date('07/17/2016');
Vue.nextTick(done);
@@ -26,7 +26,7 @@ describe('collapsedGroupedDatePicker', () => {
});
describe('minDate and maxDate', () => {
- beforeEach((done) => {
+ beforeEach(done => {
vm.minDate = new Date('07/17/2016');
vm.maxDate = new Date('07/17/2017');
Vue.nextTick(done);
@@ -42,7 +42,7 @@ describe('collapsedGroupedDatePicker', () => {
});
describe('minDate', () => {
- beforeEach((done) => {
+ beforeEach(done => {
vm.minDate = new Date('07/17/2016');
Vue.nextTick(done);
});
@@ -56,7 +56,7 @@ describe('collapsedGroupedDatePicker', () => {
});
describe('maxDate', () => {
- beforeEach((done) => {
+ beforeEach(done => {
vm.maxDate = new Date('07/17/2017');
Vue.nextTick(done);
});
diff --git a/spec/javascripts/vue_shared/components/sidebar/date_picker_spec.js b/spec/javascripts/vue_shared/components/sidebar/date_picker_spec.js
index 1581f4e3eb1..805ba7b9947 100644
--- a/spec/javascripts/vue_shared/components/sidebar/date_picker_spec.js
+++ b/spec/javascripts/vue_shared/components/sidebar/date_picker_spec.js
@@ -41,7 +41,7 @@ describe('sidebarDatePicker', () => {
expect(vm.$el.querySelector('.value-content span').innerText.trim()).toEqual('None');
});
- it('should render date-picker when editing', (done) => {
+ it('should render date-picker when editing', done => {
vm.editing = true;
Vue.nextTick(() => {
expect(vm.$el.querySelector('.pika-label')).toBeDefined();
@@ -50,7 +50,7 @@ describe('sidebarDatePicker', () => {
});
describe('editable', () => {
- beforeEach((done) => {
+ beforeEach(done => {
vm.editable = true;
Vue.nextTick(done);
});
@@ -59,7 +59,7 @@ describe('sidebarDatePicker', () => {
expect(vm.$el.querySelector('.title .btn-blank').innerText.trim()).toEqual('Edit');
});
- it('should enable editing when edit button is clicked', (done) => {
+ it('should enable editing when edit button is clicked', done => {
vm.isLoading = false;
Vue.nextTick(() => {
vm.$el.querySelector('.title .btn-blank').click();
@@ -70,7 +70,7 @@ describe('sidebarDatePicker', () => {
});
});
- it('should render date if selectedDate', (done) => {
+ it('should render date if selectedDate', done => {
vm.selectedDate = new Date('07/07/2017');
Vue.nextTick(() => {
expect(vm.$el.querySelector('.value-content strong').innerText.trim()).toEqual('Jul 7, 2017');
@@ -79,7 +79,7 @@ describe('sidebarDatePicker', () => {
});
describe('selectedDate and editable', () => {
- beforeEach((done) => {
+ beforeEach(done => {
vm.selectedDate = new Date('07/07/2017');
vm.editable = true;
Vue.nextTick(done);
@@ -100,7 +100,7 @@ describe('sidebarDatePicker', () => {
});
describe('showToggleSidebar', () => {
- beforeEach((done) => {
+ beforeEach(done => {
vm.showToggleSidebar = true;
Vue.nextTick(done);
});
diff --git a/spec/javascripts/vue_shared/components/sidebar/labels_select/dropdown_value_collapsed_spec.js b/spec/javascripts/vue_shared/components/sidebar/labels_select/dropdown_value_collapsed_spec.js
index 9a691116cf8..804b33422bd 100644
--- a/spec/javascripts/vue_shared/components/sidebar/labels_select/dropdown_value_collapsed_spec.js
+++ b/spec/javascripts/vue_shared/components/sidebar/labels_select/dropdown_value_collapsed_spec.js
@@ -49,7 +49,9 @@ describe('DropdownValueCollapsedComponent', () => {
const vmMoreLabels = createComponent(mockMoreLabels);
- expect(vmMoreLabels.labelsList).toBe('Foo Label, Foo Label, Foo Label, Foo Label, Foo Label, and 2 more');
+ expect(vmMoreLabels.labelsList).toBe(
+ 'Foo Label, Foo Label, Foo Label, Foo Label, Foo Label, and 2 more',
+ );
vmMoreLabels.$destroy();
});
diff --git a/spec/javascripts/vue_shared/components/user_avatar/user_avatar_link_spec.js b/spec/javascripts/vue_shared/components/user_avatar/user_avatar_link_spec.js
index 50b8d49d4bd..e022245d3ea 100644
--- a/spec/javascripts/vue_shared/components/user_avatar/user_avatar_link_spec.js
+++ b/spec/javascripts/vue_shared/components/user_avatar/user_avatar_link_spec.js
@@ -44,7 +44,7 @@ describe('User Avatar Link Component', function() {
expect(this.userAvatarLink.$el.querySelector('img')).not.toBeNull();
});
- it('should return neccessary props as defined', function() {
+ it('should return necessary props as defined', function() {
_.each(this.propsData, (val, key) => {
expect(this.userAvatarLink[key]).toBeDefined();
});
diff --git a/spec/lib/banzai/filter/autolink_filter_spec.rb b/spec/lib/banzai/filter/autolink_filter_spec.rb
index a50329473ad..7a457403b51 100644
--- a/spec/lib/banzai/filter/autolink_filter_spec.rb
+++ b/spec/lib/banzai/filter/autolink_filter_spec.rb
@@ -76,7 +76,7 @@ describe Banzai::Filter::AutolinkFilter do
expect(doc.at_css('a')['href']).to eq link
end
- it 'autolinks multiple occurences of smb' do
+ it 'autolinks multiple occurrences of smb' do
link1 = 'smb:///Volumes/shared/foo.pdf'
link2 = 'smb:///Volumes/shared/bar.pdf'
diff --git a/spec/lib/banzai/filter/relative_link_filter_spec.rb b/spec/lib/banzai/filter/relative_link_filter_spec.rb
index ed1ebe9ebf6..415ded05e6e 100644
--- a/spec/lib/banzai/filter/relative_link_filter_spec.rb
+++ b/spec/lib/banzai/filter/relative_link_filter_spec.rb
@@ -226,7 +226,7 @@ describe Banzai::Filter::RelativeLinkFilter do
let(:ref) {'mark#\'@],+;-._/#@!$&()+down'}
it 'correctly escapes the ref' do
- # Adressable won't escape the '#', so we do this manually
+ # Addressable won't escape the '#', so we do this manually
ref_escaped = 'mark%23\'@%5D,+;-._/%23@!$&()+down'
# Stub this method so the branch doesn't actually need to be in the repo
diff --git a/spec/lib/container_registry/blob_spec.rb b/spec/lib/container_registry/blob_spec.rb
index c73faa55513..d3fff5bad42 100644
--- a/spec/lib/container_registry/blob_spec.rb
+++ b/spec/lib/container_registry/blob_spec.rb
@@ -64,7 +64,7 @@ describe ContainerRegistry::Blob do
.to_return(status: 200)
end
- it 'returns true when blob has been successfuly deleted' do
+ it 'returns true when blob has been successfully deleted' do
expect(blob.delete).to be_truthy
end
end
diff --git a/spec/lib/gitlab/background_migration/create_fork_network_memberships_range_spec.rb b/spec/lib/gitlab/background_migration/create_fork_network_memberships_range_spec.rb
index e1c4f9cfea7..5076996474f 100644
--- a/spec/lib/gitlab/background_migration/create_fork_network_memberships_range_spec.rb
+++ b/spec/lib/gitlab/background_migration/create_fork_network_memberships_range_spec.rb
@@ -118,7 +118,7 @@ describe Gitlab::BackgroundMigration::CreateForkNetworkMembershipsRange, :migrat
expect(fork_network_members.count).to eq(12)
end
- it 'knows when not all memberships withing a batch have been created' do
+ it 'knows when not all memberships within a batch have been created' do
expect(migration.missing_members?(8, 10)).to be_truthy
end
end
diff --git a/spec/lib/gitlab/checks/lfs_integrity_spec.rb b/spec/lib/gitlab/checks/lfs_integrity_spec.rb
index 0488720cec8..887ea8fc1e0 100644
--- a/spec/lib/gitlab/checks/lfs_integrity_spec.rb
+++ b/spec/lib/gitlab/checks/lfs_integrity_spec.rb
@@ -68,7 +68,7 @@ describe Gitlab::Checks::LfsIntegrity do
expect(subject.objects_missing?).to be_truthy
end
- it 'is false parent project already conatins LFS objects for the fork' do
+ it 'is false parent project already contains LFS objects for the fork' do
lfs_object = create(:lfs_object, oid: blob_object.lfs_oid)
create(:lfs_objects_project, project: parent_project, lfs_object: lfs_object)
diff --git a/spec/lib/gitlab/ci/ansi2html_spec.rb b/spec/lib/gitlab/ci/ansi2html_spec.rb
index 7549e9941b6..5a5c071c639 100644
--- a/spec/lib/gitlab/ci/ansi2html_spec.rb
+++ b/spec/lib/gitlab/ci/ansi2html_spec.rb
@@ -7,7 +7,7 @@ describe Gitlab::Ci::Ansi2html do
expect(convert_html("Hello")).to eq('Hello')
end
- it "strips non-color-changing controll sequences" do
+ it "strips non-color-changing control sequences" do
expect(convert_html("Hello \e[2Kworld")).to eq('Hello world')
end
diff --git a/spec/lib/gitlab/ci/build/policy/variables_spec.rb b/spec/lib/gitlab/ci/build/policy/variables_spec.rb
index 854c4cb718c..c2c0742efc3 100644
--- a/spec/lib/gitlab/ci/build/policy/variables_spec.rb
+++ b/spec/lib/gitlab/ci/build/policy/variables_spec.rb
@@ -24,7 +24,7 @@ describe Gitlab::Ci::Build::Policy::Variables do
expect(policy).to be_satisfied_by(pipeline, seed)
end
- it 'is not satisfied by an overriden empty variable' do
+ it 'is not satisfied by an overridden empty variable' do
policy = described_class.new(['$CI_PROJECT_NAME'])
expect(policy).not_to be_satisfied_by(pipeline, seed)
diff --git a/spec/lib/gitlab/ci/config/entry/global_spec.rb b/spec/lib/gitlab/ci/config/entry/global_spec.rb
index 1860ed79bfd..7c18514934e 100644
--- a/spec/lib/gitlab/ci/config/entry/global_spec.rb
+++ b/spec/lib/gitlab/ci/config/entry/global_spec.rb
@@ -219,7 +219,7 @@ describe Gitlab::Ci::Config::Entry::Global do
##
# When nodes are specified but not defined, we assume that
- # configuration is valid, and we asume that entry is simply undefined,
+ # configuration is valid, and we assume that entry is simply undefined,
# despite the fact, that key is present. See issue #18775 for more
# details.
#
diff --git a/spec/lib/gitlab/cross_project_access/check_info_spec.rb b/spec/lib/gitlab/cross_project_access/check_info_spec.rb
index bc9dbf2bece..239fa364f5e 100644
--- a/spec/lib/gitlab/cross_project_access/check_info_spec.rb
+++ b/spec/lib/gitlab/cross_project_access/check_info_spec.rb
@@ -50,7 +50,7 @@ describe Gitlab::CrossProjectAccess::CheckInfo do
expect(info.should_run?(dummy_controller)).to be_truthy
end
- it 'returns the the oposite of #should_skip? when the check is a skip' do
+ it 'returns the the opposite of #should_skip? when the check is a skip' do
info = described_class.new({}, nil, nil, true)
expect(info).to receive(:should_skip?).with(dummy_controller).and_return(false)
@@ -101,7 +101,7 @@ describe Gitlab::CrossProjectAccess::CheckInfo do
expect(info.should_skip?(dummy_controller)).to be_truthy
end
- it 'returns the the oposite of #should_run? when the check is not a skip' do
+ it 'returns the the opposite of #should_run? when the check is not a skip' do
info = described_class.new({}, nil, nil, false)
expect(info).to receive(:should_run?).with(dummy_controller).and_return(false)
diff --git a/spec/lib/gitlab/database/rename_reserved_paths_migration/v1/rename_base_spec.rb b/spec/lib/gitlab/database/rename_reserved_paths_migration/v1/rename_base_spec.rb
index cc7cb3f23fd..248cca25a2c 100644
--- a/spec/lib/gitlab/database/rename_reserved_paths_migration/v1/rename_base_spec.rb
+++ b/spec/lib/gitlab/database/rename_reserved_paths_migration/v1/rename_base_spec.rb
@@ -20,7 +20,7 @@ describe Gitlab::Database::RenameReservedPathsMigration::V1::RenameBase, :delete
end
describe "#remove_last_ocurrence" do
- it "removes only the last occurance of a string" do
+ it "removes only the last occurrence of a string" do
input = "this/is/a-word-to-replace/namespace/with/a-word-to-replace"
expect(subject.remove_last_occurrence(input, "a-word-to-replace"))
diff --git a/spec/lib/gitlab/diff/position_spec.rb b/spec/lib/gitlab/diff/position_spec.rb
index 2d94356f386..cc4faf6f10b 100644
--- a/spec/lib/gitlab/diff/position_spec.rb
+++ b/spec/lib/gitlab/diff/position_spec.rb
@@ -566,13 +566,13 @@ describe Gitlab::Diff::Position do
end
end
- context "for text positon" do
+ context "for text position" do
let(:args) { args_for_text }
it_behaves_like "diff position json"
end
- context "for image positon" do
+ context "for image position" do
let(:args) { args_for_img }
it_behaves_like "diff position json"
@@ -592,13 +592,13 @@ describe Gitlab::Diff::Position do
end
end
- context "for text positon" do
+ context "for text position" do
let(:args) { args_for_text }
it_behaves_like "diff position json"
end
- context "for image positon" do
+ context "for image position" do
let(:args) { args_for_img }
it_behaves_like "diff position json"
diff --git a/spec/lib/gitlab/diff/position_tracer_spec.rb b/spec/lib/gitlab/diff/position_tracer_spec.rb
index ddc4f6c5b5c..a2eed07ca55 100644
--- a/spec/lib/gitlab/diff/position_tracer_spec.rb
+++ b/spec/lib/gitlab/diff/position_tracer_spec.rb
@@ -43,7 +43,7 @@ describe Gitlab::Diff::PositionTracer do
#
# In any case, all of this means that the tests below will be extremely
# (excessively, unjustifiably) thorough for scenarios where "the file was
- # created in the old diff" and then drop off to comparitively lackluster
+ # created in the old diff" and then drop off to comparatively lackluster
# testing of other scenarios.
#
# I did still try to cover most of the obvious and potentially tricky
diff --git a/spec/lib/gitlab/git/attributes_at_ref_parser_spec.rb b/spec/lib/gitlab/git/attributes_at_ref_parser_spec.rb
index ca067a29174..134bd5657e7 100644
--- a/spec/lib/gitlab/git/attributes_at_ref_parser_spec.rb
+++ b/spec/lib/gitlab/git/attributes_at_ref_parser_spec.rb
@@ -17,7 +17,7 @@ describe Gitlab::Git::AttributesAtRefParser, :seed_helper do
end
it 'handles missing blobs' do
- expect { described_class.new(repository, 'non-existant-branch') }.not_to raise_error
+ expect { described_class.new(repository, 'non-existent-branch') }.not_to raise_error
end
describe '#attributes' do
diff --git a/spec/lib/gitlab/gpg_spec.rb b/spec/lib/gitlab/gpg_spec.rb
index 47f37cae98f..39d09c49989 100644
--- a/spec/lib/gitlab/gpg_spec.rb
+++ b/spec/lib/gitlab/gpg_spec.rb
@@ -96,7 +96,7 @@ describe Gitlab::Gpg do
expect(described_class.current_home_dir).to eq default_home_dir
end
- it 'returns the explicitely set home dir' do
+ it 'returns the explicitly set home dir' do
GPGME::Engine.home_dir = '/tmp/gpg'
expect(described_class.current_home_dir).to eq '/tmp/gpg'
@@ -104,7 +104,7 @@ describe Gitlab::Gpg do
GPGME::Engine.home_dir = GPGME::Engine.dirinfo('homedir')
end
- it 'returns the default value when explicitely setting the home dir to nil' do
+ it 'returns the default value when explicitly setting the home dir to nil' do
GPGME::Engine.home_dir = nil
expect(described_class.current_home_dir).to eq default_home_dir
diff --git a/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb b/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb
index b0570680d5a..365bfae0d88 100644
--- a/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb
+++ b/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb
@@ -321,7 +321,7 @@ describe Gitlab::ImportExport::ProjectTreeRestorer do
end
end
- context 'when the project has overriden params in import data' do
+ context 'when the project has overridden params in import data' do
it 'overwrites the params stored in the JSON' do
project.create_import_data(data: { override_params: { description: "Overridden" } })
diff --git a/spec/lib/gitlab/view/presenter/base_spec.rb b/spec/lib/gitlab/view/presenter/base_spec.rb
index 4eca53032a2..02c2fd47197 100644
--- a/spec/lib/gitlab/view/presenter/base_spec.rb
+++ b/spec/lib/gitlab/view/presenter/base_spec.rb
@@ -40,7 +40,7 @@ describe Gitlab::View::Presenter::Base do
end
end
- context 'subject is overriden' do
+ context 'subject is overridden' do
it 'returns true' do
presenter = presenter_class.new(build_stubbed(:project, :public))
diff --git a/spec/lib/microsoft_teams/notifier_spec.rb b/spec/lib/microsoft_teams/notifier_spec.rb
index c9756544bd6..2aaa7c24ad8 100644
--- a/spec/lib/microsoft_teams/notifier_spec.rb
+++ b/spec/lib/microsoft_teams/notifier_spec.rb
@@ -48,7 +48,7 @@ describe MicrosoftTeams::Notifier do
stub_request(:post, webhook_url).with(body: JSON(body), headers: { 'Content-Type' => 'application/json' }).to_return(status: 200, body: "", headers: {})
end
- it 'expects to receive successfull answer' do
+ it 'expects to receive successful answer' do
expect(subject.ping(options)).to be true
end
end
diff --git a/spec/migrations/migrate_old_artifacts_spec.rb b/spec/migrations/migrate_old_artifacts_spec.rb
index 4187ab149a5..af77d64fdbf 100644
--- a/spec/migrations/migrate_old_artifacts_spec.rb
+++ b/spec/migrations/migrate_old_artifacts_spec.rb
@@ -76,7 +76,7 @@ describe MigrateOldArtifacts do
end
end
- context 'when there are aritfacts in old and new directory' do
+ context 'when there are artifacts in old and new directory' do
before do
store_artifacts_in_legacy_path(build2)
diff --git a/spec/models/concerns/awardable_spec.rb b/spec/models/concerns/awardable_spec.rb
index 69083bdc125..debc02fa51f 100644
--- a/spec/models/concerns/awardable_spec.rb
+++ b/spec/models/concerns/awardable_spec.rb
@@ -24,13 +24,29 @@ describe Awardable do
end
end
- describe ".awarded" do
+ describe "#awarded" do
it "filters by user and emoji name" do
expect(Issue.awarded(award_emoji.user, "thumbsup")).to be_empty
expect(Issue.awarded(award_emoji.user, "thumbsdown")).to eq [issue]
expect(Issue.awarded(award_emoji2.user, "thumbsup")).to eq [issue2]
expect(Issue.awarded(award_emoji2.user, "thumbsdown")).to be_empty
end
+
+ it "filters by user and any emoji" do
+ issue3 = create(:issue)
+ create(:award_emoji, awardable: issue3, name: "star", user: award_emoji.user)
+ create(:award_emoji, awardable: issue3, name: "star", user: award_emoji2.user)
+
+ expect(Issue.awarded(award_emoji.user)).to eq [issue, issue3]
+ expect(Issue.awarded(award_emoji2.user)).to eq [issue2, issue3]
+ end
+ end
+
+ describe "#not_awarded" do
+ it "returns issues not awarded by user" do
+ expect(Issue.not_awarded(award_emoji.user)).to eq [issue2]
+ expect(Issue.not_awarded(award_emoji2.user)).to eq [issue]
+ end
end
end
diff --git a/spec/models/concerns/cacheable_attributes_spec.rb b/spec/models/concerns/cacheable_attributes_spec.rb
index f8c2e29fadd..827fbc9d7d5 100644
--- a/spec/models/concerns/cacheable_attributes_spec.rb
+++ b/spec/models/concerns/cacheable_attributes_spec.rb
@@ -41,7 +41,7 @@ describe CacheableAttributes do
expect(minimal_test_class.current_without_cache).to eq(minimal_test_class.last)
end
- it 'can be overriden' do
+ it 'can be overridden' do
minimal_test_class.define_singleton_method(:current_without_cache) do
first
end
@@ -64,7 +64,7 @@ describe CacheableAttributes do
context 'with defaults defined' do
include_context 'with defaults'
- it 'can be overriden' do
+ it 'can be overridden' do
expect(minimal_test_class.defaults).to eq({ foo: 'a', bar: 'b', baz: 'c' })
end
end
diff --git a/spec/models/concerns/issuable_spec.rb b/spec/models/concerns/issuable_spec.rb
index ec6374f3963..a4bf3e2350a 100644
--- a/spec/models/concerns/issuable_spec.rb
+++ b/spec/models/concerns/issuable_spec.rb
@@ -519,7 +519,7 @@ describe Issuable do
end
end
- context 'substracting time' do
+ context 'subtracting time' do
before do
spend_time(1800)
end
@@ -530,7 +530,7 @@ describe Issuable do
expect(issue.total_time_spent).to eq(900)
end
- context 'when time to substract exceeds the total time spent' do
+ context 'when time to subtract exceeds the total time spent' do
it 'raise a validation error' do
Timecop.travel(1.minute.from_now) do
expect do
diff --git a/spec/models/lfs_object_spec.rb b/spec/models/lfs_object_spec.rb
index 911f85d7b28..3f929710862 100644
--- a/spec/models/lfs_object_spec.rb
+++ b/spec/models/lfs_object_spec.rb
@@ -8,7 +8,7 @@ describe LfsObject do
expect(subject.local_store?).to eq true
end
- it 'returns false whe file_store is equal to LfsObjectUploader::Store::REMOTE' do
+ it 'returns false when file_store is equal to LfsObjectUploader::Store::REMOTE' do
subject.file_store = LfsObjectUploader::Store::REMOTE
expect(subject.local_store?).to eq false
diff --git a/spec/models/merge_request_spec.rb b/spec/models/merge_request_spec.rb
index c8943f2d86f..85a4ebac66c 100644
--- a/spec/models/merge_request_spec.rb
+++ b/spec/models/merge_request_spec.rb
@@ -13,6 +13,20 @@ describe MergeRequest do
it { is_expected.to belong_to(:merge_user).class_name("User") }
it { is_expected.to belong_to(:assignee) }
it { is_expected.to have_many(:merge_request_diffs) }
+
+ context 'for forks' do
+ let!(:project) { create(:project) }
+ let!(:fork) { fork_project(project) }
+ let!(:merge_request) { create(:merge_request, target_project: project, source_project: fork) }
+
+ it 'does not load another project due to inverse relationship' do
+ expect(project.merge_requests.first.target_project.object_id).to eq(project.object_id)
+ end
+
+ it 'finds the associated merge request' do
+ expect(project.merge_requests.find(merge_request.id)).to eq(merge_request)
+ end
+ end
end
describe '#squash_in_progress?' do
diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb
index e66838edd1a..d4b9a4c8cd6 100644
--- a/spec/models/project_spec.rb
+++ b/spec/models/project_spec.rb
@@ -88,6 +88,10 @@ describe Project do
it { is_expected.to have_many(:project_deploy_tokens) }
it { is_expected.to have_many(:deploy_tokens).through(:project_deploy_tokens) }
+ it 'has an inverse relationship with merge requests' do
+ expect(described_class.reflect_on_association(:merge_requests).has_inverse?).to eq(:target_project)
+ end
+
context 'after initialized' do
it "has a project_feature" do
expect(described_class.new.project_feature).to be_present
@@ -2746,7 +2750,7 @@ describe Project do
.to raise_error(ActiveRecord::RecordNotSaved, error_message)
end
- it 'updates the project succesfully' do
+ it 'updates the project successfully' do
merge_request = create(:merge_request, target_project: project, source_project: project)
expect { project.append_or_update_attribute(:merge_requests, [merge_request]) }
@@ -3314,7 +3318,7 @@ describe Project do
end
end
- context 'when explicitely enabled' do
+ context 'when explicitly enabled' do
context 'when domain is empty' do
before do
create(:project_auto_devops, project: project, domain: nil)
diff --git a/spec/models/project_wiki_spec.rb b/spec/models/project_wiki_spec.rb
index f38fc191943..cc5e34782ec 100644
--- a/spec/models/project_wiki_spec.rb
+++ b/spec/models/project_wiki_spec.rb
@@ -145,7 +145,7 @@ describe ProjectWiki do
end
it "returns nil if the page does not exist" do
- expect(subject.find_page("non-existant")).to eq(nil)
+ expect(subject.find_page("non-existent")).to eq(nil)
end
it "can find a page by slug" do
@@ -226,7 +226,7 @@ describe ProjectWiki do
end
it 'returns nil if the page does not exist' do
- expect(subject.find_file('non-existant')).to eq(nil)
+ expect(subject.find_file('non-existent')).to eq(nil)
end
it 'returns a Gitlab::Git::WikiFile instance' do
diff --git a/spec/models/upload_spec.rb b/spec/models/upload_spec.rb
index 36b8e5d304f..3c89e99abf0 100644
--- a/spec/models/upload_spec.rb
+++ b/spec/models/upload_spec.rb
@@ -91,7 +91,7 @@ describe Upload do
.to change { upload.checksum }.from(nil).to(expected)
end
- it 'sets `checksum` to nil for a non-existant file' do
+ it 'sets `checksum` to nil for a non-existent file' do
expect(upload).to receive(:exist?).and_return(false)
checksum = Digest::SHA256.file(__FILE__).hexdigest
diff --git a/spec/presenters/project_presenter_spec.rb b/spec/presenters/project_presenter_spec.rb
index 3eb2f149311..7b0192fa9c8 100644
--- a/spec/presenters/project_presenter_spec.rb
+++ b/spec/presenters/project_presenter_spec.rb
@@ -239,7 +239,7 @@ describe ProjectPresenter do
expect(presenter.new_file_anchor_data).to have_attributes(enabled: false,
label: "New file",
link: presenter.project_new_blob_path(project, 'master'),
- class_modifier: 'new')
+ class_modifier: 'success')
end
it 'returns nil if user cannot push' do
diff --git a/spec/requests/api/commits_spec.rb b/spec/requests/api/commits_spec.rb
index 98399471f9a..2963dea634a 100644
--- a/spec/requests/api/commits_spec.rb
+++ b/spec/requests/api/commits_spec.rb
@@ -565,7 +565,7 @@ describe API::Commits do
}
end
- it 'are commited as one in project repo' do
+ it 'are committed as one in project repo' do
post api(url, user), valid_mo_params
expect(response).to have_gitlab_http_status(201)
diff --git a/spec/requests/api/issues_spec.rb b/spec/requests/api/issues_spec.rb
index 9cda39a569b..3d532dd83c7 100644
--- a/spec/requests/api/issues_spec.rb
+++ b/spec/requests/api/issues_spec.rb
@@ -55,8 +55,8 @@ describe API::Issues do
end
let!(:note) { create(:note_on_issue, author: user, project: project, noteable: issue) }
- let(:no_milestone_title) { URI.escape(Milestone::None.title) }
- let(:any_milestone_title) { URI.escape(Milestone::Any.title) }
+ let(:no_milestone_title) { "None" }
+ let(:any_milestone_title) { "Any" }
before(:all) do
project.add_reporter(user)
@@ -196,14 +196,24 @@ describe API::Issues do
expect_paginated_array_response(size: 3)
end
- it 'returns issues reacted by the authenticated user by the given emoji' do
+ it 'returns issues reacted by the authenticated user' do
issue2 = create(:issue, project: project, author: user, assignees: [user])
- award_emoji = create(:award_emoji, awardable: issue2, user: user2, name: 'star')
+ create(:award_emoji, awardable: issue2, user: user2, name: 'star')
- get api('/issues', user2), my_reaction_emoji: award_emoji.name, scope: 'all'
+ create(:award_emoji, awardable: issue, user: user2, name: 'thumbsup')
- expect_paginated_array_response(size: 1)
- expect(first_issue['id']).to eq(issue2.id)
+ get api('/issues', user2), my_reaction_emoji: 'Any', scope: 'all'
+
+ expect_paginated_array_response(size: 2)
+ end
+
+ it 'returns issues not reacted by the authenticated user' do
+ issue2 = create(:issue, project: project, author: user, assignees: [user])
+ create(:award_emoji, awardable: issue2, user: user2, name: 'star')
+
+ get api('/issues', user2), my_reaction_emoji: 'None', scope: 'all'
+
+ expect_paginated_array_response(size: 2)
end
it 'returns issues matching given search string for title' do
@@ -1791,6 +1801,74 @@ describe API::Issues do
end
end
+ describe 'GET :id/issues/:issue_iid/related_merge_requests' do
+ def get_related_merge_requests(project_id, issue_iid, user = nil)
+ get api("/projects/#{project_id}/issues/#{issue_iid}/related_merge_requests", user)
+ end
+
+ def create_referencing_mr(user, project, issue)
+ attributes = {
+ author: user,
+ source_project: project,
+ target_project: project,
+ source_branch: "master",
+ target_branch: "test",
+ description: "See #{issue.to_reference}"
+ }
+ create(:merge_request, attributes).tap do |merge_request|
+ create(:note, :system, project: project, noteable: issue, author: user, note: merge_request.to_reference(full: true))
+ end
+ end
+
+ let!(:related_mr) { create_referencing_mr(user, project, issue) }
+
+ context 'when unauthenticated' do
+ it 'return list of referenced merge requests from issue' do
+ get_related_merge_requests(project.id, issue.iid)
+
+ expect_paginated_array_response(size: 1)
+ end
+
+ it 'renders 404 if project is not visible' do
+ private_project = create(:project, :private)
+ private_issue = create(:issue, project: private_project)
+ create_referencing_mr(user, private_project, private_issue)
+
+ get_related_merge_requests(private_project.id, private_issue.iid)
+
+ expect(response).to have_gitlab_http_status(404)
+ end
+ end
+
+ it 'returns merge requests that mentioned a issue' do
+ create(:merge_request,
+ :simple,
+ author: user,
+ source_project: project,
+ target_project: project,
+ description: "Some description")
+
+ get_related_merge_requests(project.id, issue.iid, user)
+
+ expect_paginated_array_response(size: 1)
+ expect(json_response.first['id']).to eq(related_mr.id)
+ end
+
+ context 'no merge request mentioned a issue' do
+ it 'returns empty array' do
+ get_related_merge_requests(project.id, closed_issue.iid, user)
+
+ expect_paginated_array_response(size: 0)
+ end
+ end
+
+ it "returns 404 when issue doesn't exists" do
+ get_related_merge_requests(project.id, 999999, user)
+
+ expect(response).to have_gitlab_http_status(404)
+ end
+ end
+
describe "GET /projects/:id/issues/:issue_iid/user_agent_detail" do
let!(:user_agent_detail) { create(:user_agent_detail, subject: issue) }
diff --git a/spec/services/clusters/gcp/fetch_operation_service_spec.rb b/spec/services/clusters/gcp/fetch_operation_service_spec.rb
index e2fa93904c5..55f123ee786 100644
--- a/spec/services/clusters/gcp/fetch_operation_service_spec.rb
+++ b/spec/services/clusters/gcp/fetch_operation_service_spec.rb
@@ -24,7 +24,7 @@ describe Clusters::Gcp::FetchOperationService do
end
end
- context 'when suceeded to fetch operation' do
+ context 'when succeeded to fetch operation' do
before do
stub_cloud_platform_get_zone_operation(gcp_project_id, zone, operation_id)
end
diff --git a/spec/services/clusters/gcp/finalize_creation_service_spec.rb b/spec/services/clusters/gcp/finalize_creation_service_spec.rb
index 0f484222228..303d45495ef 100644
--- a/spec/services/clusters/gcp/finalize_creation_service_spec.rb
+++ b/spec/services/clusters/gcp/finalize_creation_service_spec.rb
@@ -30,7 +30,7 @@ describe Clusters::Gcp::FinalizeCreationService do
end
end
- context 'when suceeded to fetch gke cluster info' do
+ context 'when succeeded to fetch gke cluster info' do
let(:endpoint) { '111.111.111.111' }
let(:api_url) { 'https://' + endpoint }
let(:username) { 'sample-username' }
diff --git a/spec/services/clusters/gcp/provision_service_spec.rb b/spec/services/clusters/gcp/provision_service_spec.rb
index f48afdc83b2..c0bdac40938 100644
--- a/spec/services/clusters/gcp/provision_service_spec.rb
+++ b/spec/services/clusters/gcp/provision_service_spec.rb
@@ -26,7 +26,7 @@ describe Clusters::Gcp::ProvisionService do
end
end
- context 'when suceeded to request provision' do
+ context 'when succeeded to request provision' do
before do
stub_cloud_platform_create_cluster(gcp_project_id, zone)
end
diff --git a/spec/services/groups/transfer_service_spec.rb b/spec/services/groups/transfer_service_spec.rb
index 1289d3ce01f..dd8a1cee074 100644
--- a/spec/services/groups/transfer_service_spec.rb
+++ b/spec/services/groups/transfer_service_spec.rb
@@ -347,7 +347,7 @@ describe Groups::TransferService, :postgresql do
end
end
- context 'when transfering a group with nested groups and projects' do
+ context 'when transferring a group with nested groups and projects' do
let!(:group) { create(:group, :public) }
let!(:project1) { create(:project, :repository, :private, namespace: group) }
let!(:subgroup1) { create(:group, :private, parent: group) }
diff --git a/spec/services/projects/import_service_spec.rb b/spec/services/projects/import_service_spec.rb
index e6ffa2b957b..06f865dc848 100644
--- a/spec/services/projects/import_service_spec.rb
+++ b/spec/services/projects/import_service_spec.rb
@@ -125,7 +125,7 @@ describe Projects::ImportService do
project.import_type = 'bitbucket'
end
- it 'succeeds if repository import is successfull' do
+ it 'succeeds if repository import is successful' do
expect_any_instance_of(Gitlab::Shell).to receive(:import_repository).and_return(true)
expect_any_instance_of(Gitlab::BitbucketImport::Importer).to receive(:execute).and_return(true)
expect_any_instance_of(Projects::LfsPointers::LfsImportService).to receive(:execute).and_return({})
diff --git a/spec/services/projects/transfer_service_spec.rb b/spec/services/projects/transfer_service_spec.rb
index 1411723fb9e..2e07d4f8013 100644
--- a/spec/services/projects/transfer_service_spec.rb
+++ b/spec/services/projects/transfer_service_spec.rb
@@ -125,7 +125,7 @@ describe Projects::TransferService do
it { expect(project.errors.messages[:new_namespace].first).to eq 'Please select a new namespace for your project.' }
end
- context 'disallow transfering of project with tags' do
+ context 'disallow transferring of project with tags' do
let(:container_repository) { create(:container_repository) }
before do
diff --git a/spec/services/quick_actions/interpret_service_spec.rb b/spec/services/quick_actions/interpret_service_spec.rb
index 41a170e4f25..e513ee7ae44 100644
--- a/spec/services/quick_actions/interpret_service_spec.rb
+++ b/spec/services/quick_actions/interpret_service_spec.rb
@@ -315,7 +315,7 @@ describe QuickActions::InterpretService do
end
shared_examples 'award command' do
- it 'toggle award 100 emoji if content containts /award :100:' do
+ it 'toggle award 100 emoji if content contains /award :100:' do
_, updates = service.execute(content, issuable)
expect(updates).to eq(emoji_award: "100")
@@ -1395,7 +1395,7 @@ describe QuickActions::InterpretService do
it 'includes the formatted duration and proper verb' do
_, explanations = service.explain(content, issue)
- expect(explanations).to eq(['Substracts 2h spent time.'])
+ expect(explanations).to eq(['Subtracts 2h spent time.'])
end
end
diff --git a/spec/support/capybara.rb b/spec/support/capybara.rb
index c0ceb0f6605..18a7a392c12 100644
--- a/spec/support/capybara.rb
+++ b/spec/support/capybara.rb
@@ -100,7 +100,7 @@ RSpec.configure do |config|
# capybara/rspec already calls Capybara.reset_sessions! in an `after` hook,
# but `block_and_wait_for_requests_complete` is called before it so by
- # calling it explicitely here, we prevent any new requests from being fired
+ # calling it explicitly here, we prevent any new requests from being fired
# See https://github.com/teamcapybara/capybara/blob/ffb41cfad620de1961bb49b1562a9fa9b28c0903/lib/capybara/rspec.rb#L20-L25
# We don't reset the session when the example failed, because we need capybara-screenshot to have access to it.
Capybara.reset_sessions! unless example.exception
diff --git a/spec/support/helpers/filtered_search_helpers.rb b/spec/support/helpers/filtered_search_helpers.rb
index 5f42ff77fb2..6569feec39b 100644
--- a/spec/support/helpers/filtered_search_helpers.rb
+++ b/spec/support/helpers/filtered_search_helpers.rb
@@ -120,8 +120,12 @@ module FilteredSearchHelpers
create_token('Label', label_name, symbol)
end
- def emoji_token(emoji_name = nil)
- { name: 'My-Reaction', emoji_name: emoji_name }
+ def reaction_token(reaction_name = nil, is_emoji = true)
+ if is_emoji
+ { name: 'My-Reaction', emoji_name: reaction_name }
+ else
+ create_token('My-Reaction', reaction_name)
+ end
end
def default_placeholder
diff --git a/spec/support/helpers/project_forks_helper.rb b/spec/support/helpers/project_forks_helper.rb
index 6a7132c3093..9a86560da2a 100644
--- a/spec/support/helpers/project_forks_helper.rb
+++ b/spec/support/helpers/project_forks_helper.rb
@@ -35,7 +35,7 @@ module ProjectForksHelper
if create_repository
# The call to project.repository.after_import in RepositoryForkWorker does
# not reset the @exists variable of this forked_project.repository
- # so we have to explicitely call this method to clear the @exists variable.
+ # so we have to explicitly call this method to clear the @exists variable.
# of the instance we're returning here.
forked_project.repository.after_import
end
diff --git a/spec/support/shared_examples/services/boards/lists_move_service.rb b/spec/support/shared_examples/services/boards/lists_move_service.rb
index 07c98cb29b7..2cdb968a45d 100644
--- a/spec/support/shared_examples/services/boards/lists_move_service.rb
+++ b/spec/support/shared_examples/services/boards/lists_move_service.rb
@@ -14,7 +14,7 @@ shared_examples 'lists move service' do
expect(current_list_positions).to eq [0, 1, 2, 3]
end
- it 'keeps position of lists when new positon is equal to old position' do
+ it 'keeps position of lists when new position is equal to old position' do
service = described_class.new(parent, user, position: planning.position)
service.execute(planning)
@@ -22,7 +22,7 @@ shared_examples 'lists move service' do
expect(current_list_positions).to eq [0, 1, 2, 3]
end
- it 'keeps position of lists when new positon is negative' do
+ it 'keeps position of lists when new position is negative' do
service = described_class.new(parent, user, position: -1)
service.execute(planning)
@@ -30,7 +30,7 @@ shared_examples 'lists move service' do
expect(current_list_positions).to eq [0, 1, 2, 3]
end
- it 'keeps position of lists when new positon is equal to number of labels lists' do
+ it 'keeps position of lists when new position is equal to number of labels lists' do
service = described_class.new(parent, user, position: board.lists.label.size)
service.execute(planning)
@@ -38,7 +38,7 @@ shared_examples 'lists move service' do
expect(current_list_positions).to eq [0, 1, 2, 3]
end
- it 'keeps position of lists when new positon is greater than number of labels lists' do
+ it 'keeps position of lists when new position is greater than number of labels lists' do
service = described_class.new(parent, user, position: board.lists.label.size + 1)
service.execute(planning)
@@ -46,7 +46,7 @@ shared_examples 'lists move service' do
expect(current_list_positions).to eq [0, 1, 2, 3]
end
- it 'increments position of intermediate lists when new positon is equal to first position' do
+ it 'increments position of intermediate lists when new position is equal to first position' do
service = described_class.new(parent, user, position: 0)
service.execute(staging)
@@ -54,7 +54,7 @@ shared_examples 'lists move service' do
expect(current_list_positions).to eq [1, 2, 3, 0]
end
- it 'decrements position of intermediate lists when new positon is equal to last position' do
+ it 'decrements position of intermediate lists when new position is equal to last position' do
service = described_class.new(parent, user, position: board.lists.label.last.position)
service.execute(planning)
diff --git a/spec/tasks/gitlab/backup_rake_spec.rb b/spec/tasks/gitlab/backup_rake_spec.rb
index 3ba6caf1337..8c4360d4cf0 100644
--- a/spec/tasks/gitlab/backup_rake_spec.rb
+++ b/spec/tasks/gitlab/backup_rake_spec.rb
@@ -251,7 +251,7 @@ describe 'gitlab:app namespace rake task' do
allow(Gitlab.config.repositories).to receive(:storages).and_return(storages)
- # Avoid asking gitaly about the root ref (which will fail beacuse of the
+ # Avoid asking gitaly about the root ref (which will fail because of the
# mocked storages)
allow_any_instance_of(Repository).to receive(:empty?).and_return(false)
end
diff --git a/vendor/licenses.csv b/vendor/licenses.csv
index 9083fe076c3..5a7f7c0ebd1 100644
--- a/vendor/licenses.csv
+++ b/vendor/licenses.csv
@@ -67,7 +67,7 @@
@babel/template,7.1.2,MIT
@babel/traverse,7.1.0,MIT
@babel/types,7.1.2,MIT
-@gitlab-org/gitlab-svgs,1.31.0,MIT
+@gitlab/svgs,1.35.0,MIT
@gitlab-org/gitlab-ui,1.8.0,MIT
@sindresorhus/is,0.7.0,MIT
@types/jquery,2.0.48,MIT
diff --git a/yarn.lock b/yarn.lock
index 0124ee0572d..38e0f9d6201 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -621,11 +621,6 @@
resolved "https://registry.yarnpkg.com/@gitlab-org/gitlab-svgs/-/gitlab-svgs-1.32.0.tgz#a65ab7724fa7d55be8e5cc9b2dbe3f0757432fd3"
integrity sha512-L3o8dFUd2nSkVZBwh2hCJWzNzADJ3dTBZxamND8NLosZK9/ohNhccmsQOZGyMCUHaOzm4vifaaXkAXh04UtMKA==
-"@gitlab-org/gitlab-svgs@^1.33.0":
- version "1.33.0"
- resolved "https://registry.yarnpkg.com/@gitlab-org/gitlab-svgs/-/gitlab-svgs-1.33.0.tgz#068566e8ee00795f6f09f58236f08e1716f9f04a"
- integrity sha512-8ajtUHk6gQ1xosL/CO5IzHSFM/t18hx5pfzQ3cd0VuQXcyR6QKGuXTLwbYdmJDYOw1Etoo5DqDWxPEClHyZpiA==
-
"@gitlab-org/gitlab-ui@^1.10.0":
version "1.10.0"
resolved "https://registry.yarnpkg.com/@gitlab-org/gitlab-ui/-/gitlab-ui-1.10.0.tgz#3ac54ecaa25ea558324f0b382c97fcf9e3c4f0a5"
@@ -648,6 +643,11 @@
eslint-plugin-promise "^4.0.1"
eslint-plugin-vue "^5.0.0-beta.3"
+"@gitlab/svgs@^1.35.0":
+ version "1.35.0"
+ resolved "https://registry.yarnpkg.com/@gitlab/svgs/-/svgs-1.35.0.tgz#01b6a0948bb3897fbbac9f50ce23c559c514ea0e"
+ integrity sha512-XKrTniSYKG5U8+8ZqDJqoW8ORahuPBfHrfsC1dHBPvo1xA/QGJxlpUdeqSFw2O19h481ut4yW1dF+OFpIa/mrw==
+
"@sindresorhus/is@^0.7.0":
version "0.7.0"
resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-0.7.0.tgz#9a06f4f137ee84d7df0460c1fdb1135ffa6c50fd"