diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2023-08-10 15:09:48 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2023-08-10 15:09:48 +0300 |
commit | 1c00bf77814669d7d35c8aede82553c7e8883e18 (patch) | |
tree | 34f35f73a4631c81c18b07b05a4cf5886c5c2cec /spec | |
parent | f605b80ff70b395afa345bad11c7f8aa0506a9bf (diff) |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'spec')
16 files changed, 382 insertions, 124 deletions
diff --git a/spec/features/protected_branches_spec.rb b/spec/features/protected_branches_spec.rb index 9244cafbc0b..ee5d92b7cdb 100644 --- a/spec/features/protected_branches_spec.rb +++ b/spec/features/protected_branches_spec.rb @@ -40,6 +40,8 @@ RSpec.describe 'Protected Branches', :js, feature_category: :source_code_managem it 'allows to create a protected branch with name containing HTML tags' do visit project_protected_branches_path(project) + + show_add_form set_defaults set_protected_branch_name('foo<b>bar<\b>') click_on "Protect" @@ -89,6 +91,8 @@ RSpec.describe 'Protected Branches', :js, feature_category: :source_code_managem describe "explicit protected branches" do it "allows creating explicit protected branches" do visit project_protected_branches_path(project) + + show_add_form set_defaults set_protected_branch_name('some->branch') click_on "Protect" @@ -100,6 +104,8 @@ RSpec.describe 'Protected Branches', :js, feature_category: :source_code_managem it "shows success alert once protected branch is created" do visit project_protected_branches_path(project) + + show_add_form set_defaults set_protected_branch_name('some->branch') click_on "Protect" @@ -112,6 +118,8 @@ RSpec.describe 'Protected Branches', :js, feature_category: :source_code_managem project.repository.add_branch(admin, 'some-branch', commit.id) visit project_protected_branches_path(project) + + show_add_form set_defaults set_protected_branch_name('some-branch') click_on "Protect" @@ -124,6 +132,8 @@ RSpec.describe 'Protected Branches', :js, feature_category: :source_code_managem it "displays an error message if the named branch does not exist" do visit project_protected_branches_path(project) + + show_add_form set_defaults set_protected_branch_name('some-branch') click_on "Protect" @@ -135,6 +145,8 @@ RSpec.describe 'Protected Branches', :js, feature_category: :source_code_managem describe "wildcard protected branches" do it "allows creating protected branches with a wildcard" do visit project_protected_branches_path(project) + + show_add_form set_defaults set_protected_branch_name('*-stable') click_on "Protect" @@ -149,6 +161,8 @@ RSpec.describe 'Protected Branches', :js, feature_category: :source_code_managem project.repository.add_branch(admin, 'staging-stable', 'master') visit project_protected_branches_path(project) + + show_add_form set_defaults set_protected_branch_name('*-stable') click_on "Protect" @@ -164,6 +178,8 @@ RSpec.describe 'Protected Branches', :js, feature_category: :source_code_managem project.repository.add_branch(admin, 'development', 'master') visit project_protected_branches_path(project) + + show_add_form set_protected_branch_name('*-stable') set_defaults click_on "Protect" diff --git a/spec/frontend/super_sidebar/components/global_search/components/global_search_default_issuables_spec.js b/spec/frontend/super_sidebar/components/global_search/components/global_search_default_issuables_spec.js new file mode 100644 index 00000000000..934cb1678df --- /dev/null +++ b/spec/frontend/super_sidebar/components/global_search/components/global_search_default_issuables_spec.js @@ -0,0 +1,139 @@ +import { GlDisclosureDropdownGroup, GlDisclosureDropdownItem } from '@gitlab/ui'; +import Vue from 'vue'; +import Vuex from 'vuex'; +import { shallowMount } from '@vue/test-utils'; +import GlobalSearchDefaultIssuables from '~/super_sidebar/components/global_search/components/global_search_default_issuables.vue'; +import { + MOCK_SEARCH_CONTEXT, + MOCK_PROJECT_SEARCH_CONTEXT, + MOCK_GROUP_SEARCH_CONTEXT, + MOCK_DEFAULT_SEARCH_OPTIONS, +} from '../mock_data'; + +Vue.use(Vuex); + +describe('GlobalSearchDefaultPlaces', () => { + let wrapper; + + const createComponent = ({ + searchContext = null, + mockDefaultSearchOptions = [], + ...options + } = {}) => { + const store = new Vuex.Store({ + state: { + searchContext, + }, + getters: { + defaultSearchOptions: () => mockDefaultSearchOptions, + }, + }); + + wrapper = shallowMount(GlobalSearchDefaultIssuables, { + store, + stubs: { + GlDisclosureDropdownGroup, + }, + ...options, + }); + }; + + const findGroup = () => wrapper.findComponent(GlDisclosureDropdownGroup); + const findItems = () => wrapper.findAllComponents(GlDisclosureDropdownItem); + + describe('given no contextSwitcherLinks', () => { + beforeEach(() => { + createComponent(); + }); + + it('renders nothing', () => { + expect(wrapper.html()).toBe(''); + }); + + it('emits a nothing-to-render event', () => { + expect(wrapper.emitted('nothing-to-render')).toEqual([[]]); + }); + }); + + describe('given some contextSwitcherLinks', () => { + beforeEach(() => { + createComponent({ + searchContext: MOCK_SEARCH_CONTEXT, + mockDefaultSearchOptions: MOCK_DEFAULT_SEARCH_OPTIONS, + attrs: { + bordered: true, + class: 'test-class', + }, + }); + }); + + it('renders a disclosure dropdown group', () => { + expect(findGroup().exists()).toBe(true); + }); + + it('renders the expected header', () => { + expect(wrapper.text()).toContain('All GitLab'); + }); + + it('passes attrs down', () => { + const group = findGroup(); + expect(group.props('bordered')).toBe(true); + expect(group.classes()).toContain('test-class'); + }); + + it('renders the links', () => { + const itemProps = findItems().wrappers.map((item) => item.props('item')); + + expect(itemProps).toEqual([ + { + text: 'Issues assigned to me', + href: '/dashboard/issues/?assignee_username=anyone', + }, + { + text: "Issues I've created", + href: '/dashboard/issues/?author_username=anyone', + }, + { + text: 'Merge requests assigned to me', + href: '/dashboard/merge_requests/?assignee_username=anyone', + }, + { + text: "Merge requests that I'm a reviewer", + href: '/dashboard/merge_requests/?reviewer_username=anyone', + }, + { + text: "Merge requests I've created", + href: '/dashboard/merge_requests/?author_username=anyone', + }, + ]); + }); + }); + + describe('group name', () => { + describe('in a project context', () => { + beforeEach(() => { + createComponent({ + searchContext: MOCK_PROJECT_SEARCH_CONTEXT, + mockDefaultSearchOptions: MOCK_DEFAULT_SEARCH_OPTIONS, + }); + }); + + it('renders the expected header', () => { + expect(wrapper.text()).toContain('MockProject'); + }); + }); + + describe('in a group context', () => { + beforeEach(() => { + createComponent({ + searchContext: MOCK_GROUP_SEARCH_CONTEXT, + mockDefaultSearchOptions: MOCK_DEFAULT_SEARCH_OPTIONS, + }); + }); + + it('renders the expected header', () => { + expect(wrapper.text()).toContain('MockGroup'); + }); + }); + }); +}); diff --git a/spec/frontend/super_sidebar/components/global_search/components/global_search_default_items_spec.js b/spec/frontend/super_sidebar/components/global_search/components/global_search_default_items_spec.js index 0fb6585e8ca..18a6e4aa35a 100644 --- a/spec/frontend/super_sidebar/components/global_search/components/global_search_default_items_spec.js +++ b/spec/frontend/super_sidebar/components/global_search/components/global_search_default_items_spec.js @@ -1,110 +1,59 @@ -import { GlDisclosureDropdownGroup, GlDisclosureDropdownItem } from '@gitlab/ui'; -import Vue from 'vue'; -import Vuex from 'vuex'; -import { shallowMountExtended } from 'helpers/vue_test_utils_helper'; +import { shallowMount } from '@vue/test-utils'; import GlobalSearchDefaultItems from '~/super_sidebar/components/global_search/components/global_search_default_items.vue'; -import { MOCK_SEARCH_CONTEXT, MOCK_DEFAULT_SEARCH_OPTIONS } from '../mock_data'; -import { contextSwitcherLinks } from '../../../mock_data'; - -Vue.use(Vuex); +import GlobalSearchDefaultPlaces from '~/super_sidebar/components/global_search/components/global_search_default_places.vue'; +import GlobalSearchDefaultIssuables from '~/super_sidebar/components/global_search/components/global_search_default_issuables.vue'; describe('GlobalSearchDefaultItems', () => { let wrapper; - const createComponent = ({ - storeState, - mockDefaultSearchOptions = MOCK_DEFAULT_SEARCH_OPTIONS, - ...options - } = {}) => { - const store = new Vuex.Store({ - state: { - searchContext: MOCK_SEARCH_CONTEXT, - ...storeState, - }, - getters: { - defaultSearchOptions: () => mockDefaultSearchOptions, - }, - }); - - wrapper = shallowMountExtended(GlobalSearchDefaultItems, { - store, - provide: { - contextSwitcherLinks, - }, - stubs: { - GlDisclosureDropdownGroup, - }, - ...options, - }); + const createComponent = () => { + wrapper = shallowMount(GlobalSearchDefaultItems); }; - const findGroups = () => wrapper.findAllComponents(GlDisclosureDropdownGroup); - const findItems = (root = wrapper) => root.findAllComponents(GlDisclosureDropdownItem); - - describe('template', () => { - describe('Dropdown items', () => { - beforeEach(() => { - createComponent(); - }); - - it('renders two groups', () => { - const groups = findGroups(); - - expect(groups).toHaveLength(2); - - const actualNames = groups.wrappers.map((group) => group.props('group').name); - expect(actualNames).toEqual(['Places', 'All GitLab']); - }); - - it('renders context switcher links in first group', () => { - const group = findGroups().at(0); - expect(group.props('group').name).toBe('Places'); + const findPlaces = () => wrapper.findComponent(GlobalSearchDefaultPlaces); + const findIssuables = () => wrapper.findComponent(GlobalSearchDefaultIssuables); + const receivedAttrs = (wrapperInstance) => ({ + // See https://github.com/vuejs/test-utils/issues/2151. + ...wrapperInstance.vm.$attrs, + }); - const items = findItems(group); - expect(items).toHaveLength(contextSwitcherLinks.length); - }); + beforeEach(() => { + createComponent(); + }); - it('renders default search options in second group', () => { - const group = findGroups().at(1); - expect(group.props('group').name).toBe('All GitLab'); + describe('all child components can render', () => { + it('renders the components', () => { + expect(findPlaces().exists()).toBe(true); + expect(findIssuables().exists()).toBe(true); + }); - const items = findItems(group); - expect(items).toHaveLength(MOCK_DEFAULT_SEARCH_OPTIONS.length); - }); + it('sets the expected props on first component', () => { + const places = findPlaces(); + expect(receivedAttrs(places)).toEqual({}); + expect(places.classes()).toEqual([]); }); - describe('Empty groups', () => { - beforeEach(() => { - createComponent({ mockDefaultSearchOptions: [], provide: { contextSwitcherLinks: [] } }); - }); + it('sets the expected props on second component', () => { + const issuables = findIssuables(); + expect(receivedAttrs(issuables)).toEqual({ bordered: true }); + expect(issuables.classes()).toEqual(['gl-mt-3']); + }); + }); - it('does not render groups with no items', () => { - expect(findGroups()).toHaveLength(0); - }); + describe('when a child component emits nothing-to-render', () => { + beforeEach(() => { + findPlaces().vm.$emit('nothing-to-render'); }); - describe.each` - group | project | groupHeader - ${null} | ${null} | ${'All GitLab'} - ${{ name: 'Test Group' }} | ${null} | ${'Test Group'} - ${{ name: 'Test Group' }} | ${{ name: 'Test Project' }} | ${'Test Project'} - `('Current context header', ({ group, project, groupHeader }) => { - describe(`when group is ${group?.name} and project is ${project?.name}`, () => { - beforeEach(() => { - createComponent({ - storeState: { - searchContext: { - group, - project, - }, - }, - }); - }); + it('does not render the component', () => { + expect(findPlaces().exists()).toBe(false); + expect(findIssuables().exists()).toBe(true); + }); - it(`should render as ${groupHeader}`, () => { - expect(wrapper.text()).toContain(groupHeader); - }); - }); + it('sets the expected props on first component', () => { + const issuables = findIssuables(); + expect(receivedAttrs(issuables)).toEqual({}); + expect(issuables.classes()).toEqual([]); }); }); }); diff --git a/spec/frontend/super_sidebar/components/global_search/components/global_search_default_places_spec.js b/spec/frontend/super_sidebar/components/global_search/components/global_search_default_places_spec.js new file mode 100644 index 00000000000..c6126a348f5 --- /dev/null +++ b/spec/frontend/super_sidebar/components/global_search/components/global_search_default_places_spec.js @@ -0,0 +1,78 @@ +import { GlDisclosureDropdownGroup, GlDisclosureDropdownItem } from '@gitlab/ui'; +import { shallowMount } from '@vue/test-utils'; +import GlobalSearchDefaultPlaces from '~/super_sidebar/components/global_search/components/global_search_default_places.vue'; +import { contextSwitcherLinks } from '../../../mock_data'; + +describe('GlobalSearchDefaultPlaces', () => { + let wrapper; + + const createComponent = ({ links = [], attrs } = {}) => { + wrapper = shallowMount(GlobalSearchDefaultPlaces, { + provide: { + contextSwitcherLinks: links, + }, + attrs, + stubs: { + GlDisclosureDropdownGroup, + }, + }); + }; + + const findGroup = () => wrapper.findComponent(GlDisclosureDropdownGroup); + const findItems = () => wrapper.findAllComponents(GlDisclosureDropdownItem); + + describe('given no contextSwitcherLinks', () => { + beforeEach(() => { + createComponent(); + }); + + it('renders nothing', () => { + expect(wrapper.html()).toBe(''); + }); + + it('emits a nothing-to-render event', () => { + expect(wrapper.emitted('nothing-to-render')).toEqual([[]]); + }); + }); + + describe('given some contextSwitcherLinks', () => { + beforeEach(() => { + createComponent({ + links: contextSwitcherLinks, + attrs: { + bordered: true, + class: 'test-class', + }, + }); + }); + + it('renders a disclosure dropdown group', () => { + expect(findGroup().exists()).toBe(true); + }); + + it('renders the expected header', () => { + expect(wrapper.text()).toContain('Places'); + }); + + it('passes attrs down', () => { + const group = findGroup(); + expect(group.props('bordered')).toBe(true); + expect(group.classes()).toContain('test-class'); + }); + + it('renders the links', () => { + const itemProps = findItems().wrappers.map((item) => item.props('item')); + + expect(itemProps).toEqual([ + { + text: 'Explore', + href: '/explore', + }, + { + text: 'Admin area', + href: '/admin', + }, + ]); + }); + }); +}); diff --git a/spec/frontend/super_sidebar/mock_data.js b/spec/frontend/super_sidebar/mock_data.js index 0d34329c60d..6fb9715824f 100644 --- a/spec/frontend/super_sidebar/mock_data.js +++ b/spec/frontend/super_sidebar/mock_data.js @@ -73,6 +73,7 @@ export const mergeRequestMenuGroup = [ export const contextSwitcherLinks = [ { title: 'Explore', link: '/explore', icon: 'compass', link_classes: 'persistent-link-class' }, + { title: 'Admin area', link: '/admin', icon: 'admin' }, ]; export const sidebarData = { diff --git a/spec/frontend/vue_compat_test_setup.js b/spec/frontend/vue_compat_test_setup.js index fe43f8f2617..ad1230f2ca9 100644 --- a/spec/frontend/vue_compat_test_setup.js +++ b/spec/frontend/vue_compat_test_setup.js @@ -21,6 +21,13 @@ function isLegacyExtendedComponent(component) { function unwrapLegacyVueExtendComponent(selector) { return isLegacyExtendedComponent(selector) ? selector.options : selector; } +function getStubProps(component) { + const stubProps = { ...component.props }; + component.mixins?.forEach((mixin) => { + Object.assign(stubProps, unwrapLegacyVueExtendComponent(mixin).props); + }); + return stubProps; +} if (global.document) { const compatConfig = { @@ -148,33 +155,27 @@ if (global.document) { return true; }; - VTU.config.plugins.createStubs = ({ name, component: rawComponent, registerStub }) => { + VTU.config.plugins.createStubs = ({ name, component: rawComponent, registerStub, stubs }) => { const component = unwrapLegacyVueExtendComponent(rawComponent); const hyphenatedName = name.replace(/\B([A-Z])/g, '-$1').toLowerCase(); + const stubTag = stubs?.[name] ? name : hyphenatedName; const stub = Vue.defineComponent({ name: getComponentName(component), - props: component.props, - model: component.model, + props: getStubProps(component), + model: component.model ?? component.mixins?.find((m) => m.model), methods: Object.fromEntries( Object.entries(component.methods ?? {}).map(([key]) => [key, noop]), ), render() { - const { - $slots: slots = {}, - $scopedSlots: scopedSlots = {}, - $parent: parent, - $vnode: vnode, - } = this; - - const hasStaticDefaultSlot = 'default' in slots && !('default' in scopedSlots); - const isTheOnlyChild = parent?.$.subTree === vnode; - // this condition should be altered when https://github.com/vuejs/vue-test-utils/pull/2068 is merged - // and our codebase will be updated to include it (@vue/test-utils@1.3.6 I assume) - const shouldRenderAllSlots = !hasStaticDefaultSlot && isTheOnlyChild; + const { $scopedSlots: scopedSlots = {} } = this; + + // eslint-disable-next-line no-underscore-dangle + const hasDefaultSlot = 'default' in scopedSlots && scopedSlots.default._ns; + const shouldRenderAllSlots = !component.functional && !hasDefaultSlot; const renderSlotByName = (slotName) => { - const slot = scopedSlots[slotName] || slots[slotName]; + const slot = scopedSlots[slotName]; let result; if (typeof slot === 'function') { try { @@ -189,16 +190,16 @@ if (global.document) { }; const slotContents = shouldRenderAllSlots - ? [...new Set([...Object.keys(slots), ...Object.keys(scopedSlots)])] - .map(renderSlotByName) - .filter(Boolean) + ? Object.keys(scopedSlots).map(renderSlotByName).filter(Boolean) : renderSlotByName('default'); const props = Object.fromEntries( - Object.entries(this.$props).filter(([prop]) => isPropertyValidOnDomNode(prop)), + Object.entries(this.$props) + .filter(([prop]) => isPropertyValidOnDomNode(prop)) + .map(([key, value]) => [key, typeof value === 'function' ? '[Function]' : value]), ); - return Vue.h(`${hyphenatedName || 'anonymous'}-stub`, props, slotContents); + return Vue.h(`${stubTag || 'anonymous'}-stub`, props, slotContents); }, }); diff --git a/spec/lib/gitlab/hook_data/issue_builder_spec.rb b/spec/lib/gitlab/hook_data/issue_builder_spec.rb index b9490306410..9f7aaa21f5b 100644 --- a/spec/lib/gitlab/hook_data/issue_builder_spec.rb +++ b/spec/lib/gitlab/hook_data/issue_builder_spec.rb @@ -2,9 +2,13 @@ require 'spec_helper' -RSpec.describe Gitlab::HookData::IssueBuilder do - let_it_be(:label) { create(:label) } - let_it_be(:issue) { create(:labeled_issue, labels: [label], project: label.project) } +RSpec.describe Gitlab::HookData::IssueBuilder, feature_category: :webhooks do + let_it_be(:group) { create(:group) } + let_it_be(:project) { create(:project, group: group) } + let_it_be(:label) { create(:label, project: project) } + let_it_be(:issue) { create(:labeled_issue, labels: [label], project: project) } + let_it_be(:contact) { create(:contact, group: project.group) } + let_it_be(:issue_contact) { create(:issue_customer_relations_contact, issue: issue, contact: contact) } let(:builder) { described_class.new(issue) } @@ -50,6 +54,7 @@ RSpec.describe Gitlab::HookData::IssueBuilder do expect(data).to include(:state) expect(data).to include(:severity) expect(data).to include('labels' => [label.hook_attrs]) + expect(data).to include('customer_relations_contacts' => [contact.reload.hook_attrs]) end context 'when the issue has an image in the description' do diff --git a/spec/models/customer_relations/contact_spec.rb b/spec/models/customer_relations/contact_spec.rb index 3d78a9089ca..6f124662b8e 100644 --- a/spec/models/customer_relations/contact_spec.rb +++ b/spec/models/customer_relations/contact_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe CustomerRelations::Contact, type: :model do +RSpec.describe CustomerRelations::Contact, type: :model, feature_category: :team_planning do let_it_be(:group) { create(:group) } describe 'associations' do @@ -280,4 +280,25 @@ RSpec.describe CustomerRelations::Contact, type: :model do end end end + + describe '#hook_attrs' do + let_it_be(:contact) { create(:contact, group: group) } + + it 'includes the expected attributes' do + expect(contact.hook_attrs).to match a_hash_including( + { + 'created_at' => contact.created_at, + 'description' => contact.description, + 'first_name' => contact.first_name, + 'group_id' => group.id, + 'id' => contact.id, + 'last_name' => contact.last_name, + 'organization_id' => contact.organization_id, + 'state' => contact.state, + 'updated_at' => contact.updated_at + } + ) + expect(contact.hook_attrs.keys).to match_array(described_class::SAFE_ATTRIBUTES) + end + end end diff --git a/spec/models/issue_spec.rb b/spec/models/issue_spec.rb index a84445f397f..a26ab2501ec 100644 --- a/spec/models/issue_spec.rb +++ b/spec/models/issue_spec.rb @@ -188,7 +188,7 @@ RSpec.describe Issue, feature_category: :team_planning do expect(issue).not_to be_valid expect(issue.errors[:base]) - .to include(_('A confidential issue cannot have a parent that already has non-confidential children.')) + .to include(_('A confidential issue must have only confidential children. Make any child items confidential and try again.')) end it 'allows to make child confidential' do diff --git a/spec/models/merge_request_spec.rb b/spec/models/merge_request_spec.rb index b463199a85b..da3f691b63a 100644 --- a/spec/models/merge_request_spec.rb +++ b/spec/models/merge_request_spec.rb @@ -3266,6 +3266,15 @@ RSpec.describe MergeRequest, factory_default: :keep, feature_category: :code_rev end end end + + context 'with check_mergeability_retry_lease option' do + it 'call check_mergeability with sync_retry_lease' do + allow(subject).to receive(:mergeable_state?) { true } + expect(subject).to receive(:check_mergeability).with(sync_retry_lease: true) + + subject.mergeable?(check_mergeability_retry_lease: true) + end + end end describe '#skipped_mergeable_checks' do @@ -3300,6 +3309,14 @@ RSpec.describe MergeRequest, factory_default: :keep, feature_category: :code_rev subject.check_mergeability end + context 'when sync_retry_lease is true' do + it 'executes MergeabilityCheckService' do + expect(mergeability_service).to receive(:execute).with(retry_lease: true) + + subject.check_mergeability(sync_retry_lease: true) + end + end + context 'when async is true' do it 'executes MergeabilityCheckService asynchronously' do expect(mergeability_service).to receive(:async_execute) diff --git a/spec/services/merge_requests/merge_service_spec.rb b/spec/services/merge_requests/merge_service_spec.rb index c77cf288f56..c32b7b16f63 100644 --- a/spec/services/merge_requests/merge_service_spec.rb +++ b/spec/services/merge_requests/merge_service_spec.rb @@ -577,6 +577,14 @@ RSpec.describe MergeRequests::MergeService, feature_category: :code_review_workf end end end + + context 'when passing `check_mergeability_retry_lease: true` as `options` parameter' do + it 'call mergeable? with check_mergeability_retry_lease' do + expect(merge_request).to receive(:mergeable?).with(hash_including(check_mergeability_retry_lease: true)).and_call_original + + service.execute(merge_request, check_mergeability_retry_lease: true) + end + end end end diff --git a/spec/support/helpers/models/ci/partitioning_testing/rspec_hooks.rb b/spec/support/helpers/models/ci/partitioning_testing/rspec_hooks.rb index 8a1581d513a..a764e751bf5 100644 --- a/spec/support/helpers/models/ci/partitioning_testing/rspec_hooks.rb +++ b/spec/support/helpers/models/ci/partitioning_testing/rspec_hooks.rb @@ -4,10 +4,6 @@ RSpec.configure do |config| config.include ::Ci::PartitioningTesting::PartitionIdentifiers config.around(:each, :ci_partitionable) do |example| - unless ::Ci::Build.table_name.to_s.starts_with?('p_') - skip 'Skipping partitioning tests until `ci_builds` is partitioned' - end - ::Ci::PartitioningTesting::SchemaHelpers.with_routing_tables do example.run end diff --git a/spec/support/helpers/models/ci/partitioning_testing/schema_helpers.rb b/spec/support/helpers/models/ci/partitioning_testing/schema_helpers.rb index f31dbb111ee..df653c853b9 100644 --- a/spec/support/helpers/models/ci/partitioning_testing/schema_helpers.rb +++ b/spec/support/helpers/models/ci/partitioning_testing/schema_helpers.rb @@ -6,16 +6,22 @@ module Ci module_function def with_routing_tables - # model.table_name = :routing_table + previous_table_name = CommitStatus.table_name + CommitStatus.table_name = :p_ci_builds + CommitStatus.descendants.each(&:reset_table_name) + yield - # ensure - # model.table_name = :regular_table + + ensure + CommitStatus.table_name = previous_table_name + CommitStatus.descendants.each(&:reset_table_name) end def setup(connection: Ci::ApplicationRecord.connection) each_partitionable_table do |table_name| create_test_partition("p_#{table_name}", connection: connection) end + ensure_builds_id_uniquness(connection: connection) end def teardown(connection: Ci::ApplicationRecord.connection) @@ -57,6 +63,16 @@ module Ci SQL end + # This can be removed after https://gitlab.com/gitlab-org/gitlab/-/issues/421173 + # is implemented + def ensure_builds_id_uniquness(connection:) + connection.execute(<<~SQL.squish) + CREATE TRIGGER assign_p_ci_builds_id_trigger + BEFORE INSERT ON #{full_partition_name('ci_builds')} + FOR EACH ROW EXECUTE FUNCTION assign_p_ci_builds_id_value(); + SQL + end + def table_available?(table_name, connection:) connection.table_exists?(table_name) && connection.column_exists?(table_name, :partition_id) diff --git a/spec/support/protected_branch_helpers.rb b/spec/support/protected_branch_helpers.rb index d983d03fd2e..576275e9d1d 100644 --- a/spec/support/protected_branch_helpers.rb +++ b/spec/support/protected_branch_helpers.rb @@ -9,6 +9,10 @@ module ProtectedBranchHelpers end end + def show_add_form + click_button 'Add protected branch' + end + def set_protected_branch_name(branch_name) find('.js-protected-branch-select').click find('.dropdown-input-field').set(branch_name) diff --git a/spec/support/shared_examples/features/protected_branches_access_control_ce_shared_examples.rb b/spec/support/shared_examples/features/protected_branches_access_control_ce_shared_examples.rb index 2d3f1949716..fb882ef8a23 100644 --- a/spec/support/shared_examples/features/protected_branches_access_control_ce_shared_examples.rb +++ b/spec/support/shared_examples/features/protected_branches_access_control_ce_shared_examples.rb @@ -7,6 +7,7 @@ RSpec.shared_examples "protected branches > access control > CE" do it "allows creating protected branches that #{access_type_name} can push to" do visit project_protected_branches_path(project) + show_add_form set_protected_branch_name('master') set_allowed_to('merge', no_one) set_allowed_to('push', access_type_name) @@ -19,6 +20,7 @@ RSpec.shared_examples "protected branches > access control > CE" do it "allows creating protected branches that #{access_type_name} can merge to" do visit project_protected_branches_path(project) + show_add_form set_protected_branch_name('master') set_allowed_to('merge', access_type_name) set_allowed_to('push', no_one) @@ -31,6 +33,7 @@ RSpec.shared_examples "protected branches > access control > CE" do it "allows updating protected branches so that #{access_type_name} can push to them" do visit project_protected_branches_path(project) + show_add_form set_protected_branch_name('master') set_allowed_to('merge', no_one) set_allowed_to('push', no_one) @@ -52,6 +55,7 @@ RSpec.shared_examples "protected branches > access control > CE" do it "allows updating protected branches so that #{access_type_name} can merge to them" do visit project_protected_branches_path(project) + show_add_form set_protected_branch_name('master') set_allowed_to('merge', no_one) set_allowed_to('push', no_one) diff --git a/spec/support/shared_examples/features/protected_branches_with_deploy_keys_examples.rb b/spec/support/shared_examples/features/protected_branches_with_deploy_keys_examples.rb index 90b0e600228..a15ee47de34 100644 --- a/spec/support/shared_examples/features/protected_branches_with_deploy_keys_examples.rb +++ b/spec/support/shared_examples/features/protected_branches_with_deploy_keys_examples.rb @@ -20,6 +20,7 @@ RSpec.shared_examples 'Deploy keys with protected branches' do it "shows all dropdown sections in the 'Allowed to push' main dropdown, with only one deploy key" do visit project_protected_branches_path(project) + click_button 'Add protected branch' find(".js-allowed-to-push").click wait_for_requests @@ -35,6 +36,7 @@ RSpec.shared_examples 'Deploy keys with protected branches' do it "shows all sections but not deploy keys in the 'Allowed to merge' main dropdown" do visit project_protected_branches_path(project) + click_button 'Add protected branch' find(".js-allowed-to-merge").click wait_for_requests @@ -65,6 +67,7 @@ RSpec.shared_examples 'Deploy keys with protected branches' do it "just shows all sections but not deploy keys in the 'Allowed to push' dropdown" do visit project_protected_branches_path(project) + click_button 'Add protected branch' find(".js-allowed-to-push").click wait_for_requests |