summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorjerasmus <jerasmus@gitlab.com>2018-10-15 12:42:24 +0200
committerjerasmus <jerasmus@gitlab.com>2018-10-15 12:42:24 +0200
commit58f29d5f855ea2e1411b99804a74710483a13f90 (patch)
tree0b06ecd3c0bb1bcf3d3c6660c2bc49eefaff08b2
parent0572da24c990fc01d88acfbd32728221e3e3a711 (diff)
parenta9827357186e38e5732d8dae23d9d02b1f4c7218 (diff)
downloadgitlab-ce-48746-fix-files-uploaded-in-base64.tar.gz
Merge branch 'master' into 48746-fix-files-uploaded-in-base6448746-fix-files-uploaded-in-base64
-rw-r--r--.rubocop.yml4
-rw-r--r--GITLAB_PAGES_VERSION2
-rw-r--r--GITLAB_SHELL_VERSION2
-rw-r--r--app/assets/javascripts/clusters/components/applications.vue2
-rw-r--r--app/assets/javascripts/diffs/components/diff_file_header.vue10
-rw-r--r--app/assets/javascripts/diffs/store/modules/diff_state.js4
-rw-r--r--app/assets/javascripts/jobs/components/job_app.vue12
-rw-r--r--app/assets/javascripts/jobs/components/job_log_controllers.vue2
-rw-r--r--app/assets/javascripts/jobs/components/sidebar.vue5
-rw-r--r--app/assets/javascripts/jobs/components/stages_dropdown.vue21
-rw-r--r--app/assets/javascripts/jobs/job_details_bundle.js8
-rw-r--r--app/assets/javascripts/jobs/store/actions.js11
-rw-r--r--app/assets/javascripts/jobs/store/getters.js4
-rw-r--r--app/assets/javascripts/jobs/store/mutations.js13
-rw-r--r--app/assets/javascripts/jobs/store/state.js4
-rw-r--r--app/assets/javascripts/notes/components/diff_with_note.vue1
-rw-r--r--app/assets/javascripts/performance_bar/components/performance_bar_app.vue15
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_merged.vue26
-rw-r--r--app/assets/stylesheets/framework/mixins.scss9
-rw-r--r--app/assets/stylesheets/framework/variables.scss1
-rw-r--r--app/assets/stylesheets/pages/diff.scss16
-rw-r--r--app/assets/stylesheets/pages/issuable.scss4
-rw-r--r--app/assets/stylesheets/performance_bar.scss4
-rw-r--r--app/finders/branches_finder.rb41
-rw-r--r--app/finders/projects_finder.rb27
-rw-r--r--app/helpers/preferences_helper.rb36
-rw-r--r--app/helpers/sorting_helper.rb14
-rw-r--r--app/models/deployment.rb11
-rw-r--r--app/models/environment.rb2
-rw-r--r--app/models/project.rb2
-rw-r--r--app/models/user.rb2
-rw-r--r--app/serializers/build_details_entity.rb1
-rw-r--r--app/views/admin/applications/show.html.haml2
-rw-r--r--app/views/devise/shared/_omniauth_box.html.haml2
-rw-r--r--app/views/doorkeeper/applications/show.html.haml2
-rw-r--r--app/views/groups/show.html.haml2
-rw-r--r--app/views/layouts/nav/_dashboard.html.haml1
-rw-r--r--app/views/layouts/nav/sidebar/_project.html.haml2
-rw-r--r--app/views/projects/jobs/show.html.haml4
-rw-r--r--app/views/projects/project_members/_new_project_member.html.haml4
-rw-r--r--app/views/projects/project_members/_team.html.haml2
-rw-r--r--changelogs/unreleased/22311-fix-duplicated-key-in-license-management-job.yml5
-rw-r--r--changelogs/unreleased/48684-sort-projects-by-stars-in-groups.yml4
-rw-r--r--changelogs/unreleased/48889-message-for-were-merged-into.yml5
-rw-r--r--changelogs/unreleased/50185-fix-broken-file-name-navigation.yml5
-rw-r--r--changelogs/unreleased/52361-fix-file-tree-mobile.yml5
-rw-r--r--changelogs/unreleased/52421-show-canary-no-canary-in-the-performance-bar.yml5
-rw-r--r--changelogs/unreleased/52472-pipeline-endpoint-json.yml5
-rw-r--r--changelogs/unreleased/52519-runners-link.yml5
-rw-r--r--changelogs/unreleased/52570-erased-block.yml5
-rw-r--r--changelogs/unreleased/52608-sidebar.yml5
-rw-r--r--changelogs/unreleased/52614-update-job-started-check.yml5
-rw-r--r--changelogs/unreleased/52618-incorrect-stage-being-shown-in-side-bar-of-job-view-api.yml5
-rw-r--r--changelogs/unreleased/enable-frozen-string-lib-gitlab.yml5
-rw-r--r--changelogs/unreleased/even-more-frozen-string-lib.yml5
-rw-r--r--changelogs/unreleased/feature-improved-branch-filter-sorting.yml6
-rw-r--r--changelogs/unreleased/fl-update-svgs.yml5
-rw-r--r--changelogs/unreleased/gt-update-application-copy-secret-to-clipboard-data.yml5
-rw-r--r--changelogs/unreleased/sh-fix-project-deletion-with-export.yml5
-rw-r--r--config/unicorn.rb.example4
-rw-r--r--config/unicorn.rb.example.development4
-rw-r--r--doc/administration/gitaly/index.md6
-rw-r--r--doc/administration/integration/plantuml.md4
-rw-r--r--doc/administration/job_artifacts.md3
-rw-r--r--doc/api/events.md4
-rw-r--r--doc/ci/examples/laravel_with_gitlab_and_envoy/index.md12
-rw-r--r--doc/ci/examples/php.md2
-rw-r--r--doc/ci/triggers/README.md7
-rw-r--r--doc/ci/yaml/README.md4
-rw-r--r--doc/development/README.md1
-rw-r--r--doc/development/documentation/index.md2
-rw-r--r--doc/development/feature_flags.md31
-rw-r--r--doc/install/installation.md2
-rw-r--r--doc/update/11.3-to-11.4.md4
-rw-r--r--doc/user/discussions/index.md2
-rw-r--r--doc/user/project/merge_requests/cherry_pick_changes.md8
-rw-r--r--doc/user/project/merge_requests/img/merge_request_diff_file_navigation.pngbin112288 -> 111544 bytes
-rw-r--r--doc/user/project/merge_requests/index.md6
-rw-r--r--doc/user/project/repository/branches/img/branch_filter_search_box.pngbin0 -> 83225 bytes
-rw-r--r--doc/user/project/repository/branches/index.md18
-rw-r--r--doc/user/project/repository/index.md5
-rw-r--r--lib/generators/rails/post_deployment_migration/post_deployment_migration_generator.rb2
-rw-r--r--lib/gitaly/server.rb2
-rw-r--r--lib/gitlab/auth/activity.rb2
-rw-r--r--lib/gitlab/auth/database/authentication.rb2
-rw-r--r--lib/gitlab/auth/ip_rate_limiter.rb2
-rw-r--r--lib/gitlab/auth/ldap/access.rb2
-rw-r--r--lib/gitlab/auth/ldap/adapter.rb2
-rw-r--r--lib/gitlab/auth/ldap/auth_hash.rb2
-rw-r--r--lib/gitlab/auth/ldap/authentication.rb2
-rw-r--r--lib/gitlab/auth/ldap/config.rb2
-rw-r--r--lib/gitlab/auth/ldap/dn.rb1
-rw-r--r--lib/gitlab/auth/ldap/ldap_connection_error.rb2
-rw-r--r--lib/gitlab/auth/ldap/person.rb2
-rw-r--r--lib/gitlab/auth/ldap/user.rb2
-rw-r--r--lib/gitlab/auth/o_auth/auth_hash.rb2
-rw-r--r--lib/gitlab/auth/o_auth/authentication.rb2
-rw-r--r--lib/gitlab/auth/o_auth/identity_linker.rb2
-rw-r--r--lib/gitlab/auth/o_auth/provider.rb2
-rw-r--r--lib/gitlab/auth/o_auth/session.rb2
-rw-r--r--lib/gitlab/auth/o_auth/user.rb2
-rw-r--r--lib/gitlab/auth/omniauth_identity_linker_base.rb2
-rw-r--r--lib/gitlab/auth/request_authenticator.rb2
-rw-r--r--lib/gitlab/auth/result.rb5
-rw-r--r--lib/gitlab/auth/saml/auth_hash.rb2
-rw-r--r--lib/gitlab/auth/saml/config.rb2
-rw-r--r--lib/gitlab/auth/saml/identity_linker.rb2
-rw-r--r--lib/gitlab/auth/saml/user.rb2
-rw-r--r--lib/gitlab/auth/too_many_ips.rb2
-rw-r--r--lib/gitlab/auth/unique_ips_limiter.rb2
-rw-r--r--lib/gitlab/auth/user_access_denied_reason.rb2
-rw-r--r--lib/gitlab/auth/user_auth_finders.rb2
-rw-r--r--lib/gitlab/badge/base.rb2
-rw-r--r--lib/gitlab/badge/coverage/metadata.rb2
-rw-r--r--lib/gitlab/badge/coverage/report.rb2
-rw-r--r--lib/gitlab/badge/coverage/template.rb2
-rw-r--r--lib/gitlab/badge/metadata.rb2
-rw-r--r--lib/gitlab/badge/pipeline/metadata.rb2
-rw-r--r--lib/gitlab/badge/pipeline/status.rb2
-rw-r--r--lib/gitlab/badge/pipeline/template.rb2
-rw-r--r--lib/gitlab/badge/template.rb2
-rw-r--r--lib/gitlab/bare_repository_import/importer.rb7
-rw-r--r--lib/gitlab/bare_repository_import/repository.rb7
-rw-r--r--lib/gitlab/bitbucket_import/importer.rb2
-rw-r--r--lib/gitlab/bitbucket_import/project_creator.rb2
-rw-r--r--lib/gitlab/bitbucket_server_import/project_creator.rb2
-rw-r--r--lib/gitlab/cache/ci/project_pipeline_status.rb2
-rw-r--r--lib/gitlab/cache/request_cache.rb2
-rw-r--r--lib/gitlab/checks/change_access.rb2
-rw-r--r--lib/gitlab/checks/commit_check.rb2
-rw-r--r--lib/gitlab/checks/force_push.rb2
-rw-r--r--lib/gitlab/checks/lfs_integrity.rb2
-rw-r--r--lib/gitlab/checks/matching_merge_request.rb2
-rw-r--r--lib/gitlab/checks/post_push_message.rb2
-rw-r--r--lib/gitlab/checks/project_created.rb2
-rw-r--r--lib/gitlab/checks/project_moved.rb2
-rw-r--r--lib/gitlab/ci/templates/Android.gitlab-ci.yml40
-rw-r--r--lib/gitlab/ci/templates/Auto-DevOps.gitlab-ci.yml4
-rw-r--r--lib/gitlab/gon_helper.rb15
-rw-r--r--lib/google_api/auth.rb2
-rw-r--r--lib/google_api/cloud_platform/client.rb2
-rw-r--r--lib/haml_lint/inline_javascript.rb5
-rw-r--r--lib/json_web_token/rsa_token.rb2
-rw-r--r--lib/json_web_token/token.rb2
-rw-r--r--lib/mattermost/client.rb2
-rw-r--r--lib/mattermost/command.rb2
-rw-r--r--lib/mattermost/error.rb2
-rw-r--r--lib/mattermost/session.rb2
-rw-r--r--lib/mattermost/team.rb2
-rw-r--r--lib/microsoft_teams/activity.rb2
-rw-r--r--lib/microsoft_teams/notifier.rb2
-rw-r--r--lib/object_storage/direct_upload.rb2
-rw-r--r--lib/omni_auth/strategies/bitbucket.rb2
-rw-r--r--lib/omni_auth/strategies/jwt.rb2
-rw-r--r--lib/peek/rblineprof/custom_controller_helpers.rb6
-rw-r--r--lib/peek/views/gitaly.rb2
-rw-r--r--lib/peek/views/host.rb7
-rw-r--r--lib/rouge/formatters/html_gitlab.rb2
-rw-r--r--lib/rouge/plugins/common_mark.rb2
-rw-r--r--lib/rspec_flaky/config.rb2
-rw-r--r--lib/rspec_flaky/example.rb2
-rw-r--r--lib/rspec_flaky/flaky_example.rb2
-rw-r--r--lib/rspec_flaky/flaky_examples_collection.rb2
-rw-r--r--lib/rspec_flaky/listener.rb2
-rw-r--r--lib/rspec_flaky/report.rb2
-rw-r--r--lib/system_check/app/active_users_check.rb2
-rw-r--r--lib/system_check/app/database_config_exists_check.rb2
-rw-r--r--lib/system_check/app/git_config_check.rb2
-rw-r--r--lib/system_check/app/git_user_default_ssh_config_check.rb2
-rw-r--r--lib/system_check/app/git_version_check.rb2
-rw-r--r--lib/system_check/app/gitlab_config_exists_check.rb2
-rw-r--r--lib/system_check/app/gitlab_config_up_to_date_check.rb2
-rw-r--r--lib/system_check/app/init_script_exists_check.rb2
-rw-r--r--lib/system_check/app/init_script_up_to_date_check.rb2
-rw-r--r--lib/system_check/app/log_writable_check.rb2
-rw-r--r--lib/system_check/app/migrations_are_up_check.rb2
-rw-r--r--lib/system_check/app/orphaned_group_members_check.rb2
-rw-r--r--lib/system_check/app/projects_have_namespace_check.rb2
-rw-r--r--lib/system_check/app/redis_version_check.rb2
-rw-r--r--lib/system_check/app/ruby_version_check.rb2
-rw-r--r--lib/system_check/app/tmp_writable_check.rb2
-rw-r--r--lib/system_check/app/uploads_directory_exists_check.rb2
-rw-r--r--lib/system_check/app/uploads_path_permission_check.rb2
-rw-r--r--lib/system_check/app/uploads_path_tmp_permission_check.rb2
-rw-r--r--lib/system_check/base_check.rb2
-rw-r--r--lib/system_check/helpers.rb2
-rw-r--r--lib/system_check/incoming_email/foreman_configured_check.rb2
-rw-r--r--lib/system_check/incoming_email/imap_authentication_check.rb2
-rw-r--r--lib/system_check/incoming_email/initd_configured_check.rb2
-rw-r--r--lib/system_check/incoming_email/mail_room_running_check.rb2
-rw-r--r--lib/system_check/orphans/namespace_check.rb2
-rw-r--r--lib/system_check/orphans/repository_check.rb2
-rw-r--r--lib/system_check/simple_executor.rb2
-rw-r--r--locale/gitlab.pot3
-rw-r--r--package.json2
-rw-r--r--qa/qa.rb15
-rw-r--r--qa/qa/page/component/users_select.rb14
-rw-r--r--qa/qa/page/main/login.rb9
-rw-r--r--qa/qa/page/project/menu.rb9
-rw-r--r--qa/qa/page/project/settings/members.rb27
-rw-r--r--qa/qa/runtime/browser.rb4
-rw-r--r--qa/qa/runtime/env.rb4
-rw-r--r--qa/qa/scenario/test/integration/instance_saml.rb13
-rw-r--r--qa/qa/specs/features/browser_ui/1_manage/login/login_via_instance_wide_saml_sso_spec.rb17
-rw-r--r--qa/qa/specs/features/browser_ui/1_manage/project/add_project_member_spec.rb27
-rw-r--r--qa/qa/vendor/saml_idp/page/base.rb14
-rw-r--r--qa/qa/vendor/saml_idp/page/login.rb19
-rw-r--r--qa/spec/scenario/test/integration/instance_saml_spec.rb9
-rw-r--r--spec/controllers/projects/jobs_controller_spec.rb4
-rw-r--r--spec/features/groups/show_spec.rb21
-rw-r--r--spec/features/projects/jobs_spec.rb50
-rw-r--r--spec/finders/branches_finder_spec.rb33
-rw-r--r--spec/fixtures/api/schemas/job/job_details.json6
-rw-r--r--spec/helpers/preferences_helper_spec.rb7
-rw-r--r--spec/javascripts/.eslintrc.yml2
-rw-r--r--spec/javascripts/awards_handler_spec.js16
-rw-r--r--spec/javascripts/behaviors/quick_submit_spec.js2
-rw-r--r--spec/javascripts/bootstrap_jquery_spec.js2
-rw-r--r--spec/javascripts/datetime_utility_spec.js6
-rw-r--r--spec/javascripts/diffs/components/diff_file_header_spec.js15
-rw-r--r--spec/javascripts/diffs/components/diff_file_spec.js8
-rw-r--r--spec/javascripts/diffs/components/diff_line_gutter_content_spec.js2
-rw-r--r--spec/javascripts/diffs/components/inline_diff_view_spec.js4
-rw-r--r--spec/javascripts/emoji_spec.js38
-rw-r--r--spec/javascripts/filtered_search/dropdown_utils_spec.js2
-rw-r--r--spec/javascripts/filtered_search/filtered_search_token_keys_spec.js14
-rw-r--r--spec/javascripts/gl_dropdown_spec.js4
-rw-r--r--spec/javascripts/graphs/stat_graph_contributors_util_spec.js2
-rw-r--r--spec/javascripts/groups/components/group_item_spec.js2
-rw-r--r--spec/javascripts/groups/components/groups_spec.js2
-rw-r--r--spec/javascripts/groups/components/item_stats_spec.js10
-rw-r--r--spec/javascripts/groups/components/item_stats_value_spec.js4
-rw-r--r--spec/javascripts/groups/store/groups_store_spec.js12
-rw-r--r--spec/javascripts/image_diff/helpers/comment_indicator_helper_spec.js2
-rw-r--r--spec/javascripts/jobs/components/job_app_spec.js9
-rw-r--r--spec/javascripts/jobs/components/job_log_controllers_spec.js1
-rw-r--r--spec/javascripts/jobs/components/sidebar_spec.js4
-rw-r--r--spec/javascripts/jobs/components/stages_dropdown_spec.js20
-rw-r--r--spec/javascripts/jobs/store/actions_spec.js17
-rw-r--r--spec/javascripts/jobs/store/getters_spec.js10
-rw-r--r--spec/javascripts/jobs/store/mutations_spec.js27
-rw-r--r--spec/javascripts/lib/utils/common_utils_spec.js3
-rw-r--r--spec/javascripts/line_highlighter_spec.js18
-rw-r--r--spec/javascripts/new_branch_spec.js24
-rw-r--r--spec/javascripts/notes/components/note_form_spec.js1
-rw-r--r--spec/javascripts/notes_spec.js6
-rw-r--r--spec/javascripts/reports/components/report_section_spec.js1
-rw-r--r--spec/javascripts/right_sidebar_spec.js2
-rw-r--r--spec/javascripts/search_autocomplete_spec.js6
-rw-r--r--spec/javascripts/syntax_highlight_spec.js1
-rw-r--r--spec/javascripts/vue_mr_widget/components/mr_widget_memory_usage_spec.js2
-rw-r--r--spec/javascripts/vue_mr_widget/components/states/mr_widget_merged_spec.js10
-rw-r--r--spec/javascripts/vue_shared/components/memory_graph_spec.js4
-rw-r--r--spec/lib/gitlab/ci/status/pipeline/factory_spec.rb68
-rw-r--r--spec/lib/gitlab/gon_helper_spec.rb32
-rw-r--r--spec/models/deployment_spec.rb23
-rw-r--r--spec/services/projects/destroy_service_spec.rb23
-rw-r--r--yarn.lock8
258 files changed, 1343 insertions, 290 deletions
diff --git a/.rubocop.yml b/.rubocop.yml
index 242e7615211..0f4018326a1 100644
--- a/.rubocop.yml
+++ b/.rubocop.yml
@@ -50,7 +50,8 @@ Style/FrozenStringLiteralComment:
- 'danger/**/*'
- 'db/**/*'
- 'ee/**/*'
- - 'lib/**/*'
+ - 'lib/gitlab/**/*'
+ - 'lib/tasks/**/*'
- 'qa/**/*'
- 'rubocop/**/*'
- 'scripts/**/*'
@@ -84,6 +85,7 @@ Naming/FileName:
- EE
- JSON
- LDAP
+ - SAML
- IO
- HMAC
- QA
diff --git a/GITLAB_PAGES_VERSION b/GITLAB_PAGES_VERSION
index 9084fa2f716..6085e946503 100644
--- a/GITLAB_PAGES_VERSION
+++ b/GITLAB_PAGES_VERSION
@@ -1 +1 @@
-1.1.0
+1.2.1
diff --git a/GITLAB_SHELL_VERSION b/GITLAB_SHELL_VERSION
index d127a0ff9f1..9da0a092a0d 100644
--- a/GITLAB_SHELL_VERSION
+++ b/GITLAB_SHELL_VERSION
@@ -1 +1 @@
-8.3.3
+8.4.0 \ No newline at end of file
diff --git a/app/assets/javascripts/clusters/components/applications.vue b/app/assets/javascripts/clusters/components/applications.vue
index a1069985178..6e7b5eb5526 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/illustrations/kubernetes-installation.svg';
+import helmInstallIllustration from '@gitlab-org/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/diffs/components/diff_file_header.vue b/app/assets/javascripts/diffs/components/diff_file_header.vue
index 15b37243030..dcf1057eb84 100644
--- a/app/assets/javascripts/diffs/components/diff_file_header.vue
+++ b/app/assets/javascripts/diffs/components/diff_file_header.vue
@@ -20,6 +20,11 @@ export default {
Tooltip,
},
props: {
+ discussionPath: {
+ type: String,
+ required: false,
+ default: '',
+ },
diffFile: {
type: Object,
required: true,
@@ -65,8 +70,7 @@ export default {
if (this.diffFile.submodule) {
return this.diffFile.submoduleTreeUrl || this.diffFile.submoduleLink;
}
-
- return `#${this.diffFile.fileHash}`;
+ return this.discussionPath;
},
filePath() {
if (this.diffFile.submodule) {
@@ -152,7 +156,7 @@ export default {
v-once
ref="titleWrapper"
:href="titleLink"
- class="append-right-4"
+ class="append-right-4 js-title-wrapper"
>
<file-icon
:file-name="filePath"
diff --git a/app/assets/javascripts/diffs/store/modules/diff_state.js b/app/assets/javascripts/diffs/store/modules/diff_state.js
index ae8930c8968..1c5c35071de 100644
--- a/app/assets/javascripts/diffs/store/modules/diff_state.js
+++ b/app/assets/javascripts/diffs/store/modules/diff_state.js
@@ -1,5 +1,6 @@
import Cookies from 'js-cookie';
import { getParameterValues } from '~/lib/utils/url_utility';
+import bp from '~/breakpoints';
import { INLINE_DIFF_VIEW_TYPE, DIFF_VIEW_COOKIE_NAME, MR_TREE_SHOW_KEY } from '../../constants';
const viewTypeFromQueryString = getParameterValues('view')[0];
@@ -20,6 +21,7 @@ export default () => ({
diffViewType: viewTypeFromQueryString || viewTypeFromCookie || defaultViewType,
tree: [],
treeEntries: {},
- showTreeList: storedTreeShow === null ? true : storedTreeShow === 'true',
+ showTreeList:
+ storedTreeShow === null ? bp.getBreakpointSize() !== 'xs' : storedTreeShow === 'true',
currentDiffFileId: '',
});
diff --git a/app/assets/javascripts/jobs/components/job_app.vue b/app/assets/javascripts/jobs/components/job_app.vue
index 047e55866ce..4e8d3ad24cc 100644
--- a/app/assets/javascripts/jobs/components/job_app.vue
+++ b/app/assets/javascripts/jobs/components/job_app.vue
@@ -18,7 +18,7 @@
StuckBlock,
},
props: {
- runnerHelpUrl: {
+ runnerSettingsUrl: {
type: String,
required: false,
default: null,
@@ -30,7 +30,7 @@
'headerActions',
'headerTime',
'shouldRenderCalloutMessage',
- 'jobHasStarted',
+ 'shouldRenderTriggeredLabel',
'hasEnvironment',
'isJobStuck',
'hasTrace',
@@ -58,7 +58,7 @@
:user="job.user"
:actions="headerActions"
:has-sidebar-button="true"
- :should-render-triggered-label="jobHasStarted"
+ :should-render-triggered-label="shouldRenderTriggeredLabel"
:item-name="__('Job')"
/>
</div>
@@ -76,7 +76,7 @@
class="js-job-stuck"
:has-no-runners-for-project="job.runners.available"
:tags="job.tags"
- :runners-path="runnerHelpUrl"
+ :runners-path="runnerSettingsUrl"
/>
<environments-block
@@ -87,8 +87,8 @@
/>
<erased-block
- v-if="job.erased"
- class="js-job-erased"
+ v-if="job.erased_at"
+ class="js-job-erased-block"
:user="job.erased_by"
:erased-at="job.erased_at"
/>
diff --git a/app/assets/javascripts/jobs/components/job_log_controllers.vue b/app/assets/javascripts/jobs/components/job_log_controllers.vue
index 35d40c6898e..cc885ea8e1b 100644
--- a/app/assets/javascripts/jobs/components/job_log_controllers.vue
+++ b/app/assets/javascripts/jobs/components/job_log_controllers.vue
@@ -65,7 +65,7 @@ export default {
};
</script>
<template>
- <div class="top-bar">
+ <div class="top-bar affix js-top-bar">
<!-- truncate information -->
<div class="js-truncated-info truncated-info d-none d-sm-block float-left">
<template v-if="isTraceSizeVisible">
diff --git a/app/assets/javascripts/jobs/components/sidebar.vue b/app/assets/javascripts/jobs/components/sidebar.vue
index 7f0f301d72a..8f3c6aced23 100644
--- a/app/assets/javascripts/jobs/components/sidebar.vue
+++ b/app/assets/javascripts/jobs/components/sidebar.vue
@@ -36,7 +36,7 @@ export default {
},
},
computed: {
- ...mapState(['job', 'isLoading', 'stages', 'jobs']),
+ ...mapState(['job', 'isLoading', 'stages', 'jobs', 'selectedStage']),
coverage() {
return `${this.job.coverage}%`;
},
@@ -110,7 +110,7 @@ export default {
</script>
<template>
<aside
- class="right-sidebar right-sidebar-expanded build-sidebar"
+ class="js-build-sidebar right-sidebar right-sidebar-expanded build-sidebar"
data-offset-top="101"
data-spy="affix"
>
@@ -276,6 +276,7 @@ export default {
<stages-dropdown
:stages="stages"
:pipeline="job.pipeline"
+ :selected-stage="selectedStage"
@requestSidebarStageDropdown="fetchJobsForStage"
/>
diff --git a/app/assets/javascripts/jobs/components/stages_dropdown.vue b/app/assets/javascripts/jobs/components/stages_dropdown.vue
index 34d47b3a3bb..e5e1d56e287 100644
--- a/app/assets/javascripts/jobs/components/stages_dropdown.vue
+++ b/app/assets/javascripts/jobs/components/stages_dropdown.vue
@@ -2,7 +2,6 @@
import _ from 'underscore';
import CiIcon from '~/vue_shared/components/ci_icon.vue';
import Icon from '~/vue_shared/components/icon.vue';
-import { __ } from '~/locale';
export default {
components: {
@@ -18,30 +17,20 @@ export default {
type: Array,
required: true,
},
+ selectedStage: {
+ type: String,
+ required: true,
+ },
},
- data() {
- return {
- selectedStage: this.stages.length > 0 ? this.stages[0].name : __('More'),
- };
- },
+
computed: {
hasRef() {
return !_.isEmpty(this.pipeline.ref);
},
},
- watch: {
- // When the component is initially mounted it may start with an empty stages array.
- // Once the prop is updated, we set the first stage as the selected one
- stages(newVal) {
- if (newVal.length) {
- this.selectedStage = newVal[0].name;
- }
- },
- },
methods: {
onStageClick(stage) {
this.$emit('requestSidebarStageDropdown', stage);
- this.selectedStage = stage.name;
},
},
};
diff --git a/app/assets/javascripts/jobs/job_details_bundle.js b/app/assets/javascripts/jobs/job_details_bundle.js
index 3eb75e72506..15cd79b1c50 100644
--- a/app/assets/javascripts/jobs/job_details_bundle.js
+++ b/app/assets/javascripts/jobs/job_details_bundle.js
@@ -9,8 +9,7 @@ import createStore from './store';
export default () => {
const { dataset } = document.getElementById('js-job-details-vue');
- // eslint-disable-next-line no-new
- new Job();
+
const store = createStore();
store.dispatch('setJobEndpoint', dataset.endpoint);
@@ -33,7 +32,7 @@ export default () => {
props: {
isLoading: this.isLoading,
job: this.job,
- runnerHelpUrl: dataset.runnerHelpUrl,
+ runnerSettingsUrl: dataset.runnerSettingsUrl,
},
});
},
@@ -71,4 +70,7 @@ export default () => {
});
},
});
+
+ // eslint-disable-next-line no-new
+ new Job();
};
diff --git a/app/assets/javascripts/jobs/store/actions.js b/app/assets/javascripts/jobs/store/actions.js
index 298367c9342..d0040161dc3 100644
--- a/app/assets/javascripts/jobs/store/actions.js
+++ b/app/assets/javascripts/jobs/store/actions.js
@@ -139,10 +139,12 @@ export const fetchStages = ({ state, dispatch }) => {
dispatch('requestStages');
axios
- .get(state.job.pipeline.path)
+ .get(`${state.job.pipeline.path}.json`)
.then(({ data }) => {
+ // Set selected stage
dispatch('receiveStagesSuccess', data.details.stages);
- dispatch('fetchJobsForStage', data.details.stages[0]);
+ const selectedStage = data.details.stages.find(stage => stage.name === state.selectedStage);
+ dispatch('fetchJobsForStage', selectedStage);
})
.catch(() => dispatch('receiveStagesError'));
};
@@ -156,11 +158,12 @@ export const receiveStagesError = ({ commit }) => {
/**
* Jobs list on sidebar - depend on stages dropdown
*/
-export const requestJobsForStage = ({ commit }) => commit(types.REQUEST_JOBS_FOR_STAGE);
+export const requestJobsForStage = ({ commit }, stage) =>
+ commit(types.REQUEST_JOBS_FOR_STAGE, stage);
// On stage click, set selected stage + fetch job
export const fetchJobsForStage = ({ dispatch }, stage) => {
- dispatch('requestJobsForStage');
+ dispatch('requestJobsForStage', stage);
axios
.get(stage.dropdown_path, {
diff --git a/app/assets/javascripts/jobs/store/getters.js b/app/assets/javascripts/jobs/store/getters.js
index afe5f88b292..9f4f372e3d2 100644
--- a/app/assets/javascripts/jobs/store/getters.js
+++ b/app/assets/javascripts/jobs/store/getters.js
@@ -22,10 +22,10 @@ export const shouldRenderCalloutMessage = state =>
!_.isEmpty(state.job.status) && !_.isEmpty(state.job.callout_message);
/**
- * When job has not started the key will be `false`
+ * When job has not started the key will be null
* When job started the key will be a string with a date.
*/
-export const jobHasStarted = state => !(state.job.started === false);
+export const shouldRenderTriggeredLabel = state => _.isString(state.job.started);
export const hasEnvironment = state => !_.isEmpty(state.job.deployment_status);
diff --git a/app/assets/javascripts/jobs/store/mutations.js b/app/assets/javascripts/jobs/store/mutations.js
index c3f2359fa4d..f00e06e1a6c 100644
--- a/app/assets/javascripts/jobs/store/mutations.js
+++ b/app/assets/javascripts/jobs/store/mutations.js
@@ -53,6 +53,16 @@ export default {
state.isLoading = false;
state.hasError = false;
state.job = job;
+
+ /**
+ * We only update it on the first request
+ * The dropdown can be changed by the user
+ * after the first request,
+ * and we do not want to hijack that
+ */
+ if (state.selectedStage === 'More' && job.stage) {
+ state.selectedStage = job.stage;
+ }
},
[types.RECEIVE_JOB_ERROR](state) {
state.isLoading = false;
@@ -81,8 +91,9 @@ export default {
state.stages = [];
},
- [types.REQUEST_JOBS_FOR_STAGE](state) {
+ [types.REQUEST_JOBS_FOR_STAGE](state, stage) {
state.isLoadingJobs = true;
+ state.selectedStage = stage.name;
},
[types.RECEIVE_JOBS_FOR_STAGE_SUCCESS](state, jobs) {
state.isLoadingJobs = false;
diff --git a/app/assets/javascripts/jobs/store/state.js b/app/assets/javascripts/jobs/store/state.js
index 509cb69a5d3..afbc959bb71 100644
--- a/app/assets/javascripts/jobs/store/state.js
+++ b/app/assets/javascripts/jobs/store/state.js
@@ -1,3 +1,5 @@
+import { __ } from '~/locale';
+
export default () => ({
jobEndpoint: null,
traceEndpoint: null,
@@ -34,7 +36,7 @@ export default () => ({
// sidebar dropdown
isLoadingStages: false,
isLoadingJobs: false,
- selectedStage: null,
+ selectedStage: __('More'),
stages: [],
jobs: [],
});
diff --git a/app/assets/javascripts/notes/components/diff_with_note.vue b/app/assets/javascripts/notes/components/diff_with_note.vue
index 353aa790743..d9e99603238 100644
--- a/app/assets/javascripts/notes/components/diff_with_note.vue
+++ b/app/assets/javascripts/notes/components/diff_with_note.vue
@@ -94,6 +94,7 @@ export default {
class="diff-file file-holder"
>
<diff-file-header
+ :discussion-path="discussion.discussionPath"
:diff-file="diffFile"
:can-current-user-fork="false"
:discussions-expanded="isDiscussionsExpanded"
diff --git a/app/assets/javascripts/performance_bar/components/performance_bar_app.vue b/app/assets/javascripts/performance_bar/components/performance_bar_app.vue
index 1522e2227e4..300d453c174 100644
--- a/app/assets/javascripts/performance_bar/components/performance_bar_app.vue
+++ b/app/assets/javascripts/performance_bar/components/performance_bar_app.vue
@@ -1,5 +1,6 @@
<script>
import $ from 'jquery';
+import { glEmojiTag } from '~/emoji';
import detailedMetric from './detailed_metric.vue';
import requestSelector from './request_selector.vue';
@@ -64,6 +65,16 @@ export default {
lineProfileModal() {
return $('#modal-peek-line-profile');
},
+ hasHost() {
+ return this.currentRequest && this.currentRequest.details && this.currentRequest.details.host;
+ },
+ birdEmoji() {
+ if (this.hasHost && this.currentRequest.details.host.canary) {
+ return glEmojiTag('baby_chick');
+ }
+
+ return '';
+ },
},
mounted() {
this.currentRequest = this.requestId;
@@ -93,9 +104,11 @@ export default {
class="view"
>
<span
- v-if="currentRequest.details"
+ v-if="hasHost"
class="current-host"
+ :class="{ 'canary' : currentRequest.details.host.canary }"
>
+ <span v-html="birdEmoji"></span>
{{ currentRequest.details.host.hostname }}
</span>
</div>
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_merged.vue b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_merged.vue
index 8184ef33022..c19b67f00fa 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_merged.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_merged.vue
@@ -162,18 +162,20 @@
<span class="label-branch">
<a :href="mr.targetBranchPath">{{ mr.targetBranch }}</a>
</span>
- with
- <a
- :href="mr.mergeCommitPath"
- class="commit-sha js-mr-merged-commit-sha"
- v-text="mr.shortMergeCommitSha"
- >
- </a>
- <clipboard-button
- :title="__('Copy commit SHA to clipboard')"
- :text="mr.mergeCommitSha"
- css-class="btn-default btn-transparent btn-clipboard js-mr-merged-copy-sha"
- />
+ <template v-if="mr.mergeCommitSha">
+ with
+ <a
+ :href="mr.mergeCommitPath"
+ class="commit-sha js-mr-merged-commit-sha"
+ v-text="mr.shortMergeCommitSha"
+ >
+ </a>
+ <clipboard-button
+ :title="__('Copy commit SHA to clipboard')"
+ :text="mr.mergeCommitSha"
+ css-class="btn-default btn-transparent btn-clipboard js-mr-merged-copy-sha"
+ />
+ </template>
</p>
<p v-if="mr.sourceBranchRemoved">
{{ s__("mrWidget|The source branch has been removed") }}
diff --git a/app/assets/stylesheets/framework/mixins.scss b/app/assets/stylesheets/framework/mixins.scss
index be41dbfc61f..1c84baf68ed 100644
--- a/app/assets/stylesheets/framework/mixins.scss
+++ b/app/assets/stylesheets/framework/mixins.scss
@@ -12,6 +12,15 @@
max-width: $max-width;
}
+/**
+ * Mixin for fixed width container
+ */
+@mixin fixed-width-container {
+ max-width: $limited-layout-width - ($gl-padding * 2);
+ margin-left: auto;
+ margin-right: auto;
+}
+
/*
* Mixin for markdown tables
*/
diff --git a/app/assets/stylesheets/framework/variables.scss b/app/assets/stylesheets/framework/variables.scss
index b7a95f604b8..0fde6e18cc7 100644
--- a/app/assets/stylesheets/framework/variables.scss
+++ b/app/assets/stylesheets/framework/variables.scss
@@ -605,6 +605,7 @@ $perf-bar-development: #4c1210;
$perf-bar-bucket-bg: #111;
$perf-bar-bucket-box-shadow-from: rgba($white-light, 0.2);
$perf-bar-bucket-box-shadow-to: rgba($black, 0.25);
+$perf-bar-canary-text: $orange-400;
/*
Issuable warning
diff --git a/app/assets/stylesheets/pages/diff.scss b/app/assets/stylesheets/pages/diff.scss
index 17b02c6e31e..cba5324ce53 100644
--- a/app/assets/stylesheets/pages/diff.scss
+++ b/app/assets/stylesheets/pages/diff.scss
@@ -1046,3 +1046,19 @@
left: auto;
line-height: 0;
}
+
+@media (max-width: map-get($grid-breakpoints, md)-1) {
+ .diffs .files {
+ @include fixed-width-container;
+ flex-direction: column;
+
+ .diff-tree-list {
+ width: 100%;
+ }
+
+ .tree-list-holder {
+ max-height: calc(50px + 50vh);
+ padding-right: 0;
+ }
+ }
+}
diff --git a/app/assets/stylesheets/pages/issuable.scss b/app/assets/stylesheets/pages/issuable.scss
index 62a9f97caa9..00b06aea898 100644
--- a/app/assets/stylesheets/pages/issuable.scss
+++ b/app/assets/stylesheets/pages/issuable.scss
@@ -1,8 +1,6 @@
// Limit MR description for side-by-side diff view
.fixed-width-container {
- max-width: $limited-layout-width - ($gl-padding * 2);
- margin-left: auto;
- margin-right: auto;
+ @include fixed-width-container;
}
.issuable-warning-icon {
diff --git a/app/assets/stylesheets/performance_bar.scss b/app/assets/stylesheets/performance_bar.scss
index 59fdbf31fe9..9c01a2f8bda 100644
--- a/app/assets/stylesheets/performance_bar.scss
+++ b/app/assets/stylesheets/performance_bar.scss
@@ -68,6 +68,10 @@
}
}
+ .current-host.canary {
+ color: $perf-bar-canary-text;
+ }
+
strong {
color: $white-light;
}
diff --git a/app/finders/branches_finder.rb b/app/finders/branches_finder.rb
index 970efa79dfb..45d5591e81b 100644
--- a/app/finders/branches_finder.rb
+++ b/app/finders/branches_finder.rb
@@ -7,8 +7,9 @@ class BranchesFinder
end
def execute
- branches = @repository.branches_sorted_by(sort)
- filter_by_name(branches)
+ branches = repository.branches_sorted_by(sort)
+ branches = by_search(branches)
+ branches
end
private
@@ -23,11 +24,39 @@ class BranchesFinder
@params[:sort].presence || 'name'
end
- def filter_by_name(branches)
- if search
- branches.select { |branch| branch.name.upcase.include?(search.upcase) }
+ def by_search(branches)
+ return branches unless search
+
+ case search
+ when ->(v) { v.starts_with?('^') }
+ filter_branches_with_prefix(branches, search.slice(1..-1).upcase)
+ when ->(v) { v.ends_with?('$') }
+ filter_branches_with_suffix(branches, search.chop.upcase)
else
- branches
+ matches = filter_branches_by_name(branches, search.upcase)
+ set_exact_match_as_first_result(matches, search)
end
end
+
+ def filter_branches_with_prefix(branches, prefix)
+ branches.select { |branch| branch.name.upcase.starts_with?(prefix) }
+ end
+
+ def filter_branches_with_suffix(branches, suffix)
+ branches.select { |branch| branch.name.upcase.ends_with?(suffix) }
+ end
+
+ def filter_branches_by_name(branches, term)
+ branches.select { |branch| branch.name.upcase.include?(term) }
+ end
+
+ def set_exact_match_as_first_result(matches, term)
+ exact_match_index = find_exact_match_index(matches, term)
+ matches.insert(0, matches.delete_at(exact_match_index)) if exact_match_index
+ matches
+ end
+
+ def find_exact_match_index(matches, term)
+ matches.index { |branch| branch.name.casecmp(term) == 0 }
+ end
end
diff --git a/app/finders/projects_finder.rb b/app/finders/projects_finder.rb
index c2404412006..6ececcd4152 100644
--- a/app/finders/projects_finder.rb
+++ b/app/finders/projects_finder.rb
@@ -42,17 +42,7 @@ class ProjectsFinder < UnionFinder
init_collection
end
- collection = by_ids(collection)
- collection = by_personal(collection)
- collection = by_starred(collection)
- collection = by_trending(collection)
- collection = by_visibilty_level(collection)
- collection = by_tags(collection)
- collection = by_search(collection)
- collection = by_archived(collection)
- collection = by_custom_attributes(collection)
- collection = by_deleted_status(collection)
-
+ collection = filter_projects(collection)
sort(collection)
end
@@ -66,6 +56,21 @@ class ProjectsFinder < UnionFinder
end
end
+ # EE would override this to add more filters
+ def filter_projects(collection)
+ collection = by_ids(collection)
+ collection = by_personal(collection)
+ collection = by_starred(collection)
+ collection = by_trending(collection)
+ collection = by_visibilty_level(collection)
+ collection = by_tags(collection)
+ collection = by_search(collection)
+ collection = by_archived(collection)
+ collection = by_custom_attributes(collection)
+ collection = by_deleted_status(collection)
+ collection
+ end
+
# rubocop: disable CodeReuse/ActiveRecord
def collection_with_user
if owned_projects?
diff --git a/app/helpers/preferences_helper.rb b/app/helpers/preferences_helper.rb
index ff9842d4cd9..f4f46b0fe96 100644
--- a/app/helpers/preferences_helper.rb
+++ b/app/helpers/preferences_helper.rb
@@ -18,22 +18,20 @@ module PreferencesHelper
groups: _("Your Groups"),
todos: _("Your Todos"),
issues: _("Assigned Issues"),
- merge_requests: _("Assigned Merge Requests")
+ merge_requests: _("Assigned Merge Requests"),
+ operations: _("Operations Dashboard")
}.with_indifferent_access.freeze
# Returns an Array usable by a select field for more user-friendly option text
def dashboard_choices
- defined = User.dashboards
+ dashboards = User.dashboards.keys
- if defined.size != DASHBOARD_CHOICES.size
- # Ensure that anyone adding new options updates this method too
- raise "`User` defines #{defined.size} dashboard choices," \
- " but `DASHBOARD_CHOICES` defined #{DASHBOARD_CHOICES.size}."
- else
- defined.map do |key, _|
- # Use `fetch` so `KeyError` gets raised when a key is missing
- [DASHBOARD_CHOICES.fetch(key), key]
- end
+ validate_dashboard_choices!(dashboards)
+ dashboards -= excluded_dashboard_choices
+
+ dashboards.map do |key|
+ # Use `fetch` so `KeyError` gets raised when a key is missing
+ [DASHBOARD_CHOICES.fetch(key), key]
end
end
@@ -52,4 +50,20 @@ module PreferencesHelper
def user_color_scheme
Gitlab::ColorSchemes.for_user(current_user).css_class
end
+
+ private
+
+ # Ensure that anyone adding new options updates `DASHBOARD_CHOICES` too
+ def validate_dashboard_choices!(user_dashboards)
+ if user_dashboards.size != DASHBOARD_CHOICES.size
+ raise "`User` defines #{user_dashboards.size} dashboard choices," \
+ " but `DASHBOARD_CHOICES` defined #{DASHBOARD_CHOICES.size}."
+ end
+ end
+
+ # List of dashboard choice to be excluded from CE.
+ # EE would override this.
+ def excluded_dashboard_choices
+ ['operations']
+ end
end
diff --git a/app/helpers/sorting_helper.rb b/app/helpers/sorting_helper.rb
index 53bd43d4861..8ed2a2ec9f4 100644
--- a/app/helpers/sorting_helper.rb
+++ b/app/helpers/sorting_helper.rb
@@ -48,15 +48,21 @@ module SortingHelper
def groups_sort_options_hash
{
- sort_value_name => sort_title_name,
- sort_value_name_desc => sort_title_name_desc,
+ sort_value_name => sort_title_name,
+ sort_value_name_desc => sort_title_name_desc,
sort_value_recently_created => sort_title_recently_created,
- sort_value_oldest_created => sort_title_oldest_created,
+ sort_value_oldest_created => sort_title_oldest_created,
sort_value_recently_updated => sort_title_recently_updated,
- sort_value_oldest_updated => sort_title_oldest_updated
+ sort_value_oldest_updated => sort_title_oldest_updated
}
end
+ def subgroups_sort_options_hash
+ groups_sort_options_hash.merge(
+ sort_value_most_stars => sort_title_most_stars
+ )
+ end
+
def admin_groups_sort_options_hash
groups_sort_options_hash.merge(
sort_value_largest_group => sort_title_largest_group
diff --git a/app/models/deployment.rb b/app/models/deployment.rb
index 6962b54441b..62dc0f2cbeb 100644
--- a/app/models/deployment.rb
+++ b/app/models/deployment.rb
@@ -19,6 +19,17 @@ class Deployment < ActiveRecord::Base
after_create :create_ref
after_create :invalidate_cache
+ scope :for_environment, -> (environment) { where(environment_id: environment) }
+
+ def self.last_for_environment(environment)
+ ids = self
+ .for_environment(environment)
+ .select('MAX(id) AS id')
+ .group(:environment_id)
+ .map(&:id)
+ find(ids)
+ end
+
def commit
project.commit(sha)
end
diff --git a/app/models/environment.rb b/app/models/environment.rb
index 309bd4f37c9..0816c395185 100644
--- a/app/models/environment.rb
+++ b/app/models/environment.rb
@@ -48,6 +48,8 @@ class Environment < ActiveRecord::Base
order(Gitlab::Database.nulls_first_order("(#{max_deployment_id_sql})", 'ASC'))
end
scope :in_review_folder, -> { where(environment_type: "review") }
+ scope :for_name, -> (name) { where(name: name) }
+ scope :for_project, -> (project) { where(project_id: project) }
state_machine :state, initial: :available do
event :start do
diff --git a/app/models/project.rb b/app/models/project.rb
index 05e14c578b5..c7ca322853f 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -1789,7 +1789,7 @@ class Project < ActiveRecord::Base
return unless export_file_exists?
import_export_upload.remove_export_file!
- import_export_upload.save
+ import_export_upload.save unless import_export_upload.destroyed?
end
def export_file_exists?
diff --git a/app/models/user.rb b/app/models/user.rb
index 8a7acfb73b1..a0665518cf5 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -217,7 +217,7 @@ class User < ActiveRecord::Base
# User's Dashboard preference
# Note: When adding an option, it MUST go on the end of the array.
- enum dashboard: [:projects, :stars, :project_activity, :starred_project_activity, :groups, :todos, :issues, :merge_requests]
+ enum dashboard: [:projects, :stars, :project_activity, :starred_project_activity, :groups, :todos, :issues, :merge_requests, :operations]
# User's Project preference
# Note: When adding an option, it MUST go on the end of the array.
diff --git a/app/serializers/build_details_entity.rb b/app/serializers/build_details_entity.rb
index 3d508a9a407..7bdcfcc38f7 100644
--- a/app/serializers/build_details_entity.rb
+++ b/app/serializers/build_details_entity.rb
@@ -4,6 +4,7 @@ class BuildDetailsEntity < JobEntity
expose :coverage, :erased_at, :duration
expose :tag_list, as: :tags
expose :has_trace?, as: :has_trace
+ expose :stage
expose :user, using: UserEntity
expose :runner, using: RunnerEntity
expose :pipeline, using: PipelineEntity
diff --git a/app/views/admin/applications/show.html.haml b/app/views/admin/applications/show.html.haml
index e69143abe45..df3eeba907c 100644
--- a/app/views/admin/applications/show.html.haml
+++ b/app/views/admin/applications/show.html.haml
@@ -22,7 +22,7 @@
.input-group
%input.label.label-monospace{ id: "secret", type: "text", autocomplete: 'off', value: @application.secret, readonly: true }
.input-group-append
- = clipboard_button(target: '#application_id', title: _("Copy secret to clipboard"), class: "btn btn btn-default")
+ = clipboard_button(target: '#secret', title: _("Copy secret to clipboard"), class: "btn btn btn-default")
%tr
%td
= _('Callback URL')
diff --git a/app/views/devise/shared/_omniauth_box.html.haml b/app/views/devise/shared/_omniauth_box.html.haml
index 269a3721e06..12271ee5adb 100644
--- a/app/views/devise/shared/_omniauth_box.html.haml
+++ b/app/views/devise/shared/_omniauth_box.html.haml
@@ -5,7 +5,7 @@
.d-flex.justify-content-between.flex-wrap
- providers.each do |provider|
- has_icon = provider_has_icon?(provider)
- = link_to omniauth_authorize_path(:user, provider), method: :post, class: 'btn d-flex align-items-center omniauth-btn text-left oauth-login', id: "oauth-login-#{provider}" do
+ = link_to omniauth_authorize_path(:user, provider), method: :post, class: 'btn d-flex align-items-center omniauth-btn text-left oauth-login qa-saml-login-button', id: "oauth-login-#{provider}" do
- if has_icon
= provider_image_tag(provider)
%span
diff --git a/app/views/doorkeeper/applications/show.html.haml b/app/views/doorkeeper/applications/show.html.haml
index 776bbc36ec2..cac00f9c854 100644
--- a/app/views/doorkeeper/applications/show.html.haml
+++ b/app/views/doorkeeper/applications/show.html.haml
@@ -25,7 +25,7 @@
.input-group
%input.label.label-monospace{ id: "secret", type: "text", autocomplete: 'off', value: @application.secret, readonly: true }
.input-group-append
- = clipboard_button(target: '#application_id', title: _("Copy secret to clipboard"), class: "btn btn btn-default")
+ = clipboard_button(target: '#secret', title: _("Copy secret to clipboard"), class: "btn btn btn-default")
%tr
%td
= _('Callback URL')
diff --git a/app/views/groups/show.html.haml b/app/views/groups/show.html.haml
index 6a293daaf95..cc294f6a931 100644
--- a/app/views/groups/show.html.haml
+++ b/app/views/groups/show.html.haml
@@ -53,7 +53,7 @@
= _("Archived projects")
.nav-controls
- = render "shared/groups/dropdown"
+ = render "shared/groups/dropdown", options_hash: subgroups_sort_options_hash
.tab-content
#subgroups_and_projects.tab-pane
diff --git a/app/views/layouts/nav/_dashboard.html.haml b/app/views/layouts/nav/_dashboard.html.haml
index 5e467c862ab..8f8b6b454d9 100644
--- a/app/views/layouts/nav/_dashboard.html.haml
+++ b/app/views/layouts/nav/_dashboard.html.haml
@@ -66,6 +66,7 @@
- if Gitlab::Sherlock.enabled? || can?(current_user, :read_instance_statistics)
%li.line-separator.d-none.d-sm-block
+ = render_if_exists 'dashboard/operations/nav_link'
- if can?(current_user, :read_instance_statistics)
= nav_link(controller: [:conversational_development_index, :cohorts]) do
= link_to instance_statistics_root_path, title: _('Instance Statistics'), aria: { label: _('Instance Statistics') }, data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do
diff --git a/app/views/layouts/nav/sidebar/_project.html.haml b/app/views/layouts/nav/sidebar/_project.html.haml
index 48025f9bd20..3625224fbcd 100644
--- a/app/views/layouts/nav/sidebar/_project.html.haml
+++ b/app/views/layouts/nav/sidebar/_project.html.haml
@@ -309,7 +309,7 @@
%span
= _('General')
= nav_link(controller: :project_members) do
- = link_to project_project_members_path(@project), title: _('Members') do
+ = link_to project_project_members_path(@project), title: _('Members'), class: 'qa-link-members-settings' do
%span
= _('Members')
- if can_edit
diff --git a/app/views/projects/jobs/show.html.haml b/app/views/projects/jobs/show.html.haml
index a5f814b722d..02a088d338b 100644
--- a/app/views/projects/jobs/show.html.haml
+++ b/app/views/projects/jobs/show.html.haml
@@ -47,4 +47,6 @@
.js-build-options{ data: javascript_build_options }
-#js-job-details-vue{ data: { endpoint: project_job_path(@project, @build, format: :json), runner_help_url: help_page_path('ci/runners/README.html', anchor: 'setting-maximum-job-timeout-for-a-runner') } }
+#js-job-details-vue{ data: { endpoint: project_job_path(@project, @build, format: :json),
+ runner_help_url: help_page_path('ci/runners/README.html', anchor: 'setting-maximum-job-timeout-for-a-runner'),
+ runner_settings_url: project_runners_path(@build.project, anchor: 'js-runners-settings') } }
diff --git a/app/views/projects/project_members/_new_project_member.html.haml b/app/views/projects/project_members/_new_project_member.html.haml
index 517fd249f6e..5e21442bb60 100644
--- a/app/views/projects/project_members/_new_project_member.html.haml
+++ b/app/views/projects/project_members/_new_project_member.html.haml
@@ -3,7 +3,7 @@
= form_for @project_member, as: :project_member, url: project_project_members_path(@project), html: { class: 'users-project-form' } do |f|
.form-group
= label_tag :user_ids, "Select members to invite", class: "label-bold"
- = users_select_tag(:user_ids, multiple: true, class: "input-clamp", scope: :all, email_user: true, placeholder: "Search for members to update or invite")
+ = users_select_tag(:user_ids, multiple: true, class: "input-clamp qa-member-select-input", scope: :all, email_user: true, placeholder: "Search for members to update or invite")
.form-group
= label_tag :access_level, "Choose a role permission", class: "label-bold"
.select-wrapper
@@ -17,5 +17,5 @@
= label_tag :expires_at, 'Access expiration date', class: 'label-bold'
= text_field_tag :expires_at, nil, class: 'form-control js-access-expiration-date', placeholder: 'Expiration date'
%i.clear-icon.js-clear-input
- = f.submit "Add to project", class: "btn btn-success"
+ = f.submit "Add to project", class: "btn btn-success qa-add-member-button"
= link_to "Import", import_project_project_members_path(@project), class: "btn btn-default", title: "Import members from another project"
diff --git a/app/views/projects/project_members/_team.html.haml b/app/views/projects/project_members/_team.html.haml
index 0c5a187f208..9682f8ac922 100644
--- a/app/views/projects/project_members/_team.html.haml
+++ b/app/views/projects/project_members/_team.html.haml
@@ -14,5 +14,5 @@
%button.member-search-btn{ type: "submit", "aria-label" => "Submit search" }
= icon("search")
= render 'shared/members/sort_dropdown'
- %ul.content-list.members-list
+ %ul.content-list.members-list.qa-members-list
= render partial: 'shared/members/member', collection: members, as: :member
diff --git a/changelogs/unreleased/22311-fix-duplicated-key-in-license-management-job.yml b/changelogs/unreleased/22311-fix-duplicated-key-in-license-management-job.yml
new file mode 100644
index 00000000000..ab64a1387d9
--- /dev/null
+++ b/changelogs/unreleased/22311-fix-duplicated-key-in-license-management-job.yml
@@ -0,0 +1,5 @@
+---
+title: "fix duplicated key in license management job auto devops gitlab ci template"
+merge_request: 22311
+author: Adam Lemanski
+type: fixed
diff --git a/changelogs/unreleased/48684-sort-projects-by-stars-in-groups.yml b/changelogs/unreleased/48684-sort-projects-by-stars-in-groups.yml
new file mode 100644
index 00000000000..01681adab24
--- /dev/null
+++ b/changelogs/unreleased/48684-sort-projects-by-stars-in-groups.yml
@@ -0,0 +1,4 @@
+---
+title: Add new sort option "most_stars" to "Group > Children" pages
+merge_request: 22121
+author: Rene Hennig
diff --git a/changelogs/unreleased/48889-message-for-were-merged-into.yml b/changelogs/unreleased/48889-message-for-were-merged-into.yml
new file mode 100644
index 00000000000..552b8826829
--- /dev/null
+++ b/changelogs/unreleased/48889-message-for-were-merged-into.yml
@@ -0,0 +1,5 @@
+---
+title: Fix 'merged with' UI being displayed when merge request has no merge commit
+merge_request: 22022
+author:
+type: fixed
diff --git a/changelogs/unreleased/50185-fix-broken-file-name-navigation.yml b/changelogs/unreleased/50185-fix-broken-file-name-navigation.yml
new file mode 100644
index 00000000000..d1b341af457
--- /dev/null
+++ b/changelogs/unreleased/50185-fix-broken-file-name-navigation.yml
@@ -0,0 +1,5 @@
+---
+title: Fix broken file name navigation on MRs
+merge_request: 22109
+author:
+type: fixed
diff --git a/changelogs/unreleased/52361-fix-file-tree-mobile.yml b/changelogs/unreleased/52361-fix-file-tree-mobile.yml
new file mode 100644
index 00000000000..fe978eeca2d
--- /dev/null
+++ b/changelogs/unreleased/52361-fix-file-tree-mobile.yml
@@ -0,0 +1,5 @@
+---
+title: Improve MR file tree in smaller screens
+merge_request: 22273
+author:
+type: fixed
diff --git a/changelogs/unreleased/52421-show-canary-no-canary-in-the-performance-bar.yml b/changelogs/unreleased/52421-show-canary-no-canary-in-the-performance-bar.yml
new file mode 100644
index 00000000000..20e32a2e987
--- /dev/null
+++ b/changelogs/unreleased/52421-show-canary-no-canary-in-the-performance-bar.yml
@@ -0,0 +1,5 @@
+---
+title: Show canary status in the performance bar
+merge_request: 22222
+author:
+type: changed
diff --git a/changelogs/unreleased/52472-pipeline-endpoint-json.yml b/changelogs/unreleased/52472-pipeline-endpoint-json.yml
new file mode 100644
index 00000000000..feff195beb8
--- /dev/null
+++ b/changelogs/unreleased/52472-pipeline-endpoint-json.yml
@@ -0,0 +1,5 @@
+---
+title: Fix caching issue with pipelines URL
+merge_request: 22293
+author:
+type: fixed
diff --git a/changelogs/unreleased/52519-runners-link.yml b/changelogs/unreleased/52519-runners-link.yml
new file mode 100644
index 00000000000..5d904a8b340
--- /dev/null
+++ b/changelogs/unreleased/52519-runners-link.yml
@@ -0,0 +1,5 @@
+---
+title: Fixes stuck block URL linking to documentation instead of settings page
+merge_request: 22286
+author:
+type: fixed
diff --git a/changelogs/unreleased/52570-erased-block.yml b/changelogs/unreleased/52570-erased-block.yml
new file mode 100644
index 00000000000..6ec295bf81b
--- /dev/null
+++ b/changelogs/unreleased/52570-erased-block.yml
@@ -0,0 +1,5 @@
+---
+title: Fix erased block not being rendered when job was erased
+merge_request: 22294
+author:
+type: fixed
diff --git a/changelogs/unreleased/52608-sidebar.yml b/changelogs/unreleased/52608-sidebar.yml
new file mode 100644
index 00000000000..9eca30f7b95
--- /dev/null
+++ b/changelogs/unreleased/52608-sidebar.yml
@@ -0,0 +1,5 @@
+---
+title: Hides sidebar for job page in mobile
+merge_request:
+author:
+type: fixed
diff --git a/changelogs/unreleased/52614-update-job-started-check.yml b/changelogs/unreleased/52614-update-job-started-check.yml
new file mode 100644
index 00000000000..60ea237dbf3
--- /dev/null
+++ b/changelogs/unreleased/52614-update-job-started-check.yml
@@ -0,0 +1,5 @@
+---
+title: Fixes triggered/created labeled in job header
+merge_request:
+author:
+type: fixed
diff --git a/changelogs/unreleased/52618-incorrect-stage-being-shown-in-side-bar-of-job-view-api.yml b/changelogs/unreleased/52618-incorrect-stage-being-shown-in-side-bar-of-job-view-api.yml
new file mode 100644
index 00000000000..fdbde709e77
--- /dev/null
+++ b/changelogs/unreleased/52618-incorrect-stage-being-shown-in-side-bar-of-job-view-api.yml
@@ -0,0 +1,5 @@
+---
+title: Load correct stage in the stages dropdown
+merge_request: 22317
+author:
+type: fixed
diff --git a/changelogs/unreleased/enable-frozen-string-lib-gitlab.yml b/changelogs/unreleased/enable-frozen-string-lib-gitlab.yml
new file mode 100644
index 00000000000..4a216c46d38
--- /dev/null
+++ b/changelogs/unreleased/enable-frozen-string-lib-gitlab.yml
@@ -0,0 +1,5 @@
+---
+title: Enable some frozen string in lib/gitlab
+merge_request:
+author: gfyoung
+type: performance
diff --git a/changelogs/unreleased/even-more-frozen-string-lib.yml b/changelogs/unreleased/even-more-frozen-string-lib.yml
new file mode 100644
index 00000000000..3f5fd7710aa
--- /dev/null
+++ b/changelogs/unreleased/even-more-frozen-string-lib.yml
@@ -0,0 +1,5 @@
+---
+title: Enable even more frozen string in lib/**/*.rb
+merge_request:
+author: gfyoung
+type: performance
diff --git a/changelogs/unreleased/feature-improved-branch-filter-sorting.yml b/changelogs/unreleased/feature-improved-branch-filter-sorting.yml
new file mode 100644
index 00000000000..539c297e0dd
--- /dev/null
+++ b/changelogs/unreleased/feature-improved-branch-filter-sorting.yml
@@ -0,0 +1,6 @@
+---
+title: Improving branch filter sorting by listing exact matches first and added support
+ for begins_with (^) and ends_with ($) matching.
+merge_request: 22166
+author: Jason Rutherford
+type: changed
diff --git a/changelogs/unreleased/fl-update-svgs.yml b/changelogs/unreleased/fl-update-svgs.yml
new file mode 100644
index 00000000000..e6e76617df1
--- /dev/null
+++ b/changelogs/unreleased/fl-update-svgs.yml
@@ -0,0 +1,5 @@
+---
+title: Updates svg dependency
+merge_request:
+author:
+type: other
diff --git a/changelogs/unreleased/gt-update-application-copy-secret-to-clipboard-data.yml b/changelogs/unreleased/gt-update-application-copy-secret-to-clipboard-data.yml
new file mode 100644
index 00000000000..7205c138777
--- /dev/null
+++ b/changelogs/unreleased/gt-update-application-copy-secret-to-clipboard-data.yml
@@ -0,0 +1,5 @@
+---
+title: Update copy to clipboard button data for application secret
+merge_request: 22268
+author: George Tsiolis
+type: fixed
diff --git a/changelogs/unreleased/sh-fix-project-deletion-with-export.yml b/changelogs/unreleased/sh-fix-project-deletion-with-export.yml
new file mode 100644
index 00000000000..b9437f8ad6a
--- /dev/null
+++ b/changelogs/unreleased/sh-fix-project-deletion-with-export.yml
@@ -0,0 +1,5 @@
+---
+title: Fix project deletion when there is a export available
+merge_request: 22276
+author:
+type: fixed
diff --git a/config/unicorn.rb.example b/config/unicorn.rb.example
index 020e9a00d87..e06cce3e97a 100644
--- a/config/unicorn.rb.example
+++ b/config/unicorn.rb.example
@@ -95,7 +95,7 @@ end
before_fork do |server, worker|
# the following is highly recommended for Rails + "preload_app true"
# as there's no need for the master process to hold a connection
- defined?(ActiveRecord::Base) and
+ defined?(ActiveRecord::Base) &&
ActiveRecord::Base.connection.disconnect!
# The following is only recommended for memory/DB-constrained
@@ -133,7 +133,7 @@ after_fork do |server, worker|
# server.listen(addr, :tries => -1, :delay => 5, :tcp_nopush => true)
# the following is *required* for Rails + "preload_app true",
- defined?(ActiveRecord::Base) and
+ defined?(ActiveRecord::Base) &&
ActiveRecord::Base.establish_connection
# reset prometheus client, this will cause any opened metrics files to be closed
diff --git a/config/unicorn.rb.example.development b/config/unicorn.rb.example.development
index 5712549a66d..f31df66015a 100644
--- a/config/unicorn.rb.example.development
+++ b/config/unicorn.rb.example.development
@@ -7,7 +7,7 @@ check_client_connection false
before_fork do |server, worker|
# the following is highly recommended for Rails + "preload_app true"
# as there's no need for the master process to hold a connection
- defined?(ActiveRecord::Base) and
+ defined?(ActiveRecord::Base) &&
ActiveRecord::Base.connection.disconnect!
if /darwin/ =~ RUBY_PLATFORM
@@ -27,6 +27,6 @@ after_fork do |server, worker|
require 'rbtrace' if ENV['ENABLE_RBTRACE']
# the following is *required* for Rails + "preload_app true",
- defined?(ActiveRecord::Base) and
+ defined?(ActiveRecord::Base) &&
ActiveRecord::Base.establish_connection
end
diff --git a/doc/administration/gitaly/index.md b/doc/administration/gitaly/index.md
index b5e2b5448f7..e1b2a0a24eb 100644
--- a/doc/administration/gitaly/index.md
+++ b/doc/administration/gitaly/index.md
@@ -25,15 +25,13 @@ gitaly['prometheus_listen_addr'] = 'localhost:9236'
```
To change a Gitaly setting in installations from source you can edit
-`/home/git/gitaly/config.toml`.
+`/home/git/gitaly/config.toml`. Changes will be applied when you run
+`service gitlab restart`.
```toml
prometheus_listen_addr = "localhost:9236"
```
-Changes to `/home/git/gitaly/config.toml` are applied when you run `service
-gitlab restart`.
-
## Client-side GRPC logs
Gitaly uses the [gRPC](https://grpc.io/) RPC framework. The Ruby gRPC
diff --git a/doc/administration/integration/plantuml.md b/doc/administration/integration/plantuml.md
index 293036f2f4b..b61c5409a56 100644
--- a/doc/administration/integration/plantuml.md
+++ b/doc/administration/integration/plantuml.md
@@ -80,10 +80,10 @@ our AsciiDoc snippets, wikis and repos using delimited blocks:
```
[plantuml, format="png", id="myDiagram", width="200px"]
- --
+ ----
Bob->Alice : hello
Alice -> Bob : Go Away
- --
+ ----
```
- **reStructuredText**
diff --git a/doc/administration/job_artifacts.md b/doc/administration/job_artifacts.md
index 757865ea2c5..2feac1fd3b0 100644
--- a/doc/administration/job_artifacts.md
+++ b/doc/administration/job_artifacts.md
@@ -98,7 +98,7 @@ _The artifacts are stored by default in
If you don't want to use the local disk where GitLab is installed to store the
artifacts, you can use an object storage like AWS S3 instead.
This configuration relies on valid AWS credentials to be configured already.
-Use an [Object storage option][os] like AWS S3 to store job artifacts.
+Use an object storage option like AWS S3 to store job artifacts.
### Object Storage Settings
@@ -315,4 +315,3 @@ memory and disk I/O.
[reconfigure gitlab]: restart_gitlab.md#omnibus-gitlab-reconfigure "How to reconfigure Omnibus GitLab"
[restart gitlab]: restart_gitlab.md#installations-from-source "How to restart GitLab"
[gitlab workhorse]: https://gitlab.com/gitlab-org/gitlab-workhorse "GitLab Workhorse repository"
-[os]: https://docs.gitlab.com/administration/job_artifacts.html#using-object-storage
diff --git a/doc/api/events.md b/doc/api/events.md
index cd84b32029e..ccac5b8bb60 100644
--- a/doc/api/events.md
+++ b/doc/api/events.md
@@ -71,7 +71,7 @@ Parameters:
Example request:
```
-curl --header "PRIVATE-TOKEN 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/events&target_type=issue&action=created&after=2017-01-31&before=2017-03-01
+curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/events&target_type=issue&action=created&after=2017-01-31&before=2017-03-01
```
Example response:
@@ -276,7 +276,7 @@ Parameters:
Example request:
```
-curl --header "PRIVATE-TOKEN 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/projects/:project_id/events&target_type=issue&action=created&after=2017-01-31&before=2017-03-01
+curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/projects/:project_id/events&target_type=issue&action=created&after=2017-01-31&before=2017-03-01
```
Example response:
diff --git a/doc/ci/examples/laravel_with_gitlab_and_envoy/index.md b/doc/ci/examples/laravel_with_gitlab_and_envoy/index.md
index ab429e0ded3..70020d461d9 100644
--- a/doc/ci/examples/laravel_with_gitlab_and_envoy/index.md
+++ b/doc/ci/examples/laravel_with_gitlab_and_envoy/index.md
@@ -125,7 +125,7 @@ They can be added per project by navigating to the project's **Settings** > **CI
To the field **KEY**, add the name `SSH_PRIVATE_KEY`, and to the **VALUE** field, paste the private key you've copied earlier.
We'll use this variable in the `.gitlab-ci.yml` later, to easily connect to our remote server as the deployer user without entering its password.
-We also need to add the public key to **Project** > **Settings** > **Repository** as [Deploy Keys](../../../ssh/README.md/#deploy-keys), which gives us the ability to access our repository from the server through [SSH protocol](../../../gitlab-basics/command-line-commands.md/#start-working-on-your-project).
+We also need to add the public key to **Project** > **Settings** > **Repository** as [Deploy Keys](../../../ssh/README.md#deploy-keys), which gives us the ability to access our repository from the server through [SSH protocol](../../../gitlab-basics/command-line-commands.md#start-working-on-your-project).
```bash
@@ -378,7 +378,7 @@ These are persistent data and will be shared to every new release.
Now, we would need to deploy our app by running `envoy run deploy`, but it won't be necessary since GitLab can handle that for us with CI's [environments](../../environments.md), which will be described [later](#setting-up-gitlab-ci-cd) in this tutorial.
Now it's time to commit [Envoy.blade.php](https://gitlab.com/mehranrasulian/laravel-sample/blob/master/Envoy.blade.php) and push it to the `master` branch.
-To keep things simple, we commit directly to `master`, without using [feature-branches](../../../workflow/gitlab_flow.md/#github-flow-as-a-simpler-alternative) since collaboration is beyond the scope of this tutorial.
+To keep things simple, we commit directly to `master`, without using [feature-branches](../../../workflow/gitlab_flow.md#github-flow-as-a-simpler-alternative) since collaboration is beyond the scope of this tutorial.
In a real world project, teams may use [Issue Tracker](../../../user/project/issues/index.md) and [Merge Requests](../../../user/project/merge_requests/index.md) to move their code across branches:
```bash
@@ -398,7 +398,7 @@ In the case you're not familiar with Docker, refer to [How to Automate Docker De
To be able to build, test, and deploy our app with GitLab CI/CD, we need to prepare our work environment.
To do that, we'll use a Docker image which has the minimum requirements that a Laravel app needs to run.
-[There are other ways](../php.md/#test-php-projects-using-the-docker-executor) to do that as well, but they may lead our builds run slowly, which is not what we want when there are faster options to use.
+[There are other ways](../php.md#test-php-projects-using-the-docker-executor) to do that as well, but they may lead our builds run slowly, which is not what we want when there are faster options to use.
With Docker images our builds run incredibly faster!
@@ -536,7 +536,7 @@ That's a lot to take in, isn't it? Let's run through it step by step.
[GitLab Runners](../../runners/README.md) run the script defined by `.gitlab-ci.yml`.
The `image` keyword tells the Runners which image to use.
-The `services` keyword defines additional images [that are linked to the main image](../../docker/using_docker_images.md/#what-is-a-service).
+The `services` keyword defines additional images [that are linked to the main image](../../docker/using_docker_images.md#what-is-a-service).
Here we use the container image we created before as our main image and also use MySQL 5.7 as a service.
```yaml
@@ -560,7 +560,7 @@ So we should adjust the configuration of MySQL instance by defining `MYSQL_DATAB
Find out more about MySQL variables at the [official MySQL Docker Image](https://hub.docker.com/r/_/mysql/).
Also set the variables `DB_HOST` to `mysql` and `DB_USERNAME` to `root`, which are Laravel specific variables.
-We define `DB_HOST` as `mysql` instead of `127.0.0.1`, as we use MySQL Docker image as a service which [is linked to the main Docker image](../../docker/using_docker_images.md/#how-services-are-linked-to-the-build).
+We define `DB_HOST` as `mysql` instead of `127.0.0.1`, as we use MySQL Docker image as a service which [is linked to the main Docker image](../../docker/using_docker_images.md#how-services-are-linked-to-the-build).
```yaml
...
@@ -602,7 +602,7 @@ unit_test:
#### Deploy to production
The job `deploy_production` will deploy the app to the production server.
-To deploy our app with Envoy, we had to set up the `$SSH_PRIVATE_KEY` variable as an [SSH private key](../../ssh_keys/README.md/#ssh-keys-when-using-the-docker-executor).
+To deploy our app with Envoy, we had to set up the `$SSH_PRIVATE_KEY` variable as an [SSH private key](../../ssh_keys/README.md#ssh-keys-when-using-the-docker-executor).
If the SSH keys have added successfully, we can run Envoy.
As mentioned before, GitLab supports [Continuous Delivery](https://about.gitlab.com/2016/08/05/continuous-integration-delivery-and-deployment-with-gitlab/#continuous-delivery) methods as well.
diff --git a/doc/ci/examples/php.md b/doc/ci/examples/php.md
index df4805ea7ac..c1048f3d2e3 100644
--- a/doc/ci/examples/php.md
+++ b/doc/ci/examples/php.md
@@ -20,7 +20,7 @@ build environment.
Let's first specify the PHP image that will be used for the job process
(you can read more about what an image means in the Runner's lingo reading
-about [Using Docker images](../docker/using_docker_images.md#what-is-image)).
+about [Using Docker images](../docker/using_docker_images.md#what-is-an-image)).
Start by adding the image to your `.gitlab-ci.yml`:
diff --git a/doc/ci/triggers/README.md b/doc/ci/triggers/README.md
index cf92d90ba30..bffb0121603 100644
--- a/doc/ci/triggers/README.md
+++ b/doc/ci/triggers/README.md
@@ -2,7 +2,7 @@
> **Notes**:
>
-> - [Introduced][ci-229] in GitLab CE 7.14.
+> - [Introduced](https://about.gitlab.com/2015/08/22/gitlab-7-14-released/) in GitLab 7.14.
> - GitLab 8.12 has a completely redesigned job permissions system. Read all
> about the [new model and its implications](../../user/project/new_ci_build_permissions_model.md#job-triggers).
@@ -154,10 +154,10 @@ This information is also exposed in the UI.
Using trigger variables can be proven useful for a variety of reasons:
-* Identifiable jobs. Since the variable is exposed in the UI you can know
+- Identifiable jobs. Since the variable is exposed in the UI you can know
why the rebuild was triggered if you pass a variable that explains the
purpose.
-* Conditional job processing. You can have conditional jobs that run whenever
+- Conditional job processing. You can have conditional jobs that run whenever
a certain variable is present.
Consider the following `.gitlab-ci.yml` where we set three
@@ -221,7 +221,6 @@ removed with one of the future versions of GitLab. You are advised to
[take ownership](#taking-ownership) of any legacy triggers.
[ee-2017]: https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/2017
-[ci-229]: https://gitlab.com/gitlab-org/gitlab-ci/merge_requests/229
[ee]: https://about.gitlab.com/pricing/
[variables]: ../variables/README.md
[predef]: ../variables/README.md#predefined-variables-environment-variables
diff --git a/doc/ci/yaml/README.md b/doc/ci/yaml/README.md
index f0738252640..24d60a0cdcc 100644
--- a/doc/ci/yaml/README.md
+++ b/doc/ci/yaml/README.md
@@ -415,7 +415,7 @@ Four keys are now available: `refs`, `kubernetes` and `variables` and `changes`.
Refs strategy equals to simplified only/except configuration, whereas
kubernetes strategy accepts only `active` keyword.
-### `variables`
+### `only:variables`
`variables` keyword is used to define variables expressions. In other words
you can use predefined variables / project / group or
@@ -460,7 +460,7 @@ end-to-end:
Learn more about variables expressions on [a separate page][variables-expressions].
-### `changes`
+### `only:changes`
Using `changes` keyword with `only` or `except` makes it possible to define if
a job should be created based on files modified by a git push event.
diff --git a/doc/development/README.md b/doc/development/README.md
index 43d3865da0e..d8604a4f3e5 100644
--- a/doc/development/README.md
+++ b/doc/development/README.md
@@ -50,6 +50,7 @@ description: 'Learn how to contribute to GitLab.'
- [Permissions](permissions.md)
- [Prometheus metrics](prometheus_metrics.md)
- [Guidelines for reusing abstractions](reusing_abstractions.md)
+- [DeclarativePolicy framework](policies.md)
## Performance guides
diff --git a/doc/development/documentation/index.md b/doc/development/documentation/index.md
index 2db78e4a365..ce2424eca3b 100644
--- a/doc/development/documentation/index.md
+++ b/doc/development/documentation/index.md
@@ -321,7 +321,7 @@ The following sample `markdownlint` configuration modifies the available default
}
```
-For [`markdownlint`](https://gitahub.com/DavidAnson/markdownlint/), this configuration must be
+For [`markdownlint`](https://github.com/DavidAnson/markdownlint/), this configuration must be
placed in a [valid location](https://github.com/igorshubovych/markdownlint-cli#configuration). For
example, `~/.markdownlintrc`.
diff --git a/doc/development/feature_flags.md b/doc/development/feature_flags.md
index 417298205f5..0f1f079bdb4 100644
--- a/doc/development/feature_flags.md
+++ b/doc/development/feature_flags.md
@@ -69,6 +69,37 @@ For more information about rolling out changes using feature flags, refer to the
[Rolling out changes using feature flags](rolling_out_changes_using_feature_flags.md)
guide.
+### Frontend
+
+For frontend code you can use the method `push_frontend_feature_flag`, which is
+available to all controllers that inherit from `ApplicationController`. Using
+this method you can expose the state of a feature flag as follows:
+
+```ruby
+before_action do
+ push_frontend_feature_flag(:vim_bindings)
+end
+
+def index
+ # ...
+end
+
+def edit
+ # ...
+end
+```
+
+You can then check for the state of the feature flag in JavaScript as follows:
+
+```javascript
+if ( gon.features.vimBindings ) {
+ // ...
+}
+```
+
+The name of the feature flag in JavaScript will always be camelCased, meaning
+that checking for `gon.features.vim_bindings` would not work.
+
### Specs
In the test environment `Feature.enabled?` is stubbed to always respond to `true`,
diff --git a/doc/install/installation.md b/doc/install/installation.md
index 25aa5d3369d..9e2e58657f1 100644
--- a/doc/install/installation.md
+++ b/doc/install/installation.md
@@ -103,7 +103,7 @@ Is the system packaged Git too old? Remove it and compile from source.
# When editing config/gitlab.yml (Step 5), change the git -> bin_path to /usr/local/bin/git
-**Note:** In order to receive mail notifications, make sure to install a mail server. By default, Debian is shipped with exim4 but this [has problems](https://github.com/gitlabhq/gitlabhq/issues/4866#issuecomment-32726573) while Ubuntu does not ship with one. The recommended mail server is postfix and you can install it with:
+**Note:** In order to receive mail notifications, make sure to install a mail server. By default, Debian is shipped with exim4 but this [has problems](https://gitlab.com/gitlab-org/gitlab-ce/issues/12754) while Ubuntu does not ship with one. The recommended mail server is postfix and you can install it with:
sudo apt-get install -y postfix
diff --git a/doc/update/11.3-to-11.4.md b/doc/update/11.3-to-11.4.md
index 985239369d7..b50e21f27dd 100644
--- a/doc/update/11.3-to-11.4.md
+++ b/doc/update/11.3-to-11.4.md
@@ -80,8 +80,8 @@ More information can be found on the [yarn website](https://yarnpkg.com/en/docs/
### 5. Update Go
-NOTE: GitLab 11.0 and higher only supports Go 1.9.x and newer, and dropped support for Go
-1.5.x through 1.8.x. Be sure to upgrade your installation if necessary.
+NOTE: GitLab 11.4 and higher only supports Go 1.10.x and newer, and dropped support for Go
+1.9.x. Be sure to upgrade your installation if necessary.
You can check which version you are running with `go version`.
diff --git a/doc/user/discussions/index.md b/doc/user/discussions/index.md
index 1b3fb9db4ec..097b18ad496 100644
--- a/doc/user/discussions/index.md
+++ b/doc/user/discussions/index.md
@@ -281,7 +281,7 @@ Additionally locked issues can not be reopened.
[ce-14053]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/14053
[ce-14061]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/14061
[ce-14531]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/14531
-[ce-31847]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/31847
+[ce-31847]: https://gitlab.com/gitlab-org/gitlab-ce/issues/31847
[resolve-discussion-button]: img/resolve_discussion_button.png
[resolve-comment-button]: img/resolve_comment_button.png
[discussion-view]: img/discussion_view.png
diff --git a/doc/user/project/merge_requests/cherry_pick_changes.md b/doc/user/project/merge_requests/cherry_pick_changes.md
index 22ef11e4049..06b3779668b 100644
--- a/doc/user/project/merge_requests/cherry_pick_changes.md
+++ b/doc/user/project/merge_requests/cherry_pick_changes.md
@@ -12,9 +12,11 @@ to cherry-pick the changes introduced by that merge request.
![Cherry-pick Merge Request](img/cherry_pick_changes_mr.png)
-After you click that button, a modal will appear where you can choose to
-cherry-pick the changes directly into the selected branch or you can opt to
-create a new merge request with the cherry-pick changes
+After you click that button, a modal will appear showing a [branch filter search box](../repository/branches/index.md#branch-filter-search-box)
+where you can choose to either:
+
+- Cherry-pick the changes directly into the selected branch.
+- Create a new merge request with the cherry-picked changes.
## Cherry-picking a Commit
diff --git a/doc/user/project/merge_requests/img/merge_request_diff_file_navigation.png b/doc/user/project/merge_requests/img/merge_request_diff_file_navigation.png
index ac766c99935..3b3bf88df31 100644
--- a/doc/user/project/merge_requests/img/merge_request_diff_file_navigation.png
+++ b/doc/user/project/merge_requests/img/merge_request_diff_file_navigation.png
Binary files differ
diff --git a/doc/user/project/merge_requests/index.md b/doc/user/project/merge_requests/index.md
index 43ca498d006..f9ebf277125 100644
--- a/doc/user/project/merge_requests/index.md
+++ b/doc/user/project/merge_requests/index.md
@@ -205,9 +205,9 @@ have been marked as a **Work In Progress**.
## Merge request diff file navigation
-The diff view has a persistent dropdown for file navigation. As you scroll through
-diffs with a large number of files and/or many changes in those files, you can
-easily jump to any changed file through the dropdown navigation.
+The diff view has a file tree for file navigation. As you scroll through
+diffs with a large number of files, you can easily jump to any changed file
+using the file tree.
![Merge request diff file navigation](img/merge_request_diff_file_navigation.png)
diff --git a/doc/user/project/repository/branches/img/branch_filter_search_box.png b/doc/user/project/repository/branches/img/branch_filter_search_box.png
new file mode 100644
index 00000000000..c4364ef39f4
--- /dev/null
+++ b/doc/user/project/repository/branches/img/branch_filter_search_box.png
Binary files differ
diff --git a/doc/user/project/repository/branches/index.md b/doc/user/project/repository/branches/index.md
index 19417d91fec..e1d8345f415 100644
--- a/doc/user/project/repository/branches/index.md
+++ b/doc/user/project/repository/branches/index.md
@@ -6,6 +6,7 @@ Read through GiLab's branching documentation:
- [Default branch](#default-branch)
- [Protected branches](../../protected_branches.md#protected-branches)
- [Delete merged branches](#delete-merged-branches)
+- [Branch filter search box](#branch-filter-search-box)
See also:
@@ -40,5 +41,22 @@ this operation.
It's particularly useful to clean up old branches that were not deleted
automatically when a merge request was merged.
+
+## Branch filter search box
+
+> [Introduced][https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/22166] in GitLab 11.5.
+
+![Branch filter search box](img/branch_filter_search_box.png)
+
+This feature allows you to search and select branches quickly. Search results appear in the following order:
+
+- Branches with names that matched search terms exactly.
+- Other branches with names that include search terms, sorted alphabetically.
+
+Sometimes when you have hundreds of branches you may want a more flexible matching pattern. In such cases you can use the following:
+
+- `^feature` will only match branch names that begin with 'feature'.
+- `feature$` will only match branch names that end with 'feature'.
+
[ce-6449]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/6449 "Add button to delete all merged branches"
[protected]: ../../protected_branches.md
diff --git a/doc/user/project/repository/index.md b/doc/user/project/repository/index.md
index 4d016277824..ce79bfc0a03 100644
--- a/doc/user/project/repository/index.md
+++ b/doc/user/project/repository/index.md
@@ -85,12 +85,13 @@ You can live preview changes submitted to a new branch with
With [GitLab Starter](https://about.gitlab.com/pricing/), you can also request
[approval](https://docs.gitlab.com/ee/user/project/merge_requests/merge_request_approvals.html) from your managers.
-To create, delete, and [branches](branches/index.md) via GitLab's UI:
+To create, delete, and view [branches](branches/index.md) via GitLab's UI:
- [Default branches](branches/index.md#default-branch)
- [Create a branch](web_editor.md#create-a-new-branch)
- [Protected branches](../protected_branches.md#protected-branches)
- [Delete merged branches](branches/index.md#delete-merged-branches)
+- [Branch filter search box](branches/index.md#branch-filter-search-box)
Alternatively, you can use the
[command line](../../../gitlab-basics/start-using-git.md#create-a-branch).
@@ -169,7 +170,7 @@ vendored code, and most markup languages are excluded.
## Compare
-Select branches to compare and view the changes inline:
+Select branches to compare using the [branch filter search box](branches/index.md#branch-filter-search-box), then click the **Compare** button to view the changes inline:
![compare branches](img/compare_branches.png)
diff --git a/lib/generators/rails/post_deployment_migration/post_deployment_migration_generator.rb b/lib/generators/rails/post_deployment_migration/post_deployment_migration_generator.rb
index 91175b49c79..15cdd25e711 100644
--- a/lib/generators/rails/post_deployment_migration/post_deployment_migration_generator.rb
+++ b/lib/generators/rails/post_deployment_migration/post_deployment_migration_generator.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'rails/generators'
module Rails
diff --git a/lib/gitaly/server.rb b/lib/gitaly/server.rb
index f95e423ef22..7b238623418 100644
--- a/lib/gitaly/server.rb
+++ b/lib/gitaly/server.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitaly
class Server
def self.all
diff --git a/lib/gitlab/auth/activity.rb b/lib/gitlab/auth/activity.rb
index 761f0819c60..558628b5422 100644
--- a/lib/gitlab/auth/activity.rb
+++ b/lib/gitlab/auth/activity.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Auth
##
diff --git a/lib/gitlab/auth/database/authentication.rb b/lib/gitlab/auth/database/authentication.rb
index 1234ace0334..c0dc2b0875f 100644
--- a/lib/gitlab/auth/database/authentication.rb
+++ b/lib/gitlab/auth/database/authentication.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# These calls help to authenticate to OAuth provider by providing username and password
#
diff --git a/lib/gitlab/auth/ip_rate_limiter.rb b/lib/gitlab/auth/ip_rate_limiter.rb
index e6173d45af3..81e616fa20a 100644
--- a/lib/gitlab/auth/ip_rate_limiter.rb
+++ b/lib/gitlab/auth/ip_rate_limiter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Auth
class IpRateLimiter
diff --git a/lib/gitlab/auth/ldap/access.rb b/lib/gitlab/auth/ldap/access.rb
index f323d2e0f7a..c875bba4bcb 100644
--- a/lib/gitlab/auth/ldap/access.rb
+++ b/lib/gitlab/auth/ldap/access.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# LDAP authorization model
#
# * Check if we are allowed access (not blocked)
diff --git a/lib/gitlab/auth/ldap/adapter.rb b/lib/gitlab/auth/ldap/adapter.rb
index 82ff1e77e5c..42c657afe6a 100644
--- a/lib/gitlab/auth/ldap/adapter.rb
+++ b/lib/gitlab/auth/ldap/adapter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Auth
module LDAP
diff --git a/lib/gitlab/auth/ldap/auth_hash.rb b/lib/gitlab/auth/ldap/auth_hash.rb
index ac5c14d374d..83fdc8a8c76 100644
--- a/lib/gitlab/auth/ldap/auth_hash.rb
+++ b/lib/gitlab/auth/ldap/auth_hash.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# Class to parse and transform the info provided by omniauth
#
module Gitlab
diff --git a/lib/gitlab/auth/ldap/authentication.rb b/lib/gitlab/auth/ldap/authentication.rb
index 7c134fb6438..174e81dd603 100644
--- a/lib/gitlab/auth/ldap/authentication.rb
+++ b/lib/gitlab/auth/ldap/authentication.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# These calls help to authenticate to LDAP by providing username and password
#
# Since multiple LDAP servers are supported, it will loop through all of them
diff --git a/lib/gitlab/auth/ldap/config.rb b/lib/gitlab/auth/ldap/config.rb
index d4415eaa6dc..7ceb96f502b 100644
--- a/lib/gitlab/auth/ldap/config.rb
+++ b/lib/gitlab/auth/ldap/config.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# Load a specific server configuration
module Gitlab
module Auth
diff --git a/lib/gitlab/auth/ldap/dn.rb b/lib/gitlab/auth/ldap/dn.rb
index 1fa5338f5a6..5df914aa367 100644
--- a/lib/gitlab/auth/ldap/dn.rb
+++ b/lib/gitlab/auth/ldap/dn.rb
@@ -1,4 +1,5 @@
# -*- ruby encoding: utf-8 -*-
+# frozen_string_literal: true
# Based on the `ruby-net-ldap` gem's `Net::LDAP::DN`
#
diff --git a/lib/gitlab/auth/ldap/ldap_connection_error.rb b/lib/gitlab/auth/ldap/ldap_connection_error.rb
index ef0a695742b..d0e5f24d203 100644
--- a/lib/gitlab/auth/ldap/ldap_connection_error.rb
+++ b/lib/gitlab/auth/ldap/ldap_connection_error.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Auth
module LDAP
diff --git a/lib/gitlab/auth/ldap/person.rb b/lib/gitlab/auth/ldap/person.rb
index 8dfae3ee541..a0244a3cea1 100644
--- a/lib/gitlab/auth/ldap/person.rb
+++ b/lib/gitlab/auth/ldap/person.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Auth
module LDAP
diff --git a/lib/gitlab/auth/ldap/user.rb b/lib/gitlab/auth/ldap/user.rb
index 3c21ddf3241..9c71671f409 100644
--- a/lib/gitlab/auth/ldap/user.rb
+++ b/lib/gitlab/auth/ldap/user.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# LDAP extension for User model
#
# * Find or create user from omniauth.auth data
diff --git a/lib/gitlab/auth/o_auth/auth_hash.rb b/lib/gitlab/auth/o_auth/auth_hash.rb
index ed8fba94305..4a5f9d2839d 100644
--- a/lib/gitlab/auth/o_auth/auth_hash.rb
+++ b/lib/gitlab/auth/o_auth/auth_hash.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# Class to parse and transform the info provided by omniauth
#
module Gitlab
diff --git a/lib/gitlab/auth/o_auth/authentication.rb b/lib/gitlab/auth/o_auth/authentication.rb
index d4e7f35c857..5f008678bd1 100644
--- a/lib/gitlab/auth/o_auth/authentication.rb
+++ b/lib/gitlab/auth/o_auth/authentication.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# These calls help to authenticate to OAuth provider by providing username and password
#
diff --git a/lib/gitlab/auth/o_auth/identity_linker.rb b/lib/gitlab/auth/o_auth/identity_linker.rb
index de92d7a214d..e69c2bb54dc 100644
--- a/lib/gitlab/auth/o_auth/identity_linker.rb
+++ b/lib/gitlab/auth/o_auth/identity_linker.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Auth
module OAuth
diff --git a/lib/gitlab/auth/o_auth/provider.rb b/lib/gitlab/auth/o_auth/provider.rb
index 26da9d09ccc..9fdf3324db3 100644
--- a/lib/gitlab/auth/o_auth/provider.rb
+++ b/lib/gitlab/auth/o_auth/provider.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Auth
module OAuth
diff --git a/lib/gitlab/auth/o_auth/session.rb b/lib/gitlab/auth/o_auth/session.rb
index 8f2b4d58552..4925b107042 100644
--- a/lib/gitlab/auth/o_auth/session.rb
+++ b/lib/gitlab/auth/o_auth/session.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# :nocov:
module Gitlab
module Auth
diff --git a/lib/gitlab/auth/o_auth/user.rb b/lib/gitlab/auth/o_auth/user.rb
index 2b4f6ed75e5..a4e8a41b246 100644
--- a/lib/gitlab/auth/o_auth/user.rb
+++ b/lib/gitlab/auth/o_auth/user.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# OAuth extension for User model
#
# * Find GitLab user based on omniauth uid and provider
diff --git a/lib/gitlab/auth/omniauth_identity_linker_base.rb b/lib/gitlab/auth/omniauth_identity_linker_base.rb
index 8ae29a02a13..253445570f2 100644
--- a/lib/gitlab/auth/omniauth_identity_linker_base.rb
+++ b/lib/gitlab/auth/omniauth_identity_linker_base.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Auth
class OmniauthIdentityLinkerBase
diff --git a/lib/gitlab/auth/request_authenticator.rb b/lib/gitlab/auth/request_authenticator.rb
index 66de52506ce..cb9f2582936 100644
--- a/lib/gitlab/auth/request_authenticator.rb
+++ b/lib/gitlab/auth/request_authenticator.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# Use for authentication only, in particular for Rack::Attack.
# Does not perform authorization of scopes, etc.
module Gitlab
diff --git a/lib/gitlab/auth/result.rb b/lib/gitlab/auth/result.rb
index 00cdc94a9ef..78fa25c5516 100644
--- a/lib/gitlab/auth/result.rb
+++ b/lib/gitlab/auth/result.rb
@@ -1,4 +1,7 @@
-module Gitlab # rubocop:disable Naming/FileName
+# rubocop:disable Naming/FileName
+# frozen_string_literal: true
+
+module Gitlab
module Auth
Result = Struct.new(:actor, :project, :type, :authentication_abilities) do
def ci?(for_project)
diff --git a/lib/gitlab/auth/saml/auth_hash.rb b/lib/gitlab/auth/saml/auth_hash.rb
index 3bc5e2864df..316354fd50c 100644
--- a/lib/gitlab/auth/saml/auth_hash.rb
+++ b/lib/gitlab/auth/saml/auth_hash.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Auth
module Saml
diff --git a/lib/gitlab/auth/saml/config.rb b/lib/gitlab/auth/saml/config.rb
index 625dab7c6f4..8cb999f50d4 100644
--- a/lib/gitlab/auth/saml/config.rb
+++ b/lib/gitlab/auth/saml/config.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Auth
module Saml
diff --git a/lib/gitlab/auth/saml/identity_linker.rb b/lib/gitlab/auth/saml/identity_linker.rb
index 7e4b191d512..ae0d6dded4e 100644
--- a/lib/gitlab/auth/saml/identity_linker.rb
+++ b/lib/gitlab/auth/saml/identity_linker.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Auth
module Saml
diff --git a/lib/gitlab/auth/saml/user.rb b/lib/gitlab/auth/saml/user.rb
index 6c3b75f3eb0..ec95bc46791 100644
--- a/lib/gitlab/auth/saml/user.rb
+++ b/lib/gitlab/auth/saml/user.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# SAML extension for User model
#
# * Find GitLab user based on SAML uid and provider
diff --git a/lib/gitlab/auth/too_many_ips.rb b/lib/gitlab/auth/too_many_ips.rb
index ed862791551..ee4d80e6b89 100644
--- a/lib/gitlab/auth/too_many_ips.rb
+++ b/lib/gitlab/auth/too_many_ips.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Auth
class TooManyIps < StandardError
diff --git a/lib/gitlab/auth/unique_ips_limiter.rb b/lib/gitlab/auth/unique_ips_limiter.rb
index baa1f802d8a..31dd61ae6cf 100644
--- a/lib/gitlab/auth/unique_ips_limiter.rb
+++ b/lib/gitlab/auth/unique_ips_limiter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Auth
class UniqueIpsLimiter
diff --git a/lib/gitlab/auth/user_access_denied_reason.rb b/lib/gitlab/auth/user_access_denied_reason.rb
index 1893cb001b2..fd09fe76c02 100644
--- a/lib/gitlab/auth/user_access_denied_reason.rb
+++ b/lib/gitlab/auth/user_access_denied_reason.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Auth
class UserAccessDeniedReason
diff --git a/lib/gitlab/auth/user_auth_finders.rb b/lib/gitlab/auth/user_auth_finders.rb
index 064cba43278..5df6db6f366 100644
--- a/lib/gitlab/auth/user_auth_finders.rb
+++ b/lib/gitlab/auth/user_auth_finders.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Auth
AuthenticationError = Class.new(StandardError)
diff --git a/lib/gitlab/badge/base.rb b/lib/gitlab/badge/base.rb
index 909fa24fa90..fb55b9e2f1f 100644
--- a/lib/gitlab/badge/base.rb
+++ b/lib/gitlab/badge/base.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Badge
class Base
diff --git a/lib/gitlab/badge/coverage/metadata.rb b/lib/gitlab/badge/coverage/metadata.rb
index e898f5d790e..9181ba2d4b0 100644
--- a/lib/gitlab/badge/coverage/metadata.rb
+++ b/lib/gitlab/badge/coverage/metadata.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Badge
module Coverage
diff --git a/lib/gitlab/badge/coverage/report.rb b/lib/gitlab/badge/coverage/report.rb
index 16fd6f01495..a7fcb6b0fca 100644
--- a/lib/gitlab/badge/coverage/report.rb
+++ b/lib/gitlab/badge/coverage/report.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Badge
module Coverage
diff --git a/lib/gitlab/badge/coverage/template.rb b/lib/gitlab/badge/coverage/template.rb
index afbf9dd17e3..817dc28f84a 100644
--- a/lib/gitlab/badge/coverage/template.rb
+++ b/lib/gitlab/badge/coverage/template.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Badge
module Coverage
diff --git a/lib/gitlab/badge/metadata.rb b/lib/gitlab/badge/metadata.rb
index 8ad6f3cb986..b9ae68134b0 100644
--- a/lib/gitlab/badge/metadata.rb
+++ b/lib/gitlab/badge/metadata.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Badge
##
diff --git a/lib/gitlab/badge/pipeline/metadata.rb b/lib/gitlab/badge/pipeline/metadata.rb
index db1e9f8cfb8..d4d789558c9 100644
--- a/lib/gitlab/badge/pipeline/metadata.rb
+++ b/lib/gitlab/badge/pipeline/metadata.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Badge
module Pipeline
diff --git a/lib/gitlab/badge/pipeline/status.rb b/lib/gitlab/badge/pipeline/status.rb
index d1d9b7949f5..37e61f07e5b 100644
--- a/lib/gitlab/badge/pipeline/status.rb
+++ b/lib/gitlab/badge/pipeline/status.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Badge
module Pipeline
diff --git a/lib/gitlab/badge/pipeline/template.rb b/lib/gitlab/badge/pipeline/template.rb
index e09db32262d..64c3dfcd10b 100644
--- a/lib/gitlab/badge/pipeline/template.rb
+++ b/lib/gitlab/badge/pipeline/template.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Badge
module Pipeline
diff --git a/lib/gitlab/badge/template.rb b/lib/gitlab/badge/template.rb
index bfeb0052642..ed2ec50b197 100644
--- a/lib/gitlab/badge/template.rb
+++ b/lib/gitlab/badge/template.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Badge
##
diff --git a/lib/gitlab/bare_repository_import/importer.rb b/lib/gitlab/bare_repository_import/importer.rb
index 04aa6aab771..3cd327f5109 100644
--- a/lib/gitlab/bare_repository_import/importer.rb
+++ b/lib/gitlab/bare_repository_import/importer.rb
@@ -1,10 +1,15 @@
+# frozen_string_literal: true
+
module Gitlab
module BareRepositoryImport
class Importer
NoAdminError = Class.new(StandardError)
def self.execute(import_path)
- import_path << '/' unless import_path.ends_with?('/')
+ unless import_path.ends_with?('/')
+ import_path = "#{import_path}/"
+ end
+
repos_to_import = Dir.glob(import_path + '**/*.git')
unless user = User.admins.order_id_asc.first
diff --git a/lib/gitlab/bare_repository_import/repository.rb b/lib/gitlab/bare_repository_import/repository.rb
index c0c666dfb7b..b903c581aac 100644
--- a/lib/gitlab/bare_repository_import/repository.rb
+++ b/lib/gitlab/bare_repository_import/repository.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module BareRepositoryImport
class Repository
@@ -6,9 +8,12 @@ module Gitlab
attr_reader :group_path, :project_name, :repo_path
def initialize(root_path, repo_path)
+ unless root_path.ends_with?('/')
+ root_path = "#{root_path}/"
+ end
+
@root_path = root_path
@repo_path = repo_path
- @root_path << '/' unless root_path.ends_with?('/')
full_path =
if hashed? && !wiki?
diff --git a/lib/gitlab/bitbucket_import/importer.rb b/lib/gitlab/bitbucket_import/importer.rb
index a7dfccea2f6..45e550b3450 100644
--- a/lib/gitlab/bitbucket_import/importer.rb
+++ b/lib/gitlab/bitbucket_import/importer.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module BitbucketImport
class Importer
diff --git a/lib/gitlab/bitbucket_import/project_creator.rb b/lib/gitlab/bitbucket_import/project_creator.rb
index d94f70fd1fb..11070a68e02 100644
--- a/lib/gitlab/bitbucket_import/project_creator.rb
+++ b/lib/gitlab/bitbucket_import/project_creator.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module BitbucketImport
class ProjectCreator
diff --git a/lib/gitlab/bitbucket_server_import/project_creator.rb b/lib/gitlab/bitbucket_server_import/project_creator.rb
index 35e8cd7e0ab..48ca4951957 100644
--- a/lib/gitlab/bitbucket_server_import/project_creator.rb
+++ b/lib/gitlab/bitbucket_server_import/project_creator.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module BitbucketServerImport
class ProjectCreator
diff --git a/lib/gitlab/cache/ci/project_pipeline_status.rb b/lib/gitlab/cache/ci/project_pipeline_status.rb
index add048d671e..b369b9e7600 100644
--- a/lib/gitlab/cache/ci/project_pipeline_status.rb
+++ b/lib/gitlab/cache/ci/project_pipeline_status.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# This class is not backed by a table in the main database.
# It loads the latest Pipeline for the HEAD of a repository, and caches that
# in Redis.
diff --git a/lib/gitlab/cache/request_cache.rb b/lib/gitlab/cache/request_cache.rb
index b96e161a5b6..4c658dc0b8d 100644
--- a/lib/gitlab/cache/request_cache.rb
+++ b/lib/gitlab/cache/request_cache.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Cache
# See https://docs.gitlab.com/ee/development/utilities.html#requestcache
diff --git a/lib/gitlab/checks/change_access.rb b/lib/gitlab/checks/change_access.rb
index 7a4224e5bbe..49e7f7e1fd7 100644
--- a/lib/gitlab/checks/change_access.rb
+++ b/lib/gitlab/checks/change_access.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Checks
class ChangeAccess
diff --git a/lib/gitlab/checks/commit_check.rb b/lib/gitlab/checks/commit_check.rb
index 7e0c34aada3..6dd74e8fb74 100644
--- a/lib/gitlab/checks/commit_check.rb
+++ b/lib/gitlab/checks/commit_check.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Checks
class CommitCheck
diff --git a/lib/gitlab/checks/force_push.rb b/lib/gitlab/checks/force_push.rb
index 87af4a90572..263972923ed 100644
--- a/lib/gitlab/checks/force_push.rb
+++ b/lib/gitlab/checks/force_push.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Checks
class ForcePush
diff --git a/lib/gitlab/checks/lfs_integrity.rb b/lib/gitlab/checks/lfs_integrity.rb
index 3f7adecc621..fa3dc1808df 100644
--- a/lib/gitlab/checks/lfs_integrity.rb
+++ b/lib/gitlab/checks/lfs_integrity.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Checks
class LfsIntegrity
diff --git a/lib/gitlab/checks/matching_merge_request.rb b/lib/gitlab/checks/matching_merge_request.rb
index 86f4aaeb4d3..71361b12d07 100644
--- a/lib/gitlab/checks/matching_merge_request.rb
+++ b/lib/gitlab/checks/matching_merge_request.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Checks
class MatchingMergeRequest
diff --git a/lib/gitlab/checks/post_push_message.rb b/lib/gitlab/checks/post_push_message.rb
index 473c0385b34..492dbb5a596 100644
--- a/lib/gitlab/checks/post_push_message.rb
+++ b/lib/gitlab/checks/post_push_message.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Checks
class PostPushMessage
diff --git a/lib/gitlab/checks/project_created.rb b/lib/gitlab/checks/project_created.rb
index cec270d6a58..0058a402a62 100644
--- a/lib/gitlab/checks/project_created.rb
+++ b/lib/gitlab/checks/project_created.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Checks
class ProjectCreated < PostPushMessage
diff --git a/lib/gitlab/checks/project_moved.rb b/lib/gitlab/checks/project_moved.rb
index 3a197078d08..cb3b7acaaad 100644
--- a/lib/gitlab/checks/project_moved.rb
+++ b/lib/gitlab/checks/project_moved.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module Checks
class ProjectMoved < PostPushMessage
diff --git a/lib/gitlab/ci/templates/Android.gitlab-ci.yml b/lib/gitlab/ci/templates/Android.gitlab-ci.yml
index 5f9d54ff574..bf7831b937c 100644
--- a/lib/gitlab/ci/templates/Android.gitlab-ci.yml
+++ b/lib/gitlab/ci/templates/Android.gitlab-ci.yml
@@ -2,33 +2,33 @@
image: openjdk:8-jdk
variables:
- ANDROID_COMPILE_SDK: "25"
- ANDROID_BUILD_TOOLS: "24.0.0"
- ANDROID_SDK_TOOLS: "24.4.1"
+ ANDROID_COMPILE_SDK: "28"
+ ANDROID_BUILD_TOOLS: "28.0.3"
+ ANDROID_SDK_TOOLS: "26.1.1"
before_script:
- - apt-get --quiet update --yes
- - apt-get --quiet install --yes wget tar unzip lib32stdc++6 lib32z1
- - wget --quiet --output-document=android-sdk.tgz https://dl.google.com/android/android-sdk_r${ANDROID_SDK_TOOLS}-linux.tgz
- - tar --extract --gzip --file=android-sdk.tgz
- - echo y | android-sdk-linux/tools/android --silent update sdk --no-ui --all --filter android-${ANDROID_COMPILE_SDK}
- - echo y | android-sdk-linux/tools/android --silent update sdk --no-ui --all --filter platform-tools
- - echo y | android-sdk-linux/tools/android --silent update sdk --no-ui --all --filter build-tools-${ANDROID_BUILD_TOOLS}
- - echo y | android-sdk-linux/tools/android --silent update sdk --no-ui --all --filter extra-android-m2repository
- - echo y | android-sdk-linux/tools/android --silent update sdk --no-ui --all --filter extra-google-google_play_services
- - echo y | android-sdk-linux/tools/android --silent update sdk --no-ui --all --filter extra-google-m2repository
- - export ANDROID_HOME=$PWD/android-sdk-linux
- - export PATH=$PATH:$PWD/android-sdk-linux/platform-tools/
- - chmod +x ./gradlew
+- apt-get --quiet update --yes
+- apt-get --quiet install --yes wget tar unzip lib32stdc++6 lib32z1
+- wget --quiet --output-document=android-sdk.zip https://dl.google.com/android/repository/sdk-tools-linux-4333796.zip
+- unzip android-sdk.zip -d android-sdk-linux
+- echo y | android-sdk-linux/tools/bin/sdkmanager "platforms;android-${ANDROID_COMPILE_SDK}" > /dev/null
+- echo y | android-sdk-linux/tools/bin/sdkmanager platform-tools > /dev/null
+- echo y | android-sdk-linux/tools/bin/sdkmanager "build-tools;${ANDROID_BUILD_TOOLS}" > /dev/null
+- echo y | android-sdk-linux/tools/bin/sdkmanager "extras;google;google_play_services" > /dev/null
+- echo y | android-sdk-linux/tools/bin/sdkmanager "extras;google;m2repository" > /dev/null
+- export ANDROID_HOME=$PWD/android-sdk-linux
+- export PATH=$PATH:$PWD/android-sdk-linux/platform-tools/
+- yes | android-sdk-linux/tools/bin/sdkmanager --licenses &
+- chmod +x ./gradlew
stages:
- - build
- - test
+- build
+- test
build:
stage: build
script:
- - ./gradlew assembleDebug
+ - ./gradlew assembleDebug
artifacts:
paths:
- app/build/outputs/
@@ -36,7 +36,7 @@ build:
unitTests:
stage: test
script:
- - ./gradlew test
+ - ./gradlew test
functionalTests:
stage: test
diff --git a/lib/gitlab/ci/templates/Auto-DevOps.gitlab-ci.yml b/lib/gitlab/ci/templates/Auto-DevOps.gitlab-ci.yml
index 72547c1b407..6fa59e41d20 100644
--- a/lib/gitlab/ci/templates/Auto-DevOps.gitlab-ci.yml
+++ b/lib/gitlab/ci/templates/Auto-DevOps.gitlab-ci.yml
@@ -126,8 +126,8 @@ license_management:
artifacts:
paths: [gl-license-management-report.json]
only:
- - branches
- only:
+ refs:
+ - branches
variables:
- $GITLAB_FEATURES =~ /\blicense_management\b/
except:
diff --git a/lib/gitlab/gon_helper.rb b/lib/gitlab/gon_helper.rb
index deaa14c8434..c1726659a90 100644
--- a/lib/gitlab/gon_helper.rb
+++ b/lib/gitlab/gon_helper.rb
@@ -30,5 +30,20 @@ module Gitlab
gon.current_user_avatar_url = current_user.avatar_url
end
end
+
+ # Exposes the state of a feature flag to the frontend code.
+ #
+ # name - The name of the feature flag, e.g. `my_feature`.
+ # args - Any additional arguments to pass to `Feature.enabled?`. This allows
+ # you to check if a flag is enabled for a particular user.
+ def push_frontend_feature_flag(name, *args)
+ var_name = name.to_s.camelize(:lower)
+ enabled = Feature.enabled?(name, *args)
+
+ # Here the `true` argument signals gon that the value should be merged
+ # into any existing ones, instead of overwriting them. This allows you to
+ # use this method to push multiple feature flags.
+ gon.push({ features: { var_name => enabled } }, true)
+ end
end
end
diff --git a/lib/google_api/auth.rb b/lib/google_api/auth.rb
index 1aeaa387a49..e724e58e9ca 100644
--- a/lib/google_api/auth.rb
+++ b/lib/google_api/auth.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module GoogleApi
class Auth
attr_reader :access_token, :redirect_uri, :state
diff --git a/lib/google_api/cloud_platform/client.rb b/lib/google_api/cloud_platform/client.rb
index 77b6610286f..e74ff6a9129 100644
--- a/lib/google_api/cloud_platform/client.rb
+++ b/lib/google_api/cloud_platform/client.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'google/apis/compute_v1'
require 'google/apis/container_v1'
require 'google/apis/cloudbilling_v1'
diff --git a/lib/haml_lint/inline_javascript.rb b/lib/haml_lint/inline_javascript.rb
index adbed20f152..5ecd6169ecf 100644
--- a/lib/haml_lint/inline_javascript.rb
+++ b/lib/haml_lint/inline_javascript.rb
@@ -1,4 +1,7 @@
-unless Rails.env.production? # rubocop:disable Naming/FileName
+# rubocop:disable Naming/FileName
+# frozen_string_literal: true
+
+unless Rails.env.production?
require 'haml_lint/haml_visitor'
require 'haml_lint/linter'
require 'haml_lint/linter_registry'
diff --git a/lib/json_web_token/rsa_token.rb b/lib/json_web_token/rsa_token.rb
index d6d6af7089c..160e1e506f1 100644
--- a/lib/json_web_token/rsa_token.rb
+++ b/lib/json_web_token/rsa_token.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module JSONWebToken
class RSAToken < Token
attr_reader :key_file
diff --git a/lib/json_web_token/token.rb b/lib/json_web_token/token.rb
index 5b67715b0b2..ce5d6f248d0 100644
--- a/lib/json_web_token/token.rb
+++ b/lib/json_web_token/token.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module JSONWebToken
class Token
attr_accessor :issuer, :subject, :audience, :id
diff --git a/lib/mattermost/client.rb b/lib/mattermost/client.rb
index d80cd7d2a4e..293d0c563c5 100644
--- a/lib/mattermost/client.rb
+++ b/lib/mattermost/client.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Mattermost
ClientError = Class.new(Mattermost::Error)
diff --git a/lib/mattermost/command.rb b/lib/mattermost/command.rb
index 704813dfdf0..a02745486d6 100644
--- a/lib/mattermost/command.rb
+++ b/lib/mattermost/command.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Mattermost
class Command < Client
def create(params)
diff --git a/lib/mattermost/error.rb b/lib/mattermost/error.rb
index dee6deb7974..054bd5457bd 100644
--- a/lib/mattermost/error.rb
+++ b/lib/mattermost/error.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Mattermost
Error = Class.new(StandardError)
end
diff --git a/lib/mattermost/session.rb b/lib/mattermost/session.rb
index 2aa7a2f64d8..e2083848a8d 100644
--- a/lib/mattermost/session.rb
+++ b/lib/mattermost/session.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Mattermost
class NoSessionError < Mattermost::Error
def message
diff --git a/lib/mattermost/team.rb b/lib/mattermost/team.rb
index 95c2f6f9d6b..58120178f50 100644
--- a/lib/mattermost/team.rb
+++ b/lib/mattermost/team.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Mattermost
class Team < Client
# Returns all teams that the current user is a member of
diff --git a/lib/microsoft_teams/activity.rb b/lib/microsoft_teams/activity.rb
index d2c420efdaf..207e90d2638 100644
--- a/lib/microsoft_teams/activity.rb
+++ b/lib/microsoft_teams/activity.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module MicrosoftTeams
class Activity
def initialize(title:, subtitle:, text:, image:)
diff --git a/lib/microsoft_teams/notifier.rb b/lib/microsoft_teams/notifier.rb
index 226ee1373db..c7dec09ba6b 100644
--- a/lib/microsoft_teams/notifier.rb
+++ b/lib/microsoft_teams/notifier.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module MicrosoftTeams
class Notifier
def initialize(webhook)
diff --git a/lib/object_storage/direct_upload.rb b/lib/object_storage/direct_upload.rb
index 97f56e10ccf..fd26663fef0 100644
--- a/lib/object_storage/direct_upload.rb
+++ b/lib/object_storage/direct_upload.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module ObjectStorage
#
# The DirectUpload c;ass generates a set of presigned URLs
diff --git a/lib/omni_auth/strategies/bitbucket.rb b/lib/omni_auth/strategies/bitbucket.rb
index ce1bdfe6ee4..6c914b4222a 100644
--- a/lib/omni_auth/strategies/bitbucket.rb
+++ b/lib/omni_auth/strategies/bitbucket.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'omniauth-oauth2'
module OmniAuth
diff --git a/lib/omni_auth/strategies/jwt.rb b/lib/omni_auth/strategies/jwt.rb
index ebdb5c7faf0..a792903fde7 100644
--- a/lib/omni_auth/strategies/jwt.rb
+++ b/lib/omni_auth/strategies/jwt.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'omniauth'
require 'jwt'
diff --git a/lib/peek/rblineprof/custom_controller_helpers.rb b/lib/peek/rblineprof/custom_controller_helpers.rb
index 9beb442bfa3..581cc6a37b4 100644
--- a/lib/peek/rblineprof/custom_controller_helpers.rb
+++ b/lib/peek/rblineprof/custom_controller_helpers.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Peek
module Rblineprof
module CustomControllerHelpers
@@ -41,7 +43,7 @@ module Peek
]
end.sort_by{ |a,b,c,d,e,f| -f }
- output = "<div class='modal-dialog modal-xl'><div class='modal-content'>"
+ output = ["<div class='modal-dialog modal-xl'><div class='modal-content'>"]
output << "<div class='modal-header'>"
output << "<h4>Line profiling: #{human_description(params[:lineprofiler])}</h4>"
output << "<button class='close' type='button' data-dismiss='modal' aria-label='close'><span aria-hidden='true'>&times;</span></button>"
@@ -93,7 +95,7 @@ module Peek
output << "</div></div></div>"
- response.body += "<div class='modal' id='modal-peek-line-profile' tabindex=-1>#{output}</div>".html_safe
+ response.body += "<div class='modal' id='modal-peek-line-profile' tabindex=-1>#{output.join}</div>".html_safe
end
ret
diff --git a/lib/peek/views/gitaly.rb b/lib/peek/views/gitaly.rb
index ab35f7a2258..860963ef94f 100644
--- a/lib/peek/views/gitaly.rb
+++ b/lib/peek/views/gitaly.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Peek
module Views
class Gitaly < View
diff --git a/lib/peek/views/host.rb b/lib/peek/views/host.rb
index 43c8a35c7ea..b77355ea11b 100644
--- a/lib/peek/views/host.rb
+++ b/lib/peek/views/host.rb
@@ -1,8 +1,13 @@
+# frozen_string_literal: true
+
module Peek
module Views
class Host < View
def results
- { hostname: Gitlab::Environment.hostname }
+ {
+ hostname: Gitlab::Environment.hostname,
+ canary: Gitlab::Utils.to_boolean(ENV['CANARY'])
+ }
end
end
end
diff --git a/lib/rouge/formatters/html_gitlab.rb b/lib/rouge/formatters/html_gitlab.rb
index e877ab10248..e2a7d3ef5ba 100644
--- a/lib/rouge/formatters/html_gitlab.rb
+++ b/lib/rouge/formatters/html_gitlab.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Rouge
module Formatters
class HTMLGitlab < Rouge::Formatters::HTML
diff --git a/lib/rouge/plugins/common_mark.rb b/lib/rouge/plugins/common_mark.rb
index 8f9de061124..d240df5a0e0 100644
--- a/lib/rouge/plugins/common_mark.rb
+++ b/lib/rouge/plugins/common_mark.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# A rouge plugin for CommonMark markdown engine.
# Used to highlight code generated by CommonMark.
diff --git a/lib/rspec_flaky/config.rb b/lib/rspec_flaky/config.rb
index 06e96f969f1..55c1d4747b4 100644
--- a/lib/rspec_flaky/config.rb
+++ b/lib/rspec_flaky/config.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module RspecFlaky
class Config
def self.generate_report?
diff --git a/lib/rspec_flaky/example.rb b/lib/rspec_flaky/example.rb
index b6e790cbbab..3c1b05257a0 100644
--- a/lib/rspec_flaky/example.rb
+++ b/lib/rspec_flaky/example.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module RspecFlaky
# This is a wrapper class for RSpec::Core::Example
class Example
diff --git a/lib/rspec_flaky/flaky_example.rb b/lib/rspec_flaky/flaky_example.rb
index 6be24014d89..da5dbf06bc9 100644
--- a/lib/rspec_flaky/flaky_example.rb
+++ b/lib/rspec_flaky/flaky_example.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module RspecFlaky
# This represents a flaky RSpec example and is mainly meant to be saved in a JSON file
class FlakyExample < OpenStruct
diff --git a/lib/rspec_flaky/flaky_examples_collection.rb b/lib/rspec_flaky/flaky_examples_collection.rb
index dea23c325be..290a51766e9 100644
--- a/lib/rspec_flaky/flaky_examples_collection.rb
+++ b/lib/rspec_flaky/flaky_examples_collection.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'active_support/hash_with_indifferent_access'
require_relative 'flaky_example'
diff --git a/lib/rspec_flaky/listener.rb b/lib/rspec_flaky/listener.rb
index 9cd0c38cb55..19cc0baa2d3 100644
--- a/lib/rspec_flaky/listener.rb
+++ b/lib/rspec_flaky/listener.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'json'
require_dependency 'rspec_flaky/config'
diff --git a/lib/rspec_flaky/report.rb b/lib/rspec_flaky/report.rb
index 1c362fdd20d..9a0fb88c424 100644
--- a/lib/rspec_flaky/report.rb
+++ b/lib/rspec_flaky/report.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'json'
require 'time'
diff --git a/lib/system_check/app/active_users_check.rb b/lib/system_check/app/active_users_check.rb
index 1d72c8d6903..8446c2fc2c8 100644
--- a/lib/system_check/app/active_users_check.rb
+++ b/lib/system_check/app/active_users_check.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module SystemCheck
module App
class ActiveUsersCheck < SystemCheck::BaseCheck
diff --git a/lib/system_check/app/database_config_exists_check.rb b/lib/system_check/app/database_config_exists_check.rb
index d1fae192350..1769145ed63 100644
--- a/lib/system_check/app/database_config_exists_check.rb
+++ b/lib/system_check/app/database_config_exists_check.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module SystemCheck
module App
class DatabaseConfigExistsCheck < SystemCheck::BaseCheck
diff --git a/lib/system_check/app/git_config_check.rb b/lib/system_check/app/git_config_check.rb
index d08a81639e3..4e8d607096c 100644
--- a/lib/system_check/app/git_config_check.rb
+++ b/lib/system_check/app/git_config_check.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module SystemCheck
module App
class GitConfigCheck < SystemCheck::BaseCheck
diff --git a/lib/system_check/app/git_user_default_ssh_config_check.rb b/lib/system_check/app/git_user_default_ssh_config_check.rb
index ad41760dff2..6cd53779bfd 100644
--- a/lib/system_check/app/git_user_default_ssh_config_check.rb
+++ b/lib/system_check/app/git_user_default_ssh_config_check.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module SystemCheck
module App
class GitUserDefaultSSHConfigCheck < SystemCheck::BaseCheck
diff --git a/lib/system_check/app/git_version_check.rb b/lib/system_check/app/git_version_check.rb
index 44ec888c197..994af3ab53e 100644
--- a/lib/system_check/app/git_version_check.rb
+++ b/lib/system_check/app/git_version_check.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module SystemCheck
module App
class GitVersionCheck < SystemCheck::BaseCheck
diff --git a/lib/system_check/app/gitlab_config_exists_check.rb b/lib/system_check/app/gitlab_config_exists_check.rb
index 247aa0994e4..1cc5ead0d89 100644
--- a/lib/system_check/app/gitlab_config_exists_check.rb
+++ b/lib/system_check/app/gitlab_config_exists_check.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module SystemCheck
module App
class GitlabConfigExistsCheck < SystemCheck::BaseCheck
diff --git a/lib/system_check/app/gitlab_config_up_to_date_check.rb b/lib/system_check/app/gitlab_config_up_to_date_check.rb
index c609e48e133..58c7e3039c8 100644
--- a/lib/system_check/app/gitlab_config_up_to_date_check.rb
+++ b/lib/system_check/app/gitlab_config_up_to_date_check.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module SystemCheck
module App
class GitlabConfigUpToDateCheck < SystemCheck::BaseCheck
diff --git a/lib/system_check/app/init_script_exists_check.rb b/lib/system_check/app/init_script_exists_check.rb
index d246e058e86..d36dbe7d67d 100644
--- a/lib/system_check/app/init_script_exists_check.rb
+++ b/lib/system_check/app/init_script_exists_check.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module SystemCheck
module App
class InitScriptExistsCheck < SystemCheck::BaseCheck
diff --git a/lib/system_check/app/init_script_up_to_date_check.rb b/lib/system_check/app/init_script_up_to_date_check.rb
index 53a47eb0f42..569c41df6e4 100644
--- a/lib/system_check/app/init_script_up_to_date_check.rb
+++ b/lib/system_check/app/init_script_up_to_date_check.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module SystemCheck
module App
class InitScriptUpToDateCheck < SystemCheck::BaseCheck
diff --git a/lib/system_check/app/log_writable_check.rb b/lib/system_check/app/log_writable_check.rb
index 3e0c436d6ee..e26ad143eb8 100644
--- a/lib/system_check/app/log_writable_check.rb
+++ b/lib/system_check/app/log_writable_check.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module SystemCheck
module App
class LogWritableCheck < SystemCheck::BaseCheck
diff --git a/lib/system_check/app/migrations_are_up_check.rb b/lib/system_check/app/migrations_are_up_check.rb
index 5eedbacce77..b12e9ac6bba 100644
--- a/lib/system_check/app/migrations_are_up_check.rb
+++ b/lib/system_check/app/migrations_are_up_check.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module SystemCheck
module App
class MigrationsAreUpCheck < SystemCheck::BaseCheck
diff --git a/lib/system_check/app/orphaned_group_members_check.rb b/lib/system_check/app/orphaned_group_members_check.rb
index 2b46d36fe51..3e6ffb8190b 100644
--- a/lib/system_check/app/orphaned_group_members_check.rb
+++ b/lib/system_check/app/orphaned_group_members_check.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module SystemCheck
module App
class OrphanedGroupMembersCheck < SystemCheck::BaseCheck
diff --git a/lib/system_check/app/projects_have_namespace_check.rb b/lib/system_check/app/projects_have_namespace_check.rb
index a6ec9f7665c..2bf2529acf1 100644
--- a/lib/system_check/app/projects_have_namespace_check.rb
+++ b/lib/system_check/app/projects_have_namespace_check.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module SystemCheck
module App
class ProjectsHaveNamespaceCheck < SystemCheck::BaseCheck
diff --git a/lib/system_check/app/redis_version_check.rb b/lib/system_check/app/redis_version_check.rb
index a0610e73576..890f8b44d13 100644
--- a/lib/system_check/app/redis_version_check.rb
+++ b/lib/system_check/app/redis_version_check.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module SystemCheck
module App
class RedisVersionCheck < SystemCheck::BaseCheck
diff --git a/lib/system_check/app/ruby_version_check.rb b/lib/system_check/app/ruby_version_check.rb
index 57bbabece1f..d73c39f2c3f 100644
--- a/lib/system_check/app/ruby_version_check.rb
+++ b/lib/system_check/app/ruby_version_check.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module SystemCheck
module App
class RubyVersionCheck < SystemCheck::BaseCheck
diff --git a/lib/system_check/app/tmp_writable_check.rb b/lib/system_check/app/tmp_writable_check.rb
index 99a75e57abf..6687df091d3 100644
--- a/lib/system_check/app/tmp_writable_check.rb
+++ b/lib/system_check/app/tmp_writable_check.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module SystemCheck
module App
class TmpWritableCheck < SystemCheck::BaseCheck
diff --git a/lib/system_check/app/uploads_directory_exists_check.rb b/lib/system_check/app/uploads_directory_exists_check.rb
index 7026d0ba075..940eff9d4cf 100644
--- a/lib/system_check/app/uploads_directory_exists_check.rb
+++ b/lib/system_check/app/uploads_directory_exists_check.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module SystemCheck
module App
class UploadsDirectoryExistsCheck < SystemCheck::BaseCheck
diff --git a/lib/system_check/app/uploads_path_permission_check.rb b/lib/system_check/app/uploads_path_permission_check.rb
index 7df6c060254..4a49f3bc2bb 100644
--- a/lib/system_check/app/uploads_path_permission_check.rb
+++ b/lib/system_check/app/uploads_path_permission_check.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module SystemCheck
module App
class UploadsPathPermissionCheck < SystemCheck::BaseCheck
diff --git a/lib/system_check/app/uploads_path_tmp_permission_check.rb b/lib/system_check/app/uploads_path_tmp_permission_check.rb
index b276a81eac1..ae374f4707c 100644
--- a/lib/system_check/app/uploads_path_tmp_permission_check.rb
+++ b/lib/system_check/app/uploads_path_tmp_permission_check.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module SystemCheck
module App
class UploadsPathTmpPermissionCheck < SystemCheck::BaseCheck
diff --git a/lib/system_check/base_check.rb b/lib/system_check/base_check.rb
index 0f5742dd67f..e06245294c4 100644
--- a/lib/system_check/base_check.rb
+++ b/lib/system_check/base_check.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module SystemCheck
# Base class for Checks. You must inherit from here
# and implement the methods below when necessary
diff --git a/lib/system_check/helpers.rb b/lib/system_check/helpers.rb
index 6227e461d24..07d479848fe 100644
--- a/lib/system_check/helpers.rb
+++ b/lib/system_check/helpers.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module SystemCheck
module Helpers
include ::Gitlab::TaskHelpers
diff --git a/lib/system_check/incoming_email/foreman_configured_check.rb b/lib/system_check/incoming_email/foreman_configured_check.rb
index 1db7bf2b782..944913087da 100644
--- a/lib/system_check/incoming_email/foreman_configured_check.rb
+++ b/lib/system_check/incoming_email/foreman_configured_check.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module SystemCheck
module IncomingEmail
class ForemanConfiguredCheck < SystemCheck::BaseCheck
diff --git a/lib/system_check/incoming_email/imap_authentication_check.rb b/lib/system_check/incoming_email/imap_authentication_check.rb
index 3550c5796b0..613c2296375 100644
--- a/lib/system_check/incoming_email/imap_authentication_check.rb
+++ b/lib/system_check/incoming_email/imap_authentication_check.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module SystemCheck
module IncomingEmail
class ImapAuthenticationCheck < SystemCheck::BaseCheck
diff --git a/lib/system_check/incoming_email/initd_configured_check.rb b/lib/system_check/incoming_email/initd_configured_check.rb
index ea23b8ef49c..acb4b5a9e74 100644
--- a/lib/system_check/incoming_email/initd_configured_check.rb
+++ b/lib/system_check/incoming_email/initd_configured_check.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module SystemCheck
module IncomingEmail
class InitdConfiguredCheck < SystemCheck::BaseCheck
diff --git a/lib/system_check/incoming_email/mail_room_running_check.rb b/lib/system_check/incoming_email/mail_room_running_check.rb
index c1807501829..b7aead4624e 100644
--- a/lib/system_check/incoming_email/mail_room_running_check.rb
+++ b/lib/system_check/incoming_email/mail_room_running_check.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module SystemCheck
module IncomingEmail
class MailRoomRunningCheck < SystemCheck::BaseCheck
diff --git a/lib/system_check/orphans/namespace_check.rb b/lib/system_check/orphans/namespace_check.rb
index 09b57c7b408..53b2d8fd5b3 100644
--- a/lib/system_check/orphans/namespace_check.rb
+++ b/lib/system_check/orphans/namespace_check.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module SystemCheck
module Orphans
class NamespaceCheck < SystemCheck::BaseCheck
diff --git a/lib/system_check/orphans/repository_check.rb b/lib/system_check/orphans/repository_check.rb
index 2695c658874..ef8fe945f61 100644
--- a/lib/system_check/orphans/repository_check.rb
+++ b/lib/system_check/orphans/repository_check.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module SystemCheck
module Orphans
class RepositoryCheck < SystemCheck::BaseCheck
diff --git a/lib/system_check/simple_executor.rb b/lib/system_check/simple_executor.rb
index 99c9e984107..11818ae54f8 100644
--- a/lib/system_check/simple_executor.rb
+++ b/lib/system_check/simple_executor.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module SystemCheck
# Simple Executor is current default executor for GitLab
# It is a simple port from display logic in the old check.rake
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index 8e0324eb194..c1fdad64229 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -4236,6 +4236,9 @@ msgstr ""
msgid "Operations"
msgstr ""
+msgid "Operations Dashboard"
+msgstr ""
+
msgid "Optionally, you can %{link_to_customize} how FogBugz email addresses and usernames are imported into GitLab."
msgstr ""
diff --git a/package.json b/package.json
index ac9a73cd2c9..dafb03bf75a 100644
--- a/package.json
+++ b/package.json
@@ -24,7 +24,7 @@
"@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.29.0",
+ "@gitlab-org/gitlab-svgs": "^1.32.0",
"@gitlab-org/gitlab-ui": "^1.8.0",
"autosize": "^4.0.0",
"axios": "^0.17.1",
diff --git a/qa/qa.rb b/qa/qa.rb
index a0511186e70..e1737a16622 100644
--- a/qa/qa.rb
+++ b/qa/qa.rb
@@ -97,6 +97,7 @@ module QA
module Integration
autoload :Github, 'qa/scenario/test/integration/github'
autoload :LDAP, 'qa/scenario/test/integration/ldap'
+ autoload :InstanceSAML, 'qa/scenario/test/integration/instance_saml'
autoload :Kubernetes, 'qa/scenario/test/integration/kubernetes'
autoload :Mattermost, 'qa/scenario/test/integration/mattermost'
autoload :ObjectStorage, 'qa/scenario/test/integration/object_storage'
@@ -180,6 +181,7 @@ module QA
autoload :SecretVariables, 'qa/page/project/settings/secret_variables'
autoload :Runners, 'qa/page/project/settings/runners'
autoload :MergeRequest, 'qa/page/project/settings/merge_request'
+ autoload :Members, 'qa/page/project/settings/members'
end
module Issue
@@ -267,6 +269,7 @@ module QA
autoload :GroupsFilter, 'qa/page/component/groups_filter'
autoload :Select2, 'qa/page/component/select2'
autoload :DropdownFilter, 'qa/page/component/dropdown_filter'
+ autoload :UsersSelect, 'qa/page/component/users_select'
module Issuable
autoload :Common, 'qa/page/component/issuable/common'
@@ -300,6 +303,18 @@ module QA
autoload :Config, 'qa/specs/config'
autoload :Runner, 'qa/specs/runner'
end
+
+ ##
+ # Classes that describe the structure of vendor/third party application pages
+ #
+ module Vendor
+ module SAMLIdp
+ module Page
+ autoload :Base, 'qa/vendor/saml_idp/page/base'
+ autoload :Login, 'qa/vendor/saml_idp/page/login'
+ end
+ end
+ end
end
QA::Runtime::Release.extend_autoloads!
diff --git a/qa/qa/page/component/users_select.rb b/qa/qa/page/component/users_select.rb
new file mode 100644
index 00000000000..f88d6450a33
--- /dev/null
+++ b/qa/qa/page/component/users_select.rb
@@ -0,0 +1,14 @@
+# frozen_string_literal: true
+
+module QA
+ module Page
+ module Component
+ module UsersSelect
+ def select_user(element, username)
+ find("#{element_selector_css(element)} input").set(username)
+ find('.ajax-users-dropdown .user-username', text: "@#{username}").click
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/page/main/login.rb b/qa/qa/page/main/login.rb
index eab7a85ff04..94b9486b0d5 100644
--- a/qa/qa/page/main/login.rb
+++ b/qa/qa/page/main/login.rb
@@ -31,6 +31,10 @@ module QA
element :register_tab
end
+ view 'app/views/devise/shared/_omniauth_box.html.haml' do
+ element :saml_login_button
+ end
+
def initialize
# The login page is usually the entry point for all the scenarios so
# we need to wait for the instance to start. That said, in some cases
@@ -130,6 +134,11 @@ module QA
click_element :sign_in_button
end
+ def sign_in_with_saml
+ set_initial_password_if_present
+ click_element :saml_login_button
+ end
+
def sign_in_using_gitlab_credentials(user)
switch_to_sign_in_tab if has_sign_in_tab?
switch_to_standard_tab if has_standard_tab?
diff --git a/qa/qa/page/project/menu.rb b/qa/qa/page/project/menu.rb
index d9f01c50f19..63c719e5fe1 100644
--- a/qa/qa/page/project/menu.rb
+++ b/qa/qa/page/project/menu.rb
@@ -9,6 +9,7 @@ module QA
element :settings_link, 'link_to edit_project_path'
element :repository_link, "title: _('Repository')"
element :link_pipelines
+ element :link_members_settings
element :pipelines_settings_link, "title: _('CI / CD')"
element :operations_kubernetes_link, "title: _('Kubernetes')"
element :operations_environments_link
@@ -51,6 +52,14 @@ module QA
end
end
+ def click_members_settings
+ hover_settings do
+ within_submenu do
+ click_element :link_members_settings
+ end
+ end
+ end
+
def click_operations_kubernetes
hover_operations do
within_submenu do
diff --git a/qa/qa/page/project/settings/members.rb b/qa/qa/page/project/settings/members.rb
new file mode 100644
index 00000000000..7fed93ca83f
--- /dev/null
+++ b/qa/qa/page/project/settings/members.rb
@@ -0,0 +1,27 @@
+# frozen_string_literal: true
+
+module QA
+ module Page
+ module Project
+ module Settings
+ class Members < Page::Base
+ include Page::Component::UsersSelect
+
+ view 'app/views/projects/project_members/_new_project_member.html.haml' do
+ element :member_select_input
+ element :add_member_button
+ end
+
+ view 'app/views/projects/project_members/_team.html.haml' do
+ element :members_list
+ end
+
+ def add_member(username)
+ select_user :member_select_input, username
+ click_element :add_member_button
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/runtime/browser.rb b/qa/qa/runtime/browser.rb
index 4c64270ce92..9aaf57e8d83 100644
--- a/qa/qa/runtime/browser.rb
+++ b/qa/qa/runtime/browser.rb
@@ -51,6 +51,10 @@ module QA
}
)
+ if QA::Runtime::Env.accept_insecure_certs?
+ capabilities['acceptInsecureCerts'] = true
+ end
+
options = Selenium::WebDriver::Chrome::Options.new
options.add_argument("window-size=1240,1680")
diff --git a/qa/qa/runtime/env.rb b/qa/qa/runtime/env.rb
index 5bebb5ccec0..4a2109799fa 100644
--- a/qa/qa/runtime/env.rb
+++ b/qa/qa/runtime/env.rb
@@ -8,6 +8,10 @@ module QA
enabled?(ENV['CHROME_HEADLESS'])
end
+ def accept_insecure_certs?
+ enabled?(ENV['ACCEPT_INSECURE_CERTS'])
+ end
+
def running_in_ci?
ENV['CI'] || ENV['CI_SERVER']
end
diff --git a/qa/qa/scenario/test/integration/instance_saml.rb b/qa/qa/scenario/test/integration/instance_saml.rb
new file mode 100644
index 00000000000..0697d0c2a0e
--- /dev/null
+++ b/qa/qa/scenario/test/integration/instance_saml.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+module QA
+ module Scenario
+ module Test
+ module Integration
+ class InstanceSAML < Test::Instance::All
+ tags :instance_saml
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/specs/features/browser_ui/1_manage/login/login_via_instance_wide_saml_sso_spec.rb b/qa/qa/specs/features/browser_ui/1_manage/login/login_via_instance_wide_saml_sso_spec.rb
new file mode 100644
index 00000000000..8d5055aab45
--- /dev/null
+++ b/qa/qa/specs/features/browser_ui/1_manage/login/login_via_instance_wide_saml_sso_spec.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+module QA
+ context :manage, :orchestrated, :instance_saml do
+ describe 'Instance wide SAML SSO' do
+ it 'User logs in to gitlab with SAML SSO' do
+ Runtime::Browser.visit(:gitlab, Page::Main::Login)
+
+ Page::Main::Login.act { sign_in_with_saml }
+
+ Vendor::SAMLIdp::Page::Login.act { login }
+
+ expect(page).to have_content('Welcome to GitLab')
+ end
+ end
+ end
+end
diff --git a/qa/qa/specs/features/browser_ui/1_manage/project/add_project_member_spec.rb b/qa/qa/specs/features/browser_ui/1_manage/project/add_project_member_spec.rb
new file mode 100644
index 00000000000..b276c7ee579
--- /dev/null
+++ b/qa/qa/specs/features/browser_ui/1_manage/project/add_project_member_spec.rb
@@ -0,0 +1,27 @@
+# frozen_string_literal: true
+
+module QA
+ context :manage do
+ describe 'Add project member' do
+ it 'user adds project member' do
+ Runtime::Browser.visit(:gitlab, Page::Main::Login)
+
+ user = Factory::Resource::User.fabricate!
+
+ Page::Main::Menu.perform { |main| main.sign_out }
+ Page::Main::Login.act { sign_in_using_credentials }
+
+ Factory::Resource::Project.fabricate! do |resource|
+ resource.name = 'add-member-project'
+ end
+
+ Page::Project::Menu.act { click_members_settings }
+ Page::Project::Settings::Members.perform do |page|
+ page.add_member(user.username)
+ end
+
+ expect(page).to have_content("#{user.name} @#{user.username} Given access")
+ end
+ end
+ end
+end
diff --git a/qa/qa/vendor/saml_idp/page/base.rb b/qa/qa/vendor/saml_idp/page/base.rb
new file mode 100644
index 00000000000..286cb0a8cd8
--- /dev/null
+++ b/qa/qa/vendor/saml_idp/page/base.rb
@@ -0,0 +1,14 @@
+# frozen_string_literal: true
+
+module QA
+ module Vendor
+ module SAMLIdp
+ module Page
+ class Base
+ include Capybara::DSL
+ include Scenario::Actable
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/vendor/saml_idp/page/login.rb b/qa/qa/vendor/saml_idp/page/login.rb
new file mode 100644
index 00000000000..9c1f9904a7a
--- /dev/null
+++ b/qa/qa/vendor/saml_idp/page/login.rb
@@ -0,0 +1,19 @@
+# frozen_string_literal: true
+
+require 'capybara/dsl'
+
+module QA
+ module Vendor
+ module SAMLIdp
+ module Page
+ class Login < Page::Base
+ def login
+ fill_in 'username', with: 'user1'
+ fill_in 'password', with: 'user1pass'
+ click_on 'Login'
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/qa/spec/scenario/test/integration/instance_saml_spec.rb b/qa/spec/scenario/test/integration/instance_saml_spec.rb
new file mode 100644
index 00000000000..cb8a6a630cc
--- /dev/null
+++ b/qa/spec/scenario/test/integration/instance_saml_spec.rb
@@ -0,0 +1,9 @@
+# frozen_string_literal: true
+
+describe QA::Scenario::Test::Integration::InstanceSAML do
+ context '#perform' do
+ it_behaves_like 'a QA scenario class' do
+ let(:tags) { [:instance_saml] }
+ end
+ end
+end
diff --git a/spec/controllers/projects/jobs_controller_spec.rb b/spec/controllers/projects/jobs_controller_spec.rb
index 5c8180baf8a..1484676eea3 100644
--- a/spec/controllers/projects/jobs_controller_spec.rb
+++ b/spec/controllers/projects/jobs_controller_spec.rb
@@ -352,6 +352,10 @@ describe Projects::JobsController, :clean_gitlab_redis_shared_state do
expect(json_response['has_trace']).to be true
end
end
+
+ it 'exposes the stage the job belongs to' do
+ expect(json_response['stage']).to eq('test')
+ end
end
context 'when requesting JSON job is triggered' do
diff --git a/spec/features/groups/show_spec.rb b/spec/features/groups/show_spec.rb
index ac961e98a61..4e6f73ef58a 100644
--- a/spec/features/groups/show_spec.rb
+++ b/spec/features/groups/show_spec.rb
@@ -101,4 +101,25 @@ describe 'Group show page' do
expect(page).to have_emoji('smile')
end
end
+
+ context 'where group has projects' do
+ let(:user) { create(:user) }
+
+ before do
+ group.add_owner(user)
+ sign_in(user)
+ end
+
+ it 'allows users to sorts projects by most stars', :js do
+ project1 = create(:project, namespace: group, star_count: 2)
+ project2 = create(:project, namespace: group, star_count: 3)
+ project3 = create(:project, namespace: group, star_count: 0)
+
+ visit group_path(group, sort: :stars_desc)
+
+ expect(find('.group-row:nth-child(1) .namespace-title > a')).to have_content(project2.title)
+ expect(find('.group-row:nth-child(2) .namespace-title > a')).to have_content(project1.title)
+ expect(find('.group-row:nth-child(3) .namespace-title > a')).to have_content(project3.title)
+ end
+ end
end
diff --git a/spec/features/projects/jobs_spec.rb b/spec/features/projects/jobs_spec.rb
index 2076ce7b4f7..6224cbffe9d 100644
--- a/spec/features/projects/jobs_spec.rb
+++ b/spec/features/projects/jobs_spec.rb
@@ -663,6 +663,56 @@ describe 'Jobs', :clean_gitlab_redis_shared_state do
expect(page).to have_content('This job does not have a trace.')
end
end
+
+ context 'with erased job', :js do
+ let(:job) { create(:ci_build, :erased, pipeline: pipeline) }
+
+ it 'renders erased job warning' do
+ visit project_job_path(project, job)
+ wait_for_requests
+
+ page.within('.js-job-erased-block') do
+ expect(page).to have_content('Job has been erased')
+ end
+ end
+ end
+
+ context 'without erased job', :js do
+ let(:job) { create(:ci_build, pipeline: pipeline) }
+
+ it 'does not render erased job warning' do
+ visit project_job_path(project, job)
+ wait_for_requests
+
+ expect(page).not_to have_css('.js-job-erased-block')
+ end
+ end
+
+ context 'on mobile', :js do
+ let(:job) { create(:ci_build, pipeline: pipeline) }
+
+ it 'renders collpased sidebar' do
+ page.current_window.resize_to(600, 800)
+
+ visit project_job_path(project, job)
+ wait_for_requests
+
+ expect(page).to have_css('.js-build-sidebar.right-sidebar-collapsed', visible: false)
+ expect(page).not_to have_css('.js-build-sidebar.right-sidebar-expanded', visible: false)
+ end
+ end
+
+ context 'on desktop', :js do
+ let(:job) { create(:ci_build, pipeline: pipeline) }
+
+ it 'renders expanded sidebar' do
+ visit project_job_path(project, job)
+ wait_for_requests
+
+ expect(page).to have_css('.js-build-sidebar.right-sidebar-expanded')
+ expect(page).not_to have_css('.js-build-sidebar.right-sidebar-collpased')
+ end
+ end
end
describe "POST /:project/jobs/:id/cancel", :js do
diff --git a/spec/finders/branches_finder_spec.rb b/spec/finders/branches_finder_spec.rb
index 9e3f2c69606..7d164539d9a 100644
--- a/spec/finders/branches_finder_spec.rb
+++ b/spec/finders/branches_finder_spec.rb
@@ -66,7 +66,7 @@ describe BranchesFinder do
context 'filter and sort' do
it 'filters branches by name and sorts by recently_updated' do
- params = { sort: 'updated_desc', search: 'feature' }
+ params = { sort: 'updated_desc', search: 'feat' }
branches_finder = described_class.new(repository, params)
result = branches_finder.execute
@@ -75,6 +75,17 @@ describe BranchesFinder do
expect(result.count).to eq(2)
end
+ it 'filters branches by name and sorts by recently_updated, with exact matches first' do
+ params = { sort: 'updated_desc', search: 'feature' }
+ branches_finder = described_class.new(repository, params)
+
+ result = branches_finder.execute
+
+ expect(result.first.name).to eq('feature')
+ expect(result.second.name).to eq('feature_conflict')
+ expect(result.count).to eq(2)
+ end
+
it 'filters branches by name and sorts by last_updated' do
params = { sort: 'updated_asc', search: 'feature' }
branches_finder = described_class.new(repository, params)
@@ -84,6 +95,26 @@ describe BranchesFinder do
expect(result.first.name).to eq('feature')
expect(result.count).to eq(2)
end
+
+ it 'filters branches by name that begins with' do
+ params = { search: '^feature_' }
+ branches_finder = described_class.new(repository, params)
+
+ result = branches_finder.execute
+
+ expect(result.first.name).to eq('feature_conflict')
+ expect(result.count).to eq(1)
+ end
+
+ it 'filters branches by name that ends with' do
+ params = { search: 'feature$' }
+ branches_finder = described_class.new(repository, params)
+
+ result = branches_finder.execute
+
+ expect(result.first.name).to eq('feature')
+ expect(result.count).to eq(1)
+ end
end
end
end
diff --git a/spec/fixtures/api/schemas/job/job_details.json b/spec/fixtures/api/schemas/job/job_details.json
index 07e674216fa..8218474705c 100644
--- a/spec/fixtures/api/schemas/job/job_details.json
+++ b/spec/fixtures/api/schemas/job/job_details.json
@@ -7,7 +7,8 @@
"artifact",
"runner",
"runners",
- "has_trace"
+ "has_trace",
+ "stage"
],
"properties": {
"artifact": { "$ref": "artifact.json" },
@@ -16,6 +17,7 @@
"deployment_status": { "$ref": "deployment_status.json" },
"runner": { "$ref": "runner.json" },
"runners": { "$ref": "runners.json" },
- "has_trace": { "type": "boolean" }
+ "has_trace": { "type": "boolean" },
+ "stage": { "type": "string" }
}
}
diff --git a/spec/helpers/preferences_helper_spec.rb b/spec/helpers/preferences_helper_spec.rb
index 363ebc88afd..c112c8ed633 100644
--- a/spec/helpers/preferences_helper_spec.rb
+++ b/spec/helpers/preferences_helper_spec.rb
@@ -2,6 +2,13 @@ require 'spec_helper'
describe PreferencesHelper do
describe '#dashboard_choices' do
+ let(:user) { build(:user) }
+
+ before do
+ allow(helper).to receive(:current_user).and_return(user)
+ allow(helper).to receive(:can?).and_return(false)
+ end
+
it 'raises an exception when defined choices may be missing' do
expect(User).to receive(:dashboards).and_return(foo: 'foo')
expect { helper.dashboard_choices }.to raise_error(RuntimeError)
diff --git a/spec/javascripts/.eslintrc.yml b/spec/javascripts/.eslintrc.yml
index 9b2c84ce9f5..1448849a0b7 100644
--- a/spec/javascripts/.eslintrc.yml
+++ b/spec/javascripts/.eslintrc.yml
@@ -37,7 +37,5 @@ rules:
- 'fixtures/blob'
# Temporarily disabled to facilitate an upgrade to eslint-plugin-jasmine
jasmine/new-line-before-expect: off
- jasmine/new-line-between-declarations: off
jasmine/no-promise-without-done-fail: off
- jasmine/prefer-jasmine-matcher: off
jasmine/prefer-toHaveBeenCalledWith: off
diff --git a/spec/javascripts/awards_handler_spec.js b/spec/javascripts/awards_handler_spec.js
index 0c5d68990d5..3f84a46e8d9 100644
--- a/spec/javascripts/awards_handler_spec.js
+++ b/spec/javascripts/awards_handler_spec.js
@@ -55,6 +55,7 @@ import '~/lib/utils/common_utils';
});
};
});
+
afterEach(function() {
// restore original url root value
gon.relative_url_root = urlRoot;
@@ -64,6 +65,7 @@ import '~/lib/utils/common_utils';
awardsHandler.destroy();
});
+
describe('::showEmojiMenu', function() {
it('should show emoji menu when Add emoji button clicked', function(done) {
$('.js-add-award')
@@ -78,6 +80,7 @@ import '~/lib/utils/common_utils';
return expect($('.js-awards-block.current').length).toBe(1);
});
});
+
it('should also show emoji menu for the smiley icon in notes', function(done) {
$('.js-add-award.note-action-button').click();
return lazyAssert(done, function() {
@@ -85,6 +88,7 @@ import '~/lib/utils/common_utils';
return expect($emojiMenu.length).toBe(1);
});
});
+
it('should remove emoji menu when body is clicked', function(done) {
$('.js-add-award')
.eq(0)
@@ -98,6 +102,7 @@ import '~/lib/utils/common_utils';
return expect($('.js-awards-block.current').length).toBe(0);
});
});
+
it('should not remove emoji menu when search is clicked', function(done) {
$('.js-add-award')
.eq(0)
@@ -123,6 +128,7 @@ import '~/lib/utils/common_utils';
expect($emojiButton.next('.js-counter').text()).toBe('1');
return expect($votesBlock.hasClass('hidden')).toBe(false);
});
+
it('should remove the emoji when we click again', function() {
var $emojiButton, $votesBlock;
$votesBlock = $('.js-awards-block').eq(0);
@@ -142,6 +148,7 @@ import '~/lib/utils/common_utils';
return expect($emojiButton.next('.js-counter').text()).toBe('4');
});
});
+
describe('::userAuthored', function() {
it('should update tooltip to user authored title', function() {
var $thumbsUpEmoji, $votesBlock;
@@ -153,6 +160,7 @@ import '~/lib/utils/common_utils';
'You cannot vote on your own issue, MR and note',
);
});
+
it('should restore tooltip back to initial vote list', function() {
var $thumbsUpEmoji, $votesBlock;
jasmine.clock().install();
@@ -165,6 +173,7 @@ import '~/lib/utils/common_utils';
return expect($thumbsUpEmoji.data('originalTitle')).toBe('sam');
});
});
+
describe('::getAwardUrl', function() {
return it('returns the url for request', function() {
return expect(awardsHandler.getAwardUrl()).toBe(
@@ -172,6 +181,7 @@ import '~/lib/utils/common_utils';
);
});
});
+
describe('::addAward and ::checkMutuality', function() {
return it('should handle :+1: and :-1: mutuality', function() {
var $thumbsDownEmoji, $thumbsUpEmoji, $votesBlock, awardUrl;
@@ -189,6 +199,7 @@ import '~/lib/utils/common_utils';
return expect($thumbsDownEmoji.hasClass('active')).toBe(true);
});
});
+
describe('::removeEmoji', function() {
return it('should remove emoji', function() {
var $votesBlock, awardUrl;
@@ -200,6 +211,7 @@ import '~/lib/utils/common_utils';
return expect($votesBlock.find('[data-name=fire]').length).toBe(0);
});
});
+
describe('::addYouToUserList', function() {
it('should prepend "You" to the award tooltip', function() {
var $thumbsUpEmoji, $votesBlock, awardUrl;
@@ -222,6 +234,7 @@ import '~/lib/utils/common_utils';
return expect($thumbsUpEmoji.data('originalTitle')).toBe('You and sam');
});
});
+
describe('::removeYouToUserList', function() {
it('removes "You" from the front of the tooltip', function() {
var $thumbsUpEmoji, $votesBlock, awardUrl;
@@ -246,6 +259,7 @@ import '~/lib/utils/common_utils';
return expect($thumbsUpEmoji.data('originalTitle')).toBe('sam');
});
});
+
describe('::searchEmojis', () => {
it('should filter the emoji', function(done) {
return openAndWaitForEmojiMenu()
@@ -263,6 +277,7 @@ import '~/lib/utils/common_utils';
done.fail(`Failed to open and build emoji menu: ${err.message}`);
});
});
+
it('should clear the search when searching for nothing', function(done) {
return openAndWaitForEmojiMenu()
.then(() => {
@@ -305,6 +320,7 @@ import '~/lib/utils/common_utils';
done.fail(`Failed to open and build emoji menu: ${err.message}`);
});
});
+
it('should remove already selected emoji', function(done) {
return openEmojiMenuAndAddEmoji()
.then(() => {
diff --git a/spec/javascripts/behaviors/quick_submit_spec.js b/spec/javascripts/behaviors/quick_submit_spec.js
index d8aa5c636da..b3a5eac8982 100644
--- a/spec/javascripts/behaviors/quick_submit_spec.js
+++ b/spec/javascripts/behaviors/quick_submit_spec.js
@@ -58,12 +58,14 @@ describe('Quick Submit behavior', function () {
expect(submitButton).toBeDisabled();
});
+
it('disables button of type submit', () => {
const submitButton = $('.js-quick-submit input[type=submit]');
this.textarea.trigger(keydownEvent());
expect(submitButton).toBeDisabled();
});
+
it('only clicks one submit', () => {
const existingSubmit = $('.js-quick-submit input[type=submit]');
// Add an extra submit button
diff --git a/spec/javascripts/bootstrap_jquery_spec.js b/spec/javascripts/bootstrap_jquery_spec.js
index 052465d8d88..cd61d920fa0 100644
--- a/spec/javascripts/bootstrap_jquery_spec.js
+++ b/spec/javascripts/bootstrap_jquery_spec.js
@@ -9,6 +9,7 @@ import '~/commons/bootstrap';
beforeEach(function() {
return setFixtures('<input type="text" />');
});
+
it('adds the disabled attribute', function() {
var $input;
$input = $('input').first();
@@ -26,6 +27,7 @@ import '~/commons/bootstrap';
beforeEach(function() {
return setFixtures('<input type="text" disabled="disabled" class="disabled" />');
});
+
it('removes the disabled attribute', function() {
var $input;
$input = $('input').first();
diff --git a/spec/javascripts/datetime_utility_spec.js b/spec/javascripts/datetime_utility_spec.js
index 6c3e73f134e..24e4871ce44 100644
--- a/spec/javascripts/datetime_utility_spec.js
+++ b/spec/javascripts/datetime_utility_spec.js
@@ -158,9 +158,9 @@ describe('getTimeframeWindowFrom', () => {
const timeframe = datetimeUtility.getTimeframeWindowFrom(startDate, 5);
expect(timeframe.length).toBe(5);
timeframe.forEach((timeframeItem, index) => {
- expect(timeframeItem.getFullYear() === mockTimeframe[index].getFullYear()).toBe(true);
- expect(timeframeItem.getMonth() === mockTimeframe[index].getMonth()).toBe(true);
- expect(timeframeItem.getDate() === mockTimeframe[index].getDate()).toBeTruthy();
+ expect(timeframeItem.getFullYear()).toBe(mockTimeframe[index].getFullYear());
+ expect(timeframeItem.getMonth()).toBe(mockTimeframe[index].getMonth());
+ expect(timeframeItem.getDate()).toBe(mockTimeframe[index].getDate());
});
});
});
diff --git a/spec/javascripts/diffs/components/diff_file_header_spec.js b/spec/javascripts/diffs/components/diff_file_header_spec.js
index c986ea604b2..1f7d5f42322 100644
--- a/spec/javascripts/diffs/components/diff_file_header_spec.js
+++ b/spec/javascripts/diffs/components/diff_file_header_spec.js
@@ -6,6 +6,8 @@ import DiffFileHeader from '~/diffs/components/diff_file_header.vue';
import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils';
import { mountComponentWithStore } from 'spec/helpers/vue_mount_component_helper';
+Vue.use(Vuex);
+
const discussionFixture = 'merge_requests/diff_discussion.json';
describe('diff_file_header', () => {
@@ -58,19 +60,19 @@ describe('diff_file_header', () => {
describe('titleLink', () => {
beforeEach(() => {
+ props.discussionPath = 'link://to/discussion';
Object.assign(props.diffFile, {
- fileHash: 'badc0ffee',
submoduleLink: 'link://to/submodule',
submoduleTreeUrl: 'some://tree/url',
});
});
- it('returns the fileHash for files', () => {
+ it('returns the discussionPath for files', () => {
props.diffFile.submodule = false;
vm = mountComponentWithStore(Component, { props, store });
- expect(vm.titleLink).toBe(`#${props.diffFile.fileHash}`);
+ expect(vm.titleLink).toBe(props.discussionPath);
});
it('returns the submoduleTreeUrl for submodules', () => {
@@ -91,6 +93,13 @@ describe('diff_file_header', () => {
expect(vm.titleLink).toBe(props.diffFile.submoduleLink);
});
+
+ it('sets the correct path to the discussion', () => {
+ props.discussionPath = 'link://to/discussion';
+ vm = mountComponentWithStore(Component, { props, store });
+ const href = vm.$el.querySelector('.js-title-wrapper').getAttribute('href');
+ expect(href).toBe(vm.discussionPath);
+ });
});
describe('filePath', () => {
diff --git a/spec/javascripts/diffs/components/diff_file_spec.js b/spec/javascripts/diffs/components/diff_file_spec.js
index 13859f43e98..b8d4b31ee04 100644
--- a/spec/javascripts/diffs/components/diff_file_spec.js
+++ b/spec/javascripts/diffs/components/diff_file_spec.js
@@ -24,14 +24,14 @@ describe('DiffFile', () => {
expect(el.querySelectorAll('.diff-content.hidden').length).toEqual(0);
expect(el.querySelector('.js-file-title')).toBeDefined();
- expect(el.querySelector('.file-title-name').innerText.indexOf(filePath) > -1).toEqual(true);
+ expect(el.querySelector('.file-title-name').innerText.indexOf(filePath)).toBeGreaterThan(-1);
expect(el.querySelector('.js-syntax-highlight')).toBeDefined();
expect(vm.file.renderIt).toEqual(false);
vm.file.renderIt = true;
vm.$nextTick(() => {
- expect(el.querySelectorAll('.line_content').length > 5).toEqual(true);
+ expect(el.querySelectorAll('.line_content').length).toBeGreaterThan(5);
});
});
@@ -98,9 +98,7 @@ describe('DiffFile', () => {
'This source diff could not be displayed because it is too large',
);
expect(vm.$el.querySelector('.js-too-large-diff')).toBeDefined();
- expect(vm.$el.querySelector('.js-too-large-diff a').href.indexOf(BLOB_LINK) > -1).toEqual(
- true,
- );
+ expect(vm.$el.querySelector('.js-too-large-diff a').href.indexOf(BLOB_LINK)).toBeGreaterThan(-1);
done();
});
diff --git a/spec/javascripts/diffs/components/diff_line_gutter_content_spec.js b/spec/javascripts/diffs/components/diff_line_gutter_content_spec.js
index 663c0680845..f36454cc23e 100644
--- a/spec/javascripts/diffs/components/diff_line_gutter_content_spec.js
+++ b/spec/javascripts/diffs/components/diff_line_gutter_content_spec.js
@@ -94,7 +94,7 @@ describe('DiffLineGutterContent', () => {
const component = createComponent({ lineNumber, lineCode });
const link = component.$el.querySelector('a');
- expect(link.href.indexOf(`#${lineCode}`) > -1).toEqual(true);
+ expect(link.href.indexOf(`#${lineCode}`)).toBeGreaterThan(-1);
expect(link.dataset.linenumber).toEqual(lineNumber.toString());
});
diff --git a/spec/javascripts/diffs/components/inline_diff_view_spec.js b/spec/javascripts/diffs/components/inline_diff_view_spec.js
index b02328dd359..705558e860b 100644
--- a/spec/javascripts/diffs/components/inline_diff_view_spec.js
+++ b/spec/javascripts/diffs/components/inline_diff_view_spec.js
@@ -27,7 +27,7 @@ describe('InlineDiffView', () => {
expect(el.querySelectorAll('tr.line_holder').length).toEqual(6);
expect(el.querySelectorAll('tr.line_holder.new').length).toEqual(2);
expect(el.querySelectorAll('tr.line_holder.match').length).toEqual(1);
- expect(el.textContent.indexOf('Bad dates') > -1).toEqual(true);
+ expect(el.textContent.indexOf('Bad dates')).toBeGreaterThan(-1);
});
it('should render discussions', done => {
@@ -37,7 +37,7 @@ describe('InlineDiffView', () => {
Vue.nextTick(() => {
expect(el.querySelectorAll('.notes_holder').length).toEqual(1);
expect(el.querySelectorAll('.notes_holder .note-discussion li').length).toEqual(5);
- expect(el.innerText.indexOf('comment 5') > -1).toEqual(true);
+ expect(el.innerText.indexOf('comment 5')).toBeGreaterThan(-1);
component.$store.dispatch('setInitialNotes', []);
done();
diff --git a/spec/javascripts/emoji_spec.js b/spec/javascripts/emoji_spec.js
index 124d91f4477..629422780e8 100644
--- a/spec/javascripts/emoji_spec.js
+++ b/spec/javascripts/emoji_spec.js
@@ -140,6 +140,7 @@ describe('gl_emoji', () => {
},
);
});
+
it('bomb emoji with sprite fallback', () => {
const emojiKey = 'bomb';
const markup = glEmojiTag(emojiFixtureMap[emojiKey].name, {
@@ -195,24 +196,31 @@ describe('gl_emoji', () => {
it('should gracefully handle empty string', () => {
expect(isFlagEmoji('')).toBeFalsy();
});
+
it('should detect flag_ac', () => {
expect(isFlagEmoji('🇦🇨')).toBeTruthy();
});
+
it('should detect flag_us', () => {
expect(isFlagEmoji('🇺🇸')).toBeTruthy();
});
+
it('should detect flag_zw', () => {
expect(isFlagEmoji('🇿🇼')).toBeTruthy();
});
+
it('should not detect flags', () => {
expect(isFlagEmoji('🎏')).toBeFalsy();
});
+
it('should not detect triangular_flag_on_post', () => {
expect(isFlagEmoji('🚩')).toBeFalsy();
});
+
it('should not detect single letter', () => {
expect(isFlagEmoji('🇦')).toBeFalsy();
});
+
it('should not detect >2 letters', () => {
expect(isFlagEmoji('🇦🇧🇨')).toBeFalsy();
});
@@ -222,15 +230,19 @@ describe('gl_emoji', () => {
it('should gracefully handle empty string', () => {
expect(isRainbowFlagEmoji('')).toBeFalsy();
});
+
it('should detect rainbow_flag', () => {
expect(isRainbowFlagEmoji('🏳🌈')).toBeTruthy();
});
+
it('should not detect flag_white on its\' own', () => {
expect(isRainbowFlagEmoji('🏳')).toBeFalsy();
});
+
it('should not detect rainbow on its\' own', () => {
expect(isRainbowFlagEmoji('🌈')).toBeFalsy();
});
+
it('should not detect flag_white with something else', () => {
expect(isRainbowFlagEmoji('🏳🔵')).toBeFalsy();
});
@@ -240,15 +252,19 @@ describe('gl_emoji', () => {
it('should gracefully handle empty string', () => {
expect(isKeycapEmoji('')).toBeFalsy();
});
+
it('should detect one(keycap)', () => {
expect(isKeycapEmoji('1️⃣')).toBeTruthy();
});
+
it('should detect nine(keycap)', () => {
expect(isKeycapEmoji('9️⃣')).toBeTruthy();
});
+
it('should not detect ten(keycap)', () => {
expect(isKeycapEmoji('🔟')).toBeFalsy();
});
+
it('should not detect hash(keycap)', () => {
expect(isKeycapEmoji('#⃣')).toBeFalsy();
});
@@ -258,24 +274,31 @@ describe('gl_emoji', () => {
it('should gracefully handle empty string', () => {
expect(isSkinToneComboEmoji('')).toBeFalsy();
});
+
it('should detect hand_splayed_tone5', () => {
expect(isSkinToneComboEmoji('🖐🏿')).toBeTruthy();
});
+
it('should not detect hand_splayed', () => {
expect(isSkinToneComboEmoji('🖐')).toBeFalsy();
});
+
it('should detect lifter_tone1', () => {
expect(isSkinToneComboEmoji('🏋🏻')).toBeTruthy();
});
+
it('should not detect lifter', () => {
expect(isSkinToneComboEmoji('🏋')).toBeFalsy();
});
+
it('should detect rowboat_tone4', () => {
expect(isSkinToneComboEmoji('🚣🏾')).toBeTruthy();
});
+
it('should not detect rowboat', () => {
expect(isSkinToneComboEmoji('🚣')).toBeFalsy();
});
+
it('should not detect individual tone emoji', () => {
expect(isSkinToneComboEmoji('🏻')).toBeFalsy();
});
@@ -285,9 +308,11 @@ describe('gl_emoji', () => {
it('should gracefully handle empty string', () => {
expect(isHorceRacingSkinToneComboEmoji('')).toBeFalsy();
});
+
it('should detect horse_racing_tone2', () => {
expect(isHorceRacingSkinToneComboEmoji('🏇🏼')).toBeTruthy();
});
+
it('should not detect horse_racing', () => {
expect(isHorceRacingSkinToneComboEmoji('🏇')).toBeFalsy();
});
@@ -297,36 +322,47 @@ describe('gl_emoji', () => {
it('should gracefully handle empty string', () => {
expect(isPersonZwjEmoji('')).toBeFalsy();
});
+
it('should detect couple_mm', () => {
expect(isPersonZwjEmoji('👨‍❤️‍👨')).toBeTruthy();
});
+
it('should not detect couple_with_heart', () => {
expect(isPersonZwjEmoji('💑')).toBeFalsy();
});
+
it('should not detect couplekiss', () => {
expect(isPersonZwjEmoji('💏')).toBeFalsy();
});
+
it('should detect family_mmb', () => {
expect(isPersonZwjEmoji('👨‍👨‍👦')).toBeTruthy();
});
+
it('should detect family_mwgb', () => {
expect(isPersonZwjEmoji('👨‍👩‍👧‍👦')).toBeTruthy();
});
+
it('should not detect family', () => {
expect(isPersonZwjEmoji('👪')).toBeFalsy();
});
+
it('should detect kiss_ww', () => {
expect(isPersonZwjEmoji('👩‍❤️‍💋‍👩')).toBeTruthy();
});
+
it('should not detect girl', () => {
expect(isPersonZwjEmoji('👧')).toBeFalsy();
});
+
it('should not detect girl_tone5', () => {
expect(isPersonZwjEmoji('👧🏿')).toBeFalsy();
});
+
it('should not detect man', () => {
expect(isPersonZwjEmoji('👨')).toBeFalsy();
});
+
it('should not detect woman', () => {
expect(isPersonZwjEmoji('👩')).toBeFalsy();
});
@@ -341,6 +377,7 @@ describe('gl_emoji', () => {
);
expect(isSupported).toBeTruthy();
});
+
it('should gracefully handle empty string without unicode support', () => {
const isSupported = isEmojiUnicodeSupported(
{},
@@ -349,6 +386,7 @@ describe('gl_emoji', () => {
);
expect(isSupported).toBeFalsy();
});
+
it('bomb(6.0) with 6.0 support', () => {
const emojiKey = 'bomb';
const unicodeSupportMap = Object.assign({}, emptySupportMap, {
diff --git a/spec/javascripts/filtered_search/dropdown_utils_spec.js b/spec/javascripts/filtered_search/dropdown_utils_spec.js
index 68bbbf838da..3dc8089cd83 100644
--- a/spec/javascripts/filtered_search/dropdown_utils_spec.js
+++ b/spec/javascripts/filtered_search/dropdown_utils_spec.js
@@ -237,7 +237,7 @@ describe('Dropdown Utils', () => {
it('should not linear-gradient more than 4 colors', () => {
const gradient = DropdownUtils.duplicateLabelColor(['#FFFFFF', '#000000', '#333333', '#DDDDDD', '#EEEEEE']);
- expect(gradient.indexOf('#EEEEEE') === -1).toEqual(true);
+ expect(gradient.indexOf('#EEEEEE')).toBe(-1);
});
});
diff --git a/spec/javascripts/filtered_search/filtered_search_token_keys_spec.js b/spec/javascripts/filtered_search/filtered_search_token_keys_spec.js
index ab0ab72720e..cf7789a1d57 100644
--- a/spec/javascripts/filtered_search/filtered_search_token_keys_spec.js
+++ b/spec/javascripts/filtered_search/filtered_search_token_keys_spec.js
@@ -19,7 +19,7 @@ describe('Filtered Search Token Keys', () => {
describe('get', () => {
it('should return tokenKeys', () => {
- expect(new FilteredSearchTokenKeys().get() !== null).toBe(true);
+ expect(new FilteredSearchTokenKeys().get()).not.toBeNull();
});
it('should return tokenKeys as an array', () => {
@@ -40,7 +40,7 @@ describe('Filtered Search Token Keys', () => {
describe('getConditions', () => {
it('should return conditions', () => {
- expect(new FilteredSearchTokenKeys().getConditions() !== null).toBe(true);
+ expect(new FilteredSearchTokenKeys().getConditions()).not.toBeNull();
});
it('should return conditions as an array', () => {
@@ -51,7 +51,7 @@ describe('Filtered Search Token Keys', () => {
describe('searchByKey', () => {
it('should return null when key not found', () => {
const tokenKey = new FilteredSearchTokenKeys(tokenKeys).searchByKey('notakey');
- expect(tokenKey === null).toBe(true);
+ expect(tokenKey).toBeNull();
});
it('should return tokenKey when found by key', () => {
@@ -63,7 +63,7 @@ describe('Filtered Search Token Keys', () => {
describe('searchBySymbol', () => {
it('should return null when symbol not found', () => {
const tokenKey = new FilteredSearchTokenKeys(tokenKeys).searchBySymbol('notasymbol');
- expect(tokenKey === null).toBe(true);
+ expect(tokenKey).toBeNull();
});
it('should return tokenKey when found by symbol', () => {
@@ -75,7 +75,7 @@ describe('Filtered Search Token Keys', () => {
describe('searchByKeyParam', () => {
it('should return null when key param not found', () => {
const tokenKey = new FilteredSearchTokenKeys(tokenKeys).searchByKeyParam('notakeyparam');
- expect(tokenKey === null).toBe(true);
+ expect(tokenKey).toBeNull();
});
it('should return tokenKey when found by key param', () => {
@@ -92,7 +92,7 @@ describe('Filtered Search Token Keys', () => {
describe('searchByConditionUrl', () => {
it('should return null when condition url not found', () => {
const condition = new FilteredSearchTokenKeys([], [], conditions).searchByConditionUrl(null);
- expect(condition === null).toBe(true);
+ expect(condition).toBeNull();
});
it('should return condition when found by url', () => {
@@ -106,7 +106,7 @@ describe('Filtered Search Token Keys', () => {
it('should return null when condition tokenKey and value not found', () => {
const condition = new FilteredSearchTokenKeys([], [], conditions)
.searchByConditionKeyValue(null, null);
- expect(condition === null).toBe(true);
+ expect(condition).toBeNull();
});
it('should return condition when found by tokenKey and value', () => {
diff --git a/spec/javascripts/gl_dropdown_spec.js b/spec/javascripts/gl_dropdown_spec.js
index 25b819543da..62c87642184 100644
--- a/spec/javascripts/gl_dropdown_spec.js
+++ b/spec/javascripts/gl_dropdown_spec.js
@@ -168,9 +168,9 @@ describe('glDropdown', function describeDropdown() {
it('should show loading indicator while search results are being fetched by backend', () => {
const dropdownMenu = document.querySelector('.dropdown-menu');
- expect(dropdownMenu.className.indexOf('is-loading') !== -1).toEqual(true);
+ expect(dropdownMenu.className.indexOf('is-loading')).not.toBe(-1);
remoteCallback();
- expect(dropdownMenu.className.indexOf('is-loading') !== -1).toEqual(false);
+ expect(dropdownMenu.className.indexOf('is-loading')).toBe(-1);
});
it('should not focus search input while remote task is not complete', () => {
diff --git a/spec/javascripts/graphs/stat_graph_contributors_util_spec.js b/spec/javascripts/graphs/stat_graph_contributors_util_spec.js
index 02d1ca1cc3b..8854d3d554a 100644
--- a/spec/javascripts/graphs/stat_graph_contributors_util_spec.js
+++ b/spec/javascripts/graphs/stat_graph_contributors_util_spec.js
@@ -205,10 +205,12 @@ describe("ContributorsStatGraphUtil", function () {
it("returns true if date_range is null", function () {
expect(ContributorsStatGraphUtil.in_range(date, null)).toEqual(true);
});
+
it("returns true if date is in range", function () {
var date_range = [new Date("2013-01-01"), new Date("2013-12-12")];
expect(ContributorsStatGraphUtil.in_range(date, date_range)).toEqual(true);
});
+
it("returns false if date is not in range", function () {
var date_range = [new Date("1999-12-01"), new Date("2000-12-01")];
expect(ContributorsStatGraphUtil.in_range(date, date_range)).toEqual(false);
diff --git a/spec/javascripts/groups/components/group_item_spec.js b/spec/javascripts/groups/components/group_item_spec.js
index d0cac5efc40..49d4f7efd72 100644
--- a/spec/javascripts/groups/components/group_item_spec.js
+++ b/spec/javascripts/groups/components/group_item_spec.js
@@ -45,7 +45,7 @@ describe('GroupItemComponent', () => {
expect(Object.keys(rowClass).length).toBe(classes.length);
Object.keys(rowClass).forEach((className) => {
- expect(classes.indexOf(className) > -1).toBeTruthy();
+ expect(classes.indexOf(className)).toBeGreaterThan(-1);
});
});
});
diff --git a/spec/javascripts/groups/components/groups_spec.js b/spec/javascripts/groups/components/groups_spec.js
index 793c4909d89..5af86b55532 100644
--- a/spec/javascripts/groups/components/groups_spec.js
+++ b/spec/javascripts/groups/components/groups_spec.js
@@ -53,7 +53,7 @@ describe('GroupsComponent', () => {
expect(vm.$el.querySelector('.groups-list-tree-container')).toBeDefined();
expect(vm.$el.querySelector('.group-list-tree')).toBeDefined();
expect(vm.$el.querySelector('.gl-pagination')).toBeDefined();
- expect(vm.$el.querySelectorAll('.has-no-search-results').length === 0).toBeTruthy();
+ expect(vm.$el.querySelectorAll('.has-no-search-results').length).toBe(0);
done();
});
});
diff --git a/spec/javascripts/groups/components/item_stats_spec.js b/spec/javascripts/groups/components/item_stats_spec.js
index ee7ee18259e..e6a57495eb1 100644
--- a/spec/javascripts/groups/components/item_stats_spec.js
+++ b/spec/javascripts/groups/components/item_stats_spec.js
@@ -107,7 +107,7 @@ describe('ItemStatsComponent', () => {
const visibilityIconEl = vm.$el.querySelector('.item-visibility');
expect(visibilityIconEl).not.toBe(null);
expect(visibilityIconEl.dataset.originalTitle).toBe(vm.visibilityTooltip);
- expect(visibilityIconEl.querySelectorAll('svg').length > 0).toBeTruthy();
+ expect(visibilityIconEl.querySelectorAll('svg').length).toBeGreaterThan(0);
vm.$destroy();
});
@@ -120,10 +120,10 @@ describe('ItemStatsComponent', () => {
const vm = createComponent(item);
const projectStarIconEl = vm.$el.querySelector('.project-stars');
- expect(projectStarIconEl).not.toBe(null);
- expect(projectStarIconEl.querySelectorAll('svg').length > 0).toBeTruthy();
- expect(projectStarIconEl.querySelectorAll('.stat-value').length > 0).toBeTruthy();
- expect(vm.$el.querySelectorAll('.last-updated').length > 0).toBeTruthy();
+ expect(projectStarIconEl).not.toBeNull();
+ expect(projectStarIconEl.querySelectorAll('svg').length).toBeGreaterThan(0);
+ expect(projectStarIconEl.querySelectorAll('.stat-value').length).toBeGreaterThan(0);
+ expect(vm.$el.querySelectorAll('.last-updated').length).toBeGreaterThan(0);
vm.$destroy();
});
diff --git a/spec/javascripts/groups/components/item_stats_value_spec.js b/spec/javascripts/groups/components/item_stats_value_spec.js
index 5e35ae4d36c..2a995e8efe6 100644
--- a/spec/javascripts/groups/components/item_stats_value_spec.js
+++ b/spec/javascripts/groups/components/item_stats_value_spec.js
@@ -57,8 +57,8 @@ describe('ItemStatsValueComponent', () => {
it('renders component element correctly', () => {
expect(vm.$el.classList.contains('number-subgroups')).toBeTruthy();
- expect(vm.$el.querySelectorAll('svg').length > 0).toBeTruthy();
- expect(vm.$el.querySelectorAll('.stat-value').length > 0).toBeTruthy();
+ expect(vm.$el.querySelectorAll('svg').length).toBeGreaterThan(0);
+ expect(vm.$el.querySelectorAll('.stat-value').length).toBeGreaterThan(0);
});
it('renders element tooltip correctly', () => {
diff --git a/spec/javascripts/groups/store/groups_store_spec.js b/spec/javascripts/groups/store/groups_store_spec.js
index d74f38f476e..78caf8f80bf 100644
--- a/spec/javascripts/groups/store/groups_store_spec.js
+++ b/spec/javascripts/groups/store/groups_store_spec.js
@@ -29,7 +29,7 @@ describe('ProjectsStore', () => {
store.setGroups(mockGroups);
expect(store.state.groups.length).toBe(mockGroups.length);
expect(store.formatGroupItem).toHaveBeenCalledWith(jasmine.any(Object));
- expect(Object.keys(store.state.groups[0]).indexOf('fullName') > -1).toBeTruthy();
+ expect(Object.keys(store.state.groups[0]).indexOf('fullName')).toBeGreaterThan(-1);
});
});
@@ -41,8 +41,8 @@ describe('ProjectsStore', () => {
store.setSearchedGroups(mockSearchedGroups);
expect(store.state.groups.length).toBe(mockSearchedGroups.length);
expect(store.formatGroupItem).toHaveBeenCalledWith(jasmine.any(Object));
- expect(Object.keys(store.state.groups[0]).indexOf('fullName') > -1).toBeTruthy();
- expect(Object.keys(store.state.groups[0].children[0]).indexOf('fullName') > -1).toBeTruthy();
+ expect(Object.keys(store.state.groups[0]).indexOf('fullName')).toBeGreaterThan(-1);
+ expect(Object.keys(store.state.groups[0].children[0]).indexOf('fullName')).toBeGreaterThan(-1);
});
});
@@ -54,7 +54,7 @@ describe('ProjectsStore', () => {
store.setGroupChildren(mockParentGroupItem, mockRawChildren);
expect(store.formatGroupItem).toHaveBeenCalledWith(jasmine.any(Object));
expect(mockParentGroupItem.children.length).toBe(1);
- expect(Object.keys(mockParentGroupItem.children[0]).indexOf('fullName') > -1).toBeTruthy();
+ expect(Object.keys(mockParentGroupItem.children[0]).indexOf('fullName')).toBeGreaterThan(-1);
expect(mockParentGroupItem.isOpen).toBeTruthy();
expect(mockParentGroupItem.isChildrenLoading).toBeFalsy();
});
@@ -81,14 +81,14 @@ describe('ProjectsStore', () => {
store = new GroupsStore();
updatedGroupItem = store.formatGroupItem(mockRawChildren[0]);
- expect(Object.keys(updatedGroupItem).indexOf('fullName') > -1).toBeTruthy();
+ expect(Object.keys(updatedGroupItem).indexOf('fullName')).toBeGreaterThan(-1);
expect(updatedGroupItem.childrenCount).toBe(mockRawChildren[0].children_count);
expect(updatedGroupItem.isChildrenLoading).toBe(false);
expect(updatedGroupItem.isBeingRemoved).toBe(false);
store = new GroupsStore(true);
updatedGroupItem = store.formatGroupItem(mockRawChildren[0]);
- expect(Object.keys(updatedGroupItem).indexOf('fullName') > -1).toBeTruthy();
+ expect(Object.keys(updatedGroupItem).indexOf('fullName')).toBeGreaterThan(-1);
expect(updatedGroupItem.childrenCount).toBe(mockRawChildren[0].subgroup_count);
});
});
diff --git a/spec/javascripts/image_diff/helpers/comment_indicator_helper_spec.js b/spec/javascripts/image_diff/helpers/comment_indicator_helper_spec.js
index a284b981d2a..9d05c6859df 100644
--- a/spec/javascripts/image_diff/helpers/comment_indicator_helper_spec.js
+++ b/spec/javascripts/image_diff/helpers/comment_indicator_helper_spec.js
@@ -32,7 +32,7 @@ describe('commentIndicatorHelper', () => {
expect(svgEl).toBeDefined();
const svgLink = svgEl.querySelector('use').getAttribute('xlink:href');
- expect(svgLink.indexOf('image-comment-dark') !== -1).toEqual(true);
+ expect(svgLink.indexOf('image-comment-dark')).not.toBe(-1);
});
});
});
diff --git a/spec/javascripts/jobs/components/job_app_spec.js b/spec/javascripts/jobs/components/job_app_spec.js
index e02eb9723fe..6e0bcf801cd 100644
--- a/spec/javascripts/jobs/components/job_app_spec.js
+++ b/spec/javascripts/jobs/components/job_app_spec.js
@@ -41,7 +41,7 @@ describe('Job App ', () => {
};
const props = {
- runnerHelpUrl: 'help/runners',
+ runnerSettingsUrl: 'settings/ci-cd/runners',
};
beforeEach(() => {
@@ -223,7 +223,6 @@ describe('Job App ', () => {
store.dispatch(
'receiveJobSuccess',
Object.assign({}, job, {
- erased: true,
erased_by: {
username: 'root',
web_url: 'gitlab.com/root',
@@ -237,18 +236,18 @@ describe('Job App ', () => {
store,
});
- expect(vm.$el.querySelector('.js-job-erased')).not.toBeNull();
+ expect(vm.$el.querySelector('.js-job-erased-block')).not.toBeNull();
});
it('does not render erased block when `erased` is false', () => {
- store.dispatch('receiveJobSuccess', Object.assign({}, job, { erased: false }));
+ store.dispatch('receiveJobSuccess', Object.assign({}, job, { erased_at: null }));
vm = mountComponentWithStore(Component, {
props,
store,
});
- expect(vm.$el.querySelector('.js-job-erased')).toBeNull();
+ expect(vm.$el.querySelector('.js-job-erased-block')).toBeNull();
});
});
diff --git a/spec/javascripts/jobs/components/job_log_controllers_spec.js b/spec/javascripts/jobs/components/job_log_controllers_spec.js
index 099aca602c4..3dcb57d23ce 100644
--- a/spec/javascripts/jobs/components/job_log_controllers_spec.js
+++ b/spec/javascripts/jobs/components/job_log_controllers_spec.js
@@ -25,6 +25,7 @@ describe('Job log controllers', () => {
beforeEach(() => {
vm = mountComponent(Component, props);
});
+
it('renders size information', () => {
expect(vm.$el.querySelector('.js-truncated-info').textContent).toContain('499.95 KiB');
});
diff --git a/spec/javascripts/jobs/components/sidebar_spec.js b/spec/javascripts/jobs/components/sidebar_spec.js
index 2f5c4245ced..a113377b19f 100644
--- a/spec/javascripts/jobs/components/sidebar_spec.js
+++ b/spec/javascripts/jobs/components/sidebar_spec.js
@@ -161,9 +161,9 @@ describe('Sidebar details block', () => {
vm = mountComponentWithStore(SidebarComponent, { store });
});
- it('renders first stage as selected', () => {
+ it('renders value provided as selectedStage as selected', () => {
expect(vm.$el.querySelector('.js-selected-stage').textContent.trim()).toEqual(
- stages[0].name,
+ vm.selectedStage,
);
});
});
diff --git a/spec/javascripts/jobs/components/stages_dropdown_spec.js b/spec/javascripts/jobs/components/stages_dropdown_spec.js
index aa6cc0f1b1a..fcff78b943e 100644
--- a/spec/javascripts/jobs/components/stages_dropdown_spec.js
+++ b/spec/javascripts/jobs/components/stages_dropdown_spec.js
@@ -2,7 +2,7 @@ import Vue from 'vue';
import component from '~/jobs/components/stages_dropdown.vue';
import mountComponent from '../../helpers/vue_mount_component_helper';
-describe('Artifacts block', () => {
+describe('Stages Dropdown', () => {
const Component = Vue.extend(component);
let vm;
@@ -23,10 +23,6 @@ describe('Artifacts block', () => {
},
path: 'pipeline/28029444',
},
- ref: {
- path: 'commits/50101-truncated-job-information',
- name: '50101-truncated-job-information',
- },
stages: [
{
name: 'build',
@@ -35,6 +31,7 @@ describe('Artifacts block', () => {
name: 'test',
},
],
+ selectedStage: 'deploy'
});
});
@@ -53,17 +50,10 @@ describe('Artifacts block', () => {
});
it('renders dropdown with stages', () => {
- expect(vm.$el.querySelector('.dropdown button').textContent).toContain('build');
+ expect(vm.$el.querySelector('.dropdown .js-stage-item').textContent).toContain('build');
});
- it('updates selected stage on click', done => {
- vm.$el.querySelectorAll('.stage-item')[1].click();
- vm
- .$nextTick()
- .then(() => {
- expect(vm.$el.querySelector('.dropdown button').textContent).toContain('test');
- })
- .then(done)
- .catch(done.fail);
+ it('rendes selected stage', () => {
+ expect(vm.$el.querySelector('.dropdown .js-selected-stage').textContent).toContain('deploy');
});
});
diff --git a/spec/javascripts/jobs/store/actions_spec.js b/spec/javascripts/jobs/store/actions_spec.js
index 5ab1f75d0d6..bc410ae614c 100644
--- a/spec/javascripts/jobs/store/actions_spec.js
+++ b/spec/javascripts/jobs/store/actions_spec.js
@@ -422,8 +422,9 @@ describe('Job State actions', () => {
beforeEach(() => {
mockedState.job.pipeline = {
- path: `${TEST_HOST}/endpoint.json/stages`,
+ path: `${TEST_HOST}/endpoint`,
};
+ mockedState.selectedStage = 'deploy'
mock = new MockAdapter(axios);
});
@@ -434,8 +435,8 @@ describe('Job State actions', () => {
describe('success', () => {
it('dispatches requestStages and receiveStagesSuccess, fetchJobsForStage ', done => {
mock
- .onGet(`${TEST_HOST}/endpoint.json/stages`)
- .replyOnce(200, { details: { stages: [{ id: 121212, name: 'build' }] } });
+ .onGet(`${TEST_HOST}/endpoint.json`)
+ .replyOnce(200, { details: { stages: [{ name: 'build' }, { name: 'deploy' }] } });
testAction(
fetchStages,
@@ -447,11 +448,11 @@ describe('Job State actions', () => {
type: 'requestStages',
},
{
- payload: [{ id: 121212, name: 'build' }],
+ payload: [{ name: 'build' }, { name: 'deploy' }],
type: 'receiveStagesSuccess',
},
{
- payload: { id: 121212, name: 'build' },
+ payload: { name: 'deploy' },
type: 'fetchJobsForStage',
},
],
@@ -515,9 +516,9 @@ describe('Job State actions', () => {
it('should commit REQUEST_JOBS_FOR_STAGE mutation ', done => {
testAction(
requestJobsForStage,
- null,
+ { name: 'deploy' },
mockedState,
- [{ type: types.REQUEST_JOBS_FOR_STAGE }],
+ [{ type: types.REQUEST_JOBS_FOR_STAGE, payload: { name: 'deploy' } }],
[],
done,
);
@@ -549,6 +550,7 @@ describe('Job State actions', () => {
[
{
type: 'requestJobsForStage',
+ payload: { dropdown_path: `${TEST_HOST}/jobs.json` },
},
{
payload: [{ id: 121212, name: 'build' }],
@@ -574,6 +576,7 @@ describe('Job State actions', () => {
[
{
type: 'requestJobsForStage',
+ payload: { dropdown_path: `${TEST_HOST}/jobs.json` },
},
{
type: 'receiveJobsForStageError',
diff --git a/spec/javascripts/jobs/store/getters_spec.js b/spec/javascripts/jobs/store/getters_spec.js
index 160b2f4b34a..e262a47b837 100644
--- a/spec/javascripts/jobs/store/getters_spec.js
+++ b/spec/javascripts/jobs/store/getters_spec.js
@@ -77,18 +77,18 @@ describe('Job Store Getters', () => {
});
});
- describe('jobHasStarted', () => {
- describe('when started equals false', () => {
+ describe('shouldRenderTriggeredLabel', () => {
+ describe('when started equals null', () => {
it('returns false', () => {
- localState.job.started = false;
- expect(getters.jobHasStarted(localState)).toEqual(false);
+ localState.job.started = null;
+ expect(getters.shouldRenderTriggeredLabel(localState)).toEqual(false);
});
});
describe('when started equals string', () => {
it('returns true', () => {
localState.job.started = '2018-08-31T16:20:49.023Z';
- expect(getters.jobHasStarted(localState)).toEqual(true);
+ expect(getters.shouldRenderTriggeredLabel(localState)).toEqual(true);
});
});
});
diff --git a/spec/javascripts/jobs/store/mutations_spec.js b/spec/javascripts/jobs/store/mutations_spec.js
index 9ba543d32a8..701fcc7f4c8 100644
--- a/spec/javascripts/jobs/store/mutations_spec.js
+++ b/spec/javascripts/jobs/store/mutations_spec.js
@@ -108,21 +108,33 @@ describe('Jobs Store Mutations', () => {
});
describe('RECEIVE_JOB_SUCCESS', () => {
- beforeEach(() => {
- mutations[types.RECEIVE_JOB_SUCCESS](stateCopy, { id: 1312321 });
- });
-
it('sets is loading to false', () => {
+ mutations[types.RECEIVE_JOB_SUCCESS](stateCopy, { id: 1312321 });
expect(stateCopy.isLoading).toEqual(false);
});
it('sets hasError to false', () => {
+ mutations[types.RECEIVE_JOB_SUCCESS](stateCopy, { id: 1312321 });
expect(stateCopy.hasError).toEqual(false);
});
it('sets job data', () => {
+ mutations[types.RECEIVE_JOB_SUCCESS](stateCopy, { id: 1312321 });
expect(stateCopy.job).toEqual({ id: 1312321 });
});
+
+ it('sets selectedStage when the selectedStage is More', () => {
+ expect(stateCopy.selectedStage).toEqual('More');
+ mutations[types.RECEIVE_JOB_SUCCESS](stateCopy, { id: 1312321, stage: 'deploy' });
+ expect(stateCopy.selectedStage).toEqual('deploy');
+ });
+
+ it('does not set selectedStage when the selectedStage is not More', () => {
+ stateCopy.selectedStage = 'notify'
+ expect(stateCopy.selectedStage).toEqual('notify');
+ mutations[types.RECEIVE_JOB_SUCCESS](stateCopy, { id: 1312321, stage: 'deploy' });
+ expect(stateCopy.selectedStage).toEqual('notify');
+ });
});
describe('RECEIVE_JOB_ERROR', () => {
@@ -200,9 +212,14 @@ describe('Jobs Store Mutations', () => {
describe('REQUEST_JOBS_FOR_STAGE', () => {
it('sets isLoadingStages to true', () => {
- mutations[types.REQUEST_JOBS_FOR_STAGE](stateCopy);
+ mutations[types.REQUEST_JOBS_FOR_STAGE](stateCopy, { name: 'deploy' });
expect(stateCopy.isLoadingJobs).toEqual(true);
});
+
+ it('sets selectedStage', () => {
+ mutations[types.REQUEST_JOBS_FOR_STAGE](stateCopy, { name: 'deploy' });
+ expect(stateCopy.selectedStage).toEqual('deploy');
+ })
});
describe('RECEIVE_JOBS_FOR_STAGE_SUCCESS', () => {
diff --git a/spec/javascripts/lib/utils/common_utils_spec.js b/spec/javascripts/lib/utils/common_utils_spec.js
index 28151c7e658..c34622203f7 100644
--- a/spec/javascripts/lib/utils/common_utils_spec.js
+++ b/spec/javascripts/lib/utils/common_utils_spec.js
@@ -9,6 +9,7 @@ describe('common_utils', () => {
it('returns an anchor tag with url', () => {
expect(commonUtils.parseUrl('/some/absolute/url').pathname).toContain('some/absolute/url');
});
+
it('url is escaped', () => {
// IE11 will return a relative pathname while other browsers will return a full pathname.
// parseUrl uses an anchor element for parsing an url. With relative urls, the anchor
@@ -42,7 +43,7 @@ describe('common_utils', () => {
it('should remove the question mark from the search params', () => {
const paramsArray = commonUtils.urlParamsToArray('?test=thing');
- expect(paramsArray[0][0] !== '?').toBe(true);
+ expect(paramsArray[0][0]).not.toBe('?');
});
});
diff --git a/spec/javascripts/line_highlighter_spec.js b/spec/javascripts/line_highlighter_spec.js
index c32ecb17e89..15bb78032b2 100644
--- a/spec/javascripts/line_highlighter_spec.js
+++ b/spec/javascripts/line_highlighter_spec.js
@@ -23,17 +23,20 @@ import LineHighlighter from '~/line_highlighter';
__setLocationHash__: spyOn(this["class"], '__setLocationHash__').and.callFake(function() {})
};
});
+
describe('behavior', function() {
it('highlights one line given in the URL hash', function() {
new LineHighlighter({ hash: '#L13' });
return expect($('#LC13')).toHaveClass(this.css);
});
+
it('highlights one line given in the URL hash with given CSS class name', function() {
const hiliter = new LineHighlighter({ hash: '#L13', highlightLineClass: 'hilite' });
expect(hiliter.highlightLineClass).toBe('hilite');
expect($('#LC13')).toHaveClass('hilite');
expect($('#LC13')).not.toHaveClass('hll');
});
+
it('highlights a range of lines given in the URL hash', function() {
var line, results;
new LineHighlighter({ hash: '#L5-25' });
@@ -44,18 +47,21 @@ import LineHighlighter from '~/line_highlighter';
}
return results;
});
+
it('scrolls to the first highlighted line on initial load', function() {
var spy;
spy = spyOn($, 'scrollTo');
new LineHighlighter({ hash: '#L5-25' });
return expect(spy).toHaveBeenCalledWith('#L5', jasmine.anything());
});
+
it('discards click events', function() {
var spy;
spy = spyOnEvent('a[data-line-number]', 'click');
clickLine(13);
return expect(spy).toHaveBeenPrevented();
});
+
it('handles garbage input from the hash', function() {
var func;
func = function() {
@@ -64,6 +70,7 @@ import LineHighlighter from '~/line_highlighter';
return expect(func).not.toThrow();
});
});
+
describe('clickHandler', function() {
it('handles clicking on a child icon element', function() {
var spy;
@@ -72,11 +79,13 @@ import LineHighlighter from '~/line_highlighter';
expect(spy).toHaveBeenCalledWith(13);
return expect($('#LC13')).toHaveClass(this.css);
});
+
describe('without shiftKey', function() {
it('highlights one line when clicked', function() {
clickLine(13);
return expect($('#LC13')).toHaveClass(this.css);
});
+
it('unhighlights previously highlighted lines', function() {
clickLine(13);
clickLine(20);
@@ -101,6 +110,7 @@ import LineHighlighter from '~/line_highlighter';
expect(spy).toHaveBeenCalledWith(13);
return expect(spy).toHaveBeenCalledWith(13, 20);
});
+
describe('without existing highlight', function() {
it('highlights the clicked line', function() {
clickLine(13, {
@@ -118,6 +128,7 @@ import LineHighlighter from '~/line_highlighter';
return expect(spy).toHaveBeenCalledWith(13);
});
});
+
describe('with existing single-line highlight', function() {
it('uses existing line as last line when target is lesser', function() {
var line, results;
@@ -155,6 +166,7 @@ import LineHighlighter from '~/line_highlighter';
shiftKey: true
});
});
+
it('uses target as first line when it is less than existing first line', function() {
var line, results;
clickLine(5, {
@@ -182,13 +194,16 @@ import LineHighlighter from '~/line_highlighter';
});
});
});
+
describe('hashToRange', function() {
beforeEach(function() {
return this.subject = this["class"].hashToRange;
});
+
it('extracts a single line number from the hash', function() {
return expect(this.subject('#L5')).toEqual([5, null]);
});
+
it('extracts a range of line numbers from the hash', function() {
return expect(this.subject('#L5-15')).toEqual([5, 15]);
});
@@ -196,10 +211,12 @@ import LineHighlighter from '~/line_highlighter';
return expect(this.subject('#foo')).toEqual([null, null]);
});
});
+
describe('highlightLine', function() {
beforeEach(function() {
return this.subject = this["class"].highlightLine;
});
+
it('highlights the specified line', function() {
this.subject(13);
return expect($('#LC13')).toHaveClass(this.css);
@@ -213,6 +230,7 @@ import LineHighlighter from '~/line_highlighter';
beforeEach(function() {
return this.subject = this["class"].setHash;
});
+
it('sets the location hash for a single line', function() {
this.subject(5);
return expect(this.spies.__setLocationHash__).toHaveBeenCalledWith('#L5');
diff --git a/spec/javascripts/new_branch_spec.js b/spec/javascripts/new_branch_spec.js
index e52ac686435..85419e640d8 100644
--- a/spec/javascripts/new_branch_spec.js
+++ b/spec/javascripts/new_branch_spec.js
@@ -21,18 +21,22 @@ import NewBranchForm from '~/new_branch_form';
});
return this.form = new NewBranchForm($('.js-create-branch-form'), []);
});
+
it("can't start with a dot", function() {
fillNameWith('.foo');
return expectToHaveError("can't start with '.'");
});
+
it("can't start with a slash", function() {
fillNameWith('/foo');
return expectToHaveError("can't start with '/'");
});
+
it("can't have two consecutive dots", function() {
fillNameWith('foo..bar');
return expectToHaveError("can't contain '..'");
});
+
it("can't have spaces anywhere", function() {
fillNameWith(' foo');
expectToHaveError("can't contain spaces");
@@ -41,6 +45,7 @@ import NewBranchForm from '~/new_branch_form';
fillNameWith('foo ');
return expectToHaveError("can't contain spaces");
});
+
it("can't have ~ anywhere", function() {
fillNameWith('~foo');
expectToHaveError("can't contain '~'");
@@ -49,6 +54,7 @@ import NewBranchForm from '~/new_branch_form';
fillNameWith('foo~');
return expectToHaveError("can't contain '~'");
});
+
it("can't have tilde anwhere", function() {
fillNameWith('~foo');
expectToHaveError("can't contain '~'");
@@ -57,6 +63,7 @@ import NewBranchForm from '~/new_branch_form';
fillNameWith('foo~');
return expectToHaveError("can't contain '~'");
});
+
it("can't have caret anywhere", function() {
fillNameWith('^foo');
expectToHaveError("can't contain '^'");
@@ -65,6 +72,7 @@ import NewBranchForm from '~/new_branch_form';
fillNameWith('foo^');
return expectToHaveError("can't contain '^'");
});
+
it("can't have : anywhere", function() {
fillNameWith(':foo');
expectToHaveError("can't contain ':'");
@@ -73,6 +81,7 @@ import NewBranchForm from '~/new_branch_form';
fillNameWith(':foo');
return expectToHaveError("can't contain ':'");
});
+
it("can't have question mark anywhere", function() {
fillNameWith('?foo');
expectToHaveError("can't contain '?'");
@@ -81,6 +90,7 @@ import NewBranchForm from '~/new_branch_form';
fillNameWith('foo?');
return expectToHaveError("can't contain '?'");
});
+
it("can't have asterisk anywhere", function() {
fillNameWith('*foo');
expectToHaveError("can't contain '*'");
@@ -89,6 +99,7 @@ import NewBranchForm from '~/new_branch_form';
fillNameWith('foo*');
return expectToHaveError("can't contain '*'");
});
+
it("can't have open bracket anywhere", function() {
fillNameWith('[foo');
expectToHaveError("can't contain '['");
@@ -97,6 +108,7 @@ import NewBranchForm from '~/new_branch_form';
fillNameWith('foo[');
return expectToHaveError("can't contain '['");
});
+
it("can't have a backslash anywhere", function() {
fillNameWith('\\foo');
expectToHaveError("can't contain '\\'");
@@ -105,6 +117,7 @@ import NewBranchForm from '~/new_branch_form';
fillNameWith('foo\\');
return expectToHaveError("can't contain '\\'");
});
+
it("can't contain a sequence @{ anywhere", function() {
fillNameWith('@{foo');
expectToHaveError("can't contain '@{'");
@@ -113,48 +126,59 @@ import NewBranchForm from '~/new_branch_form';
fillNameWith('foo@{');
return expectToHaveError("can't contain '@{'");
});
+
it("can't have consecutive slashes", function() {
fillNameWith('foo//bar');
return expectToHaveError("can't contain consecutive slashes");
});
+
it("can't end with a slash", function() {
fillNameWith('foo/');
return expectToHaveError("can't end in '/'");
});
+
it("can't end with a dot", function() {
fillNameWith('foo.');
return expectToHaveError("can't end in '.'");
});
+
it("can't end with .lock", function() {
fillNameWith('foo.lock');
return expectToHaveError("can't end in '.lock'");
});
+
it("can't be the single character @", function() {
fillNameWith('@');
return expectToHaveError("can't be '@'");
});
+
it("concatenates all error messages", function() {
fillNameWith('/foo bar?~.');
return expectToHaveError("can't start with '/', can't contain spaces, '?', '~', can't end in '.'");
});
+
it("doesn't duplicate error messages", function() {
fillNameWith('?foo?bar?zoo?');
return expectToHaveError("can't contain '?'");
});
+
it("removes the error message when is a valid name", function() {
fillNameWith('foo?bar');
expect($('.js-branch-name-error span').length).toEqual(1);
fillNameWith('foobar');
return expect($('.js-branch-name-error span').length).toEqual(0);
});
+
it("can have dashes anywhere", function() {
fillNameWith('-foo-bar-zoo-');
return expect($('.js-branch-name-error span').length).toEqual(0);
});
+
it("can have underscores anywhere", function() {
fillNameWith('_foo_bar_zoo_');
return expect($('.js-branch-name-error span').length).toEqual(0);
});
+
it("can have numbers anywhere", function() {
fillNameWith('1foo2bar3zoo4');
return expect($('.js-branch-name-error span').length).toEqual(0);
diff --git a/spec/javascripts/notes/components/note_form_spec.js b/spec/javascripts/notes/components/note_form_spec.js
index 147ffcf1b81..eefd9ddd63c 100644
--- a/spec/javascripts/notes/components/note_form_spec.js
+++ b/spec/javascripts/notes/components/note_form_spec.js
@@ -100,6 +100,7 @@ describe('issue_note_form component', () => {
expect(vm.handleUpdate).toHaveBeenCalled();
});
+
it('should save note when ctrl+enter is pressed', () => {
spyOn(vm, 'handleUpdate').and.callThrough();
vm.$el.querySelector('textarea').value = 'Foo';
diff --git a/spec/javascripts/notes_spec.js b/spec/javascripts/notes_spec.js
index faeedae40e9..7bfbca83c77 100644
--- a/spec/javascripts/notes_spec.js
+++ b/spec/javascripts/notes_spec.js
@@ -538,7 +538,7 @@ import timeoutPromise from './helpers/set_timeout_promise_helper';
mockNotesPost();
$('.js-comment-button').click();
- expect($notesContainer.find('.note.being-posted').length > 0).toEqual(true);
+ expect($notesContainer.find('.note.being-posted').length).toBeGreaterThan(0);
});
it('should remove placeholder note when new comment is done posting', done => {
@@ -582,7 +582,7 @@ import timeoutPromise from './helpers/set_timeout_promise_helper';
$('.js-comment-button').click();
setTimeout(() => {
- expect($notesContainer.find(`#note_${note.id}`).length > 0).toEqual(true);
+ expect($notesContainer.find(`#note_${note.id}`).length).toBeGreaterThan(0);
done();
});
@@ -776,7 +776,7 @@ import timeoutPromise from './helpers/set_timeout_promise_helper';
$form.find('textarea.js-note-text').val(sampleComment);
const { formData, formContent, formAction } = this.notes.getFormData($form);
- expect(formData.indexOf(sampleComment) > -1).toBe(true);
+ expect(formData.indexOf(sampleComment)).toBeGreaterThan(-1);
expect(formContent).toEqual(sampleComment);
expect(formAction).toEqual($form.attr('action'));
});
diff --git a/spec/javascripts/reports/components/report_section_spec.js b/spec/javascripts/reports/components/report_section_spec.js
index 6f6eb161d14..bf11dbea386 100644
--- a/spec/javascripts/reports/components/report_section_spec.js
+++ b/spec/javascripts/reports/components/report_section_spec.js
@@ -86,6 +86,7 @@ describe('Report section', () => {
});
});
});
+
describe('when it is loading', () => {
it('should render loading indicator', () => {
vm = mountComponent(ReportSection, {
diff --git a/spec/javascripts/right_sidebar_spec.js b/spec/javascripts/right_sidebar_spec.js
index f9395eedfea..936e8f16c52 100644
--- a/spec/javascripts/right_sidebar_spec.js
+++ b/spec/javascripts/right_sidebar_spec.js
@@ -60,10 +60,12 @@ import Sidebar from '~/right_sidebar';
$toggle.click();
assertSidebarState('expanded');
});
+
it('should float over the page and when sidebar icons clicked', function() {
$labelsIcon.click();
return assertSidebarState('expanded');
});
+
it('should collapse when the icon arrow clicked while it is floating on page', function() {
$labelsIcon.click();
assertSidebarState('expanded');
diff --git a/spec/javascripts/search_autocomplete_spec.js b/spec/javascripts/search_autocomplete_spec.js
index b96023a33c4..bc1bb50dc5c 100644
--- a/spec/javascripts/search_autocomplete_spec.js
+++ b/spec/javascripts/search_autocomplete_spec.js
@@ -140,6 +140,7 @@ describe('Search autocomplete dropdown', () => {
removeBodyAttributes();
window.gon = {};
});
+
it('should show Dashboard specific dropdown menu', function() {
var list;
addBodyAttributes();
@@ -148,6 +149,7 @@ describe('Search autocomplete dropdown', () => {
list = widget.wrap.find('.dropdown-menu').find('ul');
return assertLinks(list, dashboardIssuesPath, dashboardMRsPath);
});
+
it('should show Group specific dropdown menu', function() {
var list;
addBodyAttributes('group');
@@ -156,6 +158,7 @@ describe('Search autocomplete dropdown', () => {
list = widget.wrap.find('.dropdown-menu').find('ul');
return assertLinks(list, groupIssuesPath, groupMRsPath);
});
+
it('should show Project specific dropdown menu', function() {
var list;
addBodyAttributes('project');
@@ -164,6 +167,7 @@ describe('Search autocomplete dropdown', () => {
list = widget.wrap.find('.dropdown-menu').find('ul');
return assertLinks(list, projectIssuesPath, projectMRsPath);
});
+
it('should show only Project mergeRequest dropdown menu items when project issues are disabled', function() {
addBodyAttributes('project');
disableProjectIssues();
@@ -172,6 +176,7 @@ describe('Search autocomplete dropdown', () => {
const list = widget.wrap.find('.dropdown-menu').find('ul');
assertLinks(list, null, projectMRsPath);
});
+
it('should not show category related menu if there is text in the input', function() {
var link, list;
addBodyAttributes('project');
@@ -182,6 +187,7 @@ describe('Search autocomplete dropdown', () => {
link = "a[href='" + projectIssuesPath + '/?assignee_id=' + userId + "']";
return expect(list.find(link).length).toBe(0);
});
+
it('should not submit the search form when selecting an autocomplete row with the keyboard', function() {
var ENTER = 13;
var DOWN = 40;
diff --git a/spec/javascripts/syntax_highlight_spec.js b/spec/javascripts/syntax_highlight_spec.js
index af3a5d58ba7..512be88c24c 100644
--- a/spec/javascripts/syntax_highlight_spec.js
+++ b/spec/javascripts/syntax_highlight_spec.js
@@ -25,6 +25,7 @@ describe('Syntax Highlighter', function() {
beforeEach(function() {
return setFixtures("<div class=\"parent\">\n <div class=\"js-syntax-highlight\"></div>\n <div class=\"foo\"></div>\n <div class=\"js-syntax-highlight\"></div>\n</div>");
});
+
it('applies highlighting to all applicable children', function() {
stubUserColorScheme('monokai');
syntaxHighlight($('.parent'));
diff --git a/spec/javascripts/vue_mr_widget/components/mr_widget_memory_usage_spec.js b/spec/javascripts/vue_mr_widget/components/mr_widget_memory_usage_spec.js
index 91e81a0675a..305cee94f57 100644
--- a/spec/javascripts/vue_mr_widget/components/mr_widget_memory_usage_spec.js
+++ b/spec/javascripts/vue_mr_widget/components/mr_widget_memory_usage_spec.js
@@ -137,7 +137,7 @@ describe('MemoryUsage', () => {
} = vm;
expect(hasMetrics).toBeTruthy();
- expect(memoryMetrics.length > 0).toBeTruthy();
+ expect(memoryMetrics.length).toBeGreaterThan(0);
expect(deploymentTime).toEqual(deployment_time);
expect(memoryFrom).toEqual('9.13');
expect(memoryTo).toEqual('4.28');
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 efa5c878678..033cb694249 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
@@ -157,6 +157,16 @@ describe('MRWidgetMerged', () => {
expect(selectors.copyMergeShaButton.getAttribute('data-clipboard-text')).toBe(vm.mr.mergeCommitSha);
});
+ it('hides button to copy commit SHA if SHA does not exist', (done) => {
+ vm.mr.mergeCommitSha = null;
+
+ Vue.nextTick(() => {
+ expect(selectors.copyMergeShaButton).not.toExist();
+ expect(vm.$el.querySelector('.mr-info-list').innerText).not.toContain('with');
+ done();
+ });
+ });
+
it('shows merge commit SHA link', () => {
expect(selectors.mergeCommitShaLink).toExist();
expect(selectors.mergeCommitShaLink.text).toContain(vm.mr.shortMergeCommitSha);
diff --git a/spec/javascripts/vue_shared/components/memory_graph_spec.js b/spec/javascripts/vue_shared/components/memory_graph_spec.js
index 65d8ed39ade..0982b3e1f38 100644
--- a/spec/javascripts/vue_shared/components/memory_graph_spec.js
+++ b/spec/javascripts/vue_shared/components/memory_graph_spec.js
@@ -52,8 +52,8 @@ describe('MemoryGraph', () => {
it('should show human readable median value based on provided median timestamp', () => {
vm.deploymentTime = mockMedian;
const formattedMedian = vm.getFormattedMedian;
- expect(formattedMedian.indexOf('Deployed') > -1).toBeTruthy();
- expect(formattedMedian.indexOf('ago') > -1).toBeTruthy();
+ expect(formattedMedian.indexOf('Deployed')).toBeGreaterThan(-1);
+ expect(formattedMedian.indexOf('ago')).toBeGreaterThan(-1);
});
});
});
diff --git a/spec/lib/gitlab/ci/status/pipeline/factory_spec.rb b/spec/lib/gitlab/ci/status/pipeline/factory_spec.rb
index 694d4ce160a..d97fdc01109 100644
--- a/spec/lib/gitlab/ci/status/pipeline/factory_spec.rb
+++ b/spec/lib/gitlab/ci/status/pipeline/factory_spec.rb
@@ -11,7 +11,7 @@ describe Gitlab::Ci::Status::Pipeline::Factory do
end
context 'when pipeline has a core status' do
- HasStatus::AVAILABLE_STATUSES.each do |simple_status|
+ (HasStatus::AVAILABLE_STATUSES - HasStatus::BLOCKED_STATUS).each do |simple_status|
context "when core status is #{simple_status}" do
let(:pipeline) { create(:ci_pipeline, status: simple_status) }
@@ -23,24 +23,12 @@ describe Gitlab::Ci::Status::Pipeline::Factory do
expect(factory.core_status).to be_a expected_status
end
- if simple_status == 'manual'
- it 'matches a correct extended statuses' do
- expect(factory.extended_statuses)
- .to eq [Gitlab::Ci::Status::Pipeline::Blocked]
- end
- elsif simple_status == 'scheduled'
- it 'matches a correct extended statuses' do
- expect(factory.extended_statuses)
- .to eq [Gitlab::Ci::Status::Pipeline::Scheduled]
- end
- else
- it 'does not match extended statuses' do
- expect(factory.extended_statuses).to be_empty
- end
-
- it "fabricates a core status #{simple_status}" do
- expect(status).to be_a expected_status
- end
+ it 'does not match extended statuses' do
+ expect(factory.extended_statuses).to be_empty
+ end
+
+ it "fabricates a core status #{simple_status}" do
+ expect(status).to be_a expected_status
end
it 'extends core status with common pipeline methods' do
@@ -51,6 +39,48 @@ describe Gitlab::Ci::Status::Pipeline::Factory do
end
end
end
+
+ context "when core status is manual" do
+ let(:pipeline) { create(:ci_pipeline, status: :manual) }
+
+ it "matches manual core status" do
+ expect(factory.core_status)
+ .to be_a Gitlab::Ci::Status::Manual
+ end
+
+ it 'matches a correct extended statuses' do
+ expect(factory.extended_statuses)
+ .to eq [Gitlab::Ci::Status::Pipeline::Blocked]
+ end
+
+ it 'extends core status with common pipeline methods' do
+ expect(status).to have_details
+ expect(status).not_to have_action
+ expect(status.details_path)
+ .to include "pipelines/#{pipeline.id}"
+ end
+ end
+
+ context "when core status is scheduled" do
+ let(:pipeline) { create(:ci_pipeline, status: :scheduled) }
+
+ it "matches scheduled core status" do
+ expect(factory.core_status)
+ .to be_a Gitlab::Ci::Status::Scheduled
+ end
+
+ it 'matches a correct extended statuses' do
+ expect(factory.extended_statuses)
+ .to eq [Gitlab::Ci::Status::Pipeline::Scheduled]
+ end
+
+ it 'extends core status with common pipeline methods' do
+ expect(status).to have_details
+ expect(status).not_to have_action
+ expect(status.details_path)
+ .to include "pipelines/#{pipeline.id}"
+ end
+ end
end
context 'when pipeline has warnings' do
diff --git a/spec/lib/gitlab/gon_helper_spec.rb b/spec/lib/gitlab/gon_helper_spec.rb
new file mode 100644
index 00000000000..c6f09ca2112
--- /dev/null
+++ b/spec/lib/gitlab/gon_helper_spec.rb
@@ -0,0 +1,32 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Gitlab::GonHelper do
+ let(:helper) do
+ Class.new do
+ include Gitlab::GonHelper
+ end.new
+ end
+
+ describe '#push_frontend_feature_flag' do
+ it 'pushes a feature flag to the frontend' do
+ gon = instance_double('gon')
+
+ allow(helper)
+ .to receive(:gon)
+ .and_return(gon)
+
+ expect(Feature)
+ .to receive(:enabled?)
+ .with(:my_feature_flag, 10)
+ .and_return(true)
+
+ expect(gon)
+ .to receive(:push)
+ .with({ features: { 'myFeatureFlag' => true } }, true)
+
+ helper.push_frontend_feature_flag(:my_feature_flag, 10)
+ end
+ end
+end
diff --git a/spec/models/deployment_spec.rb b/spec/models/deployment_spec.rb
index b335e0fbeb3..182070781dd 100644
--- a/spec/models/deployment_spec.rb
+++ b/spec/models/deployment_spec.rb
@@ -39,6 +39,29 @@ describe Deployment do
end
end
+ describe 'scopes' do
+ describe 'last_for_environment' do
+ let(:production) { create(:environment) }
+ let(:staging) { create(:environment) }
+ let(:testing) { create(:environment) }
+
+ let!(:deployments) do
+ [
+ create(:deployment, environment: production),
+ create(:deployment, environment: staging),
+ create(:deployment, environment: production)
+ ]
+ end
+
+ it 'retrieves last deployments for environments' do
+ last_deployments = described_class.last_for_environment([staging, production, testing])
+
+ expect(last_deployments.size).to eq(2)
+ expect(last_deployments).to eq(deployments.last(2))
+ end
+ end
+ end
+
describe '#includes_commit?' do
let(:project) { create(:project, :repository) }
let(:environment) { create(:environment, project: project) }
diff --git a/spec/services/projects/destroy_service_spec.rb b/spec/services/projects/destroy_service_spec.rb
index beff499f2be..1d31d26f418 100644
--- a/spec/services/projects/destroy_service_spec.rb
+++ b/spec/services/projects/destroy_service_spec.rb
@@ -65,10 +65,12 @@ describe Projects::DestroyService do
context 'Sidekiq inline' do
before do
- # Run sidekiq immediatly to check that renamed repository will be removed
+ # Run sidekiq immediately to check that renamed repository will be removed
perform_enqueued_jobs { destroy_project(project, user, {}) }
end
+ it_behaves_like 'deleting the project'
+
context 'when has remote mirrors' do
let!(:project) do
create(:project, :repository, namespace: user.namespace).tap do |project|
@@ -82,13 +84,28 @@ describe Projects::DestroyService do
end
end
- it_behaves_like 'deleting the project'
-
it 'invalidates personal_project_count cache' do
expect(user).to receive(:invalidate_personal_projects_count)
destroy_project(project, user)
end
+
+ context 'when project has exports' do
+ let!(:project_with_export) do
+ create(:project, :repository, namespace: user.namespace).tap do |project|
+ create(:import_export_upload,
+ project: project,
+ export_file: fixture_file_upload('spec/fixtures/project_export.tar.gz'))
+ end
+ end
+ let!(:async) { true }
+
+ it 'destroys project and export' do
+ expect { destroy_project(project_with_export, user) }.to change(ImportExportUpload, :count).by(-1)
+
+ expect(Project.all).not_to include(project_with_export)
+ end
+ end
end
context 'Sidekiq fake' do
diff --git a/yarn.lock b/yarn.lock
index 25ea8d7557c..5879ccb9267 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -616,10 +616,10 @@
lodash "^4.17.10"
to-fast-properties "^2.0.0"
-"@gitlab-org/gitlab-svgs@^1.23.0", "@gitlab-org/gitlab-svgs@^1.29.0":
- version "1.31.0"
- resolved "https://registry.yarnpkg.com/@gitlab-org/gitlab-svgs/-/gitlab-svgs-1.31.0.tgz#495b074669f93af40e34f9978ce887773dea470a"
- integrity sha512-tJbf99XX/ddFkXCXxQr9a0GJD9rPVoW3qMbU14dkxwG4WBmPEoVg+e7sLvm9OWTD1uUqiVW3qWKp++SGhhcRlw==
+"@gitlab-org/gitlab-svgs@^1.23.0", "@gitlab-org/gitlab-svgs@^1.32.0":
+ version "1.32.0"
+ resolved "https://registry.yarnpkg.com/@gitlab-org/gitlab-svgs/-/gitlab-svgs-1.32.0.tgz#a65ab7724fa7d55be8e5cc9b2dbe3f0757432fd3"
+ integrity sha512-L3o8dFUd2nSkVZBwh2hCJWzNzADJ3dTBZxamND8NLosZK9/ohNhccmsQOZGyMCUHaOzm4vifaaXkAXh04UtMKA==
"@gitlab-org/gitlab-ui@^1.8.0":
version "1.8.0"