summaryrefslogtreecommitdiff
path: root/spec/features
diff options
context:
space:
mode:
authorLin Jen-Shin <godfat@godfat.org>2017-05-23 02:10:29 +0800
committerLin Jen-Shin <godfat@godfat.org>2017-05-23 02:10:29 +0800
commit1a4130d3a6cfb4956f8bb1186cc499ea549d8e18 (patch)
tree076adcb3e6f3800a1a7bbc6809839d5cb3b3f372 /spec/features
parent3c8a6fba67998eb17240b15db85f8d1c8aff338e (diff)
parent18a6d9c5326bc2b90a1f0cc8664d638a39885924 (diff)
downloadgitlab-ce-27377-preload-pipeline-entity.tar.gz
Merge remote-tracking branch 'upstream/master' into 27377-preload-pipeline-entity27377-preload-pipeline-entity
* upstream/master: (2534 commits) Update VERSION to 9.3.0-pre Update CHANGELOG.md for 9.2.0 removes unnecessary redundacy in usage ping doc Respect the typo as rubocop said Add a test to ensure this works on MySQL Change pipelines schedules help page path change domain to hostname in usage ping doc Fixes broken MySQL migration for retried Show password field mask while editing service settings Add notes for supported schedulers and cloud providers Move environment monitoring to environments doc Add docs for change of Cache/Artifact restore order" Avoid resource intensive login checks if password is not provided Change translation for 'coding' by 'desarrollo' for Spanish Add to docs: issues multiple assignees rename "Add emoji" and "Award emoji" to "Add reaction" where appropriate Add project and group notification settings info 32570 Fix border-bottom for project activity tab Add users endpoint to frontend API class Rename users on mysql ...
Diffstat (limited to 'spec/features')
-rw-r--r--spec/features/admin/admin_browse_spam_logs_spec.rb2
-rw-r--r--spec/features/admin/admin_cohorts_spec.rb15
-rw-r--r--spec/features/admin/admin_deploy_keys_spec.rb2
-rw-r--r--spec/features/admin/admin_groups_spec.rb41
-rw-r--r--spec/features/admin/admin_health_check_spec.rb6
-rw-r--r--spec/features/admin/admin_hooks_spec.rb47
-rw-r--r--spec/features/admin/admin_labels_spec.rb2
-rw-r--r--spec/features/admin/admin_manage_applications_spec.rb2
-rw-r--r--spec/features/admin/admin_projects_spec.rb2
-rw-r--r--spec/features/admin/admin_requests_profiles_spec.rb69
-rw-r--r--spec/features/admin/admin_users_impersonation_tokens_spec.rb4
-rw-r--r--spec/features/admin/admin_users_spec.rb4
-rw-r--r--spec/features/admin/admin_uses_repository_checks_spec.rb2
-rw-r--r--spec/features/atom/dashboard_issues_spec.rb8
-rw-r--r--spec/features/atom/issues_spec.rb8
-rw-r--r--spec/features/atom/users_spec.rb2
-rw-r--r--spec/features/auto_deploy_spec.rb19
-rw-r--r--spec/features/boards/add_issues_modal_spec.rb19
-rw-r--r--spec/features/boards/boards_spec.rb5
-rw-r--r--spec/features/boards/issue_ordering_spec.rb2
-rw-r--r--spec/features/boards/keyboard_shortcut_spec.rb2
-rw-r--r--spec/features/boards/modal_filter_spec.rb4
-rw-r--r--spec/features/boards/new_issue_spec.rb1
-rw-r--r--spec/features/boards/sidebar_spec.rb18
-rw-r--r--spec/features/boards/sub_group_project_spec.rb45
-rw-r--r--spec/features/calendar_spec.rb4
-rw-r--r--spec/features/commits_spec.rb2
-rw-r--r--spec/features/container_registry_spec.rb62
-rw-r--r--spec/features/copy_as_gfm_spec.rb8
-rw-r--r--spec/features/cycle_analytics_spec.rb36
-rw-r--r--spec/features/dashboard/datetime_on_tooltips_spec.rb2
-rw-r--r--spec/features/dashboard/group_spec.rb16
-rw-r--r--spec/features/dashboard/groups_list_spec.rb2
-rw-r--r--spec/features/dashboard/issuables_counter_spec.rb43
-rw-r--r--spec/features/dashboard/issues_spec.rb18
-rw-r--r--spec/features/dashboard/merge_requests_spec.rb32
-rw-r--r--spec/features/dashboard/milestone_filter_spec.rb60
-rw-r--r--spec/features/dashboard/project_member_activity_index_spec.rb2
-rw-r--r--spec/features/dashboard/projects_spec.rb11
-rw-r--r--spec/features/dashboard/shortcuts_spec.rb57
-rw-r--r--spec/features/dashboard/snippets_spec.rb47
-rw-r--r--spec/features/dashboard_issues_spec.rb6
-rw-r--r--spec/features/discussion_comments/commit_spec.rb18
-rw-r--r--spec/features/discussion_comments/issue_spec.rb16
-rw-r--r--spec/features/discussion_comments/merge_request_spec.rb16
-rw-r--r--spec/features/discussion_comments/snippets_spec.rb16
-rw-r--r--spec/features/expand_collapse_diffs_spec.rb4
-rw-r--r--spec/features/explore/groups_list_spec.rb30
-rw-r--r--spec/features/gitlab_flavored_markdown_spec.rb38
-rw-r--r--spec/features/global_search_spec.rb2
-rw-r--r--spec/features/groups/empty_states_spec.rb70
-rw-r--r--spec/features/groups/group_settings_spec.rb80
-rw-r--r--spec/features/groups/issues_spec.rb16
-rw-r--r--spec/features/groups/members/list_spec.rb54
-rw-r--r--spec/features/groups/members/sorting_spec.rb4
-rw-r--r--spec/features/groups/milestone_spec.rb36
-rw-r--r--spec/features/groups_spec.rb43
-rw-r--r--spec/features/issuables/issuable_list_spec.rb14
-rw-r--r--spec/features/issues/award_emoji_spec.rb8
-rw-r--r--spec/features/issues/award_spec.rb6
-rw-r--r--spec/features/issues/bulk_assignment_labels_spec.rb2
-rw-r--r--spec/features/issues/create_branch_merge_request_spec.rb91
-rw-r--r--spec/features/issues/create_issue_for_discussions_in_merge_request_spec.rb12
-rw-r--r--spec/features/issues/create_issue_for_single_discussion_in_merge_request_spec.rb (renamed from spec/features/issues/create_issue_for_single_discussion_in_merge_request.rb)6
-rw-r--r--spec/features/issues/filtered_search/dropdown_assignee_spec.rb3
-rw-r--r--spec/features/issues/filtered_search/dropdown_author_spec.rb3
-rw-r--r--spec/features/issues/filtered_search/dropdown_hint_spec.rb15
-rw-r--r--spec/features/issues/filtered_search/dropdown_label_spec.rb33
-rw-r--r--spec/features/issues/filtered_search/dropdown_milestone_spec.rb16
-rw-r--r--spec/features/issues/filtered_search/filter_issues_spec.rb55
-rw-r--r--spec/features/issues/filtered_search/recent_searches_spec.rb109
-rw-r--r--spec/features/issues/filtered_search/search_bar_spec.rb27
-rw-r--r--spec/features/issues/form_spec.rb89
-rw-r--r--spec/features/issues/gfm_autocomplete_spec.rb28
-rw-r--r--spec/features/issues/issue_sidebar_spec.rb41
-rw-r--r--spec/features/issues/move_spec.rb4
-rw-r--r--spec/features/issues/new_branch_button_spec.rb62
-rw-r--r--spec/features/issues/note_polling_spec.rb132
-rw-r--r--spec/features/issues/notes_on_issues_spec.rb77
-rw-r--r--spec/features/issues/spam_issues_spec.rb2
-rw-r--r--spec/features/issues/update_issues_spec.rb4
-rw-r--r--spec/features/issues/user_uses_slash_commands_spec.rb1
-rw-r--r--spec/features/issues_spec.rb97
-rw-r--r--spec/features/login_spec.rb135
-rw-r--r--spec/features/markdown_spec.rb2
-rw-r--r--spec/features/merge_requests/assign_issues_spec.rb4
-rw-r--r--spec/features/merge_requests/check_if_mergeable_with_unresolved_discussions_spec.rb10
-rw-r--r--spec/features/merge_requests/cherry_pick_spec.rb2
-rw-r--r--spec/features/merge_requests/closes_issues_spec.rb15
-rw-r--r--spec/features/merge_requests/conflicts_spec.rb4
-rw-r--r--spec/features/merge_requests/create_new_mr_spec.rb28
-rw-r--r--spec/features/merge_requests/created_from_fork_spec.rb2
-rw-r--r--spec/features/merge_requests/deleted_source_branch_spec.rb6
-rw-r--r--spec/features/merge_requests/diff_notes_avatars_spec.rb8
-rw-r--r--spec/features/merge_requests/diff_notes_resolve_spec.rb6
-rw-r--r--spec/features/merge_requests/diff_notes_spec.rb238
-rw-r--r--spec/features/merge_requests/diffs_spec.rb75
-rw-r--r--spec/features/merge_requests/discussion_spec.rb51
-rw-r--r--spec/features/merge_requests/edit_mr_spec.rb12
-rw-r--r--spec/features/merge_requests/filter_by_labels_spec.rb1
-rw-r--r--spec/features/merge_requests/filter_merge_requests_spec.rb1
-rw-r--r--spec/features/merge_requests/merge_commit_message_toggle_spec.rb19
-rw-r--r--spec/features/merge_requests/merge_immediately_with_pipeline_spec.rb18
-rw-r--r--spec/features/merge_requests/merge_when_pipeline_succeeds_spec.rb59
-rw-r--r--spec/features/merge_requests/mini_pipeline_graph_spec.rb4
-rw-r--r--spec/features/merge_requests/only_allow_merge_if_build_succeeds_spec.rb54
-rw-r--r--spec/features/merge_requests/pipelines_spec.rb2
-rw-r--r--spec/features/merge_requests/reset_filters_spec.rb3
-rw-r--r--spec/features/merge_requests/target_branch_spec.rb11
-rw-r--r--spec/features/merge_requests/update_merge_requests_spec.rb2
-rw-r--r--spec/features/merge_requests/user_posts_diff_notes_spec.rb294
-rw-r--r--spec/features/merge_requests/user_posts_notes_spec.rb146
-rw-r--r--spec/features/merge_requests/user_sees_system_notes_spec.rb31
-rw-r--r--spec/features/merge_requests/user_uses_slash_commands_spec.rb2
-rw-r--r--spec/features/merge_requests/versions_spec.rb (renamed from spec/features/merge_requests/merge_request_versions_spec.rb)102
-rw-r--r--spec/features/merge_requests/widget_deployments_spec.rb10
-rw-r--r--spec/features/merge_requests/widget_spec.rb73
-rw-r--r--spec/features/milestone_spec.rb2
-rw-r--r--spec/features/milestones/milestones_spec.rb10
-rw-r--r--spec/features/milestones/show_spec.rb2
-rw-r--r--spec/features/notes_on_merge_requests_spec.rb285
-rw-r--r--spec/features/participants_autocomplete_spec.rb4
-rw-r--r--spec/features/profiles/account_spec.rb59
-rw-r--r--spec/features/profiles/personal_access_tokens_spec.rb8
-rw-r--r--spec/features/profiles/preferences_spec.rb2
-rw-r--r--spec/features/projects/artifacts/file_spec.rb59
-rw-r--r--spec/features/projects/blobs/blob_show_spec.rb466
-rw-r--r--spec/features/projects/blobs/edit_spec.rb146
-rw-r--r--spec/features/projects/blobs/user_create_spec.rb9
-rw-r--r--spec/features/projects/branches/new_branch_ref_dropdown_spec.rb48
-rw-r--r--spec/features/projects/branches_spec.rb85
-rw-r--r--spec/features/projects/builds_spec.rb71
-rw-r--r--spec/features/projects/commit/cherry_pick_spec.rb7
-rw-r--r--spec/features/projects/commit/mini_pipeline_graph_spec.rb2
-rw-r--r--spec/features/projects/deploy_keys_spec.rb12
-rw-r--r--spec/features/projects/edit_spec.rb2
-rw-r--r--spec/features/projects/environments/environment_spec.rb6
-rw-r--r--spec/features/projects/environments/environments_spec.rb40
-rw-r--r--spec/features/projects/features_visibility_spec.rb38
-rw-r--r--spec/features/projects/files/browse_files_spec.rb17
-rw-r--r--spec/features/projects/files/creating_a_file_spec.rb10
-rw-r--r--spec/features/projects/files/dockerfile_dropdown_spec.rb8
-rw-r--r--spec/features/projects/files/editing_a_file_spec.rb6
-rw-r--r--spec/features/projects/files/files_sort_submodules_with_folders_spec.rb2
-rw-r--r--spec/features/projects/files/find_file_keyboard_spec.rb2
-rw-r--r--spec/features/projects/files/find_files_spec.rb30
-rw-r--r--spec/features/projects/files/gitignore_dropdown_spec.rb2
-rw-r--r--spec/features/projects/files/gitlab_ci_yml_dropdown_spec.rb2
-rw-r--r--spec/features/projects/files/project_owner_creates_license_file_spec.rb10
-rw-r--r--spec/features/projects/files/project_owner_sees_link_to_create_license_file_in_empty_project_spec.rb8
-rw-r--r--spec/features/projects/files/template_type_dropdown_spec.rb135
-rw-r--r--spec/features/projects/files/undo_template_spec.rb66
-rw-r--r--spec/features/projects/gfm_autocomplete_load_spec.rb2
-rw-r--r--spec/features/projects/import_export/import_file_spec.rb8
-rw-r--r--spec/features/projects/import_export/test_project_export.tar.gzbin679892 -> 681478 bytes
-rw-r--r--spec/features/projects/issuable_templates_spec.rb16
-rw-r--r--spec/features/projects/labels/update_prioritization_spec.rb1
-rw-r--r--spec/features/projects/members/group_links_spec.rb2
-rw-r--r--spec/features/projects/members/list_spec.rb90
-rw-r--r--spec/features/projects/members/master_adds_member_with_expiration_date_spec.rb1
-rw-r--r--spec/features/projects/members/sorting_spec.rb4
-rw-r--r--spec/features/projects/members/user_requests_access_spec.rb11
-rw-r--r--spec/features/projects/merge_request_button_spec.rb28
-rw-r--r--spec/features/projects/merge_requests/list_spec.rb24
-rw-r--r--spec/features/projects/milestones/milestone_spec.rb23
-rw-r--r--spec/features/projects/new_project_spec.rb16
-rw-r--r--spec/features/projects/pipeline_schedules_spec.rb146
-rw-r--r--spec/features/projects/pipelines/pipeline_spec.rb53
-rw-r--r--spec/features/projects/pipelines/pipelines_spec.rb54
-rw-r--r--spec/features/projects/project_settings_spec.rb152
-rw-r--r--spec/features/projects/ref_switcher_spec.rb1
-rw-r--r--spec/features/projects/settings/integration_settings_spec.rb96
-rw-r--r--spec/features/projects/settings/pipelines_settings_spec.rb11
-rw-r--r--spec/features/projects/snippets/show_spec.rb144
-rw-r--r--spec/features/projects/snippets_spec.rb24
-rw-r--r--spec/features/projects/user_create_dir_spec.rb1
-rw-r--r--spec/features/projects/view_on_env_spec.rb6
-rw-r--r--spec/features/projects/wiki/markdown_preview_spec.rb60
-rw-r--r--spec/features/projects/wiki/shortcuts_spec.rb20
-rw-r--r--spec/features/projects/wiki/user_creates_wiki_page_spec.rb89
-rw-r--r--spec/features/projects/wiki/user_updates_wiki_page_spec.rb6
-rw-r--r--spec/features/projects_spec.rb14
-rw-r--r--spec/features/protected_branches/access_control_ce_spec.rb79
-rw-r--r--spec/features/protected_branches_spec.rb7
-rw-r--r--spec/features/protected_tags_spec.rb92
-rw-r--r--spec/features/raven_js_spec.rb23
-rw-r--r--spec/features/search_spec.rb39
-rw-r--r--spec/features/security/project/internal_access_spec.rb74
-rw-r--r--spec/features/security/project/private_access_spec.rb96
-rw-r--r--spec/features/security/project/public_access_spec.rb74
-rw-r--r--spec/features/signup_spec.rb4
-rw-r--r--spec/features/snippets/create_snippet_spec.rb8
-rw-r--r--spec/features/snippets/explore_spec.rb25
-rw-r--r--spec/features/snippets/internal_snippet_spec.rb23
-rw-r--r--spec/features/snippets/notes_on_personal_snippets_spec.rb101
-rw-r--r--spec/features/snippets/public_snippets_spec.rb3
-rw-r--r--spec/features/snippets/show_spec.rb138
-rw-r--r--spec/features/tags/master_creates_tag_spec.rb16
-rw-r--r--spec/features/tags/master_views_tags_spec.rb2
-rw-r--r--spec/features/task_lists_spec.rb38
-rw-r--r--spec/features/todos/todos_filtering_spec.rb6
-rw-r--r--spec/features/todos/todos_spec.rb85
-rw-r--r--spec/features/u2f_spec.rb39
-rw-r--r--spec/features/unsubscribe_links_spec.rb2
-rw-r--r--spec/features/uploads/user_uploads_file_to_note_spec.rb76
-rw-r--r--spec/features/user_callout_spec.rb2
-rw-r--r--spec/features/users/projects_spec.rb2
-rw-r--r--spec/features/users/snippets_spec.rb48
-rw-r--r--spec/features/users_spec.rb1
-rw-r--r--spec/features/variables_spec.rb2
210 files changed, 5778 insertions, 1640 deletions
diff --git a/spec/features/admin/admin_browse_spam_logs_spec.rb b/spec/features/admin/admin_browse_spam_logs_spec.rb
index 562ace92598..bee57472270 100644
--- a/spec/features/admin/admin_browse_spam_logs_spec.rb
+++ b/spec/features/admin/admin_browse_spam_logs_spec.rb
@@ -1,7 +1,7 @@
require 'spec_helper'
describe 'Admin browse spam logs' do
- let!(:spam_log) { create(:spam_log) }
+ let!(:spam_log) { create(:spam_log, description: 'abcde ' * 20) }
before do
login_as :admin
diff --git a/spec/features/admin/admin_cohorts_spec.rb b/spec/features/admin/admin_cohorts_spec.rb
new file mode 100644
index 00000000000..dd14ffdb2ce
--- /dev/null
+++ b/spec/features/admin/admin_cohorts_spec.rb
@@ -0,0 +1,15 @@
+require 'rails_helper'
+
+feature 'Admin cohorts page', feature: true do
+ before do
+ login_as :admin
+ end
+
+ scenario 'See users count per month' do
+ 2.times { create(:user) }
+
+ visit admin_cohorts_path
+
+ expect(page).to have_content("#{Time.now.strftime('%b %Y')} 3 0")
+ end
+end
diff --git a/spec/features/admin/admin_deploy_keys_spec.rb b/spec/features/admin/admin_deploy_keys_spec.rb
index 7ce6cce0a5c..c0b6995a84a 100644
--- a/spec/features/admin/admin_deploy_keys_spec.rb
+++ b/spec/features/admin/admin_deploy_keys_spec.rb
@@ -18,7 +18,7 @@ RSpec.describe 'admin deploy keys', type: :feature do
describe 'create new deploy key' do
before do
visit admin_deploy_keys_path
- click_link 'New Deploy Key'
+ click_link 'New deploy key'
end
it 'creates new deploy key' do
diff --git a/spec/features/admin/admin_groups_spec.rb b/spec/features/admin/admin_groups_spec.rb
index a871e370ba2..d5f595894d6 100644
--- a/spec/features/admin/admin_groups_spec.rb
+++ b/spec/features/admin/admin_groups_spec.rb
@@ -24,14 +24,23 @@ feature 'Admin Groups', feature: true do
it 'creates new group' do
visit admin_groups_path
- click_link "New Group"
- fill_in 'group_path', with: 'gitlab'
- fill_in 'group_description', with: 'Group description'
+ click_link "New group"
+ path_component = 'gitlab'
+ group_name = 'GitLab group name'
+ group_description = 'Description of group for GitLab'
+ fill_in 'group_path', with: path_component
+ fill_in 'group_name', with: group_name
+ fill_in 'group_description', with: group_description
click_button "Create group"
- expect(current_path).to eq admin_group_path(Group.find_by(path: 'gitlab'))
- expect(page).to have_content('Group: gitlab')
- expect(page).to have_content('Group description')
+ expect(current_path).to eq admin_group_path(Group.find_by(path: path_component))
+ content = page.find('div#content-body')
+ h3_texts = content.all('h3').collect(&:text).join("\n")
+ expect(h3_texts).to match group_name
+ li_texts = content.all('li').collect(&:text).join("\n")
+ expect(li_texts).to match group_name
+ expect(li_texts).to match path_component
+ expect(li_texts).to match group_description
end
scenario 'shows the visibility level radio populated with the default value' do
@@ -39,6 +48,15 @@ feature 'Admin Groups', feature: true do
expect_selected_visibility(internal)
end
+
+ scenario 'when entered in group path, it auto filled the group name', js: true do
+ visit admin_groups_path
+ click_link "New group"
+ group_path = 'gitlab'
+ fill_in 'group_path', with: group_path
+ name_field = find('input#group_name')
+ expect(name_field.value).to eq group_path
+ end
end
describe 'show a group' do
@@ -59,6 +77,17 @@ feature 'Admin Groups', feature: true do
expect_selected_visibility(group.visibility_level)
end
+
+ scenario 'edit group path does not change group name', js: true do
+ group = create(:group, :private)
+
+ visit admin_group_edit_path(group)
+ name_field = find('input#group_name')
+ original_name = name_field.value
+ fill_in 'group_path', with: 'this-new-path'
+
+ expect(name_field.value).to eq original_name
+ end
end
describe 'add user into a group', js: true do
diff --git a/spec/features/admin/admin_health_check_spec.rb b/spec/features/admin/admin_health_check_spec.rb
index f7e49a56deb..523afa2318f 100644
--- a/spec/features/admin/admin_health_check_spec.rb
+++ b/spec/features/admin/admin_health_check_spec.rb
@@ -2,7 +2,6 @@ require 'spec_helper'
feature "Admin Health Check", feature: true do
include StubENV
- include WaitForAjax
before do
stub_env('IN_MEMORY_APPLICATION_SETTINGS', 'false')
@@ -24,11 +23,12 @@ feature "Admin Health Check", feature: true do
expect(page).to have_selector('#health-check-token', text: token)
end
- describe 'reload access token', js: true do
+ describe 'reload access token' do
it 'changes the access token' do
orig_token = current_application_settings.health_check_access_token
click_button 'Reset health check access token'
- wait_for_ajax
+
+ expect(page).to have_content('New health check access token has been generated!')
expect(find('#health-check-token').text).not_to eq orig_token
end
end
diff --git a/spec/features/admin/admin_hooks_spec.rb b/spec/features/admin/admin_hooks_spec.rb
index f246997d5a2..c5f24d412d7 100644
--- a/spec/features/admin/admin_hooks_spec.rb
+++ b/spec/features/admin/admin_hooks_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe "Admin::Hooks", feature: true do
+describe 'Admin::Hooks', feature: true do
before do
@project = create(:project)
login_as :admin
@@ -8,43 +8,68 @@ describe "Admin::Hooks", feature: true do
@system_hook = create(:system_hook)
end
- describe "GET /admin/hooks" do
- it "is ok" do
+ describe 'GET /admin/hooks' do
+ it 'is ok' do
visit admin_root_path
- page.within ".layout-nav" do
- click_on "Hooks"
+ page.within '.layout-nav' do
+ click_on 'Hooks'
end
expect(current_path).to eq(admin_hooks_path)
end
- it "has hooks list" do
+ it 'has hooks list' do
visit admin_hooks_path
expect(page).to have_content(@system_hook.url)
end
end
- describe "New Hook" do
- let(:url) { FFaker::Internet.uri('http') }
+ describe 'New Hook' do
+ let(:url) { generate(:url) }
it 'adds new hook' do
visit admin_hooks_path
fill_in 'hook_url', with: url
check 'Enable SSL verification'
- expect { click_button 'Add System Hook' }.to change(SystemHook, :count).by(1)
+ expect { click_button 'Add system hook' }.to change(SystemHook, :count).by(1)
expect(page).to have_content 'SSL Verification: enabled'
expect(current_path).to eq(admin_hooks_path)
expect(page).to have_content(url)
end
end
- describe "Test" do
+ describe 'Update existing hook' do
+ let(:new_url) { generate(:url) }
+
+ it 'updates existing hook' do
+ visit admin_hooks_path
+
+ click_link 'Edit'
+ fill_in 'hook_url', with: new_url
+ check 'Enable SSL verification'
+ click_button 'Save changes'
+
+ expect(page).to have_content 'SSL Verification: enabled'
+ expect(current_path).to eq(admin_hooks_path)
+ expect(page).to have_content(new_url)
+ end
+ end
+
+ describe 'Remove existing hook' do
+ it 'remove existing hook' do
+ visit admin_hooks_path
+
+ expect { click_link 'Remove' }.to change(SystemHook, :count).by(-1)
+ end
+ end
+
+ describe 'Test' do
before do
WebMock.stub_request(:post, @system_hook.url)
visit admin_hooks_path
- click_link "Test Hook"
+ click_link 'Test hook'
end
it { expect(current_path).to eq(admin_hooks_path) }
diff --git a/spec/features/admin/admin_labels_spec.rb b/spec/features/admin/admin_labels_spec.rb
index 6d6c9165c83..fa3d9ee25c0 100644
--- a/spec/features/admin/admin_labels_spec.rb
+++ b/spec/features/admin/admin_labels_spec.rb
@@ -1,8 +1,6 @@
require 'spec_helper'
RSpec.describe 'admin issues labels' do
- include WaitForAjax
-
let!(:bug_label) { Label.create(title: 'bug', template: true) }
let!(:feature_label) { Label.create(title: 'feature', template: true) }
diff --git a/spec/features/admin/admin_manage_applications_spec.rb b/spec/features/admin/admin_manage_applications_spec.rb
index c2c618b5659..0079125889b 100644
--- a/spec/features/admin/admin_manage_applications_spec.rb
+++ b/spec/features/admin/admin_manage_applications_spec.rb
@@ -8,7 +8,7 @@ RSpec.describe 'admin manage applications', feature: true do
it do
visit admin_applications_path
- click_on 'New Application'
+ click_on 'New application'
expect(page).to have_content('New application')
fill_in :doorkeeper_application_name, with: 'test'
diff --git a/spec/features/admin/admin_projects_spec.rb b/spec/features/admin/admin_projects_spec.rb
index 87a8f62687a..9d205104ebe 100644
--- a/spec/features/admin/admin_projects_spec.rb
+++ b/spec/features/admin/admin_projects_spec.rb
@@ -109,7 +109,7 @@ describe "Admin::Projects", feature: true do
expect(page).to have_content('Developer')
end
- find(:css, 'li', text: current_user.name).find(:css, 'a.btn-remove').click
+ find(:css, '.content-list li', text: current_user.name).find(:css, 'a.btn-remove').click
expect(page).not_to have_selector(:css, '.content-list')
end
diff --git a/spec/features/admin/admin_requests_profiles_spec.rb b/spec/features/admin/admin_requests_profiles_spec.rb
new file mode 100644
index 00000000000..e8ecb70306b
--- /dev/null
+++ b/spec/features/admin/admin_requests_profiles_spec.rb
@@ -0,0 +1,69 @@
+require 'spec_helper'
+
+describe 'Admin::RequestsProfilesController', feature: true do
+ before do
+ FileUtils.mkdir_p(Gitlab::RequestProfiler::PROFILES_DIR)
+ login_as(:admin)
+ end
+
+ after do
+ Gitlab::RequestProfiler.remove_all_profiles
+ end
+
+ describe 'GET /admin/requests_profiles' do
+ it 'shows the current profile token' do
+ allow(Rails).to receive(:cache).and_return(ActiveSupport::Cache::MemoryStore.new)
+
+ visit admin_requests_profiles_path
+
+ expect(page).to have_content("X-Profile-Token: #{Gitlab::RequestProfiler.profile_token}")
+ end
+
+ it 'lists all available profiles' do
+ time1 = 1.hour.ago
+ time2 = 2.hours.ago
+ time3 = 3.hours.ago
+ profile1 = "|gitlab-org|gitlab-ce_#{time1.to_i}.html"
+ profile2 = "|gitlab-org|gitlab-ce_#{time2.to_i}.html"
+ profile3 = "|gitlab-com|infrastructure_#{time3.to_i}.html"
+
+ FileUtils.touch("#{Gitlab::RequestProfiler::PROFILES_DIR}/#{profile1}")
+ FileUtils.touch("#{Gitlab::RequestProfiler::PROFILES_DIR}/#{profile2}")
+ FileUtils.touch("#{Gitlab::RequestProfiler::PROFILES_DIR}/#{profile3}")
+
+ visit admin_requests_profiles_path
+
+ within('.panel', text: '/gitlab-org/gitlab-ce') do
+ expect(page).to have_selector("a[href='#{admin_requests_profile_path(profile1)}']", text: time1.to_s(:long))
+ expect(page).to have_selector("a[href='#{admin_requests_profile_path(profile2)}']", text: time2.to_s(:long))
+ end
+
+ within('.panel', text: '/gitlab-com/infrastructure') do
+ expect(page).to have_selector("a[href='#{admin_requests_profile_path(profile3)}']", text: time3.to_s(:long))
+ end
+ end
+ end
+
+ describe 'GET /admin/requests_profiles/:profile' do
+ context 'when a profile exists' do
+ it 'displays the content of the profile' do
+ content = 'This is a request profile'
+ profile = "|gitlab-org|gitlab-ce_#{Time.now.to_i}.html"
+
+ File.write("#{Gitlab::RequestProfiler::PROFILES_DIR}/#{profile}", content)
+
+ visit admin_requests_profile_path(profile)
+
+ expect(page).to have_content(content)
+ end
+ end
+
+ context 'when a profile does not exist' do
+ it 'shows an error message' do
+ visit admin_requests_profile_path('|non|existent_12345.html')
+
+ expect(page).to have_content('Profile not found')
+ end
+ end
+ end
+end
diff --git a/spec/features/admin/admin_users_impersonation_tokens_spec.rb b/spec/features/admin/admin_users_impersonation_tokens_spec.rb
index 9ff5c2f9d40..0fb4baeb71c 100644
--- a/spec/features/admin/admin_users_impersonation_tokens_spec.rb
+++ b/spec/features/admin/admin_users_impersonation_tokens_spec.rb
@@ -16,7 +16,7 @@ describe 'Admin > Users > Impersonation Tokens', feature: true, js: true do
describe "token creation" do
it "allows creation of a token" do
- name = FFaker::Product.brand
+ name = 'Hello World'
visit admin_user_impersonation_tokens_path(user_id: user.username)
fill_in "Name", with: name
@@ -30,7 +30,7 @@ describe 'Admin > Users > Impersonation Tokens', feature: true, js: true do
check "api"
check "read_user"
- expect { click_on "Create Impersonation Token" }.to change { PersonalAccessTokensFinder.new(impersonation: true).execute.count }
+ expect { click_on "Create impersonation token" }.to change { PersonalAccessTokensFinder.new(impersonation: true).execute.count }
expect(active_impersonation_tokens).to have_text(name)
expect(active_impersonation_tokens).to have_text('In')
expect(active_impersonation_tokens).to have_text('api')
diff --git a/spec/features/admin/admin_users_spec.rb b/spec/features/admin/admin_users_spec.rb
index c0807b8c507..c5b1ef1295c 100644
--- a/spec/features/admin/admin_users_spec.rb
+++ b/spec/features/admin/admin_users_spec.rb
@@ -1,8 +1,6 @@
require 'spec_helper'
describe "Admin::Users", feature: true do
- include WaitForAjax
-
let!(:user) do
create(:omniauth_user, provider: 'twitter', extern_uid: '123456')
end
@@ -223,7 +221,7 @@ describe "Admin::Users", feature: true do
it "changes user entry" do
user.reload
expect(user.name).to eq('Big Bang')
- expect(user.is_admin?).to be_truthy
+ expect(user.admin?).to be_truthy
expect(user.password_expires_at).to be <= Time.now
end
end
diff --git a/spec/features/admin/admin_uses_repository_checks_spec.rb b/spec/features/admin/admin_uses_repository_checks_spec.rb
index 855247de2ea..ab5c42365fe 100644
--- a/spec/features/admin/admin_uses_repository_checks_spec.rb
+++ b/spec/features/admin/admin_uses_repository_checks_spec.rb
@@ -23,7 +23,7 @@ feature 'Admin uses repository checks', feature: true do
project = create(:empty_project)
project.update_columns(
last_repository_check_failed: true,
- last_repository_check_at: Time.now,
+ last_repository_check_at: Time.now
)
visit_admin_project_page(project)
diff --git a/spec/features/atom/dashboard_issues_spec.rb b/spec/features/atom/dashboard_issues_spec.rb
index 58b14e09740..9ea325ab41b 100644
--- a/spec/features/atom/dashboard_issues_spec.rb
+++ b/spec/features/atom/dashboard_issues_spec.rb
@@ -32,7 +32,7 @@ describe "Dashboard Issues Feed", feature: true do
end
context "issue with basic fields" do
- let!(:issue2) { create(:issue, author: user, assignee: assignee, project: project2, description: 'test desc') }
+ let!(:issue2) { create(:issue, author: user, assignees: [assignee], project: project2, description: 'test desc') }
it "renders issue fields" do
visit issues_dashboard_path(:atom, private_token: user.private_token)
@@ -41,7 +41,7 @@ describe "Dashboard Issues Feed", feature: true do
expect(entry).to be_present
expect(entry).to have_selector('author email', text: issue2.author_public_email)
- expect(entry).to have_selector('assignee email', text: issue2.assignee_public_email)
+ expect(entry).to have_selector('assignees email', text: assignee.public_email)
expect(entry).not_to have_selector('labels')
expect(entry).not_to have_selector('milestone')
expect(entry).to have_selector('description', text: issue2.description)
@@ -51,7 +51,7 @@ describe "Dashboard Issues Feed", feature: true do
context "issue with label and milestone" do
let!(:milestone1) { create(:milestone, project: project1, title: 'v1') }
let!(:label1) { create(:label, project: project1, title: 'label1') }
- let!(:issue1) { create(:issue, author: user, assignee: assignee, project: project1, milestone: milestone1) }
+ let!(:issue1) { create(:issue, author: user, assignees: [assignee], project: project1, milestone: milestone1) }
before do
issue1.labels << label1
@@ -64,7 +64,7 @@ describe "Dashboard Issues Feed", feature: true do
expect(entry).to be_present
expect(entry).to have_selector('author email', text: issue1.author_public_email)
- expect(entry).to have_selector('assignee email', text: issue1.assignee_public_email)
+ expect(entry).to have_selector('assignees email', text: assignee.public_email)
expect(entry).to have_selector('labels label', text: label1.title)
expect(entry).to have_selector('milestone', text: milestone1.title)
expect(entry).not_to have_selector('description')
diff --git a/spec/features/atom/issues_spec.rb b/spec/features/atom/issues_spec.rb
index b3903ec2faf..4f6754ad541 100644
--- a/spec/features/atom/issues_spec.rb
+++ b/spec/features/atom/issues_spec.rb
@@ -6,7 +6,7 @@ describe 'Issues Feed', feature: true do
let!(:assignee) { create(:user, email: 'private2@example.com', public_email: 'public2@example.com') }
let!(:group) { create(:group) }
let!(:project) { create(:project) }
- let!(:issue) { create(:issue, author: user, assignee: assignee, project: project) }
+ let!(:issue) { create(:issue, author: user, assignees: [assignee], project: project) }
before do
project.team << [user, :developer]
@@ -22,7 +22,8 @@ describe 'Issues Feed', feature: true do
to have_content('application/atom+xml')
expect(body).to have_selector('title', text: "#{project.name} issues")
expect(body).to have_selector('author email', text: issue.author_public_email)
- expect(body).to have_selector('assignee email', text: issue.author_public_email)
+ expect(body).to have_selector('assignees assignee email', text: issue.assignees.first.public_email)
+ expect(body).to have_selector('assignee email', text: issue.assignees.first.public_email)
expect(body).to have_selector('entry summary', text: issue.title)
end
end
@@ -36,7 +37,8 @@ describe 'Issues Feed', feature: true do
to have_content('application/atom+xml')
expect(body).to have_selector('title', text: "#{project.name} issues")
expect(body).to have_selector('author email', text: issue.author_public_email)
- expect(body).to have_selector('assignee email', text: issue.author_public_email)
+ expect(body).to have_selector('assignees assignee email', text: issue.assignees.first.public_email)
+ expect(body).to have_selector('assignee email', text: issue.assignees.first.public_email)
expect(body).to have_selector('entry summary', text: issue.title)
end
end
diff --git a/spec/features/atom/users_spec.rb b/spec/features/atom/users_spec.rb
index 55e10a1a89b..7a2987e815d 100644
--- a/spec/features/atom/users_spec.rb
+++ b/spec/features/atom/users_spec.rb
@@ -53,7 +53,7 @@ describe "User Feed", feature: true do
end
it 'has XHTML summaries in issue descriptions' do
- expect(body).to match /we have a bug!<\/p>\n\n<hr ?\/>\n\n<p dir="auto">I guess/
+ expect(body).to match /<hr ?\/>/
end
it 'has XHTML summaries in notes' do
diff --git a/spec/features/auto_deploy_spec.rb b/spec/features/auto_deploy_spec.rb
index ea7a97d1d4f..6c7423e4922 100644
--- a/spec/features/auto_deploy_spec.rb
+++ b/spec/features/auto_deploy_spec.rb
@@ -1,20 +1,11 @@
require 'spec_helper'
describe 'Auto deploy' do
- include WaitForAjax
-
let(:user) { create(:user) }
- let(:project) { create(:project) }
+ let(:project) { create(:project, :repository) }
before do
- project.create_kubernetes_service(
- active: true,
- properties: {
- namespace: project.path,
- api_url: 'https://kubernetes.example.com',
- token: 'a' * 40,
- }
- )
+ create :kubernetes_service, project: project
project.team << [user, :master]
login_as user
end
@@ -42,7 +33,7 @@ describe 'Auto deploy' do
it 'includes OpenShift as an available template', js: true do
click_link 'Set up auto deploy'
- click_button 'Choose a GitLab CI Yaml template'
+ click_button 'Apply a GitLab CI Yaml template'
within '.gitlab-ci-yml-selector' do
expect(page).to have_content('OpenShift')
@@ -51,12 +42,12 @@ describe 'Auto deploy' do
it 'creates a merge request using "auto-deploy" branch', js: true do
click_link 'Set up auto deploy'
- click_button 'Choose a GitLab CI Yaml template'
+ click_button 'Apply a GitLab CI Yaml template'
within '.gitlab-ci-yml-selector' do
click_on 'OpenShift'
end
wait_for_ajax
- click_button 'Commit Changes'
+ click_button 'Commit changes'
expect(page).to have_content('New Merge Request From auto-deploy into master')
end
diff --git a/spec/features/boards/add_issues_modal_spec.rb b/spec/features/boards/add_issues_modal_spec.rb
index 1c0f97d8a1c..505e0b5c355 100644
--- a/spec/features/boards/add_issues_modal_spec.rb
+++ b/spec/features/boards/add_issues_modal_spec.rb
@@ -1,7 +1,6 @@
require 'rails_helper'
describe 'Issue Boards add issue modal', :feature, :js do
- include WaitForAjax
include WaitForVueResource
let(:project) { create(:empty_project, :public) }
@@ -145,7 +144,7 @@ describe 'Issue Boards add issue modal', :feature, :js do
context 'selecing issues' do
it 'selects single issue' do
page.within('.add-issues-modal') do
- first('.card').click
+ first('.card .card-number').click
page.within('.nav-links') do
expect(page).to have_content('Selected issues 1')
@@ -155,7 +154,7 @@ describe 'Issue Boards add issue modal', :feature, :js do
it 'changes button text' do
page.within('.add-issues-modal') do
- first('.card').click
+ first('.card .card-number').click
expect(first('.add-issues-footer .btn')).to have_content('Add 1 issue')
end
@@ -163,7 +162,7 @@ describe 'Issue Boards add issue modal', :feature, :js do
it 'changes button text with plural' do
page.within('.add-issues-modal') do
- all('.card').each do |el|
+ all('.card .card-number').each do |el|
el.click
end
@@ -173,7 +172,7 @@ describe 'Issue Boards add issue modal', :feature, :js do
it 'shows only selected issues on selected tab' do
page.within('.add-issues-modal') do
- first('.card').click
+ first('.card .card-number').click
click_link 'Selected issues'
@@ -203,7 +202,7 @@ describe 'Issue Boards add issue modal', :feature, :js do
it 'selects all that arent already selected' do
page.within('.add-issues-modal') do
- first('.card').click
+ first('.card .card-number').click
expect(page).to have_selector('.is-active', count: 1)
@@ -215,11 +214,11 @@ describe 'Issue Boards add issue modal', :feature, :js do
it 'unselects from selected tab' do
page.within('.add-issues-modal') do
- first('.card').click
+ first('.card .card-number').click
click_link 'Selected issues'
- first('.card').click
+ first('.card .card-number').click
expect(page).not_to have_selector('.is-active')
end
@@ -229,7 +228,7 @@ describe 'Issue Boards add issue modal', :feature, :js do
context 'adding issues' do
it 'adds to board' do
page.within('.add-issues-modal') do
- first('.card').click
+ first('.card .card-number').click
click_button 'Add 1 issue'
end
@@ -241,7 +240,7 @@ describe 'Issue Boards add issue modal', :feature, :js do
it 'adds to second list' do
page.within('.add-issues-modal') do
- first('.card').click
+ first('.card .card-number').click
click_button planning.title
diff --git a/spec/features/boards/boards_spec.rb b/spec/features/boards/boards_spec.rb
index e168585534d..18585488e26 100644
--- a/spec/features/boards/boards_spec.rb
+++ b/spec/features/boards/boards_spec.rb
@@ -1,7 +1,6 @@
require 'rails_helper'
describe 'Issue Boards', feature: true, js: true do
- include WaitForAjax
include WaitForVueResource
include DragTo
@@ -72,7 +71,7 @@ describe 'Issue Boards', feature: true, js: true do
let!(:list2) { create(:list, board: board, label: development, position: 1) }
let!(:confidential_issue) { create(:labeled_issue, :confidential, project: project, author: user, labels: [planning], relative_position: 9) }
- let!(:issue1) { create(:labeled_issue, project: project, assignee: user, labels: [planning], relative_position: 8) }
+ let!(:issue1) { create(:labeled_issue, project: project, assignees: [user], labels: [planning], relative_position: 8) }
let!(:issue2) { create(:labeled_issue, project: project, author: user2, labels: [planning], relative_position: 7) }
let!(:issue3) { create(:labeled_issue, project: project, labels: [planning], relative_position: 6) }
let!(:issue4) { create(:labeled_issue, project: project, labels: [planning], relative_position: 5) }
@@ -590,7 +589,7 @@ describe 'Issue Boards', feature: true, js: true do
end
def click_filter_link(link_text)
- page.within('.filtered-search-input-container') do
+ page.within('.filtered-search-box') do
expect(page).to have_button(link_text)
click_button(link_text)
diff --git a/spec/features/boards/issue_ordering_spec.rb b/spec/features/boards/issue_ordering_spec.rb
index c50155a6d14..bfa2a72a256 100644
--- a/spec/features/boards/issue_ordering_spec.rb
+++ b/spec/features/boards/issue_ordering_spec.rb
@@ -38,6 +38,8 @@ describe 'Issue Boards', :feature, :js do
it 'moves un-ordered issue to top of list' do
drag(from_index: 3, to_index: 0)
+ wait_for_vue_resource
+
page.within(first('.board')) do
expect(first('.card')).to have_content(issue4.title)
end
diff --git a/spec/features/boards/keyboard_shortcut_spec.rb b/spec/features/boards/keyboard_shortcut_spec.rb
index a5fc766401f..a9cc6c49f8e 100644
--- a/spec/features/boards/keyboard_shortcut_spec.rb
+++ b/spec/features/boards/keyboard_shortcut_spec.rb
@@ -14,7 +14,7 @@ describe 'Issue Boards shortcut', feature: true, js: true do
end
it 'takes user to issue board index' do
- find('body').native.send_keys('gl')
+ find('body').native.send_keys('gb')
expect(page).to have_selector('.boards-list')
wait_for_vue_resource
diff --git a/spec/features/boards/modal_filter_spec.rb b/spec/features/boards/modal_filter_spec.rb
index e2281a7da55..e1367c675e5 100644
--- a/spec/features/boards/modal_filter_spec.rb
+++ b/spec/features/boards/modal_filter_spec.rb
@@ -98,7 +98,7 @@ describe 'Issue Boards add issue modal filtering', :feature, :js do
end
context 'assignee' do
- let!(:issue) { create(:issue, project: project, assignee: user2) }
+ let!(:issue) { create(:issue, project: project, assignees: [user2]) }
before do
project.team << [user2, :developer]
@@ -219,7 +219,7 @@ describe 'Issue Boards add issue modal filtering', :feature, :js do
end
def click_filter_link(link_text)
- page.within('.add-issues-modal .filtered-search-input-container') do
+ page.within('.add-issues-modal .filtered-search-box') do
expect(page).to have_button(link_text)
click_button(link_text)
diff --git a/spec/features/boards/new_issue_spec.rb b/spec/features/boards/new_issue_spec.rb
index e6d7cf106d4..f04a1a89e96 100644
--- a/spec/features/boards/new_issue_spec.rb
+++ b/spec/features/boards/new_issue_spec.rb
@@ -1,7 +1,6 @@
require 'rails_helper'
describe 'Issue Boards new issue', feature: true, js: true do
- include WaitForAjax
include WaitForVueResource
let(:project) { create(:empty_project, :public) }
diff --git a/spec/features/boards/sidebar_spec.rb b/spec/features/boards/sidebar_spec.rb
index 3332e07ec31..4667be49fe6 100644
--- a/spec/features/boards/sidebar_spec.rb
+++ b/spec/features/boards/sidebar_spec.rb
@@ -1,17 +1,17 @@
require 'rails_helper'
describe 'Issue Boards', feature: true, js: true do
- include WaitForAjax
include WaitForVueResource
let(:user) { create(:user) }
+ let(:user2) { create(:user) }
let(:project) { create(:empty_project, :public) }
let!(:milestone) { create(:milestone, project: project) }
let!(:development) { create(:label, project: project, name: 'Development') }
let!(:bug) { create(:label, project: project, name: 'Bug') }
let!(:regression) { create(:label, project: project, name: 'Regression') }
let!(:stretch) { create(:label, project: project, name: 'Stretch') }
- let!(:issue1) { create(:labeled_issue, project: project, assignee: user, milestone: milestone, labels: [development], relative_position: 2) }
+ let!(:issue1) { create(:labeled_issue, project: project, assignees: [user], milestone: milestone, labels: [development], relative_position: 2) }
let!(:issue2) { create(:labeled_issue, project: project, labels: [development, stretch], relative_position: 1) }
let(:board) { create(:board, project: project) }
let!(:list) { create(:list, board: board, label: development, position: 0) }
@@ -113,10 +113,10 @@ describe 'Issue Boards', feature: true, js: true do
page.within('.dropdown-menu-user') do
click_link 'Unassigned'
-
- wait_for_vue_resource
end
+ wait_for_vue_resource
+
expect(page).to have_content('No assignee')
end
@@ -129,7 +129,7 @@ describe 'Issue Boards', feature: true, js: true do
page.within(find('.assignee')) do
expect(page).to have_content('No assignee')
- click_link 'assign yourself'
+ click_button 'assign yourself'
wait_for_vue_resource
@@ -139,7 +139,7 @@ describe 'Issue Boards', feature: true, js: true do
expect(card).to have_selector('.avatar')
end
- it 'resets assignee dropdown' do
+ it 'updates assignee dropdown' do
click_card(card)
page.within('.assignee') do
@@ -157,13 +157,13 @@ describe 'Issue Boards', feature: true, js: true do
end
page.within(first('.board')) do
- find('.card:nth-child(2)').click
+ find('.card:nth-child(2)').trigger('click')
end
page.within('.assignee') do
click_link 'Edit'
-
- expect(page).not_to have_selector('.is-active')
+
+ expect(find('.dropdown-menu')).to have_selector('.is-active')
end
end
end
diff --git a/spec/features/boards/sub_group_project_spec.rb b/spec/features/boards/sub_group_project_spec.rb
new file mode 100644
index 00000000000..6cd7fddd288
--- /dev/null
+++ b/spec/features/boards/sub_group_project_spec.rb
@@ -0,0 +1,45 @@
+require 'rails_helper'
+
+describe 'Sub-group project issue boards', :feature, :js do
+ include WaitForVueResource
+
+ let(:group) { create(:group) }
+ let(:nested_group_1) { create(:group, parent: group) }
+ let(:project) { create(:empty_project, group: nested_group_1) }
+ let(:board) { create(:board, project: project) }
+ let(:label) { create(:label, project: project) }
+ let(:user) { create(:user) }
+ let!(:list1) { create(:list, board: board, label: label, position: 0) }
+ let!(:issue) { create(:labeled_issue, project: project, labels: [label]) }
+
+ before do
+ project.add_master(user)
+
+ login_as(user)
+
+ visit namespace_project_board_path(project.namespace, project, board)
+ wait_for_vue_resource
+ end
+
+ it 'creates new label from sidebar' do
+ find('.card').click
+
+ page.within '.labels' do
+ click_link 'Edit'
+ click_link 'Create new label'
+ end
+
+ page.within '.dropdown-new-label' do
+ fill_in 'new_label_name', with: 'test label'
+ first('.suggest-colors-dropdown a').click
+
+ click_button 'Create'
+
+ wait_for_ajax
+ end
+
+ page.within '.labels' do
+ expect(page).to have_link 'test label'
+ end
+ end
+end
diff --git a/spec/features/calendar_spec.rb b/spec/features/calendar_spec.rb
index 35d090c4b7f..496faf87a16 100644
--- a/spec/features/calendar_spec.rb
+++ b/spec/features/calendar_spec.rb
@@ -1,10 +1,8 @@
require 'spec_helper'
feature 'Contributions Calendar', :feature, :js do
- include WaitForAjax
-
let(:user) { create(:user) }
- let(:contributed_project) { create(:project, :public) }
+ let(:contributed_project) { create(:empty_project, :public) }
let(:issue_note) { create(:note, project: contributed_project) }
# Ex/ Sunday Jan 1, 2016
diff --git a/spec/features/commits_spec.rb b/spec/features/commits_spec.rb
index 881f1fca4d1..e6c4ab24de5 100644
--- a/spec/features/commits_spec.rb
+++ b/spec/features/commits_spec.rb
@@ -3,7 +3,7 @@ require 'spec_helper'
describe 'Commits' do
include CiStatusHelper
- let(:project) { create(:project) }
+ let(:project) { create(:project, :repository) }
describe 'CI' do
before do
diff --git a/spec/features/container_registry_spec.rb b/spec/features/container_registry_spec.rb
index 203e55a36f2..b86609e07c5 100644
--- a/spec/features/container_registry_spec.rb
+++ b/spec/features/container_registry_spec.rb
@@ -1,45 +1,61 @@
require 'spec_helper'
describe "Container Registry" do
+ let(:user) { create(:user) }
let(:project) { create(:empty_project) }
- let(:repository) { project.container_registry_repository }
- let(:tag_name) { 'latest' }
- let(:tags) { [tag_name] }
+
+ let(:container_repository) do
+ create(:container_repository, name: 'my/image')
+ end
before do
- login_as(:user)
- project.team << [@user, :developer]
- stub_container_registry_tags(*tags)
+ login_as(user)
+ project.add_developer(user)
stub_container_registry_config(enabled: true)
- allow(Auth::ContainerRegistryAuthenticationService).to receive(:full_access_token).and_return('token')
+ stub_container_registry_tags(repository: :any, tags: [])
end
- describe 'GET /:project/container_registry' do
+ context 'when there are no image repositories' do
+ scenario 'user visits container registry main page' do
+ visit_container_registry
+
+ expect(page).to have_content 'No container image repositories'
+ end
+ end
+
+ context 'when there are image repositories' do
before do
- visit namespace_project_container_registry_index_path(project.namespace, project)
+ stub_container_registry_tags(repository: %r{my/image}, tags: %w[latest])
+ project.container_repositories << container_repository
end
- context 'when no tags' do
- let(:tags) { [] }
+ scenario 'user wants to see multi-level container repository' do
+ visit_container_registry
- it { expect(page).to have_content('No images in Container Registry for this project') }
+ expect(page).to have_content('my/image')
end
- context 'when there are tags' do
- it { expect(page).to have_content(tag_name) }
- it { expect(page).to have_content('d7a513a66') }
- end
- end
+ scenario 'user removes entire container repository' do
+ visit_container_registry
- describe 'DELETE /:project/container_registry/tag' do
- before do
- visit namespace_project_container_registry_index_path(project.namespace, project)
+ expect_any_instance_of(ContainerRepository)
+ .to receive(:delete_tags!).and_return(true)
+
+ click_on 'Remove repository'
end
- it do
- expect_any_instance_of(::ContainerRegistry::Tag).to receive(:delete).and_return(true)
+ scenario 'user removes a specific tag from container repository' do
+ visit_container_registry
- click_on 'Remove'
+ expect_any_instance_of(ContainerRegistry::Tag)
+ .to receive(:delete).and_return(true)
+
+ click_on 'Remove tag'
end
end
+
+ def visit_container_registry
+ visit namespace_project_container_registry_index_path(
+ project.namespace, project)
+ end
end
diff --git a/spec/features/copy_as_gfm_spec.rb b/spec/features/copy_as_gfm_spec.rb
index 55df7e45f79..be615519a09 100644
--- a/spec/features/copy_as_gfm_spec.rb
+++ b/spec/features/copy_as_gfm_spec.rb
@@ -1,7 +1,7 @@
require 'spec_helper'
describe 'Copy as GFM', feature: true, js: true do
- include GitlabMarkdownHelper
+ include MarkupHelper
include RepoHelpers
include ActionView::Helpers::JavaScriptHelper
@@ -96,7 +96,7 @@ describe 'Copy as GFM', feature: true, js: true do
# issue link
"[Issue](#{namespace_project_issue_url(@project.namespace, @project, @feat.issue)})",
# issue link with note anchor
- "[Issue](#{namespace_project_issue_url(@project.namespace, @project, @feat.issue, anchor: 'note_123')})",
+ "[Issue](#{namespace_project_issue_url(@project.namespace, @project, @feat.issue, anchor: 'note_123')})"
)
verify(
@@ -433,7 +433,7 @@ describe 'Copy as GFM', feature: true, js: true do
end
describe 'Copying code' do
- let(:project) { create(:project) }
+ let(:project) { create(:project, :repository) }
context 'from a diff' do
before do
@@ -479,6 +479,7 @@ describe 'Copy as GFM', feature: true, js: true do
context 'from a blob' do
before do
visit namespace_project_blob_path(project.namespace, project, File.join('master', 'files/ruby/popen.rb'))
+ wait_for_ajax
end
context 'selecting one word of text' do
@@ -520,6 +521,7 @@ describe 'Copy as GFM', feature: true, js: true do
context 'from a GFM code block' do
before do
visit namespace_project_blob_path(project.namespace, project, File.join('markdown', 'doc/api/users.md'))
+ wait_for_ajax
end
context 'selecting one word of text' do
diff --git a/spec/features/cycle_analytics_spec.rb b/spec/features/cycle_analytics_spec.rb
index 0648c89a5c7..cbeb73d9cae 100644
--- a/spec/features/cycle_analytics_spec.rb
+++ b/spec/features/cycle_analytics_spec.rb
@@ -1,21 +1,21 @@
require 'spec_helper'
feature 'Cycle Analytics', feature: true, js: true do
- include WaitForAjax
-
let(:user) { create(:user) }
let(:guest) { create(:user) }
- let(:project) { create(:project) }
+ let(:project) { create(:project, :repository) }
let(:issue) { create(:issue, project: project, created_at: 2.days.ago) }
let(:milestone) { create(:milestone, project: project) }
- let(:mr) { create_merge_request_closing_issue(issue) }
+ let(:mr) { create_merge_request_closing_issue(issue, commit_message: "References #{issue.to_reference}") }
let(:pipeline) { create(:ci_empty_pipeline, status: 'created', project: project, ref: mr.source_branch, sha: mr.source_branch_sha) }
context 'as an allowed user' do
context 'when project is new' do
before do
- project.team << [user, :master]
+ project.add_master(user)
+
login_as(user)
+
visit namespace_project_cycle_analytics_path(project.namespace, project)
wait_for_ajax
end
@@ -32,9 +32,10 @@ feature 'Cycle Analytics', feature: true, js: true do
context "when there's cycle analytics data" do
before do
- project.team << [user, :master]
-
allow_any_instance_of(Gitlab::ReferenceExtractor).to receive(:issues).and_return([issue])
+ mr.update(head_pipeline: pipeline)
+ project.add_master(user)
+
create_cycle
deploy_master
@@ -64,11 +65,30 @@ feature 'Cycle Analytics', feature: true, js: true do
expect_issue_to_be_present
end
end
+
+ context "when my preferred language is Spanish" do
+ before do
+ user.update_attribute(:preferred_language, 'es')
+
+ project.team << [user, :master]
+ login_as(user)
+ visit namespace_project_cycle_analytics_path(project.namespace, project)
+ wait_for_ajax
+ end
+
+ it 'shows the content in Spanish' do
+ expect(page).to have_content('Estado del Pipeline')
+ end
+
+ it 'resets the language to English' do
+ expect(I18n.locale).to eq(:en)
+ end
+ end
end
context "as a guest" do
before do
- project.team << [guest, :guest]
+ project.add_guest(guest)
allow_any_instance_of(Gitlab::ReferenceExtractor).to receive(:issues).and_return([issue])
create_cycle
diff --git a/spec/features/dashboard/datetime_on_tooltips_spec.rb b/spec/features/dashboard/datetime_on_tooltips_spec.rb
index dc9d09fa396..0e9e3f78be2 100644
--- a/spec/features/dashboard/datetime_on_tooltips_spec.rb
+++ b/spec/features/dashboard/datetime_on_tooltips_spec.rb
@@ -1,8 +1,6 @@
require 'spec_helper'
feature 'Tooltips on .timeago dates', feature: true, js: true do
- include WaitForAjax
-
let(:user) { create(:user) }
let(:project) { create(:project, name: 'test', namespace: user.namespace) }
let(:created_date) { Date.yesterday.to_time }
diff --git a/spec/features/dashboard/group_spec.rb b/spec/features/dashboard/group_spec.rb
index d5f8470fab0..8e20fdec8ad 100644
--- a/spec/features/dashboard/group_spec.rb
+++ b/spec/features/dashboard/group_spec.rb
@@ -5,16 +5,18 @@ RSpec.describe 'Dashboard Group', feature: true do
login_as(:user)
end
- it 'creates new grpup' do
+ it 'creates new group', js: true do
visit dashboard_groups_path
- click_link 'New Group'
+ find('.btn-new').trigger('click')
+ new_path = 'Samurai'
+ new_description = 'Tokugawa Shogunate'
- fill_in 'group_path', with: 'Samurai'
- fill_in 'group_description', with: 'Tokugawa Shogunate'
+ fill_in 'group_path', with: new_path
+ fill_in 'group_description', with: new_description
click_button 'Create group'
- expect(current_path).to eq group_path(Group.find_by(name: 'Samurai'))
- expect(page).to have_content('Samurai')
- expect(page).to have_content('Tokugawa Shogunate')
+ expect(current_path).to eq group_path(Group.find_by(name: new_path))
+ expect(page).to have_content(new_path)
+ expect(page).to have_content(new_description)
end
end
diff --git a/spec/features/dashboard/groups_list_spec.rb b/spec/features/dashboard/groups_list_spec.rb
index ca04107d33a..52b4d82e856 100644
--- a/spec/features/dashboard/groups_list_spec.rb
+++ b/spec/features/dashboard/groups_list_spec.rb
@@ -1,8 +1,6 @@
require 'spec_helper'
describe 'Dashboard Groups page', js: true, feature: true do
- include WaitForAjax
-
let!(:user) { create :user }
let!(:group) { create(:group) }
let!(:nested_group) { create(:group, :nested) }
diff --git a/spec/features/dashboard/issuables_counter_spec.rb b/spec/features/dashboard/issuables_counter_spec.rb
index a1718912fc6..354267dbee7 100644
--- a/spec/features/dashboard/issuables_counter_spec.rb
+++ b/spec/features/dashboard/issuables_counter_spec.rb
@@ -1,45 +1,64 @@
require 'spec_helper'
-describe 'Navigation bar counter', feature: true, js: true, caching: true do
+describe 'Navigation bar counter', feature: true, caching: true do
let(:user) { create(:user) }
let(:project) { create(:empty_project, namespace: user.namespace) }
let(:issue) { create(:issue, project: project) }
let(:merge_request) { create(:merge_request, source_project: project) }
before do
- issue.update(assignee: user)
+ issue.assignees = [user]
merge_request.update(assignee: user)
login_as(user)
end
it 'reflects dashboard issues count' do
- visit issues_dashboard_path
+ visit issues_path
expect_counters('issues', '1')
- issue.update(assignee: nil)
- visit issues_dashboard_path
+ issue.assignees = []
- expect_counters('issues', '1')
+ user.invalidate_cache_counts
+
+ Timecop.travel(3.minutes.from_now) do
+ visit issues_path
+
+ expect_counters('issues', '0')
+ end
end
it 'reflects dashboard merge requests count' do
- visit merge_requests_dashboard_path
+ visit merge_requests_path
expect_counters('merge_requests', '1')
merge_request.update(assignee: nil)
- visit merge_requests_dashboard_path
- expect_counters('merge_requests', '1')
+ user.invalidate_cache_counts
+
+ Timecop.travel(3.minutes.from_now) do
+ visit merge_requests_path
+
+ expect_counters('merge_requests', '0')
+ end
+ end
+
+ def issues_path
+ issues_dashboard_path(assignee_id: user.id)
+ end
+
+ def merge_requests_path
+ merge_requests_dashboard_path(assignee_id: user.id)
end
def expect_counters(issuable_type, count)
- dashboard_count = find('li.active')
- find('.global-dropdown-toggle').click
+ dashboard_count = find('.nav-links li.active')
nav_count = find(".dashboard-shortcuts-#{issuable_type}")
+ header_count = find(".header-content .#{issuable_type.tr('_', '-')}-count")
- expect(nav_count).to have_content(count)
expect(dashboard_count).to have_content(count)
+ expect(nav_count).to have_content(count)
+ expect(header_count).to have_content(count)
end
end
diff --git a/spec/features/dashboard/issues_spec.rb b/spec/features/dashboard/issues_spec.rb
index f4420814c3a..7a132dba1e9 100644
--- a/spec/features/dashboard/issues_spec.rb
+++ b/spec/features/dashboard/issues_spec.rb
@@ -11,7 +11,7 @@ RSpec.describe 'Dashboard Issues', feature: true do
let!(:authored_issue) { create :issue, author: current_user, project: project }
let!(:authored_issue_on_public_project) { create :issue, author: current_user, project: public_project }
- let!(:assigned_issue) { create :issue, assignee: current_user, project: project }
+ let!(:assigned_issue) { create :issue, assignees: [current_user], project: project }
let!(:other_issue) { create :issue, project: project }
before do
@@ -26,10 +26,26 @@ RSpec.describe 'Dashboard Issues', feature: true do
expect(page).not_to have_content(other_issue.title)
end
+ it 'shows checkmark when unassigned is selected for assignee', js: true do
+ find('.js-assignee-search').click
+ find('li', text: 'Unassigned').click
+ find('.js-assignee-search').click
+
+ expect(find('li[data-user-id="0"] a.is-active')).to be_visible
+ end
+
it 'shows issues when current user is author', js: true do
find('#assignee_id', visible: false).set('')
find('.js-author-search', match: :first).click
+
+ expect(find('li[data-user-id="null"] a.is-active')).to be_visible
+
find('.dropdown-menu-author li a', match: :first, text: current_user.to_reference).click
+ find('.js-author-search', match: :first).click
+
+ page.within '.dropdown-menu-user' do
+ expect(find('.dropdown-menu-author li a.is-active', match: :first, text: current_user.to_reference)).to be_visible
+ end
expect(page).to have_content(authored_issue.title)
expect(page).to have_content(authored_issue_on_public_project.title)
diff --git a/spec/features/dashboard/merge_requests_spec.rb b/spec/features/dashboard/merge_requests_spec.rb
new file mode 100644
index 00000000000..508ca38d7e5
--- /dev/null
+++ b/spec/features/dashboard/merge_requests_spec.rb
@@ -0,0 +1,32 @@
+require 'spec_helper'
+
+describe 'Dashboard Merge Requests' do
+ let(:current_user) { create :user }
+ let(:project) do
+ create(:empty_project) do |project|
+ project.add_master(current_user)
+ end
+ end
+
+ before do
+ login_as(current_user)
+ end
+
+ it 'should show an empty state' do
+ visit merge_requests_dashboard_path(assignee_id: current_user.id)
+
+ expect(page).to have_selector('.empty-state')
+ end
+
+ context 'if there are merge requests' do
+ before do
+ create(:merge_request, assignee: current_user, source_project: project)
+
+ visit merge_requests_dashboard_path(assignee_id: current_user.id)
+ end
+
+ it 'should not show an empty state' do
+ expect(page).not_to have_selector('.empty-state')
+ end
+ end
+end
diff --git a/spec/features/dashboard/milestone_filter_spec.rb b/spec/features/dashboard/milestone_filter_spec.rb
new file mode 100644
index 00000000000..d60a002a8d7
--- /dev/null
+++ b/spec/features/dashboard/milestone_filter_spec.rb
@@ -0,0 +1,60 @@
+require 'spec_helper'
+
+describe 'Dashboard > milestone filter', :feature, :js do
+ include WaitForAjax
+
+ let(:user) { create(:user) }
+ let(:project) { create(:project, name: 'test', namespace: user.namespace) }
+ let(:milestone) { create(:milestone, title: "v1.0", project: project) }
+ let(:milestone2) { create(:milestone, title: "v2.0", project: project) }
+ let!(:issue) { create :issue, author: user, project: project, milestone: milestone }
+ let!(:issue2) { create :issue, author: user, project: project, milestone: milestone2 }
+
+ before do
+ login_as(user)
+ visit issues_dashboard_path(author_id: user.id)
+ end
+
+ context 'default state' do
+ it 'shows issues with Any Milestone' do
+ page.all('.issue-info').each do |issue_info|
+ expect(issue_info.text).to match(/v\d.0/)
+ end
+ end
+ end
+
+ context 'filtering by milestone' do
+ milestone_select = '.js-milestone-select'
+
+ before do
+ find(milestone_select).click
+ wait_for_ajax
+
+ page.within('.dropdown-content') do
+ click_link 'v1.0'
+ end
+
+ find(milestone_select).click
+ wait_for_ajax
+ end
+
+ it 'shows issues with Milestone v1.0' do
+ expect(find('.issues-list')).to have_selector('.issue', count: 1)
+ expect(find('.dropdown-content')).to have_selector('a.is-active', count: 1)
+ end
+
+ it 'should not change active Milestone unless clicked' do
+ expect(find('.dropdown-content')).to have_selector('a.is-active', count: 1)
+
+ # open & close dropdown
+ find('.dropdown-menu-close').click
+
+ expect(find('.milestone-filter')).not_to have_selector('.dropdown.open')
+
+ find(milestone_select).click
+
+ expect(find('.dropdown-content')).to have_selector('a.is-active', count: 1)
+ expect(find('.dropdown-content a.is-active')).to have_content('v1.0')
+ end
+ end
+end
diff --git a/spec/features/dashboard/project_member_activity_index_spec.rb b/spec/features/dashboard/project_member_activity_index_spec.rb
index 49d93db58a9..16c214ae060 100644
--- a/spec/features/dashboard/project_member_activity_index_spec.rb
+++ b/spec/features/dashboard/project_member_activity_index_spec.rb
@@ -1,8 +1,6 @@
require 'spec_helper'
feature 'Project member activity', feature: true, js: true do
- include WaitForAjax
-
let(:user) { create(:user) }
let(:project) { create(:empty_project, :public, name: 'x', namespace: user.namespace) }
diff --git a/spec/features/dashboard/projects_spec.rb b/spec/features/dashboard/projects_spec.rb
index c4e58d14f75..f1789fc9d43 100644
--- a/spec/features/dashboard/projects_spec.rb
+++ b/spec/features/dashboard/projects_spec.rb
@@ -7,7 +7,6 @@ RSpec.describe 'Dashboard Projects', feature: true do
before do
project.team << [user, :developer]
login_as user
- visit dashboard_projects_path
end
it 'shows the project the user in a member of in the list' do
@@ -15,15 +14,19 @@ RSpec.describe 'Dashboard Projects', feature: true do
expect(page).to have_content('awesome stuff')
end
- describe "with a pipeline" do
- let(:pipeline) { create(:ci_pipeline, :success, project: project, sha: project.commit.sha) }
+ describe "with a pipeline", redis: true do
+ let!(:pipeline) { create(:ci_pipeline, project: project, sha: project.commit.sha) }
before do
- pipeline
+ # Since the cache isn't updated when a new pipeline is created
+ # we need the pipeline to advance in the pipeline since the cache was created
+ # by visiting the login page.
+ pipeline.succeed
end
it 'shows that the last pipeline passed' do
visit dashboard_projects_path
+
expect(page).to have_xpath("//a[@href='#{pipelines_namespace_project_commit_path(project.namespace, project, project.commit)}']")
end
end
diff --git a/spec/features/dashboard/shortcuts_spec.rb b/spec/features/dashboard/shortcuts_spec.rb
index 3642c0bfb5b..349b948eaee 100644
--- a/spec/features/dashboard/shortcuts_spec.rb
+++ b/spec/features/dashboard/shortcuts_spec.rb
@@ -1,31 +1,52 @@
require 'spec_helper'
-feature 'Dashboard shortcuts', feature: true, js: true do
- before do
- login_as :user
- visit dashboard_projects_path
- end
+feature 'Dashboard shortcuts', :feature, :js do
+ context 'logged in' do
+ before do
+ login_as :user
+ visit root_dashboard_path
+ end
+
+ scenario 'Navigate to tabs' do
+ find('body').send_keys([:shift, 'I'])
+
+ check_page_title('Issues')
+
+ find('body').send_keys([:shift, 'M'])
+
+ check_page_title('Merge Requests')
- scenario 'Navigate to tabs' do
- find('body').native.send_key('g')
- find('body').native.send_key('p')
+ find('body').send_keys([:shift, 'T'])
+
+ check_page_title('Todos')
+
+ find('body').send_keys([:shift, 'P'])
+
+ check_page_title('Projects')
+ end
+ end
- check_page_title('Projects')
+ context 'logged out' do
+ before do
+ visit explore_root_path
+ end
- find('body').native.send_key('g')
- find('body').native.send_key('i')
+ scenario 'Navigate to tabs' do
+ find('body').send_keys([:shift, 'G'])
- check_page_title('Issues')
+ find('.nothing-here-block')
+ expect(page).to have_content('No public groups')
- find('body').native.send_key('g')
- find('body').native.send_key('m')
+ find('body').send_keys([:shift, 'S'])
- check_page_title('Merge Requests')
+ find('.nothing-here-block')
+ expect(page).to have_selector('.snippets-list-holder')
- find('body').native.send_key('g')
- find('body').native.send_key('t')
+ find('body').send_keys([:shift, 'P'])
- check_page_title('Todos')
+ find('.nothing-here-block')
+ expect(page).to have_content('No projects found')
+ end
end
def check_page_title(title)
diff --git a/spec/features/dashboard/snippets_spec.rb b/spec/features/dashboard/snippets_spec.rb
index 62937688c22..c6ba118220a 100644
--- a/spec/features/dashboard/snippets_spec.rb
+++ b/spec/features/dashboard/snippets_spec.rb
@@ -12,4 +12,51 @@ describe 'Dashboard snippets', feature: true do
it_behaves_like 'paginated snippets'
end
+
+ context 'filtering by visibility' do
+ let(:user) { create(:user) }
+ let!(:snippets) do
+ [
+ create(:personal_snippet, :public, author: user),
+ create(:personal_snippet, :internal, author: user),
+ create(:personal_snippet, :private, author: user),
+ create(:personal_snippet, :public)
+ ]
+ end
+
+ before do
+ login_as(user)
+
+ visit dashboard_snippets_path
+ end
+
+ it 'contains all snippets of logged user' do
+ expect(page).to have_selector('.snippet-row', count: 3)
+
+ expect(page).to have_content(snippets[0].title)
+ expect(page).to have_content(snippets[1].title)
+ expect(page).to have_content(snippets[2].title)
+ end
+
+ it 'contains all private snippets of logged user when clicking on private' do
+ click_link('Private')
+
+ expect(page).to have_selector('.snippet-row', count: 1)
+ expect(page).to have_content(snippets[2].title)
+ end
+
+ it 'contains all internal snippets of logged user when clicking on internal' do
+ click_link('Internal')
+
+ expect(page).to have_selector('.snippet-row', count: 1)
+ expect(page).to have_content(snippets[1].title)
+ end
+
+ it 'contains all public snippets of logged user when clicking on public' do
+ click_link('Public')
+
+ expect(page).to have_selector('.snippet-row', count: 1)
+ expect(page).to have_content(snippets[0].title)
+ end
+ end
end
diff --git a/spec/features/dashboard_issues_spec.rb b/spec/features/dashboard_issues_spec.rb
index 8c61cdebc4b..ad60fb2c74f 100644
--- a/spec/features/dashboard_issues_spec.rb
+++ b/spec/features/dashboard_issues_spec.rb
@@ -2,7 +2,7 @@ require 'spec_helper'
describe "Dashboard Issues filtering", feature: true, js: true do
let(:user) { create(:user) }
- let(:project) { create(:project) }
+ let(:project) { create(:empty_project) }
let(:milestone) { create(:milestone, project: project) }
context 'filtering by milestone' do
@@ -10,8 +10,8 @@ describe "Dashboard Issues filtering", feature: true, js: true do
project.team << [user, :master]
login_as(user)
- create(:issue, project: project, author: user, assignee: user)
- create(:issue, project: project, author: user, assignee: user, milestone: milestone)
+ create(:issue, project: project, author: user, assignees: [user])
+ create(:issue, project: project, author: user, assignees: [user], milestone: milestone)
visit_issues
end
diff --git a/spec/features/discussion_comments/commit_spec.rb b/spec/features/discussion_comments/commit_spec.rb
new file mode 100644
index 00000000000..96e0b78f6b9
--- /dev/null
+++ b/spec/features/discussion_comments/commit_spec.rb
@@ -0,0 +1,18 @@
+require 'spec_helper'
+
+describe 'Discussion Comments Merge Request', :feature, :js do
+ include RepoHelpers
+
+ let(:user) { create(:user) }
+ let(:project) { create(:project) }
+ let(:merge_request) { create(:merge_request, source_project: project) }
+
+ before do
+ project.add_master(user)
+ login_as(user)
+
+ visit namespace_project_commit_path(project.namespace, project, sample_commit.id)
+ end
+
+ it_behaves_like 'discussion comments', 'commit'
+end
diff --git a/spec/features/discussion_comments/issue_spec.rb b/spec/features/discussion_comments/issue_spec.rb
new file mode 100644
index 00000000000..ccc9efccd18
--- /dev/null
+++ b/spec/features/discussion_comments/issue_spec.rb
@@ -0,0 +1,16 @@
+require 'spec_helper'
+
+describe 'Discussion Comments Issue', :feature, :js do
+ let(:user) { create(:user) }
+ let(:project) { create(:empty_project) }
+ let(:issue) { create(:issue, project: project) }
+
+ before do
+ project.add_master(user)
+ login_as(user)
+
+ visit namespace_project_issue_path(project.namespace, project, issue)
+ end
+
+ it_behaves_like 'discussion comments', 'issue'
+end
diff --git a/spec/features/discussion_comments/merge_request_spec.rb b/spec/features/discussion_comments/merge_request_spec.rb
new file mode 100644
index 00000000000..f99ebeb9cd9
--- /dev/null
+++ b/spec/features/discussion_comments/merge_request_spec.rb
@@ -0,0 +1,16 @@
+require 'spec_helper'
+
+describe 'Discussion Comments Merge Request', :feature, :js do
+ let(:user) { create(:user) }
+ let(:project) { create(:project) }
+ let(:merge_request) { create(:merge_request, source_project: project) }
+
+ before do
+ project.add_master(user)
+ login_as(user)
+
+ visit namespace_project_merge_request_path(project.namespace, project, merge_request)
+ end
+
+ it_behaves_like 'discussion comments', 'merge request'
+end
diff --git a/spec/features/discussion_comments/snippets_spec.rb b/spec/features/discussion_comments/snippets_spec.rb
new file mode 100644
index 00000000000..19a306511b2
--- /dev/null
+++ b/spec/features/discussion_comments/snippets_spec.rb
@@ -0,0 +1,16 @@
+require 'spec_helper'
+
+describe 'Discussion Comments Issue', :feature, :js do
+ let(:user) { create(:user) }
+ let(:project) { create(:empty_project) }
+ let(:snippet) { create(:project_snippet, :private, project: project, author: user) }
+
+ before do
+ project.add_master(user)
+ login_as(user)
+
+ visit namespace_project_snippet_path(project.namespace, project, snippet)
+ end
+
+ it_behaves_like 'discussion comments', 'snippet'
+end
diff --git a/spec/features/expand_collapse_diffs_spec.rb b/spec/features/expand_collapse_diffs_spec.rb
index 8c64b050e19..76c77e0bc5f 100644
--- a/spec/features/expand_collapse_diffs_spec.rb
+++ b/spec/features/expand_collapse_diffs_spec.rb
@@ -1,10 +1,8 @@
require 'spec_helper'
feature 'Expand and collapse diffs', js: true, feature: true do
- include WaitForAjax
-
let(:branch) { 'expand-collapse-diffs' }
- let(:project) { create(:project) }
+ let(:project) { create(:project, :repository) }
before do
login_as :admin
diff --git a/spec/features/explore/groups_list_spec.rb b/spec/features/explore/groups_list_spec.rb
index 9daaaa8e555..9828cb179a7 100644
--- a/spec/features/explore/groups_list_spec.rb
+++ b/spec/features/explore/groups_list_spec.rb
@@ -1,8 +1,6 @@
require 'spec_helper'
-describe 'Explore Groups page', js: true, feature: true do
- include WaitForAjax
-
+describe 'Explore Groups page', :js, :feature do
let!(:user) { create :user }
let!(:group) { create(:group) }
let!(:public_group) { create(:group, :public) }
@@ -48,19 +46,39 @@ describe 'Explore Groups page', js: true, feature: true do
it 'shows non-archived projects count' do
# Initially project is not archived
expect(find('.js-groups-list-holder .content-list li:first-child .stats span:first-child')).to have_text("1")
-
+
# Archive project
empty_project.archive!
visit explore_groups_path
# Check project count
expect(find('.js-groups-list-holder .content-list li:first-child .stats span:first-child')).to have_text("0")
-
+
# Unarchive project
empty_project.unarchive!
visit explore_groups_path
# Check project count
- expect(find('.js-groups-list-holder .content-list li:first-child .stats span:first-child')).to have_text("1")
+ expect(find('.js-groups-list-holder .content-list li:first-child .stats span:first-child')).to have_text("1")
+ end
+
+ describe 'landing component' do
+ it 'should show a landing component' do
+ expect(page).to have_content('Below you will find all the groups that are public.')
+ end
+
+ it 'should be dismissable' do
+ find('.dismiss-button').click
+
+ expect(page).not_to have_content('Below you will find all the groups that are public.')
+ end
+
+ it 'should persistently not show once dismissed' do
+ find('.dismiss-button').click
+
+ visit explore_groups_path
+
+ expect(page).not_to have_content('Below you will find all the groups that are public.')
+ end
end
end
diff --git a/spec/features/gitlab_flavored_markdown_spec.rb b/spec/features/gitlab_flavored_markdown_spec.rb
index 84d73d693bc..005a029a393 100644
--- a/spec/features/gitlab_flavored_markdown_spec.rb
+++ b/spec/features/gitlab_flavored_markdown_spec.rb
@@ -1,28 +1,28 @@
require 'spec_helper'
describe "GitLab Flavored Markdown", feature: true do
- let(:project) { create(:project) }
+ let(:project) { create(:empty_project) }
let(:issue) { create(:issue, project: project) }
- let(:merge_request) { create(:merge_request, source_project: project, target_project: project) }
let(:fred) do
- u = create(:user, name: "fred")
- project.team << [u, :master]
- u
+ create(:user, name: 'fred') do |user|
+ project.add_master(user)
+ end
end
before do
- allow_any_instance_of(Commit).to receive(:title).
- and_return("fix #{issue.to_reference}\n\nask #{fred.to_reference} for details")
+ login_as(:user)
+ project.add_developer(@user)
end
- let(:commit) { project.commit }
+ describe "for commits" do
+ let(:project) { create(:project, :repository) }
+ let(:commit) { project.commit }
- before do
- login_as :user
- project.team << [@user, :developer]
- end
+ before do
+ allow_any_instance_of(Commit).to receive(:title).
+ and_return("fix #{issue.to_reference}\n\nask #{fred.to_reference} for details")
+ end
- describe "for commits" do
it "renders title in commits#index" do
visit namespace_project_commits_path(project.namespace, project, 'master', limit: 1)
@@ -48,18 +48,22 @@ describe "GitLab Flavored Markdown", feature: true do
end
end
- describe "for issues" do
+ describe "for issues", feature: true, js: true do
+ include WaitForVueResource
+
before do
@other_issue = create(:issue,
author: @user,
- assignee: @user,
+ assignees: [@user],
project: project)
@issue = create(:issue,
author: @user,
- assignee: @user,
+ assignees: [@user],
project: project,
title: "fix #{@other_issue.to_reference}",
description: "ask #{fred.to_reference} for details")
+
+ @note = create(:note_on_issue, noteable: @issue, project: @issue.project, note: "Hello world")
end
it "renders subject in issues#index" do
@@ -82,6 +86,8 @@ describe "GitLab Flavored Markdown", feature: true do
end
describe "for merge requests" do
+ let(:project) { create(:project, :repository) }
+
before do
@merge_request = create(:merge_request, source_project: project, target_project: project, title: "fix #{issue.to_reference}")
end
diff --git a/spec/features/global_search_spec.rb b/spec/features/global_search_spec.rb
index f6409e00f22..4b22b07494d 100644
--- a/spec/features/global_search_spec.rb
+++ b/spec/features/global_search_spec.rb
@@ -2,7 +2,7 @@ require 'spec_helper'
feature 'Global search', feature: true do
let(:user) { create(:user) }
- let(:project) { create(:project, namespace: user.namespace) }
+ let(:project) { create(:empty_project, namespace: user.namespace) }
before do
project.team << [user, :master]
diff --git a/spec/features/groups/empty_states_spec.rb b/spec/features/groups/empty_states_spec.rb
new file mode 100644
index 00000000000..fef8e41bffe
--- /dev/null
+++ b/spec/features/groups/empty_states_spec.rb
@@ -0,0 +1,70 @@
+require 'spec_helper'
+
+feature 'Groups Merge Requests Empty States' do
+ let(:group) { create(:group) }
+ let(:user) { create(:group_member, :developer, user: create(:user), group: group ).user }
+
+ before do
+ login_as(user)
+ end
+
+ context 'group has a project' do
+ let(:project) { create(:empty_project, namespace: group) }
+
+ before do
+ project.add_master(user)
+ end
+
+ context 'the project has a merge request' do
+ before do
+ create(:merge_request, source_project: project)
+
+ visit merge_requests_group_path(group)
+ end
+
+ it 'should not display an empty state' do
+ expect(page).not_to have_selector('.empty-state')
+ end
+ end
+
+ context 'the project has no merge requests', :js do
+ before do
+ visit merge_requests_group_path(group)
+ end
+
+ it 'should display an empty state' do
+ expect(page).to have_selector('.empty-state')
+ end
+
+ it 'should show a new merge request button' do
+ within '.empty-state' do
+ expect(page).to have_content('New merge request')
+ end
+ end
+
+ it 'the new merge request button opens a project dropdown' do
+ within '.empty-state' do
+ find('.new-project-item-select-button').click
+ end
+
+ expect(page).to have_selector('.ajax-project-dropdown')
+ end
+ end
+ end
+
+ context 'group without a project' do
+ before do
+ visit merge_requests_group_path(group)
+ end
+
+ it 'should display an empty state' do
+ expect(page).to have_selector('.empty-state')
+ end
+
+ it 'should not show a new merge request button' do
+ within '.empty-state' do
+ expect(page).not_to have_link('New merge request')
+ end
+ end
+ end
+end
diff --git a/spec/features/groups/group_settings_spec.rb b/spec/features/groups/group_settings_spec.rb
new file mode 100644
index 00000000000..cc25db4ad60
--- /dev/null
+++ b/spec/features/groups/group_settings_spec.rb
@@ -0,0 +1,80 @@
+require 'spec_helper'
+
+feature 'Edit group settings', feature: true do
+ given(:user) { create(:user) }
+ given(:group) { create(:group, path: 'foo') }
+
+ background do
+ group.add_owner(user)
+ login_as(user)
+ end
+
+ describe 'when the group path is changed' do
+ let(:new_group_path) { 'bar' }
+ let(:old_group_full_path) { "/#{group.path}" }
+ let(:new_group_full_path) { "/#{new_group_path}" }
+
+ scenario 'the group is accessible via the new path' do
+ update_path(new_group_path)
+ visit new_group_full_path
+ expect(current_path).to eq(new_group_full_path)
+ expect(find('h1.group-title')).to have_content(new_group_path)
+ end
+
+ scenario 'the old group path redirects to the new path' do
+ update_path(new_group_path)
+ visit old_group_full_path
+ expect(current_path).to eq(new_group_full_path)
+ expect(find('h1.group-title')).to have_content(new_group_path)
+ end
+
+ context 'with a subgroup' do
+ given!(:subgroup) { create(:group, parent: group, path: 'subgroup') }
+ given(:old_subgroup_full_path) { "/#{group.path}/#{subgroup.path}" }
+ given(:new_subgroup_full_path) { "/#{new_group_path}/#{subgroup.path}" }
+
+ scenario 'the subgroup is accessible via the new path' do
+ update_path(new_group_path)
+ visit new_subgroup_full_path
+ expect(current_path).to eq(new_subgroup_full_path)
+ expect(find('h1.group-title')).to have_content(subgroup.path)
+ end
+
+ scenario 'the old subgroup path redirects to the new path' do
+ update_path(new_group_path)
+ visit old_subgroup_full_path
+ expect(current_path).to eq(new_subgroup_full_path)
+ expect(find('h1.group-title')).to have_content(subgroup.path)
+ end
+ end
+
+ context 'with a project' do
+ given!(:project) { create(:project, group: group, path: 'project') }
+ given(:old_project_full_path) { "/#{group.path}/#{project.path}" }
+ given(:new_project_full_path) { "/#{new_group_path}/#{project.path}" }
+
+ before(:context) { TestEnv.clean_test_path }
+ after(:example) { TestEnv.clean_test_path }
+
+ scenario 'the project is accessible via the new path' do
+ update_path(new_group_path)
+ visit new_project_full_path
+ expect(current_path).to eq(new_project_full_path)
+ expect(find('h1.project-title')).to have_content(project.name)
+ end
+
+ scenario 'the old project path redirects to the new path' do
+ update_path(new_group_path)
+ visit old_project_full_path
+ expect(current_path).to eq(new_project_full_path)
+ expect(find('h1.project-title')).to have_content(project.name)
+ end
+ end
+ end
+end
+
+def update_path(new_group_path)
+ visit edit_group_path(group)
+ fill_in 'group_path', with: new_group_path
+ click_button 'Save group'
+end
diff --git a/spec/features/groups/issues_spec.rb b/spec/features/groups/issues_spec.rb
index 1b3747c390b..45f57845c74 100644
--- a/spec/features/groups/issues_spec.rb
+++ b/spec/features/groups/issues_spec.rb
@@ -23,4 +23,20 @@ feature 'Group issues page', feature: true do
it_behaves_like "an autodiscoverable RSS feed without a private token"
end
end
+
+ context 'assignee', :js do
+ let(:access_level) { ProjectFeature::ENABLED }
+ let(:user) { user_in_group }
+ let(:user2) { user_outside_group }
+ let(:path) { issues_group_path(group) }
+
+ it 'filters by only group users' do
+ click_button('Assignee')
+
+ wait_for_ajax
+
+ expect(find('.dropdown-menu-assignee')).to have_link(user.name)
+ expect(find('.dropdown-menu-assignee')).not_to have_link(user2.name)
+ end
+ end
end
diff --git a/spec/features/groups/members/list_spec.rb b/spec/features/groups/members/list_spec.rb
index 14c193f7450..543879bd21d 100644
--- a/spec/features/groups/members/list_spec.rb
+++ b/spec/features/groups/members/list_spec.rb
@@ -1,6 +1,8 @@
require 'spec_helper'
feature 'Groups members list', feature: true do
+ include Select2Helper
+
let(:user1) { create(:user, name: 'John Doe') }
let(:user2) { create(:user, name: 'Mary Jane') }
let(:group) { create(:group) }
@@ -30,7 +32,7 @@ feature 'Groups members list', feature: true do
expect(second_row).to be_blank
end
- it 'updates user to owner level', :js do
+ scenario 'update user to owner level', :js do
group.add_owner(user1)
group.add_developer(user2)
@@ -38,13 +40,52 @@ feature 'Groups members list', feature: true do
page.within(second_row) do
click_button('Developer')
-
click_link('Owner')
expect(page).to have_button('Owner')
end
end
+ scenario 'add user to group', :js do
+ group.add_owner(user1)
+
+ visit group_group_members_path(group)
+
+ add_user(user2.id, 'Reporter')
+
+ page.within(second_row) do
+ expect(page).to have_content(user2.name)
+ expect(page).to have_button('Reporter')
+ end
+ end
+
+ scenario 'add yourself to group when already an owner', :js do
+ group.add_owner(user1)
+
+ visit group_group_members_path(group)
+
+ add_user(user1.id, 'Reporter')
+
+ page.within(first_row) do
+ expect(page).to have_content(user1.name)
+ expect(page).to have_content('Owner')
+ end
+ end
+
+ scenario 'invite user to group', :js do
+ group.add_owner(user1)
+
+ visit group_group_members_path(group)
+
+ add_user('test@example.com', 'Reporter')
+
+ page.within(second_row) do
+ expect(page).to have_content('test@example.com')
+ expect(page).to have_content('Invited')
+ expect(page).to have_button('Reporter')
+ end
+ end
+
def first_row
page.all('ul.content-list > li')[0]
end
@@ -52,4 +93,13 @@ feature 'Groups members list', feature: true do
def second_row
page.all('ul.content-list > li')[1]
end
+
+ def add_user(id, role)
+ page.within ".users-group-form" do
+ select2(id, from: "#user_ids", multiple: true)
+ select(role, from: "access_level")
+ end
+
+ click_button "Add to group"
+ end
end
diff --git a/spec/features/groups/members/sorting_spec.rb b/spec/features/groups/members/sorting_spec.rb
index 608aedd3471..902d3f789ff 100644
--- a/spec/features/groups/members/sorting_spec.rb
+++ b/spec/features/groups/members/sorting_spec.rb
@@ -68,7 +68,7 @@ feature 'Groups > Members > Sorting', feature: true do
expect(page).to have_css('.member-sort-dropdown .dropdown-toggle-text', text: 'Name, descending')
end
- scenario 'sorts by recent sign in' do
+ scenario 'sorts by recent sign in', :redis do
visit_members_list(sort: :recent_sign_in)
expect(first_member).to include(owner.name)
@@ -76,7 +76,7 @@ feature 'Groups > Members > Sorting', feature: true do
expect(page).to have_css('.member-sort-dropdown .dropdown-toggle-text', text: 'Recent sign in')
end
- scenario 'sorts by oldest sign in' do
+ scenario 'sorts by oldest sign in', :redis do
visit_members_list(sort: :oldest_sign_in)
expect(first_member).to include(developer.name)
diff --git a/spec/features/groups/milestone_spec.rb b/spec/features/groups/milestone_spec.rb
new file mode 100644
index 00000000000..daa2c6afd63
--- /dev/null
+++ b/spec/features/groups/milestone_spec.rb
@@ -0,0 +1,36 @@
+require 'rails_helper'
+
+feature 'Group milestones', :feature, :js do
+ let(:group) { create(:group) }
+ let!(:project) { create(:project_empty_repo, group: group) }
+ let(:user) { create(:group_member, :master, user: create(:user), group: group ).user }
+
+ before do
+ Timecop.freeze
+
+ login_as(user)
+ end
+
+ after do
+ Timecop.return
+ end
+
+ context 'create a milestone' do
+ before do
+ visit new_group_milestone_path(group)
+ end
+
+ it 'creates milestone with start date' do
+ fill_in 'Title', with: 'testing'
+ find('#milestone_start_date').click
+
+ page.within(find('.pika-single')) do
+ click_button '1'
+ end
+
+ click_button 'Create milestone'
+
+ expect(find('.start_date')).to have_content(Date.today.at_beginning_of_month.strftime('%b %-d, %Y'))
+ end
+ end
+end
diff --git a/spec/features/groups_spec.rb b/spec/features/groups_spec.rb
index c90cc06a8f5..3d32c47bf09 100644
--- a/spec/features/groups_spec.rb
+++ b/spec/features/groups_spec.rb
@@ -83,20 +83,43 @@ feature 'Group', feature: true do
end
end
- describe 'create a nested group' do
+ describe 'create a nested group', js: true do
let(:group) { create(:group, path: 'foo') }
- before do
- visit subgroups_group_path(group)
- click_link 'New Subgroup'
+ context 'as admin' do
+ before do
+ visit subgroups_group_path(group)
+ click_link 'New Subgroup'
+ end
+
+ it 'creates a nested group' do
+ fill_in 'Group path', with: 'bar'
+ click_button 'Create group'
+
+ expect(current_path).to eq(group_path('foo/bar'))
+ expect(page).to have_content("Group 'bar' was successfully created.")
+ end
end
- it 'creates a nested group' do
- fill_in 'Group path', with: 'bar'
- click_button 'Create group'
+ context 'as group owner' do
+ let(:user) { create(:user) }
- expect(current_path).to eq(group_path('foo/bar'))
- expect(page).to have_content("Group 'bar' was successfully created.")
+ before do
+ group.add_owner(user)
+ logout
+ login_as(user)
+
+ visit subgroups_group_path(group)
+ click_link 'New Subgroup'
+ end
+
+ it 'creates a nested group' do
+ fill_in 'Group path', with: 'bar'
+ click_button 'Create group'
+
+ expect(current_path).to eq(group_path('foo/bar'))
+ expect(page).to have_content("Group 'bar' was successfully created.")
+ end
end
end
@@ -130,7 +153,7 @@ feature 'Group', feature: true do
end
it 'removes group' do
- click_link 'Remove Group'
+ click_link 'Remove group'
expect(page).to have_content "scheduled for deletion"
end
diff --git a/spec/features/issuables/issuable_list_spec.rb b/spec/features/issuables/issuable_list_spec.rb
index b90bf6268fd..414838fa22e 100644
--- a/spec/features/issuables/issuable_list_spec.rb
+++ b/spec/features/issuables/issuable_list_spec.rb
@@ -46,16 +46,19 @@ describe 'issuable list', feature: true do
end
def create_issuables(issuable_type)
- 3.times do
+ 3.times do |n|
issuable =
if issuable_type == :issue
create(:issue, project: project, author: user)
else
- create(:merge_request, title: FFaker::Lorem.sentence, source_project: project, source_branch: FFaker::Name.name)
+ create(:merge_request, source_project: project, source_branch: generate(:branch))
+ source_branch = FFaker::Name.name
+ pipeline = create(:ci_empty_pipeline, project: project, ref: source_branch, status: %w(running failed success).sample, sha: 'any')
+ create(:merge_request, title: FFaker::Lorem.sentence, source_project: project, source_branch: source_branch, head_pipeline: pipeline)
end
2.times do
- create(:note_on_issue, noteable: issuable, project: project, note: 'Test note')
+ create(:note_on_issue, noteable: issuable, project: project)
end
create(:award_emoji, :downvote, awardable: issuable)
@@ -65,11 +68,10 @@ describe 'issuable list', feature: true do
if issuable_type == :issue
issue = Issue.reorder(:iid).first
merge_request = create(:merge_request,
- title: FFaker::Lorem.sentence,
source_project: project,
- source_branch: FFaker::Name.name)
+ source_branch: generate(:branch))
- MergeRequestsClosingIssues.create!(issue: issue, merge_request: merge_request)
+ create(:merge_requests_closing_issues, issue: issue, merge_request: merge_request)
end
end
end
diff --git a/spec/features/issues/award_emoji_spec.rb b/spec/features/issues/award_emoji_spec.rb
index 16e453bc328..853632614c4 100644
--- a/spec/features/issues/award_emoji_spec.rb
+++ b/spec/features/issues/award_emoji_spec.rb
@@ -1,13 +1,13 @@
require 'rails_helper'
describe 'Awards Emoji', feature: true do
- include WaitForAjax
+ include WaitForVueResource
let!(:project) { create(:project, :public) }
let!(:user) { create(:user) }
let(:issue) do
create(:issue,
- assignee: @user,
+ assignees: [user],
project: project)
end
@@ -22,10 +22,11 @@ describe 'Awards Emoji', feature: true do
# The `heart_tip` emoji is not valid anymore so we need to skip validation
issue.award_emoji.build(user: user, name: 'heart_tip').save!(validate: false)
visit namespace_project_issue_path(project.namespace, project, issue)
+ wait_for_vue_resource
end
# Regression test: https://gitlab.com/gitlab-org/gitlab-ce/issues/29529
- it 'does not shows a 500 page' do
+ it 'does not shows a 500 page', js: true do
expect(page).to have_text(issue.title)
end
end
@@ -35,6 +36,7 @@ describe 'Awards Emoji', feature: true do
before do
visit namespace_project_issue_path(project.namespace, project, issue)
+ wait_for_vue_resource
end
it 'increments the thumbsdown emoji', js: true do
diff --git a/spec/features/issues/award_spec.rb b/spec/features/issues/award_spec.rb
index 401e1ea2b89..08e3f99e29f 100644
--- a/spec/features/issues/award_spec.rb
+++ b/spec/features/issues/award_spec.rb
@@ -6,9 +6,12 @@ feature 'Issue awards', js: true, feature: true do
let(:issue) { create(:issue, project: project) }
describe 'logged in' do
+ include WaitForVueResource
+
before do
login_as(user)
visit namespace_project_issue_path(project.namespace, project, issue)
+ wait_for_vue_resource
end
it 'adds award to issue' do
@@ -38,8 +41,11 @@ feature 'Issue awards', js: true, feature: true do
end
describe 'logged out' do
+ include WaitForVueResource
+
before do
visit namespace_project_issue_path(project.namespace, project, issue)
+ wait_for_vue_resource
end
it 'does not see award menu button' do
diff --git a/spec/features/issues/bulk_assignment_labels_spec.rb b/spec/features/issues/bulk_assignment_labels_spec.rb
index 2f59630b4fb..1de50d6d77e 100644
--- a/spec/features/issues/bulk_assignment_labels_spec.rb
+++ b/spec/features/issues/bulk_assignment_labels_spec.rb
@@ -1,8 +1,6 @@
require 'rails_helper'
feature 'Issues > Labels bulk assignment', feature: true do
- include WaitForAjax
-
let(:user) { create(:user) }
let!(:project) { create(:project) }
let!(:issue1) { create(:issue, project: project, title: "Issue 1") }
diff --git a/spec/features/issues/create_branch_merge_request_spec.rb b/spec/features/issues/create_branch_merge_request_spec.rb
new file mode 100644
index 00000000000..44c19275ae5
--- /dev/null
+++ b/spec/features/issues/create_branch_merge_request_spec.rb
@@ -0,0 +1,91 @@
+require 'rails_helper'
+
+feature 'Create Branch/Merge Request Dropdown on issue page', feature: true, js: true do
+ let(:user) { create(:user) }
+ let!(:project) { create(:project) }
+ let(:issue) { create(:issue, project: project, title: 'Cherry-Coloured Funk') }
+
+ context 'for team members' do
+ before do
+ project.team << [user, :developer]
+ login_as(user)
+ end
+
+ it 'allows creating a merge request from the issue page' do
+ visit namespace_project_issue_path(project.namespace, project, issue)
+
+ select_dropdown_option('create-mr')
+
+ wait_for_ajax
+
+ expect(page).to have_content("created branch 1-cherry-coloured-funk")
+ expect(page).to have_content("mentioned in merge request !1")
+
+ visit namespace_project_merge_request_path(project.namespace, project, MergeRequest.first)
+
+ expect(page).to have_content('WIP: Resolve "Cherry-Coloured Funk"')
+ expect(current_path).to eq(namespace_project_merge_request_path(project.namespace, project, MergeRequest.first))
+ end
+
+ it 'allows creating a branch from the issue page' do
+ visit namespace_project_issue_path(project.namespace, project, issue)
+
+ select_dropdown_option('create-branch')
+
+ wait_for_ajax
+
+ expect(page).to have_selector('.dropdown-toggle-text ', text: '1-cherry-coloured-funk')
+ expect(current_path).to eq namespace_project_tree_path(project.namespace, project, '1-cherry-coloured-funk')
+ end
+
+ context "when there is a referenced merge request" do
+ let!(:note) do
+ create(:note, :on_issue, :system, project: project, noteable: issue,
+ note: "mentioned in #{referenced_mr.to_reference}")
+ end
+
+ let(:referenced_mr) do
+ create(:merge_request, :simple, source_project: project, target_project: project,
+ description: "Fixes #{issue.to_reference}", author: user)
+ end
+
+ before do
+ referenced_mr.cache_merge_request_closes_issues!(user)
+
+ visit namespace_project_issue_path(project.namespace, project, issue)
+ end
+
+ it 'disables the create branch button' do
+ expect(page).to have_css('.create-mr-dropdown-wrap .unavailable:not(.hide)')
+ expect(page).to have_css('.create-mr-dropdown-wrap .available.hide', visible: false)
+ expect(page).to have_content /1 Related Merge Request/
+ end
+ end
+
+ context 'when issue is confidential' do
+ it 'disables the create branch button' do
+ issue = create(:issue, :confidential, project: project)
+
+ visit namespace_project_issue_path(project.namespace, project, issue)
+
+ expect(page).not_to have_css('.create-mr-dropdown-wrap')
+ end
+ end
+ end
+
+ context 'for visitors' do
+ before do
+ visit namespace_project_issue_path(project.namespace, project, issue)
+ end
+
+ it 'shows no buttons' do
+ expect(page).not_to have_selector('.create-mr-dropdown-wrap')
+ end
+ end
+
+ def select_dropdown_option(option)
+ find('.create-mr-dropdown-wrap .dropdown-toggle').click
+ find("li[data-value='#{option}']").click
+ find('.js-create-merge-request').click
+ end
+end
diff --git a/spec/features/issues/create_issue_for_discussions_in_merge_request_spec.rb b/spec/features/issues/create_issue_for_discussions_in_merge_request_spec.rb
index 572bca3de21..24e2419b5ce 100644
--- a/spec/features/issues/create_issue_for_discussions_in_merge_request_spec.rb
+++ b/spec/features/issues/create_issue_for_discussions_in_merge_request_spec.rb
@@ -4,7 +4,7 @@ feature 'Resolving all open discussions in a merge request from an issue', featu
let(:user) { create(:user) }
let(:project) { create(:project) }
let(:merge_request) { create(:merge_request, source_project: project) }
- let!(:discussion) { Discussion.for_diff_notes([create(:diff_note_on_merge_request, noteable: merge_request, project: project)]).first }
+ let!(:discussion) { create(:diff_note_on_merge_request, noteable: merge_request, project: project).to_discussion }
describe 'as a user with access to the project' do
before do
@@ -14,7 +14,7 @@ feature 'Resolving all open discussions in a merge request from an issue', featu
end
it 'shows a button to resolve all discussions by creating a new issue' do
- within('li#resolve-count-app') do
+ within('#resolve-count-app') do
expect(page).to have_link "Resolve all discussions in new issue", href: new_namespace_project_issue_path(project.namespace, project, merge_request_to_resolve_discussions_of: merge_request.iid)
end
end
@@ -49,7 +49,7 @@ feature 'Resolving all open discussions in a merge request from an issue', featu
end
it 'does not show a link to create a new issue' do
- expect(page).not_to have_link 'open an issue to resolve them later'
+ expect(page).not_to have_link 'Create an issue to resolve them later'
end
end
@@ -59,18 +59,18 @@ feature 'Resolving all open discussions in a merge request from an issue', featu
end
it 'shows a warning that the merge request contains unresolved discussions' do
- expect(page).to have_content 'This merge request has unresolved discussions'
+ expect(page).to have_content 'There are unresolved discussions.'
end
it 'has a link to resolve all discussions by creating an issue' do
page.within '.mr-widget-body' do
- expect(page).to have_link 'open an issue to resolve them later', href: new_namespace_project_issue_path(project.namespace, project, merge_request_to_resolve_discussions_of: merge_request.iid)
+ expect(page).to have_link 'Create an issue to resolve them later', href: new_namespace_project_issue_path(project.namespace, project, merge_request_to_resolve_discussions_of: merge_request.iid)
end
end
context 'creating an issue for discussions' do
before do
- page.click_link 'open an issue to resolve them later', href: new_namespace_project_issue_path(project.namespace, project, merge_request_to_resolve_discussions_of: merge_request.iid)
+ page.click_link 'Create an issue to resolve them later', href: new_namespace_project_issue_path(project.namespace, project, merge_request_to_resolve_discussions_of: merge_request.iid)
end
it_behaves_like 'creating an issue for a discussion'
diff --git a/spec/features/issues/create_issue_for_single_discussion_in_merge_request.rb b/spec/features/issues/create_issue_for_single_discussion_in_merge_request_spec.rb
index 88e2cc60d79..3a5a79e03f4 100644
--- a/spec/features/issues/create_issue_for_single_discussion_in_merge_request.rb
+++ b/spec/features/issues/create_issue_for_single_discussion_in_merge_request_spec.rb
@@ -4,7 +4,7 @@ feature 'Resolve an open discussion in a merge request by creating an issue', fe
let(:user) { create(:user) }
let(:project) { create(:project, only_allow_merge_if_all_discussions_are_resolved: true) }
let(:merge_request) { create(:merge_request, source_project: project) }
- let!(:discussion) { Discussion.for_diff_notes([create(:diff_note_on_merge_request, noteable: merge_request, project: project)]).first }
+ let!(:discussion) { create(:diff_note_on_merge_request, noteable: merge_request, project: project).to_discussion }
describe 'As a user with access to the project' do
before do
@@ -74,8 +74,8 @@ feature 'Resolve an open discussion in a merge request by creating an issue', fe
it 'Shows a notice to ask someone else to resolve the discussions' do
expect(page).to have_content("The discussion at #{merge_request.to_reference}"\
- "(discussion #{discussion.first_note.id}) will stay unresolved."\
- "Ask someone with permission to resolve it.")
+ " (discussion #{discussion.first_note.id}) will stay unresolved."\
+ " Ask someone with permission to resolve it.")
end
end
end
diff --git a/spec/features/issues/filtered_search/dropdown_assignee_spec.rb b/spec/features/issues/filtered_search/dropdown_assignee_spec.rb
index 4dcc56a97d1..0b573d7cef4 100644
--- a/spec/features/issues/filtered_search/dropdown_assignee_spec.rb
+++ b/spec/features/issues/filtered_search/dropdown_assignee_spec.rb
@@ -2,7 +2,6 @@ require 'rails_helper'
describe 'Dropdown assignee', :feature, :js do
include FilteredSearchHelpers
- include WaitForAjax
let!(:project) { create(:empty_project) }
let!(:user) { create(:user, name: 'administrator', username: 'root') }
@@ -194,7 +193,7 @@ describe 'Dropdown assignee', :feature, :js do
new_user = create(:user)
project.team << [new_user, :master]
- find('.filtered-search-input-container .clear-search').click
+ find('.filtered-search-box .clear-search').click
filtered_search.set('assignee')
filtered_search.send_keys(':')
diff --git a/spec/features/issues/filtered_search/dropdown_author_spec.rb b/spec/features/issues/filtered_search/dropdown_author_spec.rb
index 1772a120045..0579d6c80ab 100644
--- a/spec/features/issues/filtered_search/dropdown_author_spec.rb
+++ b/spec/features/issues/filtered_search/dropdown_author_spec.rb
@@ -2,7 +2,6 @@ require 'rails_helper'
describe 'Dropdown author', js: true, feature: true do
include FilteredSearchHelpers
- include WaitForAjax
let!(:project) { create(:empty_project) }
let!(:user) { create(:user, name: 'administrator', username: 'root') }
@@ -172,7 +171,7 @@ describe 'Dropdown author', js: true, feature: true do
new_user = create(:user)
project.team << [new_user, :master]
- find('.filtered-search-input-container .clear-search').click
+ find('.filtered-search-box .clear-search').click
filtered_search.set('author')
send_keys_to_filtered_search(':')
diff --git a/spec/features/issues/filtered_search/dropdown_hint_spec.rb b/spec/features/issues/filtered_search/dropdown_hint_spec.rb
index bc8cbe30e66..b9a37cfcc22 100644
--- a/spec/features/issues/filtered_search/dropdown_hint_spec.rb
+++ b/spec/features/issues/filtered_search/dropdown_hint_spec.rb
@@ -1,18 +1,13 @@
require 'rails_helper'
-describe 'Dropdown hint', js: true, feature: true do
+describe 'Dropdown hint', :js, :feature do
include FilteredSearchHelpers
- include WaitForAjax
let!(:project) { create(:empty_project) }
let!(:user) { create(:user) }
let(:filtered_search) { find('.filtered-search') }
let(:js_dropdown_hint) { '#js-dropdown-hint' }
- def dropdown_hint_size
- page.all('#js-dropdown-hint .filter-dropdown .filter-dropdown-item').size
- end
-
def click_hint(text)
find('#js-dropdown-hint .filter-dropdown .filter-dropdown-item', text: text).click
end
@@ -46,14 +41,16 @@ describe 'Dropdown hint', js: true, feature: true do
it 'does not filter `Press Enter or click to search`' do
filtered_search.set('randomtext')
- expect(page).to have_css(js_dropdown_hint, text: 'Press Enter or click to search', visible: false)
- expect(dropdown_hint_size).to eq(0)
+ hint_dropdown = find(js_dropdown_hint)
+
+ expect(hint_dropdown).to have_content('Press Enter or click to search')
+ expect(hint_dropdown).to have_selector('.filter-dropdown .filter-dropdown-item', count: 0)
end
it 'filters with text' do
filtered_search.set('a')
- expect(dropdown_hint_size).to eq(3)
+ expect(find(js_dropdown_hint)).to have_selector('.filter-dropdown .filter-dropdown-item', count: 3)
end
end
diff --git a/spec/features/issues/filtered_search/dropdown_label_spec.rb b/spec/features/issues/filtered_search/dropdown_label_spec.rb
index b192064b693..abe5d61e38c 100644
--- a/spec/features/issues/filtered_search/dropdown_label_spec.rb
+++ b/spec/features/issues/filtered_search/dropdown_label_spec.rb
@@ -28,12 +28,8 @@ describe 'Dropdown label', js: true, feature: true do
filter_dropdown.find('.filter-dropdown-item', text: text).click
end
- def dropdown_label_size
- filter_dropdown.all('.filter-dropdown-item').size
- end
-
def clear_search_field
- find('.filtered-search-input-container .clear-search').click
+ find('.filtered-search-box .clear-search').click
end
before do
@@ -81,7 +77,7 @@ describe 'Dropdown label', js: true, feature: true do
filtered_search.set('label:')
expect(filter_dropdown).to have_content(bug_label.title)
- expect(dropdown_label_size).to eq(1)
+ expect(filter_dropdown).to have_selector('.filter-dropdown-item', count: 1)
end
end
@@ -97,7 +93,8 @@ describe 'Dropdown label', js: true, feature: true do
expect(filter_dropdown.find('.filter-dropdown-item', text: bug_label.title)).to be_visible
expect(filter_dropdown.find('.filter-dropdown-item', text: uppercase_label.title)).to be_visible
- expect(dropdown_label_size).to eq(2)
+
+ expect(filter_dropdown).to have_selector('.filter-dropdown-item', count: 2)
clear_search_field
init_label_search
@@ -106,14 +103,14 @@ describe 'Dropdown label', js: true, feature: true do
expect(filter_dropdown.find('.filter-dropdown-item', text: bug_label.title)).to be_visible
expect(filter_dropdown.find('.filter-dropdown-item', text: uppercase_label.title)).to be_visible
- expect(dropdown_label_size).to eq(2)
+ expect(filter_dropdown).to have_selector('.filter-dropdown-item', count: 2)
end
it 'filters by multiple words with or without symbol' do
filtered_search.send_keys('Hig')
expect(filter_dropdown.find('.filter-dropdown-item', text: two_words_label.title)).to be_visible
- expect(dropdown_label_size).to eq(1)
+ expect(filter_dropdown).to have_selector('.filter-dropdown-item', count: 1)
clear_search_field
init_label_search
@@ -121,14 +118,14 @@ describe 'Dropdown label', js: true, feature: true do
filtered_search.send_keys('~Hig')
expect(filter_dropdown.find('.filter-dropdown-item', text: two_words_label.title)).to be_visible
- expect(dropdown_label_size).to eq(1)
+ expect(filter_dropdown).to have_selector('.filter-dropdown-item', count: 1)
end
it 'filters by multiple words containing single quotes with or without symbol' do
filtered_search.send_keys('won\'t')
expect(filter_dropdown.find('.filter-dropdown-item', text: wont_fix_single_label.title)).to be_visible
- expect(dropdown_label_size).to eq(1)
+ expect(filter_dropdown).to have_selector('.filter-dropdown-item', count: 1)
clear_search_field
init_label_search
@@ -136,14 +133,14 @@ describe 'Dropdown label', js: true, feature: true do
filtered_search.send_keys('~won\'t')
expect(filter_dropdown.find('.filter-dropdown-item', text: wont_fix_single_label.title)).to be_visible
- expect(dropdown_label_size).to eq(1)
+ expect(filter_dropdown).to have_selector('.filter-dropdown-item', count: 1)
end
it 'filters by multiple words containing double quotes with or without symbol' do
filtered_search.send_keys('won"t')
expect(filter_dropdown.find('.filter-dropdown-item', text: wont_fix_label.title)).to be_visible
- expect(dropdown_label_size).to eq(1)
+ expect(filter_dropdown).to have_selector('.filter-dropdown-item', count: 1)
clear_search_field
init_label_search
@@ -151,14 +148,14 @@ describe 'Dropdown label', js: true, feature: true do
filtered_search.send_keys('~won"t')
expect(filter_dropdown.find('.filter-dropdown-item', text: wont_fix_label.title)).to be_visible
- expect(dropdown_label_size).to eq(1)
+ expect(filter_dropdown).to have_selector('.filter-dropdown-item', count: 1)
end
it 'filters by special characters with or without symbol' do
filtered_search.send_keys('^+')
expect(filter_dropdown.find('.filter-dropdown-item', text: special_label.title)).to be_visible
- expect(dropdown_label_size).to eq(1)
+ expect(filter_dropdown).to have_selector('.filter-dropdown-item', count: 1)
clear_search_field
init_label_search
@@ -166,7 +163,7 @@ describe 'Dropdown label', js: true, feature: true do
filtered_search.send_keys('~^+')
expect(filter_dropdown.find('.filter-dropdown-item', text: special_label.title)).to be_visible
- expect(dropdown_label_size).to eq(1)
+ expect(filter_dropdown).to have_selector('.filter-dropdown-item', count: 1)
end
end
@@ -280,13 +277,13 @@ describe 'Dropdown label', js: true, feature: true do
create(:label, project: project, title: 'bug-label')
init_label_search
- expect(dropdown_label_size).to eq(1)
+ expect(filter_dropdown).to have_selector('.filter-dropdown-item', count: 1)
create(:label, project: project)
clear_search_field
init_label_search
- expect(dropdown_label_size).to eq(1)
+ expect(filter_dropdown).to have_selector('.filter-dropdown-item', count: 1)
end
end
end
diff --git a/spec/features/issues/filtered_search/dropdown_milestone_spec.rb b/spec/features/issues/filtered_search/dropdown_milestone_spec.rb
index ce96a420699..448259057b0 100644
--- a/spec/features/issues/filtered_search/dropdown_milestone_spec.rb
+++ b/spec/features/issues/filtered_search/dropdown_milestone_spec.rb
@@ -65,7 +65,7 @@ describe 'Dropdown milestone', :feature, :js do
it 'should load all the milestones when opened' do
filtered_search.set('milestone:')
- expect(dropdown_milestone_size).to be > 0
+ expect(filter_dropdown).to have_selector('.filter-dropdown .filter-dropdown-item', count: 6)
end
end
@@ -84,37 +84,37 @@ describe 'Dropdown milestone', :feature, :js do
it 'filters by name' do
filtered_search.send_keys('v1')
- expect(dropdown_milestone_size).to eq(1)
+ expect(filter_dropdown).to have_selector('.filter-dropdown .filter-dropdown-item', count: 1)
end
it 'filters by case insensitive name' do
filtered_search.send_keys('V1')
- expect(dropdown_milestone_size).to eq(1)
+ expect(filter_dropdown).to have_selector('.filter-dropdown .filter-dropdown-item', count: 1)
end
it 'filters by name with symbol' do
filtered_search.send_keys('%v1')
- expect(dropdown_milestone_size).to eq(1)
+ expect(filter_dropdown).to have_selector('.filter-dropdown .filter-dropdown-item', count: 1)
end
it 'filters by case insensitive name with symbol' do
filtered_search.send_keys('%V1')
- expect(dropdown_milestone_size).to eq(1)
+ expect(filter_dropdown).to have_selector('.filter-dropdown .filter-dropdown-item', count: 1)
end
it 'filters by special characters' do
filtered_search.send_keys('(+')
- expect(dropdown_milestone_size).to eq(1)
+ expect(filter_dropdown).to have_selector('.filter-dropdown .filter-dropdown-item', count: 1)
end
it 'filters by special characters with symbol' do
filtered_search.send_keys('%(+')
- expect(dropdown_milestone_size).to eq(1)
+ expect(filter_dropdown).to have_selector('.filter-dropdown .filter-dropdown-item', count: 1)
end
end
@@ -252,7 +252,7 @@ describe 'Dropdown milestone', :feature, :js do
expect(initial_size).to be > 0
create(:milestone, project: project)
- find('.filtered-search-input-container .clear-search').click
+ find('.filtered-search-box .clear-search').click
filtered_search.set('milestone:')
expect(dropdown_milestone_size).to eq(initial_size)
diff --git a/spec/features/issues/filtered_search/filter_issues_spec.rb b/spec/features/issues/filtered_search/filter_issues_spec.rb
index f463312bf57..a8f4e2d7e10 100644
--- a/spec/features/issues/filtered_search/filter_issues_spec.rb
+++ b/spec/features/issues/filtered_search/filter_issues_spec.rb
@@ -1,18 +1,18 @@
require 'spec_helper'
describe 'Filter issues', js: true, feature: true do
+ include Devise::Test::IntegrationHelpers
include FilteredSearchHelpers
- include WaitForAjax
let!(:group) { create(:group) }
let!(:project) { create(:project, group: group) }
- let!(:user) { create(:user) }
- let!(:user2) { create(:user) }
+ let!(:user) { create(:user, username: 'joe') }
+ let!(:user2) { create(:user, username: 'jane') }
let!(:label) { create(:label, project: project) }
let!(:wontfix) { create(:label, project: project, title: "Won't fix") }
let!(:bug_label) { create(:label, project: project, title: 'bug') }
- let!(:caps_sensitive_label) { create(:label, project: project, title: 'CAPS_sensitive') }
+ let!(:caps_sensitive_label) { create(:label, project: project, title: 'CaPs') }
let!(:milestone) { create(:milestone, title: "8", project: project, start_date: 2.days.ago) }
let!(:multiple_words_label) { create(:label, project: project, title: "Two words") }
@@ -42,23 +42,24 @@ describe 'Filter issues', js: true, feature: true do
project.team << [user2, :master]
group.add_developer(user)
group.add_developer(user2)
- login_as(user)
- create(:issue, project: project)
- create(:issue, title: "Bug report 1", project: project)
- create(:issue, title: "Bug report 2", project: project)
- create(:issue, title: "issue with 'single quotes'", project: project)
- create(:issue, title: "issue with \"double quotes\"", project: project)
- create(:issue, title: "issue with !@\#{$%^&*()-+", project: project)
- create(:issue, title: "issue by assignee", project: project, milestone: milestone, author: user, assignee: user)
- create(:issue, title: "issue by assignee with searchTerm", project: project, milestone: milestone, author: user, assignee: user)
+ sign_in(user)
+
+ create(:issue, project: project)
+ create(:issue, project: project, title: "Bug report 1")
+ create(:issue, project: project, title: "Bug report 2")
+ create(:issue, project: project, title: "issue with 'single quotes'")
+ create(:issue, project: project, title: "issue with \"double quotes\"")
+ create(:issue, project: project, title: "issue with !@\#{$%^&*()-+")
+ create(:issue, project: project, title: "issue by assignee", milestone: milestone, author: user, assignees: [user])
+ create(:issue, project: project, title: "issue by assignee with searchTerm", milestone: milestone, author: user, assignees: [user])
issue = create(:issue,
title: "Bug 2",
project: project,
milestone: milestone,
author: user,
- assignee: user)
+ assignees: [user])
issue.labels << bug_label
issue_with_caps_label = create(:issue,
@@ -66,15 +67,15 @@ describe 'Filter issues', js: true, feature: true do
project: project,
milestone: milestone,
author: user,
- assignee: user)
+ assignees: [user])
issue_with_caps_label.labels << caps_sensitive_label
issue_with_everything = create(:issue,
- title: "Bug report with everything you thought was possible",
+ title: "Bug report foo was possible",
project: project,
milestone: milestone,
author: user,
- assignee: user)
+ assignees: [user])
issue_with_everything.labels << bug_label
issue_with_everything.labels << caps_sensitive_label
@@ -687,10 +688,10 @@ describe 'Filter issues', js: true, feature: true do
end
it 'filters issues by searched text, author, more text, assignee and even more text' do
- input_filtered_search("bug author:@#{user.username} report assignee:@#{user.username} with")
+ input_filtered_search("bug author:@#{user.username} report assignee:@#{user.username} foo")
expect_issues_list_count(1)
- expect_filtered_search_input('bug report with')
+ expect_filtered_search_input('bug report foo')
end
it 'filters issues by searched text, author, assignee and label' do
@@ -701,10 +702,10 @@ describe 'Filter issues', js: true, feature: true do
end
it 'filters issues by searched text, author, text, assignee, text, label and text' do
- input_filtered_search("bug author:@#{user.username} report assignee:@#{user.username} with label:~#{bug_label.title} everything")
+ input_filtered_search("bug author:@#{user.username} assignee:@#{user.username} report label:~#{bug_label.title} foo")
expect_issues_list_count(1)
- expect_filtered_search_input('bug report with everything')
+ expect_filtered_search_input('bug report foo')
end
it 'filters issues by searched text, author, assignee, label and milestone' do
@@ -715,10 +716,10 @@ describe 'Filter issues', js: true, feature: true do
end
it 'filters issues by searched text, author, text, assignee, text, label, text, milestone and text' do
- input_filtered_search("bug author:@#{user.username} report assignee:@#{user.username} with label:~#{bug_label.title} everything milestone:%#{milestone.title} you")
+ input_filtered_search("bug author:@#{user.username} assignee:@#{user.username} report label:~#{bug_label.title} milestone:%#{milestone.title} foo")
expect_issues_list_count(1)
- expect_filtered_search_input('bug report with everything you')
+ expect_filtered_search_input('bug report foo')
end
it 'filters issues by searched text, author, assignee, multiple labels and milestone' do
@@ -729,10 +730,10 @@ describe 'Filter issues', js: true, feature: true do
end
it 'filters issues by searched text, author, text, assignee, text, label1, text, label2, text, milestone and text' do
- input_filtered_search("bug author:@#{user.username} report assignee:@#{user.username} with label:~#{bug_label.title} everything label:~#{caps_sensitive_label.title} you milestone:%#{milestone.title} thought")
+ input_filtered_search("bug author:@#{user.username} assignee:@#{user.username} report label:~#{bug_label.title} label:~#{caps_sensitive_label.title} milestone:%#{milestone.title} foo")
expect_issues_list_count(1)
- expect_filtered_search_input('bug report with everything you thought')
+ expect_filtered_search_input('bug report foo')
end
end
@@ -756,10 +757,10 @@ describe 'Filter issues', js: true, feature: true do
expect_issues_list_count(2)
- sort_toggle = find('.filtered-search-container .dropdown-toggle')
+ sort_toggle = find('.filtered-search-wrapper .dropdown-toggle')
sort_toggle.click
- find('.filtered-search-container .dropdown-menu li a', text: 'Oldest updated').click
+ find('.filtered-search-wrapper .dropdown-menu li a', text: 'Oldest updated').click
wait_for_ajax
expect(find('.issues-list .issue:first-of-type .issue-title-text a')).to have_content(old_issue.title)
diff --git a/spec/features/issues/filtered_search/recent_searches_spec.rb b/spec/features/issues/filtered_search/recent_searches_spec.rb
new file mode 100644
index 00000000000..09f228bcf49
--- /dev/null
+++ b/spec/features/issues/filtered_search/recent_searches_spec.rb
@@ -0,0 +1,109 @@
+require 'spec_helper'
+
+describe 'Recent searches', js: true, feature: true do
+ include FilteredSearchHelpers
+
+ let(:project_1) { create(:empty_project, :public) }
+ let(:project_2) { create(:empty_project, :public) }
+ let(:project_1_local_storage_key) { "#{project_1.full_path}-issue-recent-searches" }
+
+ before do
+ Capybara.ignore_hidden_elements = false
+ create(:issue, project: project_1)
+ create(:issue, project: project_2)
+
+ # Visit any fast-loading page so we can clear local storage without a DOM exception
+ visit '/404'
+ remove_recent_searches
+ end
+
+ after do
+ Capybara.ignore_hidden_elements = true
+ end
+
+ it 'searching adds to recent searches' do
+ visit namespace_project_issues_path(project_1.namespace, project_1)
+
+ input_filtered_search('foo', submit: true)
+ input_filtered_search('bar', submit: true)
+
+ items = all('.filtered-search-history-dropdown-item', visible: false)
+
+ expect(items.count).to eq(2)
+ expect(items[0].text).to eq('bar')
+ expect(items[1].text).to eq('foo')
+ end
+
+ it 'visiting URL with search params adds to recent searches' do
+ visit namespace_project_issues_path(project_1.namespace, project_1, label_name: 'foo', search: 'bar')
+ visit namespace_project_issues_path(project_1.namespace, project_1, label_name: 'qux', search: 'garply')
+
+ items = all('.filtered-search-history-dropdown-item', visible: false)
+
+ expect(items.count).to eq(2)
+ expect(items[0].text).to eq('label:~qux garply')
+ expect(items[1].text).to eq('label:~foo bar')
+ end
+
+ it 'saved recent searches are restored last on the list' do
+ set_recent_searches(project_1_local_storage_key, '["saved1", "saved2"]')
+
+ visit namespace_project_issues_path(project_1.namespace, project_1, search: 'foo')
+
+ items = all('.filtered-search-history-dropdown-item', visible: false)
+
+ expect(items.count).to eq(3)
+ expect(items[0].text).to eq('foo')
+ expect(items[1].text).to eq('saved1')
+ expect(items[2].text).to eq('saved2')
+ end
+
+ it 'searches are scoped to projects' do
+ visit namespace_project_issues_path(project_1.namespace, project_1)
+
+ input_filtered_search('foo', submit: true)
+ input_filtered_search('bar', submit: true)
+
+ visit namespace_project_issues_path(project_2.namespace, project_2)
+
+ input_filtered_search('more', submit: true)
+ input_filtered_search('things', submit: true)
+
+ items = all('.filtered-search-history-dropdown-item', visible: false)
+
+ expect(items.count).to eq(2)
+ expect(items[0].text).to eq('things')
+ expect(items[1].text).to eq('more')
+ end
+
+ it 'clicking item fills search input' do
+ set_recent_searches(project_1_local_storage_key, '["foo", "bar"]')
+ visit namespace_project_issues_path(project_1.namespace, project_1)
+
+ all('.filtered-search-history-dropdown-item', visible: false)[0].trigger('click')
+ wait_for_filtered_search('foo')
+
+ expect(find('.filtered-search').value.strip).to eq('foo')
+ end
+
+ it 'clear recent searches button, clears recent searches' do
+ set_recent_searches(project_1_local_storage_key, '["foo"]')
+ visit namespace_project_issues_path(project_1.namespace, project_1)
+
+ items_before = all('.filtered-search-history-dropdown-item', visible: false)
+
+ expect(items_before.count).to eq(1)
+
+ find('.filtered-search-history-clear-button', visible: false).trigger('click')
+ items_after = all('.filtered-search-history-dropdown-item', visible: false)
+
+ expect(items_after.count).to eq(0)
+ end
+
+ it 'shows flash error when failed to parse saved history' do
+ set_recent_searches(project_1_local_storage_key, 'fail')
+ visit namespace_project_issues_path(project_1.namespace, project_1)
+
+ expect(find('.flash-alert')).to have_text('An error occured while parsing recent searches')
+ end
+end
diff --git a/spec/features/issues/filtered_search/search_bar_spec.rb b/spec/features/issues/filtered_search/search_bar_spec.rb
index 59244d65eec..3ea95aed0a6 100644
--- a/spec/features/issues/filtered_search/search_bar_spec.rb
+++ b/spec/features/issues/filtered_search/search_bar_spec.rb
@@ -2,7 +2,6 @@ require 'rails_helper'
describe 'Search bar', js: true, feature: true do
include FilteredSearchHelpers
- include WaitForAjax
let!(:project) { create(:empty_project) }
let!(:user) { create(:user) }
@@ -26,7 +25,7 @@ describe 'Search bar', js: true, feature: true do
filtered_search.native.send_keys(:down)
page.within '#js-dropdown-hint' do
- expect(page).to have_selector('.dropdown-active')
+ expect(page).to have_selector('.droplab-item-active')
end
end
@@ -44,7 +43,7 @@ describe 'Search bar', js: true, feature: true do
filtered_search.set(search_text)
expect(filtered_search.value).to eq(search_text)
- find('.filtered-search-input-container .clear-search').click
+ find('.filtered-search-box .clear-search').click
expect(filtered_search.value).to eq('')
end
@@ -55,7 +54,7 @@ describe 'Search bar', js: true, feature: true do
it 'hides after clicked' do
filtered_search.set('a')
- find('.filtered-search-input-container .clear-search').click
+ find('.filtered-search-box .clear-search').click
expect(page).to have_css('.clear-search', visible: false)
end
@@ -79,28 +78,30 @@ describe 'Search bar', js: true, feature: true do
filtered_search.set('author')
- expect(page.all('#js-dropdown-hint .filter-dropdown .filter-dropdown-item').size).to eq(1)
+ expect(find('#js-dropdown-hint')).to have_selector('.filter-dropdown .filter-dropdown-item', count: 1)
- find('.filtered-search-input-container .clear-search').click
+ find('.filtered-search-box .clear-search').click
filtered_search.click
- expect(page.all('#js-dropdown-hint .filter-dropdown .filter-dropdown-item').size).to eq(original_size)
+ expect(find('#js-dropdown-hint')).to have_selector('.filter-dropdown .filter-dropdown-item', count: original_size)
end
it 'resets the dropdown filters' do
+ filtered_search.click
+
+ hint_offset = get_left_style(find('#js-dropdown-hint')['style'])
+
filtered_search.set('a')
- hint_style = page.find('#js-dropdown-hint')['style']
- hint_offset = get_left_style(hint_style)
filtered_search.set('author:')
- expect(page.all('#js-dropdown-hint .filter-dropdown .filter-dropdown-item').size).to eq(0)
+ find('#js-dropdown-hint', visible: false)
- find('.filtered-search-input-container .clear-search').click
+ find('.filtered-search-box .clear-search').click
filtered_search.click
- expect(page.all('#js-dropdown-hint .filter-dropdown .filter-dropdown-item').size).to be > 0
- expect(get_left_style(page.find('#js-dropdown-hint')['style'])).to eq(hint_offset)
+ expect(find('#js-dropdown-hint')).to have_selector('.filter-dropdown .filter-dropdown-item', count: 4)
+ expect(get_left_style(find('#js-dropdown-hint')['style'])).to eq(hint_offset)
end
end
end
diff --git a/spec/features/issues/form_spec.rb b/spec/features/issues/form_spec.rb
index 755992069ff..5c0907e26df 100644
--- a/spec/features/issues/form_spec.rb
+++ b/spec/features/issues/form_spec.rb
@@ -1,7 +1,9 @@
require 'rails_helper'
-describe 'New/edit issue', feature: true, js: true do
+describe 'New/edit issue', :feature, :js do
include GitlabRoutingHelper
+ include ActionView::Helpers::JavaScriptHelper
+ include WaitForAjax
let!(:project) { create(:project) }
let!(:user) { create(:user)}
@@ -9,7 +11,7 @@ describe 'New/edit issue', feature: true, js: true do
let!(:milestone) { create(:milestone, project: project) }
let!(:label) { create(:label, project: project) }
let!(:label2) { create(:label, project: project) }
- let!(:issue) { create(:issue, project: project, assignee: user, milestone: milestone) }
+ let!(:issue) { create(:issue, project: project, assignees: [user], milestone: milestone) }
before do
project.team << [user, :master]
@@ -22,23 +24,67 @@ describe 'New/edit issue', feature: true, js: true do
visit new_namespace_project_issue_path(project.namespace, project)
end
+ describe 'single assignee' do
+ before do
+ click_button 'Unassigned'
+
+ wait_for_ajax
+ end
+
+ it 'unselects other assignees when unassigned is selected' do
+ page.within '.dropdown-menu-user' do
+ click_link user2.name
+ end
+
+ click_button user2.name
+
+ page.within '.dropdown-menu-user' do
+ click_link 'Unassigned'
+ end
+
+ expect(find('input[name="issue[assignee_ids][]"]', visible: false).value).to match('0')
+ end
+
+ it 'toggles assign to me when current user is selected and unselected' do
+ page.within '.dropdown-menu-user' do
+ click_link user.name
+ end
+
+ expect(find('a', text: 'Assign to me', visible: false)).not_to be_visible
+
+ click_button user.name
+
+ page.within('.dropdown-menu-user') do
+ click_link user.name
+ end
+
+ expect(page.find('.dropdown-menu-user', visible: false)).not_to be_visible
+ end
+ end
+
it 'allows user to create new issue' do
fill_in 'issue_title', with: 'title'
fill_in 'issue_description', with: 'title'
expect(find('a', text: 'Assign to me')).to be_visible
- click_button 'Assignee'
+ click_button 'Unassigned'
+
+ wait_for_ajax
+
page.within '.dropdown-menu-user' do
click_link user2.name
end
- expect(find('input[name="issue[assignee_id]"]', visible: false).value).to match(user2.id.to_s)
+ expect(find('input[name="issue[assignee_ids][]"]', visible: false).value).to match(user2.id.to_s)
page.within '.js-assignee-search' do
expect(page).to have_content user2.name
end
expect(find('a', text: 'Assign to me')).to be_visible
click_link 'Assign to me'
- expect(find('input[name="issue[assignee_id]"]', visible: false).value).to match(user.id.to_s)
+ assignee_ids = page.all('input[name="issue[assignee_ids][]"]', visible: false)
+
+ expect(assignee_ids[0].value).to match(user.id.to_s)
+
page.within '.js-assignee-search' do
expect(page).to have_content user.name
end
@@ -68,7 +114,7 @@ describe 'New/edit issue', feature: true, js: true do
page.within '.issuable-sidebar' do
page.within '.assignee' do
- expect(page).to have_content user.name
+ expect(page).to have_content "Assignee"
end
page.within '.milestone' do
@@ -105,6 +151,25 @@ describe 'New/edit issue', feature: true, js: true do
expect(find('.js-label-select')).to have_content('Labels')
end
+
+ it 'correctly updates the selected user when changing assignee' do
+ click_button 'Unassigned'
+
+ wait_for_ajax
+
+ page.within '.dropdown-menu-user' do
+ click_link user.name
+ end
+
+ expect(find('.js-assignee-search')).to have_content(user.name)
+ click_button user.name
+
+ page.within '.dropdown-menu-user' do
+ click_link user2.name
+ end
+
+ expect(find('.js-assignee-search')).to have_content(user2.name)
+ end
end
context 'edit issue' do
@@ -113,7 +178,7 @@ describe 'New/edit issue', feature: true, js: true do
end
it 'allows user to update issue' do
- expect(find('input[name="issue[assignee_id]"]', visible: false).value).to match(user.id.to_s)
+ expect(find('input[name="issue[assignee_ids][]"]', visible: false).value).to match(user.id.to_s)
expect(find('input[name="issue[milestone_id]"]', visible: false).value).to match(milestone.id.to_s)
expect(find('a', text: 'Assign to me', visible: false)).not_to be_visible
@@ -154,4 +219,14 @@ describe 'New/edit issue', feature: true, js: true do
end
end
end
+
+ def before_for_selector(selector)
+ js = <<-JS.strip_heredoc
+ (function(selector) {
+ var el = document.querySelector(selector);
+ return window.getComputedStyle(el, '::before').getPropertyValue('content');
+ })("#{escape_javascript(selector)}")
+ JS
+ page.evaluate_script(js)
+ end
end
diff --git a/spec/features/issues/gfm_autocomplete_spec.rb b/spec/features/issues/gfm_autocomplete_spec.rb
index 7135565294b..ad29911248f 100644
--- a/spec/features/issues/gfm_autocomplete_spec.rb
+++ b/spec/features/issues/gfm_autocomplete_spec.rb
@@ -1,7 +1,6 @@
require 'rails_helper'
feature 'GFM autocomplete', feature: true, js: true do
- include WaitForAjax
let(:user) { create(:user, name: '💃speciąl someone💃', username: 'someone.special') }
let(:project) { create(:project) }
let(:label) { create(:label, project: project, title: 'special+') }
@@ -46,6 +45,33 @@ feature 'GFM autocomplete', feature: true, js: true do
expect(find('#at-view-58')).not_to have_selector('.cur:first-of-type')
end
+ it 'does not open autocomplete menu when ":" is prefixed by a number and letters' do
+ note = find('#note_note')
+
+ # Number.
+ page.within '.timeline-content-form' do
+ note.native.send_keys('7:')
+ end
+
+ expect(page).not_to have_selector('.atwho-view')
+
+ # ASCII letter.
+ page.within '.timeline-content-form' do
+ note.set('')
+ note.native.send_keys('w:')
+ end
+
+ expect(page).not_to have_selector('.atwho-view')
+
+ # Non-ASCII letter.
+ page.within '.timeline-content-form' do
+ note.set('')
+ note.native.send_keys('Ё:')
+ end
+
+ expect(page).not_to have_selector('.atwho-view')
+ end
+
it 'selects the first item for assignee dropdowns' do
page.within '.timeline-content-form' do
find('#note_note').native.send_keys('')
diff --git a/spec/features/issues/issue_sidebar_spec.rb b/spec/features/issues/issue_sidebar_spec.rb
index 7b9d4534ada..0de0f93089a 100644
--- a/spec/features/issues/issue_sidebar_spec.rb
+++ b/spec/features/issues/issue_sidebar_spec.rb
@@ -1,10 +1,10 @@
require 'rails_helper'
feature 'Issue Sidebar', feature: true do
- include WaitForAjax
include MobileHelpers
- let(:project) { create(:project, :public) }
+ let(:group) { create(:group, :nested) }
+ let(:project) { create(:project, :public, namespace: group) }
let(:issue) { create(:issue, project: project) }
let!(:user) { create(:user)}
let!(:label) { create(:label, project: project, title: 'bug') }
@@ -42,6 +42,21 @@ feature 'Issue Sidebar', feature: true do
expect(page).to have_content(user2.name)
end
end
+
+ it 'assigns yourself' do
+ find('.block.assignee .dropdown-menu-toggle').click
+
+ click_button 'assign yourself'
+
+ wait_for_ajax
+
+ find('.block.assignee .edit-link').click
+
+ page.within '.dropdown-menu-user' do
+ expect(page.find('.dropdown-header')).to be_visible
+ expect(page.find('.dropdown-menu-user-link.is-active')).to have_content(user.name)
+ end
+ end
end
context 'as a allowed user' do
@@ -56,10 +71,12 @@ feature 'Issue Sidebar', feature: true do
# Resize the window
resize_screen_sm
# Make sure the sidebar is collapsed
+ find(sidebar_selector)
expect(page).to have_css(sidebar_selector)
# Once is collapsed let's open the sidebard and reload
open_issue_sidebar
refresh
+ find(sidebar_selector)
expect(page).to have_css(sidebar_selector)
# Restore the window size as it was including the sidebar
restore_window_size
@@ -120,6 +137,20 @@ feature 'Issue Sidebar', feature: true do
end
end
+ context 'as a allowed mobile user', js: true do
+ before do
+ project.team << [user, :developer]
+ resize_screen_xs
+ visit_issue(project, issue)
+ end
+
+ context 'mobile sidebar' do
+ it 'collapses the sidebar for small screens' do
+ expect(page).not_to have_css('aside.right-sidebar.right-sidebar-collapsed')
+ end
+ end
+ end
+
context 'as a guest' do
before do
project.team << [user, :guest]
@@ -136,9 +167,7 @@ feature 'Issue Sidebar', feature: true do
end
def open_issue_sidebar
- page.within('aside.right-sidebar.right-sidebar-collapsed') do
- find('.js-sidebar-toggle').click
- sleep 1
- end
+ find('aside.right-sidebar.right-sidebar-collapsed .js-sidebar-toggle').trigger('click')
+ find('aside.right-sidebar.right-sidebar-expanded')
end
end
diff --git a/spec/features/issues/move_spec.rb b/spec/features/issues/move_spec.rb
index f89b4db9e62..6c09903a2f6 100644
--- a/spec/features/issues/move_spec.rb
+++ b/spec/features/issues/move_spec.rb
@@ -37,8 +37,8 @@ feature 'issue move to another project' do
edit_issue(issue)
end
- scenario 'moving issue to another project' do
- first('#move_to_project_id', visible: false).set(new_project.id)
+ scenario 'moving issue to another project', js: true do
+ find('#move_to_project_id', visible: false).set(new_project.id)
click_button('Save changes')
expect(current_url).to include project_path(new_project)
diff --git a/spec/features/issues/new_branch_button_spec.rb b/spec/features/issues/new_branch_button_spec.rb
deleted file mode 100644
index c0ab42c6822..00000000000
--- a/spec/features/issues/new_branch_button_spec.rb
+++ /dev/null
@@ -1,62 +0,0 @@
-require 'rails_helper'
-
-feature 'Start new branch from an issue', feature: true, js: true do
- let!(:project) { create(:project) }
- let!(:issue) { create(:issue, project: project) }
- let!(:user) { create(:user)}
-
- context "for team members" do
- before do
- project.team << [user, :master]
- login_as(user)
- end
-
- it 'shows the new branch button' do
- visit namespace_project_issue_path(project.namespace, project, issue)
-
- expect(page).to have_css('#new-branch .available')
- end
-
- context "when there is a referenced merge request" do
- let!(:note) do
- create(:note, :on_issue, :system, project: project, noteable: issue,
- note: "mentioned in #{referenced_mr.to_reference}")
- end
-
- let(:referenced_mr) do
- create(:merge_request, :simple, source_project: project, target_project: project,
- description: "Fixes #{issue.to_reference}", author: user)
- end
-
- before do
- referenced_mr.cache_merge_request_closes_issues!(user)
-
- visit namespace_project_issue_path(project.namespace, project, issue)
- end
-
- it "hides the new branch button" do
- expect(page).to have_css('#new-branch .unavailable')
- expect(page).not_to have_css('#new-branch .available')
- expect(page).to have_content /1 Related Merge Request/
- end
- end
-
- context 'when issue is confidential' do
- it 'hides the new branch button' do
- issue = create(:issue, :confidential, project: project)
-
- visit namespace_project_issue_path(project.namespace, project, issue)
-
- expect(page).not_to have_css('#new-branch')
- end
- end
- end
-
- context 'for visitors' do
- it 'shows no buttons' do
- visit namespace_project_issue_path(project.namespace, project, issue)
-
- expect(page).not_to have_css('#new-branch')
- end
- end
-end
diff --git a/spec/features/issues/note_polling_spec.rb b/spec/features/issues/note_polling_spec.rb
index f5cfe2d666e..80f57906506 100644
--- a/spec/features/issues/note_polling_spec.rb
+++ b/spec/features/issues/note_polling_spec.rb
@@ -1,19 +1,131 @@
require 'spec_helper'
-feature 'Issue notes polling' do
- let!(:project) { create(:project, :public) }
- let!(:issue) { create(:issue, project: project) }
+feature 'Issue notes polling', :feature, :js do
+ let(:project) { create(:empty_project, :public) }
+ let(:issue) { create(:issue, project: project) }
- background do
- visit namespace_project_issue_path(project.namespace, project, issue)
+ describe 'creates' do
+ before do
+ visit namespace_project_issue_path(project.namespace, project, issue)
+ end
+
+ it 'displays the new comment' do
+ note = create(:note, noteable: issue, project: project, note: 'Looks good!')
+ page.execute_script('notes.refresh();')
+
+ expect(page).to have_selector("#note_#{note.id}", text: 'Looks good!')
+ end
end
- scenario 'Another user adds a comment to an issue', js: true do
- note = create(:note, noteable: issue, project: project,
- note: 'Looks good!')
+ describe 'updates' do
+ context 'when from own user' do
+ let(:user) { create(:user) }
+ let(:note_text) { "Hello World" }
+ let(:updated_text) { "Bye World" }
+ let!(:existing_note) { create(:note, noteable: issue, project: project, author: user, note: note_text) }
- page.execute_script('notes.refresh();')
+ before do
+ login_as(user)
+ visit namespace_project_issue_path(project.namespace, project, issue)
+ end
+
+ it 'has .original-note-content to compare against' do
+ expect(page).to have_selector("#note_#{existing_note.id}", text: note_text)
+ expect(page).to have_selector("#note_#{existing_note.id} .original-note-content", count: 1, visible: false)
+
+ update_note(existing_note, updated_text)
+
+ expect(page).to have_selector("#note_#{existing_note.id}", text: updated_text)
+ expect(page).to have_selector("#note_#{existing_note.id} .original-note-content", count: 1, visible: false)
+ end
+
+ it 'displays the updated content' do
+ expect(page).to have_selector("#note_#{existing_note.id}", text: note_text)
+
+ update_note(existing_note, updated_text)
+
+ expect(page).to have_selector("#note_#{existing_note.id}", text: updated_text)
+ end
+
+ it 'when editing but have not changed anything, and an update comes in, show the updated content in the textarea' do
+ find("#note_#{existing_note.id} .js-note-edit").click
+
+ expect(page).to have_field("note[note]", with: note_text)
+
+ update_note(existing_note, updated_text)
+
+ expect(page).to have_field("note[note]", with: updated_text)
+ end
+
+ it 'when editing but you changed some things, and an update comes in, show a warning' do
+ find("#note_#{existing_note.id} .js-note-edit").click
+
+ expect(page).to have_field("note[note]", with: note_text)
+
+ find("#note_#{existing_note.id} .js-note-text").set('something random')
+
+ update_note(existing_note, updated_text)
- expect(page).to have_selector("#note_#{note.id}", text: 'Looks good!')
+ expect(page).to have_selector(".alert")
+ end
+
+ it 'when editing but you changed some things, an update comes in, and you press cancel, show the updated content' do
+ find("#note_#{existing_note.id} .js-note-edit").click
+
+ expect(page).to have_field("note[note]", with: note_text)
+
+ find("#note_#{existing_note.id} .js-note-text").set('something random')
+
+ update_note(existing_note, updated_text)
+
+ find("#note_#{existing_note.id} .note-edit-cancel").click
+
+ expect(page).to have_selector("#note_#{existing_note.id}", text: updated_text)
+ end
+ end
+
+ context 'when from another user' do
+ let(:user1) { create(:user) }
+ let(:user2) { create(:user) }
+ let(:note_text) { "Hello World" }
+ let(:updated_text) { "Bye World" }
+ let!(:existing_note) { create(:note, noteable: issue, project: project, author: user1, note: note_text) }
+
+ before do
+ login_as(user2)
+ visit namespace_project_issue_path(project.namespace, project, issue)
+ end
+
+ it 'has .original-note-content to compare against' do
+ expect(page).to have_selector("#note_#{existing_note.id}", text: note_text)
+ expect(page).to have_selector("#note_#{existing_note.id} .original-note-content", count: 1, visible: false)
+
+ update_note(existing_note, updated_text)
+
+ expect(page).to have_selector("#note_#{existing_note.id}", text: updated_text)
+ expect(page).to have_selector("#note_#{existing_note.id} .original-note-content", count: 1, visible: false)
+ end
+ end
+
+ context 'system notes' do
+ let(:user) { create(:user) }
+ let(:note_text) { "Some system note" }
+ let!(:system_note) { create(:system_note, noteable: issue, project: project, author: user, note: note_text) }
+
+ before do
+ login_as(user)
+ visit namespace_project_issue_path(project.namespace, project, issue)
+ end
+
+ it 'has .original-note-content to compare against' do
+ expect(page).to have_selector("#note_#{system_note.id}", text: note_text)
+ expect(page).to have_selector("#note_#{system_note.id} .original-note-content", count: 1, visible: false)
+ end
+ end
+ end
+
+ def update_note(note, new_text)
+ note.update(note: new_text)
+ page.execute_script('notes.refresh();')
end
end
diff --git a/spec/features/issues/notes_on_issues_spec.rb b/spec/features/issues/notes_on_issues_spec.rb
new file mode 100644
index 00000000000..a4035324d2b
--- /dev/null
+++ b/spec/features/issues/notes_on_issues_spec.rb
@@ -0,0 +1,77 @@
+require 'spec_helper'
+
+describe 'Create notes on issues', :js, :feature do
+ let(:user) { create(:user) }
+
+ shared_examples 'notes with reference' do
+ let(:issue) { create(:issue, project: project) }
+ let(:note_text) { "Check #{mention.to_reference}" }
+
+ before do
+ project.team << [user, :developer]
+ login_as(user)
+ visit namespace_project_issue_path(project.namespace, project, issue)
+
+ fill_in 'note[note]', with: note_text
+ click_button 'Comment'
+
+ wait_for_ajax
+ end
+
+ it 'creates a note with reference and cross references the issue' do
+ page.within('div#notes li.note div.note-text') do
+ expect(page).to have_content(note_text)
+ expect(page.find('a')).to have_content(mention.to_reference)
+ end
+
+ find('div#notes li.note div.note-text a').click
+
+ page.within('div#notes li.note .system-note-message') do
+ expect(page).to have_content('mentioned in issue')
+ expect(page.find('a')).to have_content(issue.to_reference)
+ end
+ end
+ end
+
+ context 'mentioning issue on a private project' do
+ it_behaves_like 'notes with reference' do
+ let(:project) { create(:project, :private) }
+ let(:mention) { create(:issue, project: project) }
+ end
+ end
+
+ context 'mentioning issue on an internal project' do
+ it_behaves_like 'notes with reference' do
+ let(:project) { create(:project, :internal) }
+ let(:mention) { create(:issue, project: project) }
+ end
+ end
+
+ context 'mentioning issue on a public project' do
+ it_behaves_like 'notes with reference' do
+ let(:project) { create(:project, :public) }
+ let(:mention) { create(:issue, project: project) }
+ end
+ end
+
+ context 'mentioning merge request on a private project' do
+ it_behaves_like 'notes with reference' do
+ let(:project) { create(:project, :private) }
+ let(:mention) { create(:merge_request, source_project: project) }
+ end
+ end
+
+ context 'mentioning merge request on an internal project' do
+ it_behaves_like 'notes with reference' do
+ let(:project) { create(:project, :internal) }
+ let(:mention) { create(:merge_request, source_project: project) }
+ end
+ end
+
+ context 'mentioning merge request on a public project' do
+ it_behaves_like 'notes with reference' do
+ let(:project) { create(:project, :public) }
+ let(:mention) { create(:merge_request, source_project: project) }
+ end
+ end
+end
diff --git a/spec/features/issues/spam_issues_spec.rb b/spec/features/issues/spam_issues_spec.rb
index 4bc9b49f889..6001476d0ca 100644
--- a/spec/features/issues/spam_issues_spec.rb
+++ b/spec/features/issues/spam_issues_spec.rb
@@ -1,6 +1,6 @@
require 'rails_helper'
-describe 'New issue', feature: true do
+describe 'New issue', feature: true, js: true do
include StubENV
let(:project) { create(:project, :public) }
diff --git a/spec/features/issues/update_issues_spec.rb b/spec/features/issues/update_issues_spec.rb
index ae5da3877a8..b250fa2ed3c 100644
--- a/spec/features/issues/update_issues_spec.rb
+++ b/spec/features/issues/update_issues_spec.rb
@@ -1,8 +1,6 @@
require 'rails_helper'
feature 'Multiple issue updating from issues#index', feature: true do
- include WaitForAjax
-
let!(:project) { create(:project) }
let!(:issue) { create(:issue, project: project) }
let!(:user) { create(:user)}
@@ -101,7 +99,7 @@ feature 'Multiple issue updating from issues#index', feature: true do
end
def create_assigned
- create(:issue, project: project, assignee: user)
+ create(:issue, project: project, assignees: [user])
end
def create_with_milestone
diff --git a/spec/features/issues/user_uses_slash_commands_spec.rb b/spec/features/issues/user_uses_slash_commands_spec.rb
index 0a9cd11ad6e..4cd6c1171ac 100644
--- a/spec/features/issues/user_uses_slash_commands_spec.rb
+++ b/spec/features/issues/user_uses_slash_commands_spec.rb
@@ -2,7 +2,6 @@ require 'rails_helper'
feature 'Issues > User uses slash commands', feature: true, js: true do
include SlashCommandsHelpers
- include WaitForAjax
it_behaves_like 'issuable record that supports slash commands in its description and notes', :issue do
let(:issuable) { create(:issue, project: project) }
diff --git a/spec/features/issues_spec.rb b/spec/features/issues_spec.rb
index 7afceb88cf9..06ed2dbac64 100644
--- a/spec/features/issues_spec.rb
+++ b/spec/features/issues_spec.rb
@@ -4,29 +4,21 @@ describe 'Issues', feature: true do
include DropzoneHelper
include IssueHelpers
include SortingHelper
- include WaitForAjax
- let(:project) { create(:project, :public) }
+ let(:project) { create(:empty_project, :public) }
before do
login_as :user
user2 = create(:user)
project.team << [[@user, user2], :developer]
-
- project.repository.create_file(
- @user,
- '.gitlab/issue_templates/bug.md',
- 'this is a test "bug" template',
- message: 'added issue template',
- branch_name: 'master')
end
describe 'Edit issue' do
let!(:issue) do
create(:issue,
author: @user,
- assignee: @user,
+ assignees: [@user],
project: project)
end
@@ -38,20 +30,13 @@ describe 'Issues', feature: true do
it 'opens new issue popup' do
expect(page).to have_content("Issue ##{issue.iid}")
end
-
- describe 'fill in' do
- before do
- fill_in 'issue_title', with: 'bug 345'
- fill_in 'issue_description', with: 'bug description'
- end
- end
end
describe 'Editing issue assignee' do
let!(:issue) do
create(:issue,
author: @user,
- assignee: @user,
+ assignees: [@user],
project: project)
end
@@ -69,7 +54,7 @@ describe 'Issues', feature: true do
expect(page).to have_content 'No assignee - assign yourself'
end
- expect(issue.reload.assignee).to be_nil
+ expect(issue.reload.assignees).to be_empty
end
end
@@ -146,7 +131,7 @@ describe 'Issues', feature: true do
describe 'Issue info' do
it 'excludes award_emoji from comment count' do
- issue = create(:issue, author: @user, assignee: @user, project: project, title: 'foobar')
+ issue = create(:issue, author: @user, assignees: [@user], project: project, title: 'foobar')
create(:award_emoji, awardable: issue)
visit namespace_project_issues_path(project.namespace, project, assignee_id: @user.id)
@@ -161,14 +146,14 @@ describe 'Issues', feature: true do
%w(foobar barbaz gitlab).each do |title|
create(:issue,
author: @user,
- assignee: @user,
+ assignees: [@user],
project: project,
title: title)
end
@issue = Issue.find_by(title: 'foobar')
@issue.milestone = create(:milestone, project: project)
- @issue.assignee = nil
+ @issue.assignees = []
@issue.save
end
@@ -359,9 +344,9 @@ describe 'Issues', feature: true do
let(:user2) { create(:user) }
before do
- foo.assignee = user2
+ foo.assignees << user2
foo.save
- bar.assignee = user2
+ bar.assignees << user2
bar.save
end
@@ -378,7 +363,7 @@ describe 'Issues', feature: true do
end
describe 'when I want to reset my incoming email token' do
- let(:project1) { create(:project, namespace: @user.namespace) }
+ let(:project1) { create(:empty_project, namespace: @user.namespace) }
let!(:issue) { create(:issue, project: project1) }
before do
@@ -404,7 +389,7 @@ describe 'Issues', feature: true do
end
describe 'update labels from issue#show', js: true do
- let(:issue) { create(:issue, project: project, author: @user, assignee: @user) }
+ let(:issue) { create(:issue, project: project, author: @user, assignees: [@user]) }
let!(:label) { create(:label, project: project) }
before do
@@ -414,7 +399,8 @@ describe 'Issues', feature: true do
it 'will not send ajax request when no data is changed' do
page.within '.labels' do
click_link 'Edit'
- first('.dropdown-menu-close').click
+
+ find('.dropdown-menu-close', match: :first).click
expect(page).not_to have_selector('.block-loading')
end
@@ -422,7 +408,7 @@ describe 'Issues', feature: true do
end
describe 'update assignee from issue#show' do
- let(:issue) { create(:issue, project: project, author: @user, assignee: @user) }
+ let(:issue) { create(:issue, project: project, author: @user, assignees: [@user]) }
context 'by authorized user' do
it 'allows user to select unassigned', js: true do
@@ -433,10 +419,14 @@ describe 'Issues', feature: true do
click_link 'Edit'
click_link 'Unassigned'
+ first('.title').click
expect(page).to have_content 'No assignee'
end
- expect(issue.reload.assignee).to be_nil
+ # wait_for_ajax does not work with vue-resource at the moment
+ sleep 1
+
+ expect(issue.reload.assignees).to be_empty
end
it 'allows user to select an assignee', js: true do
@@ -468,14 +458,14 @@ describe 'Issues', feature: true do
click_link 'Edit'
click_link @user.name
- page.within '.value' do
+ page.within '.value .author' do
expect(page).to have_content @user.name
end
click_link 'Edit'
click_link @user.name
- page.within '.value' do
+ page.within '.value .assign-yourself' do
expect(page).to have_content "No assignee"
end
end
@@ -494,7 +484,7 @@ describe 'Issues', feature: true do
login_with guest
visit namespace_project_issue_path(project.namespace, project, issue)
- expect(page).to have_content issue.assignee.name
+ expect(page).to have_content issue.assignees.first.name
end
end
end
@@ -560,18 +550,11 @@ describe 'Issues', feature: true do
expect(page).to have_content milestone.title
end
end
-
- describe 'removing assignee' do
- let(:user2) { create(:user) }
-
- before do
- issue.assignee = user2
- issue.save
- end
- end
end
describe 'new issue' do
+ let!(:issue) { create(:issue, project: project) }
+
context 'by unauthenticated user' do
before do
logout
@@ -601,15 +584,24 @@ describe 'Issues', feature: true do
expect(page.find_field("issue_description").value).to have_content 'banana_sample'
end
- it 'adds double newline to end of attachment markdown' do
+ it "doesn't add double newline to end of a single attachment markdown" do
dropzone_file Rails.root.join('spec', 'fixtures', 'banana_sample.gif')
- expect(page.find_field("issue_description").value).to match /\n\n$/
+ expect(page.find_field("issue_description").value).not_to match /\n\n$/
end
end
context 'form filled by URL parameters' do
+ let(:project) { create(:project, :public, :repository) }
+
before do
+ project.repository.create_file(
+ @user,
+ '.gitlab/issue_templates/bug.md',
+ 'this is a test "bug" template',
+ message: 'added issue template',
+ branch_name: 'master')
+
visit new_namespace_project_issue_path(project.namespace, project, issuable_template: 'bug')
end
@@ -653,7 +645,7 @@ describe 'Issues', feature: true do
describe 'due date' do
context 'update due on issue#show', js: true do
- let(:issue) { create(:issue, project: project, author: @user, assignee: @user) }
+ let(:issue) { create(:issue, project: project, author: @user, assignees: [@user]) }
before do
visit namespace_project_issue_path(project.namespace, project, issue)
@@ -695,4 +687,21 @@ describe 'Issues', feature: true do
end
end
end
+
+ describe 'title issue#show', js: true do
+ include WaitForVueResource
+
+ it 'updates the title', js: true do
+ issue = create(:issue, author: @user, assignees: [@user], project: project, title: 'new title')
+
+ visit namespace_project_issue_path(project.namespace, project, issue)
+
+ expect(page).to have_text("new title")
+
+ issue.update(title: "updated title")
+
+ wait_for_vue_resource
+ expect(page).to have_text("updated title")
+ end
+ end
end
diff --git a/spec/features/login_spec.rb b/spec/features/login_spec.rb
index f32d1f78b40..c82e8c03343 100644
--- a/spec/features/login_spec.rb
+++ b/spec/features/login_spec.rb
@@ -41,7 +41,7 @@ feature 'Login', feature: true do
expect(page).to have_content('Your account has been blocked.')
end
- it 'does not update Devise trackable attributes' do
+ it 'does not update Devise trackable attributes', :redis do
user = create(:user, :blocked)
expect { login_with(user) }.not_to change { user.reload.sign_in_count }
@@ -55,7 +55,7 @@ feature 'Login', feature: true do
expect(page).to have_content('Invalid Login or password.')
end
- it 'does not update Devise trackable attributes' do
+ it 'does not update Devise trackable attributes', :redis do
expect { login_with(User.ghost) }.not_to change { User.ghost.reload.sign_in_count }
end
end
@@ -199,52 +199,125 @@ feature 'Login', feature: true do
describe 'with required two-factor authentication enabled' do
let(:user) { create(:user) }
- before(:each) { stub_application_setting(require_two_factor_authentication: true) }
+ # TODO: otp_grace_period_started_at
- context 'with grace period defined' do
- before(:each) do
- stub_application_setting(two_factor_grace_period: 48)
- login_with(user)
- end
+ context 'global setting' do
+ before(:each) { stub_application_setting(require_two_factor_authentication: true) }
- context 'within the grace period' do
- it 'redirects to two-factor configuration page' do
- expect(current_path).to eq profile_two_factor_auth_path
- expect(page).to have_content('You must enable Two-Factor Authentication for your account before')
+ context 'with grace period defined' do
+ before(:each) do
+ stub_application_setting(two_factor_grace_period: 48)
+ login_with(user)
end
- it 'allows skipping two-factor configuration', js: true do
- expect(current_path).to eq profile_two_factor_auth_path
+ context 'within the grace period' do
+ it 'redirects to two-factor configuration page' do
+ expect(current_path).to eq profile_two_factor_auth_path
+ expect(page).to have_content('The global settings require you to enable Two-Factor Authentication for your account. You need to do this before ')
+ end
- click_link 'Configure it later'
- expect(current_path).to eq root_path
+ it 'allows skipping two-factor configuration', js: true do
+ expect(current_path).to eq profile_two_factor_auth_path
+
+ click_link 'Configure it later'
+ expect(current_path).to eq root_path
+ end
end
- end
- context 'after the grace period' do
- let(:user) { create(:user, otp_grace_period_started_at: 9999.hours.ago) }
+ context 'after the grace period' do
+ let(:user) { create(:user, otp_grace_period_started_at: 9999.hours.ago) }
- it 'redirects to two-factor configuration page' do
- expect(current_path).to eq profile_two_factor_auth_path
- expect(page).to have_content('You must enable Two-Factor Authentication for your account.')
+ it 'redirects to two-factor configuration page' do
+ expect(current_path).to eq profile_two_factor_auth_path
+ expect(page).to have_content(
+ 'The global settings require you to enable Two-Factor Authentication for your account.'
+ )
+ end
+
+ it 'disallows skipping two-factor configuration', js: true do
+ expect(current_path).to eq profile_two_factor_auth_path
+ expect(page).not_to have_link('Configure it later')
+ end
+ end
+ end
+
+ context 'without grace period defined' do
+ before(:each) do
+ stub_application_setting(two_factor_grace_period: 0)
+ login_with(user)
end
- it 'disallows skipping two-factor configuration', js: true do
+ it 'redirects to two-factor configuration page' do
expect(current_path).to eq profile_two_factor_auth_path
- expect(page).not_to have_link('Configure it later')
+ expect(page).to have_content(
+ 'The global settings require you to enable Two-Factor Authentication for your account.'
+ )
end
end
end
- context 'without grace period defined' do
- before(:each) do
- stub_application_setting(two_factor_grace_period: 0)
- login_with(user)
+ context 'group setting' do
+ before do
+ group1 = create :group, name: 'Group 1', require_two_factor_authentication: true
+ group1.add_user(user, GroupMember::DEVELOPER)
+ group2 = create :group, name: 'Group 2', require_two_factor_authentication: true
+ group2.add_user(user, GroupMember::DEVELOPER)
end
- it 'redirects to two-factor configuration page' do
- expect(current_path).to eq profile_two_factor_auth_path
- expect(page).to have_content('You must enable Two-Factor Authentication for your account.')
+ context 'with grace period defined' do
+ before(:each) do
+ stub_application_setting(two_factor_grace_period: 48)
+ login_with(user)
+ end
+
+ context 'within the grace period' do
+ it 'redirects to two-factor configuration page' do
+ expect(current_path).to eq profile_two_factor_auth_path
+ expect(page).to have_content(
+ 'The group settings for Group 1 and Group 2 require you to enable ' \
+ 'Two-Factor Authentication for your account. You need to do this ' \
+ 'before ')
+ end
+
+ it 'allows skipping two-factor configuration', js: true do
+ expect(current_path).to eq profile_two_factor_auth_path
+
+ click_link 'Configure it later'
+ expect(current_path).to eq root_path
+ end
+ end
+
+ context 'after the grace period' do
+ let(:user) { create(:user, otp_grace_period_started_at: 9999.hours.ago) }
+
+ it 'redirects to two-factor configuration page' do
+ expect(current_path).to eq profile_two_factor_auth_path
+ expect(page).to have_content(
+ 'The group settings for Group 1 and Group 2 require you to enable ' \
+ 'Two-Factor Authentication for your account.'
+ )
+ end
+
+ it 'disallows skipping two-factor configuration', js: true do
+ expect(current_path).to eq profile_two_factor_auth_path
+ expect(page).not_to have_link('Configure it later')
+ end
+ end
+ end
+
+ context 'without grace period defined' do
+ before(:each) do
+ stub_application_setting(two_factor_grace_period: 0)
+ login_with(user)
+ end
+
+ it 'redirects to two-factor configuration page' do
+ expect(current_path).to eq profile_two_factor_auth_path
+ expect(page).to have_content(
+ 'The group settings for Group 1 and Group 2 require you to enable ' \
+ 'Two-Factor Authentication for your account.'
+ )
+ end
end
end
end
diff --git a/spec/features/markdown_spec.rb b/spec/features/markdown_spec.rb
index 894df13a2dc..ba930de937d 100644
--- a/spec/features/markdown_spec.rb
+++ b/spec/features/markdown_spec.rb
@@ -26,7 +26,7 @@ require 'erb'
describe 'GitLab Markdown', feature: true do
include Capybara::Node::Matchers
- include GitlabMarkdownHelper
+ include MarkupHelper
include MarkdownMatchers
# Sometimes it can be useful to see the parsed output of the Markdown document
diff --git a/spec/features/merge_requests/assign_issues_spec.rb b/spec/features/merge_requests/assign_issues_spec.rb
index 43cc6f2a2a7..b306e2f5f75 100644
--- a/spec/features/merge_requests/assign_issues_spec.rb
+++ b/spec/features/merge_requests/assign_issues_spec.rb
@@ -18,7 +18,7 @@ feature 'Merge request issue assignment', js: true, feature: true do
end
context 'logged in as author' do
- scenario 'updates related issues' do
+ it 'updates related issues' do
visit_merge_request
click_link "Assign yourself to these issues"
@@ -33,7 +33,7 @@ feature 'Merge request issue assignment', js: true, feature: true do
end
it "doesn't display if related issues are already assigned" do
- [issue1, issue2].each { |issue| issue.update!(assignee: user) }
+ [issue1, issue2].each { |issue| issue.update!(assignees: [user]) }
visit_merge_request
diff --git a/spec/features/merge_requests/check_if_mergeable_with_unresolved_discussions_spec.rb b/spec/features/merge_requests/check_if_mergeable_with_unresolved_discussions_spec.rb
index 7f11db3c417..fa306c02a43 100644
--- a/spec/features/merge_requests/check_if_mergeable_with_unresolved_discussions_spec.rb
+++ b/spec/features/merge_requests/check_if_mergeable_with_unresolved_discussions_spec.rb
@@ -19,8 +19,8 @@ feature 'Check if mergeable with unresolved discussions', js: true, feature: tru
it 'does not allow to merge' do
visit_merge_request(merge_request)
- expect(page).not_to have_button 'Accept Merge Request'
- expect(page).to have_content('This merge request has unresolved discussions')
+ expect(page).not_to have_button 'Merge'
+ expect(page).to have_content('There are unresolved discussions.')
end
end
@@ -32,7 +32,7 @@ feature 'Check if mergeable with unresolved discussions', js: true, feature: tru
it 'allows MR to be merged' do
visit_merge_request(merge_request)
- expect(page).to have_button 'Accept Merge Request'
+ expect(page).to have_button 'Merge'
end
end
end
@@ -46,7 +46,7 @@ feature 'Check if mergeable with unresolved discussions', js: true, feature: tru
it 'does not allow to merge' do
visit_merge_request(merge_request)
- expect(page).to have_button 'Accept Merge Request'
+ expect(page).to have_button 'Merge'
end
end
@@ -58,7 +58,7 @@ feature 'Check if mergeable with unresolved discussions', js: true, feature: tru
it 'allows MR to be merged' do
visit_merge_request(merge_request)
- expect(page).to have_button 'Accept Merge Request'
+ expect(page).to have_button 'Merge'
end
end
end
diff --git a/spec/features/merge_requests/cherry_pick_spec.rb b/spec/features/merge_requests/cherry_pick_spec.rb
index dfe7c910a10..6ba681e36f7 100644
--- a/spec/features/merge_requests/cherry_pick_spec.rb
+++ b/spec/features/merge_requests/cherry_pick_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe 'Cherry-pick Merge Requests' do
+describe 'Cherry-pick Merge Requests', js: true do
let(:user) { create(:user) }
let(:group) { create(:group) }
let(:project) { create(:project, namespace: group) }
diff --git a/spec/features/merge_requests/closes_issues_spec.rb b/spec/features/merge_requests/closes_issues_spec.rb
index eafcab6a0d7..ee0880a1e2f 100644
--- a/spec/features/merge_requests/closes_issues_spec.rb
+++ b/spec/features/merge_requests/closes_issues_spec.rb
@@ -1,6 +1,8 @@
require 'spec_helper'
-feature 'Merge Request closing issues message', feature: true do
+feature 'Merge Request closing issues message', feature: true, js: true do
+ include WaitForAjax
+
let(:user) { create(:user) }
let(:project) { create(:project, :public) }
let(:issue_1) { create(:issue, project: project)}
@@ -23,6 +25,7 @@ feature 'Merge Request closing issues message', feature: true do
login_as user
visit namespace_project_merge_request_path(project.namespace, project, merge_request)
+ wait_for_ajax
end
context 'not closing or mentioning any issue' do
@@ -35,7 +38,7 @@ feature 'Merge Request closing issues message', feature: true do
let(:merge_request_description) { "Description\n\nclosing #{issue_1.to_reference}, #{issue_2.to_reference}" }
it 'does not display closing issue message' do
- expect(page).to have_content("Accepting this merge request will close issues #{issue_1.to_reference} and #{issue_2.to_reference}")
+ expect(page).to have_content("Closes issues #{issue_1.to_reference} and #{issue_2.to_reference}")
end
end
@@ -51,7 +54,8 @@ feature 'Merge Request closing issues message', feature: true do
let(:merge_request_title) { "closes #{issue_1.to_reference}\n\n refers to #{issue_2.to_reference}" }
it 'does not display closing issue message' do
- expect(page).to have_content("Accepting this merge request will close issue #{issue_1.to_reference}. Issue #{issue_2.to_reference} is mentioned but will not be closed.")
+ expect(page).to have_content("Closes issue #{issue_1.to_reference}.")
+ expect(page).to have_content("Issue #{issue_2.to_reference} is mentioned but will not be closed.")
end
end
@@ -59,7 +63,7 @@ feature 'Merge Request closing issues message', feature: true do
let(:merge_request_title) { "closing #{issue_1.to_reference}, #{issue_2.to_reference}" }
it 'does not display closing issue message' do
- expect(page).to have_content("Accepting this merge request will close issues #{issue_1.to_reference} and #{issue_2.to_reference}")
+ expect(page).to have_content("Closes issues #{issue_1.to_reference} and #{issue_2.to_reference}")
end
end
@@ -75,7 +79,8 @@ feature 'Merge Request closing issues message', feature: true do
let(:merge_request_title) { "closes #{issue_1.to_reference}\n\n refers to #{issue_2.to_reference}" }
it 'does not display closing issue message' do
- expect(page).to have_content("Accepting this merge request will close issue #{issue_1.to_reference}. Issue #{issue_2.to_reference} is mentioned but will not be closed.")
+ expect(page).to have_content("Closes issue #{issue_1.to_reference}. Issue #{issue_2.to_reference} is mentioned but will not be closed.")
+ expect(page).to have_content("Issue #{issue_2.to_reference} is mentioned but will not be closed.")
end
end
end
diff --git a/spec/features/merge_requests/conflicts_spec.rb b/spec/features/merge_requests/conflicts_spec.rb
index 18508a44184..04b7593ce68 100644
--- a/spec/features/merge_requests/conflicts_spec.rb
+++ b/spec/features/merge_requests/conflicts_spec.rb
@@ -1,8 +1,6 @@
require 'spec_helper'
feature 'Merge request conflict resolution', js: true, feature: true do
- include WaitForAjax
-
let(:user) { create(:user) }
let(:project) { create(:project) }
@@ -153,7 +151,7 @@ feature 'Merge request conflict resolution', js: true, feature: true do
'conflict-too-large' => 'when the conflicts contain a large file',
'conflict-binary-file' => 'when the conflicts contain a binary file',
'conflict-missing-side' => 'when the conflicts contain a file edited in one branch and deleted in another',
- 'conflict-non-utf8' => 'when the conflicts contain a non-UTF-8 file',
+ 'conflict-non-utf8' => 'when the conflicts contain a non-UTF-8 file'
}.freeze
UNRESOLVABLE_CONFLICTS.each do |source_branch, description|
diff --git a/spec/features/merge_requests/create_new_mr_spec.rb b/spec/features/merge_requests/create_new_mr_spec.rb
index f1ad4a55246..f1b3e7f158c 100644
--- a/spec/features/merge_requests/create_new_mr_spec.rb
+++ b/spec/features/merge_requests/create_new_mr_spec.rb
@@ -15,26 +15,26 @@ feature 'Create New Merge Request', feature: true, js: true do
it 'selects the source branch sha when a tag with the same name exists' do
visit namespace_project_merge_requests_path(project.namespace, project)
- click_link 'New Merge Request'
+ click_link 'New merge request'
expect(page).to have_content('Source branch')
expect(page).to have_content('Target branch')
first('.js-source-branch').click
- first('.dropdown-source-branch .dropdown-content a', text: 'v1.1.0').click
+ find('.dropdown-source-branch .dropdown-content a', match: :first).click
expect(page).to have_content "b83d6e3"
end
it 'selects the target branch sha when a tag with the same name exists' do
visit namespace_project_merge_requests_path(project.namespace, project)
-
- click_link 'New Merge Request'
+
+ click_link 'New merge request'
expect(page).to have_content('Source branch')
expect(page).to have_content('Target branch')
first('.js-target-branch').click
- first('.dropdown-target-branch .dropdown-content a', text: 'v1.1.0').click
+ find('.dropdown-target-branch .dropdown-content a', text: 'v1.1.0', match: :first).click
expect(page).to have_content "b83d6e3"
end
@@ -42,12 +42,12 @@ feature 'Create New Merge Request', feature: true, js: true do
it 'generates a diff for an orphaned branch' do
visit namespace_project_merge_requests_path(project.namespace, project)
- click_link 'New Merge Request'
+ page.has_link?('New Merge Request') ? click_link("New Merge Request") : click_link('New merge request')
expect(page).to have_content('Source branch')
expect(page).to have_content('Target branch')
- first('.js-source-branch').click
- first('.dropdown-source-branch .dropdown-content a', text: 'orphaned-branch').click
+ find('.js-source-branch', match: :first).click
+ find('.dropdown-source-branch .dropdown-content a', text: 'orphaned-branch', match: :first).click
click_button "Compare branches"
click_link "Changes"
@@ -70,6 +70,18 @@ feature 'Create New Merge Request', feature: true, js: true do
visit new_namespace_project_merge_request_path(project.namespace, project, merge_request: { target_project_id: private_project.id })
expect(page).not_to have_content private_project.path_with_namespace
+ expect(page).to have_content project.path_with_namespace
+ end
+ end
+
+ context 'when source project cannot be viewed by the current user' do
+ it 'does not leak the private project name & namespace' do
+ private_project = create(:project, :private)
+
+ visit new_namespace_project_merge_request_path(project.namespace, project, merge_request: { source_project_id: private_project.id })
+
+ expect(page).not_to have_content private_project.path_with_namespace
+ expect(page).to have_content project.path_with_namespace
end
end
diff --git a/spec/features/merge_requests/created_from_fork_spec.rb b/spec/features/merge_requests/created_from_fork_spec.rb
index 18833ba7266..bf34c99b92a 100644
--- a/spec/features/merge_requests/created_from_fork_spec.rb
+++ b/spec/features/merge_requests/created_from_fork_spec.rb
@@ -31,7 +31,7 @@ feature 'Merge request created from fork' do
fork_project.destroy!
end
- scenario 'user can access merge request' do
+ scenario 'user can access merge request', js: true do
visit_merge_request(merge_request)
expect(page).to have_content 'Test merge request'
diff --git a/spec/features/merge_requests/deleted_source_branch_spec.rb b/spec/features/merge_requests/deleted_source_branch_spec.rb
index 0952b17b63e..01e5e4f3a05 100644
--- a/spec/features/merge_requests/deleted_source_branch_spec.rb
+++ b/spec/features/merge_requests/deleted_source_branch_spec.rb
@@ -4,8 +4,6 @@ require 'spec_helper'
# message to be shown by JavaScript when the source branch was deleted.
# Please do not remove "js: true".
describe 'Deleted source branch', feature: true, js: true do
- include WaitForAjax
-
let(:user) { create(:user) }
let(:merge_request) { create(:merge_request) }
@@ -22,7 +20,7 @@ describe 'Deleted source branch', feature: true, js: true do
it 'shows a message about missing source branch' do
expect(page).to have_content(
- 'Source branch this-branch-does-not-exist does not exist'
+ 'Source branch does not exist.'
)
end
@@ -37,6 +35,6 @@ describe 'Deleted source branch', feature: true, js: true do
wait_for_ajax
expect(page).to have_selector('.diffs.tab-pane .nothing-here-block')
- expect(page).to have_content('Nothing to merge from this-branch-does-not-exist into feature')
+ expect(page).to have_content('Source branch does not exist.')
end
end
diff --git a/spec/features/merge_requests/diff_notes_avatars_spec.rb b/spec/features/merge_requests/diff_notes_avatars_spec.rb
index a6c72b0b3ac..ccf047d3efa 100644
--- a/spec/features/merge_requests/diff_notes_avatars_spec.rb
+++ b/spec/features/merge_requests/diff_notes_avatars_spec.rb
@@ -1,8 +1,6 @@
require 'spec_helper'
feature 'Diff note avatars', feature: true, js: true do
- include WaitForAjax
-
let(:user) { create(:user) }
let(:project) { create(:project, :public) }
let(:merge_request) { create(:merge_request_with_diffs, source_project: project, author: user, title: "Bug NS-04") }
@@ -93,7 +91,7 @@ feature 'Diff note avatars', feature: true, js: true do
page.within find("[id='#{position.line_code(project.repository)}']") do
find('.diff-notes-collapse').click
- expect(first('img.js-diff-comment-avatar')["title"]).to eq("#{note.author.name}: #{note.note.truncate(17)}")
+ expect(first('img.js-diff-comment-avatar')["data-original-title"]).to eq("#{note.author.name}: #{note.note.truncate(17)}")
end
end
@@ -164,9 +162,7 @@ feature 'Diff note avatars', feature: true, js: true do
context 'multiple comments' do
before do
- create(:diff_note_on_merge_request, project: project, noteable: merge_request, position: position)
- create(:diff_note_on_merge_request, project: project, noteable: merge_request, position: position)
- create(:diff_note_on_merge_request, project: project, noteable: merge_request, position: position)
+ create_list(:diff_note_on_merge_request, 3, project: project, noteable: merge_request, in_reply_to: note)
visit diffs_namespace_project_merge_request_path(project.namespace, project, merge_request, view: view)
diff --git a/spec/features/merge_requests/diff_notes_resolve_spec.rb b/spec/features/merge_requests/diff_notes_resolve_spec.rb
index 69164aabdb2..4d549f3bdbb 100644
--- a/spec/features/merge_requests/diff_notes_resolve_spec.rb
+++ b/spec/features/merge_requests/diff_notes_resolve_spec.rb
@@ -191,13 +191,15 @@ feature 'Diff notes resolve', feature: true, js: true do
context 'multiple notes' do
before do
- create(:diff_note_on_merge_request, project: project, noteable: merge_request)
+ create(:diff_note_on_merge_request, project: project, noteable: merge_request, in_reply_to: note)
visit_merge_request
end
it 'does not mark discussion as resolved when resolving single note' do
page.first '.diff-content .note' do
first('.line-resolve-btn').click
+
+ expect(page).to have_selector('.note-action-button .loading')
expect(first('.line-resolve-btn')['data-original-title']).to eq("Resolved by #{user.name}")
end
@@ -273,7 +275,7 @@ feature 'Diff notes resolve', feature: true, js: true do
end
page.within '.line-resolve-all-container' do
- page.find('.discussion-next-btn').click
+ page.find('.discussion-next-btn').trigger('click')
end
expect(page.evaluate_script("$('body').scrollTop()")).to be > 0
diff --git a/spec/features/merge_requests/diff_notes_spec.rb b/spec/features/merge_requests/diff_notes_spec.rb
deleted file mode 100644
index 06fad1007e8..00000000000
--- a/spec/features/merge_requests/diff_notes_spec.rb
+++ /dev/null
@@ -1,238 +0,0 @@
-require 'spec_helper'
-
-feature 'Diff notes', js: true, feature: true do
- include WaitForAjax
-
- before do
- login_as :admin
- @merge_request = create(:merge_request)
- @project = @merge_request.source_project
- end
-
- context 'merge request diffs' do
- let(:comment_button_class) { '.add-diff-note' }
- let(:notes_holder_input_class) { 'js-temp-notes-holder' }
- let(:notes_holder_input_xpath) { './following-sibling::*[contains(concat(" ", @class, " "), " notes_holder ")]' }
- let(:test_note_comment) { 'this is a test note!' }
-
- context 'when hovering over a parallel view diff file' do
- before(:each) do
- visit diffs_namespace_project_merge_request_path(@project.namespace, @project, @merge_request, view: 'parallel')
- end
-
- context 'with an old line on the left and no line on the right' do
- it 'should allow commenting on the left side' do
- should_allow_commenting(find('[id="6eb14e00385d2fb284765eb1cd8d420d33d63fc9_23_22"]').find(:xpath, '..'), 'left')
- end
-
- it 'should not allow commenting on the right side' do
- should_not_allow_commenting(find('[id="6eb14e00385d2fb284765eb1cd8d420d33d63fc9_23_22"]').find(:xpath, '..'), 'right')
- end
- end
-
- context 'with no line on the left and a new line on the right' do
- it 'should not allow commenting on the left side' do
- should_not_allow_commenting(find('[id="2f6fcd96b88b36ce98c38da085c795a27d92a3dd_15_15"]').find(:xpath, '..'), 'left')
- end
-
- it 'should allow commenting on the right side' do
- should_allow_commenting(find('[id="2f6fcd96b88b36ce98c38da085c795a27d92a3dd_15_15"]').find(:xpath, '..'), 'right')
- end
- end
-
- context 'with an old line on the left and a new line on the right' do
- it 'should allow commenting on the left side' do
- should_allow_commenting(find('[id="2f6fcd96b88b36ce98c38da085c795a27d92a3dd_9_9"]').find(:xpath, '..'), 'left')
- end
-
- it 'should allow commenting on the right side' do
- should_allow_commenting(find('[id="2f6fcd96b88b36ce98c38da085c795a27d92a3dd_9_9"]').find(:xpath, '..'), 'right')
- end
- end
-
- context 'with an unchanged line on the left and an unchanged line on the right' do
- it 'should allow commenting on the left side' do
- should_allow_commenting(find('[id="2f6fcd96b88b36ce98c38da085c795a27d92a3dd_7_7"]', match: :first).find(:xpath, '..'), 'left')
- end
-
- it 'should allow commenting on the right side' do
- should_allow_commenting(find('[id="2f6fcd96b88b36ce98c38da085c795a27d92a3dd_7_7"]', match: :first).find(:xpath, '..'), 'right')
- end
- end
-
- context 'with a match line' do
- it 'should not allow commenting on the left side' do
- should_not_allow_commenting(find('.match', match: :first).find(:xpath, '..'), 'left')
- end
-
- it 'should not allow commenting on the right side' do
- should_not_allow_commenting(find('.match', match: :first).find(:xpath, '..'), 'right')
- end
- end
-
- context 'with an unfolded line' do
- before(:each) do
- find('.js-unfold', match: :first).click
- wait_for_ajax
- end
-
- # The first `.js-unfold` unfolds upwards, therefore the first
- # `.line_holder` will be an unfolded line.
- let(:line_holder) { first('.line_holder[id="1"]') }
-
- it 'should not allow commenting on the left side' do
- should_not_allow_commenting(line_holder, 'left')
- end
-
- it 'should not allow commenting on the right side' do
- should_not_allow_commenting(line_holder, 'right')
- end
- end
- end
-
- context 'when hovering over an inline view diff file' do
- before do
- visit diffs_namespace_project_merge_request_path(@project.namespace, @project, @merge_request, view: 'inline')
- end
-
- context 'with a new line' do
- it 'should allow commenting' do
- should_allow_commenting(find('[id="2f6fcd96b88b36ce98c38da085c795a27d92a3dd_10_9"]'))
- end
- end
-
- context 'with an old line' do
- it 'should allow commenting' do
- should_allow_commenting(find('[id="6eb14e00385d2fb284765eb1cd8d420d33d63fc9_22_22"]'))
- end
- end
-
- context 'with an unchanged line' do
- it 'should allow commenting' do
- should_allow_commenting(find('[id="2f6fcd96b88b36ce98c38da085c795a27d92a3dd_7_7"]'))
- end
- end
-
- context 'with a match line' do
- it 'should not allow commenting' do
- should_not_allow_commenting(find('.match', match: :first))
- end
- end
-
- context 'with an unfolded line' do
- before(:each) do
- find('.js-unfold', match: :first).click
- wait_for_ajax
- end
-
- # The first `.js-unfold` unfolds upwards, therefore the first
- # `.line_holder` will be an unfolded line.
- let(:line_holder) { first('.line_holder[id="1"]') }
-
- it 'should not allow commenting' do
- should_not_allow_commenting line_holder
- end
- end
-
- context 'when hovering over a diff discussion' do
- before do
- visit diffs_namespace_project_merge_request_path(@project.namespace, @project, @merge_request, view: 'inline')
- should_allow_commenting(find('[id="2f6fcd96b88b36ce98c38da085c795a27d92a3dd_7_7"]'))
- visit namespace_project_merge_request_path(@project.namespace, @project, @merge_request)
- end
-
- it 'should not allow commenting' do
- should_not_allow_commenting(find('.line_holder', match: :first))
- end
- end
- end
-
- context 'when the MR only supports legacy diff notes' do
- before do
- @merge_request.merge_request_diff.update_attributes(start_commit_sha: nil)
- visit diffs_namespace_project_merge_request_path(@project.namespace, @project, @merge_request, view: 'inline')
- end
-
- context 'with a new line' do
- it 'should allow commenting' do
- should_allow_commenting(find('[id="2f6fcd96b88b36ce98c38da085c795a27d92a3dd_10_9"]'))
- end
- end
-
- context 'with an old line' do
- it 'should allow commenting' do
- should_allow_commenting(find('[id="6eb14e00385d2fb284765eb1cd8d420d33d63fc9_22_22"]'))
- end
- end
-
- context 'with an unchanged line' do
- it 'should allow commenting' do
- should_allow_commenting(find('[id="2f6fcd96b88b36ce98c38da085c795a27d92a3dd_7_7"]'))
- end
- end
-
- context 'with a match line' do
- it 'should not allow commenting' do
- should_not_allow_commenting(find('.match', match: :first))
- end
- end
- end
-
- def should_allow_commenting(line_holder, diff_side = nil)
- line = get_line_components(line_holder, diff_side)
- line[:content].hover
- expect(line[:num]).to have_css comment_button_class
-
- comment_on_line(line_holder, line)
-
- assert_comment_persistence(line_holder)
- end
-
- def should_not_allow_commenting(line_holder, diff_side = nil)
- line = get_line_components(line_holder, diff_side)
- line[:content].hover
- expect(line[:num]).not_to have_css comment_button_class
- end
-
- def get_line_components(line_holder, diff_side = nil)
- if diff_side.nil?
- get_inline_line_components(line_holder)
- else
- get_parallel_line_components(line_holder, diff_side)
- end
- end
-
- def get_inline_line_components(line_holder)
- { content: line_holder.find('.line_content', match: :first), num: line_holder.find('.diff-line-num', match: :first) }
- end
-
- def get_parallel_line_components(line_holder, diff_side = nil)
- side_index = diff_side == 'left' ? 0 : 1
- # Wait for `.line_content`
- line_holder.find('.line_content', match: :first)
- # Wait for `.diff-line-num`
- line_holder.find('.diff-line-num', match: :first)
- { content: line_holder.all('.line_content')[side_index], num: line_holder.all('.diff-line-num')[side_index] }
- end
-
- def comment_on_line(line_holder, line)
- line[:num].find(comment_button_class).trigger 'click'
- line_holder.find(:xpath, notes_holder_input_xpath)
-
- notes_holder_input = line_holder.find(:xpath, notes_holder_input_xpath)
- expect(notes_holder_input[:class]).to include(notes_holder_input_class)
-
- notes_holder_input.fill_in 'note[note]', with: test_note_comment
- click_button 'Comment'
- wait_for_ajax
- end
-
- def assert_comment_persistence(line_holder)
- expect(line_holder).to have_xpath notes_holder_input_xpath
-
- notes_holder_saved = line_holder.find(:xpath, notes_holder_input_xpath)
- expect(notes_holder_saved[:class]).not_to include(notes_holder_input_class)
- expect(notes_holder_saved).to have_content test_note_comment
- end
- end
-end
diff --git a/spec/features/merge_requests/diffs_spec.rb b/spec/features/merge_requests/diffs_spec.rb
index 4a6c76a5caf..4860a2a7498 100644
--- a/spec/features/merge_requests/diffs_spec.rb
+++ b/spec/features/merge_requests/diffs_spec.rb
@@ -1,11 +1,8 @@
require 'spec_helper'
feature 'Diffs URL', js: true, feature: true do
- before do
- login_as :admin
- @merge_request = create(:merge_request)
- @project = @merge_request.source_project
- end
+ let(:project) { create(:project, :public) }
+ let(:merge_request) { create(:merge_request, source_project: project) }
context 'when visit with */* as accept header' do
before(:each) do
@@ -13,9 +10,9 @@ feature 'Diffs URL', js: true, feature: true do
end
it 'renders the notes' do
- create :note_on_merge_request, project: @project, noteable: @merge_request, note: 'Rebasing with master'
+ create :note_on_merge_request, project: project, noteable: merge_request, note: 'Rebasing with master'
- visit diffs_namespace_project_merge_request_path(@project.namespace, @project, @merge_request)
+ visit diffs_namespace_project_merge_request_path(project.namespace, project, merge_request)
# Load notes and diff through AJAX
expect(page).to have_css('.note-text', visible: false, text: 'Rebasing with master')
@@ -23,12 +20,39 @@ feature 'Diffs URL', js: true, feature: true do
end
end
+ context 'when linking to note' do
+ describe 'with unresolved note' do
+ let(:note) { create :diff_note_on_merge_request, project: project, noteable: merge_request }
+ let(:fragment) { "#note_#{note.id}" }
+
+ before do
+ visit "#{diffs_namespace_project_merge_request_path(project.namespace, project, merge_request)}#{fragment}"
+ end
+
+ it 'shows expanded note' do
+ expect(page).to have_selector(fragment, visible: true)
+ end
+ end
+
+ describe 'with resolved note' do
+ let(:note) { create :diff_note_on_merge_request, :resolved, project: project, noteable: merge_request }
+ let(:fragment) { "#note_#{note.id}" }
+
+ before do
+ visit "#{diffs_namespace_project_merge_request_path(project.namespace, project, merge_request)}#{fragment}"
+ end
+
+ it 'shows expanded note' do
+ expect(page).to have_selector(fragment, visible: true)
+ end
+ end
+ end
+
context 'when merge request has overflow' do
it 'displays warning' do
- allow_any_instance_of(MergeRequestDiff).to receive(:overflow?).and_return(true)
- allow(Commit).to receive(:max_diff_options).and_return(max_files: 20, max_lines: 20)
+ allow(Commit).to receive(:max_diff_options).and_return(max_files: 3)
- visit diffs_namespace_project_merge_request_path(@project.namespace, @project, @merge_request)
+ visit diffs_namespace_project_merge_request_path(project.namespace, project, merge_request)
page.within('.alert') do
expect(page).to have_text("Too many changes to show. Plain diff Email patch To preserve
@@ -36,4 +60,35 @@ feature 'Diffs URL', js: true, feature: true do
end
end
end
+
+ context 'when editing file' do
+ let(:author_user) { create(:user) }
+ let(:user) { create(:user) }
+ let(:forked_project) { Projects::ForkService.new(project, author_user).execute }
+ let(:merge_request) { create(:merge_request_with_diffs, source_project: forked_project, target_project: project, author: author_user) }
+ let(:changelog_id) { Digest::SHA1.hexdigest("CHANGELOG") }
+
+ context 'as author' do
+ it 'shows direct edit link' do
+ login_as(author_user)
+ visit diffs_namespace_project_merge_request_path(project.namespace, project, merge_request)
+
+ # Throws `Capybara::Poltergeist::InvalidSelector` if we try to use `#hash` syntax
+ expect(page).to have_selector("[id=\"#{changelog_id}\"] a.js-edit-blob")
+ end
+ end
+
+ context 'as user who needs to fork' do
+ it 'shows fork/cancel confirmation' do
+ login_as(user)
+ visit diffs_namespace_project_merge_request_path(project.namespace, project, merge_request)
+
+ # Throws `Capybara::Poltergeist::InvalidSelector` if we try to use `#hash` syntax
+ find("[id=\"#{changelog_id}\"] .js-edit-blob").click
+
+ expect(page).to have_selector('.js-fork-suggestion-button', count: 1)
+ expect(page).to have_selector('.js-cancel-fork-suggestion-button', count: 1)
+ end
+ end
+ end
end
diff --git a/spec/features/merge_requests/discussion_spec.rb b/spec/features/merge_requests/discussion_spec.rb
new file mode 100644
index 00000000000..f59d0faa274
--- /dev/null
+++ b/spec/features/merge_requests/discussion_spec.rb
@@ -0,0 +1,51 @@
+require 'spec_helper'
+
+feature 'Merge Request Discussions', feature: true do
+ before do
+ login_as :admin
+ end
+
+ context "Diff discussions" do
+ let(:merge_request) { create(:merge_request, importing: true) }
+ let(:project) { merge_request.source_project }
+ let!(:old_merge_request_diff) { merge_request.merge_request_diffs.create(diff_refs: outdated_diff_refs) }
+ let!(:new_merge_request_diff) { merge_request.merge_request_diffs.create }
+
+ let!(:outdated_discussion) { create(:diff_note_on_merge_request, project: project, noteable: merge_request, position: outdated_position).to_discussion }
+ let!(:active_discussion) { create(:diff_note_on_merge_request, noteable: merge_request, project: project).to_discussion }
+
+ let(:outdated_position) do
+ Gitlab::Diff::Position.new(
+ old_path: "files/ruby/popen.rb",
+ new_path: "files/ruby/popen.rb",
+ old_line: nil,
+ new_line: 9,
+ diff_refs: outdated_diff_refs
+ )
+ end
+
+ let(:outdated_diff_refs) { project.commit("874797c3a73b60d2187ed6e2fcabd289ff75171e").diff_refs }
+
+ before(:each) do
+ visit namespace_project_merge_request_path(project.namespace, project, merge_request)
+ end
+
+ context 'active discussions' do
+ it 'shows a link to the diff' do
+ within(".discussion[data-discussion-id='#{active_discussion.id}']") do
+ path = diffs_namespace_project_merge_request_path(project.namespace, project, merge_request, anchor: active_discussion.line_code)
+ expect(page).to have_link('the diff', href: path)
+ end
+ end
+ end
+
+ context 'outdated discussions' do
+ it 'shows a link to the outdated diff' do
+ within(".discussion[data-discussion-id='#{outdated_discussion.id}']") do
+ path = diffs_namespace_project_merge_request_path(project.namespace, project, merge_request, diff_id: old_merge_request_diff.id, anchor: outdated_discussion.line_code)
+ expect(page).to have_link('an outdated diff', href: path)
+ end
+ end
+ end
+ end
+end
diff --git a/spec/features/merge_requests/edit_mr_spec.rb b/spec/features/merge_requests/edit_mr_spec.rb
index cb3bc392903..ec87a99b3ab 100644
--- a/spec/features/merge_requests/edit_mr_spec.rb
+++ b/spec/features/merge_requests/edit_mr_spec.rb
@@ -29,18 +29,6 @@ feature 'Edit Merge Request', feature: true do
expect(page).to have_content 'Someone edited the merge request the same time you did'
end
- it 'allows to unselect "Remove source branch"' do
- merge_request.update(merge_params: { 'force_remove_source_branch' => '1' })
- expect(merge_request.merge_params['force_remove_source_branch']).to be_truthy
-
- visit edit_namespace_project_merge_request_path(project.namespace, project, merge_request)
- uncheck 'Remove source branch when merge request is accepted'
-
- click_button 'Save changes'
-
- expect(page).to have_content 'Remove source branch'
- end
-
it 'should preserve description textarea height', js: true do
long_description = %q(
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam ac ornare ligula, ut tempus arcu. Etiam ultricies accumsan dolor vitae faucibus. Donec at elit lacus. Mauris orci ante, aliquam quis lorem eget, convallis faucibus arcu. Aenean at pulvinar lacus. Ut viverra quam massa, molestie ornare tortor dignissim a. Suspendisse tristique pellentesque tellus, id lacinia metus elementum id. Nam tristique, arcu rhoncus faucibus viverra, lacus ipsum sagittis ligula, vitae convallis odio lacus a nibh. Ut tincidunt est purus, ac vestibulum augue maximus in. Suspendisse vel erat et mi ultricies semper. Pellentesque volutpat pellentesque consequat.
diff --git a/spec/features/merge_requests/filter_by_labels_spec.rb b/spec/features/merge_requests/filter_by_labels_spec.rb
index 55f3c1863ff..32a9082b9b9 100644
--- a/spec/features/merge_requests/filter_by_labels_spec.rb
+++ b/spec/features/merge_requests/filter_by_labels_spec.rb
@@ -3,7 +3,6 @@ require 'rails_helper'
feature 'Issue filtering by Labels', feature: true, js: true do
include FilteredSearchHelpers
include MergeRequestHelpers
- include WaitForAjax
let(:project) { create(:project, :public) }
let!(:user) { create(:user) }
diff --git a/spec/features/merge_requests/filter_merge_requests_spec.rb b/spec/features/merge_requests/filter_merge_requests_spec.rb
index 70e3997e716..2da60e9f4ad 100644
--- a/spec/features/merge_requests/filter_merge_requests_spec.rb
+++ b/spec/features/merge_requests/filter_merge_requests_spec.rb
@@ -3,7 +3,6 @@ require 'rails_helper'
describe 'Filter merge requests', feature: true do
include FilteredSearchHelpers
include MergeRequestHelpers
- include WaitForAjax
let!(:project) { create(:project) }
let!(:group) { create(:group) }
diff --git a/spec/features/merge_requests/merge_commit_message_toggle_spec.rb b/spec/features/merge_requests/merge_commit_message_toggle_spec.rb
index 1bc2a5548dd..221ddb5873c 100644
--- a/spec/features/merge_requests/merge_commit_message_toggle_spec.rb
+++ b/spec/features/merge_requests/merge_commit_message_toggle_spec.rb
@@ -14,8 +14,6 @@ feature 'Clicking toggle commit message link', feature: true, js: true do
)
end
let(:textbox) { page.find(:css, '.js-commit-message', visible: false) }
- let(:include_link) { page.find(:css, '.js-with-description-link', visible: false) }
- let(:do_not_include_link) { page.find(:css, '.js-without-description-link', visible: false) }
let(:default_message) do
[
"Merge branch 'feature' into 'master'",
@@ -40,7 +38,7 @@ feature 'Clicking toggle commit message link', feature: true, js: true do
visit namespace_project_merge_request_path(project.namespace, project, merge_request)
- expect(textbox).not_to be_visible
+ expect(page).not_to have_selector('.js-commit-message')
click_button "Modify commit message"
expect(textbox).to be_visible
end
@@ -56,19 +54,4 @@ feature 'Clicking toggle commit message link', feature: true, js: true do
expect(textbox.value).to eq(default_message)
end
-
- it "toggles link between 'Include description' and 'Don't include description'" do
- expect(include_link).to be_visible
- expect(do_not_include_link).not_to be_visible
-
- click_link "Include description in commit message"
-
- expect(include_link).not_to be_visible
- expect(do_not_include_link).to be_visible
-
- click_link "Don't include description in commit message"
-
- expect(include_link).to be_visible
- expect(do_not_include_link).not_to be_visible
- end
end
diff --git a/spec/features/merge_requests/merge_immediately_with_pipeline_spec.rb b/spec/features/merge_requests/merge_immediately_with_pipeline_spec.rb
index 79105b1ee46..c102722d6db 100644
--- a/spec/features/merge_requests/merge_immediately_with_pipeline_spec.rb
+++ b/spec/features/merge_requests/merge_immediately_with_pipeline_spec.rb
@@ -4,16 +4,18 @@ feature 'Merge immediately', :feature, :js do
let(:user) { create(:user) }
let(:project) { create(:project, :public) }
- let(:merge_request) do
+ let!(:merge_request) do
create(:merge_request_with_diffs, source_project: project,
author: user,
- title: 'Bug NS-04')
+ title: 'Bug NS-04',
+ head_pipeline: pipeline,
+ source_branch: pipeline.ref)
end
let(:pipeline) do
create(:ci_pipeline, project: project,
- sha: merge_request.diff_head_sha,
- ref: merge_request.source_branch)
+ ref: 'master',
+ sha: project.repository.commit('master').id)
end
before { project.team << [user, :master] }
@@ -32,11 +34,13 @@ feature 'Merge immediately', :feature, :js do
page.within '.mr-widget-body' do
find('.dropdown-toggle').click
- click_link 'Merge Immediately'
+ Sidekiq::Testing.fake! do
+ click_link 'Merge immediately'
- expect(find('.js-merge-when-pipeline-succeeds-button')).to have_content('Merge in progress')
+ expect(find('.accept-merge-request.btn-info')).to have_content('Merge in progress')
- wait_for_ajax
+ wait_for_vue_resource
+ end
end
end
end
diff --git a/spec/features/merge_requests/merge_when_pipeline_succeeds_spec.rb b/spec/features/merge_requests/merge_when_pipeline_succeeds_spec.rb
index ed7193b9777..11b6f0c0a64 100644
--- a/spec/features/merge_requests/merge_when_pipeline_succeeds_spec.rb
+++ b/spec/features/merge_requests/merge_when_pipeline_succeeds_spec.rb
@@ -16,7 +16,10 @@ feature 'Merge When Pipeline Succeeds', :feature, :js do
ref: merge_request.source_branch)
end
- before { project.team << [user, :master] }
+ before do
+ project.add_master(user)
+ merge_request.update(head_pipeline_id: pipeline.id)
+ end
context 'when there is active pipeline for merge request' do
background do
@@ -28,25 +31,25 @@ feature 'Merge When Pipeline Succeeds', :feature, :js do
visit_merge_request(merge_request)
end
- it 'displays the Merge When Pipeline Succeeds button' do
- expect(page).to have_button "Merge When Pipeline Succeeds"
+ it 'displays the Merge when pipeline succeeds button' do
+ expect(page).to have_button "Merge when pipeline succeeds"
end
- describe 'enabling Merge When Pipeline Succeeds' do
- shared_examples 'Merge When Pipeline Succeeds activator' do
- it 'activates the Merge When Pipeline Succeeds feature' do
- click_button "Merge When Pipeline Succeeds"
+ describe 'enabling Merge when pipeline succeeds' do
+ shared_examples 'Merge when pipeline succeeds activator' do
+ it 'activates the Merge when pipeline succeeds feature' do
+ click_button "Merge when pipeline succeeds"
expect(page).to have_content "Set by #{user.name} to be merged automatically when the pipeline succeeds."
- expect(page).to have_content "The source branch will not be removed."
- expect(page).to have_link "Cancel Automatic Merge"
+ expect(page).to have_content "The source branch will be removed."
+ expect(page).to have_selector ".js-cancel-auto-merge"
visit_merge_request(merge_request) # Needed to refresh the page
expect(page).to have_content /enabled an automatic merge when the pipeline for \h{8} succeeds/i
end
end
context "when enabled immediately" do
- it_behaves_like 'Merge When Pipeline Succeeds activator'
+ it_behaves_like 'Merge when pipeline succeeds activator'
end
context 'when enabled after pipeline status changed' do
@@ -60,16 +63,16 @@ feature 'Merge When Pipeline Succeeds', :feature, :js do
expect(page).to have_content "Pipeline ##{pipeline.id} running"
end
- it_behaves_like 'Merge When Pipeline Succeeds activator'
+ it_behaves_like 'Merge when pipeline succeeds activator'
end
context 'when enabled after it was previously canceled' do
before do
- click_button "Merge When Pipeline Succeeds"
- click_link "Cancel Automatic Merge"
+ click_button "Merge when pipeline succeeds"
+ click_link "Cancel automatic merge"
end
- it_behaves_like 'Merge When Pipeline Succeeds activator'
+ it_behaves_like 'Merge when pipeline succeeds activator'
end
context 'when it was enabled and then canceled' do
@@ -83,10 +86,21 @@ feature 'Merge When Pipeline Succeeds', :feature, :js do
end
before do
- click_link "Cancel Automatic Merge"
+ click_link "Cancel automatic merge"
end
- it_behaves_like 'Merge When Pipeline Succeeds activator'
+ it_behaves_like 'Merge when pipeline succeeds activator'
+ end
+ end
+
+ describe 'enabling Merge when pipeline succeeds via dropdown' do
+ it 'activates the Merge when pipeline succeeds feature' do
+ click_button 'Select merge moment'
+ click_link 'Merge when pipeline succeeds'
+
+ expect(page).to have_content "Set by #{user.name} to be merged automatically when the pipeline succeeds."
+ expect(page).to have_content "The source branch will be removed."
+ expect(page).to have_link "Cancel automatic merge"
end
end
end
@@ -110,21 +124,14 @@ feature 'Merge When Pipeline Succeeds', :feature, :js do
end
it 'allows to cancel the automatic merge' do
- click_link "Cancel Automatic Merge"
+ click_link "Cancel automatic merge"
- expect(page).to have_button "Merge When Pipeline Succeeds"
+ expect(page).to have_button "Merge when pipeline succeeds"
visit_merge_request(merge_request) # refresh the page
expect(page).to have_content "canceled the automatic merge"
end
- it "allows the user to remove the source branch" do
- expect(page).to have_link "Remove Source Branch When Merged"
-
- click_link "Remove Source Branch When Merged"
- expect(page).to have_content "The source branch will be removed"
- end
-
context 'when pipeline succeeds' do
background { build.success }
@@ -141,7 +148,7 @@ feature 'Merge When Pipeline Succeeds', :feature, :js do
it "does not allow to enable merge when pipeline succeeds" do
visit_merge_request(merge_request)
- expect(page).not_to have_link 'Merge When Pipeline Succeeds'
+ expect(page).not_to have_link 'Merge when pipeline succeeds'
end
end
diff --git a/spec/features/merge_requests/mini_pipeline_graph_spec.rb b/spec/features/merge_requests/mini_pipeline_graph_spec.rb
index 84ad8765d8f..5b2798af32f 100644
--- a/spec/features/merge_requests/mini_pipeline_graph_spec.rb
+++ b/spec/features/merge_requests/mini_pipeline_graph_spec.rb
@@ -1,11 +1,9 @@
require 'rails_helper'
feature 'Mini Pipeline Graph', :js, :feature do
- include WaitForAjax
-
let(:user) { create(:user) }
let(:project) { create(:project, :public) }
- let(:merge_request) { create(:merge_request, source_project: project) }
+ let(:merge_request) { create(:merge_request, source_project: project, head_pipeline: pipeline) }
let(:pipeline) { create(:ci_empty_pipeline, project: project, ref: 'master', status: 'running', sha: project.commit.id) }
let(:build) { create(:ci_build, pipeline: pipeline, stage: 'test', commands: 'test') }
diff --git a/spec/features/merge_requests/only_allow_merge_if_build_succeeds_spec.rb b/spec/features/merge_requests/only_allow_merge_if_build_succeeds_spec.rb
index 447764566e0..cdda0542c51 100644
--- a/spec/features/merge_requests/only_allow_merge_if_build_succeeds_spec.rb
+++ b/spec/features/merge_requests/only_allow_merge_if_build_succeeds_spec.rb
@@ -1,6 +1,8 @@
require 'spec_helper'
-feature 'Only allow merge requests to be merged if the pipeline succeeds', feature: true do
+feature 'Only allow merge requests to be merged if the pipeline succeeds', feature: true, js: true do
+ include WaitForVueResource
+
let(:merge_request) { create(:merge_request_with_diffs) }
let(:project) { merge_request.target_project }
@@ -10,15 +12,17 @@ feature 'Only allow merge requests to be merged if the pipeline succeeds', featu
project.team << [merge_request.author, :master]
end
- context 'project does not have CI enabled' do
+ context 'project does not have CI enabled', js: true do
it 'allows MR to be merged' do
visit_merge_request(merge_request)
- expect(page).to have_button 'Accept Merge Request'
+ wait_for_vue_resource
+
+ expect(page).to have_button 'Merge'
end
end
- context 'when project has CI enabled' do
+ context 'when project has CI enabled', js: true do
given!(:pipeline) do
create(:ci_empty_pipeline,
project: project,
@@ -27,6 +31,8 @@ feature 'Only allow merge requests to be merged if the pipeline succeeds', featu
status: status)
end
+ before { merge_request.update(head_pipeline: pipeline) }
+
context 'when merge requests can only be merged if the pipeline succeeds' do
before do
project.update_attribute(:only_allow_merge_if_pipeline_succeeds, true)
@@ -38,8 +44,10 @@ feature 'Only allow merge requests to be merged if the pipeline succeeds', featu
it 'does not allow to merge immediately' do
visit_merge_request(merge_request)
- expect(page).to have_button 'Merge When Pipeline Succeeds'
- expect(page).not_to have_button 'Select Merge Moment'
+ wait_for_vue_resource
+
+ expect(page).to have_button 'Merge when pipeline succeeds'
+ expect(page).not_to have_button 'Select merge moment'
end
end
@@ -49,7 +57,9 @@ feature 'Only allow merge requests to be merged if the pipeline succeeds', featu
it 'does not allow MR to be merged' do
visit_merge_request(merge_request)
- expect(page).not_to have_button 'Accept Merge Request'
+ wait_for_vue_resource
+
+ expect(page).to have_css('button[disabled="disabled"]', text: 'Merge')
expect(page).to have_content('Please retry the job or push a new commit to fix the failure.')
end
end
@@ -60,7 +70,9 @@ feature 'Only allow merge requests to be merged if the pipeline succeeds', featu
it 'does not allow MR to be merged' do
visit_merge_request(merge_request)
- expect(page).not_to have_button 'Accept Merge Request'
+ wait_for_vue_resource
+
+ expect(page).not_to have_button 'Merge'
expect(page).to have_content('Please retry the job or push a new commit to fix the failure.')
end
end
@@ -71,7 +83,9 @@ feature 'Only allow merge requests to be merged if the pipeline succeeds', featu
it 'allows MR to be merged' do
visit_merge_request(merge_request)
- expect(page).to have_button 'Accept Merge Request'
+ wait_for_vue_resource
+
+ expect(page).to have_button 'Merge'
end
end
@@ -81,7 +95,9 @@ feature 'Only allow merge requests to be merged if the pipeline succeeds', featu
it 'allows MR to be merged' do
visit_merge_request(merge_request)
- expect(page).to have_button 'Accept Merge Request'
+ wait_for_vue_resource
+
+ expect(page).to have_button 'Merge'
end
end
end
@@ -94,13 +110,15 @@ feature 'Only allow merge requests to be merged if the pipeline succeeds', featu
context 'when CI is running' do
given(:status) { :running }
- it 'allows MR to be merged immediately', js: true do
+ it 'allows MR to be merged immediately' do
visit_merge_request(merge_request)
- expect(page).to have_button 'Merge When Pipeline Succeeds'
+ wait_for_vue_resource
+
+ expect(page).to have_button 'Merge when pipeline succeeds'
- click_button 'Select Merge Moment'
- expect(page).to have_content 'Merge Immediately'
+ click_button 'Select merge moment'
+ expect(page).to have_content 'Merge immediately'
end
end
@@ -110,7 +128,9 @@ feature 'Only allow merge requests to be merged if the pipeline succeeds', featu
it 'allows MR to be merged' do
visit_merge_request(merge_request)
- expect(page).to have_button 'Accept Merge Request'
+ wait_for_vue_resource
+
+ expect(page).to have_button 'Merge'
end
end
@@ -120,7 +140,9 @@ feature 'Only allow merge requests to be merged if the pipeline succeeds', featu
it 'allows MR to be merged' do
visit_merge_request(merge_request)
- expect(page).to have_button 'Accept Merge Request'
+ wait_for_vue_resource
+
+ expect(page).to have_button 'Merge'
end
end
end
diff --git a/spec/features/merge_requests/pipelines_spec.rb b/spec/features/merge_requests/pipelines_spec.rb
index 9c4c0525267..99e283ac181 100644
--- a/spec/features/merge_requests/pipelines_spec.rb
+++ b/spec/features/merge_requests/pipelines_spec.rb
@@ -1,8 +1,6 @@
require 'spec_helper'
feature 'Pipelines for Merge Requests', feature: true, js: true do
- include WaitForAjax
-
given(:user) { create(:user) }
given(:merge_request) { create(:merge_request) }
given(:project) { merge_request.target_project }
diff --git a/spec/features/merge_requests/reset_filters_spec.rb b/spec/features/merge_requests/reset_filters_spec.rb
index 14511707af4..275f81f50dc 100644
--- a/spec/features/merge_requests/reset_filters_spec.rb
+++ b/spec/features/merge_requests/reset_filters_spec.rb
@@ -3,7 +3,6 @@ require 'rails_helper'
feature 'Merge requests filter clear button', feature: true, js: true do
include FilteredSearchHelpers
include MergeRequestHelpers
- include WaitForAjax
include IssueHelpers
let!(:project) { create(:project, :public) }
@@ -14,7 +13,7 @@ feature 'Merge requests filter clear button', feature: true, js: true do
let!(:mr2) { create(:merge_request, title: "Bugfix1", source_project: project, target_project: project, source_branch: "Bugfix1") }
let(:merge_request_css) { '.merge-request' }
- let(:clear_search_css) { '.filtered-search-input-container .clear-search' }
+ let(:clear_search_css) { '.filtered-search-box .clear-search' }
before do
mr2.labels << bug
diff --git a/spec/features/merge_requests/target_branch_spec.rb b/spec/features/merge_requests/target_branch_spec.rb
index b6134540273..c154cf8ade9 100644
--- a/spec/features/merge_requests/target_branch_spec.rb
+++ b/spec/features/merge_requests/target_branch_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe 'Target branch', feature: true do
+describe 'Target branch', feature: true, js: true do
let(:user) { create(:user) }
let(:merge_request) { create(:merge_request) }
let(:project) { merge_request.project }
@@ -17,11 +17,6 @@ describe 'Target branch', feature: true do
project.team << [user, :master]
end
- it 'shows link to target branch' do
- visit path_to_merge_request
- expect(page).to have_link('feature', href: namespace_project_commits_path(project.namespace, project, merge_request.target_branch))
- end
-
context 'when branch was deleted' do
before do
DeleteBranchService.new(project, user).execute('feature')
@@ -30,12 +25,12 @@ describe 'Target branch', feature: true do
it 'shows a message about missing target branch' do
expect(page).to have_content(
- 'Target branch feature does not exist'
+ 'Target branch does not exist'
)
end
it 'does not show link to target branch' do
- expect(page).not_to have_link('feature')
+ expect(page).not_to have_selector('.mr-widget-body .js-branch-text a')
end
end
end
diff --git a/spec/features/merge_requests/update_merge_requests_spec.rb b/spec/features/merge_requests/update_merge_requests_spec.rb
index b56fdfe5611..9ecc998785b 100644
--- a/spec/features/merge_requests/update_merge_requests_spec.rb
+++ b/spec/features/merge_requests/update_merge_requests_spec.rb
@@ -1,8 +1,6 @@
require 'rails_helper'
feature 'Multiple merge requests updating from merge_requests#index', feature: true do
- include WaitForAjax
-
let!(:user) { create(:user)}
let!(:project) { create(:project) }
let!(:merge_request) { create(:merge_request, source_project: project, target_project: project) }
diff --git a/spec/features/merge_requests/user_posts_diff_notes_spec.rb b/spec/features/merge_requests/user_posts_diff_notes_spec.rb
new file mode 100644
index 00000000000..7756202e3f5
--- /dev/null
+++ b/spec/features/merge_requests/user_posts_diff_notes_spec.rb
@@ -0,0 +1,294 @@
+require 'spec_helper'
+
+feature 'Merge requests > User posts diff notes', :js do
+ let(:user) { create(:user) }
+ let(:merge_request) { create(:merge_request) }
+ let(:project) { merge_request.source_project }
+
+ before do
+ project.add_developer(user)
+ login_as(user)
+ end
+
+ let(:comment_button_class) { '.add-diff-note' }
+ let(:notes_holder_input_class) { 'js-temp-notes-holder' }
+ let(:notes_holder_input_xpath) { './following-sibling::*[contains(concat(" ", @class, " "), " notes_holder ")]' }
+ let(:test_note_comment) { 'this is a test note!' }
+
+ context 'when hovering over a parallel view diff file' do
+ before do
+ visit diffs_namespace_project_merge_request_path(project.namespace, project, merge_request, view: 'parallel')
+ end
+
+ context 'with an old line on the left and no line on the right' do
+ it 'allows commenting on the left side' do
+ should_allow_commenting(find('[id="6eb14e00385d2fb284765eb1cd8d420d33d63fc9_23_22"]').find(:xpath, '..'), 'left')
+ end
+
+ it 'does not allow commenting on the right side' do
+ should_not_allow_commenting(find('[id="6eb14e00385d2fb284765eb1cd8d420d33d63fc9_23_22"]').find(:xpath, '..'), 'right')
+ end
+ end
+
+ context 'with no line on the left and a new line on the right' do
+ it 'does not allow commenting on the left side' do
+ should_not_allow_commenting(find('[id="2f6fcd96b88b36ce98c38da085c795a27d92a3dd_15_15"]').find(:xpath, '..'), 'left')
+ end
+
+ it 'allows commenting on the right side' do
+ should_allow_commenting(find('[id="2f6fcd96b88b36ce98c38da085c795a27d92a3dd_15_15"]').find(:xpath, '..'), 'right')
+ end
+ end
+
+ context 'with an old line on the left and a new line on the right' do
+ it 'allows commenting on the left side' do
+ should_allow_commenting(find('[id="2f6fcd96b88b36ce98c38da085c795a27d92a3dd_9_9"]').find(:xpath, '..'), 'left')
+ end
+
+ it 'allows commenting on the right side' do
+ should_allow_commenting(find('[id="2f6fcd96b88b36ce98c38da085c795a27d92a3dd_9_9"]').find(:xpath, '..'), 'right')
+ end
+ end
+
+ context 'with an unchanged line on the left and an unchanged line on the right' do
+ it 'allows commenting on the left side' do
+ should_allow_commenting(find('[id="2f6fcd96b88b36ce98c38da085c795a27d92a3dd_7_7"]', match: :first).find(:xpath, '..'), 'left')
+ end
+
+ it 'allows commenting on the right side' do
+ should_allow_commenting(find('[id="2f6fcd96b88b36ce98c38da085c795a27d92a3dd_7_7"]', match: :first).find(:xpath, '..'), 'right')
+ end
+ end
+
+ context 'with a match line' do
+ it 'does not allow commenting on the left side' do
+ should_not_allow_commenting(find('.match', match: :first).find(:xpath, '..'), 'left')
+ end
+
+ it 'does not allow commenting on the right side' do
+ should_not_allow_commenting(find('.match', match: :first).find(:xpath, '..'), 'right')
+ end
+ end
+
+ context 'with an unfolded line' do
+ before(:each) do
+ find('.js-unfold', match: :first).click
+ wait_for_ajax
+ end
+
+ # The first `.js-unfold` unfolds upwards, therefore the first
+ # `.line_holder` will be an unfolded line.
+ let(:line_holder) { first('.line_holder[id="1"]') }
+
+ it 'does not allow commenting on the left side' do
+ should_not_allow_commenting(line_holder, 'left')
+ end
+
+ it 'does not allow commenting on the right side' do
+ should_not_allow_commenting(line_holder, 'right')
+ end
+ end
+ end
+
+ context 'when hovering over an inline view diff file' do
+ before do
+ visit diffs_namespace_project_merge_request_path(project.namespace, project, merge_request, view: 'inline')
+ end
+
+ context 'with a new line' do
+ it 'allows commenting' do
+ should_allow_commenting(find('[id="2f6fcd96b88b36ce98c38da085c795a27d92a3dd_10_9"]'))
+ end
+ end
+
+ context 'with an old line' do
+ it 'allows commenting' do
+ should_allow_commenting(find('[id="6eb14e00385d2fb284765eb1cd8d420d33d63fc9_22_22"]'))
+ end
+ end
+
+ context 'with an unchanged line' do
+ it 'allows commenting' do
+ should_allow_commenting(find('[id="2f6fcd96b88b36ce98c38da085c795a27d92a3dd_7_7"]'))
+ end
+ end
+
+ context 'with a match line' do
+ it 'does not allow commenting' do
+ should_not_allow_commenting(find('.match', match: :first))
+ end
+ end
+
+ context 'with an unfolded line' do
+ before(:each) do
+ find('.js-unfold', match: :first).click
+ wait_for_ajax
+ end
+
+ # The first `.js-unfold` unfolds upwards, therefore the first
+ # `.line_holder` will be an unfolded line.
+ let(:line_holder) { first('.line_holder[id="1"]') }
+
+ it 'does not allow commenting' do
+ should_not_allow_commenting line_holder
+ end
+ end
+
+ context 'when hovering over a diff discussion' do
+ before do
+ visit diffs_namespace_project_merge_request_path(project.namespace, project, merge_request, view: 'inline')
+ should_allow_commenting(find('[id="2f6fcd96b88b36ce98c38da085c795a27d92a3dd_7_7"]'))
+ visit namespace_project_merge_request_path(project.namespace, project, merge_request)
+ end
+
+ it 'does not allow commenting' do
+ should_not_allow_commenting(find('.line_holder', match: :first))
+ end
+ end
+ end
+
+ context 'when cancelling the comment addition' do
+ before do
+ visit diffs_namespace_project_merge_request_path(project.namespace, project, merge_request, view: 'inline')
+ end
+
+ context 'with a new line' do
+ it 'allows dismissing a comment' do
+ should_allow_dismissing_a_comment(find('[id="2f6fcd96b88b36ce98c38da085c795a27d92a3dd_10_9"]'))
+ end
+ end
+ end
+
+ describe 'with muliple note forms' do
+ before do
+ visit diffs_namespace_project_merge_request_path(project.namespace, project, merge_request, view: 'inline')
+ click_diff_line(find('[id="2f6fcd96b88b36ce98c38da085c795a27d92a3dd_10_9"]'))
+ click_diff_line(find('[id="6eb14e00385d2fb284765eb1cd8d420d33d63fc9_22_22"]'))
+ end
+
+ describe 'posting a note' do
+ it 'adds as discussion' do
+ expect(page).to have_css('.js-temp-notes-holder', count: 2)
+
+ should_allow_commenting(find('[id="6eb14e00385d2fb284765eb1cd8d420d33d63fc9_22_22"]'), asset_form_reset: false)
+ expect(page).to have_css('.notes_holder .note', count: 1)
+ expect(page).to have_css('.js-temp-notes-holder', count: 1)
+ expect(page).to have_button('Reply...')
+ end
+ end
+ end
+
+ context 'when the MR only supports legacy diff notes' do
+ before do
+ merge_request.merge_request_diff.update_attributes(start_commit_sha: nil)
+ visit diffs_namespace_project_merge_request_path(project.namespace, project, merge_request, view: 'inline')
+ end
+
+ context 'with a new line' do
+ it 'allows commenting' do
+ should_allow_commenting(find('[id="2f6fcd96b88b36ce98c38da085c795a27d92a3dd_10_9"]'))
+ end
+ end
+
+ context 'with an old line' do
+ it 'allows commenting' do
+ should_allow_commenting(find('[id="6eb14e00385d2fb284765eb1cd8d420d33d63fc9_22_22"]'))
+ end
+ end
+
+ context 'with an unchanged line' do
+ it 'allows commenting' do
+ should_allow_commenting(find('[id="2f6fcd96b88b36ce98c38da085c795a27d92a3dd_7_7"]'))
+ end
+ end
+
+ context 'with a match line' do
+ it 'does not allow commenting' do
+ should_not_allow_commenting(find('.match', match: :first))
+ end
+ end
+ end
+
+ def should_allow_commenting(line_holder, diff_side = nil, asset_form_reset: true)
+ write_comment_on_line(line_holder, diff_side)
+
+ click_button 'Comment'
+ wait_for_ajax
+
+ assert_comment_persistence(line_holder, asset_form_reset: asset_form_reset)
+ end
+
+ def should_allow_dismissing_a_comment(line_holder, diff_side = nil)
+ write_comment_on_line(line_holder, diff_side)
+
+ find('.js-close-discussion-note-form').trigger('click')
+
+ assert_comment_dismissal(line_holder)
+ end
+
+ def should_not_allow_commenting(line_holder, diff_side = nil)
+ line = get_line_components(line_holder, diff_side)
+ line[:content].hover
+ expect(line[:num]).not_to have_css comment_button_class
+ end
+
+ def get_line_components(line_holder, diff_side = nil)
+ if diff_side.nil?
+ get_inline_line_components(line_holder)
+ else
+ get_parallel_line_components(line_holder, diff_side)
+ end
+ end
+
+ def get_inline_line_components(line_holder)
+ { content: line_holder.find('.line_content', match: :first), num: line_holder.find('.diff-line-num', match: :first) }
+ end
+
+ def get_parallel_line_components(line_holder, diff_side = nil)
+ side_index = diff_side == 'left' ? 0 : 1
+ # Wait for `.line_content`
+ line_holder.find('.line_content', match: :first)
+ # Wait for `.diff-line-num`
+ line_holder.find('.diff-line-num', match: :first)
+ { content: line_holder.all('.line_content')[side_index], num: line_holder.all('.diff-line-num')[side_index] }
+ end
+
+ def click_diff_line(line_holder, diff_side = nil)
+ line = get_line_components(line_holder, diff_side)
+ line[:content].hover
+
+ expect(line[:num]).to have_css comment_button_class
+
+ line[:num].find(comment_button_class).trigger 'click'
+ end
+
+ def write_comment_on_line(line_holder, diff_side)
+ click_diff_line(line_holder, diff_side)
+
+ notes_holder_input = line_holder.find(:xpath, notes_holder_input_xpath)
+
+ expect(notes_holder_input[:class]).to include(notes_holder_input_class)
+
+ notes_holder_input.fill_in 'note[note]', with: test_note_comment
+ end
+
+ def assert_comment_persistence(line_holder, asset_form_reset:)
+ notes_holder_saved = line_holder.find(:xpath, notes_holder_input_xpath)
+
+ expect(notes_holder_saved[:class]).not_to include(notes_holder_input_class)
+ expect(notes_holder_saved).to have_content test_note_comment
+
+ assert_form_is_reset if asset_form_reset
+ end
+
+ def assert_comment_dismissal(line_holder)
+ expect(line_holder).not_to have_xpath notes_holder_input_xpath
+ expect(page).not_to have_content test_note_comment
+
+ assert_form_is_reset
+ end
+
+ def assert_form_is_reset
+ expect(page).to have_no_css('.js-temp-notes-holder')
+ end
+end
diff --git a/spec/features/merge_requests/user_posts_notes_spec.rb b/spec/features/merge_requests/user_posts_notes_spec.rb
new file mode 100644
index 00000000000..7fc0e2ce6ec
--- /dev/null
+++ b/spec/features/merge_requests/user_posts_notes_spec.rb
@@ -0,0 +1,146 @@
+require 'spec_helper'
+
+describe 'Merge requests > User posts notes', :js do
+ let(:project) { create(:project) }
+ let(:merge_request) do
+ create(:merge_request, source_project: project, target_project: project)
+ end
+ let!(:note) do
+ create(:note_on_merge_request, :with_attachment, noteable: merge_request,
+ project: project)
+ end
+
+ before do
+ login_as :admin
+ visit namespace_project_merge_request_path(project.namespace, project, merge_request)
+ end
+
+ subject { page }
+
+ describe 'the note form' do
+ it 'is valid' do
+ is_expected.to have_css('.js-main-target-form', visible: true, count: 1)
+ expect(find('.js-main-target-form .js-comment-button').value).
+ to eq('Comment')
+ page.within('.js-main-target-form') do
+ expect(page).not_to have_link('Cancel')
+ end
+ end
+
+ describe 'with text' do
+ before do
+ page.within('.js-main-target-form') do
+ fill_in 'note[note]', with: 'This is awesome'
+ end
+ end
+
+ it 'has enable submit button and preview button' do
+ page.within('.js-main-target-form') do
+ expect(page).not_to have_css('.js-comment-button[disabled]')
+ expect(page).to have_css('.js-md-preview-button', visible: true)
+ end
+ end
+ end
+ end
+
+ describe 'when posting a note' do
+ before do
+ page.within('.js-main-target-form') do
+ fill_in 'note[note]', with: 'This is awesome!'
+ find('.js-md-preview-button').click
+ click_button 'Comment'
+ end
+ end
+
+ it 'is added and form reset' do
+ is_expected.to have_content('This is awesome!')
+ page.within('.js-main-target-form') do
+ expect(page).to have_no_field('note[note]', with: 'This is awesome!')
+ expect(page).to have_css('.js-md-preview', visible: :hidden)
+ end
+ page.within('.js-main-target-form') do
+ is_expected.to have_css('.js-note-text', visible: true)
+ end
+ end
+ end
+
+ describe 'when editing a note' do
+ it 'there should be a hidden edit form' do
+ is_expected.to have_css('.note-edit-form:not(.mr-note-edit-form)', visible: false, count: 1)
+ is_expected.to have_css('.note-edit-form.mr-note-edit-form', visible: false, count: 1)
+ end
+
+ describe 'editing the note' do
+ before do
+ find('.note').hover
+ find('.js-note-edit').click
+ end
+
+ it 'shows the note edit form and hide the note body' do
+ page.within("#note_#{note.id}") do
+ expect(find('.current-note-edit-form', visible: true)).to be_visible
+ expect(find('.note-edit-form', visible: true)).to be_visible
+ expect(find(:css, '.note-body > .note-text', visible: false)).not_to be_visible
+ end
+ end
+
+ it 'resets the edit note form textarea with the original content of the note if cancelled' do
+ within('.current-note-edit-form') do
+ fill_in 'note[note]', with: 'Some new content'
+ find('.btn-cancel').click
+ expect(find('.js-note-text', visible: false).text).to eq ''
+ end
+ end
+
+ it 'allows using markdown buttons after saving a note and then trying to edit it again' do
+ page.within('.current-note-edit-form') do
+ fill_in 'note[note]', with: 'This is the new content'
+ find('.btn-save').click
+ end
+
+ wait_for_ajax
+ find('.note').hover
+ find('.js-note-edit').click
+
+ page.within('.current-note-edit-form') do
+ expect(find('#note_note').value).to eq('This is the new content')
+ find('.js-md:first-child').click
+ expect(find('#note_note').value).to eq('This is the new content****')
+ end
+ end
+
+ it 'appends the edited at time to the note' do
+ page.within('.current-note-edit-form') do
+ fill_in 'note[note]', with: 'Some new content'
+ find('.btn-save').click
+ end
+
+ page.within("#note_#{note.id}") do
+ is_expected.to have_css('.note_edited_ago')
+ expect(find('.note_edited_ago').text).
+ to match(/less than a minute ago/)
+ end
+ end
+ end
+
+ describe 'deleting an attachment' do
+ before do
+ find('.note').hover
+ find('.js-note-edit').click
+ end
+
+ it 'shows the delete link' do
+ page.within('.note-attachment') do
+ is_expected.to have_css('.js-note-attachment-delete')
+ end
+ end
+
+ it 'removes the attachment div and resets the edit form' do
+ find('.js-note-attachment-delete').click
+ is_expected.not_to have_css('.note-attachment')
+ is_expected.not_to have_css('.current-note-edit-form')
+ wait_for_ajax
+ end
+ end
+ end
+end
diff --git a/spec/features/merge_requests/user_sees_system_notes_spec.rb b/spec/features/merge_requests/user_sees_system_notes_spec.rb
new file mode 100644
index 00000000000..55d0f9d728c
--- /dev/null
+++ b/spec/features/merge_requests/user_sees_system_notes_spec.rb
@@ -0,0 +1,31 @@
+require 'spec_helper'
+
+feature 'Merge requests > User sees system notes' do
+ let(:public_project) { create(:project, :public) }
+ let(:private_project) { create(:project, :private) }
+ let(:issue) { create(:issue, project: private_project) }
+ let(:merge_request) { create(:merge_request, source_project: public_project, source_branch: 'markdown') }
+ let!(:note) { create(:note_on_merge_request, :system, noteable: merge_request, project: public_project, note: "mentioned in #{issue.to_reference(public_project)}") }
+
+ context 'when logged-in as a member of the private project' do
+ before do
+ user = create(:user)
+ private_project.add_developer(user)
+ login_as(user)
+ end
+
+ it 'shows the system note' do
+ visit namespace_project_merge_request_path(public_project.namespace, public_project, merge_request)
+
+ expect(page).to have_css('.system-note')
+ end
+ end
+
+ context 'when not logged-in' do
+ it 'hides the system note' do
+ visit namespace_project_merge_request_path(public_project.namespace, public_project, merge_request)
+
+ expect(page).not_to have_css('.system-note')
+ end
+ end
+end
diff --git a/spec/features/merge_requests/user_uses_slash_commands_spec.rb b/spec/features/merge_requests/user_uses_slash_commands_spec.rb
index a1f4eb2688b..f0ad57eb92f 100644
--- a/spec/features/merge_requests/user_uses_slash_commands_spec.rb
+++ b/spec/features/merge_requests/user_uses_slash_commands_spec.rb
@@ -2,7 +2,6 @@ require 'rails_helper'
feature 'Merge Requests > User uses slash commands', feature: true, js: true do
include SlashCommandsHelpers
- include WaitForAjax
let(:user) { create(:user) }
let(:project) { create(:project, :public) }
@@ -161,6 +160,7 @@ feature 'Merge Requests > User uses slash commands', feature: true, js: true do
it 'changes target branch from a note' do
write_note("message start \n/target_branch merge-test\n message end.")
+ wait_for_ajax
expect(page).not_to have_content('/target_branch')
expect(page).to have_content('message start')
expect(page).to have_content('message end.')
diff --git a/spec/features/merge_requests/merge_request_versions_spec.rb b/spec/features/merge_requests/versions_spec.rb
index 04e85ed3f73..2b5b803946c 100644
--- a/spec/features/merge_requests/merge_request_versions_spec.rb
+++ b/spec/features/merge_requests/versions_spec.rb
@@ -24,7 +24,12 @@ feature 'Merge Request versions', js: true, feature: true do
before do
page.within '.mr-version-dropdown' do
find('.btn-default').click
- find(:link, 'version 1').trigger('click')
+ click_link 'version 1'
+ end
+
+ # Wait for the page to load
+ page.within '.mr-version-dropdown' do
+ expect(page).to have_content 'version 1'
end
end
@@ -36,8 +41,44 @@ feature 'Merge Request versions', js: true, feature: true do
expect(page).to have_content '5 changed files'
end
- it 'show the message about disabled comments' do
- expect(page).to have_content 'Comments are disabled'
+ it 'show the message about comments' do
+ expect(page).to have_content 'Not all comments are displayed'
+ end
+
+ it 'shows comments that were last relevant at that version' do
+ position = Gitlab::Diff::Position.new(
+ old_path: ".gitmodules",
+ new_path: ".gitmodules",
+ old_line: nil,
+ new_line: 4,
+ diff_refs: merge_request_diff1.diff_refs
+ )
+ outdated_diff_note = create(:diff_note_on_merge_request, project: project, noteable: merge_request, position: position)
+ outdated_diff_note.position = outdated_diff_note.original_position
+ outdated_diff_note.save!
+
+ visit current_url
+
+ expect(page).to have_css(".diffs .notes[data-discussion-id='#{outdated_diff_note.discussion_id}']")
+ end
+
+ it 'allows commenting' do
+ diff_file_selector = ".diff-file[id='7445606fbf8f3683cd42bdc54b05d7a0bc2dfc44']"
+ line_code = '7445606fbf8f3683cd42bdc54b05d7a0bc2dfc44_2_2'
+
+ page.within(diff_file_selector) do
+ find(".line_holder[id='#{line_code}'] td:nth-of-type(1)").trigger 'mouseover'
+ find(".line_holder[id='#{line_code}'] button").trigger 'click'
+
+ page.within("form[data-line-code='#{line_code}']") do
+ fill_in "note[note]", with: "Typo, please fix"
+ find(".js-comment-button").click
+ end
+
+ wait_for_ajax
+
+ expect(page).to have_content("Typo, please fix")
+ end
end
end
@@ -45,7 +86,12 @@ feature 'Merge Request versions', js: true, feature: true do
before do
page.within '.mr-version-compare-dropdown' do
find('.btn-default').click
- find(:link, 'version 1').trigger('click')
+ click_link 'version 1'
+ end
+
+ # Wait for the page to load
+ page.within '.mr-version-compare-dropdown' do
+ expect(page).to have_content 'version 1'
end
end
@@ -65,8 +111,43 @@ feature 'Merge Request versions', js: true, feature: true do
end
end
- it 'show the message about disabled comments' do
- expect(page).to have_content 'Comments are disabled'
+ it 'show the message about comments' do
+ expect(page).to have_content 'Not all comments are displayed'
+ end
+
+ it 'shows comments that were last relevant at that version' do
+ position = Gitlab::Diff::Position.new(
+ old_path: ".gitmodules",
+ new_path: ".gitmodules",
+ old_line: 4,
+ new_line: 4,
+ diff_refs: merge_request_diff3.compare_with(merge_request_diff1.head_commit_sha).diff_refs
+ )
+ outdated_diff_note = create(:diff_note_on_merge_request, project: project, noteable: merge_request, position: position)
+
+ visit current_url
+ wait_for_ajax
+
+ expect(page).to have_css(".diffs .notes[data-discussion-id='#{outdated_diff_note.discussion_id}']")
+ end
+
+ it 'allows commenting' do
+ diff_file_selector = ".diff-file[id='7445606fbf8f3683cd42bdc54b05d7a0bc2dfc44']"
+ line_code = '7445606fbf8f3683cd42bdc54b05d7a0bc2dfc44_4_4'
+
+ page.within(diff_file_selector) do
+ find(".line_holder[id='#{line_code}'] td:nth-of-type(1)").trigger 'mouseover'
+ find(".line_holder[id='#{line_code}'] button").trigger 'click'
+
+ page.within("form[data-line-code='#{line_code}']") do
+ fill_in "note[note]", with: "Typo, please fix"
+ find(".js-comment-button").click
+ end
+
+ wait_for_ajax
+
+ expect(page).to have_content("Typo, please fix")
+ end
end
it 'show diff between new and old version' do
@@ -92,14 +173,13 @@ feature 'Merge Request versions', js: true, feature: true do
it 'should have 0 chages between versions' do
page.within '.mr-version-compare-dropdown' do
- expect(page).to have_content 'version 1'
+ expect(find('.dropdown-toggle')).to have_content 'version 1'
end
page.within '.mr-version-dropdown' do
find('.btn-default').click
- find(:link, 'version 1').trigger('click')
+ click_link 'version 1'
end
-
expect(page).to have_content '0 changed files'
end
end
@@ -114,12 +194,12 @@ feature 'Merge Request versions', js: true, feature: true do
it 'should set the compared versions to be the same' do
page.within '.mr-version-compare-dropdown' do
- expect(page).to have_content 'version 2'
+ expect(find('.dropdown-toggle')).to have_content 'version 2'
end
page.within '.mr-version-dropdown' do
find('.btn-default').click
- find(:link, 'version 1').trigger('click')
+ click_link 'version 1'
end
page.within '.mr-version-compare-dropdown' do
diff --git a/spec/features/merge_requests/widget_deployments_spec.rb b/spec/features/merge_requests/widget_deployments_spec.rb
index 6676821b807..8370499f6ed 100644
--- a/spec/features/merge_requests/widget_deployments_spec.rb
+++ b/spec/features/merge_requests/widget_deployments_spec.rb
@@ -1,8 +1,6 @@
require 'spec_helper'
feature 'Widget Deployments Header', feature: true, js: true do
- include WaitForAjax
-
describe 'when deployed to an environment' do
given(:user) { create(:user) }
given(:project) { merge_request.target_project }
@@ -23,7 +21,7 @@ feature 'Widget Deployments Header', feature: true, js: true do
wait_for_ajax
expect(page).to have_content("Deployed to #{environment.name}")
- expect(find('.ci_widget > span > span')['data-title']).to eq(deployment.created_at.to_time.in_time_zone.to_s(:medium))
+ expect(find('.js-deploy-time')['data-title']).to eq(deployment.created_at.to_time.in_time_zone.to_s(:medium))
end
context 'with stop action' do
@@ -40,11 +38,11 @@ feature 'Widget Deployments Header', feature: true, js: true do
end
scenario 'does show stop button' do
- expect(page).to have_link('Stop environment')
+ expect(page).to have_button('Stop environment')
end
scenario 'does start build when stop button clicked' do
- click_link('Stop environment')
+ click_button('Stop environment')
expect(page).to have_content('close_app')
end
@@ -53,7 +51,7 @@ feature 'Widget Deployments Header', feature: true, js: true do
given(:role) { :reporter }
scenario 'does not show stop button' do
- expect(page).not_to have_link('Stop environment')
+ expect(page).not_to have_button('Stop environment')
end
end
end
diff --git a/spec/features/merge_requests/widget_spec.rb b/spec/features/merge_requests/widget_spec.rb
index c2db7d8da3c..ae799584c0f 100644
--- a/spec/features/merge_requests/widget_spec.rb
+++ b/spec/features/merge_requests/widget_spec.rb
@@ -1,8 +1,6 @@
require 'rails_helper'
describe 'Merge request', :feature, :js do
- include WaitForAjax
-
let(:user) { create(:user) }
let(:project) { create(:project) }
let(:merge_request) { create(:merge_request, source_project: project) }
@@ -32,6 +30,7 @@ describe 'Merge request', :feature, :js do
wait_for_ajax
expect(page).to have_selector('.accept-merge-request')
+ expect(find('.accept-merge-request')['disabled']).not_to be(true)
end
end
@@ -53,14 +52,15 @@ describe 'Merge request', :feature, :js do
page.within('.mr-widget-heading') do
expect(page).to have_content("Deployed to #{environment.name}")
- expect(find('.js-environment-link')[:href]).to include(environment.formatted_external_url)
+ expect(find('.js-deploy-url')[:href]).to include(environment.formatted_external_url)
end
end
it 'shows green accept merge request button' do
# Wait for the `ci_status` and `merge_check` requests
wait_for_ajax
- expect(page).to have_selector('.accept-merge-request.btn-create')
+ expect(page).to have_selector('.accept-merge-request')
+ expect(find('.accept-merge-request')['disabled']).not_to be(true)
end
end
@@ -91,6 +91,8 @@ describe 'Merge request', :feature, :js do
statuses: [commit_status])
create(:ci_build, :pending, pipeline: pipeline)
+ merge_request.update(head_pipeline: pipeline)
+
visit namespace_project_merge_request_path(project.namespace, project, merge_request)
end
@@ -103,10 +105,15 @@ describe 'Merge request', :feature, :js do
context 'when merge request is in the blocked pipeline state' do
before do
- create(:ci_pipeline, project: project,
- sha: merge_request.diff_head_sha,
- ref: merge_request.source_branch,
- status: :manual)
+ pipeline = create(
+ :ci_pipeline,
+ project: project,
+ sha: merge_request.diff_head_sha,
+ ref: merge_request.source_branch,
+ status: :manual
+ )
+
+ merge_request.update(head_pipeline: pipeline)
visit namespace_project_merge_request_path(project.namespace,
project,
@@ -131,13 +138,57 @@ describe 'Merge request', :feature, :js do
statuses: [commit_status])
create(:ci_build, :pending, pipeline: pipeline)
+ merge_request.update(head_pipeline: pipeline)
+
visit namespace_project_merge_request_path(project.namespace, project, merge_request)
end
it 'has info button when MWBS button' do
# Wait for the `ci_status` and `merge_check` requests
wait_for_ajax
- expect(page).to have_selector('.merge-when-pipeline-succeeds.btn-info')
+ expect(page).to have_selector('.accept-merge-request.btn-info')
+ end
+ end
+
+ context 'view merge request with MWPS enabled but automatically merge fails' do
+ before do
+ merge_request.update(
+ merge_when_pipeline_succeeds: true,
+ merge_user: merge_request.author,
+ merge_error: 'Something went wrong'
+ )
+
+ visit namespace_project_merge_request_path(project.namespace, project, merge_request)
+ end
+
+ it 'shows information about the merge error' do
+ # Wait for the `ci_status` and `merge_check` requests
+ wait_for_ajax
+
+ page.within('.mr-widget-body') do
+ expect(page).to have_content('Something went wrong')
+ end
+ end
+ end
+
+ context 'view merge request with MWPS enabled but automatically merge fails' do
+ before do
+ merge_request.update(
+ merge_when_pipeline_succeeds: true,
+ merge_user: merge_request.author,
+ merge_error: 'Something went wrong'
+ )
+
+ visit namespace_project_merge_request_path(project.namespace, project, merge_request)
+ end
+
+ it 'shows information about the merge error' do
+ # Wait for the `ci_status` and `merge_check` requests
+ wait_for_ajax
+
+ page.within('.mr-widget-body') do
+ expect(page).to have_content('Something went wrong')
+ end
end
end
@@ -145,11 +196,11 @@ describe 'Merge request', :feature, :js do
before do
allow_any_instance_of(Repository).to receive(:merge).and_return(false)
visit namespace_project_merge_request_path(project.namespace, project, merge_request)
- click_button 'Accept Merge Request'
- wait_for_ajax
end
it 'updates the MR widget' do
+ click_button 'Merge'
+
page.within('.mr-widget-body') do
expect(page).to have_content('Conflicts detected during merge')
end
diff --git a/spec/features/milestone_spec.rb b/spec/features/milestone_spec.rb
index c3297de709a..c07de01c594 100644
--- a/spec/features/milestone_spec.rb
+++ b/spec/features/milestone_spec.rb
@@ -1,8 +1,6 @@
require 'rails_helper'
feature 'Milestone', feature: true do
- include WaitForAjax
-
let(:project) { create(:empty_project, :public) }
let(:user) { create(:user) }
diff --git a/spec/features/milestones/milestones_spec.rb b/spec/features/milestones/milestones_spec.rb
index 8de9942c54e..9eec3d7f270 100644
--- a/spec/features/milestones/milestones_spec.rb
+++ b/spec/features/milestones/milestones_spec.rb
@@ -1,7 +1,6 @@
require 'rails_helper'
describe 'Milestone draggable', feature: true, js: true do
- include WaitForAjax
include DragTo
let(:milestone) { create(:milestone, project: project, title: 8.14) }
@@ -76,6 +75,7 @@ describe 'Milestone draggable', feature: true, js: true do
create(:issue, params.merge(title: 'Foo', project: project, milestone: milestone))
visit namespace_project_milestone_path(project.namespace, project, milestone)
+ scroll_into_view('.milestone-content')
drag_to(selector: '.issues-sortable-list', list_to_index: 1)
wait_for_ajax
@@ -86,8 +86,16 @@ describe 'Milestone draggable', feature: true, js: true do
visit namespace_project_milestone_path(project.namespace, project, milestone)
page.find("a[href='#tab-merge-requests']").click
+
+ wait_for_ajax
+
+ scroll_into_view('.milestone-content')
drag_to(selector: '.merge_requests-sortable-list', list_to_index: 1)
wait_for_ajax
end
+
+ def scroll_into_view(selector)
+ page.evaluate_script("document.querySelector('#{selector}').scrollIntoView();")
+ end
end
diff --git a/spec/features/milestones/show_spec.rb b/spec/features/milestones/show_spec.rb
index 40b4dc63697..227eb04ba72 100644
--- a/spec/features/milestones/show_spec.rb
+++ b/spec/features/milestones/show_spec.rb
@@ -5,7 +5,7 @@ describe 'Milestone show', feature: true do
let(:project) { create(:empty_project) }
let(:milestone) { create(:milestone, project: project) }
let(:labels) { create_list(:label, 2, project: project) }
- let(:issue_params) { { project: project, assignee: user, author: user, milestone: milestone, labels: labels } }
+ let(:issue_params) { { project: project, assignees: [user], author: user, milestone: milestone, labels: labels } }
before do
project.add_user(user, :developer)
diff --git a/spec/features/notes_on_merge_requests_spec.rb b/spec/features/notes_on_merge_requests_spec.rb
deleted file mode 100644
index fab2d532e06..00000000000
--- a/spec/features/notes_on_merge_requests_spec.rb
+++ /dev/null
@@ -1,285 +0,0 @@
-require 'spec_helper'
-
-describe 'Comments', feature: true do
- include RepoHelpers
- include WaitForAjax
-
- describe 'On a merge request', js: true, feature: true do
- let!(:project) { create(:project) }
- let!(:merge_request) do
- create(:merge_request, source_project: project, target_project: project)
- end
-
- let!(:note) do
- create(:note_on_merge_request, :with_attachment, noteable: merge_request,
- project: project)
- end
-
- before do
- login_as :admin
- visit namespace_project_merge_request_path(project.namespace, project, merge_request)
- end
-
- subject { page }
-
- describe 'the note form' do
- it 'is valid' do
- is_expected.to have_css('.js-main-target-form', visible: true, count: 1)
- expect(find('.js-main-target-form input[type=submit]').value).
- to eq('Comment')
- page.within('.js-main-target-form') do
- expect(page).not_to have_link('Cancel')
- end
- end
-
- describe 'with text' do
- before do
- page.within('.js-main-target-form') do
- fill_in 'note[note]', with: 'This is awesome'
- end
- end
-
- it 'has enable submit button and preview button' do
- page.within('.js-main-target-form') do
- expect(page).not_to have_css('.js-comment-button[disabled]')
- expect(page).to have_css('.js-md-preview-button', visible: true)
- end
- end
- end
- end
-
- describe 'when posting a note' do
- before do
- page.within('.js-main-target-form') do
- fill_in 'note[note]', with: 'This is awsome!'
- find('.js-md-preview-button').click
- click_button 'Comment'
- end
- end
-
- it 'is added and form reset' do
- is_expected.to have_content('This is awsome!')
- page.within('.js-main-target-form') do
- expect(page).to have_no_field('note[note]', with: 'This is awesome!')
- expect(page).to have_css('.js-md-preview', visible: :hidden)
- end
- page.within('.js-main-target-form') do
- is_expected.to have_css('.js-note-text', visible: true)
- end
- end
- end
-
- describe 'when editing a note', js: true do
- it 'there should be a hidden edit form' do
- is_expected.to have_css('.note-edit-form:not(.mr-note-edit-form)', visible: false, count: 1)
- is_expected.to have_css('.note-edit-form.mr-note-edit-form', visible: false, count: 1)
- end
-
- describe 'editing the note' do
- before do
- find('.note').hover
- find('.js-note-edit').click
- end
-
- it 'shows the note edit form and hide the note body' do
- page.within("#note_#{note.id}") do
- expect(find('.current-note-edit-form', visible: true)).to be_visible
- expect(find('.note-edit-form', visible: true)).to be_visible
- expect(find(:css, '.note-body > .note-text', visible: false)).not_to be_visible
- end
- end
-
- it 'resets the edit note form textarea with the original content of the note if cancelled' do
- within('.current-note-edit-form') do
- fill_in 'note[note]', with: 'Some new content'
- find('.btn-cancel').click
- expect(find('.js-note-text', visible: false).text).to eq ''
- end
- end
-
- it 'allows using markdown buttons after saving a note and then trying to edit it again' do
- page.within('.current-note-edit-form') do
- fill_in 'note[note]', with: 'This is the new content'
- find('.btn-save').click
- end
-
- find('.note').hover
- find('.js-note-edit').click
-
- page.within('.current-note-edit-form') do
- expect(find('#note_note').value).to eq('This is the new content')
- find('.js-md:first-child').click
- expect(find('#note_note').value).to eq('This is the new content****')
- end
- end
-
- it 'appends the edited at time to the note' do
- page.within('.current-note-edit-form') do
- fill_in 'note[note]', with: 'Some new content'
- find('.btn-save').click
- end
-
- page.within("#note_#{note.id}") do
- is_expected.to have_css('.note_edited_ago')
- expect(find('.note_edited_ago').text).
- to match(/less than a minute ago/)
- end
- end
- end
-
- describe 'deleting an attachment' do
- before do
- find('.note').hover
- find('.js-note-edit').click
- end
-
- it 'shows the delete link' do
- page.within('.note-attachment') do
- is_expected.to have_css('.js-note-attachment-delete')
- end
- end
-
- it 'removes the attachment div and resets the edit form' do
- find('.js-note-attachment-delete').click
- is_expected.not_to have_css('.note-attachment')
- is_expected.not_to have_css('.current-note-edit-form')
- wait_for_ajax
- end
- end
- end
- end
-
- describe 'Handles cross-project system notes', js: true, feature: true do
- let(:user) { create(:user) }
- let(:project) { create(:project, :public) }
- let(:project2) { create(:project, :private) }
- let(:issue) { create(:issue, project: project2) }
- let(:merge_request) { create(:merge_request, source_project: project, source_branch: 'markdown') }
- let!(:note) { create(:note_on_merge_request, :system, noteable: merge_request, project: project, note: "mentioned in #{issue.to_reference(project)}") }
-
- it 'shows the system note' do
- login_as :admin
- visit namespace_project_merge_request_path(project.namespace, project, merge_request)
-
- expect(page).to have_css('.system-note')
- end
-
- it 'hides redacted system note' do
- visit namespace_project_merge_request_path(project.namespace, project, merge_request)
-
- expect(page).not_to have_css('.system-note')
- end
- end
-
- describe 'On a merge request diff', js: true, feature: true do
- let(:merge_request) { create(:merge_request) }
- let(:project) { merge_request.source_project }
-
- before do
- login_as :admin
- visit diffs_namespace_project_merge_request_path(project.namespace, project, merge_request)
- end
-
- subject { page }
-
- describe 'when adding a note' do
- before do
- click_diff_line
- end
-
- describe 'the notes holder' do
- it { is_expected.to have_css('.js-temp-notes-holder') }
-
- it 'has .new_note css class' do
- page.within('.js-temp-notes-holder') do
- expect(subject).to have_css('.new-note')
- end
- end
- end
-
- describe 'the note form' do
- it "does not add a second form for same row" do
- click_diff_line
-
- is_expected.
- to have_css("form[data-line-code='#{line_code}']",
- count: 1)
- end
-
- it 'is removed when canceled' do
- is_expected.to have_css('.js-temp-notes-holder')
-
- page.within("form[data-line-code='#{line_code}']") do
- find('.js-close-discussion-note-form').trigger('click')
- end
-
- is_expected.to have_no_css('.js-temp-notes-holder')
- end
- end
- end
-
- describe 'with muliple note forms' do
- before do
- click_diff_line
- click_diff_line(line_code_2)
- end
-
- it { is_expected.to have_css('.js-temp-notes-holder', count: 2) }
-
- describe 'previewing them separately' do
- before do
- # add two separate texts and trigger previews on both
- page.within("tr[id='#{line_code}'] + .js-temp-notes-holder") do
- fill_in 'note[note]', with: 'One comment on line 7'
- find('.js-md-preview-button').click
- end
- page.within("tr[id='#{line_code_2}'] + .js-temp-notes-holder") do
- fill_in 'note[note]', with: 'Another comment on line 10'
- find('.js-md-preview-button').click
- end
- end
- end
-
- describe 'posting a note' do
- before do
- page.within("tr[id='#{line_code_2}'] + .js-temp-notes-holder") do
- fill_in 'note[note]', with: 'Another comment on line 10'
- click_button('Comment')
- end
- end
-
- it 'adds as discussion' do
- is_expected.to have_content('Another comment on line 10')
- is_expected.to have_css('.notes_holder')
- is_expected.to have_css('.notes_holder .note', count: 1)
- is_expected.to have_button('Reply...')
- end
-
- it 'adds code to discussion' do
- click_button 'Reply...'
-
- page.within(first('.js-discussion-note-form')) do
- fill_in 'note[note]', with: '```{{ test }}```'
-
- click_button('Comment')
- end
-
- expect(page).to have_content('{{ test }}')
- end
- end
- end
- end
-
- def line_code
- sample_compare.changes.first[:line_code]
- end
-
- def line_code_2
- sample_compare.changes.last[:line_code]
- end
-
- def click_diff_line(data = line_code)
- find(".line_holder[id='#{data}'] td.line_content").hover
- find(".line_holder[id='#{data}'] button").trigger('click')
- end
-end
diff --git a/spec/features/participants_autocomplete_spec.rb b/spec/features/participants_autocomplete_spec.rb
index decad589c23..449ce80bc71 100644
--- a/spec/features/participants_autocomplete_spec.rb
+++ b/spec/features/participants_autocomplete_spec.rb
@@ -1,7 +1,7 @@
require 'spec_helper'
feature 'Member autocomplete', :js do
- let(:project) { create(:project, :public) }
+ let(:project) { create(:empty_project, :public) }
let(:user) { create(:user) }
let(:author) { create(:user) }
let(:note) { create(:note, noteable: noteable, project: noteable.project) }
@@ -36,6 +36,7 @@ feature 'Member autocomplete', :js do
end
context 'adding a new note on a Merge Request' do
+ let(:project) { create(:project, :public, :repository) }
let(:noteable) do
create(:merge_request, source_project: project,
target_project: project, author: author)
@@ -48,6 +49,7 @@ feature 'Member autocomplete', :js do
end
context 'adding a new note on a Commit' do
+ let(:project) { create(:project, :public, :repository) }
let(:noteable) { project.commit }
let(:note) { create(:note_on_commit, project: project, commit_id: project.commit.id) }
diff --git a/spec/features/profiles/account_spec.rb b/spec/features/profiles/account_spec.rb
new file mode 100644
index 00000000000..05a7587f8d4
--- /dev/null
+++ b/spec/features/profiles/account_spec.rb
@@ -0,0 +1,59 @@
+require 'rails_helper'
+
+feature 'Profile > Account', feature: true do
+ given(:user) { create(:user, username: 'foo') }
+
+ before do
+ login_as(user)
+ end
+
+ describe 'Change username' do
+ given(:new_username) { 'bar' }
+ given(:new_user_path) { "/#{new_username}" }
+ given(:old_user_path) { "/#{user.username}" }
+
+ scenario 'the user is accessible via the new path' do
+ update_username(new_username)
+ visit new_user_path
+ expect(current_path).to eq(new_user_path)
+ expect(find('.user-info')).to have_content(new_username)
+ end
+
+ scenario 'the old user path redirects to the new path' do
+ update_username(new_username)
+ visit old_user_path
+ expect(current_path).to eq(new_user_path)
+ expect(find('.user-info')).to have_content(new_username)
+ end
+
+ context 'with a project' do
+ given!(:project) { create(:project, namespace: user.namespace, path: 'project') }
+ given(:new_project_path) { "/#{new_username}/#{project.path}" }
+ given(:old_project_path) { "/#{user.username}/#{project.path}" }
+
+ before(:context) { TestEnv.clean_test_path }
+ after(:example) { TestEnv.clean_test_path }
+
+ scenario 'the project is accessible via the new path' do
+ update_username(new_username)
+ visit new_project_path
+ expect(current_path).to eq(new_project_path)
+ expect(find('h1.project-title')).to have_content(project.name)
+ end
+
+ scenario 'the old project path redirects to the new path' do
+ update_username(new_username)
+ visit old_project_path
+ expect(current_path).to eq(new_project_path)
+ expect(find('h1.project-title')).to have_content(project.name)
+ end
+ end
+ end
+end
+
+def update_username(new_username)
+ allow(user.namespace).to receive(:move_dir)
+ visit profile_account_path
+ fill_in 'user_username', with: new_username
+ click_button 'Update username'
+end
diff --git a/spec/features/profiles/personal_access_tokens_spec.rb b/spec/features/profiles/personal_access_tokens_spec.rb
index 0917d4dc3ef..27a20e78a43 100644
--- a/spec/features/profiles/personal_access_tokens_spec.rb
+++ b/spec/features/profiles/personal_access_tokens_spec.rb
@@ -27,7 +27,7 @@ describe 'Profile > Personal Access Tokens', feature: true, js: true do
describe "token creation" do
it "allows creation of a personal access token" do
- name = FFaker::Product.brand
+ name = 'My PAT'
visit profile_personal_access_tokens_path
fill_in "Name", with: name
@@ -41,7 +41,7 @@ describe 'Profile > Personal Access Tokens', feature: true, js: true do
check "api"
check "read_user"
- click_on "Create Personal Access Token"
+ click_on "Create personal access token"
expect(active_personal_access_tokens).to have_text(name)
expect(active_personal_access_tokens).to have_text('In')
expect(active_personal_access_tokens).to have_text('api')
@@ -52,9 +52,9 @@ describe 'Profile > Personal Access Tokens', feature: true, js: true do
it "displays an error message" do
disallow_personal_access_token_saves!
visit profile_personal_access_tokens_path
- fill_in "Name", with: FFaker::Product.brand
+ fill_in "Name", with: 'My PAT'
- expect { click_on "Create Personal Access Token" }.not_to change { PersonalAccessToken.count }
+ expect { click_on "Create personal access token" }.not_to change { PersonalAccessToken.count }
expect(page).to have_content("Name cannot be nil")
end
end
diff --git a/spec/features/profiles/preferences_spec.rb b/spec/features/profiles/preferences_spec.rb
index 15c8677fcd3..d368bc4d753 100644
--- a/spec/features/profiles/preferences_spec.rb
+++ b/spec/features/profiles/preferences_spec.rb
@@ -44,7 +44,7 @@ describe 'Profile > Preferences', feature: true do
expect(page.current_path).to eq starred_dashboard_projects_path
end
- click_link 'Your projects'
+ find('.shortcuts-activity').trigger('click')
expect(page).not_to have_content("You don't have starred projects yet")
expect(page.current_path).to eq dashboard_projects_path
diff --git a/spec/features/projects/artifacts/file_spec.rb b/spec/features/projects/artifacts/file_spec.rb
new file mode 100644
index 00000000000..74308a7e8dd
--- /dev/null
+++ b/spec/features/projects/artifacts/file_spec.rb
@@ -0,0 +1,59 @@
+require 'spec_helper'
+
+feature 'Artifact file', :js, feature: true do
+ let(:project) { create(:project, :public) }
+ let(:pipeline) { create(:ci_empty_pipeline, project: project, sha: project.commit.sha, ref: 'master') }
+ let(:build) { create(:ci_build, :artifacts, pipeline: pipeline) }
+
+ def visit_file(path)
+ visit file_namespace_project_build_artifacts_path(project.namespace, project, build, path)
+ end
+
+ context 'Text file' do
+ before do
+ visit_file('other_artifacts_0.1.2/doc_sample.txt')
+
+ wait_for_ajax
+ end
+
+ it 'displays an error' do
+ aggregate_failures do
+ # shows an error message
+ expect(page).to have_content('The source could not be displayed because it is stored as a job artifact. You can download it instead.')
+
+ # does not show a viewer switcher
+ expect(page).not_to have_selector('.js-blob-viewer-switcher')
+
+ # does not show a copy button
+ expect(page).not_to have_selector('.js-copy-blob-source-btn')
+
+ # shows a download button
+ expect(page).to have_link('Download')
+ end
+ end
+ end
+
+ context 'JPG file' do
+ before do
+ visit_file('rails_sample.jpg')
+
+ wait_for_ajax
+ end
+
+ it 'displays the blob' do
+ aggregate_failures do
+ # shows rendered image
+ expect(page).to have_selector('.image_file img')
+
+ # does not show a viewer switcher
+ expect(page).not_to have_selector('.js-blob-viewer-switcher')
+
+ # does not show a copy button
+ expect(page).not_to have_selector('.js-copy-blob-source-btn')
+
+ # shows a download button
+ expect(page).to have_link('Download')
+ end
+ end
+ end
+end
diff --git a/spec/features/projects/blobs/blob_show_spec.rb b/spec/features/projects/blobs/blob_show_spec.rb
new file mode 100644
index 00000000000..fc242082278
--- /dev/null
+++ b/spec/features/projects/blobs/blob_show_spec.rb
@@ -0,0 +1,466 @@
+require 'spec_helper'
+
+feature 'File blob', :js, feature: true do
+ let(:project) { create(:project, :public) }
+
+ def visit_blob(path, fragment = nil)
+ visit namespace_project_blob_path(project.namespace, project, File.join('master', path), anchor: fragment)
+
+ wait_for_ajax
+ end
+
+ context 'Ruby file' do
+ before do
+ visit_blob('files/ruby/popen.rb')
+ end
+
+ it 'displays the blob' do
+ aggregate_failures do
+ # shows highlighted Ruby code
+ expect(page).to have_content("require 'fileutils'")
+
+ # does not show a viewer switcher
+ expect(page).not_to have_selector('.js-blob-viewer-switcher')
+
+ # shows an enabled copy button
+ expect(page).to have_selector('.js-copy-blob-source-btn:not(.disabled)')
+
+ # shows a raw button
+ expect(page).to have_link('Open raw')
+ end
+ end
+ end
+
+ context 'Markdown file' do
+ context 'visiting directly' do
+ before do
+ visit_blob('files/markdown/ruby-style-guide.md')
+ end
+
+ it 'displays the blob using the rich viewer' do
+ aggregate_failures do
+ # hides the simple viewer
+ expect(page).to have_selector('.blob-viewer[data-type="simple"]', visible: false)
+ expect(page).to have_selector('.blob-viewer[data-type="rich"]')
+
+ # shows rendered Markdown
+ expect(page).to have_link("PEP-8")
+
+ # shows a viewer switcher
+ expect(page).to have_selector('.js-blob-viewer-switcher')
+
+ # shows a disabled copy button
+ expect(page).to have_selector('.js-copy-blob-source-btn.disabled')
+
+ # shows a raw button
+ expect(page).to have_link('Open raw')
+ end
+ end
+
+ context 'switching to the simple viewer' do
+ before do
+ find('.js-blob-viewer-switch-btn[data-viewer=simple]').click
+
+ wait_for_ajax
+ end
+
+ it 'displays the blob using the simple viewer' do
+ aggregate_failures do
+ # hides the rich viewer
+ expect(page).to have_selector('.blob-viewer[data-type="simple"]')
+ expect(page).to have_selector('.blob-viewer[data-type="rich"]', visible: false)
+
+ # shows highlighted Markdown code
+ expect(page).to have_content("[PEP-8](http://www.python.org/dev/peps/pep-0008/)")
+
+ # shows an enabled copy button
+ expect(page).to have_selector('.js-copy-blob-source-btn:not(.disabled)')
+ end
+ end
+
+ context 'switching to the rich viewer again' do
+ before do
+ find('.js-blob-viewer-switch-btn[data-viewer=rich]').click
+
+ wait_for_ajax
+ end
+
+ it 'displays the blob using the rich viewer' do
+ aggregate_failures do
+ # hides the simple viewer
+ expect(page).to have_selector('.blob-viewer[data-type="simple"]', visible: false)
+ expect(page).to have_selector('.blob-viewer[data-type="rich"]')
+
+ # shows an enabled copy button
+ expect(page).to have_selector('.js-copy-blob-source-btn:not(.disabled)')
+ end
+ end
+ end
+ end
+ end
+
+ context 'visiting with a line number anchor' do
+ before do
+ visit_blob('files/markdown/ruby-style-guide.md', 'L1')
+ end
+
+ it 'displays the blob using the simple viewer' do
+ aggregate_failures do
+ # hides the rich viewer
+ expect(page).to have_selector('.blob-viewer[data-type="simple"]')
+ expect(page).to have_selector('.blob-viewer[data-type="rich"]', visible: false)
+
+ # highlights the line in question
+ expect(page).to have_selector('#LC1.hll')
+
+ # shows highlighted Markdown code
+ expect(page).to have_content("[PEP-8](http://www.python.org/dev/peps/pep-0008/)")
+
+ # shows an enabled copy button
+ expect(page).to have_selector('.js-copy-blob-source-btn:not(.disabled)')
+ end
+ end
+ end
+ end
+
+ context 'Markdown file (stored in LFS)' do
+ before do
+ project.add_master(project.creator)
+
+ Files::CreateService.new(
+ project,
+ project.creator,
+ start_branch: 'master',
+ branch_name: 'master',
+ commit_message: "Add Markdown in LFS",
+ file_path: 'files/lfs/file.md',
+ file_content: project.repository.blob_at('master', 'files/lfs/lfs_object.iso').data
+ ).execute
+ end
+
+ context 'when LFS is enabled on the project' do
+ before do
+ allow(Gitlab.config.lfs).to receive(:enabled).and_return(true)
+ project.update_attribute(:lfs_enabled, true)
+
+ visit_blob('files/lfs/file.md')
+ end
+
+ it 'displays an error' do
+ aggregate_failures do
+ # hides the simple viewer
+ expect(page).to have_selector('.blob-viewer[data-type="simple"]', visible: false)
+ expect(page).to have_selector('.blob-viewer[data-type="rich"]')
+
+ # shows an error message
+ expect(page).to have_content('The rendered file could not be displayed because it is stored in LFS. You can download it instead.')
+
+ # shows a viewer switcher
+ expect(page).to have_selector('.js-blob-viewer-switcher')
+
+ # does not show a copy button
+ expect(page).not_to have_selector('.js-copy-blob-source-btn')
+
+ # shows a download button
+ expect(page).to have_link('Download')
+ end
+ end
+
+ context 'switching to the simple viewer' do
+ before do
+ find('.js-blob-viewer-switcher .js-blob-viewer-switch-btn[data-viewer=simple]').click
+
+ wait_for_ajax
+ end
+
+ it 'displays an error' do
+ aggregate_failures do
+ # hides the rich viewer
+ expect(page).to have_selector('.blob-viewer[data-type="simple"]')
+ expect(page).to have_selector('.blob-viewer[data-type="rich"]', visible: false)
+
+ # shows an error message
+ expect(page).to have_content('The source could not be displayed because it is stored in LFS. You can download it instead.')
+
+ # does not show a copy button
+ expect(page).not_to have_selector('.js-copy-blob-source-btn')
+ end
+ end
+ end
+ end
+
+ context 'when LFS is disabled on the project' do
+ before do
+ visit_blob('files/lfs/file.md')
+ end
+
+ it 'displays the blob' do
+ aggregate_failures do
+ # shows text
+ expect(page).to have_content('size 1575078')
+
+ # does not show a viewer switcher
+ expect(page).not_to have_selector('.js-blob-viewer-switcher')
+
+ # shows an enabled copy button
+ expect(page).to have_selector('.js-copy-blob-source-btn:not(.disabled)')
+
+ # shows a raw button
+ expect(page).to have_link('Open raw')
+ end
+ end
+ end
+ end
+
+ context 'PDF file' do
+ before do
+ project.add_master(project.creator)
+
+ Files::CreateService.new(
+ project,
+ project.creator,
+ start_branch: 'master',
+ branch_name: 'master',
+ commit_message: "Add PDF",
+ file_path: 'files/test.pdf',
+ file_content: project.repository.blob_at('add-pdf-file', 'files/pdf/test.pdf').data
+ ).execute
+
+ visit_blob('files/test.pdf')
+ end
+
+ it 'displays the blob' do
+ aggregate_failures do
+ # shows rendered PDF
+ expect(page).to have_selector('.js-pdf-viewer')
+
+ # does not show a viewer switcher
+ expect(page).not_to have_selector('.js-blob-viewer-switcher')
+
+ # does not show a copy button
+ expect(page).not_to have_selector('.js-copy-blob-source-btn')
+
+ # shows a download button
+ expect(page).to have_link('Download')
+ end
+ end
+ end
+
+ context 'ISO file (stored in LFS)' do
+ context 'when LFS is enabled on the project' do
+ before do
+ allow(Gitlab.config.lfs).to receive(:enabled).and_return(true)
+ project.update_attribute(:lfs_enabled, true)
+
+ visit_blob('files/lfs/lfs_object.iso')
+ end
+
+ it 'displays the blob' do
+ aggregate_failures do
+ # shows a download link
+ expect(page).to have_link('Download (1.5 MB)')
+
+ # does not show a viewer switcher
+ expect(page).not_to have_selector('.js-blob-viewer-switcher')
+
+ # does not show a copy button
+ expect(page).not_to have_selector('.js-copy-blob-source-btn')
+
+ # shows a download button
+ expect(page).to have_link('Download')
+ end
+ end
+ end
+
+ context 'when LFS is disabled on the project' do
+ before do
+ visit_blob('files/lfs/lfs_object.iso')
+ end
+
+ it 'displays the blob' do
+ aggregate_failures do
+ # shows text
+ expect(page).to have_content('size 1575078')
+
+ # does not show a viewer switcher
+ expect(page).not_to have_selector('.js-blob-viewer-switcher')
+
+ # shows an enabled copy button
+ expect(page).to have_selector('.js-copy-blob-source-btn:not(.disabled)')
+
+ # shows a raw button
+ expect(page).to have_link('Open raw')
+ end
+ end
+ end
+ end
+
+ context 'ZIP file' do
+ before do
+ visit_blob('Gemfile.zip')
+ end
+
+ it 'displays the blob' do
+ aggregate_failures do
+ # shows a download link
+ expect(page).to have_link('Download (2.11 KB)')
+
+ # does not show a viewer switcher
+ expect(page).not_to have_selector('.js-blob-viewer-switcher')
+
+ # does not show a copy button
+ expect(page).not_to have_selector('.js-copy-blob-source-btn')
+
+ # shows a download button
+ expect(page).to have_link('Download')
+ end
+ end
+ end
+
+ context 'empty file' do
+ before do
+ project.add_master(project.creator)
+
+ Files::CreateService.new(
+ project,
+ project.creator,
+ start_branch: 'master',
+ branch_name: 'master',
+ commit_message: "Add empty file",
+ file_path: 'files/empty.md',
+ file_content: ''
+ ).execute
+
+ visit_blob('files/empty.md')
+ end
+
+ it 'displays an error' do
+ aggregate_failures do
+ # shows an error message
+ expect(page).to have_content('Empty file')
+
+ # does not show a viewer switcher
+ expect(page).not_to have_selector('.js-blob-viewer-switcher')
+
+ # does not show a copy button
+ expect(page).not_to have_selector('.js-copy-blob-source-btn')
+
+ # does not show a download or raw button
+ expect(page).not_to have_link('Download')
+ expect(page).not_to have_link('Open raw')
+ end
+ end
+ end
+
+ context '.gitlab-ci.yml' do
+ before do
+ project.add_master(project.creator)
+
+ Files::CreateService.new(
+ project,
+ project.creator,
+ start_branch: 'master',
+ branch_name: 'master',
+ commit_message: "Add .gitlab-ci.yml",
+ file_path: '.gitlab-ci.yml',
+ file_content: File.read(Rails.root.join('spec/support/gitlab_stubs/gitlab_ci.yml'))
+ ).execute
+
+ visit_blob('.gitlab-ci.yml')
+ end
+
+ it 'displays an auxiliary viewer' do
+ aggregate_failures do
+ # shows that configuration is valid
+ expect(page).to have_content('This GitLab CI configuration is valid.')
+
+ # shows a learn more link
+ expect(page).to have_link('Learn more')
+ end
+ end
+ end
+
+ context '.gitlab/route-map.yml' do
+ before do
+ project.add_master(project.creator)
+
+ Files::CreateService.new(
+ project,
+ project.creator,
+ start_branch: 'master',
+ branch_name: 'master',
+ commit_message: "Add .gitlab/route-map.yml",
+ file_path: '.gitlab/route-map.yml',
+ file_content: <<-MAP.strip_heredoc
+ # Team data
+ - source: 'data/team.yml'
+ public: 'team/'
+ MAP
+ ).execute
+
+ visit_blob('.gitlab/route-map.yml')
+ end
+
+ it 'displays an auxiliary viewer' do
+ aggregate_failures do
+ # shows that map is valid
+ expect(page).to have_content('This Route Map is valid.')
+
+ # shows a learn more link
+ expect(page).to have_link('Learn more')
+ end
+ end
+ end
+
+ context 'LICENSE' do
+ before do
+ visit_blob('LICENSE')
+ end
+
+ it 'displays an auxiliary viewer' do
+ aggregate_failures do
+ # shows license
+ expect(page).to have_content('This project is licensed under the MIT License.')
+
+ # shows a learn more link
+ expect(page).to have_link('Learn more', 'http://choosealicense.com/licenses/mit/')
+ end
+ end
+ end
+
+ context '*.gemspec' do
+ before do
+ project.add_master(project.creator)
+
+ Files::CreateService.new(
+ project,
+ project.creator,
+ start_branch: 'master',
+ branch_name: 'master',
+ commit_message: "Add activerecord.gemspec",
+ file_path: 'activerecord.gemspec',
+ file_content: <<-SPEC.strip_heredoc
+ Gem::Specification.new do |s|
+ s.platform = Gem::Platform::RUBY
+ s.name = "activerecord"
+ end
+ SPEC
+ ).execute
+
+ visit_blob('activerecord.gemspec')
+ end
+
+ it 'displays an auxiliary viewer' do
+ aggregate_failures do
+ # shows names of dependency manager and package
+ expect(page).to have_content('This project manages its dependencies using RubyGems and defines a gem named activerecord.')
+
+ # shows a link to the gem
+ expect(page).to have_link('activerecord', 'https://rubygems.org/gems/activerecord')
+
+ # shows a learn more link
+ expect(page).to have_link('Learn more', 'http://choosealicense.com/licenses/mit/')
+ end
+ end
+ end
+end
diff --git a/spec/features/projects/blobs/edit_spec.rb b/spec/features/projects/blobs/edit_spec.rb
index a820d07ab3b..cc5b1a7e734 100644
--- a/spec/features/projects/blobs/edit_spec.rb
+++ b/spec/features/projects/blobs/edit_spec.rb
@@ -1,45 +1,135 @@
require 'spec_helper'
feature 'Editing file blob', feature: true, js: true do
- include WaitForAjax
+ include TreeHelper
- given(:user) { create(:user) }
- given(:role) { :developer }
- given(:merge_request) { create(:merge_request, source_branch: 'feature', target_branch: 'master') }
- given(:project) { merge_request.target_project }
+ let(:project) { create(:project, :public, :test_repo) }
+ let(:merge_request) { create(:merge_request, source_project: project, source_branch: 'feature', target_branch: 'master') }
+ let(:branch) { 'master' }
+ let(:file_path) { project.repository.ls_files(project.repository.root_ref)[1] }
- background do
- login_as(user)
- project.team << [user, role]
- end
-
- def edit_and_commit
- wait_for_ajax
- first('.file-actions').click_link 'Edit'
- execute_script('ace.edit("editor").setValue("class NextFeature\nend\n")')
- click_button 'Commit Changes'
- end
+ context 'as a developer' do
+ let(:user) { create(:user) }
+ let(:role) { :developer }
- context 'from MR diff' do
before do
- visit diffs_namespace_project_merge_request_path(project.namespace, project, merge_request)
- edit_and_commit
+ project.team << [user, role]
+ login_as(user)
+ end
+
+ def edit_and_commit
+ wait_for_ajax
+ find('.js-edit-blob').click
+ execute_script('ace.edit("editor").setValue("class NextFeature\nend\n")')
+ click_button 'Commit changes'
+ end
+
+ context 'from MR diff' do
+ before do
+ visit diffs_namespace_project_merge_request_path(project.namespace, project, merge_request)
+ edit_and_commit
+ end
+
+ it 'returns me to the mr' do
+ expect(page).to have_content(merge_request.title)
+ end
end
- scenario 'returns me to the mr' do
- expect(page).to have_content(merge_request.title)
+ context 'from blob file path' do
+ before do
+ visit namespace_project_blob_path(project.namespace, project, tree_join(branch, file_path))
+ edit_and_commit
+ end
+
+ it 'updates content' do
+ expect(page).to have_content 'successfully committed'
+ expect(page).to have_content 'NextFeature'
+ end
end
end
- context 'from blob file path' do
- before do
- visit namespace_project_blob_path(project.namespace, project, '/feature/files/ruby/feature.rb')
- edit_and_commit
+ context 'visit blob edit' do
+ context 'redirects to sign in and returns' do
+ context 'as developer' do
+ let(:user) { create(:user) }
+
+ before do
+ project.team << [user, :developer]
+ visit namespace_project_edit_blob_path(project.namespace, project, tree_join(branch, file_path))
+ end
+
+ it 'redirects to sign in and returns' do
+ expect(page).to have_current_path(new_user_session_path)
+
+ login_as(user)
+
+ expect(page).to have_current_path(namespace_project_edit_blob_path(project.namespace, project, tree_join(branch, file_path)))
+ end
+ end
+
+ context 'as guest' do
+ let(:user) { create(:user) }
+
+ before do
+ visit namespace_project_edit_blob_path(project.namespace, project, tree_join(branch, file_path))
+ end
+
+ it 'redirects to sign in and returns' do
+ expect(page).to have_current_path(new_user_session_path)
+
+ login_as(user)
+
+ expect(page).to have_current_path(namespace_project_blob_path(project.namespace, project, tree_join(branch, file_path)))
+ end
+ end
+ end
+
+ context 'as developer' do
+ let(:user) { create(:user) }
+ let(:protected_branch) { 'protected-branch' }
+
+ before do
+ project.team << [user, :developer]
+ project.repository.add_branch(user, protected_branch, 'master')
+ create(:protected_branch, project: project, name: protected_branch)
+ login_as(user)
+ end
+
+ context 'on some branch' do
+ before do
+ visit namespace_project_edit_blob_path(project.namespace, project, tree_join(branch, file_path))
+ end
+
+ it 'shows blob editor with same branch' do
+ expect(page).to have_current_path(namespace_project_edit_blob_path(project.namespace, project, tree_join(branch, file_path)))
+ expect(find('.js-target-branch .dropdown-toggle-text').text).to eq(branch)
+ end
+ end
+
+ context 'with protected branch' do
+ before do
+ visit namespace_project_edit_blob_path(project.namespace, project, tree_join(protected_branch, file_path))
+ end
+
+ it 'shows blob editor with patch branch' do
+ expect(find('.js-target-branch .dropdown-toggle-text').text).to eq('patch-1')
+ end
+ end
end
- scenario 'updates content' do
- expect(page).to have_content 'successfully committed'
- expect(page).to have_content 'NextFeature'
+ context 'as master' do
+ let(:user) { create(:user) }
+
+ before do
+ project.team << [user, :master]
+ login_as(user)
+ visit namespace_project_edit_blob_path(project.namespace, project, tree_join(branch, file_path))
+ end
+
+ it 'shows blob editor with same branch' do
+ expect(page).to have_current_path(namespace_project_edit_blob_path(project.namespace, project, tree_join(branch, file_path)))
+ expect(find('.js-target-branch .dropdown-toggle-text').text).to eq(branch)
+ end
end
end
end
diff --git a/spec/features/projects/blobs/user_create_spec.rb b/spec/features/projects/blobs/user_create_spec.rb
index 5686868a0c4..d805450e095 100644
--- a/spec/features/projects/blobs/user_create_spec.rb
+++ b/spec/features/projects/blobs/user_create_spec.rb
@@ -1,7 +1,6 @@
require 'spec_helper'
feature 'New blob creation', feature: true, js: true do
- include WaitForAjax
include TargetBranchHelpers
given(:user) { create(:user) }
@@ -22,7 +21,7 @@ feature 'New blob creation', feature: true, js: true do
end
def commit_file
- click_button 'Commit Changes'
+ click_button 'Commit changes'
end
context 'with default target branch' do
@@ -77,7 +76,7 @@ feature 'New blob creation', feature: true, js: true do
project,
user,
start_branch: 'master',
- target_branch: 'master',
+ branch_name: 'master',
commit_message: 'Create file',
file_path: 'feature.rb',
file_content: content
@@ -87,8 +86,8 @@ feature 'New blob creation', feature: true, js: true do
end
scenario 'shows error message' do
- expect(page).to have_content('Your changes could not be committed because a file with the same name already exists')
- expect(page).to have_content('New File')
+ expect(page).to have_content('A file with this name already exists')
+ expect(page).to have_content('New file')
expect(page).to have_content('NextFeature')
end
end
diff --git a/spec/features/projects/branches/new_branch_ref_dropdown_spec.rb b/spec/features/projects/branches/new_branch_ref_dropdown_spec.rb
new file mode 100644
index 00000000000..c5e0a0f0517
--- /dev/null
+++ b/spec/features/projects/branches/new_branch_ref_dropdown_spec.rb
@@ -0,0 +1,48 @@
+require 'spec_helper'
+
+describe 'New Branch Ref Dropdown', :js, :feature do
+ let(:user) { create(:user) }
+ let(:project) { create(:project, :public) }
+ let(:toggle) { find('.create-from .dropdown-menu-toggle') }
+
+ before do
+ project.add_master(user)
+
+ login_as(user)
+ visit new_namespace_project_branch_path(project.namespace, project)
+ end
+
+ it 'filters a list of branches and tags' do
+ toggle.click
+
+ filter_by('v1.0.0')
+
+ expect(items_count).to be(1)
+
+ filter_by('video')
+
+ expect(items_count).to be(1)
+
+ find('.create-from .dropdown-content li').click
+
+ expect(toggle).to have_content 'video'
+ end
+
+ it 'accepts a manually entered commit SHA' do
+ toggle.click
+
+ filter_by('somecommitsha')
+
+ find('.create-from input[type=search]').send_keys(:enter)
+
+ expect(toggle).to have_content 'somecommitsha'
+ end
+
+ def items_count
+ all('.create-from .dropdown-content li').length
+ end
+
+ def filter_by(filter_text)
+ fill_in 'Filter by Git revision', with: filter_text
+ end
+end
diff --git a/spec/features/projects/branches_spec.rb b/spec/features/projects/branches_spec.rb
index 8e0306ce83b..7668ce5f8be 100644
--- a/spec/features/projects/branches_spec.rb
+++ b/spec/features/projects/branches_spec.rb
@@ -4,7 +4,13 @@ describe 'Branches', feature: true do
let(:project) { create(:project, :public) }
let(:repository) { project.repository }
- context 'logged in' do
+ def set_protected_branch_name(branch_name)
+ find(".js-protected-branch-select").click
+ find(".dropdown-input-field").set(branch_name)
+ click_on("Create wildcard #{branch_name}")
+ end
+
+ context 'logged in as developer' do
before do
login_as :user
project.team << [@user, :developer]
@@ -38,6 +44,83 @@ describe 'Branches', feature: true do
expect(find('.all-branches')).to have_selector('li', count: 1)
end
end
+
+ describe 'Delete unprotected branch' do
+ it 'removes branch after confirmation', js: true do
+ visit namespace_project_branches_path(project.namespace, project)
+
+ fill_in 'branch-search', with: 'fix'
+
+ find('#branch-search').native.send_keys(:enter)
+
+ expect(page).to have_content('fix')
+ expect(find('.all-branches')).to have_selector('li', count: 1)
+ find('.js-branch-fix .btn-remove').trigger(:click)
+
+ expect(page).not_to have_content('fix')
+ expect(find('.all-branches')).to have_selector('li', count: 0)
+ end
+ end
+
+ describe 'Delete protected branch' do
+ before do
+ project.add_user(@user, :master)
+ visit namespace_project_protected_branches_path(project.namespace, project)
+ set_protected_branch_name('fix')
+ click_on "Protect"
+
+ within(".protected-branches-list") { expect(page).to have_content('fix') }
+ expect(ProtectedBranch.count).to eq(1)
+ project.add_user(@user, :developer)
+ end
+
+ it 'does not allow devleoper to removes protected branch', js: true do
+ visit namespace_project_branches_path(project.namespace, project)
+
+ fill_in 'branch-search', with: 'fix'
+ find('#branch-search').native.send_keys(:enter)
+
+ expect(page).to have_css('.btn-remove.disabled')
+ end
+ end
+ end
+
+ context 'logged in as master' do
+ before do
+ login_as :user
+ project.team << [@user, :master]
+ end
+
+ describe 'Delete protected branch' do
+ before do
+ visit namespace_project_protected_branches_path(project.namespace, project)
+ set_protected_branch_name('fix')
+ click_on "Protect"
+
+ within(".protected-branches-list") { expect(page).to have_content('fix') }
+ expect(ProtectedBranch.count).to eq(1)
+ end
+
+ it 'removes branch after modal confirmation', js: true do
+ visit namespace_project_branches_path(project.namespace, project)
+
+ fill_in 'branch-search', with: 'fix'
+ find('#branch-search').native.send_keys(:enter)
+
+ expect(page).to have_content('fix')
+ expect(find('.all-branches')).to have_selector('li', count: 1)
+ page.find('[data-target="#modal-delete-branch"]').trigger(:click)
+
+ expect(page).to have_css('.js-delete-branch[disabled]')
+ fill_in 'delete_branch_input', with: 'fix'
+ click_link 'Delete protected branch'
+
+ fill_in 'branch-search', with: 'fix'
+ find('#branch-search').native.send_keys(:enter)
+
+ expect(page).to have_content('No branches to show')
+ end
+ end
end
context 'logged out' do
diff --git a/spec/features/projects/builds_spec.rb b/spec/features/projects/builds_spec.rb
index 2116721b224..ab10434e10c 100644
--- a/spec/features/projects/builds_spec.rb
+++ b/spec/features/projects/builds_spec.rb
@@ -205,21 +205,13 @@ feature 'Builds', :feature do
it 'loads job trace' do
expect(page).to have_content 'BUILD TRACE'
- build.append_trace(' and more trace', 11)
+ build.trace.write do |stream|
+ stream.append(' and more trace', 11)
+ end
expect(page).to have_content 'BUILD TRACE and more trace'
end
end
-
- context 'when build does not have an initial trace' do
- let(:build) { create(:ci_build, pipeline: pipeline) }
-
- it 'loads new trace' do
- build.append_trace('build trace', 0)
-
- expect(page).to have_content 'build trace'
- end
- end
end
feature 'Variables' do
@@ -390,7 +382,7 @@ feature 'Builds', :feature do
it 'sends the right headers' do
expect(page.status_code).to eq(200)
expect(page.response_headers['Content-Type']).to eq('text/plain; charset=utf-8')
- expect(page.response_headers['X-Sendfile']).to eq(build.path_to_trace)
+ expect(page.response_headers['X-Sendfile']).to eq(build.trace.send(:current_path))
end
end
@@ -409,43 +401,24 @@ feature 'Builds', :feature do
context 'storage form' do
let(:existing_file) { Tempfile.new('existing-trace-file').path }
- let(:non_existing_file) do
- file = Tempfile.new('non-existing-trace-file')
- path = file.path
- file.unlink
- path
- end
- context 'when build has trace in file' do
- before do
- Capybara.current_session.driver.header('X-Sendfile-Type', 'X-Sendfile')
- build.run!
- visit namespace_project_build_path(project.namespace, project, build)
+ before do
+ Capybara.current_session.driver.header('X-Sendfile-Type', 'X-Sendfile')
- allow_any_instance_of(Project).to receive(:ci_id).and_return(nil)
- allow_any_instance_of(Ci::Build).to receive(:path_to_trace).and_return(existing_file)
- allow_any_instance_of(Ci::Build).to receive(:old_path_to_trace).and_return(non_existing_file)
+ build.run!
- page.within('.js-build-sidebar') { click_link 'Raw' }
- end
+ allow_any_instance_of(Gitlab::Ci::Trace).to receive(:paths)
+ .and_return(paths)
- it 'sends the right headers' do
- expect(page.status_code).to eq(200)
- expect(page.response_headers['Content-Type']).to eq('text/plain; charset=utf-8')
- expect(page.response_headers['X-Sendfile']).to eq(existing_file)
- end
+ visit namespace_project_build_path(project.namespace, project, build)
end
- context 'when build has trace in old file' do
- before do
- Capybara.current_session.driver.header('X-Sendfile-Type', 'X-Sendfile')
- build.run!
- visit namespace_project_build_path(project.namespace, project, build)
-
- allow_any_instance_of(Project).to receive(:ci_id).and_return(999)
- allow_any_instance_of(Ci::Build).to receive(:path_to_trace).and_return(non_existing_file)
- allow_any_instance_of(Ci::Build).to receive(:old_path_to_trace).and_return(existing_file)
+ context 'when build has trace in file' do
+ let(:paths) do
+ [existing_file]
+ end
+ before do
page.within('.js-build-sidebar') { click_link 'Raw' }
end
@@ -457,20 +430,10 @@ feature 'Builds', :feature do
end
context 'when build has trace in DB' do
- before do
- Capybara.current_session.driver.header('X-Sendfile-Type', 'X-Sendfile')
- build.run!
- visit namespace_project_build_path(project.namespace, project, build)
-
- allow_any_instance_of(Project).to receive(:ci_id).and_return(nil)
- allow_any_instance_of(Ci::Build).to receive(:path_to_trace).and_return(non_existing_file)
- allow_any_instance_of(Ci::Build).to receive(:old_path_to_trace).and_return(non_existing_file)
-
- page.within('.js-build-sidebar') { click_link 'Raw' }
- end
+ let(:paths) { [] }
it 'sends the right headers' do
- expect(page.status_code).to eq(404)
+ expect(page.status_code).not_to have_link('Raw')
end
end
end
diff --git a/spec/features/projects/commit/cherry_pick_spec.rb b/spec/features/projects/commit/cherry_pick_spec.rb
index 0b972d2a439..fa67d390c47 100644
--- a/spec/features/projects/commit/cherry_pick_spec.rb
+++ b/spec/features/projects/commit/cherry_pick_spec.rb
@@ -1,5 +1,4 @@
require 'spec_helper'
-include WaitForAjax
describe 'Cherry-pick Commits' do
let(:group) { create(:group) }
@@ -75,8 +74,10 @@ describe 'Cherry-pick Commits' do
wait_for_ajax
- page.within('#modal-cherry-pick-commit .dropdown-menu .dropdown-content') do
- click_link 'feature'
+ page.within('#modal-cherry-pick-commit .dropdown-menu') do
+ find('.dropdown-input input').set('feature')
+ wait_for_ajax
+ click_link "feature"
end
page.within('#modal-cherry-pick-commit') do
diff --git a/spec/features/projects/commit/mini_pipeline_graph_spec.rb b/spec/features/projects/commit/mini_pipeline_graph_spec.rb
index 30a2b2bcf8c..98c0f2c63b0 100644
--- a/spec/features/projects/commit/mini_pipeline_graph_spec.rb
+++ b/spec/features/projects/commit/mini_pipeline_graph_spec.rb
@@ -1,8 +1,6 @@
require 'rails_helper'
feature 'Mini Pipeline Graph in Commit View', :js, :feature do
- include WaitForAjax
-
let(:user) { create(:user) }
let(:project) { create(:project, :public) }
diff --git a/spec/features/projects/deploy_keys_spec.rb b/spec/features/projects/deploy_keys_spec.rb
index 0b997f130ea..06abfbbc86b 100644
--- a/spec/features/projects/deploy_keys_spec.rb
+++ b/spec/features/projects/deploy_keys_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe 'Project deploy keys', feature: true do
+describe 'Project deploy keys', :js, :feature do
let(:user) { create(:user) }
let(:project) { create(:project_empty_repo) }
@@ -17,9 +17,13 @@ describe 'Project deploy keys', feature: true do
it 'removes association between project and deploy key' do
visit namespace_project_settings_repository_path(project.namespace, project)
- page.within '.deploy-keys' do
- expect { click_on 'Remove' }
- .to change { project.deploy_keys.count }.by(-1)
+ page.within(find('.deploy-keys')) do
+ expect(page).to have_selector('.deploy-keys li', count: 1)
+
+ click_on 'Remove'
+
+ expect(page).not_to have_selector('.fa-spinner', count: 0)
+ expect(page).to have_selector('.deploy-keys li', count: 0)
end
end
end
diff --git a/spec/features/projects/edit_spec.rb b/spec/features/projects/edit_spec.rb
index 7c319af893b..a263781c43c 100644
--- a/spec/features/projects/edit_spec.rb
+++ b/spec/features/projects/edit_spec.rb
@@ -1,8 +1,6 @@
require 'rails_helper'
feature 'Project edit', feature: true, js: true do
- include WaitForAjax
-
let(:user) { create(:user) }
let(:project) { create(:project) }
diff --git a/spec/features/projects/environments/environment_spec.rb b/spec/features/projects/environments/environment_spec.rb
index acc3efe04e6..86ce50c976f 100644
--- a/spec/features/projects/environments/environment_spec.rb
+++ b/spec/features/projects/environments/environment_spec.rb
@@ -62,6 +62,8 @@ feature 'Environment', :feature do
name: 'deploy to production')
end
+ given(:role) { :master }
+
scenario 'does show a play button' do
expect(page).to have_link(action.name.humanize)
end
@@ -132,6 +134,8 @@ feature 'Environment', :feature do
on_stop: 'close_app')
end
+ given(:role) { :master }
+
scenario 'does allow to stop environment' do
click_link('Stop')
@@ -200,7 +204,7 @@ feature 'Environment', :feature do
end
scenario 'user deletes the branch with running environment' do
- visit namespace_project_branches_path(project.namespace, project)
+ visit namespace_project_branches_path(project.namespace, project, search: 'feature')
remove_branch_with_hooks(project, user, 'feature') do
page.within('.js-branch-feature') { find('a.btn-remove').click }
diff --git a/spec/features/projects/environments/environments_spec.rb b/spec/features/projects/environments/environments_spec.rb
index 641e2cf7402..cf393afccbb 100644
--- a/spec/features/projects/environments/environments_spec.rb
+++ b/spec/features/projects/environments/environments_spec.rb
@@ -23,6 +23,46 @@ feature 'Environments page', :feature, :js do
expect(page).to have_link('Available')
expect(page).to have_link('Stopped')
end
+
+ describe 'with one available environment' do
+ given(:environment) { create(:environment, project: project, state: :available) }
+
+ describe 'in available tab page' do
+ it 'should show one environment' do
+ visit namespace_project_environments_path(project.namespace, project, scope: 'available')
+ expect(page).to have_css('.environments-container')
+ expect(page.all('tbody > tr').length).to eq(1)
+ end
+ end
+
+ describe 'in stopped tab page' do
+ it 'should show no environments' do
+ visit namespace_project_environments_path(project.namespace, project, scope: 'stopped')
+ expect(page).to have_css('.environments-container')
+ expect(page).to have_content('You don\'t have any environments right now')
+ end
+ end
+ end
+
+ describe 'with one stopped environment' do
+ given(:environment) { create(:environment, project: project, state: :stopped) }
+
+ describe 'in available tab page' do
+ it 'should show no environments' do
+ visit namespace_project_environments_path(project.namespace, project, scope: 'available')
+ expect(page).to have_css('.environments-container')
+ expect(page).to have_content('You don\'t have any environments right now')
+ end
+ end
+
+ describe 'in stopped tab page' do
+ it 'should show one environment' do
+ visit namespace_project_environments_path(project.namespace, project, scope: 'stopped')
+ expect(page).to have_css('.environments-container')
+ expect(page.all('tbody > tr').length).to eq(1)
+ end
+ end
+ end
end
context 'without environments' do
diff --git a/spec/features/projects/features_visibility_spec.rb b/spec/features/projects/features_visibility_spec.rb
index 9079350186d..4533a6fb144 100644
--- a/spec/features/projects/features_visibility_spec.rb
+++ b/spec/features/projects/features_visibility_spec.rb
@@ -1,9 +1,6 @@
require 'spec_helper'
-include WaitForAjax
describe 'Edit Project Settings', feature: true do
- include WaitForAjax
-
let(:member) { create(:user) }
let!(:project) { create(:project, :public, path: 'gitlab', name: 'sample') }
let!(:issue) { create(:issue, project: project) }
@@ -71,20 +68,23 @@ describe 'Edit Project Settings', feature: true do
end
describe 'project features visibility pages' do
- before do
- @tools =
- {
- builds: namespace_project_pipelines_path(project.namespace, project),
- issues: namespace_project_issues_path(project.namespace, project),
- wiki: namespace_project_wiki_path(project.namespace, project, :home),
- snippets: namespace_project_snippets_path(project.namespace, project),
- merge_requests: namespace_project_merge_requests_path(project.namespace, project),
- }
+ let(:tools) do
+ {
+ builds: namespace_project_pipelines_path(project.namespace, project),
+ issues: namespace_project_issues_path(project.namespace, project),
+ wiki: namespace_project_wiki_path(project.namespace, project, :home),
+ snippets: namespace_project_snippets_path(project.namespace, project),
+ merge_requests: namespace_project_merge_requests_path(project.namespace, project)
+ }
end
context 'normal user' do
+ before do
+ login_as(member)
+ end
+
it 'renders 200 if tool is enabled' do
- @tools.each do |method_name, url|
+ tools.each do |method_name, url|
project.project_feature.update_attribute("#{method_name}_access_level", ProjectFeature::ENABLED)
visit url
expect(page.status_code).to eq(200)
@@ -92,7 +92,7 @@ describe 'Edit Project Settings', feature: true do
end
it 'renders 404 if feature is disabled' do
- @tools.each do |method_name, url|
+ tools.each do |method_name, url|
project.project_feature.update_attribute("#{method_name}_access_level", ProjectFeature::DISABLED)
visit url
expect(page.status_code).to eq(404)
@@ -102,21 +102,21 @@ describe 'Edit Project Settings', feature: true do
it 'renders 404 if feature is enabled only for team members' do
project.team.truncate
- @tools.each do |method_name, url|
+ tools.each do |method_name, url|
project.project_feature.update_attribute("#{method_name}_access_level", ProjectFeature::PRIVATE)
visit url
expect(page.status_code).to eq(404)
end
end
- it 'renders 200 if users is member of group' do
+ it 'renders 200 if user is member of group' do
group = create(:group)
project.group = group
project.save
group.add_owner(member)
- @tools.each do |method_name, url|
+ tools.each do |method_name, url|
project.project_feature.update_attribute("#{method_name}_access_level", ProjectFeature::PRIVATE)
visit url
expect(page.status_code).to eq(200)
@@ -131,7 +131,7 @@ describe 'Edit Project Settings', feature: true do
end
it 'renders 404 if feature is disabled' do
- @tools.each do |method_name, url|
+ tools.each do |method_name, url|
project.project_feature.update_attribute("#{method_name}_access_level", ProjectFeature::DISABLED)
visit url
expect(page.status_code).to eq(404)
@@ -141,7 +141,7 @@ describe 'Edit Project Settings', feature: true do
it 'renders 200 if feature is enabled only for team members' do
project.team.truncate
- @tools.each do |method_name, url|
+ tools.each do |method_name, url|
project.project_feature.update_attribute("#{method_name}_access_level", ProjectFeature::PRIVATE)
visit url
expect(page.status_code).to eq(200)
diff --git a/spec/features/projects/files/browse_files_spec.rb b/spec/features/projects/files/browse_files_spec.rb
index d281043caa3..4166aec1956 100644
--- a/spec/features/projects/files/browse_files_spec.rb
+++ b/spec/features/projects/files/browse_files_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-feature 'user browses project', feature: true do
+feature 'user browses project', feature: true, js: true do
let(:project) { create(:project) }
let(:user) { create(:user) }
@@ -13,7 +13,7 @@ feature 'user browses project', feature: true do
scenario "can see blame of '.gitignore'" do
click_link ".gitignore"
click_link 'Blame'
-
+
expect(page).to have_content "*.rb"
expect(page).to have_content "Dmitriy Zaporozhets"
expect(page).to have_content "Initial commit"
@@ -24,10 +24,23 @@ feature 'user browses project', feature: true do
click_link 'files'
click_link 'lfs'
click_link 'lfs_object.iso'
+ wait_for_ajax
expect(page).not_to have_content 'Download (1.5 MB)'
expect(page).to have_content 'version https://git-lfs.github.com/spec/v1'
expect(page).to have_content 'oid sha256:91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897'
expect(page).to have_content 'size 1575078'
end
+
+ scenario 'can see last commit for current directory' do
+ last_commit = project.repository.last_commit_for_path(project.default_branch, 'files')
+
+ click_link 'files'
+ wait_for_ajax
+
+ page.within('.blob-commit-info') do
+ expect(page).to have_content last_commit.short_id
+ expect(page).to have_content last_commit.author_name
+ end
+ end
end
diff --git a/spec/features/projects/files/creating_a_file_spec.rb b/spec/features/projects/files/creating_a_file_spec.rb
index ae448706130..69744ac3948 100644
--- a/spec/features/projects/files/creating_a_file_spec.rb
+++ b/spec/features/projects/files/creating_a_file_spec.rb
@@ -1,8 +1,6 @@
require 'spec_helper'
feature 'User wants to create a file', feature: true do
- include WaitForAjax
-
let(:project) { create(:project) }
let(:user) { create(:user) }
@@ -19,7 +17,7 @@ feature 'User wants to create a file', feature: true do
file_content = find('#file-content')
file_content.set options[:file_content] || 'Some content'
- click_button 'Commit Changes'
+ click_button 'Commit changes'
end
scenario 'file name contains Chinese characters' do
@@ -29,16 +27,16 @@ feature 'User wants to create a file', feature: true do
scenario 'directory name contains Chinese characters' do
submit_new_file(file_name: '中文/测试.md')
- expect(page).to have_content 'The file has been successfully created.'
+ expect(page).to have_content 'The file has been successfully created'
end
scenario 'file name contains invalid characters' do
submit_new_file(file_name: '\\')
- expect(page).to have_content 'Your changes could not be committed, because the file name can contain only'
+ expect(page).to have_content 'Path can contain only'
end
scenario 'file name contains directory traversal' do
submit_new_file(file_name: '../README.md')
- expect(page).to have_content 'Your changes could not be committed, because the file name cannot include directory traversal.'
+ expect(page).to have_content 'Path cannot include directory traversal'
end
end
diff --git a/spec/features/projects/files/dockerfile_dropdown_spec.rb b/spec/features/projects/files/dockerfile_dropdown_spec.rb
index 32f33a3ca97..548131c7cd4 100644
--- a/spec/features/projects/files/dockerfile_dropdown_spec.rb
+++ b/spec/features/projects/files/dockerfile_dropdown_spec.rb
@@ -1,13 +1,14 @@
require 'spec_helper'
+require 'fileutils'
feature 'User wants to add a Dockerfile file', feature: true do
- include WaitForAjax
-
before do
user = create(:user)
project = create(:project)
project.team << [user, :master]
+
login_as user
+
visit namespace_project_new_blob_path(project.namespace, project, 'master', file_name: 'Dockerfile')
end
@@ -17,11 +18,14 @@ feature 'User wants to add a Dockerfile file', feature: true do
scenario 'user can pick a Dockerfile file from the dropdown', js: true do
find('.js-dockerfile-selector').click
+
wait_for_ajax
+
within '.dockerfile-selector' do
find('.dropdown-input-field').set('HTTPd')
find('.dropdown-content li', text: 'HTTPd').click
end
+
wait_for_ajax
expect(page).to have_css('.dockerfile-selector .dropdown-toggle-text', text: 'HTTPd')
diff --git a/spec/features/projects/files/editing_a_file_spec.rb b/spec/features/projects/files/editing_a_file_spec.rb
index 36a80d7575d..7a3afafec29 100644
--- a/spec/features/projects/files/editing_a_file_spec.rb
+++ b/spec/features/projects/files/editing_a_file_spec.rb
@@ -1,14 +1,12 @@
require 'spec_helper'
feature 'User wants to edit a file', feature: true do
- include WaitForAjax
-
let(:project) { create(:project) }
let(:user) { create(:user) }
let(:commit_params) do
{
start_branch: project.default_branch,
- target_branch: project.default_branch,
+ branch_name: project.default_branch,
commit_message: "Committing First Update",
file_path: ".gitignore",
file_content: "First Update",
@@ -27,7 +25,7 @@ feature 'User wants to edit a file', feature: true do
scenario 'file has been updated since the user opened the edit page' do
Files::UpdateService.new(project, user, commit_params).execute
- click_button 'Commit Changes'
+ click_button 'Commit changes'
expect(page).to have_content 'Someone edited the file the same time you did.'
end
diff --git a/spec/features/projects/files/files_sort_submodules_with_folders_spec.rb b/spec/features/projects/files/files_sort_submodules_with_folders_spec.rb
index 10b91d8990b..5c8105de4cb 100644
--- a/spec/features/projects/files/files_sort_submodules_with_folders_spec.rb
+++ b/spec/features/projects/files/files_sort_submodules_with_folders_spec.rb
@@ -1,8 +1,6 @@
require 'spec_helper'
feature 'User views files page', feature: true do
- include WaitForAjax
-
let(:user) { create(:user) }
let(:project) { create(:forked_project_with_submodules) }
diff --git a/spec/features/projects/files/find_file_keyboard_spec.rb b/spec/features/projects/files/find_file_keyboard_spec.rb
index 582349d8d5b..e7a6749d8ac 100644
--- a/spec/features/projects/files/find_file_keyboard_spec.rb
+++ b/spec/features/projects/files/find_file_keyboard_spec.rb
@@ -1,8 +1,6 @@
require 'spec_helper'
feature 'Find file keyboard shortcuts', feature: true, js: true do
- include WaitForAjax
-
let(:user) { create(:user) }
let(:project) { create(:project) }
diff --git a/spec/features/projects/files/find_files_spec.rb b/spec/features/projects/files/find_files_spec.rb
new file mode 100644
index 00000000000..716b7591b95
--- /dev/null
+++ b/spec/features/projects/files/find_files_spec.rb
@@ -0,0 +1,30 @@
+require 'spec_helper'
+
+feature 'Find files button in the tree header', feature: true do
+ given(:user) { create(:user) }
+ given(:project) { create(:project) }
+
+ background do
+ login_as(user)
+ project.team << [user, :developer]
+ end
+
+ scenario 'project main screen' do
+ visit namespace_project_path(
+ project.namespace,
+ project
+ )
+
+ expect(page).to have_selector('.tree-controls .shortcuts-find-file')
+ end
+
+ scenario 'project tree screen' do
+ visit namespace_project_tree_path(
+ project.namespace,
+ project,
+ project.default_branch
+ )
+
+ expect(page).to have_selector('.tree-controls .shortcuts-find-file')
+ end
+end
diff --git a/spec/features/projects/files/gitignore_dropdown_spec.rb b/spec/features/projects/files/gitignore_dropdown_spec.rb
index 9ebef505b92..e59428f8b24 100644
--- a/spec/features/projects/files/gitignore_dropdown_spec.rb
+++ b/spec/features/projects/files/gitignore_dropdown_spec.rb
@@ -1,8 +1,6 @@
require 'spec_helper'
feature 'User wants to add a .gitignore file', feature: true do
- include WaitForAjax
-
before do
user = create(:user)
project = create(:project)
diff --git a/spec/features/projects/files/gitlab_ci_yml_dropdown_spec.rb b/spec/features/projects/files/gitlab_ci_yml_dropdown_spec.rb
index fca40f68b01..85b66b93fba 100644
--- a/spec/features/projects/files/gitlab_ci_yml_dropdown_spec.rb
+++ b/spec/features/projects/files/gitlab_ci_yml_dropdown_spec.rb
@@ -1,8 +1,6 @@
require 'spec_helper'
feature 'User wants to add a .gitlab-ci.yml file', feature: true do
- include WaitForAjax
-
before do
user = create(:user)
project = create(:project)
diff --git a/spec/features/projects/files/project_owner_creates_license_file_spec.rb b/spec/features/projects/files/project_owner_creates_license_file_spec.rb
index ccadc936567..249830921ac 100644
--- a/spec/features/projects/files/project_owner_creates_license_file_spec.rb
+++ b/spec/features/projects/files/project_owner_creates_license_file_spec.rb
@@ -1,8 +1,6 @@
require 'spec_helper'
feature 'project owner creates a license file', feature: true, js: true do
- include WaitForAjax
-
let(:project_master) { create(:user) }
let(:project) { create(:project) }
background do
@@ -29,7 +27,7 @@ feature 'project owner creates a license file', feature: true, js: true do
expect(file_content).to have_content("Copyright (c) #{Time.now.year} #{project.namespace.human_name}")
fill_in :commit_message, with: 'Add a LICENSE file', visible: true
- click_button 'Commit Changes'
+ click_button 'Commit changes'
expect(current_path).to eq(
namespace_project_blob_path(project.namespace, project, 'master/LICENSE'))
@@ -40,7 +38,7 @@ feature 'project owner creates a license file', feature: true, js: true do
scenario 'project master creates a license file from the "Add license" link' do
click_link 'Add License'
- expect(page).to have_content('New File')
+ expect(page).to have_content('New file')
expect(current_path).to eq(
namespace_project_new_blob_path(project.namespace, project, 'master'))
expect(find('#file_name').value).to eq('LICENSE')
@@ -53,7 +51,7 @@ feature 'project owner creates a license file', feature: true, js: true do
expect(file_content).to have_content("Copyright (c) #{Time.now.year} #{project.namespace.human_name}")
fill_in :commit_message, with: 'Add a LICENSE file', visible: true
- click_button 'Commit Changes'
+ click_button 'Commit changes'
expect(current_path).to eq(
namespace_project_blob_path(project.namespace, project, 'master/LICENSE'))
@@ -63,7 +61,7 @@ feature 'project owner creates a license file', feature: true, js: true do
def select_template(template)
page.within('.js-license-selector-wrap') do
- click_button 'Choose a License template'
+ click_button 'Apply a license template'
click_link template
wait_for_ajax
end
diff --git a/spec/features/projects/files/project_owner_sees_link_to_create_license_file_in_empty_project_spec.rb b/spec/features/projects/files/project_owner_sees_link_to_create_license_file_in_empty_project_spec.rb
index 420db962318..70a41886985 100644
--- a/spec/features/projects/files/project_owner_sees_link_to_create_license_file_in_empty_project_spec.rb
+++ b/spec/features/projects/files/project_owner_sees_link_to_create_license_file_in_empty_project_spec.rb
@@ -1,8 +1,6 @@
require 'spec_helper'
feature 'project owner sees a link to create a license file in empty project', feature: true, js: true do
- include WaitForAjax
-
let(:project_master) { create(:user) }
let(:project) { create(:empty_project) }
background do
@@ -14,7 +12,7 @@ feature 'project owner sees a link to create a license file in empty project', f
visit namespace_project_path(project.namespace, project)
click_link 'Create empty bare repository'
click_on 'LICENSE'
- expect(page).to have_content('New File')
+ expect(page).to have_content('New file')
expect(current_path).to eq(
namespace_project_new_blob_path(project.namespace, project, 'master'))
@@ -30,7 +28,7 @@ feature 'project owner sees a link to create a license file in empty project', f
fill_in :commit_message, with: 'Add a LICENSE file', visible: true
# Remove pre-receive hook so we can push without auth
FileUtils.rm_f(File.join(project.repository.path, 'hooks', 'pre-receive'))
- click_button 'Commit Changes'
+ click_button 'Commit changes'
expect(current_path).to eq(
namespace_project_blob_path(project.namespace, project, 'master/LICENSE'))
@@ -40,7 +38,7 @@ feature 'project owner sees a link to create a license file in empty project', f
def select_template(template)
page.within('.js-license-selector-wrap') do
- click_button 'Choose a License template'
+ click_button 'Apply a license template'
click_link template
wait_for_ajax
end
diff --git a/spec/features/projects/files/template_type_dropdown_spec.rb b/spec/features/projects/files/template_type_dropdown_spec.rb
new file mode 100644
index 00000000000..9fcf12e6cb9
--- /dev/null
+++ b/spec/features/projects/files/template_type_dropdown_spec.rb
@@ -0,0 +1,135 @@
+require 'spec_helper'
+
+feature 'Template type dropdown selector', js: true do
+ let(:project) { create(:project) }
+ let(:user) { create(:user) }
+
+ before do
+ project.team << [user, :master]
+ login_as user
+ end
+
+ context 'editing a non-matching file' do
+ before do
+ create_and_edit_file('.random-file.js')
+ end
+
+ scenario 'not displayed' do
+ check_type_selector_display(false)
+ end
+
+ scenario 'selects every template type correctly' do
+ fill_in 'file_path', with: '.gitignore'
+ try_selecting_all_types
+ end
+
+ scenario 'updates toggle value when input matches' do
+ fill_in 'file_path', with: '.gitignore'
+ check_type_selector_toggle_text('.gitignore')
+ end
+ end
+
+ context 'editing a matching file' do
+ before do
+ visit namespace_project_edit_blob_path(project.namespace, project, File.join(project.default_branch, 'LICENSE'))
+ end
+
+ scenario 'displayed' do
+ check_type_selector_display(true)
+ end
+
+ scenario 'is displayed when input matches' do
+ check_type_selector_display(true)
+ end
+
+ scenario 'selects every template type correctly' do
+ try_selecting_all_types
+ end
+
+ context 'user previews changes' do
+ before do
+ click_link 'Preview changes'
+ end
+
+ scenario 'type selector is hidden and shown correctly' do
+ check_type_selector_display(false)
+ click_link 'Write'
+ check_type_selector_display(true)
+ end
+ end
+ end
+
+ context 'creating a matching file' do
+ before do
+ visit namespace_project_new_blob_path(project.namespace, project, 'master', file_name: '.gitignore')
+ end
+
+ scenario 'is displayed' do
+ check_type_selector_display(true)
+ end
+
+ scenario 'toggle is set to the correct value' do
+ check_type_selector_toggle_text('.gitignore')
+ end
+
+ scenario 'selects every template type correctly' do
+ try_selecting_all_types
+ end
+ end
+
+ context 'creating a file' do
+ before do
+ visit namespace_project_new_blob_path(project.namespace, project, project.default_branch)
+ end
+
+ scenario 'type selector is shown' do
+ check_type_selector_display(true)
+ end
+
+ scenario 'toggle is set to the proper value' do
+ check_type_selector_toggle_text('Choose type')
+ end
+
+ scenario 'selects every template type correctly' do
+ try_selecting_all_types
+ end
+ end
+end
+
+def check_type_selector_display(is_visible)
+ count = is_visible ? 1 : 0
+ expect(page).to have_css('.js-template-type-selector', count: count)
+end
+
+def try_selecting_all_types
+ try_selecting_template_type('LICENSE', 'Apply a license template')
+ try_selecting_template_type('Dockerfile', 'Apply a Dockerfile template')
+ try_selecting_template_type('.gitlab-ci.yml', 'Apply a GitLab CI Yaml template')
+ try_selecting_template_type('.gitignore', 'Apply a .gitignore template')
+end
+
+def try_selecting_template_type(template_type, selector_label)
+ select_template_type(template_type)
+ check_template_selector_display(selector_label)
+ check_type_selector_toggle_text(template_type)
+end
+
+def select_template_type(template_type)
+ find('.js-template-type-selector').click
+ find('.dropdown-content li', text: template_type).click
+end
+
+def check_template_selector_display(content)
+ expect(page).to have_content(content)
+end
+
+def check_type_selector_toggle_text(template_type)
+ dropdown_toggle_button = find('.template-type-selector .dropdown-toggle-text')
+ expect(dropdown_toggle_button).to have_content(template_type)
+end
+
+def create_and_edit_file(file_name)
+ visit namespace_project_new_blob_path(project.namespace, project, 'master', file_name: file_name)
+ click_button "Commit changes"
+ visit namespace_project_edit_blob_path(project.namespace, project, File.join(project.default_branch, file_name))
+end
diff --git a/spec/features/projects/files/undo_template_spec.rb b/spec/features/projects/files/undo_template_spec.rb
new file mode 100644
index 00000000000..cd3af0b7d29
--- /dev/null
+++ b/spec/features/projects/files/undo_template_spec.rb
@@ -0,0 +1,66 @@
+require 'spec_helper'
+
+feature 'Template Undo Button', js: true do
+ let(:project) { create(:project) }
+ let(:user) { create(:user) }
+
+ before do
+ project.team << [user, :master]
+ login_as user
+ end
+
+ context 'editing a matching file and applying a template' do
+ before do
+ visit namespace_project_edit_blob_path(project.namespace, project, File.join(project.default_branch, "LICENSE"))
+ select_file_template('.js-license-selector', 'Apache License 2.0')
+ end
+
+ scenario 'reverts template application' do
+ try_template_undo('http://www.apache.org/licenses/', 'Apply a license template')
+ end
+ end
+
+ context 'creating a non-matching file' do
+ before do
+ visit namespace_project_new_blob_path(project.namespace, project, 'master')
+ select_file_template_type('LICENSE')
+ select_file_template('.js-license-selector', 'Apache License 2.0')
+ end
+
+ scenario 'reverts template application' do
+ try_template_undo('http://www.apache.org/licenses/', 'Apply a license template')
+ end
+ end
+end
+
+def try_template_undo(template_content, toggle_text)
+ check_undo_button_display
+ check_content_reverted(template_content)
+ check_toggle_text_set(toggle_text)
+end
+
+def check_toggle_text_set(neutral_toggle_text)
+ expect(page).to have_content(neutral_toggle_text)
+end
+
+def check_undo_button_display
+ expect(page).to have_content('Template applied')
+ expect(page).to have_css('.template-selectors-undo-menu .btn-info')
+end
+
+def check_content_reverted(template_content)
+ find('.template-selectors-undo-menu .btn-info').click
+ expect(page).not_to have_content(template_content)
+ expect(find('.template-type-selector .dropdown-toggle-text')).to have_content()
+end
+
+def select_file_template(template_selector_selector, template_name)
+ find(template_selector_selector).click
+ find('.dropdown-content li', text: template_name).click
+ wait_for_ajax
+end
+
+def select_file_template_type(template_type)
+ find('.js-template-type-selector').click
+ find('.dropdown-content li', text: template_type).click
+end
diff --git a/spec/features/projects/gfm_autocomplete_load_spec.rb b/spec/features/projects/gfm_autocomplete_load_spec.rb
index dd9622f16a0..67bc9142356 100644
--- a/spec/features/projects/gfm_autocomplete_load_spec.rb
+++ b/spec/features/projects/gfm_autocomplete_load_spec.rb
@@ -10,7 +10,7 @@ describe 'GFM autocomplete loading', feature: true, js: true do
end
it 'does not load on project#show' do
- expect(evaluate_script('gl.GfmAutoComplete.dataSources')).to eq({})
+ expect(evaluate_script('gl.GfmAutoComplete')).to eq(nil)
end
it 'loads on new issue page' do
diff --git a/spec/features/projects/import_export/import_file_spec.rb b/spec/features/projects/import_export/import_file_spec.rb
index 2d1106ea3e8..583f479ec18 100644
--- a/spec/features/projects/import_export/import_file_spec.rb
+++ b/spec/features/projects/import_export/import_file_spec.rb
@@ -69,12 +69,8 @@ feature 'Import/Export - project import integration test', feature: true, js: tr
select2(namespace.id, from: '#project_namespace_id')
- # click on disabled element
- find(:link, 'GitLab export').trigger('click')
-
- page.within('.flash-container') do
- expect(page).to have_content('Please enter path and name')
- end
+ # Check for tooltip disabled import button
+ expect(find('.import_gitlab_project')['title']).to eq('Please enter a valid project name.')
end
end
diff --git a/spec/features/projects/import_export/test_project_export.tar.gz b/spec/features/projects/import_export/test_project_export.tar.gz
index 399c1d478c5..4efd5a26a82 100644
--- a/spec/features/projects/import_export/test_project_export.tar.gz
+++ b/spec/features/projects/import_export/test_project_export.tar.gz
Binary files differ
diff --git a/spec/features/projects/issuable_templates_spec.rb b/spec/features/projects/issuable_templates_spec.rb
index 62d0aedda48..fa5e30075e3 100644
--- a/spec/features/projects/issuable_templates_spec.rb
+++ b/spec/features/projects/issuable_templates_spec.rb
@@ -1,8 +1,6 @@
require 'spec_helper'
feature 'issuable templates', feature: true, js: true do
- include WaitForAjax
-
let(:user) { create(:user) }
let(:project) { create(:project, :public) }
@@ -14,7 +12,7 @@ feature 'issuable templates', feature: true, js: true do
context 'user creates an issue using templates' do
let(:template_content) { 'this is a test "bug" template' }
let(:longtemplate_content) { %Q(this\n\n\n\n\nis\n\n\n\n\na\n\n\n\n\nbug\n\n\n\n\ntemplate) }
- let(:issue) { create(:issue, author: user, assignee: user, project: project) }
+ let(:issue) { create(:issue, author: user, assignees: [user], project: project) }
let(:description_addition) { ' appending to description' }
background do
@@ -74,7 +72,7 @@ feature 'issuable templates', feature: true, js: true do
context 'user creates an issue using templates, with a prior description' do
let(:prior_description) { 'test issue description' }
let(:template_content) { 'this is a test "bug" template' }
- let(:issue) { create(:issue, author: user, assignee: user, project: project) }
+ let(:issue) { create(:issue, author: user, assignees: [user], project: project) }
background do
project.repository.create_file(
@@ -163,12 +161,14 @@ feature 'issuable templates', feature: true, js: true do
end
def select_template(name)
- first('.js-issuable-selector').click
- first('.js-issuable-selector-wrap .dropdown-content a', text: name).click
+ find('.js-issuable-selector').click
+
+ find('.js-issuable-selector-wrap .dropdown-content a', text: name, match: :first).click
end
def select_option(name)
- first('.js-issuable-selector').click
- first('.js-issuable-selector-wrap .dropdown-footer-list a', text: name).click
+ find('.js-issuable-selector').click
+
+ find('.js-issuable-selector-wrap .dropdown-footer-list a', text: name, match: :first).click
end
end
diff --git a/spec/features/projects/labels/update_prioritization_spec.rb b/spec/features/projects/labels/update_prioritization_spec.rb
index 1e900d7e660..836f81fb16d 100644
--- a/spec/features/projects/labels/update_prioritization_spec.rb
+++ b/spec/features/projects/labels/update_prioritization_spec.rb
@@ -1,7 +1,6 @@
require 'spec_helper'
feature 'Prioritize labels', feature: true do
- include WaitForAjax
include DragTo
let(:user) { create(:user) }
diff --git a/spec/features/projects/members/group_links_spec.rb b/spec/features/projects/members/group_links_spec.rb
index cffb935ad5a..ab2b089db2e 100644
--- a/spec/features/projects/members/group_links_spec.rb
+++ b/spec/features/projects/members/group_links_spec.rb
@@ -1,8 +1,6 @@
require 'spec_helper'
feature 'Projects > Members > Anonymous user sees members', feature: true, js: true do
- include WaitForAjax
-
let(:user) { create(:user) }
let(:group) { create(:group, :public) }
let(:project) { create(:empty_project, :public) }
diff --git a/spec/features/projects/members/list_spec.rb b/spec/features/projects/members/list_spec.rb
new file mode 100644
index 00000000000..deea34214fb
--- /dev/null
+++ b/spec/features/projects/members/list_spec.rb
@@ -0,0 +1,90 @@
+require 'spec_helper'
+
+feature 'Project members list', feature: true do
+ include Select2Helper
+
+ let(:user1) { create(:user, name: 'John Doe') }
+ let(:user2) { create(:user, name: 'Mary Jane') }
+ let(:group) { create(:group) }
+ let(:project) { create(:project, namespace: group) }
+
+ background do
+ login_as(user1)
+ group.add_owner(user1)
+ end
+
+ scenario 'show members from project and group' do
+ project.add_developer(user2)
+
+ visit_members_page
+
+ expect(first_row.text).to include(user1.name)
+ expect(second_row.text).to include(user2.name)
+ end
+
+ scenario 'show user once if member of both group and project' do
+ project.add_developer(user1)
+
+ visit_members_page
+
+ expect(first_row.text).to include(user1.name)
+ expect(second_row).to be_blank
+ end
+
+ scenario 'update user acess level', :js do
+ project.add_developer(user2)
+
+ visit_members_page
+
+ page.within(second_row) do
+ click_button('Developer')
+ click_link('Reporter')
+
+ expect(page).to have_button('Reporter')
+ end
+ end
+
+ scenario 'add user to project', :js do
+ visit_members_page
+
+ add_user(user2.id, 'Reporter')
+
+ page.within(second_row) do
+ expect(page).to have_content(user2.name)
+ expect(page).to have_button('Reporter')
+ end
+ end
+
+ scenario 'invite user to project', :js do
+ visit_members_page
+
+ add_user('test@example.com', 'Reporter')
+
+ page.within(second_row) do
+ expect(page).to have_content('test@example.com')
+ expect(page).to have_content('Invited')
+ expect(page).to have_button('Reporter')
+ end
+ end
+
+ def first_row
+ page.all('ul.content-list > li')[0]
+ end
+
+ def second_row
+ page.all('ul.content-list > li')[1]
+ end
+
+ def add_user(id, role)
+ page.within ".users-project-form" do
+ select2(id, from: "#user_ids", multiple: true)
+ select(role, from: "access_level")
+ end
+
+ click_button "Add to project"
+ end
+
+ def visit_members_page
+ visit namespace_project_settings_members_path(project.namespace, project)
+ end
+end
diff --git a/spec/features/projects/members/master_adds_member_with_expiration_date_spec.rb b/spec/features/projects/members/master_adds_member_with_expiration_date_spec.rb
index c3f45be6e4b..19d14ad9af4 100644
--- a/spec/features/projects/members/master_adds_member_with_expiration_date_spec.rb
+++ b/spec/features/projects/members/master_adds_member_with_expiration_date_spec.rb
@@ -1,7 +1,6 @@
require 'spec_helper'
feature 'Projects > Members > Master adds member with expiration date', feature: true, js: true do
- include WaitForAjax
include Select2Helper
include ActiveSupport::Testing::TimeHelpers
diff --git a/spec/features/projects/members/sorting_spec.rb b/spec/features/projects/members/sorting_spec.rb
index c7a32a65e49..b7ae5f0b925 100644
--- a/spec/features/projects/members/sorting_spec.rb
+++ b/spec/features/projects/members/sorting_spec.rb
@@ -68,7 +68,7 @@ feature 'Projects > Members > Sorting', feature: true do
expect(page).to have_css('.member-sort-dropdown .dropdown-toggle-text', text: 'Name, descending')
end
- scenario 'sorts by recent sign in' do
+ scenario 'sorts by recent sign in', :redis do
visit_members_list(sort: :recent_sign_in)
expect(first_member).to include(master.name)
@@ -76,7 +76,7 @@ feature 'Projects > Members > Sorting', feature: true do
expect(page).to have_css('.member-sort-dropdown .dropdown-toggle-text', text: 'Recent sign in')
end
- scenario 'sorts by oldest sign in' do
+ scenario 'sorts by oldest sign in', :redis do
visit_members_list(sort: :oldest_sign_in)
expect(first_member).to include(developer.name)
diff --git a/spec/features/projects/members/user_requests_access_spec.rb b/spec/features/projects/members/user_requests_access_spec.rb
index de25d45f447..1bf8f710b9f 100644
--- a/spec/features/projects/members/user_requests_access_spec.rb
+++ b/spec/features/projects/members/user_requests_access_spec.rb
@@ -31,6 +31,17 @@ feature 'Projects > Members > User requests access', feature: true do
expect(page).not_to have_content 'Leave Project'
end
+ context 'code access is restricted' do
+ scenario 'user can request access' do
+ project.project_feature.update!(repository_access_level: ProjectFeature::PRIVATE,
+ builds_access_level: ProjectFeature::PRIVATE,
+ merge_requests_access_level: ProjectFeature::PRIVATE)
+ visit namespace_project_path(project.namespace, project)
+
+ expect(page).to have_content 'Request Access'
+ end
+ end
+
scenario 'user is not listed in the project members page' do
click_link 'Request Access'
diff --git a/spec/features/projects/merge_request_button_spec.rb b/spec/features/projects/merge_request_button_spec.rb
index b6728960fb8..1370ab1c521 100644
--- a/spec/features/projects/merge_request_button_spec.rb
+++ b/spec/features/projects/merge_request_button_spec.rb
@@ -1,13 +1,13 @@
require 'spec_helper'
feature 'Merge Request button', feature: true do
- shared_examples 'Merge Request button only shown when allowed' do
+ shared_examples 'Merge request button only shown when allowed' do
let(:user) { create(:user) }
let(:project) { create(:project, :public) }
let(:forked_project) { create(:project, :public, forked_from_project: project) }
context 'not logged in' do
- it 'does not show Create Merge Request button' do
+ it 'does not show Create merge request button' do
visit url
within("#content-body") do
@@ -22,7 +22,7 @@ feature 'Merge Request button', feature: true do
project.team << [user, :developer]
end
- it 'shows Create Merge Request button' do
+ it 'shows Create merge request button' do
href = new_namespace_project_merge_request_path(project.namespace,
project,
merge_request: { source_branch: 'feature',
@@ -40,7 +40,7 @@ feature 'Merge Request button', feature: true do
project.project_feature.update!(merge_requests_access_level: ProjectFeature::DISABLED)
end
- it 'does not show Create Merge Request button' do
+ it 'does not show Create merge request button' do
visit url
within("#content-body") do
@@ -55,7 +55,7 @@ feature 'Merge Request button', feature: true do
login_as(user)
end
- it 'does not show Create Merge Request button' do
+ it 'does not show Create merge request button' do
visit url
within("#content-body") do
@@ -66,7 +66,7 @@ feature 'Merge Request button', feature: true do
context 'on own fork of project' do
let(:user) { forked_project.owner }
- it 'shows Create Merge Request button' do
+ it 'shows Create merge request button' do
href = new_namespace_project_merge_request_path(forked_project.namespace,
forked_project,
merge_request: { source_branch: 'feature',
@@ -83,24 +83,24 @@ feature 'Merge Request button', feature: true do
end
context 'on branches page' do
- it_behaves_like 'Merge Request button only shown when allowed' do
- let(:label) { 'Merge Request' }
- let(:url) { namespace_project_branches_path(project.namespace, project) }
- let(:fork_url) { namespace_project_branches_path(forked_project.namespace, forked_project) }
+ it_behaves_like 'Merge request button only shown when allowed' do
+ let(:label) { 'Merge request' }
+ let(:url) { namespace_project_branches_path(project.namespace, project, search: 'feature') }
+ let(:fork_url) { namespace_project_branches_path(forked_project.namespace, forked_project, search: 'feature') }
end
end
context 'on compare page' do
- it_behaves_like 'Merge Request button only shown when allowed' do
- let(:label) { 'Create Merge Request' }
+ it_behaves_like 'Merge request button only shown when allowed' do
+ let(:label) { 'Create merge request' }
let(:url) { namespace_project_compare_path(project.namespace, project, from: 'master', to: 'feature') }
let(:fork_url) { namespace_project_compare_path(forked_project.namespace, forked_project, from: 'master', to: 'feature') }
end
end
context 'on commits page' do
- it_behaves_like 'Merge Request button only shown when allowed' do
- let(:label) { 'Create Merge Request' }
+ it_behaves_like 'Merge request button only shown when allowed' do
+ let(:label) { 'Create merge request' }
let(:url) { namespace_project_commits_path(project.namespace, project, 'feature') }
let(:fork_url) { namespace_project_commits_path(forked_project.namespace, forked_project, 'feature') }
end
diff --git a/spec/features/projects/merge_requests/list_spec.rb b/spec/features/projects/merge_requests/list_spec.rb
index 5dd58ad66a7..7e8a796c55d 100644
--- a/spec/features/projects/merge_requests/list_spec.rb
+++ b/spec/features/projects/merge_requests/list_spec.rb
@@ -17,4 +17,28 @@ feature 'Merge Requests List' do
expect(page).not_to have_selector('.js-new-board-list')
end
+
+ it 'should show an empty state' do
+ visit namespace_project_merge_requests_path(project.namespace, project)
+
+ expect(page).to have_selector('.empty-state')
+ end
+
+ it 'empty state should have a create merge request button' do
+ visit namespace_project_merge_requests_path(project.namespace, project)
+
+ expect(page).to have_link 'New merge request', href: new_namespace_project_merge_request_path(project.namespace, project)
+ end
+
+ context 'if there are merge requests' do
+ before do
+ create(:merge_request, assignee: user, source_project: project)
+
+ visit namespace_project_merge_requests_path(project.namespace, project)
+ end
+
+ it 'should not show an empty state' do
+ expect(page).not_to have_selector('.empty-state')
+ end
+ end
end
diff --git a/spec/features/projects/milestones/milestone_spec.rb b/spec/features/projects/milestones/milestone_spec.rb
index dab78fd3571..b4fc0edbde8 100644
--- a/spec/features/projects/milestones/milestone_spec.rb
+++ b/spec/features/projects/milestones/milestone_spec.rb
@@ -63,4 +63,27 @@ feature 'Project milestone', :feature do
expect(page).not_to have_content('Assign some issues to this milestone.')
end
end
+
+ context 'when project has an issue' do
+ before do
+ create(:issue, project: project, milestone: milestone)
+
+ visit namespace_project_milestone_path(project.namespace, project, milestone)
+ end
+
+ describe 'the collapsed sidebar' do
+ before do
+ find('.milestone-sidebar .gutter-toggle').click
+ end
+
+ it 'shows the total MR and issue counts' do
+ find('.milestone-sidebar .block', match: :first)
+
+ aggregate_failures 'MR and issue blocks' do
+ expect(find('.milestone-sidebar .block.issues')).to have_content 1
+ expect(find('.milestone-sidebar .block.merge-requests')).to have_content 0
+ end
+ end
+ end
+ end
end
diff --git a/spec/features/projects/new_project_spec.rb b/spec/features/projects/new_project_spec.rb
index 52196ce49bd..c66b9a34b86 100644
--- a/spec/features/projects/new_project_spec.rb
+++ b/spec/features/projects/new_project_spec.rb
@@ -71,6 +71,22 @@ feature "New project", feature: true do
end
end
end
+
+ context "with subgroup namespace" do
+ let(:group) { create(:group, :private, owner: user) }
+ let(:subgroup) { create(:group, parent: group) }
+
+ before do
+ group.add_master(user)
+ visit new_project_path(namespace_id: subgroup.id)
+ end
+
+ it "selects the group namespace" do
+ namespace = find("#project_namespace_id option[selected]")
+
+ expect(namespace.text).to eq subgroup.full_path
+ end
+ end
end
context 'Import project options' do
diff --git a/spec/features/projects/pipeline_schedules_spec.rb b/spec/features/projects/pipeline_schedules_spec.rb
new file mode 100644
index 00000000000..1211b17b3d8
--- /dev/null
+++ b/spec/features/projects/pipeline_schedules_spec.rb
@@ -0,0 +1,146 @@
+require 'spec_helper'
+
+feature 'Pipeline Schedules', :feature do
+ include PipelineSchedulesHelper
+ include WaitForAjax
+
+ let!(:project) { create(:project) }
+ let!(:pipeline_schedule) { create(:ci_pipeline_schedule, project: project) }
+ let!(:pipeline) { create(:ci_pipeline, pipeline_schedule: pipeline_schedule) }
+ let(:scope) { nil }
+ let!(:user) { create(:user) }
+
+ before do
+ project.add_master(user)
+
+ login_as(user)
+ visit_page
+ end
+
+ describe 'GET /projects/pipeline_schedules' do
+ let(:visit_page) { visit_pipelines_schedules }
+
+ it 'avoids N + 1 queries' do
+ control_count = ActiveRecord::QueryRecorder.new { visit_pipelines_schedules }.count
+
+ create_list(:ci_pipeline_schedule, 2, project: project)
+
+ expect { visit_pipelines_schedules }.not_to exceed_query_limit(control_count)
+ end
+
+ describe 'The view' do
+ it 'displays the required information description' do
+ page.within('.pipeline-schedule-table-row') do
+ expect(page).to have_content('pipeline schedule')
+ expect(page).to have_link('master')
+ expect(page).to have_link("##{pipeline.id}")
+ end
+ end
+
+ it 'creates a new scheduled pipeline' do
+ click_link 'New schedule'
+
+ expect(page).to have_content('Schedule a new pipeline')
+ end
+
+ it 'changes ownership of the pipeline' do
+ click_link 'Take ownership'
+ page.within('.pipeline-schedule-table-row') do
+ expect(page).not_to have_content('No owner')
+ expect(page).to have_link('John Doe')
+ end
+ end
+
+ it 'edits the pipeline' do
+ page.within('.pipeline-schedule-table-row') do
+ click_link 'Edit'
+ end
+
+ expect(page).to have_content('Edit Pipeline Schedule')
+ end
+
+ it 'deletes the pipeline' do
+ click_link 'Delete'
+
+ expect(page).not_to have_content('pipeline schedule')
+ end
+ end
+ end
+
+ describe 'POST /projects/pipeline_schedules/new', js: true do
+ let(:visit_page) { visit_new_pipeline_schedule }
+
+ it 'sets defaults for timezone and target branch' do
+ expect(page).to have_button('master')
+ expect(page).to have_button('UTC')
+ end
+
+ it 'it creates a new scheduled pipeline' do
+ fill_in_schedule_form
+ save_pipeline_schedule
+
+ expect(page).to have_content('my fancy description')
+ end
+
+ it 'it prevents an invalid form from being submitted' do
+ save_pipeline_schedule
+
+ expect(page).to have_content('This field is required')
+ end
+ end
+
+ describe 'PATCH /projects/pipelines_schedules/:id/edit', js: true do
+ let(:visit_page) do
+ edit_pipeline_schedule
+ end
+
+ it 'it displays existing properties' do
+ description = find_field('schedule_description').value
+ expect(description).to eq('pipeline schedule')
+ expect(page).to have_button('master')
+ expect(page).to have_button('UTC')
+ end
+
+ it 'edits the scheduled pipeline' do
+ fill_in 'schedule_description', with: 'my brand new description'
+
+ save_pipeline_schedule
+
+ expect(page).to have_content('my brand new description')
+ end
+ end
+
+ def visit_new_pipeline_schedule
+ visit new_namespace_project_pipeline_schedule_path(project.namespace, project, pipeline_schedule)
+ end
+
+ def edit_pipeline_schedule
+ visit edit_namespace_project_pipeline_schedule_path(project.namespace, project, pipeline_schedule)
+ end
+
+ def visit_pipelines_schedules
+ visit namespace_project_pipeline_schedules_path(project.namespace, project, scope: scope)
+ end
+
+ def select_timezone
+ find('.js-timezone-dropdown').click
+ click_link 'American Samoa'
+ end
+
+ def select_target_branch
+ find('.js-target-branch-dropdown').click
+ click_link 'master'
+ end
+
+ def save_pipeline_schedule
+ click_button 'Save pipeline schedule'
+ end
+
+ def fill_in_schedule_form
+ fill_in 'schedule_description', with: 'my fancy description'
+ fill_in 'schedule_cron', with: '* 1 2 3 4'
+
+ select_timezone
+ select_target_branch
+ end
+end
diff --git a/spec/features/projects/pipelines/pipeline_spec.rb b/spec/features/projects/pipelines/pipeline_spec.rb
index 5a53e48f5f8..cfac54ef259 100644
--- a/spec/features/projects/pipelines/pipeline_spec.rb
+++ b/spec/features/projects/pipelines/pipeline_spec.rb
@@ -254,4 +254,57 @@ describe 'Pipeline', :feature, :js do
it { expect(build_manual.reload).to be_pending }
end
end
+
+ describe 'GET /:project/pipelines/:id/failures' do
+ let(:project) { create(:project) }
+ let(:pipeline) { create(:ci_pipeline, project: project, ref: 'master', sha: project.commit.id) }
+ let(:pipeline_failures_page) { failures_namespace_project_pipeline_path(project.namespace, project, pipeline) }
+ let!(:failed_build) { create(:ci_build, :failed, pipeline: pipeline) }
+
+ context 'with failed build' do
+ before do
+ failed_build.trace.set('4 examples, 1 failure')
+
+ visit pipeline_failures_page
+ end
+
+ it 'shows jobs tab pane as active' do
+ expect(page).to have_content('Failed Jobs')
+ expect(page).to have_css('#js-tab-failures.active')
+ end
+
+ it 'lists failed builds' do
+ expect(page).to have_content(failed_build.name)
+ expect(page).to have_content(failed_build.stage)
+ end
+
+ it 'shows build failure logs' do
+ expect(page).to have_content('4 examples, 1 failure')
+ end
+ end
+
+ context 'when missing build logs' do
+ before do
+ visit pipeline_failures_page
+ end
+
+ it 'includes failed jobs' do
+ expect(page).to have_content('No job trace')
+ end
+ end
+
+ context 'without failures' do
+ before do
+ failed_build.update!(status: :success)
+
+ visit pipeline_failures_page
+ end
+
+ it 'displays the pipeline graph' do
+ expect(current_path).to eq(pipeline_path(pipeline))
+ expect(page).not_to have_content('Failed Jobs')
+ expect(page).to have_selector('.pipeline-visualization')
+ end
+ end
+ end
end
diff --git a/spec/features/projects/pipelines/pipelines_spec.rb b/spec/features/projects/pipelines/pipelines_spec.rb
index 2272b19bc8f..5f82cf2f5e5 100644
--- a/spec/features/projects/pipelines/pipelines_spec.rb
+++ b/spec/features/projects/pipelines/pipelines_spec.rb
@@ -22,7 +22,7 @@ describe 'Pipelines', :feature, :js do
project: project,
ref: 'master',
status: 'running',
- sha: project.commit.id,
+ sha: project.commit.id
)
end
@@ -370,6 +370,58 @@ describe 'Pipelines', :feature, :js do
end
end
+ describe 'GET /:project/pipelines/show' do
+ let(:project) { create(:project) }
+
+ let(:pipeline) do
+ create(:ci_empty_pipeline,
+ project: project,
+ sha: project.commit.id,
+ user: user)
+ end
+
+ before do
+ create_build('build', 0, 'build', :success)
+ create_build('test', 1, 'rspec 0:2', :pending)
+ create_build('test', 1, 'rspec 1:2', :running)
+ create_build('test', 1, 'spinach 0:2', :created)
+ create_build('test', 1, 'spinach 1:2', :created)
+ create_build('test', 1, 'audit', :created)
+ create_build('deploy', 2, 'production', :created)
+
+ create(:generic_commit_status, pipeline: pipeline, stage: 'external', name: 'jenkins', stage_idx: 3)
+
+ visit namespace_project_pipeline_path(project.namespace, project, pipeline)
+ wait_for_vue_resource
+ end
+
+ it 'shows a graph with grouped stages' do
+ expect(page).to have_css('.js-pipeline-graph')
+
+ # header
+ expect(page).to have_text("##{pipeline.id}")
+ expect(page).to have_selector(%Q(img[alt$="#{pipeline.user.name}'s avatar"]))
+ expect(page).to have_link(pipeline.user.name, href: user_path(pipeline.user))
+
+ # stages
+ expect(page).to have_text('Build')
+ expect(page).to have_text('Test')
+ expect(page).to have_text('Deploy')
+ expect(page).to have_text('External')
+
+ # builds
+ expect(page).to have_text('rspec')
+ expect(page).to have_text('spinach')
+ expect(page).to have_text('rspec')
+ expect(page).to have_text('production')
+ expect(page).to have_text('jenkins')
+ end
+
+ def create_build(stage, stage_idx, name, status)
+ create(:ci_build, pipeline: pipeline, stage: stage, stage_idx: stage_idx, name: name, status: status)
+ end
+ end
+
describe 'POST /:project/pipelines' do
let(:project) { create(:project) }
diff --git a/spec/features/projects/project_settings_spec.rb b/spec/features/projects/project_settings_spec.rb
index 5d0314d5c09..11dcab4d737 100644
--- a/spec/features/projects/project_settings_spec.rb
+++ b/spec/features/projects/project_settings_spec.rb
@@ -1,64 +1,158 @@
require 'spec_helper'
describe 'Edit Project Settings', feature: true do
+ include Select2Helper
+
let(:user) { create(:user) }
- let(:project) { create(:empty_project, path: 'gitlab', name: 'sample') }
+ let(:project) { create(:empty_project, namespace: user.namespace, path: 'gitlab', name: 'sample') }
before do
login_as(user)
- project.team << [user, :master]
end
- describe 'Project settings', js: true do
+ describe 'Project settings section', js: true do
it 'shows errors for invalid project name' do
visit edit_namespace_project_path(project.namespace, project)
-
fill_in 'project_name_edit', with: 'foo&bar'
-
click_button 'Save changes'
-
expect(page).to have_field 'project_name_edit', with: 'foo&bar'
expect(page).to have_content "Name can contain only letters, digits, emojis, '_', '.', dash, space. It must start with letter, digit, emoji or '_'."
expect(page).to have_button 'Save changes'
end
- scenario 'shows a successful notice when the project is updated' do
+ it 'shows a successful notice when the project is updated' do
visit edit_namespace_project_path(project.namespace, project)
-
fill_in 'project_name_edit', with: 'hello world'
-
click_button 'Save changes'
-
expect(page).to have_content "Project 'hello world' was successfully updated."
end
end
- describe 'Rename repository' do
- it 'shows errors for invalid project path/name' do
- visit edit_namespace_project_path(project.namespace, project)
-
- fill_in 'project_name', with: 'foo&bar'
- fill_in 'Path', with: 'foo&bar'
+ describe 'Rename repository section' do
+ context 'with invalid characters' do
+ it 'shows errors for invalid project path/name' do
+ rename_project(project, name: 'foo&bar', path: 'foo&bar')
+ expect(page).to have_field 'Project name', with: 'foo&bar'
+ expect(page).to have_field 'Path', with: 'foo&bar'
+ expect(page).to have_content "Name can contain only letters, digits, emojis, '_', '.', dash, space. It must start with letter, digit, emoji or '_'."
+ expect(page).to have_content "Path can contain only letters, digits, '_', '-' and '.'. Cannot start with '-', end in '.git' or end in '.atom'"
+ end
+ end
- click_button 'Rename project'
+ context 'when changing project name' do
+ it 'renames the repository' do
+ rename_project(project, name: 'bar')
+ expect(find('h1.title')).to have_content(project.name)
+ end
+
+ context 'with emojis' do
+ it 'shows error for invalid project name' do
+ rename_project(project, name: '🚀 foo bar ☁️')
+ expect(page).to have_field 'Project name', with: '🚀 foo bar ☁️'
+ expect(page).not_to have_content "Name can contain only letters, digits, emojis '_', '.', dash and space. It must start with letter, digit, emoji or '_'."
+ end
+ end
+ end
- expect(page).to have_field 'Project name', with: 'foo&bar'
- expect(page).to have_field 'Path', with: 'foo&bar'
- expect(page).to have_content "Name can contain only letters, digits, emojis, '_', '.', dash, space. It must start with letter, digit, emoji or '_'."
- expect(page).to have_content "Path can contain only letters, digits, '_', '-' and '.'. Cannot start with '-', end in '.git' or end in '.atom'"
+ context 'when changing project path' do
+ # Not using empty project because we need a repo to exist
+ let(:project) { create(:project, namespace: user.namespace, name: 'gitlabhq') }
+
+ before(:context) { TestEnv.clean_test_path }
+ after(:example) { TestEnv.clean_test_path }
+
+ specify 'the project is accessible via the new path' do
+ rename_project(project, path: 'bar')
+ new_path = namespace_project_path(project.namespace, 'bar')
+ visit new_path
+ expect(current_path).to eq(new_path)
+ expect(find('h1.title')).to have_content(project.name)
+ end
+
+ specify 'the project is accessible via a redirect from the old path' do
+ old_path = namespace_project_path(project.namespace, project)
+ rename_project(project, path: 'bar')
+ new_path = namespace_project_path(project.namespace, 'bar')
+ visit old_path
+ expect(current_path).to eq(new_path)
+ expect(find('h1.title')).to have_content(project.name)
+ end
+
+ context 'and a new project is added with the same path' do
+ it 'overrides the redirect' do
+ old_path = namespace_project_path(project.namespace, project)
+ rename_project(project, path: 'bar')
+ new_project = create(:empty_project, namespace: user.namespace, path: 'gitlabhq', name: 'quz')
+ visit old_path
+ expect(current_path).to eq(old_path)
+ expect(find('h1.title')).to have_content(new_project.name)
+ end
+ end
end
end
- describe 'Rename repository name with emojis' do
- it 'shows error for invalid project name' do
- visit edit_namespace_project_path(project.namespace, project)
-
- fill_in 'project_name', with: '🚀 foo bar ☁️'
+ describe 'Transfer project section', js: true do
+ # Not using empty project because we need a repo to exist
+ let!(:project) { create(:project, namespace: user.namespace, name: 'gitlabhq') }
+ let!(:group) { create(:group) }
+
+ before(:context) { TestEnv.clean_test_path }
+ before(:example) { group.add_owner(user) }
+ after(:example) { TestEnv.clean_test_path }
+
+ specify 'the project is accessible via the new path' do
+ transfer_project(project, group)
+ new_path = namespace_project_path(group, project)
+ visit new_path
+ expect(current_path).to eq(new_path)
+ expect(find('h1.title')).to have_content(project.name)
+ end
- click_button 'Rename project'
+ specify 'the project is accessible via a redirect from the old path' do
+ old_path = namespace_project_path(project.namespace, project)
+ transfer_project(project, group)
+ new_path = namespace_project_path(group, project)
+ visit old_path
+ expect(current_path).to eq(new_path)
+ expect(find('h1.title')).to have_content(project.name)
+ end
- expect(page).to have_field 'Project name', with: '🚀 foo bar ☁️'
- expect(page).not_to have_content "Name can contain only letters, digits, emojis '_', '.', dash and space. It must start with letter, digit, emoji or '_'."
+ context 'and a new project is added with the same path' do
+ it 'overrides the redirect' do
+ old_path = namespace_project_path(project.namespace, project)
+ transfer_project(project, group)
+ new_project = create(:empty_project, namespace: user.namespace, path: 'gitlabhq', name: 'quz')
+ visit old_path
+ expect(current_path).to eq(old_path)
+ expect(find('h1.title')).to have_content(new_project.name)
+ end
end
end
end
+
+def rename_project(project, name: nil, path: nil)
+ visit edit_namespace_project_path(project.namespace, project)
+ fill_in('project_name', with: name) if name
+ fill_in('Path', with: path) if path
+ click_button('Rename project')
+ wait_for_edit_project_page_reload
+ project.reload
+end
+
+def transfer_project(project, namespace)
+ visit edit_namespace_project_path(project.namespace, project)
+ select2(namespace.id, from: '#new_namespace_id')
+ click_button('Transfer project')
+ confirm_transfer_modal
+ wait_for_edit_project_page_reload
+ project.reload
+end
+
+def confirm_transfer_modal
+ fill_in('confirm_name_input', with: project.path)
+ click_button 'Confirm'
+end
+
+def wait_for_edit_project_page_reload
+ expect(find('.project-edit-container')).to have_content('Rename repository')
+end
diff --git a/spec/features/projects/ref_switcher_spec.rb b/spec/features/projects/ref_switcher_spec.rb
index 3b8f0b2d3f8..881ad7910dd 100644
--- a/spec/features/projects/ref_switcher_spec.rb
+++ b/spec/features/projects/ref_switcher_spec.rb
@@ -1,7 +1,6 @@
require 'rails_helper'
feature 'Ref switcher', feature: true, js: true do
- include WaitForAjax
let(:user) { create(:user) }
let(:project) { create(:project, :public) }
diff --git a/spec/features/projects/settings/integration_settings_spec.rb b/spec/features/projects/settings/integration_settings_spec.rb
new file mode 100644
index 00000000000..d3232f0cc16
--- /dev/null
+++ b/spec/features/projects/settings/integration_settings_spec.rb
@@ -0,0 +1,96 @@
+require 'spec_helper'
+
+feature 'Integration settings', feature: true do
+ let(:project) { create(:empty_project) }
+ let(:user) { create(:user) }
+ let(:role) { :developer }
+ let(:integrations_path) { namespace_project_settings_integrations_path(project.namespace, project) }
+
+ background do
+ login_as(user)
+ project.team << [user, role]
+ end
+
+ context 'for developer' do
+ given(:role) { :developer }
+
+ scenario 'to be disallowed to view' do
+ visit integrations_path
+
+ expect(page.status_code).to eq(404)
+ end
+ end
+
+ context 'for master' do
+ given(:role) { :master }
+
+ context 'Webhooks' do
+ let(:hook) { create(:project_hook, :all_events_enabled, enable_ssl_verification: true, project: project) }
+ let(:url) { generate(:url) }
+
+ scenario 'show list of webhooks' do
+ hook
+
+ visit integrations_path
+
+ expect(page.status_code).to eq(200)
+ expect(page).to have_content(hook.url)
+ expect(page).to have_content('SSL Verification: enabled')
+ expect(page).to have_content('Push Events')
+ expect(page).to have_content('Tag Push Events')
+ expect(page).to have_content('Issues Events')
+ expect(page).to have_content('Confidential Issues Events')
+ expect(page).to have_content('Note Events')
+ expect(page).to have_content('Merge Requests Events')
+ expect(page).to have_content('Pipeline Events')
+ expect(page).to have_content('Wiki Page Events')
+ end
+
+ scenario 'create webhook' do
+ visit integrations_path
+
+ fill_in 'hook_url', with: url
+ check 'Tag push events'
+ check 'Enable SSL verification'
+ check 'Job events'
+
+ click_button 'Add webhook'
+
+ expect(page).to have_content(url)
+ expect(page).to have_content('SSL Verification: enabled')
+ expect(page).to have_content('Push Events')
+ expect(page).to have_content('Tag Push Events')
+ expect(page).to have_content('Job events')
+ end
+
+ scenario 'edit existing webhook' do
+ hook
+ visit integrations_path
+
+ click_link 'Edit'
+ fill_in 'hook_url', with: url
+ check 'Enable SSL verification'
+ click_button 'Save changes'
+
+ expect(page).to have_content 'SSL Verification: enabled'
+ expect(page).to have_content(url)
+ end
+
+ scenario 'test existing webhook' do
+ WebMock.stub_request(:post, hook.url)
+ visit integrations_path
+
+ click_link 'Test'
+
+ expect(current_path).to eq(integrations_path)
+ end
+
+ scenario 'remove existing webhook' do
+ hook
+ visit integrations_path
+
+ expect { click_link 'Remove' }.to change(ProjectHook, :count).by(-1)
+ end
+ end
+ end
+end
diff --git a/spec/features/projects/settings/pipelines_settings_spec.rb b/spec/features/projects/settings/pipelines_settings_spec.rb
index 76cb240ea98..035c57eaa47 100644
--- a/spec/features/projects/settings/pipelines_settings_spec.rb
+++ b/spec/features/projects/settings/pipelines_settings_spec.rb
@@ -32,5 +32,16 @@ feature "Pipelines settings", feature: true do
expect(page).to have_button('Save changes', disabled: false)
expect(page).to have_field('Test coverage parsing', with: 'coverage_regex')
end
+
+ scenario 'updates auto_cancel_pending_pipelines' do
+ page.check('Auto-cancel redundant, pending pipelines')
+ click_on 'Save changes'
+
+ expect(page.status_code).to eq(200)
+ expect(page).to have_button('Save changes', disabled: false)
+
+ checkbox = find_field('project_auto_cancel_pending_pipelines')
+ expect(checkbox).to be_checked
+ end
end
end
diff --git a/spec/features/projects/snippets/show_spec.rb b/spec/features/projects/snippets/show_spec.rb
new file mode 100644
index 00000000000..cedf3778c7e
--- /dev/null
+++ b/spec/features/projects/snippets/show_spec.rb
@@ -0,0 +1,144 @@
+require 'spec_helper'
+
+feature 'Project snippet', :js, feature: true do
+ let(:user) { create(:user) }
+ let(:project) { create(:project, :repository) }
+ let(:snippet) { create(:project_snippet, project: project, file_name: file_name, content: content) }
+
+ before do
+ project.team << [user, :master]
+ login_as(user)
+ end
+
+ context 'Ruby file' do
+ let(:file_name) { 'popen.rb' }
+ let(:content) { project.repository.blob_at('master', 'files/ruby/popen.rb').data }
+
+ before do
+ visit namespace_project_snippet_path(project.namespace, project, snippet)
+
+ wait_for_ajax
+ end
+
+ it 'displays the blob' do
+ aggregate_failures do
+ # shows highlighted Ruby code
+ expect(page).to have_content("require 'fileutils'")
+
+ # does not show a viewer switcher
+ expect(page).not_to have_selector('.js-blob-viewer-switcher')
+
+ # shows an enabled copy button
+ expect(page).to have_selector('.js-copy-blob-source-btn:not(.disabled)')
+
+ # shows a raw button
+ expect(page).to have_link('Open raw')
+
+ # shows a download button
+ expect(page).to have_link('Download')
+ end
+ end
+ end
+
+ context 'Markdown file' do
+ let(:file_name) { 'ruby-style-guide.md' }
+ let(:content) { project.repository.blob_at('master', 'files/markdown/ruby-style-guide.md').data }
+
+ context 'visiting directly' do
+ before do
+ visit namespace_project_snippet_path(project.namespace, project, snippet)
+
+ wait_for_ajax
+ end
+
+ it 'displays the blob using the rich viewer' do
+ aggregate_failures do
+ # hides the simple viewer
+ expect(page).to have_selector('.blob-viewer[data-type="simple"]', visible: false)
+ expect(page).to have_selector('.blob-viewer[data-type="rich"]')
+
+ # shows rendered Markdown
+ expect(page).to have_link("PEP-8")
+
+ # shows a viewer switcher
+ expect(page).to have_selector('.js-blob-viewer-switcher')
+
+ # shows a disabled copy button
+ expect(page).to have_selector('.js-copy-blob-source-btn.disabled')
+
+ # shows a raw button
+ expect(page).to have_link('Open raw')
+
+ # shows a download button
+ expect(page).to have_link('Download')
+ end
+ end
+
+ context 'switching to the simple viewer' do
+ before do
+ find('.js-blob-viewer-switch-btn[data-viewer=simple]').click
+
+ wait_for_ajax
+ end
+
+ it 'displays the blob using the simple viewer' do
+ aggregate_failures do
+ # hides the rich viewer
+ expect(page).to have_selector('.blob-viewer[data-type="simple"]')
+ expect(page).to have_selector('.blob-viewer[data-type="rich"]', visible: false)
+
+ # shows highlighted Markdown code
+ expect(page).to have_content("[PEP-8](http://www.python.org/dev/peps/pep-0008/)")
+
+ # shows an enabled copy button
+ expect(page).to have_selector('.js-copy-blob-source-btn:not(.disabled)')
+ end
+ end
+
+ context 'switching to the rich viewer again' do
+ before do
+ find('.js-blob-viewer-switch-btn[data-viewer=rich]').click
+
+ wait_for_ajax
+ end
+
+ it 'displays the blob using the rich viewer' do
+ aggregate_failures do
+ # hides the simple viewer
+ expect(page).to have_selector('.blob-viewer[data-type="simple"]', visible: false)
+ expect(page).to have_selector('.blob-viewer[data-type="rich"]')
+
+ # shows an enabled copy button
+ expect(page).to have_selector('.js-copy-blob-source-btn:not(.disabled)')
+ end
+ end
+ end
+ end
+ end
+
+ context 'visiting with a line number anchor' do
+ before do
+ visit namespace_project_snippet_path(project.namespace, project, snippet, anchor: 'L1')
+
+ wait_for_ajax
+ end
+
+ it 'displays the blob using the simple viewer' do
+ aggregate_failures do
+ # hides the rich viewer
+ expect(page).to have_selector('.blob-viewer[data-type="simple"]')
+ expect(page).to have_selector('.blob-viewer[data-type="rich"]', visible: false)
+
+ # highlights the line in question
+ expect(page).to have_selector('#LC1.hll')
+
+ # shows highlighted Markdown code
+ expect(page).to have_content("[PEP-8](http://www.python.org/dev/peps/pep-0008/)")
+
+ # shows an enabled copy button
+ expect(page).to have_selector('.js-copy-blob-source-btn:not(.disabled)')
+ end
+ end
+ end
+ end
+end
diff --git a/spec/features/projects/snippets_spec.rb b/spec/features/projects/snippets_spec.rb
index d37e8ed4699..18689c17fe9 100644
--- a/spec/features/projects/snippets_spec.rb
+++ b/spec/features/projects/snippets_spec.rb
@@ -4,11 +4,27 @@ describe 'Project snippets', feature: true do
context 'when the project has snippets' do
let(:project) { create(:empty_project, :public) }
let!(:snippets) { create_list(:project_snippet, 2, :public, author: project.owner, project: project) }
- before do
- allow(Snippet).to receive(:default_per_page).and_return(1)
- visit namespace_project_snippets_path(project.namespace, project)
+ let!(:other_snippet) { create(:project_snippet) }
+
+ context 'pagination' do
+ before do
+ allow(Snippet).to receive(:default_per_page).and_return(1)
+
+ visit namespace_project_snippets_path(project.namespace, project)
+ end
+
+ it_behaves_like 'paginated snippets'
end
- it_behaves_like 'paginated snippets'
+ context 'list content' do
+ it 'contains all project snippets' do
+ visit namespace_project_snippets_path(project.namespace, project)
+
+ expect(page).to have_selector('.snippet-row', count: 2)
+
+ expect(page).to have_content(snippets[0].title)
+ expect(page).to have_content(snippets[1].title)
+ end
+ end
end
end
diff --git a/spec/features/projects/user_create_dir_spec.rb b/spec/features/projects/user_create_dir_spec.rb
index 2065abfb248..5dfdc465d7d 100644
--- a/spec/features/projects/user_create_dir_spec.rb
+++ b/spec/features/projects/user_create_dir_spec.rb
@@ -1,7 +1,6 @@
require 'spec_helper'
feature 'New directory creation', feature: true, js: true do
- include WaitForAjax
include TargetBranchHelpers
given(:user) { create(:user) }
diff --git a/spec/features/projects/view_on_env_spec.rb b/spec/features/projects/view_on_env_spec.rb
index ce5c5f21167..b7a41ca54e6 100644
--- a/spec/features/projects/view_on_env_spec.rb
+++ b/spec/features/projects/view_on_env_spec.rb
@@ -1,8 +1,6 @@
require 'spec_helper'
describe 'View on environment', js: true do
- include WaitForAjax
-
let(:branch_name) { 'feature' }
let(:file_path) { 'files/ruby/feature.rb' }
let(:project) { create(:project, :repository) }
@@ -25,7 +23,7 @@ describe 'View on environment', js: true do
project,
user,
start_branch: branch_name,
- target_branch: branch_name,
+ branch_name: branch_name,
commit_message: "Add .gitlab/route-map.yml",
file_path: '.gitlab/route-map.yml',
file_content: route_map
@@ -36,7 +34,7 @@ describe 'View on environment', js: true do
project,
user,
start_branch: branch_name,
- target_branch: branch_name,
+ branch_name: branch_name,
commit_message: "Update feature",
file_path: file_path,
file_content: "# Noop"
diff --git a/spec/features/projects/wiki/markdown_preview_spec.rb b/spec/features/projects/wiki/markdown_preview_spec.rb
index a1c386ddc18..49d7ef09e64 100644
--- a/spec/features/projects/wiki/markdown_preview_spec.rb
+++ b/spec/features/projects/wiki/markdown_preview_spec.rb
@@ -17,19 +17,23 @@ feature 'Projects > Wiki > User previews markdown changes', feature: true, js: t
login_as(user)
visit namespace_project_path(project.namespace, project)
- click_link 'Wiki'
+ find('.shortcuts-wiki').trigger('click')
WikiPages::CreateService.new(project, user, title: 'home', content: 'Home page').execute
end
context "while creating a new wiki page" do
context "when there are no spaces or hyphens in the page name" do
it "rewrites relative links as expected" do
- click_link 'New Page'
- fill_in :new_wiki_path, with: 'a/b/c/d'
- click_button 'Create Page'
+ find('.add-new-wiki').trigger('click')
+ page.within '#modal-new-wiki' do
+ fill_in :new_wiki_path, with: 'a/b/c/d'
+ click_button 'Create page'
+ end
- fill_in :wiki_content, with: wiki_content
- click_on "Preview"
+ page.within '.wiki-form' do
+ fill_in :wiki_content, with: wiki_content
+ click_on "Preview"
+ end
expect(page).to have_content("regular link")
@@ -42,12 +46,16 @@ feature 'Projects > Wiki > User previews markdown changes', feature: true, js: t
context "when there are spaces in the page name" do
it "rewrites relative links as expected" do
- click_link 'New Page'
- fill_in :new_wiki_path, with: 'a page/b page/c page/d page'
- click_button 'Create Page'
+ click_link 'New page'
+ page.within '#modal-new-wiki' do
+ fill_in :new_wiki_path, with: 'a page/b page/c page/d page'
+ click_button 'Create page'
+ end
- fill_in :wiki_content, with: wiki_content
- click_on "Preview"
+ page.within '.wiki-form' do
+ fill_in :wiki_content, with: wiki_content
+ click_on "Preview"
+ end
expect(page).to have_content("regular link")
@@ -60,12 +68,16 @@ feature 'Projects > Wiki > User previews markdown changes', feature: true, js: t
context "when there are hyphens in the page name" do
it "rewrites relative links as expected" do
- click_link 'New Page'
- fill_in :new_wiki_path, with: 'a-page/b-page/c-page/d-page'
- click_button 'Create Page'
+ click_link 'New page'
+ page.within '#modal-new-wiki' do
+ fill_in :new_wiki_path, with: 'a-page/b-page/c-page/d-page'
+ click_button 'Create page'
+ end
- fill_in :wiki_content, with: wiki_content
- click_on "Preview"
+ page.within '.wiki-form' do
+ fill_in :wiki_content, with: wiki_content
+ click_on "Preview"
+ end
expect(page).to have_content("regular link")
@@ -79,11 +91,17 @@ feature 'Projects > Wiki > User previews markdown changes', feature: true, js: t
context "while editing a wiki page" do
def create_wiki_page(path)
- click_link 'New Page'
- fill_in :new_wiki_path, with: path
- click_button 'Create Page'
- fill_in :wiki_content, with: 'content'
- click_on "Create page"
+ find('.add-new-wiki').trigger('click')
+
+ page.within '#modal-new-wiki' do
+ fill_in :new_wiki_path, with: path
+ click_button 'Create page'
+ end
+
+ page.within '.wiki-form' do
+ fill_in :wiki_content, with: 'content'
+ click_on "Create page"
+ end
end
context "when there are no spaces or hyphens in the page name" do
diff --git a/spec/features/projects/wiki/shortcuts_spec.rb b/spec/features/projects/wiki/shortcuts_spec.rb
new file mode 100644
index 00000000000..c1f6b0cce3b
--- /dev/null
+++ b/spec/features/projects/wiki/shortcuts_spec.rb
@@ -0,0 +1,20 @@
+require 'spec_helper'
+
+feature 'Wiki shortcuts', :feature, :js do
+ let(:user) { create(:user) }
+ let(:project) { create(:empty_project, namespace: user.namespace) }
+ let(:wiki_page) do
+ WikiPages::CreateService.new(project, user, title: 'home', content: 'Home page').execute
+ end
+
+ before do
+ login_as(user)
+ visit namespace_project_wiki_path(project.namespace, project, wiki_page)
+ end
+
+ scenario 'Visit edit wiki page using "e" keyboard shortcut' do
+ find('body').native.send_key('e')
+
+ expect(find('.wiki-page-title')).to have_content('Edit Page')
+ end
+end
diff --git a/spec/features/projects/wiki/user_creates_wiki_page_spec.rb b/spec/features/projects/wiki/user_creates_wiki_page_spec.rb
index fff8b9f3447..5c502ce4fb5 100644
--- a/spec/features/projects/wiki/user_creates_wiki_page_spec.rb
+++ b/spec/features/projects/wiki/user_creates_wiki_page_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-feature 'Projects > Wiki > User creates wiki page', feature: true do
+feature 'Projects > Wiki > User creates wiki page', js: true, feature: true do
let(:user) { create(:user) }
background do
@@ -8,17 +8,22 @@ feature 'Projects > Wiki > User creates wiki page', feature: true do
login_as(user)
visit namespace_project_path(project.namespace, project)
- click_link 'Wiki'
+ find('.shortcuts-wiki').trigger('click')
end
context 'in the user namespace' do
let(:project) { create(:project, namespace: user.namespace) }
context 'when wiki is empty' do
+ scenario 'commit message field has value "Create home"' do
+ expect(page).to have_field('wiki[message]', with: 'Create home')
+ end
+
scenario 'directly from the wiki home page' do
fill_in :wiki_content, with: 'My awesome wiki!'
- click_button 'Create page'
-
+ page.within '.wiki-form' do
+ click_button 'Create page'
+ end
expect(page).to have_content('Home')
expect(page).to have_content("Last edited by #{user.name}")
expect(page).to have_content('My awesome wiki!')
@@ -32,13 +37,20 @@ feature 'Projects > Wiki > User creates wiki page', feature: true do
context 'via the "new wiki page" page' do
scenario 'when the wiki page has a single word name', js: true do
- click_link 'New Page'
+ click_link 'New page'
- fill_in :new_wiki_path, with: 'foo'
- click_button 'Create Page'
+ page.within '#modal-new-wiki' do
+ fill_in :new_wiki_path, with: 'foo'
+ click_button 'Create page'
+ end
- fill_in :wiki_content, with: 'My awesome wiki!'
- click_button 'Create page'
+ # Commit message field should have correct value.
+ expect(page).to have_field('wiki[message]', with: 'Create foo')
+
+ page.within '.wiki-form' do
+ fill_in :wiki_content, with: 'My awesome wiki!'
+ click_button 'Create page'
+ end
expect(page).to have_content('Foo')
expect(page).to have_content("Last edited by #{user.name}")
@@ -46,13 +58,20 @@ feature 'Projects > Wiki > User creates wiki page', feature: true do
end
scenario 'when the wiki page has spaces in the name', js: true do
- click_link 'New Page'
+ click_link 'New page'
- fill_in :new_wiki_path, with: 'Spaces in the name'
- click_button 'Create Page'
+ page.within '#modal-new-wiki' do
+ fill_in :new_wiki_path, with: 'Spaces in the name'
+ click_button 'Create page'
+ end
- fill_in :wiki_content, with: 'My awesome wiki!'
- click_button 'Create page'
+ # Commit message field should have correct value.
+ expect(page).to have_field('wiki[message]', with: 'Create spaces in the name')
+
+ page.within '.wiki-form' do
+ fill_in :wiki_content, with: 'My awesome wiki!'
+ click_button 'Create page'
+ end
expect(page).to have_content('Spaces in the name')
expect(page).to have_content("Last edited by #{user.name}")
@@ -60,13 +79,20 @@ feature 'Projects > Wiki > User creates wiki page', feature: true do
end
scenario 'when the wiki page has hyphens in the name', js: true do
- click_link 'New Page'
+ click_link 'New page'
- fill_in :new_wiki_path, with: 'hyphens-in-the-name'
- click_button 'Create Page'
+ page.within '#modal-new-wiki' do
+ fill_in :new_wiki_path, with: 'hyphens-in-the-name'
+ click_button 'Create page'
+ end
- fill_in :wiki_content, with: 'My awesome wiki!'
- click_button 'Create page'
+ # Commit message field should have correct value.
+ expect(page).to have_field('wiki[message]', with: 'Create hyphens in the name')
+
+ page.within '.wiki-form' do
+ fill_in :wiki_content, with: 'My awesome wiki!'
+ click_button 'Create page'
+ end
expect(page).to have_content('Hyphens in the name')
expect(page).to have_content("Last edited by #{user.name}")
@@ -80,9 +106,15 @@ feature 'Projects > Wiki > User creates wiki page', feature: true do
let(:project) { create(:project, namespace: create(:group, :public)) }
context 'when wiki is empty' do
+ scenario 'commit message field has value "Create home"' do
+ expect(page).to have_field('wiki[message]', with: 'Create home')
+ end
+
scenario 'directly from the wiki home page' do
fill_in :wiki_content, with: 'My awesome wiki!'
- click_button 'Create page'
+ page.within '.wiki-form' do
+ click_button 'Create page'
+ end
expect(page).to have_content('Home')
expect(page).to have_content("Last edited by #{user.name}")
@@ -96,13 +128,20 @@ feature 'Projects > Wiki > User creates wiki page', feature: true do
end
scenario 'via the "new wiki page" page', js: true do
- click_link 'New Page'
+ click_link 'New page'
- fill_in :new_wiki_path, with: 'foo'
- click_button 'Create Page'
+ page.within '#modal-new-wiki' do
+ fill_in :new_wiki_path, with: 'foo'
+ click_button 'Create page'
+ end
- fill_in :wiki_content, with: 'My awesome wiki!'
- click_button 'Create page'
+ # Commit message field should have correct value.
+ expect(page).to have_field('wiki[message]', with: 'Create foo')
+
+ page.within '.wiki-form' do
+ fill_in :wiki_content, with: 'My awesome wiki!'
+ click_button 'Create page'
+ end
expect(page).to have_content('Foo')
expect(page).to have_content("Last edited by #{user.name}")
diff --git a/spec/features/projects/wiki/user_updates_wiki_page_spec.rb b/spec/features/projects/wiki/user_updates_wiki_page_spec.rb
index aedc0333cb9..86cf520ea80 100644
--- a/spec/features/projects/wiki/user_updates_wiki_page_spec.rb
+++ b/spec/features/projects/wiki/user_updates_wiki_page_spec.rb
@@ -19,6 +19,9 @@ feature 'Projects > Wiki > User updates wiki page', feature: true do
scenario 'success when the wiki content is not empty' do
click_link 'Edit'
+ # Commit message field should have correct value.
+ expect(page).to have_field('wiki[message]', with: 'Update home')
+
fill_in :wiki_content, with: 'My awesome wiki!'
click_button 'Save changes'
@@ -48,6 +51,9 @@ feature 'Projects > Wiki > User updates wiki page', feature: true do
scenario 'the home page' do
click_link 'Edit'
+ # Commit message field should have correct value.
+ expect(page).to have_field('wiki[message]', with: 'Update home')
+
fill_in :wiki_content, with: 'My awesome wiki!'
click_button 'Save changes'
diff --git a/spec/features/projects_spec.rb b/spec/features/projects_spec.rb
index ba56030e28d..060e19596ae 100644
--- a/spec/features/projects_spec.rb
+++ b/spec/features/projects_spec.rb
@@ -2,7 +2,7 @@ require 'spec_helper'
feature 'Project', feature: true do
describe 'description' do
- let(:project) { create(:project) }
+ let(:project) { create(:project, :repository) }
let(:path) { namespace_project_path(project.namespace, project) }
before do
@@ -36,7 +36,7 @@ feature 'Project', feature: true do
describe 'remove forked relationship', js: true do
let(:user) { create(:user) }
- let(:project) { create(:project, namespace: user.namespace) }
+ let(:project) { create(:empty_project, namespace: user.namespace) }
before do
login_with user
@@ -57,7 +57,7 @@ feature 'Project', feature: true do
describe 'removal', js: true do
let(:user) { create(:user, username: 'test', name: 'test') }
- let(:project) { create(:project, namespace: user.namespace, name: 'project1') }
+ let(:project) { create(:empty_project, namespace: user.namespace, name: 'project1') }
before do
login_with(user)
@@ -75,10 +75,8 @@ feature 'Project', feature: true do
end
describe 'project title' do
- include WaitForAjax
-
let(:user) { create(:user) }
- let(:project) { create(:project, namespace: user.namespace) }
+ let(:project) { create(:empty_project, namespace: user.namespace) }
before do
login_with(user)
@@ -94,8 +92,8 @@ feature 'Project', feature: true do
describe 'project title' do
let(:user) { create(:user) }
- let(:project) { create(:project, namespace: user.namespace) }
- let(:project2) { create(:project, namespace: user.namespace, path: 'test') }
+ let(:project) { create(:empty_project, namespace: user.namespace) }
+ let(:project2) { create(:empty_project, namespace: user.namespace, path: 'test') }
let(:issue) { create(:issue, project: project) }
context 'on issues page', js: true do
diff --git a/spec/features/protected_branches/access_control_ce_spec.rb b/spec/features/protected_branches/access_control_ce_spec.rb
deleted file mode 100644
index e4aca25a339..00000000000
--- a/spec/features/protected_branches/access_control_ce_spec.rb
+++ /dev/null
@@ -1,79 +0,0 @@
-RSpec.shared_examples "protected branches > access control > CE" do
- ProtectedBranch::PushAccessLevel.human_access_levels.each do |(access_type_id, access_type_name)|
- it "allows creating protected branches that #{access_type_name} can push to" do
- visit namespace_project_protected_branches_path(project.namespace, project)
- set_protected_branch_name('master')
- within('.new_protected_branch') do
- allowed_to_push_button = find(".js-allowed-to-push")
-
- unless allowed_to_push_button.text == access_type_name
- allowed_to_push_button.click
- within(".dropdown.open .dropdown-menu") { click_on access_type_name }
- end
- end
- click_on "Protect"
-
- expect(ProtectedBranch.count).to eq(1)
- expect(ProtectedBranch.last.push_access_levels.map(&:access_level)).to eq([access_type_id])
- end
-
- it "allows updating protected branches so that #{access_type_name} can push to them" do
- visit namespace_project_protected_branches_path(project.namespace, project)
- set_protected_branch_name('master')
- click_on "Protect"
-
- expect(ProtectedBranch.count).to eq(1)
-
- within(".protected-branches-list") do
- find(".js-allowed-to-push").click
-
- within('.js-allowed-to-push-container') do
- expect(first("li")).to have_content("Roles")
- click_on access_type_name
- end
- end
-
- wait_for_ajax
- expect(ProtectedBranch.last.push_access_levels.map(&:access_level)).to include(access_type_id)
- end
- end
-
- ProtectedBranch::MergeAccessLevel.human_access_levels.each do |(access_type_id, access_type_name)|
- it "allows creating protected branches that #{access_type_name} can merge to" do
- visit namespace_project_protected_branches_path(project.namespace, project)
- set_protected_branch_name('master')
- within('.new_protected_branch') do
- allowed_to_merge_button = find(".js-allowed-to-merge")
-
- unless allowed_to_merge_button.text == access_type_name
- allowed_to_merge_button.click
- within(".dropdown.open .dropdown-menu") { click_on access_type_name }
- end
- end
- click_on "Protect"
-
- expect(ProtectedBranch.count).to eq(1)
- expect(ProtectedBranch.last.merge_access_levels.map(&:access_level)).to eq([access_type_id])
- end
-
- it "allows updating protected branches so that #{access_type_name} can merge to them" do
- visit namespace_project_protected_branches_path(project.namespace, project)
- set_protected_branch_name('master')
- click_on "Protect"
-
- expect(ProtectedBranch.count).to eq(1)
-
- within(".protected-branches-list") do
- find(".js-allowed-to-merge").click
-
- within('.js-allowed-to-merge-container') do
- expect(first("li")).to have_content("Roles")
- click_on access_type_name
- end
- end
-
- wait_for_ajax
- expect(ProtectedBranch.last.merge_access_levels.map(&:access_level)).to include(access_type_id)
- end
- end
-end
diff --git a/spec/features/protected_branches_spec.rb b/spec/features/protected_branches_spec.rb
index 1a3f7b970f6..884d1bbb10c 100644
--- a/spec/features/protected_branches_spec.rb
+++ b/spec/features/protected_branches_spec.rb
@@ -1,16 +1,13 @@
require 'spec_helper'
-Dir["./spec/features/protected_branches/*.rb"].sort.each { |f| require f }
feature 'Projected Branches', feature: true, js: true do
- include WaitForAjax
-
let(:user) { create(:user, :admin) }
- let(:project) { create(:project) }
+ let(:project) { create(:project, :repository) }
before { login_as(user) }
def set_protected_branch_name(branch_name)
- find(".js-protected-branch-select").click
+ find(".js-protected-branch-select").trigger('click')
find(".dropdown-input-field").set(branch_name)
click_on("Create wildcard #{branch_name}")
end
diff --git a/spec/features/protected_tags_spec.rb b/spec/features/protected_tags_spec.rb
new file mode 100644
index 00000000000..66236dbc7fc
--- /dev/null
+++ b/spec/features/protected_tags_spec.rb
@@ -0,0 +1,92 @@
+require 'spec_helper'
+
+feature 'Projected Tags', feature: true, js: true do
+ let(:user) { create(:user, :admin) }
+ let(:project) { create(:project, :repository) }
+
+ before { login_as(user) }
+
+ def set_protected_tag_name(tag_name)
+ find(".js-protected-tag-select").click
+ find(".dropdown-input-field").set(tag_name)
+ click_on("Create wildcard #{tag_name}")
+ find('.protected-tags-dropdown .dropdown-menu', visible: false)
+ end
+
+ describe "explicit protected tags" do
+ it "allows creating explicit protected tags" do
+ visit namespace_project_protected_tags_path(project.namespace, project)
+ set_protected_tag_name('some-tag')
+ click_on "Protect"
+
+ within(".protected-tags-list") { expect(page).to have_content('some-tag') }
+ expect(ProtectedTag.count).to eq(1)
+ expect(ProtectedTag.last.name).to eq('some-tag')
+ end
+
+ it "displays the last commit on the matching tag if it exists" do
+ commit = create(:commit, project: project)
+ project.repository.add_tag(user, 'some-tag', commit.id)
+
+ visit namespace_project_protected_tags_path(project.namespace, project)
+ set_protected_tag_name('some-tag')
+ click_on "Protect"
+
+ within(".protected-tags-list") { expect(page).to have_content(commit.id[0..7]) }
+ end
+
+ it "displays an error message if the named tag does not exist" do
+ visit namespace_project_protected_tags_path(project.namespace, project)
+ set_protected_tag_name('some-tag')
+ click_on "Protect"
+
+ within(".protected-tags-list") { expect(page).to have_content('tag was removed') }
+ end
+ end
+
+ describe "wildcard protected tags" do
+ it "allows creating protected tags with a wildcard" do
+ visit namespace_project_protected_tags_path(project.namespace, project)
+ set_protected_tag_name('*-stable')
+ click_on "Protect"
+
+ within(".protected-tags-list") { expect(page).to have_content('*-stable') }
+ expect(ProtectedTag.count).to eq(1)
+ expect(ProtectedTag.last.name).to eq('*-stable')
+ end
+
+ it "displays the number of matching tags" do
+ project.repository.add_tag(user, 'production-stable', 'master')
+ project.repository.add_tag(user, 'staging-stable', 'master')
+
+ visit namespace_project_protected_tags_path(project.namespace, project)
+ set_protected_tag_name('*-stable')
+ click_on "Protect"
+
+ within(".protected-tags-list") { expect(page).to have_content("2 matching tags") }
+ end
+
+ it "displays all the tags matching the wildcard" do
+ project.repository.add_tag(user, 'production-stable', 'master')
+ project.repository.add_tag(user, 'staging-stable', 'master')
+ project.repository.add_tag(user, 'development', 'master')
+
+ visit namespace_project_protected_tags_path(project.namespace, project)
+ set_protected_tag_name('*-stable')
+ click_on "Protect"
+
+ visit namespace_project_protected_tags_path(project.namespace, project)
+ click_on "2 matching tags"
+
+ within(".protected-tags-list") do
+ expect(page).to have_content("production-stable")
+ expect(page).to have_content("staging-stable")
+ expect(page).not_to have_content("development")
+ end
+ end
+ end
+
+ describe "access control" do
+ include_examples "protected tags > access control > CE"
+ end
+end
diff --git a/spec/features/raven_js_spec.rb b/spec/features/raven_js_spec.rb
new file mode 100644
index 00000000000..e8fa49c18cb
--- /dev/null
+++ b/spec/features/raven_js_spec.rb
@@ -0,0 +1,23 @@
+require 'spec_helper'
+
+feature 'RavenJS', :feature, :js do
+ let(:raven_path) { '/raven.bundle.js' }
+
+ it 'should not load raven if sentry is disabled' do
+ visit new_user_session_path
+
+ expect(has_requested_raven).to eq(false)
+ end
+
+ it 'should load raven if sentry is enabled' do
+ stub_application_setting(clientside_sentry_dsn: 'https://key@domain.com/id', clientside_sentry_enabled: true)
+
+ visit new_user_session_path
+
+ expect(has_requested_raven).to eq(true)
+ end
+
+ def has_requested_raven
+ page.driver.network_traffic.one? {|request| request.url.end_with?(raven_path)}
+ end
+end
diff --git a/spec/features/search_spec.rb b/spec/features/search_spec.rb
index a6560a81096..2fda7758407 100644
--- a/spec/features/search_spec.rb
+++ b/spec/features/search_spec.rb
@@ -2,11 +2,10 @@ require 'spec_helper'
describe "Search", feature: true do
include FilteredSearchHelpers
- include WaitForAjax
let(:user) { create(:user) }
- let(:project) { create(:project, namespace: user.namespace) }
- let!(:issue) { create(:issue, project: project, assignee: user) }
+ let(:project) { create(:empty_project, namespace: user.namespace) }
+ let!(:issue) { create(:issue, project: project, assignees: [user]) }
let!(:issue2) { create(:issue, project: project, author: user) }
before do
@@ -21,13 +20,14 @@ describe "Search", feature: true do
context 'search filters', js: true do
let(:group) { create(:group) }
+ let!(:group_project) { create(:empty_project, group: group) }
before do
group.add_owner(user)
end
it 'shows group name after filtering' do
- find('.js-search-group-dropdown').click
+ find('.js-search-group-dropdown').trigger('click')
wait_for_ajax
page.within '.search-holder' do
@@ -37,9 +37,27 @@ describe "Search", feature: true do
expect(find('.js-search-group-dropdown')).to have_content(group.name)
end
+ it 'filters by group projects after filtering by group' do
+ find('.js-search-group-dropdown').trigger('click')
+ wait_for_ajax
+
+ page.within '.search-holder' do
+ click_link group.name
+ end
+
+ expect(find('.js-search-group-dropdown')).to have_content(group.name)
+
+ page.within('.project-filter') do
+ find('.js-search-project-dropdown').trigger('click')
+ wait_for_ajax
+
+ expect(page).to have_link(group_project.name_with_namespace)
+ end
+ end
+
it 'shows project name after filtering' do
page.within('.project-filter') do
- find('.js-search-project-dropdown').click
+ find('.js-search-project-dropdown').trigger('click')
wait_for_ajax
click_link project.name_with_namespace
@@ -62,6 +80,7 @@ describe "Search", feature: true do
context 'search for comments' do
context 'when comment belongs to a invalid commit' do
+ let(:project) { create(:project, :repository) }
let(:note) { create(:note_on_commit, author: user, project: project, commit_id: project.repository.commit.id, note: 'Bug here') }
before { note.update_attributes(commit_id: 12345678) }
@@ -103,6 +122,7 @@ describe "Search", feature: true do
end
it 'finds a commit' do
+ project = create(:project, :repository) { |p| p.add_reporter(user) }
visit namespace_project_path(project.namespace, project)
page.within '.search' do
@@ -116,16 +136,19 @@ describe "Search", feature: true do
end
it 'finds a code' do
+ project = create(:project, :repository) { |p| p.add_reporter(user) }
visit namespace_project_path(project.namespace, project)
page.within '.search' do
- fill_in 'search', with: 'def'
+ fill_in 'search', with: 'application.js'
click_button 'Go'
end
click_link "Code"
expect(page).to have_selector('.file-content .code')
+
+ expect(page).to have_selector("span.line[lang='javascript']")
end
end
@@ -162,6 +185,8 @@ describe "Search", feature: true do
end
context 'click the links in the category search dropdown', js: true do
+ let!(:merge_request) { create(:merge_request, source_project: project, author: user, assignee: user) }
+
before do
page.find('#search').click
end
@@ -218,6 +243,8 @@ describe "Search", feature: true do
end
describe 'search for commits' do
+ let(:project) { create(:project, :repository) }
+
before do
visit search_path(project_id: project.id)
end
diff --git a/spec/features/security/project/internal_access_spec.rb b/spec/features/security/project/internal_access_spec.rb
index 1a66d1a6a1e..78a76d9c112 100644
--- a/spec/features/security/project/internal_access_spec.rb
+++ b/spec/features/security/project/internal_access_spec.rb
@@ -3,7 +3,7 @@ require 'spec_helper'
describe "Internal Project Access", feature: true do
include AccessMatchers
- let(:project) { create(:project, :internal) }
+ set(:project) { create(:project, :internal) }
describe "Project should be internal" do
describe '#internal?' do
@@ -399,6 +399,58 @@ describe "Internal Project Access", feature: true do
end
end
+ describe 'GET /:project_path/builds/:id/trace' do
+ let(:pipeline) { create(:ci_pipeline, project: project) }
+ let(:build) { create(:ci_build, pipeline: pipeline) }
+ subject { trace_namespace_project_build_path(project.namespace, project, build.id) }
+
+ context 'when allowed for public and internal' do
+ before do
+ project.update(public_builds: true)
+ end
+
+ it { is_expected.to be_allowed_for(:admin) }
+ it { is_expected.to be_allowed_for(:owner).of(project) }
+ it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:developer).of(project) }
+ it { is_expected.to be_allowed_for(:reporter).of(project) }
+ it { is_expected.to be_allowed_for(:guest).of(project) }
+ it { is_expected.to be_allowed_for(:user) }
+ it { is_expected.to be_denied_for(:external) }
+ it { is_expected.to be_denied_for(:visitor) }
+ end
+
+ context 'when disallowed for public and internal' do
+ before do
+ project.update(public_builds: false)
+ end
+
+ it { is_expected.to be_allowed_for(:admin) }
+ it { is_expected.to be_allowed_for(:owner).of(project) }
+ it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:developer).of(project) }
+ it { is_expected.to be_allowed_for(:reporter).of(project) }
+ it { is_expected.to be_denied_for(:guest).of(project) }
+ it { is_expected.to be_denied_for(:user) }
+ it { is_expected.to be_denied_for(:external) }
+ it { is_expected.to be_denied_for(:visitor) }
+ end
+ end
+
+ describe "GET /:project_path/pipeline_schedules" do
+ subject { namespace_project_pipeline_schedules_path(project.namespace, project) }
+
+ it { is_expected.to be_allowed_for(:admin) }
+ it { is_expected.to be_allowed_for(:owner).of(project) }
+ it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:developer).of(project) }
+ it { is_expected.to be_allowed_for(:reporter).of(project) }
+ it { is_expected.to be_allowed_for(:guest).of(project) }
+ it { is_expected.to be_allowed_for(:user) }
+ it { is_expected.to be_denied_for(:external) }
+ it { is_expected.to be_denied_for(:visitor) }
+ end
+
describe "GET /:project_path/environments" do
subject { namespace_project_environments_path(project.namespace, project) }
@@ -428,6 +480,21 @@ describe "Internal Project Access", feature: true do
it { is_expected.to be_denied_for(:visitor) }
end
+ describe "GET /:project_path/environments/:id/deployments" do
+ let(:environment) { create(:environment, project: project) }
+ subject { namespace_project_environment_deployments_path(project.namespace, project, environment) }
+
+ it { is_expected.to be_allowed_for(:admin) }
+ it { is_expected.to be_allowed_for(:owner).of(project) }
+ it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:developer).of(project) }
+ it { is_expected.to be_allowed_for(:reporter).of(project) }
+ it { is_expected.to be_denied_for(:guest).of(project) }
+ it { is_expected.to be_denied_for(:user) }
+ it { is_expected.to be_denied_for(:external) }
+ it { is_expected.to be_denied_for(:visitor) }
+ end
+
describe "GET /:project_path/environments/new" do
subject { new_namespace_project_environment_path(project.namespace, project) }
@@ -443,9 +510,12 @@ describe "Internal Project Access", feature: true do
end
describe "GET /:project_path/container_registry" do
+ let(:container_repository) { create(:container_repository) }
+
before do
- stub_container_registry_tags('latest')
+ stub_container_registry_tags(repository: :any, tags: ['latest'])
stub_container_registry_config(enabled: true)
+ project.container_repositories << container_repository
end
subject { namespace_project_container_registry_index_path(project.namespace, project) }
diff --git a/spec/features/security/project/private_access_spec.rb b/spec/features/security/project/private_access_spec.rb
index ad3bd60a313..a66f6e09055 100644
--- a/spec/features/security/project/private_access_spec.rb
+++ b/spec/features/security/project/private_access_spec.rb
@@ -3,7 +3,7 @@ require 'spec_helper'
describe "Private Project Access", feature: true do
include AccessMatchers
- let(:project) { create(:project, :private, public_builds: false) }
+ set(:project) { create(:project, :private, public_builds: false) }
describe "Project should be private" do
describe '#private?' do
@@ -388,6 +388,38 @@ describe "Private Project Access", feature: true do
end
end
+ describe 'GET /:project_path/builds/:id/trace' do
+ let(:pipeline) { create(:ci_pipeline, project: project) }
+ let(:build) { create(:ci_build, pipeline: pipeline) }
+ subject { trace_namespace_project_build_path(project.namespace, project, build.id) }
+
+ it { is_expected.to be_allowed_for(:admin) }
+ it { is_expected.to be_allowed_for(:owner).of(project) }
+ it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:developer).of(project) }
+ it { is_expected.to be_allowed_for(:reporter).of(project) }
+ it { is_expected.to be_denied_for(:guest).of(project) }
+ it { is_expected.to be_denied_for(:user) }
+ it { is_expected.to be_denied_for(:external) }
+ it { is_expected.to be_denied_for(:visitor) }
+
+ context 'when public builds is enabled' do
+ before do
+ project.update(public_builds: true)
+ end
+
+ it { is_expected.to be_allowed_for(:guest).of(project) }
+ end
+
+ context 'when public builds is disabled' do
+ before do
+ project.update(public_builds: false)
+ end
+
+ it { is_expected.to be_denied_for(:guest).of(project) }
+ end
+ end
+
describe "GET /:project_path/environments" do
subject { namespace_project_environments_path(project.namespace, project) }
@@ -417,6 +449,21 @@ describe "Private Project Access", feature: true do
it { is_expected.to be_denied_for(:visitor) }
end
+ describe "GET /:project_path/environments/:id/deployments" do
+ let(:environment) { create(:environment, project: project) }
+ subject { namespace_project_environment_deployments_path(project.namespace, project, environment) }
+
+ it { is_expected.to be_allowed_for(:admin) }
+ it { is_expected.to be_allowed_for(:owner).of(project) }
+ it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:developer).of(project) }
+ it { is_expected.to be_allowed_for(:reporter).of(project) }
+ it { is_expected.to be_denied_for(:guest).of(project) }
+ it { is_expected.to be_denied_for(:user) }
+ it { is_expected.to be_denied_for(:external) }
+ it { is_expected.to be_denied_for(:visitor) }
+ end
+
describe "GET /:project_path/environments/new" do
subject { new_namespace_project_environment_path(project.namespace, project) }
@@ -431,10 +478,55 @@ describe "Private Project Access", feature: true do
it { is_expected.to be_denied_for(:visitor) }
end
+ describe "GET /:project_path/pipeline_schedules" do
+ subject { namespace_project_pipeline_schedules_path(project.namespace, project) }
+
+ it { is_expected.to be_allowed_for(:admin) }
+ it { is_expected.to be_allowed_for(:owner).of(project) }
+ it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:developer).of(project) }
+ it { is_expected.to be_allowed_for(:reporter).of(project) }
+ it { is_expected.to be_denied_for(:guest).of(project) }
+ it { is_expected.to be_denied_for(:user) }
+ it { is_expected.to be_denied_for(:external) }
+ it { is_expected.to be_denied_for(:visitor) }
+ end
+
+ describe "GET /:project_path/pipeline_schedules/new" do
+ subject { new_namespace_project_pipeline_schedule_path(project.namespace, project) }
+
+ it { is_expected.to be_allowed_for(:admin) }
+ it { is_expected.to be_allowed_for(:owner).of(project) }
+ it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:developer).of(project) }
+ it { is_expected.to be_denied_for(:reporter).of(project) }
+ it { is_expected.to be_denied_for(:guest).of(project) }
+ it { is_expected.to be_denied_for(:user) }
+ it { is_expected.to be_denied_for(:external) }
+ it { is_expected.to be_denied_for(:visitor) }
+ end
+
+ describe "GET /:project_path/environments/new" do
+ subject { new_namespace_project_pipeline_schedule_path(project.namespace, project) }
+
+ it { is_expected.to be_allowed_for(:admin) }
+ it { is_expected.to be_allowed_for(:owner).of(project) }
+ it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:developer).of(project) }
+ it { is_expected.to be_denied_for(:reporter).of(project) }
+ it { is_expected.to be_denied_for(:guest).of(project) }
+ it { is_expected.to be_denied_for(:user) }
+ it { is_expected.to be_denied_for(:external) }
+ it { is_expected.to be_denied_for(:visitor) }
+ end
+
describe "GET /:project_path/container_registry" do
+ let(:container_repository) { create(:container_repository) }
+
before do
- stub_container_registry_tags('latest')
+ stub_container_registry_tags(repository: :any, tags: ['latest'])
stub_container_registry_config(enabled: true)
+ project.container_repositories << container_repository
end
subject { namespace_project_container_registry_index_path(project.namespace, project) }
diff --git a/spec/features/security/project/public_access_spec.rb b/spec/features/security/project/public_access_spec.rb
index e06aab4e0b2..5cd575500c3 100644
--- a/spec/features/security/project/public_access_spec.rb
+++ b/spec/features/security/project/public_access_spec.rb
@@ -3,7 +3,7 @@ require 'spec_helper'
describe "Public Project Access", feature: true do
include AccessMatchers
- let(:project) { create(:project, :public) }
+ set(:project) { create(:project, :public) }
describe "Project should be public" do
describe '#public?' do
@@ -219,6 +219,58 @@ describe "Public Project Access", feature: true do
end
end
+ describe 'GET /:project_path/builds/:id/trace' do
+ let(:pipeline) { create(:ci_pipeline, project: project) }
+ let(:build) { create(:ci_build, pipeline: pipeline) }
+ subject { trace_namespace_project_build_path(project.namespace, project, build.id) }
+
+ context 'when allowed for public' do
+ before do
+ project.update(public_builds: true)
+ end
+
+ it { is_expected.to be_allowed_for(:admin) }
+ it { is_expected.to be_allowed_for(:owner).of(project) }
+ it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:developer).of(project) }
+ it { is_expected.to be_allowed_for(:reporter).of(project) }
+ it { is_expected.to be_allowed_for(:guest).of(project) }
+ it { is_expected.to be_allowed_for(:user) }
+ it { is_expected.to be_allowed_for(:external) }
+ it { is_expected.to be_allowed_for(:visitor) }
+ end
+
+ context 'when disallowed for public' do
+ before do
+ project.update(public_builds: false)
+ end
+
+ it { is_expected.to be_allowed_for(:admin) }
+ it { is_expected.to be_allowed_for(:owner).of(project) }
+ it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:developer).of(project) }
+ it { is_expected.to be_allowed_for(:reporter).of(project) }
+ it { is_expected.to be_denied_for(:guest).of(project) }
+ it { is_expected.to be_denied_for(:user) }
+ it { is_expected.to be_denied_for(:external) }
+ it { is_expected.to be_denied_for(:visitor) }
+ end
+ end
+
+ describe "GET /:project_path/pipeline_schedules" do
+ subject { namespace_project_pipeline_schedules_path(project.namespace, project) }
+
+ it { is_expected.to be_allowed_for(:admin) }
+ it { is_expected.to be_allowed_for(:owner).of(project) }
+ it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:developer).of(project) }
+ it { is_expected.to be_allowed_for(:reporter).of(project) }
+ it { is_expected.to be_allowed_for(:guest).of(project) }
+ it { is_expected.to be_allowed_for(:user) }
+ it { is_expected.to be_allowed_for(:external) }
+ it { is_expected.to be_allowed_for(:visitor) }
+ end
+
describe "GET /:project_path/environments" do
subject { namespace_project_environments_path(project.namespace, project) }
@@ -248,6 +300,21 @@ describe "Public Project Access", feature: true do
it { is_expected.to be_denied_for(:visitor) }
end
+ describe "GET /:project_path/environments/:id/deployments" do
+ let(:environment) { create(:environment, project: project) }
+ subject { namespace_project_environment_deployments_path(project.namespace, project, environment) }
+
+ it { is_expected.to be_allowed_for(:admin) }
+ it { is_expected.to be_allowed_for(:owner).of(project) }
+ it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:developer).of(project) }
+ it { is_expected.to be_allowed_for(:reporter).of(project) }
+ it { is_expected.to be_denied_for(:guest).of(project) }
+ it { is_expected.to be_denied_for(:user) }
+ it { is_expected.to be_denied_for(:external) }
+ it { is_expected.to be_denied_for(:visitor) }
+ end
+
describe "GET /:project_path/environments/new" do
subject { new_namespace_project_environment_path(project.namespace, project) }
@@ -443,9 +510,12 @@ describe "Public Project Access", feature: true do
end
describe "GET /:project_path/container_registry" do
+ let(:container_repository) { create(:container_repository) }
+
before do
- stub_container_registry_tags('latest')
+ stub_container_registry_tags(repository: :any, tags: ['latest'])
stub_container_registry_config(enabled: true)
+ project.container_repositories << container_repository
end
subject { namespace_project_container_registry_index_path(project.namespace, project) }
diff --git a/spec/features/signup_spec.rb b/spec/features/signup_spec.rb
index 9fde8d6e5cf..d7b6dda4946 100644
--- a/spec/features/signup_spec.rb
+++ b/spec/features/signup_spec.rb
@@ -3,7 +3,7 @@ require 'spec_helper'
feature 'Signup', feature: true do
describe 'signup with no errors' do
context "when sending confirmation email" do
- before { allow_any_instance_of(ApplicationSetting).to receive(:send_user_confirmation_email).and_return(true) }
+ before { stub_application_setting(send_user_confirmation_email: true) }
it 'creates the user account and sends a confirmation email' do
user = build(:user)
@@ -23,7 +23,7 @@ feature 'Signup', feature: true do
end
context "when not sending confirmation email" do
- before { allow_any_instance_of(ApplicationSetting).to receive(:send_user_confirmation_email).and_return(false) }
+ before { stub_application_setting(send_user_confirmation_email: false) }
it 'creates the user account and goes to dashboard' do
user = build(:user)
diff --git a/spec/features/snippets/create_snippet_spec.rb b/spec/features/snippets/create_snippet_spec.rb
index 5470276bf06..9409c323288 100644
--- a/spec/features/snippets/create_snippet_spec.rb
+++ b/spec/features/snippets/create_snippet_spec.rb
@@ -1,6 +1,6 @@
require 'rails_helper'
-feature 'Create Snippet', feature: true do
+feature 'Create Snippet', :js, feature: true do
before do
login_as :user
visit new_snippet_path
@@ -9,10 +9,11 @@ feature 'Create Snippet', feature: true do
scenario 'Authenticated user creates a snippet' do
fill_in 'personal_snippet_title', with: 'My Snippet Title'
page.within('.file-editor') do
- find(:xpath, "//input[@id='personal_snippet_content']").set 'Hello World!'
+ find('.ace_editor').native.send_keys 'Hello World!'
end
click_button 'Create snippet'
+ wait_for_ajax
expect(page).to have_content('My Snippet Title')
expect(page).to have_content('Hello World!')
@@ -22,10 +23,11 @@ feature 'Create Snippet', feature: true do
fill_in 'personal_snippet_title', with: 'My Snippet Title'
page.within('.file-editor') do
find(:xpath, "//input[@id='personal_snippet_file_name']").set 'snippet+file+name'
- find(:xpath, "//input[@id='personal_snippet_content']").set 'Hello World!'
+ find('.ace_editor').native.send_keys 'Hello World!'
end
click_button 'Create snippet'
+ wait_for_ajax
expect(page).to have_content('My Snippet Title')
expect(page).to have_content('snippet+file+name')
diff --git a/spec/features/snippets/explore_spec.rb b/spec/features/snippets/explore_spec.rb
index 10a4597e467..fd097fe2e74 100644
--- a/spec/features/snippets/explore_spec.rb
+++ b/spec/features/snippets/explore_spec.rb
@@ -1,11 +1,11 @@
require 'rails_helper'
feature 'Explore Snippets', feature: true do
- scenario 'User should see snippets that are not private' do
- public_snippet = create(:personal_snippet, :public)
- internal_snippet = create(:personal_snippet, :internal)
- private_snippet = create(:personal_snippet, :private)
+ let!(:public_snippet) { create(:personal_snippet, :public) }
+ let!(:internal_snippet) { create(:personal_snippet, :internal) }
+ let!(:private_snippet) { create(:personal_snippet, :private) }
+ scenario 'User should see snippets that are not private' do
login_as create(:user)
visit explore_snippets_path
@@ -13,4 +13,21 @@ feature 'Explore Snippets', feature: true do
expect(page).to have_content(internal_snippet.title)
expect(page).not_to have_content(private_snippet.title)
end
+
+ scenario 'External user should see only public snippets' do
+ login_as create(:user, :external)
+ visit explore_snippets_path
+
+ expect(page).to have_content(public_snippet.title)
+ expect(page).not_to have_content(internal_snippet.title)
+ expect(page).not_to have_content(private_snippet.title)
+ end
+
+ scenario 'Not authenticated user should see only public snippets' do
+ visit explore_snippets_path
+
+ expect(page).to have_content(public_snippet.title)
+ expect(page).not_to have_content(internal_snippet.title)
+ expect(page).not_to have_content(private_snippet.title)
+ end
end
diff --git a/spec/features/snippets/internal_snippet_spec.rb b/spec/features/snippets/internal_snippet_spec.rb
new file mode 100644
index 00000000000..93382f4c359
--- /dev/null
+++ b/spec/features/snippets/internal_snippet_spec.rb
@@ -0,0 +1,23 @@
+require 'rails_helper'
+
+feature 'Internal Snippets', feature: true, js: true do
+ let(:internal_snippet) { create(:personal_snippet, :internal) }
+
+ describe 'normal user' do
+ before do
+ login_as :user
+ end
+
+ scenario 'sees internal snippets' do
+ visit snippet_path(internal_snippet)
+
+ expect(page).to have_content(internal_snippet.content)
+ end
+
+ scenario 'sees raw internal snippets' do
+ visit raw_snippet_path(internal_snippet)
+
+ expect(page).to have_content(internal_snippet.content)
+ end
+ end
+end
diff --git a/spec/features/snippets/notes_on_personal_snippets_spec.rb b/spec/features/snippets/notes_on_personal_snippets_spec.rb
new file mode 100644
index 00000000000..698eb46573f
--- /dev/null
+++ b/spec/features/snippets/notes_on_personal_snippets_spec.rb
@@ -0,0 +1,101 @@
+require 'spec_helper'
+
+describe 'Comments on personal snippets', :js, feature: true do
+ let!(:user) { create(:user) }
+ let!(:snippet) { create(:personal_snippet, :public) }
+ let!(:snippet_notes) do
+ [
+ create(:note_on_personal_snippet, noteable: snippet, author: user),
+ create(:note_on_personal_snippet, noteable: snippet)
+ ]
+ end
+ let!(:other_note) { create(:note_on_personal_snippet) }
+
+ before do
+ login_as user
+ visit snippet_path(snippet)
+ end
+
+ subject { page }
+
+ context 'when viewing the snippet detail page' do
+ it 'contains notes for a snippet with correct action icons' do
+ expect(page).to have_selector('#notes-list li', count: 2)
+
+ # comment authored by current user
+ page.within("#notes-list li#note_#{snippet_notes[0].id}") do
+ expect(page).to have_content(snippet_notes[0].note)
+ expect(page).to have_selector('.js-note-delete')
+ expect(page).to have_selector('.note-emoji-button')
+ end
+
+ page.within("#notes-list li#note_#{snippet_notes[1].id}") do
+ expect(page).to have_content(snippet_notes[1].note)
+ expect(page).not_to have_selector('.js-note-delete')
+ expect(page).to have_selector('.note-emoji-button')
+ end
+ end
+ end
+
+ context 'when submitting a note' do
+ it 'shows a valid form' do
+ is_expected.to have_css('.js-main-target-form', visible: true, count: 1)
+ expect(find('.js-main-target-form .js-comment-button').value).
+ to eq('Comment')
+
+ page.within('.js-main-target-form') do
+ expect(page).not_to have_link('Cancel')
+ end
+ end
+
+ it 'previews a note' do
+ fill_in 'note[note]', with: 'This is **awesome**!'
+ find('.js-md-preview-button').click
+
+ page.within('.new-note .md-preview') do
+ expect(page).to have_content('This is awesome!')
+ expect(page).to have_selector('strong')
+ end
+ end
+
+ it 'creates a note' do
+ fill_in 'note[note]', with: 'This is **awesome**!'
+ click_button 'Comment'
+
+ expect(find('div#notes')).to have_content('This is awesome!')
+ end
+ end
+
+ context 'when editing a note' do
+ it 'changes the text' do
+ page.within("#notes-list li#note_#{snippet_notes[0].id}") do
+ click_on 'Edit comment'
+ end
+
+ page.within('.current-note-edit-form') do
+ fill_in 'note[note]', with: 'new content'
+ find('.btn-save').click
+ end
+
+ page.within("#notes-list li#note_#{snippet_notes[0].id}") do
+ edited_text = find('.edited-text')
+
+ expect(page).to have_css('.note_edited_ago')
+ expect(page).to have_content('new content')
+ expect(edited_text).to have_selector('.note_edited_ago')
+ end
+ end
+ end
+
+ context 'when deleting a note' do
+ it 'removes the note from the snippet detail page' do
+ page.within("#notes-list li#note_#{snippet_notes[0].id}") do
+ click_on 'Remove comment'
+ end
+
+ wait_for_ajax
+
+ expect(page).not_to have_selector("#notes-list li#note_#{snippet_notes[0].id}")
+ end
+ end
+end
diff --git a/spec/features/snippets/public_snippets_spec.rb b/spec/features/snippets/public_snippets_spec.rb
index 34300ccb940..2df483818c3 100644
--- a/spec/features/snippets/public_snippets_spec.rb
+++ b/spec/features/snippets/public_snippets_spec.rb
@@ -1,10 +1,11 @@
require 'rails_helper'
-feature 'Public Snippets', feature: true do
+feature 'Public Snippets', :js, feature: true do
scenario 'Unauthenticated user should see public snippets' do
public_snippet = create(:personal_snippet, :public)
visit snippet_path(public_snippet)
+ wait_for_ajax
expect(page).to have_content(public_snippet.content)
end
diff --git a/spec/features/snippets/show_spec.rb b/spec/features/snippets/show_spec.rb
new file mode 100644
index 00000000000..e36cf547f80
--- /dev/null
+++ b/spec/features/snippets/show_spec.rb
@@ -0,0 +1,138 @@
+require 'spec_helper'
+
+feature 'Snippet', :js, feature: true do
+ let(:project) { create(:project, :repository) }
+ let(:snippet) { create(:personal_snippet, :public, file_name: file_name, content: content) }
+
+ context 'Ruby file' do
+ let(:file_name) { 'popen.rb' }
+ let(:content) { project.repository.blob_at('master', 'files/ruby/popen.rb').data }
+
+ before do
+ visit snippet_path(snippet)
+
+ wait_for_ajax
+ end
+
+ it 'displays the blob' do
+ aggregate_failures do
+ # shows highlighted Ruby code
+ expect(page).to have_content("require 'fileutils'")
+
+ # does not show a viewer switcher
+ expect(page).not_to have_selector('.js-blob-viewer-switcher')
+
+ # shows an enabled copy button
+ expect(page).to have_selector('.js-copy-blob-source-btn:not(.disabled)')
+
+ # shows a raw button
+ expect(page).to have_link('Open raw')
+
+ # shows a download button
+ expect(page).to have_link('Download')
+ end
+ end
+ end
+
+ context 'Markdown file' do
+ let(:file_name) { 'ruby-style-guide.md' }
+ let(:content) { project.repository.blob_at('master', 'files/markdown/ruby-style-guide.md').data }
+
+ context 'visiting directly' do
+ before do
+ visit snippet_path(snippet)
+
+ wait_for_ajax
+ end
+
+ it 'displays the blob using the rich viewer' do
+ aggregate_failures do
+ # hides the simple viewer
+ expect(page).to have_selector('.blob-viewer[data-type="simple"]', visible: false)
+ expect(page).to have_selector('.blob-viewer[data-type="rich"]')
+
+ # shows rendered Markdown
+ expect(page).to have_link("PEP-8")
+
+ # shows a viewer switcher
+ expect(page).to have_selector('.js-blob-viewer-switcher')
+
+ # shows a disabled copy button
+ expect(page).to have_selector('.js-copy-blob-source-btn.disabled')
+
+ # shows a raw button
+ expect(page).to have_link('Open raw')
+
+ # shows a download button
+ expect(page).to have_link('Download')
+ end
+ end
+
+ context 'switching to the simple viewer' do
+ before do
+ find('.js-blob-viewer-switch-btn[data-viewer=simple]').click
+
+ wait_for_ajax
+ end
+
+ it 'displays the blob using the simple viewer' do
+ aggregate_failures do
+ # hides the rich viewer
+ expect(page).to have_selector('.blob-viewer[data-type="simple"]')
+ expect(page).to have_selector('.blob-viewer[data-type="rich"]', visible: false)
+
+ # shows highlighted Markdown code
+ expect(page).to have_content("[PEP-8](http://www.python.org/dev/peps/pep-0008/)")
+
+ # shows an enabled copy button
+ expect(page).to have_selector('.js-copy-blob-source-btn:not(.disabled)')
+ end
+ end
+
+ context 'switching to the rich viewer again' do
+ before do
+ find('.js-blob-viewer-switch-btn[data-viewer=rich]').click
+
+ wait_for_ajax
+ end
+
+ it 'displays the blob using the rich viewer' do
+ aggregate_failures do
+ # hides the simple viewer
+ expect(page).to have_selector('.blob-viewer[data-type="simple"]', visible: false)
+ expect(page).to have_selector('.blob-viewer[data-type="rich"]')
+
+ # shows an enabled copy button
+ expect(page).to have_selector('.js-copy-blob-source-btn:not(.disabled)')
+ end
+ end
+ end
+ end
+ end
+
+ context 'visiting with a line number anchor' do
+ before do
+ visit snippet_path(snippet, anchor: 'L1')
+
+ wait_for_ajax
+ end
+
+ it 'displays the blob using the simple viewer' do
+ aggregate_failures do
+ # hides the rich viewer
+ expect(page).to have_selector('.blob-viewer[data-type="simple"]')
+ expect(page).to have_selector('.blob-viewer[data-type="rich"]', visible: false)
+
+ # highlights the line in question
+ expect(page).to have_selector('#LC1.hll')
+
+ # shows highlighted Markdown code
+ expect(page).to have_content("[PEP-8](http://www.python.org/dev/peps/pep-0008/)")
+
+ # shows an enabled copy button
+ expect(page).to have_selector('.js-copy-blob-source-btn:not(.disabled)')
+ end
+ end
+ end
+ end
+end
diff --git a/spec/features/tags/master_creates_tag_spec.rb b/spec/features/tags/master_creates_tag_spec.rb
index ca25c696f75..af25eebed13 100644
--- a/spec/features/tags/master_creates_tag_spec.rb
+++ b/spec/features/tags/master_creates_tag_spec.rb
@@ -51,10 +51,24 @@ feature 'Master creates tag', feature: true do
end
end
+ scenario 'opens dropdown for ref', js: true do
+ click_link 'New tag'
+ ref_row = find('.form-group:nth-of-type(2) .col-sm-10')
+ page.within ref_row do
+ ref_input = find('[name="ref"]', visible: false)
+ expect(ref_input.value).to eq 'master'
+ expect(find('.dropdown-toggle-text')).to have_content 'master'
+
+ find('.js-branch-select').trigger('click')
+
+ expect(find('.dropdown-menu')).to have_content 'empty-branch'
+ end
+ end
+
def create_tag_in_form(tag:, ref:, message: nil, desc: nil)
click_link 'New tag'
fill_in 'tag_name', with: tag
- fill_in 'ref', with: ref
+ find('#ref', visible: false).set(ref)
fill_in 'message', with: message unless message.nil?
fill_in 'release_description', with: desc unless desc.nil?
click_button 'Create tag'
diff --git a/spec/features/tags/master_views_tags_spec.rb b/spec/features/tags/master_views_tags_spec.rb
index 555f84c4772..922ac15a2eb 100644
--- a/spec/features/tags/master_views_tags_spec.rb
+++ b/spec/features/tags/master_views_tags_spec.rb
@@ -16,7 +16,7 @@ feature 'Master views tags', feature: true do
fill_in :commit_message, with: 'Add a README file', visible: true
# Remove pre-receive hook so we can push without auth
FileUtils.rm_f(File.join(project.repository.path, 'hooks', 'pre-receive'))
- click_button 'Commit Changes'
+ click_button 'Commit changes'
visit namespace_project_tags_path(project.namespace, project)
end
diff --git a/spec/features/task_lists_spec.rb b/spec/features/task_lists_spec.rb
index a5d14aa19f1..8bd13caf2b0 100644
--- a/spec/features/task_lists_spec.rb
+++ b/spec/features/task_lists_spec.rb
@@ -3,7 +3,7 @@ require 'spec_helper'
feature 'Task Lists', feature: true do
include Warden::Test::Helpers
- let(:project) { create(:project) }
+ let(:project) { create(:empty_project) }
let(:user) { create(:user) }
let(:user2) { create(:user) }
@@ -62,12 +62,15 @@ feature 'Task Lists', feature: true do
visit namespace_project_issue_path(project.namespace, project, issue)
end
- describe 'for Issues' do
- describe 'multiple tasks' do
+ describe 'for Issues', feature: true do
+ describe 'multiple tasks', js: true do
+ include WaitForVueResource
+
let!(:issue) { create(:issue, description: markdown, author: user, project: project) }
it 'renders' do
visit_issue(project, issue)
+ wait_for_vue_resource
expect(page).to have_selector('ul.task-list', count: 1)
expect(page).to have_selector('li.task-list-item', count: 6)
@@ -76,25 +79,24 @@ feature 'Task Lists', feature: true do
it 'contains the required selectors' do
visit_issue(project, issue)
+ wait_for_vue_resource
- container = '.detail-page-description .description.js-task-list-container'
-
- expect(page).to have_selector(container)
- expect(page).to have_selector("#{container} .wiki .task-list .task-list-item .task-list-item-checkbox")
- expect(page).to have_selector("#{container} .js-task-list-field")
- expect(page).to have_selector('form.js-issuable-update')
+ expect(page).to have_selector(".wiki .task-list .task-list-item .task-list-item-checkbox")
expect(page).to have_selector('a.btn-close')
end
it 'is only editable by author' do
visit_issue(project, issue)
- expect(page).to have_selector('.js-task-list-container')
+ wait_for_vue_resource
- logout(:user)
+ expect(page).to have_selector(".wiki .task-list .task-list-item .task-list-item-checkbox")
+ logout(:user)
login_as(user2)
visit current_path
- expect(page).not_to have_selector('.js-task-list-container')
+ wait_for_vue_resource
+
+ expect(page).to have_selector(".wiki .task-list .task-list-item .task-list-item-checkbox")
end
it 'provides a summary on Issues#index' do
@@ -103,11 +105,14 @@ feature 'Task Lists', feature: true do
end
end
- describe 'single incomplete task' do
+ describe 'single incomplete task', js: true do
+ include WaitForVueResource
+
let!(:issue) { create(:issue, description: singleIncompleteMarkdown, author: user, project: project) }
it 'renders' do
visit_issue(project, issue)
+ wait_for_vue_resource
expect(page).to have_selector('ul.task-list', count: 1)
expect(page).to have_selector('li.task-list-item', count: 1)
@@ -116,15 +121,18 @@ feature 'Task Lists', feature: true do
it 'provides a summary on Issues#index' do
visit namespace_project_issues_path(project.namespace, project)
+
expect(page).to have_content("0 of 1 task completed")
end
end
- describe 'single complete task' do
+ describe 'single complete task', js: true do
+ include WaitForVueResource
let!(:issue) { create(:issue, description: singleCompleteMarkdown, author: user, project: project) }
it 'renders' do
visit_issue(project, issue)
+ wait_for_vue_resource
expect(page).to have_selector('ul.task-list', count: 1)
expect(page).to have_selector('li.task-list-item', count: 1)
@@ -133,6 +141,7 @@ feature 'Task Lists', feature: true do
it 'provides a summary on Issues#index' do
visit namespace_project_issues_path(project.namespace, project)
+
expect(page).to have_content("1 of 1 task completed")
end
end
@@ -240,6 +249,7 @@ feature 'Task Lists', feature: true do
end
describe 'multiple tasks' do
+ let(:project) { create(:project, :repository) }
let!(:merge) { create(:merge_request, :simple, description: markdown, author: user, source_project: project) }
it 'renders for description' do
diff --git a/spec/features/todos/todos_filtering_spec.rb b/spec/features/todos/todos_filtering_spec.rb
index e8f06916d53..f32e70c2c3f 100644
--- a/spec/features/todos/todos_filtering_spec.rb
+++ b/spec/features/todos/todos_filtering_spec.rb
@@ -1,8 +1,6 @@
require 'spec_helper'
describe 'Dashboard > User filters todos', feature: true, js: true do
- include WaitForAjax
-
let(:user_1) { create(:user, username: 'user_1', name: 'user_1') }
let(:user_2) { create(:user, username: 'user_2', name: 'user_2') }
@@ -47,8 +45,8 @@ describe 'Dashboard > User filters todos', feature: true, js: true do
wait_for_ajax
- expect(find('.todos-list')).to have_content user_1.name
- expect(find('.todos-list')).not_to have_content user_2.name
+ expect(find('.todos-list')).to have_content 'merge request'
+ expect(find('.todos-list')).not_to have_content 'issue'
end
it "shows only authors of existing todos" do
diff --git a/spec/features/todos/todos_spec.rb b/spec/features/todos/todos_spec.rb
index c270511c903..55b3e3d9424 100644
--- a/spec/features/todos/todos_spec.rb
+++ b/spec/features/todos/todos_spec.rb
@@ -1,8 +1,6 @@
require 'spec_helper'
describe 'Dashboard Todos', feature: true do
- include WaitForAjax
-
let(:user) { create(:user) }
let(:author) { create(:user) }
let(:project) { create(:project, visibility_level: Gitlab::VisibilityLevel::PUBLIC) }
@@ -101,6 +99,83 @@ describe 'Dashboard Todos', feature: true do
end
end
+ context 'User created todos for themself' do
+ before do
+ login_as(user)
+ end
+
+ context 'issue assigned todo' do
+ before do
+ create(:todo, :assigned, user: user, project: project, target: issue, author: user)
+ visit dashboard_todos_path
+ end
+
+ it 'shows issue assigned to yourself message' do
+ page.within('.js-todos-all') do
+ expect(page).to have_content("You assigned issue #{issue.to_reference(full: true)} to yourself")
+ end
+ end
+ end
+
+ context 'marked todo' do
+ before do
+ create(:todo, :marked, user: user, project: project, target: issue, author: user)
+ visit dashboard_todos_path
+ end
+
+ it 'shows you added a todo message' do
+ page.within('.js-todos-all') do
+ expect(page).to have_content("You added a todo for issue #{issue.to_reference(full: true)}")
+ expect(page).not_to have_content('to yourself')
+ end
+ end
+ end
+
+ context 'mentioned todo' do
+ before do
+ create(:todo, :mentioned, user: user, project: project, target: issue, author: user)
+ visit dashboard_todos_path
+ end
+
+ it 'shows you mentioned yourself message' do
+ page.within('.js-todos-all') do
+ expect(page).to have_content("You mentioned yourself on issue #{issue.to_reference(full: true)}")
+ expect(page).not_to have_content('to yourself')
+ end
+ end
+ end
+
+ context 'directly_addressed todo' do
+ before do
+ create(:todo, :directly_addressed, user: user, project: project, target: issue, author: user)
+ visit dashboard_todos_path
+ end
+
+ it 'shows you directly addressed yourself message' do
+ page.within('.js-todos-all') do
+ expect(page).to have_content("You directly addressed yourself on issue #{issue.to_reference(full: true)}")
+ expect(page).not_to have_content('to yourself')
+ end
+ end
+ end
+
+ context 'approval todo' do
+ let(:merge_request) { create(:merge_request) }
+
+ before do
+ create(:todo, :approval_required, user: user, project: project, target: merge_request, author: user)
+ visit dashboard_todos_path
+ end
+
+ it 'shows you set yourself as an approver message' do
+ page.within('.js-todos-all') do
+ expect(page).to have_content("You set yourself as an approver for merge request #{merge_request.to_reference(full: true)}")
+ expect(page).not_to have_content('to yourself')
+ end
+ end
+ end
+ end
+
context 'User has done todos', js: true do
before do
create(:todo, :mentioned, :done, user: user, project: project, target: issue, author: author)
@@ -176,7 +251,7 @@ describe 'Dashboard Todos', feature: true do
describe 'mark all as done', js: true do
before do
visit dashboard_todos_path
- click_link 'Mark all as done'
+ find('.js-todos-mark-all').trigger('click')
end
it 'shows "All done" message!' do
@@ -233,9 +308,9 @@ describe 'Dashboard Todos', feature: true do
end
def mark_all_and_undo
- click_link 'Mark all as done'
+ find('.js-todos-mark-all').trigger('click')
wait_for_ajax
- click_link 'Undo mark all as done'
+ find('.js-todos-undo-all').trigger('click')
wait_for_ajax
end
end
diff --git a/spec/features/u2f_spec.rb b/spec/features/u2f_spec.rb
index a8d00bb8e5a..544d2dcb87f 100644
--- a/spec/features/u2f_spec.rb
+++ b/spec/features/u2f_spec.rb
@@ -1,24 +1,21 @@
require 'spec_helper'
-feature 'Using U2F (Universal 2nd Factor) Devices for Authentication', feature: true, js: true do
- include WaitForAjax
-
+feature 'Using U2F (Universal 2nd Factor) Devices for Authentication', :js do
before { allow_any_instance_of(U2fHelper).to receive(:inject_u2f_api?).and_return(true) }
def manage_two_factor_authentication
- click_on 'Manage Two-Factor Authentication'
- expect(page).to have_content("Setup New U2F Device")
+ click_on 'Manage two-factor authentication'
+ expect(page).to have_content("Setup new U2F device")
wait_for_ajax
end
- def register_u2f_device(u2f_device = nil)
- name = FFaker::Name.first_name
+ def register_u2f_device(u2f_device = nil, name: 'My device')
u2f_device ||= FakeU2fDevice.new(page, name)
u2f_device.respond_to_u2f_registration
- click_on 'Setup New U2F Device'
+ click_on 'Setup new U2F device'
expect(page).to have_content('Your device was successfully set up')
fill_in "Pick a name", with: name
- click_on 'Register U2F Device'
+ click_on 'Register U2F device'
u2f_device
end
@@ -35,9 +32,9 @@ feature 'Using U2F (Universal 2nd Factor) Devices for Authentication', feature:
it 'does not allow registering a new device' do
visit profile_account_path
- click_on 'Enable Two-Factor Authentication'
+ click_on 'Enable two-factor authentication'
- expect(page).to have_button('Setup New U2F Device', disabled: true)
+ expect(page).to have_button('Setup new U2F device', disabled: true)
end
end
@@ -62,7 +59,7 @@ feature 'Using U2F (Universal 2nd Factor) Devices for Authentication', feature:
expect(page).to have_content('Your U2F device was registered')
# Second device
- second_device = register_u2f_device
+ second_device = register_u2f_device(name: 'My other device')
expect(page).to have_content('Your U2F device was registered')
expect(page).to have_content(first_device.name)
@@ -76,7 +73,7 @@ feature 'Using U2F (Universal 2nd Factor) Devices for Authentication', feature:
expect(page).to have_content("You've already enabled two-factor authentication using mobile")
first_u2f_device = register_u2f_device
- second_u2f_device = register_u2f_device
+ second_u2f_device = register_u2f_device(name: 'My other device')
click_on "Delete", match: :first
@@ -99,7 +96,7 @@ feature 'Using U2F (Universal 2nd Factor) Devices for Authentication', feature:
user.update_attribute(:otp_required_for_login, true)
visit profile_account_path
manage_two_factor_authentication
- register_u2f_device(u2f_device)
+ register_u2f_device(u2f_device, name: 'My other device')
expect(page).to have_content('Your U2F device was registered')
expect(U2fRegistration.count).to eq(2)
@@ -112,9 +109,9 @@ feature 'Using U2F (Universal 2nd Factor) Devices for Authentication', feature:
# Have the "u2f device" respond with bad data
page.execute_script("u2f.register = function(_,_,_,callback) { callback('bad response'); };")
- click_on 'Setup New U2F Device'
+ click_on 'Setup new U2F device'
expect(page).to have_content('Your device was successfully set up')
- click_on 'Register U2F Device'
+ click_on 'Register U2F device'
expect(U2fRegistration.count).to eq(0)
expect(page).to have_content("The form contains the following error")
@@ -127,9 +124,9 @@ feature 'Using U2F (Universal 2nd Factor) Devices for Authentication', feature:
# Failed registration
page.execute_script("u2f.register = function(_,_,_,callback) { callback('bad response'); };")
- click_on 'Setup New U2F Device'
+ click_on 'Setup new U2F device'
expect(page).to have_content('Your device was successfully set up')
- click_on 'Register U2F Device'
+ click_on 'Register U2F device'
expect(page).to have_content("The form contains the following error")
# Successful registration
@@ -198,7 +195,7 @@ feature 'Using U2F (Universal 2nd Factor) Devices for Authentication', feature:
current_user.update_attribute(:otp_required_for_login, true)
visit profile_account_path
manage_two_factor_authentication
- register_u2f_device
+ register_u2f_device(name: 'My other device')
logout
# Try authenticating user with the old U2F device
@@ -231,7 +228,7 @@ feature 'Using U2F (Universal 2nd Factor) Devices for Authentication', feature:
describe "when a given U2F device has not been registered" do
it "does not allow logging in with that particular device" do
- unregistered_device = FakeU2fDevice.new(page, FFaker::Name.first_name)
+ unregistered_device = FakeU2fDevice.new(page, 'My device')
login_as(user)
unregistered_device.respond_to_u2f_authentication
expect(page).to have_content('We heard back from your U2F device')
@@ -252,7 +249,7 @@ feature 'Using U2F (Universal 2nd Factor) Devices for Authentication', feature:
# Register second device
visit profile_two_factor_auth_path
expect(page).to have_content("Your U2F device needs to be set up.")
- second_device = register_u2f_device
+ second_device = register_u2f_device(name: 'My other device')
logout
# Authenticate as both devices
diff --git a/spec/features/unsubscribe_links_spec.rb b/spec/features/unsubscribe_links_spec.rb
index e2d9cfdd0b0..a23c4ca2b92 100644
--- a/spec/features/unsubscribe_links_spec.rb
+++ b/spec/features/unsubscribe_links_spec.rb
@@ -6,7 +6,7 @@ describe 'Unsubscribe links', feature: true do
let(:recipient) { create(:user) }
let(:author) { create(:user) }
let(:project) { create(:empty_project, :public) }
- let(:params) { { title: 'A bug!', description: 'Fix it!', assignee: recipient } }
+ let(:params) { { title: 'A bug!', description: 'Fix it!', assignees: [recipient] } }
let(:issue) { Issues::CreateService.new(project, author, params).execute }
let(:mail) { ActionMailer::Base.deliveries.last }
diff --git a/spec/features/uploads/user_uploads_file_to_note_spec.rb b/spec/features/uploads/user_uploads_file_to_note_spec.rb
index 0c160dd74b4..8f03024ea06 100644
--- a/spec/features/uploads/user_uploads_file_to_note_spec.rb
+++ b/spec/features/uploads/user_uploads_file_to_note_spec.rb
@@ -5,18 +5,78 @@ feature 'User uploads file to note', feature: true do
let(:user) { create(:user) }
let(:project) { create(:empty_project, creator: user, namespace: user.namespace) }
+ let(:issue) { create(:issue, project: project, author: user) }
- scenario 'they see the attached file', js: true do
- issue = create(:issue, project: project, author: user)
-
+ before do
login_as(user)
visit namespace_project_issue_path(project.namespace, project, issue)
+ end
+
+ context 'before uploading' do
+ it 'shows "Attach a file" button', js: true do
+ expect(page).to have_button('Attach a file')
+ expect(page).not_to have_selector('.uploading-progress-container', visible: true)
+ end
+ end
+
+ context 'uploading is in progress' do
+ it 'shows "Cancel" button on uploading', js: true do
+ dropzone_file([Rails.root.join('spec', 'fixtures', 'dk.png')], 0, false)
+
+ expect(page).to have_button('Cancel')
+ end
+
+ it 'cancels uploading on clicking to "Cancel" button', js: true do
+ dropzone_file([Rails.root.join('spec', 'fixtures', 'dk.png')], 0, false)
+
+ click_button 'Cancel'
+
+ expect(page).to have_button('Attach a file')
+ expect(page).not_to have_button('Cancel')
+ expect(page).not_to have_selector('.uploading-progress-container', visible: true)
+ end
+
+ it 'shows "Attaching a file" message on uploading 1 file', js: true do
+ dropzone_file([Rails.root.join('spec', 'fixtures', 'dk.png')], 0, false)
+
+ expect(page).to have_selector('.attaching-file-message', visible: true, text: 'Attaching a file -')
+ end
+
+ it 'shows "Attaching 2 files" message on uploading 2 file', js: true do
+ dropzone_file([Rails.root.join('spec', 'fixtures', 'video_sample.mp4'),
+ Rails.root.join('spec', 'fixtures', 'dk.png')], 0, false)
+
+ expect(page).to have_selector('.attaching-file-message', visible: true, text: 'Attaching 2 files -')
+ end
+
+ it 'shows error message, "retry" and "attach a new file" link a if file is too big', js: true do
+ dropzone_file([Rails.root.join('spec', 'fixtures', 'video_sample.mp4')], 0.01)
+
+ error_text = 'File is too big (0.06MiB). Max filesize: 0.01MiB.'
+
+ expect(page).to have_selector('.uploading-error-message', visible: true, text: error_text)
+ expect(page).to have_selector('.retry-uploading-link', visible: true, text: 'Try again')
+ expect(page).to have_selector('.attach-new-file', visible: true, text: 'attach a new file')
+ expect(page).not_to have_button('Attach a file')
+ end
+ end
+
+ context 'uploading is complete' do
+ it 'shows "Attach a file" button on uploading complete', js: true do
+ dropzone_file([Rails.root.join('spec', 'fixtures', 'dk.png')])
+ wait_for_ajax
+
+ expect(page).to have_button('Attach a file')
+ expect(page).not_to have_selector('.uploading-progress-container', visible: true)
+ end
- dropzone_file(Rails.root.join('spec', 'fixtures', 'dk.png'))
- click_button 'Comment'
- wait_for_ajax
+ scenario 'they see the attached file', js: true do
+ dropzone_file([Rails.root.join('spec', 'fixtures', 'dk.png')])
+ click_button 'Comment'
+ wait_for_ajax
- expect(find('a.no-attachment-icon img[alt="dk"]')['src'])
- .to match(%r{/#{project.full_path}/uploads/\h{32}/dk\.png$})
+ expect(find('a.no-attachment-icon img[alt="dk"]')['src'])
+ .to match(%r{/#{project.full_path}/uploads/\h{32}/dk\.png$})
+ end
end
end
diff --git a/spec/features/user_callout_spec.rb b/spec/features/user_callout_spec.rb
index 848af5e3a4d..b84f834ff1e 100644
--- a/spec/features/user_callout_spec.rb
+++ b/spec/features/user_callout_spec.rb
@@ -20,7 +20,7 @@ describe 'User Callouts', js: true do
visit dashboard_projects_path
within('.user-callout') do
- find('.close').click
+ find('.close').trigger('click')
end
visit dashboard_projects_path
diff --git a/spec/features/users/projects_spec.rb b/spec/features/users/projects_spec.rb
index 1d75fe434b0..373b64808f8 100644
--- a/spec/features/users/projects_spec.rb
+++ b/spec/features/users/projects_spec.rb
@@ -1,8 +1,6 @@
require 'spec_helper'
describe 'Projects tab on a user profile', :feature, :js do
- include WaitForAjax
-
let(:user) { create(:user) }
let!(:project) { create(:empty_project, namespace: user.namespace) }
let!(:project2) { create(:empty_project, namespace: user.namespace) }
diff --git a/spec/features/users/snippets_spec.rb b/spec/features/users/snippets_spec.rb
index ce7e809ec76..4efbd672322 100644
--- a/spec/features/users/snippets_spec.rb
+++ b/spec/features/users/snippets_spec.rb
@@ -1,18 +1,48 @@
require 'spec_helper'
describe 'Snippets tab on a user profile', feature: true, js: true do
- include WaitForAjax
-
context 'when the user has snippets' do
let(:user) { create(:user) }
- let!(:snippets) { create_list(:snippet, 2, :public, author: user) }
- before do
- allow(Snippet).to receive(:default_per_page).and_return(1)
- visit user_path(user)
- page.within('.user-profile-nav') { click_link 'Snippets' }
- wait_for_ajax
+
+ context 'pagination' do
+ let!(:snippets) { create_list(:snippet, 2, :public, author: user) }
+
+ before do
+ allow(Snippet).to receive(:default_per_page).and_return(1)
+ visit user_path(user)
+ page.within('.user-profile-nav') { click_link 'Snippets' }
+ wait_for_ajax
+ end
+
+ it_behaves_like 'paginated snippets', remote: true
end
- it_behaves_like 'paginated snippets', remote: true
+ context 'list content' do
+ let!(:public_snippet) { create(:snippet, :public, author: user) }
+ let!(:internal_snippet) { create(:snippet, :internal, author: user) }
+ let!(:private_snippet) { create(:snippet, :private, author: user) }
+ let!(:other_snippet) { create(:snippet, :public) }
+
+ it 'contains only internal and public snippets of a user when a user is logged in' do
+ login_as(:user)
+ visit user_path(user)
+ page.within('.user-profile-nav') { click_link 'Snippets' }
+ wait_for_ajax
+
+ expect(page).to have_selector('.snippet-row', count: 2)
+
+ expect(page).to have_content(public_snippet.title)
+ expect(page).to have_content(internal_snippet.title)
+ end
+
+ it 'contains only public snippets of a user when a user is not logged in' do
+ visit user_path(user)
+ page.within('.user-profile-nav') { click_link 'Snippets' }
+ wait_for_ajax
+
+ expect(page).to have_selector('.snippet-row', count: 1)
+ expect(page).to have_content(public_snippet.title)
+ end
+ end
end
end
diff --git a/spec/features/users_spec.rb b/spec/features/users_spec.rb
index 2de0fbe7ab2..c43feadc808 100644
--- a/spec/features/users_spec.rb
+++ b/spec/features/users_spec.rb
@@ -68,7 +68,6 @@ feature 'Users', feature: true, js: true do
end
feature 'username validation' do
- include WaitForAjax
let(:loading_icon) { '.fa.fa-spinner' }
let(:username_input) { 'new_user_username' }
diff --git a/spec/features/variables_spec.rb b/spec/features/variables_spec.rb
index a362d6fd3b6..b83a230c1f8 100644
--- a/spec/features/variables_spec.rb
+++ b/spec/features/variables_spec.rb
@@ -2,7 +2,7 @@ require 'spec_helper'
describe 'Project variables', js: true do
let(:user) { create(:user) }
- let(:project) { create(:project) }
+ let(:project) { create(:empty_project) }
let(:variable) { create(:ci_variable, key: 'test_key', value: 'test value') }
before do