diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2023-03-21 06:08:15 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2023-03-21 06:08:15 +0300 |
commit | ca80962ae504c439159c474c2a74b8dfe139263c (patch) | |
tree | 00138fbb041d461ab15d659b68cdc2ab278503a1 | |
parent | f52791a91fe3284747b8c9022ee584911ece8dd3 (diff) |
Add latest changes from gitlab-org/gitlab@master
31 files changed, 164 insertions, 130 deletions
diff --git a/app/assets/javascripts/deprecated_notes.js b/app/assets/javascripts/deprecated_notes.js index 0008c3504ce..70be08a8d7c 100644 --- a/app/assets/javascripts/deprecated_notes.js +++ b/app/assets/javascripts/deprecated_notes.js @@ -1179,9 +1179,11 @@ export default class Notes { const form = textarea.parents('form'); const reopenbtn = form.find('.js-note-target-reopen'); const closebtn = form.find('.js-note-target-close'); + const savebtn = form.find('.js-comment-save-button'); const commentTypeComponent = form.get(0)?.commentTypeComponent; if (textarea.val().trim().length > 0) { + savebtn.enable(); reopentext = reopenbtn.attr('data-alternative-text'); closetext = closebtn.attr('data-alternative-text'); if (reopenbtn.text() !== reopentext) { @@ -1200,6 +1202,7 @@ export default class Notes { commentTypeComponent.disabled = false; } } else { + savebtn.disable(); reopentext = reopenbtn.data('originalText'); closetext = closebtn.data('originalText'); if (reopenbtn.text() !== reopentext) { diff --git a/app/assets/javascripts/pipelines/components/pipelines_list/pipeline_url.vue b/app/assets/javascripts/pipelines/components/pipelines_list/pipeline_url.vue index fe2ef2c2d71..7ad12d397e5 100644 --- a/app/assets/javascripts/pipelines/components/pipelines_list/pipeline_url.vue +++ b/app/assets/javascripts/pipelines/components/pipelines_list/pipeline_url.vue @@ -144,14 +144,16 @@ export default { <tooltip-on-truncate :title="commitTitle" class="gl-flex-grow-1 gl-text-truncate"> <gl-link :href="commitUrl" - class="commit-row-message gl-text-gray-900" + class="commit-row-message gl-font-weight-bold gl-text-gray-900" data-testid="commit-title" @click="trackClick('click_commit_title')" >{{ commitTitle }}</gl-link > </tooltip-on-truncate> </span> - <span v-else>{{ __("Can't find HEAD commit for this branch") }}</span> + <span v-else class="gl-text-gray-500">{{ + __("Can't find HEAD commit for this branch") + }}</span> </div> <div class="gl-mb-2"> <gl-link diff --git a/app/assets/javascripts/pipelines/components/pipelines_list/pipelines.vue b/app/assets/javascripts/pipelines/components/pipelines_list/pipelines.vue index 640129b9c4c..c5537b7ad54 100644 --- a/app/assets/javascripts/pipelines/components/pipelines_list/pipelines.vue +++ b/app/assets/javascripts/pipelines/components/pipelines_list/pipelines.vue @@ -346,7 +346,7 @@ export default { </div> <div v-if="stateToRender !== $options.stateMap.emptyState" class="gl-display-flex"> - <div class="row-content-block gl-display-flex gl-flex-grow-1"> + <div class="row-content-block gl-display-flex gl-flex-grow-1 gl-border-b-0"> <pipelines-filtered-search class="gl-display-flex gl-flex-grow-1 gl-mr-4" :project-id="projectId" diff --git a/app/assets/javascripts/pipelines/components/pipelines_list/pipelines_table.vue b/app/assets/javascripts/pipelines/components/pipelines_list/pipelines_table.vue index 365572f194b..b2da0df17c0 100644 --- a/app/assets/javascripts/pipelines/components/pipelines_list/pipelines_table.vue +++ b/app/assets/javascripts/pipelines/components/pipelines_list/pipelines_table.vue @@ -15,7 +15,7 @@ import PipelinesStatusBadge from './pipelines_status_badge.vue'; const DEFAULT_TD_CLASS = 'gl-p-5!'; const HIDE_TD_ON_MOBILE = 'gl-display-none! gl-lg-display-table-cell!'; const DEFAULT_TH_CLASSES = - 'gl-bg-transparent! gl-border-b-solid! gl-border-b-gray-100! gl-p-5! gl-border-b-1! gl-font-sm!'; + 'gl-bg-transparent! gl-border-b-solid! gl-border-b-gray-100! gl-p-5! gl-border-b-1!'; export default { components: { diff --git a/app/assets/javascripts/pipelines/components/pipelines_list/time_ago.vue b/app/assets/javascripts/pipelines/components/pipelines_list/time_ago.vue index 960af030421..e15676849da 100644 --- a/app/assets/javascripts/pipelines/components/pipelines_list/time_ago.vue +++ b/app/assets/javascripts/pipelines/components/pipelines_list/time_ago.vue @@ -41,7 +41,7 @@ export default { }; </script> <template> - <div class="gl-display-flex gl-flex-direction-column time-ago"> + <div class="gl-display-flex gl-flex-direction-column gl-font-sm time-ago"> <span v-if="showInProgress" class="gl-display-inline-flex gl-align-items-center" diff --git a/app/models/project.rb b/app/models/project.rb index 0c5257065c1..ef1a7b1cb8c 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -2238,7 +2238,7 @@ class Project < ApplicationRecord wiki.repository.expire_content_cache DetectRepositoryLanguagesWorker.perform_async(id) - ProjectCacheWorker.perform_async(self.id, [], [:repository_size]) + ProjectCacheWorker.perform_async(self.id, [], [:repository_size, :wiki_size]) AuthorizedProjectUpdate::ProjectRecalculateWorker.perform_async(id) enqueue_record_project_target_platforms diff --git a/doc/api/graphql/reference/index.md b/doc/api/graphql/reference/index.md index 312e8894071..696ff2643ab 100644 --- a/doc/api/graphql/reference/index.md +++ b/doc/api/graphql/reference/index.md @@ -6278,7 +6278,7 @@ Input type: `VulnerabilityConfirmInput` | Name | Type | Description | | ---- | ---- | ----------- | | <a id="mutationvulnerabilityconfirmclientmutationid"></a>`clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. | -| <a id="mutationvulnerabilityconfirmcomment"></a>`comment` | [`String`](#string) | Comment why vulnerability was marked as confirmed (max. 50 000 characters). | +| <a id="mutationvulnerabilityconfirmcomment"></a>`comment` | [`String`](#string) | Comment why vulnerability was confirmed (maximum 50,000 characters). | | <a id="mutationvulnerabilityconfirmid"></a>`id` | [`VulnerabilityID!`](#vulnerabilityid) | ID of the vulnerability to be confirmed. | #### Fields @@ -6330,7 +6330,7 @@ Input type: `VulnerabilityDismissInput` | Name | Type | Description | | ---- | ---- | ----------- | | <a id="mutationvulnerabilitydismissclientmutationid"></a>`clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. | -| <a id="mutationvulnerabilitydismisscomment"></a>`comment` | [`String`](#string) | Comment why vulnerability should be dismissed (max. 50 000 characters). | +| <a id="mutationvulnerabilitydismisscomment"></a>`comment` | [`String`](#string) | Comment why vulnerability was dismissed (maximum 50,000 characters). | | <a id="mutationvulnerabilitydismissdismissalreason"></a>`dismissalReason` | [`VulnerabilityDismissalReason`](#vulnerabilitydismissalreason) | Reason why vulnerability should be dismissed. | | <a id="mutationvulnerabilitydismissid"></a>`id` | [`VulnerabilityID!`](#vulnerabilityid) | ID of the vulnerability to be dismissed. | @@ -6340,7 +6340,7 @@ Input type: `VulnerabilityDismissInput` | ---- | ---- | ----------- | | <a id="mutationvulnerabilitydismissclientmutationid"></a>`clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. | | <a id="mutationvulnerabilitydismisserrors"></a>`errors` | [`[String!]!`](#string) | Errors encountered during execution of the mutation. | -| <a id="mutationvulnerabilitydismissvulnerability"></a>`vulnerability` | [`Vulnerability`](#vulnerability) | Vulnerability after dismissal. | +| <a id="mutationvulnerabilitydismissvulnerability"></a>`vulnerability` | [`Vulnerability`](#vulnerability) | Vulnerability after state change. | ### `Mutation.vulnerabilityExternalIssueLinkCreate` @@ -6416,7 +6416,7 @@ Input type: `VulnerabilityResolveInput` | Name | Type | Description | | ---- | ---- | ----------- | | <a id="mutationvulnerabilityresolveclientmutationid"></a>`clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. | -| <a id="mutationvulnerabilityresolvecomment"></a>`comment` | [`String`](#string) | Comment why vulnerability was reverted to detected (max. 50 000 characters). | +| <a id="mutationvulnerabilityresolvecomment"></a>`comment` | [`String`](#string) | Comment why vulnerability was resolved (maximum 50,000 characters). | | <a id="mutationvulnerabilityresolveid"></a>`id` | [`VulnerabilityID!`](#vulnerabilityid) | ID of the vulnerability to be resolved. | #### Fields @@ -6436,8 +6436,8 @@ Input type: `VulnerabilityRevertToDetectedInput` | Name | Type | Description | | ---- | ---- | ----------- | | <a id="mutationvulnerabilityreverttodetectedclientmutationid"></a>`clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. | -| <a id="mutationvulnerabilityreverttodetectedcomment"></a>`comment` | [`String`](#string) | Comment why vulnerability was reverted to detected (max. 50 000 characters). | -| <a id="mutationvulnerabilityreverttodetectedid"></a>`id` | [`VulnerabilityID!`](#vulnerabilityid) | ID of the vulnerability to be reverted. | +| <a id="mutationvulnerabilityreverttodetectedcomment"></a>`comment` | [`String`](#string) | Comment why vulnerability was reverted to detected (maximum 50,000 characters). | +| <a id="mutationvulnerabilityreverttodetectedid"></a>`id` | [`VulnerabilityID!`](#vulnerabilityid) | ID of the vulnerability to be reverted to detected. | #### Fields @@ -6445,7 +6445,7 @@ Input type: `VulnerabilityRevertToDetectedInput` | ---- | ---- | ----------- | | <a id="mutationvulnerabilityreverttodetectedclientmutationid"></a>`clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. | | <a id="mutationvulnerabilityreverttodetectederrors"></a>`errors` | [`[String!]!`](#string) | Errors encountered during execution of the mutation. | -| <a id="mutationvulnerabilityreverttodetectedvulnerability"></a>`vulnerability` | [`Vulnerability`](#vulnerability) | Vulnerability after revert. | +| <a id="mutationvulnerabilityreverttodetectedvulnerability"></a>`vulnerability` | [`Vulnerability`](#vulnerability) | Vulnerability after state change. | ### `Mutation.workItemCreate` diff --git a/doc/development/documentation/styleguide/word_list.md b/doc/development/documentation/styleguide/word_list.md index 1ea95367d01..58e1b605e97 100644 --- a/doc/development/documentation/styleguide/word_list.md +++ b/doc/development/documentation/styleguide/word_list.md @@ -305,6 +305,12 @@ You can also use **cloud-native GitLab** for short. It should be hyphenated and Use **collapse** instead of **close** when you are talking about expanding or collapsing a section in the UI. +## command line + +Use **From the command line** to introduce commands. + +Hyphenate when using as an adjective. For example, **a command-line tool**. + ## confirmation dialog Use **confirmation dialog** to describe the dialog box that asks you to confirm your action. For example: diff --git a/spec/features/snippets/notes_on_personal_snippets_spec.rb b/spec/features/snippets/notes_on_personal_snippets_spec.rb index c281e5906ad..5aac27a71e4 100644 --- a/spec/features/snippets/notes_on_personal_snippets_spec.rb +++ b/spec/features/snippets/notes_on_personal_snippets_spec.rb @@ -119,20 +119,36 @@ RSpec.describe 'Comments on personal snippets', :js, feature_category: :source_c end context 'when editing a note' do - it 'changes the text' do - find('.js-note-edit').click + context 'when note is empty' do + before do + find('.js-note-edit').click - page.within('.current-note-edit-form') do - fill_in 'note[note]', with: 'new content' - find('.btn-confirm').click + page.within('.current-note-edit-form') do + fill_in 'note[note]', with: '' + end end - page.within("#notes-list li#note_#{snippet_notes[0].id}") do - edited_text = find('.edited-text') + it 'disables save button' do + expect(page).to have_button('Save comment', disabled: true) + end + end + + context 'when note is not empty' do + it 'changes the text' do + find('.js-note-edit').click + + page.within('.current-note-edit-form') do + fill_in 'note[note]', with: 'new content' + find('.btn-confirm').click + end + + page.within("#notes-list li#note_#{snippet_notes[0].id}") do + edited_text = find('.edited-text') - expect(page).to have_css('.note_edited_ago') - expect(page).to have_content('new content') - expect(edited_text).to have_selector('.note_edited_ago') + expect(page).to have_css('.note_edited_ago') + expect(page).to have_content('new content') + expect(edited_text).to have_selector('.note_edited_ago') + end end end end diff --git a/spec/frontend/groups/components/app_spec.js b/spec/frontend/groups/components/app_spec.js index 98868de8475..69f460d990e 100644 --- a/spec/frontend/groups/components/app_spec.js +++ b/spec/frontend/groups/components/app_spec.js @@ -112,7 +112,7 @@ describe('AppComponent', () => { }); }); - it('should show alert error when request fails', () => { + it('should show an alert when request fails', () => { mock.onGet('/dashboard/groups.json').reply(HTTP_STATUS_BAD_REQUEST); jest.spyOn(window, 'scrollTo').mockImplementation(() => {}); @@ -320,7 +320,7 @@ describe('AppComponent', () => { }); }); - it('should show error alert message if request failed to leave group', () => { + it('should show error alert if request failed to leave group', () => { const message = 'An error occurred. Please try again.'; jest .spyOn(vm.service, 'leaveGroup') @@ -337,7 +337,7 @@ describe('AppComponent', () => { }); }); - it('should show appropriate error alert message if request forbids to leave group', () => { + it('shows appropriate error alert if request forbidden to leave group', () => { const message = 'Failed to leave the group. Please make sure you are not the only owner.'; jest.spyOn(vm.service, 'leaveGroup').mockRejectedValue({ status: HTTP_STATUS_FORBIDDEN }); jest.spyOn(vm.store, 'removeGroup'); diff --git a/spec/frontend/groups/components/transfer_group_form_spec.js b/spec/frontend/groups/components/transfer_group_form_spec.js index fd0c3907e04..4d4de1ae3d5 100644 --- a/spec/frontend/groups/components/transfer_group_form_spec.js +++ b/spec/frontend/groups/components/transfer_group_form_spec.js @@ -69,7 +69,7 @@ describe('Transfer group form', () => { expect(findHiddenInput().attributes('value')).toBeUndefined(); }); - it('does not render the alert message', () => { + it('does not render the alert', () => { expect(findAlert().exists()).toBe(false); }); diff --git a/spec/frontend/ide/components/cannot_push_code_alert_spec.js b/spec/frontend/ide/components/cannot_push_code_alert_spec.js index d4db2246008..c72d8c5fccd 100644 --- a/spec/frontend/ide/components/cannot_push_code_alert_spec.js +++ b/spec/frontend/ide/components/cannot_push_code_alert_spec.js @@ -45,7 +45,7 @@ describe('ide/components/cannot_push_code_alert', () => { createComponent(); }); - it('shows alert with message', () => { + it('shows an alert with message', () => { expect(findAlert().props()).toMatchObject({ dismissible: false }); expect(findAlert().text()).toBe(TEST_MESSAGE); }); diff --git a/spec/frontend/ide/components/ide_spec.js b/spec/frontend/ide/components/ide_spec.js index 1c8d570cdce..ad4484587d3 100644 --- a/spec/frontend/ide/components/ide_spec.js +++ b/spec/frontend/ide/components/ide_spec.js @@ -169,7 +169,7 @@ describe('WebIDE', () => { }); }); - it('when user cannot push code, shows alert', () => { + it('when user cannot push code, shows an alert', () => { store.state.links = { forkInfo: { ide_path: TEST_FORK_IDE_PATH, diff --git a/spec/frontend/ide/stores/actions_spec.js b/spec/frontend/ide/stores/actions_spec.js index 63b63af667c..a8153eca39b 100644 --- a/spec/frontend/ide/stores/actions_spec.js +++ b/spec/frontend/ide/stores/actions_spec.js @@ -210,7 +210,7 @@ describe('Multi-file store actions', () => { expect(store.dispatch).toHaveBeenCalledWith('setFileActive', 'test'); }); - it('creates alert message if file already exists', async () => { + it('creates alert if file already exists', async () => { const f = file('test', '1', 'blob'); store.state.trees['abcproject/mybranch'].tree = [f]; store.state.entries[f.path] = f; diff --git a/spec/frontend/import_entities/import_groups/services/status_poller_spec.js b/spec/frontend/import_entities/import_groups/services/status_poller_spec.js index 5ee2b2e698f..e1ed739a708 100644 --- a/spec/frontend/import_entities/import_groups/services/status_poller_spec.js +++ b/spec/frontend/import_entities/import_groups/services/status_poller_spec.js @@ -81,7 +81,7 @@ describe('Bulk import status poller', () => { expect(pollInstance.makeRequest).toHaveBeenCalled(); }); - it('when error occurs shows alert with error', () => { + it('when error occurs shows an alert with error', () => { const [[pollConfig]] = Poll.mock.calls; pollConfig.errorCallback(); expect(createAlert).toHaveBeenCalled(); diff --git a/spec/frontend/incidents_settings/components/incidents_settings_service_spec.js b/spec/frontend/incidents_settings/components/incidents_settings_service_spec.js index c5c29b4bb19..9b11fe2bff0 100644 --- a/spec/frontend/incidents_settings/components/incidents_settings_service_spec.js +++ b/spec/frontend/incidents_settings/components/incidents_settings_service_spec.js @@ -33,7 +33,7 @@ describe('IncidentsSettingsService', () => { }); }); - it('should display an alert message on update error', () => { + it('should display an alert on update error', () => { mock.onPatch().reply(HTTP_STATUS_BAD_REQUEST); return service.updateSettings({}).then(() => { diff --git a/spec/frontend/invite_members/utils/trigger_successful_invite_alert_spec.js b/spec/frontend/invite_members/utils/trigger_successful_invite_alert_spec.js index fd011658f95..6192713f121 100644 --- a/spec/frontend/invite_members/utils/trigger_successful_invite_alert_spec.js +++ b/spec/frontend/invite_members/utils/trigger_successful_invite_alert_spec.js @@ -13,7 +13,7 @@ jest.mock('~/alert'); useLocalStorageSpy(); describe('Display Successful Invitation Alert', () => { - it('does not show alert if localStorage key not present', () => { + it('does not show an alert if localStorage key not present', () => { localStorage.removeItem(TOAST_MESSAGE_LOCALSTORAGE_KEY); displaySuccessfulInvitationAlert(); @@ -21,7 +21,7 @@ describe('Display Successful Invitation Alert', () => { expect(createAlert).not.toHaveBeenCalled(); }); - it('shows alert when localStorage key is present', () => { + it('shows an alert when localStorage key is present', () => { localStorage.setItem(TOAST_MESSAGE_LOCALSTORAGE_KEY, 'true'); displaySuccessfulInvitationAlert(); diff --git a/spec/frontend/issues/list/components/jira_issues_import_status_app_spec.js b/spec/frontend/issues/list/components/jira_issues_import_status_app_spec.js index 7bbb5a954ae..81739f6ef1d 100644 --- a/spec/frontend/issues/list/components/jira_issues_import_status_app_spec.js +++ b/spec/frontend/issues/list/components/jira_issues_import_status_app_spec.js @@ -94,7 +94,7 @@ describe('JiraIssuesImportStatus', () => { }); }); - describe('alert message', () => { + describe('alert', () => { it('is hidden when dismissed', async () => { wrapper = mountComponent({ shouldShowInProgressAlert: true, diff --git a/spec/frontend/monitoring/store/actions_spec.js b/spec/frontend/monitoring/store/actions_spec.js index 8097857f226..b3b198d6b51 100644 --- a/spec/frontend/monitoring/store/actions_spec.js +++ b/spec/frontend/monitoring/store/actions_spec.js @@ -260,7 +260,7 @@ describe('Monitoring store actions', () => { }); }); - it('does not show an alert error when showErrorBanner is disabled', async () => { + it('does not show an alert when showErrorBanner is disabled', async () => { state.showErrorBanner = false; await result(); diff --git a/spec/frontend/operation_settings/components/metrics_settings_spec.js b/spec/frontend/operation_settings/components/metrics_settings_spec.js index ee450dfc851..6ea08d4a9a5 100644 --- a/spec/frontend/operation_settings/components/metrics_settings_spec.js +++ b/spec/frontend/operation_settings/components/metrics_settings_spec.js @@ -198,7 +198,7 @@ describe('operation settings external dashboard component', () => { expect(refreshCurrentPage).toHaveBeenCalled(); }); - it('creates alert banner on error', async () => { + it('creates an alert on error', async () => { mountComponent(false); const message = 'mockErrorMessage'; axios.patch.mockRejectedValue({ response: { data: { message } } }); diff --git a/spec/frontend/packages_and_registries/package_registry/components/list/packages_list_spec.js b/spec/frontend/packages_and_registries/package_registry/components/list/packages_list_spec.js index ae990f3ea00..433d04017dc 100644 --- a/spec/frontend/packages_and_registries/package_registry/components/list/packages_list_spec.js +++ b/spec/frontend/packages_and_registries/package_registry/components/list/packages_list_spec.js @@ -258,7 +258,7 @@ describe('packages_list', () => { return nextTick(); }); - it('should display an alert message', () => { + it('should display an alert', () => { expect(findErrorPackageAlert().exists()).toBe(true); expect(findErrorPackageAlert().props('title')).toBe( 'There was an error publishing a error package package', diff --git a/spec/frontend/packages_and_registries/settings/project/settings/components/cleanup_image_tags_spec.js b/spec/frontend/packages_and_registries/settings/project/settings/components/cleanup_image_tags_spec.js index 49e8601da88..cbe68df5343 100644 --- a/spec/frontend/packages_and_registries/settings/project/settings/components/cleanup_image_tags_spec.js +++ b/spec/frontend/packages_and_registries/settings/project/settings/components/cleanup_image_tags_spec.js @@ -126,7 +126,7 @@ describe('Cleanup image tags project settings', () => { }); describe('an admin is visiting the page', () => { - it('shows the admin part of the alert message', async () => { + it('shows the admin part of the alert', async () => { mountComponentWithApollo({ provide: { ...defaultProvidedValues, isAdmin: true }, resolver: jest.fn().mockResolvedValue(nullExpirationPolicyPayload()), diff --git a/spec/frontend/packages_and_registries/settings/project/settings/components/container_expiration_policy_spec.js b/spec/frontend/packages_and_registries/settings/project/settings/components/container_expiration_policy_spec.js index 19f25d0aef7..c9dd9ce7a45 100644 --- a/spec/frontend/packages_and_registries/settings/project/settings/components/container_expiration_policy_spec.js +++ b/spec/frontend/packages_and_registries/settings/project/settings/components/container_expiration_policy_spec.js @@ -109,7 +109,7 @@ describe('Container expiration policy project settings', () => { }); describe('an admin is visiting the page', () => { - it('shows the admin part of the alert message', async () => { + it('shows the admin part of the alert', async () => { mountComponentWithApollo({ provide: { ...defaultProvidedValues, isAdmin: true }, resolver: jest.fn().mockResolvedValue(nullExpirationPolicyPayload()), diff --git a/spec/frontend/pages/projects/forks/new/components/project_namespace_spec.js b/spec/frontend/pages/projects/forks/new/components/project_namespace_spec.js index af578b69a81..b308d6305da 100644 --- a/spec/frontend/pages/projects/forks/new/components/project_namespace_spec.js +++ b/spec/frontend/pages/projects/forks/new/components/project_namespace_spec.js @@ -145,7 +145,7 @@ describe('ProjectNamespace component', () => { await nextTick(); }); - it('creates an alert message and captures the error', () => { + it('creates an alert and captures the error', () => { expect(createAlert).toHaveBeenCalledWith({ message: 'Something went wrong while loading data. Please refresh the page to try again.', captureError: true, diff --git a/spec/frontend/pipelines/graph/graph_component_wrapper_spec.js b/spec/frontend/pipelines/graph/graph_component_wrapper_spec.js index 42e47a23db8..cc952eac1d7 100644 --- a/spec/frontend/pipelines/graph/graph_component_wrapper_spec.js +++ b/spec/frontend/pipelines/graph/graph_component_wrapper_spec.js @@ -1,10 +1,10 @@ -import { GlAlert, GlButton, GlButtonGroup, GlLoadingIcon } from '@gitlab/ui'; -import { mount, shallowMount } from '@vue/test-utils'; +import { GlAlert, GlButton, GlButtonGroup, GlLoadingIcon, GlToggle } from '@gitlab/ui'; import MockAdapter from 'axios-mock-adapter'; import Vue from 'vue'; import VueApollo from 'vue-apollo'; import { useLocalStorageSpy } from 'helpers/local_storage_helper'; import createMockApollo from 'helpers/mock_apollo_helper'; +import { mountExtended, shallowMountExtended } from 'helpers/vue_test_utils_helper'; import { stubPerformanceWebAPI } from 'helpers/performance'; import waitForPromises from 'helpers/wait_for_promises'; import getPipelineDetails from 'shared_queries/pipelines/get_pipeline_details.query.graphql'; @@ -48,23 +48,25 @@ describe('Pipeline graph wrapper', () => { useLocalStorageSpy(); let wrapper; - const getAlert = () => wrapper.findComponent(GlAlert); - const getDependenciesToggle = () => wrapper.find('[data-testid="show-links-toggle"]'); - const getLoadingIcon = () => wrapper.findComponent(GlLoadingIcon); - const getLinksLayer = () => wrapper.findComponent(LinksLayer); - const getGraph = () => wrapper.findComponent(PipelineGraph); - const getStageColumnTitle = () => wrapper.find('[data-testid="stage-column-title"]'); - const getAllStageColumnGroupsInColumn = () => + let requestHandlers; + const findAlert = () => wrapper.findComponent(GlAlert); + const findDependenciesToggle = () => wrapper.findByTestId('show-links-toggle'); + const findLoadingIcon = () => wrapper.findComponent(GlLoadingIcon); + const findLinksLayer = () => wrapper.findComponent(LinksLayer); + const findGraph = () => wrapper.findComponent(PipelineGraph); + const findStageColumnTitle = () => wrapper.findByTestId('stage-column-title'); + const findAllStageColumnGroupsInColumn = () => wrapper.findComponent(StageColumnComponent).findAll('[data-testid="stage-column-group"]'); - const getViewSelector = () => wrapper.findComponent(GraphViewSelector); - const getViewSelectorTrip = () => getViewSelector().findComponent(GlAlert); + const findViewSelector = () => wrapper.findComponent(GraphViewSelector); + const findViewSelectorToggle = () => findViewSelector().findComponent(GlToggle); + const findViewSelectorTrip = () => findViewSelector().findComponent(GlAlert); const getLocalStorageSync = () => wrapper.findComponent(LocalStorageSync); const createComponent = ({ apolloProvider, data = {}, provide = {}, - mountFn = shallowMount, + mountFn = shallowMountExtended, } = {}) => { wrapper = mountFn(PipelineGraphWrapper, { provide: { @@ -84,37 +86,41 @@ describe('Pipeline graph wrapper', () => { calloutsList = [], data = {}, getPipelineDetailsHandler = jest.fn().mockResolvedValue(mockPipelineResponse), - mountFn = shallowMount, + mountFn = shallowMountExtended, provide = {}, } = {}) => { const callouts = mapCallouts(calloutsList); - const getUserCalloutsHandler = jest.fn().mockResolvedValue(mockCalloutsResponse(callouts)); - const getPipelineHeaderDataHandler = jest.fn().mockResolvedValue(mockRunningPipelineHeaderData); - const requestHandlers = [ - [getPipelineHeaderData, getPipelineHeaderDataHandler], - [getPipelineDetails, getPipelineDetailsHandler], - [getUserCallouts, getUserCalloutsHandler], + requestHandlers = { + getUserCalloutsHandler: jest.fn().mockResolvedValue(mockCalloutsResponse(callouts)), + getPipelineHeaderDataHandler: jest.fn().mockResolvedValue(mockRunningPipelineHeaderData), + getPipelineDetailsHandler, + }; + + const handlers = [ + [getPipelineHeaderData, requestHandlers.getPipelineHeaderDataHandler], + [getPipelineDetails, requestHandlers.getPipelineDetailsHandler], + [getUserCallouts, requestHandlers.getUserCalloutsHandler], ]; - const apolloProvider = createMockApollo(requestHandlers); + const apolloProvider = createMockApollo(handlers); createComponent({ apolloProvider, data, provide, mountFn }); }; describe('when data is loading', () => { it('displays the loading icon', () => { createComponentWithApollo(); - expect(getLoadingIcon().exists()).toBe(true); + expect(findLoadingIcon().exists()).toBe(true); }); it('does not display the alert', () => { createComponentWithApollo(); - expect(getAlert().exists()).toBe(false); + expect(findAlert().exists()).toBe(false); }); it('does not display the graph', () => { createComponentWithApollo(); - expect(getGraph().exists()).toBe(false); + expect(findGraph().exists()).toBe(false); }); it('skips querying headerPipeline', () => { @@ -130,19 +136,19 @@ describe('Pipeline graph wrapper', () => { }); it('does not display the loading icon', () => { - expect(getLoadingIcon().exists()).toBe(false); + expect(findLoadingIcon().exists()).toBe(false); }); it('does not display the alert', () => { - expect(getAlert().exists()).toBe(false); + expect(findAlert().exists()).toBe(false); }); it('displays the graph', () => { - expect(getGraph().exists()).toBe(true); + expect(findGraph().exists()).toBe(true); }); it('passes the etag resource and metrics path to the graph', () => { - expect(getGraph().props('configPaths')).toMatchObject({ + expect(findGraph().props('configPaths')).toMatchObject({ graphqlResourceEtag: defaultProvide.graphqlResourceEtag, metricsPath: defaultProvide.metricsPath, }); @@ -158,15 +164,15 @@ describe('Pipeline graph wrapper', () => { }); it('does not display the loading icon', () => { - expect(getLoadingIcon().exists()).toBe(false); + expect(findLoadingIcon().exists()).toBe(false); }); it('displays the alert', () => { - expect(getAlert().exists()).toBe(true); + expect(findAlert().exists()).toBe(true); }); it('does not display the graph', () => { - expect(getGraph().exists()).toBe(false); + expect(findGraph().exists()).toBe(false); }); }); @@ -181,18 +187,18 @@ describe('Pipeline graph wrapper', () => { }); it('does not display the loading icon', () => { - expect(getLoadingIcon().exists()).toBe(false); + expect(findLoadingIcon().exists()).toBe(false); }); it('displays the no iid alert', () => { - expect(getAlert().exists()).toBe(true); - expect(getAlert().text()).toBe( + expect(findAlert().exists()).toBe(true); + expect(findAlert().text()).toBe( 'The data in this pipeline is too old to be rendered as a graph. Please check the Jobs tab to access historical data.', ); }); it('does not display the graph', () => { - expect(getGraph().exists()).toBe(false); + expect(findGraph().exists()).toBe(false); }); }); @@ -203,11 +209,11 @@ describe('Pipeline graph wrapper', () => { }); describe('when receiving `setSkipRetryModal` event', () => { it('passes down `skipRetryModal` value as true', async () => { - expect(getGraph().props('skipRetryModal')).toBe(false); + expect(findGraph().props('skipRetryModal')).toBe(false); - await getGraph().vm.$emit('setSkipRetryModal'); + await findGraph().vm.$emit('setSkipRetryModal'); - expect(getGraph().props('skipRetryModal')).toBe(true); + expect(findGraph().props('skipRetryModal')).toBe(true); }); }); }); @@ -216,36 +222,37 @@ describe('Pipeline graph wrapper', () => { beforeEach(async () => { createComponentWithApollo(); await waitForPromises(); - await getGraph().vm.$emit('error', { type: ACTION_FAILURE }); + await findGraph().vm.$emit('error', { type: ACTION_FAILURE }); }); it('does not display the loading icon', () => { - expect(getLoadingIcon().exists()).toBe(false); + expect(findLoadingIcon().exists()).toBe(false); }); it('displays the action error alert', () => { - expect(getAlert().exists()).toBe(true); - expect(getAlert().text()).toBe('An error occurred while performing this action.'); + expect(findAlert().exists()).toBe(true); + expect(findAlert().text()).toBe('An error occurred while performing this action.'); }); it('displays the graph', () => { - expect(getGraph().exists()).toBe(true); + expect(findGraph().exists()).toBe(true); }); }); describe('when refresh action is emitted', () => { beforeEach(async () => { createComponentWithApollo(); - jest.spyOn(wrapper.vm.$apollo.queries.headerPipeline, 'refetch'); - jest.spyOn(wrapper.vm.$apollo.queries.pipeline, 'refetch'); await waitForPromises(); - getGraph().vm.$emit('refreshPipelineGraph'); + findGraph().vm.$emit('refreshPipelineGraph'); }); it('calls refetch', () => { - expect(wrapper.vm.$apollo.queries.headerPipeline.skip).toBe(false); - expect(wrapper.vm.$apollo.queries.headerPipeline.refetch).toHaveBeenCalled(); - expect(wrapper.vm.$apollo.queries.pipeline.refetch).toHaveBeenCalled(); + expect(requestHandlers.getPipelineHeaderDataHandler).toHaveBeenCalledWith({ + fullPath: 'frog/amphibirama', + iid: '22', + }); + expect(requestHandlers.getPipelineDetailsHandler).toHaveBeenCalledTimes(2); + expect(requestHandlers.getUserCalloutsHandler).toHaveBeenCalledWith({}); }); }); @@ -277,18 +284,18 @@ describe('Pipeline graph wrapper', () => { it('shows correct errors and does not overwrite populated data when data is empty', async () => { /* fails at first, shows error, no data yet */ - expect(getAlert().exists()).toBe(true); - expect(getGraph().exists()).toBe(false); + expect(findAlert().exists()).toBe(true); + expect(findGraph().exists()).toBe(false); /* succeeds, clears error, shows graph */ await advanceApolloTimers(); - expect(getAlert().exists()).toBe(false); - expect(getGraph().exists()).toBe(true); + expect(findAlert().exists()).toBe(false); + expect(findGraph().exists()).toBe(true); /* fails again, alert returns but data persists */ await advanceApolloTimers(); - expect(getAlert().exists()).toBe(true); - expect(getGraph().exists()).toBe(true); + expect(findAlert().exists()).toBe(true); + expect(findGraph().exists()).toBe(true); }); }); @@ -298,38 +305,38 @@ describe('Pipeline graph wrapper', () => { beforeEach(async () => { layersFn = jest.spyOn(parsingUtils, 'listByLayers'); createComponentWithApollo({ - mountFn: mount, + mountFn: mountExtended, }); await waitForPromises(); }); it('appears when pipeline uses needs', () => { - expect(getViewSelector().exists()).toBe(true); + expect(findViewSelector().exists()).toBe(true); }); it('switches between views', async () => { const groupsInFirstColumn = mockPipelineResponse.data.project.pipeline.stages.nodes[0].groups.nodes.length; - expect(getAllStageColumnGroupsInColumn()).toHaveLength(groupsInFirstColumn); - expect(getStageColumnTitle().text()).toBe('build'); - await getViewSelector().vm.$emit('updateViewType', LAYER_VIEW); - expect(getAllStageColumnGroupsInColumn()).toHaveLength(groupsInFirstColumn + 1); - expect(getStageColumnTitle().text()).toBe(''); + expect(findAllStageColumnGroupsInColumn()).toHaveLength(groupsInFirstColumn); + expect(findStageColumnTitle().text()).toBe('build'); + await findViewSelector().vm.$emit('updateViewType', LAYER_VIEW); + expect(findAllStageColumnGroupsInColumn()).toHaveLength(groupsInFirstColumn + 1); + expect(findStageColumnTitle().text()).toBe(''); }); it('saves the view type to local storage', async () => { - await getViewSelector().vm.$emit('updateViewType', LAYER_VIEW); + await findViewSelector().vm.$emit('updateViewType', LAYER_VIEW); expect(localStorage.setItem.mock.calls).toEqual([[VIEW_TYPE_KEY, LAYER_VIEW]]); }); it('calls listByLayers only once no matter how many times view is switched', async () => { expect(layersFn).not.toHaveBeenCalled(); - await getViewSelector().vm.$emit('updateViewType', LAYER_VIEW); + await findViewSelector().vm.$emit('updateViewType', LAYER_VIEW); expect(layersFn).toHaveBeenCalledTimes(1); - await getViewSelector().vm.$emit('updateViewType', STAGE_VIEW); - await getViewSelector().vm.$emit('updateViewType', LAYER_VIEW); - await getViewSelector().vm.$emit('updateViewType', STAGE_VIEW); + await findViewSelector().vm.$emit('updateViewType', STAGE_VIEW); + await findViewSelector().vm.$emit('updateViewType', LAYER_VIEW); + await findViewSelector().vm.$emit('updateViewType', STAGE_VIEW); expect(layersFn).toHaveBeenCalledTimes(1); }); }); @@ -340,7 +347,7 @@ describe('Pipeline graph wrapper', () => { data: { currentViewType: LAYER_VIEW, }, - mountFn: mount, + mountFn: mountExtended, }); jest.runOnlyPendingTimers(); @@ -349,10 +356,10 @@ describe('Pipeline graph wrapper', () => { it('sets showLinks to true', async () => { /* This spec uses .props for performance reasons. */ - expect(getLinksLayer().exists()).toBe(true); - expect(getLinksLayer().props('showLinks')).toBe(false); - expect(getViewSelector().props('type')).toBe(LAYER_VIEW); - await getDependenciesToggle().vm.$emit('change', true); + expect(findLinksLayer().exists()).toBe(true); + expect(findLinksLayer().props('showLinks')).toBe(false); + expect(findViewSelector().props('type')).toBe(LAYER_VIEW); + await findDependenciesToggle().vm.$emit('change', true); jest.runOnlyPendingTimers(); await waitForPromises(); @@ -367,15 +374,15 @@ describe('Pipeline graph wrapper', () => { currentViewType: LAYER_VIEW, showLinks: true, }, - mountFn: mount, + mountFn: mountExtended, }); await waitForPromises(); }); it('shows the hover tip in the view selector', async () => { - await getViewSelector().setData({ showLinksActive: true }); - expect(getViewSelectorTrip().exists()).toBe(true); + await findViewSelectorToggle().vm.$emit('change', true); + expect(findViewSelectorTrip().exists()).toBe(true); }); }); @@ -386,7 +393,7 @@ describe('Pipeline graph wrapper', () => { currentViewType: LAYER_VIEW, showLinks: true, }, - mountFn: mount, + mountFn: mountExtended, calloutsList: ['pipeline_needs_hover_tip'.toUpperCase()], }); @@ -395,8 +402,8 @@ describe('Pipeline graph wrapper', () => { }); it('does not show the hover tip', async () => { - await getViewSelector().setData({ showLinksActive: true }); - expect(getViewSelectorTrip().exists()).toBe(false); + await findViewSelectorToggle().vm.$emit('change', true); + expect(findViewSelectorTrip().exists()).toBe(false); }); }); @@ -405,7 +412,7 @@ describe('Pipeline graph wrapper', () => { localStorage.setItem(VIEW_TYPE_KEY, LAYER_VIEW); createComponentWithApollo({ - mountFn: mount, + mountFn: mountExtended, }); await waitForPromises(); @@ -436,7 +443,7 @@ describe('Pipeline graph wrapper', () => { localStorage.setItem(VIEW_TYPE_KEY, LAYER_VIEW); createComponentWithApollo({ - mountFn: mount, + mountFn: mountExtended, getPipelineDetailsHandler: jest.fn().mockResolvedValue(nonNeedsResponse), }); @@ -448,7 +455,7 @@ describe('Pipeline graph wrapper', () => { }); it('still passes stage type to graph', () => { - expect(getGraph().props('viewType')).toBe(STAGE_VIEW); + expect(findGraph().props('viewType')).toBe(STAGE_VIEW); }); }); @@ -458,7 +465,7 @@ describe('Pipeline graph wrapper', () => { nonNeedsResponse.data.project.pipeline.usesNeeds = false; createComponentWithApollo({ - mountFn: mount, + mountFn: mountExtended, getPipelineDetailsHandler: jest.fn().mockResolvedValue(nonNeedsResponse), }); @@ -467,7 +474,7 @@ describe('Pipeline graph wrapper', () => { }); it('does not appear when pipeline does not use needs', () => { - expect(getViewSelector().exists()).toBe(false); + expect(findViewSelector().exists()).toBe(false); }); }); }); diff --git a/spec/frontend/pipelines/test_reports/stores/mutations_spec.js b/spec/frontend/pipelines/test_reports/stores/mutations_spec.js index 9c374ea817a..685ac6ea3e5 100644 --- a/spec/frontend/pipelines/test_reports/stores/mutations_spec.js +++ b/spec/frontend/pipelines/test_reports/stores/mutations_spec.js @@ -58,7 +58,7 @@ describe('Mutations TestReports Store', () => { expect(mockState.errorMessage).toBe(message); }); - it('should show an alert message otherwise', () => { + it('should show an alert otherwise', () => { mutations[types.SET_SUITE_ERROR](mockState, {}); expect(createAlert).toHaveBeenCalled(); diff --git a/spec/frontend/projects/commit/store/actions_spec.js b/spec/frontend/projects/commit/store/actions_spec.js index d48f9fd6fc0..adb87142fee 100644 --- a/spec/frontend/projects/commit/store/actions_spec.js +++ b/spec/frontend/projects/commit/store/actions_spec.js @@ -63,7 +63,7 @@ describe('Commit form modal store actions', () => { ); }); - it('should show alert error and set error in state on fetchBranches failure', async () => { + it('should show an alert and set error in state on fetchBranches failure', async () => { jest.spyOn(axios, 'get').mockRejectedValue(); await testAction(actions.fetchBranches, {}, state, [], [{ type: 'requestBranches' }]); diff --git a/spec/frontend/projects/commits/store/actions_spec.js b/spec/frontend/projects/commits/store/actions_spec.js index f5184e59420..8afa2a6fb8f 100644 --- a/spec/frontend/projects/commits/store/actions_spec.js +++ b/spec/frontend/projects/commits/store/actions_spec.js @@ -34,7 +34,7 @@ describe('Project commits actions', () => { ])); }); - describe('shows an alert message when there is an error', () => { + describe('shows an alert when there is an error', () => { it('creates an alert', () => { const mockDispatchContext = { dispatch: () => {}, commit: () => {}, state }; actions.receiveAuthorsError(mockDispatchContext); diff --git a/spec/frontend/projects/compare/components/revision_dropdown_legacy_spec.js b/spec/frontend/projects/compare/components/revision_dropdown_legacy_spec.js index 645d0483a5f..e289569f8ce 100644 --- a/spec/frontend/projects/compare/components/revision_dropdown_legacy_spec.js +++ b/spec/frontend/projects/compare/components/revision_dropdown_legacy_spec.js @@ -90,7 +90,7 @@ describe('RevisionDropdown component', () => { expect(findTagsDropdownItem()).toHaveLength(0); }); - it('shows alert message on error', async () => { + it('shows an alert on error', async () => { axiosMock.onGet('some/invalid/path').replyOnce(HTTP_STATUS_NOT_FOUND); await waitForPromises(); diff --git a/spec/frontend/projects/compare/components/revision_dropdown_spec.js b/spec/frontend/projects/compare/components/revision_dropdown_spec.js index 3a256682549..1cf99f16601 100644 --- a/spec/frontend/projects/compare/components/revision_dropdown_spec.js +++ b/spec/frontend/projects/compare/components/revision_dropdown_spec.js @@ -78,7 +78,7 @@ describe('RevisionDropdown component', () => { }); }); - it('shows alert message on error', async () => { + it('shows an alert on error', async () => { axiosMock.onGet('some/invalid/path').replyOnce(HTTP_STATUS_NOT_FOUND); createComponent(); @@ -104,7 +104,7 @@ describe('RevisionDropdown component', () => { }); describe('search', () => { - it('shows alert message on error', async () => { + it('shows alert on error', async () => { axiosMock.onGet('some/invalid/path').replyOnce(HTTP_STATUS_NOT_FOUND); createComponent(); diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb index caaf85c79c3..0e3c2112691 100644 --- a/spec/models/project_spec.rb +++ b/spec/models/project_spec.rb @@ -5920,7 +5920,7 @@ RSpec.describe Project, factory_default: :keep, feature_category: :projects do expect(project).to receive(:after_create_default_branch) expect(project).to receive(:refresh_markdown_cache!) expect(InternalId).to receive(:flush_records!).with(project: project) - expect(ProjectCacheWorker).to receive(:perform_async).with(project.id, [], [:repository_size]) + expect(ProjectCacheWorker).to receive(:perform_async).with(project.id, [], [:repository_size, :wiki_size]) expect(DetectRepositoryLanguagesWorker).to receive(:perform_async).with(project.id) expect(AuthorizedProjectUpdate::ProjectRecalculateWorker).to receive(:perform_async).with(project.id) expect(project).to receive(:set_full_path) |