diff --git a/.rubocop_todo/layout/line_continuation_spacing.yml b/.rubocop_todo/layout/line_continuation_spacing.yml index 26ce7068a38..b63363c9a10 100644 --- a/.rubocop_todo/layout/line_continuation_spacing.yml +++ b/.rubocop_todo/layout/line_continuation_spacing.yml @@ -176,6 +176,5 @@ Layout/LineContinuationSpacing: - 'spec/support/shared_examples/requests/api/notes_shared_examples.rb' - 'spec/support/shared_examples/requests/api/resolvable_discussions_shared_examples.rb' - 'spec/uploaders/file_mover_spec.rb' - - 'spec/views/devise/shared/_signup_box.html.haml_spec.rb' - 'spec/views/projects/issues/show.html.haml_spec.rb' - 'spec/views/projects/pages/show.html.haml_spec.rb' diff --git a/.rubocop_todo/rspec/named_subject.yml b/.rubocop_todo/rspec/named_subject.yml index 660dc3a94f2..536591ff27f 100644 --- a/.rubocop_todo/rspec/named_subject.yml +++ b/.rubocop_todo/rspec/named_subject.yml @@ -1090,7 +1090,6 @@ RSpec/NamedSubject: - 'ee/spec/tasks/gitlab/spdx_rake_spec.rb' - 'ee/spec/validators/user_existence_validator_spec.rb' - 'ee/spec/validators/user_id_existence_validator_spec.rb' - - 'ee/spec/views/devise/registrations/new.html.haml_spec.rb' - 'ee/spec/workers/active_user_count_threshold_worker_spec.rb' - 'ee/spec/workers/admin_emails_worker_spec.rb' - 'ee/spec/workers/app_sec/dast/profile_schedule_worker_spec.rb' diff --git a/app/graphql/resolvers/concerns/resolves_merge_requests.rb b/app/graphql/resolvers/concerns/resolves_merge_requests.rb index d356e168572..7e21e3617f4 100644 --- a/app/graphql/resolvers/concerns/resolves_merge_requests.rb +++ b/app/graphql/resolvers/concerns/resolves_merge_requests.rb @@ -61,6 +61,7 @@ module ResolvesMergeRequests commit_count: [:metrics], diff_stats_summary: [:metrics], approved_by: [:approved_by_users], + merge_after: [:merge_schedule], milestone: [:milestone], security_auto_fix: [:author], head_pipeline: [:merge_request_diff, { head_pipeline: [:merge_request] }], diff --git a/app/graphql/types/merge_request_type.rb b/app/graphql/types/merge_request_type.rb index 8daef281b60..e261b55300f 100644 --- a/app/graphql/types/merge_request_type.rb +++ b/app/graphql/types/merge_request_type.rb @@ -100,6 +100,11 @@ module Types method: :public_merge_status, null: true, description: 'Merge status of the merge request.' + field :merge_after, ::Types::TimeType, + null: true, + description: 'Date after which the merge request can be merged.', + alpha: { milestone: '17.4' } + field :detailed_merge_status, ::Types::MergeRequests::DetailedMergeStatusEnum, null: true, calls_gitaly: true, description: 'Detailed merge status of the merge request.' @@ -347,6 +352,10 @@ module Types object.metrics&.merged_by || object.merge_user end + def merge_after + object.merge_schedule&.merge_after + end + def detailed_merge_status ::MergeRequests::Mergeability::DetailedMergeStatusService.new(merge_request: object).execute end diff --git a/app/models/ci/build_trace_chunks/fog.rb b/app/models/ci/build_trace_chunks/fog.rb index 1cae2279434..f7db76856ae 100644 --- a/app/models/ci/build_trace_chunks/fog.rb +++ b/app/models/ci/build_trace_chunks/fog.rb @@ -112,7 +112,9 @@ module Ci def connection return unless available? - @connection ||= ::Fog::Storage.new(object_store.connection.to_hash.deep_symbolize_keys) + ::Gitlab::SafeRequestStore.fetch(object_store_raw_config) do + ::Fog::Storage.new(object_store.connection.to_hash.deep_symbolize_keys) + end end def fog_directory diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb index 93ec0430866..699af3ec43c 100644 --- a/app/models/merge_request.rb +++ b/app/models/merge_request.rb @@ -66,6 +66,8 @@ class MergeRequest < ApplicationRecord has_one :predictions, inverse_of: :merge_request delegate :suggested_reviewers, to: :predictions + has_one :merge_schedule, class_name: 'MergeRequests::MergeSchedule', inverse_of: :merge_request + belongs_to :latest_merge_request_diff, class_name: 'MergeRequestDiff' manual_inverse_association :latest_merge_request_diff, :merge_request @@ -378,6 +380,7 @@ class MergeRequest < ApplicationRecord preload_routables.preload( :assignees, :author, :unresolved_notes, :labels, :milestone, :timelogs, :latest_merge_request_diff, :reviewers, + :merge_schedule, target_project: :project_feature, metrics: [:latest_closed_by, :merged_by] ) diff --git a/app/models/merge_requests/merge_schedule.rb b/app/models/merge_requests/merge_schedule.rb new file mode 100644 index 00000000000..5749768b5a1 --- /dev/null +++ b/app/models/merge_requests/merge_schedule.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +module MergeRequests + class MergeSchedule < ApplicationRecord + self.table_name = 'merge_request_merge_schedules' + + belongs_to :merge_request, optional: false, inverse_of: :merge_schedule + + before_validation :set_sharding_key + + def set_sharding_key + self.project_id = merge_request&.target_project&.id + end + end +end diff --git a/app/models/onboarding/progress.rb b/app/models/onboarding/progress.rb index 9c3dc8f4bf0..2f02e9b0b55 100644 --- a/app/models/onboarding/progress.rb +++ b/app/models/onboarding/progress.rb @@ -4,24 +4,6 @@ module Onboarding class Progress < ApplicationRecord self.table_name = 'onboarding_progresses' - include IgnorableColumns - - ignore_columns %i[ - git_pull_at - subscription_created_at - scoped_label_created_at - security_scan_enabled_at - issue_auto_closed_at - repository_imported_at - repository_mirrored_at - secure_container_scanning_run_at - secure_secret_detection_run_at - secure_coverage_fuzzing_run_at - secure_cluster_image_scanning_run_at - secure_api_fuzzing_run_at - ], - remove_with: '17.5', remove_after: '2024-09-14' - belongs_to :namespace, optional: false validate :namespace_is_root_namespace diff --git a/app/views/devise/shared/_signup_box_form.html.haml b/app/views/devise/registrations/_signup_box_form.html.haml similarity index 68% rename from app/views/devise/shared/_signup_box_form.html.haml rename to app/views/devise/registrations/_signup_box_form.html.haml index 8dc812d8813..9d3b98e3023 100644 --- a/app/views/devise/shared/_signup_box_form.html.haml +++ b/app/views/devise/registrations/_signup_box_form.html.haml @@ -2,7 +2,9 @@ - form_resource_name = "new_#{resource_name}" - button_class = local_assigns[:button_class] -= gitlab_ui_form_for(resource, as: form_resource_name, url: url, html: { class: 'gl-show-field-errors js-arkose-labs-form', aria: { live: 'assertive' }}, data: { testid: 'signup-form' }) do |f| += gitlab_ui_form_for(resource, as: form_resource_name, url: url, + html: { class: 'gl-show-field-errors js-arkose-labs-form', aria: { live: 'assertive' }}, + data: { testid: 'signup-form' }) do |f| .devise-errors = render 'devise/shared/error_messages', resource: resource - if Gitlab::CurrentSettings.invisible_captcha_enabled @@ -21,8 +23,9 @@ = f.text_field :first_name, class: 'form-control gl-form-input top js-block-emoji js-validate-length js-track-error', data: { max_length: max_first_name_length, - max_length_message: s_('SignUp|First name is too long (maximum is %{max_length} characters).') % { max_length: max_first_name_length }, - track_action_for_errors: tracking_label, + max_length_message: safe_format(s_('SignUp|First name is too long (maximum is %{max_length} characters).'), + max_length: max_first_name_length), + track_action_for_errors: preregistration_tracking_label, testid: 'new-user-first-name-field' }, required: true, title: _('This field is required.') @@ -31,8 +34,9 @@ = f.text_field :last_name, class: 'form-control gl-form-input top js-block-emoji js-validate-length js-track-error', data: { max_length: max_last_name_length, - max_length_message: s_('SignUp|Last name is too long (maximum is %{max_length} characters).') % { max_length: max_last_name_length }, - track_action_for_errors: tracking_label, + max_length_message: safe_format(s_('SignUp|Last name is too long (maximum is %{max_length} characters).'), + max_length: max_last_name_length), + track_action_for_errors: preregistration_tracking_label, testid: 'new-user-last-name-field' }, required: true, title: _('This field is required.') @@ -40,36 +44,45 @@ = f.label :username, _('Username') = f.text_field :username, class: 'form-control gl-form-input middle js-block-emoji js-validate-length js-validate-username js-track-error', - data: signup_username_data_attributes.merge(track_action_for_errors: tracking_label), + data: signup_username_data_attributes.merge(track_action_for_errors: preregistration_tracking_label), pattern: Gitlab::PathRegex::NAMESPACE_FORMAT_REGEX_JS, required: true, title: _('Please create a username with only alphanumeric characters.') - %p.validation-error.gl-text-red-500.gl-field-error-ignore.gl-mt-2.field-validation.hide= _('Username is already taken.') - %p.validation-success.gl-text-green-600.gl-field-error-ignore.gl-mt-2.field-validation.hide= _('Username is available.') - %p.validation-pending.gl-field-error-ignore.gl-mt-2.field-validation.hide= _('Checking username availability...') + %p.validation-error.gl-text-red-500.gl-field-error-ignore.gl-mt-2.field-validation.hide + = _('Username is already taken.') + %p.validation-success.gl-text-green-600.gl-field-error-ignore.gl-mt-2.field-validation.hide + = _('Username is available.') + %p.validation-pending.gl-field-error-ignore.gl-mt-2.field-validation.hide + = _('Checking username availability...') - unless @invite_email.present? .form-group = f.label :email, _('Email') = f.email_field :email, class: 'form-control gl-form-input middle js-validate-email js-track-error', - data: { testid: 'new-user-email-field', track_action_for_errors: tracking_label }, + data: { testid: 'new-user-email-field', track_action_for_errors: preregistration_tracking_label }, required: true, title: _('Please provide a valid email address.') - %p.validation-hint.gl-field-hint.text-secondary= _('We recommend a work email address.') - %p.validation-warning.gl-field-error-ignore.text-secondary.hide= _('This email address does not look right, are you sure you typed it correctly?') + %p.validation-hint.gl-field-hint.text-secondary + = _('We recommend a work email address.') + %p.validation-warning.gl-field-error-ignore.text-secondary.hide + = _('This email address does not look right, are you sure you typed it correctly?') -# This is used for providing entry to Jihu on email verification = render_if_exists 'devise/shared/signup_email_additional_info' .form-group.gl-mb-5 = f.label :password, _('Password') %input.form-control.gl-form-input.js-password{ data: { id: "#{form_resource_name}_password", - title: s_('SignUp|Minimum length is %{minimum_password_length} characters.') % { minimum_password_length: @minimum_password_length }, + title: safe_format(s_('SignUp|Minimum length is %{minimum_password_length} characters.'), + minimum_password_length: @minimum_password_length), minimum_password_length: @minimum_password_length, testid: 'new-user-password-field', - track_action_for_errors: tracking_label, + track_action_for_errors: preregistration_tracking_label, autocomplete: 'new-password', name: "#{form_resource_name}[password]" } } - %p.gl-field-hint-valid.text-secondary= s_('SignUp|Minimum length is %{minimum_password_length} characters.') % { minimum_password_length: @minimum_password_length } + %p.gl-field-hint-valid.text-secondary + = safe_format(s_('SignUp|Minimum length is %{minimum_password_length} characters.'), + minimum_password_length: @minimum_password_length) = render_if_exists 'shared/password_requirements_list', show_basic_requirements: true + -# This is used for providing entry to Jihu phone verification = render_if_exists 'devise/shared/phone_verification', form: f .form-group @@ -78,11 +91,9 @@ - elsif show_recaptcha_sign_up? = recaptcha_tags nonce: content_security_policy_nonce - = render Pajamas::ButtonComponent.new(type: :submit, variant: :confirm, block: true, - button_options: { class: button_class, - data: { testid: 'new-user-register-button', track_action: 'register', track_label: tracking_label }}) do - = button_text - = render 'devise/shared/terms_of_service_notice', button_text: button_text -= yield :omniauth_providers_bottom if show_omniauth_providers + = render Pajamas::ButtonComponent.new(type: :submit, variant: :confirm, block: true, + button_options: { class: "#{button_class} gl-mt-4", + data: { testid: 'new-user-register-button', track_action: 'register', track_label: preregistration_tracking_label }}) do + = button_text diff --git a/app/views/devise/registrations/new.html.haml b/app/views/devise/registrations/new.html.haml index 88270cd58b1..8583840c35c 100644 --- a/app/views/devise/registrations/new.html.haml +++ b/app/views/devise/registrations/new.html.haml @@ -7,16 +7,23 @@ = render "layouts/bizible" = render_if_exists "layouts/google_tag_manager_body" -- content_for :omniauth_providers_bottom do - = render 'devise/shared/signup_omniauth_providers', - tracking_label: preregistration_tracking_label - .signup-page - = render 'devise/shared/signup_box', - url: registration_path(resource_name, registration_path_params), - button_text: _('Register'), - show_omniauth_providers: omniauth_enabled? && button_based_providers_enabled?, - tracking_label: preregistration_tracking_label + .gl-mb-3.gl-p-4 + = render 'signup_box_form', + url: registration_path(resource_name, registration_path_params), + button_text: _('Continue') + + - if omniauth_enabled? && button_based_providers_enabled? + .gl-text-center.gl-pt-5 + %label.gl-font-normal + = _('Continue with:') + .gl-flex.gl-flex-col.gl-gap-3 + - enabled_button_based_providers.each do |provider| + = render 'devise/shared/omniauth_provider_button', + href: omniauth_authorize_path(:user, provider, register_omniauth_params({})), + provider: provider, + classes: 'js-track-omni-auth', + data: { provider: provider, track_action: "#{provider}_sso", track_label: preregistration_tracking_label } = render 'devise/shared/sign_in_link', tracking_action: 'click_sign_in_from_registration_page', diff --git a/app/views/devise/shared/_signup_box.html.haml b/app/views/devise/shared/_signup_box.html.haml deleted file mode 100644 index b875d245fec..00000000000 --- a/app/views/devise/shared/_signup_box.html.haml +++ /dev/null @@ -1,8 +0,0 @@ -.gl-mb-3.gl-p-4 - = yield :omniauth_providers_top if show_omniauth_providers - - = render 'devise/shared/signup_box_form', - button_text: button_text, - url: url, - show_omniauth_providers: omniauth_enabled? && button_based_providers_enabled?, - tracking_label: tracking_label diff --git a/app/views/devise/shared/_signup_omniauth_provider_button.haml b/app/views/devise/shared/_signup_omniauth_provider_button.haml deleted file mode 100644 index 0c08940f4a8..00000000000 --- a/app/views/devise/shared/_signup_omniauth_provider_button.haml +++ /dev/null @@ -1,5 +0,0 @@ -= render 'devise/shared/omniauth_provider_button', - href: href, - provider: provider, - classes: 'js-track-omni-auth', - data: { provider: provider, track_action: "#{provider}_sso", track_label: tracking_label } diff --git a/app/views/devise/shared/_signup_omniauth_provider_list.haml b/app/views/devise/shared/_signup_omniauth_provider_list.haml deleted file mode 100644 index 03b9817fec6..00000000000 --- a/app/views/devise/shared/_signup_omniauth_provider_list.haml +++ /dev/null @@ -1,9 +0,0 @@ -.gl-text-center.gl-pt-5 - %label.gl-font-normal - = _("Register with:") - .gl-flex.gl-flex-col.gl-gap-3 - - providers.each do |provider| - = render 'devise/shared/signup_omniauth_provider_button', - href: omniauth_authorize_path(:user, provider, register_omniauth_params(local_assigns)), - provider: provider, - tracking_label: tracking_label diff --git a/app/views/devise/shared/_signup_omniauth_providers.haml b/app/views/devise/shared/_signup_omniauth_providers.haml deleted file mode 100644 index 37790cec808..00000000000 --- a/app/views/devise/shared/_signup_omniauth_providers.haml +++ /dev/null @@ -1,3 +0,0 @@ -= render 'devise/shared/signup_omniauth_provider_list', - providers: enabled_button_based_providers, - tracking_label: local_assigns[:tracking_label] diff --git a/config/gitleaks.toml b/config/gitleaks.toml index 7b2065a025a..bd1bf82e214 100644 --- a/config/gitleaks.toml +++ b/config/gitleaks.toml @@ -10,6 +10,8 @@ path = "/gitleaks.toml" regexes = [ # spotted in doc/user/application_security/secret_detection/index.md and some frontend specs "glpat-1234567890abcdefghij", + # doc/user/application_security/secret_detection/remove_secrets_tutorial.md + "glpat-12345678901234567890", # spec/frontend/lib/utils/secret_detection_spec.js "glpat-cgyKc1k_AsnEpmP-5fRL", "gldt-cgyKc1k_AsnEpmP-5fRL", diff --git a/db/docs/merge_request_merge_schedules.yml b/db/docs/merge_request_merge_schedules.yml new file mode 100644 index 00000000000..7923501b06b --- /dev/null +++ b/db/docs/merge_request_merge_schedules.yml @@ -0,0 +1,12 @@ +--- +table_name: merge_request_merge_schedules +classes: +- MergeRequests::MergeSchedule +feature_categories: +- code_review_workflow +description: Stores timestamps for scheduled merges +introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/165092 +milestone: '17.4' +gitlab_schema: gitlab_main_cell +sharding_key: + project_id: projects diff --git a/db/migrate/20240911181854_create_merge_request_merge_schedules.rb b/db/migrate/20240911181854_create_merge_request_merge_schedules.rb new file mode 100644 index 00000000000..2c694ceead3 --- /dev/null +++ b/db/migrate/20240911181854_create_merge_request_merge_schedules.rb @@ -0,0 +1,17 @@ +# frozen_string_literal: true + +class CreateMergeRequestMergeSchedules < Gitlab::Database::Migration[2.2] + milestone '17.4' + + def change + create_table :merge_request_merge_schedules do |t| # rubocop:disable Migration/EnsureFactoryForTable -- factory exists in spec/factories/merge_request_merge_schedule.rb + t.references :merge_request, foreign_key: { on_delete: :cascade }, index: false, null: false + t.datetime_with_timezone :merge_after + + t.bigint :project_id, null: false + + t.index :merge_request_id, unique: true + t.index :project_id + end + end +end diff --git a/db/migrate/20240911181855_create_merge_request_merge_schedules_sharding_key_fk.rb b/db/migrate/20240911181855_create_merge_request_merge_schedules_sharding_key_fk.rb new file mode 100644 index 00000000000..143248ee3c2 --- /dev/null +++ b/db/migrate/20240911181855_create_merge_request_merge_schedules_sharding_key_fk.rb @@ -0,0 +1,17 @@ +# frozen_string_literal: true + +class CreateMergeRequestMergeSchedulesShardingKeyFk < Gitlab::Database::Migration[2.2] + milestone '17.4' + + disable_ddl_transaction! + + def up + add_concurrent_foreign_key :merge_request_merge_schedules, :projects, column: :project_id, on_delete: :cascade + end + + def down + with_lock_retries do + remove_foreign_key :merge_request_merge_schedules, column: :project_id + end + end +end diff --git a/db/schema_migrations/20240911181854 b/db/schema_migrations/20240911181854 new file mode 100644 index 00000000000..75e2bbe9721 --- /dev/null +++ b/db/schema_migrations/20240911181854 @@ -0,0 +1 @@ +6dd9484b5c7d61943dae2ed684c8d84d0c0319e009c26d1bcbfcbe0ae69939e7 \ No newline at end of file diff --git a/db/schema_migrations/20240911181855 b/db/schema_migrations/20240911181855 new file mode 100644 index 00000000000..b3cf9c49b4c --- /dev/null +++ b/db/schema_migrations/20240911181855 @@ -0,0 +1 @@ +4e56708b920c40ba9c6050f2441f313715e3db134ad6114fb9091829506736c1 \ No newline at end of file diff --git a/db/structure.sql b/db/structure.sql index f4a64ef0386..bdbe2ba67b1 100644 --- a/db/structure.sql +++ b/db/structure.sql @@ -13199,6 +13199,22 @@ CREATE SEQUENCE merge_request_diffs_id_seq ALTER SEQUENCE merge_request_diffs_id_seq OWNED BY merge_request_diffs.id; +CREATE TABLE merge_request_merge_schedules ( + id bigint NOT NULL, + merge_request_id bigint NOT NULL, + merge_after timestamp with time zone, + project_id bigint NOT NULL +); + +CREATE SEQUENCE merge_request_merge_schedules_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + +ALTER SEQUENCE merge_request_merge_schedules_id_seq OWNED BY merge_request_merge_schedules.id; + CREATE TABLE merge_request_metrics ( merge_request_id bigint NOT NULL, latest_build_started_at timestamp without time zone, @@ -21922,6 +21938,8 @@ ALTER TABLE ONLY merge_request_diff_details ALTER COLUMN merge_request_diff_id S ALTER TABLE ONLY merge_request_diffs ALTER COLUMN id SET DEFAULT nextval('merge_request_diffs_id_seq'::regclass); +ALTER TABLE ONLY merge_request_merge_schedules ALTER COLUMN id SET DEFAULT nextval('merge_request_merge_schedules_id_seq'::regclass); + ALTER TABLE ONLY merge_request_metrics ALTER COLUMN id SET DEFAULT nextval('merge_request_metrics_id_seq'::regclass); ALTER TABLE ONLY merge_request_predictions ALTER COLUMN merge_request_id SET DEFAULT nextval('merge_request_predictions_merge_request_id_seq'::regclass); @@ -24263,6 +24281,9 @@ ALTER TABLE ONLY merge_request_diff_files ALTER TABLE ONLY merge_request_diffs ADD CONSTRAINT merge_request_diffs_pkey PRIMARY KEY (id); +ALTER TABLE ONLY merge_request_merge_schedules + ADD CONSTRAINT merge_request_merge_schedules_pkey PRIMARY KEY (id); + ALTER TABLE ONLY merge_request_metrics ADD CONSTRAINT merge_request_metrics_pkey PRIMARY KEY (id); @@ -28945,6 +28966,10 @@ CREATE INDEX index_merge_request_diffs_on_project_id ON merge_request_diffs USIN CREATE UNIQUE INDEX index_merge_request_diffs_on_unique_merge_request_id ON merge_request_diffs USING btree (merge_request_id) WHERE (diff_type = 2); +CREATE UNIQUE INDEX index_merge_request_merge_schedules_on_merge_request_id ON merge_request_merge_schedules USING btree (merge_request_id); + +CREATE INDEX index_merge_request_merge_schedules_on_project_id ON merge_request_merge_schedules USING btree (project_id); + CREATE INDEX index_merge_request_metrics_on_first_deployed_to_production_at ON merge_request_metrics USING btree (first_deployed_to_production_at); CREATE INDEX index_merge_request_metrics_on_latest_closed_at ON merge_request_metrics USING btree (latest_closed_at) WHERE (latest_closed_at IS NOT NULL); @@ -34063,6 +34088,9 @@ ALTER TABLE ONLY operations_strategies ALTER TABLE ONLY lfs_objects_projects ADD CONSTRAINT fk_a56e02279c FOREIGN KEY (lfs_object_id) REFERENCES lfs_objects(id) ON DELETE RESTRICT NOT VALID; +ALTER TABLE ONLY merge_request_merge_schedules + ADD CONSTRAINT fk_a5ff9339a9 FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE; + ALTER TABLE ONLY merge_requests ADD CONSTRAINT fk_a6963e8447 FOREIGN KEY (target_project_id) REFERENCES projects(id) ON DELETE CASCADE; @@ -35242,6 +35270,9 @@ ALTER TABLE zoekt_tasks ALTER TABLE ONLY ml_models ADD CONSTRAINT fk_rails_51e87f7c50 FOREIGN KEY (project_id) REFERENCES projects(id); +ALTER TABLE ONLY merge_request_merge_schedules + ADD CONSTRAINT fk_rails_5294434bc3 FOREIGN KEY (merge_request_id) REFERENCES merge_requests(id) ON DELETE CASCADE; + ALTER TABLE ONLY elastic_group_index_statuses ADD CONSTRAINT fk_rails_52b9969b12 FOREIGN KEY (namespace_id) REFERENCES namespaces(id) ON DELETE CASCADE; diff --git a/doc/api/graphql/reference/index.md b/doc/api/graphql/reference/index.md index a05d25e4da4..e131f7bd125 100644 --- a/doc/api/graphql/reference/index.md +++ b/doc/api/graphql/reference/index.md @@ -25665,6 +25665,7 @@ Defines which user roles, users, or groups can merge into a protected branch. | `iid` | [`String!`](#string) | Internal ID of the merge request. | | `inProgressMergeCommitSha` | [`String`](#string) | Commit SHA of the merge request if merge is in progress. | | `labels` | [`LabelConnection`](#labelconnection) | Labels of the merge request. (see [Connections](#connections)) | +| `mergeAfter` **{warning-solid}** | [`Time`](#time) | **Introduced** in GitLab 17.4. **Status**: Experiment. Date after which the merge request can be merged. | | `mergeCommitSha` | [`String`](#string) | SHA of the merge request commit (set once merged). | | `mergeError` | [`String`](#string) | Error message due to a merge error. | | `mergeOngoing` | [`Boolean!`](#boolean) | Indicates if a merge is currently occurring. | diff --git a/doc/api/merge_requests.md b/doc/api/merge_requests.md index 22404f2332d..b61888e6e28 100644 --- a/doc/api/merge_requests.md +++ b/doc/api/merge_requests.md @@ -17,6 +17,7 @@ DETAILS: > - `with_merge_status_recheck` [changed](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/115948) in GitLab 15.11 [with a flag](../administration/feature_flags.md) named `restrict_merge_status_recheck` to be ignored for requests from users insufficient permissions. Disabled by default. > - `approvals_before_merge` [deprecated](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/119503) in GitLab 16.0. > - `prepared_at` [introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/122001) in GitLab 16.1. +> - `merge_after` [introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/165092) in GitLab 17.4. All API calls to non-public information require authentication. @@ -118,6 +119,7 @@ Example response: "web_url": "https://gitlab.com/DouweM" }, "merged_at": "2018-09-07T11:16:17.520Z", + "merge_after": "2018-09-07T11:16:00.000Z", "prepared_at": "2018-09-04T11:16:17.520Z", "closed_by": null, "closed_at": null, @@ -367,6 +369,7 @@ Example response: "web_url": "https://gitlab.com/DouweM" }, "merged_at": "2018-09-07T11:16:17.520Z", + "merge_after": "2018-09-07T11:16:00.000Z", "prepared_at": "2018-09-04T11:16:17.520Z", "closed_by": null, "closed_at": null, @@ -553,6 +556,7 @@ Example response: "web_url": "https://gitlab.com/DouweM" }, "merged_at": "2018-09-07T11:16:17.520Z", + "merge_after": "2018-09-07T11:16:00.000Z", "prepared_at": "2018-09-04T11:16:17.520Z", "closed_by": null, "closed_at": null, @@ -747,6 +751,7 @@ Example response: "merged_by": null, // Deprecated and will be removed in API v5. Use `merge_user` instead. "merge_user": null, "merged_at": null, + "merge_after": "2018-09-07T11:16:00.000Z", "prepared_at": "2018-09-04T11:16:17.520Z", "closed_by": null, "closed_at": null, @@ -1134,6 +1139,7 @@ Example response: "merged_by": null, "merge_user": null, "merged_at": null, + "merge_after": "2018-09-07T11:16:00.000Z", "closed_by": null, "closed_at": null, "target_branch": "master", @@ -1243,6 +1249,7 @@ Example response: "merged_by": null, "merge_user": null, "merged_at": null, + "merge_after": "2018-09-07T11:16:00.000Z", "closed_by": null, "closed_at": null, "target_branch": "master", @@ -1787,6 +1794,7 @@ Example response: "web_url": "https://gitlab.com/DouweM" }, "merged_at": "2018-09-07T11:16:17.520Z", + "merge_after": "2018-09-07T11:16:00.000Z", "prepared_at": "2018-09-04T11:16:17.520Z", "closed_by": null, "closed_at": null, @@ -1960,6 +1968,7 @@ Example response: "web_url": "https://gitlab.com/DouweM" }, "merged_at": "2018-09-07T11:16:17.520Z", + "merge_after": "2018-09-07T11:16:00.000Z", "prepared_at": "2018-09-04T11:16:17.520Z", "closed_by": null, "closed_at": null, @@ -2153,6 +2162,7 @@ Example response: "web_url": "https://gitlab.com/DouweM" }, "merged_at": "2018-09-07T11:16:17.520Z", + "merge_after": "2018-09-07T11:16:00.000Z", "prepared_at": "2018-09-04T11:16:17.520Z", "closed_by": null, "closed_at": null, @@ -2354,6 +2364,7 @@ Example response: "web_url": "https://gitlab.com/DouweM" }, "merged_at": "2018-09-07T11:16:17.520Z", + "merge_after": "2018-09-07T11:16:00.000Z", "prepared_at": "2018-09-04T11:16:17.520Z", "closed_by": null, "closed_at": null, @@ -2826,6 +2837,7 @@ Example response: "web_url": "https://gitlab.com/DouweM" }, "merged_at": "2018-09-07T11:16:17.520Z", + "merge_after": "2018-09-07T11:16:00.000Z", "prepared_at": "2018-09-04T11:16:17.520Z", "closed_by": null, "closed_at": null, @@ -2990,6 +3002,7 @@ Example response: "web_url": "https://gitlab.com/DouweM" }, "merged_at": "2018-09-07T11:16:17.520Z", + "merge_after": "2018-09-07T11:16:00.000Z", "prepared_at": "2018-09-04T11:16:17.520Z", "closed_by": null, "closed_at": null, diff --git a/doc/development/documentation/workflow.md b/doc/development/documentation/workflow.md index a91fcf61334..9e8c9d4c072 100644 --- a/doc/development/documentation/workflow.md +++ b/doc/development/documentation/workflow.md @@ -33,6 +33,27 @@ to make the shorter pipeline run, you must follow these guidelines when naming y | Starting with `docs-` | `docs-update-api-issues` | | Ending in `-docs` | `123-update-api-issues-docs` | +### Moving content + +When you move content to a new location, and edit the content in the same merge request, +use separate commits. + +Separate commits help the reviewer, because the MR diff for moved content +does not clearly highlight edits. +When you use separate commits, the reviewer can verify the location change +in the first commit diff, then the content changes in subsequent commits. + +For example, if you move a page, but also update the content of the page: + +1. In the first commit: Move the content to its new location and put [redirects](redirects.md) in place if required. + If you can, fix broken links in this commit. +1. In subsequent commits: Make content changes. Fix broken links if you haven't already. +1. In the merge request: Explain the commits in the MR description and in a + comment to the reviewer. + +You can add as many commits as you want, but make sure the first commit only moves the content, +and does not edit it. + ## Documentation labels When you author an issue or merge request, choose the diff --git a/doc/install/docker/backup_restore.md b/doc/install/docker/backup_restore.md index c89d5cfb12d..aefe071cf5b 100644 --- a/doc/install/docker/backup_restore.md +++ b/doc/install/docker/backup_restore.md @@ -32,7 +32,7 @@ GitLab from backup. The secrets file is stored at `/etc/gitlab/gitlab-secrets.js ## Create a database backup -A database backup is required to roll back a GitLab upgrade if you encounter issues. +Before upgrading GitLab, you should create a database-only backup. If you encounter issues during the GitLab upgrade, you can restore the database backup to roll back the upgrade. To create a database backup, run this command: ```shell docker exec -t gitlab-backup create SKIP=artifacts,repositories,registry,uploads,builds,pages,lfs,packages,terraform_state @@ -40,3 +40,5 @@ docker exec -t gitlab-backup create SKIP=artifacts,repositories The backup is written to `/var/opt/gitlab/backups` which should be on a [volume mounted by Docker](installation.md#set-up-the-volumes-location). + +For more information on using the backup to roll back an upgrade, see [Downgrade GitLab](upgrade.md#downgrade-gitlab). diff --git a/lib/api/entities/merge_request_basic.rb b/lib/api/entities/merge_request_basic.rb index fce85ff1c49..4348d292eb4 100644 --- a/lib/api/entities/merge_request_basic.rb +++ b/lib/api/entities/merge_request_basic.rb @@ -64,6 +64,11 @@ module API merge_request.public_merge_status end expose :detailed_merge_status + + expose :merge_after do |merge_request, _options| + merge_request.merge_schedule&.merge_after + end + expose :diff_head_sha, as: :sha expose :merge_commit_sha expose :squash_commit_sha diff --git a/lib/gitlab/import_export/base/relation_factory.rb b/lib/gitlab/import_export/base/relation_factory.rb index a48d2823074..952aef56255 100644 --- a/lib/gitlab/import_export/base/relation_factory.rb +++ b/lib/gitlab/import_export/base/relation_factory.rb @@ -9,7 +9,7 @@ module Gitlab IMPORTED_OBJECT_MAX_RETRIES = 5 - OVERRIDES = { user_contributions: :user }.freeze + OVERRIDES = { user_contributions: :user, merge_schedule: 'MergeRequests::MergeSchedule' }.freeze EXISTING_OBJECT_RELATIONS = %i[].freeze # This represents all relations that have unique key on `project_id` or `group_id` diff --git a/lib/gitlab/usage_data_counters/issue_activity_unique_counter.rb b/lib/gitlab/usage_data_counters/issue_activity_unique_counter.rb index 39d9560c0fb..8e4cb66d850 100644 --- a/lib/gitlab/usage_data_counters/issue_activity_unique_counter.rb +++ b/lib/gitlab/usage_data_counters/issue_activity_unique_counter.rb @@ -3,10 +3,6 @@ module Gitlab module UsageDataCounters module IssueActivityUniqueCounter - ISSUE_CATEGORY = 'issues_edit' - ISSUE_ACTION = 'perform_issue_action' - ISSUE_LABEL = 'redis_hll_counters.issues_edit.issues_edit_total_unique_counts_monthly' - ISSUE_ASSIGNEE_CHANGED = 'g_project_management_issue_assignee_changed' ISSUE_CREATED = 'g_project_management_issue_created' ISSUE_CLOSED = 'g_project_management_issue_closed' diff --git a/locale/gitlab.pot b/locale/gitlab.pot index a8a4e7c7f5c..65f51af98e2 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -15277,6 +15277,9 @@ msgstr "" msgid "Continue with overages" msgstr "" +msgid "Continue with:" +msgstr "" + msgid "Continue…" msgstr "" @@ -37966,9 +37969,6 @@ msgstr "" msgid "Options" msgstr "" -msgid "Or create your own GitLab account" -msgstr "" - msgid "Ordered list" msgstr "" @@ -44870,9 +44870,6 @@ msgstr "" msgid "Register with two-factor app" msgstr "" -msgid "Register with:" -msgstr "" - msgid "RegistrationFeatures|Enable Service Ping and register for this feature." msgstr "" @@ -51467,9 +51464,6 @@ msgstr "" msgid "Sign up" msgstr "" -msgid "Sign up for your free trial with:" -msgstr "" - msgid "Sign-in count:" msgstr "" diff --git a/qa/qa/page/registration/sign_up.rb b/qa/qa/page/registration/sign_up.rb index 7d501bafe3b..46d2c1b8398 100644 --- a/qa/qa/page/registration/sign_up.rb +++ b/qa/qa/page/registration/sign_up.rb @@ -4,7 +4,7 @@ module QA module Page module Registration class SignUp < Page::Base - view 'app/views/devise/shared/_signup_box_form.html.haml' do + view 'app/views/devise/registrations/_signup_box_form.html.haml' do element 'new-user-first-name-field' element 'new-user-last-name-field' element 'new-user-email-field' diff --git a/spec/factories/merge_request_merge_schedule.rb b/spec/factories/merge_request_merge_schedule.rb new file mode 100644 index 00000000000..2be331efe15 --- /dev/null +++ b/spec/factories/merge_request_merge_schedule.rb @@ -0,0 +1,7 @@ +# frozen_string_literal: true + +FactoryBot.define do + factory :merge_request_merge_schedule, class: 'MergeRequests::MergeSchedule' do + merge_request + end +end diff --git a/spec/features/invites_spec.rb b/spec/features/invites_spec.rb index 63694d1254a..11cce78b06a 100644 --- a/spec/features/invites_spec.rb +++ b/spec/features/invites_spec.rb @@ -228,7 +228,7 @@ RSpec.describe 'Group or Project invitations', :with_current_organization, :aggr expect(page).to have_current_path(new_user_registration_path, ignore_query: true) - fill_in_sign_up_form(new_user, 'Register', invite: true) + fill_in_sign_up_form(new_user, invite: true) expect(page).to have_current_path(group_path(group)) expect(page) diff --git a/spec/features/users/signup_spec.rb b/spec/features/users/signup_spec.rb index 4abe90dc463..a363c1b50ac 100644 --- a/spec/features/users/signup_spec.rb +++ b/spec/features/users/signup_spec.rb @@ -47,7 +47,7 @@ RSpec.describe 'Signup', :with_current_organization, :js, feature_category: :use let(:terms_text) do <<~TEXT.squish - By clicking Register or registering through a third party you accept the + By clicking Continue or registering through a third party you accept the Terms of Use and acknowledge the Privacy Statement and Cookie Policy TEXT end @@ -130,7 +130,7 @@ RSpec.describe 'Signup', :with_current_organization, :js, feature_category: :use fill_in 'new_user_username', with: 'new$user!username' wait_for_requests - click_button "Register" + click_button _('Continue') expect(page).to have_content("Please create a username with only alphanumeric characters.") end diff --git a/spec/graphql/types/merge_request_type_spec.rb b/spec/graphql/types/merge_request_type_spec.rb index ebd3cd5f4fc..773565955f7 100644 --- a/spec/graphql/types/merge_request_type_spec.rb +++ b/spec/graphql/types/merge_request_type_spec.rb @@ -37,7 +37,7 @@ RSpec.describe GitlabSchema.types['MergeRequest'], feature_category: :code_revie squash_on_merge available_auto_merge_strategies has_ci mergeable commits committers commits_without_merge_commits squash security_auto_fix default_squash_commit_message auto_merge_strategy merge_user award_emoji prepared_at codequality_reports_comparer supports_lock_on_merge - mergeability_checks + mergeability_checks merge_after allows_multiple_assignees allows_multiple_reviewers retargeted name ] diff --git a/spec/lib/api/entities/merge_request_basic_spec.rb b/spec/lib/api/entities/merge_request_basic_spec.rb index 09f064c4cdd..d0a056547f7 100644 --- a/spec/lib/api/entities/merge_request_basic_spec.rb +++ b/spec/lib/api/entities/merge_request_basic_spec.rb @@ -20,7 +20,7 @@ RSpec.describe ::API::Entities::MergeRequestBasic, feature_category: :code_revie expected_fields = %i[ merged_by merge_user merged_at closed_by closed_at target_branch user_notes_count upvotes downvotes author assignees assignee reviewers source_project_id target_project_id labels draft work_in_progress - milestone merge_when_pipeline_succeeds merge_status detailed_merge_status sha merge_commit_sha + milestone merge_when_pipeline_succeeds merge_status detailed_merge_status merge_after sha merge_commit_sha squash_commit_sha discussion_locked should_remove_source_branch force_remove_source_branch prepared_at reference references web_url time_stats squash task_completion_status has_conflicts blocking_discussions_resolved imported imported_from diff --git a/spec/lib/gitlab/import_export/all_models.yml b/spec/lib/gitlab/import_export/all_models.yml index 54ce7055be4..f567e15ca59 100644 --- a/spec/lib/gitlab/import_export/all_models.yml +++ b/spec/lib/gitlab/import_export/all_models.yml @@ -227,6 +227,7 @@ merge_requests: - merge_request_context_commits - merge_request_context_commit_diff_files - merge_request_stage_events +- merge_schedule - events - merge_requests_closing_issues - cached_closes_issues @@ -293,6 +294,8 @@ merge_request_diff_files: merge_request_context_commits: - merge_request - diff_files +merge_schedule: +- merge_request cleanup_schedule: - merge_request ci_pipelines: diff --git a/spec/models/ci/build_trace_chunk_spec.rb b/spec/models/ci/build_trace_chunk_spec.rb index 355905cdabd..b56c0608488 100644 --- a/spec/models/ci/build_trace_chunk_spec.rb +++ b/spec/models/ci/build_trace_chunk_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe Ci::BuildTraceChunk, :clean_gitlab_redis_shared_state, :clean_gitlab_redis_trace_chunks do +RSpec.describe Ci::BuildTraceChunk, :clean_gitlab_redis_shared_state, :clean_gitlab_redis_trace_chunks, feature_category: :pipeline_execution do include ExclusiveLeaseHelpers let_it_be(:build) { create(:ci_build, :running) } @@ -142,18 +142,29 @@ RSpec.describe Ci::BuildTraceChunk, :clean_gitlab_redis_shared_state, :clean_git it { is_expected.to eq('Sample data in database') } end - context 'when data_store is fog' do + context 'when data_store is fog', :request_store do let(:data_store) { :fog } + let(:sample_data) { +'Sample data in fog' } before do - build_trace_chunk.send(:unsafe_set_data!, +'Sample data in fog') + build_trace_chunk.send(:unsafe_set_data!, sample_data) end - it { is_expected.to eq('Sample data in fog') } + it { is_expected.to eq(sample_data) } it 'returns a new Fog store' do expect(described_class.get_store_class(data_store)).to be_a(Ci::BuildTraceChunks::Fog) end + + it 'only initializes Fog::Storage once' do + RequestStore.clear! + + expect(Fog::Storage).to receive(:new).and_call_original + + 2.times do + expect(build_trace_chunk.reload.build.trace_chunks.first.data).to eq(sample_data) + end + end end end diff --git a/spec/models/merge_request_spec.rb b/spec/models/merge_request_spec.rb index 8ef72206675..2fab1f07887 100644 --- a/spec/models/merge_request_spec.rb +++ b/spec/models/merge_request_spec.rb @@ -36,6 +36,7 @@ RSpec.describe MergeRequest, factory_default: :keep, feature_category: :code_rev it { is_expected.to have_many(:reviews).inverse_of(:merge_request) } it { is_expected.to have_many(:reviewed_by_users).through(:reviews).source(:author) } it { is_expected.to have_one(:cleanup_schedule).inverse_of(:merge_request) } + it { is_expected.to have_one(:merge_schedule).class_name('MergeRequests::MergeSchedule').inverse_of(:merge_request) } it { is_expected.to have_many(:created_environments).class_name('Environment').inverse_of(:merge_request) } it { is_expected.to have_many(:assignment_events).class_name('ResourceEvents::MergeRequestAssignmentEvent').inverse_of(:merge_request) } diff --git a/spec/models/merge_requests/merge_schedule_spec.rb b/spec/models/merge_requests/merge_schedule_spec.rb new file mode 100644 index 00000000000..a869032f118 --- /dev/null +++ b/spec/models/merge_requests/merge_schedule_spec.rb @@ -0,0 +1,23 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe MergeRequests::MergeSchedule, feature_category: :code_review_workflow do + subject { create(:merge_request_merge_schedule) } + + describe 'associations' do + it { is_expected.to belong_to(:merge_request).required } + end + + describe 'callbacks' do + let(:merge_request) { create(:merge_request) } + let(:schedule) do + create(:merge_request_merge_schedule, merge_request: merge_request, + project_id: merge_request.target_project.id + 1) + end + + it 'overrides project_id to the correct sharding key' do + expect(schedule.project_id).to eq(merge_request.target_project.id) + end + end +end diff --git a/spec/support/helpers/login_helpers.rb b/spec/support/helpers/login_helpers.rb index a99774dc89b..53adc11adfe 100644 --- a/spec/support/helpers/login_helpers.rb +++ b/spec/support/helpers/login_helpers.rb @@ -137,7 +137,7 @@ module LoginHelpers def register_via(provider, uid, email, additional_info: {}) mock_auth_hash(provider, uid, email, additional_info: additional_info) visit new_user_registration_path - expect(page).to have_content('Create an account using').or(have_content('Register with')) + expect(page).to have_content('Create an account using').or(have_content('Continue with')) click_button Gitlab::Auth::OAuth::Provider.label_for(provider) end diff --git a/spec/support/helpers/sign_up_helpers.rb b/spec/support/helpers/sign_up_helpers.rb index 8712a8c1b35..a8b1701db03 100644 --- a/spec/support/helpers/sign_up_helpers.rb +++ b/spec/support/helpers/sign_up_helpers.rb @@ -3,7 +3,7 @@ require 'spec_helper' module SignUpHelpers - def fill_in_sign_up_form(new_user, submit_button_text = 'Register', invite: false) + def fill_in_sign_up_form(new_user, invite: false) fill_in 'new_user_first_name', with: new_user.first_name fill_in 'new_user_last_name', with: new_user.last_name fill_in 'new_user_username', with: new_user.username @@ -16,7 +16,7 @@ module SignUpHelpers yield if block_given? - click_button submit_button_text + click_button _('Continue') end def confirm_email(new_user) diff --git a/spec/support/shared_examples/services/snowplow_tracking_shared_examples.rb b/spec/support/shared_examples/services/snowplow_tracking_shared_examples.rb deleted file mode 100644 index d3b3434b339..00000000000 --- a/spec/support/shared_examples/services/snowplow_tracking_shared_examples.rb +++ /dev/null @@ -1,10 +0,0 @@ -# frozen_string_literal: true - -RSpec.shared_examples 'issue_edit snowplow tracking' do - let(:category) { Gitlab::UsageDataCounters::IssueActivityUniqueCounter::ISSUE_CATEGORY } - let(:action) { Gitlab::UsageDataCounters::IssueActivityUniqueCounter::ISSUE_ACTION } - let(:label) { Gitlab::UsageDataCounters::IssueActivityUniqueCounter::ISSUE_LABEL } - let(:namespace) { project.namespace } - - it_behaves_like 'Snowplow event tracking with RedisHLL context' -end diff --git a/spec/views/devise/shared/_signup_box.html.haml_spec.rb b/spec/views/devise/registrations/_signup_box_form.html.haml_spec.rb similarity index 76% rename from spec/views/devise/shared/_signup_box.html.haml_spec.rb rename to spec/views/devise/registrations/_signup_box_form.html.haml_spec.rb index 43ca2af87e8..15b570c364d 100644 --- a/spec/views/devise/shared/_signup_box.html.haml_spec.rb +++ b/spec/views/devise/registrations/_signup_box_form.html.haml_spec.rb @@ -2,14 +2,13 @@ require 'spec_helper' -RSpec.describe 'devise/shared/_signup_box', feature_category: :system_access do +RSpec.describe 'devise/registrations/_signup_box_form', feature_category: :system_access do before do stub_devise allow(view).to receive(:arkose_labs_enabled?).and_return(false) - allow(view).to receive(:show_omniauth_providers).and_return(false) allow(view).to receive(:url).and_return('_url_') allow(view).to receive(:button_text).and_return('') - allow(view).to receive(:tracking_label).and_return('') + allow(view).to receive(:preregistration_tracking_label).and_return('') stub_template 'devise/shared/_error_messages.html.haml' => '' end diff --git a/spec/views/devise/registrations/new.html.haml_spec.rb b/spec/views/devise/registrations/new.html.haml_spec.rb new file mode 100644 index 00000000000..0961da8d1cd --- /dev/null +++ b/spec/views/devise/registrations/new.html.haml_spec.rb @@ -0,0 +1,36 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe 'devise/registrations/new', feature_category: :system_access do + let(:resource) { Users::RegistrationsBuildService.new(nil, {}).execute } + let(:tracking_label) { '_some_registration_' } + + subject { render && rendered } + + before do + allow(view).to receive(:resource).and_return(resource) + allow(view).to receive(:resource_name).and_return(:user) + allow(view).to receive(:glm_tracking_params).and_return({}) + allow(view).to receive(:registration_path_params).and_return({}) + allow(view).to receive(:preregistration_tracking_label).and_return(tracking_label) + allow(view).to receive(:arkose_labs_enabled?) + end + + context 'for omniauth provider buttons' do + let(:provider_label) { :github } + let(:tracking_action) { "#{provider_label}_sso" } + + before do + allow(view).to receive(:providers).and_return([provider_label]) + end + + it { is_expected.to have_css("[data-track-action='#{tracking_action}'][data-track-label='#{tracking_label}']") } + it { is_expected.to have_content(_('Continue with:')) } + it { is_expected.to have_css('form[action="/users/auth/github"]') } + end + + context 'without broadcast messaging' do + it { is_expected.not_to render_template('layouts/_broadcast') } + end +end diff --git a/spec/views/devise/shared/_signup_omniauth_provider_list_spec.rb b/spec/views/devise/shared/_signup_omniauth_provider_list_spec.rb deleted file mode 100644 index 988f2e2df8a..00000000000 --- a/spec/views/devise/shared/_signup_omniauth_provider_list_spec.rb +++ /dev/null @@ -1,35 +0,0 @@ -# frozen_string_literal: true - -require 'spec_helper' - -RSpec.describe 'devise/shared/_signup_omniauth_provider_list', feature_category: :system_access do - let_it_be(:provider_label) { :github }.freeze - let_it_be(:tracking_label) { 'free_registration' }.freeze - let_it_be(:tracking_action) { "#{provider_label}_sso" }.freeze - - subject { rendered } - - before do - allow(view).to receive(:providers).and_return([provider_label]) - allow(view).to receive(:tracking_label).and_return(tracking_label) - allow(view).to receive(:glm_tracking_params).and_return({}) - render - end - - shared_examples 'sso buttons have snowplow tracking' do - it 'contains tracking attributes' do - css = "[data-track-action='#{tracking_action}']" - css += "[data-track-label='#{tracking_label}']" - - expect(rendered).to have_css(css) - end - end - - it { is_expected.to have_content(_("Register with:")) } - - it_behaves_like 'sso buttons have snowplow tracking' - - it 'renders button in form' do - expect(rendered).to have_css('form[action="/users/auth/github"]') - end -end