diff options
36 files changed, 493 insertions, 192 deletions
diff --git a/app/assets/javascripts/boards/components/board.js b/app/assets/javascripts/boards/components/board.js index 8ebdfede8f7..a6deb656b37 100644 --- a/app/assets/javascripts/boards/components/board.js +++ b/app/assets/javascripts/boards/components/board.js @@ -3,7 +3,7 @@ import Sortable from 'sortablejs'; import Vue from 'vue'; import { GlButtonGroup, GlButton, GlTooltip } from '@gitlab/ui'; import isWipLimitsOn from 'ee_else_ce/boards/mixins/is_wip_limits'; -import { n__, s__ } from '~/locale'; +import { s__, __, sprintf } from '~/locale'; import Icon from '~/vue_shared/components/icon.vue'; import Tooltip from '~/vue_shared/directives/tooltip'; import AccessorUtilities from '../../lib/utils/accessor'; @@ -67,10 +67,13 @@ export default Vue.extend({ !this.disabled && this.list.type !== ListType.closed && this.list.type !== ListType.blank ); }, - counterTooltip() { + issuesTooltip() { const { issuesSize } = this.list; - return `${n__('%d issue', '%d issues', issuesSize)}`; + + return sprintf(__('%{issuesSize} issues'), { issuesSize }); }, + // Only needed to make karma pass. + weightCountToolTip() {}, // eslint-disable-line vue/return-in-computed-property caretTooltip() { return this.list.isExpanded ? s__('Boards|Collapse') : s__('Boards|Expand'); }, diff --git a/app/assets/javascripts/create_cluster/eks_cluster/components/service_credentials_form.vue b/app/assets/javascripts/create_cluster/eks_cluster/components/service_credentials_form.vue index 1dd4c468ae6..49a5d4657af 100644 --- a/app/assets/javascripts/create_cluster/eks_cluster/components/service_credentials_form.vue +++ b/app/assets/javascripts/create_cluster/eks_cluster/components/service_credentials_form.vue @@ -82,7 +82,7 @@ export default { }; </script> <template> - <form name="service-credentials-form" @submit.prevent="createRole({ roleArn, externalId })"> + <form name="service-credentials-form"> <h2>{{ s__('ClusterIntegration|Authenticate with Amazon Web Services') }}</h2> <p> {{ @@ -136,6 +136,7 @@ export default { :disabled="submitButtonDisabled" :loading="isCreatingRole" :label="submitButtonLabel" + @click.prevent="createRole({ roleArn, externalId })" /> </form> </template> diff --git a/app/assets/javascripts/error_tracking/components/error_details.vue b/app/assets/javascripts/error_tracking/components/error_details.vue index 3d3447012db..51a1ae50467 100644 --- a/app/assets/javascripts/error_tracking/components/error_details.vue +++ b/app/assets/javascripts/error_tracking/components/error_details.vue @@ -235,18 +235,23 @@ export default { >{{ error.tags.logger }} </gl-badge> </template> - - <h3>{{ __('Error details') }}</h3> <ul> + <li v-if="GQLerror.gitlabCommit"> + <strong class="bold">{{ __('GitLab commit') }}:</strong> + <gl-link :href="GQLerror.gitlabCommitPath"> + <span>{{ GQLerror.gitlabCommit.substr(0, 10) }}</span> + </gl-link> + </li> <li v-if="error.gitlab_issue"> - <span class="bold">{{ __('GitLab Issue') }}:</span> + <strong class="bold">{{ __('GitLab Issue') }}:</strong> <gl-link :href="error.gitlab_issue"> <span>{{ error.gitlab_issue }}</span> </gl-link> </li> <li> - <span class="bold">{{ __('Sentry event') }}:</span> + <strong class="bold">{{ __('Sentry event') }}:</strong> <gl-link + class="d-inline-flex align-items-center" v-track-event="trackClickErrorLinkToSentryOptions(GQLerror.externalUrl)" :href="GQLerror.externalUrl" target="_blank" @@ -256,25 +261,25 @@ export default { </gl-link> </li> <li v-if="GQLerror.firstReleaseShortVersion"> - <span class="bold">{{ __('First seen') }}:</span> + <strong class="bold">{{ __('First seen') }}:</strong> {{ formatDate(GQLerror.firstSeen) }} <gl-link :href="firstReleaseLink" target="_blank"> - <span>{{ __('Release') }}: {{ GQLerror.firstReleaseShortVersion }}</span> + <span>{{ __('Release') }}: {{ GQLerror.firstReleaseShortVersion.substr(0, 10) }}</span> </gl-link> </li> <li v-if="GQLerror.lastReleaseShortVersion"> - <span class="bold">{{ __('Last seen') }}:</span> + <strong class="bold">{{ __('Last seen') }}:</strong> {{ formatDate(GQLerror.lastSeen) }} <gl-link :href="lastReleaseLink" target="_blank"> - <span>{{ __('Release') }}: {{ GQLerror.lastReleaseShortVersion }}</span> + <span>{{ __('Release') }}: {{ GQLerror.lastReleaseShortVersion.substr(0, 10) }}</span> </gl-link> </li> <li> - <span class="bold">{{ __('Events') }}:</span> + <strong class="bold">{{ __('Events') }}:</strong> <span>{{ GQLerror.count }}</span> </li> <li> - <span class="bold">{{ __('Users') }}:</span> + <strong class="bold">{{ __('Users') }}:</strong> <span>{{ GQLerror.userCount }}</span> </li> </ul> diff --git a/app/assets/javascripts/error_tracking/queries/details.query.graphql b/app/assets/javascripts/error_tracking/queries/details.query.graphql index f65bdb9b968..625ce3030d9 100644 --- a/app/assets/javascripts/error_tracking/queries/details.query.graphql +++ b/app/assets/javascripts/error_tracking/queries/details.query.graphql @@ -13,6 +13,8 @@ query errorDetails($fullPath: ID!, $errorId: ID!) { externalUrl firstReleaseShortVersion lastReleaseShortVersion + gitlabCommit + gitlabCommitPath } } } diff --git a/app/assets/javascripts/ide/components/commit_sidebar/form.vue b/app/assets/javascripts/ide/components/commit_sidebar/form.vue index 002c00599bb..9d5473a1201 100644 --- a/app/assets/javascripts/ide/components/commit_sidebar/form.vue +++ b/app/assets/javascripts/ide/components/commit_sidebar/form.vue @@ -60,6 +60,11 @@ export default { ); } }, + + lastCommitMsg() { + this.isCompact = + this.currentActivityView !== activityBarViews.commit && this.lastCommitMsg === ''; + }, }, methods: { ...mapActions(['updateActivityBarView']), diff --git a/app/controllers/registrations_controller.rb b/app/controllers/registrations_controller.rb index 5fc7f5c84f0..c0ba87bf3ed 100644 --- a/app/controllers/registrations_controller.rb +++ b/app/controllers/registrations_controller.rb @@ -60,7 +60,7 @@ class RegistrationsController < Devise::RegistrationsController end def update_registration - user_params = params.require(:user).permit(:name, :role, :setup_for_company) + user_params = params.require(:user).permit(:role, :setup_for_company) result = ::Users::SignupService.new(current_user, user_params).execute if result[:status] == :success @@ -152,13 +152,7 @@ class RegistrationsController < Devise::RegistrationsController end def sign_up_params - clean_params = params.require(:user).permit(:username, :email, :email_confirmation, :name, :password) - - if experiment_enabled?(:signup_flow) - clean_params[:name] = clean_params[:username] - end - - clean_params + params.require(:user).permit(:username, :email, :email_confirmation, :name, :first_name, :last_name, :password) end def resource_name diff --git a/app/models/user.rb b/app/models/user.rb index 4bba4d47b8f..df54f358ffa 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -164,9 +164,9 @@ class User < ApplicationRecord # Validations # # Note: devise :validatable above adds validations for :email and :password - validates :name, presence: true, length: { maximum: 128 } - validates :first_name, length: { maximum: 255 } - validates :last_name, length: { maximum: 255 } + validates :name, presence: true, length: { maximum: 255 } + validates :first_name, length: { maximum: 127 } + validates :last_name, length: { maximum: 127 } validates :email, confirmation: true validates :notification_email, presence: true validates :notification_email, devise_email: true, if: ->(user) { user.notification_email != user.email } diff --git a/app/presenters/project_presenter.rb b/app/presenters/project_presenter.rb index 45f4668112b..8c24d07675a 100644 --- a/app/presenters/project_presenter.rb +++ b/app/presenters/project_presenter.rb @@ -24,7 +24,8 @@ class ProjectPresenter < Gitlab::View::Presenter::Delegated commits_anchor_data, branches_anchor_data, tags_anchor_data, - files_anchor_data + files_anchor_data, + releases_anchor_data ].compact.select(&:is_link) end @@ -153,6 +154,22 @@ class ProjectPresenter < Gitlab::View::Presenter::Delegated empty_repo? ? nil : project_tree_path(project)) end + def releases_anchor_data + return unless can?(current_user, :read_release, project) + + releases_count = project.releases.count + return if releases_count < 1 + + AnchorData.new(true, + statistic_icon('rocket') + + n_('%{strong_start}%{release_count}%{strong_end} Release', '%{strong_start}%{release_count}%{strong_end} Releases', releases_count).html_safe % { + release_count: number_with_delimiter(releases_count), + strong_start: '<strong class="project-stat-value">'.html_safe, + strong_end: '</strong>'.html_safe + }, + project_releases_path(project)) + end + def commits_anchor_data AnchorData.new(true, statistic_icon('commit') + diff --git a/app/services/users/build_service.rb b/app/services/users/build_service.rb index ea4d11e728e..d18f20bc1db 100644 --- a/app/services/users/build_service.rb +++ b/app/services/users/build_service.rb @@ -86,6 +86,8 @@ module Users :email_confirmation, :password_automatically_set, :name, + :first_name, + :last_name, :password, :username ] @@ -107,6 +109,12 @@ module Users if user_params[:skip_confirmation].nil? user_params[:skip_confirmation] = skip_user_confirmation_email_from_setting end + + fallback_name = "#{user_params[:first_name]} #{user_params[:last_name]}" + + if user_params[:name].blank? && fallback_name.present? + user_params = user_params.merge(name: fallback_name) + end end if user_default_internal_regex_enabled? && !user_params.key?(:external) diff --git a/app/views/devise/shared/_experimental_separate_sign_up_flow_box.html.haml b/app/views/devise/shared/_experimental_separate_sign_up_flow_box.html.haml index e3142ff96a1..4832861445b 100644 --- a/app/views/devise/shared/_experimental_separate_sign_up_flow_box.html.haml +++ b/app/views/devise/shared/_experimental_separate_sign_up_flow_box.html.haml @@ -1,4 +1,5 @@ - content_for(:page_title, _('Register for GitLab')) +- max_first_name_length = max_last_name_length = 127 - max_username_length = 255 .signup-box.p-3.mb-2 .signup-body @@ -7,9 +8,16 @@ = render "devise/shared/error_messages", resource: resource - if Feature.enabled?(:invisible_captcha) = invisible_captcha + .name.form-row + .col.form-group + = f.label :first_name, _('First name'), for: 'new_user_first_name', class: 'label-bold' + = f.text_field :first_name, class: 'form-control top js-block-emoji js-validate-length', :data => { :max_length => max_first_name_length, :max_length_message => _("First Name is too long (maximum is %{max_length} characters).") % { max_length: max_first_name_length }, :qa_selector => 'new_user_firstname_field' }, required: true, title: _("This field is required.") + .col.form-group + = f.label :last_name, _('Last name'), for: 'new_user_last_name', class: 'label-bold' + = f.text_field :last_name, class: "form-control top js-block-emoji js-validate-length", :data => { :max_length => max_last_name_length, :max_length_message => _("Last Name is too long (maximum is %{max_length} characters).") % { max_length: max_last_name_length }, :qa_selector => 'new_user_lastname_field' }, required: true, title: _("This field is required.") .username.form-group = f.label :username, class: 'label-bold' - = f.text_field :username, class: "form-control middle js-block-emoji js-validate-length js-validate-username", :data => { :max_length => max_username_length, :max_length_message => s_("SignUp|Username is too long (maximum is %{max_length} characters).") % { max_length: max_username_length }, :qa_selector => 'new_user_username_field' }, pattern: Gitlab::PathRegex::NAMESPACE_FORMAT_REGEX_JS, required: true, title: _("Please create a username with only alphanumeric characters.") + = f.text_field :username, class: "form-control middle js-block-emoji js-validate-length js-validate-username", :data => { :max_length => max_username_length, :max_length_message => _("Username is too long (maximum is %{max_length} characters).") % { max_length: max_username_length }, :qa_selector => 'new_user_username_field' }, pattern: Gitlab::PathRegex::NAMESPACE_FORMAT_REGEX_JS, required: true, title: _("Please create a username with only alphanumeric characters.") %p.validation-error.gl-field-error-ignore.field-validation.mt-1.hide.cred= _('Username is already taken.') %p.validation-success.gl-field-error-ignore.field-validation.mt-1.hide.cgreen= _('Username is available.') %p.validation-pending.gl-field-error-ignore.field-validation.mt-1.hide= _('Checking username availability...') diff --git a/app/views/devise/shared/_signup_box.html.haml b/app/views/devise/shared/_signup_box.html.haml index 2cd77af6877..7c5b85c903c 100644 --- a/app/views/devise/shared/_signup_box.html.haml +++ b/app/views/devise/shared/_signup_box.html.haml @@ -1,4 +1,4 @@ -- max_name_length = 128 +- max_name_length = 255 - max_username_length = 255 #register-pane.tab-pane.login-box{ role: 'tabpanel' } .login-body diff --git a/app/views/registrations/welcome.html.haml b/app/views/registrations/welcome.html.haml index 7b92f5070df..bc8d7ed10ef 100644 --- a/app/views/registrations/welcome.html.haml +++ b/app/views/registrations/welcome.html.haml @@ -1,5 +1,4 @@ -- content_for(:page_title, _('Welcome to GitLab @%{username}!') % { username: current_user.username }) -- max_name_length = 128 +- content_for(:page_title, _('Welcome to GitLab %{name}!') % { name: current_user.name }) .text-center.mb-3 = _('In order to tailor your experience with GitLab we<br>would like to know a bit more about you.').html_safe .signup-box.p-3.mb-2 @@ -7,9 +6,6 @@ = form_for(current_user, url: users_sign_up_update_registration_path, html: { class: 'new_new_user gl-show-field-errors', 'aria-live' => 'assertive' }) do |f| .devise-errors.mt-0 = render 'devise/shared/error_messages', resource: current_user - .name.form-group - = f.label :name, _('Full name'), class: 'label-bold' - = f.text_field :name, class: 'form-control top js-block-emoji js-validate-length', :data => { :max_length => max_name_length, :max_length_message => s_('Name is too long (maximum is %{max_length} characters).') % { max_length: max_name_length }, :qa_selector => 'new_user_name_field' }, required: true, title: _('This field is required.') .form-group = f.label :role, _('Role'), class: 'label-bold' = f.select :role, ::User.roles.keys.map { |role| [role.titleize, role] }, {}, class: 'form-control' diff --git a/app/views/shared/boards/components/_board.html.haml b/app/views/shared/boards/components/_board.html.haml index eb9b7f6c48a..a62c385d711 100644 --- a/app/views/shared/boards/components/_board.html.haml +++ b/app/views/shared/boards/components/_board.html.haml @@ -42,9 +42,10 @@ %button.board-delete.no-drag.p-0.border-0.has-tooltip.float-right{ type: "button", title: _("Delete list"), ":class": "{ 'd-none': !list.isExpanded }", "aria-label" => _("Delete list"), data: { placement: "bottom" }, "@click.stop" => "deleteBoard" } = icon("trash") - .issue-count-badge.pr-0.no-drag.text-secondary{ "v-if" => "showBoardListAndBoardInfo", ":title": "counterTooltip", "v-tooltip": true, data: { placement: "top" } } + .issue-count-badge.pr-0.no-drag.text-secondary{ "v-if" => "showBoardListAndBoardInfo" } %span.d-inline-flex - %span.issue-count-badge-count + %gl-tooltip{ ":target" => "() => $refs.issueCount", ":title" => "issuesTooltip" } + %span.issue-count-badge-count{ "ref" => "issueCount" } %icon.mr-1{ name: "issues" } %issue-count{ ":maxIssueCount" => "list.maxIssueCount", ":issuesSize" => "list.issuesSize" } diff --git a/changelogs/unreleased/197343-iscompact.yml b/changelogs/unreleased/197343-iscompact.yml new file mode 100644 index 00000000000..d85e729d2bd --- /dev/null +++ b/changelogs/unreleased/197343-iscompact.yml @@ -0,0 +1,5 @@ +--- +title: Fix unexpected behaviour of the commit form after committing in Web IDE +merge_request: 23238 +author: +type: fixed diff --git a/changelogs/unreleased/36667-add-release-count-to-project-homepage.yml b/changelogs/unreleased/36667-add-release-count-to-project-homepage.yml new file mode 100644 index 00000000000..3a8e26b0087 --- /dev/null +++ b/changelogs/unreleased/36667-add-release-count-to-project-homepage.yml @@ -0,0 +1,5 @@ +--- +title: Add release count to project homepage +merge_request: 21350 +author: +type: added diff --git a/changelogs/unreleased/37449-fix-eks-authenticate-button.yml b/changelogs/unreleased/37449-fix-eks-authenticate-button.yml new file mode 100644 index 00000000000..e0871e7d7c6 --- /dev/null +++ b/changelogs/unreleased/37449-fix-eks-authenticate-button.yml @@ -0,0 +1,5 @@ +--- +title: 'fix: EKS credentials form does not reset after error' +merge_request: 21958 +author: +type: other diff --git a/changelogs/unreleased/38223-link-to-gitlab-commit-in-error-detail-page-FE.yml b/changelogs/unreleased/38223-link-to-gitlab-commit-in-error-detail-page-FE.yml new file mode 100644 index 00000000000..8adae3d266d --- /dev/null +++ b/changelogs/unreleased/38223-link-to-gitlab-commit-in-error-detail-page-FE.yml @@ -0,0 +1,5 @@ +--- +title: Link to GitLab commit in Sentry error details page +merge_request: 22431 +author: +type: added diff --git a/changelogs/unreleased/nicolasdular-split-signup-full-name.yml b/changelogs/unreleased/nicolasdular-split-signup-full-name.yml new file mode 100644 index 00000000000..08c45cb22ba --- /dev/null +++ b/changelogs/unreleased/nicolasdular-split-signup-full-name.yml @@ -0,0 +1,5 @@ +--- +title: Update name max length +merge_request: 22840 +author: +type: changed diff --git a/doc/administration/monitoring/gitlab_instance_administration_project/index.md b/doc/administration/monitoring/gitlab_instance_administration_project/index.md index b07bbafaf7d..8675521ddb1 100644 --- a/doc/administration/monitoring/gitlab_instance_administration_project/index.md +++ b/doc/administration/monitoring/gitlab_instance_administration_project/index.md @@ -1,7 +1,9 @@ # GitLab instance administration project NOTE: **Note:** -This feature is not yet available and is [planned for 12.6](https://gitlab.com/gitlab-org/gitlab/issues/32351). +This feature is available behind a feature flag called `self_monitoring_project` +since [12.7](https://gitlab.com/gitlab-org/gitlab/issues/32351). The feature flag +will be removed once we [add dashboards to display metrics](https://gitlab.com/groups/gitlab-org/-/epics/2367). GitLab has been adding the ability for administrators to see insights into the health of their GitLab instance. In order to surface this experience in a native way, similar to how diff --git a/doc/user/application_security/container_scanning/index.md b/doc/user/application_security/container_scanning/index.md index eb726ee2ed7..de8854bda0a 100644 --- a/doc/user/application_security/container_scanning/index.md +++ b/doc/user/application_security/container_scanning/index.md @@ -305,7 +305,7 @@ the report JSON unless stated otherwise. Presence of optional fields depends on | `vulnerabilities[].location.dependency.package.name` | Name of the package where the vulnerability is located. | | `vulnerabilities[].location.dependency.version` | Version of the vulnerable package. Optional. | | `vulnerabilities[].location.operating_system` | The operating system that contains the vulnerable package. | -| `vulnerabilities[].location.image` | The Docker image that was analyzed. Optional. | +| `vulnerabilities[].location.image` | The Docker image that was analyzed. | | `vulnerabilities[].identifiers` | An ordered array of references that identify a vulnerability on internal or external DBs. | | `vulnerabilities[].identifiers[].type` | Type of the identifier. Possible values: common identifier types (among `cve`, `cwe`, `osvdb`, and `usn`). | | `vulnerabilities[].identifiers[].name` | Name of the identifier for display purpose. | diff --git a/doc/user/packages/index.md b/doc/user/packages/index.md index 594be1757c1..c58effac59d 100644 --- a/doc/user/packages/index.md +++ b/doc/user/packages/index.md @@ -13,7 +13,7 @@ The Packages feature allows GitLab to act as a repository for the following: | [Conan Repository](conan_repository/index.md) **(PREMIUM)** | The GitLab Conan Repository enables every project in GitLab to have its own space to store [Conan](https://conan.io/) packages. | 12.6+ | | [Maven Repository](maven_repository/index.md) **(PREMIUM)** | The GitLab Maven Repository enables every project in GitLab to have its own space to store [Maven](https://maven.apache.org/) packages. | 11.3+ | | [NPM Registry](npm_registry/index.md) **(PREMIUM)** | The GitLab NPM Registry enables every project in GitLab to have its own space to store [NPM](https://www.npmjs.com/) packages. | 11.7+ | -| [NuGet Repository](nuget_repository/index.md) **(PREMIUM)** | *COMING SOON* The GitLab NuGet Repository will enable every project in GitLab to have its own space to store [NuGet](https://www.nuget.org/) packages. | 12.8+ | +| [NuGet Repository](nuget_repository/index.md) **(PREMIUM)** | *PLANNED* The GitLab NuGet Repository will enable every project in GitLab to have its own space to store [NuGet](https://www.nuget.org/) packages. | 12.8+ | TIP: **Tip:** Don't you see your package management system supported yet? Consider contributing diff --git a/doc/user/project/operations/error_tracking.md b/doc/user/project/operations/error_tracking.md index 5246f8072e2..f76c9d9912b 100644 --- a/doc/user/project/operations/error_tracking.md +++ b/doc/user/project/operations/error_tracking.md @@ -55,13 +55,13 @@ This page has: - A link to the Sentry issue. - Other details about the issue, including a full stack trace. -If the error has not been linked to an existing GitLab issue, a 'Create Issue' button will be visible: +If the error has not been linked to an existing GitLab issue, a 'Create issue' button will be visible: ![Error Details without Issue Link](img/error_details_v12_6.png) -If a link does exist, it will be shown in the details and the 'Create Issue' button will be hidden: +If a link does exist, it will be shown in the details and the 'Create issue' button will change to a 'View issue' button: -![Error Details with Issue Link](img/error_details_with_issue_v12_6.png) +![Error Details with Issue Link](img/error_details_with_issue_v12_7.png) ## Taking Action on errors @@ -74,3 +74,9 @@ You can take action on Sentry Errors from within the GitLab UI. From within the [Error Details](#error-details) page you can ignore a Sentry error by simply clicking the **Ignore** button near the top of the page. Ignoring an error will prevent it from appearing in the [Error Tracking List](#error-tracking-list), and will silence notifications that were set up within Sentry. + +### Resolving errors + +From within the [Error Details](#error-details) page you can resolve a Sentry error by simply clicking the **Resolve** button near the top of the page. + +Marking an error as resolved indicates that the error has stopped firing events. If another event occurs, the error reverts to unresolved. diff --git a/doc/user/project/operations/img/error_details_with_issue_v12_7.png b/doc/user/project/operations/img/error_details_with_issue_v12_7.png Binary files differnew file mode 100644 index 00000000000..aa846ee7220 --- /dev/null +++ b/doc/user/project/operations/img/error_details_with_issue_v12_7.png diff --git a/locale/gitlab.pot b/locale/gitlab.pot index e1770da3e99..5f1f9c0d49f 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -284,6 +284,12 @@ msgstr "" msgid "%{issuableType} will be removed! Are you sure?" msgstr "" +msgid "%{issuesSize} issues" +msgstr "" + +msgid "%{issuesSize} issues with a limit of %{maxIssueCount}" +msgstr "" + msgid "%{label_for_message} unavailable" msgstr "" @@ -401,6 +407,11 @@ msgstr[1] "" msgid "%{strong_start}%{human_size}%{strong_end} Files" msgstr "" +msgid "%{strong_start}%{release_count}%{strong_end} Release" +msgid_plural "%{strong_start}%{release_count}%{strong_end} Releases" +msgstr[0] "" +msgstr[1] "" + msgid "%{strong_start}%{tag_count}%{strong_end} Tag" msgid_plural "%{strong_start}%{tag_count}%{strong_end} Tags" msgstr[0] "" @@ -435,6 +446,9 @@ msgstr "" msgid "%{title} changes" msgstr "" +msgid "%{totalWeight} total weight" +msgstr "" + msgid "%{total} open issue weight" msgstr "" @@ -3299,9 +3313,21 @@ msgstr "" msgid "Checkout" msgstr "" +msgid "Checkout|$%{selectedPlanPrice} per user per year" +msgstr "" + +msgid "Checkout|%{name}'s GitLab subscription" +msgstr "" + msgid "Checkout|%{selectedPlanText} plan" msgstr "" +msgid "Checkout|%{startDate} - %{endDate}" +msgstr "" + +msgid "Checkout|(x%{numberOfUsers})" +msgstr "" + msgid "Checkout|1. Your profile" msgstr "" @@ -3338,9 +3364,21 @@ msgstr "" msgid "Checkout|Subscription details" msgstr "" +msgid "Checkout|Subtotal" +msgstr "" + +msgid "Checkout|Tax" +msgstr "" + +msgid "Checkout|Total" +msgstr "" + msgid "Checkout|Users" msgstr "" +msgid "Checkout|Your organization" +msgstr "" + msgid "Checkout|company or team" msgstr "" @@ -7275,9 +7313,6 @@ msgstr "" msgid "Error deleting project. Check logs for error details." msgstr "" -msgid "Error details" -msgstr "" - msgid "Error fetching diverging counts for branches. Please try again." msgstr "" @@ -8139,6 +8174,9 @@ msgstr "" msgid "Finished" msgstr "" +msgid "First Name is too long (maximum is %{max_length} characters)." +msgstr "" + msgid "First Seen" msgstr "" @@ -8838,6 +8876,9 @@ msgstr "" msgid "GitLab allows you to continue using your license even if you exceed the number of seats you purchased. You will be required to pay for these seats when you renew your license." msgstr "" +msgid "GitLab commit" +msgstr "" + msgid "GitLab for Slack" msgstr "" @@ -10668,6 +10709,9 @@ msgstr "" msgid "Last Accessed On" msgstr "" +msgid "Last Name is too long (maximum is %{max_length} characters)." +msgstr "" + msgid "Last Pipeline" msgstr "" @@ -11991,9 +12035,6 @@ msgstr "" msgid "Name has already been taken" msgstr "" -msgid "Name is too long (maximum is %{max_length} characters)." -msgstr "" - msgid "Name new label" msgstr "" @@ -20461,6 +20502,9 @@ msgstr "" msgid "Username is available." msgstr "" +msgid "Username is too long (maximum is %{max_length} characters)." +msgstr "" + msgid "Username or email" msgstr "" @@ -20835,7 +20879,7 @@ msgstr "" msgid "Welcome to GitLab" msgstr "" -msgid "Welcome to GitLab @%{username}!" +msgid "Welcome to GitLab %{name}!" msgstr "" msgid "Welcome to the Guided GitLab Tour" diff --git a/spec/controllers/registrations_controller_spec.rb b/spec/controllers/registrations_controller_spec.rb index da36148ba85..214eb35ec9d 100644 --- a/spec/controllers/registrations_controller_spec.rb +++ b/spec/controllers/registrations_controller_spec.rb @@ -306,6 +306,23 @@ describe RegistrationsController do expect(subject.current_user).not_to be_nil end + + context 'with the experimental signup flow enabled and the user is part of the experimental group' do + before do + stub_experiment(signup_flow: true) + stub_experiment_for_user(signup_flow: true) + end + + let(:base_user_params) { { first_name: 'First', last_name: 'Last', username: 'new_username', email: 'new@user.com', password: 'Any_password' } } + + it 'sets name from first and last name' do + post :create, params: { new_user: base_user_params } + + expect(User.last.first_name).to eq(base_user_params[:first_name]) + expect(User.last.last_name).to eq(base_user_params[:last_name]) + expect(User.last.name).to eq("#{base_user_params[:first_name]} #{base_user_params[:last_name]}") + end + end end describe '#destroy' do @@ -395,7 +412,7 @@ describe RegistrationsController do label: anything, property: 'experimental_group' ) - patch :update_registration, params: { user: { name: 'New name', role: 'software_developer', setup_for_company: 'false' } } + patch :update_registration, params: { user: { role: 'software_developer', setup_for_company: 'false' } } end end end diff --git a/spec/features/users/signup_spec.rb b/spec/features/users/signup_spec.rb index 3b19bd423a4..30f298b1fc3 100644 --- a/spec/features/users/signup_spec.rb +++ b/spec/features/users/signup_spec.rb @@ -123,50 +123,6 @@ shared_examples 'Signup' do end end - describe 'user\'s full name validation', :js do - before do - if Gitlab::Experimentation.enabled?(:signup_flow) - user = create(:user, role: nil) - sign_in(user) - visit users_sign_up_welcome_path - @user_name_field = 'user_name' - else - visit new_user_registration_path - @user_name_field = 'new_user_name' - end - end - - it 'does not show an error border if the user\'s fullname length is not longer than 128 characters' do - fill_in @user_name_field, with: 'u' * 128 - - expect(find('.name')).not_to have_css '.gl-field-error-outline' - end - - it 'shows an error border if the user\'s fullname contains an emoji' do - simulate_input("##{@user_name_field}", 'Ehsan 🦋') - - expect(find('.name')).to have_css '.gl-field-error-outline' - end - - it 'shows an error border if the user\'s fullname is longer than 128 characters' do - fill_in @user_name_field, with: 'n' * 129 - - expect(find('.name')).to have_css '.gl-field-error-outline' - end - - it 'shows an error message if the user\'s fullname is longer than 128 characters' do - fill_in @user_name_field, with: 'n' * 129 - - expect(page).to have_content("Name is too long (maximum is 128 characters).") - end - - it 'shows an error message if the username contains emojis' do - simulate_input("##{@user_name_field}", 'Ehsan 🦋') - - expect(page).to have_content("Invalid input, please avoid emojis") - end - end - context 'with no errors' do context 'when sending confirmation email' do before do @@ -184,7 +140,10 @@ shared_examples 'Signup' do fill_in 'new_user_username', with: new_user.username fill_in 'new_user_email', with: new_user.email - unless Gitlab::Experimentation.enabled?(:signup_flow) + if Gitlab::Experimentation.enabled?(:signup_flow) + fill_in 'new_user_first_name', with: new_user.first_name + fill_in 'new_user_last_name', with: new_user.last_name + else fill_in 'new_user_name', with: new_user.name fill_in 'new_user_email_confirmation', with: new_user.email end @@ -209,7 +168,10 @@ shared_examples 'Signup' do fill_in 'new_user_username', with: new_user.username fill_in 'new_user_email', with: new_user.email - unless Gitlab::Experimentation.enabled?(:signup_flow) + if Gitlab::Experimentation.enabled?(:signup_flow) + fill_in 'new_user_first_name', with: new_user.first_name + fill_in 'new_user_last_name', with: new_user.last_name + else fill_in 'new_user_name', with: new_user.name fill_in 'new_user_email_confirmation', with: new_user.email end @@ -235,7 +197,10 @@ shared_examples 'Signup' do fill_in 'new_user_username', with: new_user.username fill_in 'new_user_email', with: new_user.email - unless Gitlab::Experimentation.enabled?(:signup_flow) + if Gitlab::Experimentation.enabled?(:signup_flow) + fill_in 'new_user_first_name', with: new_user.first_name + fill_in 'new_user_last_name', with: new_user.last_name + else fill_in 'new_user_name', with: new_user.name fill_in 'new_user_email_confirmation', with: new_user.email.capitalize end @@ -263,7 +228,10 @@ shared_examples 'Signup' do fill_in 'new_user_username', with: new_user.username fill_in 'new_user_email', with: new_user.email - unless Gitlab::Experimentation.enabled?(:signup_flow) + if Gitlab::Experimentation.enabled?(:signup_flow) + fill_in 'new_user_first_name', with: new_user.first_name + fill_in 'new_user_last_name', with: new_user.last_name + else fill_in 'new_user_name', with: new_user.name fill_in 'new_user_email_confirmation', with: new_user.email end @@ -287,7 +255,10 @@ shared_examples 'Signup' do visit new_user_registration_path - unless Gitlab::Experimentation.enabled?(:signup_flow) + if Gitlab::Experimentation.enabled?(:signup_flow) + fill_in 'new_user_first_name', with: new_user.first_name + fill_in 'new_user_last_name', with: new_user.last_name + else fill_in 'new_user_name', with: new_user.name end @@ -313,7 +284,10 @@ shared_examples 'Signup' do visit new_user_registration_path - unless Gitlab::Experimentation.enabled?(:signup_flow) + if Gitlab::Experimentation.enabled?(:signup_flow) + fill_in 'new_user_first_name', with: new_user.first_name + fill_in 'new_user_last_name', with: new_user.last_name + else fill_in 'new_user_name', with: new_user.name end @@ -338,7 +312,10 @@ shared_examples 'Signup' do fill_in 'new_user_username', with: new_user.username fill_in 'new_user_email', with: new_user.email - unless Gitlab::Experimentation.enabled?(:signup_flow) + if Gitlab::Experimentation.enabled?(:signup_flow) + fill_in 'new_user_first_name', with: new_user.first_name + fill_in 'new_user_last_name', with: new_user.last_name + else fill_in 'new_user_name', with: new_user.name fill_in 'new_user_email_confirmation', with: new_user.email end @@ -357,7 +334,10 @@ shared_examples 'Signup' do fill_in 'new_user_username', with: new_user.username fill_in 'new_user_email', with: new_user.email - unless Gitlab::Experimentation.enabled?(:signup_flow) + if Gitlab::Experimentation.enabled?(:signup_flow) + fill_in 'new_user_first_name', with: new_user.first_name + fill_in 'new_user_last_name', with: new_user.last_name + else fill_in 'new_user_name', with: new_user.name fill_in 'new_user_email_confirmation', with: new_user.email end @@ -394,7 +374,10 @@ shared_examples 'Signup' do fill_in 'new_user_username', with: new_user.username fill_in 'new_user_email', with: new_user.email - unless Gitlab::Experimentation.enabled?(:signup_flow) + if Gitlab::Experimentation.enabled?(:signup_flow) + fill_in 'new_user_first_name', with: new_user.first_name + fill_in 'new_user_last_name', with: new_user.last_name + else fill_in 'new_user_name', with: new_user.name fill_in 'new_user_email_confirmation', with: new_user.email end @@ -412,6 +395,44 @@ shared_examples 'Signup' do end end +shared_examples 'Signup name validation' do |field, max_length| + before do + visit new_user_registration_path + end + + describe "#{field} validation", :js do + it "does not show an error border if the user's fullname length is not longer than #{max_length} characters" do + fill_in field, with: 'u' * max_length + + expect(find('.name')).not_to have_css '.gl-field-error-outline' + end + + it 'shows an error border if the user\'s fullname contains an emoji' do + simulate_input("##{field}", 'Ehsan 🦋') + + expect(find('.name')).to have_css '.gl-field-error-outline' + end + + it "shows an error border if the user\'s fullname is longer than #{max_length} characters" do + fill_in field, with: 'n' * (max_length + 1) + + expect(find('.name')).to have_css '.gl-field-error-outline' + end + + it "shows an error message if the user\'s fullname is longer than #{max_length} characters" do + fill_in field, with: 'n' * (max_length + 1) + + expect(page).to have_content("Name is too long (maximum is #{max_length} characters).") + end + + it 'shows an error message if the username contains emojis' do + simulate_input("##{field}", 'Ehsan 🦋') + + expect(page).to have_content("Invalid input, please avoid emojis") + end + end +end + describe 'With original flow' do before do stub_experiment(signup_flow: false) @@ -419,6 +440,7 @@ describe 'With original flow' do end it_behaves_like 'Signup' + it_behaves_like 'Signup name validation', 'new_user_name', 255 end describe 'With experimental flow' do @@ -428,11 +450,15 @@ describe 'With experimental flow' do end it_behaves_like 'Signup' + it_behaves_like 'Signup name validation', 'new_user_first_name', 127 + it_behaves_like 'Signup name validation', 'new_user_last_name', 127 describe 'when role is required' do it 'after registering, it redirects to step 2 of the signup process, sets the name and role and then redirects to the original requested url' do new_user = build_stubbed(:user) visit new_user_registration_path + fill_in 'new_user_first_name', with: new_user.first_name + fill_in 'new_user_last_name', with: new_user.last_name fill_in 'new_user_username', with: new_user.username fill_in 'new_user_email', with: new_user.email fill_in 'new_user_password', with: new_user.password @@ -441,13 +467,11 @@ describe 'With experimental flow' do expect(page).to have_current_path(users_sign_up_welcome_path) - fill_in 'user_name', with: 'New name' select 'Software Developer', from: 'user_role' choose 'user_setup_for_company_true' click_button 'Get started!' new_user = User.find_by_username(new_user.username) - expect(new_user.name).to eq 'New name' expect(new_user.software_developer_role?).to be_truthy expect(new_user.setup_for_company).to be_truthy expect(page).to have_current_path(new_project_path) diff --git a/spec/frontend/boards/issue_card_spec.js b/spec/frontend/boards/issue_card_spec.js index b9a88324763..df55a106945 100644 --- a/spec/frontend/boards/issue_card_spec.js +++ b/spec/frontend/boards/issue_card_spec.js @@ -50,7 +50,6 @@ describe('Issue card component', () => { rootPath: '/', }, store, - attachToDocument: true, }); }); diff --git a/spec/frontend/create_cluster/eks_cluster/components/service_credentials_form_spec.js b/spec/frontend/create_cluster/eks_cluster/components/service_credentials_form_spec.js index 92a5f5116ac..c58638f5c80 100644 --- a/spec/frontend/create_cluster/eks_cluster/components/service_credentials_form_spec.js +++ b/spec/frontend/create_cluster/eks_cluster/components/service_credentials_form_spec.js @@ -47,7 +47,6 @@ describe('ServiceCredentialsForm', () => { const findCopyExternalIdButton = () => vm.find('.js-copy-external-id-button'); const findInvalidCredentials = () => vm.find('.js-invalid-credentials'); const findSubmitButton = () => vm.find(LoadingButton); - const findForm = () => vm.find('form[name="service-credentials-form"]'); it('displays provided account id', () => { expect(findAccountIdInput().attributes('value')).toBe(accountId); @@ -77,8 +76,10 @@ describe('ServiceCredentialsForm', () => { }); }); - it('dispatches createRole action when form is submitted', () => { - findForm().trigger('submit'); + it('dispatches createRole action when submit button is clicked', () => { + vm.setData({ roleArn: '123' }); // set role ARN to enable button + + findSubmitButton().vm.$emit('click', new Event('click')); expect(createRoleAction).toHaveBeenCalled(); }); diff --git a/spec/frontend/cycle_analytics/limit_warning_component_spec.js b/spec/frontend/cycle_analytics/limit_warning_component_spec.js index a4606099b06..e712dea67cb 100644 --- a/spec/frontend/cycle_analytics/limit_warning_component_spec.js +++ b/spec/frontend/cycle_analytics/limit_warning_component_spec.js @@ -10,7 +10,6 @@ const createComponent = props => propsData: { ...props, }, - attachToDocument: true, }); describe('Limit warning component', () => { diff --git a/spec/frontend/error_tracking/components/error_details_spec.js b/spec/frontend/error_tracking/components/error_details_spec.js index e876a6da922..e19cb1fd3c1 100644 --- a/spec/frontend/error_tracking/components/error_details_spec.js +++ b/spec/frontend/error_tracking/components/error_details_spec.js @@ -246,5 +246,37 @@ describe('ErrorDetails', () => { }); }); }); + + describe('GitLab commit link', () => { + const gitlabCommit = '7975be0116940bf2ad4321f79d02a55c5f7779aa'; + const gitlabCommitPath = + '/gitlab-org/gitlab-test/commit/7975be0116940bf2ad4321f79d02a55c5f7779aa'; + const findGitLabCommitLink = () => wrapper.find(`[href$="${gitlabCommitPath}"]`); + + it('should display a link', () => { + mocks.$apollo.queries.GQLerror.loading = false; + wrapper.setData({ + GQLerror: { + gitlabCommit, + gitlabCommitPath, + }, + }); + return wrapper.vm.$nextTick().then(() => { + expect(findGitLabCommitLink().exists()).toBe(true); + }); + }); + + it('should display a link', () => { + mocks.$apollo.queries.GQLerror.loading = false; + wrapper.setData({ + GQLerror: { + gitlabCommit: null, + }, + }); + return wrapper.vm.$nextTick().then(() => { + expect(findGitLabCommitLink().exists()).toBe(false); + }); + }); + }); }); }); diff --git a/spec/frontend/jobs/components/erased_block_spec.js b/spec/frontend/jobs/components/erased_block_spec.js index 29a7676abe5..d66ee71df6a 100644 --- a/spec/frontend/jobs/components/erased_block_spec.js +++ b/spec/frontend/jobs/components/erased_block_spec.js @@ -13,7 +13,6 @@ describe('Erased block', () => { const createComponent = props => { wrapper = mount(ErasedBlock, { propsData: props, - attachToDocument: true, }); }; diff --git a/spec/frontend/notes/components/discussion_actions_spec.js b/spec/frontend/notes/components/discussion_actions_spec.js index 7e30e166d65..2d95a86d8a6 100644 --- a/spec/frontend/notes/components/discussion_actions_spec.js +++ b/spec/frontend/notes/components/discussion_actions_spec.js @@ -35,7 +35,6 @@ describe('DiscussionActions', () => { shouldShowJumpToNextDiscussion: true, ...props, }, - attachToDocument: true, }); }; diff --git a/spec/frontend/releases/detail/components/app_spec.js b/spec/frontend/releases/detail/components/app_spec.js index 09f348018f4..fd5239ad44e 100644 --- a/spec/frontend/releases/detail/components/app_spec.js +++ b/spec/frontend/releases/detail/components/app_spec.js @@ -31,7 +31,6 @@ describe('Release detail component', () => { wrapper = mount(ReleaseDetailApp, { store, - attachToDocument: true, }); return wrapper.vm.$nextTick(); diff --git a/spec/javascripts/ide/components/commit_sidebar/form_spec.js b/spec/javascripts/ide/components/commit_sidebar/form_spec.js index 2ee0b94582c..e984389bd46 100644 --- a/spec/javascripts/ide/components/commit_sidebar/form_spec.js +++ b/spec/javascripts/ide/components/commit_sidebar/form_spec.js @@ -76,6 +76,25 @@ describe('IDE commit form', () => { done(); }); }); + + it('collapses if lastCommitMsg is set to empty and current view is not commit view', done => { + store.state.lastCommitMsg = 'abc'; + store.state.currentActivityView = activityBarViews.edit; + + vm.$nextTick(() => { + // if commit message is set, form is uncollapsed + expect(vm.isCompact).toBe(false); + + store.state.lastCommitMsg = ''; + + vm.$nextTick(() => { + // collapsed when set to empty + expect(vm.isCompact).toBe(true); + + done(); + }); + }); + }); }); describe('full', () => { diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb index 110f7a5af65..5620f211d9c 100644 --- a/spec/models/user_spec.rb +++ b/spec/models/user_spec.rb @@ -147,15 +147,15 @@ describe User, :do_not_mock_admin_mode do describe 'name' do it { is_expected.to validate_presence_of(:name) } - it { is_expected.to validate_length_of(:name).is_at_most(128) } + it { is_expected.to validate_length_of(:name).is_at_most(255) } end describe 'first name' do - it { is_expected.to validate_length_of(:first_name).is_at_most(255) } + it { is_expected.to validate_length_of(:first_name).is_at_most(127) } end describe 'last name' do - it { is_expected.to validate_length_of(:last_name).is_at_most(255) } + it { is_expected.to validate_length_of(:last_name).is_at_most(127) } end describe 'username' do diff --git a/spec/presenters/project_presenter_spec.rb b/spec/presenters/project_presenter_spec.rb index 09ca106c54c..620ef3ff21a 100644 --- a/spec/presenters/project_presenter_spec.rb +++ b/spec/presenters/project_presenter_spec.rb @@ -33,10 +33,10 @@ describe ProjectPresenter do describe '#default_view' do context 'user not signed in' do - let(:user) { nil } + let_it_be(:user) { nil } context 'when repository is empty' do - let(:project) { create(:project_empty_repo, :public) } + let_it_be(:project) { create(:project_empty_repo, :public) } it 'returns activity if user has repository access' do allow(presenter).to receive(:can?).with(nil, :download_code, project).and_return(true) @@ -52,7 +52,8 @@ describe ProjectPresenter do end context 'when repository is not empty' do - let(:project) { create(:project, :public, :repository) } + let_it_be(:project) { create(:project, :public, :repository) } + let(:release) { create(:release, project: project, author: user) } it 'returns files and readme if user has repository access' do allow(presenter).to receive(:can?).with(nil, :download_code, project).and_return(true) @@ -65,6 +66,15 @@ describe ProjectPresenter do expect(presenter.default_view).to eq('activity') end + + it 'returns releases anchor' do + expect(release).to be_truthy + expect(presenter.releases_anchor_data).to have_attributes( + is_link: true, + label: a_string_including("#{project.releases.count}"), + link: presenter.project_releases_path(project) + ) + end end end @@ -121,10 +131,8 @@ describe ProjectPresenter do end describe '#can_current_user_push_code?' do - let(:project) { create(:project, :repository) } - context 'empty repo' do - let(:project) { create(:project) } + let_it_be(:project) { create(:project) } it 'returns true if user can push_code' do project.add_developer(user) @@ -150,6 +158,7 @@ describe ProjectPresenter do it 'returns false if default branch is protected' do project.add_developer(user) + create(:protected_branch, project: project, name: project.default_branch) expect(presenter.can_current_user_push_code?).to be(false) @@ -158,73 +167,125 @@ describe ProjectPresenter do end context 'statistics anchors (empty repo)' do - let(:project) { create(:project, :empty_repo) } + let_it_be(:project) { create(:project, :empty_repo) } describe '#files_anchor_data' do it 'returns files data' do - expect(presenter.files_anchor_data).to have_attributes(is_link: true, - label: a_string_including('0 Bytes'), - link: nil) + expect(presenter.files_anchor_data).to have_attributes( + is_link: true, + label: a_string_including('0 Bytes'), + link: nil + ) + end + end + + describe '#releases_anchor_data' do + it 'does not return release count' do + expect(presenter.releases_anchor_data).to be_nil end end describe '#commits_anchor_data' do it 'returns commits data' do - expect(presenter.commits_anchor_data).to have_attributes(is_link: true, - label: a_string_including('0'), - link: nil) + expect(presenter.commits_anchor_data).to have_attributes( + is_link: true, + label: a_string_including('0'), + link: nil + ) end end describe '#branches_anchor_data' do it 'returns branches data' do - expect(presenter.branches_anchor_data).to have_attributes(is_link: true, - label: a_string_including('0'), - link: nil) + expect(presenter.branches_anchor_data).to have_attributes( + is_link: true, + label: a_string_including('0'), + link: nil + ) end end describe '#tags_anchor_data' do it 'returns tags data' do - expect(presenter.tags_anchor_data).to have_attributes(is_link: true, - label: a_string_including('0'), - link: nil) + expect(presenter.tags_anchor_data).to have_attributes( + is_link: true, + label: a_string_including('0'), + link: nil + ) end end end context 'statistics anchors' do - let(:project) { create(:project, :repository) } + let_it_be(:user) { create(:user) } + let_it_be(:project) { create(:project, :repository) } + let_it_be(:release) { create(:release, project: project, author: user) } + let(:presenter) { described_class.new(project, current_user: user) } describe '#files_anchor_data' do it 'returns files data' do - expect(presenter.files_anchor_data).to have_attributes(is_link: true, - label: a_string_including('0 Bytes'), - link: presenter.project_tree_path(project)) + expect(presenter.files_anchor_data).to have_attributes( + is_link: true, + label: a_string_including('0 Bytes'), + link: presenter.project_tree_path(project) + ) + end + end + + describe '#releases_anchor_data' do + it 'returns release count if user can read release' do + project.add_maintainer(user) + + expect(release).to be_truthy + expect(presenter.releases_anchor_data).to have_attributes( + is_link: true, + label: a_string_including("#{project.releases.count}"), + link: presenter.project_releases_path(project) + ) + end + + it 'returns nil if user cannot read release' do + expect(release).to be_truthy + expect(presenter.releases_anchor_data).to be_nil + end + + context 'user not signed in' do + let_it_be(:user) { nil } + + it 'returns nil if user is signed out' do + expect(release).to be_truthy + expect(presenter.releases_anchor_data).to be_nil + end end end describe '#commits_anchor_data' do it 'returns commits data' do - expect(presenter.commits_anchor_data).to have_attributes(is_link: true, - label: a_string_including('0'), - link: presenter.project_commits_path(project, project.repository.root_ref)) + expect(presenter.commits_anchor_data).to have_attributes( + is_link: true, + label: a_string_including('0'), + link: presenter.project_commits_path(project, project.repository.root_ref) + ) end end describe '#branches_anchor_data' do it 'returns branches data' do - expect(presenter.branches_anchor_data).to have_attributes(is_link: true, - label: a_string_including("#{project.repository.branches.size}"), - link: presenter.project_branches_path(project)) + expect(presenter.branches_anchor_data).to have_attributes( + is_link: true, + label: a_string_including("#{project.repository.branches.size}"), + link: presenter.project_branches_path(project) + ) end end describe '#tags_anchor_data' do it 'returns tags data' do - expect(presenter.tags_anchor_data).to have_attributes(is_link: true, - label: a_string_including("#{project.repository.tags.size}"), - link: presenter.project_tags_path(project)) + expect(presenter.tags_anchor_data).to have_attributes( + is_link: true, + label: a_string_including("#{project.repository.tags.size}"), + link: presenter.project_tags_path(project) + ) end end @@ -232,10 +293,12 @@ describe ProjectPresenter do it 'returns new file data if user can push' do project.add_developer(user) - expect(presenter.new_file_anchor_data).to have_attributes(is_link: false, - label: a_string_including("New file"), - link: presenter.project_new_blob_path(project, 'master'), - class_modifier: 'success') + expect(presenter.new_file_anchor_data).to have_attributes( + is_link: false, + label: a_string_including("New file"), + link: presenter.project_new_blob_path(project, 'master'), + class_modifier: 'success' + ) end it 'returns nil if user cannot push' do @@ -243,7 +306,7 @@ describe ProjectPresenter do end context 'when the project is empty' do - let(:project) { create(:project, :empty_repo) } + let_it_be(:project) { create(:project, :empty_repo) } # Since we protect the default branch for empty repos it 'is empty for a developer' do @@ -258,11 +321,14 @@ describe ProjectPresenter do context 'when user can push and README does not exists' do it 'returns anchor data' do project.add_developer(user) + allow(project.repository).to receive(:readme).and_return(nil) - expect(presenter.readme_anchor_data).to have_attributes(is_link: false, - label: a_string_including('Add README'), - link: presenter.add_readme_path) + expect(presenter.readme_anchor_data).to have_attributes( + is_link: false, + label: a_string_including('Add README'), + link: presenter.add_readme_path + ) end end @@ -270,9 +336,11 @@ describe ProjectPresenter do it 'returns anchor data' do allow(project.repository).to receive(:readme).and_return(double(name: 'readme')) - expect(presenter.readme_anchor_data).to have_attributes(is_link: false, - label: a_string_including('README'), - link: presenter.readme_path) + expect(presenter.readme_anchor_data).to have_attributes( + is_link: false, + label: a_string_including('README'), + link: presenter.readme_path + ) end end end @@ -281,11 +349,14 @@ describe ProjectPresenter do context 'when user can push and CHANGELOG does not exist' do it 'returns anchor data' do project.add_developer(user) + allow(project.repository).to receive(:changelog).and_return(nil) - expect(presenter.changelog_anchor_data).to have_attributes(is_link: false, - label: a_string_including('Add CHANGELOG'), - link: presenter.add_changelog_path) + expect(presenter.changelog_anchor_data).to have_attributes( + is_link: false, + label: a_string_including('Add CHANGELOG'), + link: presenter.add_changelog_path + ) end end @@ -293,9 +364,11 @@ describe ProjectPresenter do it 'returns anchor data' do allow(project.repository).to receive(:changelog).and_return(double(name: 'foo')) - expect(presenter.changelog_anchor_data).to have_attributes(is_link: false, - label: a_string_including('CHANGELOG'), - link: presenter.changelog_path) + expect(presenter.changelog_anchor_data).to have_attributes( + is_link: false, + label: a_string_including('CHANGELOG'), + link: presenter.changelog_path + ) end end end @@ -304,11 +377,14 @@ describe ProjectPresenter do context 'when user can push and LICENSE does not exist' do it 'returns anchor data' do project.add_developer(user) + allow(project.repository).to receive(:license_blob).and_return(nil) - expect(presenter.license_anchor_data).to have_attributes(is_link: false, - label: a_string_including('Add LICENSE'), - link: presenter.add_license_path) + expect(presenter.license_anchor_data).to have_attributes( + is_link: false, + label: a_string_including('Add LICENSE'), + link: presenter.add_license_path + ) end end @@ -316,9 +392,11 @@ describe ProjectPresenter do it 'returns anchor data' do allow(project.repository).to receive(:license_blob).and_return(double(name: 'foo')) - expect(presenter.license_anchor_data).to have_attributes(is_link: false, - label: a_string_including(presenter.license_short_name), - link: presenter.license_path) + expect(presenter.license_anchor_data).to have_attributes( + is_link: false, + label: a_string_including(presenter.license_short_name), + link: presenter.license_path + ) end end end @@ -327,11 +405,14 @@ describe ProjectPresenter do context 'when user can push and CONTRIBUTING does not exist' do it 'returns anchor data' do project.add_developer(user) + allow(project.repository).to receive(:contribution_guide).and_return(nil) - expect(presenter.contribution_guide_anchor_data).to have_attributes(is_link: false, - label: a_string_including('Add CONTRIBUTING'), - link: presenter.add_contribution_guide_path) + expect(presenter.contribution_guide_anchor_data).to have_attributes( + is_link: false, + label: a_string_including('Add CONTRIBUTING'), + link: presenter.add_contribution_guide_path + ) end end @@ -339,9 +420,11 @@ describe ProjectPresenter do it 'returns anchor data' do allow(project.repository).to receive(:contribution_guide).and_return(double(name: 'foo')) - expect(presenter.contribution_guide_anchor_data).to have_attributes(is_link: false, - label: a_string_including('CONTRIBUTING'), - link: presenter.contribution_guide_path) + expect(presenter.contribution_guide_anchor_data).to have_attributes( + is_link: false, + label: a_string_including('CONTRIBUTING'), + link: presenter.contribution_guide_path + ) end end end @@ -351,21 +434,26 @@ describe ProjectPresenter do it 'returns anchor data' do allow(project).to receive(:auto_devops_enabled?).and_return(true) - expect(presenter.autodevops_anchor_data).to have_attributes(is_link: false, - label: a_string_including('Auto DevOps enabled'), - link: nil) + expect(presenter.autodevops_anchor_data).to have_attributes( + is_link: false, + label: a_string_including('Auto DevOps enabled'), + link: nil + ) end end context 'when user can admin pipeline and CI yml does not exist' do it 'returns anchor data' do project.add_maintainer(user) + allow(project).to receive(:auto_devops_enabled?).and_return(false) allow(project.repository).to receive(:gitlab_ci_yml).and_return(nil) - expect(presenter.autodevops_anchor_data).to have_attributes(is_link: false, - label: a_string_including('Enable Auto DevOps'), - link: presenter.project_settings_ci_cd_path(project, anchor: 'autodevops-settings')) + expect(presenter.autodevops_anchor_data).to have_attributes( + is_link: false, + label: a_string_including('Enable Auto DevOps'), + link: presenter.project_settings_ci_cd_path(project, anchor: 'autodevops-settings') + ) end end end @@ -374,29 +462,37 @@ describe ProjectPresenter do context 'when user can create Kubernetes cluster' do it 'returns link to cluster if only one exists' do project.add_maintainer(user) + cluster = create(:cluster, projects: [project]) - expect(presenter.kubernetes_cluster_anchor_data).to have_attributes(is_link: false, - label: a_string_including('Kubernetes configured'), - link: presenter.project_cluster_path(project, cluster)) + expect(presenter.kubernetes_cluster_anchor_data).to have_attributes( + is_link: false, + label: a_string_including('Kubernetes configured'), + link: presenter.project_cluster_path(project, cluster) + ) end it 'returns link to clusters page if more than one exists' do project.add_maintainer(user) + create(:cluster, :production_environment, projects: [project]) create(:cluster, projects: [project]) - expect(presenter.kubernetes_cluster_anchor_data).to have_attributes(is_link: false, - label: a_string_including('Kubernetes configured'), - link: presenter.project_clusters_path(project)) + expect(presenter.kubernetes_cluster_anchor_data).to have_attributes( + is_link: false, + label: a_string_including('Kubernetes configured'), + link: presenter.project_clusters_path(project) + ) end it 'returns link to create a cluster if no cluster exists' do project.add_maintainer(user) - expect(presenter.kubernetes_cluster_anchor_data).to have_attributes(is_link: false, - label: a_string_including('Add Kubernetes cluster'), - link: presenter.new_project_cluster_path(project)) + expect(presenter.kubernetes_cluster_anchor_data).to have_attributes( + is_link: false, + label: a_string_including('Add Kubernetes cluster'), + link: presenter.new_project_cluster_path(project) + ) end end @@ -464,7 +560,7 @@ describe ProjectPresenter do end context 'initialized repo' do - let(:project) { create(:project, :repository) } + let_it_be(:project) { create(:project, :repository) } it 'orders the items correctly' do expect(empty_repo_statistics_buttons.map(&:label)).to start_with( |