Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
b539ac1d61
commit
228d752ff0
|
|
@ -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');
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
@ -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>
|
||||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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')
|
||||
|
|
|
|||
|
|
@ -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',
|
||||
|
|
|
|||
|
|
@ -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')
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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')
|
||||
|
|
|
|||
|
|
@ -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')
|
||||
|
|
|
|||
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: 'Puma only: database connection pool now always >= number of worker threads'
|
||||
merge_request: 19286
|
||||
author:
|
||||
type: performance
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Fix keyboard shortcuts in header search autocomplete
|
||||
merge_request: 18685
|
||||
author:
|
||||
type: fixed
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Update expired trial status copy
|
||||
merge_request: 18962
|
||||
author:
|
||||
type: changed
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Fix errors in GraphQL Todos API due to missing TargetTypeEnum values
|
||||
merge_request: 19052
|
||||
author:
|
||||
type: fixed
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Add endpoint for a group's vulnerable projects
|
||||
merge_request: 15317
|
||||
author:
|
||||
type: added
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Remove empty Github service templates from database
|
||||
merge_request: 18868
|
||||
author:
|
||||
type: fixed
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Enforce default, global project and snippet visibilities
|
||||
merge_request: 19188
|
||||
author:
|
||||
type: fixed
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 ""
|
||||
|
||||
|
|
|
|||
|
|
@ -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,14 +87,20 @@ 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
|
||||
|
||||
expect(page).not_to have_selector('.dropdown-header', text: /#{scope_name}/i)
|
||||
end
|
||||
|
||||
it 'does not display the category search dropdown' do
|
||||
expect(page).not_to have_selector('.dropdown-header', text: /#{scope_name}/i)
|
||||
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
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
`;
|
||||
|
|
@ -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();
|
||||
});
|
||||
});
|
||||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Reference in New Issue