diff options
23 files changed, 298 insertions, 358 deletions
diff --git a/app/assets/javascripts/boards/components/board_add_new_column_form.vue b/app/assets/javascripts/boards/components/board_add_new_column_form.vue index 10c7a3db2d3..c4a2f83ab50 100644 --- a/app/assets/javascripts/boards/components/board_add_new_column_form.vue +++ b/app/assets/javascripts/boards/components/board_add_new_column_form.vue @@ -19,6 +19,7 @@ export default { scope: __('Scope'), scopeDescription: __('Issues must match this scope to appear in this list.'), selected: __('Selected'), + requiredFieldFeedback: __('This field is required.'), }, components: { GlButton, @@ -55,12 +56,21 @@ export default { data() { return { searchValue: '', + selectedIdValid: true, }; }, + computed: { + toggleClassList() { + return `gl-max-w-full gl-display-flex gl-align-items-center gl-text-trunate ${ + this.selectedIdValid ? '' : 'gl-inset-border-1-red-400!' + }`; + }, + }, watch: { selectedId(val) { if (val) { this.$refs.dropdown.hide(true); + this.selectedIdValid = true; } }, }, @@ -74,6 +84,13 @@ export default { this.$emit('filter-items', ''); this.$emit('hide'); }, + onSubmit() { + if (!this.selectedId) { + this.selectedIdValid = false; + } else { + this.$emit('add-list'); + } + }, }, }; </script> @@ -103,11 +120,16 @@ export default { <slot name="select-list-type"></slot> - <gl-form-group class="gl-px-5 lg-mb-3 gl-max-w-full" :label="searchLabel"> + <gl-form-group + class="gl-px-5 lg-mb-3 gl-max-w-full" + :label="searchLabel" + :state="selectedIdValid" + :invalid-feedback="$options.i18n.requiredFieldFeedback" + > <gl-dropdown ref="dropdown" class="gl-mb-3 gl-max-w-full" - toggle-class="gl-max-w-full gl-display-flex gl-align-items-center gl-text-trunate" + :toggle-class="toggleClassList" boundary="viewport" @shown="setFocus" @hide="onHide" @@ -147,10 +169,9 @@ export default { <div class="gl-display-flex gl-mb-4"> <gl-button data-testid="addNewColumnButton" - :disabled="!selectedId" variant="confirm" class="gl-mr-3 gl-ml-4" - @click="$emit('add-list')" + @click="onSubmit" >{{ $options.i18n.add }}</gl-button > <gl-button data-testid="cancelAddNewColumn" @click="setAddColumnFormVisibility(false)">{{ diff --git a/app/assets/javascripts/pipelines/components/test_reports/test_reports.vue b/app/assets/javascripts/pipelines/components/test_reports/test_reports.vue index 3fb46a4f128..13e942e55d3 100644 --- a/app/assets/javascripts/pipelines/components/test_reports/test_reports.vue +++ b/app/assets/javascripts/pipelines/components/test_reports/test_reports.vue @@ -74,7 +74,7 @@ export default { <div v-else-if="!isLoading && showTests" ref="container" - class="position-relative" + class="gl-relative" data-testid="tests-detail" > <transition @@ -82,13 +82,13 @@ export default { @before-enter="beforeEnterTransition" @after-leave="afterLeaveTransition" > - <div v-if="showSuite" key="detail" class="w-100 slide-enter-to-element"> + <div v-if="showSuite" key="detail" class="gl-w-full slide-enter-to-element"> <test-summary :report="getSelectedSuite" show-back @on-back-click="summaryBackClick" /> <test-suite-table /> </div> - <div v-else key="summary" class="w-100 slide-enter-from-element"> + <div v-else key="summary" class="gl-w-full slide-enter-from-element"> <test-summary :report="testReports" /> <test-summary-table @row-click="summaryTableRowClick" /> diff --git a/app/assets/javascripts/pipelines/components/test_reports/test_suite_table.vue b/app/assets/javascripts/pipelines/components/test_reports/test_suite_table.vue index 1f438c63fee..7d0f1ba4b5f 100644 --- a/app/assets/javascripts/pipelines/components/test_reports/test_suite_table.vue +++ b/app/assets/javascripts/pipelines/components/test_reports/test_suite_table.vue @@ -80,7 +80,10 @@ export default { <h4>{{ heading }}</h4> </div> </div> - <div role="row" class="gl-responsive-table-row table-row-header font-weight-bold fgray"> + <div + role="row" + class="gl-responsive-table-row table-row-header gl-font-weight-bold gl-fill-gray-700" + > <div role="rowheader" class="table-section section-20"> {{ __('Suite') }} </div> @@ -104,7 +107,7 @@ export default { <div v-for="(testCase, index) in getSuiteTests" :key="index" - class="gl-responsive-table-row rounded align-items-md-start" + class="gl-responsive-table-row gl-rounded-base gl-align-items-flex-start" data-testid="test-case-row" > <div class="table-section section-20 section-wrap"> @@ -142,11 +145,8 @@ export default { <div class="table-section section-10 section-wrap"> <div role="rowheader" class="table-mobile-header">{{ __('Status') }}</div> - <div class="table-mobile-content text-center"> - <div - class="ci-status-icon d-flex align-items-center justify-content-end justify-content-md-center" - :class="`ci-status-icon-${testCase.status}`" - > + <div class="table-mobile-content gl-md-display-flex gl-justify-content-center"> + <div class="ci-status-icon" :class="`ci-status-icon-${testCase.status}`"> <gl-icon :size="24" :name="testCase.icon" /> </div> </div> @@ -156,7 +156,7 @@ export default { <div role="rowheader" class="table-mobile-header"> {{ __('Duration') }} </div> - <div class="table-mobile-content pr-sm-1"> + <div class="table-mobile-content gl-sm-pr-2"> {{ testCase.formattedTime }} </div> </div> diff --git a/app/assets/javascripts/pipelines/components/test_reports/test_summary.vue b/app/assets/javascripts/pipelines/components/test_reports/test_summary.vue index 2f5301715c3..6b723ad5481 100644 --- a/app/assets/javascripts/pipelines/components/test_reports/test_summary.vue +++ b/app/assets/javascripts/pipelines/components/test_reports/test_summary.vue @@ -65,58 +65,53 @@ export default { <template> <div> - <div class="row"> - <div class="col-12 d-flex gl-mt-3 align-items-center"> - <gl-button - v-if="showBack" - size="small" - class="gl-mr-3 js-back-button" - icon="chevron-lg-left" - :aria-label="__('Go back')" - @click="onBackClick" - /> + <div class="gl-w-full gl-display-flex gl-mt-3 gl-align-items-center"> + <gl-button + v-if="showBack" + size="small" + class="gl-mr-3 js-back-button" + icon="chevron-lg-left" + :aria-label="__('Go back')" + @click="onBackClick" + /> - <h4>{{ heading }}</h4> - </div> + <h4>{{ heading }}</h4> </div> - <div class="row mt-2"> - <div class="col-4 col-md"> - <span class="js-total-tests">{{ + <div + class="gl-display-flex gl-flex-direction-column gl-md-flex-direction-row gl-w-full gl-mt-3" + > + <div class="gl-display-flex gl-justify-content-space-between gl-flex-basis-half"> + <span class="js-total-tests gl-flex-grow-1">{{ sprintf(s__('TestReports|%{count} tests'), { count: report.total_count }) }}</span> - </div> - <div class="col-4 col-md text-center text-md-center"> - <span class="js-failed-tests">{{ + <span class="js-failed-tests gl-flex-grow-1">{{ sprintf(s__('TestReports|%{count} failures'), { count: report.failed_count }) }}</span> - </div> - <div class="col-4 col-md text-right text-md-center"> <span class="js-errored-tests">{{ sprintf(s__('TestReports|%{count} errors'), { count: report.error_count }) }}</span> </div> - - <div class="col-6 mt-3 col-md mt-md-0 text-md-center"> - <span class="js-success-rate">{{ + <div class="gl-display-flex gl-justify-content-space-between gl-flex-grow-1"> + <div class="gl-display-none gl-md-display-block gl-flex-grow-1"></div> + <span class="js-success-rate gl-flex-grow-1">{{ sprintf(s__('TestReports|%{rate}%{sign} success rate'), { rate: successPercentage, sign: '%', }) }}</span> - </div> - <div class="col-6 mt-3 col-md mt-md-0 text-right"> <span class="js-duration">{{ formattedDuration }}</span> </div> </div> - <div class="row mt-3"> - <div class="col-12"> - <gl-progress-bar :value="successPercentage" :variant="progressBarVariant" height="10px" /> - </div> - </div> + <gl-progress-bar + class="gl-mt-5" + :value="successPercentage" + :variant="progressBarVariant" + height="10px" + /> </div> </template> diff --git a/app/assets/javascripts/pipelines/components/test_reports/test_summary_table.vue b/app/assets/javascripts/pipelines/components/test_reports/test_summary_table.vue index 8389c2a5104..7ab48da1a9d 100644 --- a/app/assets/javascripts/pipelines/components/test_reports/test_summary_table.vue +++ b/app/assets/javascripts/pipelines/components/test_reports/test_summary_table.vue @@ -34,33 +34,31 @@ export default { <template> <div> - <div class="row gl-mt-3"> - <div class="col-12"> - <h4>{{ heading }}</h4> - </div> + <div class="gl-mt-5"> + <h4>{{ heading }}</h4> </div> - <div v-if="hasSuites" class="test-reports-table gl-mb-3 js-test-suites-table"> - <div role="row" class="gl-responsive-table-row table-row-header font-weight-bold"> - <div role="rowheader" class="table-section section-25 pl-3"> + <div v-if="hasSuites" class="js-test-suites-table"> + <div role="row" class="gl-responsive-table-row table-row-header gl-font-weight-bold"> + <div role="rowheader" class="table-section section-25 gl-pl-5"> {{ __('Job') }} </div> <div role="rowheader" class="table-section section-25"> {{ __('Duration') }} </div> - <div role="rowheader" class="table-section section-10 text-center"> + <div role="rowheader" class="table-section section-10 gl-text-center"> {{ __('Failed') }} </div> - <div role="rowheader" class="table-section section-10 text-center"> + <div role="rowheader" class="table-section section-10 gl-text-center"> {{ __('Errors'), }} </div> - <div role="rowheader" class="table-section section-10 text-center"> + <div role="rowheader" class="table-section section-10 gl-text-center"> {{ __('Skipped'), }} </div> - <div role="rowheader" class="table-section section-10 text-center"> + <div role="rowheader" class="table-section section-10 gl-text-center"> {{ __('Passed'), }} </div> - <div role="rowheader" class="table-section section-10 pr-3 text-right"> + <div role="rowheader" class="table-section section-10 gl-pr-5 gl-text-right"> {{ __('Total') }} </div> </div> @@ -69,17 +67,17 @@ export default { v-for="(testSuite, index) in getTestSuites" :key="index" role="row" - class="gl-responsive-table-row test-reports-summary-row rounded js-suite-row" + class="gl-responsive-table-row gl-rounded-base js-suite-row" :class="{ - 'gl-responsive-table-row-clickable cursor-pointer': !testSuite.suite_error, + 'gl-responsive-table-row-clickable gl-cursor-pointer': !testSuite.suite_error, }" @click="tableRowClick(index)" > <div class="table-section section-25"> - <div role="rowheader" class="table-mobile-header font-weight-bold"> + <div role="rowheader" class="table-mobile-header gl-font-weight-bold"> {{ __('Suite') }} </div> - <div class="table-mobile-content underline cgray pl-3"> + <div class="table-mobile-content underline gl-text-gray-900 gl-pl-5"> {{ testSuite.name }} <gl-icon v-if="testSuite.suite_error" @@ -93,44 +91,44 @@ export default { </div> <div class="table-section section-25"> - <div role="rowheader" class="table-mobile-header font-weight-bold"> + <div role="rowheader" class="table-mobile-header gl-font-weight-bold"> {{ __('Duration') }} </div> - <div class="table-mobile-content text-md-left"> + <div class="table-mobile-content gl-text-left"> {{ testSuite.formattedTime }} </div> </div> - <div class="table-section section-10 text-center"> - <div role="rowheader" class="table-mobile-header font-weight-bold"> + <div class="table-section section-10 gl-text-center"> + <div role="rowheader" class="table-mobile-header gl-font-weight-bold"> {{ __('Failed') }} </div> <div class="table-mobile-content">{{ testSuite.failed_count }}</div> </div> - <div class="table-section section-10 text-center"> - <div role="rowheader" class="table-mobile-header font-weight-bold"> + <div class="table-section section-10 gl-text-center"> + <div role="rowheader" class="table-mobile-header gl-font-weight-bold"> {{ __('Errors') }} </div> <div class="table-mobile-content">{{ testSuite.error_count }}</div> </div> - <div class="table-section section-10 text-center"> - <div role="rowheader" class="table-mobile-header font-weight-bold"> + <div class="table-section section-10 gl-text-center"> + <div role="rowheader" class="table-mobile-header gl-font-weight-bold"> {{ __('Skipped') }} </div> <div class="table-mobile-content">{{ testSuite.skipped_count }}</div> </div> - <div class="table-section section-10 text-center"> - <div role="rowheader" class="table-mobile-header font-weight-bold"> + <div class="table-section section-10 gl-text-center"> + <div role="rowheader" class="table-mobile-header gl-font-weight-bold"> {{ __('Passed') }} </div> <div class="table-mobile-content">{{ testSuite.success_count }}</div> </div> - <div class="table-section section-10 text-right pr-md-3"> - <div role="rowheader" class="table-mobile-header font-weight-bold"> + <div class="table-section section-10 gl-text-right pr-md-3"> + <div role="rowheader" class="table-mobile-header gl-font-weight-bold"> {{ __('Total') }} </div> <div class="table-mobile-content">{{ testSuite.total_count }}</div> diff --git a/app/assets/javascripts/runner/graphql/list/list_item_shared.fragment.graphql b/app/assets/javascripts/runner/graphql/list/list_item_shared.fragment.graphql index cf925359ffb..5e873135480 100644 --- a/app/assets/javascripts/runner/graphql/list/list_item_shared.fragment.graphql +++ b/app/assets/javascripts/runner/graphql/list/list_item_shared.fragment.graphql @@ -5,7 +5,6 @@ fragment ListItemShared on CiRunner { runnerType shortSha version - revision ipAddress active locked diff --git a/app/assets/stylesheets/page_bundles/pipeline.scss b/app/assets/stylesheets/page_bundles/pipeline.scss index e6afc70acbb..59d2c1bb6e6 100644 --- a/app/assets/stylesheets/page_bundles/pipeline.scss +++ b/app/assets/stylesheets/page_bundles/pipeline.scss @@ -225,12 +225,6 @@ } } -.test-reports-table { - .build-log { - @include build-log(); - } -} - .progress-bar.bg-primary { background-color: var(--blue-500, $blue-500) !important; } diff --git a/app/models/hooks/web_hook.rb b/app/models/hooks/web_hook.rb index f428d07cd7f..925a8628196 100644 --- a/app/models/hooks/web_hook.rb +++ b/app/models/hooks/web_hook.rb @@ -57,14 +57,14 @@ class WebHook < ApplicationRecord !temporarily_disabled? && !permanently_disabled? end - def temporarily_disabled?(ignore_flag: false) - return false unless ignore_flag || web_hooks_disable_failed? + def temporarily_disabled? + return false unless web_hooks_disable_failed? disabled_until.present? && disabled_until >= Time.current end - def permanently_disabled?(ignore_flag: false) - return false unless ignore_flag || web_hooks_disable_failed? + def permanently_disabled? + return false unless web_hooks_disable_failed? recent_failures > FAILURE_THRESHOLD end @@ -126,13 +126,6 @@ class WebHook < ApplicationRecord save(validate: false) end - def active_state(ignore_flag: false) - return :permanently_disabled if permanently_disabled?(ignore_flag: ignore_flag) - return :temporarily_disabled if temporarily_disabled?(ignore_flag: ignore_flag) - - :enabled - end - # @return [Boolean] Whether or not the WebHook is currently throttled. def rate_limited? rate_limiter.rate_limited? diff --git a/app/services/web_hooks/log_execution_service.rb b/app/services/web_hooks/log_execution_service.rb index 17dcf615830..5be8aee3ae8 100644 --- a/app/services/web_hooks/log_execution_service.rb +++ b/app/services/web_hooks/log_execution_service.rb @@ -14,7 +14,6 @@ module WebHooks @hook = hook @log_data = log_data.transform_keys(&:to_sym) @response_category = response_category - @prev_state = hook.active_state(ignore_flag: true) end def execute @@ -43,36 +42,12 @@ module WebHooks hook.failed! end - log_state_change hook.update_last_failure end rescue Gitlab::ExclusiveLeaseHelpers::FailedToObtainLockError raise if raise_lock_error? end - def log_state_change - new_state = hook.active_state(ignore_flag: true) - - return if @prev_state == new_state - - Gitlab::AuthLogger.info( - message: 'WebHook change active_state', - # identification - hook_id: hook.id, - hook_type: hook.type, - project_id: hook.project_id, - group_id: hook.group_id, - # relevant data - prev_state: @prev_state, - new_state: new_state, - duration: log_data[:execution_duration], - response_status: log_data[:response_status], - recent_hook_failures: hook.recent_failures, - # context - **Gitlab::ApplicationContext.current - ) - end - def lock_name "web_hooks:update_hook_failure_state:#{hook.id}" end diff --git a/app/views/projects/merge_requests/_close_reopen_draft_report_toggle.html.haml b/app/views/projects/merge_requests/_close_reopen_draft_report_toggle.html.haml index 6b367c735c3..62cd8bd94e3 100644 --- a/app/views/projects/merge_requests/_close_reopen_draft_report_toggle.html.haml +++ b/app/views/projects/merge_requests/_close_reopen_draft_report_toggle.html.haml @@ -34,9 +34,10 @@ = display_issuable_type - unless current_controller?('conflicts') - - if current_user && moved_mr_sidebar_enabled? && !@merge_request.merged? - %li.gl-new-dropdown-divider - %hr.dropdown-divider + - if current_user && moved_mr_sidebar_enabled? + - if !@merge_request.merged? + %li.gl-new-dropdown-divider + %hr.dropdown-divider %li.gl-new-dropdown-item.js-sidebar-subscriptions-entry-point - unless issuable_author_is_current_user(@merge_request) %li.gl-new-dropdown-item diff --git a/data/whats_new/202206220001_15_1.yml b/data/whats_new/202206220001_15_1.yml index 76ef7fd1a46..eacad83f378 100644 --- a/data/whats_new/202206220001_15_1.yml +++ b/data/whats_new/202206220001_15_1.yml @@ -42,7 +42,7 @@ gitlab-com: true packages: [Free, Premium, Ultimate] url: https://docs.gitlab.com/ee/ci/runners/configure_runners.html#artifact-attestation - image_url: https://www.youtube.com/embed/MlIdqrDgI8U + image_url: https://img.youtube.com/vi/MlIdqrDgI8U/hqdefault.jpg published_at: 2022-06-22 release: 15.1 - title: "Link to included CI/CD configuration from the pipeline editor" @@ -54,6 +54,6 @@ gitlab-com: true packages: [Free, Premium, Ultimate] url: https://docs.gitlab.com/ee/ci/pipeline_editor/ - image_url: https://www.youtube.com/embed/7BNDUYfY_ok + image_url: https://img.youtube.com/vi/7BNDUYfY_ok/hqdefault.jpg published_at: 2022-06-22 release: 15.1 diff --git a/doc/development/integrations/secure.md b/doc/development/integrations/secure.md index 3dae0d159a5..2788cd02fcf 100644 --- a/doc/development/integrations/secure.md +++ b/doc/development/integrations/secure.md @@ -253,6 +253,10 @@ then `artifacts:reports:dependency_scanning` must be set to `depscan.json`. Following the POSIX exit code standard, the scanner exits with 0 for success and any number from 1 to 255 for anything else. Success also includes the case when vulnerabilities are found. +When a CI job fails, security report results are not ingested by GitLab, even if the job +[allows failure](../../ci/yaml/#allow_failure). The report artifacts are still uploaded to GitLab and available +for [download in the pipeline security tab](../../user/application_security/vulnerability_report/pipeline.md#download-security-scan-outputs). + When executing a scanning job using the [Docker-in-Docker privileged mode](../../user/application_security/sast/index.md#requirements), we reserve the following standard exit codes. diff --git a/spec/features/admin/admin_appearance_spec.rb b/spec/features/admin/admin_appearance_spec.rb index 8bf8ef56353..ec0399e0ee3 100644 --- a/spec/features/admin/admin_appearance_spec.rb +++ b/spec/features/admin/admin_appearance_spec.rb @@ -101,7 +101,7 @@ RSpec.describe 'Admin Appearance' do context 'Profile page with custom profile image guidelines' do before do - sign_in(create(:admin)) + sign_in(admin) gitlab_enable_admin_mode_sign_in(admin) visit admin_application_settings_appearances_path fill_in 'appearance_profile_image_guidelines', with: 'Custom profile image guidelines, please :smile:!' diff --git a/spec/features/profile_spec.rb b/spec/features/profile_spec.rb index 1013937ebb9..2836ac2f801 100644 --- a/spec/features/profile_spec.rb +++ b/spec/features/profile_spec.rb @@ -30,7 +30,7 @@ RSpec.describe 'Profile account page', :js do it 'deletes user', :js, :sidekiq_might_not_need_inline do click_button 'Delete account' - fill_in 'password', with: '12345678' + fill_in 'password', with: user.password page.within '.modal' do click_button 'Delete account' diff --git a/spec/features/profiles/password_spec.rb b/spec/features/profiles/password_spec.rb index 07dfbca8cbd..1d0db488751 100644 --- a/spec/features/profiles/password_spec.rb +++ b/spec/features/profiles/password_spec.rb @@ -13,6 +13,7 @@ RSpec.describe 'Profile > Password' do end context 'Password authentication enabled' do + let(:new_password) { User.random_password } let(:user) { create(:user, password_automatically_set: true) } before do @@ -23,7 +24,7 @@ RSpec.describe 'Profile > Password' do context 'User with password automatically set' do describe 'User puts different passwords in the field and in the confirmation' do it 'shows an error message' do - fill_passwords('mypassword', 'mypassword2') + fill_passwords(new_password, "#{new_password}2") page.within('.gl-alert-danger') do expect(page).to have_content("Password confirmation doesn't match Password") @@ -31,7 +32,7 @@ RSpec.describe 'Profile > Password' do end it 'does not contain the current password field after an error' do - fill_passwords('mypassword', 'mypassword2') + fill_passwords(new_password, "#{new_password}2") expect(page).to have_no_field('user[current_password]') end @@ -39,7 +40,7 @@ RSpec.describe 'Profile > Password' do describe 'User puts the same passwords in the field and in the confirmation' do it 'shows a success message' do - fill_passwords('mypassword', 'mypassword') + fill_passwords(new_password, new_password) page.within('[data-testid="alert-info"]') do expect(page).to have_content('Password was successfully updated. Please sign in again.') @@ -79,7 +80,7 @@ RSpec.describe 'Profile > Password' do end context 'Change password' do - let(:new_password) { '22233344' } + let(:new_password) { User.random_password } before do sign_in(user) @@ -156,6 +157,8 @@ RSpec.describe 'Profile > Password' do end context 'when password is expired' do + let(:new_password) { User.random_password } + before do sign_in(user) @@ -170,8 +173,8 @@ RSpec.describe 'Profile > Password' do expect(page).to have_current_path new_profile_password_path, ignore_query: true fill_in :user_password, with: user.password - fill_in :user_new_password, with: '12345678' - fill_in :user_password_confirmation, with: '12345678' + fill_in :user_new_password, with: new_password + fill_in :user_password_confirmation, with: new_password click_button 'Set new password' expect(page).to have_current_path new_user_session_path, ignore_query: true diff --git a/spec/features/users/login_spec.rb b/spec/features/users/login_spec.rb index 08d0a8d556a..259a82498b9 100644 --- a/spec/features/users/login_spec.rb +++ b/spec/features/users/login_spec.rb @@ -49,15 +49,15 @@ RSpec.describe 'Login', :clean_gitlab_redis_sessions do expect(page).to have_current_path edit_user_password_path, ignore_query: true expect(page).to have_content('Please create a password for your new account.') - fill_in 'user_password', with: 'password' - fill_in 'user_password_confirmation', with: 'password' + fill_in 'user_password', with: user.password + fill_in 'user_password_confirmation', with: user.password click_button 'Change your password' expect(page).to have_current_path new_user_session_path, ignore_query: true expect(page).to have_content(I18n.t('devise.passwords.updated_not_active')) fill_in 'user_login', with: user.username - fill_in 'user_password', with: 'password' + fill_in 'user_password', with: user.password click_button 'Sign in' expect_single_session_with_authenticated_ttl @@ -231,7 +231,7 @@ RSpec.describe 'Login', :clean_gitlab_redis_sessions do end it 'does not allow sign-in if the user password is updated before entering a one-time code' do - user.update!(password: 'new_password') + user.update!(password: User.random_password) enter_code(user.current_otp) @@ -468,7 +468,7 @@ RSpec.describe 'Login', :clean_gitlab_redis_sessions do visit new_user_session_path fill_in 'user_login', with: user.email - fill_in 'user_password', with: '12345678' + fill_in 'user_password', with: user.password click_button 'Sign in' expect(page).to have_current_path(new_profile_password_path, ignore_query: true) @@ -788,7 +788,7 @@ RSpec.describe 'Login', :clean_gitlab_redis_sessions do visit new_user_session_path fill_in 'user_login', with: user.email - fill_in 'user_password', with: '12345678' + fill_in 'user_password', with: user.password click_button 'Sign in' @@ -809,7 +809,7 @@ RSpec.describe 'Login', :clean_gitlab_redis_sessions do visit new_user_session_path fill_in 'user_login', with: user.email - fill_in 'user_password', with: '12345678' + fill_in 'user_password', with: user.password click_button 'Sign in' @@ -830,7 +830,7 @@ RSpec.describe 'Login', :clean_gitlab_redis_sessions do visit new_user_session_path fill_in 'user_login', with: user.email - fill_in 'user_password', with: '12345678' + fill_in 'user_password', with: user.password click_button 'Sign in' @@ -873,7 +873,7 @@ RSpec.describe 'Login', :clean_gitlab_redis_sessions do visit new_user_session_path fill_in 'user_login', with: user.email - fill_in 'user_password', with: '12345678' + fill_in 'user_password', with: user.password click_button 'Sign in' fill_in 'user_otp_attempt', with: user.reload.current_otp @@ -899,7 +899,7 @@ RSpec.describe 'Login', :clean_gitlab_redis_sessions do visit new_user_session_path fill_in 'user_login', with: user.email - fill_in 'user_password', with: '12345678' + fill_in 'user_password', with: user.password click_button 'Sign in' expect_to_be_on_terms_page @@ -907,9 +907,11 @@ RSpec.describe 'Login', :clean_gitlab_redis_sessions do expect(page).to have_current_path(new_profile_password_path, ignore_query: true) - fill_in 'user_password', with: '12345678' - fill_in 'user_new_password', with: 'new password' - fill_in 'user_password_confirmation', with: 'new password' + new_password = User.random_password + + fill_in 'user_password', with: user.password + fill_in 'user_new_password', with: new_password + fill_in 'user_password_confirmation', with: new_password click_button 'Set new password' expect(page).to have_content('Password successfully changed') diff --git a/spec/frontend/boards/components/board_add_new_column_form_spec.js b/spec/frontend/boards/components/board_add_new_column_form_spec.js index 3b26ca57d6f..7855b041951 100644 --- a/spec/frontend/boards/components/board_add_new_column_form_spec.js +++ b/spec/frontend/boards/components/board_add_new_column_form_spec.js @@ -61,7 +61,7 @@ describe('Board card layout', () => { const formTitle = () => wrapper.findByTestId('board-add-column-form-title').text(); const findSearchInput = () => wrapper.find(GlSearchBoxByType); - const findSearchLabel = () => wrapper.find(GlFormGroup); + const findSearchLabelFormGroup = () => wrapper.find(GlFormGroup); const cancelButton = () => wrapper.findByTestId('cancelAddNewColumn'); const submitButton = () => wrapper.findByTestId('addNewColumnButton'); const findDropdown = () => wrapper.findComponent(GlDropdown); @@ -121,10 +121,17 @@ describe('Board card layout', () => { mountComponent(props); - expect(findSearchLabel().attributes('label')).toEqual(props.searchLabel); + expect(findSearchLabelFormGroup().attributes('label')).toEqual(props.searchLabel); expect(findSearchInput().attributes('placeholder')).toEqual(props.searchPlaceholder); }); + it('does not show the dropdown as invalid by default', () => { + mountComponent(); + + expect(findSearchLabelFormGroup().attributes('state')).toBe('true'); + expect(findDropdown().props('toggleClass')).not.toContain('gl-inset-border-1-red-400!'); + }); + it('emits filter event on input', () => { mountComponent(); @@ -137,13 +144,13 @@ describe('Board card layout', () => { }); describe('Add list button', () => { - it('is disabled if no item is selected', () => { + it('is enabled by default', () => { mountComponent(); - expect(submitButton().props('disabled')).toBe(true); + expect(submitButton().props('disabled')).toBe(false); }); - it('emits add-list event on click', () => { + it('emits add-list event on click when an ID is selected', () => { mountComponent({ selectedId: mockLabelList.label.id, }); @@ -152,5 +159,16 @@ describe('Board card layout', () => { expect(wrapper.emitted('add-list')).toEqual([[]]); }); + + it('does not emit the add-list event on click and shows the dropdown as invalid when no ID is selected', async () => { + mountComponent(); + + await submitButton().vm.$emit('click'); + + expect(findSearchLabelFormGroup().attributes('state')).toBeUndefined(); + expect(findDropdown().props('toggleClass')).toContain('gl-inset-border-1-red-400!'); + + expect(wrapper.emitted('add-list')).toBeUndefined(); + }); }); }); diff --git a/spec/frontend/fixtures/runner.rb b/spec/frontend/fixtures/runner.rb index 91eef8b485c..22039d076b2 100644 --- a/spec/frontend/fixtures/runner.rb +++ b/spec/frontend/fixtures/runner.rb @@ -13,10 +13,10 @@ RSpec.describe 'Runner (JavaScript fixtures)' do let_it_be(:project) { create(:project, :repository, :public) } let_it_be(:project_2) { create(:project, :repository, :public) } - let_it_be(:instance_runner) { create(:ci_runner, :instance, version: '1.0.0', revision: '123', description: 'Instance runner', ip_address: '127.0.0.1') } - let_it_be(:group_runner) { create(:ci_runner, :group, groups: [group], active: false, version: '2.0.0', revision: '456', description: 'Group runner', ip_address: '127.0.0.1') } - let_it_be(:group_runner_2) { create(:ci_runner, :group, groups: [group], active: false, version: '2.0.0', revision: '456', description: 'Group runner 2', ip_address: '127.0.0.1') } - let_it_be(:project_runner) { create(:ci_runner, :project, projects: [project, project_2], active: false, version: '2.0.0', revision: '456', description: 'Project runner', ip_address: '127.0.0.1') } + let_it_be(:instance_runner) { create(:ci_runner, :instance, version: '1.0.0', description: 'Instance runner', ip_address: '127.0.0.1') } + let_it_be(:group_runner) { create(:ci_runner, :group, groups: [group], active: false, version: '2.0.0', description: 'Group runner', ip_address: '127.0.0.1') } + let_it_be(:group_runner_2) { create(:ci_runner, :group, groups: [group], active: false, version: '2.0.0', description: 'Group runner 2', ip_address: '127.0.0.1') } + let_it_be(:project_runner) { create(:ci_runner, :project, projects: [project, project_2], active: false, version: '2.0.0', description: 'Project runner', ip_address: '127.0.0.1') } let_it_be(:build) { create(:ci_build, runner: instance_runner) } query_path = 'runner/graphql/' diff --git a/spec/frontend/ide/components/activity_bar_spec.js b/spec/frontend/ide/components/activity_bar_spec.js index 39fe2c7e723..a97e883a8bf 100644 --- a/spec/frontend/ide/components/activity_bar_spec.js +++ b/spec/frontend/ide/components/activity_bar_spec.js @@ -1,86 +1,82 @@ -import Vue, { nextTick } from 'vue'; -import { createComponentWithStore } from 'helpers/vue_mount_component_helper'; +import { GlBadge } from '@gitlab/ui'; +import { shallowMount } from '@vue/test-utils'; import ActivityBar from '~/ide/components/activity_bar.vue'; import { leftSidebarViews } from '~/ide/constants'; import { createStore } from '~/ide/stores'; -describe('IDE activity bar', () => { - const Component = Vue.extend(ActivityBar); - let vm; +describe('IDE ActivityBar component', () => { + let wrapper; let store; - const findChangesBadge = () => vm.$el.querySelector('.badge'); + const findChangesBadge = () => wrapper.findComponent(GlBadge); - beforeEach(() => { + const mountComponent = (state) => { store = createStore(); - - Vue.set(store.state.projects, 'abcproject', { - web_url: 'testing', + store.replaceState({ + ...store.state, + projects: { abcproject: { web_url: 'testing' } }, + currentProjectId: 'abcproject', + ...state, }); - Vue.set(store.state, 'currentProjectId', 'abcproject'); - vm = createComponentWithStore(Component, store); - }); + wrapper = shallowMount(ActivityBar, { store }); + }; afterEach(() => { - vm.$destroy(); + wrapper.destroy(); }); describe('updateActivityBarView', () => { beforeEach(() => { - jest.spyOn(vm, 'updateActivityBarView').mockImplementation(() => {}); - - vm.$mount(); + mountComponent(); + jest.spyOn(wrapper.vm, 'updateActivityBarView').mockImplementation(() => {}); }); it('calls updateActivityBarView with edit value on click', () => { - vm.$el.querySelector('.js-ide-edit-mode').click(); + wrapper.find('.js-ide-edit-mode').trigger('click'); - expect(vm.updateActivityBarView).toHaveBeenCalledWith(leftSidebarViews.edit.name); + expect(wrapper.vm.updateActivityBarView).toHaveBeenCalledWith(leftSidebarViews.edit.name); }); it('calls updateActivityBarView with commit value on click', () => { - vm.$el.querySelector('.js-ide-commit-mode').click(); + wrapper.find('.js-ide-commit-mode').trigger('click'); - expect(vm.updateActivityBarView).toHaveBeenCalledWith(leftSidebarViews.commit.name); + expect(wrapper.vm.updateActivityBarView).toHaveBeenCalledWith(leftSidebarViews.commit.name); }); it('calls updateActivityBarView with review value on click', () => { - vm.$el.querySelector('.js-ide-review-mode').click(); + wrapper.find('.js-ide-review-mode').trigger('click'); - expect(vm.updateActivityBarView).toHaveBeenCalledWith(leftSidebarViews.review.name); + expect(wrapper.vm.updateActivityBarView).toHaveBeenCalledWith(leftSidebarViews.review.name); }); }); describe('active item', () => { - beforeEach(() => { - vm.$mount(); - }); - it('sets edit item active', () => { - expect(vm.$el.querySelector('.js-ide-edit-mode').classList).toContain('active'); + mountComponent(); + + expect(wrapper.find('.js-ide-edit-mode').classes()).toContain('active'); }); - it('sets commit item active', async () => { - vm.$store.state.currentActivityView = leftSidebarViews.commit.name; + it('sets commit item active', () => { + mountComponent({ currentActivityView: leftSidebarViews.commit.name }); - await nextTick(); - expect(vm.$el.querySelector('.js-ide-commit-mode').classList).toContain('active'); + expect(wrapper.find('.js-ide-commit-mode').classes()).toContain('active'); }); }); describe('changes badge', () => { it('is rendered when files are staged', () => { - store.state.stagedFiles = [{ path: '/path/to/file' }]; - vm.$mount(); + mountComponent({ stagedFiles: [{ path: '/path/to/file' }] }); - expect(findChangesBadge()).toBeTruthy(); - expect(findChangesBadge().textContent.trim()).toBe('1'); + expect(findChangesBadge().exists()).toBe(true); + expect(findChangesBadge().text()).toBe('1'); }); it('is not rendered when no changes are present', () => { - vm.$mount(); - expect(findChangesBadge()).toBeFalsy(); + mountComponent(); + + expect(findChangesBadge().exists()).toBe(false); }); }); }); diff --git a/spec/frontend/ide/components/ide_status_bar_spec.js b/spec/frontend/ide/components/ide_status_bar_spec.js index 17a5aa17b1f..e6e0ebaf1e8 100644 --- a/spec/frontend/ide/components/ide_status_bar_spec.js +++ b/spec/frontend/ide/components/ide_status_bar_spec.js @@ -1,8 +1,8 @@ +import { mount } from '@vue/test-utils'; import _ from 'lodash'; -import Vue, { nextTick } from 'vue'; import { TEST_HOST } from 'helpers/test_constants'; -import { createComponentWithStore } from 'helpers/vue_mount_component_helper'; import IdeStatusBar from '~/ide/components/ide_status_bar.vue'; +import IdeStatusMR from '~/ide/components/ide_status_mr.vue'; import { rightSidebarViews } from '~/ide/constants'; import { createStore } from '~/ide/stores'; import { projectData } from '../mock_data'; @@ -13,42 +13,48 @@ const TEST_MERGE_REQUEST_URL = `${TEST_HOST}merge-requests/${TEST_MERGE_REQUEST_ jest.mock('~/lib/utils/poll'); -describe('ideStatusBar', () => { - let store; - let vm; +describe('IdeStatusBar component', () => { + let wrapper; + + const findMRStatus = () => wrapper.findComponent(IdeStatusMR); + + const mountComponent = (state = {}) => { + const store = createStore(); + store.replaceState({ + ...store.state, + currentBranchId: 'main', + currentProjectId: TEST_PROJECT_ID, + projects: { + ...store.state.projects, + [TEST_PROJECT_ID]: _.clone(projectData), + }, + ...state, + }); - const createComponent = () => { - vm = createComponentWithStore(Vue.extend(IdeStatusBar), store).$mount(); + wrapper = mount(IdeStatusBar, { store }); }; - const findMRStatus = () => vm.$el.querySelector('.js-ide-status-mr'); - - beforeEach(() => { - store = createStore(); - store.state.currentProjectId = TEST_PROJECT_ID; - store.state.projects[TEST_PROJECT_ID] = _.clone(projectData); - store.state.currentBranchId = 'main'; - }); afterEach(() => { - vm.$destroy(); + wrapper.destroy(); }); describe('default', () => { - beforeEach(() => { - createComponent(); - }); - it('triggers a setInterval', () => { - expect(vm.intervalId).not.toBe(null); + mountComponent(); + + expect(wrapper.vm.intervalId).not.toBe(null); }); it('renders the statusbar', () => { - expect(vm.$el.className).toBe('ide-status-bar'); + mountComponent(); + + expect(wrapper.classes()).toEqual(['ide-status-bar']); }); describe('commitAgeUpdate', () => { beforeEach(() => { - jest.spyOn(vm, 'commitAgeUpdate').mockImplementation(() => {}); + mountComponent(); + jest.spyOn(wrapper.vm, 'commitAgeUpdate').mockImplementation(() => {}); }); afterEach(() => { @@ -56,70 +62,82 @@ describe('ideStatusBar', () => { }); it('gets called every second', () => { - expect(vm.commitAgeUpdate).not.toHaveBeenCalled(); + expect(wrapper.vm.commitAgeUpdate).not.toHaveBeenCalled(); jest.advanceTimersByTime(1000); - expect(vm.commitAgeUpdate.mock.calls.length).toEqual(1); + expect(wrapper.vm.commitAgeUpdate.mock.calls).toHaveLength(1); jest.advanceTimersByTime(1000); - expect(vm.commitAgeUpdate.mock.calls.length).toEqual(2); + expect(wrapper.vm.commitAgeUpdate.mock.calls).toHaveLength(2); }); }); describe('getCommitPath', () => { it('returns the path to the commit details', () => { - expect(vm.getCommitPath('abc123de')).toBe('/commit/abc123de'); + mountComponent(); + + expect(wrapper.vm.getCommitPath('abc123de')).toBe('/commit/abc123de'); }); }); describe('pipeline status', () => { - it('opens right sidebar on clicking icon', async () => { - jest.spyOn(vm, 'openRightPane').mockImplementation(() => {}); - Vue.set(vm.$store.state.pipelines, 'latestPipeline', { - details: { - status: { - text: 'success', - details_path: 'test', - icon: 'status_success', + it('opens right sidebar on clicking icon', () => { + const pipelines = { + latestPipeline: { + details: { + status: { + text: 'success', + details_path: 'test', + icon: 'status_success', + }, + }, + commit: { + author_gravatar_url: 'www', }, }, - commit: { - author_gravatar_url: 'www', - }, - }); + }; + mountComponent({ pipelines }); + jest.spyOn(wrapper.vm, 'openRightPane').mockImplementation(() => {}); - await nextTick(); - vm.$el.querySelector('.ide-status-pipeline button').click(); + wrapper.find('button').trigger('click'); - expect(vm.openRightPane).toHaveBeenCalledWith(rightSidebarViews.pipelines); + expect(wrapper.vm.openRightPane).toHaveBeenCalledWith(rightSidebarViews.pipelines); }); }); it('does not show merge request status', () => { - expect(findMRStatus()).toBe(null); + mountComponent(); + + expect(findMRStatus().exists()).toBe(false); }); }); describe('with merge request in store', () => { beforeEach(() => { - store.state.projects[TEST_PROJECT_ID].mergeRequests = { - [TEST_MERGE_REQUEST_ID]: { - web_url: TEST_MERGE_REQUEST_URL, - references: { - short: `!${TEST_MERGE_REQUEST_ID}`, + const state = { + currentMergeRequestId: TEST_MERGE_REQUEST_ID, + projects: { + [TEST_PROJECT_ID]: { + ..._.clone(projectData), + mergeRequests: { + [TEST_MERGE_REQUEST_ID]: { + web_url: TEST_MERGE_REQUEST_URL, + references: { + short: `!${TEST_MERGE_REQUEST_ID}`, + }, + }, + }, }, }, }; - store.state.currentMergeRequestId = TEST_MERGE_REQUEST_ID; - - createComponent(); + mountComponent(state); }); it('shows merge request status', () => { - expect(findMRStatus().textContent.trim()).toEqual(`Merge request !${TEST_MERGE_REQUEST_ID}`); - expect(findMRStatus().querySelector('a').href).toEqual(TEST_MERGE_REQUEST_URL); + expect(findMRStatus().text()).toBe(`Merge request !${TEST_MERGE_REQUEST_ID}`); + expect(findMRStatus().find('a').attributes('href')).toBe(TEST_MERGE_REQUEST_URL); }); }); }); diff --git a/spec/frontend/ide/components/new_dropdown/index_spec.js b/spec/frontend/ide/components/new_dropdown/index_spec.js index 19dcd9569b3..747c099db33 100644 --- a/spec/frontend/ide/components/new_dropdown/index_spec.js +++ b/spec/frontend/ide/components/new_dropdown/index_spec.js @@ -1,70 +1,66 @@ -import Vue, { nextTick } from 'vue'; -import { createComponentWithStore } from 'helpers/vue_mount_component_helper'; -import newDropdown from '~/ide/components/new_dropdown/index.vue'; +import { mount } from '@vue/test-utils'; +import NewDropdown from '~/ide/components/new_dropdown/index.vue'; +import Button from '~/ide/components/new_dropdown/button.vue'; import { createStore } from '~/ide/stores'; describe('new dropdown component', () => { - let store; - let vm; - - beforeEach(() => { - store = createStore(); - - const component = Vue.extend(newDropdown); - - vm = createComponentWithStore(component, store, { - branch: 'main', - path: '', - mouseOver: false, - type: 'tree', + let wrapper; + + const findAllButtons = () => wrapper.findAllComponents(Button); + + const mountComponent = () => { + const store = createStore(); + store.state.currentProjectId = 'abcproject'; + store.state.path = ''; + store.state.trees['abcproject/mybranch'] = { tree: [] }; + + wrapper = mount(NewDropdown, { + store, + propsData: { + branch: 'main', + path: '', + mouseOver: false, + type: 'tree', + }, }); + }; - vm.$store.state.currentProjectId = 'abcproject'; - vm.$store.state.path = ''; - vm.$store.state.trees['abcproject/mybranch'] = { - tree: [], - }; - - vm.$mount(); - - jest.spyOn(vm.$refs.newModal, 'open').mockImplementation(() => {}); + beforeEach(() => { + mountComponent(); + jest.spyOn(wrapper.vm.$refs.newModal, 'open').mockImplementation(() => {}); }); afterEach(() => { - vm.$destroy(); + wrapper.destroy(); }); it('renders new file, upload and new directory links', () => { - const buttons = vm.$el.querySelectorAll('.dropdown-menu button'); - - expect(buttons[0].textContent.trim()).toBe('New file'); - expect(buttons[1].textContent.trim()).toBe('Upload file'); - expect(buttons[2].textContent.trim()).toBe('New directory'); + expect(findAllButtons().at(0).text()).toBe('New file'); + expect(findAllButtons().at(1).text()).toBe('Upload file'); + expect(findAllButtons().at(2).text()).toBe('New directory'); }); describe('createNewItem', () => { it('opens modal for a blob when new file is clicked', () => { - vm.$el.querySelectorAll('.dropdown-menu button')[0].click(); + findAllButtons().at(0).trigger('click'); - expect(vm.$refs.newModal.open).toHaveBeenCalledWith('blob', ''); + expect(wrapper.vm.$refs.newModal.open).toHaveBeenCalledWith('blob', ''); }); it('opens modal for a tree when new directory is clicked', () => { - vm.$el.querySelectorAll('.dropdown-menu button')[2].click(); + findAllButtons().at(2).trigger('click'); - expect(vm.$refs.newModal.open).toHaveBeenCalledWith('tree', ''); + expect(wrapper.vm.$refs.newModal.open).toHaveBeenCalledWith('tree', ''); }); }); describe('isOpen', () => { it('scrolls dropdown into view', async () => { - jest.spyOn(vm.$refs.dropdownMenu, 'scrollIntoView').mockImplementation(() => {}); - - vm.isOpen = true; + jest.spyOn(wrapper.vm.$refs.dropdownMenu, 'scrollIntoView').mockImplementation(() => {}); - await nextTick(); + await wrapper.setProps({ isOpen: true }); - expect(vm.$refs.dropdownMenu.scrollIntoView).toHaveBeenCalledWith({ + expect(wrapper.vm.$refs.dropdownMenu.scrollIntoView).toHaveBeenCalledWith({ block: 'nearest', }); }); @@ -72,11 +68,11 @@ describe('new dropdown component', () => { describe('delete entry', () => { it('calls delete action', () => { - jest.spyOn(vm, 'deleteEntry').mockImplementation(() => {}); + jest.spyOn(wrapper.vm, 'deleteEntry').mockImplementation(() => {}); - vm.$el.querySelectorAll('.dropdown-menu button')[4].click(); + findAllButtons().at(4).trigger('click'); - expect(vm.deleteEntry).toHaveBeenCalledWith(''); + expect(wrapper.vm.deleteEntry).toHaveBeenCalledWith(''); }); }); }); diff --git a/spec/models/hooks/web_hook_spec.rb b/spec/models/hooks/web_hook_spec.rb index 9faa5e1567c..036d2effc0f 100644 --- a/spec/models/hooks/web_hook_spec.rb +++ b/spec/models/hooks/web_hook_spec.rb @@ -482,12 +482,6 @@ RSpec.describe WebHook do expect(hook).not_to be_temporarily_disabled end - - it 'can ignore the feature flag' do - stub_feature_flags(web_hooks_disable_failed: false) - - expect(hook).to be_temporarily_disabled(ignore_flag: true) - end end end @@ -510,12 +504,6 @@ RSpec.describe WebHook do expect(hook).not_to be_permanently_disabled end - - it 'can ignore the feature flag' do - stub_feature_flags(web_hooks_disable_failed: false) - - expect(hook).to be_permanently_disabled(ignore_flag: true) - end end end diff --git a/spec/services/web_hooks/log_execution_service_spec.rb b/spec/services/web_hooks/log_execution_service_spec.rb index 873f6adc8dc..1967a8368fb 100644 --- a/spec/services/web_hooks/log_execution_service_spec.rb +++ b/spec/services/web_hooks/log_execution_service_spec.rb @@ -101,27 +101,6 @@ RSpec.describe WebHooks::LogExecutionService do it 'resets the failure count' do expect { service.execute }.to change(project_hook, :recent_failures).to(0) end - - it 'sends a message to AuthLogger if the hook as not previously enabled' do - project_hook.update!(recent_failures: ::WebHook::FAILURE_THRESHOLD + 1) - - expect(Gitlab::AuthLogger).to receive(:info).with include( - message: 'WebHook change active_state', - # identification - hook_id: project_hook.id, - hook_type: project_hook.type, - project_id: project_hook.project_id, - group_id: nil, - # relevant data - prev_state: :permanently_disabled, - new_state: :enabled, - duration: 1.2, - response_status: '200', - recent_hook_failures: 0 - ) - - service.execute - end end end @@ -158,27 +137,6 @@ RSpec.describe WebHooks::LogExecutionService do expect { service.execute }.not_to change(project_hook, :recent_failures) end end - - it 'sends a message to AuthLogger if the state would change' do - project_hook.update!(recent_failures: ::WebHook::FAILURE_THRESHOLD) - - expect(Gitlab::AuthLogger).to receive(:info).with include( - message: 'WebHook change active_state', - # identification - hook_id: project_hook.id, - hook_type: project_hook.type, - project_id: project_hook.project_id, - group_id: nil, - # relevant data - prev_state: :enabled, - new_state: :permanently_disabled, - duration: (be > 0), - response_status: data[:response_status], - recent_hook_failures: ::WebHook::FAILURE_THRESHOLD + 1 - ) - - service.execute - end end context 'when response_category is :error' do @@ -200,25 +158,6 @@ RSpec.describe WebHooks::LogExecutionService do expect { service.execute }.to change(project_hook, :backoff_count).by(1) end - it 'sends a message to AuthLogger if the state would change' do - expect(Gitlab::AuthLogger).to receive(:info).with include( - message: 'WebHook change active_state', - # identification - hook_id: project_hook.id, - hook_type: project_hook.type, - project_id: project_hook.project_id, - group_id: nil, - # relevant data - prev_state: :enabled, - new_state: :temporarily_disabled, - duration: (be > 0), - response_status: data[:response_status], - recent_hook_failures: 0 - ) - - service.execute - end - context 'when the previous cool-off was near the maximum' do before do project_hook.update!(disabled_until: 5.minutes.ago, backoff_count: 8) |