diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2023-03-02 09:13:09 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2023-03-02 09:13:09 +0300 |
commit | 46fd9b1dd86370ae2c986a393b63dbce3315982f (patch) | |
tree | 1c1c9e56aba32169caf2f163463046350c47ceb9 | |
parent | d96347b5c2dcec453de2583fd6654e2c34eedfdc (diff) |
Add latest changes from gitlab-org/gitlab@master
-rw-r--r-- | .rubocop_todo/rspec/missing_feature_category.yml | 4 | ||||
-rw-r--r-- | config/feature_flags/development/refactor_ci_minutes_consumption.yml | 8 | ||||
-rw-r--r-- | doc/ci/yaml/index.md | 10 | ||||
-rw-r--r-- | locale/gitlab.pot | 3 | ||||
-rw-r--r-- | spec/fast_spec_helper.rb | 10 | ||||
-rw-r--r-- | spec/frontend/vue_shared/components/filtered_search_bar/tokens/milestone_token_spec.js | 104 | ||||
-rw-r--r-- | spec/frontend/vue_shared/components/filtered_search_bar/tokens/user_token_spec.js | 120 | ||||
-rw-r--r-- | spec/spec_helper.rb | 7 | ||||
-rw-r--r-- | spec/support/rspec.rb | 7 |
9 files changed, 121 insertions, 152 deletions
diff --git a/.rubocop_todo/rspec/missing_feature_category.yml b/.rubocop_todo/rspec/missing_feature_category.yml index b592a402f40..f7f8de0f90b 100644 --- a/.rubocop_todo/rspec/missing_feature_category.yml +++ b/.rubocop_todo/rspec/missing_feature_category.yml @@ -907,10 +907,8 @@ RSpec/MissingFeatureCategory: - 'ee/spec/lib/gitlab/ci/config/entry/vault/engine_spec.rb' - 'ee/spec/lib/gitlab/ci/config/entry/vault/secret_spec.rb' - 'ee/spec/lib/gitlab/ci/config/required/processor_spec.rb' - - 'ee/spec/lib/gitlab/ci/minutes/build_consumption_spec.rb' - 'ee/spec/lib/gitlab/ci/minutes/cached_quota_spec.rb' - 'ee/spec/lib/gitlab/ci/minutes/cost_factor_spec.rb' - - 'ee/spec/lib/gitlab/ci/minutes/gitlab_contribution_cost_factor_spec.rb' - 'ee/spec/lib/gitlab/ci/minutes/runners_availability_spec.rb' - 'ee/spec/lib/gitlab/ci/parsers/license_compliance/license_scanning_spec.rb' - 'ee/spec/lib/gitlab/ci/parsers/metrics/generic_spec.rb' @@ -1762,8 +1760,6 @@ RSpec/MissingFeatureCategory: - 'ee/spec/services/ci/minutes/email_notification_service_spec.rb' - 'ee/spec/services/ci/minutes/refresh_cached_data_service_spec.rb' - 'ee/spec/services/ci/minutes/reset_usage_service_spec.rb' - - 'ee/spec/services/ci/minutes/track_live_consumption_service_spec.rb' - - 'ee/spec/services/ci/minutes/update_build_minutes_service_spec.rb' - 'ee/spec/services/ci/pipeline_bridge_status_service_spec.rb' - 'ee/spec/services/ci/pipeline_creation/drop_not_runnable_builds_service_spec.rb' - 'ee/spec/services/ci/pipeline_creation/start_pipeline_service_spec.rb' diff --git a/config/feature_flags/development/refactor_ci_minutes_consumption.yml b/config/feature_flags/development/refactor_ci_minutes_consumption.yml new file mode 100644 index 00000000000..b24cf3d30b2 --- /dev/null +++ b/config/feature_flags/development/refactor_ci_minutes_consumption.yml @@ -0,0 +1,8 @@ +--- +name: refactor_ci_minutes_consumption +introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/112352 +rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/392949 +milestone: '15.10' +type: development +group: group::pipeline execution +default_enabled: false diff --git a/doc/ci/yaml/index.md b/doc/ci/yaml/index.md index 26f95c00b51..2bef3abf030 100644 --- a/doc/ci/yaml/index.md +++ b/doc/ci/yaml/index.md @@ -171,7 +171,7 @@ the time limit to resolve all files is 30 seconds. #### `include:local` -Use `include:local` to include a file that is in the same repository as the project running the pipeline. +Use `include:local` to include a file that is in the same repository as the configuration file containing the `include` keyword. Use `include:local` instead of symbolic links. **Keyword type**: Global keyword. @@ -201,8 +201,8 @@ include: '.gitlab-ci-production.yml' - The `.gitlab-ci.yml` file and the local file must be on the same branch. - You can't include local files through Git submodules paths. -- All [nested includes](includes.md#use-nested-includes) are executed in the scope of the same project, - so you can use local, project, remote, or template includes. +- All [nested includes](includes.md#use-nested-includes) are executed in the scope of the project containing the configuration file with the `include` keyword, not the project running the pipeline. + You can use local, project, remote, or template includes. #### `include:project` @@ -252,8 +252,8 @@ include: **Additional details**: -- All [nested includes](includes.md#use-nested-includes) are executed in the scope of the target project. - You can use `local` (relative to the target project), `project`, `remote`, or `template` includes. +- All [nested includes](includes.md#use-nested-includes) are executed in the scope of the project containing the configuration file with the nested `include` keyword. + You can use `local` (relative to the project containing the configuration file with the `include` keyword), `project`, `remote`, or `template` includes. - When the pipeline starts, the `.gitlab-ci.yml` file configuration included by all methods is evaluated. The configuration is a snapshot in time and persists in the database. GitLab does not reflect any changes to the referenced `.gitlab-ci.yml` file configuration until the next pipeline starts. diff --git a/locale/gitlab.pot b/locale/gitlab.pot index 129d1707e29..70cab3002ab 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -6406,9 +6406,6 @@ msgstr "" msgid "Below you will find all the groups that are public." msgstr "" -msgid "Beta" -msgstr "" - msgid "Bi-weekly code coverage" msgstr "" diff --git a/spec/fast_spec_helper.rb b/spec/fast_spec_helper.rb index 8395124cbe4..451f3d56af7 100644 --- a/spec/fast_spec_helper.rb +++ b/spec/fast_spec_helper.rb @@ -27,11 +27,5 @@ SimpleCovEnv.start! ActiveSupport::XmlMini.backend = 'Nokogiri' -RSpec.configure do |config| - # Makes diffs show entire non-truncated values. - config.before(:each, unlimited_max_formatted_output_length: true) do |_example| - config.expect_with :rspec do |c| - c.max_formatted_output_length = nil - end - end -end +# Consider tweaking configuration in `spec/support/rspec.rb` which is also +# used by `spec/spec_helper.rb`. diff --git a/spec/frontend/vue_shared/components/filtered_search_bar/tokens/milestone_token_spec.js b/spec/frontend/vue_shared/components/filtered_search_bar/tokens/milestone_token_spec.js index 589697fe542..8a06c990711 100644 --- a/spec/frontend/vue_shared/components/filtered_search_bar/tokens/milestone_token_spec.js +++ b/spec/frontend/vue_shared/components/filtered_search_bar/tokens/milestone_token_spec.js @@ -14,6 +14,7 @@ import { sortMilestonesByDueDate } from '~/milestones/utils'; import { DEFAULT_MILESTONES } from '~/vue_shared/components/filtered_search_bar/constants'; import MilestoneToken from '~/vue_shared/components/filtered_search_bar/tokens/milestone_token.vue'; +import BaseToken from '~/vue_shared/components/filtered_search_bar/tokens/base_token.vue'; import { mockMilestoneToken, mockMilestones, mockRegularMilestone } from '../mock_data'; @@ -57,6 +58,12 @@ describe('MilestoneToken', () => { let mock; let wrapper; + const findBaseToken = () => wrapper.findComponent(BaseToken); + const triggerFetchMilestones = (searchTerm = null) => { + findBaseToken().vm.$emit('fetch-suggestions', searchTerm); + return waitForPromises(); + }; + beforeEach(() => { mock = new MockAdapter(axios); wrapper = createComponent(); @@ -64,73 +71,77 @@ describe('MilestoneToken', () => { afterEach(() => { mock.restore(); - wrapper.destroy(); }); describe('methods', () => { describe('fetchMilestones', () => { - describe('when config.shouldSkipSort is true', () => { - beforeEach(() => { - wrapper.vm.config.shouldSkipSort = true; + it('sets loading state', async () => { + wrapper = createComponent({ + config: { + fetchMilestones: jest.fn().mockResolvedValue(new Promise(() => {})), + }, }); + await nextTick(); - afterEach(() => { - wrapper.vm.config.shouldSkipSort = false; - }); + expect(findBaseToken().props('suggestionsLoading')).toBe(true); + }); + + describe('when config.shouldSkipSort is true', () => { it('does not call sortMilestonesByDueDate', async () => { - jest.spyOn(wrapper.vm.config, 'fetchMilestones').mockResolvedValue({ - data: mockMilestones, + wrapper = createComponent({ + config: { + shouldSkipSort: true, + fetchMilestones: jest.fn().mockResolvedValue({ data: mockMilestones }), + }, }); - wrapper.vm.fetchMilestones(); - - await waitForPromises(); + await triggerFetchMilestones(); expect(sortMilestonesByDueDate).toHaveBeenCalledTimes(0); }); }); - it('calls `config.fetchMilestones` with provided searchTerm param', () => { - jest.spyOn(wrapper.vm.config, 'fetchMilestones'); - - wrapper.vm.fetchMilestones('foo'); + describe('when request is successful', () => { + const searchTerm = 'foo'; - expect(wrapper.vm.config.fetchMilestones).toHaveBeenCalledWith('foo'); - }); - - it('sets response to `milestones` when request is successful', () => { - wrapper.vm.config.shouldSkipSort = false; + beforeEach(() => { + wrapper = createComponent({ + config: { + shouldSkipSort: false, + fetchMilestones: jest.fn().mockResolvedValue({ data: mockMilestones }), + }, + }); + return triggerFetchMilestones(searchTerm); + }); - jest.spyOn(wrapper.vm.config, 'fetchMilestones').mockResolvedValue({ - data: mockMilestones, + it('calls `config.fetchMilestones` with provided searchTerm param', () => { + expect(findBaseToken().props('config').fetchMilestones).toHaveBeenCalledWith(searchTerm); }); - wrapper.vm.fetchMilestones(); - return waitForPromises().then(() => { - expect(wrapper.vm.milestones).toEqual(mockMilestones); + it('sets response to `milestones`', () => { expect(sortMilestonesByDueDate).toHaveBeenCalled(); + expect(findBaseToken().props('suggestions')).toEqual(mockMilestones); }); }); - it('calls `createAlert` with flash error message when request fails', () => { - jest.spyOn(wrapper.vm.config, 'fetchMilestones').mockRejectedValue({}); - - wrapper.vm.fetchMilestones('foo'); + describe('when request fails', () => { + beforeEach(() => { + wrapper = createComponent({ + config: { + fetchMilestones: jest.fn().mockRejectedValue({}), + }, + }); + return triggerFetchMilestones(); + }); - return waitForPromises().then(() => { + it('calls `createAlert` with flash error message', () => { expect(createAlert).toHaveBeenCalledWith({ message: 'There was a problem fetching milestones.', }); }); - }); - it('sets `loading` to false when request completes', () => { - jest.spyOn(wrapper.vm.config, 'fetchMilestones').mockRejectedValue({}); - - wrapper.vm.fetchMilestones('foo'); - - return waitForPromises().then(() => { - expect(wrapper.vm.loading).toBe(false); + it('sets `loading` to false when request completes', () => { + expect(findBaseToken().props('suggestionsLoading')).toBe(false); }); }); }); @@ -143,15 +154,12 @@ describe('MilestoneToken', () => { ]; beforeEach(async () => { - wrapper = createComponent({ value: { data: `"${mockRegularMilestone.title}"` } }); - - // setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details - // eslint-disable-next-line no-restricted-syntax - wrapper.setData({ - milestones: mockMilestones, + wrapper = createComponent({ + value: { data: `"${mockRegularMilestone.title}"` }, + config: { + initialMilestones: mockMilestones, + }, }); - - await nextTick(); }); it('renders gl-filtered-search-token component', () => { @@ -228,7 +236,7 @@ describe('MilestoneToken', () => { it('finds the correct value from the activeToken', () => { DEFAULT_MILESTONES.forEach(({ value, title }) => { - const activeToken = wrapper.vm.getActiveMilestone([], value); + const activeToken = findBaseToken().props('getActiveTokenValue')([], value); expect(activeToken.title).toEqual(title); }); diff --git a/spec/frontend/vue_shared/components/filtered_search_bar/tokens/user_token_spec.js b/spec/frontend/vue_shared/components/filtered_search_bar/tokens/user_token_spec.js index 32cb74d5f80..4671ee43d9e 100644 --- a/spec/frontend/vue_shared/components/filtered_search_bar/tokens/user_token_spec.js +++ b/spec/frontend/vue_shared/components/filtered_search_bar/tokens/user_token_spec.js @@ -72,7 +72,7 @@ describe('UserToken', () => { let mock; let wrapper; - const getBaseToken = () => wrapper.findComponent(BaseToken); + const findBaseToken = () => wrapper.findComponent(BaseToken); beforeEach(() => { mock = new MockAdapter(axios); @@ -81,85 +81,70 @@ describe('UserToken', () => { afterEach(() => { window.gon = originalGon; mock.restore(); - wrapper.destroy(); }); describe('methods', () => { describe('fetchUsers', () => { + const triggerFetchUsers = (searchTerm = null) => { + findBaseToken().vm.$emit('fetch-suggestions', searchTerm); + return waitForPromises(); + }; + beforeEach(() => { wrapper = createComponent(); }); - it('calls `config.fetchUsers` with provided searchTerm param', () => { - jest.spyOn(wrapper.vm.config, 'fetchUsers'); - - getBaseToken().vm.$emit('fetch-suggestions', mockUsers[0].username); - - expect(wrapper.vm.config.fetchUsers).toHaveBeenCalledWith( - mockAuthorToken.fetchPath, - mockUsers[0].username, - ); - }); - - it('sets response to `users` when request is successful', () => { - jest.spyOn(wrapper.vm.config, 'fetchUsers').mockResolvedValue(mockUsers); - - getBaseToken().vm.$emit('fetch-suggestions', 'root'); - - return waitForPromises().then(() => { - expect(getBaseToken().props('suggestions')).toEqual(mockUsers); + it('sets loading state', async () => { + wrapper = createComponent({ + config: { + fetchUsers: jest.fn().mockResolvedValue(new Promise(() => {})), + }, }); + await nextTick(); + + expect(findBaseToken().props('suggestionsLoading')).toBe(true); }); - // TODO: rm when completed https://gitlab.com/gitlab-org/gitlab/-/issues/345756 - describe('when there are null users presents', () => { - const mockUsersWithNullUser = mockUsers.concat([null]); + describe('when request is successful', () => { + const searchTerm = 'foo'; beforeEach(() => { - jest - .spyOn(wrapper.vm.config, 'fetchUsers') - .mockResolvedValue({ data: mockUsersWithNullUser }); - - getBaseToken().vm.$emit('fetch-suggestions', 'root'); + wrapper = createComponent({ + config: { + fetchUsers: jest.fn().mockResolvedValue({ data: mockUsers }), + }, + }); + return triggerFetchUsers(searchTerm); }); - describe('when res.data is present', () => { - it('filters the successful response when null values are present', () => { - return waitForPromises().then(() => { - expect(getBaseToken().props('suggestions')).toEqual(mockUsers); - }); - }); + it('calls `config.fetchUsers` with provided searchTerm param', () => { + expect(findBaseToken().props('config').fetchUsers).toHaveBeenCalledWith(searchTerm); }); - describe('when response is an array', () => { - it('filters the successful response when null values are present', () => { - return waitForPromises().then(() => { - expect(getBaseToken().props('suggestions')).toEqual(mockUsers); - }); - }); + it('sets response to `users` when request is successful', () => { + expect(findBaseToken().props('suggestions')).toEqual(mockUsers); }); }); - it('calls `createAlert` with flash error message when request fails', () => { - jest.spyOn(wrapper.vm.config, 'fetchUsers').mockRejectedValue({}); - - getBaseToken().vm.$emit('fetch-suggestions', 'root'); + describe('when request fails', () => { + beforeEach(() => { + wrapper = createComponent({ + config: { + fetchUsers: jest.fn().mockRejectedValue({}), + }, + }); + return triggerFetchUsers(); + }); - return waitForPromises().then(() => { + it('calls `createAlert` with flash error message', () => { expect(createAlert).toHaveBeenCalledWith({ message: 'There was a problem fetching users.', }); }); - }); - - it('sets `loading` to false when request completes', async () => { - jest.spyOn(wrapper.vm.config, 'fetchUsers').mockRejectedValue({}); - - getBaseToken().vm.$emit('fetch-suggestions', 'root'); - await waitForPromises(); - - expect(getBaseToken().props('suggestionsLoading')).toBe(false); + it('sets `loading` to false when request completes', async () => { + expect(findBaseToken().props('suggestionsLoading')).toBe(false); + }); }); }); }); @@ -178,12 +163,12 @@ describe('UserToken', () => { data: { users: mockUsers }, }); - const baseTokenEl = getBaseToken(); + const baseTokenEl = findBaseToken(); expect(baseTokenEl.exists()).toBe(true); expect(baseTokenEl.props()).toMatchObject({ suggestions: mockUsers, - getActiveTokenValue: wrapper.vm.getActiveUser, + getActiveTokenValue: baseTokenEl.props('getActiveTokenValue'), }); }); @@ -191,7 +176,6 @@ describe('UserToken', () => { wrapper = createComponent({ value: { data: mockUsers[0].username }, data: { users: mockUsers }, - stubs: { Portal: true }, }); await nextTick(); @@ -215,30 +199,13 @@ describe('UserToken', () => { users: [ { ...mockUsers[0], + avatarUrl: mockUsers[0].avatar_url, + avatar_url: undefined, }, ], }, - stubs: { Portal: true }, }); - await nextTick(); - - expect(getAvatarEl().props('src')).toBe(mockUsers[0].avatar_url); - - // setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details - // eslint-disable-next-line no-restricted-syntax - wrapper.setData({ - users: [ - { - ...mockUsers[0], - avatarUrl: mockUsers[0].avatar_url, - avatar_url: undefined, - }, - ], - }); - - await nextTick(); - expect(getAvatarEl().props('src')).toBe(mockUsers[0].avatar_url); }); @@ -264,7 +231,6 @@ describe('UserToken', () => { wrapper = createComponent({ active: true, config: { ...mockAuthorToken, defaultUsers: [] }, - stubs: { Portal: true }, }); const tokenSegments = wrapper.findAllComponents(GlFilteredSearchTokenSegment); const suggestionsSegment = tokenSegments.at(2); diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index f76ba3528e9..d9afe9c5284 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -540,13 +540,6 @@ RSpec.configure do |config| end end - # Makes diffs show entire non-truncated values. - config.before(:each, unlimited_max_formatted_output_length: true) do |_example| - config.expect_with :rspec do |c| - c.max_formatted_output_length = nil - end - end - # Ensures that any Javascript script that tries to make the external VersionCheck API call skips it and returns a response config.before(:each, :js) do allow_any_instance_of(VersionCheck).to receive(:response).and_return({ "severity" => "success" }) diff --git a/spec/support/rspec.rb b/spec/support/rspec.rb index ff0b5bebe33..6e377b8cb0d 100644 --- a/spec/support/rspec.rb +++ b/spec/support/rspec.rb @@ -19,6 +19,13 @@ RSpec.configure do |config| # Re-run failures locally with `--only-failures` config.example_status_persistence_file_path = ENV.fetch('RSPEC_LAST_RUN_RESULTS_FILE', './spec/examples.txt') + # Makes diffs show entire non-truncated values. + config.before(:each, :unlimited_max_formatted_output_length) do + config.expect_with :rspec do |c| + c.max_formatted_output_length = nil + end + end + unless ENV['CI'] # Allow running `:focus` examples locally, # falling back to all tests when there is no `:focus` example. |