summaryrefslogtreecommitdiff
path: root/spec/features/projects
diff options
context:
space:
mode:
Diffstat (limited to 'spec/features/projects')
-rw-r--r--spec/features/projects/branches_spec.rb10
-rw-r--r--spec/features/projects/commit/cherry_pick_spec.rb160
-rw-r--r--spec/features/projects/commit/user_reverts_commit_spec.rb91
-rw-r--r--spec/features/projects/commits/user_browses_commits_spec.rb9
-rw-r--r--spec/features/projects/compare_spec.rb23
-rw-r--r--spec/features/projects/features_visibility_spec.rb6
-rw-r--r--spec/features/projects/files/dockerfile_dropdown_spec.rb8
-rw-r--r--spec/features/projects/files/gitignore_dropdown_spec.rb10
-rw-r--r--spec/features/projects/files/gitlab_ci_syntax_yml_dropdown_spec.rb7
-rw-r--r--spec/features/projects/files/gitlab_ci_yml_dropdown_spec.rb10
-rw-r--r--spec/features/projects/fork_spec.rb39
-rw-r--r--spec/features/projects/graph_spec.rb6
-rw-r--r--spec/features/projects/issuable_templates_spec.rb4
-rw-r--r--spec/features/projects/jobs_spec.rb52
-rw-r--r--spec/features/projects/members/anonymous_user_sees_members_spec.rb26
-rw-r--r--spec/features/projects/members/group_members_spec.rb243
-rw-r--r--spec/features/projects/members/groups_with_access_list_spec.rb180
-rw-r--r--spec/features/projects/members/invite_group_spec.rb86
-rw-r--r--spec/features/projects/members/list_spec.rb222
-rw-r--r--spec/features/projects/members/master_adds_member_with_expiration_date_spec.rb111
-rw-r--r--spec/features/projects/members/sorting_spec.rb204
-rw-r--r--spec/features/projects/members/tabs_spec.rb99
-rw-r--r--spec/features/projects/navbar_spec.rb19
-rw-r--r--spec/features/projects/pages/user_adds_domain_spec.rb185
-rw-r--r--spec/features/projects/pages/user_edits_lets_encrypt_settings_spec.rb (renamed from spec/features/projects/pages_lets_encrypt_spec.rb)2
-rw-r--r--spec/features/projects/pages/user_edits_settings_spec.rb201
-rw-r--r--spec/features/projects/pages_spec.rb411
-rw-r--r--spec/features/projects/pipelines/pipeline_spec.rb221
-rw-r--r--spec/features/projects/pipelines/pipelines_spec.rb113
-rw-r--r--spec/features/projects/services/user_activates_slack_slash_command_spec.rb4
-rw-r--r--spec/features/projects/settings/pipelines_settings_spec.rb2
-rw-r--r--spec/features/projects/settings/project_settings_spec.rb4
-rw-r--r--spec/features/projects/settings/registry_settings_spec.rb4
-rw-r--r--spec/features/projects/settings/repository_settings_spec.rb6
-rw-r--r--spec/features/projects/settings/user_manages_merge_requests_settings_spec.rb6
-rw-r--r--spec/features/projects/settings/user_manages_project_members_spec.rb134
-rw-r--r--spec/features/projects/settings/visibility_settings_spec.rb4
-rw-r--r--spec/features/projects/show/user_manages_notifications_spec.rb1
-rw-r--r--spec/features/projects/show/user_sees_git_instructions_spec.rb7
-rw-r--r--spec/features/projects/terraform_spec.rb2
-rw-r--r--spec/features/projects/user_creates_project_spec.rb6
-rw-r--r--spec/features/projects/user_sees_user_popover_spec.rb2
-rw-r--r--spec/features/projects/user_uses_shortcuts_spec.rb7
43 files changed, 1885 insertions, 1062 deletions
diff --git a/spec/features/projects/branches_spec.rb b/spec/features/projects/branches_spec.rb
index dcad7ee66a3..4bfe8852291 100644
--- a/spec/features/projects/branches_spec.rb
+++ b/spec/features/projects/branches_spec.rb
@@ -21,11 +21,11 @@ RSpec.describe 'Branches' do
before do
# Add 4 stale branches
(1..4).reverse_each do |i|
- travel_to((threshold + i).ago) { create_file(message: "a commit in stale-#{i}", branch_name: "stale-#{i}") }
+ travel_to((threshold + i.hours).ago) { create_file(message: "a commit in stale-#{i}", branch_name: "stale-#{i}") }
end
# Add 6 active branches
(1..6).each do |i|
- travel_to((threshold - i).ago) { create_file(message: "a commit in active-#{i}", branch_name: "active-#{i}") }
+ travel_to((threshold - i.hours).ago) { create_file(message: "a commit in active-#{i}", branch_name: "active-#{i}") }
end
end
@@ -34,7 +34,7 @@ RSpec.describe 'Branches' do
visit project_branches_path(project)
expect(page).to have_content(sorted_branches(repository, count: 5, sort_by: :updated_desc, state: 'active'))
- expect(page).to have_content(sorted_branches(repository, count: 4, sort_by: :updated_desc, state: 'stale'))
+ expect(page).to have_content(sorted_branches(repository, count: 4, sort_by: :updated_asc, state: 'stale'))
expect(page).to have_link('Show more active branches', href: project_branches_filtered_path(project, state: 'active'))
expect(page).not_to have_content('Show more stale branches')
@@ -50,10 +50,10 @@ RSpec.describe 'Branches' do
end
describe 'Stale branches page' do
- it 'shows 4 active branches sorted by last updated' do
+ it 'shows 4 stale branches sorted by last updated' do
visit project_branches_filtered_path(project, state: 'stale')
- expect(page).to have_content(sorted_branches(repository, count: 4, sort_by: :updated_desc, state: 'stale'))
+ expect(page).to have_content(sorted_branches(repository, count: 4, sort_by: :updated_asc, state: 'stale'))
end
end
diff --git a/spec/features/projects/commit/cherry_pick_spec.rb b/spec/features/projects/commit/cherry_pick_spec.rb
index 9fe3f4cd63e..489a90cc8fc 100644
--- a/spec/features/projects/commit/cherry_pick_spec.rb
+++ b/spec/features/projects/commit/cherry_pick_spec.rb
@@ -2,108 +2,126 @@
require 'spec_helper'
-RSpec.describe 'Cherry-pick Commits' do
- let(:user) { create(:user) }
- let(:group) { create(:group) }
- let(:project) { create(:project, :repository, namespace: group) }
- let(:master_pickable_commit) { project.commit('7d3b0f7cff5f37573aea97cebfd5692ea1689924') }
- let(:master_pickable_merge) { project.commit('e56497bb5f03a90a51293fc6d516788730953899') }
+RSpec.describe 'Cherry-pick Commits', :js do
+ let_it_be(:user) { create(:user) }
+ let_it_be(:sha) { '7d3b0f7cff5f37573aea97cebfd5692ea1689924' }
+ let!(:project) { create_default(:project, :repository, namespace: user.namespace) }
+ let(:master_pickable_commit) { project.commit(sha) }
before do
sign_in(user)
- project.add_maintainer(user)
- visit project_commit_path(project, master_pickable_commit.id)
end
- context "I cherry-pick a commit" do
- it do
- find("a[href='#modal-cherry-pick-commit']").click
- expect(page).not_to have_content('v1.0.0') # Only branches, not tags
- page.within('#modal-cherry-pick-commit') do
- uncheck 'create_merge_request'
- click_button 'Cherry-pick'
- end
- expect(page).to have_content('The commit has been successfully cherry-picked into master.')
- end
- end
+ context 'when clicking cherry-pick from the dropdown for a commit on pipelines tab' do
+ it 'launches the modal form' do
+ create(:ci_empty_pipeline, sha: sha)
+ visit project_commit_path(project, master_pickable_commit.id)
+ click_link 'Pipelines'
- context "I cherry-pick a merge commit" do
- it do
- find("a[href='#modal-cherry-pick-commit']").click
- page.within('#modal-cherry-pick-commit') do
- uncheck 'create_merge_request'
- click_button 'Cherry-pick'
+ open_modal
+
+ page.within(modal_selector) do
+ expect(page).to have_content('Cherry-pick this commit')
end
- expect(page).to have_content('The commit has been successfully cherry-picked into master.')
end
end
- context "I cherry-pick a commit that was previously cherry-picked" do
- it do
- find("a[href='#modal-cherry-pick-commit']").click
- page.within('#modal-cherry-pick-commit') do
- uncheck 'create_merge_request'
- click_button 'Cherry-pick'
- end
+ context 'when starting from the commit tab' do
+ before do
visit project_commit_path(project, master_pickable_commit.id)
- find("a[href='#modal-cherry-pick-commit']").click
- page.within('#modal-cherry-pick-commit') do
- uncheck 'create_merge_request'
- click_button 'Cherry-pick'
- end
- expect(page).to have_content('Sorry, we cannot cherry-pick this commit automatically.')
end
- end
- context "I cherry-pick a commit in a new merge request", :js do
- it do
- find('.header-action-buttons a.dropdown-toggle').click
- find("a[href='#modal-cherry-pick-commit']").click
- page.within('#modal-cherry-pick-commit') do
- click_button 'Cherry-pick'
+ context 'when cherry-picking a commit' do
+ specify do
+ cherry_pick_commit
+
+ expect(page).to have_content('The commit has been successfully cherry-picked into master.')
end
+ end
- wait_for_requests
+ context 'when cherry-picking a merge commit' do
+ specify do
+ cherry_pick_commit
- expect(page).to have_content("The commit has been successfully cherry-picked into cherry-pick-#{master_pickable_commit.short_id}. You can now submit a merge request to get this change into the original branch.")
- expect(page).to have_content("From cherry-pick-#{master_pickable_commit.short_id} into master")
+ expect(page).to have_content('The commit has been successfully cherry-picked into master.')
+ end
end
- end
- context "I cherry-pick a commit from a different branch", :js do
- it do
- find('.header-action-buttons a.dropdown-toggle').click
- find(:css, "a[href='#modal-cherry-pick-commit']").click
+ context 'when cherry-picking a commit that was previously cherry-picked' do
+ specify do
+ cherry_pick_commit
- page.within('#modal-cherry-pick-commit') do
- click_button 'master'
+ visit project_commit_path(project, master_pickable_commit.id)
+
+ cherry_pick_commit
+
+ expect(page).to have_content('Sorry, we cannot cherry-pick this commit automatically.')
end
+ end
- wait_for_requests
+ context 'when cherry-picking a commit in a new merge request' do
+ specify do
+ cherry_pick_commit(create_merge_request: true)
- page.within('#modal-cherry-pick-commit .dropdown-menu') do
- find('.dropdown-input input').set('feature')
- wait_for_requests
- click_link "feature"
+ expect(page).to have_content("The commit has been successfully cherry-picked into cherry-pick-#{master_pickable_commit.short_id}. You can now submit a merge request to get this change into the original branch.")
+ expect(page).to have_content("From cherry-pick-#{master_pickable_commit.short_id} into master")
end
+ end
- page.within('#modal-cherry-pick-commit') do
- uncheck 'create_merge_request'
- click_button 'Cherry-pick'
+ context 'when I cherry-picking a commit from a different branch' do
+ specify do
+ open_modal
+
+ page.within(modal_selector) do
+ click_button 'master'
+ end
+
+ page.within("#{modal_selector} .dropdown-menu") do
+ find('[data-testid="dropdown-search-box"]').set('feature')
+ wait_for_requests
+ click_button 'feature'
+ end
+
+ submit_cherry_pick
+
+ expect(page).to have_content('The commit has been successfully cherry-picked into feature.')
end
+ end
+
+ context 'when the project is archived' do
+ let(:project) { create(:project, :repository, :archived, namespace: user.namespace) }
- expect(page).to have_content('The commit has been successfully cherry-picked into feature.')
+ it 'does not show the cherry-pick link' do
+ open_dropdown
+
+ expect(page).not_to have_text("Cherry-pick")
+ end
end
end
- context 'when the project is archived' do
- let(:project) { create(:project, :repository, :archived, namespace: group) }
+ def cherry_pick_commit(create_merge_request: false)
+ open_modal
- it 'does not show the cherry-pick link' do
- find('.header-action-buttons a.dropdown-toggle').click
+ submit_cherry_pick(create_merge_request: create_merge_request)
+ end
+
+ def open_dropdown
+ find('.header-action-buttons .dropdown').click
+ end
- expect(page).not_to have_text("Cherry-pick")
- expect(page).not_to have_css("a[href='#modal-cherry-pick-commit']")
+ def open_modal
+ open_dropdown
+ find('[data-testid="cherry-pick-commit-link"]').click
+ end
+
+ def submit_cherry_pick(create_merge_request: false)
+ page.within(modal_selector) do
+ uncheck('create_merge_request') unless create_merge_request
+ click_button('Cherry-pick')
end
end
+
+ def modal_selector
+ '[data-testid="modal-commit"]'
+ end
end
diff --git a/spec/features/projects/commit/user_reverts_commit_spec.rb b/spec/features/projects/commit/user_reverts_commit_spec.rb
index f3c364dab97..72c639a027e 100644
--- a/spec/features/projects/commit/user_reverts_commit_spec.rb
+++ b/spec/features/projects/commit/user_reverts_commit_spec.rb
@@ -6,58 +6,89 @@ RSpec.describe 'User reverts a commit', :js do
include RepoHelpers
let_it_be(:user) { create(:user) }
- let(:project) { create(:project, :repository, namespace: user.namespace) }
+ let!(:project) { create_default(:project, :repository, namespace: user.namespace) }
before do
sign_in(user)
-
- visit(project_commit_path(project, sample_commit.id))
end
- def revert_commit(create_merge_request: false)
- find('.header-action-buttons .dropdown').click
- find('[data-testid="revert-commit-link"]').click
+ context 'when clicking revert from the dropdown for a commit on pipelines tab' do
+ it 'launches the modal and is able to submit the revert' do
+ sha = '7d3b0f7cff5f37573aea97cebfd5692ea1689924'
+ create(:ci_empty_pipeline, sha: sha)
+ visit project_commit_path(project, project.commit(sha).id)
+ click_link 'Pipelines'
- page.within('[data-testid="modal-commit"]') do
- uncheck('create_merge_request') unless create_merge_request
- click_button('Revert')
+ open_modal
+
+ page.within(modal_selector) do
+ expect(page).to have_content('Revert this commit')
+ end
end
end
- context 'without creating a new merge request' do
- it 'reverts a commit' do
- revert_commit
+ context 'when starting from the commit tab' do
+ before do
+ visit project_commit_path(project, sample_commit.id)
+ end
+
+ context 'without creating a new merge request' do
+ it 'reverts a commit' do
+ revert_commit
+
+ expect(page).to have_content('The commit has been successfully reverted.')
+ end
+
+ it 'does not revert a previously reverted commit' do
+ revert_commit
+ # Visit the comment again once it was reverted.
+ visit project_commit_path(project, sample_commit.id)
+
+ revert_commit
- expect(page).to have_content('The commit has been successfully reverted.')
+ expect(page).to have_content('Sorry, we cannot revert this commit automatically.')
+ end
end
- it 'does not revert a previously reverted commit' do
- revert_commit
- # Visit the comment again once it was reverted.
- visit project_commit_path(project, sample_commit.id)
+ context 'with creating a new merge request' do
+ it 'reverts a commit' do
+ revert_commit(create_merge_request: true)
+
+ expect(page).to have_content('The commit has been successfully reverted. You can now submit a merge request to get this change into the original branch.')
+ expect(page).to have_content("From revert-#{Commit.truncate_sha(sample_commit.id)} into master")
+ end
+ end
- revert_commit
+ context 'when the project is archived' do
+ let(:project) { create(:project, :repository, :archived, namespace: user.namespace) }
- expect(page).to have_content('Sorry, we cannot revert this commit automatically.')
+ it 'does not show the revert link' do
+ open_dropdown
+
+ expect(page).not_to have_link('Revert')
+ end
end
end
- context 'with creating a new merge request' do
- it 'reverts a commit' do
- revert_commit(create_merge_request: true)
+ def revert_commit(create_merge_request: false)
+ open_modal
- expect(page).to have_content('The commit has been successfully reverted. You can now submit a merge request to get this change into the original branch.')
- expect(page).to have_content("From revert-#{Commit.truncate_sha(sample_commit.id)} into master")
+ page.within(modal_selector) do
+ uncheck('create_merge_request') unless create_merge_request
+ click_button('Revert')
end
end
- context 'when the project is archived' do
- let(:project) { create(:project, :repository, :archived, namespace: user.namespace) }
+ def open_dropdown
+ find('.header-action-buttons .dropdown').click
+ end
- it 'does not show the revert link' do
- find('.header-action-buttons .dropdown').click
+ def open_modal
+ open_dropdown
+ find('[data-testid="revert-commit-link"]').click
+ end
- expect(page).not_to have_link('Revert')
- end
+ def modal_selector
+ '[data-testid="modal-commit"]'
end
end
diff --git a/spec/features/projects/commits/user_browses_commits_spec.rb b/spec/features/projects/commits/user_browses_commits_spec.rb
index 596b4773716..4894e2b7f3e 100644
--- a/spec/features/projects/commits/user_browses_commits_spec.rb
+++ b/spec/features/projects/commits/user_browses_commits_spec.rb
@@ -203,10 +203,11 @@ RSpec.describe 'User browses commits' do
context 'when click the compare tab' do
before do
+ wait_for_requests
click_link('Compare')
end
- it 'does not render create merge request button' do
+ it 'does not render create merge request button', :js do
expect(page).not_to have_link 'Create merge request'
end
end
@@ -236,10 +237,11 @@ RSpec.describe 'User browses commits' do
context 'when click the compare tab' do
before do
+ wait_for_requests
click_link('Compare')
end
- it 'renders create merge request button' do
+ it 'renders create merge request button', :js do
expect(page).to have_link 'Create merge request'
end
end
@@ -276,10 +278,11 @@ RSpec.describe 'User browses commits' do
context 'when click the compare tab' do
before do
+ wait_for_requests
click_link('Compare')
end
- it 'renders button to the merge request' do
+ it 'renders button to the merge request', :js do
expect(page).not_to have_link 'Create merge request'
expect(page).to have_link 'View open merge request', href: project_merge_request_path(project, merge_request)
end
diff --git a/spec/features/projects/compare_spec.rb b/spec/features/projects/compare_spec.rb
index e387ea4d473..64e9968061c 100644
--- a/spec/features/projects/compare_spec.rb
+++ b/spec/features/projects/compare_spec.rb
@@ -17,10 +17,10 @@ RSpec.describe "Compare", :js do
visit project_compare_index_path(project, from: 'master', to: 'master')
select_using_dropdown 'from', 'feature'
- expect(find('.js-compare-from-dropdown .dropdown-toggle-text')).to have_content('feature')
+ expect(find('.js-compare-from-dropdown .gl-new-dropdown-button-text')).to have_content('feature')
select_using_dropdown 'to', 'binary-encoding'
- expect(find('.js-compare-to-dropdown .dropdown-toggle-text')).to have_content('binary-encoding')
+ expect(find('.js-compare-to-dropdown .gl-new-dropdown-button-text')).to have_content('binary-encoding')
click_button 'Compare'
@@ -32,8 +32,8 @@ RSpec.describe "Compare", :js do
it "pre-populates fields" do
visit project_compare_index_path(project, from: "master", to: "master")
- expect(find(".js-compare-from-dropdown .dropdown-toggle-text")).to have_content("master")
- expect(find(".js-compare-to-dropdown .dropdown-toggle-text")).to have_content("master")
+ expect(find(".js-compare-from-dropdown .gl-new-dropdown-button-text")).to have_content("master")
+ expect(find(".js-compare-to-dropdown .gl-new-dropdown-button-text")).to have_content("master")
end
it_behaves_like 'compares branches'
@@ -99,7 +99,7 @@ RSpec.describe "Compare", :js do
find(".js-compare-from-dropdown .compare-dropdown-toggle").click
- expect(find(".js-compare-from-dropdown .dropdown-content")).to have_selector("li", count: 3)
+ expect(find(".js-compare-from-dropdown .gl-new-dropdown-contents")).to have_selector('li.gl-new-dropdown-item', count: 1)
end
context 'when commit has overflow', :js do
@@ -125,10 +125,10 @@ RSpec.describe "Compare", :js do
visit project_compare_index_path(project, from: "master", to: "master")
select_using_dropdown "from", "v1.0.0"
- expect(find(".js-compare-from-dropdown .dropdown-toggle-text")).to have_content("v1.0.0")
+ expect(find(".js-compare-from-dropdown .gl-new-dropdown-button-text")).to have_content("v1.0.0")
select_using_dropdown "to", "v1.1.0"
- expect(find(".js-compare-to-dropdown .dropdown-toggle-text")).to have_content("v1.1.0")
+ expect(find(".js-compare-to-dropdown .gl-new-dropdown-button-text")).to have_content("v1.1.0")
click_button "Compare"
expect(page).to have_content "Commits"
@@ -136,19 +136,22 @@ RSpec.describe "Compare", :js do
end
def select_using_dropdown(dropdown_type, selection, commit: false)
+ wait_for_requests
+
dropdown = find(".js-compare-#{dropdown_type}-dropdown")
dropdown.find(".compare-dropdown-toggle").click
# find input before using to wait for the inputs visibility
dropdown.find('.dropdown-menu')
dropdown.fill_in("Filter by Git revision", with: selection)
+
wait_for_requests
if commit
- dropdown.find('input[type="search"]').send_keys(:return)
+ dropdown.find('.gl-search-box-by-type-input').send_keys(:return)
else
# find before all to wait for the items visibility
- dropdown.find("a[data-ref=\"#{selection}\"]", match: :first)
- dropdown.all("a[data-ref=\"#{selection}\"]").last.click
+ dropdown.find(".js-compare-#{dropdown_type}-dropdown .dropdown-item", text: selection, match: :first)
+ dropdown.all(".js-compare-#{dropdown_type}-dropdown .dropdown-item", text: selection).first.click
end
end
end
diff --git a/spec/features/projects/features_visibility_spec.rb b/spec/features/projects/features_visibility_spec.rb
index 2f0fbd29cb5..c94247f65d2 100644
--- a/spec/features/projects/features_visibility_spec.rb
+++ b/spec/features/projects/features_visibility_spec.rb
@@ -187,7 +187,7 @@ RSpec.describe 'Edit Project Settings' do
click_button "Save changes"
end
- expect(find(".sharing-permissions")).to have_selector(".project-feature-toggle.is-disabled", count: 4)
+ expect(find(".sharing-permissions")).to have_selector(".gl-toggle.is-disabled", minimum: 4)
end
it "shows empty features project homepage" do
@@ -282,10 +282,10 @@ RSpec.describe 'Edit Project Settings' do
end
def toggle_feature_off(feature_name)
- find(".project-feature-controls[data-for=\"#{feature_name}\"] .project-feature-toggle.is-checked").click
+ find(".project-feature-controls[data-for=\"#{feature_name}\"] .gl-toggle.is-checked").click
end
def toggle_feature_on(feature_name)
- find(".project-feature-controls[data-for=\"#{feature_name}\"] .project-feature-toggle:not(.is-checked)").click
+ find(".project-feature-controls[data-for=\"#{feature_name}\"] .gl-toggle:not(.is-checked)").click
end
end
diff --git a/spec/features/projects/files/dockerfile_dropdown_spec.rb b/spec/features/projects/files/dockerfile_dropdown_spec.rb
index 17258f7042f..40d19a94b42 100644
--- a/spec/features/projects/files/dockerfile_dropdown_spec.rb
+++ b/spec/features/projects/files/dockerfile_dropdown_spec.rb
@@ -2,14 +2,16 @@
require 'spec_helper'
-RSpec.describe 'Projects > Files > User wants to add a Dockerfile file', quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/297400' do
+RSpec.describe 'Projects > Files > User wants to add a Dockerfile file', :js do
+ include Spec::Support::Helpers::Features::EditorLiteSpecHelpers
+
before do
project = create(:project, :repository)
sign_in project.owner
visit project_new_blob_path(project, 'master', file_name: 'Dockerfile')
end
- it 'user can pick a Dockerfile file from the dropdown', :js do
+ it 'user can pick a Dockerfile file from the dropdown' do
expect(page).to have_css('.dockerfile-selector')
find('.js-dockerfile-selector').click
@@ -24,6 +26,6 @@ RSpec.describe 'Projects > Files > User wants to add a Dockerfile file', quarant
wait_for_requests
expect(page).to have_css('.dockerfile-selector .dropdown-toggle-text', text: 'Apply a template')
- expect(page).to have_content('COPY ./ /usr/local/apache2/htdocs/')
+ expect(editor_get_value).to have_content('COPY ./ /usr/local/apache2/htdocs/')
end
end
diff --git a/spec/features/projects/files/gitignore_dropdown_spec.rb b/spec/features/projects/files/gitignore_dropdown_spec.rb
index 5a39f2bcd98..a9f2463ecf6 100644
--- a/spec/features/projects/files/gitignore_dropdown_spec.rb
+++ b/spec/features/projects/files/gitignore_dropdown_spec.rb
@@ -2,14 +2,16 @@
require 'spec_helper'
-RSpec.describe 'Projects > Files > User wants to add a .gitignore file' do
+RSpec.describe 'Projects > Files > User wants to add a .gitignore file', :js do
+ include Spec::Support::Helpers::Features::EditorLiteSpecHelpers
+
before do
project = create(:project, :repository)
sign_in project.owner
visit project_new_blob_path(project, 'master', file_name: '.gitignore')
end
- it 'user can pick a .gitignore file from the dropdown', :js do
+ it 'user can pick a .gitignore file from the dropdown' do
expect(page).to have_css('.gitignore-selector')
find('.js-gitignore-selector').click
@@ -24,7 +26,7 @@ RSpec.describe 'Projects > Files > User wants to add a .gitignore file' do
wait_for_requests
expect(page).to have_css('.gitignore-selector .dropdown-toggle-text', text: 'Apply a template')
- expect(page).to have_content('/.bundle')
- expect(page).to have_content('config/initializers/secret_token.rb')
+ expect(editor_get_value).to have_content('/.bundle')
+ expect(editor_get_value).to have_content('config/initializers/secret_token.rb')
end
end
diff --git a/spec/features/projects/files/gitlab_ci_syntax_yml_dropdown_spec.rb b/spec/features/projects/files/gitlab_ci_syntax_yml_dropdown_spec.rb
index 6308acb41f5..ca6f03472dd 100644
--- a/spec/features/projects/files/gitlab_ci_syntax_yml_dropdown_spec.rb
+++ b/spec/features/projects/files/gitlab_ci_syntax_yml_dropdown_spec.rb
@@ -3,6 +3,8 @@
require 'spec_helper'
RSpec.describe 'Projects > Files > User wants to add a .gitlab-ci.yml file' do
+ include Spec::Support::Helpers::Features::EditorLiteSpecHelpers
+
before do
project = create(:project, :repository)
sign_in project.owner
@@ -34,8 +36,7 @@ RSpec.describe 'Projects > Files > User wants to add a .gitlab-ci.yml file' do
let(:experiment_active) { true }
let(:in_experiment_group) { true }
- it 'allows the user to pick a "Learn CI/CD syntax" template from the dropdown', :js,
- { quarantine: { issue: 'https://gitlab.com/gitlab-org/gitlab/-/issues/297347' } } do
+ it 'allows the user to pick a "Learn CI/CD syntax" template from the dropdown', :js do
expect(page).to have_css('.gitlab-ci-syntax-yml-selector')
find('.js-gitlab-ci-syntax-yml-selector').click
@@ -50,7 +51,7 @@ RSpec.describe 'Projects > Files > User wants to add a .gitlab-ci.yml file' do
wait_for_requests
expect(page).to have_css('.gitlab-ci-syntax-yml-selector .dropdown-toggle-text', text: 'Learn CI/CD syntax')
- expect(page).to have_content('You can use artifacts to pass data to jobs in later stages.')
+ expect(editor_get_value).to have_content('You can use artifacts to pass data to jobs in later stages.')
end
end
end
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 879cb6a65c8..55b9f38d8e7 100644
--- a/spec/features/projects/files/gitlab_ci_yml_dropdown_spec.rb
+++ b/spec/features/projects/files/gitlab_ci_yml_dropdown_spec.rb
@@ -2,14 +2,16 @@
require 'spec_helper'
-RSpec.describe 'Projects > Files > User wants to add a .gitlab-ci.yml file' do
+RSpec.describe 'Projects > Files > User wants to add a .gitlab-ci.yml file', :js do
+ include Spec::Support::Helpers::Features::EditorLiteSpecHelpers
+
before do
project = create(:project, :repository)
sign_in project.owner
visit project_new_blob_path(project, 'master', file_name: '.gitlab-ci.yml')
end
- it 'user can pick a template from the dropdown', :js do
+ it 'user can pick a template from the dropdown' do
expect(page).to have_css('.gitlab-ci-yml-selector')
find('.js-gitlab-ci-yml-selector').click
@@ -24,7 +26,7 @@ RSpec.describe 'Projects > Files > User wants to add a .gitlab-ci.yml file' do
wait_for_requests
expect(page).to have_css('.gitlab-ci-yml-selector .dropdown-toggle-text', text: 'Apply a template')
- expect(page).to have_content('This file is a template, and might need editing before it works on your project')
- expect(page).to have_content('jekyll build -d test')
+ expect(editor_get_value).to have_content('This file is a template, and might need editing before it works on your project')
+ expect(editor_get_value).to have_content('jekyll build -d test')
end
end
diff --git a/spec/features/projects/fork_spec.rb b/spec/features/projects/fork_spec.rb
index 1e84d1552a1..8d0500f5e13 100644
--- a/spec/features/projects/fork_spec.rb
+++ b/spec/features/projects/fork_spec.rb
@@ -9,22 +9,45 @@ RSpec.describe 'Project fork' do
let(:project) { create(:project, :public, :repository) }
before do
- sign_in user
+ sign_in(user)
end
- it 'allows user to fork project' do
+ it 'allows user to fork project from the project page' do
visit project_path(project)
- expect(page).not_to have_css('a.disabled', text: 'Select')
+ expect(page).not_to have_css('a.disabled', text: 'Fork')
end
- it 'disables fork button when user has exceeded project limit' do
- user.projects_limit = 0
- user.save!
+ context 'user has exceeded personal project limit' do
+ before do
+ user.update!(projects_limit: 0)
+ end
- visit project_path(project)
+ it 'disables fork button on project page' do
+ visit project_path(project)
+
+ expect(page).to have_css('a.disabled', text: 'Fork')
+ end
+
+ context 'with a group to fork to' do
+ let!(:group) { create(:group).tap { |group| group.add_owner(user) } }
+
+ it 'enables fork button on project page' do
+ visit project_path(project)
+
+ expect(page).not_to have_css('a.disabled', text: 'Fork')
+ end
+
+ it 'allows user to fork only to the group on fork page', :js do
+ visit new_project_fork_path(project)
+
+ to_personal_namespace = find('[data-qa-selector=fork_namespace_button].disabled')
+ to_group = find(".fork-groups button[data-qa-name=#{group.name}]")
- expect(page).to have_css('a.disabled', text: 'Fork')
+ expect(to_personal_namespace).not_to be_nil
+ expect(to_group).not_to be_disabled
+ end
+ end
end
context 'forking enabled / disabled in project settings' do
diff --git a/spec/features/projects/graph_spec.rb b/spec/features/projects/graph_spec.rb
index 7b9f79c9f7f..72df84bf905 100644
--- a/spec/features/projects/graph_spec.rb
+++ b/spec/features/projects/graph_spec.rb
@@ -72,9 +72,9 @@ RSpec.describe 'Project Graph', :js do
it 'renders CI graphs' do
expect(page).to have_content 'Overall'
- expect(page).to have_content 'Pipelines for last week'
- expect(page).to have_content 'Pipelines for last month'
- expect(page).to have_content 'Pipelines for last year'
+ expect(page).to have_content 'Last week'
+ expect(page).to have_content 'Last month'
+ expect(page).to have_content 'Last year'
expect(page).to have_content 'Duration for the last 30 commits'
end
end
diff --git a/spec/features/projects/issuable_templates_spec.rb b/spec/features/projects/issuable_templates_spec.rb
index 8f1c31f229f..12c5820a69d 100644
--- a/spec/features/projects/issuable_templates_spec.rb
+++ b/spec/features/projects/issuable_templates_spec.rb
@@ -95,7 +95,7 @@ RSpec.describe 'issuable templates', :js do
let(:bug_template_content) { 'this is merge request bug template' }
let(:template_override_warning) { 'Applying a template will replace the existing issue description.' }
let(:updated_description) { 'updated merge request description' }
- let(:merge_request) { create(:merge_request, :with_diffs, source_project: project) }
+ let(:merge_request) { create(:merge_request, source_project: project) }
before do
project.repository.create_file(
@@ -154,7 +154,7 @@ RSpec.describe 'issuable templates', :js do
let(:template_content) { 'this is a test "feature-proposal" template' }
let(:fork_user) { create(:user) }
let(:forked_project) { fork_project(project, fork_user, repository: true) }
- let(:merge_request) { create(:merge_request, :with_diffs, source_project: forked_project, target_project: project) }
+ let(:merge_request) { create(:merge_request, source_project: forked_project, target_project: project) }
before do
sign_out(:user)
diff --git a/spec/features/projects/jobs_spec.rb b/spec/features/projects/jobs_spec.rb
index 1557a8a2d72..7811394b541 100644
--- a/spec/features/projects/jobs_spec.rb
+++ b/spec/features/projects/jobs_spec.rb
@@ -27,40 +27,12 @@ RSpec.describe 'Jobs', :clean_gitlab_redis_shared_state do
describe "GET /:project/jobs" do
context 'with no jobs' do
before do
- stub_experiment(jobs_empty_state: experiment_active)
- stub_experiment_for_subject(jobs_empty_state: in_experiment_group)
-
visit project_jobs_path(project)
end
- context 'when experiment not active' do
- let(:experiment_active) { false }
- let(:in_experiment_group) { false }
-
- it 'shows the empty state control page' do
- expect(page).to have_content('No jobs to show')
- expect(page).to have_link('Get started with Pipelines')
- end
- end
-
- context 'when experiment active and user in control group' do
- let(:experiment_active) { true }
- let(:in_experiment_group) { false }
-
- it 'shows the empty state control page' do
- expect(page).to have_content('No jobs to show')
- expect(page).to have_link('Get started with Pipelines')
- end
- end
-
- context 'when experiment active and user in experimental group' do
- let(:experiment_active) { true }
- let(:in_experiment_group) { true }
-
- it 'shows the empty state experiment page' do
- expect(page).to have_content('Use jobs to automate your tasks')
- expect(page).to have_link('Create CI/CD configuration file')
- end
+ it 'shows the empty state page' do
+ expect(page).to have_content('Use jobs to automate your tasks')
+ expect(page).to have_link('Create CI/CD configuration file', href: project.present(current_user: user).add_ci_yml_path)
end
end
@@ -102,7 +74,7 @@ RSpec.describe 'Jobs', :clean_gitlab_redis_shared_state do
it "shows Finished tab jobs" do
expect(page).to have_selector('.nav-links li.active', text: 'Finished')
- expect(page).to have_content 'No jobs to show'
+ expect(page).to have_content('Use jobs to automate your tasks')
end
end
@@ -533,10 +505,10 @@ RSpec.describe 'Jobs', :clean_gitlab_redis_shared_state do
expect(page).to have_content('Trigger token')
expect(page).to have_content('Trigger variables')
- expect(page).not_to have_css('.js-reveal-variables')
+ expect(page).not_to have_selector('[data-testid="trigger-reveal-values-button"]')
- expect(page).to have_selector('.js-build-variable', text: 'TRIGGER_KEY_1')
- expect(page).to have_selector('.js-build-value', text: '••••••')
+ expect(page).to have_selector('[data-testid="trigger-build-key"]', text: 'TRIGGER_KEY_1')
+ expect(page).to have_selector('[data-testid="trigger-build-value"]', text: '••••••')
end
end
@@ -571,17 +543,17 @@ RSpec.describe 'Jobs', :clean_gitlab_redis_shared_state do
expect(page).to have_content('Trigger token')
expect(page).to have_content('Trigger variables')
- expect(page).to have_css('.js-reveal-variables')
+ expect(page).to have_selector('[data-testid="trigger-reveal-values-button"]')
- expect(page).to have_selector('.js-build-variable', text: 'TRIGGER_KEY_1')
- expect(page).to have_selector('.js-build-value', text: '••••••')
+ expect(page).to have_selector('[data-testid="trigger-build-key"]', text: 'TRIGGER_KEY_1')
+ expect(page).to have_selector('[data-testid="trigger-build-value"]', text: '••••••')
end
it 'reveals values on button click', :js do
click_button 'Reveal values'
- expect(page).to have_selector('.js-build-variable', text: 'TRIGGER_KEY_1')
- expect(page).to have_selector('.js-build-value', text: 'TRIGGER_VALUE_1')
+ expect(page).to have_selector('[data-testid="trigger-build-key"]', text: 'TRIGGER_KEY_1')
+ expect(page).to have_selector('[data-testid="trigger-build-value"]', text: 'TRIGGER_VALUE_1')
end
end
diff --git a/spec/features/projects/members/anonymous_user_sees_members_spec.rb b/spec/features/projects/members/anonymous_user_sees_members_spec.rb
index 3b0f00c5494..d710ecf6c88 100644
--- a/spec/features/projects/members/anonymous_user_sees_members_spec.rb
+++ b/spec/features/projects/members/anonymous_user_sees_members_spec.rb
@@ -3,6 +3,8 @@
require 'spec_helper'
RSpec.describe 'Projects > Members > Anonymous user sees members' do
+ include Spec::Support::Helpers::Features::MembersHelpers
+
let(:user) { create(:user) }
let(:group) { create(:group, :public) }
let(:project) { create(:project, :public) }
@@ -12,11 +14,25 @@ RSpec.describe 'Projects > Members > Anonymous user sees members' do
create(:project_group_link, project: project, group: group)
end
- it "anonymous user visits the project's members page and sees the list of members" do
- visit project_project_members_path(project)
+ context 'when `vue_project_members_list` feature flag is enabled', :js do
+ it "anonymous user visits the project's members page and sees the list of members" do
+ visit project_project_members_path(project)
+
+ expect(find_member_row(user)).to have_content(user.name)
+ end
+ end
+
+ context 'when `vue_project_members_list` feature flag is disabled' do
+ before do
+ stub_feature_flags(vue_project_members_list: false)
+ end
+
+ it "anonymous user visits the project's members page and sees the list of members" do
+ visit project_project_members_path(project)
- expect(current_path).to eq(
- project_project_members_path(project))
- expect(page).to have_content(user.name)
+ expect(current_path).to eq(
+ project_project_members_path(project))
+ expect(page).to have_content(user.name)
+ end
end
end
diff --git a/spec/features/projects/members/group_members_spec.rb b/spec/features/projects/members/group_members_spec.rb
index aa15f04bf24..1abd00421ec 100644
--- a/spec/features/projects/members/group_members_spec.rb
+++ b/spec/features/projects/members/group_members_spec.rb
@@ -3,6 +3,8 @@
require 'spec_helper'
RSpec.describe 'Projects members', :js do
+ include Spec::Support::Helpers::Features::MembersHelpers
+
let(:user) { create(:user) }
let(:developer) { create(:user) }
let(:group) { create(:group, :public) }
@@ -18,117 +20,218 @@ RSpec.describe 'Projects members', :js do
sign_in(user)
end
- context 'with a group invitee' do
- before do
- group_invitee
- visit project_project_members_path(project)
- end
+ context 'when `vue_project_members_list` feature flag is enabled' do
+ context 'with a group invitee' do
+ before do
+ group_invitee
+ visit project_project_members_path(project)
+ end
- it 'does not appear in the project members page' do
- page.within first('.content-list') do
- expect(page).not_to have_content('test2@abc.com')
+ it 'does not appear in the project members page' do
+ expect(members_table).not_to have_content('test2@abc.com')
end
end
- end
- context 'with a group' do
- it 'shows group and project members by default' do
- visit project_project_members_path(project)
+ context 'with a group' do
+ it 'shows group and project members by default' do
+ visit project_project_members_path(project)
+
+ expect(members_table).to have_content(developer.name)
+ expect(members_table).to have_content(user.name)
+ expect(members_table).to have_content(group.name)
+ end
+
+ it 'shows project members only if requested' do
+ visit project_project_members_path(project, with_inherited_permissions: 'exclude')
+
+ expect(members_table).to have_content(developer.name)
+ expect(members_table).not_to have_content(user.name)
+ expect(members_table).not_to have_content(group.name)
+ end
- page.within first('.content-list') do
- expect(page).to have_content(developer.name)
+ it 'shows group members only if requested' do
+ visit project_project_members_path(project, with_inherited_permissions: 'only')
- expect(page).to have_content(user.name)
- expect(page).to have_content(group.name)
+ expect(members_table).not_to have_content(developer.name)
+ expect(members_table).to have_content(user.name)
+ expect(members_table).to have_content(group.name)
end
end
- it 'shows project members only if requested' do
- visit project_project_members_path(project, with_inherited_permissions: 'exclude')
+ context 'with a group, a project invitee, and a project requester' do
+ before do
+ group.request_access(group_requester)
+ project.request_access(project_requester)
+ group_invitee
+ project_invitee
+ visit project_project_members_path(project)
+ end
+
+ it 'shows the group owner' do
+ expect(members_table).to have_content(user.name)
+ expect(members_table).to have_content(group.name)
+ end
+
+ it 'shows the project developer' do
+ expect(members_table).to have_content(developer.name)
+ end
+
+ it 'shows the project invitee' do
+ click_link 'Invited'
+
+ expect(members_table).to have_content('test1@abc.com')
+ expect(members_table).not_to have_content('test2@abc.com')
+ end
- page.within first('.content-list') do
- expect(page).to have_content(developer.name)
+ it 'shows the project requester' do
+ click_link 'Access requests'
- expect(page).not_to have_content(user.name)
- expect(page).not_to have_content(group.name)
+ expect(members_table).to have_content(project_requester.name)
+ expect(members_table).not_to have_content(group_requester.name)
end
end
- it 'shows group members only if requested' do
- visit project_project_members_path(project, with_inherited_permissions: 'only')
+ context 'with a group requester' do
+ before do
+ stub_feature_flags(invite_members_group_modal: false)
+ group.request_access(group_requester)
+ visit project_project_members_path(project)
+ end
+
+ it 'does not appear in the project members page' do
+ expect(page).not_to have_link('Access requests')
+ expect(members_table).not_to have_content(group_requester.name)
+ end
+ end
+
+ context 'showing status of members' do
+ it 'shows the status' do
+ create(:user_status, user: user, emoji: 'smirk', message: 'Authoring this object')
- page.within first('.content-list') do
- expect(page).not_to have_content(developer.name)
+ visit project_project_members_path(project)
- expect(page).to have_content(user.name)
- expect(page).to have_content(group.name)
+ expect(first_row).to have_selector('gl-emoji[data-name="smirk"]')
end
end
end
- context 'with a group, a project invitee, and a project requester' do
+ context 'when `vue_project_members_list` feature flag is disabled' do
before do
- group.request_access(group_requester)
- project.request_access(project_requester)
- group_invitee
- project_invitee
- visit project_project_members_path(project)
+ stub_feature_flags(vue_project_members_list: false)
end
- it 'shows the group owner' do
- page.within first('.content-list') do
- # Group owner
- expect(page).to have_content(user.name)
- expect(page).to have_content(group.name)
+ context 'with a group invitee' do
+ before do
+ group_invitee
+ visit project_project_members_path(project)
end
- end
- it 'shows the project developer' do
- page.within first('.content-list') do
- # Project developer
- expect(page).to have_content(developer.name)
+ it 'does not appear in the project members page' do
+ page.within first('.content-list') do
+ expect(page).not_to have_content('test2@abc.com')
+ end
end
end
- it 'shows the project invitee' do
- click_link 'Invited'
+ context 'with a group' do
+ it 'shows group and project members by default' do
+ visit project_project_members_path(project)
- page.within first('.content-list') do
- expect(page).to have_content('test1@abc.com')
- expect(page).not_to have_content('test2@abc.com')
+ page.within first('.content-list') do
+ expect(page).to have_content(developer.name)
+
+ expect(page).to have_content(user.name)
+ expect(page).to have_content(group.name)
+ end
+ end
+
+ it 'shows project members only if requested' do
+ visit project_project_members_path(project, with_inherited_permissions: 'exclude')
+
+ page.within first('.content-list') do
+ expect(page).to have_content(developer.name)
+
+ expect(page).not_to have_content(user.name)
+ expect(page).not_to have_content(group.name)
+ end
end
- end
- it 'shows the project requester' do
- click_link 'Access requests'
+ it 'shows group members only if requested' do
+ visit project_project_members_path(project, with_inherited_permissions: 'only')
- page.within first('.content-list') do
- expect(page).to have_content(project_requester.name)
- expect(page).not_to have_content(group_requester.name)
+ page.within first('.content-list') do
+ expect(page).not_to have_content(developer.name)
+
+ expect(page).to have_content(user.name)
+ expect(page).to have_content(group.name)
+ end
end
end
- end
- context 'with a group requester' do
- before do
- stub_feature_flags(invite_members_group_modal: false)
- group.request_access(group_requester)
- visit project_project_members_path(project)
+ context 'with a group, a project invitee, and a project requester' do
+ before do
+ group.request_access(group_requester)
+ project.request_access(project_requester)
+ group_invitee
+ project_invitee
+ visit project_project_members_path(project)
+ end
+
+ it 'shows the group owner' do
+ page.within first('.content-list') do
+ # Group owner
+ expect(page).to have_content(user.name)
+ expect(page).to have_content(group.name)
+ end
+ end
+
+ it 'shows the project developer' do
+ page.within first('.content-list') do
+ # Project developer
+ expect(page).to have_content(developer.name)
+ end
+ end
+
+ it 'shows the project invitee' do
+ click_link 'Invited'
+
+ page.within first('.content-list') do
+ expect(page).to have_content('test1@abc.com')
+ expect(page).not_to have_content('test2@abc.com')
+ end
+ end
+
+ it 'shows the project requester' do
+ click_link 'Access requests'
+
+ page.within first('.content-list') do
+ expect(page).to have_content(project_requester.name)
+ expect(page).not_to have_content(group_requester.name)
+ end
+ end
end
- it 'does not appear in the project members page' do
- expect(page).not_to have_link('Access requests')
- page.within first('.content-list') do
- expect(page).not_to have_content(group_requester.name)
+ context 'with a group requester' do
+ before do
+ stub_feature_flags(invite_members_group_modal: false)
+ group.request_access(group_requester)
+ visit project_project_members_path(project)
+ end
+
+ it 'does not appear in the project members page' do
+ expect(page).not_to have_link('Access requests')
+ page.within first('.content-list') do
+ expect(page).not_to have_content(group_requester.name)
+ end
end
end
- end
- describe 'showing status of members' do
- it_behaves_like 'showing user status' do
- let(:user_with_status) { developer }
+ context 'showing status of members' do
+ it_behaves_like 'showing user status' do
+ let(:user_with_status) { developer }
- subject { visit project_project_members_path(project) }
+ subject { visit project_project_members_path(project) }
+ end
end
end
end
diff --git a/spec/features/projects/members/groups_with_access_list_spec.rb b/spec/features/projects/members/groups_with_access_list_spec.rb
index 686d86b1783..9d087dfd5f6 100644
--- a/spec/features/projects/members/groups_with_access_list_spec.rb
+++ b/spec/features/projects/members/groups_with_access_list_spec.rb
@@ -3,6 +3,8 @@
require 'spec_helper'
RSpec.describe 'Projects > Members > Groups with access list', :js do
+ include Spec::Support::Helpers::Features::MembersHelpers
+
let_it_be(:user) { create(:user) }
let_it_be(:group) { create(:group, :public) }
let_it_be(:project) { create(:project, :public) }
@@ -15,86 +17,172 @@ RSpec.describe 'Projects > Members > Groups with access list', :js do
project.add_maintainer(user)
sign_in(user)
- visit project_project_members_path(project)
- click_groups_tab
end
- it 'updates group access level' do
- click_button group_link.human_access
-
- page.within '.dropdown-menu' do
- click_link 'Guest'
+ context 'when `vue_project_members_list` feature flag is enabled' do
+ before do
+ visit project_project_members_path(project)
+ click_groups_tab
end
- wait_for_requests
+ it 'updates group access level' do
+ click_button group_link.human_access
+ click_button 'Guest'
- visit project_project_members_path(project)
+ wait_for_requests
- click_groups_tab
+ visit project_project_members_path(project)
- expect(first('.group_member')).to have_content('Guest')
- end
+ click_groups_tab
+
+ expect(find_group_row(group)).to have_content('Guest')
+ end
- it 'updates expiry date' do
- expires_at_field = "member_expires_at_#{group.id}"
- fill_in expires_at_field, with: 3.days.from_now.to_date
+ it 'updates expiry date' do
+ page.within find_group_row(group) do
+ fill_in 'Expiration date', with: 5.days.from_now.to_date
+ find_field('Expiration date').native.send_keys :enter
- find_field(expires_at_field).native.send_keys :enter
- wait_for_requests
+ wait_for_requests
- page.within(find('li.group_member')) do
- expect(page).to have_content('Expires in 3 days')
+ expect(page).to have_content(/in \d days/)
+ end
end
- end
- context 'when link has expiry date set' do
- let(:additional_link_attrs) { { expires_at: 3.days.from_now.to_date } }
+ context 'when link has expiry date set' do
+ let(:additional_link_attrs) { { expires_at: 5.days.from_now.to_date } }
- it 'clears expiry date' do
- page.within(find('li.group_member')) do
- expect(page).to have_content('Expires in 3 days')
+ it 'clears expiry date' do
+ page.within find_group_row(group) do
+ expect(page).to have_content(/in \d days/)
- page.within(find('.js-edit-member-form')) do
- find('.js-clear-input').click
+ find('[data-testid="clear-button"]').click
+
+ wait_for_requests
+
+ expect(page).to have_content('No expiration set')
end
+ end
+ end
- wait_for_requests
+ it 'deletes group link' do
+ expect(page).to have_content(group.full_name)
+
+ page.within find_group_row(group) do
+ click_button 'Remove group'
+ end
- expect(page).not_to have_content('Expires in')
+ page.within('[role="dialog"]') do
+ click_button('Remove group')
+ end
+
+ expect(page).not_to have_content(group.full_name)
+ end
+
+ context 'search in existing members' do
+ it 'finds no results' do
+ fill_in_filtered_search 'Search groups', with: 'testing 123'
+
+ click_groups_tab
+
+ expect(page).not_to have_content(group.full_name)
+ end
+
+ it 'finds results' do
+ fill_in_filtered_search 'Search groups', with: group.full_name
+
+ click_groups_tab
+
+ expect(members_table).to have_content(group.full_name)
end
end
end
- it 'deletes group link' do
- page.within(first('.group_member')) do
- accept_confirm { find('.btn-danger').click }
+ context 'when `vue_project_members_list` feature flag is disabled' do
+ before do
+ stub_feature_flags(vue_project_members_list: false)
+
+ visit project_project_members_path(project)
+ click_groups_tab
end
- wait_for_requests
- expect(page).not_to have_selector('.group_member')
- end
+ it 'updates group access level' do
+ click_button group_link.human_access
- context 'search in existing members' do
- it 'finds no results' do
- page.within '.user-search-form' do
- fill_in 'search_groups', with: 'testing 123'
- find('.user-search-btn').click
+ page.within '.dropdown-menu' do
+ click_link 'Guest'
end
+ wait_for_requests
+
+ visit project_project_members_path(project)
+
click_groups_tab
+ expect(first('.group_member')).to have_content('Guest')
+ end
+
+ it 'updates expiry date' do
+ expires_at_field = "member_expires_at_#{group.id}"
+ fill_in expires_at_field, with: 3.days.from_now.to_date
+
+ find_field(expires_at_field).native.send_keys :enter
+ wait_for_requests
+
+ page.within(find('li.group_member')) do
+ expect(page).to have_content('Expires in 3 days')
+ end
+ end
+
+ context 'when link has expiry date set' do
+ let(:additional_link_attrs) { { expires_at: 3.days.from_now.to_date } }
+
+ it 'clears expiry date' do
+ page.within(find('li.group_member')) do
+ expect(page).to have_content('Expires in 3 days')
+
+ page.within(find('.js-edit-member-form')) do
+ find('.js-clear-input').click
+ end
+
+ wait_for_requests
+
+ expect(page).not_to have_content('Expires in')
+ end
+ end
+ end
+
+ it 'deletes group link' do
+ page.within(first('.group_member')) do
+ accept_confirm { find('.btn-danger').click }
+ end
+ wait_for_requests
+
expect(page).not_to have_selector('.group_member')
end
- it 'finds results' do
- page.within '.user-search-form' do
- fill_in 'search_groups', with: group.name
- find('.user-search-btn').click
+ context 'search in existing members' do
+ it 'finds no results' do
+ page.within '.user-search-form' do
+ fill_in 'search_groups', with: 'testing 123'
+ find('.user-search-btn').click
+ end
+
+ click_groups_tab
+
+ expect(page).not_to have_selector('.group_member')
end
- click_groups_tab
+ it 'finds results' do
+ page.within '.user-search-form' do
+ fill_in 'search_groups', with: group.name
+ find('.user-search-btn').click
+ end
+
+ click_groups_tab
- expect(page).to have_selector('.group_member', count: 1)
+ expect(page).to have_selector('.group_member', count: 1)
+ end
end
end
diff --git a/spec/features/projects/members/invite_group_spec.rb b/spec/features/projects/members/invite_group_spec.rb
index bb56ae348fb..f0d115fef1d 100644
--- a/spec/features/projects/members/invite_group_spec.rb
+++ b/spec/features/projects/members/invite_group_spec.rb
@@ -5,9 +5,14 @@ require 'spec_helper'
RSpec.describe 'Project > Members > Invite group', :js do
include Select2Helper
include ActionView::Helpers::DateHelper
+ include Spec::Support::Helpers::Features::MembersHelpers
let(:maintainer) { create(:user) }
+ before do
+ stub_feature_flags(invite_members_group_modal: false)
+ end
+
describe 'Share with group lock' do
shared_examples 'the project can be shared with groups' do
it 'the "Invite group" tab exists' do
@@ -36,21 +41,45 @@ RSpec.describe 'Project > Members > Invite group', :js do
context 'when the group has "Share with group lock" disabled' do
it_behaves_like 'the project can be shared with groups'
- it 'the project can be shared with another group' do
- visit project_project_members_path(project)
+ context 'when `vue_project_members_list` feature flag is enabled' do
+ it 'the project can be shared with another group' do
+ visit project_project_members_path(project)
- expect(page).not_to have_link 'Groups'
+ expect(page).not_to have_link 'Groups'
- click_on 'invite-group-tab'
+ click_on 'invite-group-tab'
- select2 group_to_share_with.id, from: '#link_group_id'
- page.find('body').click
- find('.btn-success').click
+ select2 group_to_share_with.id, from: '#link_group_id'
+ page.find('body').click
+ find('.btn-success').click
- click_link 'Groups'
+ click_link 'Groups'
- page.within('[data-testid="project-member-groups"]') do
- expect(page).to have_content(group_to_share_with.name)
+ expect(members_table).to have_content(group_to_share_with.name)
+ end
+ end
+
+ context 'when `vue_project_members_list` feature flag is disabled' do
+ before do
+ stub_feature_flags(vue_project_members_list: false)
+ end
+
+ it 'the project can be shared with another group' do
+ visit project_project_members_path(project)
+
+ expect(page).not_to have_link 'Groups'
+
+ click_on 'invite-group-tab'
+
+ select2 group_to_share_with.id, from: '#link_group_id'
+ page.find('body').click
+ find('.btn-success').click
+
+ click_link 'Groups'
+
+ page.within('[data-testid="project-member-groups"]') do
+ expect(page).to have_content(group_to_share_with.name)
+ end
end
end
end
@@ -117,7 +146,7 @@ RSpec.describe 'Project > Members > Invite group', :js do
freeze_time { example.run }
end
- before do
+ def setup
project.add_maintainer(maintainer)
group.add_guest(maintainer)
sign_in(maintainer)
@@ -128,20 +157,37 @@ RSpec.describe 'Project > Members > Invite group', :js do
select2 group.id, from: '#link_group_id'
- fill_in 'expires_at_groups', with: (Time.now + 4.5.days).strftime('%Y-%m-%d')
+ fill_in 'expires_at_groups', with: 5.days.from_now.strftime('%Y-%m-%d')
click_on 'invite-group-tab'
find('.btn-success').click
end
- it 'the group link shows the expiration time with a warning class' do
- click_link 'Groups'
+ context 'when `vue_project_members_list` feature flag is enabled' do
+ it 'the group link shows the expiration time with a warning class' do
+ setup
+ click_link 'Groups'
+
+ expect(find_group_row(group)).to have_content(/in \d days/)
+ expect(find_group_row(group)).to have_selector('.gl-text-orange-500')
+ end
+ end
+
+ context 'when `vue_project_members_list` feature flag is disabled' do
+ before do
+ stub_feature_flags(vue_project_members_list: false)
+ end
- page.within('[data-testid="project-member-groups"]') do
- # Using distance_of_time_in_words_to_now because it is not the same as
- # subtraction, and this way avoids time zone issues as well
- expires_in_text = distance_of_time_in_words_to_now(project.project_group_links.first.expires_at)
- expect(page).to have_content(expires_in_text)
- expect(page).to have_selector('.text-warning')
+ it 'the group link shows the expiration time with a warning class' do
+ setup
+ click_link 'Groups'
+
+ page.within('[data-testid="project-member-groups"]') do
+ # Using distance_of_time_in_words_to_now because it is not the same as
+ # subtraction, and this way avoids time zone issues as well
+ expires_in_text = distance_of_time_in_words_to_now(project.project_group_links.first.expires_at)
+ expect(page).to have_content(expires_in_text)
+ expect(page).to have_selector('.text-warning')
+ end
end
end
end
diff --git a/spec/features/projects/members/list_spec.rb b/spec/features/projects/members/list_spec.rb
index 62115f2dce6..b0fe5b9c48a 100644
--- a/spec/features/projects/members/list_spec.rb
+++ b/spec/features/projects/members/list_spec.rb
@@ -4,7 +4,6 @@ require 'spec_helper'
RSpec.describe 'Project members list' do
include Select2Helper
- include Spec::Support::Helpers::Features::ListRowsHelpers
let(:user1) { create(:user, name: 'John Doe') }
let(:user2) { create(:user, name: 'Mary Jane') }
@@ -13,102 +12,215 @@ RSpec.describe 'Project members list' do
before do
stub_feature_flags(invite_members_group_modal: false)
+
sign_in(user1)
group.add_owner(user1)
end
- it 'show members from project and group' do
- project.add_developer(user2)
+ context 'when `vue_project_members_list` feature flag is enabled', :js do
+ include Spec::Support::Helpers::Features::MembersHelpers
- visit_members_page
+ it 'pushes `vue_project_members_list` feature flag to the frontend' do
+ visit_members_page
- expect(first_row.text).to include(user1.name)
- expect(second_row.text).to include(user2.name)
- end
+ expect(page).to have_pushed_frontend_feature_flags(vueProjectMembersList: true)
+ end
- it 'show user once if member of both group and project' do
- project.add_developer(user1)
+ it 'show members from project and group' do
+ project.add_developer(user2)
- visit_members_page
+ visit_members_page
- expect(first_row.text).to include(user1.name)
- expect(second_row).to be_blank
- end
+ expect(first_row).to have_content(user1.name)
+ expect(second_row).to have_content(user2.name)
+ end
+
+ it 'show user once if member of both group and project' do
+ project.add_developer(user1)
- it 'update user access level', :js do
- project.add_developer(user2)
+ visit_members_page
+
+ expect(first_row).to have_content(user1.name)
+ expect(second_row).to be_blank
+ end
+
+ it 'update user access level', :js do
+ project.add_developer(user2)
- visit_members_page
+ visit_members_page
- page.within(second_row) do
- click_button('Developer')
- click_link('Reporter')
+ page.within find_member_row(user2) do
+ click_button('Developer')
+ click_button('Reporter')
- expect(page).to have_button('Reporter')
+ expect(page).to have_button('Reporter')
+ end
end
- end
- it 'add user to project', :js do
- visit_members_page
+ it 'add user to project', :js do
+ visit_members_page
- add_user(user2.id, 'Reporter')
+ add_user(user2.id, 'Reporter')
- page.within(second_row) do
- expect(page).to have_content(user2.name)
- expect(page).to have_button('Reporter')
+ page.within find_member_row(user2) do
+ expect(page).to have_button('Reporter')
+ end
end
- end
- it 'remove user from project', :js do
- other_user = create(:user)
- project.add_developer(other_user)
+ it 'remove user from project', :js do
+ other_user = create(:user)
+ project.add_developer(other_user)
- visit_members_page
+ visit_members_page
- # Open modal
- find(:css, 'li.project_member', text: other_user.name).find(:css, 'button.btn-danger').click
+ # Open modal
+ page.within find_member_row(other_user) do
+ click_button 'Remove member'
+ end
- expect(page).to have_unchecked_field 'Also unassign this user from related issues and merge requests'
+ page.within('[role="dialog"]') do
+ expect(page).to have_unchecked_field 'Also unassign this user from related issues and merge requests'
+ click_button('Remove member')
+ end
- click_on('Remove member')
+ wait_for_requests
- wait_for_requests
+ expect(members_table).not_to have_content(other_user.name)
+ end
- expect(page).not_to have_content(other_user.name)
- expect(project.users).not_to include(other_user)
- end
+ it 'invite user to project', :js do
+ visit_members_page
+
+ add_user('test@example.com', 'Reporter')
+
+ click_link 'Invited'
+
+ page.within find_invited_member_row('test@example.com') do
+ expect(page).to have_button('Reporter')
+ end
+ end
- it 'invite user to project', :js do
- visit_members_page
+ context 'project bots' do
+ let(:project_bot) { create(:user, :project_bot, name: 'project_bot') }
- add_user('test@example.com', 'Reporter')
+ before do
+ project.add_maintainer(project_bot)
+ end
- click_link 'Invited'
+ it 'does not show form used to change roles and "Expiration date" or the remove user button' do
+ visit_members_page
- page.within(first_row) do
- expect(page).to have_content('test@example.com')
- expect(page).to have_content('Invited')
- expect(page).to have_button('Reporter')
+ page.within find_member_row(project_bot) do
+ expect(page).not_to have_button('Maintainer')
+ expect(page).to have_field('Expiration date', disabled: true)
+ expect(page).not_to have_button('Remove member')
+ end
+ end
end
end
- context 'project bots' do
- let(:project_bot) { create(:user, :project_bot, name: 'project_bot') }
+ context 'when `vue_project_members_list` feature flag is disabled' do
+ include Spec::Support::Helpers::Features::ListRowsHelpers
before do
- project.add_maintainer(project_bot)
+ stub_feature_flags(vue_project_members_list: false)
+ end
+
+ it '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
+
+ it '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
- it 'does not show form used to change roles and "Expiration date" or the remove user button' do
- project_member = project.project_members.find_by(user_id: project_bot.id)
+ it 'update user access 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
+ it 'add user to project', :js do
visit_members_page
- expect(page).not_to have_selector("#edit_project_member_#{project_member.id}")
- expect(page).to have_no_selector("#project_member_#{project_member.id} .btn-danger")
+ 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
+
+ it 'remove user from project', :js do
+ other_user = create(:user)
+ project.add_developer(other_user)
+
+ visit_members_page
+
+ # Open modal
+ find(:css, 'li.project_member', text: other_user.name).find(:css, 'button.btn-danger').click
+
+ expect(page).to have_unchecked_field 'Also unassign this user from related issues and merge requests'
+
+ click_on('Remove member')
+
+ wait_for_requests
+
+ expect(page).not_to have_content(other_user.name)
+ expect(project.users).not_to include(other_user)
+ end
+
+ it 'invite user to project', :js do
+ visit_members_page
+
+ add_user('test@example.com', 'Reporter')
+
+ click_link 'Invited'
+
+ page.within(first_row) do
+ expect(page).to have_content('test@example.com')
+ expect(page).to have_content('Invited')
+ expect(page).to have_button('Reporter')
+ end
+ end
+
+ context 'project bots' do
+ let(:project_bot) { create(:user, :project_bot, name: 'project_bot') }
+
+ before do
+ project.add_maintainer(project_bot)
+ end
+
+ it 'does not show form used to change roles and "Expiration date" or the remove user button' do
+ project_member = project.project_members.find_by(user_id: project_bot.id)
+
+ visit_members_page
+
+ expect(page).not_to have_selector("#edit_project_member_#{project_member.id}")
+ expect(page).to have_no_selector("#project_member_#{project_member.id} .btn-danger")
+ end
end
end
+ private
+
def add_user(id, role)
page.within ".invite-users-form" do
select2(id, from: "#user_ids", multiple: true)
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 d69c3f2652c..1127c64e0c7 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
@@ -5,6 +5,7 @@ require 'spec_helper'
RSpec.describe 'Projects > Members > Maintainer adds member with expiration date', :js do
include Select2Helper
include ActiveSupport::Testing::TimeHelpers
+ include Spec::Support::Helpers::Features::MembersHelpers
let_it_be(:maintainer) { create(:user) }
let_it_be(:project) { create(:project) }
@@ -17,49 +18,107 @@ RSpec.describe 'Projects > Members > Maintainer adds member with expiration date
sign_in(maintainer)
end
- it 'expiration date is displayed in the members list' do
- visit project_project_members_path(project)
+ context 'when `vue_project_members_list` feature flag is enabled' do
+ it 'expiration date is displayed in the members list' do
+ stub_feature_flags(invite_members_group_modal: false)
- page.within '.invite-users-form' do
- select2(new_member.id, from: '#user_ids', multiple: true)
+ visit project_project_members_path(project)
- fill_in 'expires_at', with: 3.days.from_now.to_date
- find_field('expires_at').native.send_keys :enter
+ page.within '.invite-users-form' do
+ select2(new_member.id, from: '#user_ids', multiple: true)
- click_on 'Invite'
+ fill_in 'expires_at', with: 5.days.from_now.to_date
+ find_field('expires_at').native.send_keys :enter
+
+ click_on 'Invite'
+ end
+
+ page.within find_member_row(new_member) do
+ expect(page).to have_content(/in \d days/)
+ end
end
- page.within "#project_member_#{project_member_id}" do
- expect(page).to have_content('Expires in 3 days')
+ it 'changes expiration date' do
+ project.team.add_users([new_member.id], :developer, expires_at: 3.days.from_now.to_date)
+ visit project_project_members_path(project)
+
+ page.within find_member_row(new_member) do
+ fill_in 'Expiration date', with: 5.days.from_now.to_date
+ find_field('Expiration date').native.send_keys :enter
+
+ wait_for_requests
+
+ expect(page).to have_content(/in \d days/)
+ end
end
- end
- it 'changes expiration date' do
- project.team.add_users([new_member.id], :developer, expires_at: Date.today.to_date)
- visit project_project_members_path(project)
+ it 'clears expiration date' do
+ project.team.add_users([new_member.id], :developer, expires_at: 5.days.from_now.to_date)
+ visit project_project_members_path(project)
- page.within "#project_member_#{project_member_id}" do
- fill_in 'Expiration date', with: 3.days.from_now.to_date
- find_field('Expiration date').native.send_keys :enter
+ page.within find_member_row(new_member) do
+ expect(page).to have_content(/in \d days/)
- wait_for_requests
+ find('[data-testid="clear-button"]').click
- expect(page).to have_content('Expires in 3 days')
+ wait_for_requests
+
+ expect(page).to have_content('No expiration set')
+ end
end
end
- it 'clears expiration date' do
- project.team.add_users([new_member.id], :developer, expires_at: 3.days.from_now.to_date)
- visit project_project_members_path(project)
+ context 'when `vue_project_members_list` feature flag is disabled' do
+ before do
+ stub_feature_flags(vue_project_members_list: false)
+ end
+
+ it 'expiration date is displayed in the members list' do
+ stub_feature_flags(invite_members_group_modal: false)
+
+ visit project_project_members_path(project)
+
+ page.within '.invite-users-form' do
+ select2(new_member.id, from: '#user_ids', multiple: true)
+
+ fill_in 'expires_at', with: 3.days.from_now.to_date
+ find_field('expires_at').native.send_keys :enter
+
+ click_on 'Invite'
+ end
+
+ page.within "#project_member_#{project_member_id}" do
+ expect(page).to have_content('Expires in 3 days')
+ end
+ end
+
+ it 'changes expiration date' do
+ project.team.add_users([new_member.id], :developer, expires_at: 1.day.from_now.to_date)
+ visit project_project_members_path(project)
+
+ page.within "#project_member_#{project_member_id}" do
+ fill_in 'Expiration date', with: 3.days.from_now.to_date
+ find_field('Expiration date').native.send_keys :enter
+
+ wait_for_requests
+
+ expect(page).to have_content('Expires in 3 days')
+ end
+ end
+
+ it 'clears expiration date' do
+ project.team.add_users([new_member.id], :developer, expires_at: 3.days.from_now.to_date)
+ visit project_project_members_path(project)
- page.within "#project_member_#{project_member_id}" do
- expect(page).to have_content('Expires in 3 days')
+ page.within "#project_member_#{project_member_id}" do
+ expect(page).to have_content('Expires in 3 days')
- find('.js-clear-input').click
+ find('.js-clear-input').click
- wait_for_requests
+ wait_for_requests
- expect(page).not_to have_content('Expires in')
+ expect(page).not_to have_content('Expires in')
+ end
end
end
diff --git a/spec/features/projects/members/sorting_spec.rb b/spec/features/projects/members/sorting_spec.rb
index be27cbc0d66..3c132747bc4 100644
--- a/spec/features/projects/members/sorting_spec.rb
+++ b/spec/features/projects/members/sorting_spec.rb
@@ -3,6 +3,8 @@
require 'spec_helper'
RSpec.describe 'Projects > Members > Sorting' do
+ include Spec::Support::Helpers::Features::MembersHelpers
+
let(:maintainer) { create(:user, name: 'John Doe') }
let(:developer) { create(:user, name: 'Mary Jane', last_sign_in_at: 5.days.ago) }
let(:project) { create(:project, namespace: maintainer.namespace, creator: maintainer) }
@@ -13,78 +15,169 @@ RSpec.describe 'Projects > Members > Sorting' do
sign_in(maintainer)
end
- it 'sorts alphabetically by default' do
- visit_members_list(sort: nil)
+ context 'when `vue_project_members_list` feature flag is enabled', :js do
+ it 'sorts by account by default' do
+ visit_members_list(sort: nil)
- expect(first_member).to include(maintainer.name)
- expect(second_member).to include(developer.name)
- expect(page).to have_css('.qa-user-sort-dropdown .dropdown-toggle-text', text: 'Name, ascending')
- end
+ expect(first_row).to have_content(maintainer.name)
+ expect(second_row).to have_content(developer.name)
- it 'sorts by access level ascending' do
- visit_members_list(sort: :access_level_asc)
+ expect_sort_by('Account', :asc)
+ end
- expect(first_member).to include(developer.name)
- expect(second_member).to include(maintainer.name)
- expect(page).to have_css('.qa-user-sort-dropdown .dropdown-toggle-text', text: 'Access level, ascending')
- end
+ it 'sorts by max role ascending' do
+ visit_members_list(sort: :access_level_asc)
- it 'sorts by access level descending' do
- visit_members_list(sort: :access_level_desc)
+ expect(first_row).to have_content(developer.name)
+ expect(second_row).to have_content(maintainer.name)
- expect(first_member).to include(maintainer.name)
- expect(second_member).to include(developer.name)
- expect(page).to have_css('.qa-user-sort-dropdown .dropdown-toggle-text', text: 'Access level, descending')
- end
+ expect_sort_by('Max role', :asc)
+ end
- it 'sorts by last joined' do
- visit_members_list(sort: :last_joined)
+ it 'sorts by max role descending' do
+ visit_members_list(sort: :access_level_desc)
- expect(first_member).to include(maintainer.name)
- expect(second_member).to include(developer.name)
- expect(page).to have_css('.qa-user-sort-dropdown .dropdown-toggle-text', text: 'Last joined')
- end
+ expect(first_row).to have_content(maintainer.name)
+ expect(second_row).to have_content(developer.name)
- it 'sorts by oldest joined' do
- visit_members_list(sort: :oldest_joined)
+ expect_sort_by('Max role', :desc)
+ end
- expect(first_member).to include(developer.name)
- expect(second_member).to include(maintainer.name)
- expect(page).to have_css('.qa-user-sort-dropdown .dropdown-toggle-text', text: 'Oldest joined')
- end
+ it 'sorts by access granted ascending' do
+ visit_members_list(sort: :last_joined)
- it 'sorts by name ascending' do
- visit_members_list(sort: :name_asc)
+ expect(first_row).to have_content(maintainer.name)
+ expect(second_row).to have_content(developer.name)
- expect(first_member).to include(maintainer.name)
- expect(second_member).to include(developer.name)
- expect(page).to have_css('.qa-user-sort-dropdown .dropdown-toggle-text', text: 'Name, ascending')
- end
+ expect_sort_by('Access granted', :asc)
+ end
- it 'sorts by name descending' do
- visit_members_list(sort: :name_desc)
+ it 'sorts by access granted descending' do
+ visit_members_list(sort: :oldest_joined)
- expect(first_member).to include(developer.name)
- expect(second_member).to include(maintainer.name)
- expect(page).to have_css('.qa-user-sort-dropdown .dropdown-toggle-text', text: 'Name, descending')
- end
+ expect(first_row).to have_content(developer.name)
+ expect(second_row).to have_content(maintainer.name)
+
+ expect_sort_by('Access granted', :desc)
+ end
+
+ it 'sorts by account ascending' do
+ visit_members_list(sort: :name_asc)
- it 'sorts by recent sign in', :clean_gitlab_redis_shared_state do
- visit_members_list(sort: :recent_sign_in)
+ expect(first_row).to have_content(maintainer.name)
+ expect(second_row).to have_content(developer.name)
- expect(first_member).to include(maintainer.name)
- expect(second_member).to include(developer.name)
- expect(page).to have_css('.qa-user-sort-dropdown .dropdown-toggle-text', text: 'Recent sign in')
+ expect_sort_by('Account', :asc)
+ end
+
+ it 'sorts by account descending' do
+ visit_members_list(sort: :name_desc)
+
+ expect(first_row).to have_content(developer.name)
+ expect(second_row).to have_content(maintainer.name)
+
+ expect_sort_by('Account', :desc)
+ end
+
+ it 'sorts by last sign-in ascending', :clean_gitlab_redis_shared_state do
+ visit_members_list(sort: :recent_sign_in)
+
+ expect(first_row).to have_content(maintainer.name)
+ expect(second_row).to have_content(developer.name)
+
+ expect_sort_by('Last sign-in', :asc)
+ end
+
+ it 'sorts by last sign-in descending', :clean_gitlab_redis_shared_state do
+ visit_members_list(sort: :oldest_sign_in)
+
+ expect(first_row).to have_content(developer.name)
+ expect(second_row).to have_content(maintainer.name)
+
+ expect_sort_by('Last sign-in', :desc)
+ end
end
- it 'sorts by oldest sign in', :clean_gitlab_redis_shared_state do
- visit_members_list(sort: :oldest_sign_in)
+ context 'when `vue_project_members_list` feature flag is disabled' do
+ before do
+ stub_feature_flags(vue_project_members_list: false)
+ end
+
+ it 'sorts alphabetically by default' do
+ visit_members_list(sort: nil)
- expect(first_member).to include(developer.name)
- expect(second_member).to include(maintainer.name)
- expect(page).to have_css('.qa-user-sort-dropdown .dropdown-toggle-text', text: 'Oldest sign in')
+ expect(first_member).to include(maintainer.name)
+ expect(second_member).to include(developer.name)
+ expect(page).to have_css('.qa-user-sort-dropdown .dropdown-toggle-text', text: 'Name, ascending')
+ end
+
+ it 'sorts by access level ascending' do
+ visit_members_list(sort: :access_level_asc)
+
+ expect(first_member).to include(developer.name)
+ expect(second_member).to include(maintainer.name)
+ expect(page).to have_css('.qa-user-sort-dropdown .dropdown-toggle-text', text: 'Access level, ascending')
+ end
+
+ it 'sorts by access level descending' do
+ visit_members_list(sort: :access_level_desc)
+
+ expect(first_member).to include(maintainer.name)
+ expect(second_member).to include(developer.name)
+ expect(page).to have_css('.qa-user-sort-dropdown .dropdown-toggle-text', text: 'Access level, descending')
+ end
+
+ it 'sorts by last joined' do
+ visit_members_list(sort: :last_joined)
+
+ expect(first_member).to include(maintainer.name)
+ expect(second_member).to include(developer.name)
+ expect(page).to have_css('.qa-user-sort-dropdown .dropdown-toggle-text', text: 'Last joined')
+ end
+
+ it 'sorts by oldest joined' do
+ visit_members_list(sort: :oldest_joined)
+
+ expect(first_member).to include(developer.name)
+ expect(second_member).to include(maintainer.name)
+ expect(page).to have_css('.qa-user-sort-dropdown .dropdown-toggle-text', text: 'Oldest joined')
+ end
+
+ it 'sorts by name ascending' do
+ visit_members_list(sort: :name_asc)
+
+ expect(first_member).to include(maintainer.name)
+ expect(second_member).to include(developer.name)
+ expect(page).to have_css('.qa-user-sort-dropdown .dropdown-toggle-text', text: 'Name, ascending')
+ end
+
+ it 'sorts by name descending' do
+ visit_members_list(sort: :name_desc)
+
+ expect(first_member).to include(developer.name)
+ expect(second_member).to include(maintainer.name)
+ expect(page).to have_css('.qa-user-sort-dropdown .dropdown-toggle-text', text: 'Name, descending')
+ end
+
+ it 'sorts by recent sign in', :clean_gitlab_redis_shared_state do
+ visit_members_list(sort: :recent_sign_in)
+
+ expect(first_member).to include(maintainer.name)
+ expect(second_member).to include(developer.name)
+ expect(page).to have_css('.qa-user-sort-dropdown .dropdown-toggle-text', text: 'Recent sign in')
+ end
+
+ it 'sorts by oldest sign in', :clean_gitlab_redis_shared_state do
+ visit_members_list(sort: :oldest_sign_in)
+
+ expect(first_member).to include(developer.name)
+ expect(second_member).to include(maintainer.name)
+ expect(page).to have_css('.qa-user-sort-dropdown .dropdown-toggle-text', text: 'Oldest sign in')
+ end
end
+ private
+
def visit_members_list(sort:)
visit project_project_members_path(project, sort: sort)
end
@@ -96,4 +189,11 @@ RSpec.describe 'Projects > Members > Sorting' do
def second_member
page.all('ul.content-list > li').last.text
end
+
+ def expect_sort_by(text, sort_direction)
+ within('[data-testid="members-sort-dropdown"]') do
+ expect(page).to have_css('button[aria-haspopup="true"]', text: text)
+ expect(page).to have_button("Sorting Direction: #{sort_direction == :asc ? 'Ascending' : 'Descending'}")
+ end
+ end
end
diff --git a/spec/features/projects/members/tabs_spec.rb b/spec/features/projects/members/tabs_spec.rb
index bdcf02c82a4..eef3395de91 100644
--- a/spec/features/projects/members/tabs_spec.rb
+++ b/spec/features/projects/members/tabs_spec.rb
@@ -3,6 +3,7 @@
require 'spec_helper'
RSpec.describe 'Projects > Members > Tabs' do
+ include Spec::Support::Helpers::Features::MembersHelpers
using RSpec::Parameterized::TableSyntax
let_it_be(:user) { create(:user) }
@@ -19,55 +20,93 @@ RSpec.describe 'Projects > Members > Tabs' do
end
end
- before do
- allow(Kaminari.config).to receive(:default_per_page).and_return(1)
-
- sign_in(user)
- visit project_project_members_path(project)
- end
+ context 'tabs' do
+ before do
+ sign_in(user)
+ visit project_project_members_path(project)
+ end
- where(:tab, :count) do
- 'Members' | 3
- 'Invited' | 2
- 'Groups' | 2
- 'Access requests' | 2
- end
+ where(:tab, :count) do
+ 'Members' | 3
+ 'Invited' | 2
+ 'Groups' | 2
+ 'Access requests' | 2
+ end
- with_them do
- it "renders #{params[:tab]} tab" do
- expect(page).to have_selector('.nav-link', text: "#{tab} #{count}")
+ with_them do
+ it "renders #{params[:tab]} tab" do
+ expect(page).to have_selector('.nav-link', text: "#{tab} #{count}")
+ end
end
- end
- context 'displays "Members" tab by default' do
- it_behaves_like 'active "Members" tab'
+ context 'displays "Members" tab by default' do
+ it_behaves_like 'active "Members" tab'
+ end
end
- context 'when searching "Groups"', :js do
+ context 'when `vue_project_members_list` feature flag is enabled' do
before do
- click_link 'Groups'
+ sign_in(user)
+ visit project_project_members_path(project)
+ end
+
+ context 'when searching "Groups"', :js do
+ before do
+ click_link 'Groups'
+
+ fill_in_filtered_search 'Search groups', with: 'group'
+ end
- page.within '[data-testid="group-link-search-form"]' do
- fill_in 'search_groups', with: 'group'
- find('button[type="submit"]').click
+ it 'displays "Groups" tab' do
+ expect(page).to have_selector('.nav-link.active', text: 'Groups')
+ end
+
+ context 'and then searching "Members"' do
+ before do
+ click_link 'Members 3'
+
+ fill_in_filtered_search 'Filter members', with: 'user'
+ end
+
+ it_behaves_like 'active "Members" tab'
end
end
+ end
+
+ context 'when `vue_project_members_list` feature flag is disabled' do
+ before do
+ stub_feature_flags(vue_project_members_list: false)
- it 'displays "Groups" tab' do
- expect(page).to have_selector('.nav-link.active', text: 'Groups')
+ sign_in(user)
+ visit project_project_members_path(project)
end
- context 'and then searching "Members"' do
+ context 'when searching "Groups"', :js do
before do
- click_link 'Members 3'
+ click_link 'Groups'
- page.within '[data-testid="user-search-form"]' do
- fill_in 'search', with: 'user'
+ page.within '[data-testid="group-link-search-form"]' do
+ fill_in 'search_groups', with: 'group'
find('button[type="submit"]').click
end
end
- it_behaves_like 'active "Members" tab'
+ it 'displays "Groups" tab' do
+ expect(page).to have_selector('.nav-link.active', text: 'Groups')
+ end
+
+ context 'and then searching "Members"' do
+ before do
+ click_link 'Members 3'
+
+ page.within '[data-testid="user-search-form"]' do
+ fill_in 'search', with: 'user'
+ find('button[type="submit"]').click
+ end
+ end
+
+ it_behaves_like 'active "Members" tab'
+ end
end
end
end
diff --git a/spec/features/projects/navbar_spec.rb b/spec/features/projects/navbar_spec.rb
index 25791b393bc..4ff3827b240 100644
--- a/spec/features/projects/navbar_spec.rb
+++ b/spec/features/projects/navbar_spec.rb
@@ -67,23 +67,4 @@ RSpec.describe 'Project navbar' do
it_behaves_like 'verified navigation bar'
end
-
- context 'when invite team members is not available' do
- it 'does not display the js-invite-members-trigger' do
- visit project_path(project)
-
- expect(page).not_to have_selector('.js-invite-members-trigger')
- end
- end
-
- context 'when invite team members is available' do
- it 'includes the div for js-invite-members-trigger' do
- stub_feature_flags(invite_members_group_modal: true)
- allow_any_instance_of(InviteMembersHelper).to receive(:invite_members_allowed?).and_return(true)
-
- visit project_path(project)
-
- expect(page).to have_selector('.js-invite-members-trigger')
- end
- end
end
diff --git a/spec/features/projects/pages/user_adds_domain_spec.rb b/spec/features/projects/pages/user_adds_domain_spec.rb
new file mode 100644
index 00000000000..24c9edb79e5
--- /dev/null
+++ b/spec/features/projects/pages/user_adds_domain_spec.rb
@@ -0,0 +1,185 @@
+# frozen_string_literal: true
+require 'spec_helper'
+
+RSpec.describe 'User adds pages domain', :js do
+ include LetsEncryptHelpers
+
+ let_it_be(:project) { create(:project, pages_https_only: false) }
+ let(:user) { create(:user) }
+
+ before do
+ allow(Gitlab.config.pages).to receive(:enabled).and_return(true)
+
+ project.add_maintainer(user)
+
+ sign_in(user)
+ end
+
+ context 'when pages are exposed on external HTTP address', :http_pages_enabled do
+ let(:project) { create(:project, pages_https_only: false) }
+
+ shared_examples 'adds new domain' do
+ it 'adds new domain' do
+ visit new_project_pages_domain_path(project)
+
+ fill_in 'Domain', with: 'my.test.domain.com'
+ click_button 'Create New Domain'
+
+ expect(page).to have_content('my.test.domain.com')
+ end
+ end
+
+ it 'allows to add new domain' do
+ visit project_pages_path(project)
+
+ expect(page).to have_content('New Domain')
+ end
+
+ it_behaves_like 'adds new domain'
+
+ context 'when project in group namespace' do
+ it_behaves_like 'adds new domain' do
+ let(:group) { create :group }
+ let(:project) { create(:project, namespace: group, pages_https_only: false) }
+ end
+ end
+
+ context 'when pages domain is added' do
+ before do
+ create(:pages_domain, project: project, domain: 'my.test.domain.com')
+
+ visit new_project_pages_domain_path(project)
+ end
+
+ it 'renders certificates is disabled' do
+ expect(page).to have_content('Support for custom certificates is disabled')
+ end
+
+ it 'does not adds new domain and renders error message' do
+ fill_in 'Domain', with: 'my.test.domain.com'
+ click_button 'Create New Domain'
+
+ expect(page).to have_content('Domain has already been taken')
+ end
+ end
+ end
+
+ context 'when pages are exposed on external HTTPS address', :https_pages_enabled, :js do
+ let(:certificate_pem) do
+ attributes_for(:pages_domain)[:certificate]
+ end
+
+ let(:certificate_key) do
+ attributes_for(:pages_domain)[:key]
+ end
+
+ it 'adds new domain with certificate' do
+ visit new_project_pages_domain_path(project)
+
+ fill_in 'Domain', with: 'my.test.domain.com'
+
+ fill_in 'Certificate (PEM)', with: certificate_pem
+ fill_in 'Key (PEM)', with: certificate_key
+ click_button 'Create New Domain'
+
+ expect(page).to have_content('my.test.domain.com')
+ end
+
+ it "adds new domain with certificate if Let's Encrypt is enabled" do
+ stub_lets_encrypt_settings
+
+ visit new_project_pages_domain_path(project)
+
+ fill_in 'Domain', with: 'my.test.domain.com'
+
+ find('.js-auto-ssl-toggle-container .project-feature-toggle').click
+
+ fill_in 'Certificate (PEM)', with: certificate_pem
+ fill_in 'Key (PEM)', with: certificate_key
+ click_button 'Create New Domain'
+
+ expect(page).to have_content('my.test.domain.com')
+ end
+
+ it 'shows validation error if domain is duplicated' do
+ project.pages_domains.create!(domain: 'my.test.domain.com')
+
+ visit new_project_pages_domain_path(project)
+
+ fill_in 'Domain', with: 'my.test.domain.com'
+ click_button 'Create New Domain'
+
+ expect(page).to have_content('Domain has already been taken')
+ end
+
+ describe 'with dns verification enabled' do
+ before do
+ stub_application_setting(pages_domain_verification_enabled: true)
+ end
+
+ it 'shows the DNS verification record' do
+ domain = create(:pages_domain, project: project)
+
+ visit project_pages_path(project)
+
+ within('#content-body') { click_link 'Edit' }
+ expect(page).to have_field :domain_verification, with: "#{domain.verification_domain} TXT #{domain.keyed_verification_code}"
+ end
+ end
+
+ describe 'updating the certificate for an existing domain' do
+ let!(:domain) do
+ create(:pages_domain, project: project, auto_ssl_enabled: false)
+ end
+
+ it 'allows the certificate to be updated' do
+ visit project_pages_path(project)
+
+ within('#content-body') { click_link 'Edit' }
+ click_button 'Save Changes'
+
+ expect(page).to have_content('Domain was updated')
+ end
+
+ context 'when the certificate is invalid' do
+ let!(:domain) do
+ create(:pages_domain, :without_certificate, :without_key, project: project)
+ end
+
+ it 'tells the user what the problem is' do
+ visit project_pages_path(project)
+
+ within('#content-body') { click_link 'Edit' }
+
+ fill_in 'Certificate (PEM)', with: 'invalid data'
+ click_button 'Save Changes'
+
+ expect(page).to have_content('Certificate must be a valid PEM certificate')
+ expect(page).to have_content('Certificate misses intermediates')
+ expect(page).to have_content("Key doesn't match the certificate")
+ end
+ end
+
+ it 'allows the certificate to be removed', :js do
+ visit project_pages_path(project)
+
+ within('#content-body') { click_link 'Edit' }
+
+ accept_confirm { click_link 'Remove' }
+
+ expect(page).to have_field('Certificate (PEM)', with: '')
+ expect(page).to have_field('Key (PEM)', with: '')
+ domain.reload
+ expect(domain.certificate).to be_nil
+ expect(domain.key).to be_nil
+ end
+
+ it 'shows the DNS CNAME record' do
+ visit project_pages_path(project)
+
+ within('#content-body') { click_link 'Edit' }
+ expect(page).to have_field :domain_dns, with: "#{domain.domain} CNAME #{domain.project.pages_subdomain}.#{Settings.pages.host}."
+ end
+ end
+ end
+end
diff --git a/spec/features/projects/pages_lets_encrypt_spec.rb b/spec/features/projects/pages/user_edits_lets_encrypt_settings_spec.rb
index 302e9f5e533..cf8438d5e6f 100644
--- a/spec/features/projects/pages_lets_encrypt_spec.rb
+++ b/spec/features/projects/pages/user_edits_lets_encrypt_settings_spec.rb
@@ -17,7 +17,7 @@ RSpec.describe "Pages with Let's Encrypt", :https_pages_enabled do
project.add_role(user, role)
sign_in(user)
- project.namespace.update(owner: user)
+ project.namespace.update!(owner: user)
allow_next_instance_of(Project) do |instance|
allow(instance).to receive(:pages_deployed?) { true }
end
diff --git a/spec/features/projects/pages/user_edits_settings_spec.rb b/spec/features/projects/pages/user_edits_settings_spec.rb
new file mode 100644
index 00000000000..3649fae17ce
--- /dev/null
+++ b/spec/features/projects/pages/user_edits_settings_spec.rb
@@ -0,0 +1,201 @@
+# frozen_string_literal: true
+require 'spec_helper'
+
+RSpec.describe 'Pages edits pages settings', :js do
+ let(:project) { create(:project, pages_https_only: false) }
+ let(:user) { create(:user) }
+
+ before do
+ allow(Gitlab.config.pages).to receive(:enabled).and_return(true)
+
+ project.add_maintainer(user)
+
+ sign_in(user)
+ end
+
+ context 'when user is the owner' do
+ before do
+ project.namespace.update!(owner: user)
+ end
+
+ context 'when pages deployed' do
+ before do
+ project.mark_pages_as_deployed
+ end
+
+ it 'renders Access pages' do
+ visit project_pages_path(project)
+
+ expect(page).to have_content('Access pages')
+ end
+
+ context 'when pages are disabled in the project settings' do
+ it 'renders disabled warning' do
+ project.project_feature.update!(pages_access_level: ProjectFeature::DISABLED)
+
+ visit project_pages_path(project)
+
+ expect(page).to have_content('GitLab Pages are disabled for this project')
+ end
+ end
+
+ it 'renders first deployment warning' do
+ visit project_pages_path(project)
+
+ expect(page).to have_content('It may take up to 30 minutes before the site is available after the first deployment.')
+ end
+
+ shared_examples 'does not render access control warning' do
+ it 'does not render access control warning' do
+ visit project_pages_path(project)
+
+ expect(page).not_to have_content('Access Control is enabled for this Pages website')
+ end
+ end
+
+ include_examples 'does not render access control warning'
+
+ context 'when access control is enabled in gitlab settings' do
+ before do
+ stub_pages_setting(access_control: true)
+ end
+
+ it 'renders access control warning' do
+ visit project_pages_path(project)
+
+ expect(page).to have_content('Access Control is enabled for this Pages website')
+ end
+
+ context 'when pages are public' do
+ before do
+ project.project_feature.update!(pages_access_level: ProjectFeature::PUBLIC)
+ end
+
+ include_examples 'does not render access control warning'
+ end
+ end
+
+ context 'when support for external domains is disabled' do
+ it 'renders message that support is disabled' do
+ visit project_pages_path(project)
+
+ expect(page).to have_content('Support for domains and certificates is disabled')
+ end
+ end
+ end
+
+ it 'does not see anything to destroy' do
+ visit project_pages_path(project)
+
+ expect(page).to have_content('Configure pages')
+ expect(page).not_to have_link('Remove pages')
+ end
+
+ describe 'project settings page' do
+ it 'renders "Pages" tab' do
+ visit edit_project_path(project)
+
+ page.within '.nav-sidebar' do
+ expect(page).to have_link('Pages')
+ end
+ end
+
+ context 'when pages are disabled' do
+ before do
+ allow(Gitlab.config.pages).to receive(:enabled).and_return(false)
+ end
+
+ it 'does not render "Pages" tab' do
+ visit edit_project_path(project)
+
+ page.within '.nav-sidebar' do
+ expect(page).not_to have_link('Pages')
+ end
+ end
+ end
+ end
+ end
+
+ describe 'HTTPS settings', :https_pages_enabled do
+ before do
+ project.namespace.update!(owner: user)
+
+ project.mark_pages_as_deployed
+ end
+
+ it 'tries to change the setting' do
+ visit project_pages_path(project)
+ expect(page).to have_content("Force HTTPS (requires valid certificates)")
+
+ uncheck :project_pages_https_only
+
+ click_button 'Save'
+
+ expect(page).to have_text('Your changes have been saved')
+ expect(page).not_to have_checked_field('project_pages_https_only')
+ end
+
+ context 'setting could not be updated' do
+ let(:service) { instance_double('Projects::UpdateService') }
+
+ before do
+ allow(Projects::UpdateService).to receive(:new).and_return(service)
+ allow(service).to receive(:execute).and_return(status: :error, message: 'Some error has occured')
+ end
+
+ it 'tries to change the setting' do
+ visit project_pages_path(project)
+
+ uncheck :project_pages_https_only
+
+ click_button 'Save'
+
+ expect(page).to have_text('Some error has occured')
+ end
+ end
+
+ context 'non-HTTPS domain exists' do
+ let(:project) { create(:project, pages_https_only: false) }
+
+ before do
+ create(:pages_domain, :without_key, :without_certificate, project: project)
+ end
+
+ it 'the setting is disabled' do
+ visit project_pages_path(project)
+
+ expect(page).to have_field(:project_pages_https_only, disabled: true)
+ expect(page).to have_button('Save')
+ end
+ end
+
+ context 'HTTPS pages are disabled', :https_pages_disabled do
+ it 'the setting is unavailable' do
+ visit project_pages_path(project)
+
+ expect(page).not_to have_field(:project_pages_https_only)
+ expect(page).not_to have_content('Force HTTPS (requires valid certificates)')
+ expect(page).to have_button('Save')
+ end
+ end
+ end
+
+ describe 'Remove page' do
+ context 'when pages are deployed' do
+ before do
+ project.mark_pages_as_deployed
+ end
+
+ it 'removes the pages', :sidekiq_inline do
+ visit project_pages_path(project)
+
+ expect(page).to have_link('Remove pages')
+
+ accept_confirm { click_link 'Remove pages' }
+
+ expect(page).to have_content('Pages were scheduled for removal')
+ expect(project.reload.pages_deployed?).to be_falsey
+ end
+ end
+ end
+end
diff --git a/spec/features/projects/pages_spec.rb b/spec/features/projects/pages_spec.rb
deleted file mode 100644
index 11f712fde81..00000000000
--- a/spec/features/projects/pages_spec.rb
+++ /dev/null
@@ -1,411 +0,0 @@
-# frozen_string_literal: true
-require 'spec_helper'
-
-RSpec.shared_examples 'pages settings editing' do
- let_it_be(:project) { create(:project, pages_https_only: false) }
- let(:user) { create(:user) }
- let(:role) { :maintainer }
-
- before do
- allow(Gitlab.config.pages).to receive(:enabled).and_return(true)
-
- project.add_role(user, role)
-
- sign_in(user)
- end
-
- context 'when user is the owner' do
- before do
- project.namespace.update(owner: user)
- end
-
- context 'when pages deployed' do
- before do
- allow_any_instance_of(Project).to receive(:pages_deployed?) { true }
- end
-
- it 'renders Access pages' do
- visit project_pages_path(project)
-
- expect(page).to have_content('Access pages')
- end
-
- context 'when pages are disabled in the project settings' do
- it 'renders disabled warning' do
- project.project_feature.update!(pages_access_level: ProjectFeature::DISABLED)
-
- visit project_pages_path(project)
-
- expect(page).to have_content('GitLab Pages are disabled for this project')
- end
- end
-
- it 'renders first deployment warning' do
- visit project_pages_path(project)
-
- expect(page).to have_content('It may take up to 30 minutes before the site is available after the first deployment.')
- end
-
- shared_examples 'does not render access control warning' do
- it 'does not render access control warning' do
- visit project_pages_path(project)
-
- expect(page).not_to have_content('Access Control is enabled for this Pages website')
- end
- end
-
- include_examples 'does not render access control warning'
-
- context 'when access control is enabled in gitlab settings' do
- before do
- stub_pages_setting(access_control: true)
- end
-
- it 'renders access control warning' do
- visit project_pages_path(project)
-
- expect(page).to have_content('Access Control is enabled for this Pages website')
- end
-
- context 'when pages are public' do
- before do
- project.project_feature.update!(pages_access_level: ProjectFeature::PUBLIC)
- end
-
- include_examples 'does not render access control warning'
- end
- end
-
- context 'when support for external domains is disabled' do
- it 'renders message that support is disabled' do
- visit project_pages_path(project)
-
- expect(page).to have_content('Support for domains and certificates is disabled')
- end
- end
-
- context 'when pages are exposed on external HTTP address', :http_pages_enabled do
- let(:project) { create(:project, pages_https_only: false) }
-
- shared_examples 'adds new domain' do
- it 'adds new domain' do
- visit new_project_pages_domain_path(project)
-
- fill_in 'Domain', with: 'my.test.domain.com'
- click_button 'Create New Domain'
-
- expect(page).to have_content('my.test.domain.com')
- end
- end
-
- it 'allows to add new domain' do
- visit project_pages_path(project)
-
- expect(page).to have_content('New Domain')
- end
-
- it_behaves_like 'adds new domain'
-
- context 'when project in group namespace' do
- it_behaves_like 'adds new domain' do
- let(:group) { create :group }
- let(:project) { create(:project, namespace: group, pages_https_only: false) }
- end
- end
-
- context 'when pages domain is added' do
- before do
- create(:pages_domain, project: project, domain: 'my.test.domain.com')
-
- visit new_project_pages_domain_path(project)
- end
-
- it 'renders certificates is disabled' do
- expect(page).to have_content('Support for custom certificates is disabled')
- end
-
- it 'does not adds new domain and renders error message' do
- fill_in 'Domain', with: 'my.test.domain.com'
- click_button 'Create New Domain'
-
- expect(page).to have_content('Domain has already been taken')
- end
- end
- end
-
- context 'when pages are exposed on external HTTPS address', :https_pages_enabled, :js do
- let(:certificate_pem) do
- attributes_for(:pages_domain)[:certificate]
- end
-
- let(:certificate_key) do
- attributes_for(:pages_domain)[:key]
- end
-
- it 'adds new domain with certificate' do
- visit new_project_pages_domain_path(project)
-
- fill_in 'Domain', with: 'my.test.domain.com'
-
- if ::Gitlab::LetsEncrypt.enabled?
- find('.js-auto-ssl-toggle-container .project-feature-toggle').click
- end
-
- fill_in 'Certificate (PEM)', with: certificate_pem
- fill_in 'Key (PEM)', with: certificate_key
- click_button 'Create New Domain'
-
- expect(page).to have_content('my.test.domain.com')
- end
-
- it 'shows validation error if domain is duplicated' do
- project.pages_domains.create!(domain: 'my.test.domain.com')
-
- visit new_project_pages_domain_path(project)
-
- fill_in 'Domain', with: 'my.test.domain.com'
- click_button 'Create New Domain'
-
- expect(page).to have_content('Domain has already been taken')
- end
-
- describe 'with dns verification enabled' do
- before do
- stub_application_setting(pages_domain_verification_enabled: true)
- end
-
- it 'shows the DNS verification record' do
- domain = create(:pages_domain, project: project)
-
- visit project_pages_path(project)
-
- within('#content-body') { click_link 'Edit' }
- expect(page).to have_field :domain_verification, with: "#{domain.verification_domain} TXT #{domain.keyed_verification_code}"
- end
- end
-
- describe 'updating the certificate for an existing domain' do
- let!(:domain) do
- create(:pages_domain, project: project, auto_ssl_enabled: false)
- end
-
- it 'allows the certificate to be updated' do
- visit project_pages_path(project)
-
- within('#content-body') { click_link 'Edit' }
- click_button 'Save Changes'
-
- expect(page).to have_content('Domain was updated')
- end
-
- context 'when the certificate is invalid' do
- let!(:domain) do
- create(:pages_domain, :without_certificate, :without_key, project: project)
- end
-
- it 'tells the user what the problem is' do
- visit project_pages_path(project)
-
- within('#content-body') { click_link 'Edit' }
-
- if ::Gitlab::LetsEncrypt.enabled?
- find('.js-auto-ssl-toggle-container .project-feature-toggle').click
- end
-
- fill_in 'Certificate (PEM)', with: 'invalid data'
- click_button 'Save Changes'
-
- expect(page).to have_content('Certificate must be a valid PEM certificate')
- expect(page).to have_content('Certificate misses intermediates')
- expect(page).to have_content("Key doesn't match the certificate")
- end
- end
-
- it 'allows the certificate to be removed', :js do
- visit project_pages_path(project)
-
- within('#content-body') { click_link 'Edit' }
-
- accept_confirm { click_link 'Remove' }
-
- expect(page).to have_field('Certificate (PEM)', with: '')
- expect(page).to have_field('Key (PEM)', with: '')
- domain.reload
- expect(domain.certificate).to be_nil
- expect(domain.key).to be_nil
- end
-
- it 'shows the DNS CNAME record' do
- visit project_pages_path(project)
-
- within('#content-body') { click_link 'Edit' }
- expect(page).to have_field :domain_dns, with: "#{domain.domain} CNAME #{domain.project.pages_subdomain}.#{Settings.pages.host}."
- end
- end
- end
- end
-
- it 'does not see anything to destroy' do
- visit project_pages_path(project)
-
- expect(page).to have_content('Configure pages')
- expect(page).not_to have_link('Remove pages')
- end
-
- describe 'project settings page' do
- it 'renders "Pages" tab' do
- visit edit_project_path(project)
-
- page.within '.nav-sidebar' do
- expect(page).to have_link('Pages')
- end
- end
-
- context 'when pages are disabled' do
- before do
- allow(Gitlab.config.pages).to receive(:enabled).and_return(false)
- end
-
- it 'does not render "Pages" tab' do
- visit edit_project_path(project)
-
- page.within '.nav-sidebar' do
- expect(page).not_to have_link('Pages')
- end
- end
- end
- end
- end
-
- describe 'HTTPS settings', :https_pages_enabled do
- before do
- project.namespace.update(owner: user)
-
- allow_any_instance_of(Project).to receive(:pages_deployed?) { true }
- end
-
- it 'tries to change the setting' do
- visit project_pages_path(project)
- expect(page).to have_content("Force HTTPS (requires valid certificates)")
-
- uncheck :project_pages_https_only
-
- click_button 'Save'
-
- expect(page).to have_text('Your changes have been saved')
- expect(page).not_to have_checked_field('project_pages_https_only')
- end
-
- context 'setting could not be updated' do
- let(:service) { instance_double('Projects::UpdateService') }
-
- before do
- allow(Projects::UpdateService).to receive(:new).and_return(service)
- allow(service).to receive(:execute).and_return(status: :error, message: 'Some error has occured')
- end
-
- it 'tries to change the setting' do
- visit project_pages_path(project)
-
- uncheck :project_pages_https_only
-
- click_button 'Save'
-
- expect(page).to have_text('Some error has occured')
- end
- end
-
- context 'non-HTTPS domain exists' do
- let(:project) { create(:project, pages_https_only: false) }
-
- before do
- create(:pages_domain, :without_key, :without_certificate, project: project)
- end
-
- it 'the setting is disabled' do
- visit project_pages_path(project)
-
- expect(page).to have_field(:project_pages_https_only, disabled: true)
- expect(page).to have_button('Save')
- end
- end
-
- context 'HTTPS pages are disabled', :https_pages_disabled do
- it 'the setting is unavailable' do
- visit project_pages_path(project)
-
- expect(page).not_to have_field(:project_pages_https_only)
- expect(page).not_to have_content('Force HTTPS (requires valid certificates)')
- expect(page).to have_button('Save')
- end
- end
- end
-
- describe 'Remove page' do
- let(:project) { create :project, :repository }
-
- context 'when pages are deployed' do
- let(:pipeline) do
- commit_sha = project.commit('HEAD').sha
-
- project.ci_pipelines.create(
- ref: 'HEAD',
- sha: commit_sha,
- source: :push,
- protected: false
- )
- end
-
- let(:ci_build) do
- create(
- :ci_build,
- project: project,
- pipeline: pipeline,
- ref: 'HEAD')
- end
-
- let!(:artifact) do
- create(:ci_job_artifact, :archive, :correct_checksum,
- file: fixture_file_upload(File.join('spec/fixtures/pages.zip')), job: ci_build)
- end
-
- let!(:metadata) do
- create(:ci_job_artifact, :metadata,
- file: fixture_file_upload(File.join('spec/fixtures/pages.zip.meta')), job: ci_build)
- end
-
- before do
- result = Projects::UpdatePagesService.new(project, ci_build).execute
- expect(result[:status]).to eq(:success)
- expect(project).to be_pages_deployed
- end
-
- it 'removes the pages', :sidekiq_inline do
- visit project_pages_path(project)
-
- expect(page).to have_link('Remove pages')
-
- accept_confirm { click_link 'Remove pages' }
-
- expect(page).to have_content('Pages were scheduled for removal')
- expect(project.reload.pages_deployed?).to be_falsey
- end
- end
- end
-end
-
-RSpec.describe 'Pages', :js do
- include LetsEncryptHelpers
-
- context 'when editing normally' do
- include_examples 'pages settings editing'
- end
-
- context 'when letsencrypt support is enabled' do
- before do
- stub_lets_encrypt_settings
- end
-
- include_examples 'pages settings editing'
- end
-end
diff --git a/spec/features/projects/pipelines/pipeline_spec.rb b/spec/features/projects/pipelines/pipeline_spec.rb
index ac3566fbbdd..94800717677 100644
--- a/spec/features/projects/pipelines/pipeline_spec.rb
+++ b/spec/features/projects/pipelines/pipeline_spec.rb
@@ -14,7 +14,6 @@ RSpec.describe 'Pipeline', :js do
before do
sign_in(user)
project.add_role(user, role)
- stub_feature_flags(graphql_pipeline_details: false)
end
shared_context 'pipeline builds' do
@@ -57,7 +56,7 @@ RSpec.describe 'Pipeline', :js do
end
end
- describe 'GET /:project/pipelines/:id' do
+ describe 'GET /:project/-/pipelines/:id' do
include_context 'pipeline builds'
let(:group) { create(:group) }
@@ -69,7 +68,7 @@ RSpec.describe 'Pipeline', :js do
it 'shows the pipeline graph' do
visit_pipeline
- expect(page).to have_selector('.pipeline-visualization')
+ expect(page).to have_selector('.js-pipeline-graph')
expect(page).to have_content('Build')
expect(page).to have_content('Test')
expect(page).to have_content('Deploy')
@@ -625,20 +624,6 @@ RSpec.describe 'Pipeline', :js do
end
end
end
-
- context 'when FF dag_pipeline_tab is disabled' do
- before do
- stub_feature_flags(dag_pipeline_tab: false)
- visit_pipeline
- end
-
- it 'does not show DAG link' do
- expect(page).to have_link('Pipeline')
- expect(page).to have_link('Jobs')
- expect(page).not_to have_link('DAG')
- expect(page).to have_link('Failed Jobs')
- end
- end
end
context 'when user does not have access to read jobs' do
@@ -646,7 +631,7 @@ RSpec.describe 'Pipeline', :js do
project.update(public_builds: false)
end
- describe 'GET /:project/pipelines/:id' do
+ describe 'GET /:project/-/pipelines/:id' do
include_context 'pipeline builds'
let(:project) { create(:project, :repository) }
@@ -657,7 +642,7 @@ RSpec.describe 'Pipeline', :js do
end
it 'shows the pipeline graph' do
- expect(page).to have_selector('.pipeline-visualization')
+ expect(page).to have_selector('.js-pipeline-graph')
expect(page).to have_content('Build')
expect(page).to have_content('Test')
expect(page).to have_content('Deploy')
@@ -691,13 +676,13 @@ RSpec.describe 'Pipeline', :js do
downstream: downstream)
end
- describe 'GET /:project/pipelines/:id' do
+ describe 'GET /:project/-/pipelines/:id' do
before do
visit project_pipeline_path(project, pipeline)
end
it 'shows the pipeline with a bridge job' do
- expect(page).to have_selector('.pipeline-visualization')
+ expect(page).to have_selector('.js-pipeline-graph')
expect(page).to have_content('cross-build')
end
@@ -740,7 +725,7 @@ RSpec.describe 'Pipeline', :js do
end
end
- describe 'GET /:project/pipelines/:id/builds' do
+ describe 'GET /:project/-/pipelines/:id/builds' do
before do
visit builds_project_pipeline_path(project, pipeline)
end
@@ -767,9 +752,64 @@ RSpec.describe 'Pipeline', :js do
stage_idx: 2, pipeline: pipeline, project: project, resource_group: resource_group)
end
- describe 'GET /:project/pipelines/:id' do
+ describe 'GET /:project/-/pipelines/:id' do
subject { visit project_pipeline_path(project, pipeline) }
+ # remove when :graphql_pipeline_details flag is removed
+ # https://gitlab.com/gitlab-org/gitlab/-/issues/299112
+ context 'when :graphql_pipeline_details flag is off' do
+ before do
+ stub_feature_flags(graphql_pipeline_details: false)
+ stub_feature_flags(graphql_pipeline_details_users: false)
+ end
+
+ it 'shows deploy job as created' do
+ subject
+
+ within('.pipeline-header-container') do
+ expect(page).to have_content('pending')
+ end
+
+ within('.js-pipeline-graph') do
+ within '.stage-column:nth-child(1)' do
+ expect(page).to have_content('test')
+ expect(page).to have_css('.ci-status-icon-pending')
+ end
+
+ within '.stage-column:nth-child(2)' do
+ expect(page).to have_content('deploy')
+ expect(page).to have_css('.ci-status-icon-created')
+ end
+ end
+ end
+
+ context 'when test job succeeded' do
+ before do
+ test_job.success!
+ end
+
+ it 'shows deploy job as pending' do
+ subject
+
+ within('.pipeline-header-container') do
+ expect(page).to have_content('running')
+ end
+
+ within('.pipeline-graph') do
+ within '.stage-column:nth-child(1)' do
+ expect(page).to have_content('test')
+ expect(page).to have_css('.ci-status-icon-success')
+ end
+
+ within '.stage-column:nth-child(2)' do
+ expect(page).to have_content('deploy')
+ expect(page).to have_css('.ci-status-icon-pending')
+ end
+ end
+ end
+ end
+ end
+
it 'shows deploy job as created' do
subject
@@ -777,13 +817,13 @@ RSpec.describe 'Pipeline', :js do
expect(page).to have_content('pending')
end
- within('.pipeline-graph') do
- within '.stage-column:nth-child(1)' do
+ within('.js-pipeline-graph') do
+ within(all('[data-testid="stage-column"]')[0]) do
expect(page).to have_content('test')
expect(page).to have_css('.ci-status-icon-pending')
end
- within '.stage-column:nth-child(2)' do
+ within(all('[data-testid="stage-column"]')[1]) do
expect(page).to have_content('deploy')
expect(page).to have_css('.ci-status-icon-created')
end
@@ -802,13 +842,13 @@ RSpec.describe 'Pipeline', :js do
expect(page).to have_content('running')
end
- within('.pipeline-graph') do
- within '.stage-column:nth-child(1)' do
+ within('.js-pipeline-graph') do
+ within(all('[data-testid="stage-column"]')[0]) do
expect(page).to have_content('test')
expect(page).to have_css('.ci-status-icon-success')
end
- within '.stage-column:nth-child(2)' do
+ within(all('[data-testid="stage-column"]')[1]) do
expect(page).to have_content('deploy')
expect(page).to have_css('.ci-status-icon-pending')
end
@@ -831,14 +871,37 @@ RSpec.describe 'Pipeline', :js do
expect(page).to have_content('waiting')
end
- within('.pipeline-graph') do
- within '.stage-column:nth-child(2)' do
+ within('.js-pipeline-graph') do
+ within(all('[data-testid="stage-column"]')[1]) do
expect(page).to have_content('deploy')
expect(page).to have_css('.ci-status-icon-waiting-for-resource')
end
end
end
+ # remove when :graphql_pipeline_details flag is removed
+ # https://gitlab.com/gitlab-org/gitlab/-/issues/299112
+ context 'when :graphql_pipeline_details flag is off' do
+ before do
+ stub_feature_flags(graphql_pipeline_details: false)
+ stub_feature_flags(graphql_pipeline_details_users: false)
+ end
+ it 'shows deploy job as waiting for resource' do
+ subject
+
+ within('.pipeline-header-container') do
+ expect(page).to have_content('waiting')
+ end
+
+ within('.pipeline-graph') do
+ within '.stage-column:nth-child(2)' do
+ expect(page).to have_content('deploy')
+ expect(page).to have_css('.ci-status-icon-waiting-for-resource')
+ end
+ end
+ end
+ end
+
context 'when resource is released from another job' do
before do
another_job.success!
@@ -851,19 +914,86 @@ RSpec.describe 'Pipeline', :js do
expect(page).to have_content('running')
end
- within('.pipeline-graph') do
- within '.stage-column:nth-child(2)' do
+ within('.js-pipeline-graph') do
+ within(all('[data-testid="stage-column"]')[1]) do
expect(page).to have_content('deploy')
expect(page).to have_css('.ci-status-icon-pending')
end
end
end
+
+ # remove when :graphql_pipeline_details flag is removed
+ # https://gitlab.com/gitlab-org/gitlab/-/issues/299112
+ context 'when :graphql_pipeline_details flag is off' do
+ before do
+ stub_feature_flags(graphql_pipeline_details: false)
+ stub_feature_flags(graphql_pipeline_details_users: false)
+ end
+ it 'shows deploy job as pending' do
+ subject
+
+ within('.pipeline-header-container') do
+ expect(page).to have_content('running')
+ end
+
+ within('.pipeline-graph') do
+ within '.stage-column:nth-child(2)' do
+ expect(page).to have_content('deploy')
+ expect(page).to have_css('.ci-status-icon-pending')
+ end
+ end
+ end
+ end
+ end
+
+ context 'when deploy job is a bridge to trigger a downstream pipeline' do
+ let!(:deploy_job) do
+ create(:ci_bridge, :created, stage: 'deploy', name: 'deploy',
+ stage_idx: 2, pipeline: pipeline, project: project, resource_group: resource_group)
+ end
+
+ it 'shows deploy job as waiting for resource' do
+ subject
+
+ within('.pipeline-header-container') do
+ expect(page).to have_content('waiting')
+ end
+
+ within('.js-pipeline-graph') do
+ within(all('[data-testid="stage-column"]')[1]) do
+ expect(page).to have_content('deploy')
+ expect(page).to have_css('.ci-status-icon-waiting-for-resource')
+ end
+ end
+ end
+ end
+
+ context 'when deploy job is a bridge to trigger a downstream pipeline' do
+ let!(:deploy_job) do
+ create(:ci_bridge, :created, stage: 'deploy', name: 'deploy',
+ stage_idx: 2, pipeline: pipeline, project: project, resource_group: resource_group)
+ end
+
+ it 'shows deploy job as waiting for resource' do
+ subject
+
+ within('.pipeline-header-container') do
+ expect(page).to have_content('waiting')
+ end
+
+ within('.js-pipeline-graph') do
+ within(all('[data-testid="stage-column"]')[1]) do
+ expect(page).to have_content('deploy')
+ expect(page).to have_css('.ci-status-icon-waiting-for-resource')
+ end
+ end
+ end
end
end
end
end
- describe 'GET /:project/pipelines/:id/builds' do
+ describe 'GET /:project/-/pipelines/:id/builds' do
include_context 'pipeline builds'
let(:project) { create(:project, :repository) }
@@ -965,7 +1095,7 @@ RSpec.describe 'Pipeline', :js do
end
end
- describe 'GET /:project/pipelines/:id/failures' do
+ describe 'GET /:project/-/pipelines/:id/failures' do
let(:pipeline) { create(:ci_pipeline, project: project, ref: 'master', sha: '1234') }
let(:pipeline_failures_page) { failures_project_pipeline_path(project, pipeline) }
let!(:failed_build) { create(:ci_build, :failed, pipeline: pipeline) }
@@ -1078,12 +1208,29 @@ RSpec.describe 'Pipeline', :js do
expect(current_path).to eq(pipeline_path(pipeline))
expect(page).not_to have_content('Failed Jobs')
- expect(page).to have_selector('.pipeline-visualization')
+ expect(page).to have_selector('.js-pipeline-graph')
+ end
+
+ # remove when :graphql_pipeline_details flag is removed
+ # https://gitlab.com/gitlab-org/gitlab/-/issues/299112
+ context 'when :graphql_pipeline_details flag is off' do
+ before do
+ stub_feature_flags(graphql_pipeline_details: false)
+ stub_feature_flags(graphql_pipeline_details_users: false)
+ end
+
+ it 'displays the pipeline graph' do
+ subject
+
+ 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
- describe 'GET /:project/pipelines/:id/dag' do
+ describe 'GET /:project/-/pipelines/:id/dag' do
include_context 'pipeline builds'
let(:project) { create(:project, :repository) }
diff --git a/spec/features/projects/pipelines/pipelines_spec.rb b/spec/features/projects/pipelines/pipelines_spec.rb
index 450524b8d70..6421d3db2cd 100644
--- a/spec/features/projects/pipelines/pipelines_spec.rb
+++ b/spec/features/projects/pipelines/pipelines_spec.rb
@@ -13,11 +13,13 @@ RSpec.describe 'Pipelines', :js do
before do
sign_in(user)
stub_feature_flags(graphql_pipeline_details: false)
+ stub_feature_flags(graphql_pipeline_details_users: false)
+
project.add_developer(user)
project.update!(auto_devops_attributes: { enabled: false })
end
- describe 'GET /:project/pipelines' do
+ describe 'GET /:project/-/pipelines' do
let(:project) { create(:project, :repository) }
let!(:pipeline) do
@@ -287,23 +289,23 @@ RSpec.describe 'Pipelines', :js do
end
it 'has a dropdown with play button' do
- expect(page).to have_selector('.dropdown-new.btn.btn-default .icon-play')
+ expect(page).to have_selector('[data-testid="pipelines-manual-actions-dropdown"] [data-testid="play-icon"]')
end
it 'has link to the manual action' do
- find('.js-pipeline-dropdown-manual-actions').click
+ find('[data-testid="pipelines-manual-actions-dropdown"]').click
expect(page).to have_button('manual build')
end
context 'when manual action was played' do
before do
- find('.js-pipeline-dropdown-manual-actions').click
+ find('[data-testid="pipelines-manual-actions-dropdown"]').click
click_button('manual build')
end
it 'enqueues manual action job' do
- expect(page).to have_selector('.js-pipeline-dropdown-manual-actions:disabled')
+ expect(page).to have_selector('[data-testid="pipelines-manual-actions-dropdown"] .gl-dropdown-toggle:disabled')
end
end
end
@@ -321,11 +323,11 @@ RSpec.describe 'Pipelines', :js do
end
it 'has a dropdown for actionable jobs' do
- expect(page).to have_selector('.dropdown-new.btn.btn-default .icon-play')
+ expect(page).to have_selector('[data-testid="pipelines-manual-actions-dropdown"] [data-testid="play-icon"]')
end
it "has link to the delayed job's action" do
- find('.js-pipeline-dropdown-manual-actions').click
+ find('[data-testid="pipelines-manual-actions-dropdown"]').click
time_diff = [0, delayed_job.scheduled_at - Time.now].max
expect(page).to have_button('delayed job 1')
@@ -341,7 +343,7 @@ RSpec.describe 'Pipelines', :js do
end
it "shows 00:00:00 as the remaining time" do
- find('.js-pipeline-dropdown-manual-actions').click
+ find('[data-testid="pipelines-manual-actions-dropdown"]').click
expect(page).to have_content("00:00:00")
end
@@ -349,7 +351,7 @@ RSpec.describe 'Pipelines', :js do
context 'when user played a delayed job immediately' do
before do
- find('.js-pipeline-dropdown-manual-actions').click
+ find('[data-testid="pipelines-manual-actions-dropdown"]').click
page.accept_confirm { click_button('delayed job 1') }
wait_for_requests
end
@@ -517,56 +519,75 @@ RSpec.describe 'Pipelines', :js do
end
end
- context 'mini pipeline graph' do
- let!(:build) do
- create(:ci_build, :pending, pipeline: pipeline,
- stage: 'build',
- name: 'build')
- end
-
- before do
- visit_project_pipelines
- end
+ shared_examples 'mini pipeline renders' do |ci_mini_pipeline_gl_dropdown_enabled|
+ context 'mini pipeline graph' do
+ let!(:build) do
+ create(:ci_build, :pending, pipeline: pipeline,
+ stage: 'build',
+ name: 'build')
+ end
- it 'renders a mini pipeline graph' do
- expect(page).to have_selector('[data-testid="widget-mini-pipeline-graph"]')
- expect(page).to have_selector('.js-builds-dropdown-button')
- end
+ before do
+ stub_feature_flags(ci_mini_pipeline_gl_dropdown: ci_mini_pipeline_gl_dropdown_enabled)
+ visit_project_pipelines
+ end
- context 'when clicking a stage badge' do
- it 'opens a dropdown' do
- find('.js-builds-dropdown-button').click
+ let_it_be(:dropdown_toggle_selector) do
+ if ci_mini_pipeline_gl_dropdown_enabled
+ '[data-testid="mini-pipeline-graph-dropdown"] .dropdown-toggle'
+ else
+ '[data-testid="mini-pipeline-graph-dropdown-toggle"]'
+ end
+ end
- expect(page).to have_link build.name
+ it 'renders a mini pipeline graph' do
+ expect(page).to have_selector('[data-testid="widget-mini-pipeline-graph"]')
+ expect(page).to have_selector(dropdown_toggle_selector)
end
- it 'is possible to cancel pending build' do
- find('.js-builds-dropdown-button').click
- find('.js-ci-action').click
- wait_for_requests
+ context 'when clicking a stage badge' do
+ it 'opens a dropdown' do
+ find(dropdown_toggle_selector).click
- expect(build.reload).to be_canceled
- end
- end
+ expect(page).to have_link build.name
+ end
- context 'for a failed pipeline' do
- let!(:build) do
- create(:ci_build, :failed, pipeline: pipeline,
- stage: 'build',
- name: 'build')
+ it 'is possible to cancel pending build' do
+ find(dropdown_toggle_selector).click
+ find('.js-ci-action').click
+ wait_for_requests
+
+ expect(build.reload).to be_canceled
+ end
end
- it 'displays the failure reason' do
- find('.js-builds-dropdown-button').click
+ context 'for a failed pipeline' do
+ let!(:build) do
+ create(:ci_build, :failed, pipeline: pipeline,
+ stage: 'build',
+ name: 'build')
+ end
+
+ it 'displays the failure reason' do
+ find(dropdown_toggle_selector).click
- within('.js-builds-dropdown-list') do
- build_element = page.find('.mini-pipeline-graph-dropdown-item')
- expect(build_element['title']).to eq('build - failed - (unknown failure)')
+ within('.js-builds-dropdown-list') do
+ build_element = page.find('.mini-pipeline-graph-dropdown-item')
+ expect(build_element['title']).to eq('build - failed - (unknown failure)')
+ end
end
end
end
end
+ context 'with ci_mini_pipeline_gl_dropdown disabled' do
+ it_behaves_like "mini pipeline renders", false
+ end
+
+ context 'with ci_mini_pipeline_gl_dropdown enabled' do
+ it_behaves_like "mini pipeline renders", true
+ end
+
context 'with pagination' do
before do
allow(Ci::Pipeline).to receive(:default_per_page).and_return(1)
@@ -597,7 +618,7 @@ RSpec.describe 'Pipelines', :js do
end
end
- describe 'GET /:project/pipelines/show' do
+ describe 'GET /:project/-/pipelines/show' do
let(:project) { create(:project, :repository) }
let(:pipeline) do
@@ -649,7 +670,7 @@ RSpec.describe 'Pipelines', :js do
end
end
- describe 'POST /:project/pipelines' do
+ describe 'POST /:project/-/pipelines' do
let(:project) { create(:project, :repository) }
before do
diff --git a/spec/features/projects/services/user_activates_slack_slash_command_spec.rb b/spec/features/projects/services/user_activates_slack_slash_command_spec.rb
index 3994f55caee..4dfd4416eeb 100644
--- a/spec/features/projects/services/user_activates_slack_slash_command_spec.rb
+++ b/spec/features/projects/services/user_activates_slack_slash_command_spec.rb
@@ -40,4 +40,8 @@ RSpec.describe 'Slack slash commands', :js do
value = find_field('url').value
expect(value).to match("api/v4/projects/#{project.id}/services/slack_slash_commands/trigger")
end
+
+ it 'shows help content' do
+ expect(page).to have_content('This service allows users to perform common operations on this project by entering slash commands in Slack.')
+ end
end
diff --git a/spec/features/projects/settings/pipelines_settings_spec.rb b/spec/features/projects/settings/pipelines_settings_spec.rb
index c087237fd7c..39c4315bf0f 100644
--- a/spec/features/projects/settings/pipelines_settings_spec.rb
+++ b/spec/features/projects/settings/pipelines_settings_spec.rb
@@ -46,7 +46,7 @@ RSpec.describe "Projects > Settings > Pipelines settings" do
it 'updates auto_cancel_pending_pipelines' do
visit project_settings_ci_cd_path(project)
- page.check('Auto-cancel redundant, pending pipelines')
+ page.check('Auto-cancel redundant pipelines')
page.within '#js-general-pipeline-settings' do
click_on 'Save changes'
end
diff --git a/spec/features/projects/settings/project_settings_spec.rb b/spec/features/projects/settings/project_settings_spec.rb
index 7b2b5594c22..cd1c9ecde9c 100644
--- a/spec/features/projects/settings/project_settings_spec.rb
+++ b/spec/features/projects/settings/project_settings_spec.rb
@@ -6,7 +6,7 @@ RSpec.describe 'Projects settings' do
let_it_be(:project) { create(:project) }
let(:user) { project.owner }
let(:panel) { find('.general-settings', match: :first) }
- let(:button) { panel.find('.btn.js-settings-toggle') }
+ let(:button) { panel.find('.btn.gl-button.js-settings-toggle') }
let(:title) { panel.find('.settings-title') }
before do
@@ -39,7 +39,7 @@ RSpec.describe 'Projects settings' do
visit edit_project_path(project)
forking_enabled_input = find('input[name="project[project_feature_attributes][forking_access_level]"]', visible: :hidden)
- forking_enabled_button = find('input[name="project[project_feature_attributes][forking_access_level]"] + label > button')
+ forking_enabled_button = find('[data-for="project[project_feature_attributes][forking_access_level]"] .gl-toggle')
expect(forking_enabled_input.value).to eq('20')
diff --git a/spec/features/projects/settings/registry_settings_spec.rb b/spec/features/projects/settings/registry_settings_spec.rb
index 2b03ecf5af1..6e4082d1391 100644
--- a/spec/features/projects/settings/registry_settings_spec.rb
+++ b/spec/features/projects/settings/registry_settings_spec.rb
@@ -39,7 +39,7 @@ RSpec.describe 'Project > Settings > CI/CD > Container registry tag expiration p
select('7 days', from: 'Remove tags older than:')
fill_in('Remove tags matching:', with: '.*-production')
- submit_button = find('.btn.btn-success')
+ submit_button = find('.btn.gl-button.btn-success')
expect(submit_button).not_to be_disabled
submit_button.click
end
@@ -53,7 +53,7 @@ RSpec.describe 'Project > Settings > CI/CD > Container registry tag expiration p
within '#js-registry-policies' do
fill_in('Remove tags matching:', with: '*-production')
- submit_button = find('.btn.btn-success')
+ submit_button = find('.btn.gl-button.btn-success')
expect(submit_button).not_to be_disabled
submit_button.click
end
diff --git a/spec/features/projects/settings/repository_settings_spec.rb b/spec/features/projects/settings/repository_settings_spec.rb
index 3e520142117..2f257d299d8 100644
--- a/spec/features/projects/settings/repository_settings_spec.rb
+++ b/spec/features/projects/settings/repository_settings_spec.rb
@@ -63,7 +63,7 @@ RSpec.describe 'Projects > Settings > Repository settings' do
click_button 'Add key'
expect(page).to have_content('new_deploy_key')
- expect(page).to have_content('Write access allowed')
+ expect(page).to have_content('Grant write permissions to this key')
end
it 'edit an existing deploy key' do
@@ -77,7 +77,7 @@ RSpec.describe 'Projects > Settings > Repository settings' do
click_button 'Save changes'
expect(page).to have_content('updated_deploy_key')
- expect(page).to have_content('Write access allowed')
+ expect(page).to have_content('Grant write permissions to this key')
end
it 'edit an existing public deploy key to be writable' do
@@ -90,7 +90,7 @@ RSpec.describe 'Projects > Settings > Repository settings' do
click_button 'Save changes'
expect(page).to have_content('public_deploy_key')
- expect(page).to have_content('Write access allowed')
+ expect(page).to have_content('Grant write permissions to this key')
end
it 'edit a deploy key from projects user has access to' do
diff --git a/spec/features/projects/settings/user_manages_merge_requests_settings_spec.rb b/spec/features/projects/settings/user_manages_merge_requests_settings_spec.rb
index e97e4a2030a..e8e32d93f7b 100644
--- a/spec/features/projects/settings/user_manages_merge_requests_settings_spec.rb
+++ b/spec/features/projects/settings/user_manages_merge_requests_settings_spec.rb
@@ -51,7 +51,7 @@ RSpec.describe 'Projects > Settings > User manages merge request settings' do
expect(page).to have_content 'All discussions must be resolved'
within('.sharing-permissions-form') do
- find('.project-feature-controls[data-for="project[project_feature_attributes][merge_requests_access_level]"] .project-feature-toggle').click
+ find('.project-feature-controls[data-for="project[project_feature_attributes][merge_requests_access_level]"] .gl-toggle').click
find('input[value="Save changes"]').send_keys(:return)
end
@@ -71,7 +71,7 @@ RSpec.describe 'Projects > Settings > User manages merge request settings' do
expect(page).to have_content 'All discussions must be resolved'
within('.sharing-permissions-form') do
- find('.project-feature-controls[data-for="project[project_feature_attributes][builds_access_level]"] .project-feature-toggle').click
+ find('.project-feature-controls[data-for="project[project_feature_attributes][builds_access_level]"] .gl-toggle').click
find('input[value="Save changes"]').send_keys(:return)
end
@@ -92,7 +92,7 @@ RSpec.describe 'Projects > Settings > User manages merge request settings' do
expect(page).not_to have_content 'All discussions must be resolved'
within('.sharing-permissions-form') do
- find('.project-feature-controls[data-for="project[project_feature_attributes][merge_requests_access_level]"] .project-feature-toggle').click
+ find('.project-feature-controls[data-for="project[project_feature_attributes][merge_requests_access_level]"] .gl-toggle').click
find('input[value="Save changes"]').send_keys(:return)
end
diff --git a/spec/features/projects/settings/user_manages_project_members_spec.rb b/spec/features/projects/settings/user_manages_project_members_spec.rb
index 726b8fb6840..0d22da34b91 100644
--- a/spec/features/projects/settings/user_manages_project_members_spec.rb
+++ b/spec/features/projects/settings/user_manages_project_members_spec.rb
@@ -3,6 +3,9 @@
require 'spec_helper'
RSpec.describe 'Projects > Settings > User manages project members' do
+ include Spec::Support::Helpers::Features::MembersHelpers
+ include Select2Helper
+
let(:group) { create(:group, name: 'OpenSource') }
let(:project) { create(:project) }
let(:project2) { create(:project) }
@@ -16,62 +19,123 @@ RSpec.describe 'Projects > Settings > User manages project members' do
sign_in(user)
end
- it 'cancels a team member', :js do
- visit(project_project_members_path(project))
+ context 'when `vue_project_members_list` feature flag is enabled' do
+ it 'cancels a team member', :js do
+ visit(project_project_members_path(project))
+
+ page.within find_member_row(user_dmitriy) do
+ click_button 'Remove member'
+ end
+
+ page.within('[role="dialog"]') do
+ expect(page).to have_unchecked_field 'Also unassign this user from related issues and merge requests'
+ click_button('Remove member')
+ end
+
+ visit(project_project_members_path(project))
+
+ expect(members_table).not_to have_content(user_dmitriy.name)
+ expect(members_table).not_to have_content(user_dmitriy.username)
+ end
+
+ it 'imports a team from another project', :js do
+ stub_feature_flags(invite_members_group_modal: false)
+
+ project2.add_maintainer(user)
+ project2.add_reporter(user_mike)
+
+ visit(project_project_members_path(project))
- project_member = project.project_members.find_by(user_id: user_dmitriy.id)
+ page.within('.invite-users-form') do
+ click_link('Import')
+ end
- page.within("#project_member_#{project_member.id}") do
- # Open modal
- click_on('Remove user from project')
+ select2(project2.id, from: '#source_project_id')
+ click_button('Import project members')
+
+ expect(find_member_row(user_mike)).to have_content('Reporter')
end
- expect(page).to have_unchecked_field 'Also unassign this user from related issues and merge requests'
+ it 'shows all members of project shared group', :js do
+ group.add_owner(user)
+ group.add_developer(user_dmitriy)
+
+ share_link = project.project_group_links.new(group_access: Gitlab::Access::MAINTAINER)
+ share_link.group_id = group.id
+ share_link.save!
- click_on('Remove member')
+ visit(project_project_members_path(project))
- visit(project_project_members_path(project))
+ click_link 'Groups'
- expect(page).not_to have_content(user_dmitriy.name)
- expect(page).not_to have_content(user_dmitriy.username)
+ expect(find_group_row(group)).to have_content('Maintainer')
+ end
end
- it 'imports a team from another project' do
- project2.add_maintainer(user)
- project2.add_reporter(user_mike)
+ context 'when `vue_project_members_list` feature flag is disabled' do
+ before do
+ stub_feature_flags(vue_project_members_list: false)
+ end
+
+ it 'cancels a team member', :js do
+ visit(project_project_members_path(project))
+
+ project_member = project.project_members.find_by(user_id: user_dmitriy.id)
+
+ page.within("#project_member_#{project_member.id}") do
+ # Open modal
+ click_on('Remove user from project')
+ end
+
+ expect(page).to have_unchecked_field 'Also unassign this user from related issues and merge requests'
+
+ click_on('Remove member')
- visit(project_project_members_path(project))
+ visit(project_project_members_path(project))
- page.within('.invite-users-form') do
- click_link('Import')
+ expect(page).not_to have_content(user_dmitriy.name)
+ expect(page).not_to have_content(user_dmitriy.username)
end
- select(project2.full_name, from: 'source_project_id')
- click_button('Import')
+ it 'imports a team from another project' do
+ stub_feature_flags(invite_members_group_modal: false)
- project_member = project.project_members.find_by(user_id: user_mike.id)
+ project2.add_maintainer(user)
+ project2.add_reporter(user_mike)
- page.within("#project_member_#{project_member.id}") do
- expect(page).to have_content('Mike')
- expect(page).to have_content('Reporter')
+ visit(project_project_members_path(project))
+
+ page.within('.invite-users-form') do
+ click_link('Import')
+ end
+
+ select(project2.full_name, from: 'source_project_id')
+ click_button('Import')
+
+ project_member = project.project_members.find_by(user_id: user_mike.id)
+
+ page.within("#project_member_#{project_member.id}") do
+ expect(page).to have_content('Mike')
+ expect(page).to have_content('Reporter')
+ end
end
- end
- it 'shows all members of project shared group', :js do
- group.add_owner(user)
- group.add_developer(user_dmitriy)
+ it 'shows all members of project shared group', :js do
+ group.add_owner(user)
+ group.add_developer(user_dmitriy)
- share_link = project.project_group_links.new(group_access: Gitlab::Access::MAINTAINER)
- share_link.group_id = group.id
- share_link.save!
+ share_link = project.project_group_links.new(group_access: Gitlab::Access::MAINTAINER)
+ share_link.group_id = group.id
+ share_link.save!
- visit(project_project_members_path(project))
+ visit(project_project_members_path(project))
- click_link 'Groups'
+ click_link 'Groups'
- page.within('[data-testid="project-member-groups"]') do
- expect(page).to have_content('OpenSource')
- expect(first('.group_member')).to have_content('Maintainer')
+ page.within('[data-testid="project-member-groups"]') do
+ expect(page).to have_content('OpenSource')
+ expect(first('.group_member')).to have_content('Maintainer')
+ end
end
end
end
diff --git a/spec/features/projects/settings/visibility_settings_spec.rb b/spec/features/projects/settings/visibility_settings_spec.rb
index 6cecbbdb3d0..becb30c02b7 100644
--- a/spec/features/projects/settings/visibility_settings_spec.rb
+++ b/spec/features/projects/settings/visibility_settings_spec.rb
@@ -30,7 +30,7 @@ RSpec.describe 'Projects > Settings > Visibility settings', :js do
context 'merge requests select' do
it 'hides merge requests section' do
- find('.project-feature-controls[data-for="project[project_feature_attributes][merge_requests_access_level]"] .project-feature-toggle').click
+ find('.project-feature-controls[data-for="project[project_feature_attributes][merge_requests_access_level]"] .gl-toggle').click
expect(page).to have_selector('.merge-requests-feature', visible: false)
end
@@ -46,7 +46,7 @@ RSpec.describe 'Projects > Settings > Visibility settings', :js do
context 'builds select' do
it 'hides builds select section' do
- find('.project-feature-controls[data-for="project[project_feature_attributes][builds_access_level]"] .project-feature-toggle').click
+ find('.project-feature-controls[data-for="project[project_feature_attributes][builds_access_level]"] .gl-toggle').click
expect(page).to have_selector('.builds-feature', visible: false)
end
diff --git a/spec/features/projects/show/user_manages_notifications_spec.rb b/spec/features/projects/show/user_manages_notifications_spec.rb
index d444ea27d35..5f7d9b0963b 100644
--- a/spec/features/projects/show/user_manages_notifications_spec.rb
+++ b/spec/features/projects/show/user_manages_notifications_spec.rb
@@ -6,6 +6,7 @@ RSpec.describe 'Projects > Show > User manages notifications', :js do
let(:project) { create(:project, :public, :repository) }
before do
+ stub_feature_flags(vue_notification_dropdown: false)
sign_in(project.owner)
end
diff --git a/spec/features/projects/show/user_sees_git_instructions_spec.rb b/spec/features/projects/show/user_sees_git_instructions_spec.rb
index febdb70de86..e6157887c12 100644
--- a/spec/features/projects/show/user_sees_git_instructions_spec.rb
+++ b/spec/features/projects/show/user_sees_git_instructions_spec.rb
@@ -5,6 +5,13 @@ require 'spec_helper'
RSpec.describe 'Projects > Show > User sees Git instructions' do
let_it_be(:user) { create(:user) }
+ before do
+ # Reset user notification settings between examples to prevent
+ # validation failure on NotificationSetting.
+ # See https://gitlab.com/gitlab-org/gitlab/-/issues/299822#note_492817174
+ user.notification_settings.reset
+ end
+
shared_examples_for 'redirects to the sign in page' do
it 'redirects to the sign in page' do
expect(current_path).to eq(new_user_session_path)
diff --git a/spec/features/projects/terraform_spec.rb b/spec/features/projects/terraform_spec.rb
index dfa4dad8490..55b906c2bc5 100644
--- a/spec/features/projects/terraform_spec.rb
+++ b/spec/features/projects/terraform_spec.rb
@@ -68,7 +68,7 @@ RSpec.describe 'Terraform', :js do
fill_in "terraform-state-remove-input-#{additional_state.name}", with: additional_state.name
click_button 'Remove'
- expect(page).not_to have_content(additional_state.name)
+ expect(page).to have_content("#{additional_state.name} successfully removed")
expect { additional_state.reload }.to raise_error ActiveRecord::RecordNotFound
end
end
diff --git a/spec/features/projects/user_creates_project_spec.rb b/spec/features/projects/user_creates_project_spec.rb
index feb5f348256..aff3022bd4e 100644
--- a/spec/features/projects/user_creates_project_spec.rb
+++ b/spec/features/projects/user_creates_project_spec.rb
@@ -8,6 +8,8 @@ RSpec.describe 'User creates a project', :js do
before do
sign_in(user)
create(:personal_key, user: user)
+
+ stub_experiments(new_project_readme: :candidate)
end
it 'creates a new project' do
@@ -16,6 +18,10 @@ RSpec.describe 'User creates a project', :js do
find('[data-qa-selector="blank_project_link"]').click
fill_in(:project_name, with: 'Empty')
+ # part of the new_project_readme experiment
+ expect(page).to have_checked_field 'Initialize repository with a README'
+ uncheck 'Initialize repository with a README'
+
page.within('#content-body') do
click_button('Create project')
end
diff --git a/spec/features/projects/user_sees_user_popover_spec.rb b/spec/features/projects/user_sees_user_popover_spec.rb
index 9cfc6234969..52e65deae3b 100644
--- a/spec/features/projects/user_sees_user_popover_spec.rb
+++ b/spec/features/projects/user_sees_user_popover_spec.rb
@@ -38,8 +38,6 @@ RSpec.describe 'User sees user popover', :js do
it "displays user popover in system note" do
add_note("/assign @#{user.username}")
- wait_for_requests
-
find('.system-note-message .js-user-link').hover
page.within(popover_selector) do
diff --git a/spec/features/projects/user_uses_shortcuts_spec.rb b/spec/features/projects/user_uses_shortcuts_spec.rb
index 8fa5f741a95..13ae035e8ef 100644
--- a/spec/features/projects/user_uses_shortcuts_spec.rb
+++ b/spec/features/projects/user_uses_shortcuts_spec.rb
@@ -27,14 +27,13 @@ RSpec.describe 'User uses shortcuts', :js do
open_modal_shortcut_keys
- # modal-shortcuts still in the DOM, but hidden
- expect(find('#modal-shortcuts', visible: false)).not_to be_visible
+ expect(page).not_to have_selector('[data-testid="modal-shortcuts"]')
page.refresh
open_modal_shortcut_keys
# after reload, shortcuts modal doesn't exist at all until we add it
- expect(page).not_to have_selector('#modal-shortcuts')
+ expect(page).not_to have_selector('[data-testid="modal-shortcuts"]')
end
it 're-enables shortcuts' do
@@ -47,7 +46,7 @@ RSpec.describe 'User uses shortcuts', :js do
close_modal
open_modal_shortcut_keys
- expect(find('#modal-shortcuts')).to be_visible
+ expect(find('[data-testid="modal-shortcuts"]')).to be_visible
end
def open_modal_shortcut_keys