diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2023-12-01 09:07:32 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2023-12-01 09:07:32 +0300 |
commit | 916bb1bb577b64ec769b4c081c4f861a2f47322f (patch) | |
tree | 5184da3dd62647179130811c75de9631c925300d | |
parent | a3e0d4c59ff43ee406d2fee12a29339b95a0787d (diff) |
Add latest changes from gitlab-org/gitlab@master
33 files changed, 101 insertions, 442 deletions
diff --git a/.rubocop_todo/gitlab/avoid_gitlab_instance_checks.yml b/.rubocop_todo/gitlab/avoid_gitlab_instance_checks.yml index cf0f863f7db..77a880115da 100644 --- a/.rubocop_todo/gitlab/avoid_gitlab_instance_checks.yml +++ b/.rubocop_todo/gitlab/avoid_gitlab_instance_checks.yml @@ -132,7 +132,6 @@ Gitlab/AvoidGitlabInstanceChecks: - 'lib/gitlab/database/migration_helpers/convert_to_bigint.rb' - 'lib/gitlab/database/migration_helpers/wraparound_autovacuum.rb' - 'lib/gitlab/database/migration_helpers/wraparound_vacuum_helpers.rb' - - 'lib/gitlab/experiment/rollout/feature.rb' - 'lib/gitlab/gon_helper.rb' - 'lib/gitlab/monitor/demo_projects.rb' - 'lib/gitlab/qa.rb' diff --git a/.rubocop_todo/rspec/named_subject.yml b/.rubocop_todo/rspec/named_subject.yml index 38c55d261f0..9ee86882271 100644 --- a/.rubocop_todo/rspec/named_subject.yml +++ b/.rubocop_todo/rspec/named_subject.yml @@ -2171,7 +2171,6 @@ RSpec/NamedSubject: - 'spec/lib/gitlab/event_store/store_spec.rb' - 'spec/lib/gitlab/exclusive_lease_helpers/sleeping_lock_spec.rb' - 'spec/lib/gitlab/exclusive_lease_helpers_spec.rb' - - 'spec/lib/gitlab/experiment/rollout/feature_spec.rb' - 'spec/lib/gitlab/faraday/error_callback_spec.rb' - 'spec/lib/gitlab/favicon_spec.rb' - 'spec/lib/gitlab/feature_categories_spec.rb' diff --git a/.rubocop_todo/rspec/verified_doubles.yml b/.rubocop_todo/rspec/verified_doubles.yml index d907b372477..8feedc2c4f9 100644 --- a/.rubocop_todo/rspec/verified_doubles.yml +++ b/.rubocop_todo/rspec/verified_doubles.yml @@ -497,7 +497,6 @@ RSpec/VerifiedDoubles: - 'spec/lib/gitlab/etag_caching/router/rails_spec.rb' - 'spec/lib/gitlab/etag_caching/router_spec.rb' - 'spec/lib/gitlab/event_store/store_spec.rb' - - 'spec/lib/gitlab/experiment/rollout/feature_spec.rb' - 'spec/lib/gitlab/external_authorization/access_spec.rb' - 'spec/lib/gitlab/external_authorization/logger_spec.rb' - 'spec/lib/gitlab/faraday/error_callback_spec.rb' diff --git a/.rubocop_todo/style/inline_disable_annotation.yml b/.rubocop_todo/style/inline_disable_annotation.yml index f5a43785756..6140c5227a6 100644 --- a/.rubocop_todo/style/inline_disable_annotation.yml +++ b/.rubocop_todo/style/inline_disable_annotation.yml @@ -1903,7 +1903,6 @@ Style/InlineDisableAnnotation: - 'ee/lib/ee/gitlab/import_export/project/object_builder.rb' - 'ee/lib/ee/gitlab/object_hierarchy.rb' - 'ee/lib/ee/gitlab/quick_actions/users_extractor.rb' - - 'ee/lib/ee/gitlab/saas.rb' - 'ee/lib/ee/gitlab/tracking.rb' - 'ee/lib/ee/gitlab/usage_data.rb' - 'ee/lib/ee/users/internal.rb' @@ -2526,7 +2525,6 @@ Style/InlineDisableAnnotation: - 'lib/gitlab/error_tracking/processor/sanitizer_processor.rb' - 'lib/gitlab/etag_caching/store.rb' - 'lib/gitlab/exclusive_lease.rb' - - 'lib/gitlab/experiment/rollout/feature.rb' - 'lib/gitlab/external_authorization/cache.rb' - 'lib/gitlab/faraday/error_callback.rb' - 'lib/gitlab/file_hook.rb' @@ -2864,7 +2862,6 @@ Style/InlineDisableAnnotation: - 'spec/controllers/projects/runners_controller_spec.rb' - 'spec/db/docs_spec.rb' - 'spec/deprecation_warnings.rb' - - 'spec/experiments/application_experiment_spec.rb' - 'spec/factories/design_management/designs.rb' - 'spec/factories/events.rb' - 'spec/factories/go_module_commits.rb' @@ -2990,7 +2987,6 @@ Style/InlineDisableAnnotation: - 'spec/lib/gitlab/doorkeeper_secret_storing/secret/pbkdf2_sha512_spec.rb' - 'spec/lib/gitlab/doorkeeper_secret_storing/token/pbkdf2_sha512_spec.rb' - 'spec/lib/gitlab/encoding_helper_spec.rb' - - 'spec/lib/gitlab/experiment/rollout/feature_spec.rb' - 'spec/lib/gitlab/gfm/uploads_rewriter_spec.rb' - 'spec/lib/gitlab/git/object_pool_spec.rb' - 'spec/lib/gitlab/git/remote_mirror_spec.rb' diff --git a/app/assets/javascripts/emoji/queries/custom_emoji.query.graphql b/app/assets/javascripts/emoji/queries/custom_emoji.query.graphql index ba3911ab091..951027ec274 100644 --- a/app/assets/javascripts/emoji/queries/custom_emoji.query.graphql +++ b/app/assets/javascripts/emoji/queries/custom_emoji.query.graphql @@ -1,7 +1,7 @@ query getCustomEmoji($groupPath: ID!) { group(fullPath: $groupPath) { id - customEmoji(includeAncestorGroups: true) { + customEmoji { nodes { id name diff --git a/app/assets/javascripts/vue_shared/components/awards_list.vue b/app/assets/javascripts/vue_shared/components/awards_list.vue index 3c19df9c196..59f03b41144 100644 --- a/app/assets/javascripts/vue_shared/components/awards_list.vue +++ b/app/assets/javascripts/vue_shared/components/awards_list.vue @@ -94,12 +94,14 @@ export default { return awardList.some((award) => award.user.id === this.currentUserId); }, createAwardList(name, list) { + const url = list.length ? list[0].url : null; + return { name, list, title: this.getAwardListTitle(list, name), classes: this.getAwardClassBindings(list), - html: glEmojiTag(name), + html: glEmojiTag(name, { url }), }; }, getAwardListTitle(awardsList, name) { diff --git a/app/experiments/application_experiment.rb b/app/experiments/application_experiment.rb index 60bf47c2f12..cfbd4eb6b75 100644 --- a/app/experiments/application_experiment.rb +++ b/app/experiments/application_experiment.rb @@ -3,6 +3,18 @@ class ApplicationExperiment < Gitlab::Experiment control { nil } # provide a default control for anonymous experiments + # We have experiments in ce/foss code even though they will never be available + # for ce/foss instances. + # We do that since we currently only experiment on the ee with SaaS instance. + # However, if the experiment is successful, we may commit the final code to ce/foss + # if the feature we are experimenting on is not a licensed or SaaS feature. + # + # This follows the https://docs.gitlab.com/ee/development/ee_features.html + # guidelines and therefore we have hardcoded `false` here. + def self.available? + false + end + def control_behavior # define a default nil control behavior so we can omit it when not needed end @@ -44,3 +56,5 @@ class ApplicationExperiment < Gitlab::Experiment actor.respond_to?(:id) ? actor : context.try(:user) end end + +ApplicationExperiment.prepend_mod diff --git a/app/finders/groups/custom_emoji_finder.rb b/app/finders/groups/custom_emoji_finder.rb deleted file mode 100644 index 80a4e948f8b..00000000000 --- a/app/finders/groups/custom_emoji_finder.rb +++ /dev/null @@ -1,26 +0,0 @@ -# frozen_string_literal: true - -module Groups - class CustomEmojiFinder < Base - include FinderWithGroupHierarchy - include Gitlab::Utils::StrongMemoize - - def initialize(group, params = {}) - @group = group - @params = params - @skip_authorization = true - end - - def execute - return CustomEmoji.none if Feature.disabled?(:custom_emoji, group) - - return CustomEmoji.for_resource(group) unless params[:include_ancestor_groups] - - CustomEmoji.for_namespaces(group_ids_for(group)) - end - - private - - attr_reader :group, :params, :skip_authorization - end -end diff --git a/app/graphql/resolvers/custom_emoji_resolver.rb b/app/graphql/resolvers/custom_emoji_resolver.rb deleted file mode 100644 index 1e39fafe486..00000000000 --- a/app/graphql/resolvers/custom_emoji_resolver.rb +++ /dev/null @@ -1,23 +0,0 @@ -# frozen_string_literal: true - -module Resolvers - class CustomEmojiResolver < BaseResolver - include Gitlab::Graphql::Authorize::AuthorizeResource - - authorizes_object! - - authorize :read_custom_emoji - - argument :include_ancestor_groups, - GraphQL::Types::Boolean, - required: false, - default_value: false, - description: 'Includes custom emoji from parent groups.' - - type Types::CustomEmojiType, null: true - - def resolve(**args) - Groups::CustomEmojiFinder.new(object, args).execute - end - end -end diff --git a/app/graphql/types/group_type.rb b/app/graphql/types/group_type.rb index a4eba3c63ae..74e7f256b44 100644 --- a/app/graphql/types/group_type.rb +++ b/app/graphql/types/group_type.rb @@ -21,8 +21,7 @@ module Types field :custom_emoji, type: Types::CustomEmojiType.connection_type, null: true, - resolver: Resolvers::CustomEmojiResolver, - description: 'Custom emoji in this namespace.', + description: 'Custom emoji within this namespace.', alpha: { milestone: '13.6' } field :share_with_group_lock, @@ -331,6 +330,10 @@ module Types group.dependency_proxy_setting || group.create_dependency_proxy_setting end + def custom_emoji + object.custom_emoji if Feature.enabled?(:custom_emoji) + end + private def group diff --git a/app/models/award_emoji.rb b/app/models/award_emoji.rb index 9c1005e19c7..4dc26a2d197 100644 --- a/app/models/award_emoji.rb +++ b/app/models/award_emoji.rb @@ -66,8 +66,7 @@ class AwardEmoji < ApplicationRecord def url return if TanukiEmoji.find_by_alpha_code(name) - Groups::CustomEmojiFinder.new(resource_parent, { include_ancestor_groups: true }).execute - .by_name(name)&.select(:url)&.first&.url + CustomEmoji.for_resource(resource_parent).by_name(name).select(:url).first&.url end def expire_cache diff --git a/app/models/custom_emoji.rb b/app/models/custom_emoji.rb index a3318cd0bd8..c704795130b 100644 --- a/app/models/custom_emoji.rb +++ b/app/models/custom_emoji.rb @@ -25,33 +25,20 @@ class CustomEmoji < ApplicationRecord format: { with: /\A#{NAME_REGEXP}\z/o } scope :by_name, -> (names) { where(name: names) } - scope :for_namespaces, -> (namespace_ids) do - order = Gitlab::Pagination::Keyset::Order.build([ - Gitlab::Pagination::Keyset::ColumnOrderDefinition.new( - attribute_name: 'name', - order_expression: CustomEmoji.arel_table[:name].asc, - nullable: :not_nullable, - distinct: true - ), - Gitlab::Pagination::Keyset::ColumnOrderDefinition.new( - attribute_name: 'current_namespace', - order_expression: Arel.sql("CASE WHEN namespace_id = #{namespace_ids.first} THEN 0 ELSE 1 END").asc, - nullable: :not_nullable, - add_to_projections: true - ) - ]) - where(namespace_id: namespace_ids) - .select("DISTINCT ON (name) *") - .order(order) - end alias_attribute :url, :file # this might need a change in https://gitlab.com/gitlab-org/gitlab/-/issues/230467 + # Find custom emoji for the given resource. + # A resource can be either a Project or a Group, or anything responding to #root_ancestor. + # Usually it's the return value of #resource_parent on any model. scope :for_resource, -> (resource) do - return none if resource.nil? || Feature.disabled?(:custom_emoji, resource) - return none unless resource.is_a?(Group) + return none if resource.nil? + + namespace = resource.root_ancestor + + return none if namespace.nil? || Feature.disabled?(:custom_emoji, namespace) - resource.custom_emoji + namespace.custom_emoji end private diff --git a/app/serializers/award_emoji_entity.rb b/app/serializers/award_emoji_entity.rb index 212931a2fa9..6ca782d8203 100644 --- a/app/serializers/award_emoji_entity.rb +++ b/app/serializers/award_emoji_entity.rb @@ -3,4 +3,5 @@ class AwardEmojiEntity < Grape::Entity expose :name expose :user, using: API::Entities::UserSafe + expose :url end diff --git a/app/validators/gitlab/emoji_name_validator.rb b/app/validators/gitlab/emoji_name_validator.rb index 68743530d83..c034a79214b 100644 --- a/app/validators/gitlab/emoji_name_validator.rb +++ b/app/validators/gitlab/emoji_name_validator.rb @@ -25,12 +25,8 @@ module Gitlab def valid_custom_emoji?(record, value) resource = record.try(:resource_parent) - namespace = resource.try(:namespace) - return unless resource.is_a?(Group) || namespace.is_a?(Group) - - Groups::CustomEmojiFinder.new(resource, { include_ancestor_groups: true }).execute - .by_name(value.to_s).any? + CustomEmoji.for_resource(resource).by_name(value.to_s).any? end end end diff --git a/config/initializers/gitlab_experiment.rb b/config/initializers/gitlab_experiment.rb index a54a741ed65..10ae1fa8ff0 100644 --- a/config/initializers/gitlab_experiment.rb +++ b/config/initializers/gitlab_experiment.rb @@ -49,7 +49,7 @@ Gitlab::Experiment.configure do |config| # valid_domains = %w[about.gitlab.com docs.gitlab.com gitlab.com gdk.test localhost] config.redirect_url_validator = lambda do |url| - Gitlab.com? && (url = URI.parse(url)) && valid_domains.include?(url.host) + ApplicationExperiment.available? && (url = URI.parse(url)) && valid_domains.include?(url.host) rescue URI::InvalidURIError false end diff --git a/doc/administration/auth/crowd.md b/doc/administration/auth/crowd.md index 574b8967b02..0034f244809 100644 --- a/doc/administration/auth/crowd.md +++ b/doc/administration/auth/crowd.md @@ -5,7 +5,7 @@ group: Authentication info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments --- -# Use Atlassian Crowd as an OAuth 2.0 authentication provider (deprecated) **(FREE SELF)** +# Use Atlassian Crowd as an authentication provider (deprecated) **(FREE SELF)** WARNING: This feature was [deprecated](https://gitlab.com/gitlab-org/gitlab/-/issues/369117) in GitLab 15.3 and is planned for diff --git a/doc/api/graphql/reference/index.md b/doc/api/graphql/reference/index.md index fc1c9c41f7f..bf25ee8182b 100644 --- a/doc/api/graphql/reference/index.md +++ b/doc/api/graphql/reference/index.md @@ -18894,6 +18894,7 @@ GPG signature for a signed commit. | <a id="groupcontainerrepositoriescount"></a>`containerRepositoriesCount` | [`Int!`](#int) | Number of container repositories in the group. | | <a id="groupcontainslockedprojects"></a>`containsLockedProjects` | [`Boolean!`](#boolean) | Includes at least one project where the repository size exceeds the limit. This only applies to namespaces under Project limit enforcement. | | <a id="groupcrossprojectpipelineavailable"></a>`crossProjectPipelineAvailable` | [`Boolean!`](#boolean) | Indicates if the cross_project_pipeline feature is available for the namespace. | +| <a id="groupcustomemoji"></a>`customEmoji` **{warning-solid}** | [`CustomEmojiConnection`](#customemojiconnection) | **Introduced** in 13.6. This feature is an Experiment. It can be changed or removed at any time. Custom emoji within this namespace. | | <a id="groupdependencyproxyblobcount"></a>`dependencyProxyBlobCount` | [`Int!`](#int) | Number of dependency proxy blobs cached in the group. | | <a id="groupdependencyproxyblobs"></a>`dependencyProxyBlobs` | [`DependencyProxyBlobConnection`](#dependencyproxyblobconnection) | Dependency Proxy blobs. (see [Connections](#connections)) | | <a id="groupdependencyproxyimagecount"></a>`dependencyProxyImageCount` | [`Int!`](#int) | Number of dependency proxy images cached in the group. | @@ -19185,26 +19186,6 @@ four standard [pagination arguments](#connection-pagination-arguments): | <a id="groupcontributionsfrom"></a>`from` | [`ISO8601Date!`](#iso8601date) | Start date of the reporting time range. | | <a id="groupcontributionsto"></a>`to` | [`ISO8601Date!`](#iso8601date) | End date of the reporting time range. The end date must be within 93 days after the start date. | -##### `Group.customEmoji` - -Custom emoji in this namespace. - -WARNING: -**Introduced** in 13.6. -This feature is an Experiment. It can be changed or removed at any time. - -Returns [`CustomEmojiConnection`](#customemojiconnection). - -This field returns a [connection](#connections). It accepts the -four standard [pagination arguments](#connection-pagination-arguments): -`before: String`, `after: String`, `first: Int`, `last: Int`. - -###### Arguments - -| Name | Type | Description | -| ---- | ---- | ----------- | -| <a id="groupcustomemojiincludeancestorgroups"></a>`includeAncestorGroups` | [`Boolean`](#boolean) | Includes custom emoji from parent groups. | - ##### `Group.customizableDashboardVisualizations` Visualizations of the group or associated configuration project. diff --git a/doc/user/group/insights/index.md b/doc/user/group/insights/index.md index 2ded1b08de2..251965904a1 100644 --- a/doc/user/group/insights/index.md +++ b/doc/user/group/insights/index.md @@ -56,6 +56,16 @@ By default, insights display all available dimensions on the chart. To exclude a dimension, from the legend below the chart, select the name of the dimension. +### Drill down on charts + +> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/372215/) in GitLab 16.7. + +You can drill down into the data of the **Bugs created per month by priority** and **Bugs created per month by severity** charts from the [default configuration file](https://gitlab.com/gitlab-org/gitlab/-/blob/master/ee/fixtures/insights/default.yml). + +To view a drill-down report of the data for a specific priority or severity in a month: + +- On the chart, select the bar stack you want to drill down on. + ## Configure group insights GitLab reads insights from the diff --git a/lib/banzai/filter/custom_emoji_filter.rb b/lib/banzai/filter/custom_emoji_filter.rb index 4dd6bada306..dddaaebc9de 100644 --- a/lib/banzai/filter/custom_emoji_filter.rb +++ b/lib/banzai/filter/custom_emoji_filter.rb @@ -48,9 +48,10 @@ module Banzai private def has_custom_emoji? - all_custom_emoji&.any? + strong_memoize(:has_custom_emoji) do + CustomEmoji.for_resource(resource_parent).any? + end end - strong_memoize_attr :has_custom_emoji? def resource_parent context[:project] || context[:group] @@ -61,12 +62,9 @@ module Banzai end def all_custom_emoji - Groups::CustomEmojiFinder.new(resource_parent, { include_ancestor_groups: true }) - .execute - .by_name(custom_emoji_candidates) - .index_by(&:name) + @all_custom_emoji ||= + CustomEmoji.for_resource(resource_parent).by_name(custom_emoji_candidates).index_by(&:name) end - strong_memoize_attr :all_custom_emoji end end end diff --git a/lib/gitlab/experiment/rollout/feature.rb b/lib/gitlab/experiment/rollout/feature.rb index 4ff61aa3551..0f2a1b9fb1d 100644 --- a/lib/gitlab/experiment/rollout/feature.rb +++ b/lib/gitlab/experiment/rollout/feature.rb @@ -13,7 +13,7 @@ module Gitlab # no inclusions, etc.) def enabled? return false unless feature_flag_defined? - return false unless Gitlab.com? + return false unless available? return false unless ::Feature.enabled?(:gitlab_experiment, type: :ops) feature_flag_instance.state != :off @@ -57,8 +57,12 @@ module Gitlab private + def available? + ApplicationExperiment.available? + end + def feature_flag_instance - ::Feature.get(feature_flag_name) # rubocop:disable Gitlab/AvoidFeatureGet + ::Feature.get(feature_flag_name) # rubocop:disable Gitlab/AvoidFeatureGet -- We are using at a lower layer here in experiment framework end def feature_flag_defined? diff --git a/locale/gitlab.pot b/locale/gitlab.pot index 095ebbead51..b2f358fa500 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -8975,6 +8975,12 @@ msgstr "" msgid "Browse templates" msgstr "" +msgid "Bugs created per month by Priority" +msgstr "" + +msgid "Bugs created per month by Severity" +msgstr "" + msgid "Build cannot be erased" msgstr "" diff --git a/spec/experiments/application_experiment_spec.rb b/spec/experiments/application_experiment_spec.rb index 00370a5b7e3..24e08241fe7 100644 --- a/spec/experiments/application_experiment_spec.rb +++ b/spec/experiments/application_experiment_spec.rb @@ -24,6 +24,12 @@ RSpec.describe ApplicationExperiment, :experiment, feature_category: :acquisitio expect { experiment(:example) {} }.not_to raise_error end + describe ".available?" do + it 'is false for foss' do + expect(described_class).not_to be_available + end + end + describe "#publish" do it "tracks the assignment", :snowplow do expect(application_experiment).to receive(:track).with(:assignment) @@ -169,33 +175,6 @@ RSpec.describe ApplicationExperiment, :experiment, feature_category: :acquisitio end describe "#process_redirect_url" do - using RSpec::Parameterized::TableSyntax - - where(:url, :processed_url) do - 'https://about.gitlab.com/' | 'https://about.gitlab.com/' - 'https://gitlab.com/' | 'https://gitlab.com/' - 'http://docs.gitlab.com' | 'http://docs.gitlab.com' - 'https://docs.gitlab.com/some/path?foo=bar' | 'https://docs.gitlab.com/some/path?foo=bar' - 'http://badgitlab.com' | nil - 'https://gitlab.com.nefarious.net' | nil - 'https://unknown.gitlab.com' | nil - "https://badplace.com\nhttps://gitlab.com" | nil - 'https://gitlabbcom' | nil - 'https://gitlabbcom/' | nil - 'http://gdk.test/foo/bar' | 'http://gdk.test/foo/bar' - 'http://localhost:3000/foo/bar' | 'http://localhost:3000/foo/bar' - end - - with_them do - it "returns the url or nil if invalid on SaaS", :saas do - expect(application_experiment.process_redirect_url(url)).to eq(processed_url) - end - - it "considers all urls invalid when not on SaaS" do - expect(application_experiment.process_redirect_url(url)).to be_nil - end - end - it "generates the correct urls based on where the engine was mounted" do url = Rails.application.routes.url_helpers.experiment_redirect_url(application_experiment, url: 'https://docs.gitlab.com') expect(url).to include("/-/experiment/namespaced%2Fstub:#{application_experiment.context.key}?https://docs.gitlab.com") @@ -227,7 +206,7 @@ RSpec.describe ApplicationExperiment, :experiment, feature_category: :acquisitio it "tracks an event", :snowplow do experiment(:top) { |e| e.control { experiment(:nested) {} } } - expect(Gitlab::Tracking).to have_received(:event).with( # rubocop:disable RSpec/ExpectGitlabTracking + expect(Gitlab::Tracking).to have_received(:event).with( # rubocop:disable RSpec/ExpectGitlabTracking -- Testing nested functionality 'top', :nested, hash_including(label: 'nested') diff --git a/spec/finders/groups/custom_emoji_finder_spec.rb b/spec/finders/groups/custom_emoji_finder_spec.rb deleted file mode 100644 index f1044997d4f..00000000000 --- a/spec/finders/groups/custom_emoji_finder_spec.rb +++ /dev/null @@ -1,65 +0,0 @@ -# frozen_string_literal: true - -require 'spec_helper' - -RSpec.describe Groups::CustomEmojiFinder, feature_category: :code_review_workflow do - describe '#execute' do - let(:params) { {} } - - subject(:execute) { described_class.new(group, params).execute } - - context 'when inside a group' do - let_it_be(:group) { create(:group) } - let_it_be(:custom_emoji) { create(:custom_emoji, group: group) } - - it 'returns custom emoji from group' do - expect(execute).to contain_exactly(custom_emoji) - end - end - - context 'when group is nil' do - let_it_be(:group) { nil } - - it 'returns nil' do - expect(execute).to be_empty - end - end - - context 'when group is a subgroup' do - let_it_be(:parent) { create(:group) } - let_it_be(:group) { create(:group, parent: parent) } - let_it_be(:custom_emoji) { create(:custom_emoji, group: group) } - - it 'returns custom emoji' do - expect(described_class.new(group, params).execute).to contain_exactly(custom_emoji) - end - end - - describe 'when custom emoji is in parent group' do - let_it_be(:parent) { create(:group) } - let_it_be(:group) { create(:group, parent: parent) } - let_it_be(:custom_emoji) { create(:custom_emoji, group: parent) } - let(:params) { { include_ancestor_groups: true } } - - it 'returns custom emoji' do - expect(execute).to contain_exactly(custom_emoji) - end - - context 'when params is empty' do - let(:params) { {} } - - it 'returns empty record' do - expect(execute).to eq([]) - end - end - - context 'when include_ancestor_groups is false' do - let(:params) { { include_ancestor_groups: false } } - - it 'returns empty record' do - expect(execute).to eq([]) - end - end - end - end -end diff --git a/spec/graphql/types/group_type_spec.rb b/spec/graphql/types/group_type_spec.rb index d3f9053faf3..6622551f063 100644 --- a/spec/graphql/types/group_type_spec.rb +++ b/spec/graphql/types/group_type_spec.rb @@ -125,37 +125,4 @@ RSpec.describe GitlabSchema.types['Group'] do expect { clean_state_query }.not_to exceed_all_query_limit(control) end end - - describe 'custom emoji' do - let_it_be(:user) { create(:user) } - let_it_be(:group) { create(:group) } - let_it_be(:subgroup) { create(:group, parent: group) } - let_it_be(:custom_emoji) { create(:custom_emoji, group: group) } - let_it_be(:custom_emoji_subgroup) { create(:custom_emoji, group: subgroup) } - let(:query) do - %( - query { - group(fullPath: "#{subgroup.full_path}") { - customEmoji(includeAncestorGroups: true) { - nodes { - id - } - } - } - } - ) - end - - before_all do - group.add_reporter(user) - end - - describe 'when includeAncestorGroups is true' do - it 'returns emoji from ancestor groups' do - result = GitlabSchema.execute(query, context: { current_user: user }).as_json - - expect(result.dig('data', 'group', 'customEmoji', 'nodes').count).to eq(2) - end - end - end end diff --git a/spec/lib/banzai/filter/custom_emoji_filter_spec.rb b/spec/lib/banzai/filter/custom_emoji_filter_spec.rb index 4fc9d9dd4f6..7fd25eac81b 100644 --- a/spec/lib/banzai/filter/custom_emoji_filter_spec.rb +++ b/spec/lib/banzai/filter/custom_emoji_filter_spec.rb @@ -55,12 +55,4 @@ RSpec.describe Banzai::Filter::CustomEmojiFilter, feature_category: :team_planni filter('<p>:tanuki:</p> <p>:party-parrot:</p>') end.not_to exceed_all_query_limit(control_count.count) end - - it 'uses custom emoji from ancestor group' do - subgroup = create(:group, parent: group) - - doc = filter('<p>:tanuki:</p>', group: subgroup) - - expect(doc.css('gl-emoji').size).to eq 1 - end end diff --git a/spec/lib/gitlab/experiment/rollout/feature_spec.rb b/spec/lib/gitlab/experiment/rollout/feature_spec.rb index 6d01b7a175f..9d602083ad6 100644 --- a/spec/lib/gitlab/experiment/rollout/feature_spec.rb +++ b/spec/lib/gitlab/experiment/rollout/feature_spec.rb @@ -3,50 +3,25 @@ require 'spec_helper' RSpec.describe Gitlab::Experiment::Rollout::Feature, :experiment, feature_category: :acquisition do - subject { described_class.new(subject_experiment) } + subject(:experiment_instance) { described_class.new(subject_experiment) } let(:subject_experiment) { experiment('namespaced/stub') } - describe "#enabled?", :saas do + describe "#enabled?" do before do stub_feature_flags(gitlab_experiment: true) - allow(subject).to receive(:feature_flag_defined?).and_return(true) - allow(subject).to receive(:feature_flag_instance).and_return(double(state: :on)) + allow(experiment_instance).to receive(:feature_flag_defined?).and_return(true) + allow(experiment_instance) + .to receive(:feature_flag_instance).and_return(instance_double('Flipper::Feature', state: :on)) end - it "is enabled when all criteria are met" do - expect(subject).to be_enabled - end - - it "isn't enabled if the feature definition doesn't exist" do - expect(subject).to receive(:feature_flag_defined?).and_return(false) - - expect(subject).not_to be_enabled - end - - it "isn't enabled if we're not in dev or dotcom environments" do - expect(Gitlab).to receive(:com?).and_return(false) - - expect(subject).not_to be_enabled - end - - it "isn't enabled if the feature flag state is :off" do - expect(subject).to receive(:feature_flag_instance).and_return(double(state: :off)) - - expect(subject).not_to be_enabled - end - - it "isn't enabled if the gitlab_experiment feature flag is false" do - stub_feature_flags(gitlab_experiment: false) - - expect(subject).not_to be_enabled - end + it { is_expected.not_to be_enabled } end describe "#execute_assignment" do let(:variants) do ->(e) do - # rubocop:disable Lint/EmptyBlock + # rubocop:disable Lint/EmptyBlock -- Specific for test e.control {} e.variant(:red) {} e.variant(:blue) {} @@ -63,26 +38,26 @@ RSpec.describe Gitlab::Experiment::Rollout::Feature, :experiment, feature_catego it "uses the default value as specified in the yaml" do expect(Feature).to receive(:enabled?).with( 'namespaced_stub', - subject, + experiment_instance, type: :experiment ).and_return(false) - expect(subject.execute_assignment).to be_nil + expect(experiment_instance.execute_assignment).to be_nil end it "returns an assigned name" do - expect(subject.execute_assignment).to eq(:blue) + expect(experiment_instance.execute_assignment).to eq(:blue) end context "when there are no behaviors" do - let(:variants) { ->(e) { e.control {} } } # rubocop:disable Lint/EmptyBlock + let(:variants) { ->(e) { e.control {} } } # rubocop:disable Lint/EmptyBlock -- Specific for test it "does not raise an error" do - expect { subject.execute_assignment }.not_to raise_error + expect { experiment_instance.execute_assignment }.not_to raise_error end end - context "for even rollout to non-control", :saas do + context "for even rollout to non-control" do let(:counts) { Hash.new(0) } let(:subject_experiment) { experiment('namespaced/stub') } @@ -91,8 +66,8 @@ RSpec.describe Gitlab::Experiment::Rollout::Feature, :experiment, feature_catego allow(instance).to receive(:enabled?).and_return(true) end - subject_experiment.variant(:variant1) {} # rubocop:disable Lint/EmptyBlock - subject_experiment.variant(:variant2) {} # rubocop:disable Lint/EmptyBlock + subject_experiment.variant(:variant1) {} # rubocop:disable Lint/EmptyBlock -- Specific for test + subject_experiment.variant(:variant2) {} # rubocop:disable Lint/EmptyBlock -- Specific for test end it "rolls out relatively evenly to 2 behaviors" do @@ -102,7 +77,7 @@ RSpec.describe Gitlab::Experiment::Rollout::Feature, :experiment, feature_catego end it "rolls out relatively evenly to 3 behaviors" do - subject_experiment.variant(:variant3) {} # rubocop:disable Lint/EmptyBlock + subject_experiment.variant(:variant3) {} # rubocop:disable Lint/EmptyBlock -- Specific for test 100.times { |i| run_cycle(subject_experiment, value: i) } @@ -115,7 +90,7 @@ RSpec.describe Gitlab::Experiment::Rollout::Feature, :experiment, feature_catego end it "rolls out with the expected distribution" do - subject_experiment.variant(:variant3) {} # rubocop:disable Lint/EmptyBlock + subject_experiment.variant(:variant3) {} # rubocop:disable Lint/EmptyBlock -- Specific for test 100.times { |i| run_cycle(subject_experiment, value: i) } @@ -152,14 +127,14 @@ RSpec.describe Gitlab::Experiment::Rollout::Feature, :experiment, feature_catego describe "#flipper_id" do it "returns the expected flipper id if the experiment doesn't provide one" do - subject.instance_variable_set(:@experiment, double(id: '__id__')) - expect(subject.flipper_id).to eq('Experiment;__id__') + experiment_instance.instance_variable_set(:@experiment, instance_double('Gitlab::Experiment', id: '__id__')) + expect(experiment_instance.flipper_id).to eq('Experiment;__id__') end it "lets the experiment provide a flipper id so it can override the default" do allow(subject_experiment).to receive(:flipper_id).and_return('_my_overridden_id_') - expect(subject.flipper_id).to eq('_my_overridden_id_') + expect(experiment_instance.flipper_id).to eq('_my_overridden_id_') end end end diff --git a/spec/models/award_emoji_spec.rb b/spec/models/award_emoji_spec.rb index a901453ba9f..87abd8a676d 100644 --- a/spec/models/award_emoji_spec.rb +++ b/spec/models/award_emoji_spec.rb @@ -319,17 +319,6 @@ RSpec.describe AwardEmoji, feature_category: :team_planning do expect(new_award.url).to eq(custom_emoji.url) end - describe 'when inside subgroup' do - let_it_be(:subgroup) { create(:group, parent: custom_emoji.group) } - let_it_be(:project) { create(:project, namespace: subgroup) } - - it 'is set for custom emoji' do - new_award = build_award(custom_emoji.name) - - expect(new_award.url).to eq(custom_emoji.url) - end - end - context 'feature flag disabled' do before do stub_feature_flags(custom_emoji: false) diff --git a/spec/models/custom_emoji_spec.rb b/spec/models/custom_emoji_spec.rb index cbdf05cf28f..15655d08556 100644 --- a/spec/models/custom_emoji_spec.rb +++ b/spec/models/custom_emoji_spec.rb @@ -48,45 +48,4 @@ RSpec.describe CustomEmoji do expect(emoji.errors.messages).to eq(file: ["is blocked: Only allowed schemes are http, https"]) end end - - describe '#for_resource' do - let_it_be(:group) { create(:group) } - let_it_be(:custom_emoji) { create(:custom_emoji, namespace: group) } - - context 'when custom_emoji feature flag is disabled' do - before do - stub_feature_flags(custom_emoji: false) - end - - it { expect(described_class.for_resource(group)).to eq([]) } - end - - context 'when group is nil' do - let_it_be(:group) { nil } - - it { expect(described_class.for_resource(group)).to eq([]) } - end - - context 'when resource is a project' do - let_it_be(:project) { create(:project) } - - it { expect(described_class.for_resource(project)).to eq([]) } - end - - it { expect(described_class.for_resource(group)).to eq([custom_emoji]) } - end - - describe '#for_namespaces' do - let_it_be(:group) { create(:group) } - let_it_be(:custom_emoji) { create(:custom_emoji, namespace: group, name: 'parrot') } - - it { expect(described_class.for_namespaces([group.id])).to eq([custom_emoji]) } - - context 'with subgroup' do - let_it_be(:subgroup) { create(:group, parent: group) } - let_it_be(:subgroup_emoji) { create(:custom_emoji, namespace: subgroup, name: 'parrot') } - - it { expect(described_class.for_namespaces([subgroup.id, group.id])).to eq([subgroup_emoji]) } - end - end end diff --git a/spec/requests/api/graphql/custom_emoji_query_spec.rb b/spec/requests/api/graphql/custom_emoji_query_spec.rb index c89ad0002b4..1858ea831dd 100644 --- a/spec/requests/api/graphql/custom_emoji_query_spec.rb +++ b/spec/requests/api/graphql/custom_emoji_query_spec.rb @@ -35,14 +35,14 @@ RSpec.describe 'getting custom emoji within namespace', feature_category: :share expect(graphql_data['group']['customEmoji']['nodes'].first['name']).to eq(custom_emoji.name) end - it 'returns empty array when the custom_emoji feature flag is disabled' do + it 'returns nil custom emoji when the custom_emoji feature flag is disabled' do stub_feature_flags(custom_emoji: false) post_graphql(custom_emoji_query(group), current_user: current_user) expect(response).to have_gitlab_http_status(:ok) expect(graphql_data['group']).to be_present - expect(graphql_data['group']['customEmoji']['nodes']).to eq([]) + expect(graphql_data['group']['customEmoji']).to be_nil end it 'returns nil group when unauthorised' do diff --git a/spec/serializers/discussion_entity_spec.rb b/spec/serializers/discussion_entity_spec.rb index 4b818ce35e6..0fe10ed2c6d 100644 --- a/spec/serializers/discussion_entity_spec.rb +++ b/spec/serializers/discussion_entity_spec.rb @@ -53,6 +53,13 @@ RSpec.describe DiscussionEntity do .to match_schema('entities/note_user_entity') end + it 'exposes the url for custom award emoji' do + custom_emoji = create(:custom_emoji, group: group) + create(:award_emoji, awardable: note, name: custom_emoji.name) + + expect(subject[:notes].last[:award_emoji].first.keys).to include(:url) + end + context 'when is LegacyDiffDiscussion' do let(:discussion) { create(:legacy_diff_note_on_merge_request, noteable: note.noteable, project: project).to_discussion } diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 4876acfce8a..e57e110926b 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -212,7 +212,6 @@ RSpec.configure do |config| config.include_context 'when rendered has no HTML escapes', type: :view include StubFeatureFlags - include StubSaasFeatures include StubSnowplow include StubMember diff --git a/spec/support/helpers/stub_saas_features.rb b/spec/support/helpers/stub_saas_features.rb deleted file mode 100644 index 82ad6e59cb4..00000000000 --- a/spec/support/helpers/stub_saas_features.rb +++ /dev/null @@ -1,23 +0,0 @@ -# frozen_string_literal: true - -module StubSaasFeatures - # Stub SaaS feature with `feature_name: true/false` - # - # @param [Hash] features where key is feature name and value is boolean whether enabled or not. - # - # Examples - # - `stub_saas_features(onboarding: false)` ... Disable `onboarding` - # SaaS feature globally. - # - `stub_saas_features(onboarding: true)` ... Enable `onboarding` - # SaaS feature globally. - def stub_saas_features(features) - all_features = ::Gitlab::Saas::FEATURES.index_with { false } - all_features.merge!(features) - - all_features.each do |feature_name, value| - raise ArgumentError, 'value must be boolean' unless value.in? [true, false] - - allow(::Gitlab::Saas).to receive(:feature_available?).with(feature_name).and_return(value) - end - end -end diff --git a/spec/support_specs/helpers/stub_saas_features_spec.rb b/spec/support_specs/helpers/stub_saas_features_spec.rb deleted file mode 100644 index db608c774c0..00000000000 --- a/spec/support_specs/helpers/stub_saas_features_spec.rb +++ /dev/null @@ -1,65 +0,0 @@ -# frozen_string_literal: true - -require 'spec_helper' - -RSpec.describe StubSaasFeatures, feature_category: :shared do - describe '#stub_saas_features' do - using RSpec::Parameterized::TableSyntax - - let(:feature_name) { :feature_one } - let(:all_features) { [feature_name, :feature_two, :feature_three] } - - before do - stub_const("Gitlab::Saas::FEATURES", all_features) - end - - context 'when checking global state' do - where(:feature_value) do - [true, false] - end - - with_them do - before do - stub_saas_features(feature_name => feature_value) - end - - it { expect(::Gitlab::Saas.feature_available?(feature_name)).to eq(feature_value) } - end - end - - context 'when value is not boolean' do - it 'raises an error' do - expect do - stub_saas_features(feature_name => '_not_boolean_') - end.to raise_error(ArgumentError, /value must be boolean/) - end - end - - it 'subsequent run changes state' do - # enable FF on all - stub_saas_features({ feature_name => true }) - expect(::Gitlab::Saas.feature_available?(feature_name)).to eq(true) - - # disable FF on all - stub_saas_features({ feature_name => false }) - expect(::Gitlab::Saas.feature_available?(feature_name)).to eq(false) - end - - it 'handles multiple features' do - stub_saas_features(feature_name => false, some_new_feature: true) - - expect(::Gitlab::Saas.feature_available?(feature_name)).to eq(false) - expect(::Gitlab::Saas.feature_available?(:some_new_feature)).to eq(true) - end - - context 'when checking multiple features' do - it 'stubs defaults false value for other known features' do - stub_saas_features(feature_one: true) - - expect(::Gitlab::Saas.feature_available?(:feature_one)).to eq(true) - expect(::Gitlab::Saas.feature_available?(:feature_two)).to eq(false) - expect(::Gitlab::Saas.feature_available?(:feature_three)).to eq(false) - end - end - end -end |