diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2023-06-19 18:09:36 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2023-06-19 18:09:36 +0300 |
commit | 8bb837c4d180720d4d923ef2e7bd2c9a46ca97a0 (patch) | |
tree | 7dcb166661ba29fb6cd5935f0db34eee6c935388 /spec | |
parent | eef2437c0a359ec3437d31d1b1ea959e54c71458 (diff) |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'spec')
33 files changed, 474 insertions, 149 deletions
diff --git a/spec/contracts/provider/spec_helper.rb b/spec/contracts/provider/spec_helper.rb index 44e4d29c18e..f1ceca16b4b 100644 --- a/spec/contracts/provider/spec_helper.rb +++ b/spec/contracts/provider/spec_helper.rb @@ -2,10 +2,10 @@ require 'spec_helper' require 'zeitwerk' +require 'gitlab/rspec/all' require_relative 'helpers/users_helper' require_relative('../../../ee/spec/contracts/provider/spec_helper') if Gitlab.ee? require Rails.root.join("spec/support/helpers/rails_helpers.rb") -require Rails.root.join("spec/support/helpers/stub_env.rb") # Opt out of telemetry collection. We can't allow all engineers, and users who install GitLab from source, to be # automatically enrolled in sending data on their usage without their knowledge. diff --git a/spec/fast_spec_helper.rb b/spec/fast_spec_helper.rb index fcf0c43243f..03c8919912c 100644 --- a/spec/fast_spec_helper.rb +++ b/spec/fast_spec_helper.rb @@ -22,6 +22,7 @@ require_relative 'rails_autoload' require_relative '../config/settings' require_relative 'support/rspec' +require_relative '../lib/gitlab' require_relative '../lib/gitlab/utils' require_relative '../lib/gitlab/utils/strong_memoize' diff --git a/spec/features/groups/members/manage_groups_spec.rb b/spec/features/groups/members/manage_groups_spec.rb index 599b31827da..87de0e2e46b 100644 --- a/spec/features/groups/members/manage_groups_spec.rb +++ b/spec/features/groups/members/manage_groups_spec.rb @@ -3,6 +3,7 @@ require 'spec_helper' RSpec.describe 'Groups > Members > Manage groups', :js, feature_category: :groups_and_projects do + include ListboxHelpers include Features::MembersHelpers include Features::InviteMembersModalHelpers include Spec::Support::Helpers::ModalHelpers @@ -75,8 +76,7 @@ RSpec.describe 'Groups > Members > Manage groups', :js, feature_category: :group click_groups_tab page.within(first_row) do - click_button('Developer') - click_button('Maintainer') + select_from_listbox('Maintainer', from: 'Developer') wait_for_requests diff --git a/spec/features/groups/members/manage_members_spec.rb b/spec/features/groups/members/manage_members_spec.rb index 3c1425fc9ca..138031ffaac 100644 --- a/spec/features/groups/members/manage_members_spec.rb +++ b/spec/features/groups/members/manage_members_spec.rb @@ -3,6 +3,7 @@ require 'spec_helper' RSpec.describe 'Groups > Members > Manage members', feature_category: :groups_and_projects do + include ListboxHelpers include Features::MembersHelpers include Features::InviteMembersModalHelpers include Spec::Support::Helpers::ModalHelpers @@ -35,8 +36,7 @@ RSpec.describe 'Groups > Members > Manage members', feature_category: :groups_an visit group_group_members_path(group) page.within(second_row) do - click_button('Developer') - click_button('Owner') + select_from_listbox('Owner', from: 'Developer') expect(page).to have_button('Owner') end diff --git a/spec/features/projects/members/groups_with_access_list_spec.rb b/spec/features/projects/members/groups_with_access_list_spec.rb index 0f80c6cb3b8..a2a04ada627 100644 --- a/spec/features/projects/members/groups_with_access_list_spec.rb +++ b/spec/features/projects/members/groups_with_access_list_spec.rb @@ -3,6 +3,7 @@ require 'spec_helper' RSpec.describe 'Projects > Members > Groups with access list', :js, feature_category: :groups_and_projects do + include ListboxHelpers include Features::MembersHelpers include Spec::Support::Helpers::ModalHelpers include Features::InviteMembersModalHelpers @@ -26,8 +27,7 @@ RSpec.describe 'Projects > Members > Groups with access list', :js, feature_cate end it 'updates group access level' do - click_button group_link.human_access - click_button 'Guest' + select_from_listbox('Guest', from: group_link.human_access) wait_for_requests diff --git a/spec/features/projects/members/manage_members_spec.rb b/spec/features/projects/members/manage_members_spec.rb index 5ae6eb83b6b..0e3ac5ff3ac 100644 --- a/spec/features/projects/members/manage_members_spec.rb +++ b/spec/features/projects/members/manage_members_spec.rb @@ -3,6 +3,7 @@ require 'spec_helper' RSpec.describe 'Projects > Members > Manage members', :js, feature_category: :onboarding do + include ListboxHelpers include Features::MembersHelpers include Features::InviteMembersModalHelpers include Spec::Support::Helpers::ModalHelpers @@ -61,11 +62,8 @@ RSpec.describe 'Projects > Members > Manage members', :js, feature_category: :on page.within find_member_row(project_developer) do click_button('Developer') - page.within '.dropdown-menu' do - expect(page).not_to have_button('Owner') - end - - click_button('Reporter') + expect_no_listbox_item('Owner') + select_listbox_item('Reporter') expect(page).to have_button('Reporter') end @@ -87,8 +85,7 @@ RSpec.describe 'Projects > Members > Manage members', :js, feature_category: :on visit_members_page page.within find_member_row(project_owner) do - click_button('Owner') - click_button('Reporter') + select_from_listbox('Reporter', from: 'Owner') expect(page).to have_button('Reporter') end diff --git a/spec/frontend/admin/users/components/user_actions_spec.js b/spec/frontend/admin/users/components/user_actions_spec.js index 73d8c082bb9..69755c6142a 100644 --- a/spec/frontend/admin/users/components/user_actions_spec.js +++ b/spec/frontend/admin/users/components/user_actions_spec.js @@ -91,7 +91,7 @@ describe('AdminUserActions component', () => { initComponent({ actions: [LDAP] }); }); - it('renders the LDAP dropdown item without a link', () => { + it('renders the LDAP dropdown footer without a link', () => { const dropdownAction = wrapper.find(`[data-testid="${LDAP}"]`); expect(dropdownAction.exists()).toBe(true); expect(dropdownAction.attributes('href')).toBe(undefined); diff --git a/spec/frontend/error_tracking/components/error_details_spec.js b/spec/frontend/error_tracking/components/error_details_spec.js index 92f2e62b3a7..c9238c4b636 100644 --- a/spec/frontend/error_tracking/components/error_details_spec.js +++ b/spec/frontend/error_tracking/components/error_details_spec.js @@ -17,6 +17,7 @@ import ErrorDetailsInfo from '~/error_tracking/components/error_details_info.vue import { createAlert, VARIANT_WARNING } from '~/alert'; import { __ } from '~/locale'; import Tracking from '~/tracking'; +import TimelineChart from '~/error_tracking/components/timeline_chart.vue'; jest.mock('~/alert'); jest.mock('~/tracking'); @@ -159,6 +160,7 @@ describe('ErrorDetails', () => { mocks.$apollo.queries.error.loading = false; mountComponent(); // setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details + // TODO remove setData usage https://gitlab.com/gitlab-org/opstrace/opstrace/-/issues/2216 // eslint-disable-next-line no-restricted-syntax wrapper.setData({ error: { @@ -183,6 +185,7 @@ describe('ErrorDetails', () => { beforeEach(() => { store.state.details.loadingStacktrace = false; // setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details + // TODO remove setData usage https://gitlab.com/gitlab-org/opstrace/opstrace/-/issues/2216 // eslint-disable-next-line no-restricted-syntax wrapper.setData({ error: { @@ -204,6 +207,7 @@ describe('ErrorDetails', () => { describe('Badges', () => { it('should show language and error level badges', async () => { // setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details + // TODO remove setData usage https://gitlab.com/gitlab-org/opstrace/opstrace/-/issues/2216 // eslint-disable-next-line no-restricted-syntax wrapper.setData({ error: { @@ -216,6 +220,7 @@ describe('ErrorDetails', () => { it('should NOT show the badge if the tag is not present', async () => { // setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details + // TODO remove setData usage https://gitlab.com/gitlab-org/opstrace/opstrace/-/issues/2216 // eslint-disable-next-line no-restricted-syntax wrapper.setData({ error: { @@ -230,6 +235,7 @@ describe('ErrorDetails', () => { 'should set correct severity level variant for %s badge', async (level) => { // setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details + // TODO remove setData usage https://gitlab.com/gitlab-org/opstrace/opstrace/-/issues/2216 // eslint-disable-next-line no-restricted-syntax wrapper.setData({ error: { @@ -245,6 +251,7 @@ describe('ErrorDetails', () => { it('should fallback for ERROR severityLevelVariant when severityLevel is unknown', async () => { // setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details + // TODO remove setData usage https://gitlab.com/gitlab-org/opstrace/opstrace/-/issues/2216 // eslint-disable-next-line no-restricted-syntax wrapper.setData({ error: { @@ -268,6 +275,32 @@ describe('ErrorDetails', () => { }); }); + describe('timeline chart', () => { + it('should not show timeline chart if frequency data does not exist', () => { + expect(wrapper.findComponent(TimelineChart).exists()).toBe(false); + expect(wrapper.text()).not.toContain('Last 24 hours'); + }); + + it('should show timeline chart', async () => { + const mockFrequency = [ + [0, 1], + [2, 3], + ]; + // setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details + // TODO remove setData usage https://gitlab.com/gitlab-org/opstrace/opstrace/-/issues/2216 + // eslint-disable-next-line no-restricted-syntax + wrapper.setData({ + error: { + frequency: mockFrequency, + }, + }); + await nextTick(); + expect(wrapper.findComponent(TimelineChart).exists()).toBe(true); + expect(wrapper.findComponent(TimelineChart).props('timelineData')).toEqual(mockFrequency); + expect(wrapper.text()).toContain('Last 24 hours'); + }); + }); + describe('Stacktrace', () => { it('should show stacktrace', async () => { store.state.details.loadingStacktrace = false; @@ -402,6 +435,7 @@ describe('ErrorDetails', () => { it('should show alert with closed issueId', async () => { const closedIssueId = 123; // setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details + // TODO remove setData usage https://gitlab.com/gitlab-org/opstrace/opstrace/-/issues/2216 // eslint-disable-next-line no-restricted-syntax wrapper.setData({ isAlertVisible: true, @@ -424,6 +458,7 @@ describe('ErrorDetails', () => { describe('is present', () => { beforeEach(() => { // setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details + // TODO remove setData usage https://gitlab.com/gitlab-org/opstrace/opstrace/-/issues/2216 // eslint-disable-next-line no-restricted-syntax wrapper.setData({ error: { @@ -448,6 +483,7 @@ describe('ErrorDetails', () => { describe('is not present', () => { beforeEach(() => { // setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details + // TODO remove setData usage https://gitlab.com/gitlab-org/opstrace/opstrace/-/issues/2216 // eslint-disable-next-line no-restricted-syntax wrapper.setData({ error: { @@ -482,6 +518,7 @@ describe('ErrorDetails', () => { beforeEach(() => { mountComponent({ integratedErrorTrackingEnabled: integrated }); // setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details + // TODO remove setData usage https://gitlab.com/gitlab-org/opstrace/opstrace/-/issues/2216 // eslint-disable-next-line no-restricted-syntax wrapper.setData({ error: {}, diff --git a/spec/frontend/error_tracking/components/error_tracking_list_spec.js b/spec/frontend/error_tracking/components/error_tracking_list_spec.js index 5483500e1dd..49f365e8c60 100644 --- a/spec/frontend/error_tracking/components/error_tracking_list_spec.js +++ b/spec/frontend/error_tracking/components/error_tracking_list_spec.js @@ -12,6 +12,7 @@ import Vuex from 'vuex'; import stubChildren from 'helpers/stub_children'; import ErrorTrackingActions from '~/error_tracking/components/error_tracking_actions.vue'; import ErrorTrackingList from '~/error_tracking/components/error_tracking_list.vue'; +import TimelineChart from '~/error_tracking/components/timeline_chart.vue'; import Tracking from '~/tracking'; import { extendedWrapper } from 'helpers/vue_test_utils_helper'; import errorsList from './list_mock.json'; @@ -158,6 +159,30 @@ describe('ErrorTrackingList', () => { }); }); + describe('timeline graph', () => { + it('should show the timeline chart', () => { + findErrorListRows().wrappers.forEach((row, index) => { + expect(row.findComponent(TimelineChart).exists()).toBe(true); + const mockFrequency = errorsList[index].frequency; + expect(row.findComponent(TimelineChart).props('timelineData')).toEqual(mockFrequency); + }); + }); + + it('should not show the timeline chart if frequency data does not exist', () => { + store.state.list.errors = errorsList.map((e) => ({ ...e, frequency: undefined })); + mountComponent({ + stubs: { + GlTable: false, + GlLink: false, + }, + }); + + findErrorListRows().wrappers.forEach((row) => { + expect(row.findComponent(TimelineChart).exists()).toBe(false); + }); + }); + }); + describe('filtering', () => { const findSearchBox = () => wrapper.findComponent(GlFormInput); diff --git a/spec/frontend/error_tracking/components/list_mock.json b/spec/frontend/error_tracking/components/list_mock.json index 54ae0a4c7cf..f8addef324e 100644 --- a/spec/frontend/error_tracking/components/list_mock.json +++ b/spec/frontend/error_tracking/components/list_mock.json @@ -7,7 +7,17 @@ "count": "52", "firstSeen": "2019-05-30T07:21:46Z", "lastSeen": "2019-11-06T03:21:39Z", - "status": "unresolved" + "status": "unresolved", + "frequency": [ + [ + 0, + 1 + ], + [ + 1, + 2 + ] + ] }, { "id": "2", @@ -17,7 +27,17 @@ "count": "12", "firstSeen": "2019-10-19T03:53:56Z", "lastSeen": "2019-11-05T03:51:54Z", - "status": "unresolved" + "status": "unresolved", + "frequency": [ + [ + 0, + 1 + ], + [ + 1, + 2 + ] + ] }, { "id": "3", @@ -27,6 +47,16 @@ "count": "275", "firstSeen": "2019-02-12T07:22:36Z", "lastSeen": "2019-10-22T03:20:48Z", - "status": "unresolved" + "status": "unresolved", + "frequency": [ + [ + 0, + 1 + ], + [ + 1, + 2 + ] + ] } -]
\ No newline at end of file +] diff --git a/spec/frontend/error_tracking/components/timeline_chart_spec.js b/spec/frontend/error_tracking/components/timeline_chart_spec.js new file mode 100644 index 00000000000..f864d11804c --- /dev/null +++ b/spec/frontend/error_tracking/components/timeline_chart_spec.js @@ -0,0 +1,94 @@ +import { GlChart } from '@gitlab/ui/dist/charts'; +import { shallowMountExtended } from 'helpers/vue_test_utils_helper'; +import TimelineChart from '~/error_tracking/components/timeline_chart.vue'; + +const MOCK_HEIGHT = 123; + +describe('TimelineChart', () => { + let wrapper; + + function mountComponent(timelineData = []) { + wrapper = shallowMountExtended(TimelineChart, { + stubs: { GlChart: true }, + propsData: { + timelineData: [...timelineData], + height: MOCK_HEIGHT, + }, + }); + } + + beforeEach(() => { + mountComponent(); + }); + + it('renders the component', () => { + expect(wrapper.exists()).toBe(true); + }); + + it('does not render a chart if timelineData is missing', () => { + wrapper = shallowMountExtended(TimelineChart, { + stubs: { GlChart: true }, + propsData: { + timelineData: undefined, + height: MOCK_HEIGHT, + }, + }); + expect(wrapper.findComponent(GlChart).exists()).toBe(false); + }); + + it('renders a gl-chart', () => { + expect(wrapper.findComponent(GlChart).exists()).toBe(true); + expect(wrapper.findComponent(GlChart).props('height')).toBe(MOCK_HEIGHT); + }); + + describe('timeline-data', () => { + describe.each([ + { + mockItems: [ + [1686218400, 1], + [1686222000, 2], + ], + expectedX: ['Jun 8, 2023 10:00am UTC', 'Jun 8, 2023 11:00am UTC'], + expectedY: [1, 2], + description: 'tuples with dates as timestamps in seconds', + }, + { + mockItems: [ + ['06-05-2023', 1], + ['06-06-2023', 2], + ], + expectedX: ['Jun 5, 2023 12:00am UTC', 'Jun 6, 2023 12:00am UTC'], + expectedY: [1, 2], + description: 'tuples with non-numeric dates', + }, + { + mockItems: [ + { time: 1686218400, count: 1 }, + { time: 1686222000, count: 2 }, + ], + expectedX: ['Jun 8, 2023 10:00am UTC', 'Jun 8, 2023 11:00am UTC'], + expectedY: [1, 2], + description: 'objects with dates as timestamps in seconds', + }, + { + mockItems: [ + { time: '06-05-2023', count: 1 }, + { time: '06-06-2023', count: 2 }, + ], + expectedX: ['Jun 5, 2023 12:00am UTC', 'Jun 6, 2023 12:00am UTC'], + expectedY: [1, 2], + description: 'objects with non-numeric dates', + }, + ])('when timeline-data items are $description', ({ mockItems, expectedX, expectedY }) => { + it(`renders the chart correctly`, () => { + mountComponent(mockItems); + + const chartOptions = wrapper.findComponent(GlChart).props('options'); + const xData = chartOptions.xAxis.data; + const yData = chartOptions.series[0].data; + expect(xData).toEqual(expectedX); + expect(yData).toEqual(expectedY); + }); + }); + }); +}); diff --git a/spec/frontend/fixtures/users.rb b/spec/frontend/fixtures/users.rb index 90a6895af91..89bffea7e4c 100644 --- a/spec/frontend/fixtures/users.rb +++ b/spec/frontend/fixtures/users.rb @@ -42,7 +42,7 @@ RSpec.describe 'Users (JavaScript fixtures)', feature_category: :user_profile do context 'for user achievements' do let_it_be(:group) { create(:group, :public) } let_it_be(:private_group) { create(:group, :private) } - let_it_be(:achievement1) { create(:achievement, namespace: group) } + let_it_be(:achievement1) { create(:achievement, namespace: group, name: 'Multiple') } let_it_be(:achievement2) { create(:achievement, namespace: group) } let_it_be(:achievement3) { create(:achievement, namespace: group) } let_it_be(:achievement_from_private_group) { create(:achievement, namespace: private_group) } @@ -94,6 +94,7 @@ RSpec.describe 'Users (JavaScript fixtures)', feature_category: :user_profile do [achievement1, achievement2, achievement3, achievement_with_avatar_and_description].each do |achievement| create(:user_achievement, user: user, achievement: achievement) end + create(:user_achievement, user: user, achievement: achievement1) post_graphql(query, current_user: user, variables: { id: user.to_global_id }) diff --git a/spec/frontend/members/components/table/role_dropdown_spec.js b/spec/frontend/members/components/table/role_dropdown_spec.js index 1045e3f9849..1285404fd9f 100644 --- a/spec/frontend/members/components/table/role_dropdown_spec.js +++ b/spec/frontend/members/components/table/role_dropdown_spec.js @@ -1,8 +1,7 @@ -import { GlDropdown, GlDropdownItem } from '@gitlab/ui'; +import { GlCollapsibleListbox, GlListboxItem } from '@gitlab/ui'; import { GlBreakpointInstance as bp } from '@gitlab/ui/dist/utils'; import * as Sentry from '@sentry/browser'; -import { within } from '@testing-library/dom'; -import { mount, createWrapper } from '@vue/test-utils'; +import { mount } from '@vue/test-utils'; import Vue, { nextTick } from 'vue'; import Vuex from 'vuex'; import waitForPromises from 'helpers/wait_for_promises'; @@ -55,59 +54,50 @@ describe('RoleDropdown', () => { }); }; - const getDropdownMenu = () => within(wrapper.element).getByRole('menu'); - const getByTextInDropdownMenu = (text, options = {}) => - createWrapper(within(getDropdownMenu()).getByText(text, options)); - const getDropdownItemByText = (text) => - createWrapper( - within(getDropdownMenu()) - .getByText(text, { selector: '[role="menuitem"] p' }) - .closest('[role="menuitem"]'), - ); - const getCheckedDropdownItem = () => - wrapper - .findAllComponents(GlDropdownItem) - .wrappers.find((dropdownItemWrapper) => dropdownItemWrapper.props('isChecked')); - - const findDropdownToggle = () => wrapper.find('button[aria-haspopup="menu"]'); - const findDropdown = () => wrapper.findComponent(GlDropdown); + const findListbox = () => wrapper.findComponent(GlCollapsibleListbox); + const findListboxItems = () => wrapper.findAllComponents(GlListboxItem); + const findListboxItemByText = (text) => + findListboxItems().wrappers.find((item) => item.text() === text); beforeEach(() => { gon.features = { showOverageOnRolePromotion: true }; }); - describe('when dropdown is open', () => { + it('has correct header text props', () => { + createComponent(); + expect(findListbox().props('headerText')).toBe('Change role'); + }); + + it('has items prop with all valid roles', () => { + createComponent(); + const roles = findListbox() + .props('items') + .map((item) => item.text); + expect(roles).toEqual(Object.keys(member.validRoles)); + }); + + describe('when listbox is open', () => { beforeEach(async () => { guestOverageConfirmAction.mockReturnValue(true); createComponent(); - await findDropdownToggle().trigger('click'); - }); - - it('renders all valid roles', () => { - Object.keys(member.validRoles).forEach((role) => { - expect(getDropdownItemByText(role).exists()).toBe(true); - }); - }); - - it('renders dropdown header', () => { - expect(getByTextInDropdownMenu('Change role').exists()).toBe(true); + await findListbox().vm.$emit('click'); }); it('sets dropdown toggle and checks selected role', () => { - expect(findDropdownToggle().text()).toBe('Owner'); - expect(getCheckedDropdownItem().text()).toBe('Owner'); + expect(findListbox().props('toggleText')).toBe('Owner'); + expect(findListbox().find('[aria-selected=true]').text()).toBe('Owner'); }); describe('when dropdown item is selected', () => { it('does nothing if the item selected was already selected', async () => { - await getDropdownItemByText('Owner').trigger('click'); + await findListboxItemByText('Owner').trigger('click'); expect(actions.updateMemberRole).not.toHaveBeenCalled(); }); it('calls `updateMemberRole` Vuex action', async () => { - await getDropdownItemByText('Developer').trigger('click'); + await findListboxItemByText('Developer').trigger('click'); expect(actions.updateMemberRole).toHaveBeenCalledWith(expect.any(Object), { memberId: member.id, @@ -117,7 +107,7 @@ describe('RoleDropdown', () => { describe('when updateMemberRole is successful', () => { it('displays toast', async () => { - await getDropdownItemByText('Developer').trigger('click'); + await findListboxItemByText('Developer').trigger('click'); await nextTick(); @@ -125,21 +115,21 @@ describe('RoleDropdown', () => { }); it('puts dropdown in loading state while waiting for `updateMemberRole` to resolve', async () => { - await getDropdownItemByText('Developer').trigger('click'); + await findListboxItemByText('Developer').trigger('click'); - expect(findDropdown().props('loading')).toBe(true); + expect(findListbox().props('loading')).toBe(true); }); it('enables dropdown after `updateMemberRole` resolves', async () => { - await getDropdownItemByText('Developer').trigger('click'); + await findListboxItemByText('Developer').trigger('click'); await waitForPromises(); - expect(findDropdown().props('disabled')).toBe(false); + expect(findListbox().props('disabled')).toBe(false); }); it('does not log error to Sentry', async () => { - await getDropdownItemByText('Developer').trigger('click'); + await findListboxItemByText('Developer').trigger('click'); await waitForPromises(); @@ -155,7 +145,7 @@ describe('RoleDropdown', () => { }); it('does not display toast', async () => { - await getDropdownItemByText('Developer').trigger('click'); + await findListboxItemByText('Developer').trigger('click'); await nextTick(); @@ -163,21 +153,21 @@ describe('RoleDropdown', () => { }); it('puts dropdown in loading state while waiting for `updateMemberRole` to resolve', async () => { - await getDropdownItemByText('Developer').trigger('click'); + await findListboxItemByText('Developer').trigger('click'); - expect(findDropdown().props('loading')).toBe(true); + expect(findListbox().props('loading')).toBe(true); }); it('enables dropdown after `updateMemberRole` resolves', async () => { - await getDropdownItemByText('Developer').trigger('click'); + await findListboxItemByText('Developer').trigger('click'); await waitForPromises(); - expect(findDropdown().props('disabled')).toBe(false); + expect(findListbox().props('disabled')).toBe(false); }); it('logs error to Sentry', async () => { - await getDropdownItemByText('Developer').trigger('click'); + await findListboxItemByText('Developer').trigger('click'); await waitForPromises(); @@ -190,7 +180,7 @@ describe('RoleDropdown', () => { it("sets initial dropdown toggle value to member's role", () => { createComponent(); - expect(findDropdownToggle().text()).toBe('Owner'); + expect(findListbox().props('toggleText')).toBe('Owner'); }); it('sets the dropdown alignment to right on mobile', async () => { @@ -199,7 +189,7 @@ describe('RoleDropdown', () => { await nextTick(); - expect(findDropdown().props('right')).toBe(true); + expect(findListbox().props('placement')).toBe('right'); }); it('sets the dropdown alignment to left on desktop', async () => { @@ -208,7 +198,7 @@ describe('RoleDropdown', () => { await nextTick(); - expect(findDropdown().props('right')).toBe(false); + expect(findListbox().props('placement')).toBe('left'); }); describe('guestOverageConfirmAction', () => { @@ -219,7 +209,7 @@ describe('RoleDropdown', () => { beforeEach(() => { createComponent(); - findDropdownToggle().trigger('click'); + findListbox().vm.$emit('click'); }); afterEach(() => { @@ -230,7 +220,7 @@ describe('RoleDropdown', () => { beforeEach(() => { mockConfirmAction({ confirmed: true }); - getDropdownItemByText('Reporter').trigger('click'); + findListboxItemByText('Reporter').trigger('click'); }); it('calls updateMemberRole', () => { @@ -242,7 +232,7 @@ describe('RoleDropdown', () => { beforeEach(() => { mockConfirmAction({ confirmed: false }); - getDropdownItemByText('Reporter').trigger('click'); + findListboxItemByText('Reporter').trigger('click'); }); it('does not call updateMemberRole', () => { diff --git a/spec/frontend/profile/components/user_achievements_spec.js b/spec/frontend/profile/components/user_achievements_spec.js index ff6f323621a..5743c8575d5 100644 --- a/spec/frontend/profile/components/user_achievements_spec.js +++ b/spec/frontend/profile/components/user_achievements_spec.js @@ -1,5 +1,6 @@ import Vue from 'vue'; import VueApollo from 'vue-apollo'; +import { GlBadge } from '@gitlab/ui'; import getUserAchievementsEmptyResponse from 'test_fixtures/graphql/get_user_achievements_empty_response.json'; import getUserAchievementsLongResponse from 'test_fixtures/graphql/get_user_achievements_long_response.json'; import getUserAchievementsResponse from 'test_fixtures/graphql/get_user_achievements_with_avatar_and_description_response.json'; @@ -63,6 +64,14 @@ describe('UserAchievements', () => { expect(wrapper.findAllByTestId('user-achievement').length).toBe(3); }); + it('renders count for achievements awarded more than once', async () => { + createComponent({ queryHandler: jest.fn().mockResolvedValue(getUserAchievementsLongResponse) }); + + await waitForPromises(); + + expect(achievement().findComponent(GlBadge).text()).toBe('2x'); + }); + it('renders correctly if the achievement is from a private namespace', async () => { createComponent({ queryHandler: jest.fn().mockResolvedValue(getUserAchievementsPrivateGroupResponse), diff --git a/spec/frontend/work_items/components/notes/work_item_add_note_spec.js b/spec/frontend/work_items/components/notes/work_item_add_note_spec.js index 9bb84947db5..e6d20dcb0d9 100644 --- a/spec/frontend/work_items/components/notes/work_item_add_note_spec.js +++ b/spec/frontend/work_items/components/notes/work_item_add_note_spec.js @@ -32,12 +32,14 @@ describe('Work item add note', () => { const findCommentForm = () => wrapper.findComponent(WorkItemCommentForm); const findTextarea = () => wrapper.findByTestId('note-reply-textarea'); + const findWorkItemLockedComponent = () => wrapper.findComponent(WorkItemCommentLocked); const createComponent = async ({ mutationHandler = mutationSuccessHandler, canUpdate = true, + canCreateNote = true, workItemIid = '1', - workItemResponse = workItemByIidResponseFactory({ canUpdate }), + workItemResponse = workItemByIidResponseFactory({ canUpdate, canCreateNote }), signedIn = true, isEditing = true, workItemType = 'Task', @@ -265,4 +267,13 @@ describe('Work item add note', () => { expect(wrapper.attributes('class')).toContain('internal-note'); }); + + describe('when work item`createNote` permission false', () => { + it('cannot add comment', async () => { + await createComponent({ isEditing: false, canCreateNote: false }); + + expect(findWorkItemLockedComponent().exists()).toBe(true); + expect(findCommentForm().exists()).toBe(false); + }); + }); }); diff --git a/spec/frontend/work_items/mock_data.js b/spec/frontend/work_items/mock_data.js index 84e5e76cb4c..a873462ea63 100644 --- a/spec/frontend/work_items/mock_data.js +++ b/spec/frontend/work_items/mock_data.js @@ -110,6 +110,7 @@ export const workItemQueryResponse = { updateWorkItem: false, setWorkItemMetadata: false, adminParentLink: false, + createNote: false, __typename: 'WorkItemPermissions', }, widgets: [ @@ -214,6 +215,7 @@ export const updateWorkItemMutationResponse = { updateWorkItem: false, setWorkItemMetadata: false, adminParentLink: false, + createNote: false, __typename: 'WorkItemPermissions', }, reference: 'test-project-path#1', @@ -322,6 +324,7 @@ export const convertWorkItemMutationResponse = { updateWorkItem: false, setWorkItemMetadata: false, adminParentLink: false, + createNote: false, __typename: 'WorkItemPermissions', }, reference: 'gitlab-org/gitlab-test#1', @@ -418,6 +421,7 @@ export const objectiveType = { export const workItemResponseFactory = ({ canUpdate = false, canDelete = false, + canCreateNote = false, adminParentLink = false, notificationsWidgetPresent = true, currentUserTodosWidgetPresent = true, @@ -473,6 +477,7 @@ export const workItemResponseFactory = ({ updateWorkItem: canUpdate, setWorkItemMetadata: canUpdate, adminParentLink, + createNote: canCreateNote, __typename: 'WorkItemPermissions', }, reference: 'test-project-path#1', @@ -751,6 +756,7 @@ export const createWorkItemMutationResponse = { updateWorkItem: false, setWorkItemMetadata: false, adminParentLink: false, + createNote: false, __typename: 'WorkItemPermissions', }, reference: 'test-project-path#1', @@ -980,6 +986,7 @@ export const workItemHierarchyEmptyResponse = { updateWorkItem: false, setWorkItemMetadata: false, adminParentLink: false, + createNote: false, __typename: 'WorkItemPermissions', }, confidential: false, @@ -1029,6 +1036,7 @@ export const workItemHierarchyNoUpdatePermissionResponse = { updateWorkItem: false, setWorkItemMetadata: false, adminParentLink: false, + createNote: false, __typename: 'WorkItemPermissions', }, project: { @@ -1178,6 +1186,7 @@ export const workItemHierarchyResponse = { updateWorkItem: true, setWorkItemMetadata: true, adminParentLink: true, + createNote: true, __typename: 'WorkItemPermissions', }, author: { @@ -1277,6 +1286,7 @@ export const workItemObjectiveWithChild = { updateWorkItem: true, setWorkItemMetadata: true, adminParentLink: true, + createNote: true, __typename: 'WorkItemPermissions', }, author: { @@ -1346,6 +1356,7 @@ export const workItemHierarchyTreeResponse = { updateWorkItem: true, setWorkItemMetadata: true, adminParentLink: true, + createNote: true, __typename: 'WorkItemPermissions', }, confidential: false, @@ -1426,6 +1437,7 @@ export const changeIndirectWorkItemParentMutationResponse = { updateWorkItem: true, setWorkItemMetadata: true, adminParentLink: true, + createNote: true, __typename: 'WorkItemPermissions', }, description: null, @@ -1493,6 +1505,7 @@ export const changeWorkItemParentMutationResponse = { updateWorkItem: true, setWorkItemMetadata: true, adminParentLink: true, + createNote: true, __typename: 'WorkItemPermissions', }, description: null, diff --git a/spec/lib/gitlab/email/handler/service_desk_handler_spec.rb b/spec/lib/gitlab/email/handler/service_desk_handler_spec.rb index ef2acc9ec92..98522c53a47 100644 --- a/spec/lib/gitlab/email/handler/service_desk_handler_spec.rb +++ b/spec/lib/gitlab/email/handler/service_desk_handler_spec.rb @@ -381,6 +381,125 @@ RSpec.describe Gitlab::Email::Handler::ServiceDeskHandler, feature_category: :se it_behaves_like 'a new issue request' end end + + context 'when receiving a service desk custom email address verification email' do + let(:email_raw) { service_desk_fixture('emails/service_desk_custom_email_address_verification.eml') } + + shared_examples 'an early exiting handler' do + it 'does not trigger the verification process and does not add an issue' do + expect(ServiceDesk::CustomEmailVerifications::UpdateService).to receive(:execute).exactly(0).times + expect { receiver.execute }.to not_change { Issue.count } + end + end + + shared_examples 'a handler that does not verify the custom email' do |error_identifier| + it 'does not verify the custom email address' do + # project has no owner, so only notify verification triggerer + expect(Notify).to receive(:service_desk_verification_result_email).once + + receiver.execute + + expect(settings.reload.custom_email_enabled).to be false + expect(verification.reload).to have_attributes( + state: 'failed', + error: error_identifier + ) + end + end + + shared_examples 'a handler that verifies Service Desk custom email verification emails' do + it_behaves_like 'an early exiting handler' + + context 'with valid service desk settings' do + let_it_be(:user) { create(:user) } + + let!(:settings) { create(:service_desk_setting, project: project, custom_email: 'custom-support-email@example.com') } + let!(:verification) { create(:service_desk_custom_email_verification, project: project, token: 'ZROT4ZZXA-Y6', triggerer: user) } + + let(:message_delivery) { instance_double(ActionMailer::MessageDelivery) } + + before do + project.add_maintainer(user) + + allow(message_delivery).to receive(:deliver_later) + allow(Notify).to receive(:service_desk_verification_result_email).and_return(message_delivery) + end + + it 'successfully verifies the custom email address' do + # project has no owner, so only notify verification triggerer + expect(Notify).to receive(:service_desk_verification_result_email).once + + receiver.execute + + expect(settings.reload.custom_email_enabled).to be false + expect(verification.reload).to have_attributes( + state: 'finished', + error: nil + ) + end + + context 'and custom email address is not the configured subaddress of the project' do + before do + settings.update!(custom_email: 'custom-support-email@example.com') + end + + it_behaves_like 'an early exiting handler' + end + + context 'and verification tokens do not match' do + before do + verification.update!(token: 'XXXXXXXXXXXX') + end + + it_behaves_like 'a handler that does not verify the custom email', 'incorrect_token' + end + + context 'and verification email ingested too late' do + before do + verification.update!(triggered_at: ServiceDesk::CustomEmailVerification::TIMEFRAME.ago) + end + + it_behaves_like 'a handler that does not verify the custom email', 'mail_not_received_within_timeframe' + end + + context 'and from header differs from custom email address' do + before do + settings.update!(custom_email: 'different-from@example.com') + end + + it_behaves_like 'a handler that does not verify the custom email', 'incorrect_from' + end + end + + context 'when service_desk_custom_email feature flag is disabled' do + before do + stub_feature_flags(service_desk_custom_email: false) + end + + it 'does not trigger the verification process and adds an issue instead' do + expect { receiver.execute }.to change { Issue.count }.by(1) + end + end + end + + context 'when using incoming_email address' do + before do + stub_incoming_email_setting(enabled: true, address: 'support+%{key}@example.com') + end + + it_behaves_like 'a handler that verifies Service Desk custom email verification emails' + end + + context 'when using service_desk_email address' do + let(:receiver) { Gitlab::Email::ServiceDeskReceiver.new(email_raw) } + + before do + stub_service_desk_email_setting(enabled: true, address: 'support+%{key}@example.com') + end + + it_behaves_like 'a handler that verifies Service Desk custom email verification emails' + end + end end context 'when issue email creation fails' do diff --git a/spec/lib/gitlab/metrics/environment_spec.rb b/spec/lib/gitlab/metrics/environment_spec.rb index e94162e625e..4e3b1b5273e 100644 --- a/spec/lib/gitlab/metrics/environment_spec.rb +++ b/spec/lib/gitlab/metrics/environment_spec.rb @@ -1,8 +1,7 @@ # frozen_string_literal: true require 'fast_spec_helper' require 'rspec-parameterized' - -require_relative '../../../support/helpers/stub_env' +require 'gitlab/rspec/all' RSpec.describe Gitlab::Metrics::Environment, feature_category: :error_budgets do include StubENV diff --git a/spec/requests/api/projects_spec.rb b/spec/requests/api/projects_spec.rb index bb96771b3d5..53e216323d1 100644 --- a/spec/requests/api/projects_spec.rb +++ b/spec/requests/api/projects_spec.rb @@ -223,14 +223,6 @@ RSpec.describe API::Projects, :aggregate_failures, feature_category: :groups_and include_examples 'includes container_registry_access_level' - context 'when projects_preloader_fix is disabled' do - before do - stub_feature_flags(projects_preloader_fix: false) - end - - include_examples 'includes container_registry_access_level' - end - it 'includes various project feature fields' do get api(path, user) project_response = json_response.find { |p| p['id'] == project.id } diff --git a/spec/scripts/generate_failed_package_and_test_mr_message_spec.rb b/spec/scripts/generate_failed_package_and_test_mr_message_spec.rb index 79e63c95f65..7aff33855aa 100644 --- a/spec/scripts/generate_failed_package_and_test_mr_message_spec.rb +++ b/spec/scripts/generate_failed_package_and_test_mr_message_spec.rb @@ -1,8 +1,8 @@ # frozen_string_literal: true require 'fast_spec_helper' +require 'gitlab/rspec/all' require_relative '../../scripts/generate-failed-package-and-test-mr-message' -require_relative '../support/helpers/stub_env' RSpec.describe GenerateFailedPackageAndTestMrMessage, feature_category: :tooling do include StubENV diff --git a/spec/scripts/generate_message_to_run_e2e_pipeline_spec.rb b/spec/scripts/generate_message_to_run_e2e_pipeline_spec.rb index aee16334003..61307937101 100644 --- a/spec/scripts/generate_message_to_run_e2e_pipeline_spec.rb +++ b/spec/scripts/generate_message_to_run_e2e_pipeline_spec.rb @@ -3,8 +3,8 @@ # rubocop:disable RSpec/VerifiedDoubles require 'fast_spec_helper' +require 'gitlab/rspec/all' require_relative '../../scripts/generate-message-to-run-e2e-pipeline' -require_relative '../support/helpers/stub_env' RSpec.describe GenerateMessageToRunE2ePipeline, feature_category: :tooling do include StubENV diff --git a/spec/services/merge_requests/refresh_service_spec.rb b/spec/services/merge_requests/refresh_service_spec.rb index 4d533b67690..4271a71372e 100644 --- a/spec/services/merge_requests/refresh_service_spec.rb +++ b/spec/services/merge_requests/refresh_service_spec.rb @@ -1024,4 +1024,61 @@ RSpec.describe MergeRequests::RefreshService, feature_category: :code_review_wor end end end + + describe '#abort_auto_merges' do + let_it_be(:project) { create(:project, :repository) } + let_it_be(:user) { create(:user) } + let_it_be(:author) { user } + + let_it_be(:merge_request, refind: true) do + create( + :merge_request, + source_project: project, + target_project: project, + merge_user: user, + auto_merge_enabled: true, + auto_merge_strategy: AutoMergeService::STRATEGY_MERGE_WHEN_PIPELINE_SUCCEEDS + ) + end + + let(:service) { described_class.new(project: project, current_user: user) } + let(:oldrev) { merge_request.diff_refs.base_sha } + let(:newrev) { merge_request.diff_refs.head_sha } + let(:merge_sha) { oldrev } + let(:fix_interrupted_mwps) { true } + + before do + stub_feature_flags(fix_interrupted_mwps: fix_interrupted_mwps) + + merge_request.merge_params[:sha] = merge_sha + merge_request.save! + + service.execute(oldrev, newrev, "refs/heads/#{merge_request.source_branch}") + + merge_request.reload + end + + it 'aborts MWPS for merge requests' do + expect(merge_request.auto_merge_enabled?).to be_falsey + expect(merge_request.merge_user).to be_nil + end + + context 'when merge params contains up-to-date sha' do + let(:merge_sha) { newrev } + + it 'maintains MWPS for merge requests' do + expect(merge_request.auto_merge_enabled?).to be_truthy + expect(merge_request.merge_user).to eq(user) + end + + context 'when fix_interrupted_mwps ff is disabled' do + let(:fix_interrupted_mwps) { false } + + it 'aborts MWPS for merge requests' do + expect(merge_request.auto_merge_enabled?).to be_falsey + expect(merge_request.merge_user).to be_nil + end + end + end + end end diff --git a/spec/support/helpers/stub_env.rb b/spec/support/helpers/stub_env.rb deleted file mode 100644 index afa501d6279..00000000000 --- a/spec/support/helpers/stub_env.rb +++ /dev/null @@ -1,52 +0,0 @@ -# frozen_string_literal: true - -# Inspired by https://github.com/ljkbennett/stub_env/blob/master/lib/stub_env/helpers.rb -module StubENV - # Stub ENV variables - # - # You can provide either a key and value as separate params or both in a Hash format - # - # Keys and values will always be converted to String, to comply with how ENV behaves - # - # @param key_or_hash [String, Hash<String,String>] - # @param value [String] - def stub_env(key_or_hash, value = nil) - init_stub unless env_stubbed? - - if key_or_hash.is_a? Hash - key_or_hash.each do |key, value| - add_stubbed_value(key, ensure_env_type(value)) - end - else - add_stubbed_value key_or_hash, ensure_env_type(value) - end - end - - private - - STUBBED_KEY = '__STUBBED__' - - def add_stubbed_value(key, value) - allow(ENV).to receive(:[]).with(key).and_return(value) - allow(ENV).to receive(:key?).with(key).and_return(true) - allow(ENV).to receive(:fetch).with(key).and_return(value) - allow(ENV).to receive(:fetch).with(key, anything) do |_, default_val| - value || default_val - end - end - - def env_stubbed? - ENV[STUBBED_KEY] - end - - def init_stub - allow(ENV).to receive(:[]).and_call_original - allow(ENV).to receive(:key?).and_call_original - allow(ENV).to receive(:fetch).and_call_original - add_stubbed_value(STUBBED_KEY, true) - end - - def ensure_env_type(value) - value.nil? ? value : value.to_s - end -end diff --git a/spec/support/rspec.rb b/spec/support/rspec.rb index 94c43669173..b34f8fe9a22 100644 --- a/spec/support/rspec.rb +++ b/spec/support/rspec.rb @@ -5,10 +5,9 @@ require_relative "system_exit_detected" require_relative "helpers/stub_configuration" require_relative "helpers/stub_metrics" require_relative "helpers/stub_object_storage" -require_relative "helpers/stub_env" require_relative "helpers/fast_rails_root" -require_relative "../../lib/gitlab/utils" +require 'gitlab/rspec/all' RSpec::Expectations.configuration.on_potential_false_positives = :raise diff --git a/spec/support/shared_contexts/email_shared_context.rb b/spec/support/shared_contexts/email_shared_context.rb index 12d4af5170b..8b4c1c1e243 100644 --- a/spec/support/shared_contexts/email_shared_context.rb +++ b/spec/support/shared_contexts/email_shared_context.rb @@ -24,7 +24,10 @@ end def service_desk_fixture(path, slug: nil, key: 'mykey') slug ||= project.full_path_slug.to_s - fixture_file(path).gsub('project_slug', slug).gsub('project_key', key) + fixture_file(path) + .gsub('project_slug', slug) + .gsub('project_key', key) + .gsub('project_id', project.project_id.to_s) end RSpec.shared_examples 'reply processing shared examples' do diff --git a/spec/tooling/danger/project_helper_spec.rb b/spec/tooling/danger/project_helper_spec.rb index 3910f569400..fb0603a207a 100644 --- a/spec/tooling/danger/project_helper_spec.rb +++ b/spec/tooling/danger/project_helper_spec.rb @@ -5,9 +5,9 @@ require 'gitlab-dangerfiles' require 'danger' require 'danger/plugins/internal/helper' require 'gitlab/dangerfiles/spec_helper' +require 'gitlab/rspec/all' require_relative '../../../danger/plugins/project_helper' -require_relative '../../../spec/support/helpers/stub_env' RSpec.describe Tooling::Danger::ProjectHelper do include StubENV diff --git a/spec/tooling/lib/tooling/find_changes_spec.rb b/spec/tooling/lib/tooling/find_changes_spec.rb index 43c3da5699d..fef29ad3f2c 100644 --- a/spec/tooling/lib/tooling/find_changes_spec.rb +++ b/spec/tooling/lib/tooling/find_changes_spec.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true require_relative '../../../../tooling/lib/tooling/find_changes' -require_relative '../../../support/helpers/stub_env' +require 'gitlab/rspec/all' require 'json' require 'tempfile' diff --git a/spec/tooling/lib/tooling/find_tests_spec.rb b/spec/tooling/lib/tooling/find_tests_spec.rb index 905f81c4bbd..67b6650b335 100644 --- a/spec/tooling/lib/tooling/find_tests_spec.rb +++ b/spec/tooling/lib/tooling/find_tests_spec.rb @@ -2,7 +2,7 @@ require 'tempfile' require_relative '../../../../tooling/lib/tooling/find_tests' -require_relative '../../../support/helpers/stub_env' +require 'gitlab/rspec/all' RSpec.describe Tooling::FindTests, feature_category: :tooling do include StubENV diff --git a/spec/tooling/lib/tooling/gettext_extractor_spec.rb b/spec/tooling/lib/tooling/gettext_extractor_spec.rb index 3c0f91342c2..14310c804f1 100644 --- a/spec/tooling/lib/tooling/gettext_extractor_spec.rb +++ b/spec/tooling/lib/tooling/gettext_extractor_spec.rb @@ -1,9 +1,9 @@ # frozen_string_literal: true require 'rspec/parameterized' +require 'gitlab/rspec/all' require_relative '../../../../tooling/lib/tooling/gettext_extractor' -require_relative '../../../support/helpers/stub_env' require_relative '../../../support/tmpdir' RSpec.describe Tooling::GettextExtractor, feature_category: :tooling do diff --git a/spec/tooling/lib/tooling/predictive_tests_spec.rb b/spec/tooling/lib/tooling/predictive_tests_spec.rb index b82364fe6f6..fdb7d09a3e2 100644 --- a/spec/tooling/lib/tooling/predictive_tests_spec.rb +++ b/spec/tooling/lib/tooling/predictive_tests_spec.rb @@ -2,8 +2,8 @@ require 'tempfile' require 'fileutils' +require 'gitlab/rspec/all' require_relative '../../../../tooling/lib/tooling/predictive_tests' -require_relative '../../../support/helpers/stub_env' RSpec.describe Tooling::PredictiveTests, feature_category: :tooling do include StubENV diff --git a/spec/tooling/rspec_flaky/config_spec.rb b/spec/tooling/rspec_flaky/config_spec.rb index 63f42d7c6cc..8ba4dd1640e 100644 --- a/spec/tooling/rspec_flaky/config_spec.rb +++ b/spec/tooling/rspec_flaky/config_spec.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true require 'rspec-parameterized' -require_relative '../../support/helpers/stub_env' +require 'gitlab/rspec/all' require_relative '../../../tooling/rspec_flaky/config' diff --git a/spec/tooling/rspec_flaky/flaky_example_spec.rb b/spec/tooling/rspec_flaky/flaky_example_spec.rb index 511f3286f56..68e40bc1050 100644 --- a/spec/tooling/rspec_flaky/flaky_example_spec.rb +++ b/spec/tooling/rspec_flaky/flaky_example_spec.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require_relative '../../support/helpers/stub_env' +require 'gitlab/rspec/all' require_relative '../../support/time_travel' require_relative '../../../tooling/rspec_flaky/flaky_example' diff --git a/spec/tooling/rspec_flaky/listener_spec.rb b/spec/tooling/rspec_flaky/listener_spec.rb index 0bbd6454969..c0b71403347 100644 --- a/spec/tooling/rspec_flaky/listener_spec.rb +++ b/spec/tooling/rspec_flaky/listener_spec.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require_relative '../../support/helpers/stub_env' +require 'gitlab/rspec/all' require_relative '../../support/time_travel' require_relative '../../../tooling/rspec_flaky/listener' |