diff options
86 files changed, 462 insertions, 439 deletions
diff --git a/.gitlab/ci/reports.gitlab-ci.yml b/.gitlab/ci/reports.gitlab-ci.yml index b9d2e31191a..d040abfe902 100644 --- a/.gitlab/ci/reports.gitlab-ci.yml +++ b/.gitlab/ci/reports.gitlab-ci.yml @@ -121,6 +121,11 @@ yarn-audit-dependency_scanning: - cd .. && tar -I "gzip --best" -cf gitlab.tgz gitlab/ script: - DEBUG=* node /usr/src/app/cli.js analyze --format gitlab --manager ${PACKAGE_MANAGER} gitlab.tgz | tee ${CI_PROJECT_DIR}/gl-dependency-scanning-report.json + after_script: + - mkdir ~/.aws + - '[[ -z "${AWS_SIEM_REPORT_INGESTION_CREDENTIALS_FILE}" ]] || mv "${AWS_SIEM_REPORT_INGESTION_CREDENTIALS_FILE}" ~/.aws/credentials' + - npm install --no-save --ignore-scripts @aws-sdk/client-s3@3.49.0 + - scripts/ingest-reports-to-siem artifacts: paths: - gl-dependency-scanning-report.json diff --git a/app/assets/javascripts/deploy_tokens/components/revoke_button.vue b/app/assets/javascripts/deploy_tokens/components/revoke_button.vue index fdf8b7796bf..7879357a042 100644 --- a/app/assets/javascripts/deploy_tokens/components/revoke_button.vue +++ b/app/assets/javascripts/deploy_tokens/components/revoke_button.vue @@ -17,9 +17,6 @@ export default { revokePath: { default: '', }, - buttonClass: { - default: '', - }, }, computed: { modalId() { @@ -38,10 +35,9 @@ export default { <div> <gl-button v-gl-modal="modalId" - :class="buttonClass" category="primary" variant="danger" - class="float-right" + class="gl-float-right" data-testid="revoke-button" >{{ s__('DeployTokens|Revoke') }}</gl-button > diff --git a/app/assets/javascripts/deploy_tokens/init_revoke_button.js b/app/assets/javascripts/deploy_tokens/init_revoke_button.js index 20187150a60..bc3f3c9ddf4 100644 --- a/app/assets/javascripts/deploy_tokens/init_revoke_button.js +++ b/app/assets/javascripts/deploy_tokens/init_revoke_button.js @@ -9,14 +9,13 @@ export default () => { } return containers.forEach((el) => { - const { token, revokePath, buttonClass } = el.dataset; + const { token, revokePath } = el.dataset; return new Vue({ el, provide: { token: JSON.parse(token), revokePath, - buttonClass, }, render(h) { return h(RevokeButton); diff --git a/app/assets/javascripts/integrations/edit/components/jira_issues_fields.vue b/app/assets/javascripts/integrations/edit/components/jira_issues_fields.vue index 7f2f7620a86..198cabfad81 100644 --- a/app/assets/javascripts/integrations/edit/components/jira_issues_fields.vue +++ b/app/assets/javascripts/integrations/edit/components/jira_issues_fields.vue @@ -83,17 +83,17 @@ export default { i18n: { sectionTitle: s__('JiraService|View Jira issues in GitLab'), sectionDescription: s__( - 'JiraService|Work on Jira issues without leaving GitLab. Adds a Jira menu to access your list of Jira issues and view any issue as read-only.', + 'JiraService|Work on Jira issues without leaving GitLab. Add a Jira menu to access a read-only list of your Jira issues.', ), enableCheckboxLabel: s__('JiraService|Enable Jira issues'), enableCheckboxHelp: s__( - 'JiraService|Warning: All GitLab users that have access to this GitLab project are able to view all issues from the Jira project specified below.', + 'JiraService|Warning: All GitLab users with access to this GitLab project can view all issues from the Jira project you select.', ), projectKeyLabel: s__('JiraService|Jira project key'), projectKeyPlaceholder: s__('JiraService|For example, AB'), requiredFieldFeedback: __('This field is required.'), issueTrackerConflictWarning: s__( - 'JiraService|Displaying Jira issues while leaving the GitLab issue functionality enabled might be confusing. Consider %{linkStart}disabling GitLab issues%{linkEnd} if they won’t otherwise be used.', + 'JiraService|Displaying Jira issues while leaving GitLab issues also enabled might be confusing. Consider %{linkStart}disabling GitLab issues%{linkEnd} if they won’t otherwise be used.', ), }, }; diff --git a/app/assets/javascripts/integrations/edit/components/jira_trigger_fields.vue b/app/assets/javascripts/integrations/edit/components/jira_trigger_fields.vue index df5946b814a..bb8d630fb0e 100644 --- a/app/assets/javascripts/integrations/edit/components/jira_trigger_fields.vue +++ b/app/assets/javascripts/integrations/edit/components/jira_trigger_fields.vue @@ -138,7 +138,7 @@ export default { label-for="service[trigger]" :description=" s__( - 'Integrations|When you mention a Jira issue in a commit or merge request, GitLab creates a remote link and comment (if enabled).', + 'JiraService|When a Jira issue is mentioned in a commit or merge request, a remote link and comment (if enabled) will be created.', ) " > diff --git a/app/assets/javascripts/invite_members/components/invite_modal_base.vue b/app/assets/javascripts/invite_members/components/invite_modal_base.vue index fc00f5b9343..e92433e0ccf 100644 --- a/app/assets/javascripts/invite_members/components/invite_modal_base.vue +++ b/app/assets/javascripts/invite_members/components/invite_modal_base.vue @@ -264,7 +264,7 @@ export default { <gl-button :disabled="submitDisabled" :loading="isLoading" - variant="success" + variant="confirm" data-qa-selector="invite_button" data-testid="invite-button" @click="submit" diff --git a/app/assets/javascripts/issues/show/components/header_actions.vue b/app/assets/javascripts/issues/show/components/header_actions.vue index 8ba08472ea0..78ec5071b59 100644 --- a/app/assets/javascripts/issues/show/components/header_actions.vue +++ b/app/assets/javascripts/issues/show/components/header_actions.vue @@ -135,6 +135,14 @@ export default { const canReopen = this.isClosed && this.canReopenIssue; return canClose || canReopen; }, + hasDesktopDropdown() { + return ( + this.canCreateIssue || this.canPromoteToEpic || !this.isIssueAuthor || this.canReportSpam + ); + }, + hasMobileDropdown() { + return this.hasDesktopDropdown || this.showToggleIssueStateButton; + }, }, created() { eventHub.$on('toggle.issuable.state', this.toggleIssueState); @@ -223,10 +231,12 @@ export default { <template> <div class="detail-page-header-actions gl-display-flex"> <gl-dropdown + v-if="hasMobileDropdown" class="gl-sm-display-none! w-100" block :text="dropdownText" data-qa-selector="issue_actions_dropdown" + data-testid="mobile-dropdown" :loading="isToggleStateButtonLoading" > <gl-dropdown-item @@ -276,11 +286,13 @@ export default { </gl-button> <gl-dropdown + v-if="hasDesktopDropdown" class="gl-display-none gl-sm-display-inline-flex! gl-ml-3" icon="ellipsis_v" category="tertiary" :text="dropdownText" :text-sr-only="true" + data-testid="desktop-dropdown" no-caret right > diff --git a/app/assets/javascripts/merge_conflicts/merge_conflict_resolver_app.vue b/app/assets/javascripts/merge_conflicts/merge_conflict_resolver_app.vue index 5fcc778a714..fdcb99351a7 100644 --- a/app/assets/javascripts/merge_conflicts/merge_conflict_resolver_app.vue +++ b/app/assets/javascripts/merge_conflicts/merge_conflict_resolver_app.vue @@ -1,5 +1,5 @@ <script> -import { GlSprintf, GlButton, GlButtonGroup } from '@gitlab/ui'; +import { GlSprintf, GlButton, GlButtonGroup, GlLoadingIcon } from '@gitlab/ui'; import { mapGetters, mapState, mapActions } from 'vuex'; import { __ } from '~/locale'; import FileIcon from '~/vue_shared/components/file_icon.vue'; @@ -23,6 +23,7 @@ export default { GlButton, GlButtonGroup, GlSprintf, + GlLoadingIcon, FileIcon, DiffFileEditor, InlineConflictLines, @@ -72,9 +73,7 @@ export default { </script> <template> <div id="conflicts"> - <div v-if="isLoading" class="loading"> - <div class="spinner spinner-md"></div> - </div> + <gl-loading-icon v-if="isLoading" size="md" data-testid="loading-spinner" /> <div v-if="hasError" class="nothing-here-block"> {{ conflictsData.errorMessage }} </div> diff --git a/app/assets/javascripts/pipeline_editor/pipeline_editor_app.vue b/app/assets/javascripts/pipeline_editor/pipeline_editor_app.vue index 1da50c55a68..a5436ca63cb 100644 --- a/app/assets/javascripts/pipeline_editor/pipeline_editor_app.vue +++ b/app/assets/javascripts/pipeline_editor/pipeline_editor_app.vue @@ -69,9 +69,10 @@ export default { // If it's a brand new file, we don't want to fetch the content. // Then when the user commits the first time, the query would run // to get the initial file content, but we already have it in `lastCommitedContent` - // so we skip the loading altogether. - skip({ isNewCiConfigFile, lastCommittedContent }) { - return isNewCiConfigFile || lastCommittedContent; + // so we skip the loading altogether. We also wait for the currentBranch + // to have been fetched + skip() { + return this.shouldSkipBlobContentQuery; }, variables() { return { @@ -128,8 +129,8 @@ export default { }, ciConfigData: { query: getCiConfigData, - skip({ currentCiFileContent }) { - return !currentCiFileContent; + skip() { + return this.shouldSkipCiConfigQuery; }, variables() { return { @@ -174,6 +175,9 @@ export default { }, commitSha: { query: getLatestCommitShaQuery, + skip({ currentBranch }) { + return !currentBranch; + }, variables() { return { projectPath: this.projectFullPath, @@ -181,7 +185,7 @@ export default { }; }, update(data) { - const latestCommitSha = data.project?.repository?.tree?.lastCommit?.sha; + const latestCommitSha = data?.project?.repository?.tree?.lastCommit?.sha; if (this.isFetchingCommitSha && latestCommitSha === this.commitSha) { this.$apollo.queries.commitSha.startPolling(COMMIT_SHA_POLL_INTERVAL); @@ -192,6 +196,9 @@ export default { this.$apollo.queries.commitSha.stopPolling(); return latestCommitSha; }, + error() { + this.reportFailure(LOAD_FAILURE_UNKNOWN); + }, }, currentBranch: { query: getCurrentBranch, @@ -234,6 +241,12 @@ export default { isEmpty() { return this.currentCiFileContent === ''; }, + shouldSkipBlobContentQuery() { + return this.isNewCiConfigFile || this.lastCommittedContent || !this.currentBranch; + }, + shouldSkipCiConfigQuery() { + return !this.currentCiFileContent || !this.commitSha; + }, }, i18n: { resetModal: { diff --git a/app/assets/javascripts/runner/admin_runner_edit/admin_runner_edit_app.vue b/app/assets/javascripts/runner/admin_runner_edit/admin_runner_edit_app.vue index 4d2ca9b0c58..c2db3b9facd 100644 --- a/app/assets/javascripts/runner/admin_runner_edit/admin_runner_edit_app.vue +++ b/app/assets/javascripts/runner/admin_runner_edit/admin_runner_edit_app.vue @@ -5,7 +5,7 @@ import { convertToGraphQLId } from '~/graphql_shared/utils'; import RunnerHeader from '../components/runner_header.vue'; import RunnerUpdateForm from '../components/runner_update_form.vue'; import { I18N_FETCH_ERROR } from '../constants'; -import getRunnerQuery from '../graphql/get_runner.query.graphql'; +import runnerQuery from '../graphql/details/runner.query.graphql'; import { captureException } from '../sentry_utils'; export default { @@ -27,7 +27,7 @@ export default { }, apollo: { runner: { - query: getRunnerQuery, + query: runnerQuery, variables() { return { id: convertToGraphQLId(TYPE_CI_RUNNER, this.runnerId), diff --git a/app/assets/javascripts/runner/admin_runner_show/admin_runner_show_app.vue b/app/assets/javascripts/runner/admin_runner_show/admin_runner_show_app.vue index 2795ddbbbcb..86ad912f017 100644 --- a/app/assets/javascripts/runner/admin_runner_show/admin_runner_show_app.vue +++ b/app/assets/javascripts/runner/admin_runner_show/admin_runner_show_app.vue @@ -8,7 +8,7 @@ import RunnerPauseButton from '../components/runner_pause_button.vue'; import RunnerHeader from '../components/runner_header.vue'; import RunnerDetails from '../components/runner_details.vue'; import { I18N_FETCH_ERROR } from '../constants'; -import getRunnerQuery from '../graphql/get_runner.query.graphql'; +import runnerQuery from '../graphql/details/runner.query.graphql'; import { captureException } from '../sentry_utils'; export default { @@ -35,7 +35,7 @@ export default { }, apollo: { runner: { - query: getRunnerQuery, + query: runnerQuery, variables() { return { id: convertToGraphQLId(TYPE_CI_RUNNER, this.runnerId), diff --git a/app/assets/javascripts/runner/admin_runners/admin_runners_app.vue b/app/assets/javascripts/runner/admin_runners/admin_runners_app.vue index 3ce9a890b37..5e487ee5c52 100644 --- a/app/assets/javascripts/runner/admin_runners/admin_runners_app.vue +++ b/app/assets/javascripts/runner/admin_runners/admin_runners_app.vue @@ -26,8 +26,8 @@ import { STATUS_STALE, I18N_FETCH_ERROR, } from '../constants'; -import getRunnersQuery from '../graphql/get_runners.query.graphql'; -import getRunnersCountQuery from '../graphql/get_runners_count.query.graphql'; +import runnersAdminQuery from '../graphql/list/admin_runners.query.graphql'; +import runnersAdminCountQuery from '../graphql/list/admin_runners_count.query.graphql'; import { fromUrlQueryToSearch, fromSearchToUrl, @@ -36,7 +36,7 @@ import { import { captureException } from '../sentry_utils'; const runnersCountSmartQuery = { - query: getRunnersCountQuery, + query: runnersAdminCountQuery, fetchPolicy: fetchPolicies.CACHE_AND_NETWORK, update(data) { return data?.runners?.count; @@ -77,7 +77,7 @@ export default { }, apollo: { runners: { - query: getRunnersQuery, + query: runnersAdminQuery, // Runners can be updated by users directly in this list. // A "cache and network" policy prevents outdated filtered // results. diff --git a/app/assets/javascripts/runner/components/registration/registration_token_reset_dropdown_item.vue b/app/assets/javascripts/runner/components/registration/registration_token_reset_dropdown_item.vue index e65d30afee1..1234054c660 100644 --- a/app/assets/javascripts/runner/components/registration/registration_token_reset_dropdown_item.vue +++ b/app/assets/javascripts/runner/components/registration/registration_token_reset_dropdown_item.vue @@ -4,7 +4,7 @@ import { createAlert } from '~/flash'; import { TYPE_GROUP, TYPE_PROJECT } from '~/graphql_shared/constants'; import { convertToGraphQLId } from '~/graphql_shared/utils'; import { __, s__ } from '~/locale'; -import runnersRegistrationTokenResetMutation from '~/runner/graphql/runners_registration_token_reset.mutation.graphql'; +import runnersRegistrationTokenResetMutation from '~/runner/graphql/list/runners_registration_token_reset.mutation.graphql'; import { captureException } from '~/runner/sentry_utils'; import { INSTANCE_TYPE, GROUP_TYPE, PROJECT_TYPE } from '../../constants'; diff --git a/app/assets/javascripts/runner/components/runner_delete_button.vue b/app/assets/javascripts/runner/components/runner_delete_button.vue index 4a508c64214..cf71a4705ca 100644 --- a/app/assets/javascripts/runner/components/runner_delete_button.vue +++ b/app/assets/javascripts/runner/components/runner_delete_button.vue @@ -1,6 +1,6 @@ <script> import { GlButton, GlModalDirective, GlTooltipDirective } from '@gitlab/ui'; -import runnerDeleteMutation from '~/runner/graphql/runner_delete.mutation.graphql'; +import runnerDeleteMutation from '~/runner/graphql/shared/runner_delete.mutation.graphql'; import { createAlert } from '~/flash'; import { s__, sprintf } from '~/locale'; import { captureException } from '~/runner/sentry_utils'; diff --git a/app/assets/javascripts/runner/components/runner_jobs.vue b/app/assets/javascripts/runner/components/runner_jobs.vue index f9804ed815a..eb77babcc57 100644 --- a/app/assets/javascripts/runner/components/runner_jobs.vue +++ b/app/assets/javascripts/runner/components/runner_jobs.vue @@ -1,7 +1,7 @@ <script> import { GlSkeletonLoading } from '@gitlab/ui'; import { createAlert } from '~/flash'; -import getRunnerJobsQuery from '../graphql/get_runner_jobs.query.graphql'; +import runnerJobsQuery from '../graphql/details/runner_jobs.query.graphql'; import { I18N_FETCH_ERROR, I18N_NO_JOBS_FOUND, RUNNER_DETAILS_JOBS_PAGE_SIZE } from '../constants'; import { captureException } from '../sentry_utils'; import { getPaginationVariables } from '../utils'; @@ -34,7 +34,7 @@ export default { }, apollo: { jobs: { - query: getRunnerJobsQuery, + query: runnerJobsQuery, variables() { return this.variables; }, diff --git a/app/assets/javascripts/runner/components/runner_pause_button.vue b/app/assets/javascripts/runner/components/runner_pause_button.vue index 3ea2006d17a..a1901c55e61 100644 --- a/app/assets/javascripts/runner/components/runner_pause_button.vue +++ b/app/assets/javascripts/runner/components/runner_pause_button.vue @@ -1,6 +1,6 @@ <script> import { GlButton, GlTooltipDirective } from '@gitlab/ui'; -import runnerToggleActiveMutation from '~/runner/graphql/runner_toggle_active.mutation.graphql'; +import runnerToggleActiveMutation from '~/runner/graphql/shared/runner_toggle_active.mutation.graphql'; import { createAlert } from '~/flash'; import { captureException } from '~/runner/sentry_utils'; import { I18N_PAUSE, I18N_RESUME } from '../constants'; diff --git a/app/assets/javascripts/runner/components/runner_projects.vue b/app/assets/javascripts/runner/components/runner_projects.vue index 2040b39e948..f8ec29b8a24 100644 --- a/app/assets/javascripts/runner/components/runner_projects.vue +++ b/app/assets/javascripts/runner/components/runner_projects.vue @@ -2,7 +2,7 @@ import { GlSkeletonLoading } from '@gitlab/ui'; import { sprintf, formatNumber } from '~/locale'; import { createAlert } from '~/flash'; -import getRunnerProjectsQuery from '../graphql/get_runner_projects.query.graphql'; +import runnerProjectsQuery from '../graphql/details/runner_projects.query.graphql'; import { I18N_ASSIGNED_PROJECTS, I18N_NONE, @@ -41,7 +41,7 @@ export default { }, apollo: { projects: { - query: getRunnerProjectsQuery, + query: runnerProjectsQuery, variables() { return this.variables; }, diff --git a/app/assets/javascripts/runner/components/runner_update_form.vue b/app/assets/javascripts/runner/components/runner_update_form.vue index abff3e59407..e44450a2a8d 100644 --- a/app/assets/javascripts/runner/components/runner_update_form.vue +++ b/app/assets/javascripts/runner/components/runner_update_form.vue @@ -15,7 +15,7 @@ import { createAlert, VARIANT_SUCCESS } from '~/flash'; import { __ } from '~/locale'; import { captureException } from '~/runner/sentry_utils'; import { ACCESS_LEVEL_NOT_PROTECTED, ACCESS_LEVEL_REF_PROTECTED, PROJECT_TYPE } from '../constants'; -import runnerUpdateMutation from '../graphql/runner_update.mutation.graphql'; +import runnerUpdateMutation from '../graphql/details/runner_update.mutation.graphql'; export default { name: 'RunnerUpdateForm', diff --git a/app/assets/javascripts/runner/graphql/get_runner.query.graphql b/app/assets/javascripts/runner/graphql/details/runner.query.graphql index f6ce8281c64..4792a186160 100644 --- a/app/assets/javascripts/runner/graphql/get_runner.query.graphql +++ b/app/assets/javascripts/runner/graphql/details/runner.query.graphql @@ -1,10 +1,9 @@ -#import "ee_else_ce/runner/graphql/runner_details.fragment.graphql" +#import "ee_else_ce/runner/graphql/details/runner_details.fragment.graphql" query getRunner($id: CiRunnerID!) { # We have an id in deeply nested fragment # eslint-disable-next-line @graphql-eslint/require-id-when-available runner(id: $id) { - __typename ...RunnerDetails } } diff --git a/app/assets/javascripts/runner/graphql/runner_details.fragment.graphql b/app/assets/javascripts/runner/graphql/details/runner_details.fragment.graphql index 2449ee0fc0f..2449ee0fc0f 100644 --- a/app/assets/javascripts/runner/graphql/runner_details.fragment.graphql +++ b/app/assets/javascripts/runner/graphql/details/runner_details.fragment.graphql diff --git a/app/assets/javascripts/runner/graphql/runner_details_shared.fragment.graphql b/app/assets/javascripts/runner/graphql/details/runner_details_shared.fragment.graphql index 74760bbaa07..d8c67728fac 100644 --- a/app/assets/javascripts/runner/graphql/runner_details_shared.fragment.graphql +++ b/app/assets/javascripts/runner/graphql/details/runner_details_shared.fragment.graphql @@ -1,4 +1,5 @@ fragment RunnerDetailsShared on CiRunner { + __typename id runnerType active @@ -22,7 +23,7 @@ fragment RunnerDetailsShared on CiRunner { groups { # Only a single group can be loaded here, while projects # are loaded separately using the query with pagination - # parameters `get_runner_projects.query.graphql`. + # parameters `runner_projects.query.graphql`. nodes { id avatarUrl diff --git a/app/assets/javascripts/runner/graphql/get_runner_jobs.query.graphql b/app/assets/javascripts/runner/graphql/details/runner_jobs.query.graphql index 2b1decd3ddd..2b1decd3ddd 100644 --- a/app/assets/javascripts/runner/graphql/get_runner_jobs.query.graphql +++ b/app/assets/javascripts/runner/graphql/details/runner_jobs.query.graphql diff --git a/app/assets/javascripts/runner/graphql/get_runner_projects.query.graphql b/app/assets/javascripts/runner/graphql/details/runner_projects.query.graphql index f97237b8267..f97237b8267 100644 --- a/app/assets/javascripts/runner/graphql/get_runner_projects.query.graphql +++ b/app/assets/javascripts/runner/graphql/details/runner_projects.query.graphql diff --git a/app/assets/javascripts/runner/graphql/runner_update.mutation.graphql b/app/assets/javascripts/runner/graphql/details/runner_update.mutation.graphql index 8d1b75828be..e4bf51e2c30 100644 --- a/app/assets/javascripts/runner/graphql/runner_update.mutation.graphql +++ b/app/assets/javascripts/runner/graphql/details/runner_update.mutation.graphql @@ -1,4 +1,4 @@ -#import "ee_else_ce/runner/graphql/runner_details.fragment.graphql" +#import "ee_else_ce/runner/graphql/details/runner_details.fragment.graphql" # Mutation for updates from the runner form, loads # attributes shown in the runner details. diff --git a/app/assets/javascripts/runner/graphql/get_runners.query.graphql b/app/assets/javascripts/runner/graphql/list/admin_runners.query.graphql index ed03a8c34ae..8df4c2fc65c 100644 --- a/app/assets/javascripts/runner/graphql/get_runners.query.graphql +++ b/app/assets/javascripts/runner/graphql/list/admin_runners.query.graphql @@ -1,4 +1,4 @@ -#import "~/runner/graphql/runner_node.fragment.graphql" +#import "~/runner/graphql/list/list_item.fragment.graphql" #import "~/graphql_shared/fragments/pageInfo.fragment.graphql" query getRunners( @@ -24,7 +24,7 @@ query getRunners( sort: $sort ) { nodes { - ...RunnerNode + ...ListItem adminUrl editAdminUrl } diff --git a/app/assets/javascripts/runner/graphql/get_runners_count.query.graphql b/app/assets/javascripts/runner/graphql/list/admin_runners_count.query.graphql index 181a4495cae..181a4495cae 100644 --- a/app/assets/javascripts/runner/graphql/get_runners_count.query.graphql +++ b/app/assets/javascripts/runner/graphql/list/admin_runners_count.query.graphql diff --git a/app/assets/javascripts/runner/graphql/get_group_runners.query.graphql b/app/assets/javascripts/runner/graphql/list/group_runners.query.graphql index f9c7b89fc01..b517f5e89a8 100644 --- a/app/assets/javascripts/runner/graphql/get_group_runners.query.graphql +++ b/app/assets/javascripts/runner/graphql/list/group_runners.query.graphql @@ -1,4 +1,4 @@ -#import "~/runner/graphql/runner_node.fragment.graphql" +#import "~/runner/graphql/list/list_item.fragment.graphql" #import "~/graphql_shared/fragments/pageInfo.fragment.graphql" query getGroupRunners( @@ -29,8 +29,7 @@ query getGroupRunners( webUrl editUrl node { - __typename - ...RunnerNode + ...ListItem } } pageInfo { diff --git a/app/assets/javascripts/runner/graphql/get_group_runners_count.query.graphql b/app/assets/javascripts/runner/graphql/list/group_runners_count.query.graphql index 554eb09e372..554eb09e372 100644 --- a/app/assets/javascripts/runner/graphql/get_group_runners_count.query.graphql +++ b/app/assets/javascripts/runner/graphql/list/group_runners_count.query.graphql diff --git a/app/assets/javascripts/runner/graphql/runner_node.fragment.graphql b/app/assets/javascripts/runner/graphql/list/list_item.fragment.graphql index fbdef817f2f..620c18c5bc0 100644 --- a/app/assets/javascripts/runner/graphql/runner_node.fragment.graphql +++ b/app/assets/javascripts/runner/graphql/list/list_item.fragment.graphql @@ -1,4 +1,4 @@ -fragment RunnerNode on CiRunner { +fragment ListItem on CiRunner { __typename id description diff --git a/app/assets/javascripts/runner/graphql/runners_registration_token_reset.mutation.graphql b/app/assets/javascripts/runner/graphql/list/runners_registration_token_reset.mutation.graphql index 9c2797732ad..9c2797732ad 100644 --- a/app/assets/javascripts/runner/graphql/runners_registration_token_reset.mutation.graphql +++ b/app/assets/javascripts/runner/graphql/list/runners_registration_token_reset.mutation.graphql diff --git a/app/assets/javascripts/runner/graphql/runner_delete.mutation.graphql b/app/assets/javascripts/runner/graphql/shared/runner_delete.mutation.graphql index d580ea2785e..d580ea2785e 100644 --- a/app/assets/javascripts/runner/graphql/runner_delete.mutation.graphql +++ b/app/assets/javascripts/runner/graphql/shared/runner_delete.mutation.graphql diff --git a/app/assets/javascripts/runner/graphql/runner_toggle_active.mutation.graphql b/app/assets/javascripts/runner/graphql/shared/runner_toggle_active.mutation.graphql index 9b15570dbc0..9b15570dbc0 100644 --- a/app/assets/javascripts/runner/graphql/runner_toggle_active.mutation.graphql +++ b/app/assets/javascripts/runner/graphql/shared/runner_toggle_active.mutation.graphql diff --git a/app/assets/javascripts/runner/group_runners/group_runners_app.vue b/app/assets/javascripts/runner/group_runners/group_runners_app.vue index ba33ead6f46..f6b631bd216 100644 --- a/app/assets/javascripts/runner/group_runners/group_runners_app.vue +++ b/app/assets/javascripts/runner/group_runners/group_runners_app.vue @@ -24,8 +24,8 @@ import { STATUS_OFFLINE, STATUS_STALE, } from '../constants'; -import getGroupRunnersQuery from '../graphql/get_group_runners.query.graphql'; -import getGroupRunnersCountQuery from '../graphql/get_group_runners_count.query.graphql'; +import groupRunnersQuery from '../graphql/list/group_runners.query.graphql'; +import groupRunnersCountQuery from '../graphql/list/group_runners_count.query.graphql'; import { fromUrlQueryToSearch, fromSearchToUrl, @@ -34,7 +34,7 @@ import { import { captureException } from '../sentry_utils'; const runnersCountSmartQuery = { - query: getGroupRunnersCountQuery, + query: groupRunnersCountQuery, fetchPolicy: fetchPolicies.CACHE_AND_NETWORK, update(data) { return data?.group?.runners?.count; @@ -84,7 +84,7 @@ export default { }, apollo: { runners: { - query: getGroupRunnersQuery, + query: groupRunnersQuery, // Runners can be updated by users directly in this list. // A "cache and network" policy prevents outdated filtered // results. diff --git a/app/controllers/projects/cluster_agents_controller.rb b/app/controllers/projects/cluster_agents_controller.rb index 84bb01ee266..282b9ef1fb7 100644 --- a/app/controllers/projects/cluster_agents_controller.rb +++ b/app/controllers/projects/cluster_agents_controller.rb @@ -3,10 +3,6 @@ class Projects::ClusterAgentsController < Projects::ApplicationController before_action :authorize_can_read_cluster_agent! - before_action do - push_frontend_feature_flag(:cluster_vulnerabilities, project, default_enabled: :yaml) - end - feature_category :kubernetes_management def show diff --git a/app/experiments/application_experiment.rb b/app/experiments/application_experiment.rb index 15c7f3389c4..f74e7fe3b1d 100644 --- a/app/experiments/application_experiment.rb +++ b/app/experiments/application_experiment.rb @@ -3,20 +3,6 @@ class ApplicationExperiment < Gitlab::Experiment control { nil } # provide a default control for anonymous experiments - def publish(_result = nil) - super - - publish_to_client - end - - def publish_to_client - return unless should_track? - - Gon.push({ experiment: { name => signature } }, true) - rescue NoMethodError - # means we're not in the request cycle, and can't add to Gon. Log a warning maybe? - end - def publish_to_database ActiveSupport::Deprecation.warn('publish_to_database is deprecated and should not be used for reporting anymore') diff --git a/app/helpers/deploy_tokens_helper.rb b/app/helpers/deploy_tokens_helper.rb index d6fbe0b6b45..560d2fcd29f 100644 --- a/app/helpers/deploy_tokens_helper.rb +++ b/app/helpers/deploy_tokens_helper.rb @@ -16,4 +16,11 @@ module DeployTokensHelper Gitlab.config.packages.enabled && can?(current_user, :read_package, group_or_project) end + + def deploy_token_revoke_button_data(token:, group_or_project:) + { + token: token.to_json(only: [:id, :name]), + revoke_path: revoke_deploy_token_path(group_or_project, token) + } + end end diff --git a/app/models/integrations/jira.rb b/app/models/integrations/jira.rb index 8266d113492..8fc0a3461f7 100644 --- a/app/models/integrations/jira.rb +++ b/app/models/integrations/jira.rb @@ -111,8 +111,8 @@ module Integrations end def help - jira_doc_link_start = '<a href="%{url}" target="_blank" rel="noopener noreferrer">'.html_safe % { url: help_page_url('integration/jira/index.html') } - s_("JiraService|You must configure Jira before enabling this integration. For more details, read the %{jira_doc_link_start}Jira integration documentation%{link_end}.") % { jira_doc_link_start: jira_doc_link_start, link_end: '</a>'.html_safe } + jira_doc_link_start = '<a href="%{url}">'.html_safe % { url: help_page_url('integration/jira/index.html') } + s_("JiraService|You must configure Jira before enabling this integration. %{jira_doc_link_start}Learn more.%{link_end}") % { jira_doc_link_start: jira_doc_link_start, link_end: '</a>'.html_safe } end def title diff --git a/app/models/namespace/traversal_hierarchy.rb b/app/models/namespace/traversal_hierarchy.rb index 93193cc2022..d2de85b5dd4 100644 --- a/app/models/namespace/traversal_hierarchy.rb +++ b/app/models/namespace/traversal_hierarchy.rb @@ -40,12 +40,7 @@ class Namespace SQL Namespace.transaction do - if Feature.enabled?(:for_no_key_update_lock, default_enabled: :yaml) - @root.lock!("FOR NO KEY UPDATE") - else - @root.lock! - end - + @root.lock!("FOR NO KEY UPDATE") Namespace.connection.exec_query(sql) end rescue ActiveRecord::Deadlocked diff --git a/app/models/namespaces/traversal/linear.rb b/app/models/namespaces/traversal/linear.rb index 99a5b8cb063..1963745cf4d 100644 --- a/app/models/namespaces/traversal/linear.rb +++ b/app/models/namespaces/traversal/linear.rb @@ -44,22 +44,15 @@ module Namespaces included do before_update :lock_both_roots, if: -> { sync_traversal_ids? && parent_id_changed? } after_update :sync_traversal_ids, if: -> { sync_traversal_ids? && saved_change_to_parent_id? } - # sync traversal_ids on namespace create, which can happen quite early within a transaction, thus keeping the lock on root namespace record - # for a relatively long time, e.g. creating the project namespace when a project is being created. - after_create :sync_traversal_ids, if: -> { sync_traversal_ids? && !sync_traversal_ids_before_commit? } # This uses rails internal before_commit API to sync traversal_ids on namespace create, right before transaction is committed. # This helps reduce the time during which the root namespace record is locked to ensure updated traversal_ids are valid - before_commit :sync_traversal_ids, on: [:create], if: -> { sync_traversal_ids? && sync_traversal_ids_before_commit? } + before_commit :sync_traversal_ids, on: [:create], if: -> { sync_traversal_ids? } end def sync_traversal_ids? Feature.enabled?(:sync_traversal_ids, root_ancestor, default_enabled: :yaml) end - def sync_traversal_ids_before_commit? - Feature.enabled?(:sync_traversal_ids_before_commit, root_ancestor, default_enabled: :yaml) - end - def use_traversal_ids? return false unless Feature.enabled?(:use_traversal_ids, default_enabled: :yaml) diff --git a/app/views/admin/application_settings/_eks.html.haml b/app/views/admin/application_settings/_eks.html.haml index c83e28d7f0b..d9c0a01beb0 100644 --- a/app/views/admin/application_settings/_eks.html.haml +++ b/app/views/admin/application_settings/_eks.html.haml @@ -22,15 +22,15 @@ = f.label :eks_account_id, _('Account ID'), class: 'label-bold' = f.text_field :eks_account_id, class: 'form-control gl-form-input' .form-group - = f.label :eks_access_key_id, _('Access key ID'), class: 'label-bold' + = f.label :eks_access_key_id, _('AWS access key ID (Optional)'), class: 'label-bold' = f.text_field :eks_access_key_id, class: 'form-control gl-form-input' .form-text.text-muted - = _('AWS Access Key. Only required if not using role instance credentials') + = _('Only required if not using role instance credentials.') .form-group - = f.label :eks_secret_access_key, _('Secret access key'), class: 'label-bold' + = f.label :eks_secret_access_key, _('AWS secret access key (Optional)'), class: 'label-bold' = f.password_field :eks_secret_access_key, autocomplete: 'off', class: 'form-control gl-form-input' .form-text.text-muted - = _('AWS Secret Access Key. Only required if not using role instance credentials') + = _('Only required if not using role instance credentials.') = f.submit _('Save changes'), class: "gl-button btn btn-confirm" diff --git a/app/views/admin/application_settings/_sourcegraph.html.haml b/app/views/admin/application_settings/_sourcegraph.html.haml index b92cf7b156a..65b2a95bcc1 100644 --- a/app/views/admin/application_settings/_sourcegraph.html.haml +++ b/app/views/admin/application_settings/_sourcegraph.html.haml @@ -12,7 +12,7 @@ - link_end = "#{sprite_icon('external-link', size: 12, css_class: 'ml-1 vertical-align-center')}</a>".html_safe = s_('SourcegraphAdmin|Enable code intelligence powered by %{link_start}Sourcegraph%{link_end} on your GitLab instance\'s code views and merge requests.').html_safe % { link_start: link_start, link_end: link_end } %span - = link_to s_('SourcegraphAdmin|More information'), help_page_path('integration/sourcegraph.md'), target: '_blank', rel: 'noopener noreferrer' + = link_to s_('SourcegraphAdmin|Learn more.'), help_page_path('integration/sourcegraph.md'), target: '_blank', rel: 'noopener noreferrer' .settings-content @@ -29,10 +29,10 @@ = f.check_box :sourcegraph_public_only, class: 'form-check-input' = f.label :sourcegraph_public_only, s_('SourcegraphAdmin|Block on private and internal projects'), class: 'form-check-label' .form-text.text-muted - = s_('SourcegraphAdmin|If checked, only public projects will have code intelligence and communicate with Sourcegraph.') + = s_('SourcegraphAdmin|Only public projects have code intelligence enabled and communicate with Sourcegraph.') .form-group = f.label :sourcegraph_url, s_('SourcegraphAdmin|Sourcegraph URL'), class: 'label-bold' - = f.text_field :sourcegraph_url, class: 'form-control gl-form-input', placeholder: s_('SourcegraphAdmin|e.g. https://sourcegraph.example.com') + = f.text_field :sourcegraph_url, class: 'form-control gl-form-input', placeholder: s_('SourcegraphAdmin|https://sourcegraph.example.com') .form-text.text-muted = s_('SourcegraphAdmin|Configure the URL to a Sourcegraph instance which can read your GitLab projects.') = f.submit s_('SourcegraphAdmin|Save changes'), class: 'gl-button btn btn-confirm' diff --git a/app/views/groups/group_members/index.html.haml b/app/views/groups/group_members/index.html.haml index d1f56a50907..dce1754a66d 100644 --- a/app/views/groups/group_members/index.html.haml +++ b/app/views/groups/group_members/index.html.haml @@ -15,7 +15,7 @@ .gl-w-half.gl-xs-w-full .gl-display-flex.gl-flex-wrap.gl-justify-content-end.gl-mb-3 .js-invite-group-trigger{ data: { classes: 'gl-mt-3 gl-sm-w-auto gl-w-full', display_text: _('Invite a group') } } - .js-invite-members-trigger{ data: { variant: 'success', + .js-invite-members-trigger{ data: { variant: 'confirm', classes: 'gl-mt-3 gl-sm-w-auto gl-w-full gl-sm-ml-3', trigger_source: 'group-members-page', display_text: _('Invite members') } } diff --git a/app/views/projects/project_members/index.html.haml b/app/views/projects/project_members/index.html.haml index 220e44679cd..ef83c512e8d 100644 --- a/app/views/projects/project_members/index.html.haml +++ b/app/views/projects/project_members/index.html.haml @@ -23,7 +23,7 @@ .js-invite-group-trigger{ data: { classes: 'gl-mt-3 gl-sm-w-auto gl-w-full gl-sm-ml-3', display_text: _('Invite a group') } } = render 'projects/invite_groups_modal', project: @project - if can_admin_project_member?(@project) - .js-invite-members-trigger{ data: { variant: 'success', + .js-invite-members-trigger{ data: { variant: 'confirm', classes: 'gl-mt-3 gl-sm-w-auto gl-w-full gl-sm-ml-3', trigger_source: 'project-members-page', display_text: _('Invite members') } } diff --git a/app/views/shared/deploy_tokens/_table.html.haml b/app/views/shared/deploy_tokens/_table.html.haml index db9c646b694..a7bf3bfb81e 100644 --- a/app/views/shared/deploy_tokens/_table.html.haml +++ b/app/views/shared/deploy_tokens/_table.html.haml @@ -25,7 +25,7 @@ %span.token-never-expires-label= _('Never') %td= token.scopes.present? ? token.scopes.join(', ') : _('no scopes selected') %td - .js-deploy-token-revoke-button{ data: { button_class: 'float-right', token: token.to_json, revoke_path: revoke_deploy_token_path(group_or_project, token) } } + .js-deploy-token-revoke-button{ data: deploy_token_revoke_button_data(token: token, group_or_project: group_or_project) } - else .settings-message.text-center diff --git a/config/feature_flags/development/cluster_vulnerabilities.yml b/config/feature_flags/development/cluster_vulnerabilities.yml deleted file mode 100644 index 9d7f3228d66..00000000000 --- a/config/feature_flags/development/cluster_vulnerabilities.yml +++ /dev/null @@ -1,8 +0,0 @@ ---- -name: cluster_vulnerabilities -introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/73321 -rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/343917 -milestone: '14.5' -type: development -group: group::container security -default_enabled: true diff --git a/config/feature_flags/development/for_no_key_update_lock.yml b/config/feature_flags/development/for_no_key_update_lock.yml deleted file mode 100644 index a4ff426e876..00000000000 --- a/config/feature_flags/development/for_no_key_update_lock.yml +++ /dev/null @@ -1,8 +0,0 @@ ---- -name: for_no_key_update_lock -introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/81239 -rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/353619 -milestone: '14.9' -type: development -group: group::workspaces -default_enabled: false diff --git a/config/feature_flags/development/sync_traversal_ids_before_commit.yml b/config/feature_flags/development/sync_traversal_ids_before_commit.yml deleted file mode 100644 index f8f1e854fa5..00000000000 --- a/config/feature_flags/development/sync_traversal_ids_before_commit.yml +++ /dev/null @@ -1,8 +0,0 @@ ---- -name: sync_traversal_ids_before_commit -introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/79964 -rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/352499 -group: group::workspace -type: development -default_enabled: false -milestone: '14.8' diff --git a/doc/development/experiment_guide/gitlab_experiment.md b/doc/development/experiment_guide/gitlab_experiment.md index 5359a6f21a3..78e1f84d701 100644 --- a/doc/development/experiment_guide/gitlab_experiment.md +++ b/doc/development/experiment_guide/gitlab_experiment.md @@ -317,7 +317,7 @@ of tracking an event in Ruby would be: experiment(:pill_color, actor: current_user).track(:clicked) ``` -When you run an experiment with any of the examples so far, an `:assigned` event +When you run an experiment with any of the examples so far, an `:assignment` event is tracked automatically by default. All events that are tracked from an experiment have a special [experiment context](https://gitlab.com/gitlab-org/iglu/-/blob/master/public/schemas/com.gitlab/gitlab_experiment/jsonschema/1-0-3) diff --git a/doc/integration/jira/configure.md b/doc/integration/jira/configure.md index 979d8a52fb8..2033ddbad6f 100644 --- a/doc/integration/jira/configure.md +++ b/doc/integration/jira/configure.md @@ -50,7 +50,7 @@ To configure your project: If you enable Jira issues with this setting, all users with access to this GitLab project can view all issues from the specified Jira project. -1. To enable [issue creation for vulnerabilities](../../user/application_security/vulnerabilities/index.md#create-a-jira-issue-for-a-vulnerability), select **Enable Jira issues creation from vulnerabilities**. +1. To enable [issue creation for vulnerabilities](../../user/application_security/vulnerabilities/index.md#create-a-jira-issue-for-a-vulnerability), select **Enable Jira issue creation from vulnerabilities**. 1. Select the **Jira issue type**. If the dropdown is empty, select refresh (**{retry}**) and try again. 1. To verify the Jira connection is working, select **Test settings**. 1. Select **Save changes**. diff --git a/doc/user/application_security/vulnerabilities/index.md b/doc/user/application_security/vulnerabilities/index.md index 7b39002bac3..db8b3fe8126 100644 --- a/doc/user/application_security/vulnerabilities/index.md +++ b/doc/user/application_security/vulnerabilities/index.md @@ -80,7 +80,7 @@ The issue is then opened so you can take further action. Prerequisites: - [Enable Jira integration](../../../integration/jira/index.md). - The **Enable Jira issues creation from vulnerabilities** option must be selected as part of the configuration. + The **Enable Jira issue creation from vulnerabilities** option must be selected as part of the configuration. - Each user must have a personal Jira user account with permission to create issues in the target project. To create a Jira issue for a vulnerability: diff --git a/doc/user/clusters/agent/vulnerabilities.md b/doc/user/clusters/agent/vulnerabilities.md index ef3fd800220..41a80f43467 100644 --- a/doc/user/clusters/agent/vulnerabilities.md +++ b/doc/user/clusters/agent/vulnerabilities.md @@ -6,7 +6,7 @@ info: To determine the technical writer assigned to the Stage/Group associated w # Container vulnerability scanning **(ULTIMATE)** -> [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/6346) in GitLab 14.8 [with a flag](../../../administration/feature_flags.md) named `cluster_vulnerabilities`. Enabled by default. +> [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/6346) in GitLab 14.8. To view cluster vulnerabilities, you can view the [vulnerability report](../../application_security/vulnerabilities/index.md). You can also configure your agent so the vulnerabilities are displayed with other agent information in GitLab. diff --git a/doc/user/markdown.md b/doc/user/markdown.md index a16f8fd39b1..fa739843e3b 100644 --- a/doc/user/markdown.md +++ b/doc/user/markdown.md @@ -67,15 +67,15 @@ Features not found in standard Markdown: Features [extended from standard Markdown](#features-extended-from-standard-markdown): -| Standard Markdown | Extended Markdown in GitLab | -| ------------------------------------- | ------------------------- | -| [blockquotes](#blockquotes) | [multi-line blockquotes](#multiline-blockquote) | -| [code blocks](#code-spans-and-blocks) | [colored code and syntax highlighting](#colored-code-and-syntax-highlighting) | -| [emphasis](#emphasis) | [multiple underscores in words](#multiple-underscores-in-words-and-mid-word-emphasis) -| [headers](#headers) | [linkable Header IDs](#header-ids-and-links) | -| [images](#images) | [embedded videos](#videos) and [audio](#audio) | -| [line breaks](#line-breaks) | [more line break control](#newlines) | -| [links](#links) | [automatically linking URLs](#url-auto-linking) | +| Standard Markdown | Extended Markdown in GitLab | +|---------------------------------------|---------------------------------------------------------------------------------------| +| [blockquotes](#blockquotes) | [multi-line blockquotes](#multiline-blockquote) | +| [code blocks](#code-spans-and-blocks) | [colored code and syntax highlighting](#colored-code-and-syntax-highlighting) | +| [emphasis](#emphasis) | [multiple underscores in words](#multiple-underscores-in-words-and-mid-word-emphasis) | +| [headers](#headers) | [linkable Header IDs](#header-ids-and-links) | +| [images](#images) | [embedded videos](#videos) and [audio](#audio) | +| [line breaks](#line-breaks) | [more line break control](#newlines) | +| [links](#links) | [automatically linking URLs](#url-auto-linking) | ## Features not found in standard Markdown @@ -262,7 +262,7 @@ The following delimiters are supported: --- title: About Front Matter example: - language: yaml + language: yaml --- ``` @@ -515,31 +515,31 @@ version to reference other projects from the same namespace. GitLab Flavored Markdown recognizes the following: -| references | input | cross-project reference | shortcut inside same namespace | -| :--------------------------------------------------- | :---------------------------- | :----------------------------------------- | :------------------------------- | -| specific user | `@user_name` | | | -| specific group | `@group_name` | | | -| entire team | `@all` | | | -| project | `namespace/project>` | | | -| issue | ``#123`` | `namespace/project#123` | `project#123` | -| merge request | `!123` | `namespace/project!123` | `project!123` | -| snippet | `$123` | `namespace/project$123` | `project$123` | -| [epic](group/epics/index.md) | `&123` | `group1/subgroup&123` | | -| vulnerability **(ULTIMATE)** <sup>1</sup> | `[vulnerability:123]` | `[vulnerability:namespace/project/123]` | `[vulnerability:project/123]` | -| feature flag | `[feature_flag:123]` | `[feature_flag:namespace/project/123]` | `[feature_flag:project/123]` | -| label by ID | `~123` | `namespace/project~123` | `project~123` | -| one-word label by name | `~bug` | `namespace/project~bug` | `project~bug` | -| multi-word label by name | `~"feature request"` | `namespace/project~"feature request"` | `project~"feature request"` | -| scoped label by name | `~"priority::high"` | `namespace/project~"priority::high"` | `project~"priority::high"` | -| project milestone by ID | `%123` | `namespace/project%123` | `project%123` | -| one-word milestone by name | `%v1.23` | `namespace/project%v1.23` | `project%v1.23` | -| multi-word milestone by name | `%"release candidate"` | `namespace/project%"release candidate"` | `project%"release candidate"` | -| specific commit | `9ba12248` | `namespace/project@9ba12248` | `project@9ba12248` | -| commit range comparison | `9ba12248...b19a04f5` | `namespace/project@9ba12248...b19a04f5` | `project@9ba12248...b19a04f5` | -| repository file references | `[README](doc/README.md)` | | | -| repository file line references | `[README](doc/README.md#L13)` | | | -| [alert](../operations/incident_management/alerts.md) | `^alert#123` | `namespace/project^alert#123` | `project^alert#123` | -| contact | `[contact:test@example.com]` | | | +| references | input | cross-project reference | shortcut inside same namespace | +|:----------------------------------------------------------------------------|:------------------------------|:----------------------------------------|:-------------------------------| +| specific user | `@user_name` | | | +| specific group | `@group_name` | | | +| entire team | `@all` | | | +| project | `namespace/project>` | | | +| issue | ``#123`` | `namespace/project#123` | `project#123` | +| merge request | `!123` | `namespace/project!123` | `project!123` | +| snippet | `$123` | `namespace/project$123` | `project$123` | +| [epic](group/epics/index.md) | `&123` | `group1/subgroup&123` | | +| [vulnerability](application_security/vulnerabilities/index.md) <sup>1</sup> | `[vulnerability:123]` | `[vulnerability:namespace/project/123]` | `[vulnerability:project/123]` | +| feature flag | `[feature_flag:123]` | `[feature_flag:namespace/project/123]` | `[feature_flag:project/123]` | +| label by ID | `~123` | `namespace/project~123` | `project~123` | +| one-word label by name | `~bug` | `namespace/project~bug` | `project~bug` | +| multi-word label by name | `~"feature request"` | `namespace/project~"feature request"` | `project~"feature request"` | +| scoped label by name | `~"priority::high"` | `namespace/project~"priority::high"` | `project~"priority::high"` | +| project milestone by ID | `%123` | `namespace/project%123` | `project%123` | +| one-word milestone by name | `%v1.23` | `namespace/project%v1.23` | `project%v1.23` | +| multi-word milestone by name | `%"release candidate"` | `namespace/project%"release candidate"` | `project%"release candidate"` | +| specific commit | `9ba12248` | `namespace/project@9ba12248` | `project@9ba12248` | +| commit range comparison | `9ba12248...b19a04f5` | `namespace/project@9ba12248...b19a04f5` | `project@9ba12248...b19a04f5` | +| repository file references | `[README](doc/README.md)` | | | +| repository file line references | `[README](doc/README.md#L13)` | | | +| [alert](../operations/incident_management/alerts.md) | `^alert#123` | `namespace/project^alert#123` | `project^alert#123` | +| contact | `[contact:test@example.com]` | | | 1. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/222483) in GitLab 13.7. @@ -1486,7 +1486,7 @@ but they do not render properly on `docs.gitlab.com`: #### Copy from spreadsheet and paste in Markdown -[Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/27205) in GitLab 12.7. +> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/27205) in GitLab 12.7. If you're working in spreadsheet software (for example, Microsoft Excel, Google Sheets, or Apple Numbers), GitLab creates a Markdown table when you copy-and-paste diff --git a/doc/user/search/index.md b/doc/user/search/index.md index f96ca746474..e57f9260f2c 100644 --- a/doc/user/search/index.md +++ b/doc/user/search/index.md @@ -34,7 +34,8 @@ in the search field in the upper right corner: > - Filtering by epics was [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/195704) in GitLab 12.9. > - Filtering by child epics was [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/9029) in GitLab 13.0. -> - Filtering by iterations was [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/118742) in GitLab 13.6. Moved from GitLab Ultimate to Premium in 13.9. +> - Filtering by iterations was [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/118742) in GitLab 13.6. +> - Filtering by iterations was moved from GitLab Ultimate to GitLab Premium in 13.9. > - Filtering by type was [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/322755) in GitLab 13.10 [with a flag](../../administration/feature_flags.md) named `vue_issues_list`. Disabled by default. Follow these steps to filter the **Issues** and **Merge requests** list pages in projects and @@ -127,14 +128,14 @@ the dropdown list) **Approved-By** and select the user. ![Filter MRs by approved by](img/filter_approved_by_merge_requests_v14_6.png) -### Filtering merge requests by reviewer **(FREE)** +### Filtering merge requests by reviewer > [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/47605) in GitLab 13.7. To filter review requested merge requests for a specific individual, you can type (or select from the dropdown list) **Reviewer** and select the user. -### Filtering merge requests by environment or deployment date **(FREE)** +### Filtering merge requests by environment or deployment date > [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/44041) in GitLab 13.6. @@ -307,7 +308,7 @@ GitLab instance. ## Search settings > - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/292941) in GitLab 13.8 [with a flag](../../administration/feature_flags.md) named `search_settings_in_page`. Disabled by default. -> - [Added to Group, Administrator, and User settings](https://gitlab.com/groups/gitlab-org/-/epics/4842) in GitLab 13.9. +> - [Added](https://gitlab.com/groups/gitlab-org/-/epics/4842) to Group, Administrator, and User settings in GitLab 13.9. > - [Feature flag `search_settings_in_page` removed](https://gitlab.com/gitlab-org/gitlab/-/issues/294025) in GitLab 13.11. > - [Generally available](https://gitlab.com/gitlab-org/gitlab/-/issues/294025) in GitLab 13.11. diff --git a/doc/user/snippets.md b/doc/user/snippets.md index fc6ae11c724..1750e2c8d10 100644 --- a/doc/user/snippets.md +++ b/doc/user/snippets.md @@ -37,8 +37,7 @@ You can create snippets in multiple ways, depending on whether you want to creat **New snippet**, or: - *If you're on a project's page,* select the plus icon (**{plus-square-o}**) in the top navigation bar, and then select **New snippet** from the - **GitLab** (GitLab SaaS) or **Your Instance** (self-managed) section - of the same dropdown list. + **GitLab** section of the same dropdown list. - *For all other pages,* select the plus icon (**{plus-square-o}**) in the top navigation bar, then select **New snippet** from the dropdown list. - If you installed the [GitLab Workflow VS Code extension](project/repository/vscode.md), @@ -153,8 +152,7 @@ To delete a file from your snippet through the GitLab UI: 1. Go to your snippet in the GitLab UI. 1. Select **Edit** in the top right corner. -1. Select **Delete file** alongside the filename of each file -you wish to delete. +1. Select **Delete file** alongside the filename of each file you wish to delete. 1. Select **Save changes**. ## Clone snippets diff --git a/locale/gitlab.pot b/locale/gitlab.pot index a2c12665fb0..7c7e919fa88 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -1773,13 +1773,13 @@ msgstr "" msgid "AWS Access Key" msgstr "" -msgid "AWS Access Key. Only required if not using role instance credentials" +msgid "AWS Secret Access Key" msgstr "" -msgid "AWS Secret Access Key" +msgid "AWS access key ID (Optional)" msgstr "" -msgid "AWS Secret Access Key. Only required if not using role instance credentials" +msgid "AWS secret access key (Optional)" msgstr "" msgid "AWS service error: %{error}" @@ -1839,9 +1839,6 @@ msgstr "" msgid "Access granted" msgstr "" -msgid "Access key ID" -msgstr "" - msgid "Access requests" msgstr "" @@ -19864,9 +19861,6 @@ msgstr "" msgid "Integrations|Use default settings" msgstr "" -msgid "Integrations|When you mention a Jira issue in a commit or merge request, GitLab creates a remote link and comment (if enabled)." -msgstr "" - msgid "Integrations|You can now close this window and return to the GitLab for Jira application." msgstr "" @@ -20830,13 +20824,13 @@ msgstr "" msgid "JiraService|Define the type of Jira issue to create from a vulnerability." msgstr "" -msgid "JiraService|Displaying Jira issues while leaving the GitLab issue functionality enabled might be confusing. Consider %{linkStart}disabling GitLab issues%{linkEnd} if they won’t otherwise be used." +msgid "JiraService|Displaying Jira issues while leaving GitLab issues also enabled might be confusing. Consider %{linkStart}disabling GitLab issues%{linkEnd} if they won’t otherwise be used." msgstr "" -msgid "JiraService|Enable Jira issues" +msgid "JiraService|Enable Jira issue creation from vulnerabilities" msgstr "" -msgid "JiraService|Enable Jira issues creation from vulnerabilities" +msgid "JiraService|Enable Jira issues" msgstr "" msgid "JiraService|Enable Jira transitions" @@ -20953,16 +20947,19 @@ msgstr "" msgid "JiraService|View Jira issues in GitLab" msgstr "" -msgid "JiraService|Warning: All GitLab users that have access to this GitLab project are able to view all issues from the Jira project specified below." +msgid "JiraService|Warning: All GitLab users with access to this GitLab project can view all issues from the Jira project you select." msgstr "" msgid "JiraService|Web URL" msgstr "" -msgid "JiraService|Work on Jira issues without leaving GitLab. Adds a Jira menu to access your list of Jira issues and view any issue as read-only." +msgid "JiraService|When a Jira issue is mentioned in a commit or merge request, a remote link and comment (if enabled) will be created." +msgstr "" + +msgid "JiraService|Work on Jira issues without leaving GitLab. Add a Jira menu to access a read-only list of your Jira issues." msgstr "" -msgid "JiraService|You must configure Jira before enabling this integration. For more details, read the %{jira_doc_link_start}Jira integration documentation%{link_end}." +msgid "JiraService|You must configure Jira before enabling this integration. %{jira_doc_link_start}Learn more.%{link_end}" msgstr "" msgid "Job" @@ -25611,6 +25608,9 @@ msgstr "" msgid "Only reCAPTCHA v2 is supported:" msgstr "" +msgid "Only required if not using role instance credentials." +msgstr "" + msgid "Only use lowercase letters, numbers, and underscores." msgstr "" @@ -32290,9 +32290,6 @@ msgstr "" msgid "Secret Detection" msgstr "" -msgid "Secret access key" -msgstr "" - msgid "Secret token" msgstr "" @@ -32491,9 +32488,6 @@ msgstr "" msgid "SecurityOrchestration|.yaml preview" msgstr "" -msgid "SecurityOrchestration|Action" -msgstr "" - msgid "SecurityOrchestration|Actions" msgstr "" @@ -32536,9 +32530,6 @@ msgstr "" msgid "SecurityOrchestration|Enforce security for this project. %{linkStart}More information.%{linkEnd}" msgstr "" -msgid "SecurityOrchestration|Executes a %{scanType} scan" -msgstr "" - msgid "SecurityOrchestration|If you are using Auto DevOps, your %{monospacedStart}auto-deploy-values.yaml%{monospacedEnd} file will not be updated if you change a policy in this section. Auto DevOps users should make changes by following the %{linkStart}Container Network Policy documentation%{linkEnd}." msgstr "" @@ -32554,6 +32545,9 @@ msgstr "" msgid "SecurityOrchestration|New policy" msgstr "" +msgid "SecurityOrchestration|No actions defined - policy will not run." +msgstr "" + msgid "SecurityOrchestration|No description" msgstr "" @@ -32596,10 +32590,13 @@ msgstr "" msgid "SecurityOrchestration|Require %{approvals} %{plural} from %{approvers} if any of the following occur:" msgstr "" -msgid "SecurityOrchestration|Rule" +msgid "SecurityOrchestration|Rules" +msgstr "" + +msgid "SecurityOrchestration|Runs %{actions} and %{lastAction} scans" msgstr "" -msgid "SecurityOrchestration|Rules" +msgid "SecurityOrchestration|Runs a %{action} scan" msgstr "" msgid "SecurityOrchestration|Scan Execution" @@ -34688,10 +34685,10 @@ msgstr "" msgid "SourcegraphAdmin|Enable code intelligence powered by %{link_start}Sourcegraph%{link_end} on your GitLab instance's code views and merge requests." msgstr "" -msgid "SourcegraphAdmin|If checked, only public projects will have code intelligence and communicate with Sourcegraph." +msgid "SourcegraphAdmin|Learn more." msgstr "" -msgid "SourcegraphAdmin|More information" +msgid "SourcegraphAdmin|Only public projects have code intelligence enabled and communicate with Sourcegraph." msgstr "" msgid "SourcegraphAdmin|Save changes" @@ -34700,7 +34697,7 @@ msgstr "" msgid "SourcegraphAdmin|Sourcegraph URL" msgstr "" -msgid "SourcegraphAdmin|e.g. https://sourcegraph.example.com" +msgid "SourcegraphAdmin|https://sourcegraph.example.com" msgstr "" msgid "SourcegraphPreferences|This feature is experimental and currently limited to certain projects." diff --git a/scripts/ingest-reports-to-siem b/scripts/ingest-reports-to-siem new file mode 100755 index 00000000000..5f5397fcd72 --- /dev/null +++ b/scripts/ingest-reports-to-siem @@ -0,0 +1,38 @@ +#!/usr/bin/env node + +const { S3Client, PutObjectCommand } = require('@aws-sdk/client-s3') +const { fromIni } = require('@aws-sdk/credential-provider-ini') +const path = require('path') +const fs = require('fs') +const crypto = require('crypto') + +function getMD5HashFromFile(data) { + const hash = crypto.createHash('md5').update(data).digest('base64') + return hash +} + +(async function () { + const s3Client = new S3Client({ + region: 'us-east-2', + credentials: fromIni({ profile: 'gl-logs-for-panther' }), + }) + try { + const file = 'gl-dependency-scanning-report.json' + const data = fs.readFileSync(file) + const responseData = await s3Client.send( + new PutObjectCommand({ + Bucket: 'gl-logs-for-panther-test', + Key: path.join('package_hunter_test', path.basename(file)), + Body: data, + ContentMD5: getMD5HashFromFile(data), + }), + ) + console.log('Successfully uploaded %s', file) + } catch (err) { + if (err.name === 'CredentialsProviderError' || err.name === 'AuthorizationHeaderMalformed') + console.log('Could not upload the report. Are AWS credentials configured in ~/.aws/credentials?') + else + console.log('Unexpected error during upload.') + process.exit(1) + } +})() diff --git a/spec/experiments/application_experiment_spec.rb b/spec/experiments/application_experiment_spec.rb index f3bd9eafbc3..f0a993bd03b 100644 --- a/spec/experiments/application_experiment_spec.rb +++ b/spec/experiments/application_experiment_spec.rb @@ -28,61 +28,23 @@ RSpec.describe ApplicationExperiment, :experiment do end describe "#publish" do - let(:should_track) { true } - - before do - allow(application_experiment).to receive(:should_track?).and_return(should_track) - end - it "tracks the assignment", :snowplow do - application_experiment.publish - - expect_snowplow_event( - category: 'namespaced/stub', - action: 'assignment', - context: [{ schema: anything, data: anything }] - ) - end - - it "publishes to the client" do - expect(application_experiment).to receive(:publish_to_client) + expect(application_experiment).to receive(:track).with(:assignment) application_experiment.publish end - context 'when we should not track' do - let(:should_track) { false } - - it 'does not track an event to Snowplow', :snowplow do - application_experiment.publish - - expect_no_snowplow_event - end - end - - describe "#publish_to_client" do - it "adds the data into Gon" do - signature = { key: '86208ac54ca798e11f127e8b23ec396a', variant: 'control' } - expect(Gon).to receive(:push).with({ experiment: { 'namespaced/stub' => hash_including(signature) } }, true) - - application_experiment.publish_to_client - end - - it "handles when Gon raises exceptions (like when it can't be pushed into)" do - expect(Gon).to receive(:push).and_raise(NoMethodError) - - expect { application_experiment.publish_to_client }.not_to raise_error - end - - context 'when we should not track' do - let(:should_track) { false } - - it 'returns early' do - expect(Gon).not_to receive(:push) + it "adds to the published experiments" do + # These are surfaced in the client layer by rendering them in the + # _published_experiments.html.haml partial. + application_experiment.publish - application_experiment.publish_to_client - end - end + expect(ApplicationExperiment.published_experiments['namespaced/stub']).to include( + experiment: 'namespaced/stub', + excluded: false, + key: anything, + variant: 'control' + ) end describe '#publish_to_database' do diff --git a/spec/features/incidents/user_views_incident_spec.rb b/spec/features/incidents/user_views_incident_spec.rb index fe54f7708c9..e0261ad4d0b 100644 --- a/spec/features/incidents/user_views_incident_spec.rb +++ b/spec/features/incidents/user_views_incident_spec.rb @@ -40,10 +40,8 @@ RSpec.describe "User views incident" do visit(project_issues_incident_path(project, incident)) end - it 'does not show the incident action', :js, :aggregate_failures do - click_button 'Incident actions' - - expect(page).not_to have_link('New incident') + it 'does not show the incident actions', :js, :aggregate_failures do + expect(page).not_to have_button('Incident actions') end end end diff --git a/spec/frontend/deploy_tokens/components/revoke_button_spec.js b/spec/frontend/deploy_tokens/components/revoke_button_spec.js index e70dfe4d2e6..fa2a7d9b155 100644 --- a/spec/frontend/deploy_tokens/components/revoke_button_spec.js +++ b/spec/frontend/deploy_tokens/components/revoke_button_spec.js @@ -70,11 +70,6 @@ describe('RevokeButton', () => { expect(findRevokeButton().exists()).toBe(true); }); - it('passes the buttonClass to the button', () => { - wrapper = createComponent({ buttonClass: 'my-revoke-button' }); - expect(findRevokeButton().classes()).toContain('my-revoke-button'); - }); - it('opens the modal', () => { findRevokeButton().trigger('click'); expect(glModalDirective).toHaveBeenCalledWith(wrapper.vm.modalId); diff --git a/spec/frontend/fixtures/runner.rb b/spec/frontend/fixtures/runner.rb index cdb4c3fd8ba..25049ee4722 100644 --- a/spec/frontend/fixtures/runner.rb +++ b/spec/frontend/fixtures/runner.rb @@ -33,19 +33,19 @@ RSpec.describe 'Runner (JavaScript fixtures)' do end describe GraphQL::Query, type: :request do - get_runners_query_name = 'get_runners.query.graphql' + admin_runners_query = 'list/admin_runners.query.graphql' let_it_be(:query) do - get_graphql_query_as_string("#{query_path}#{get_runners_query_name}") + get_graphql_query_as_string("#{query_path}#{admin_runners_query}") end - it "#{fixtures_path}#{get_runners_query_name}.json" do + it "#{fixtures_path}#{admin_runners_query}.json" do post_graphql(query, current_user: admin, variables: {}) expect_graphql_errors_to_be_empty end - it "#{fixtures_path}#{get_runners_query_name}.paginated.json" do + it "#{fixtures_path}#{admin_runners_query}.paginated.json" do post_graphql(query, current_user: admin, variables: { first: 2 }) expect_graphql_errors_to_be_empty @@ -53,13 +53,13 @@ RSpec.describe 'Runner (JavaScript fixtures)' do end describe GraphQL::Query, type: :request do - get_runners_count_query_name = 'get_runners_count.query.graphql' + admin_runners_count_query = 'list/admin_runners_count.query.graphql' let_it_be(:query) do - get_graphql_query_as_string("#{query_path}#{get_runners_count_query_name}") + get_graphql_query_as_string("#{query_path}#{admin_runners_count_query}") end - it "#{fixtures_path}#{get_runners_count_query_name}.json" do + it "#{fixtures_path}#{admin_runners_count_query}.json" do post_graphql(query, current_user: admin, variables: {}) expect_graphql_errors_to_be_empty @@ -67,13 +67,13 @@ RSpec.describe 'Runner (JavaScript fixtures)' do end describe GraphQL::Query, type: :request do - get_runner_query_name = 'get_runner.query.graphql' + runner_query = 'details/runner.query.graphql' let_it_be(:query) do - get_graphql_query_as_string("#{query_path}#{get_runner_query_name}") + get_graphql_query_as_string("#{query_path}#{runner_query}") end - it "#{fixtures_path}#{get_runner_query_name}.json" do + it "#{fixtures_path}#{runner_query}.json" do post_graphql(query, current_user: admin, variables: { id: instance_runner.to_global_id.to_s }) @@ -81,7 +81,7 @@ RSpec.describe 'Runner (JavaScript fixtures)' do expect_graphql_errors_to_be_empty end - it "#{fixtures_path}#{get_runner_query_name}.with_group.json" do + it "#{fixtures_path}#{runner_query}.with_group.json" do post_graphql(query, current_user: admin, variables: { id: group_runner.to_global_id.to_s }) @@ -91,13 +91,13 @@ RSpec.describe 'Runner (JavaScript fixtures)' do end describe GraphQL::Query, type: :request do - get_runner_projects_query_name = 'get_runner_projects.query.graphql' + runner_projects_query = 'details/runner_projects.query.graphql' let_it_be(:query) do - get_graphql_query_as_string("#{query_path}#{get_runner_projects_query_name}") + get_graphql_query_as_string("#{query_path}#{runner_projects_query}") end - it "#{fixtures_path}#{get_runner_projects_query_name}.json" do + it "#{fixtures_path}#{runner_projects_query}.json" do post_graphql(query, current_user: admin, variables: { id: project_runner.to_global_id.to_s }) @@ -107,13 +107,13 @@ RSpec.describe 'Runner (JavaScript fixtures)' do end describe GraphQL::Query, type: :request do - get_runner_jobs_query_name = 'get_runner_jobs.query.graphql' + runner_jobs_query = 'details/runner_jobs.query.graphql' let_it_be(:query) do - get_graphql_query_as_string("#{query_path}#{get_runner_jobs_query_name}") + get_graphql_query_as_string("#{query_path}#{runner_jobs_query}") end - it "#{fixtures_path}#{get_runner_jobs_query_name}.json" do + it "#{fixtures_path}#{runner_jobs_query}.json" do post_graphql(query, current_user: admin, variables: { id: instance_runner.to_global_id.to_s }) @@ -131,13 +131,13 @@ RSpec.describe 'Runner (JavaScript fixtures)' do end describe GraphQL::Query, type: :request do - get_group_runners_query_name = 'get_group_runners.query.graphql' + group_runners_query = 'list/group_runners.query.graphql' let_it_be(:query) do - get_graphql_query_as_string("#{query_path}#{get_group_runners_query_name}") + get_graphql_query_as_string("#{query_path}#{group_runners_query}") end - it "#{fixtures_path}#{get_group_runners_query_name}.json" do + it "#{fixtures_path}#{group_runners_query}.json" do post_graphql(query, current_user: group_owner, variables: { groupFullPath: group.full_path }) @@ -145,7 +145,7 @@ RSpec.describe 'Runner (JavaScript fixtures)' do expect_graphql_errors_to_be_empty end - it "#{fixtures_path}#{get_group_runners_query_name}.paginated.json" do + it "#{fixtures_path}#{group_runners_query}.paginated.json" do post_graphql(query, current_user: group_owner, variables: { groupFullPath: group.full_path, first: 1 @@ -156,13 +156,13 @@ RSpec.describe 'Runner (JavaScript fixtures)' do end describe GraphQL::Query, type: :request do - get_group_runners_count_query_name = 'get_group_runners_count.query.graphql' + group_runners_count_query = 'list/group_runners_count.query.graphql' let_it_be(:query) do - get_graphql_query_as_string("#{query_path}#{get_group_runners_count_query_name}") + get_graphql_query_as_string("#{query_path}#{group_runners_count_query}") end - it "#{fixtures_path}#{get_group_runners_count_query_name}.json" do + it "#{fixtures_path}#{group_runners_count_query}.json" do post_graphql(query, current_user: group_owner, variables: { groupFullPath: group.full_path }) diff --git a/spec/frontend/issues/show/components/header_actions_spec.js b/spec/frontend/issues/show/components/header_actions_spec.js index 4a557a60b94..6640025a652 100644 --- a/spec/frontend/issues/show/components/header_actions_spec.js +++ b/spec/frontend/issues/show/components/header_actions_spec.js @@ -1,5 +1,5 @@ -import { GlButton, GlDropdown, GlDropdownItem, GlLink, GlModal } from '@gitlab/ui'; import Vue, { nextTick } from 'vue'; +import { GlButton, GlDropdownItem, GlLink, GlModal } from '@gitlab/ui'; import { shallowMount } from '@vue/test-utils'; import Vuex from 'vuex'; import { mockTracking } from 'helpers/tracking_helper'; @@ -65,12 +65,17 @@ describe('HeaderActions component', () => { }, }; - const findToggleIssueStateButton = () => wrapper.findComponent(GlButton); - const findDropdownAt = (index) => wrapper.findAllComponents(GlDropdown).at(index); - const findMobileDropdownItems = () => findDropdownAt(0).findAllComponents(GlDropdownItem); - const findDesktopDropdownItems = () => findDropdownAt(1).findAllComponents(GlDropdownItem); - const findModal = () => wrapper.findComponent(GlModal); - const findModalLinkAt = (index) => findModal().findAllComponents(GlLink).at(index); + const findToggleIssueStateButton = () => wrapper.find(GlButton); + + const findDropdownBy = (dataTestId) => wrapper.find(`[data-testid="${dataTestId}"]`); + const findMobileDropdown = () => findDropdownBy('mobile-dropdown'); + const findDesktopDropdown = () => findDropdownBy('desktop-dropdown'); + const findMobileDropdownItems = () => findMobileDropdown().findAll(GlDropdownItem); + const findDesktopDropdownItems = () => findDesktopDropdown().findAll(GlDropdownItem); + + const findModal = () => wrapper.find(GlModal); + + const findModalLinkAt = (index) => findModal().findAll(GlLink).at(index); const mountComponent = ({ props = {}, @@ -161,10 +166,10 @@ describe('HeaderActions component', () => { }); describe.each` - description | isCloseIssueItemVisible | findDropdownItems - ${'mobile dropdown'} | ${true} | ${findMobileDropdownItems} - ${'desktop dropdown'} | ${false} | ${findDesktopDropdownItems} - `('$description', ({ isCloseIssueItemVisible, findDropdownItems }) => { + description | isCloseIssueItemVisible | findDropdownItems | findDropdown + ${'mobile dropdown'} | ${true} | ${findMobileDropdownItems} | ${findMobileDropdown} + ${'desktop dropdown'} | ${false} | ${findDesktopDropdownItems} | ${findDesktopDropdown} + `('$description', ({ isCloseIssueItemVisible, findDropdownItems, findDropdown }) => { describe.each` description | itemText | isItemVisible | canUpdateIssue | canCreateIssue | isIssueAuthor | canReportSpam | canPromoteToEpic | canDestroyIssue ${`when user can update ${issueType}`} | ${`Close ${issueType}`} | ${isCloseIssueItemVisible} | ${true} | ${true} | ${true} | ${true} | ${true} | ${true} @@ -214,6 +219,24 @@ describe('HeaderActions component', () => { }); }, ); + + describe(`when user can update but not create ${issueType}`, () => { + beforeEach(() => { + wrapper = mountComponent({ + props: { + canUpdateIssue: true, + canCreateIssue: false, + isIssueAuthor: true, + issueType, + canReportSpam: false, + canPromoteToEpic: false, + }, + }); + }); + it(`${isCloseIssueItemVisible ? 'shows' : 'hides'} the dropdown button`, () => { + expect(findDropdown().exists()).toBe(isCloseIssueItemVisible); + }); + }); }); }); diff --git a/spec/frontend/merge_conflicts/components/merge_conflict_resolver_app_spec.js b/spec/frontend/merge_conflicts/components/merge_conflict_resolver_app_spec.js index 750fff9b0aa..55e666609bd 100644 --- a/spec/frontend/merge_conflicts/components/merge_conflict_resolver_app_spec.js +++ b/spec/frontend/merge_conflicts/components/merge_conflict_resolver_app_spec.js @@ -1,7 +1,7 @@ import { GlSprintf } from '@gitlab/ui'; -import { shallowMount } from '@vue/test-utils'; import Vue, { nextTick } from 'vue'; import Vuex from 'vuex'; +import { shallowMountExtended, extendedWrapper } from 'helpers/vue_test_utils_helper'; import InlineConflictLines from '~/merge_conflicts/components/inline_conflict_lines.vue'; import ParallelConflictLines from '~/merge_conflicts/components/parallel_conflict_lines.vue'; import component from '~/merge_conflicts/merge_conflict_resolver_app.vue'; @@ -18,7 +18,7 @@ describe('Merge Conflict Resolver App', () => { const decoratedMockFiles = decorateFiles(conflictsMock.files); const mountComponent = () => { - wrapper = shallowMount(component, { + wrapper = shallowMountExtended(component, { store, stubs: { GlSprintf }, provide() { @@ -41,15 +41,17 @@ describe('Merge Conflict Resolver App', () => { wrapper.destroy(); }); - const findConflictsCount = () => wrapper.find('[data-testid="conflicts-count"]'); - const findFiles = () => wrapper.findAll('[data-testid="files"]'); - const findFileHeader = (w = wrapper) => w.find('[data-testid="file-name"]'); - const findFileInteractiveButton = (w = wrapper) => w.find('[data-testid="interactive-button"]'); - const findFileInlineButton = (w = wrapper) => w.find('[data-testid="inline-button"]'); - const findSideBySideButton = () => wrapper.find('[data-testid="side-by-side"]'); + const findLoadingSpinner = () => wrapper.findByTestId('loading-spinner'); + const findConflictsCount = () => wrapper.findByTestId('conflicts-count'); + const findFiles = () => wrapper.findAllByTestId('files'); + const findFileHeader = (w = wrapper) => extendedWrapper(w).findByTestId('file-name'); + const findFileInteractiveButton = (w = wrapper) => + extendedWrapper(w).findByTestId('interactive-button'); + const findFileInlineButton = (w = wrapper) => extendedWrapper(w).findByTestId('inline-button'); + const findSideBySideButton = () => wrapper.findByTestId('side-by-side'); const findInlineConflictLines = (w = wrapper) => w.find(InlineConflictLines); const findParallelConflictLines = (w = wrapper) => w.find(ParallelConflictLines); - const findCommitMessageTextarea = () => wrapper.find('[data-testid="commit-message"]'); + const findCommitMessageTextarea = () => wrapper.findByTestId('commit-message'); it('shows the amount of conflicts', () => { mountComponent(); @@ -60,6 +62,19 @@ describe('Merge Conflict Resolver App', () => { expect(title.text().trim()).toBe('Showing 3 conflicts between test-conflicts and main'); }); + it('shows a loading spinner while loading', () => { + store.commit('SET_LOADING_STATE', true); + mountComponent(); + + expect(findLoadingSpinner().exists()).toBe(true); + }); + + it('does not show a loading spinner once loaded', () => { + mountComponent(); + + expect(findLoadingSpinner().exists()).toBe(false); + }); + describe('files', () => { it('shows one file area for each file', () => { mountComponent(); diff --git a/spec/frontend/pipeline_editor/pipeline_editor_app_spec.js b/spec/frontend/pipeline_editor/pipeline_editor_app_spec.js index 0a2c03b7850..0ce6cc3f2d4 100644 --- a/spec/frontend/pipeline_editor/pipeline_editor_app_spec.js +++ b/spec/frontend/pipeline_editor/pipeline_editor_app_spec.js @@ -18,12 +18,15 @@ import { COMMIT_SUCCESS, COMMIT_SUCCESS_WITH_REDIRECT, COMMIT_FAILURE, + EDITOR_APP_STATUS_LOADING, } from '~/pipeline_editor/constants'; import getBlobContent from '~/pipeline_editor/graphql/queries/blob_content.query.graphql'; import getCiConfigData from '~/pipeline_editor/graphql/queries/ci_config.query.graphql'; import getTemplate from '~/pipeline_editor/graphql/queries/get_starter_template.query.graphql'; import getLatestCommitShaQuery from '~/pipeline_editor/graphql/queries/latest_commit_sha.query.graphql'; import getPipelineQuery from '~/pipeline_editor/graphql/queries/pipeline.query.graphql'; +import getCurrentBranch from '~/pipeline_editor/graphql/queries/client/current_branch.query.graphql'; +import getAppStatus from '~/pipeline_editor/graphql/queries/client/app_status.query.graphql'; import PipelineEditorApp from '~/pipeline_editor/pipeline_editor_app.vue'; import PipelineEditorHome from '~/pipeline_editor/pipeline_editor_home.vue'; @@ -84,9 +87,6 @@ describe('Pipeline editor app component', () => { initialCiFileContent: { loading: blobLoading, }, - ciConfigData: { - loading: false, - }, }, }, }, @@ -94,7 +94,11 @@ describe('Pipeline editor app component', () => { }); }; - const createComponentWithApollo = async ({ provide = {}, stubs = {} } = {}) => { + const createComponentWithApollo = async ({ + provide = {}, + stubs = {}, + withUndefinedBranch = false, + } = {}) => { const handlers = [ [getBlobContent, mockBlobContentData], [getCiConfigData, mockCiConfigData], @@ -105,6 +109,31 @@ describe('Pipeline editor app component', () => { mockApollo = createMockApollo(handlers, resolvers); + if (!withUndefinedBranch) { + mockApollo.clients.defaultClient.cache.writeQuery({ + query: getCurrentBranch, + data: { + workBranches: { + __typename: 'BranchList', + current: { + __typename: 'WorkBranch', + name: mockDefaultBranch, + }, + }, + }, + }); + } + + mockApollo.clients.defaultClient.cache.writeQuery({ + query: getAppStatus, + data: { + app: { + __typename: 'AppData', + status: EDITOR_APP_STATUS_LOADING, + }, + }, + }); + const options = { localVue, mocks: {}, @@ -145,6 +174,55 @@ describe('Pipeline editor app component', () => { }); }); + describe('skipping queries', () => { + describe('when branchName is undefined', () => { + beforeEach(async () => { + await createComponentWithApollo({ withUndefinedBranch: true }); + }); + + it('does not calls getBlobContent', () => { + expect(mockBlobContentData).not.toHaveBeenCalled(); + }); + }); + + describe('when branchName is defined', () => { + beforeEach(async () => { + await createComponentWithApollo(); + }); + + it('calls getBlobContent', () => { + expect(mockBlobContentData).toHaveBeenCalled(); + }); + }); + + describe('when commit sha is undefined', () => { + beforeEach(async () => { + mockLatestCommitShaQuery.mockResolvedValue(undefined); + await createComponentWithApollo(); + }); + + it('calls getBlobContent', () => { + expect(mockBlobContentData).toHaveBeenCalled(); + }); + + it('does not call ciConfigData', () => { + expect(mockCiConfigData).not.toHaveBeenCalled(); + }); + }); + + describe('when commit sha is defined', () => { + beforeEach(async () => { + mockBlobContentData.mockResolvedValue(mockBlobContentQueryResponse); + mockLatestCommitShaQuery.mockResolvedValue(mockCommitShaResults); + await createComponentWithApollo(); + }); + + it('calls ciConfigData', () => { + expect(mockCiConfigData).toHaveBeenCalled(); + }); + }); + }); + describe('when queries are called', () => { beforeEach(() => { mockBlobContentData.mockResolvedValue(mockBlobContentQueryResponse); diff --git a/spec/frontend/runner/admin_runner_edit/admin_runner_edit_app_spec.js b/spec/frontend/runner/admin_runner_edit/admin_runner_edit_app_spec.js index ff6a632a4f8..d121c6be218 100644 --- a/spec/frontend/runner/admin_runner_edit/admin_runner_edit_app_spec.js +++ b/spec/frontend/runner/admin_runner_edit/admin_runner_edit_app_spec.js @@ -7,7 +7,7 @@ import { createAlert } from '~/flash'; import { getIdFromGraphQLId } from '~/graphql_shared/utils'; import RunnerHeader from '~/runner/components/runner_header.vue'; -import getRunnerQuery from '~/runner/graphql/get_runner.query.graphql'; +import runnerQuery from '~/runner/graphql/details/runner.query.graphql'; import AdminRunnerEditApp from '~//runner/admin_runner_edit/admin_runner_edit_app.vue'; import { captureException } from '~/runner/sentry_utils'; @@ -29,7 +29,7 @@ describe('AdminRunnerEditApp', () => { const createComponentWithApollo = ({ props = {}, mountFn = shallowMount } = {}) => { wrapper = mountFn(AdminRunnerEditApp, { - apolloProvider: createMockApollo([[getRunnerQuery, mockRunnerQuery]]), + apolloProvider: createMockApollo([[runnerQuery, mockRunnerQuery]]), propsData: { runnerId: mockRunnerId, ...props, diff --git a/spec/frontend/runner/admin_runner_show/admin_runner_show_app_spec.js b/spec/frontend/runner/admin_runner_show/admin_runner_show_app_spec.js index 4b651961112..f994ff24c21 100644 --- a/spec/frontend/runner/admin_runner_show/admin_runner_show_app_spec.js +++ b/spec/frontend/runner/admin_runner_show/admin_runner_show_app_spec.js @@ -9,7 +9,7 @@ import { getIdFromGraphQLId } from '~/graphql_shared/utils'; import RunnerHeader from '~/runner/components/runner_header.vue'; import RunnerPauseButton from '~/runner/components/runner_pause_button.vue'; import RunnerEditButton from '~/runner/components/runner_edit_button.vue'; -import getRunnerQuery from '~/runner/graphql/get_runner.query.graphql'; +import runnerQuery from '~/runner/graphql/details/runner.query.graphql'; import AdminRunnerShowApp from '~/runner/admin_runner_show/admin_runner_show_app.vue'; import { captureException } from '~/runner/sentry_utils'; @@ -42,7 +42,7 @@ describe('AdminRunnerShowApp', () => { const createComponent = ({ props = {}, mountFn = shallowMount } = {}) => { wrapper = mountFn(AdminRunnerShowApp, { - apolloProvider: createMockApollo([[getRunnerQuery, mockRunnerQuery]]), + apolloProvider: createMockApollo([[runnerQuery, mockRunnerQuery]]), propsData: { runnerId: mockRunnerId, ...props, diff --git a/spec/frontend/runner/admin_runners/admin_runners_app_spec.js b/spec/frontend/runner/admin_runners/admin_runners_app_spec.js index 2251f26d0c6..fe7d920800d 100644 --- a/spec/frontend/runner/admin_runners/admin_runners_app_spec.js +++ b/spec/frontend/runner/admin_runners/admin_runners_app_spec.js @@ -35,8 +35,8 @@ import { STATUS_ACTIVE, RUNNER_PAGE_SIZE, } from '~/runner/constants'; -import getRunnersQuery from '~/runner/graphql/get_runners.query.graphql'; -import getRunnersCountQuery from '~/runner/graphql/get_runners_count.query.graphql'; +import adminRunnersQuery from '~/runner/graphql/list/admin_runners.query.graphql'; +import adminRunnersCountQuery from '~/runner/graphql/list/admin_runners_count.query.graphql'; import { captureException } from '~/runner/sentry_utils'; import FilteredSearch from '~/vue_shared/components/filtered_search_bar/filtered_search_bar_root.vue'; @@ -71,8 +71,8 @@ describe('AdminRunnersApp', () => { const createComponent = ({ props = {}, mountFn = shallowMountExtended } = {}) => { const handlers = [ - [getRunnersQuery, mockRunnersQuery], - [getRunnersCountQuery, mockRunnersCountQuery], + [adminRunnersQuery, mockRunnersQuery], + [adminRunnersCountQuery, mockRunnersCountQuery], ]; wrapper = mountFn(AdminRunnersApp, { diff --git a/spec/frontend/runner/components/registration/registration_token_reset_dropdown_item_spec.js b/spec/frontend/runner/components/registration/registration_token_reset_dropdown_item_spec.js index d2deb49a5f7..2510aaf0334 100644 --- a/spec/frontend/runner/components/registration/registration_token_reset_dropdown_item_spec.js +++ b/spec/frontend/runner/components/registration/registration_token_reset_dropdown_item_spec.js @@ -8,7 +8,7 @@ import waitForPromises from 'helpers/wait_for_promises'; import { createAlert } from '~/flash'; import RegistrationTokenResetDropdownItem from '~/runner/components/registration/registration_token_reset_dropdown_item.vue'; import { INSTANCE_TYPE, GROUP_TYPE, PROJECT_TYPE } from '~/runner/constants'; -import runnersRegistrationTokenResetMutation from '~/runner/graphql/runners_registration_token_reset.mutation.graphql'; +import runnersRegistrationTokenResetMutation from '~/runner/graphql/list/runners_registration_token_reset.mutation.graphql'; import { captureException } from '~/runner/sentry_utils'; import { createMockDirective, getBinding } from 'helpers/vue_mock_directive'; diff --git a/spec/frontend/runner/components/runner_delete_button_spec.js b/spec/frontend/runner/components/runner_delete_button_spec.js index 8479fa35dd2..5fa2d033dcc 100644 --- a/spec/frontend/runner/components/runner_delete_button_spec.js +++ b/spec/frontend/runner/components/runner_delete_button_spec.js @@ -4,7 +4,7 @@ import VueApollo from 'vue-apollo'; import createMockApollo from 'helpers/mock_apollo_helper'; import { createMockDirective, getBinding } from 'helpers/vue_mock_directive'; import { shallowMountExtended, mountExtended } from 'helpers/vue_test_utils_helper'; -import runnerDeleteMutation from '~/runner/graphql/runner_delete.mutation.graphql'; +import runnerDeleteMutation from '~/runner/graphql/shared/runner_delete.mutation.graphql'; import waitForPromises from 'helpers/wait_for_promises'; import { captureException } from '~/runner/sentry_utils'; import { getIdFromGraphQLId } from '~/graphql_shared/utils'; diff --git a/spec/frontend/runner/components/runner_jobs_spec.js b/spec/frontend/runner/components/runner_jobs_spec.js index 97339056370..9abb2861005 100644 --- a/spec/frontend/runner/components/runner_jobs_spec.js +++ b/spec/frontend/runner/components/runner_jobs_spec.js @@ -11,7 +11,7 @@ import RunnerPagination from '~/runner/components/runner_pagination.vue'; import { captureException } from '~/runner/sentry_utils'; import { I18N_NO_JOBS_FOUND, RUNNER_DETAILS_JOBS_PAGE_SIZE } from '~/runner/constants'; -import getRunnerJobsQuery from '~/runner/graphql/get_runner_jobs.query.graphql'; +import runnerJobsQuery from '~/runner/graphql/details/runner_jobs.query.graphql'; import { runnerData, runnerJobsData } from '../mock_data'; @@ -34,7 +34,7 @@ describe('RunnerJobs', () => { const createComponent = ({ mountFn = shallowMountExtended } = {}) => { wrapper = mountFn(RunnerJobs, { - apolloProvider: createMockApollo([[getRunnerJobsQuery, mockRunnerJobsQuery]]), + apolloProvider: createMockApollo([[runnerJobsQuery, mockRunnerJobsQuery]]), propsData: { runner: mockRunner, }, diff --git a/spec/frontend/runner/components/runner_pause_button_spec.js b/spec/frontend/runner/components/runner_pause_button_spec.js index 278f3dec2ee..c69bae05e45 100644 --- a/spec/frontend/runner/components/runner_pause_button_spec.js +++ b/spec/frontend/runner/components/runner_pause_button_spec.js @@ -4,7 +4,7 @@ import VueApollo from 'vue-apollo'; import createMockApollo from 'helpers/mock_apollo_helper'; import { createMockDirective, getBinding } from 'helpers/vue_mock_directive'; import { shallowMountExtended, mountExtended } from 'helpers/vue_test_utils_helper'; -import runnerToggleActiveMutation from '~/runner/graphql/runner_toggle_active.mutation.graphql'; +import runnerToggleActiveMutation from '~/runner/graphql/shared/runner_toggle_active.mutation.graphql'; import waitForPromises from 'helpers/wait_for_promises'; import { captureException } from '~/runner/sentry_utils'; import { createAlert } from '~/flash'; diff --git a/spec/frontend/runner/components/runner_projects_spec.js b/spec/frontend/runner/components/runner_projects_spec.js index 68a2130d6d9..96de8d11bca 100644 --- a/spec/frontend/runner/components/runner_projects_spec.js +++ b/spec/frontend/runner/components/runner_projects_spec.js @@ -16,7 +16,7 @@ import RunnerAssignedItem from '~/runner/components/runner_assigned_item.vue'; import RunnerPagination from '~/runner/components/runner_pagination.vue'; import { captureException } from '~/runner/sentry_utils'; -import getRunnerProjectsQuery from '~/runner/graphql/get_runner_projects.query.graphql'; +import runnerProjectsQuery from '~/runner/graphql/details/runner_projects.query.graphql'; import { runnerData, runnerProjectsData } from '../mock_data'; @@ -40,7 +40,7 @@ describe('RunnerProjects', () => { const createComponent = ({ mountFn = shallowMountExtended } = {}) => { wrapper = mountFn(RunnerProjects, { - apolloProvider: createMockApollo([[getRunnerProjectsQuery, mockRunnerProjectsQuery]]), + apolloProvider: createMockApollo([[runnerProjectsQuery, mockRunnerProjectsQuery]]), propsData: { runner: mockRunner, }, diff --git a/spec/frontend/runner/components/runner_update_form_spec.js b/spec/frontend/runner/components/runner_update_form_spec.js index 8b76be396ef..b071791e39f 100644 --- a/spec/frontend/runner/components/runner_update_form_spec.js +++ b/spec/frontend/runner/components/runner_update_form_spec.js @@ -13,7 +13,7 @@ import { ACCESS_LEVEL_REF_PROTECTED, ACCESS_LEVEL_NOT_PROTECTED, } from '~/runner/constants'; -import runnerUpdateMutation from '~/runner/graphql/runner_update.mutation.graphql'; +import runnerUpdateMutation from '~/runner/graphql/details/runner_update.mutation.graphql'; import { captureException } from '~/runner/sentry_utils'; import { runnerData } from '../mock_data'; diff --git a/spec/frontend/runner/group_runners/group_runners_app_spec.js b/spec/frontend/runner/group_runners/group_runners_app_spec.js index 4f373516abb..857db7663ec 100644 --- a/spec/frontend/runner/group_runners/group_runners_app_spec.js +++ b/spec/frontend/runner/group_runners/group_runners_app_spec.js @@ -32,8 +32,8 @@ import { RUNNER_PAGE_SIZE, I18N_EDIT, } from '~/runner/constants'; -import getGroupRunnersQuery from '~/runner/graphql/get_group_runners.query.graphql'; -import getGroupRunnersCountQuery from '~/runner/graphql/get_group_runners_count.query.graphql'; +import getGroupRunnersQuery from '~/runner/graphql/list/group_runners.query.graphql'; +import getGroupRunnersCountQuery from '~/runner/graphql/list/group_runners_count.query.graphql'; import GroupRunnersApp from '~/runner/group_runners/group_runners_app.vue'; import { captureException } from '~/runner/sentry_utils'; import FilteredSearch from '~/vue_shared/components/filtered_search_bar/filtered_search_bar_root.vue'; diff --git a/spec/frontend/runner/mock_data.js b/spec/frontend/runner/mock_data.js index d80caa47752..49c25039719 100644 --- a/spec/frontend/runner/mock_data.js +++ b/spec/frontend/runner/mock_data.js @@ -1,18 +1,18 @@ // Fixtures generated by: spec/frontend/fixtures/runner.rb -// Admin queries -import runnersData from 'test_fixtures/graphql/runner/get_runners.query.graphql.json'; -import runnersCountData from 'test_fixtures/graphql/runner/get_runners_count.query.graphql.json'; -import runnersDataPaginated from 'test_fixtures/graphql/runner/get_runners.query.graphql.paginated.json'; -import runnerData from 'test_fixtures/graphql/runner/get_runner.query.graphql.json'; -import runnerWithGroupData from 'test_fixtures/graphql/runner/get_runner.query.graphql.with_group.json'; -import runnerProjectsData from 'test_fixtures/graphql/runner/get_runner_projects.query.graphql.json'; -import runnerJobsData from 'test_fixtures/graphql/runner/get_runner_jobs.query.graphql.json'; +// List queries +import runnersData from 'test_fixtures/graphql/runner/list/admin_runners.query.graphql.json'; +import runnersDataPaginated from 'test_fixtures/graphql/runner/list/admin_runners.query.graphql.paginated.json'; +import runnersCountData from 'test_fixtures/graphql/runner/list/admin_runners_count.query.graphql.json'; +import groupRunnersData from 'test_fixtures/graphql/runner/list/group_runners.query.graphql.json'; +import groupRunnersDataPaginated from 'test_fixtures/graphql/runner/list/group_runners.query.graphql.paginated.json'; +import groupRunnersCountData from 'test_fixtures/graphql/runner/list/group_runners_count.query.graphql.json'; -// Group queries -import groupRunnersData from 'test_fixtures/graphql/runner/get_group_runners.query.graphql.json'; -import groupRunnersCountData from 'test_fixtures/graphql/runner/get_group_runners_count.query.graphql.json'; -import groupRunnersDataPaginated from 'test_fixtures/graphql/runner/get_group_runners.query.graphql.paginated.json'; +// Details queries +import runnerData from 'test_fixtures/graphql/runner/details/runner.query.graphql.json'; +import runnerWithGroupData from 'test_fixtures/graphql/runner/details/runner.query.graphql.with_group.json'; +import runnerProjectsData from 'test_fixtures/graphql/runner/details/runner_projects.query.graphql.json'; +import runnerJobsData from 'test_fixtures/graphql/runner/details/runner_jobs.query.graphql.json'; export { runnersData, diff --git a/spec/helpers/deploy_tokens_helper_spec.rb b/spec/helpers/deploy_tokens_helper_spec.rb new file mode 100644 index 00000000000..e5dd5ff79a2 --- /dev/null +++ b/spec/helpers/deploy_tokens_helper_spec.rb @@ -0,0 +1,20 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe DeployTokensHelper do + describe '#deploy_token_revoke_button_data' do + let_it_be(:token) { build(:deploy_token) } + let_it_be(:project) { build(:project) } + let_it_be(:revoke_deploy_token_path) { '/foobar/baz/-/deploy_tokens/1/revoke' } + + it 'returns expected hash' do + expect(helper).to receive(:revoke_deploy_token_path).with(project, token).and_return(revoke_deploy_token_path) + + expect(helper.deploy_token_revoke_button_data(token: token, group_or_project: project)).to match({ + token: token.to_json(only: [:id, :name]), + revoke_path: revoke_deploy_token_path + }) + end + end +end diff --git a/spec/lib/gitlab/database/rename_reserved_paths_migration/v1/rename_projects_spec.rb b/spec/lib/gitlab/database/rename_reserved_paths_migration/v1/rename_projects_spec.rb index 3799fe3c316..50071e3e22b 100644 --- a/spec/lib/gitlab/database/rename_reserved_paths_migration/v1/rename_projects_spec.rb +++ b/spec/lib/gitlab/database/rename_reserved_paths_migration/v1/rename_projects_spec.rb @@ -49,7 +49,7 @@ RSpec.describe Gitlab::Database::RenameReservedPathsMigration::V1::RenameProject it 'invalidates the markdown cache of related projects' do expect(subject).to receive(:remove_cached_html_for_projects) - .with(projects.map(&:id)) + .with(a_collection_containing_exactly(*projects.map(&:id))) subject.rename_projects end diff --git a/spec/models/group_spec.rb b/spec/models/group_spec.rb index 5f8d75d1f59..297b11bd72a 100644 --- a/spec/models/group_spec.rb +++ b/spec/models/group_spec.rb @@ -390,26 +390,8 @@ RSpec.describe Group do let!(:old_parent) { create(:group, parent: root) } let!(:new_parent) { create(:group, parent: root) } - context 'with FOR UPDATE lock' do - before do - stub_feature_flags(for_no_key_update_lock: false) - subject - reload_models(old_parent, new_parent, group) - end - - it 'updates traversal_ids' do - expect(group.traversal_ids).to eq [root.id, new_parent.id, group.id] - end - - it_behaves_like 'hierarchy with traversal_ids' - it_behaves_like 'locked row', 'FOR UPDATE' do - let(:row) { root } - end - end - context 'with FOR NO KEY UPDATE lock' do before do - stub_feature_flags(for_no_key_update_lock: true) subject reload_models(old_parent, new_parent, group) end @@ -419,7 +401,7 @@ RSpec.describe Group do end it_behaves_like 'hierarchy with traversal_ids' - it_behaves_like 'locked row', 'FOR NO KEY UPDATE' do + it_behaves_like 'locked row' do let(:row) { root } end end diff --git a/spec/models/namespace/traversal_hierarchy_spec.rb b/spec/models/namespace/traversal_hierarchy_spec.rb index eeea071d326..51932ab943c 100644 --- a/spec/models/namespace/traversal_hierarchy_spec.rb +++ b/spec/models/namespace/traversal_hierarchy_spec.rb @@ -68,24 +68,11 @@ RSpec.describe Namespace::TraversalHierarchy, type: :model do end end - it_behaves_like 'locked row', 'FOR UPDATE' do + it_behaves_like 'locked row' do let(:recorded_queries) { ActiveRecord::QueryRecorder.new } let(:row) { root } before do - stub_feature_flags(for_no_key_update_lock: false) - - recorded_queries.record { subject } - end - end - - it_behaves_like 'locked row', 'FOR NO KEY UPDATE' do - let(:recorded_queries) { ActiveRecord::QueryRecorder.new } - let(:row) { root } - - before do - stub_feature_flags(for_no_key_update_lock: true) - recorded_queries.record { subject } end end diff --git a/spec/models/namespace_spec.rb b/spec/models/namespace_spec.rb index 1728d4fc3f3..ebd153f6f10 100644 --- a/spec/models/namespace_spec.rb +++ b/spec/models/namespace_spec.rb @@ -436,17 +436,7 @@ RSpec.describe Namespace do it { expect(namespace.traversal_ids).to eq [namespace.id] } end - context 'with before_commit callback' do - it_behaves_like 'default traversal_ids' - end - - context 'with after_create callback' do - before do - stub_feature_flags(sync_traversal_ids_before_commit: false) - end - - it_behaves_like 'default traversal_ids' - end + it_behaves_like 'default traversal_ids' end describe "after_commit :expire_child_caches" do diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb index 139223e4245..6516b5f777e 100644 --- a/spec/models/project_spec.rb +++ b/spec/models/project_spec.rb @@ -262,17 +262,7 @@ RSpec.describe Project, factory_default: :keep do end end - context 'sync-ing traversal_ids in before_commit callback' do - it_behaves_like 'creates project namespace' - end - - context 'sync-ing traversal_ids in after_create callback' do - before do - stub_feature_flags(sync_traversal_ids_before_commit: false) - end - - it_behaves_like 'creates project namespace' - end + it_behaves_like 'creates project namespace' end end diff --git a/spec/services/groups/create_service_spec.rb b/spec/services/groups/create_service_spec.rb index 7ec523a1f2b..819569d6e67 100644 --- a/spec/services/groups/create_service_spec.rb +++ b/spec/services/groups/create_service_spec.rb @@ -85,14 +85,6 @@ RSpec.describe Groups::CreateService, '#execute' do context 'with before_commit callback' do it_behaves_like 'has sync-ed traversal_ids' end - - context 'with after_create callback' do - before do - stub_feature_flags(sync_traversal_ids_before_commit: false) - end - - it_behaves_like 'has sync-ed traversal_ids' - end end context 'when user can not create a group' do @@ -119,17 +111,7 @@ RSpec.describe Groups::CreateService, '#execute' do expect { subject }.not_to change(OnboardingProgress, :count).from(0) end - context 'with before_commit callback' do - it_behaves_like 'has sync-ed traversal_ids' - end - - context 'with after_create callback' do - before do - stub_feature_flags(sync_traversal_ids_before_commit: false) - end - - it_behaves_like 'has sync-ed traversal_ids' - end + it_behaves_like 'has sync-ed traversal_ids' end context 'as guest' do diff --git a/spec/services/projects/create_service_spec.rb b/spec/services/projects/create_service_spec.rb index 0d4974a7a86..377acdd14cd 100644 --- a/spec/services/projects/create_service_spec.rb +++ b/spec/services/projects/create_service_spec.rb @@ -225,17 +225,7 @@ RSpec.describe Projects::CreateService, '#execute' do expect(project.project_namespace).to be_in_sync_with_project(project) end - context 'with before_commit callback' do - it_behaves_like 'has sync-ed traversal_ids' - end - - context 'with after_create callback' do - before do - stub_feature_flags(sync_traversal_ids_before_commit: false) - end - - it_behaves_like 'has sync-ed traversal_ids' - end + it_behaves_like 'has sync-ed traversal_ids' end context 'group sharing', :sidekiq_inline do diff --git a/spec/simplecov_env.rb b/spec/simplecov_env.rb index da4a0e8da80..be2ff396bc4 100644 --- a/spec/simplecov_env.rb +++ b/spec/simplecov_env.rb @@ -49,6 +49,7 @@ module SimpleCovEnv def configure_profile SimpleCov.configure do + enable_coverage :branch load_profile 'test_frameworks' track_files '{app,config/initializers,config/initializers_before_autoloader,db/post_migrate,haml_lint,lib,rubocop,tooling}/**/*.rb' diff --git a/spec/support/shared_examples/row_lock_shared_examples.rb b/spec/support/shared_examples/row_lock_shared_examples.rb index e7eec88ec42..24fb2d41bdf 100644 --- a/spec/support/shared_examples/row_lock_shared_examples.rb +++ b/spec/support/shared_examples/row_lock_shared_examples.rb @@ -4,10 +4,10 @@ # Ensure a transaction also occurred. # Be careful! This form of spec is not foolproof, but better than nothing. -RSpec.shared_examples 'locked row' do |lock_type| +RSpec.shared_examples 'locked row' do it "has locked row" do table_name = row.class.table_name - ids_regex = /SELECT.*FROM.*#{table_name}.*"#{table_name}"."id" = #{row.id}.+#{lock_type}/m + ids_regex = /SELECT.*FROM.*#{table_name}.*"#{table_name}"."id" = #{row.id}.+FOR NO KEY UPDATE/m expect(recorded_queries.log).to include a_string_matching 'SAVEPOINT' expect(recorded_queries.log).to include a_string_matching ids_regex diff --git a/spec/views/admin/application_settings/_eks.html.haml_spec.rb b/spec/views/admin/application_settings/_eks.html.haml_spec.rb index e407970c7a4..d16cbc7b299 100644 --- a/spec/views/admin/application_settings/_eks.html.haml_spec.rb +++ b/spec/views/admin/application_settings/_eks.html.haml_spec.rb @@ -16,8 +16,8 @@ RSpec.describe 'admin/application_settings/_eks' do shared_examples 'EKS secret access key input' do it 'renders an empty password field' do render - expect(rendered).to have_field('Secret access key', type: 'password') - expect(page.find_field('Secret access key').value).to be_blank + expect(rendered).to have_field('AWS secret access key (Optional)', type: 'password') + expect(page.find_field('AWS secret access key (Optional)').value).to be_blank end end diff --git a/spec/views/layouts/_published_experiments.html.haml_spec.rb b/spec/views/layouts/_published_experiments.html.haml_spec.rb index d1ade8ddd6e..84894554bd9 100644 --- a/spec/views/layouts/_published_experiments.html.haml_spec.rb +++ b/spec/views/layouts/_published_experiments.html.haml_spec.rb @@ -4,22 +4,20 @@ require 'spec_helper' RSpec.describe 'layouts/_published_experiments', :experiment do before do - stub_const('TestControlExperiment', ApplicationExperiment) - stub_const('TestCandidateExperiment', ApplicationExperiment) - stub_const('TestExcludedExperiment', ApplicationExperiment) + # Stub each experiment to be enabled, otherwise tracking does not happen. + stub_experiments( + test_control: :control, + test_excluded: true, + test_published_only: :control, + test_candidate: :candidate, + test_variant: :variant_name + ) - TestControlExperiment.new('test_control').tap do |e| - e.variant(:control) - e.publish - end - TestCandidateExperiment.new('test_candidate').tap do |e| - e.variant(:candidate) - e.publish - end - TestExcludedExperiment.new('test_excluded').tap do |e| - e.exclude! - e.publish - end + experiment(:test_control) { } + experiment(:test_excluded) { |e| e.exclude! } + experiment(:test_candidate) { |e| e.candidate { } } + experiment(:test_variant) { |e| e.variant(:variant_name) { } } + experiment(:test_published_only).publish render end @@ -29,7 +27,9 @@ RSpec.describe 'layouts/_published_experiments', :experiment do expect(output).to include('gl.experiments = {') expect(output).to match(/"test_control":\{[^}]*"variant":"control"/) - expect(output).to match(/"test_candidate":\{[^}]*"variant":"candidate"/) expect(output).not_to include('"test_excluded"') + expect(output).to match(/"test_candidate":\{[^}]*"variant":"candidate"/) + expect(output).to match(/"test_variant":\{[^}]*"variant":"variant_name"/) + expect(output).to match(/"test_published_only":\{[^}]*"variant":"control"/) end end |