diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2019-10-30 12:06:34 +0000 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2019-10-30 12:06:34 +0000 |
commit | 228d752ff09362002cc904d28edee7d63cc3cef2 (patch) | |
tree | 63e7ff466c0b0794f67c87c34e874f8682fb5de0 | |
parent | b539ac1d619c0aafe5988ab8b125a8b43b14d87f (diff) | |
download | gitlab-ce-228d752ff09362002cc904d28edee7d63cc3cef2.tar.gz |
Add latest changes from gitlab-org/gitlab@master
35 files changed, 603 insertions, 67 deletions
diff --git a/app/assets/javascripts/gl_dropdown.js b/app/assets/javascripts/gl_dropdown.js index 4e1b4f2652c..045f77af7ea 100644 --- a/app/assets/javascripts/gl_dropdown.js +++ b/app/assets/javascripts/gl_dropdown.js @@ -617,7 +617,7 @@ GitLabDropdown = (function() { GitLabDropdown.prototype.hidden = function(e) { var $input; this.resetRows(); - this.removeArrayKeyEvent(); + this.removeArrowKeyEvent(); $input = this.dropdown.find('.dropdown-input-field'); if (this.options.filterable) { $input.blur(); @@ -900,7 +900,7 @@ GitLabDropdown = (function() { ); }; - GitLabDropdown.prototype.removeArrayKeyEvent = function() { + GitLabDropdown.prototype.removeArrowKeyEvent = function() { return $('body').off('keydown'); }; diff --git a/app/assets/javascripts/repository/components/directory_download_links.vue b/app/assets/javascripts/repository/components/directory_download_links.vue new file mode 100644 index 00000000000..dffadade082 --- /dev/null +++ b/app/assets/javascripts/repository/components/directory_download_links.vue @@ -0,0 +1,47 @@ +<script> +import { GlLink } from '@gitlab/ui'; + +export default { + components: { + GlLink, + }, + props: { + currentPath: { + type: String, + required: false, + default: null, + }, + links: { + type: Array, + required: true, + }, + }, + computed: { + normalizedLinks() { + return this.links.map(link => ({ + text: link.text, + path: `${link.path}?path=${this.currentPath}`, + })); + }, + }, +}; +</script> + +<template> + <section class="border-top pt-1 mt-1"> + <h5 class="m-0 dropdown-bold-header">{{ __('Download this directory') }}</h5> + <div class="dropdown-menu-content"> + <div class="btn-group ml-0 w-100"> + <gl-link + v-for="(link, index) in normalizedLinks" + :key="index" + :href="link.path" + :class="{ 'btn-primary': index === 0 }" + class="btn btn-xs" + > + {{ link.text }} + </gl-link> + </div> + </div> + </section> +</template> diff --git a/app/assets/javascripts/repository/components/tree_action_link.vue b/app/assets/javascripts/repository/components/tree_action_link.vue new file mode 100644 index 00000000000..72764f3ccc9 --- /dev/null +++ b/app/assets/javascripts/repository/components/tree_action_link.vue @@ -0,0 +1,28 @@ +<script> +import { GlLink } from '@gitlab/ui'; + +export default { + components: { + GlLink, + }, + props: { + path: { + type: String, + required: true, + }, + text: { + type: String, + required: true, + }, + cssClass: { + type: String, + required: false, + default: null, + }, + }, +}; +</script> + +<template> + <gl-link :href="path" :class="cssClass" class="btn">{{ text }}</gl-link> +</template> diff --git a/app/assets/javascripts/repository/index.js b/app/assets/javascripts/repository/index.js index 6a6e7f73188..de7350f0d2f 100644 --- a/app/assets/javascripts/repository/index.js +++ b/app/assets/javascripts/repository/index.js @@ -3,9 +3,13 @@ import createRouter from './router'; import App from './components/app.vue'; import Breadcrumbs from './components/breadcrumbs.vue'; import LastCommit from './components/last_commit.vue'; +import TreeActionLink from './components/tree_action_link.vue'; +import DirectoryDownloadLinks from './components/directory_download_links.vue'; import apolloProvider from './graphql'; import { setTitle } from './utils/title'; import { parseBoolean } from '../lib/utils/common_utils'; +import { webIDEUrl } from '../lib/utils/url_utility'; +import { __ } from '../locale'; export default function setupVueRepositoryList() { const el = document.getElementById('js-tree-list'); @@ -91,6 +95,66 @@ export default function setupVueRepositoryList() { }, }); + const treeHistoryLinkEl = document.getElementById('js-tree-history-link'); + const { historyLink } = treeHistoryLinkEl.dataset; + + // eslint-disable-next-line no-new + new Vue({ + el: treeHistoryLinkEl, + router, + render(h) { + return h(TreeActionLink, { + props: { + path: historyLink + (this.$route.params.pathMatch || '/'), + text: __('History'), + }, + }); + }, + }); + + const webIdeLinkEl = document.getElementById('js-tree-web-ide-link'); + + if (webIdeLinkEl) { + // eslint-disable-next-line no-new + new Vue({ + el: webIdeLinkEl, + router, + render(h) { + return h(TreeActionLink, { + props: { + path: webIDEUrl(`/${projectPath}/edit/${ref}/-${this.$route.params.pathMatch || '/'}`), + text: __('Web IDE'), + cssClass: 'qa-web-ide-button', + }, + }); + }, + }); + } + + const directoryDownloadLinks = document.getElementById('js-directory-downloads'); + + if (directoryDownloadLinks) { + // eslint-disable-next-line no-new + new Vue({ + el: directoryDownloadLinks, + router, + render(h) { + const currentPath = this.$route.params.pathMatch || '/'; + + if (currentPath !== '/') { + return h(DirectoryDownloadLinks, { + props: { + currentPath: currentPath.replace(/^\//, ''), + links: JSON.parse(directoryDownloadLinks.dataset.links), + }, + }); + } + + return null; + }, + }); + } + // eslint-disable-next-line no-new new Vue({ el, diff --git a/app/assets/javascripts/search_autocomplete.js b/app/assets/javascripts/search_autocomplete.js index e08a67ec604..57bf9a2d233 100644 --- a/app/assets/javascripts/search_autocomplete.js +++ b/app/assets/javascripts/search_autocomplete.js @@ -1,4 +1,4 @@ -/* eslint-disable no-return-assign, one-var, no-var, consistent-return, class-methods-use-this, no-lonely-if, vars-on-top */ +/* eslint-disable no-return-assign, one-var, no-var, consistent-return, class-methods-use-this, vars-on-top */ import $ from 'jquery'; import { escape, throttle } from 'underscore'; @@ -95,7 +95,6 @@ export class SearchAutocomplete { this.createAutocomplete(); } - this.saveTextLength(); this.bindEvents(); this.dropdownToggle.dropdown(); this.searchInput.addClass('js-autocomplete-disabled'); @@ -107,7 +106,7 @@ export class SearchAutocomplete { this.onClearInputClick = this.onClearInputClick.bind(this); this.onSearchInputFocus = this.onSearchInputFocus.bind(this); this.onSearchInputKeyUp = this.onSearchInputKeyUp.bind(this); - this.onSearchInputKeyDown = this.onSearchInputKeyDown.bind(this); + this.onSearchInputChange = this.onSearchInputChange.bind(this); this.setScrollFade = this.setScrollFade.bind(this); } getElement(selector) { @@ -118,10 +117,6 @@ export class SearchAutocomplete { return (this.originalState = this.serializeState()); } - saveTextLength() { - return (this.lastTextLength = this.searchInput.val().length); - } - createAutocomplete() { return this.searchInput.glDropdown({ filterInputBlur: false, @@ -318,12 +313,16 @@ export class SearchAutocomplete { } bindEvents() { - this.searchInput.on('keydown', this.onSearchInputKeyDown); + this.searchInput.on('input', this.onSearchInputChange); this.searchInput.on('keyup', this.onSearchInputKeyUp); this.searchInput.on('focus', this.onSearchInputFocus); this.searchInput.on('blur', this.onSearchInputBlur); this.clearInput.on('click', this.onClearInputClick); this.dropdownContent.on('scroll', throttle(this.setScrollFade, 250)); + + this.searchInput.on('click', e => { + e.stopPropagation(); + }); } enableAutocomplete() { @@ -342,43 +341,19 @@ export class SearchAutocomplete { } } - // Saves last length of the entered text - onSearchInputKeyDown() { - return this.saveTextLength(); + onSearchInputChange() { + this.enableAutocomplete(); } onSearchInputKeyUp(e) { switch (e.keyCode) { - case KEYCODE.BACKSPACE: - // When removing the last character and no badge is present - if (this.lastTextLength === 1) { - this.disableAutocomplete(); - } - // When removing any character from existin value - if (this.lastTextLength > 1) { - this.enableAutocomplete(); - } - break; case KEYCODE.ESCAPE: this.restoreOriginalState(); break; case KEYCODE.ENTER: this.disableAutocomplete(); break; - case KEYCODE.UP: - case KEYCODE.DOWN: - return; default: - // Handle the case when deleting the input value other than backspace - // e.g. Pressing ctrl + backspace or ctrl + x - if (this.searchInput.val() === '') { - this.disableAutocomplete(); - } else { - // We should display the menu only when input is not empty - if (e.keyCode !== KEYCODE.ENTER) { - this.enableAutocomplete(); - } - } } this.wrap.toggleClass('has-value', Boolean(e.target.value)); } @@ -434,7 +409,7 @@ export class SearchAutocomplete { disableAutocomplete() { if (!this.searchInput.hasClass('js-autocomplete-disabled') && this.dropdown.hasClass('show')) { this.searchInput.addClass('js-autocomplete-disabled'); - this.dropdown.removeClass('show').trigger('hidden.bs.dropdown'); + this.dropdown.dropdown('toggle'); this.restoreMenu(); } } diff --git a/app/graphql/types/todo_target_enum.rb b/app/graphql/types/todo_target_enum.rb index 9a7391dcd99..8358a86b35c 100644 --- a/app/graphql/types/todo_target_enum.rb +++ b/app/graphql/types/todo_target_enum.rb @@ -2,8 +2,10 @@ module Types class TodoTargetEnum < BaseEnum - value 'Issue' - value 'MergeRequest' - value 'Epic' + value 'COMMIT', value: 'Commit', description: 'A Commit' + value 'ISSUE', value: 'Issue', description: 'An Issue' + value 'MERGEREQUEST', value: 'MergeRequest', description: 'A MergeRequest' end end + +Types::TodoTargetEnum.prepend_if_ee('::EE::Types::TodoTargetEnum') diff --git a/app/graphql/types/todo_type.rb b/app/graphql/types/todo_type.rb index d36daaf7dec..5ce5093c55e 100644 --- a/app/graphql/types/todo_type.rb +++ b/app/graphql/types/todo_type.rb @@ -40,7 +40,8 @@ module Types field :body, GraphQL::STRING_TYPE, description: 'Body of the todo', - null: false + null: false, + calls_gitaly: true # TODO This is only true when `target_type` is `Commit`. See https://gitlab.com/gitlab-org/gitlab/issues/34757#note_234752665 field :state, Types::TodoStateEnum, description: 'State of the todo', diff --git a/app/helpers/tree_helper.rb b/app/helpers/tree_helper.rb index 1020c91b245..28adc7ccbdb 100644 --- a/app/helpers/tree_helper.rb +++ b/app/helpers/tree_helper.rb @@ -195,6 +195,17 @@ module TreeHelper full_name: project.name_with_namespace } end + + def directory_download_links(project, ref, archive_prefix) + formats = ['zip', 'tar.gz', 'tar.bz2', 'tar'] + + formats.map do |fmt| + { + text: fmt, + path: project_archive_path(project, id: tree_join(ref, archive_prefix), format: fmt) + } + end + end end TreeHelper.prepend_if_ee('::EE::TreeHelper') diff --git a/app/services/clusters/update_service.rb b/app/services/clusters/update_service.rb index 98dd6b26a47..8cb77040b14 100644 --- a/app/services/clusters/update_service.rb +++ b/app/services/clusters/update_service.rb @@ -23,7 +23,7 @@ module Clusters end def validate_params(cluster) - if params[:management_project_id] + if params[:management_project_id].present? management_project = management_project_scope(cluster).find_by_id(params[:management_project_id]) unless management_project diff --git a/app/views/projects/buttons/_download.html.haml b/app/views/projects/buttons/_download.html.haml index 96df3cd18fe..e8aff58b505 100644 --- a/app/views/projects/buttons/_download.html.haml +++ b/app/views/projects/buttons/_download.html.haml @@ -12,11 +12,14 @@ %h5.m-0.dropdown-bold-header= _('Download source code') .dropdown-menu-content = render 'projects/buttons/download_links', project: project, ref: ref, archive_prefix: archive_prefix, path: nil - - if directory? && Feature.enabled?(:git_archive_path, default_enabled: true) - %section.border-top.pt-1.mt-1 - %h5.m-0.dropdown-bold-header= _('Download this directory') - .dropdown-menu-content - = render 'projects/buttons/download_links', project: project, ref: ref, archive_prefix: archive_prefix, path: @path + - if Feature.enabled?(:git_archive_path, default_enabled: true) + - if vue_file_list_enabled? + #js-directory-downloads{ data: { links: directory_download_links(project, ref, archive_prefix).to_json } } + - elsif directory? + %section.border-top.pt-1.mt-1 + %h5.m-0.dropdown-bold-header= _('Download this directory') + .dropdown-menu-content + = render 'projects/buttons/download_links', project: project, ref: ref, archive_prefix: archive_prefix, path: @path - if pipeline && pipeline.latest_builds_with_artifacts.any? %section.border-top.pt-1.mt-1 %h5.m-0.dropdown-bold-header= _('Download artifacts') diff --git a/app/views/projects/tree/_tree_header.html.haml b/app/views/projects/tree/_tree_header.html.haml index 38422d4533d..127734ddfd7 100644 --- a/app/views/projects/tree/_tree_header.html.haml +++ b/app/views/projects/tree/_tree_header.html.haml @@ -77,15 +77,21 @@ .tree-controls = render_if_exists 'projects/tree/lock_link' - = link_to s_('Commits|History'), project_commits_path(@project, @id), class: 'btn' + - if vue_file_list_enabled? + #js-tree-history-link.d-inline-block{ data: { history_link: project_commits_path(@project, @ref) } } + - else + = link_to s_('Commits|History'), project_commits_path(@project, @id), class: 'btn' = render 'projects/find_file_link' - if can_create_mr_from_fork = succeed " " do - if can_collaborate || current_user&.already_forked?(@project) - = link_to ide_edit_path(@project, @ref, @path), class: 'btn btn-default qa-web-ide-button' do - = _('Web IDE') + - if vue_file_list_enabled? + #js-tree-web-ide-link.d-inline-block + - else + = link_to ide_edit_path(@project, @ref, @path), class: 'btn btn-default qa-web-ide-button' do + = _('Web IDE') - else = link_to '#modal-confirm-fork', class: 'btn btn-default qa-web-ide-button', data: { target: '#modal-confirm-fork', toggle: 'modal'} do = _('Web IDE') diff --git a/changelogs/unreleased/32562-dynamic-db-pool-size-in-puma.yml b/changelogs/unreleased/32562-dynamic-db-pool-size-in-puma.yml new file mode 100644 index 00000000000..e90de9a4d6a --- /dev/null +++ b/changelogs/unreleased/32562-dynamic-db-pool-size-in-puma.yml @@ -0,0 +1,5 @@ +--- +title: 'Puma only: database connection pool now always >= number of worker threads' +merge_request: 19286 +author: +type: performance diff --git a/changelogs/unreleased/33897-make-arrow-buttons-work-within-search-box.yml b/changelogs/unreleased/33897-make-arrow-buttons-work-within-search-box.yml new file mode 100644 index 00000000000..8132343322b --- /dev/null +++ b/changelogs/unreleased/33897-make-arrow-buttons-work-within-search-box.yml @@ -0,0 +1,5 @@ +--- +title: Fix keyboard shortcuts in header search autocomplete +merge_request: 18685 +author: +type: fixed diff --git a/changelogs/unreleased/34717-update-expired-trial-copy.yml b/changelogs/unreleased/34717-update-expired-trial-copy.yml new file mode 100644 index 00000000000..d1f77d3e4cb --- /dev/null +++ b/changelogs/unreleased/34717-update-expired-trial-copy.yml @@ -0,0 +1,5 @@ +--- +title: Update expired trial status copy +merge_request: 18962 +author: +type: changed diff --git a/changelogs/unreleased/34757-bugfix-graphql-missing-todo-types.yml b/changelogs/unreleased/34757-bugfix-graphql-missing-todo-types.yml new file mode 100644 index 00000000000..94ed1963f53 --- /dev/null +++ b/changelogs/unreleased/34757-bugfix-graphql-missing-todo-types.yml @@ -0,0 +1,5 @@ +--- +title: Fix errors in GraphQL Todos API due to missing TargetTypeEnum values +merge_request: 19052 +author: +type: fixed diff --git a/changelogs/unreleased/most-affected-projects.yml b/changelogs/unreleased/most-affected-projects.yml new file mode 100644 index 00000000000..1835f62e533 --- /dev/null +++ b/changelogs/unreleased/most-affected-projects.yml @@ -0,0 +1,5 @@ +--- +title: Add endpoint for a group's vulnerable projects +merge_request: 15317 +author: +type: added diff --git a/changelogs/unreleased/remove-empty-github-service-templates.yml b/changelogs/unreleased/remove-empty-github-service-templates.yml new file mode 100644 index 00000000000..6f5bb3ddcf1 --- /dev/null +++ b/changelogs/unreleased/remove-empty-github-service-templates.yml @@ -0,0 +1,5 @@ +--- +title: Remove empty Github service templates from database +merge_request: 18868 +author: +type: fixed diff --git a/changelogs/unreleased/sh-set-admin-default-visibilities.yml b/changelogs/unreleased/sh-set-admin-default-visibilities.yml new file mode 100644 index 00000000000..46e0b5c0ca1 --- /dev/null +++ b/changelogs/unreleased/sh-set-admin-default-visibilities.yml @@ -0,0 +1,5 @@ +--- +title: Enforce default, global project and snippet visibilities +merge_request: 19188 +author: +type: fixed diff --git a/config/initializers/database_config.rb b/config/initializers/database_config.rb new file mode 100644 index 00000000000..d8c2821066b --- /dev/null +++ b/config/initializers/database_config.rb @@ -0,0 +1,18 @@ +# frozen_string_literal: true + +# when running on puma, scale connection pool size with the number +# of threads per worker process +if defined?(::Puma) + db_config = Gitlab::Database.config || + Rails.application.config.database_configuration[Rails.env] + puma_options = Puma.cli_config.options + + # We use either the maximum number of threads per worker process, or + # the user specified value, whichever is larger. + desired_pool_size = [db_config['pool'].to_i, puma_options[:max_threads]].max + + db_config['pool'] = desired_pool_size + + # recreate the connection pool from the new config + ActiveRecord::Base.establish_connection(db_config) +end diff --git a/db/migrate/20191026124116_set_application_settings_default_project_and_snippet_visibility.rb b/db/migrate/20191026124116_set_application_settings_default_project_and_snippet_visibility.rb new file mode 100644 index 00000000000..9d19279510a --- /dev/null +++ b/db/migrate/20191026124116_set_application_settings_default_project_and_snippet_visibility.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +class SetApplicationSettingsDefaultProjectAndSnippetVisibility < ActiveRecord::Migration[5.2] + include Gitlab::Database::MigrationHelpers + + DOWNTIME = false + + def change + change_column_null :application_settings, :default_project_visibility, false, 0 + change_column_default :application_settings, :default_project_visibility, from: nil, to: 0 + + change_column_null :application_settings, :default_snippet_visibility, false, 0 + change_column_default :application_settings, :default_snippet_visibility, from: nil, to: 0 + end +end diff --git a/db/post_migrate/20191021101942_remove_empty_github_service_templates.rb b/db/post_migrate/20191021101942_remove_empty_github_service_templates.rb new file mode 100644 index 00000000000..64abe93b3e8 --- /dev/null +++ b/db/post_migrate/20191021101942_remove_empty_github_service_templates.rb @@ -0,0 +1,28 @@ +# frozen_string_literal: true + +## It's expected to delete one record on GitLab.com +# +class RemoveEmptyGithubServiceTemplates < ActiveRecord::Migration[5.2] + DOWNTIME = false + + class Service < ActiveRecord::Base + self.table_name = 'services' + self.inheritance_column = :_type_disabled + + serialize :properties, JSON + end + + def up + relationship.where(properties: {}).delete_all + end + + def down + relationship.find_or_create_by!(properties: {}) + end + + private + + def relationship + RemoveEmptyGithubServiceTemplates::Service.where(template: true, type: 'GithubService') + end +end diff --git a/db/schema.rb b/db/schema.rb index 09149cfbcfe..c4a541824c8 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 2019_10_26_041447) do +ActiveRecord::Schema.define(version: 2019_10_26_124116) do # These are extensions that must be enabled in order to support this database enable_extension "pg_trgm" @@ -158,8 +158,8 @@ ActiveRecord::Schema.define(version: 2019_10_26_041447) do t.text "restricted_visibility_levels" t.boolean "version_check_enabled", default: true t.integer "max_attachment_size", default: 10, null: false - t.integer "default_project_visibility" - t.integer "default_snippet_visibility" + t.integer "default_project_visibility", default: 0, null: false + t.integer "default_snippet_visibility", default: 0, null: false t.text "domain_whitelist" t.boolean "user_oauth_applications", default: true t.string "after_sign_out_path" 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 15cdd25e711..568104cb30b 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 @@ -5,7 +5,7 @@ require 'rails/generators' module Rails class PostDeploymentMigrationGenerator < Rails::Generators::NamedBase def create_migration_file - timestamp = Time.now.strftime('%Y%m%d%H%M%S') + timestamp = Time.now.utc.strftime('%Y%m%d%H%M%S') template "migration.rb", "db/post_migrate/#{timestamp}_#{file_name}.rb" end diff --git a/locale/gitlab.pot b/locale/gitlab.pot index 1097cc0238d..a0b661c023f 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -2421,9 +2421,6 @@ msgstr "" msgid "BillingPlans|Learn more about each plan by reading our %{faq_link}, or start a free 30-day trial of GitLab.com Gold." msgstr "" -msgid "BillingPlans|Learn more about each plan by reading our %{faq_link}." -msgstr "" - msgid "BillingPlans|Learn more about each plan by visiting our %{pricing_page_link}." msgstr "" @@ -2442,18 +2439,15 @@ msgstr "" msgid "BillingPlans|To manage the plan for this group, visit the billing section of %{parent_billing_page_link}." msgstr "" -msgid "BillingPlans|Your GitLab.com trial expired on %{expiration_date}. %{learn_more_text}" +msgid "BillingPlans|Your GitLab.com Gold trial expired on %{expiration_date}. You can restore access to the Gold features at any time by upgrading below." msgstr "" -msgid "BillingPlans|Your GitLab.com trial will <strong>expire after %{expiration_date}</strong>. You can learn more about GitLab.com Gold by reading about our %{features_link}." +msgid "BillingPlans|Your GitLab.com Gold trial will <strong>expire after %{expiration_date}</strong>. You can retain access to the Gold features by upgrading below." msgstr "" msgid "BillingPlans|billed annually at %{price_per_year}" msgstr "" -msgid "BillingPlans|features" -msgstr "" - msgid "BillingPlans|frequently asked questions" msgstr "" diff --git a/spec/features/search/user_uses_header_search_field_spec.rb b/spec/features/search/user_uses_header_search_field_spec.rb index d386e489739..0f60cd42748 100644 --- a/spec/features/search/user_uses_header_search_field_spec.rb +++ b/spec/features/search/user_uses_header_search_field_spec.rb @@ -26,6 +26,16 @@ describe 'User uses header search field', :js do end end + context 'when using the keyboard shortcut' do + before do + find('body').native.send_keys('s') + end + + it 'shows the category search dropdown' do + expect(page).to have_selector('.dropdown-header', text: /#{scope_name}/i) + end + end + context 'when clicking the search field' do before do page.find('#search.js-autocomplete-disabled').click @@ -77,15 +87,21 @@ describe 'User uses header search field', :js do end context 'when entering text into the search field' do - before do + it 'does not display the category search dropdown' do page.within('.search-input-wrap') do fill_in('search', with: scope_name.first(4)) end - end - it 'does not display the category search dropdown' do expect(page).not_to have_selector('.dropdown-header', text: /#{scope_name}/i) end + + it 'hides the dropdown when there are no results' do + page.within('.search-input-wrap') do + fill_in('search', with: 'a_search_term_with_no_results') + end + + expect(page).not_to have_selector('.dropdown-menu') + end end end diff --git a/spec/frontend/repository/components/__snapshots__/directory_download_links_spec.js.snap b/spec/frontend/repository/components/__snapshots__/directory_download_links_spec.js.snap new file mode 100644 index 00000000000..31a1cd23060 --- /dev/null +++ b/spec/frontend/repository/components/__snapshots__/directory_download_links_spec.js.snap @@ -0,0 +1,75 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Repository directory download links component renders downloads links for path app 1`] = ` +<section + class="border-top pt-1 mt-1" +> + <h5 + class="m-0 dropdown-bold-header" + > + Download this directory + </h5> + + <div + class="dropdown-menu-content" + > + <div + class="btn-group ml-0 w-100" + > + <gllink-stub + class="btn btn-xs btn-primary" + href="http://test.com/?path=app" + > + + zip + + </gllink-stub> + <gllink-stub + class="btn btn-xs" + href="http://test.com/?path=app" + > + + tar + + </gllink-stub> + </div> + </div> +</section> +`; + +exports[`Repository directory download links component renders downloads links for path app/assets 1`] = ` +<section + class="border-top pt-1 mt-1" +> + <h5 + class="m-0 dropdown-bold-header" + > + Download this directory + </h5> + + <div + class="dropdown-menu-content" + > + <div + class="btn-group ml-0 w-100" + > + <gllink-stub + class="btn btn-xs btn-primary" + href="http://test.com/?path=app/assets" + > + + zip + + </gllink-stub> + <gllink-stub + class="btn btn-xs" + href="http://test.com/?path=app/assets" + > + + tar + + </gllink-stub> + </div> + </div> +</section> +`; diff --git a/spec/frontend/repository/components/directory_download_links_spec.js b/spec/frontend/repository/components/directory_download_links_spec.js new file mode 100644 index 00000000000..4d70b44de08 --- /dev/null +++ b/spec/frontend/repository/components/directory_download_links_spec.js @@ -0,0 +1,29 @@ +import { shallowMount } from '@vue/test-utils'; +import DirectoryDownloadLinks from '~/repository/components/directory_download_links.vue'; + +let vm; + +function factory(currentPath) { + vm = shallowMount(DirectoryDownloadLinks, { + propsData: { + currentPath, + links: [{ text: 'zip', path: 'http://test.com/' }, { text: 'tar', path: 'http://test.com/' }], + }, + }); +} + +describe('Repository directory download links component', () => { + afterEach(() => { + vm.destroy(); + }); + + it.each` + path + ${'app'} + ${'app/assets'} + `('renders downloads links for path $path', ({ path }) => { + factory(path); + + expect(vm.element).toMatchSnapshot(); + }); +}); diff --git a/spec/frontend/vue_shared/components/issue/related_issuable_item_spec.js b/spec/frontend/vue_shared/components/issue/related_issuable_item_spec.js index b85e2673624..575d35c50a9 100644 --- a/spec/frontend/vue_shared/components/issue/related_issuable_item_spec.js +++ b/spec/frontend/vue_shared/components/issue/related_issuable_item_spec.js @@ -1,6 +1,7 @@ import Vue from 'vue'; import { formatDate } from '~/lib/utils/datetime_utility'; import { mount, createLocalVue } from '@vue/test-utils'; +import { PathIdSeparator } from 'ee/related_issues/constants'; import RelatedIssuableItem from '~/vue_shared/components/issue/related_issuable_item.vue'; import { defaultAssignees, @@ -12,7 +13,7 @@ describe('RelatedIssuableItem', () => { const props = { idKey: 1, displayReference: 'gitlab-org/gitlab-test#1', - pathIdSeparator: '#', + pathIdSeparator: PathIdSeparator.Issue, path: `${gl.TEST_HOST}/path`, title: 'title', confidential: true, diff --git a/spec/initializers/database_config_spec.rb b/spec/initializers/database_config_spec.rb new file mode 100644 index 00000000000..a5a074f5884 --- /dev/null +++ b/spec/initializers/database_config_spec.rb @@ -0,0 +1,73 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe 'Database config initializer' do + subject do + load Rails.root.join('config/initializers/database_config.rb') + end + + before do + allow(ActiveRecord::Base).to receive(:establish_connection) + end + + context "when using Puma" do + let(:puma) { double('puma') } + let(:puma_options) { { max_threads: 8 } } + + before do + stub_const("Puma", puma) + allow(puma).to receive_message_chain(:cli_config, :options).and_return(puma_options) + end + + context "and no existing pool size is set" do + before do + stub_database_config(pool_size: nil) + end + + it "sets it to the max number of worker threads" do + expect { subject }.to change { Gitlab::Database.config['pool'] }.from(nil).to(8) + end + end + + context "and the existing pool size is smaller than the max number of worker threads" do + before do + stub_database_config(pool_size: 7) + end + + it "sets it to the max number of worker threads" do + expect { subject }.to change { Gitlab::Database.config['pool'] }.from(7).to(8) + end + end + + context "and the existing pool size is larger than the max number of worker threads" do + before do + stub_database_config(pool_size: 9) + end + + it "keeps the configured pool size" do + expect { subject }.not_to change { Gitlab::Database.config['pool'] } + end + end + end + + context "when not using Puma" do + before do + stub_database_config(pool_size: 7) + end + + it "does nothing" do + expect { subject }.not_to change { Gitlab::Database.config['pool'] } + end + end + + def stub_database_config(pool_size:) + config = { + 'adapter' => 'postgresql', + 'host' => 'db.host.com', + 'pool' => pool_size + }.compact + + allow(Gitlab::Database).to receive(:config).and_return(config) + end +end diff --git a/spec/migrations/remove_empty_github_service_templates_spec.rb b/spec/migrations/remove_empty_github_service_templates_spec.rb new file mode 100644 index 00000000000..c128c8538db --- /dev/null +++ b/spec/migrations/remove_empty_github_service_templates_spec.rb @@ -0,0 +1,55 @@ +# frozen_string_literal: true + +require 'spec_helper' +require Rails.root.join('db', 'post_migrate', '20191021101942_remove_empty_github_service_templates.rb') + +describe RemoveEmptyGithubServiceTemplates, :migration do + subject(:migration) { described_class.new } + + let(:services) do + table(:services).tap do |klass| + klass.class_eval do + serialize :properties, JSON + end + end + end + + before do + services.delete_all + + create_service(properties: nil) + create_service(properties: {}) + create_service(properties: { some: :value }) + create_service(properties: {}, template: false) + create_service(properties: {}, type: 'SomeType') + end + + def all_service_properties + services.where(template: true, type: 'GithubService').pluck(:properties) + end + + it 'correctly migrates up and down service templates' do + reversible_migration do |migration| + migration.before -> do + expect(services.count).to eq(5) + + expect(all_service_properties) + .to match(a_collection_containing_exactly(nil, {}, { 'some' => 'value' })) + end + + migration.after -> do + expect(services.count).to eq(4) + + expect(all_service_properties) + .to match(a_collection_containing_exactly(nil, { 'some' => 'value' })) + end + end + end + + def create_service(params) + data = { template: true, type: 'GithubService' } + data.merge!(params) + + services.create!(data) + end +end diff --git a/spec/requests/api/graphql/current_user/todos_query_spec.rb b/spec/requests/api/graphql/current_user/todos_query_spec.rb index 6817e37e64b..82deba0d92c 100644 --- a/spec/requests/api/graphql/current_user/todos_query_spec.rb +++ b/spec/requests/api/graphql/current_user/todos_query_spec.rb @@ -13,7 +13,7 @@ describe 'Query current user todos' do let(:fields) do <<~QUERY nodes { - id + #{all_graphql_fields_for('todos'.classify)} } QUERY end @@ -28,6 +28,8 @@ describe 'Query current user todos' do post_graphql(query, current_user: current_user) end + it_behaves_like 'a working graphql query' + it 'contains the expected ids' do is_expected.to include( a_hash_including('id' => commit_todo.to_global_id.to_s), @@ -35,4 +37,12 @@ describe 'Query current user todos' do a_hash_including('id' => merge_request_todo.to_global_id.to_s) ) end + + it 'returns Todos for all target types' do + is_expected.to include( + a_hash_including('targetType' => 'COMMIT'), + a_hash_including('targetType' => 'ISSUE'), + a_hash_including('targetType' => 'MERGEREQUEST') + ) + end end diff --git a/spec/requests/api/graphql/current_user_query_spec.rb b/spec/requests/api/graphql/current_user_query_spec.rb new file mode 100644 index 00000000000..9db638ea59e --- /dev/null +++ b/spec/requests/api/graphql/current_user_query_spec.rb @@ -0,0 +1,33 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe 'getting project information' do + include GraphqlHelpers + + let(:query) do + graphql_query_for('currentUser', {}, 'name') + end + + subject { graphql_data['currentUser'] } + + before do + post_graphql(query, current_user: current_user) + end + + context 'when there is a current_user' do + set(:current_user) { create(:user) } + + it_behaves_like 'a working graphql query' + + it { is_expected.to include('name' => current_user.name) } + end + + context 'when there is no current_user' do + let(:current_user) { nil } + + it_behaves_like 'a working graphql query' + + it { is_expected.to be_nil } + end +end diff --git a/spec/services/clusters/update_service_spec.rb b/spec/services/clusters/update_service_spec.rb index 8c2d8c9246e..fdbed4fa5d8 100644 --- a/spec/services/clusters/update_service_spec.rb +++ b/spec/services/clusters/update_service_spec.rb @@ -138,6 +138,23 @@ describe Clusters::UpdateService do expect(cluster.management_project_id).to be_nil end end + + context 'cluster already has a management project set' do + before do + cluster.update!(management_project: create(:project)) + end + + let(:params) do + { management_project_id: '' } + end + + it 'unsets management_project_id' do + is_expected.to eq(true) + + cluster.reload + expect(cluster.management_project_id).to be_nil + end + end end context 'project cluster' do diff --git a/vendor/gitignore/C++.gitignore b/vendor/gitignore/C++.gitignore index 259148fa18f..259148fa18f 100644..100755 --- a/vendor/gitignore/C++.gitignore +++ b/vendor/gitignore/C++.gitignore diff --git a/vendor/gitignore/Java.gitignore b/vendor/gitignore/Java.gitignore index a1c2a238a96..a1c2a238a96 100644..100755 --- a/vendor/gitignore/Java.gitignore +++ b/vendor/gitignore/Java.gitignore |