Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
3b30c28dc6
commit
69eacec239
|
@ -1008,8 +1008,6 @@ module Ci
|
|||
# Set scheduling type of processables if they were created before scheduling_type
|
||||
# data was deployed (https://gitlab.com/gitlab-org/gitlab/-/merge_requests/22246).
|
||||
def ensure_scheduling_type!
|
||||
return unless ::Gitlab::Ci::Features.ensure_scheduling_type_enabled?
|
||||
|
||||
processables.populate_scheduling_type!
|
||||
end
|
||||
|
||||
|
|
|
@ -65,6 +65,7 @@ class Project < ApplicationRecord
|
|||
|
||||
cache_markdown_field :description, pipeline: :description
|
||||
|
||||
default_value_for :packages_enabled, true
|
||||
default_value_for :archived, false
|
||||
default_value_for :resolve_outdated_diff_discussions, false
|
||||
default_value_for :container_registry_enabled, gitlab_config_features.container_registry
|
||||
|
@ -446,6 +447,7 @@ class Project < ApplicationRecord
|
|||
# Sometimes queries (e.g. using CTEs) require explicit disambiguation with table name
|
||||
scope :projects_order_id_desc, -> { reorder(self.arel_table['id'].desc) }
|
||||
|
||||
scope :with_packages, -> { joins(:packages) }
|
||||
scope :in_namespace, ->(namespace_ids) { where(namespace_id: namespace_ids) }
|
||||
scope :personal, ->(user) { where(namespace_id: user.namespace_id) }
|
||||
scope :joined, ->(user) { where('namespace_id != ?', user.namespace_id) }
|
||||
|
@ -866,6 +868,15 @@ class Project < ApplicationRecord
|
|||
end
|
||||
end
|
||||
|
||||
# Because we use default_value_for we need to be sure
|
||||
# packages_enabled= method does exist even if we rollback migration.
|
||||
# Otherwise many tests from spec/migrations will fail.
|
||||
def packages_enabled=(value)
|
||||
if has_attribute?(:packages_enabled)
|
||||
write_attribute(:packages_enabled, value)
|
||||
end
|
||||
end
|
||||
|
||||
def cleanup
|
||||
@repository = nil
|
||||
end
|
||||
|
|
|
@ -154,6 +154,9 @@ class ProjectPolicy < BasePolicy
|
|||
::Feature.enabled?(:build_service_proxy, @subject)
|
||||
end
|
||||
|
||||
with_scope :subject
|
||||
condition(:packages_disabled) { !@subject.packages_enabled }
|
||||
|
||||
features = %w[
|
||||
merge_requests
|
||||
issues
|
||||
|
@ -296,12 +299,17 @@ class ProjectPolicy < BasePolicy
|
|||
enable :read_metrics_user_starred_dashboard
|
||||
end
|
||||
|
||||
rule { packages_disabled | repository_disabled }.policy do
|
||||
prevent(*create_read_update_admin_destroy(:package))
|
||||
end
|
||||
|
||||
rule { owner | admin | guest | group_member }.prevent :request_access
|
||||
rule { ~request_access_enabled }.prevent :request_access
|
||||
|
||||
rule { can?(:developer_access) & can?(:create_issue) }.enable :import_issues
|
||||
|
||||
rule { can?(:developer_access) }.policy do
|
||||
enable :create_package
|
||||
enable :admin_board
|
||||
enable :admin_merge_request
|
||||
enable :admin_milestone
|
||||
|
@ -342,6 +350,7 @@ class ProjectPolicy < BasePolicy
|
|||
end
|
||||
|
||||
rule { can?(:maintainer_access) }.policy do
|
||||
enable :destroy_package
|
||||
enable :admin_board
|
||||
enable :push_to_delete_protected_branch
|
||||
enable :update_snippet
|
||||
|
|
|
@ -34,10 +34,6 @@ module Ci
|
|||
|
||||
attributes[:user] = current_user
|
||||
|
||||
# TODO: we can probably remove this logic
|
||||
# see: https://gitlab.com/gitlab-org/gitlab/-/issues/217930
|
||||
attributes[:scheduling_type] ||= build.find_legacy_scheduling_type
|
||||
|
||||
Ci::Build.transaction do
|
||||
# mark all other builds of that name as retried
|
||||
build.pipeline.builds.latest
|
||||
|
|
|
@ -45,7 +45,7 @@
|
|||
|
||||
- if note_editable
|
||||
.note-actions-item
|
||||
= button_tag title: 'Edit comment', class: 'note-action-button js-note-edit has-tooltip btn btn-transparent', data: { container: 'body' } do
|
||||
= button_tag title: 'Edit comment', class: 'note-action-button js-note-edit has-tooltip btn btn-transparent', data: { container: 'body', qa_selector: 'edit_comment_button' } do
|
||||
%span.link-highlight
|
||||
= custom_icon('icon_pencil')
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
- if note_editable || !is_current_user
|
||||
.dropdown.more-actions.note-actions-item
|
||||
= button_tag title: 'More actions', class: 'note-action-button more-actions-toggle has-tooltip btn btn-transparent', data: { toggle: 'dropdown', container: 'body' } do
|
||||
= button_tag title: 'More actions', class: 'note-action-button more-actions-toggle has-tooltip btn btn-transparent', data: { toggle: 'dropdown', container: 'body', qa_selector: 'more_actions_dropdown' } do
|
||||
%span.icon
|
||||
= custom_icon('ellipsis_v')
|
||||
%ul.dropdown-menu.more-actions-dropdown.dropdown-open-left
|
||||
|
@ -14,6 +14,6 @@
|
|||
= _('Report abuse to admin')
|
||||
- if note_editable
|
||||
%li
|
||||
= link_to note_url(note), method: :delete, data: { confirm: 'Are you sure you want to delete this comment?' }, remote: true, class: 'js-note-delete' do
|
||||
= link_to note_url(note), method: :delete, data: { confirm: 'Are you sure you want to delete this comment?', qa_selector: 'delete_comment_button' }, remote: true, class: 'js-note-delete' do
|
||||
%span.text-danger
|
||||
= _('Delete comment')
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
- noteable_name = @note.noteable.human_class_name
|
||||
|
||||
.float-left.btn-group.append-right-10.droplab-dropdown.comment-type-dropdown.js-comment-type-dropdown
|
||||
%input.btn.btn-nr.btn-success.js-comment-button.js-comment-submit-button{ type: 'submit', value: _('Comment') }
|
||||
%input.btn.btn-nr.btn-success.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: 'btn btn-nr 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
|
||||
|
|
|
@ -3,12 +3,12 @@
|
|||
= hidden_field_tag :target_id, '', class: 'js-form-target-id'
|
||||
= hidden_field_tag :target_type, '', class: 'js-form-target-type'
|
||||
= render layout: 'shared/md_preview', locals: { url: preview_markdown_path(project), referenced_users: true } do
|
||||
= render 'shared/zen', attr: 'note[note]', classes: 'note-textarea js-note-text js-task-list-field', placeholder: _("Write a comment or drag your files here…")
|
||||
= render 'shared/zen', attr: 'note[note]', classes: 'note-textarea js-note-text js-task-list-field', qa_selector: 'edit_note_field', placeholder: _("Write a comment or drag your files here…")
|
||||
= render 'shared/notes/hints'
|
||||
|
||||
.note-form-actions.clearfix
|
||||
.settings-message.note-edit-warning.js-finish-edit-warning
|
||||
= _("Finish editing this message first!")
|
||||
= submit_tag _('Save comment'), class: 'btn btn-nr btn-success js-comment-save-button'
|
||||
= submit_tag _('Save comment'), class: 'btn btn-nr btn-success js-comment-save-button', data: { qa_selector: 'save_comment_button' }
|
||||
%button.btn.btn-nr.btn-cancel.note-edit-cancel{ type: 'button' }
|
||||
= _("Cancel")
|
||||
|
|
|
@ -26,7 +26,7 @@
|
|||
|
||||
.discussion-form-container.discussion-with-resolve-btn.flex-column.p-0
|
||||
= render layout: 'shared/md_preview', locals: { url: preview_url, referenced_users: true } do
|
||||
= render 'shared/zen', f: f,
|
||||
= render 'shared/zen', f: f, qa_selector: 'note_field',
|
||||
attr: :note,
|
||||
classes: 'note-textarea js-note-text',
|
||||
placeholder: _("Write a comment or drag your files here…"),
|
||||
|
|
|
@ -34,7 +34,7 @@
|
|||
%span.note-header-author-name.bold
|
||||
= note.author.name
|
||||
= user_status(note.author)
|
||||
%span.note-headline-light
|
||||
%span.note-headline-light{ data: { qa_selector: 'note_author_content' } }
|
||||
= note.author.to_reference
|
||||
%span.note-headline-light.note-headline-meta
|
||||
- if note.system
|
||||
|
@ -51,7 +51,7 @@
|
|||
- else
|
||||
= render 'projects/notes/actions', note: note, note_editable: note_editable
|
||||
.note-body{ class: note_editable ? 'js-task-list-container' : '' }
|
||||
.note-text.md
|
||||
.note-text.md{ data: { qa_selector: 'note_content' } }
|
||||
= markdown_field(note, :note)
|
||||
= edited_time_ago_with_tooltip(note, placement: 'bottom', html_class: 'note_edited_ago')
|
||||
.original-note-content.hidden{ data: { post_url: note_url(note), target_id: note.noteable.id, target_type: note.noteable.class.name.underscore } }
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
|
||||
- if note_editable
|
||||
.note-actions-item
|
||||
= button_tag title: _('Edit comment'), class: 'note-action-button js-note-edit has-tooltip btn btn-transparent', data: { container: 'body' } do
|
||||
= button_tag title: _('Edit comment'), class: 'note-action-button js-note-edit has-tooltip btn btn-transparent', data: { container: 'body', qa_selector: 'edit_comment_button' } do
|
||||
%span.link-highlight
|
||||
= custom_icon('icon_pencil')
|
||||
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Add installed state metrics for Cilium cluster application
|
||||
merge_request: 35808
|
||||
author:
|
||||
type: added
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Removes ci_ensure_scheduling_type feature flag
|
||||
merge_request: 36140
|
||||
author:
|
||||
type: other
|
|
@ -20,7 +20,7 @@ Every feature or use case document should include the following content in the f
|
|||
with exceptions and details noted below and in the template included on this page.
|
||||
|
||||
- **Title**: Top-level heading with the feature name, or a use case name, which would start with
|
||||
a verb, like Configuring, Enabling, and so on.
|
||||
a verb, like "Configure", "Enable", and so on.
|
||||
- **Introduction**: A couple sentences about the subject matter and what's to be found
|
||||
on this page. Describe what the feature or topic is, what it does, and in what context it should
|
||||
be used. There is no need to add a title called "Introduction" or "Overview," because people rarely
|
||||
|
@ -53,7 +53,7 @@ info: To determine the technical writer assigned to the Stage/Group associated w
|
|||
---
|
||||
|
||||
# Feature Name or Use Case Name **[TIER]** (1)
|
||||
<!--If writing about a use case, drop the tier, and start with a verb, e.g. 'Configuring', 'Implementing', + the goal/scenario-->
|
||||
<!--If writing about a use case, drop the tier, and start with a verb, e.g. "Configure", "Implement", + the goal/scenario-->
|
||||
|
||||
<!--For pages on newly introduced features, add the following line. If only some aspects of the feature have been introduced, specify what parts of the feature.-->
|
||||
> [Introduced](link_to_issue_or_mr) in GitLab (Tier) X.Y (2).
|
||||
|
@ -102,12 +102,12 @@ Larger instruction sets may have subsections covering specific phases of the pro
|
|||
Where appropriate, provide examples of code or configuration files to better clarify intended usage.
|
||||
|
||||
- Write a step-by-step guide, with no gaps between the steps.
|
||||
- Include example code or configurations as part of the relevant step. Use appropriate markdown to [wrap code blocks with syntax highlighting](../../user/markdown.md#colored-code-and-syntax-highlighting).
|
||||
- Include example code or configurations as part of the relevant step. Use appropriate Markdown to [wrap code blocks with syntax highlighting](../../user/markdown.md#colored-code-and-syntax-highlighting).
|
||||
- Start with an h2 (`##`), break complex steps into small steps using
|
||||
subheadings h3 > h4 > h5 > h6. _Never skip a hierarchy level, such
|
||||
as h2 > h4_, as it will break the TOC and may affect the breadcrumbs.
|
||||
- Use short and descriptive headings (up to ~50 chars). You can use one
|
||||
single heading like `## Configuring X` for instructions when the feature
|
||||
single heading like `## Configure X` for instructions when the feature
|
||||
is simple and the document is short.
|
||||
|
||||
<!-- ## Troubleshooting
|
||||
|
|
|
@ -451,6 +451,7 @@ appear to be associated to any of the services running, since they all appear to
|
|||
| `clusters_applications_runner` | `counts` | `configure` | | | Total GitLab Managed clusters with Runner enabled |
|
||||
| `clusters_applications_knative` | `counts` | `configure` | | | Total GitLab Managed clusters with Knative enabled |
|
||||
| `clusters_applications_elastic_stack` | `counts` | `configure` | | | Total GitLab Managed clusters with Elastic Stack enabled |
|
||||
| `clusters_applications_cilium` | `counts` | `configure` | | | Total GitLab Managed clusters with Cilium enabled |
|
||||
| `clusters_management_project` | `counts` | `configure` | | | Total GitLab Managed clusters with defined cluster management project |
|
||||
| `in_review_folder` | `counts` | | | | |
|
||||
| `grafana_integrated_projects` | `counts` | | | | |
|
||||
|
|
|
@ -10,10 +10,6 @@ module Gitlab
|
|||
::Feature.enabled?(:ci_artifacts_exclude, default_enabled: true)
|
||||
end
|
||||
|
||||
def self.ensure_scheduling_type_enabled?
|
||||
::Feature.enabled?(:ci_ensure_scheduling_type, default_enabled: true)
|
||||
end
|
||||
|
||||
def self.job_heartbeats_runner?(project)
|
||||
::Feature.enabled?(:ci_job_heartbeats_runner, project, default_enabled: true)
|
||||
end
|
||||
|
|
|
@ -109,6 +109,7 @@ module Gitlab
|
|||
clusters_applications_knative: count(::Clusters::Applications::Knative.available),
|
||||
clusters_applications_elastic_stack: count(::Clusters::Applications::ElasticStack.available),
|
||||
clusters_applications_jupyter: count(::Clusters::Applications::Jupyter.available),
|
||||
clusters_applications_cilium: count(::Clusters::Applications::Cilium.available),
|
||||
clusters_management_project: count(::Clusters::Cluster.with_management_project),
|
||||
in_review_folder: count(::Environment.in_review_folder),
|
||||
grafana_integrated_projects: count(GrafanaIntegration.enabled),
|
||||
|
|
|
@ -7318,6 +7318,15 @@ msgstr ""
|
|||
msgid "Dashboard|Unable to add %{invalidProjects}. This dashboard is available for public projects, and private projects in groups with a Silver plan."
|
||||
msgstr ""
|
||||
|
||||
msgid "DastProfiles|Manage profiles"
|
||||
msgstr ""
|
||||
|
||||
msgid "DastProfiles|New Site Profile"
|
||||
msgstr ""
|
||||
|
||||
msgid "DastProfiles|New site profile"
|
||||
msgstr ""
|
||||
|
||||
msgid "Data is still calculating..."
|
||||
msgstr ""
|
||||
|
||||
|
|
3
qa/qa.rb
3
qa/qa.rb
|
@ -365,6 +365,7 @@ module QA
|
|||
|
||||
module Snippet
|
||||
autoload :New, 'qa/page/project/snippet/new'
|
||||
autoload :Show, 'qa/page/project/snippet/show'
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -462,6 +463,8 @@ module QA
|
|||
autoload :CustomMetric, 'qa/page/component/custom_metric'
|
||||
autoload :DesignManagement, 'qa/page/component/design_management'
|
||||
autoload :ProjectSelector, 'qa/page/component/project_selector'
|
||||
autoload :Snippet, 'qa/page/component/snippet'
|
||||
autoload :NewSnippet, 'qa/page/component/new_snippet'
|
||||
|
||||
module Issuable
|
||||
autoload :Common, 'qa/page/component/issuable/common'
|
||||
|
|
|
@ -0,0 +1,80 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module QA
|
||||
module Page
|
||||
module Component
|
||||
module NewSnippet
|
||||
extend QA::Page::PageConcern
|
||||
|
||||
def self.included(base)
|
||||
super
|
||||
|
||||
base.view 'app/assets/javascripts/snippets/components/edit.vue' do
|
||||
element :snippet_title_field, required: true
|
||||
element :submit_button
|
||||
end
|
||||
|
||||
base.view 'app/assets/javascripts/snippets/components/snippet_description_edit.vue' do
|
||||
element :snippet_description_field
|
||||
element :description_placeholder, required: true
|
||||
end
|
||||
|
||||
base.view 'app/assets/javascripts/snippets/components/snippet_blob_edit.vue' do
|
||||
element :file_name_field
|
||||
end
|
||||
|
||||
base.view 'app/views/shared/form_elements/_description.html.haml' do
|
||||
element :issuable_form_description
|
||||
end
|
||||
|
||||
base.view 'app/views/shared/snippets/_form.html.haml' do
|
||||
element :snippet_description_field
|
||||
element :description_placeholder
|
||||
element :snippet_title_field
|
||||
element :file_name_field
|
||||
element :submit_button
|
||||
end
|
||||
|
||||
base.view 'app/views/shared/_zen.html.haml' do
|
||||
# This 'element' is here only to ensure the changes in the view source aren't mistakenly changed
|
||||
element :_, "qa_selector = local_assigns.fetch(:qa_selector, '')" # rubocop:disable QA/ElementWithPattern
|
||||
end
|
||||
end
|
||||
|
||||
def fill_title(title)
|
||||
fill_element :snippet_title_field, title
|
||||
end
|
||||
|
||||
def fill_description(description)
|
||||
click_element :description_placeholder
|
||||
fill_element :snippet_description_field, description
|
||||
end
|
||||
|
||||
def set_visibility(visibility)
|
||||
choose visibility
|
||||
end
|
||||
|
||||
def fill_file_name(name)
|
||||
finished_loading?
|
||||
fill_element :file_name_field, name
|
||||
end
|
||||
|
||||
def fill_file_content(content)
|
||||
finished_loading?
|
||||
text_area.set content
|
||||
end
|
||||
|
||||
def click_create_snippet_button
|
||||
wait_until(reload: false) { !find_element(:submit_button).disabled? }
|
||||
click_element(:submit_button, Page::Dashboard::Snippet::Show)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def text_area
|
||||
find('#editor textarea', visible: false)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,174 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module QA
|
||||
module Page
|
||||
module Component
|
||||
module Snippet
|
||||
extend QA::Page::PageConcern
|
||||
|
||||
def self.included(base)
|
||||
super
|
||||
|
||||
base.view 'app/assets/javascripts/snippets/components/snippet_title.vue' do
|
||||
element :snippet_title_content, required: true
|
||||
end
|
||||
|
||||
base.view 'app/assets/javascripts/snippets/components/snippet_description_view.vue' do
|
||||
element :snippet_description_content
|
||||
end
|
||||
|
||||
base.view 'app/assets/javascripts/snippets/components/snippet_header.vue' do
|
||||
element :snippet_container
|
||||
end
|
||||
|
||||
base.view 'app/assets/javascripts/blob/components/blob_header_filepath.vue' do
|
||||
element :file_title_content
|
||||
end
|
||||
|
||||
base.view 'app/assets/javascripts/vue_shared/components/blob_viewers/simple_viewer.vue' do
|
||||
element :file_content
|
||||
end
|
||||
|
||||
base.view 'app/assets/javascripts/blob/components/blob_content.vue' do
|
||||
element :file_content
|
||||
end
|
||||
|
||||
base.view 'app/assets/javascripts/snippets/components/snippet_header.vue' do
|
||||
element :snippet_action_button
|
||||
element :delete_snippet_button
|
||||
end
|
||||
|
||||
base.view 'app/assets/javascripts/snippets/components/snippet_blob_view.vue' do
|
||||
element :clone_button
|
||||
end
|
||||
|
||||
base.view 'app/assets/javascripts/vue_shared/components/clone_dropdown.vue' do
|
||||
element :copy_http_url_button
|
||||
element :copy_ssh_url_button
|
||||
end
|
||||
|
||||
base.view 'app/views/shared/notes/_comment_button.html.haml' do
|
||||
element :comment_button
|
||||
end
|
||||
|
||||
base.view 'app/views/shared/notes/_form.html.haml' do
|
||||
element :note_field
|
||||
end
|
||||
|
||||
base.view 'app/views/snippets/notes/_actions.html.haml' do
|
||||
element :edit_comment_button
|
||||
end
|
||||
|
||||
base.view 'app/views/shared/notes/_edit_form.html.haml' do
|
||||
element :edit_note_field
|
||||
element :save_comment_button
|
||||
end
|
||||
|
||||
base.view 'app/views/shared/notes/_note.html.haml' do
|
||||
element :note_content
|
||||
element :note_author_content
|
||||
end
|
||||
|
||||
base.view 'app/views/projects/notes/_more_actions_dropdown.html.haml' do
|
||||
element :more_actions_dropdown
|
||||
element :delete_comment_button
|
||||
end
|
||||
end
|
||||
|
||||
def has_snippet_title?(snippet_title)
|
||||
has_element? :snippet_title_content, text: snippet_title
|
||||
end
|
||||
|
||||
def has_snippet_description?(snippet_description)
|
||||
has_element? :snippet_description_content, text: snippet_description
|
||||
end
|
||||
|
||||
def has_no_snippet_description?
|
||||
has_no_element?(:snippet_description_field)
|
||||
end
|
||||
|
||||
def has_visibility_type?(visibility_type)
|
||||
within_element(:snippet_container) do
|
||||
has_text?(visibility_type)
|
||||
end
|
||||
end
|
||||
|
||||
def has_file_name?(file_name)
|
||||
within_element(:file_title_content) do
|
||||
has_text?(file_name)
|
||||
end
|
||||
end
|
||||
|
||||
def has_file_content?(file_content)
|
||||
finished_loading?
|
||||
within_element(:file_content) do
|
||||
has_text?(file_content)
|
||||
end
|
||||
end
|
||||
|
||||
def click_edit_button
|
||||
finished_loading?
|
||||
click_element(:snippet_action_button, action: 'Edit')
|
||||
end
|
||||
|
||||
def click_delete_button
|
||||
finished_loading?
|
||||
click_element(:snippet_action_button, action: 'Delete')
|
||||
click_element(:delete_snippet_button)
|
||||
# wait for the page to reload after deletion
|
||||
wait_until(reload: false) do
|
||||
has_no_element?(:delete_snippet_button) &&
|
||||
has_no_element?(:snippet_action_button, action: 'Delete')
|
||||
end
|
||||
end
|
||||
|
||||
def get_repository_uri_http
|
||||
finished_loading?
|
||||
click_element(:clone_button)
|
||||
Git::Location.new(find_element(:copy_http_url_button)['data-clipboard-text']).uri.to_s
|
||||
end
|
||||
|
||||
def get_repository_uri_ssh
|
||||
finished_loading?
|
||||
click_element(:clone_button)
|
||||
Git::Location.new(find_element(:copy_ssh_url_button)['data-clipboard-text']).uri.to_s
|
||||
end
|
||||
|
||||
def add_comment(comment)
|
||||
finished_loading?
|
||||
fill_element(:note_field, comment)
|
||||
click_element(:comment_button)
|
||||
end
|
||||
|
||||
def has_comment_author?(author_username)
|
||||
finished_loading?
|
||||
within_element(:note_author_content) do
|
||||
has_text?('@' + author_username)
|
||||
end
|
||||
end
|
||||
|
||||
def has_comment_content?(comment_content)
|
||||
finished_loading?
|
||||
within_element(:note_content) do
|
||||
has_text?(comment_content)
|
||||
end
|
||||
end
|
||||
|
||||
def edit_comment(comment)
|
||||
finished_loading?
|
||||
click_element(:edit_comment_button)
|
||||
fill_element(:edit_note_field, comment)
|
||||
click_element(:save_comment_button)
|
||||
end
|
||||
|
||||
def delete_comment(comment)
|
||||
finished_loading?
|
||||
click_element(:more_actions_dropdown)
|
||||
accept_alert do
|
||||
click_element(:delete_comment_button)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -5,70 +5,7 @@ module QA
|
|||
module Dashboard
|
||||
module Snippet
|
||||
class New < Page::Base
|
||||
view 'app/assets/javascripts/snippets/components/edit.vue' do
|
||||
element :snippet_title_field, required: true
|
||||
element :submit_button
|
||||
end
|
||||
|
||||
view 'app/assets/javascripts/snippets/components/snippet_description_edit.vue' do
|
||||
element :snippet_description_field
|
||||
element :description_placeholder, required: true
|
||||
end
|
||||
|
||||
view 'app/assets/javascripts/snippets/components/snippet_blob_edit.vue' do
|
||||
element :file_name_field
|
||||
end
|
||||
|
||||
view 'app/views/shared/form_elements/_description.html.haml' do
|
||||
element :issuable_form_description
|
||||
end
|
||||
|
||||
view 'app/views/shared/snippets/_form.html.haml' do
|
||||
element :snippet_description_field
|
||||
element :description_placeholder
|
||||
element :snippet_title_field
|
||||
element :file_name_field
|
||||
element :submit_button
|
||||
end
|
||||
|
||||
view 'app/views/shared/_zen.html.haml' do
|
||||
# This 'element' is here only to ensure the changes in the view source aren't mistakenly changed
|
||||
element :_, "qa_selector = local_assigns.fetch(:qa_selector, '')" # rubocop:disable QA/ElementWithPattern
|
||||
end
|
||||
|
||||
def fill_title(title)
|
||||
fill_element :snippet_title_field, title
|
||||
end
|
||||
|
||||
def fill_description(description)
|
||||
click_element :description_placeholder
|
||||
fill_element :snippet_description_field, description
|
||||
end
|
||||
|
||||
def set_visibility(visibility)
|
||||
choose visibility
|
||||
end
|
||||
|
||||
def fill_file_name(name)
|
||||
finished_loading?
|
||||
fill_element :file_name_field, name
|
||||
end
|
||||
|
||||
def fill_file_content(content)
|
||||
finished_loading?
|
||||
text_area.set content
|
||||
end
|
||||
|
||||
def click_create_snippet_button
|
||||
wait_until(reload: false) { !find_element(:submit_button).disabled? }
|
||||
click_element :submit_button
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def text_area
|
||||
find('#editor textarea', visible: false)
|
||||
end
|
||||
include Page::Component::NewSnippet
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -5,102 +5,7 @@ module QA
|
|||
module Dashboard
|
||||
module Snippet
|
||||
class Show < Page::Base
|
||||
view 'app/assets/javascripts/snippets/components/snippet_description_view.vue' do
|
||||
element :snippet_description_content
|
||||
end
|
||||
|
||||
view 'app/assets/javascripts/snippets/components/snippet_title.vue' do
|
||||
element :snippet_title_content, required: true
|
||||
end
|
||||
|
||||
view 'app/assets/javascripts/snippets/components/snippet_header.vue' do
|
||||
element :snippet_container
|
||||
end
|
||||
|
||||
view 'app/assets/javascripts/blob/components/blob_header_filepath.vue' do
|
||||
element :file_title_content
|
||||
end
|
||||
|
||||
view 'app/assets/javascripts/vue_shared/components/blob_viewers/simple_viewer.vue' do
|
||||
element :file_content
|
||||
end
|
||||
|
||||
view 'app/assets/javascripts/blob/components/blob_content.vue' do
|
||||
element :file_content
|
||||
end
|
||||
|
||||
view 'app/assets/javascripts/snippets/components/snippet_header.vue' do
|
||||
element :snippet_action_button
|
||||
element :delete_snippet_button
|
||||
end
|
||||
|
||||
view 'app/assets/javascripts/snippets/components/snippet_blob_view.vue' do
|
||||
element :clone_button
|
||||
end
|
||||
|
||||
view 'app/assets/javascripts/vue_shared/components/clone_dropdown.vue' do
|
||||
element :copy_http_url_button
|
||||
element :copy_ssh_url_button
|
||||
end
|
||||
|
||||
def has_snippet_title?(snippet_title)
|
||||
has_element? :snippet_title_content, text: snippet_title
|
||||
end
|
||||
|
||||
def has_snippet_description?(snippet_description)
|
||||
has_element? :snippet_description_content, text: snippet_description
|
||||
end
|
||||
|
||||
def has_no_snippet_description?
|
||||
has_no_element?(:snippet_description_field)
|
||||
end
|
||||
|
||||
def has_visibility_type?(visibility_type)
|
||||
within_element(:snippet_container) do
|
||||
has_text?(visibility_type)
|
||||
end
|
||||
end
|
||||
|
||||
def has_file_name?(file_name)
|
||||
within_element(:file_title_content) do
|
||||
has_text?(file_name)
|
||||
end
|
||||
end
|
||||
|
||||
def has_file_content?(file_content)
|
||||
finished_loading?
|
||||
within_element(:file_content) do
|
||||
has_text?(file_content)
|
||||
end
|
||||
end
|
||||
|
||||
def click_edit_button
|
||||
finished_loading?
|
||||
click_element(:snippet_action_button, action: 'Edit')
|
||||
end
|
||||
|
||||
def click_delete_button
|
||||
finished_loading?
|
||||
click_element(:snippet_action_button, action: 'Delete')
|
||||
click_element(:delete_snippet_button)
|
||||
# wait for the page to reload after deletion
|
||||
wait_until(reload: false) do
|
||||
has_no_element?(:delete_snippet_button) &&
|
||||
has_no_element?(:snippet_action_button, action: 'Delete')
|
||||
end
|
||||
end
|
||||
|
||||
def get_repository_uri_http
|
||||
finished_loading?
|
||||
click_element(:clone_button)
|
||||
Git::Location.new(find_element(:copy_http_url_button)['data-clipboard-text']).uri.to_s
|
||||
end
|
||||
|
||||
def get_repository_uri_ssh
|
||||
finished_loading?
|
||||
click_element(:clone_button)
|
||||
Git::Location.new(find_element(:copy_ssh_url_button)['data-clipboard-text']).uri.to_s
|
||||
end
|
||||
include Page::Component::Snippet
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -4,7 +4,8 @@ module QA
|
|||
module Page
|
||||
module Project
|
||||
module Snippet
|
||||
class New < Page::Dashboard::Snippet::New
|
||||
class New < Page::Base
|
||||
include Page::Component::NewSnippet
|
||||
include Component::LazyLoader
|
||||
view 'app/views/shared/empty_states/_snippets.html.haml' do
|
||||
element :create_first_snippet_link
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module QA
|
||||
module Page
|
||||
module Project
|
||||
module Snippet
|
||||
class Show < Page::Base
|
||||
include Page::Component::Snippet
|
||||
|
||||
view 'app/views/projects/notes/_actions.html.haml' do
|
||||
element :edit_comment_button
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,81 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module QA
|
||||
RSpec.describe 'Create' do
|
||||
describe 'Adding comments on snippets' do
|
||||
let(:comment_author) { Resource::User.fabricate_or_use(Runtime::Env.gitlab_qa_username_1, Runtime::Env.gitlab_qa_password_1) }
|
||||
let(:comment_content) { 'Comment 123' }
|
||||
let(:edited_comment_content) { 'Nice snippet!' }
|
||||
|
||||
let(:personal_snippet) do
|
||||
Resource::Snippet.fabricate! do |snippet|
|
||||
snippet.title = 'Personal snippet with a comment'
|
||||
end
|
||||
end
|
||||
|
||||
let(:project_snippet) do
|
||||
Resource::ProjectSnippet.fabricate! do |snippet|
|
||||
snippet.title = 'Project snippet with a comment'
|
||||
end
|
||||
end
|
||||
|
||||
before do
|
||||
Flow::Login.sign_in
|
||||
end
|
||||
|
||||
shared_examples 'comments on snippets' do |snippet_type|
|
||||
it "adds, edits, and deletes a comment on a #{snippet_type}" do
|
||||
send(snippet_type)
|
||||
|
||||
Page::Main::Menu.perform(&:sign_out)
|
||||
|
||||
Flow::Login.sign_in(as: comment_author)
|
||||
|
||||
send(snippet_type).visit!
|
||||
|
||||
create_comment
|
||||
verify_comment_content(comment_author.username, comment_content)
|
||||
|
||||
edit_comment
|
||||
verify_comment_content(comment_author.username, edited_comment_content)
|
||||
|
||||
delete_comment
|
||||
verify_comment_deleted
|
||||
end
|
||||
end
|
||||
|
||||
it_behaves_like 'comments on snippets', :personal_snippet
|
||||
it_behaves_like 'comments on snippets', :project_snippet
|
||||
|
||||
def create_comment
|
||||
Page::Dashboard::Snippet::Show.perform do |snippet|
|
||||
snippet.add_comment(comment_content)
|
||||
end
|
||||
end
|
||||
|
||||
def edit_comment
|
||||
Page::Dashboard::Snippet::Show.perform do |snippet|
|
||||
snippet.edit_comment(edited_comment_content)
|
||||
end
|
||||
end
|
||||
|
||||
def delete_comment
|
||||
Page::Dashboard::Snippet::Show.perform do |snippet|
|
||||
snippet.delete_comment(edited_comment_content)
|
||||
end
|
||||
end
|
||||
|
||||
def verify_comment_content(author, comment_content)
|
||||
Page::Dashboard::Snippet::Show.perform do |comment|
|
||||
expect(comment).to have_comment_author(author)
|
||||
expect(comment).to have_comment_content(comment_content)
|
||||
end
|
||||
end
|
||||
|
||||
def verify_comment_deleted
|
||||
expect(page).not_to have_content(comment_author.username)
|
||||
expect(page).not_to have_content(edited_comment_content)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -84,6 +84,7 @@ FactoryBot.define do
|
|||
create(:clusters_applications_knative, :installed, cluster: gcp_cluster)
|
||||
create(:clusters_applications_elastic_stack, :installed, cluster: gcp_cluster)
|
||||
create(:clusters_applications_jupyter, :installed, cluster: gcp_cluster)
|
||||
create(:clusters_applications_cilium, :installed, cluster: gcp_cluster)
|
||||
|
||||
create(:grafana_integration, project: projects[0], enabled: true)
|
||||
create(:grafana_integration, project: projects[1], enabled: true)
|
||||
|
|
|
@ -377,6 +377,7 @@ RSpec.describe Gitlab::UsageData, :aggregate_failures do
|
|||
expect(count_data[:clusters_applications_elastic_stack]).to eq(1)
|
||||
expect(count_data[:grafana_integrated_projects]).to eq(2)
|
||||
expect(count_data[:clusters_applications_jupyter]).to eq(1)
|
||||
expect(count_data[:clusters_applications_cilium]).to eq(1)
|
||||
expect(count_data[:clusters_management_project]).to eq(1)
|
||||
|
||||
expect(count_data[:deployments]).to eq(4)
|
||||
|
|
|
@ -119,6 +119,8 @@ RSpec.describe Project do
|
|||
it { is_expected.to have_many(:metrics_users_starred_dashboards).inverse_of(:project) }
|
||||
it { is_expected.to have_many(:repository_storage_moves) }
|
||||
it { is_expected.to have_many(:reviews).inverse_of(:project) }
|
||||
it { is_expected.to have_many(:packages).class_name('Packages::Package') }
|
||||
it { is_expected.to have_many(:package_files).class_name('Packages::PackageFile') }
|
||||
|
||||
it_behaves_like 'model with repository' do
|
||||
let_it_be(:container) { create(:project, :repository, path: 'somewhere') }
|
||||
|
@ -6167,6 +6169,39 @@ RSpec.describe Project do
|
|||
end
|
||||
end
|
||||
|
||||
describe '#packages_enabled' do
|
||||
subject { create(:project).packages_enabled }
|
||||
|
||||
it { is_expected.to be true }
|
||||
end
|
||||
|
||||
describe '#package_already_taken?' do
|
||||
let(:namespace) { create(:namespace) }
|
||||
let(:project) { create(:project, :public, namespace: namespace) }
|
||||
let!(:package) { create(:npm_package, project: project, name: "@#{namespace.path}/foo") }
|
||||
|
||||
context 'no package exists with the same name' do
|
||||
it 'returns false' do
|
||||
result = project.package_already_taken?("@#{namespace.path}/bar")
|
||||
expect(result).to be false
|
||||
end
|
||||
|
||||
it 'returns false if it is the project that the package belongs to' do
|
||||
result = project.package_already_taken?("@#{namespace.path}/foo")
|
||||
expect(result).to be false
|
||||
end
|
||||
end
|
||||
|
||||
context 'a package already exists with the same name' do
|
||||
let(:alt_project) { create(:project, :public, namespace: namespace) }
|
||||
|
||||
it 'returns true' do
|
||||
result = alt_project.package_already_taken?("@#{namespace.path}/foo")
|
||||
expect(result).to be true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#design_management_enabled?' do
|
||||
let(:project) { build(:project) }
|
||||
|
||||
|
|
|
@ -941,4 +941,64 @@ RSpec.describe ProjectPolicy do
|
|||
it { is_expected.to be_disallowed(:read_build_report_results) }
|
||||
end
|
||||
end
|
||||
|
||||
describe 'read_package' do
|
||||
subject { described_class.new(current_user, project) }
|
||||
|
||||
context 'with admin' do
|
||||
let(:current_user) { admin }
|
||||
|
||||
it { is_expected.to be_allowed(:read_package) }
|
||||
|
||||
context 'when repository is disabled' do
|
||||
before do
|
||||
project.project_feature.update(repository_access_level: ProjectFeature::DISABLED)
|
||||
end
|
||||
|
||||
it { is_expected.to be_disallowed(:read_package) }
|
||||
end
|
||||
end
|
||||
|
||||
context 'with owner' do
|
||||
let(:current_user) { owner }
|
||||
|
||||
it { is_expected.to be_allowed(:read_package) }
|
||||
end
|
||||
|
||||
context 'with maintainer' do
|
||||
let(:current_user) { maintainer }
|
||||
|
||||
it { is_expected.to be_allowed(:read_package) }
|
||||
end
|
||||
|
||||
context 'with developer' do
|
||||
let(:current_user) { developer }
|
||||
|
||||
it { is_expected.to be_allowed(:read_package) }
|
||||
end
|
||||
|
||||
context 'with reporter' do
|
||||
let(:current_user) { reporter }
|
||||
|
||||
it { is_expected.to be_allowed(:read_package) }
|
||||
end
|
||||
|
||||
context 'with guest' do
|
||||
let(:current_user) { guest }
|
||||
|
||||
it { is_expected.to be_allowed(:read_package) }
|
||||
end
|
||||
|
||||
context 'with non member' do
|
||||
let(:current_user) { create(:user) }
|
||||
|
||||
it { is_expected.to be_allowed(:read_package) }
|
||||
end
|
||||
|
||||
context 'with anonymous' do
|
||||
let(:current_user) { nil }
|
||||
|
||||
it { is_expected.to be_allowed(:read_package) }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -278,28 +278,6 @@ RSpec.describe Ci::RetryBuildService do
|
|||
expect(new_build.metadata.expanded_environment_name).to eq('production')
|
||||
end
|
||||
end
|
||||
|
||||
context 'when scheduling_type of build is nil' do
|
||||
before do
|
||||
build.update_columns(scheduling_type: nil)
|
||||
end
|
||||
|
||||
context 'when build has not needs' do
|
||||
it 'sets scheduling_type as :stage' do
|
||||
expect(new_build.scheduling_type).to eq('stage')
|
||||
end
|
||||
end
|
||||
|
||||
context 'when build has needs' do
|
||||
before do
|
||||
create(:ci_build_need, build: build)
|
||||
end
|
||||
|
||||
it 'sets scheduling_type as :dag' do
|
||||
expect(new_build.scheduling_type).to eq('dag')
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when user does not have ability to execute build' do
|
||||
|
|
Loading…
Reference in New Issue