Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
2f229658ae
commit
6d18e2830d
74 changed files with 557 additions and 205 deletions
|
@ -181,16 +181,6 @@ Rails/SaveBang:
|
|||
- 'spec/features/dashboard/issuables_counter_spec.rb'
|
||||
- 'spec/features/dashboard/project_member_activity_index_spec.rb'
|
||||
- 'spec/features/dashboard/projects_spec.rb'
|
||||
- 'spec/features/issues/bulk_assignment_labels_spec.rb'
|
||||
- 'spec/features/issues/gfm_autocomplete_spec.rb'
|
||||
- 'spec/features/issues/issue_sidebar_spec.rb'
|
||||
- 'spec/features/issues/note_polling_spec.rb'
|
||||
- 'spec/features/issues/user_creates_branch_and_merge_request_spec.rb'
|
||||
- 'spec/features/issues/user_creates_confidential_merge_request_spec.rb'
|
||||
- 'spec/features/issues/user_edits_issue_spec.rb'
|
||||
- 'spec/features/issues/user_filters_issues_spec.rb'
|
||||
- 'spec/features/issues/user_sees_live_update_spec.rb'
|
||||
- 'spec/features/issues/user_sorts_issues_spec.rb'
|
||||
- 'spec/frontend/fixtures/issues.rb'
|
||||
- 'spec/frontend/fixtures/merge_requests.rb'
|
||||
- 'spec/graphql/mutations/merge_requests/set_locked_spec.rb'
|
||||
|
@ -302,23 +292,6 @@ Rails/SaveBang:
|
|||
- 'spec/models/user_status_spec.rb'
|
||||
- 'spec/models/wiki_page/meta_spec.rb'
|
||||
- 'spec/models/wiki_page_spec.rb'
|
||||
- 'spec/requests/api/ci/runner_spec.rb'
|
||||
- 'spec/requests/api/commit_statuses_spec.rb'
|
||||
- 'spec/requests/api/conan_packages_spec.rb'
|
||||
- 'spec/requests/api/deployments_spec.rb'
|
||||
- 'spec/requests/api/environments_spec.rb'
|
||||
- 'spec/requests/api/go_proxy_spec.rb'
|
||||
- 'spec/requests/api/graphql/mutations/merge_requests/set_labels_spec.rb'
|
||||
- 'spec/requests/api/graphql/user_query_spec.rb'
|
||||
- 'spec/requests/api/graphql_spec.rb'
|
||||
- 'spec/requests/api/group_import_spec.rb'
|
||||
- 'spec/requests/api/group_milestones_spec.rb'
|
||||
- 'spec/requests/api/internal/base_spec.rb'
|
||||
- 'spec/requests/api/issues/get_group_issues_spec.rb'
|
||||
- 'spec/requests/api/issues/post_projects_issues_spec.rb'
|
||||
- 'spec/requests/api/jobs_spec.rb'
|
||||
- 'spec/requests/api/labels_spec.rb'
|
||||
- 'spec/requests/api/project_import_spec.rb'
|
||||
|
||||
Rails/TimeZone:
|
||||
Enabled: true
|
||||
|
@ -1102,8 +1075,6 @@ RSpec/EmptyLineAfterFinalLetItBe:
|
|||
- spec/models/ci/stage_spec.rb
|
||||
- spec/models/clusters/kubernetes_namespace_spec.rb
|
||||
- spec/models/commit_spec.rb
|
||||
- spec/models/concerns/batch_destroy_dependent_associations_spec.rb
|
||||
- spec/models/concerns/featurable_spec.rb
|
||||
- spec/models/deploy_token_spec.rb
|
||||
- spec/models/deployment_spec.rb
|
||||
- spec/models/diff_viewer/server_side_spec.rb
|
||||
|
@ -3335,26 +3306,6 @@ Style/HashTransformation:
|
|||
- 'ee/lib/gitlab/custom_file_templates.rb'
|
||||
- 'ee/spec/elastic_integration/global_search_spec.rb'
|
||||
- 'ee/spec/lib/ee/gitlab/application_context_spec.rb'
|
||||
- 'lib/api/helpers/packages/conan/api_helpers.rb'
|
||||
- 'lib/api/projects.rb'
|
||||
- 'lib/atlassian/jira_connect/client.rb'
|
||||
- 'lib/banzai/filter/repository_link_filter.rb'
|
||||
- 'lib/gitlab/ci/config/entry/product/variables.rb'
|
||||
- 'lib/gitlab/ci/config/entry/variables.rb'
|
||||
- 'lib/gitlab/ci/variables/collection.rb'
|
||||
- 'lib/gitlab/ci/variables/helpers.rb'
|
||||
- 'lib/gitlab/git/commit.rb'
|
||||
- 'lib/gitlab/import_sources.rb'
|
||||
- 'lib/gitlab/language_detection.rb'
|
||||
- 'lib/gitlab/metrics/samplers/database_sampler.rb'
|
||||
- 'lib/gitlab/metrics/subscribers/active_record.rb'
|
||||
- 'lib/gitlab/phabricator_import/project_creator.rb'
|
||||
- 'lib/gitlab/prometheus_client.rb'
|
||||
- 'lib/gitlab/repository_hash_cache.rb'
|
||||
- 'lib/gitlab/static_site_editor/config/file_config.rb'
|
||||
- 'lib/gitlab/template/base_template.rb'
|
||||
- 'lib/gitlab/usage_data_counters/base_counter.rb'
|
||||
- 'lib/gitlab/usage_data_counters/note_counter.rb'
|
||||
- 'spec/lib/atlassian/jira_connect/serializers/pull_request_entity_spec.rb'
|
||||
- 'spec/lib/gitlab/ci/status/composite_spec.rb'
|
||||
- 'spec/lib/gitlab/conflict/file_spec.rb'
|
||||
|
|
|
@ -42,6 +42,7 @@ export default {
|
|||
:member-id="member.id"
|
||||
:message="message"
|
||||
:title="s__('Member|Revoke invite')"
|
||||
is-invite
|
||||
/>
|
||||
</div>
|
||||
</action-button-group>
|
||||
|
|
|
@ -36,6 +36,11 @@ export default {
|
|||
required: false,
|
||||
default: false,
|
||||
},
|
||||
isInvite: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
...mapState(['memberPath']),
|
||||
|
@ -57,6 +62,7 @@ export default {
|
|||
:data-member-path="computedMemberPath"
|
||||
:data-member-type="memberType"
|
||||
:data-is-access-request="isAccessRequest"
|
||||
:data-is-invite="isInvite"
|
||||
:data-message="message"
|
||||
data-qa-selector="delete_member_button"
|
||||
/>
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
import { GlFormCheckbox, GlModal } from '@gitlab/ui';
|
||||
import { parseBoolean } from '~/lib/utils/common_utils';
|
||||
import csrf from '~/lib/utils/csrf';
|
||||
import { __ } from '~/locale';
|
||||
import { s__, __ } from '~/locale';
|
||||
|
||||
export default {
|
||||
actionCancel: {
|
||||
|
@ -22,11 +22,20 @@ export default {
|
|||
isAccessRequest() {
|
||||
return parseBoolean(this.modalData.isAccessRequest);
|
||||
},
|
||||
isInvite() {
|
||||
return parseBoolean(this.modalData.isInvite);
|
||||
},
|
||||
isGroupMember() {
|
||||
return this.modalData.memberType === 'GroupMember';
|
||||
},
|
||||
actionText() {
|
||||
return this.isAccessRequest ? __('Deny access request') : __('Remove member');
|
||||
if (this.isAccessRequest) {
|
||||
return __('Deny access request');
|
||||
} else if (this.isInvite) {
|
||||
return s__('Member|Revoke invite');
|
||||
}
|
||||
|
||||
return __('Remove member');
|
||||
},
|
||||
actionPrimary() {
|
||||
return {
|
||||
|
@ -36,6 +45,9 @@ export default {
|
|||
},
|
||||
};
|
||||
},
|
||||
showUnassignIssuablesCheckbox() {
|
||||
return !this.isAccessRequest && !this.isInvite;
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
document.addEventListener('click', this.handleClick);
|
||||
|
@ -76,7 +88,7 @@ export default {
|
|||
<gl-form-checkbox v-if="isGroupMember" name="remove_sub_memberships">
|
||||
{{ __('Also remove direct user membership from subgroups and projects') }}
|
||||
</gl-form-checkbox>
|
||||
<gl-form-checkbox v-if="!isAccessRequest" name="unassign_issuables">
|
||||
<gl-form-checkbox v-if="showUnassignIssuablesCheckbox" name="unassign_issuables">
|
||||
{{ __('Also unassign this user from related issues and merge requests') }}
|
||||
</gl-form-checkbox>
|
||||
</form>
|
||||
|
|
|
@ -26,6 +26,8 @@ module Types
|
|||
description: 'Whether this job is allowed to fail.'
|
||||
field :duration, GraphQL::INT_TYPE, null: true,
|
||||
description: 'Duration of the job in seconds.'
|
||||
field :tags, [GraphQL::STRING_TYPE], null: true,
|
||||
description: 'Tags for the current job.'
|
||||
|
||||
# Life-cycle timestamps:
|
||||
field :created_at, Types::TimeType, null: false,
|
||||
|
@ -68,6 +70,10 @@ module Types
|
|||
Gitlab::Graphql::Loaders::BatchModelLoader.new(::Ci::Pipeline, object.pipeline_id).find
|
||||
end
|
||||
|
||||
def tags
|
||||
object.tags.map(&:name) if object.is_a?(::Ci::Build)
|
||||
end
|
||||
|
||||
def detailed_status
|
||||
object.detailed_status(context[:current_user])
|
||||
end
|
||||
|
|
|
@ -187,7 +187,7 @@ module ObjectStorage
|
|||
hash[:TempPath] = workhorse_local_upload_path
|
||||
end
|
||||
|
||||
hash[:FeatureFlagExtractBase] = Feature.enabled?(:workhorse_extract_filename_base)
|
||||
hash[:FeatureFlagExtractBase] = Feature.enabled?(:workhorse_extract_filename_base, default_enabled: :yaml)
|
||||
hash[:MaximumSize] = maximum_size if maximum_size.present?
|
||||
end
|
||||
end
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
|
||||
.content-block.oneline-block.files-changed.diff-files-changed.js-diff-files-changed
|
||||
.files-changed-inner
|
||||
.inline-parallel-buttons.d-none.d-md-block
|
||||
.inline-parallel-buttons.gl-display-none.gl-md-display-flex
|
||||
- if !diffs_expanded? && diff_files.any? { |diff_file| diff_file.collapsed? }
|
||||
= link_to _('Expand all'), url_for(safe_params.merge(expanded: 1, format: nil)), class: 'gl-button btn btn-default'
|
||||
- if show_whitespace_toggle
|
||||
|
@ -20,7 +20,7 @@
|
|||
= diff_compare_whitespace_link(diffs.project, params[:from], params[:to], class: 'd-none d-sm-inline-block')
|
||||
- elsif current_controller?(:wikis)
|
||||
= toggle_whitespace_link(url_for(params_with_whitespace), class: 'd-none d-sm-inline-block')
|
||||
.btn-group
|
||||
.btn-group.gl-ml-3
|
||||
= inline_diff_btn
|
||||
= parallel_diff_btn
|
||||
= render 'projects/diffs/stats', diff_files: diff_files
|
||||
|
|
|
@ -14,10 +14,10 @@
|
|||
= submodule_diff_compare_link(diff_file)
|
||||
|
||||
- unless diff_file.submodule?
|
||||
.file-actions.d-none.d-sm-block
|
||||
.file-actions.gl-display-none.gl-sm-display-flex
|
||||
- if diff_file.blob&.readable_text?
|
||||
%span.has-tooltip{ title: _("Toggle comments for this file") }
|
||||
= link_to '#', class: 'js-toggle-diff-comments btn gl-button btn-default selected', disabled: @diff_notes_disabled do
|
||||
%span.has-tooltip.gl-mr-3{ title: _("Toggle comments for this file") }
|
||||
= link_to '#', class: 'js-toggle-diff-comments btn gl-button btn-default btn-icon selected', disabled: @diff_notes_disabled do
|
||||
= sprite_icon('comment')
|
||||
\
|
||||
- if editable_diff?(diff_file)
|
||||
|
|
|
@ -1,15 +1,15 @@
|
|||
- noteable_name = @note.noteable.human_class_name
|
||||
|
||||
.float-left.btn-group.gl-mr-3.droplab-dropdown.comment-type-dropdown.js-comment-type-dropdown
|
||||
%input.btn.gl-button.btn-success.js-comment-button.js-comment-submit-button{ type: 'submit', value: _('Comment'), data: { qa_selector: 'comment_button' } }
|
||||
%input.btn.gl-button.btn-confirm.js-comment-button.js-comment-submit-button{ type: 'submit', value: _('Comment'), data: { qa_selector: 'comment_button' } }
|
||||
|
||||
- if @note.can_be_discussion_note?
|
||||
= button_tag type: 'button', class: 'gl-button btn dropdown-toggle btn-success js-note-new-discussion js-disable-on-submit', data: { 'dropdown-trigger' => '#resolvable-comment-menu' }, 'aria-label' => _('Open comment type dropdown') do
|
||||
= button_tag type: 'button', class: 'gl-button btn dropdown-toggle btn-confirm btn-icon js-note-new-discussion js-disable-on-submit', data: { 'dropdown-trigger' => '#resolvable-comment-menu' }, 'aria-label' => _('Open comment type dropdown') do
|
||||
= sprite_icon('chevron-down')
|
||||
|
||||
%ul#resolvable-comment-menu.dropdown-menu.dropdown-open-top{ data: { dropdown: true } }
|
||||
%li#comment.droplab-item-selected{ data: { value: '', 'submit-text' => _('Comment'), 'close-text' => _("Comment & close %{noteable_name}") % { noteable_name: noteable_name }, 'reopen-text' => _("Comment & reopen %{noteable_name}") % { noteable_name: noteable_name } } }
|
||||
%button{ type: 'button' }
|
||||
%button.btn.gl-button.btn-default-tertiary
|
||||
= sprite_icon('check', css_class: 'icon')
|
||||
.description
|
||||
%strong= _("Comment")
|
||||
|
@ -19,7 +19,7 @@
|
|||
%li.divider.droplab-item-ignore
|
||||
|
||||
%li#discussion{ data: { value: 'DiscussionNote', 'submit-text' => _('Start thread'), 'close-text' => _("Start thread & close %{noteable_name}") % { noteable_name: noteable_name }, 'reopen-text' => _("Start thread & reopen %{noteable_name}") % { noteable_name: noteable_name } } }
|
||||
%button{ type: 'button' }
|
||||
%button.btn.gl-button.btn-default-tertiary
|
||||
= sprite_icon('check', css_class: 'icon')
|
||||
.description
|
||||
%strong= _("Start thread")
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Update title on revoke member invite modal and hide unneeded related issues and merge requests checkbox
|
||||
merge_request: 57755
|
||||
author:
|
||||
type: changed
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Update buttons and spacing on commit page
|
||||
merge_request: 56793
|
||||
author:
|
||||
type: changed
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Fix Rails/SaveBang Rubocop offenses for requests/api module
|
||||
merge_request: 57887
|
||||
author: Huzaifa Iftikhar @huzaifaiftikhar
|
||||
type: fixed
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Fix EmptyLineAfterFinalLetItBe offenses in spec/models/concerns
|
||||
merge_request: 58367
|
||||
author: Huzaifa Iftikhar @huzaifaiftikhar
|
||||
type: fixed
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Add tags field to jobType in the CI namespace
|
||||
merge_request: 57631
|
||||
author:
|
||||
type: changed
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Fix cop offenses for Style/HashTransformation in lib directory
|
||||
merge_request: 56583
|
||||
author: Karthik Sivadas @karthik.sivadas
|
||||
type: other
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Fix Rails/SaveBang rubocop offenses in spec/features/issues
|
||||
merge_request: 57900
|
||||
author: Abdul Wadood @abdulwd
|
||||
type: fixed
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Set workhorse_extract_filename_base feature flag to default
|
||||
merge_request: 58504
|
||||
author:
|
||||
type: changed
|
|
@ -5,4 +5,4 @@ rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/326379
|
|||
milestone: '13.11'
|
||||
type: development
|
||||
group: group::source code
|
||||
default_enabled: false
|
||||
default_enabled: true
|
||||
|
|
|
@ -21,6 +21,9 @@ You can use the following environment variables to override certain values:
|
|||
|--------------------------------------------|---------|---------------------------------------------------------------------------------------------------------|
|
||||
| `DATABASE_URL` | string | The database URL; is of the form: `postgresql://localhost/blog_development`. |
|
||||
| `ENABLE_BOOTSNAP` | string | Enables Bootsnap for speeding up initial Rails boot (`1` to enable). |
|
||||
| `EXTERNAL_VALIDATION_SERVICE_TIMEOUT` | integer | Timeout, in seconds, for an [external CI/CD pipeline validation service](external_pipeline_validation.md). Default is `5`. |
|
||||
| `EXTERNAL_VALIDATION_SERVICE_URL` | string | URL to an [external CI/CD pipeline validation service](external_pipeline_validation.md). |
|
||||
| `EXTERNAL_VALIDATION_SERVICE_TOKEN` | string | The `X-Gitlab-Token` for authentication with an [external CI/CD pipeline validation service](external_pipeline_validation.md). |
|
||||
| `GITLAB_CDN_HOST` | string | Sets the base URL for a CDN to serve static assets (for example, `//mycdnsubdomain.fictional-cdn.com`). |
|
||||
| `GITLAB_EMAIL_DISPLAY_NAME` | string | The name used in the **From** field in emails sent by GitLab. |
|
||||
| `GITLAB_EMAIL_FROM` | string | The email address used in the **From** field in emails sent by GitLab. |
|
||||
|
@ -29,8 +32,8 @@ You can use the following environment variables to override certain values:
|
|||
| `GITLAB_HOST` | string | The full URL of the GitLab server (including `http://` or `https://`). |
|
||||
| `GITLAB_ROOT_PASSWORD` | string | Sets the password for the `root` user on installation. |
|
||||
| `GITLAB_SHARED_RUNNERS_REGISTRATION_TOKEN` | string | Sets the initial registration token used for runners. |
|
||||
| `GITLAB_UNICORN_MEMORY_MAX` | integer | The maximum memory threshold (in bytes) for the [unicorn-worker-killer](operations/unicorn.md#unicorn-worker-killer). |
|
||||
| `GITLAB_UNICORN_MEMORY_MIN` | integer | The minimum memory threshold (in bytes) for the [unicorn-worker-killer](operations/unicorn.md#unicorn-worker-killer). |
|
||||
| `GITLAB_UNICORN_MEMORY_MAX` | integer | The maximum memory threshold (in bytes) for the [unicorn-worker-killer](operations/unicorn.md#unicorn-worker-killer). |
|
||||
| `GITLAB_UNICORN_MEMORY_MIN` | integer | The minimum memory threshold (in bytes) for the [unicorn-worker-killer](operations/unicorn.md#unicorn-worker-killer). |
|
||||
| `RAILS_ENV` | string | The Rails environment; can be one of `production`, `development`, `staging`, or `test`. |
|
||||
| `UNSTRUCTURED_RAILS_LOG` | string | Enables the unstructured log in addition to JSON logs (defaults to `true`). |
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@ type: reference, howto
|
|||
|
||||
# External Pipeline Validation
|
||||
|
||||
You can use an external service for validating a pipeline before it's created.
|
||||
You can use an external service to validate a pipeline before it's created.
|
||||
|
||||
WARNING:
|
||||
This is an experimental feature and subject to change without notice.
|
||||
|
@ -19,19 +19,17 @@ data as payload. GitLab then invalidates the pipeline based on the response
|
|||
code. If there's an error or the request times out, the pipeline is not
|
||||
invalidated.
|
||||
|
||||
Response Code Legend:
|
||||
Response codes:
|
||||
|
||||
- `200` - Accepted
|
||||
- `406` - Not Accepted
|
||||
- Other Codes - Accepted and Logged
|
||||
- `200`: Accepted
|
||||
- `4XX`: Not accepted
|
||||
- All other codes: accepted and logged
|
||||
|
||||
## Configuration
|
||||
|
||||
To configure external pipeline validation:
|
||||
|
||||
1. Set the `EXTERNAL_VALIDATION_SERVICE_URL` environment variable to the external
|
||||
service URL.
|
||||
1. Enable the `ci_external_validation_service` feature flag.
|
||||
To configure external pipeline validation, add the
|
||||
[`EXTERNAL_VALIDATION_SERVICE_URL` environment variable](environment_variables.md)
|
||||
and set it to the external service URL.
|
||||
|
||||
By default, requests to the external service time out after five seconds. To override
|
||||
the default, set the `EXTERNAL_VALIDATION_SERVICE_TIMEOUT` environment variable to the
|
||||
|
@ -131,3 +129,6 @@ required number of seconds.
|
|||
}
|
||||
}
|
||||
```
|
||||
|
||||
The `namespace` field is only available in [GitLab Premium](https://about.gitlab.com/pricing/)
|
||||
and higher.
|
||||
|
|
|
@ -1235,6 +1235,7 @@ An edge in a connection.
|
|||
| `stage` | [`CiStage`](#cistage) | Stage of the job. |
|
||||
| `startedAt` | [`Time`](#time) | When the job was started. |
|
||||
| `status` | [`CiJobStatus`](#cijobstatus) | Status of the job. |
|
||||
| `tags` | [`[String!]`](#string) | Tags for the current job. |
|
||||
|
||||
### `CiJobArtifact`
|
||||
|
||||
|
|
|
@ -182,3 +182,17 @@ Watch a walkthrough of this feature in details in the video below.
|
|||
<figure class="video-container">
|
||||
<iframe src="https://www.youtube.com/embed/NmdWRGT8kZg" frameborder="0" allowfullscreen="true"> </iframe>
|
||||
</figure>
|
||||
|
||||
## External pipeline validation service
|
||||
|
||||
The [external CI/CD pipeline validation service](../../administration/external_pipeline_validation.md)
|
||||
is available for use on self-managed GitLab instances, but is not in use on GitLab.com.
|
||||
It is configured with [environment variables](../../administration/environment_variables.md)
|
||||
on the instance.
|
||||
|
||||
To enable the feature on GitLab.com, enable the `ci_external_validation_service`
|
||||
[feature flag](../feature_flags/index.md). The valid "Not accepted" response code
|
||||
for GitLab.com is `406` only.
|
||||
|
||||
For more details, see the linked issues and MRs in the
|
||||
[feature flag rollout issue](https://gitlab.com/gitlab-org/gitlab/-/issues/325982).
|
||||
|
|
|
@ -212,8 +212,8 @@ When it comes to querying DOM elements in your tests, it is best to uniquely and
|
|||
the element.
|
||||
|
||||
Preferentially, this is done by targeting what the user actually sees using [DOM Testing Library](https://testing-library.com/docs/dom-testing-library/intro/).
|
||||
When selecting by text it is best to use [`getByRole` or `findByRole`](https://testing-library.com/docs/queries/byrole/)
|
||||
as these enforce accessibility best practices as well. The examples below demonstrate the order of preference.
|
||||
When selecting by text it is best to use the [`byRole`](https://testing-library.com/docs/queries/byrole) query
|
||||
as it helps enforce accessibility best practices. `findByRole` and the other [DOM Testing Library queries](https://testing-library.com/docs/queries/about) are available when using [`shallowMountExtended` or `mountExtended`](#shallowmountextended-and-mountextended).
|
||||
|
||||
When writing Vue component unit tests, it can be wise to query children by component, so that the unit test can focus on comprehensive value coverage
|
||||
rather than dealing with the complexity of a child component's behavior.
|
||||
|
@ -223,25 +223,27 @@ possible selectors include:
|
|||
|
||||
- A semantic attribute like `name` (also verifies that `name` was setup properly)
|
||||
- A `data-testid` attribute ([recommended by maintainers of `@vue/test-utils`](https://github.com/vuejs/vue-test-utils/issues/1498#issuecomment-610133465))
|
||||
optionally combined with [`findByTestId`](#extendedwrapper-and-findbytestid)
|
||||
optionally combined with [`shallowMountExtended` or `mountExtended`](#shallowmountextended-and-mountextended)
|
||||
- a Vue `ref` (if using `@vue/test-utils`)
|
||||
|
||||
```javascript
|
||||
import { getByRole, getByText } from '@testing-library/dom'
|
||||
import { shallowMountExtended } from 'helpers/vue_test_utils_helper'
|
||||
|
||||
const wrapper = shallowMountExtended(ExampleComponent);
|
||||
|
||||
// In this example, `wrapper` is a `@vue/test-utils` wrapper returned from `mount` or `shallowMount`.
|
||||
it('exists', () => {
|
||||
// Best (especially for integration tests)
|
||||
getByRole(wrapper.element, 'link', { name: /Click Me/i })
|
||||
getByRole(wrapper.element, 'link', { name: 'Click Me' })
|
||||
getByText(wrapper.element, 'Click Me')
|
||||
getByText(wrapper.element, /Click Me/i)
|
||||
wrapper.findByRole('link', { name: /Click Me/i })
|
||||
wrapper.findByRole('link', { name: 'Click Me' })
|
||||
wrapper.findByText('Click Me')
|
||||
wrapper.findByText(/Click Me/i)
|
||||
|
||||
// Good (especially for unit tests)
|
||||
wrapper.find(FooComponent);
|
||||
wrapper.find('input[name=foo]');
|
||||
wrapper.find('[data-testid="my-foo-id"]');
|
||||
wrapper.findByTestId('my-foo-id'); // with the extendedWrapper utility – check below
|
||||
wrapper.findByTestId('my-foo-id'); // with shallowMountExtended or mountExtended – check below
|
||||
wrapper.find({ ref: 'foo'});
|
||||
|
||||
// Bad
|
||||
|
@ -1138,23 +1140,40 @@ These are very useful if you don't have a handle to the request's Promise, for e
|
|||
|
||||
Both functions run `callback` on the next tick after the requests finish (using `setImmediate()`), to allow any `.then()` or `.catch()` handlers to run.
|
||||
|
||||
### `extendedWrapper` and `findByTestId`
|
||||
### `shallowMountExtended` and `mountExtended`
|
||||
|
||||
Using `data-testid` is one of the [recommended ways to query DOM elements](#how-to-query-dom-elements).
|
||||
You can use the `extendedWrapper` utility on the `wrapper` returned by `shalowMount`/`mount`.
|
||||
By doing so, the `wrapper` provides you with the ability to perform a `findByTestId`,
|
||||
which is a shortcut to the more verbose `wrapper.find('[data-testid="my-test-id"]');`
|
||||
The `shallowMountExtended` and `mountExtended` utilities provide you with the ability to perform
|
||||
any of the available [DOM Testing Library queries](https://testing-library.com/docs/queries/about)
|
||||
by prefixing them with `find` or `findAll`.
|
||||
|
||||
```javascript
|
||||
import { extendedWrapper } from 'helpers/vue_test_utils_helper';
|
||||
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
|
||||
|
||||
describe('FooComponent', () => {
|
||||
const wrapper = extendedWrapper(shallowMount({
|
||||
template: `<div data-testid="my-test-id"></div>`,
|
||||
}));
|
||||
const wrapper = shallowMountExtended({
|
||||
template: `
|
||||
<div data-testid="gitlab-frontend-stack">
|
||||
<p>GitLab frontend stack</p>
|
||||
<div role="tablist">
|
||||
<button role="tab" aria-selected="true">Vue.js</button>
|
||||
<button role="tab" aria-selected="false">GraphQL</button>
|
||||
<button role="tab" aria-selected="false">SCSS</button>
|
||||
</div>
|
||||
</div>
|
||||
`,
|
||||
});
|
||||
|
||||
it('exists', () => {
|
||||
expect(wrapper.findByTestId('my-test-id').exists()).toBe(true);
|
||||
it('finds elements with `findByTestId`', () => {
|
||||
expect(wrapper.findByTestId('gitlab-frontend-stack').exists()).toBe(true);
|
||||
});
|
||||
|
||||
it('finds elements with `findByText`', () => {
|
||||
expect(wrapper.findByText('GitLab frontend stack').exists()).toBe(true);
|
||||
expect(wrapper.findByText('TypeScript').exists()).toBe(false);
|
||||
});
|
||||
|
||||
it('finds elements with `findAllByRole`', () => {
|
||||
expect(wrapper.findAllByRole('tab').length).toBe(3);
|
||||
});
|
||||
});
|
||||
```
|
||||
|
|
|
@ -32,19 +32,15 @@ module API
|
|||
end
|
||||
|
||||
def recipe_upload_urls
|
||||
{ upload_urls: Hash[
|
||||
file_names.select(&method(:recipe_file?)).map do |file_name|
|
||||
[file_name, build_recipe_file_upload_url(file_name)]
|
||||
end
|
||||
] }
|
||||
{ upload_urls: file_names.select(&method(:recipe_file?)).to_h do |file_name|
|
||||
[file_name, build_recipe_file_upload_url(file_name)]
|
||||
end }
|
||||
end
|
||||
|
||||
def package_upload_urls
|
||||
{ upload_urls: Hash[
|
||||
file_names.select(&method(:package_file?)).map do |file_name|
|
||||
[file_name, build_package_file_upload_url(file_name)]
|
||||
end
|
||||
] }
|
||||
{ upload_urls: file_names.select(&method(:package_file?)).to_h do |file_name|
|
||||
[file_name, build_package_file_upload_url(file_name)]
|
||||
end }
|
||||
end
|
||||
|
||||
def recipe_file?(file_name)
|
||||
|
|
|
@ -485,7 +485,7 @@ module API
|
|||
get ':id/languages', feature_category: :source_code_management do
|
||||
::Projects::RepositoryLanguagesService
|
||||
.new(user_project, current_user)
|
||||
.execute.map { |lang| [lang.name, lang.share] }.to_h
|
||||
.execute.to_h { |lang| [lang.name, lang.share] }
|
||||
end
|
||||
|
||||
desc 'Delete a project'
|
||||
|
|
|
@ -141,9 +141,9 @@ module Atlassian
|
|||
def user_notes_count(merge_requests)
|
||||
return unless merge_requests
|
||||
|
||||
Note.count_for_collection(merge_requests.map(&:id), 'MergeRequest').map do |count_group|
|
||||
Note.count_for_collection(merge_requests.map(&:id), 'MergeRequest').to_h do |count_group|
|
||||
[count_group.noteable_id, count_group.count]
|
||||
end.to_h
|
||||
end
|
||||
end
|
||||
|
||||
def jwt_token(http_method, uri)
|
||||
|
|
|
@ -60,7 +60,7 @@ module Banzai
|
|||
def get_uri_types(paths)
|
||||
return {} if paths.empty?
|
||||
|
||||
uri_types = Hash[paths.collect { |name| [name, nil] }]
|
||||
uri_types = paths.to_h { |name| [name, nil] }
|
||||
|
||||
get_blob_types(paths).each do |name, type|
|
||||
if type == :blob
|
||||
|
|
|
@ -25,8 +25,7 @@ module Gitlab
|
|||
|
||||
def value
|
||||
@config
|
||||
.map { |key, value| [key.to_s, Array(value).map(&:to_s)] }
|
||||
.to_h
|
||||
.to_h { |key, value| [key.to_s, Array(value).map(&:to_s)] }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -18,7 +18,7 @@ module Gitlab
|
|||
end
|
||||
|
||||
def value
|
||||
Hash[@config.map { |key, value| [key.to_s, expand_value(value)[:value]] }]
|
||||
@config.to_h { |key, value| [key.to_s, expand_value(value)[:value]] }
|
||||
end
|
||||
|
||||
def self.default(**)
|
||||
|
@ -26,7 +26,7 @@ module Gitlab
|
|||
end
|
||||
|
||||
def value_with_data
|
||||
Hash[@config.map { |key, value| [key.to_s, expand_value(value)] }]
|
||||
@config.to_h { |key, value| [key.to_s, expand_value(value)] }
|
||||
end
|
||||
|
||||
def use_value_data?
|
||||
|
|
|
@ -23,7 +23,7 @@ module Gitlab
|
|||
def transform_from_yaml_variables(vars)
|
||||
return vars.stringify_keys if vars.is_a?(Hash)
|
||||
|
||||
vars.to_a.map { |var| [var[:key].to_s, var[:value]] }.to_h
|
||||
vars.to_a.to_h { |var| [var[:key].to_s, var[:value]] }
|
||||
end
|
||||
|
||||
def inherit_yaml_variables(from:, to:, inheritance:)
|
||||
|
|
|
@ -390,7 +390,7 @@ module Gitlab
|
|||
@committer_name = commit.committer.name.dup
|
||||
@committer_email = commit.committer.email.dup
|
||||
@parent_ids = Array(commit.parent_ids)
|
||||
@trailers = Hash[commit.trailers.map { |t| [t.key, t.value] }]
|
||||
@trailers = commit.trailers.to_h { |t| [t.key, t.value] }
|
||||
end
|
||||
|
||||
# Gitaly provides a UNIX timestamp in author.date.seconds, and a timezone
|
||||
|
|
|
@ -28,7 +28,7 @@ module Gitlab
|
|||
prepend_if_ee('EE::Gitlab::ImportSources') # rubocop: disable Cop/InjectEnterpriseEditionModule
|
||||
|
||||
def options
|
||||
Hash[import_table.map { |importer| [importer.title, importer.name] }]
|
||||
import_table.to_h { |importer| [importer.title, importer.name] }
|
||||
end
|
||||
|
||||
def values
|
||||
|
|
|
@ -20,7 +20,7 @@ module Gitlab
|
|||
# Newly detected languages, returned in a structure accepted by
|
||||
# Gitlab::Database.bulk_insert
|
||||
def insertions(programming_languages)
|
||||
lang_to_id = programming_languages.map { |p| [p.name, p.id] }.to_h
|
||||
lang_to_id = programming_languages.to_h { |p| [p.name, p.id] }
|
||||
|
||||
(languages - previous_language_names).map do |new_lang|
|
||||
{
|
||||
|
@ -63,8 +63,7 @@ module Gitlab
|
|||
@repository
|
||||
.languages
|
||||
.first(MAX_LANGUAGES)
|
||||
.map { |l| [l[:label], l] }
|
||||
.to_h
|
||||
.to_h { |l| [l[:label], l] }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -32,9 +32,9 @@ module Gitlab
|
|||
private
|
||||
|
||||
def init_metrics
|
||||
METRIC_DESCRIPTIONS.map do |name, description|
|
||||
METRIC_DESCRIPTIONS.to_h do |name, description|
|
||||
[name, ::Gitlab::Metrics.gauge(:"#{METRIC_PREFIX}#{name}", description)]
|
||||
end.to_h
|
||||
end
|
||||
end
|
||||
|
||||
def host_stats
|
||||
|
|
|
@ -55,12 +55,13 @@ module Gitlab
|
|||
end
|
||||
|
||||
def project_feature_attributes
|
||||
@project_features_attributes ||= begin
|
||||
# everything disabled except for issues
|
||||
ProjectFeature::FEATURES.map do |feature|
|
||||
[ProjectFeature.access_level_attribute(feature), ProjectFeature::DISABLED]
|
||||
end.to_h.merge(ProjectFeature.access_level_attribute(:issues) => ProjectFeature::ENABLED)
|
||||
end
|
||||
@project_features_attributes ||=
|
||||
begin
|
||||
# everything disabled except for issues
|
||||
ProjectFeature::FEATURES.to_h do |feature|
|
||||
[ProjectFeature.access_level_attribute(feature), ProjectFeature::DISABLED]
|
||||
end.merge(ProjectFeature.access_level_attribute(:issues) => ProjectFeature::ENABLED)
|
||||
end
|
||||
end
|
||||
|
||||
def import_data
|
||||
|
|
|
@ -140,7 +140,7 @@ module Gitlab
|
|||
end
|
||||
|
||||
def mapped_options
|
||||
options.keys.map { |k| [gitlab_http_key(k), options[k]] }.to_h
|
||||
options.keys.to_h { |k| [gitlab_http_key(k), options[k]] }
|
||||
end
|
||||
|
||||
def http_options
|
||||
|
|
|
@ -148,7 +148,7 @@ module Gitlab
|
|||
# @param hash [Hash]
|
||||
# @return [Hash] the stringified hash
|
||||
def standardize_hash(hash)
|
||||
hash.map { |k, v| [k.to_s, v.to_s] }.to_h
|
||||
hash.to_h { |k, v| [k.to_s, v.to_s] }
|
||||
end
|
||||
|
||||
# Record metrics in Prometheus.
|
||||
|
|
|
@ -28,7 +28,7 @@ module Gitlab
|
|||
def to_hash_with_defaults
|
||||
# NOTE: The current approach of simply mapping all the descendents' keys and values ('config')
|
||||
# into a flat hash may need to be enhanced as we add more complex, non-scalar entries.
|
||||
@global.descendants.map { |descendant| [descendant.key, descendant.config] }.to_h
|
||||
@global.descendants.to_h { |descendant| [descendant.key, descendant.config] }
|
||||
end
|
||||
|
||||
private
|
||||
|
|
|
@ -130,10 +130,10 @@ module Gitlab
|
|||
return [] if project && !project.repository.exists?
|
||||
|
||||
if categories.any?
|
||||
categories.keys.map do |category|
|
||||
categories.keys.to_h do |category|
|
||||
files = self.by_category(category, project)
|
||||
[category, files.map { |t| { key: t.key, name: t.name, content: t.content } }]
|
||||
end.to_h
|
||||
end
|
||||
else
|
||||
files = self.all(project)
|
||||
files.map { |t| { key: t.key, name: t.name, content: t.content } }
|
||||
|
|
|
@ -22,11 +22,11 @@ module Gitlab::UsageDataCounters
|
|||
end
|
||||
|
||||
def totals
|
||||
known_events.map { |event| [counter_key(event), read(event)] }.to_h
|
||||
known_events.to_h { |event| [counter_key(event), read(event)] }
|
||||
end
|
||||
|
||||
def fallback_totals
|
||||
known_events.map { |event| [counter_key(event), -1] }.to_h
|
||||
known_events.to_h { |event| [counter_key(event), -1] }
|
||||
end
|
||||
|
||||
def fetch_supported_event(event_name)
|
||||
|
|
|
@ -24,13 +24,13 @@ module Gitlab::UsageDataCounters
|
|||
end
|
||||
|
||||
def totals
|
||||
COUNTABLE_TYPES.map do |countable_type|
|
||||
COUNTABLE_TYPES.to_h do |countable_type|
|
||||
[counter_key(countable_type), read(:create, countable_type)]
|
||||
end.to_h
|
||||
end
|
||||
end
|
||||
|
||||
def fallback_totals
|
||||
COUNTABLE_TYPES.map { |counter_key| [counter_key(counter_key), -1] }.to_h
|
||||
COUNTABLE_TYPES.to_h { |counter_key| [counter_key(counter_key), -1] }
|
||||
end
|
||||
|
||||
private
|
||||
|
|
|
@ -295,8 +295,8 @@ RSpec.describe 'Issues > Labels bulk assignment' do
|
|||
before do
|
||||
issue1.milestone = milestone
|
||||
issue2.milestone = milestone
|
||||
issue1.save
|
||||
issue2.save
|
||||
issue1.save!
|
||||
issue2.save!
|
||||
issue1.labels << bug
|
||||
issue2.labels << feature
|
||||
|
||||
|
|
|
@ -171,7 +171,7 @@ RSpec.describe 'Issue Sidebar' do
|
|||
|
||||
context 'editing issue labels', :js do
|
||||
before do
|
||||
issue.update(labels: [label])
|
||||
issue.update!(labels: [label])
|
||||
page.within('.block.labels') do
|
||||
click_on 'Edit'
|
||||
end
|
||||
|
|
|
@ -103,7 +103,7 @@ RSpec.describe 'Issue notes polling', :js do
|
|||
end
|
||||
|
||||
def update_note(note, new_text)
|
||||
note.update(note: new_text)
|
||||
note.update!(note: new_text)
|
||||
wait_for_requests
|
||||
end
|
||||
|
||||
|
|
|
@ -150,7 +150,7 @@ RSpec.describe 'User creates branch and merge request on issue page', :js do
|
|||
|
||||
context 'when merge requests are disabled' do
|
||||
before do
|
||||
project.project_feature.update(merge_requests_access_level: 0)
|
||||
project.project_feature.update!(merge_requests_access_level: 0)
|
||||
|
||||
visit project_issue_path(project, issue)
|
||||
end
|
||||
|
|
|
@ -38,7 +38,7 @@ RSpec.describe 'User creates confidential merge request on issue page', :js do
|
|||
let(:forked_project) { fork_project(project, user, repository: true) }
|
||||
|
||||
before do
|
||||
forked_project.update(visibility: Gitlab::VisibilityLevel::PRIVATE)
|
||||
forked_project.update!(visibility: Gitlab::VisibilityLevel::PRIVATE)
|
||||
visit_confidential_issue
|
||||
end
|
||||
|
||||
|
|
|
@ -78,7 +78,7 @@ RSpec.describe "Issues > User edits issue", :js do
|
|||
end
|
||||
|
||||
it 'warns about version conflict' do
|
||||
issue.update(title: "New title")
|
||||
issue.update!(title: "New title")
|
||||
|
||||
fill_in 'issue_title', with: 'bug 345'
|
||||
fill_in 'issue_description', with: 'bug description'
|
||||
|
@ -307,7 +307,7 @@ RSpec.describe "Issues > User edits issue", :js do
|
|||
before do
|
||||
project.add_guest(guest)
|
||||
issue.milestone = milestone
|
||||
issue.save
|
||||
issue.save!
|
||||
end
|
||||
|
||||
it 'shows milestone text' do
|
||||
|
|
|
@ -18,7 +18,7 @@ RSpec.describe 'User filters issues', :js do
|
|||
@issue = Issue.find_by(title: 'foobar')
|
||||
@issue.milestone = create(:milestone, project: project)
|
||||
@issue.assignees = []
|
||||
@issue.save
|
||||
@issue.save!
|
||||
end
|
||||
|
||||
let(:issue) { @issue }
|
||||
|
|
|
@ -18,7 +18,7 @@ RSpec.describe 'Issues > User sees live update', :js do
|
|||
|
||||
expect(page).to have_text("new title")
|
||||
|
||||
issue.update(title: "updated title")
|
||||
issue.update!(title: "updated title")
|
||||
|
||||
wait_for_requests
|
||||
expect(page).to have_text("updated title")
|
||||
|
|
|
@ -77,7 +77,7 @@ RSpec.describe "User sorts issues" do
|
|||
|
||||
it 'sorts by most recently updated', :js do
|
||||
issue3.updated_at = Time.now + 100
|
||||
issue3.save
|
||||
issue3.save!
|
||||
visit project_issues_path(project, sort: sort_value_recently_updated)
|
||||
|
||||
expect(first_issue).to include('baz')
|
||||
|
@ -85,8 +85,8 @@ RSpec.describe "User sorts issues" do
|
|||
|
||||
describe 'sorting by due date', :js do
|
||||
before do
|
||||
issue1.update(due_date: 1.day.from_now)
|
||||
issue2.update(due_date: 6.days.from_now)
|
||||
issue1.update!(due_date: 1.day.from_now)
|
||||
issue2.update!(due_date: 6.days.from_now)
|
||||
end
|
||||
|
||||
it 'sorts by due date' do
|
||||
|
@ -96,7 +96,7 @@ RSpec.describe "User sorts issues" do
|
|||
end
|
||||
|
||||
it 'sorts by due date by excluding nil due dates' do
|
||||
issue2.update(due_date: nil)
|
||||
issue2.update!(due_date: nil)
|
||||
|
||||
visit project_issues_path(project, sort: sort_value_due_date)
|
||||
|
||||
|
@ -111,7 +111,7 @@ RSpec.describe "User sorts issues" do
|
|||
end
|
||||
|
||||
it 'sorts by least recently due date by excluding nil due dates' do
|
||||
issue2.update(due_date: nil)
|
||||
issue2.update!(due_date: nil)
|
||||
|
||||
visit project_issues_path(project, label_names: [label.name], sort: sort_value_due_date_later)
|
||||
|
||||
|
@ -122,8 +122,8 @@ RSpec.describe "User sorts issues" do
|
|||
|
||||
describe 'filtering by due date', :js do
|
||||
before do
|
||||
issue1.update(due_date: 1.day.from_now)
|
||||
issue2.update(due_date: 6.days.from_now)
|
||||
issue1.update!(due_date: 1.day.from_now)
|
||||
issue2.update!(due_date: 6.days.from_now)
|
||||
end
|
||||
|
||||
it 'filters by none' do
|
||||
|
@ -147,9 +147,9 @@ RSpec.describe "User sorts issues" do
|
|||
end
|
||||
|
||||
it 'filters by due this week' do
|
||||
issue1.update(due_date: Date.today.beginning_of_week + 2.days)
|
||||
issue2.update(due_date: Date.today.end_of_week)
|
||||
issue3.update(due_date: Date.today - 8.days)
|
||||
issue1.update!(due_date: Date.today.beginning_of_week + 2.days)
|
||||
issue2.update!(due_date: Date.today.end_of_week)
|
||||
issue3.update!(due_date: Date.today - 8.days)
|
||||
|
||||
visit project_issues_path(project, due_date: Issue::DueThisWeek.name)
|
||||
|
||||
|
@ -161,9 +161,9 @@ RSpec.describe "User sorts issues" do
|
|||
end
|
||||
|
||||
it 'filters by due this month' do
|
||||
issue1.update(due_date: Date.today.beginning_of_month + 2.days)
|
||||
issue2.update(due_date: Date.today.end_of_month)
|
||||
issue3.update(due_date: Date.today - 50.days)
|
||||
issue1.update!(due_date: Date.today.beginning_of_month + 2.days)
|
||||
issue2.update!(due_date: Date.today.end_of_month)
|
||||
issue3.update!(due_date: Date.today - 50.days)
|
||||
|
||||
visit project_issues_path(project, due_date: Issue::DueThisMonth.name)
|
||||
|
||||
|
@ -175,9 +175,9 @@ RSpec.describe "User sorts issues" do
|
|||
end
|
||||
|
||||
it 'filters by overdue' do
|
||||
issue1.update(due_date: Date.today + 2.days)
|
||||
issue2.update(due_date: Date.today + 20.days)
|
||||
issue3.update(due_date: Date.yesterday)
|
||||
issue1.update!(due_date: Date.today + 2.days)
|
||||
issue2.update!(due_date: Date.today + 20.days)
|
||||
issue3.update!(due_date: Date.yesterday)
|
||||
|
||||
visit project_issues_path(project, due_date: Issue::Overdue.name)
|
||||
|
||||
|
@ -189,9 +189,9 @@ RSpec.describe "User sorts issues" do
|
|||
end
|
||||
|
||||
it 'filters by due next month and previous two weeks' do
|
||||
issue1.update(due_date: Date.today - 4.weeks)
|
||||
issue2.update(due_date: (Date.today + 2.months).beginning_of_month)
|
||||
issue3.update(due_date: Date.yesterday)
|
||||
issue1.update!(due_date: Date.today - 4.weeks)
|
||||
issue2.update!(due_date: (Date.today + 2.months).beginning_of_month)
|
||||
issue3.update!(due_date: Date.yesterday)
|
||||
|
||||
visit project_issues_path(project, due_date: Issue::DueNextMonthAndPreviousTwoWeeks.name)
|
||||
|
||||
|
@ -206,9 +206,9 @@ RSpec.describe "User sorts issues" do
|
|||
describe 'sorting by milestone', :js do
|
||||
before do
|
||||
issue1.milestone = newer_due_milestone
|
||||
issue1.save
|
||||
issue1.save!
|
||||
issue2.milestone = later_due_milestone
|
||||
issue2.save
|
||||
issue2.save!
|
||||
end
|
||||
|
||||
it 'sorts by milestone' do
|
||||
|
@ -224,9 +224,9 @@ RSpec.describe "User sorts issues" do
|
|||
|
||||
before do
|
||||
issue1.assignees << user2
|
||||
issue1.save
|
||||
issue1.save!
|
||||
issue2.assignees << user2
|
||||
issue2.save
|
||||
issue2.save!
|
||||
end
|
||||
|
||||
it 'sorts with a filter applied' do
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
import { isArray } from 'lodash';
|
||||
import * as testingLibrary from '@testing-library/dom';
|
||||
import { createWrapper, WrapperArray, mount, shallowMount } from '@vue/test-utils';
|
||||
import { isArray, upperFirst } from 'lodash';
|
||||
|
||||
const vNodeContainsText = (vnode, text) =>
|
||||
(vnode.text && vnode.text.includes(text)) ||
|
||||
|
@ -37,6 +39,17 @@ export const waitForMutation = (store, expectedMutationType) =>
|
|||
});
|
||||
|
||||
export const extendedWrapper = (wrapper) => {
|
||||
// https://testing-library.com/docs/queries/about
|
||||
const AVAILABLE_QUERIES = [
|
||||
'byRole',
|
||||
'byLabelText',
|
||||
'byPlaceholderText',
|
||||
'byText',
|
||||
'byDisplayValue',
|
||||
'byAltText',
|
||||
'byTitle',
|
||||
];
|
||||
|
||||
if (isArray(wrapper) || !wrapper?.find) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.warn(
|
||||
|
@ -56,5 +69,63 @@ export const extendedWrapper = (wrapper) => {
|
|||
return this.findAll(`[data-testid="${id}"]`);
|
||||
},
|
||||
},
|
||||
// `findBy`
|
||||
...AVAILABLE_QUERIES.reduce((accumulator, query) => {
|
||||
return {
|
||||
...accumulator,
|
||||
[`find${upperFirst(query)}`]: {
|
||||
value(text, options = {}) {
|
||||
const elements = testingLibrary[`queryAll${upperFirst(query)}`](
|
||||
wrapper.element,
|
||||
text,
|
||||
options,
|
||||
);
|
||||
|
||||
// Return VTU `ErrorWrapper` if element is not found
|
||||
// https://github.com/vuejs/vue-test-utils/blob/dev/packages/test-utils/src/error-wrapper.js
|
||||
// VTU does not expose `ErrorWrapper` so, as of now, this is the best way to
|
||||
// create an `ErrorWrapper`
|
||||
if (!elements.length) {
|
||||
const emptyElement = document.createElement('div');
|
||||
|
||||
return createWrapper(emptyElement).find('testing-library-element-not-found');
|
||||
}
|
||||
|
||||
return createWrapper(elements[0], this.options || {});
|
||||
},
|
||||
},
|
||||
};
|
||||
}, {}),
|
||||
// `findAllBy`
|
||||
...AVAILABLE_QUERIES.reduce((accumulator, query) => {
|
||||
return {
|
||||
...accumulator,
|
||||
[`findAll${upperFirst(query)}`]: {
|
||||
value(text, options = {}) {
|
||||
const elements = testingLibrary[`queryAll${upperFirst(query)}`](
|
||||
wrapper.element,
|
||||
text,
|
||||
options,
|
||||
);
|
||||
|
||||
const wrappers = elements.map((element) => {
|
||||
const elementWrapper = createWrapper(element, this.options || {});
|
||||
elementWrapper.selector = text;
|
||||
|
||||
return elementWrapper;
|
||||
});
|
||||
|
||||
const wrapperArray = new WrapperArray(wrappers);
|
||||
wrapperArray.selector = text;
|
||||
|
||||
return wrapperArray;
|
||||
},
|
||||
},
|
||||
};
|
||||
}, {}),
|
||||
});
|
||||
};
|
||||
|
||||
export const shallowMountExtended = (...args) => extendedWrapper(shallowMount(...args));
|
||||
|
||||
export const mountExtended = (...args) => extendedWrapper(mount(...args));
|
||||
|
|
|
@ -1,7 +1,27 @@
|
|||
import { shallowMount } from '@vue/test-utils';
|
||||
import { extendedWrapper, shallowWrapperContainsSlotText } from './vue_test_utils_helper';
|
||||
import * as testingLibrary from '@testing-library/dom';
|
||||
import * as vtu from '@vue/test-utils';
|
||||
import {
|
||||
shallowMount,
|
||||
Wrapper as VTUWrapper,
|
||||
WrapperArray as VTUWrapperArray,
|
||||
} from '@vue/test-utils';
|
||||
import {
|
||||
extendedWrapper,
|
||||
shallowMountExtended,
|
||||
mountExtended,
|
||||
shallowWrapperContainsSlotText,
|
||||
} from './vue_test_utils_helper';
|
||||
|
||||
jest.mock('@testing-library/dom', () => ({
|
||||
__esModule: true,
|
||||
...jest.requireActual('@testing-library/dom'),
|
||||
}));
|
||||
|
||||
describe('Vue test utils helpers', () => {
|
||||
afterAll(() => {
|
||||
jest.unmock('@testing-library/dom');
|
||||
});
|
||||
|
||||
describe('shallowWrapperContainsSlotText', () => {
|
||||
const mockText = 'text';
|
||||
const mockSlot = `<div>${mockText}</div>`;
|
||||
|
@ -84,7 +104,7 @@ describe('Vue test utils helpers', () => {
|
|||
);
|
||||
});
|
||||
|
||||
it('should find the component by test id', () => {
|
||||
it('should find the element by test id', () => {
|
||||
expect(mockComponent.findByTestId(testId).exists()).toBe(true);
|
||||
});
|
||||
});
|
||||
|
@ -105,5 +125,187 @@ describe('Vue test utils helpers', () => {
|
|||
expect(mockComponent.findAllByTestId(testId)).toHaveLength(2);
|
||||
});
|
||||
});
|
||||
|
||||
describe.each`
|
||||
findMethod | expectedQuery
|
||||
${'findByRole'} | ${'queryAllByRole'}
|
||||
${'findByLabelText'} | ${'queryAllByLabelText'}
|
||||
${'findByPlaceholderText'} | ${'queryAllByPlaceholderText'}
|
||||
${'findByText'} | ${'queryAllByText'}
|
||||
${'findByDisplayValue'} | ${'queryAllByDisplayValue'}
|
||||
${'findByAltText'} | ${'queryAllByAltText'}
|
||||
`('$findMethod', ({ findMethod, expectedQuery }) => {
|
||||
const text = 'foo bar';
|
||||
const options = { selector: 'div' };
|
||||
const mockDiv = document.createElement('div');
|
||||
|
||||
let wrapper;
|
||||
beforeEach(() => {
|
||||
wrapper = extendedWrapper(
|
||||
shallowMount({
|
||||
template: `<div>foo bar</div>`,
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
it(`calls Testing Library \`${expectedQuery}\` function with correct parameters`, () => {
|
||||
jest.spyOn(testingLibrary, expectedQuery).mockImplementation(() => [mockDiv]);
|
||||
|
||||
wrapper[findMethod](text, options);
|
||||
|
||||
expect(testingLibrary[expectedQuery]).toHaveBeenLastCalledWith(
|
||||
wrapper.element,
|
||||
text,
|
||||
options,
|
||||
);
|
||||
});
|
||||
|
||||
describe('when element is found', () => {
|
||||
beforeEach(() => {
|
||||
jest.spyOn(testingLibrary, expectedQuery).mockImplementation(() => [mockDiv]);
|
||||
jest.spyOn(vtu, 'createWrapper');
|
||||
});
|
||||
|
||||
it('returns a VTU wrapper', () => {
|
||||
const result = wrapper[findMethod](text, options);
|
||||
|
||||
expect(vtu.createWrapper).toHaveBeenCalledWith(mockDiv, wrapper.options);
|
||||
expect(result).toBeInstanceOf(VTUWrapper);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when multiple elements are found', () => {
|
||||
beforeEach(() => {
|
||||
const mockSpan = document.createElement('span');
|
||||
jest.spyOn(testingLibrary, expectedQuery).mockImplementation(() => [mockDiv, mockSpan]);
|
||||
jest.spyOn(vtu, 'createWrapper');
|
||||
});
|
||||
|
||||
it('returns the first element as a VTU wrapper', () => {
|
||||
const result = wrapper[findMethod](text, options);
|
||||
|
||||
expect(vtu.createWrapper).toHaveBeenCalledWith(mockDiv, wrapper.options);
|
||||
expect(result).toBeInstanceOf(VTUWrapper);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when element is not found', () => {
|
||||
beforeEach(() => {
|
||||
jest.spyOn(testingLibrary, expectedQuery).mockImplementation(() => []);
|
||||
});
|
||||
|
||||
it('returns a VTU error wrapper', () => {
|
||||
expect(wrapper[findMethod](text, options).exists()).toBe(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe.each`
|
||||
findMethod | expectedQuery
|
||||
${'findAllByRole'} | ${'queryAllByRole'}
|
||||
${'findAllByLabelText'} | ${'queryAllByLabelText'}
|
||||
${'findAllByPlaceholderText'} | ${'queryAllByPlaceholderText'}
|
||||
${'findAllByText'} | ${'queryAllByText'}
|
||||
${'findAllByDisplayValue'} | ${'queryAllByDisplayValue'}
|
||||
${'findAllByAltText'} | ${'queryAllByAltText'}
|
||||
`('$findMethod', ({ findMethod, expectedQuery }) => {
|
||||
const text = 'foo bar';
|
||||
const options = { selector: 'div' };
|
||||
const mockElements = [
|
||||
document.createElement('li'),
|
||||
document.createElement('li'),
|
||||
document.createElement('li'),
|
||||
];
|
||||
|
||||
let wrapper;
|
||||
beforeEach(() => {
|
||||
wrapper = extendedWrapper(
|
||||
shallowMount({
|
||||
template: `
|
||||
<ul>
|
||||
<li>foo</li>
|
||||
<li>bar</li>
|
||||
<li>baz</li>
|
||||
</ul>
|
||||
`,
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
it(`calls Testing Library \`${expectedQuery}\` function with correct parameters`, () => {
|
||||
jest.spyOn(testingLibrary, expectedQuery).mockImplementation(() => mockElements);
|
||||
|
||||
wrapper[findMethod](text, options);
|
||||
|
||||
expect(testingLibrary[expectedQuery]).toHaveBeenLastCalledWith(
|
||||
wrapper.element,
|
||||
text,
|
||||
options,
|
||||
);
|
||||
});
|
||||
|
||||
describe('when elements are found', () => {
|
||||
beforeEach(() => {
|
||||
jest.spyOn(testingLibrary, expectedQuery).mockImplementation(() => mockElements);
|
||||
});
|
||||
|
||||
it('returns a VTU wrapper array', () => {
|
||||
const result = wrapper[findMethod](text, options);
|
||||
|
||||
expect(result).toBeInstanceOf(VTUWrapperArray);
|
||||
expect(
|
||||
result.wrappers.every(
|
||||
(resultWrapper) =>
|
||||
resultWrapper instanceof VTUWrapper && resultWrapper.options === wrapper.options,
|
||||
),
|
||||
).toBe(true);
|
||||
expect(result.length).toBe(3);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when elements are not found', () => {
|
||||
beforeEach(() => {
|
||||
jest.spyOn(testingLibrary, expectedQuery).mockImplementation(() => []);
|
||||
});
|
||||
|
||||
it('returns an empty VTU wrapper array', () => {
|
||||
const result = wrapper[findMethod](text, options);
|
||||
|
||||
expect(result).toBeInstanceOf(VTUWrapperArray);
|
||||
expect(result.length).toBe(0);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe.each`
|
||||
mountExtendedFunction | expectedMountFunction
|
||||
${shallowMountExtended} | ${'shallowMount'}
|
||||
${mountExtended} | ${'mount'}
|
||||
`('$mountExtendedFunction', ({ mountExtendedFunction, expectedMountFunction }) => {
|
||||
const FakeComponent = jest.fn();
|
||||
const options = {
|
||||
propsData: {
|
||||
foo: 'bar',
|
||||
},
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
const mockWrapper = { find: jest.fn() };
|
||||
jest.spyOn(vtu, expectedMountFunction).mockImplementation(() => mockWrapper);
|
||||
});
|
||||
|
||||
it(`calls \`${expectedMountFunction}\` with passed arguments`, () => {
|
||||
mountExtendedFunction(FakeComponent, options);
|
||||
|
||||
expect(vtu[expectedMountFunction]).toHaveBeenCalledWith(FakeComponent, options);
|
||||
});
|
||||
|
||||
it('returns extended wrapper', () => {
|
||||
const result = mountExtendedFunction(FakeComponent, options);
|
||||
|
||||
expect(result).toHaveProperty('find');
|
||||
expect(result).toHaveProperty('findByTestId');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -42,6 +42,7 @@ describe('AccessRequestActionButtons', () => {
|
|||
memberId: member.id,
|
||||
title: 'Deny access',
|
||||
isAccessRequest: true,
|
||||
isInvite: false,
|
||||
icon: 'close',
|
||||
});
|
||||
});
|
||||
|
|
|
@ -43,6 +43,7 @@ describe('InviteActionButtons', () => {
|
|||
message: `Are you sure you want to revoke the invitation for ${member.invite.email} to join "${member.source.fullName}"`,
|
||||
title: 'Revoke invite',
|
||||
isAccessRequest: false,
|
||||
isInvite: true,
|
||||
icon: 'remove',
|
||||
});
|
||||
});
|
||||
|
|
|
@ -28,6 +28,7 @@ describe('RemoveMemberButton', () => {
|
|||
message: 'Are you sure you want to remove John Smith?',
|
||||
title: 'Remove member',
|
||||
isAccessRequest: true,
|
||||
isInvite: true,
|
||||
...propsData,
|
||||
},
|
||||
directives: {
|
||||
|
@ -48,6 +49,7 @@ describe('RemoveMemberButton', () => {
|
|||
'data-member-type': 'GroupMember',
|
||||
'data-message': 'Are you sure you want to remove John Smith?',
|
||||
'data-is-access-request': 'true',
|
||||
'data-is-invite': 'true',
|
||||
'aria-label': 'Remove member',
|
||||
title: 'Remove member',
|
||||
icon: 'remove',
|
||||
|
|
|
@ -43,6 +43,7 @@ describe('UserActionButtons', () => {
|
|||
message: `Are you sure you want to remove ${member.user.name} from "${member.source.fullName}"`,
|
||||
title: 'Remove member',
|
||||
isAccessRequest: false,
|
||||
isInvite: false,
|
||||
icon: 'remove',
|
||||
});
|
||||
});
|
||||
|
|
|
@ -15,16 +15,18 @@ describe('RemoveMemberModal', () => {
|
|||
});
|
||||
|
||||
describe.each`
|
||||
state | memberType | isAccessRequest | actionText | removeSubMembershipsCheckboxExpected | unassignIssuablesCheckboxExpected | message
|
||||
${'removing a group member'} | ${'GroupMember'} | ${'false'} | ${'Remove member'} | ${true} | ${true} | ${'Are you sure you want to remove Jane Doe from the Gitlab Org / Gitlab Test project?'}
|
||||
${'removing a project member'} | ${'ProjectMember'} | ${'false'} | ${'Remove member'} | ${false} | ${true} | ${'Are you sure you want to remove Jane Doe from the Gitlab Org / Gitlab Test project?'}
|
||||
${'denying an access request'} | ${'ProjectMember'} | ${'true'} | ${'Deny access request'} | ${false} | ${false} | ${"Are you sure you want to deny Jane Doe's request to join the Gitlab Org / Gitlab Test project?"}
|
||||
state | memberType | isAccessRequest | isInvite | actionText | removeSubMembershipsCheckboxExpected | unassignIssuablesCheckboxExpected | message
|
||||
${'removing a group member'} | ${'GroupMember'} | ${'false'} | ${'false'} | ${'Remove member'} | ${true} | ${true} | ${'Are you sure you want to remove Jane Doe from the Gitlab Org / Gitlab Test project?'}
|
||||
${'removing a project member'} | ${'ProjectMember'} | ${'false'} | ${'false'} | ${'Remove member'} | ${false} | ${true} | ${'Are you sure you want to remove Jane Doe from the Gitlab Org / Gitlab Test project?'}
|
||||
${'denying an access request'} | ${'ProjectMember'} | ${'true'} | ${'false'} | ${'Deny access request'} | ${false} | ${false} | ${"Are you sure you want to deny Jane Doe's request to join the Gitlab Org / Gitlab Test project?"}
|
||||
${'revoking invite'} | ${'ProjectMember'} | ${'false'} | ${'true'} | ${'Revoke invite'} | ${false} | ${false} | ${'Are you sure you want to revoke the invitation for foo@bar.com to join the Gitlab Org / Gitlab Test project?'}
|
||||
`(
|
||||
'when $state',
|
||||
({
|
||||
actionText,
|
||||
memberType,
|
||||
isAccessRequest,
|
||||
isInvite,
|
||||
message,
|
||||
removeSubMembershipsCheckboxExpected,
|
||||
unassignIssuablesCheckboxExpected,
|
||||
|
@ -35,6 +37,7 @@ describe('RemoveMemberModal', () => {
|
|||
return {
|
||||
modalData: {
|
||||
isAccessRequest,
|
||||
isInvite,
|
||||
message,
|
||||
memberPath,
|
||||
memberType,
|
||||
|
|
|
@ -13,6 +13,7 @@ RSpec.describe Resolvers::Ci::JobsResolver do
|
|||
create(:ci_build, :sast, name: 'DAST job', pipeline: pipeline)
|
||||
create(:ci_build, :dast, name: 'SAST job', pipeline: pipeline)
|
||||
create(:ci_build, :container_scanning, name: 'Container scanning job', pipeline: pipeline)
|
||||
create(:ci_build, name: 'Job with tags', pipeline: pipeline, tag_list: ['review'])
|
||||
end
|
||||
|
||||
describe '#resolve' do
|
||||
|
@ -24,7 +25,8 @@ RSpec.describe Resolvers::Ci::JobsResolver do
|
|||
have_attributes(name: 'Normal job'),
|
||||
have_attributes(name: 'DAST job'),
|
||||
have_attributes(name: 'SAST job'),
|
||||
have_attributes(name: 'Container scanning job')
|
||||
have_attributes(name: 'Container scanning job'),
|
||||
have_attributes(name: 'Job with tags')
|
||||
)
|
||||
end
|
||||
end
|
||||
|
@ -43,5 +45,18 @@ RSpec.describe Resolvers::Ci::JobsResolver do
|
|||
)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when a job has tags' do
|
||||
it "returns jobs with tags when applicable" do
|
||||
jobs = resolve(described_class, obj: pipeline)
|
||||
expect(jobs).to contain_exactly(
|
||||
have_attributes(tag_list: []),
|
||||
have_attributes(tag_list: []),
|
||||
have_attributes(tag_list: []),
|
||||
have_attributes(tag_list: []),
|
||||
have_attributes(tag_list: ['review'])
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -33,6 +33,7 @@ RSpec.describe Types::Ci::JobType do
|
|||
stage
|
||||
started_at
|
||||
status
|
||||
tags
|
||||
]
|
||||
|
||||
expect(described_class).to have_graphql_fields(*expected_fields)
|
||||
|
|
|
@ -26,6 +26,7 @@ RSpec.describe BatchDestroyDependentAssociations do
|
|||
let_it_be(:project) { create(:project) }
|
||||
let_it_be(:build) { create(:ci_build, project: project) }
|
||||
let_it_be(:notification_setting) { create(:notification_setting, project: project) }
|
||||
|
||||
let!(:todos) { create(:todo, project: project) }
|
||||
|
||||
it 'destroys multiple builds' do
|
||||
|
|
|
@ -4,6 +4,7 @@ require 'spec_helper'
|
|||
|
||||
RSpec.describe Featurable do
|
||||
let_it_be(:user) { create(:user) }
|
||||
|
||||
let(:project) { create(:project) }
|
||||
let(:feature_class) { subject.class }
|
||||
let(:features) { feature_class::FEATURES }
|
||||
|
|
|
@ -14,8 +14,8 @@ RSpec.describe API::CommitStatuses do
|
|||
let(:get_url) { "/projects/#{project.id}/repository/commits/#{sha}/statuses" }
|
||||
|
||||
context 'ci commit exists' do
|
||||
let!(:master) { project.ci_pipelines.create(source: :push, sha: commit.id, ref: 'master', protected: false) }
|
||||
let!(:develop) { project.ci_pipelines.create(source: :push, sha: commit.id, ref: 'develop', protected: false) }
|
||||
let!(:master) { project.ci_pipelines.create!(source: :push, sha: commit.id, ref: 'master', protected: false) }
|
||||
let!(:develop) { project.ci_pipelines.create!(source: :push, sha: commit.id, ref: 'develop', protected: false) }
|
||||
|
||||
context "reporter user" do
|
||||
let(:statuses_id) { json_response.map { |status| status['id'] } }
|
||||
|
@ -270,8 +270,8 @@ RSpec.describe API::CommitStatuses do
|
|||
end
|
||||
|
||||
context 'when a pipeline id is specified' do
|
||||
let!(:first_pipeline) { project.ci_pipelines.create(source: :push, sha: commit.id, ref: 'master', status: 'created') }
|
||||
let!(:other_pipeline) { project.ci_pipelines.create(source: :push, sha: commit.id, ref: 'master', status: 'created') }
|
||||
let!(:first_pipeline) { project.ci_pipelines.create!(source: :push, sha: commit.id, ref: 'master', status: 'created') }
|
||||
let!(:other_pipeline) { project.ci_pipelines.create!(source: :push, sha: commit.id, ref: 'master', status: 'created') }
|
||||
|
||||
subject do
|
||||
post api(post_url, developer), params: {
|
||||
|
|
|
@ -345,7 +345,7 @@ RSpec.describe API::Deployments do
|
|||
|
||||
context 'as a maintainer' do
|
||||
it 'returns a 403 when updating a deployment with a build' do
|
||||
deploy.update(deployable: build)
|
||||
deploy.update!(deployable: build)
|
||||
|
||||
put(
|
||||
api("/projects/#{project.id}/deployments/#{deploy.id}", user),
|
||||
|
@ -394,7 +394,7 @@ RSpec.describe API::Deployments do
|
|||
end
|
||||
|
||||
it 'returns a 403 when updating a deployment with a build' do
|
||||
deploy.update(deployable: build)
|
||||
deploy.update!(deployable: build)
|
||||
|
||||
put(
|
||||
api("/projects/#{project.id}/deployments/#{deploy.id}", developer),
|
||||
|
|
|
@ -214,7 +214,7 @@ RSpec.describe API::Environments do
|
|||
context 'as a maintainer' do
|
||||
context 'with a stoppable environment' do
|
||||
before do
|
||||
environment.update(state: :available)
|
||||
environment.update!(state: :available)
|
||||
|
||||
post api("/projects/#{project.id}/environments/#{environment.id}/stop", user)
|
||||
end
|
||||
|
|
|
@ -363,7 +363,7 @@ RSpec.describe API::GoProxy do
|
|||
let(:module_name) { base }
|
||||
|
||||
before do
|
||||
project.update(visibility_level: Gitlab::VisibilityLevel::PRIVATE)
|
||||
project.update!(visibility_level: Gitlab::VisibilityLevel::PRIVATE)
|
||||
end
|
||||
|
||||
describe 'GET /projects/:id/packages/go/*module_name/@v/list' do
|
||||
|
@ -412,7 +412,7 @@ RSpec.describe API::GoProxy do
|
|||
let(:module_name) { base }
|
||||
|
||||
before do
|
||||
project.update(visibility_level: Gitlab::VisibilityLevel::PUBLIC)
|
||||
project.update!(visibility_level: Gitlab::VisibilityLevel::PUBLIC)
|
||||
end
|
||||
|
||||
describe 'GET /projects/:id/packages/go/*module_name/@v/list' do
|
||||
|
|
|
@ -52,7 +52,7 @@ RSpec.describe 'Setting labels of a merge request' do
|
|||
end
|
||||
|
||||
it 'sets the merge request labels, removing existing ones' do
|
||||
merge_request.update(labels: [label2])
|
||||
merge_request.update!(labels: [label2])
|
||||
|
||||
post_graphql_mutation(mutation, current_user: current_user)
|
||||
|
||||
|
|
|
@ -218,12 +218,14 @@ RSpec.describe API::GroupImport do
|
|||
stub_uploads_object_storage(ImportExportUploader, direct_upload: true)
|
||||
end
|
||||
|
||||
# rubocop:disable Rails/SaveBang
|
||||
let(:tmp_object) do
|
||||
fog_connection.directories.new(key: 'uploads').files.create(
|
||||
key: "tmp/uploads/#{file_name}",
|
||||
body: file_upload
|
||||
)
|
||||
end
|
||||
# rubocop:enable Rails/SaveBang
|
||||
|
||||
let(:fog_file) { fog_to_uploaded_file(tmp_object) }
|
||||
let(:params) do
|
||||
|
|
|
@ -20,7 +20,7 @@ RSpec.describe API::GroupMilestones do
|
|||
let_it_be(:params) { { include_parent_milestones: true } }
|
||||
|
||||
before_all do
|
||||
group.update(parent: ancestor_group)
|
||||
group.update!(parent: ancestor_group)
|
||||
end
|
||||
|
||||
shared_examples 'listing all milestones' do
|
||||
|
@ -83,9 +83,9 @@ RSpec.describe API::GroupMilestones do
|
|||
end
|
||||
|
||||
def setup_for_group
|
||||
context_group.update(visibility_level: Gitlab::VisibilityLevel::PUBLIC)
|
||||
context_group.update!(visibility_level: Gitlab::VisibilityLevel::PUBLIC)
|
||||
context_group.add_developer(user)
|
||||
public_project.update(namespace: context_group)
|
||||
public_project.update!(namespace: context_group)
|
||||
context_group.reload
|
||||
end
|
||||
end
|
||||
|
|
|
@ -887,7 +887,7 @@ RSpec.describe API::Internal::Base do
|
|||
context 'project does not exist' do
|
||||
context 'git pull' do
|
||||
it 'returns a 200 response with status: false' do
|
||||
project.destroy
|
||||
project.destroy!
|
||||
|
||||
pull(key, project)
|
||||
|
||||
|
|
|
@ -754,7 +754,7 @@ RSpec.describe API::Issues do
|
|||
let(:parent_group) { create(:group) }
|
||||
|
||||
before do
|
||||
group.update(parent_id: parent_group.id)
|
||||
group.update!(parent_id: parent_group.id)
|
||||
group_closed_issue.reload
|
||||
end
|
||||
|
||||
|
|
|
@ -111,7 +111,7 @@ RSpec.describe API::Issues do
|
|||
let(:not_member) { create(:user) }
|
||||
|
||||
before do
|
||||
project.project_feature.update(issues_access_level: ProjectFeature::PRIVATE)
|
||||
project.project_feature.update!(issues_access_level: ProjectFeature::PRIVATE)
|
||||
end
|
||||
|
||||
it 'renders 403' do
|
||||
|
|
|
@ -215,7 +215,7 @@ RSpec.describe API::Jobs do
|
|||
first_build = create(:ci_build, :trace_artifact, :artifacts, :test_reports, pipeline: pipeline)
|
||||
first_build.runner = create(:ci_runner)
|
||||
first_build.user = create(:user)
|
||||
first_build.save
|
||||
first_build.save!
|
||||
|
||||
control_count = ActiveRecord::QueryRecorder.new { go }.count
|
||||
|
||||
|
@ -223,7 +223,7 @@ RSpec.describe API::Jobs do
|
|||
second_build = create(:ci_build, :trace_artifact, :artifacts, :test_reports, pipeline: second_pipeline)
|
||||
second_build.runner = create(:ci_runner)
|
||||
second_build.user = create(:user)
|
||||
second_build.save
|
||||
second_build.save!
|
||||
|
||||
expect { go }.not_to exceed_query_limit(control_count)
|
||||
end
|
||||
|
@ -684,7 +684,7 @@ RSpec.describe API::Jobs do
|
|||
context 'with regular branch' do
|
||||
before do
|
||||
pipeline.reload
|
||||
pipeline.update(ref: 'master',
|
||||
pipeline.update!(ref: 'master',
|
||||
sha: project.commit('master').sha)
|
||||
|
||||
get_for_ref('master')
|
||||
|
@ -696,7 +696,7 @@ RSpec.describe API::Jobs do
|
|||
context 'with branch name containing slash' do
|
||||
before do
|
||||
pipeline.reload
|
||||
pipeline.update(ref: 'improve/awesome',
|
||||
pipeline.update!(ref: 'improve/awesome',
|
||||
sha: project.commit('improve/awesome').sha)
|
||||
end
|
||||
|
||||
|
@ -732,7 +732,7 @@ RSpec.describe API::Jobs do
|
|||
stub_artifacts_object_storage
|
||||
job.success
|
||||
|
||||
project.update(visibility_level: visibility_level,
|
||||
project.update!(visibility_level: visibility_level,
|
||||
public_builds: public_builds)
|
||||
|
||||
get_artifact_file(artifact)
|
||||
|
@ -826,7 +826,7 @@ RSpec.describe API::Jobs do
|
|||
context 'with branch name containing slash' do
|
||||
before do
|
||||
pipeline.reload
|
||||
pipeline.update(ref: 'improve/awesome',
|
||||
pipeline.update!(ref: 'improve/awesome',
|
||||
sha: project.commit('improve/awesome').sha)
|
||||
end
|
||||
|
||||
|
|
|
@ -119,7 +119,7 @@ RSpec.describe API::Labels do
|
|||
|
||||
expect(label).not_to be_nil
|
||||
|
||||
label.priorities.create(project: label.project, priority: 1)
|
||||
label.priorities.create!(project: label.project, priority: 1)
|
||||
label.save!
|
||||
|
||||
request_params = {
|
||||
|
@ -139,7 +139,7 @@ RSpec.describe API::Labels do
|
|||
expect(label).not_to be_nil
|
||||
label_id = spec_params[:name] || spec_params[:label_id]
|
||||
|
||||
label.priorities.create(project: label.project, priority: 1)
|
||||
label.priorities.create!(project: label.project, priority: 1)
|
||||
label.save!
|
||||
|
||||
request_params = {
|
||||
|
@ -383,7 +383,7 @@ RSpec.describe API::Labels do
|
|||
it 'returns 409 if label already exists in group' do
|
||||
group = create(:group)
|
||||
group_label = create(:group_label, group: group)
|
||||
project.update(group: group)
|
||||
project.update!(group: group)
|
||||
|
||||
post api("/projects/#{project.id}/labels", user),
|
||||
params: {
|
||||
|
|
|
@ -235,12 +235,14 @@ RSpec.describe API::ProjectImport do
|
|||
stub_uploads_object_storage(ImportExportUploader, direct_upload: true)
|
||||
end
|
||||
|
||||
# rubocop:disable Rails/SaveBang
|
||||
let(:tmp_object) do
|
||||
fog_connection.directories.new(key: 'uploads').files.create(
|
||||
key: "tmp/uploads/#{file_name}",
|
||||
body: fixture_file_upload(file)
|
||||
)
|
||||
end
|
||||
# rubocop:enable Rails/SaveBang
|
||||
|
||||
let(:file_upload) { fog_to_uploaded_file(tmp_object) }
|
||||
|
||||
|
@ -285,7 +287,7 @@ RSpec.describe API::ProjectImport do
|
|||
it 'returns the import status and the error if failed' do
|
||||
project = create(:project, :import_failed)
|
||||
project.add_maintainer(user)
|
||||
project.import_state.update(last_error: 'error')
|
||||
project.import_state.update!(last_error: 'error')
|
||||
|
||||
get api("/projects/#{project.id}/import", user)
|
||||
|
||||
|
|
Loading…
Reference in a new issue