Commit 756e9a8c authored by Sanad Liaquat's avatar Sanad Liaquat

Merge branch 'qa-e2e-add-comment-to-snippet' into 'master'

E2E for adding comments to snippets

Closes gitlab-org/quality/testcases#814

See merge request gitlab-org/gitlab!35383
parents f5a7e709 5ab4fe78
......@@ -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')
- 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')
......
......@@ -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'
......
# 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
# 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
......
# 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
# 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
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment