diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2022-10-25 09:10:36 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2022-10-25 09:10:36 +0300 |
commit | 866b1f8ed7db9b29b1188ffcba309b92572f354b (patch) | |
tree | 48f59dd9b5a292e58e8ce20fbff7e95b6178afe1 | |
parent | 01fbd09ea9ea4eeae52ed9fb4f7cc4dd97b4eb69 (diff) |
Add latest changes from gitlab-org/gitlab@master
36 files changed, 1114 insertions, 98 deletions
diff --git a/GITALY_SERVER_VERSION b/GITALY_SERVER_VERSION index 1a2cb3681bc..06bd7d509ce 100644 --- a/GITALY_SERVER_VERSION +++ b/GITALY_SERVER_VERSION @@ -1 +1 @@ -e1dd9bfe694190e9350dad37b5cd8b5ea44eafa3 +bb32df05c777d0cd8a8d15249c99e2d8055e0769 diff --git a/app/assets/javascripts/access_tokens/components/new_access_token_app.vue b/app/assets/javascripts/access_tokens/components/new_access_token_app.vue index ce5342ad1ea..d24285af5c3 100644 --- a/app/assets/javascripts/access_tokens/components/new_access_token_app.vue +++ b/app/assets/javascripts/access_tokens/components/new_access_token_app.vue @@ -104,18 +104,13 @@ export default { @[$options.EVENT_ERROR]="onError" @[$options.EVENT_SUCCESS]="onSuccess" > - <div ref="container"> + <div ref="container" data-testid="access-token-section" data-qa-selector="access_token_section"> <template v-if="newToken"> - <!-- - After issue https://gitlab.com/gitlab-org/gitlab/-/issues/360921 is - closed remove the `initial-visibility`. - --> <input-copy-toggle-visibility :copy-button-title="copyButtonTitle" :label="label" :label-for="$options.tokenInputId" :value="newToken" - initial-visibility :form-input-group-props="formInputGroupProps" > <template #description> diff --git a/app/assets/javascripts/pages/projects/hooks/index.js b/app/assets/javascripts/pages/projects/hooks/index.js index ed476d25f8b..9e559354205 100644 --- a/app/assets/javascripts/pages/projects/hooks/index.js +++ b/app/assets/javascripts/pages/projects/hooks/index.js @@ -1,5 +1,7 @@ import initSearchSettings from '~/search_settings'; import initWebhookForm from '~/webhooks'; +import { initPushEventsEditForm } from '~/webhooks/webhook'; initSearchSettings(); initWebhookForm(); +initPushEventsEditForm(); diff --git a/app/assets/javascripts/vue_shared/components/form/input_copy_toggle_visibility.vue b/app/assets/javascripts/vue_shared/components/form/input_copy_toggle_visibility.vue index 482a2964b4c..2f10e068542 100644 --- a/app/assets/javascripts/vue_shared/components/form/input_copy_toggle_visibility.vue +++ b/app/assets/javascripts/vue_shared/components/form/input_copy_toggle_visibility.vue @@ -129,6 +129,8 @@ export default { v-gl-tooltip.hover="toggleVisibilityLabel" :aria-label="toggleVisibilityLabel" :icon="toggleVisibilityIcon" + data-testid="toggle-visibility-button" + data-qa-selector="toggle_visibility_button" @click.stop="handleToggleVisibilityButtonClick" /> <clipboard-button diff --git a/app/assets/javascripts/webhooks/components/push_events.vue b/app/assets/javascripts/webhooks/components/push_events.vue new file mode 100644 index 00000000000..677f06314e0 --- /dev/null +++ b/app/assets/javascripts/webhooks/components/push_events.vue @@ -0,0 +1,112 @@ +<script> +import { GlFormCheckbox, GlFormRadio, GlFormRadioGroup, GlFormInput, GlSprintf } from '@gitlab/ui'; +import { + BRANCH_FILTER_ALL_BRANCHES, + WILDCARD_CODE_STABLE, + WILDCARD_CODE_PRODUCTION, + REGEX_CODE, + descriptionText, +} from '~/webhooks/constants'; + +export default { + components: { + GlFormCheckbox, + GlFormRadio, + GlFormRadioGroup, + GlFormInput, + GlSprintf, + }, + inject: ['pushEvents', 'strategy', 'isNewHook', 'pushEventsBranchFilter'], + data() { + return { + pushEventsData: !this.isNewHook && this.pushEvents, + branchFilterStrategyData: this.isNewHook ? BRANCH_FILTER_ALL_BRANCHES : this.strategy, + pushEventsBranchFilterData: this.pushEventsBranchFilter, + }; + }, + WILDCARD_CODE_STABLE, + WILDCARD_CODE_PRODUCTION, + REGEX_CODE, + descriptionText, +}; +</script> + +<template> + <div> + <gl-form-checkbox v-model="pushEventsData">{{ s__('Webhooks|Push events') }}</gl-form-checkbox> + <input type="hidden" :value="pushEventsData" name="hook[push_events]" /> + + <div v-if="pushEventsData" class="gl-pl-6"> + <gl-form-radio-group v-model="branchFilterStrategyData" name="hook[branch_filter_strategy]"> + <gl-form-radio + class="gl-mt-2 branch-filter-strategy-radio" + value="all_branches" + data-testid="rule_all_branches" + > + <div data-qa-selector="strategy_radio_all">{{ __('All branches') }}</div> + </gl-form-radio> + + <!-- wildcard --> + <gl-form-radio + class="gl-mt-2 branch-filter-strategy-radio" + value="wildcard" + data-testid="rule_wildcard" + > + <div data-qa-selector="strategy_radio_wildcard"> + {{ s__('Webhooks|Wildcard pattern') }} + </div> + </gl-form-radio> + <div class="gl-ml-6"> + <gl-form-input + v-if="branchFilterStrategyData === 'wildcard'" + v-model="pushEventsBranchFilterData" + name="hook[push_events_branch_filter]" + data-qa-selector="webhook_branch_filter_field" + data-testid="webhook_branch_filter_field" + /> + </div> + <p + v-if="branchFilterStrategyData === 'wildcard'" + class="form-text text-muted custom-control" + > + <gl-sprintf :message="$options.descriptionText.wildcard"> + <template #WILDCARD_CODE_STABLE> + <code>{{ $options.WILDCARD_CODE_STABLE }}</code> + </template> + <template #WILDCARD_CODE_PRODUCTION> + <code>{{ $options.WILDCARD_CODE_PRODUCTION }}</code> + </template> + </gl-sprintf> + </p> + + <!-- regex --> + <gl-form-radio + class="gl-mt-2 branch-filter-strategy-radio" + value="regex" + data-testid="rule_regex" + > + <div data-qa-selector="strategy_radio_regex"> + {{ s__('Webhooks|Regular expression') }} + </div> + </gl-form-radio> + <div class="gl-ml-6"> + <gl-form-input + v-if="branchFilterStrategyData === 'regex'" + v-model="pushEventsBranchFilterData" + name="hook[push_events_branch_filter]" + data-qa-selector="webhook_branch_filter_field" + data-testid="webhook_branch_filter_field" + /> + </div> + + <p v-if="branchFilterStrategyData === 'regex'" class="form-text text-muted custom-control"> + <gl-sprintf :message="$options.descriptionText.regex"> + <template #REGEX_CODE> + <code>{{ $options.REGEX_CODE }}</code> + </template> + </gl-sprintf> + </p> + </gl-form-radio-group> + </div> + </div> +</template> diff --git a/app/assets/javascripts/webhooks/constants.js b/app/assets/javascripts/webhooks/constants.js new file mode 100644 index 00000000000..abef16545bc --- /dev/null +++ b/app/assets/javascripts/webhooks/constants.js @@ -0,0 +1,17 @@ +import { s__ } from '~/locale'; + +export const BRANCH_FILTER_ALL_BRANCHES = 'all_branches'; +export const BRANCH_FILTER_WILDCARD = 'wildcard'; +export const BRANCH_FILTER_REGEX = 'regex'; + +export const WILDCARD_CODE_STABLE = '*-stable'; +export const WILDCARD_CODE_PRODUCTION = 'production/*'; + +export const REGEX_CODE = '(feature|hotfix)/*'; + +export const descriptionText = { + [BRANCH_FILTER_WILDCARD]: s__( + 'Webhooks|Wildcards such as %{WILDCARD_CODE_STABLE} or %{WILDCARD_CODE_PRODUCTION} are supported.', + ), + [BRANCH_FILTER_REGEX]: s__('Webhooks|Regex such as %{REGEX_CODE} is supported.'), +}; diff --git a/app/assets/javascripts/webhooks/webhook.js b/app/assets/javascripts/webhooks/webhook.js new file mode 100644 index 00000000000..ca631502745 --- /dev/null +++ b/app/assets/javascripts/webhooks/webhook.js @@ -0,0 +1,23 @@ +import Vue from 'vue'; +import { parseBoolean } from '~/lib/utils/common_utils'; +import pushEvents from './components/push_events.vue'; + +export function initPushEventsEditForm() { + const el = document.querySelector('.js-vue-push-events'); + + if (!el) return false; + + const provide = { + isNewHook: parseBoolean(el.dataset.isNewHook), + pushEvents: parseBoolean(el.dataset.pushEvents), + strategy: el.dataset.strategy, + pushEventsBranchFilter: el.dataset.pushEventsBranchFilter, + }; + return new Vue({ + el, + provide, + render(createElement) { + return createElement(pushEvents); + }, + }); +} diff --git a/app/helpers/events_helper.rb b/app/helpers/events_helper.rb index b717cbcc312..087e4838ed9 100644 --- a/app/helpers/events_helper.rb +++ b/app/helpers/events_helper.rb @@ -68,7 +68,7 @@ module EventsHelper author = event.author if author - name = self_added ? 'You' : author.name + name = self_added ? _('You') : author.name link_to name, user_path(author.username), title: name else escape_once(event.author_name) diff --git a/app/helpers/integrations_helper.rb b/app/helpers/integrations_helper.rb index a1512d40235..abfa55cff24 100644 --- a/app/helpers/integrations_helper.rb +++ b/app/helpers/integrations_helper.rb @@ -160,6 +160,31 @@ module IntegrationsHelper !Gitlab.com? end + def integration_issue_type(issue_type) + issue_type_i18n_map = { + 'issue' => _('Issue'), + 'incident' => _('Incident'), + 'test_case' => _('Test case'), + 'requirement' => _('Requirement'), + 'task' => _('Task') + } + + issue_type_i18n_map[issue_type] || issue_type + end + + def integration_todo_target_type(target_type) + target_type_i18n_map = { + 'Commit' => _('Commit'), + 'Issue' => _('Issue'), + 'MergeRequest' => _('Merge Request'), + 'Epic' => _('Epic'), + DesignManagement::Design.name => _('design'), + AlertManagement::Alert.name => _('alert') + } + + target_type_i18n_map[target_type] || target_type + end + extend self private diff --git a/app/helpers/todos_helper.rb b/app/helpers/todos_helper.rb index 520cde9ecee..2663246ef81 100644 --- a/app/helpers/todos_helper.rb +++ b/app/helpers/todos_helper.rb @@ -15,21 +15,25 @@ module TodosHelper def todo_action_name(todo) case todo.action - when Todo::ASSIGNED then todo.self_added? ? 'assigned' : 'assigned you' - when Todo::REVIEW_REQUESTED then 'requested a review of' - when Todo::MENTIONED, Todo::DIRECTLY_ADDRESSED then "mentioned #{todo_action_subject(todo)} on" - when Todo::BUILD_FAILED then 'The pipeline failed in' - when Todo::MARKED then 'added a todo for' - when Todo::APPROVAL_REQUIRED then "set #{todo_action_subject(todo)} as an approver for" - when Todo::UNMERGEABLE then 'Could not merge' - when Todo::MERGE_TRAIN_REMOVED then "Removed from Merge Train:" + when Todo::ASSIGNED then todo.self_added? ? _('assigned') : _('assigned you') + when Todo::REVIEW_REQUESTED then s_('Todos|requested a review of') + when Todo::MENTIONED, Todo::DIRECTLY_ADDRESSED then format( + s_("Todos|mentioned %{who} on"), who: todo_action_subject(todo) + ) + when Todo::BUILD_FAILED then s_('Todos|The pipeline failed in') + when Todo::MARKED then s_('Todos|added a todo for') + when Todo::APPROVAL_REQUIRED then format( + s_("Todos|set %{who} as an approver for"), who: todo_action_subject(todo) + ) + when Todo::UNMERGEABLE then s_('Todos|Could not merge') + when Todo::MERGE_TRAIN_REMOVED then s_("Todos|Removed from Merge Train:") end end def todo_self_addressing(todo) case todo.action - when Todo::ASSIGNED then 'to yourself' - when Todo::REVIEW_REQUESTED then 'from yourself' + when Todo::ASSIGNED then _('to yourself') + when Todo::REVIEW_REQUESTED then _('from yourself') end end @@ -66,9 +70,9 @@ module TodosHelper return _('alert') if todo.for_alert? target_type = if todo.for_issue_or_work_item? - todo.target.issue_type + IntegrationsHelper.integration_issue_type(todo.target.issue_type) else - todo.target_type + IntegrationsHelper.integration_todo_target_type(todo.target_type) end target_type.titleize.downcase @@ -109,6 +113,11 @@ module TodosHelper return unless show_todo_state?(todo) state = todo.target.state.to_s + raw_state_to_i18n = { + "closed" => _('Closed'), + "merged" => _('Merged'), + "resolved" => _('Resolved') + } case todo.target when MergeRequest @@ -124,7 +133,7 @@ module TodosHelper end tag.span class: "gl-my-0 gl-px-2 status-box #{background_class}" do - todo.target.state.to_s.capitalize + raw_state_to_i18n[state] || state.capitalize end end @@ -237,7 +246,7 @@ module TodosHelper end def todo_action_subject(todo) - todo.self_added? ? 'yourself' : 'you' + todo.self_added? ? s_('Todos|yourself') : _('you') end def show_todo_state?(todo) diff --git a/app/views/dashboard/todos/_todo.html.haml b/app/views/dashboard/todos/_todo.html.haml index b4668b1e52a..47bc8f5c95b 100644 --- a/app/views/dashboard/todos/_todo.html.haml +++ b/app/views/dashboard/todos/_todo.html.haml @@ -27,8 +27,7 @@ = todo_target_title(todo) %span.title-item.todo-project.todo-label - at - = todo_parent_path(todo) + = s_('Todo|at %{todo_parent_path}').html_safe % { todo_parent_path: todo_parent_path(todo) } - if todo.self_assigned? %span.title-item.action-name diff --git a/db/post_migrate/20221006070927_finalize_invalid_member_cleanup.rb b/db/post_migrate/20221006070927_finalize_invalid_member_cleanup.rb new file mode 100644 index 00000000000..78786e46f5c --- /dev/null +++ b/db/post_migrate/20221006070927_finalize_invalid_member_cleanup.rb @@ -0,0 +1,22 @@ +# frozen_string_literal: true + +class FinalizeInvalidMemberCleanup < Gitlab::Database::Migration[2.0] + disable_ddl_transaction! + + restrict_gitlab_migration gitlab_schema: :gitlab_main + + MIGRATION = 'DestroyInvalidMembers' + + def up + ensure_batched_background_migration_is_finished( + job_class_name: MIGRATION, + table_name: :members, + column_name: :id, + job_arguments: [] + ) + end + + def down + # noop + end +end diff --git a/db/schema_migrations/20221006070927 b/db/schema_migrations/20221006070927 new file mode 100644 index 00000000000..804f77384e0 --- /dev/null +++ b/db/schema_migrations/20221006070927 @@ -0,0 +1 @@ +933cb5a869696f2343b0b8dfc32f94a64ed7a5119c3f6b2b64ce30e3ae4e555c
\ No newline at end of file diff --git a/lib/banzai/filter/math_filter.rb b/lib/banzai/filter/math_filter.rb index 1ca4b2c89db..ca13a8d6474 100644 --- a/lib/banzai/filter/math_filter.rb +++ b/lib/banzai/filter/math_filter.rb @@ -32,7 +32,7 @@ module Banzai # Corresponds to the $$\n...\n$$ syntax DOLLAR_DISPLAY_BLOCK_PATTERN = %r{ ^(?<matched>\$\$\ *\n(?<math>.*)\n\$\$\ *)$ - }x.freeze + }mx.freeze # Order dependent. Handle the `$$` syntax before the `$` syntax DOLLAR_MATH_PIPELINE = [ diff --git a/locale/gitlab.pot b/locale/gitlab.pot index c765793c1c6..3575e9b637c 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -42348,6 +42348,9 @@ msgstr "" msgid "Todos|Assigned" msgstr "" +msgid "Todos|Could not merge" +msgstr "" + msgid "Todos|Design" msgstr "" @@ -42399,9 +42402,15 @@ msgstr "" msgid "Todos|Pipelines" msgstr "" +msgid "Todos|Removed from Merge Train:" +msgstr "" + msgid "Todos|Review requested" msgstr "" +msgid "Todos|The pipeline failed in" +msgstr "" + msgid "Todos|Undo mark all as done" msgstr "" @@ -42414,6 +42423,24 @@ msgstr "" msgid "Todos|Your To-Do List shows what to work on next" msgstr "" +msgid "Todos|added a todo for" +msgstr "" + +msgid "Todos|mentioned %{who} on" +msgstr "" + +msgid "Todos|requested a review of" +msgstr "" + +msgid "Todos|set %{who} as an approver for" +msgstr "" + +msgid "Todos|yourself" +msgstr "" + +msgid "Todo|at %{todo_parent_path}" +msgstr "" + msgid "Toggle GitLab Next" msgstr "" @@ -45418,6 +45445,12 @@ msgstr "" msgid "Webhooks|Push to the repository." msgstr "" +msgid "Webhooks|Regex such as %{REGEX_CODE} is supported." +msgstr "" + +msgid "Webhooks|Regular expression" +msgstr "" + msgid "Webhooks|Releases events" msgstr "" @@ -45478,6 +45511,12 @@ msgstr "" msgid "Webhooks|Wiki page events" msgstr "" +msgid "Webhooks|Wildcard pattern" +msgstr "" + +msgid "Webhooks|Wildcards such as %{WILDCARD_CODE_STABLE} or %{WILDCARD_CODE_PRODUCTION} are supported." +msgstr "" + msgid "Website" msgstr "" @@ -47276,6 +47315,12 @@ msgstr "" msgid "assign yourself" msgstr "" +msgid "assigned" +msgstr "" + +msgid "assigned you" +msgstr "" + msgid "at" msgstr "" @@ -48018,6 +48063,9 @@ msgid_plural "from %d jobs" msgstr[0] "" msgstr[1] "" +msgid "from yourself" +msgstr "" + msgid "frontmatter" msgstr "" @@ -49076,6 +49124,9 @@ msgstr "" msgid "time summary" msgstr "" +msgid "to yourself" +msgstr "" + msgid "today" msgstr "" @@ -49212,6 +49263,9 @@ msgstr "" msgid "yaml invalid" msgstr "" +msgid "you" +msgstr "" + msgid "your GitLab instance" msgstr "" diff --git a/package.json b/package.json index 1101cb65fd6..bf0dbf59f9d 100644 --- a/package.json +++ b/package.json @@ -106,7 +106,7 @@ "codesandbox-api": "0.0.23", "compression-webpack-plugin": "^5.0.2", "copy-webpack-plugin": "^6.4.1", - "core-js": "^3.25.5", + "core-js": "^3.26.0", "cron-validator": "^1.1.1", "cronstrue": "^1.122.0", "cropper": "^2.3.0", diff --git a/qa/qa/page/component/access_tokens.rb b/qa/qa/page/component/access_tokens.rb index 8723f6619d9..586f69b8a64 100644 --- a/qa/qa/page/component/access_tokens.rb +++ b/qa/qa/page/component/access_tokens.rb @@ -28,9 +28,14 @@ module QA end base.view 'app/assets/javascripts/access_tokens/components/new_access_token_app.vue' do + element :access_token_section element :created_access_token_field end + base.view 'app/assets/javascripts/vue_shared/components/form/input_copy_toggle_visibility.vue' do + element :toggle_visibility_button + end + base.view 'app/assets/javascripts/access_tokens/components/access_token_table_app.vue' do element :revoke_button end @@ -49,7 +54,10 @@ module QA end def created_access_token - find_element(:created_access_token_field, wait: 30).value + within_element(:access_token_section) do + click_element(:toggle_visibility_button, wait: 30) + find_element(:created_access_token_field).value + end end def fill_expiry_date(date) diff --git a/spec/controllers/projects/jobs_controller_spec.rb b/spec/controllers/projects/jobs_controller_spec.rb index 556dd23c135..58627c4dc74 100644 --- a/spec/controllers/projects/jobs_controller_spec.rb +++ b/spec/controllers/projects/jobs_controller_spec.rb @@ -1380,7 +1380,7 @@ RSpec.describe Projects::JobsController, :clean_gitlab_redis_shared_state do { 'Channel' => { 'Subprotocols' => ["terminal.gitlab.com"], - 'Url' => 'wss://localhost/proxy/build/default_port/', + 'Url' => 'wss://gitlab.example.com/proxy/build/default_port/', 'Header' => { 'Authorization' => [nil] }, @@ -1536,7 +1536,8 @@ RSpec.describe Projects::JobsController, :clean_gitlab_redis_shared_state do allow(Gitlab::Workhorse).to receive(:verify_api_request!).and_return(nil) expect(job.runner_session_url).to start_with('https://') - expect(Gitlab::Workhorse).to receive(:channel_websocket).with(a_hash_including(url: "wss://localhost/proxy/build/default_port/")) + expect(Gitlab::Workhorse).to receive(:channel_websocket) + .with(a_hash_including(url: "wss://gitlab.example.com/proxy/build/default_port/")) make_request end diff --git a/spec/factories/ci/builds.rb b/spec/factories/ci/builds.rb index 9a3b2837ab8..8396ef480c7 100644 --- a/spec/factories/ci/builds.rb +++ b/spec/factories/ci/builds.rb @@ -716,7 +716,7 @@ FactoryBot.define do trait :with_runner_session do after(:build) do |build| - build.build_runner_session(url: 'https://localhost') + build.build_runner_session(url: 'https://gitlab.example.com') end end diff --git a/spec/features/admin/admin_users_impersonation_tokens_spec.rb b/spec/features/admin/admin_users_impersonation_tokens_spec.rb index 45dccf9921f..d93dac4834e 100644 --- a/spec/features/admin/admin_users_impersonation_tokens_spec.rb +++ b/spec/features/admin/admin_users_impersonation_tokens_spec.rb @@ -4,18 +4,11 @@ require 'spec_helper' RSpec.describe 'Admin > Users > Impersonation Tokens', :js do include Spec::Support::Helpers::ModalHelpers + include Spec::Support::Helpers::AccessTokenHelpers let(:admin) { create(:admin) } let!(:user) { create(:user) } - def active_impersonation_tokens - find("[data-testid='active-tokens']") - end - - def created_impersonation_token - find_field('new-access-token').value - end - before do sign_in(admin) gitlab_enable_admin_mode_sign_in(admin) @@ -39,12 +32,12 @@ RSpec.describe 'Admin > Users > Impersonation Tokens', :js do click_on "Create impersonation token" - expect(active_impersonation_tokens).to have_text(name) - expect(active_impersonation_tokens).to have_text('in') - expect(active_impersonation_tokens).to have_text('read_api') - expect(active_impersonation_tokens).to have_text('read_user') + expect(active_access_tokens).to have_text(name) + expect(active_access_tokens).to have_text('in') + expect(active_access_tokens).to have_text('read_api') + expect(active_access_tokens).to have_text('read_user') expect(PersonalAccessTokensFinder.new(impersonation: true).execute.count).to equal(1) - expect(created_impersonation_token).not_to be_empty + expect(created_access_token).to match(/[\w-]{20}/) end end @@ -55,16 +48,16 @@ RSpec.describe 'Admin > Users > Impersonation Tokens', :js do it 'only shows impersonation tokens' do visit admin_user_impersonation_tokens_path(user_id: user.username) - expect(active_impersonation_tokens).to have_text(impersonation_token.name) - expect(active_impersonation_tokens).not_to have_text(personal_access_token.name) - expect(active_impersonation_tokens).to have_text('in') + expect(active_access_tokens).to have_text(impersonation_token.name) + expect(active_access_tokens).not_to have_text(personal_access_token.name) + expect(active_access_tokens).to have_text('in') end it 'shows absolute times' do admin.update!(time_display_relative: false) visit admin_user_impersonation_tokens_path(user_id: user.username) - expect(active_impersonation_tokens).to have_text(personal_access_token.expires_at.strftime('%b %-d')) + expect(active_access_tokens).to have_text(personal_access_token.expires_at.strftime('%b %-d')) end end @@ -76,7 +69,7 @@ RSpec.describe 'Admin > Users > Impersonation Tokens', :js do accept_gl_confirm(button_text: 'Revoke') { click_on "Revoke" } - expect(active_impersonation_tokens).to have_text("This user has no active impersonation tokens.") + expect(active_access_tokens).to have_text("This user has no active impersonation tokens.") end it "removes expired tokens from 'active' section" do @@ -84,7 +77,7 @@ RSpec.describe 'Admin > Users > Impersonation Tokens', :js do visit admin_user_impersonation_tokens_path(user_id: user.username) - expect(active_impersonation_tokens).to have_text("This user has no active impersonation tokens.") + expect(active_access_tokens).to have_text("This user has no active impersonation tokens.") end end diff --git a/spec/features/profiles/personal_access_tokens_spec.rb b/spec/features/profiles/personal_access_tokens_spec.rb index 088c8a7a15a..3ae88da06f6 100644 --- a/spec/features/profiles/personal_access_tokens_spec.rb +++ b/spec/features/profiles/personal_access_tokens_spec.rb @@ -4,22 +4,11 @@ require 'spec_helper' RSpec.describe 'Profile > Personal Access Tokens', :js do include Spec::Support::Helpers::ModalHelpers + include Spec::Support::Helpers::AccessTokenHelpers let(:user) { create(:user) } let(:pat_create_service) { double('PersonalAccessTokens::CreateService', execute: ServiceResponse.error(message: 'error', payload: { personal_access_token: PersonalAccessToken.new })) } - def active_personal_access_tokens - find("[data-testid='active-tokens']") - end - - def created_personal_access_token - find_field('new-access-token').value - end - - def feed_token_description - "Your feed token authenticates you when your RSS reader loads a personalized RSS feed or when your calendar application loads a personalized calendar. It is visible in those feed URLs." - end - before do sign_in(user) end @@ -43,11 +32,11 @@ RSpec.describe 'Profile > Personal Access Tokens', :js do click_on "Create personal access token" wait_for_all_requests - expect(active_personal_access_tokens).to have_text(name) - expect(active_personal_access_tokens).to have_text('in') - expect(active_personal_access_tokens).to have_text('read_api') - expect(active_personal_access_tokens).to have_text('read_user') - expect(created_personal_access_token).not_to be_empty + expect(active_access_tokens).to have_text(name) + expect(active_access_tokens).to have_text('in') + expect(active_access_tokens).to have_text('read_api') + expect(active_access_tokens).to have_text('read_user') + expect(created_access_token).to match(/[\w-]{20}/) end context "when creation fails" do @@ -73,8 +62,8 @@ RSpec.describe 'Profile > Personal Access Tokens', :js do it 'only shows personal access tokens' do visit profile_personal_access_tokens_path - expect(active_personal_access_tokens).to have_text(personal_access_token.name) - expect(active_personal_access_tokens).not_to have_text(impersonation_token.name) + expect(active_access_tokens).to have_text(personal_access_token.name) + expect(active_access_tokens).not_to have_text(impersonation_token.name) end context 'when User#time_display_relative is false' do @@ -85,7 +74,7 @@ RSpec.describe 'Profile > Personal Access Tokens', :js do it 'shows absolute times for expires_at' do visit profile_personal_access_tokens_path - expect(active_personal_access_tokens).to have_text(PersonalAccessToken.last.expires_at.strftime('%b %-d')) + expect(active_access_tokens).to have_text(PersonalAccessToken.last.expires_at.strftime('%b %-d')) end end end @@ -97,14 +86,14 @@ RSpec.describe 'Profile > Personal Access Tokens', :js do visit profile_personal_access_tokens_path accept_gl_confirm(button_text: 'Revoke') { click_on "Revoke" } - expect(active_personal_access_tokens).to have_text("This user has no active personal access tokens.") + expect(active_access_tokens).to have_text("This user has no active personal access tokens.") end it "removes expired tokens from 'active' section" do personal_access_token.update!(expires_at: 5.days.ago) visit profile_personal_access_tokens_path - expect(active_personal_access_tokens).to have_text("This user has no active personal access tokens.") + expect(active_access_tokens).to have_text("This user has no active personal access tokens.") end context "when revocation fails" do @@ -115,12 +104,16 @@ RSpec.describe 'Profile > Personal Access Tokens', :js do visit profile_personal_access_tokens_path accept_gl_confirm(button_text: "Revoke") { click_on "Revoke" } - expect(active_personal_access_tokens).to have_text(personal_access_token.name) + expect(active_access_tokens).to have_text(personal_access_token.name) end end end describe "feed token" do + def feed_token_description + "Your feed token authenticates you when your RSS reader loads a personalized RSS feed or when your calendar application loads a personalized calendar. It is visible in those feed URLs." + end + context "when enabled" do it "displays feed token" do allow(Gitlab::CurrentSettings).to receive(:disable_feed_token).and_return(false) diff --git a/spec/features/projects/settings/webhooks_settings_spec.rb b/spec/features/projects/settings/webhooks_settings_spec.rb index ad434749198..adbf2f6ee5c 100644 --- a/spec/features/projects/settings/webhooks_settings_spec.rb +++ b/spec/features/projects/settings/webhooks_settings_spec.rb @@ -87,7 +87,7 @@ RSpec.describe 'Projects > Settings > Webhook Settings' do expect(page).to have_content('SSL Verification: enabled') expect(page).to have_content('Tag push events') expect(page).to have_content('Job events') - expect(page).to have_selector('.js-vue-push-events', visible: :all) + expect(page).to have_content('Push events') end end diff --git a/spec/frontend/access_tokens/components/new_access_token_app_spec.js b/spec/frontend/access_tokens/components/new_access_token_app_spec.js index b4af11169ad..e4313bdfa26 100644 --- a/spec/frontend/access_tokens/components/new_access_token_app_spec.js +++ b/spec/frontend/access_tokens/components/new_access_token_app_spec.js @@ -73,7 +73,6 @@ describe('~/access_tokens/components/new_access_token_app', () => { expect(InputCopyToggleVisibilityComponent.props('copyButtonTitle')).toBe( sprintf(__('Copy %{accessTokenType}'), { accessTokenType }), ); - expect(InputCopyToggleVisibilityComponent.props('initialVisibility')).toBe(true); expect(InputCopyToggleVisibilityComponent.attributes('label')).toBe( sprintf(__('Your new %{accessTokenType}'), { accessTokenType }), ); diff --git a/spec/frontend/webhooks/components/__snapshots__/push_events_spec.js.snap b/spec/frontend/webhooks/components/__snapshots__/push_events_spec.js.snap new file mode 100644 index 00000000000..3dbff024a6b --- /dev/null +++ b/spec/frontend/webhooks/components/__snapshots__/push_events_spec.js.snap @@ -0,0 +1,453 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Webhook push events form editor component Different push events rules when editing existing hook with "all_branches" strategy selected 1`] = ` +<gl-form-radio-group-stub + checked="all_branches" + disabledfield="disabled" + htmlfield="html" + name="hook[branch_filter_strategy]" + options="" + textfield="text" + valuefield="value" +> + <gl-form-radio-stub + class="gl-mt-2 branch-filter-strategy-radio" + data-testid="rule_all_branches" + value="all_branches" + > + <div + data-qa-selector="strategy_radio_all" + > + All branches + </div> + </gl-form-radio-stub> + + <gl-form-radio-stub + class="gl-mt-2 branch-filter-strategy-radio" + data-testid="rule_wildcard" + value="wildcard" + > + <div + data-qa-selector="strategy_radio_wildcard" + > + + Wildcard pattern + + </div> + </gl-form-radio-stub> + + <div + class="gl-ml-6" + > + <!----> + </div> + + <!----> + + <gl-form-radio-stub + class="gl-mt-2 branch-filter-strategy-radio" + data-testid="rule_regex" + value="regex" + > + <div + data-qa-selector="strategy_radio_regex" + > + + Regular expression + + </div> + </gl-form-radio-stub> + + <div + class="gl-ml-6" + > + <!----> + </div> + + <!----> +</gl-form-radio-group-stub> +`; + +exports[`Webhook push events form editor component Different push events rules when editing existing hook with "regex" strategy selected 1`] = ` +<gl-form-radio-group-stub + checked="regex" + disabledfield="disabled" + htmlfield="html" + name="hook[branch_filter_strategy]" + options="" + textfield="text" + valuefield="value" +> + <gl-form-radio-stub + class="gl-mt-2 branch-filter-strategy-radio" + data-testid="rule_all_branches" + value="all_branches" + > + <div + data-qa-selector="strategy_radio_all" + > + All branches + </div> + </gl-form-radio-stub> + + <gl-form-radio-stub + class="gl-mt-2 branch-filter-strategy-radio" + data-testid="rule_wildcard" + value="wildcard" + > + <div + data-qa-selector="strategy_radio_wildcard" + > + + Wildcard pattern + + </div> + </gl-form-radio-stub> + + <div + class="gl-ml-6" + > + <!----> + </div> + + <!----> + + <gl-form-radio-stub + class="gl-mt-2 branch-filter-strategy-radio" + data-testid="rule_regex" + value="regex" + > + <div + data-qa-selector="strategy_radio_regex" + > + + Regular expression + + </div> + </gl-form-radio-stub> + + <div + class="gl-ml-6" + > + <gl-form-input-stub + data-qa-selector="webhook_branch_filter_field" + data-testid="webhook_branch_filter_field" + name="hook[push_events_branch_filter]" + value="foo" + /> + </div> + + <p + class="form-text text-muted custom-control" + > + <gl-sprintf-stub + message="Regex such as %{REGEX_CODE} is supported." + /> + </p> +</gl-form-radio-group-stub> +`; + +exports[`Webhook push events form editor component Different push events rules when editing existing hook with "wildcard" strategy selected 1`] = ` +<gl-form-radio-group-stub + checked="wildcard" + disabledfield="disabled" + htmlfield="html" + name="hook[branch_filter_strategy]" + options="" + textfield="text" + valuefield="value" +> + <gl-form-radio-stub + class="gl-mt-2 branch-filter-strategy-radio" + data-testid="rule_all_branches" + value="all_branches" + > + <div + data-qa-selector="strategy_radio_all" + > + All branches + </div> + </gl-form-radio-stub> + + <gl-form-radio-stub + class="gl-mt-2 branch-filter-strategy-radio" + data-testid="rule_wildcard" + value="wildcard" + > + <div + data-qa-selector="strategy_radio_wildcard" + > + + Wildcard pattern + + </div> + </gl-form-radio-stub> + + <div + class="gl-ml-6" + > + <gl-form-input-stub + data-qa-selector="webhook_branch_filter_field" + data-testid="webhook_branch_filter_field" + name="hook[push_events_branch_filter]" + value="foo" + /> + </div> + + <p + class="form-text text-muted custom-control" + > + <gl-sprintf-stub + message="Wildcards such as %{WILDCARD_CODE_STABLE} or %{WILDCARD_CODE_PRODUCTION} are supported." + /> + </p> + + <gl-form-radio-stub + class="gl-mt-2 branch-filter-strategy-radio" + data-testid="rule_regex" + value="regex" + > + <div + data-qa-selector="strategy_radio_regex" + > + + Regular expression + + </div> + </gl-form-radio-stub> + + <div + class="gl-ml-6" + > + <!----> + </div> + + <!----> +</gl-form-radio-group-stub> +`; + +exports[`Webhook push events form editor component Different push events rules when editing new hook all_branches should be selected by default 1`] = ` +<gl-form-radio-group-stub + checked="all_branches" + disabledfield="disabled" + htmlfield="html" + name="hook[branch_filter_strategy]" + options="" + textfield="text" + valuefield="value" +> + <gl-form-radio-stub + class="gl-mt-2 branch-filter-strategy-radio" + data-testid="rule_all_branches" + value="all_branches" + > + <div + data-qa-selector="strategy_radio_all" + > + All branches + </div> + </gl-form-radio-stub> + + <gl-form-radio-stub + class="gl-mt-2 branch-filter-strategy-radio" + data-testid="rule_wildcard" + value="wildcard" + > + <div + data-qa-selector="strategy_radio_wildcard" + > + + Wildcard pattern + + </div> + </gl-form-radio-stub> + + <div + class="gl-ml-6" + > + <!----> + </div> + + <!----> + + <gl-form-radio-stub + class="gl-mt-2 branch-filter-strategy-radio" + data-testid="rule_regex" + value="regex" + > + <div + data-qa-selector="strategy_radio_regex" + > + + Regular expression + + </div> + </gl-form-radio-stub> + + <div + class="gl-ml-6" + > + <!----> + </div> + + <!----> +</gl-form-radio-group-stub> +`; + +exports[`Webhook push events form editor component Different push events rules when editing new hook should be able to set regex rule 1`] = ` +<gl-form-radio-group-stub + checked="regex" + disabledfield="disabled" + htmlfield="html" + name="hook[branch_filter_strategy]" + options="" + textfield="text" + valuefield="value" +> + <gl-form-radio-stub + class="gl-mt-2 branch-filter-strategy-radio" + data-testid="rule_all_branches" + value="all_branches" + > + <div + data-qa-selector="strategy_radio_all" + > + All branches + </div> + </gl-form-radio-stub> + + <gl-form-radio-stub + class="gl-mt-2 branch-filter-strategy-radio" + data-testid="rule_wildcard" + value="wildcard" + > + <div + data-qa-selector="strategy_radio_wildcard" + > + + Wildcard pattern + + </div> + </gl-form-radio-stub> + + <div + class="gl-ml-6" + > + <!----> + </div> + + <!----> + + <gl-form-radio-stub + class="gl-mt-2 branch-filter-strategy-radio" + data-testid="rule_regex" + value="regex" + > + <div + data-qa-selector="strategy_radio_regex" + > + + Regular expression + + </div> + </gl-form-radio-stub> + + <div + class="gl-ml-6" + > + <gl-form-input-stub + data-qa-selector="webhook_branch_filter_field" + data-testid="webhook_branch_filter_field" + name="hook[push_events_branch_filter]" + value="" + /> + </div> + + <p + class="form-text text-muted custom-control" + > + <gl-sprintf-stub + message="Regex such as %{REGEX_CODE} is supported." + /> + </p> +</gl-form-radio-group-stub> +`; + +exports[`Webhook push events form editor component Different push events rules when editing new hook should be able to set wildcard rule 1`] = ` +<gl-form-radio-group-stub + checked="wildcard" + disabledfield="disabled" + htmlfield="html" + name="hook[branch_filter_strategy]" + options="" + textfield="text" + valuefield="value" +> + <gl-form-radio-stub + class="gl-mt-2 branch-filter-strategy-radio" + data-testid="rule_all_branches" + value="all_branches" + > + <div + data-qa-selector="strategy_radio_all" + > + All branches + </div> + </gl-form-radio-stub> + + <gl-form-radio-stub + class="gl-mt-2 branch-filter-strategy-radio" + data-testid="rule_wildcard" + value="wildcard" + > + <div + data-qa-selector="strategy_radio_wildcard" + > + + Wildcard pattern + + </div> + </gl-form-radio-stub> + + <div + class="gl-ml-6" + > + <gl-form-input-stub + data-qa-selector="webhook_branch_filter_field" + data-testid="webhook_branch_filter_field" + name="hook[push_events_branch_filter]" + value="" + /> + </div> + + <p + class="form-text text-muted custom-control" + > + <gl-sprintf-stub + message="Wildcards such as %{WILDCARD_CODE_STABLE} or %{WILDCARD_CODE_PRODUCTION} are supported." + /> + </p> + + <gl-form-radio-stub + class="gl-mt-2 branch-filter-strategy-radio" + data-testid="rule_regex" + value="regex" + > + <div + data-qa-selector="strategy_radio_regex" + > + + Regular expression + + </div> + </gl-form-radio-stub> + + <div + class="gl-ml-6" + > + <!----> + </div> + + <!----> +</gl-form-radio-group-stub> +`; diff --git a/spec/frontend/webhooks/components/push_events_spec.js b/spec/frontend/webhooks/components/push_events_spec.js new file mode 100644 index 00000000000..ccb61c4049a --- /dev/null +++ b/spec/frontend/webhooks/components/push_events_spec.js @@ -0,0 +1,117 @@ +import { nextTick } from 'vue'; +import { GlFormCheckbox, GlFormRadioGroup } from '@gitlab/ui'; +import { shallowMountExtended } from 'helpers/vue_test_utils_helper'; +import PushEvents from '~/webhooks/components/push_events.vue'; + +describe('Webhook push events form editor component', () => { + let wrapper; + + const findPushEventsCheckBox = (w = wrapper) => w.findComponent(GlFormCheckbox); + const findPushEventsIndicator = (w = wrapper) => w.find('input[name="hook[push_events]"]'); + const findPushEventRulesGroup = (w = wrapper) => w.findComponent(GlFormRadioGroup); + const getPushEventsRuleValue = (w = wrapper) => findPushEventRulesGroup(w).vm.$attrs.checked; + const findWildcardRuleInput = (w = wrapper) => w.findByTestId('webhook_branch_filter_field'); + const findRegexRuleInput = (w = wrapper) => w.findByTestId('webhook_branch_filter_field'); + + const createComponent = (provides) => + shallowMountExtended(PushEvents, { + provide: { + isNewHook: true, + pushEvents: false, + strategy: 'wildcard', + pushEventsBranchFilter: '', + ...provides, + }, + }); + + describe('Renders push events checkbox', () => { + it('when it is a new hook', async () => { + wrapper = createComponent({ + isNewHook: true, + }); + await nextTick(); + + const checkbox = findPushEventsCheckBox(); + expect(checkbox.exists()).toBe(true); + expect(findPushEventRulesGroup().exists()).toBe(false); + expect(findPushEventsIndicator().attributes('value')).toBe('false'); + }); + + it('when it is not a new hook and push events is enabled', async () => { + wrapper = createComponent({ + isNewHook: false, + pushEvents: true, + }); + await nextTick(); + + expect(findPushEventsCheckBox().exists()).toBe(true); + expect(findPushEventRulesGroup().exists()).toBe(true); + expect(findPushEventsIndicator().attributes('value')).toBe('true'); + }); + }); + + describe('Different push events rules', () => { + describe('when editing new hook', () => { + beforeEach(async () => { + wrapper = createComponent({ + isNewHook: true, + }); + await nextTick(); + await findPushEventsCheckBox().vm.$emit('input', true); + await nextTick(); + }); + + it('all_branches should be selected by default', async () => { + expect(findPushEventRulesGroup().element).toMatchSnapshot(); + }); + + it('should be able to set wildcard rule', async () => { + expect(getPushEventsRuleValue()).toBe('all_branches'); + expect(findWildcardRuleInput().exists()).toBe(false); + expect(findRegexRuleInput().exists()).toBe(false); + + await findPushEventRulesGroup(wrapper).vm.$emit('input', 'wildcard'); + expect(findWildcardRuleInput().exists()).toBe(true); + expect(findPushEventRulesGroup().element).toMatchSnapshot(); + + const testVal = 'test-val'; + findWildcardRuleInput().vm.$emit('input', testVal); + await nextTick(); + expect(findWildcardRuleInput().attributes('value')).toBe(testVal); + }); + + it('should be able to set regex rule', async () => { + expect(getPushEventsRuleValue()).toBe('all_branches'); + expect(findRegexRuleInput().exists()).toBe(false); + expect(findWildcardRuleInput().exists()).toBe(false); + + await findPushEventRulesGroup(wrapper).vm.$emit('input', 'regex'); + expect(findRegexRuleInput().exists()).toBe(true); + expect(findPushEventRulesGroup().element).toMatchSnapshot(); + + const testVal = 'test-val'; + findRegexRuleInput().vm.$emit('input', testVal); + await nextTick(); + expect(findRegexRuleInput().attributes('value')).toBe(testVal); + }); + }); + + describe('when editing existing hook', () => { + it.each(['all_branches', 'wildcard', 'regex'])( + 'with "%s" strategy selected', + async (strategy) => { + wrapper = createComponent({ + isNewHook: false, + pushEvents: true, + pushEventsBranchFilter: 'foo', + strategy, + }); + await nextTick(); + + expect(findPushEventsIndicator().attributes('value')).toBe('true'); + expect(findPushEventRulesGroup().element).toMatchSnapshot(); + }, + ); + }); + }); +}); diff --git a/spec/helpers/events_helper_spec.rb b/spec/helpers/events_helper_spec.rb index 7005b3dc53e..39901047b0f 100644 --- a/spec/helpers/events_helper_spec.rb +++ b/spec/helpers/events_helper_spec.rb @@ -4,6 +4,30 @@ require 'spec_helper' RSpec.describe EventsHelper do include Gitlab::Routing + include Banzai::Filter::OutputSafety + + describe '#link_to_author' do + let(:user) { create(:user) } + let(:event) { create(:event, author: user) } + + it 'returns a link to the author' do + name = user.name + expect(helper.link_to_author(event)).to eq(link_to(name, user_path(user.username), title: name)) + end + + it 'returns the author name if the author is not present' do + event.author = nil + + expect(helper.link_to_author(event)).to eq(escape_once(event.author_name)) + end + + it 'returns "You" if the author is the current user' do + allow(helper).to receive(:current_user).and_return(user) + + name = _('You') + expect(helper.link_to_author(event, self_added: true)).to eq(link_to(name, user_path(user.username), title: name)) + end + end describe '#event_target_path' do subject { helper.event_target_path(event.present) } diff --git a/spec/helpers/integrations_helper_spec.rb b/spec/helpers/integrations_helper_spec.rb index 95dfc51e8fd..3e5ec9b5348 100644 --- a/spec/helpers/integrations_helper_spec.rb +++ b/spec/helpers/integrations_helper_spec.rb @@ -150,4 +150,54 @@ RSpec.describe IntegrationsHelper do end end end + + describe '#integration_issue_type' do + using RSpec::Parameterized::TableSyntax + let_it_be(:issue) { create(:issue) } + + where(:issue_type, :expected_i18n_issue_type) do + "issue" | _('Issue') + "incident" | _('Incident') + "test_case" | _('Test case') + "requirement" | _('Requirement') + "task" | _('Task') + end + + with_them do + before do + issue.update!(issue_type: issue_type) + end + + it "return the correct i18n issue type" do + expect(described_class.integration_issue_type(issue.issue_type)).to eq(expected_i18n_issue_type) + end + end + + it "only consider these enumeration values are valid" do + expected_valid_types = %w[issue incident test_case requirement task] + expect(Issue.issue_types.keys).to contain_exactly(*expected_valid_types) + end + end + + describe '#integration_todo_target_type' do + using RSpec::Parameterized::TableSyntax + let!(:todo) { create(:todo, commit_id: '123') } + + where(:target_type, :expected_i18n_target_type) do + "Commit" | _("Commit") + "Issue" | _("Issue") + "MergeRequest" | _("Merge Request") + 'Epic' | _('Epic') + DesignManagement::Design.name | _('design') + AlertManagement::Alert.name | _('alert') + end + + with_them do + before do + todo.update!(target_type: target_type) + end + + it { expect(described_class.integration_todo_target_type(todo.target_type)).to eq(expected_i18n_target_type) } + end + end end diff --git a/spec/helpers/todos_helper_spec.rb b/spec/helpers/todos_helper_spec.rb index c64d5990cd9..49e3c6dd740 100644 --- a/spec/helpers/todos_helper_spec.rb +++ b/spec/helpers/todos_helper_spec.rb @@ -310,4 +310,33 @@ RSpec.describe TodosHelper do it { expect(helper.todos_filter_params[:state]).to eq(result) } end end + + describe '#todo_action_name' do + using RSpec::Parameterized::TableSyntax + + where(:action, :self_added?, :expected_action_name) do + Todo::ASSIGNED | false | s_('Todos|assigned you') + Todo::ASSIGNED | true | s_('Todos|assigned') + Todo::REVIEW_REQUESTED | true | s_('Todos|requested a review of') + Todo::MENTIONED | true | format(s_("Todos|mentioned %{who} on"), who: s_('Todos|yourself')) + Todo::MENTIONED | false | format(s_("Todos|mentioned %{who} on"), who: _('you')) + Todo::DIRECTLY_ADDRESSED | true | format(s_("Todos|mentioned %{who} on"), who: s_('Todos|yourself')) + Todo::DIRECTLY_ADDRESSED | false | format(s_("Todos|mentioned %{who} on"), who: _('you')) + Todo::BUILD_FAILED | true | s_('Todos|The pipeline failed in') + Todo::MARKED | true | s_('Todos|added a todo for') + Todo::APPROVAL_REQUIRED | true | format(s_("Todos|set %{who} as an approver for"), who: s_('Todos|yourself')) + Todo::APPROVAL_REQUIRED | false | format(s_("Todos|set %{who} as an approver for"), who: _('you')) + Todo::UNMERGEABLE | true | s_('Todos|Could not merge') + Todo::MERGE_TRAIN_REMOVED | true | s_("Todos|Removed from Merge Train:") + end + + with_them do + before do + alert_todo.action = action + alert_todo.user = self_added? ? alert_todo.author : user + end + + it { expect(helper.todo_action_name(alert_todo)).to eq(expected_action_name) } + end + end end diff --git a/spec/lib/banzai/filter/math_filter_spec.rb b/spec/lib/banzai/filter/math_filter_spec.rb index dd116eb1109..350ac1edbe1 100644 --- a/spec/lib/banzai/filter/math_filter_spec.rb +++ b/spec/lib/banzai/filter/math_filter_spec.rb @@ -97,7 +97,8 @@ RSpec.describe Banzai::Filter::MathFilter do describe 'block display math using $$\n...\n$$ syntax' do context 'with valid syntax' do where(:text, :result_template) do - "$$\n2+2\n$$" | "<math>2+2</math>" + "$$\n2+2\n$$" | "<math>2+2</math>" + "$$\n2+2\n3+4\n$$" | "<math>2+2\n3+4</math>" end with_them do diff --git a/spec/migrations/finalize_invalid_member_cleanup_spec.rb b/spec/migrations/finalize_invalid_member_cleanup_spec.rb new file mode 100644 index 00000000000..a29a89c2396 --- /dev/null +++ b/spec/migrations/finalize_invalid_member_cleanup_spec.rb @@ -0,0 +1,72 @@ +# frozen_string_literal: true + +require 'spec_helper' +require_migration! + +RSpec.describe FinalizeInvalidMemberCleanup, :migration do + let(:batched_migrations) { table(:batched_background_migrations) } + + let_it_be(:migration) { described_class::MIGRATION } + + describe '#up' do + shared_examples 'finalizes the migration' do + it 'finalizes the migration' do + allow_next_instance_of(Gitlab::Database::BackgroundMigration::BatchedMigrationRunner) do |runner| + expect(runner).to receive(:finalize).with('DestroyInvalidMembers', :members, :id, []) + end + end + end + + context 'when migration is missing' do + it 'warns migration not found' do + expect(Gitlab::AppLogger) + .to receive(:warn).with(/Could not find batched background migration for the given configuration:/) + + migrate! + end + end + + context 'with migration present' do + let!(:destroy_invalid_member_migration) do + batched_migrations.create!( + job_class_name: 'DestroyInvalidMembers', + table_name: :members, + column_name: :id, + job_arguments: [], + interval: 2.minutes, + min_value: 1, + max_value: 2, + batch_size: 1000, + sub_batch_size: 200, + gitlab_schema: :gitlab_main, + status: 3 # finished + ) + end + + context 'when migration finished successfully' do + it 'does not raise exception' do + expect { migrate! }.not_to raise_error + end + end + + context 'with different migration statuses' do + using RSpec::Parameterized::TableSyntax + + where(:status, :description) do + 0 | 'paused' + 1 | 'active' + 4 | 'failed' + 5 | 'finalizing' + end + + with_them do + before do + destroy_invalid_member_migration.update!(status: status) + end + + it_behaves_like 'finalizes the migration' + end + end + end + end +end diff --git a/spec/models/ci/build_runner_session_spec.rb b/spec/models/ci/build_runner_session_spec.rb index ed5ed456d7b..9bb8a1bd626 100644 --- a/spec/models/ci/build_runner_session_spec.rb +++ b/spec/models/ci/build_runner_session_spec.rb @@ -72,7 +72,7 @@ RSpec.describe Ci::BuildRunnerSession, model: true do let(:specification) { subject.service_specification(service: service, port: port, path: path, subprotocols: subprotocols) } it 'returns service proxy url' do - expect(specification[:url]).to eq "https://localhost/proxy/#{service}/#{port}/#{path}" + expect(specification[:url]).to eq "https://gitlab.example.com/proxy/#{service}/#{port}/#{path}" end it 'returns default service proxy websocket subprotocol' do @@ -89,7 +89,7 @@ RSpec.describe Ci::BuildRunnerSession, model: true do let(:port) { nil } it 'uses the default port name' do - expect(specification[:url]).to eq "https://localhost/proxy/#{service}/default_port/#{path}" + expect(specification[:url]).to eq "https://gitlab.example.com/proxy/#{service}/default_port/#{path}" end end @@ -97,7 +97,7 @@ RSpec.describe Ci::BuildRunnerSession, model: true do let(:service) { '' } it 'uses the service name "build" as default' do - expect(specification[:url]).to eq "https://localhost/proxy/build/#{port}/#{path}" + expect(specification[:url]).to eq "https://gitlab.example.com/proxy/build/#{port}/#{path}" end end diff --git a/spec/support/helpers/features/access_token_helpers.rb b/spec/support/helpers/features/access_token_helpers.rb new file mode 100644 index 00000000000..f4bdb70c160 --- /dev/null +++ b/spec/support/helpers/features/access_token_helpers.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true +module Spec + module Support + module Helpers + module AccessTokenHelpers + def active_access_tokens + find("[data-testid='active-tokens']") + end + + def created_access_token + within('[data-testid=access-token-section]') do + find('[data-testid=toggle-visibility-button]').click + find_field('new-access-token').value + end + end + end + end + end +end diff --git a/spec/support/shared_examples/features/access_tokens_shared_examples.rb b/spec/support/shared_examples/features/access_tokens_shared_examples.rb index cd255abd7a8..4940c0d04cc 100644 --- a/spec/support/shared_examples/features/access_tokens_shared_examples.rb +++ b/spec/support/shared_examples/features/access_tokens_shared_examples.rb @@ -9,13 +9,7 @@ RSpec.shared_examples 'resource access tokens missing access rights' do end RSpec.shared_examples 'resource access tokens creation' do |resource_type| - def active_resource_access_tokens - find("[data-testid='active-tokens']") - end - - def created_resource_access_token - find_field('new-access-token').value - end + include Spec::Support::Helpers::AccessTokenHelpers it 'allows creation of an access token', :aggregate_failures do name = 'My access token' @@ -34,12 +28,12 @@ RSpec.shared_examples 'resource access tokens creation' do |resource_type| click_on "Create #{resource_type} access token" - expect(active_resource_access_tokens).to have_text(name) - expect(active_resource_access_tokens).to have_text('in') - expect(active_resource_access_tokens).to have_text('read_api') - expect(active_resource_access_tokens).to have_text('read_repository') - expect(active_resource_access_tokens).to have_text('Guest') - expect(created_resource_access_token).not_to be_empty + expect(active_access_tokens).to have_text(name) + expect(active_access_tokens).to have_text('in') + expect(active_access_tokens).to have_text('read_api') + expect(active_access_tokens).to have_text('read_repository') + expect(active_access_tokens).to have_text('Guest') + expect(created_access_token).to match(/[\w-]{20}/) end end diff --git a/workhorse/go.mod b/workhorse/go.mod index 28d1eb5f975..3b2a4b1fc70 100644 --- a/workhorse/go.mod +++ b/workhorse/go.mod @@ -25,7 +25,7 @@ require ( github.com/sebest/xff v0.0.0-20210106013422-671bd2870b3a github.com/sirupsen/logrus v1.9.0 github.com/smartystreets/goconvey v1.7.2 - github.com/stretchr/testify v1.8.0 + github.com/stretchr/testify v1.8.1 gitlab.com/gitlab-org/gitaly/v15 v15.4.2 gitlab.com/gitlab-org/golang-archive-zip v0.1.1 gitlab.com/gitlab-org/labkit v1.16.0 diff --git a/workhorse/go.sum b/workhorse/go.sum index c4f8f62c551..91b92d55bc6 100644 --- a/workhorse/go.sum +++ b/workhorse/go.sum @@ -896,8 +896,9 @@ github.com/ssgelm/cookiejarparser v1.0.1/go.mod h1:DUfC0mpjIzlDN7DzKjXpHj0qMI5m9 github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= -github.com/stretchr/objx v0.4.0 h1:M2gUjqZET1qApGOWNSnZ49BAIMX4F/1plDv3+l31EJ4= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= @@ -906,8 +907,9 @@ github.com/stretchr/testify v1.6.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= github.com/tinylib/msgp v1.1.2 h1:gWmO7n0Ys2RBEb7GPYB9Ujq8Mk5p2U08lRnmMcGy6BQ= github.com/tinylib/msgp v1.1.2/go.mod h1:+d+yLhGm8mzTaHzB+wgMYrodPfmZrzkirds8fDWklFE= diff --git a/yarn.lock b/yarn.lock index f9063cf1e62..ed6bdf1fa74 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3903,10 +3903,10 @@ core-js-pure@^3.0.0: resolved "https://registry.yarnpkg.com/core-js-pure/-/core-js-pure-3.6.5.tgz#c79e75f5e38dbc85a662d91eea52b8256d53b813" integrity sha512-lacdXOimsiD0QyNf9BC/mxivNJ/ybBGJXQFKzRekp1WTHoVUWsUHEn+2T8GJAzzIhyOuXA+gOxCVN3l+5PLPUA== -core-js@^3.25.5: - version "3.25.5" - resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.25.5.tgz#e86f651a2ca8a0237a5f064c2fe56cef89646e27" - integrity sha512-nbm6eZSjm+ZuBQxCUPQKQCoUEfFOXjUZ8dTTyikyKaWrTYmAVbykQfwsKE5dBK88u3QCkCrzsx/PPlKfhsvgpw== +core-js@^3.26.0: + version "3.26.0" + resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.26.0.tgz#a516db0ed0811be10eac5d94f3b8463d03faccfe" + integrity sha512-+DkDrhoR4Y0PxDz6rurahuB+I45OsEUv8E1maPTB6OuHRohMMcznBq9TMpdpDMm/hUPob/mJJS3PqgbHpMTQgw== core-util-is@~1.0.0: version "1.0.3" |