Commit 5e8db1dc authored by GitLab Bot's avatar GitLab Bot

Automatic merge of gitlab-org/gitlab master

parents fbe7149e d6640605
......@@ -272,9 +272,6 @@ gem 'loofah', '~> 2.2'
# Working with license
gem 'licensee', '~> 8.9'
# Ace editor
gem 'ace-rails-ap', '~> 4.1.0'
# Detect and convert string character encoding
gem 'charlock_holmes', '~> 0.7.7'
......
......@@ -3,7 +3,6 @@ GEM
specs:
RedCloth (4.3.2)
abstract_type (0.0.7)
ace-rails-ap (4.1.2)
acme-client (2.0.6)
faraday (>= 0.17, < 2.0.0)
actioncable (6.0.3.3)
......@@ -1253,7 +1252,6 @@ PLATFORMS
DEPENDENCIES
RedCloth (~> 4.3.2)
ace-rails-ap (~> 4.1.0)
acme-client (~> 2.0, >= 2.0.6)
activerecord-explain-analyze (~> 0.1)
acts-as-taggable-on (~> 6.0)
......
......@@ -9,6 +9,7 @@ import {
GlLink,
GlSprintf,
} from '@gitlab/ui';
import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import { s__ } from '~/locale';
import { integrationTypes } from '../constants';
......@@ -18,8 +19,14 @@ export default {
'AlertSettings|Learn more about our upcoming %{linkStart}integrations%{linkEnd}',
),
integrationFormSteps: {
step1: s__('AlertSettings|1. Select integration type'),
step2: s__('AlertSettings|2. Name integration'),
step1: { title: s__('AlertSettings|1. Select integration type') },
step2: { title: s__('AlertSettings|2. Name integration') },
step5: {
title: s__('AlertSettings|5. Map fields (optional)'),
intro: s__(
'AlertSettings|The default GitLab alert keys are listed below. In the event an exact match could be found in the sample payload provided, that key will be mapped automatically. In all other cases, please define which payload key should map to the specified GitLab key. Any payload keys not shown in this list will not display in the alert list, but will display on the alert details page.',
),
},
},
},
components: {
......@@ -32,6 +39,7 @@ export default {
GlLink,
GlSprintf,
},
mixins: [glFeatureFlagsMixin()],
data() {
return {
selectedIntegration: integrationTypes[0].value,
......@@ -66,11 +74,10 @@ export default {
<gl-form-group
id="integration-type"
:label="$options.i18n.integrationFormSteps.step1"
:label="$options.i18n.integrationFormSteps.step1.title"
label-for="integration-type"
>
<gl-form-select
id="integration-type"
v-model="selectedIntegration"
:options="options"
@change="onIntegrationTypeSelect"
......@@ -91,16 +98,26 @@ export default {
<gl-collapse v-model="formVisible" class="gl-mt-3">
<gl-form-group
id="name-integration"
:label="$options.i18n.integrationFormSteps.step2"
:label="$options.i18n.integrationFormSteps.step2.title"
label-for="name-integration"
>
<gl-form-input
id="name-integration"
v-model="form.name"
type="text"
:placeholder="s__('AlertSettings|Enter integration name')"
/>
</gl-form-group>
<gl-form-group
v-if="glFeatures.multipleHttpIntegrationsCustomMapping"
id="mapping-builder"
:label="$options.i18n.integrationFormSteps.step5.title"
label-for="mapping-builder"
>
<span class="gl-text-gray-500">{{ $options.i18n.integrationFormSteps.step5.intro }}</span>
<!--mapping builder will be added here-->
</gl-form-group>
<div class="gl-display-flex gl-justify-content-end">
<gl-button type="reset" class="gl-mr-3 js-no-auto-disable">{{ __('Cancel') }}</gl-button>
<gl-button
......
/*= require ace/ace */
/*= require ace/ext-modelist */
/*= require ace/ext-searchbox */
/*= require ./ace/ace_config_paths */
<%
ace_gem_path = Bundler.rubygems.find_name('ace-rails-ap').first.full_gem_path
ace_workers = Dir[ace_gem_path + '/vendor/assets/javascripts/ace/worker-*.js'].sort.map do |file|
File.basename(file, '.js').sub(/^worker-/, '')
end
ace_modes = Dir[ace_gem_path + '/vendor/assets/javascripts/ace/mode-*.js'].sort.map do |file|
File.basename(file, '.js').sub(/^mode-/, '')
end
%>
// Lazy-load configuration when ace.edit is called
(function() {
var basePath;
var ace = window.ace;
var edit = ace.edit;
ace.edit = function() {
window.gon = window.gon || {};
basePath = (window.gon.relative_url_root || '').replace(/\/$/, '') + '/assets/ace';
ace.config.set('basePath', basePath);
// configure paths for all worker modules
<% ace_workers.each do |worker| %>
ace.config.setModuleUrl('ace/mode/<%= worker %>_worker', basePath + '/<%= File.basename(asset_path("ace/worker-#{worker}.js")) %>');
<% end %>
// configure paths for all mode modules
<% ace_modes.each do |mode| %>
ace.config.setModuleUrl('ace/mode/<%= mode %>', basePath + '/<%= File.basename(asset_path("ace/mode-#{mode}.js")) %>');
<% end %>
// restore original method
ace.edit = edit;
return ace.edit.apply(ace, arguments);
};
})();
/* global ace */
export default function getModeByFileExtension(path) {
const modelist = ace.require('ace/ext/modelist');
return modelist.getModeForPath(path).mode;
}
......@@ -8,6 +8,7 @@ module Projects
before_action do
push_frontend_feature_flag(:http_integrations_list, @project)
push_frontend_feature_flag(:multiple_http_integrations_custom_mapping, @project)
end
respond_to :json, only: [:reset_alerting_token, :reset_pagerduty_token]
......
......@@ -150,7 +150,7 @@ module SearchHelper
if @project && @project.repository.root_ref
ref = @ref || @project.repository.root_ref
[
result = [
{ category: "In this project", label: _("Files"), url: project_tree_path(@project, ref) },
{ category: "In this project", label: _("Commits"), url: project_commits_path(@project, ref) },
{ category: "In this project", label: _("Network"), url: project_network_path(@project, ref) },
......@@ -162,6 +162,12 @@ module SearchHelper
{ category: "In this project", label: _("Members"), url: project_project_members_path(@project) },
{ category: "In this project", label: _("Wiki"), url: project_wikis_path(@project) }
]
if can?(current_user, :read_feature_flag, @project)
result << { category: "In this project", label: _("Feature Flags"), url: project_feature_flags_path(@project) }
end
result
else
[]
end
......
# frozen_string_literal: true
class Experiment < ApplicationRecord
include ::Gitlab::Experimentation::GroupTypes
has_many :experiment_users
has_many :users, through: :experiment_users
has_many :control_group_users, -> { merge(ExperimentUser.control) }, through: :experiment_users, source: :user
......@@ -14,7 +16,7 @@ class Experiment < ApplicationRecord
return unless experiment
return if experiment.experiment_users.where(user: user).exists?
group_type == ::Gitlab::Experimentation::GROUP_CONTROL ? experiment.add_control_user(user) : experiment.add_experimental_user(user)
group_type == GROUP_CONTROL ? experiment.add_control_user(user) : experiment.add_experimental_user(user)
end
def add_control_user(user)
......
# frozen_string_literal: true
class ExperimentUser < ApplicationRecord
include ::Gitlab::Experimentation::GroupTypes
belongs_to :experiment
belongs_to :user
enum group_type: { control: 0, experimental: 1 }
enum group_type: { GROUP_CONTROL => 0, GROUP_EXPERIMENTAL => 1 }
validates :group_type, presence: true
end
......@@ -12,7 +12,7 @@
qa_selector: 'search_term_field' },
aria: { label: _('Search or jump to…') }
%button.hidden.js-dropdown-search-toggle{ type: 'button', data: { toggle: 'dropdown' } }
.dropdown-menu.dropdown-select.js-dashboard-search-options
.dropdown-menu.dropdown-select{ data: { testid: 'dashboard-search-options' } }
= dropdown_content do
%ul
%li.dropdown-menu-empty-item
......
- page_title _("Merge Conflicts"), "#{@merge_request.title} (#{@merge_request.to_reference}", _("Merge Requests")
- add_page_specific_style 'page_bundles/merge_conflicts'
- content_for :page_specific_javascripts do
= page_specific_javascript_tag('lib/ace.js')
= render "projects/merge_requests/mr_title"
.merge-request-details.issuable-details
......
---
title: Removed ACE editor from the codebase
merge_request: 46420
author:
type: removed
---
title: Fix when Feature Flags link is shown in search bar results
merge_request: 45803
author:
type: fixed
......@@ -201,7 +201,6 @@ module Gitlab
config.assets.precompile << "page_bundles/xterm.css"
config.assets.precompile << "lazy_bundles/cropper.css"
config.assets.precompile << "performance_bar.css"
config.assets.precompile << "lib/ace.js"
config.assets.precompile << "disable_animations.css"
config.assets.precompile << "snippets.css"
config.assets.precompile << "locale/**/app.js"
......
---
name: multiple_http_integrations_custom_mapping
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/46437
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/273573
type: development
group: group::health
default_enabled: false
......@@ -34,13 +34,6 @@ module EE
end
end
override :project_autocomplete
def project_autocomplete
return super unless @project && @project.feature_available?(:repository)
super + [{ category: "In this project", label: _("Feature Flags"), url: project_feature_flags_path(@project) }]
end
override :search_entries_scope_label
def search_entries_scope_label(scope, count)
case scope
......
......@@ -92,21 +92,6 @@ RSpec.describe SearchHelper do
end
end
describe '#project_autocomplete' do
let(:user) { create(:user) }
before do
@project = create(:project, :repository)
allow(self).to receive(:current_user).and_return(user)
end
context 'with a licensed user' do
it "does include feature flags" do
expect(project_autocomplete.find { |i| i[:label] == 'Feature Flags' }).to be_present
end
end
end
describe '#search_entries_info_template' do
let(:com_value) { true }
let(:elasticsearch_enabled) { true }
......
......@@ -91,15 +91,13 @@ module Gitlab
}
}.freeze
GROUP_CONTROL = :control
GROUP_EXPERIMENTAL = :experimental
# Controller concern that checks if an `experimentation_subject_id cookie` is present and sets it if absent.
# Used for A/B testing of experimental features. Exposes the `experiment_enabled?(experiment_name)` method
# to controllers and views. It returns true when the experiment is enabled and the user is selected as part
# of the experimental group.
#
module ControllerConcern
include ::Gitlab::Experimentation::GroupTypes
extend ActiveSupport::Concern
included do
......
# frozen_string_literal: true
module Gitlab
module Experimentation
module GroupTypes
GROUP_CONTROL = :control
GROUP_EXPERIMENTAL = :experimental
end
end
end
......@@ -2498,6 +2498,9 @@ msgstr ""
msgid "AlertSettings|2. Name integration"
msgstr ""
msgid "AlertSettings|5. Map fields (optional)"
msgstr ""
msgid "AlertSettings|API URL"
msgstr ""
......@@ -2573,6 +2576,9 @@ msgstr ""
msgid "AlertSettings|Test failed. Do you still want to save your changes anyway?"
msgstr ""
msgid "AlertSettings|The default GitLab alert keys are listed below. In the event an exact match could be found in the sample payload provided, that key will be mapped automatically. In all other cases, please define which payload key should map to the specified GitLab key. Any payload keys not shown in this list will not display in the alert list, but will display on the alert details page."
msgstr ""
msgid "AlertSettings|There was an error updating the alert settings."
msgstr ""
......
......@@ -36,15 +36,15 @@ RSpec.describe 'Global search' do
end
end
it 'closes the dropdown on blur', :js, quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/201841' do
it 'closes the dropdown on blur', :js do
find('#search').click
fill_in 'search', with: "a"
dropdown = find('.js-dashboard-search-options')
expect(dropdown[:class]).to include 'show'
expect(page).to have_selector("div[data-testid='dashboard-search-options'].show")
find('#search').send_keys(:backspace)
find('body').click
expect(dropdown[:class]).not_to include 'show'
expect(page).to have_no_selector("div[data-testid='dashboard-search-options'].show")
end
end
......@@ -5,11 +5,18 @@ require 'spec_helper'
RSpec.describe 'User uses header search field', :js do
include FilteredSearchHelpers
let(:project) { create(:project) }
let(:user) { create(:user) }
let_it_be(:project) { create(:project, :repository) }
let_it_be(:reporter) { create(:user) }
let_it_be(:developer) { create(:user) }
let(:user) { reporter }
before_all do
project.add_reporter(reporter)
project.add_developer(developer)
end
before do
project.add_reporter(user)
sign_in(user)
end
......@@ -132,6 +139,10 @@ RSpec.describe 'User uses header search field', :js do
let(:group) { create(:group) }
let(:project) { create(:project, namespace: group) }
before do
project.add_reporter(user)
end
include_examples 'search field examples' do
let(:url) { project_path(project) }
let(:scope_name) { project.name }
......@@ -159,6 +170,35 @@ RSpec.describe 'User uses header search field', :js do
expect(page).not_to have_selector(scoped_search_link('test', group_id: project.namespace_id))
expect(page).to have_selector(scoped_search_link('test', project_id: project.id))
end
it 'displays a link to project merge requests' do
fill_in_search('Merge')
within(dashboard_search_options_popup_menu) do
expect(page).to have_text('Merge Requests')
end
end
it 'does not display a link to project feature flags' do
fill_in_search('Feature')
within(dashboard_search_options_popup_menu) do
expect(page).to have_text('"Feature" in all GitLab')
expect(page).to have_no_text('Feature Flags')
end
end
context 'and user is a developer' do
let(:user) { developer }
it 'displays a link to project feature flags' do
fill_in_search('Feature')
within(dashboard_search_options_popup_menu) do
expect(page).to have_text('Feature Flags')
end
end
end
end
end
......@@ -217,4 +257,8 @@ RSpec.describe 'User uses header search field', :js do
".dropdown a[href='#{href}']"
end
def dashboard_search_options_popup_menu
"div[data-testid='dashboard-search-options']"
end
end
......@@ -4,12 +4,13 @@ exports[`AlertsSettingsFormNew with default values renders the initial template
"<gl-form-stub class=\\"gl-mt-6\\">
<h5 class=\\"gl-font-lg gl-my-5\\">Add new integrations</h5>
<gl-form-group-stub id=\\"integration-type\\" label=\\"1. Select integration type\\" label-for=\\"integration-type\\">
<gl-form-select-stub id=\\"integration-type\\" options=\\"[object Object],[object Object],[object Object],[object Object]\\" value=\\"\\"></gl-form-select-stub> <span class=\\"gl-text-gray-500\\"><gl-sprintf-stub message=\\"Learn more about our upcoming %{linkStart}integrations%{linkEnd}\\"></gl-sprintf-stub></span>
<gl-form-select-stub options=\\"[object Object],[object Object],[object Object],[object Object]\\" value=\\"\\"></gl-form-select-stub> <span class=\\"gl-text-gray-500\\"><gl-sprintf-stub message=\\"Learn more about our upcoming %{linkStart}integrations%{linkEnd}\\"></gl-sprintf-stub></span>
</gl-form-group-stub>
<b-collapse-stub tag=\\"div\\" class=\\"gl-mt-3\\">
<gl-form-group-stub id=\\"name-integration\\" label=\\"2. Name integration\\" label-for=\\"name-integration\\">
<b-form-input-stub id=\\"name-integration\\" value=\\"\\" placeholder=\\"Enter integration name\\" debounce=\\"0\\" type=\\"text\\" class=\\"gl-form-input\\"></b-form-input-stub>
<b-form-input-stub value=\\"\\" placeholder=\\"Enter integration name\\" debounce=\\"0\\" type=\\"text\\" class=\\"gl-form-input\\"></b-form-input-stub>
</gl-form-group-stub>
<!---->
<div class=\\"gl-display-flex gl-justify-content-end\\">
<gl-button-stub category=\\"primary\\" variant=\\"default\\" size=\\"medium\\" icon=\\"\\" buttontextclasses=\\"\\" type=\\"reset\\" class=\\"gl-mr-3 js-no-auto-disable\\">Cancel</gl-button-stub>
<gl-button-stub category=\\"secondary\\" variant=\\"success\\" size=\\"medium\\" icon=\\"\\" buttontextclasses=\\"\\" type=\\"submit\\" class=\\"gl-mr-1 js-no-auto-disable\\">Save and test payload</gl-button-stub>
......
......@@ -6,12 +6,17 @@ import { defaultAlertSettingsConfig } from './util';
describe('AlertsSettingsFormNew', () => {
let wrapper;
const createComponent = ({ methods } = {}, data) => {
const createComponent = (
{ methods } = {},
data,
multipleHttpIntegrationsCustomMapping = false,
) => {
wrapper = shallowMount(AlertsSettingsForm, {
data() {
return { ...data };
},
provide: {
glFeatures: { multipleHttpIntegrationsCustomMapping },
...defaultAlertSettingsConfig,
},
methods,
......@@ -23,6 +28,7 @@ describe('AlertsSettingsFormNew', () => {
const findSelect = () => wrapper.find(GlFormSelect);
const findFormSteps = () => wrapper.find(GlCollapse);
const findFormName = () => wrapper.find(GlFormInput);
const findMappingBuilderSection = () => wrapper.find(`[id = "mapping-builder"]`);
afterEach(() => {
if (wrapper) {
......@@ -54,4 +60,19 @@ describe('AlertsSettingsFormNew', () => {
expect(findFormName().isVisible()).toBe(true);
});
});
describe('Mapping builder section', () => {
beforeEach(() => {
createComponent({}, {});
});
it('should NOT render when feature flag disabled', () => {
expect(findMappingBuilderSection().exists()).toBe(false);
});
it('should render when feature flag is enabled', () => {
createComponent({}, {}, true);
expect(findMappingBuilderSection().exists()).toBe(true);
});
});
});
......@@ -143,6 +143,7 @@ RSpec.describe SearchHelper do
context "with a current project" do
before do
@project = create(:project, :repository)
allow(self).to receive(:can?).with(user, :read_feature_flag, @project).and_return(false)
end
it "includes project-specific sections", :aggregate_failures do
......@@ -162,6 +163,7 @@ RSpec.describe SearchHelper do
context 'when user has project access' do
before do
@project = create(:project, :repository, namespace: user.namespace)
allow(self).to receive(:can?).with(user, :read_feature_flag, @project).and_return(true)
end
it 'includes issues by iid', :aggregate_failures do
......
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Gitlab::Experimentation::GroupTypes do
it 'defines a GROUP_CONTROL constant' do
expect(described_class.const_defined?(:GROUP_CONTROL)).to be_truthy
end
it 'defines a GROUP_EXPERIMENTAL constant' do
expect(described_class.const_defined?(:GROUP_EXPERIMENTAL)).to be_truthy
end
end
......@@ -3,6 +3,7 @@
module SearchHelpers
def fill_in_search(text)
page.within('.search-input-wrap') do
find('#search').click
fill_in('search', with: text)
end
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment