Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2022-09-12 15:14:03 +00:00
parent 07d9675a80
commit a75c69379b
165 changed files with 1549 additions and 448 deletions

View File

@ -249,18 +249,56 @@ Dangerfile @gl-quality/eng-prod
^[Growth Experiments]
/app/experiments/ @gitlab-org/growth/experiment-devs
/spec/experiments/ @gitlab-org/growth/experiment-devs
/app/models/experiment.rb @gitlab-org/growth/experiment-devs
/spec/models/experiment.rb @gitlab-org/growth/experiment-devs
/app/models/experiment_subject.rb @gitlab-org/growth/experiment-devs
/spec/models/experiment_subject.rb @gitlab-org/growth/experiment-devs
/app/models/experiment_user.rb @gitlab-org/growth/experiment-devs
/spec/models/experiment_user.rb @gitlab-org/growth/experiment-devs
/app/workers/experiments/ @gitlab-org/growth/experiment-devs
/spec/workers/experiments/ @gitlab-org/growth/experiment-devs
/config/initializers/gitlab_experiment.rb @gitlab-org/growth/experiment-devs
/config/feature_flags/experiment/ @gitlab-org/growth/experiment-devs
/ee/config/feature_flags/experiment/ @gitlab-org/growth/experiment-devs
/ee/lib/api/experiments.rb @gitlab-org/growth/experiment-devs
/ee/lib/ee/api/entities/experiment.rb @gitlab-org/growth/experiment-devs
/lib/gitlab/experimentation/ @gitlab-org/growth/experiment-devs
/lib/gitlab/experimentation.rb @gitlab-org/growth/experiment-devs
/lib/gitlab/experimentation_logger.rb @gitlab-org/growth/experiment-devs
/ee/spec/requests/api/experiments_spec.rb @gitlab-org/growth/experiment-devs
/ee/lib/ee/api/entities/experiment.rb @gitlab-org/growth/experiment-devs
/ee/spec/lib/ee/api/entities/experiment_spec.rb @gitlab-org/growth/experiment-devs
/lib/gitlab/experimentation/ @gitlab-org/growth/experiment-devs
/spec/lib/gitlab/experimentation/ @gitlab-org/growth/experiment-devs
/lib/gitlab/experimentation.rb @gitlab-org/growth/experiment-devs
/spec/lib/gitlab/experimentation_spec.rb @gitlab-org/growth/experiment-devs
/lib/gitlab/experimentation_logger.rb @gitlab-org/growth/experiment-devs
^[Growth]
/ee/app/workers/onboarding/ @gitlab-org/growth/engineers
/ee/spec/workers/onboarding/ @gitlab-org/growth/engineers
/app/models/onboarding/ @gitlab-org/growth/engineers
/spec/models/onboarding/ @gitlab-org/growth/engineers
/app/services/onboarding/ @gitlab-org/growth/engineers
/spec/services/onboarding/ @gitlab-org/growth/engineers
/ee/app/controllers/registrations/ @gitlab-org/growth/engineers
/ee/app/components/namespaces/free_user_cap/ @gitlab-org/growth/engineers
/ee/spec/components/namespaces/free_user_cap/ @gitlab-org/growth/engineers
/ee/app/models/namespaces/free_user_cap/ @gitlab-org/growth/engineers
/ee/spec/models/namespaces/free_user_cap/ @gitlab-org/growth/engineers
/app/services/users/in_product_marketing_email_records.rb @gitlab-org/growth/engineers
/spec/services/users/in_product_marketing_email_records_spec.rb @gitlab-org/growth/engineers
/app/workers/namespaces/in_product_marketing_emails_worker.rb @gitlab-org/growth/engineers
/spec/workers/namespaces/in_product_marketing_emails_worker_spec.rb @gitlab-org/growth/engineers
/ee/app/workers/ee/namespaces/in_product_marketing_emails_worker.rb @gitlab-org/growth/engineers
/ee/spec/workers/ee/namespaces/in_product_marketing_emails_worker_spec.rb @gitlab-org/growth/engineers
/app/models/users/in_product_marketing_email.rb @gitlab-org/growth/engineers
/spec/models/users/in_product_marketing_email_spec.rb @gitlab-org/growth/engineers
/app/services/namespaces/in_product_marketing_emails_service.rb @gitlab-org/growth/engineers
/spec/services/namespaces/in_product_marketing_emails_service_spec.rb @gitlab-org/growth/engineers
/ee/app/services/ee/namespaces/in_product_marketing_emails_service.rb @gitlab-org/growth/engineers
/ee/spec/services/namespaces/in_product_marketing_emails_service_spec.rb @gitlab-org/growth/engineers
/app/workers/projects/record_target_platforms_worker.rb @gitlab-org/growth/engineers
/spec/workers/projects/record_target_platforms_worker_spec.rb @gitlab-org/growth/engineers
/ee/app/controllers/groups/feature_discovery_moments_controller.rb @gitlab-org/growth/engineers
/ee/spec/requests/groups/feature_discovery_moments_spec.rb @gitlab-org/growth/engineers
^[Legal]
/config/dependency_decisions.yml @gitlab-org/legal-reviewers

View File

@ -18,6 +18,7 @@
# Disable warnings in browserslist which can break on backports
# https://github.com/browserslist/browserslist/blob/a287ec6/node.js#L367-L384
BROWSERSLIST_IGNORE_OLD_DATA: "true"
WEBPACK_COMPILE_LOG_PATH: "tmp/webpack-output.log"
stage: prepare
script:
- *yarn-install
@ -31,7 +32,6 @@ compile-production-assets:
variables:
NODE_ENV: "production"
RAILS_ENV: "production"
WEBPACK_REPORT: "true"
artifacts:
name: webpack-report
expire_in: 31d
@ -40,7 +40,7 @@ compile-production-assets:
# - in `build-assets-image` job to create assets image for packaging systems
# - GitLab UI for integration tests: https://gitlab.com/gitlab-org/gitlab-ui/-/blob/e88493b3c855aea30bf60baee692a64606b0eb1e/.storybook/preview-head.pug#L1
- public/assets/
- webpack-report/
- "${WEBPACK_COMPILE_LOG_PATH}"
when: always
before_script:
- !reference [.default-before_script, before_script]
@ -56,6 +56,7 @@ compile-test-assets:
paths:
- public/assets/
- node_modules/@gitlab/svgs/dist/icons.json # app/helpers/icons_helper.rb uses this file
- "${WEBPACK_COMPILE_LOG_PATH}"
when: always
compile-test-assets as-if-foss:
@ -321,24 +322,20 @@ webpack-dev-server:
bundle-size-review:
extends:
- .default-retry
- .assets-compile-cache
- .frontend:rules:bundle-size-review
image: ${REGISTRY_HOST}/${REGISTRY_GROUP}/gitlab-build-images:danger
stage: test
needs: ["compile-production-assets"]
needs: []
script:
- source scripts/utils.sh
- mkdir -p bundle-size-review
- cp webpack-report/index.html bundle-size-review/bundle-report.html
- yarn global add https://gitlab.com/gitlab-org/frontend/playground/webpack-memory-metrics.git
- |
danger_id=$(echo -n ${DANGER_GITLAB_API_TOKEN} | md5sum | awk '{print $1}' | cut -c5-10)
run_timed_command "danger --dangerfile=danger/Dangerfile-bundle_size --fail-on-errors=true --verbose --danger_id=bundle-size-review-${danger_id}"
- *yarn-install
- scripts/bundle_size_review
artifacts:
when: always
name: bundle-size-review
expire_in: 31d
paths:
- bundle-size-review
- bundle-size-review/
.startup-css-check-base:
extends:

View File

@ -879,6 +879,9 @@
rules:
- <<: *if-not-canonical-namespace
when: never
- <<: *if-default-branch-refs
changes: *frontend-build-patterns
allow_failure: true
- if: '$DANGER_GITLAB_API_TOKEN && $CI_MERGE_REQUEST_IID && $CI_MERGE_REQUEST_TARGET_BRANCH_NAME == $CI_DEFAULT_BRANCH'
changes: *frontend-build-patterns
allow_failure: true

View File

@ -140,6 +140,7 @@ Gitlab/FeatureAvailableUsage:
- ee/lib/ee/gitlab/tree_summary.rb
- ee/lib/gitlab/alert_management.rb
- ee/lib/gitlab/ci/pipeline/chain/config/content/compliance.rb
- ee/lib/gitlab/ci/project_config/compliance.rb
- ee/lib/gitlab/code_owners.rb
- ee/lib/gitlab/incident_management.rb
- ee/lib/gitlab/path_locks_finder.rb

View File

@ -404,6 +404,7 @@ Gitlab/NamespacedClass:
- 'app/policies/project_hook_policy.rb'
- 'app/policies/prometheus_alert_policy.rb'
- 'app/policies/protected_branch_policy.rb'
- 'app/policies/protected_branch_access_policy.rb'
- 'app/policies/release_policy.rb'
- 'app/policies/repository_policy.rb'
- 'app/policies/resource_label_event_policy.rb'

View File

@ -186,27 +186,34 @@ export default {
:data-username="author.username"
>
<span
v-if="glFeatures.removeUserAttributes"
v-if="glFeatures.removeUserAttributesProjects || glFeatures.removeUserAttributesGroups"
class="note-header-author-name gl-font-weight-bold"
>
{{ authorName }}
</span>
<user-name-with-status
v-if="!glFeatures.removeUserAttributes"
v-else
:name="authorName"
:availability="userAvailability(author)"
container-classes="note-header-author-name gl-font-weight-bold"
/>
</a>
<span
v-if="authorStatus && !glFeatures.removeUserAttributes"
v-if="
authorStatus &&
!glFeatures.removeUserAttributesProjects &&
!glFeatures.removeUserAttributesGroups
"
ref="authorStatus"
v-safe-html:[$options.safeHtmlConfig]="authorStatus"
v-on="
authorStatusHasTooltip ? { mouseenter: removeEmojiTitle, mouseleave: addEmojiTitle } : {}
"
></span>
<span v-if="!glFeatures.removeUserAttributes" class="text-nowrap author-username">
<span
v-if="!glFeatures.removeUserAttributesProjects && !glFeatures.removeUserAttributesGroups"
class="text-nowrap author-username"
>
<a
ref="authorUsernameLink"
class="author-username-link"

View File

@ -65,7 +65,7 @@ export default {
fetchArtifacts() {
// refactor tracking based on action once this dropdown supports
// actions other than artifacts
this.track('click_artifacts_dropdown', { label: TRACKING_CATEGORIES.index });
this.track('click_artifacts_dropdown', { label: TRACKING_CATEGORIES.table });
this.isLoading = true;
// Replace the placeholder with the ID of the pipeline we are viewing

View File

@ -66,7 +66,7 @@ export default {
eventHub.$emit('retryPipeline', this.pipeline.retry_path);
},
trackClick(action) {
this.track(action, { label: TRACKING_CATEGORIES.index });
this.track(action, { label: TRACKING_CATEGORIES.table });
},
},
};

View File

@ -118,7 +118,7 @@ export default {
},
methods: {
trackClick(action) {
this.track(action, { label: TRACKING_CATEGORIES.index });
this.track(action, { label: TRACKING_CATEGORIES.table });
},
},
};

View File

@ -4,6 +4,7 @@ import { isEqual } from 'lodash';
import createFlash from '~/flash';
import { getParameterByName } from '~/lib/utils/url_utility';
import { __, s__ } from '~/locale';
import Tracking from '~/tracking';
import NavigationTabs from '~/vue_shared/components/navigation_tabs.vue';
import TablePagination from '~/vue_shared/components/pagination/table_pagination.vue';
import {
@ -11,6 +12,7 @@ import {
RAW_TEXT_WARNING,
FILTER_TAG_IDENTIFIER,
PipelineKeyOptions,
TRACKING_CATEGORIES,
} from '../../constants';
import PipelinesMixin from '../../mixins/pipelines_mixin';
import PipelinesService from '../../services/pipelines_service';
@ -35,7 +37,7 @@ export default {
PipelinesTableComponent,
TablePagination,
},
mixins: [PipelinesMixin],
mixins: [PipelinesMixin, Tracking.mixin()],
props: {
store: {
type: Object,
@ -246,6 +248,8 @@ export default {
params = this.onChangeWithFilter(params);
this.updateContent(params);
this.track('click_filter_tabs', { label: TRACKING_CATEGORIES.tabs });
},
successCallback(resp) {
// Because we are polling & the user is interacting verify if the response received

View File

@ -113,7 +113,7 @@ export default {
},
methods: {
onSubmit(filters) {
this.track('click_filtered_search', { label: TRACKING_CATEGORIES.index });
this.track('click_filtered_search', { label: TRACKING_CATEGORIES.search });
this.$emit('filterPipelines', filters);
},
},

View File

@ -77,7 +77,7 @@ export default {
return !action.playable;
},
trackClick() {
this.track('click_manual_actions', { label: TRACKING_CATEGORIES.index });
this.track('click_manual_actions', { label: TRACKING_CATEGORIES.table });
},
},
};

View File

@ -30,7 +30,7 @@ export default {
},
methods: {
trackClick() {
this.track('click_ci_status_badge', { label: TRACKING_CATEGORIES.index });
this.track('click_ci_status_badge', { label: TRACKING_CATEGORIES.table });
},
},
};

View File

@ -111,5 +111,7 @@ export const DEFAULT_FIELDS = [
];
export const TRACKING_CATEGORIES = {
index: 'pipelines_table_component',
table: 'pipelines_table_component',
tabs: 'pipelines_filter_tabs',
search: 'pipelines_filtered_search',
};

View File

@ -42,7 +42,7 @@ export default {
</script>
<template>
<div class="mr-widget-body media">
<div class="mr-widget-body media" v-on="$listeners">
<div v-if="isLoading" class="gl-w-full mr-conflict-loader">
<slot name="loading">
<div class="gl-display-flex">

View File

@ -11,7 +11,7 @@ class Projects::IncidentsController < Projects::ApplicationController
push_force_frontend_feature_flag(:work_items, @project&.work_items_feature_flag_enabled?)
push_force_frontend_feature_flag(:work_items_mvc_2, @project&.work_items_mvc_2_feature_flag_enabled?)
push_frontend_feature_flag(:work_items_hierarchy, @project)
push_frontend_feature_flag(:remove_user_attributes, @group)
push_frontend_feature_flag(:remove_user_attributes_projects, @project)
end
feature_category :incident_management

View File

@ -42,7 +42,7 @@ class Projects::IssuesController < Projects::ApplicationController
before_action do
push_frontend_feature_flag(:incident_timeline, project)
push_frontend_feature_flag(:remove_user_attributes, @group)
push_frontend_feature_flag(:remove_user_attributes_projects, project)
end
before_action only: [:index, :show] do

View File

@ -45,7 +45,7 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo
push_frontend_feature_flag(:paginated_mr_discussions, project)
push_frontend_feature_flag(:mr_review_submit_comment, project)
push_frontend_feature_flag(:mr_experience_survey, project)
push_frontend_feature_flag(:remove_user_attributes, @group)
push_frontend_feature_flag(:remove_user_attributes_projects, @project)
end
before_action do

View File

@ -6,6 +6,8 @@ module Mutations
class PromoteFromNote < Base
graphql_name 'TimelineEventPromoteFromNote'
include NotesHelper
argument :note_id, Types::GlobalIDType[::Note],
required: true,
description: 'Note ID from which the timeline event promoted.'
@ -20,7 +22,7 @@ module Mutations
incident,
current_user,
promoted_from_note: note,
note: note.note,
note: build_note_string(note),
occurred_at: note.created_at,
editable: true
).execute
@ -38,6 +40,11 @@ module Mutations
super
end
def build_note_string(note)
commented = _('commented')
"@#{note.author.username} [#{commented}](#{noteable_note_url(note)}): '#{note.note}'"
end
def raise_noteable_not_incident!
raise_resource_not_available_error! 'Note does not belong to an incident'
end

View File

@ -0,0 +1,20 @@
# frozen_string_literal: true
module Types
module BranchProtections
class BaseAccessLevelType < Types::BaseObject
authorize :read_protected_branch
field :access_level,
type: GraphQL::Types::Int,
null: false,
description: 'GitLab::Access level.'
field :access_level_description,
type: GraphQL::Types::String,
null: false,
description: 'Human readable representation for this access level.',
hash_key: 'humanize'
end
end
end

View File

@ -0,0 +1,11 @@
# frozen_string_literal: true
module Types
module BranchProtections
class MergeAccessLevelType < BaseAccessLevelType # rubocop:disable Graphql/AuthorizeTypes
graphql_name 'MergeAccessLevel'
description 'Represents the merge access level of a branch protection.'
accepts ::ProtectedBranch::MergeAccessLevel
end
end
end

View File

@ -8,6 +8,11 @@ module Types
accepts ::ProtectedBranch
authorize :read_protected_branch
field :merge_access_levels,
type: Types::BranchProtections::MergeAccessLevelType.connection_type,
null: true,
description: 'Details about who can merge when this branch is the source branch.'
field :allow_force_push,
type: GraphQL::Types::Boolean,
null: false,

View File

@ -0,0 +1,5 @@
# frozen_string_literal: true
class ProtectedBranchAccessPolicy < BasePolicy
delegate { @subject.protected_branch }
end

View File

@ -1,8 +1,8 @@
---
name: group_level_protected_environment_settings_permission
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/92801
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/369873
milestone: '15.3'
name: ci_project_pipeline_config_refactoring
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/97240
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/372867
milestone: '15.4'
type: development
group: group::release
default_enabled: true
group: group::pipeline authoring
default_enabled: false

View File

@ -1,6 +1,6 @@
---
name: remove_user_attributes
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/96447
name: remove_user_attributes_groups
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/97520
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/372047
milestone: '15.4'
type: development

View File

@ -0,0 +1,8 @@
---
name: remove_user_attributes_projects
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/97520
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/372047
milestone: '15.4'
type: development
group: group::code review
default_enabled: false

View File

@ -0,0 +1,8 @@
---
name: ci_partitioning_analyze_queries
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/97113
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/372840
milestone: '15.4'
type: ops
group: group::pipeline execution
default_enabled: false

View File

@ -55,8 +55,17 @@ const INCREMENTAL_COMPILER_RECORD_HISTORY = IS_DEV_SERVER && !process.env.CI;
const WEBPACK_REPORT = process.env.WEBPACK_REPORT && process.env.WEBPACK_REPORT !== 'false';
const WEBPACK_MEMORY_TEST =
process.env.WEBPACK_MEMORY_TEST && process.env.WEBPACK_MEMORY_TEST !== 'false';
const NO_COMPRESSION = process.env.NO_COMPRESSION && process.env.NO_COMPRESSION !== 'false';
const NO_SOURCEMAPS = process.env.NO_SOURCEMAPS && process.env.NO_SOURCEMAPS !== 'false';
let NO_COMPRESSION = process.env.NO_COMPRESSION && process.env.NO_COMPRESSION !== 'false';
let NO_SOURCEMAPS = process.env.NO_SOURCEMAPS && process.env.NO_SOURCEMAPS !== 'false';
let NO_HASHED_CHUNKS = process.env.NO_HASHED_CHUNKS && process.env.NO_HASHED_CHUNKS !== 'false';
if (WEBPACK_REPORT) {
console.log('Webpack report enabled. Running a "slim" production build.');
// For our webpack report we need no source maps, compression _or_ hashed file names.
NO_SOURCEMAPS = true;
NO_COMPRESSION = true;
NO_HASHED_CHUNKS = true;
}
const WEBPACK_OUTPUT_PATH = path.join(ROOT_PATH, 'public/assets/webpack');
const WEBPACK_PUBLIC_PATH = '/assets/webpack/';
@ -251,8 +260,10 @@ module.exports = {
output: {
path: WEBPACK_OUTPUT_PATH,
publicPath: WEBPACK_PUBLIC_PATH,
filename: IS_PRODUCTION ? '[name].[contenthash:8].bundle.js' : '[name].bundle.js',
chunkFilename: IS_PRODUCTION ? '[name].[contenthash:8].chunk.js' : '[name].chunk.js',
filename:
IS_PRODUCTION && !NO_HASHED_CHUNKS ? '[name].[contenthash:8].bundle.js' : '[name].bundle.js',
chunkFilename:
IS_PRODUCTION && !NO_HASHED_CHUNKS ? '[name].[contenthash:8].chunk.js' : '[name].chunk.js',
globalObject: 'this', // allow HMR and web workers to play nice
},
@ -687,6 +698,8 @@ module.exports = {
statsFilename: path.join(ROOT_PATH, 'webpack-report/stats.json'),
statsOptions: {
source: false,
errors: false,
warnings: false,
},
}),

View File

@ -1,38 +1,7 @@
# frozen_string_literal: true
# This file isn't named "Dangerfile" so that it's not imported by default since it's only meant to be run in the `bundle-size-review` job.
analysis_result = "./bundle-size-review/analysis.json"
markdown_result = "./bundle-size-review/comparison.md"
# Executing the webpack-entry-point-analyser
# We would like to do that in the CI file directly,
# but unfortunately the head_commit SHA is not available
# as a CI variable due to our merge into master simulation
analyze_cmd = [
"webpack-entry-point-analyser",
"--from-file ./webpack-report/stats.json",
"--json #{analysis_result}",
" --sha #{gitlab&.head_commit}"
].join(" ")
# execute analysis
`#{analyze_cmd}`
# We are executing the comparison by comparing the start_sha
# to the current pipeline result. The start_sha is the commit
# from master that was merged into for the merged pipeline.
comparison_cmd = [
"webpack-compare-reports",
"--job #{ENV["CI_JOB_ID"]}",
"--to-file #{analysis_result}",
"--html ./bundle-size-review/comparison.html",
"--markdown #{markdown_result}"
].join(" ")
# execute comparison
`#{comparison_cmd}`
comment = `cat #{markdown_result}`
comment = `cat ./bundle-size-review/comparison.md`
unless comment.strip.empty?
markdown(<<~MARKDOWN)

View File

@ -31,9 +31,8 @@ by removing the ~database label and re-running the [`danger-review` job](#{ENV['
MSG
DB_MIGRATION_TESTING_REQUIRED_MESSAGE = <<~MSG
1. If this is not a ~"Community contribution" or from a Fork, kick off the
`db:gitlabcom-database-testing` manual job.
1. Kick off the `db:gitlabcom-database-testing` manual job. This job can also be used before
requesting review to test your migrations against production data.
MSG
DATABASE_APPROVED_LABEL = 'database::approved'

View File

@ -105,3 +105,6 @@ parameter:
| `deployment_frequency` | The number of successful deployments during the time period. |
| `lead_time_for_changes` | The median number of seconds between the merge of the merge request (MR) and the deployment of the MR's commits for all MRs deployed during the time period. |
| `time_to_restore_service` | The median number of seconds an incident was open during the time period. Available only for production environment. |
NOTE:
The API returns the `monthly` and `all` intervals by calculating the median of the daily median values. This can introduce a slight inaccuracy in the returned data.

View File

@ -7837,6 +7837,29 @@ The edge type for [`MemberInterface`](#memberinterface).
| <a id="memberinterfaceedgecursor"></a>`cursor` | [`String!`](#string) | A cursor for use in pagination. |
| <a id="memberinterfaceedgenode"></a>`node` | [`MemberInterface`](#memberinterface) | The item at the end of the edge. |
#### `MergeAccessLevelConnection`
The connection type for [`MergeAccessLevel`](#mergeaccesslevel).
##### Fields
| Name | Type | Description |
| ---- | ---- | ----------- |
| <a id="mergeaccesslevelconnectionedges"></a>`edges` | [`[MergeAccessLevelEdge]`](#mergeaccessleveledge) | A list of edges. |
| <a id="mergeaccesslevelconnectionnodes"></a>`nodes` | [`[MergeAccessLevel]`](#mergeaccesslevel) | A list of nodes. |
| <a id="mergeaccesslevelconnectionpageinfo"></a>`pageInfo` | [`PageInfo!`](#pageinfo) | Information to aid in pagination. |
#### `MergeAccessLevelEdge`
The edge type for [`MergeAccessLevel`](#mergeaccesslevel).
##### Fields
| Name | Type | Description |
| ---- | ---- | ----------- |
| <a id="mergeaccessleveledgecursor"></a>`cursor` | [`String!`](#string) | A cursor for use in pagination. |
| <a id="mergeaccessleveledgenode"></a>`node` | [`MergeAccessLevel`](#mergeaccesslevel) | The item at the end of the edge. |
#### `MergeRequestAssigneeConnection`
The connection type for [`MergeRequestAssignee`](#mergerequestassignee).
@ -10083,6 +10106,7 @@ Branch protection details for a branch rule.
| Name | Type | Description |
| ---- | ---- | ----------- |
| <a id="branchprotectionallowforcepush"></a>`allowForcePush` | [`Boolean!`](#boolean) | Toggle force push to the branch for users with write access. |
| <a id="branchprotectionmergeaccesslevels"></a>`mergeAccessLevels` | [`MergeAccessLevelConnection`](#mergeaccesslevelconnection) | Details about who can merge when this branch is the source branch. (see [Connections](#connections)) |
### `BranchRule`
@ -13783,6 +13807,17 @@ Maven metadata.
| <a id="mavenmetadatapath"></a>`path` | [`String!`](#string) | Path of the Maven package. |
| <a id="mavenmetadataupdatedat"></a>`updatedAt` | [`Time!`](#time) | Date of most recent update. |
### `MergeAccessLevel`
Represents the merge access level of a branch protection.
#### Fields
| Name | Type | Description |
| ---- | ---- | ----------- |
| <a id="mergeaccesslevelaccesslevel"></a>`accessLevel` | [`Int!`](#int) | GitLab::Access level. |
| <a id="mergeaccesslevelaccessleveldescription"></a>`accessLevelDescription` | [`String!`](#string) | Human readable representation for this access level. |
### `MergeRequest`
#### Fields

View File

@ -156,6 +156,12 @@ the time limit to resolve all files is 30 seconds.
- You can override included configuration by having the same job name or global keyword
in the `.gitlab-ci.yml` file. The two configurations are merged together, and the
configuration in the `.gitlab-ci.yml` file takes precedence over the included configuration.
- If you rerun a:
- Job, the `include` files are not fetched again. All jobs in a pipeline use the configuration
fetched when the pipeline was created. Any changes to the source `include` files
do not affect job reruns.
- Pipeline, the `include` files are fetched again. If they changed after the last
pipeline run, the new pipeline uses the changed configuration.
**Related topics**:

View File

@ -113,16 +113,23 @@ To view the lead time for changes for merge requests in your project:
The **Lead Time for Changes** metrics display below the **Filter results** text box.
## View number of successful deployments **(PREMIUM)**
## View number of successful deployments **(FREE)**
To view deployment metrics, you must have a
Prerequisites:
- To view deployment metrics, you must have a
[production environment configured](../../ci/environments/index.md#deployment-tier-of-environments).
Value stream analytics shows the following deployment metrics for your project:
Value stream analytics shows the following deployment metrics for your project within the specified date range:
- Deploys: The number of successful deployments in the date range.
- Deployment Frequency: The average number of successful deployments per day in the date range.
If you have a GitLab Premium or Ultimate subscription:
- The number of successful deployments is calculated with DORA data.
- The data is filtered based on environment and environment tier.
To view deployment metrics for your project:
1. On the top bar, select **Menu > Projects** and find your project.

View File

@ -7,6 +7,7 @@ module Gitlab
module Config
class Content < Chain::Base
include Chain::Helpers
include ::Gitlab::Utils::StrongMemoize
SOURCES = [
Gitlab::Ci::Pipeline::Chain::Config::Content::Parameter,
@ -18,10 +19,10 @@ module Gitlab
].freeze
def perform!
if config = find_config
@pipeline.build_pipeline_config(content: config.content)
@command.config_content = config.content
@pipeline.config_source = config.source
if pipeline_config&.exists?
@pipeline.build_pipeline_config(content: pipeline_config.content)
@command.config_content = pipeline_config.content
@pipeline.config_source = pipeline_config.source
else
error('Missing CI config file')
end
@ -33,7 +34,19 @@ module Gitlab
private
def find_config
def pipeline_config
strong_memoize(:pipeline_config) do
next legacy_find_config if ::Feature.disabled?(:ci_project_pipeline_config_refactoring, project)
::Gitlab::Ci::ProjectConfig.new(
project: project, sha: @pipeline.sha,
custom_content: @command.content,
pipeline_source: @command.source, pipeline_source_bridge: @command.bridge
)
end
end
def legacy_find_config
sources.each do |source|
config = source.new(@pipeline, @command)
return config if config.exists?

View File

@ -6,6 +6,7 @@ module Gitlab
module Chain
module Config
class Content
# When removing ci_project_pipeline_config_refactoring, this and its subclasses will be removed.
class Source
include Gitlab::Utils::StrongMemoize

View File

@ -0,0 +1,52 @@
# frozen_string_literal: true
module Gitlab
module Ci
# Locates project CI config
class ProjectConfig
# The order of sources is important:
# - EE uses Compliance first since it must be used first if compliance templates are enabled.
# (see ee/lib/ee/gitlab/ci/project_config.rb)
# - Parameter is used by on-demand security scanning which passes the actual CI YAML to use as argument.
# - Bridge is used for downstream pipelines since the config is defined in the bridge job. If lower in priority,
# it would evaluate the project's YAML file instead.
# - Repository / ExternalProject / Remote: their order is not important between each other.
# - AutoDevops is used as default option if nothing else is found and if AutoDevops is enabled.
SOURCES = [
ProjectConfig::Parameter,
ProjectConfig::Bridge,
ProjectConfig::Repository,
ProjectConfig::ExternalProject,
ProjectConfig::Remote,
ProjectConfig::AutoDevops
].freeze
def initialize(project:, sha:, custom_content: nil, pipeline_source: nil, pipeline_source_bridge: nil)
@config = find_config(project, sha, custom_content, pipeline_source, pipeline_source_bridge)
end
delegate :content, :source, to: :@config, allow_nil: true
def exists?
!!@config&.exists?
end
private
def find_config(project, sha, custom_content, pipeline_source, pipeline_source_bridge)
sources.each do |source|
config = source.new(project, sha, custom_content, pipeline_source, pipeline_source_bridge)
return config if config.exists?
end
nil
end
def sources
SOURCES
end
end
end
end
Gitlab::Ci::ProjectConfig.prepend_mod_with('Gitlab::Ci::ProjectConfig')

View File

@ -0,0 +1,28 @@
# frozen_string_literal: true
module Gitlab
module Ci
class ProjectConfig
class AutoDevops < Source
def content
strong_memoize(:content) do
next unless project&.auto_devops_enabled?
template = Gitlab::Template::GitlabCiYmlTemplate.find(template_name)
YAML.dump('include' => [{ 'template' => template.full_name }])
end
end
def source
:auto_devops_source
end
private
def template_name
'Auto-DevOps'
end
end
end
end
end

View File

@ -0,0 +1,19 @@
# frozen_string_literal: true
module Gitlab
module Ci
class ProjectConfig
class Bridge < Source
def content
return unless pipeline_source_bridge
pipeline_source_bridge.yaml_for_downstream
end
def source
:bridge_source
end
end
end
end
end

View File

@ -0,0 +1,45 @@
# frozen_string_literal: true
module Gitlab
module Ci
class ProjectConfig
class ExternalProject < Source
def content
strong_memoize(:content) do
next unless external_project_path?
path_file, path_project, ref = extract_location_tokens
config_location = { 'project' => path_project, 'file' => path_file }
config_location['ref'] = ref if ref.present?
YAML.dump('include' => [config_location])
end
end
def source
:external_project_source
end
private
# Example: path/to/.gitlab-ci.yml@another-group/another-project
def external_project_path?
ci_config_path =~ /\A.+(yml|yaml)@.+\z/
end
# Example: path/to/.gitlab-ci.yml@another-group/another-project:refname
def extract_location_tokens
path_file, path_project = ci_config_path.split('@', 2)
if path_project.include? ":"
project, ref = path_project.split(':', 2)
[path_file, project, ref]
else
[path_file, path_project]
end
end
end
end
end
end

View File

@ -0,0 +1,21 @@
# frozen_string_literal: true
module Gitlab
module Ci
class ProjectConfig
class Parameter < Source
def content
strong_memoize(:content) do
next unless custom_content.present?
custom_content
end
end
def source
:parameter_source
end
end
end
end
end

View File

@ -0,0 +1,21 @@
# frozen_string_literal: true
module Gitlab
module Ci
class ProjectConfig
class Remote < Source
def content
strong_memoize(:content) do
next unless ci_config_path =~ URI::DEFAULT_PARSER.make_regexp(%w[http https])
YAML.dump('include' => [{ 'remote' => ci_config_path }])
end
end
def source
:remote_source
end
end
end
end
end

View File

@ -0,0 +1,32 @@
# frozen_string_literal: true
module Gitlab
module Ci
class ProjectConfig
class Repository < Source
def content
strong_memoize(:content) do
next unless file_in_repository?
YAML.dump('include' => [{ 'local' => ci_config_path }])
end
end
def source
:repository_source
end
private
def file_in_repository?
return unless project
return unless sha
project.repository.gitlab_ci_yml_for(sha, ci_config_path).present?
rescue GRPC::NotFound, GRPC::Internal
nil
end
end
end
end
end

View File

@ -0,0 +1,41 @@
# frozen_string_literal: true
module Gitlab
module Ci
class ProjectConfig
class Source
include Gitlab::Utils::StrongMemoize
def initialize(project, sha, custom_content, pipeline_source, pipeline_source_bridge)
@project = project
@sha = sha
@custom_content = custom_content
@pipeline_source = pipeline_source
@pipeline_source_bridge = pipeline_source_bridge
end
def exists?
strong_memoize(:exists) do
content.present?
end
end
def content
raise NotImplementedError
end
def source
raise NotImplementedError
end
private
attr_reader :project, :sha, :custom_content, :pipeline_source, :pipeline_source_bridge
def ci_config_path
@ci_config_path ||= project.ci_config_path_or_default
end
end
end
end
end

View File

@ -0,0 +1,41 @@
# frozen_string_literal: true
module Gitlab
module Database
module QueryAnalyzers
module Ci
# The purpose of this analyzer is to detect queries not going through a partitioning routing table
class PartitioningAnalyzer < Database::QueryAnalyzers::Base
RoutingTableNotUsedError = Class.new(QueryAnalyzerError)
ENABLED_TABLES = %w[
ci_builds_metadata
].freeze
class << self
def enabled?
::Feature::FlipperFeature.table_exists? &&
::Feature.enabled?(:ci_partitioning_analyze_queries, type: :ops)
end
def analyze(parsed)
analyze_legacy_tables_usage(parsed)
end
private
def analyze_legacy_tables_usage(parsed)
detected = ENABLED_TABLES & (parsed.pg.dml_tables + parsed.pg.select_tables)
return if detected.none?
::Gitlab::ErrorTracking.track_and_raise_for_dev_exception(
RoutingTableNotUsedError.new("Detected non-partitioned table use #{detected.inspect}: #{parsed.sql}")
)
end
end
end
end
end
end
end

View File

@ -44,30 +44,30 @@ module Gitlab
TRANSLATION_LEVELS = {
'bg' => 0,
'cs_CZ' => 0,
'da_DK' => 39,
'da_DK' => 38,
'de' => 17,
'en' => 100,
'eo' => 0,
'es' => 38,
'es' => 37,
'fil_PH' => 0,
'fr' => 11,
'gl_ES' => 0,
'id_ID' => 0,
'it' => 1,
'ja' => 32,
'ko' => 12,
'ja' => 31,
'ko' => 17,
'nb_NO' => 26,
'nl_NL' => 0,
'pl_PL' => 4,
'pt_BR' => 55,
'ro_RO' => 100,
'pt_BR' => 56,
'ro_RO' => 99,
'ru' => 27,
'si_LK' => 10,
'tr_TR' => 12,
'tr_TR' => 11,
'uk' => 50,
'zh_CN' => 99,
'zh_CN' => 97,
'zh_HK' => 1,
'zh_TW' => 100
'zh_TW' => 99
}.freeze
private_constant :TRANSLATION_LEVELS

View File

@ -40,6 +40,7 @@ module Tasks
asset_files
end
private_class_method :assets_impacting_webpack_compilation
end
end
@ -84,9 +85,17 @@ namespace :gitlab do
if head_assets_sha256 != master_assets_sha256 || !public_assets_webpack_dir_exists
FileUtils.rm_r(Tasks::Gitlab::Assets::PUBLIC_ASSETS_WEBPACK_DIR) if public_assets_webpack_dir_exists
unless system('yarn webpack')
log_path = ENV['WEBPACK_COMPILE_LOG_PATH']
cmd = 'yarn webpack'
cmd += " > #{log_path}" if log_path
unless system(cmd)
abort 'Error: Unable to compile webpack production bundle.'.color(:red)
end
puts "Written webpack stdout log to #{log_path}" if log_path
puts "You can inspect the webpack log here: #{ENV['CI_JOB_URL']}/artifacts/file/#{log_path}" if log_path && ENV['CI_JOB_URL']
end
end

View File

@ -46564,6 +46564,9 @@ msgstr ""
msgid "comment"
msgstr ""
msgid "commented"
msgstr ""
msgid "commented on %{link_to_project}"
msgstr ""

View File

@ -1,7 +1,10 @@
# frozen_string_literal: true
module QA
RSpec.describe 'Create', :reliable do
RSpec.describe 'Create', :reliable, quarantine: {
type: :investigating,
issue: 'https://gitlab.com/gitlab-org/gitlab/-/issues/373093'
} do
context 'Content Editor' do
let(:initial_wiki) { Resource::Wiki::ProjectPage.fabricate_via_api! }
let(:page_title) { 'Content Editor Page' }

68
scripts/bundle_size_review Executable file
View File

@ -0,0 +1,68 @@
#!/usr/bin/env bash
set -euo pipefail
IFS=$'\n\t'
#
# # How does this work in general?
#
# 1. We run webpack in a production like mode and enable the BundleAnalyzerPlugin
# 2. The Plugin builds a index.html for human consumption _and_ a stats.json
# 3. This script creates a smaller analysis.json from the gargantuan stats.json
# 4. In Merge Requests:
# - compare that smaller to analysis.json to the one from the base commit on master
# - report the comparison results via danger
source scripts/utils.sh
# For now we only want bundle-size-review to run in CI
# Maybe we could create a "local mode"
if [[ -z "${CI:-}" ]]; then
echo 'Not running in a CI context, skipping bundle analysis'
exit "0"
fi
# Get the _current_ commit sha
if [[ -z "${CI_MERGE_REQUEST_IID:-}" ]]; then
echo 'Not in a merge request, setting COMMIT_SHA to $CI_COMMIT_SHA'
COMMIT_SHA="${CI_COMMIT_SHA}"
else
echo 'In a merge request, setting COMMIT_SHA to $CI_MERGE_REQUEST_SOURCE_BRANCH_SHA'
COMMIT_SHA="${CI_MERGE_REQUEST_SOURCE_BRANCH_SHA}"
fi
# Create output directory
mkdir -p bundle-size-review
# Running webpack
export WEBPACK_REPORT="true"
run_timed_command "yarn run webpack-prod > bundle-size-review/webpack-output.log"
# Copy results from stats plugin
cp webpack-report/index.html bundle-size-review/bundle-report.html
# Run comparison in danger
if [[ -z "${DANGER_GITLAB_API_TOKEN:-}" ]]; then
echo 'No Danger token available, skipping bundle analysis'
exit "0"
fi
# TODO: Make this a dependency of GitLab itself after a proper release
yarn global add https://gitlab.com/gitlab-org/frontend/playground/webpack-memory-metrics.git
# Create smaller analysis.json
run_timed_command "webpack-entry-point-analyser --from-file ./webpack-report/stats.json --json ./bundle-size-review/analysis.json --sha ${COMMIT_SHA}"
rm -rf webpack-report
if [[ -z "${CI_MERGE_REQUEST_IID:-}" ]]; then
echo 'Not in a merge request, skipping comparison'
exit "0"
fi
# Run comparison
run_timed_command "webpack-compare-reports --job ${CI_JOB_ID} --to-file ./bundle-size-review/analysis.json --html ./bundle-size-review/comparison.html --markdown ./bundle-size-review/comparison.md"
# Execute danger
danger_id=$(echo -n "${DANGER_GITLAB_API_TOKEN}" | md5sum | awk '{print $1}' | cut -c5-10)
run_timed_command "danger --dangerfile=danger/Dangerfile-bundle_size --fail-on-errors=true --verbose --danger_id=bundle-size-review-${danger_id}"
exit "0"

View File

@ -17,6 +17,16 @@ FactoryBot.define do
ProtectedBranches::CacheService.new(protected_branch.project).refresh
end
after(:build) do |protected_branch, evaluator|
if evaluator.default_access_level && evaluator.default_push_level
protected_branch.push_access_levels.new(access_level: Gitlab::Access::MAINTAINER)
end
if evaluator.default_access_level && evaluator.default_merge_level
protected_branch.merge_access_levels.new(access_level: Gitlab::Access::MAINTAINER)
end
end
trait :create_branch_on_repository do
association :project, factory: [:project, :repository]
@ -31,6 +41,26 @@ FactoryBot.define do
end
end
trait :maintainers_can_push do
transient do
default_push_level { false }
end
after(:build) do |protected_branch|
protected_branch.push_access_levels.new(access_level: Gitlab::Access::MAINTAINER)
end
end
trait :maintainers_can_merge do
transient do
default_push_level { false }
end
after(:build) do |protected_branch|
protected_branch.push_access_levels.new(access_level: Gitlab::Access::MAINTAINER)
end
end
trait :developers_can_push do
transient do
default_push_level { false }
@ -61,29 +91,13 @@ FactoryBot.define do
end
end
trait :maintainers_can_push do
trait :no_one_can_merge do
transient do
default_push_level { false }
default_merge_level { false }
end
after(:build) do |protected_branch|
protected_branch.push_access_levels.new(access_level: Gitlab::Access::MAINTAINER)
end
end
after(:build) do |protected_branch, evaluator|
if evaluator.default_access_level && evaluator.default_push_level
protected_branch.push_access_levels.new(access_level: Gitlab::Access::MAINTAINER)
end
if evaluator.default_access_level && evaluator.default_merge_level
protected_branch.merge_access_levels.new(access_level: Gitlab::Access::MAINTAINER)
end
end
trait :no_one_can_merge do
after(:create) do |protected_branch|
protected_branch.merge_access_levels.first.update!(access_level: Gitlab::Access::NO_ACCESS)
protected_branch.merge_access_levels.new(access_level: Gitlab::Access::NO_ACCESS)
end
end
end

View File

@ -8,7 +8,7 @@ RSpec.describe 'Thread Comments Issue', :js do
let(:issue) { create(:issue, project: project) }
before do
stub_feature_flags(remove_user_attributes: false)
stub_feature_flags(remove_user_attributes_projects: false)
project.add_maintainer(user)
sign_in(user)

View File

@ -8,7 +8,7 @@ RSpec.describe 'Thread Comments Merge Request', :js do
let(:merge_request) { create(:merge_request, source_project: project) }
before do
stub_feature_flags(remove_user_attributes: false)
stub_feature_flags(remove_user_attributes_projects: false)
project.add_maintainer(user)
sign_in(user)

View File

@ -15,7 +15,7 @@ RSpec.describe "Jira", :js do
before do
remotelink = double(:remotelink, all: [], build: double(save!: true))
stub_feature_flags(remove_user_attributes: false)
stub_feature_flags(remove_user_attributes_projects: false)
stub_request(:get, "https://jira.example.com/rest/api/2/issue/JIRA-5")
stub_request(:post, "https://jira.example.com/rest/api/2/issue/JIRA-5/comment")

View File

@ -14,7 +14,7 @@ RSpec.describe 'List issue resource label events', :js do
let!(:event) { create(:resource_label_event, user: user, issue: issue, label: label) }
before do
stub_feature_flags(remove_user_attributes: false)
stub_feature_flags(remove_user_attributes_projects: false)
visit project_issue_path(project, issue)
wait_for_requests
end

View File

@ -14,7 +14,7 @@ RSpec.describe 'User comments on a diff', :js do
let(:user) { create(:user) }
before do
stub_feature_flags(remove_user_attributes: false)
stub_feature_flags(remove_user_attributes_projects: false)
project.add_maintainer(user)
sign_in(user)

View File

@ -10,7 +10,7 @@ RSpec.describe 'User comments on a merge request', :js do
let(:user) { create(:user) }
before do
stub_feature_flags(remove_user_attributes: false)
stub_feature_flags(remove_user_attributes_projects: false)
project.add_maintainer(user)
sign_in(user)

View File

@ -9,7 +9,7 @@ RSpec.describe 'Project > Merge request > View user status' do
end
before do
stub_feature_flags(remove_user_attributes: false)
stub_feature_flags(remove_user_attributes_projects: false)
end
subject { visit merge_request_path(merge_request) }

View File

@ -8,7 +8,7 @@ RSpec.describe 'User edit profile' do
let(:user) { create(:user) }
before do
stub_feature_flags(remove_user_attributes: false)
stub_feature_flags(remove_user_attributes_projects: false)
sign_in(user)
visit(profile_path)
end

View File

@ -49,7 +49,8 @@ describe('NoteHeader component', () => {
stubs: { GlSprintf, UserNameWithStatus },
provide: {
glFeatures: {
removeUserAttributes: userAttributes,
removeUserAttributesProjects: userAttributes,
removeUserAttributesGroups: userAttributes,
},
},
});
@ -60,7 +61,7 @@ describe('NoteHeader component', () => {
wrapper = null;
});
describe('when removeUserAttributes feature flag is enabled', () => {
describe('when removeUserAttributesProjects feature flag is enabled', () => {
it('does not render busy status', () => {
createComponent({ author: { ...author, availability: AVAILABILITY_STATUS.BUSY } }, true);

View File

@ -79,7 +79,7 @@ describe('ImportHistoryApp', () => {
describe('general behavior', () => {
it('renders loading state when loading', () => {
createComponent();
expect(wrapper.find(GlLoadingIcon).exists()).toBe(true);
expect(wrapper.findComponent(GlLoadingIcon).exists()).toBe(true);
});
it('renders empty state when no data is available', async () => {
@ -87,8 +87,8 @@ describe('ImportHistoryApp', () => {
createComponent();
await axios.waitForAll();
expect(wrapper.find(GlLoadingIcon).exists()).toBe(false);
expect(wrapper.find(GlEmptyState).exists()).toBe(true);
expect(wrapper.findComponent(GlLoadingIcon).exists()).toBe(false);
expect(wrapper.findComponent(GlEmptyState).exists()).toBe(true);
});
it('renders table with data when history is available', async () => {
@ -96,7 +96,7 @@ describe('ImportHistoryApp', () => {
createComponent();
await axios.waitForAll();
const table = wrapper.find(GlTable);
const table = wrapper.findComponent(GlTable);
expect(table.exists()).toBe(true);
expect(table.props().items).toStrictEqual(DUMMY_RESPONSE);
});
@ -127,7 +127,7 @@ describe('ImportHistoryApp', () => {
expect(mock.history.get.length).toBe(1);
expect(mock.history.get[0].params).toStrictEqual(expect.objectContaining({ page: NEW_PAGE }));
expect(wrapper.find(GlTable).props().items).toStrictEqual(FAKE_NEXT_PAGE_REPLY);
expect(wrapper.findComponent(GlTable).props().items).toStrictEqual(FAKE_NEXT_PAGE_REPLY);
});
});

View File

@ -362,7 +362,7 @@ describe('ForkForm component', () => {
const submitForm = async () => {
fillForm();
await nextTick();
const form = wrapper.find(GlForm);
const form = wrapper.findComponent(GlForm);
await form.trigger('submit');
await nextTick();

View File

@ -20,8 +20,8 @@ describe('Code Coverage', () => {
const graphRef = 'master';
const graphCsvPath = 'url/';
const findAlert = () => wrapper.find(GlAlert);
const findAreaChart = () => wrapper.find(GlAreaChart);
const findAlert = () => wrapper.findComponent(GlAlert);
const findAreaChart = () => wrapper.findComponent(GlAreaChart);
const findAllDropdownItems = () => wrapper.findAll(GlDropdownItem);
const findFirstDropdownItem = () => findAllDropdownItems().at(0);
const findSecondDropdownItem = () => findAllDropdownItems().at(1);
@ -142,7 +142,7 @@ describe('Code Coverage', () => {
});
it('renders the dropdown with all custom names as options', () => {
expect(wrapper.find(GlDropdown).exists()).toBeDefined();
expect(wrapper.findComponent(GlDropdown).exists()).toBeDefined();
expect(findAllDropdownItems()).toHaveLength(codeCoverageMockData.length);
expect(findFirstDropdownItem().text()).toBe(codeCoverageMockData[0].group_name);
});

View File

@ -21,7 +21,7 @@ describe('Pipeline Schedule Callout', () => {
};
const findInnerContentOfCallout = () => wrapper.find('[data-testid="innerContent"]');
const findDismissCalloutBtn = () => wrapper.find(GlButton);
const findDismissCalloutBtn = () => wrapper.findComponent(GlButton);
describe(`when ${cookieKey} cookie is set`, () => {
beforeEach(async () => {

View File

@ -81,15 +81,17 @@ describe('Settings Panel', () => {
});
};
const findLFSSettingsRow = () => wrapper.find({ ref: 'git-lfs-settings' });
const findLFSSettingsRow = () => wrapper.findComponent({ ref: 'git-lfs-settings' });
const findLFSSettingsMessage = () => findLFSSettingsRow().find('p');
const findLFSFeatureToggle = () => findLFSSettingsRow().find(GlToggle);
const findRepositoryFeatureProjectRow = () => wrapper.find({ ref: 'repository-settings' });
const findLFSFeatureToggle = () => findLFSSettingsRow().findComponent(GlToggle);
const findRepositoryFeatureProjectRow = () =>
wrapper.findComponent({ ref: 'repository-settings' });
const findRepositoryFeatureSetting = () =>
findRepositoryFeatureProjectRow().find(ProjectFeatureSetting);
const findProjectVisibilitySettings = () => wrapper.find({ ref: 'project-visibility-settings' });
const findIssuesSettingsRow = () => wrapper.find({ ref: 'issues-settings' });
const findAnalyticsRow = () => wrapper.find({ ref: 'analytics-settings' });
findRepositoryFeatureProjectRow().findComponent(ProjectFeatureSetting);
const findProjectVisibilitySettings = () =>
wrapper.findComponent({ ref: 'project-visibility-settings' });
const findIssuesSettingsRow = () => wrapper.findComponent({ ref: 'issues-settings' });
const findAnalyticsRow = () => wrapper.findComponent({ ref: 'analytics-settings' });
const findProjectVisibilityLevelInput = () => wrapper.find('[name="project[visibility_level]"]');
const findRequestAccessEnabledInput = () =>
wrapper.find('[name="project[request_access_enabled]"]');
@ -99,31 +101,33 @@ describe('Settings Panel', () => {
wrapper.find('[name="project[project_feature_attributes][forking_access_level]"]');
const findBuildsAccessLevelInput = () =>
wrapper.find('[name="project[project_feature_attributes][builds_access_level]"]');
const findContainerRegistrySettings = () => wrapper.find({ ref: 'container-registry-settings' });
const findContainerRegistrySettings = () =>
wrapper.findComponent({ ref: 'container-registry-settings' });
const findContainerRegistryPublicNoteGlSprintfComponent = () =>
findContainerRegistrySettings().findComponent(GlSprintf);
const findContainerRegistryAccessLevelInput = () =>
wrapper.find('[name="project[project_feature_attributes][container_registry_access_level]"]');
const findPackageSettings = () => wrapper.find({ ref: 'package-settings' });
const findPackageSettings = () => wrapper.findComponent({ ref: 'package-settings' });
const findPackageAccessLevel = () =>
wrapper.find('[data-testid="package-registry-access-level"]');
const findPackageAccessLevels = () =>
wrapper.find('[name="project[project_feature_attributes][package_registry_access_level]"]');
const findPackagesEnabledInput = () => wrapper.find('[name="project[packages_enabled]"]');
const findPagesSettings = () => wrapper.find({ ref: 'pages-settings' });
const findPagesSettings = () => wrapper.findComponent({ ref: 'pages-settings' });
const findPagesAccessLevels = () =>
wrapper.find('[name="project[project_feature_attributes][pages_access_level]"]');
const findEmailSettings = () => wrapper.find({ ref: 'email-settings' });
const findEmailSettings = () => wrapper.findComponent({ ref: 'email-settings' });
const findShowDefaultAwardEmojis = () =>
wrapper.find('input[name="project[project_setting_attributes][show_default_award_emojis]"]');
const findWarnAboutPuc = () =>
wrapper.find(
'input[name="project[project_setting_attributes][warn_about_potentially_unwanted_characters]"]',
);
const findMetricsVisibilitySettings = () => wrapper.find({ ref: 'metrics-visibility-settings' });
const findMetricsVisibilitySettings = () =>
wrapper.findComponent({ ref: 'metrics-visibility-settings' });
const findMetricsVisibilityInput = () =>
findMetricsVisibilitySettings().findComponent(ProjectFeatureSetting);
const findOperationsSettings = () => wrapper.find({ ref: 'operations-settings' });
const findOperationsSettings = () => wrapper.findComponent({ ref: 'operations-settings' });
const findOperationsVisibilityInput = () =>
findOperationsSettings().findComponent(ProjectFeatureSetting);
const findConfirmDangerButton = () => wrapper.findComponent(ConfirmDanger);

View File

@ -39,7 +39,7 @@ describe('WikiForm', () => {
const findMarkdownHelpLink = () => wrapper.findByTestId('wiki-markdown-help-link');
const findContentEditor = () => wrapper.findComponent(ContentEditor);
const findClassicEditor = () => wrapper.findComponent(MarkdownField);
const findLocalStorageSync = () => wrapper.find(LocalStorageSync);
const findLocalStorageSync = () => wrapper.findComponent(LocalStorageSync);
const setFormat = (value) => {
const format = findFormat();

View File

@ -257,7 +257,7 @@ describe('detailedMetric', () => {
});
it('displays request warnings', () => {
expect(wrapper.find(RequestWarning).exists()).toBe(true);
expect(wrapper.findComponent(RequestWarning).exists()).toBe(true);
});
it('can open and close traces', async () => {

View File

@ -24,11 +24,11 @@ describe('CI Lint Results', () => {
});
};
const findTable = () => wrapper.find(GlTableLite);
const findTable = () => wrapper.findComponent(GlTableLite);
const findByTestId = (selector) => () => wrapper.find(`[data-testid="ci-lint-${selector}"]`);
const findAllByTestId = (selector) => () =>
wrapper.findAll(`[data-testid="ci-lint-${selector}"]`);
const findLinkToDoc = () => wrapper.find(GlLink);
const findLinkToDoc = () => wrapper.findComponent(GlLink);
const findErrors = findByTestId('errors');
const findWarnings = findByTestId('warnings');
const findStatus = findByTestId('status');

View File

@ -17,9 +17,9 @@ describe('CI lint warnings', () => {
});
};
const findWarningAlert = () => wrapper.find(GlAlert);
const findWarningAlert = () => wrapper.findComponent(GlAlert);
const findWarnings = () => wrapper.findAll('[data-testid="ci-lint-warning"]');
const findWarningMessage = () => trimText(wrapper.find(GlSprintf).text());
const findWarningMessage = () => trimText(wrapper.findComponent(GlSprintf).text());
afterEach(() => {
wrapper.destroy();

View File

@ -254,7 +254,7 @@ describe('Pipeline editor home wrapper', () => {
expect(findPipelineEditorDrawer().props('isVisible')).toBe(true);
findPipelineEditorDrawer().find(GlDrawer).vm.$emit('close');
findPipelineEditorDrawer().findComponent(GlDrawer).vm.$emit('close');
await nextTick();
expect(findPipelineEditorDrawer().props('isVisible')).toBe(false);

View File

@ -34,7 +34,7 @@ describe('Pipeline New Form', () => {
let mock;
let dummySubmitEvent;
const findForm = () => wrapper.find(GlForm);
const findForm = () => wrapper.findComponent(GlForm);
const findRefsDropdown = () => wrapper.findComponent(RefsDropdown);
const findSubmitButton = () => wrapper.find('[data-testid="run_pipeline_button"]');
const findVariableRows = () => wrapper.findAll('[data-testid="ci-variable-row"]');
@ -44,9 +44,9 @@ describe('Pipeline New Form', () => {
const findValueInputs = () => wrapper.findAll('[data-testid="pipeline-form-ci-variable-value"]');
const findErrorAlert = () => wrapper.find('[data-testid="run-pipeline-error-alert"]');
const findWarningAlert = () => wrapper.find('[data-testid="run-pipeline-warning-alert"]');
const findWarningAlertSummary = () => findWarningAlert().find(GlSprintf);
const findWarningAlertSummary = () => findWarningAlert().findComponent(GlSprintf);
const findWarnings = () => wrapper.findAll('[data-testid="run-pipeline-warning"]');
const findLoadingIcon = () => wrapper.find(GlLoadingIcon);
const findLoadingIcon = () => wrapper.findComponent(GlLoadingIcon);
const findCCAlert = () => wrapper.findComponent(CreditCardValidationRequiredAlert);
const getFormPostParams = () => JSON.parse(mock.history.post[0].data);

View File

@ -34,7 +34,7 @@ describe('Pipeline New Form', () => {
let mock;
let dummySubmitEvent;
const findForm = () => wrapper.find(GlForm);
const findForm = () => wrapper.findComponent(GlForm);
const findRefsDropdown = () => wrapper.findComponent(RefsDropdown);
const findSubmitButton = () => wrapper.find('[data-testid="run_pipeline_button"]');
const findVariableRows = () => wrapper.findAll('[data-testid="ci-variable-row"]');
@ -44,9 +44,9 @@ describe('Pipeline New Form', () => {
const findValueInputs = () => wrapper.findAll('[data-testid="pipeline-form-ci-variable-value"]');
const findErrorAlert = () => wrapper.find('[data-testid="run-pipeline-error-alert"]');
const findWarningAlert = () => wrapper.find('[data-testid="run-pipeline-warning-alert"]');
const findWarningAlertSummary = () => findWarningAlert().find(GlSprintf);
const findWarningAlertSummary = () => findWarningAlert().findComponent(GlSprintf);
const findWarnings = () => wrapper.findAll('[data-testid="run-pipeline-warning"]');
const findLoadingIcon = () => wrapper.find(GlLoadingIcon);
const findLoadingIcon = () => wrapper.findComponent(GlLoadingIcon);
const findCCAlert = () => wrapper.findComponent(CreditCardValidationRequiredAlert);
const getFormPostParams = () => JSON.parse(mock.history.post[0].data);

View File

@ -19,7 +19,7 @@ describe('Pipeline New Form', () => {
let wrapper;
let mock;
const findDropdown = () => wrapper.find(GlDropdown);
const findDropdown = () => wrapper.findComponent(GlDropdown);
const findRefsDropdownItems = () => wrapper.findAll(GlDropdownItem);
const findSearchBox = () => wrapper.findComponent(GlSearchBoxByType);

View File

@ -30,7 +30,7 @@ describe('Pipeline Wizard -- Input Wrapper', () => {
beforeEach(() => {
createComponent({});
inputChild = wrapper.find(TextWidget);
inputChild = wrapper.findComponent(TextWidget);
});
afterEach(() => {

View File

@ -11,7 +11,7 @@ describe('The DAG annotations', () => {
const getAllColorBlocks = () => wrapper.findAll('[data-testid="dag-color-block"]');
const getTextBlock = () => wrapper.find('[data-testid="dag-note-text"]');
const getAllTextBlocks = () => wrapper.findAll('[data-testid="dag-note-text"]');
const getToggleButton = () => wrapper.find(GlButton);
const getToggleButton = () => wrapper.findComponent(GlButton);
const createComponent = (propsData = {}, method = shallowMount) => {
if (wrapper?.destroy) {

View File

@ -18,12 +18,12 @@ import {
describe('Pipeline DAG graph wrapper', () => {
let wrapper;
const getAlert = () => wrapper.find(GlAlert);
const getAlert = () => wrapper.findComponent(GlAlert);
const getAllAlerts = () => wrapper.findAll(GlAlert);
const getGraph = () => wrapper.find(DagGraph);
const getNotes = () => wrapper.find(DagAnnotations);
const getGraph = () => wrapper.findComponent(DagGraph);
const getNotes = () => wrapper.findComponent(DagAnnotations);
const getErrorText = (type) => wrapper.vm.$options.errorTexts[type];
const getEmptyState = () => wrapper.find(GlEmptyState);
const getEmptyState = () => wrapper.findComponent(GlEmptyState);
const createComponent = ({
graphData = mockParsedGraphQLNodes,

View File

@ -14,7 +14,7 @@ describe('Pipelines filtered search', () => {
let wrapper;
let mock;
const findFilteredSearch = () => wrapper.find(GlFilteredSearch);
const findFilteredSearch = () => wrapper.findComponent(GlFilteredSearch);
const getSearchToken = (type) =>
findFilteredSearch()
.props('availableTokens')
@ -191,7 +191,7 @@ describe('Pipelines filtered search', () => {
findFilteredSearch().vm.$emit('submit', mockSearch);
expect(trackingSpy).toHaveBeenCalledWith(undefined, 'click_filtered_search', {
label: TRACKING_CATEGORIES.index,
label: TRACKING_CATEGORIES.search,
});
});
});

View File

@ -9,7 +9,7 @@ import ActionComponent from '~/pipelines/components/jobs_shared/action_component
describe('pipeline graph action component', () => {
let wrapper;
let mock;
const findButton = () => wrapper.find(GlButton);
const findButton = () => wrapper.findComponent(GlButton);
const findTooltipWrapper = () => wrapper.find('[data-testid="ci-action-icon-tooltip-wrapper"]');
beforeEach(() => {

View File

@ -16,7 +16,7 @@ describe('graph component', () => {
let wrapper;
const findLinkedColumns = () => wrapper.findAll(LinkedPipelinesColumn);
const findLinksLayer = () => wrapper.find(LinksLayer);
const findLinksLayer = () => wrapper.findComponent(LinksLayer);
const findStageColumns = () => wrapper.findAll(StageColumnComponent);
const findStageNameInJob = () => wrapper.find('[data-testid="stage-name-in-job"]');
@ -107,7 +107,7 @@ describe('graph component', () => {
});
it('dims unrelated jobs', () => {
const unrelatedJob = wrapper.find(JobItem);
const unrelatedJob = wrapper.findComponent(JobItem);
expect(findLinksLayer().emitted().highlightedJobsChange).toHaveLength(1);
expect(unrelatedJob.classes('gl-opacity-3')).toBe(true);
});

View File

@ -51,11 +51,11 @@ describe('Pipeline graph wrapper', () => {
const getDependenciesToggle = () => wrapper.find('[data-testid="show-links-toggle"]');
const getLoadingIcon = () => wrapper.findComponent(GlLoadingIcon);
const getLinksLayer = () => wrapper.findComponent(LinksLayer);
const getGraph = () => wrapper.find(PipelineGraph);
const getGraph = () => wrapper.findComponent(PipelineGraph);
const getStageColumnTitle = () => wrapper.find('[data-testid="stage-column-title"]');
const getAllStageColumnGroupsInColumn = () =>
wrapper.find(StageColumnComponent).findAll('[data-testid="stage-column-group"]');
const getViewSelector = () => wrapper.find(GraphViewSelector);
wrapper.findComponent(StageColumnComponent).findAll('[data-testid="stage-column-group"]');
const getViewSelector = () => wrapper.findComponent(GraphViewSelector);
const getViewSelectorTrip = () => getViewSelector().findComponent(GlAlert);
const getLocalStorageSync = () => wrapper.findComponent(LocalStorageSync);
@ -408,7 +408,7 @@ describe('Pipeline graph wrapper', () => {
it('reads the view type from localStorage when available', () => {
const viewSelectorNeedsSegment = wrapper
.find(GlButtonGroup)
.findComponent(GlButtonGroup)
.findAllComponents(GlButton)
.at(1);
expect(viewSelectorNeedsSegment.classes()).toContain('selected');

View File

@ -11,7 +11,7 @@ describe('the graph view selector component', () => {
const findStageViewButton = () => findViewTypeSelector().findAllComponents(GlButton).at(0);
const findLayerViewButton = () => findViewTypeSelector().findAllComponents(GlButton).at(1);
const findSwitcherLoader = () => wrapper.find('[data-testid="switcher-loading-state"]');
const findToggleLoader = () => findDependenciesToggle().find(GlLoadingIcon);
const findToggleLoader = () => findDependenciesToggle().findComponent(GlLoadingIcon);
const findHoverTip = () => wrapper.findComponent(GlAlert);
const defaultProps = {

View File

@ -24,7 +24,7 @@ describe('job name component', () => {
});
it('should render an icon with the provided status', () => {
expect(wrapper.find(ciIcon).exists()).toBe(true);
expect(wrapper.findComponent(ciIcon).exists()).toBe(true);
expect(wrapper.find('.ci-status-icon-success').exists()).toBe(true);
});
});

View File

@ -36,13 +36,13 @@ describe('Linked pipeline', () => {
type: UPSTREAM,
};
const findButton = () => wrapper.find(GlButton);
const findButton = () => wrapper.findComponent(GlButton);
const findCancelButton = () => wrapper.findByLabelText('Cancel downstream pipeline');
const findCardTooltip = () => wrapper.findComponent(GlTooltip);
const findDownstreamPipelineTitle = () => wrapper.findByTestId('downstream-title');
const findExpandButton = () => wrapper.findByTestId('expand-pipeline-button');
const findLinkedPipeline = () => wrapper.find({ ref: 'linkedPipeline' });
const findLoadingIcon = () => wrapper.find(GlLoadingIcon);
const findLinkedPipeline = () => wrapper.findComponent({ ref: 'linkedPipeline' });
const findLoadingIcon = () => wrapper.findComponent(GlLoadingIcon);
const findPipelineLabel = () => wrapper.findByTestId('downstream-pipeline-label');
const findPipelineLink = () => wrapper.findByTestId('pipelineLink');
const findRetryButton = () => wrapper.findByLabelText('Retry downstream pipeline');
@ -80,7 +80,7 @@ describe('Linked pipeline', () => {
});
it('should render an svg within the status container', () => {
const pipelineStatusElement = wrapper.find(CiStatus);
const pipelineStatusElement = wrapper.findComponent(CiStatus);
expect(pipelineStatusElement.find('svg').exists()).toBe(true);
});
@ -90,7 +90,7 @@ describe('Linked pipeline', () => {
});
it('should have a ci-status child component', () => {
expect(wrapper.find(CiStatus).exists()).toBe(true);
expect(wrapper.findComponent(CiStatus).exists()).toBe(true);
});
it('should render the pipeline id', () => {

View File

@ -39,7 +39,7 @@ describe('Linked Pipelines Column', () => {
let wrapper;
const findLinkedColumnTitle = () => wrapper.find('[data-testid="linked-column-title"]');
const findLinkedPipelineElements = () => wrapper.findAll(LinkedPipeline);
const findPipelineGraph = () => wrapper.find(PipelineGraph);
const findPipelineGraph = () => wrapper.findComponent(PipelineGraph);
const findExpandButton = () => wrapper.find('[data-testid="expand-pipeline-button"]');
Vue.use(VueApollo);

View File

@ -42,8 +42,8 @@ describe('stage column component', () => {
const findStageColumnTitle = () => wrapper.find('[data-testid="stage-column-title"]');
const findStageColumnGroup = () => wrapper.find('[data-testid="stage-column-group"]');
const findAllStageColumnGroups = () => wrapper.findAll('[data-testid="stage-column-group"]');
const findJobItem = () => wrapper.find(JobItem);
const findActionComponent = () => wrapper.find(ActionComponent);
const findJobItem = () => wrapper.findComponent(JobItem);
const findActionComponent = () => wrapper.findComponent(ActionComponent);
const createComponent = ({ method = shallowMount, props = {} } = {}) => {
wrapper = method(StageColumnComponent, {

View File

@ -6,7 +6,7 @@ import { generateResponse, mockPipelineResponse } from '../graph/mock_data';
describe('links layer component', () => {
let wrapper;
const findLinksInner = () => wrapper.find(LinksInner);
const findLinksInner = () => wrapper.findComponent(LinksInner);
const pipeline = generateResponse(mockPipelineResponse, 'root/fungi-xoxo');
const containerId = `pipeline-links-container-${pipeline.id}`;

View File

@ -21,12 +21,12 @@ describe('Pipeline details header', () => {
let glModalDirective;
let mutate = jest.fn();
const findAlert = () => wrapper.find(GlAlert);
const findDeleteModal = () => wrapper.find(GlModal);
const findAlert = () => wrapper.findComponent(GlAlert);
const findDeleteModal = () => wrapper.findComponent(GlModal);
const findRetryButton = () => wrapper.find('[data-testid="retryPipeline"]');
const findCancelButton = () => wrapper.find('[data-testid="cancelPipeline"]');
const findDeleteButton = () => wrapper.find('[data-testid="deletePipeline"]');
const findLoadingIcon = () => wrapper.find(GlLoadingIcon);
const findLoadingIcon = () => wrapper.findComponent(GlLoadingIcon);
const defaultProvideOptions = {
pipelineId: '14',

View File

@ -152,7 +152,7 @@ describe('Pipeline Multi Actions Dropdown', () => {
findDropdown().vm.$emit('show');
expect(trackingSpy).toHaveBeenCalledWith(undefined, 'click_artifacts_dropdown', {
label: TRACKING_CATEGORIES.index,
label: TRACKING_CATEGORIES.table,
});
});
});

View File

@ -117,7 +117,7 @@ describe('Pipeline Url Component', () => {
findPipelineUrlLink().vm.$emit('click');
expect(trackingSpy).toHaveBeenCalledWith(undefined, 'click_pipeline_id', {
label: TRACKING_CATEGORIES.index,
label: TRACKING_CATEGORIES.table,
});
});
@ -127,7 +127,7 @@ describe('Pipeline Url Component', () => {
findRefName().vm.$emit('click');
expect(trackingSpy).toHaveBeenCalledWith(undefined, 'click_mr_ref', {
label: TRACKING_CATEGORIES.index,
label: TRACKING_CATEGORIES.table,
});
});
@ -137,7 +137,7 @@ describe('Pipeline Url Component', () => {
findCommitRefName().vm.$emit('click');
expect(trackingSpy).toHaveBeenCalledWith(undefined, 'click_commit_name', {
label: TRACKING_CATEGORIES.index,
label: TRACKING_CATEGORIES.table,
});
});
@ -147,7 +147,7 @@ describe('Pipeline Url Component', () => {
findCommitTitle(findCommitTitleContainer()).vm.$emit('click');
expect(trackingSpy).toHaveBeenCalledWith(undefined, 'click_commit_title', {
label: TRACKING_CATEGORIES.index,
label: TRACKING_CATEGORIES.table,
});
});
@ -157,7 +157,7 @@ describe('Pipeline Url Component', () => {
findCommitShortSha().vm.$emit('click');
expect(trackingSpy).toHaveBeenCalledWith(undefined, 'click_commit_sha', {
label: TRACKING_CATEGORIES.index,
label: TRACKING_CATEGORIES.table,
});
});
});

View File

@ -31,7 +31,7 @@ describe('Pipelines Actions dropdown', () => {
});
};
const findDropdown = () => wrapper.find(GlDropdown);
const findDropdown = () => wrapper.findComponent(GlDropdown);
const findAllDropdownItems = () => wrapper.findAll(GlDropdownItem);
const findAllCountdowns = () => wrapper.findAll(GlCountdown);
@ -110,7 +110,7 @@ describe('Pipelines Actions dropdown', () => {
findDropdown().vm.$emit('shown');
expect(trackingSpy).toHaveBeenCalledWith(undefined, 'click_manual_actions', {
label: TRACKING_CATEGORIES.index,
label: TRACKING_CATEGORIES.table,
});
});
});

View File

@ -30,8 +30,8 @@ describe('Pipelines Artifacts dropdown', () => {
};
const findDropdown = () => wrapper.findComponent(GlDropdown);
const findFirstGlDropdownItem = () => wrapper.find(GlDropdownItem);
const findAllGlDropdownItems = () => wrapper.find(GlDropdown).findAll(GlDropdownItem);
const findFirstGlDropdownItem = () => wrapper.findComponent(GlDropdownItem);
const findAllGlDropdownItems = () => wrapper.findComponent(GlDropdown).findAll(GlDropdownItem);
afterEach(() => {
wrapper.destroy();

View File

@ -7,6 +7,7 @@ import { nextTick } from 'vue';
import mockPipelinesResponse from 'test_fixtures/pipelines/pipelines.json';
import setWindowLocation from 'helpers/set_window_location_helper';
import { TEST_HOST } from 'helpers/test_constants';
import { mockTracking } from 'helpers/tracking_helper';
import { extendedWrapper } from 'helpers/vue_test_utils_helper';
import waitForPromises from 'helpers/wait_for_promises';
import Api from '~/api';
@ -16,7 +17,7 @@ import NavigationControls from '~/pipelines/components/pipelines_list/nav_contro
import PipelinesComponent from '~/pipelines/components/pipelines_list/pipelines.vue';
import PipelinesCiTemplates from '~/pipelines/components/pipelines_list/empty_state/pipelines_ci_templates.vue';
import PipelinesTableComponent from '~/pipelines/components/pipelines_list/pipelines_table.vue';
import { RAW_TEXT_WARNING } from '~/pipelines/constants';
import { RAW_TEXT_WARNING, TRACKING_CATEGORIES } from '~/pipelines/constants';
import Store from '~/pipelines/stores/pipelines_store';
import NavigationTabs from '~/vue_shared/components/navigation_tabs.vue';
import TablePagination from '~/vue_shared/components/pagination/table_pagination.vue';
@ -37,6 +38,7 @@ const mockPipelineWithStages = mockPipelinesResponse.pipelines.find(
describe('Pipelines', () => {
let wrapper;
let mock;
let trackingSpy;
const paths = {
emptyStateSvgPath: '/assets/illustrations/pipelines_empty.svg',
@ -123,7 +125,7 @@ describe('Pipelines', () => {
});
it('shows loading state when the app is loading', () => {
expect(wrapper.find(GlLoadingIcon).exists()).toBe(true);
expect(wrapper.findComponent(GlLoadingIcon).exists()).toBe(true);
});
it('does not display tabs when the first request has not yet been made', () => {
@ -236,6 +238,8 @@ describe('Pipelines', () => {
count: mockPipelinesResponse.count,
});
trackingSpy = mockTracking(undefined, wrapper.element, jest.spyOn);
goToTab('finished');
await waitForPromises();
@ -256,6 +260,12 @@ describe('Pipelines', () => {
`${window.location.pathname}?scope=finished&page=1`,
);
});
it('tracks tab change click', () => {
expect(trackingSpy).toHaveBeenCalledWith(undefined, 'click_filter_tabs', {
label: TRACKING_CATEGORIES.tabs,
});
});
});
describe('when the scope in the tab is empty', () => {
@ -375,7 +385,7 @@ describe('Pipelines', () => {
const [firstPage, secondPage] = chunk(mockPipelinesResponse.pipelines, mockPageSize);
const goToPage = (page) => {
findTablePagination().find(GlPagination).vm.$emit('input', page);
findTablePagination().findComponent(GlPagination).vm.$emit('input', page);
};
beforeEach(async () => {
@ -583,7 +593,7 @@ describe('Pipelines', () => {
'This project is not currently set up to run pipelines.',
);
expect(findEmptyState().find(GlButton).exists()).toBe(false);
expect(findEmptyState().findComponent(GlButton).exists()).toBe(false);
});
it('does not render tabs or buttons', () => {

View File

@ -181,7 +181,7 @@ describe('Pipelines Table', () => {
findStatusBadge().vm.$emit('ciStatusBadgeClick');
expect(trackingSpy).toHaveBeenCalledWith(undefined, 'click_ci_status_badge', {
label: TRACKING_CATEGORIES.index,
label: TRACKING_CATEGORIES.table,
});
});
@ -189,7 +189,7 @@ describe('Pipelines Table', () => {
findRetryBtn().vm.$emit('click');
expect(trackingSpy).toHaveBeenCalledWith(undefined, 'click_retry_button', {
label: TRACKING_CATEGORIES.index,
label: TRACKING_CATEGORIES.table,
});
});
@ -197,7 +197,7 @@ describe('Pipelines Table', () => {
findCancelBtn().vm.$emit('click');
expect(trackingSpy).toHaveBeenCalledWith(undefined, 'click_cancel_button', {
label: TRACKING_CATEGORIES.index,
label: TRACKING_CATEGORIES.table,
});
});
});

View File

@ -26,10 +26,10 @@ describe('Test reports suite table', () => {
const noCasesMessage = () => wrapper.findByTestId('no-test-cases');
const artifactsExpiredMessage = () => wrapper.findByTestId('artifacts-expired');
const artifactsExpiredEmptyState = () => wrapper.find(GlEmptyState);
const artifactsExpiredEmptyState = () => wrapper.findComponent(GlEmptyState);
const allCaseRows = () => wrapper.findAllByTestId('test-case-row');
const findCaseRowAtIndex = (index) => wrapper.findAllByTestId('test-case-row').at(index);
const findLinkForRow = (row) => row.find(GlLink);
const findLinkForRow = (row) => row.findComponent(GlLink);
const findIconForRow = (row, status) => row.find(`.ci-status-icon-${status}`);
const createComponent = ({ suite = testSuite, perPage = 20, errorMessage } = {}) => {
@ -113,7 +113,7 @@ describe('Test reports suite table', () => {
const filePath = `${blobPath}/${relativeFile}`;
const row = findCaseRowAtIndex(0);
const fileLink = findLinkForRow(row);
const button = row.find(GlButton);
const button = row.findComponent(GlButton);
expect(fileLink.attributes('href')).toBe(filePath);
expect(row.text()).toContain(file);
@ -134,7 +134,7 @@ describe('Test reports suite table', () => {
});
it('renders a pagination component', () => {
expect(wrapper.find(GlPagination).exists()).toBe(true);
expect(wrapper.findComponent(GlPagination).exists()).toBe(true);
});
});

View File

@ -44,7 +44,7 @@ describe('Test reports summary table', () => {
describe('when test reports are supplied', () => {
beforeEach(() => createComponent());
const findErrorIcon = () => wrapper.find({ ref: 'suiteErrorIcon' });
const findErrorIcon = () => wrapper.findComponent({ ref: 'suiteErrorIcon' });
it('renders the correct number of rows', () => {
expect(noSuitesToShow().exists()).toBe(false);

View File

@ -48,7 +48,7 @@ describe('Timeago component', () => {
});
it('should render duration and timer svg', () => {
const icon = duration().find(GlIcon);
const icon = duration().findComponent(GlIcon);
expect(duration().exists()).toBe(true);
expect(icon.props('name')).toBe('timer');
@ -71,7 +71,7 @@ describe('Timeago component', () => {
});
it('should render time and calendar icon', () => {
const icon = finishedAt().find(GlIcon);
const icon = finishedAt().findComponent(GlIcon);
const time = finishedAt().find('time');
expect(finishedAt().exists()).toBe(true);

View File

@ -9,9 +9,9 @@ import { branches, mockBranchesAfterMap } from '../mock_data';
describe('Pipeline Branch Name Token', () => {
let wrapper;
const findFilteredSearchToken = () => wrapper.find(GlFilteredSearchToken);
const findFilteredSearchToken = () => wrapper.findComponent(GlFilteredSearchToken);
const findAllFilteredSearchSuggestions = () => wrapper.findAll(GlFilteredSearchSuggestion);
const findLoadingIcon = () => wrapper.find(GlLoadingIcon);
const findLoadingIcon = () => wrapper.findComponent(GlLoadingIcon);
const getBranchSuggestions = () =>
findAllFilteredSearchSuggestions().wrappers.map((w) => w.text());

View File

@ -7,7 +7,7 @@ import PipelineSourceToken from '~/pipelines/components/pipelines_list/tokens/pi
describe('Pipeline Source Token', () => {
let wrapper;
const findFilteredSearchToken = () => wrapper.find(GlFilteredSearchToken);
const findFilteredSearchToken = () => wrapper.findComponent(GlFilteredSearchToken);
const findAllFilteredSearchSuggestions = () => wrapper.findAll(GlFilteredSearchSuggestion);
const defaultProps = {

View File

@ -6,7 +6,7 @@ import PipelineStatusToken from '~/pipelines/components/pipelines_list/tokens/pi
describe('Pipeline Status Token', () => {
let wrapper;
const findFilteredSearchToken = () => wrapper.find(GlFilteredSearchToken);
const findFilteredSearchToken = () => wrapper.findComponent(GlFilteredSearchToken);
const findAllFilteredSearchSuggestions = () => wrapper.findAll(GlFilteredSearchSuggestion);
const findAllGlIcons = () => wrapper.findAll(GlIcon);

View File

@ -7,9 +7,9 @@ import { tags, mockTagsAfterMap } from '../mock_data';
describe('Pipeline Branch Name Token', () => {
let wrapper;
const findFilteredSearchToken = () => wrapper.find(GlFilteredSearchToken);
const findFilteredSearchToken = () => wrapper.findComponent(GlFilteredSearchToken);
const findAllFilteredSearchSuggestions = () => wrapper.findAll(GlFilteredSearchSuggestion);
const findLoadingIcon = () => wrapper.find(GlLoadingIcon);
const findLoadingIcon = () => wrapper.findComponent(GlLoadingIcon);
const stubs = {
GlFilteredSearchToken: {

Some files were not shown because too many files have changed in this diff Show More