diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2023-06-20 00:10:01 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2023-06-20 00:10:01 +0300 |
commit | 7361375554b55ca52e0282bbe6cd063e2848bc2b (patch) | |
tree | 17a82da8e5ecd722da643554d521900d7e60f29b /spec | |
parent | 2f00709f337c76982dfe69cbc62dc3cb148131f2 (diff) |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'spec')
3 files changed, 170 insertions, 58 deletions
diff --git a/spec/frontend/vue_shared/alert_details/alert_status_spec.js b/spec/frontend/vue_shared/alert_details/alert_status_spec.js index 98cb2f5cb0b..90d29f0bfd4 100644 --- a/spec/frontend/vue_shared/alert_details/alert_status_spec.js +++ b/spec/frontend/vue_shared/alert_details/alert_status_spec.js @@ -1,7 +1,9 @@ +import Vue from 'vue'; +import VueApollo from 'vue-apollo'; import { GlDropdown, GlDropdownItem } from '@gitlab/ui'; -import { nextTick } from 'vue'; import { shallowMountExtended } from 'helpers/vue_test_utils_helper'; import waitForPromises from 'helpers/wait_for_promises'; +import createMockApollo from 'helpers/mock_apollo_helper'; import updateAlertStatusMutation from '~/graphql_shared//mutations/alert_status_update.mutation.graphql'; import Tracking from '~/tracking'; import AlertManagementStatus from '~/vue_shared/alert_details/components/alert_status.vue'; @@ -11,6 +13,27 @@ const mockAlert = mockAlerts[0]; describe('AlertManagementStatus', () => { let wrapper; + let requestHandler; + + const iid = '1527542'; + const mockUpdatedMutationResult = ({ errors = [], nodes = [] } = {}) => + jest.fn().mockResolvedValue({ + data: { + updateAlertStatus: { + errors, + alert: { + id: '1', + iid, + status: 'acknowledged', + endedAt: 'endedAt', + notes: { + nodes, + }, + }, + }, + }, + }); + const findStatusDropdown = () => wrapper.findComponent(GlDropdown); const findFirstStatusOption = () => findStatusDropdown().findComponent(GlDropdownItem); const findAllStatusOptions = () => findStatusDropdown().findAllComponents(GlDropdownItem); @@ -22,8 +45,20 @@ describe('AlertManagementStatus', () => { return waitForPromises(); }; - function mountComponent({ props = {}, provide = {}, loading = false, stubs = {} } = {}) { + const createMockApolloProvider = (handler) => { + Vue.use(VueApollo); + requestHandler = handler; + + return createMockApollo([[updateAlertStatusMutation, handler]]); + }; + + function mountComponent({ + props = {}, + provide = {}, + handler = mockUpdatedMutationResult(), + } = {}) { wrapper = shallowMountExtended(AlertManagementStatus, { + apolloProvider: createMockApolloProvider(handler), propsData: { alert: { ...mockAlert }, projectPath: 'gitlab-org/gitlab', @@ -31,17 +66,6 @@ describe('AlertManagementStatus', () => { ...props, }, provide, - mocks: { - $apollo: { - mutate: jest.fn(), - queries: { - alert: { - loading, - }, - }, - }, - }, - stubs, }); } @@ -63,43 +87,32 @@ describe('AlertManagementStatus', () => { }); describe('updating the alert status', () => { - const iid = '1527542'; - const mockUpdatedMutationResult = { - data: { - updateAlertStatus: { - errors: [], - alert: { - iid, - status: 'acknowledged', - }, - }, - }, - }; - beforeEach(() => { - mountComponent({}); + mountComponent(); }); - it('calls `$apollo.mutate` with `updateAlertStatus` mutation and variables containing `iid`, `status`, & `projectPath`', () => { - jest.spyOn(wrapper.vm.$apollo, 'mutate').mockResolvedValue(mockUpdatedMutationResult); + it('calls `$apollo.mutate` with `updateAlertStatus` mutation and variables containing `iid`, `status`, & `projectPath`', async () => { findFirstStatusOption().vm.$emit('click'); + await waitForPromises(); - expect(wrapper.vm.$apollo.mutate).toHaveBeenCalledWith({ - mutation: updateAlertStatusMutation, - variables: { - iid, - status: 'TRIGGERED', - projectPath: 'gitlab-org/gitlab', - }, + expect(requestHandler).toHaveBeenCalledWith({ + iid, + status: 'TRIGGERED', + projectPath: 'gitlab-org/gitlab', }); }); describe('when a request fails', () => { - beforeEach(() => { - jest.spyOn(wrapper.vm.$apollo, 'mutate').mockReturnValue(Promise.reject(new Error())); + beforeEach(async () => { + mountComponent({ + handler: mockUpdatedMutationResult({ errors: ['<span data-testid="htmlError" />'] }), + }); + await waitForPromises(); }); it('emits an error', async () => { + mountComponent({ handler: jest.fn().mockRejectedValue({}) }); + await waitForPromises(); await selectFirstStatusOption(); expect(wrapper.emitted('alert-error')[0]).toEqual([ @@ -116,7 +129,6 @@ describe('AlertManagementStatus', () => { it('emits an error when triggered a second time', async () => { await selectFirstStatusOption(); - await nextTick(); await selectFirstStatusOption(); // Should emit two errors [0,1] expect(wrapper.emitted('alert-error').length > 1).toBe(true); @@ -124,19 +136,9 @@ describe('AlertManagementStatus', () => { }); it('shows an error when response includes HTML errors', async () => { - const mockUpdatedMutationErrorResult = { - data: { - updateAlertStatus: { - errors: ['<span data-testid="htmlError" />'], - alert: { - iid, - status: 'acknowledged', - }, - }, - }, - }; - - jest.spyOn(wrapper.vm.$apollo, 'mutate').mockResolvedValue(mockUpdatedMutationErrorResult); + mountComponent({ + handler: mockUpdatedMutationResult({ errors: ['<span data-testid="htmlError" />'] }), + }); await selectFirstStatusOption(); @@ -160,7 +162,7 @@ describe('AlertManagementStatus', () => { mountComponent({ props: { alert: { ...mockAlert, status }, statuses: { [status]: translatedStatus } }, }); - expect(findAllStatusOptions().length).toBe(1); + expect(findAllStatusOptions()).toHaveLength(1); expect(findFirstStatusOption().text()).toBe(translatedStatus); }); }); @@ -173,10 +175,10 @@ describe('AlertManagementStatus', () => { it('should not track alert status updates when the tracking options do not exist', async () => { mountComponent({}); Tracking.event.mockClear(); - jest.spyOn(wrapper.vm.$apollo, 'mutate').mockResolvedValue({}); + findFirstStatusOption().vm.$emit('click'); - await nextTick(); + await waitForPromises(); expect(Tracking.event).not.toHaveBeenCalled(); }); @@ -187,12 +189,14 @@ describe('AlertManagementStatus', () => { action: 'update_alert_status', label: 'Status', }; - mountComponent({ provide: { trackAlertStatusUpdateOptions } }); + mountComponent({ + provide: { trackAlertStatusUpdateOptions }, + handler: mockUpdatedMutationResult({ nodes: mockAlerts }), + }); Tracking.event.mockClear(); - jest.spyOn(wrapper.vm.$apollo, 'mutate').mockResolvedValue({}); findFirstStatusOption().vm.$emit('click'); - await nextTick(); + await waitForPromises(); const status = findFirstStatusOption().text(); const { category, action, label } = trackAlertStatusUpdateOptions; diff --git a/spec/lib/gitlab/i18n/pluralization_spec.rb b/spec/lib/gitlab/i18n/pluralization_spec.rb index 857562d549c..6bfc500c8b8 100644 --- a/spec/lib/gitlab/i18n/pluralization_spec.rb +++ b/spec/lib/gitlab/i18n/pluralization_spec.rb @@ -2,6 +2,7 @@ require 'fast_spec_helper' require 'rspec-parameterized' +require 'rails/version' require 'gettext_i18n_rails' RSpec.describe Gitlab::I18n::Pluralization, feature_category: :internationalization do diff --git a/spec/rubocop/cop/background_migration/avoid_silent_rescue_exceptions_spec.rb b/spec/rubocop/cop/background_migration/avoid_silent_rescue_exceptions_spec.rb new file mode 100644 index 00000000000..ea4f7d5fca8 --- /dev/null +++ b/spec/rubocop/cop/background_migration/avoid_silent_rescue_exceptions_spec.rb @@ -0,0 +1,107 @@ +# frozen_string_literal: true + +require 'rubocop_spec_helper' +require_relative '../../../../rubocop/cop/background_migration/avoid_silent_rescue_exceptions' + +RSpec.describe RuboCop::Cop::BackgroundMigration::AvoidSilentRescueExceptions, feature_category: :database do + shared_examples 'expecting offense when' do |node| + it 'throws offense when rescuing exceptions without re-raising them' do + %w[Gitlab::BackgroundMigration::BatchedMigrationJob BatchedMigrationJob].each do |base_class| + expect_offense(<<~RUBY) + module Gitlab + module BackgroundMigration + class MyJob < #{base_class} + #{node} + end + end + end + RUBY + end + end + end + + shared_examples 'not expecting offense when' do |node| + it 'does not throw any offense if exception is re-raised' do + %w[Gitlab::BackgroundMigration::BatchedMigrationJob BatchedMigrationJob].each do |base_class| + expect_no_offenses(<<~RUBY) + module Gitlab + module BackgroundMigration + class MyJob < #{base_class} + #{node} + end + end + end + RUBY + end + end + end + + context "when the migration class doesn't inherits from BatchedMigrationJob" do + it 'does not throw any offense' do + expect_no_offenses(<<~RUBY) + module Gitlab + module BackgroundMigration + class MyClass < ::Gitlab::BackgroundMigration::Logger + def my_method + execute + rescue StandardError => error + puts error.message + end + end + end + end + RUBY + end + end + + context 'when the migration class inherits from BatchedMigrationJob' do + context 'when specifying an error class' do + it_behaves_like 'expecting offense when', <<~RUBY + def perform + connection.execute('SELECT 1;') + rescue JSON::ParserError + ^^^^^^^^^^^^^^^^^^^^^^^^ #{described_class::MSG} + logger.error(message: error.message, class: self.class.name) + end + RUBY + + it_behaves_like 'expecting offense when', <<~RUBY + def perform + connection.execute('SELECT 1;') + rescue StandardError, ActiveRecord::StatementTimeout => error + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ #{described_class::MSG} + logger.error(message: error.message, class: self.class.name) + end + RUBY + + it_behaves_like 'not expecting offense when', <<~RUBY + def perform + connection.execute('SELECT 1;') + rescue StandardError, ActiveRecord::StatementTimeout => error + logger.error(message: error.message, class: self.class.name) + raise error + end + RUBY + end + + context 'without specifying an error class' do + it_behaves_like 'expecting offense when', <<~RUBY + def perform + connection.execute('SELECT 1;') + rescue => error + ^^^^^^ #{described_class::MSG} + logger.error(message: error.message, class: self.class.name) + end + RUBY + + it_behaves_like 'not expecting offense when', <<~RUBY + def perform + connection.execute('SELECT 1;') + rescue => error + logger.error(message: error.message, class: self.class.name) + raise error + end + RUBY + end + end +end |