summaryrefslogtreecommitdiff
path: root/spec/features
diff options
context:
space:
mode:
authorRobert Speicher <rspeicher@gmail.com>2016-06-15 13:14:01 -0400
committerRobert Speicher <rspeicher@gmail.com>2016-06-15 13:14:01 -0400
commitd44e89144b62109900cef2a75a6974490bd2a2eb (patch)
tree4b75495bd856ef4317c1e77c76ee97d61637961e /spec/features
parentb6a703e71546acc482ba58e1bba48f0eb747653c (diff)
parentaf493ccf3658c9f8b1266a880eb1798fe30cb324 (diff)
downloadgitlab-ce-d44e89144b62109900cef2a75a6974490bd2a2eb.tar.gz
Merge branch 'master' into 8-9-stable
Diffstat (limited to 'spec/features')
-rw-r--r--spec/features/admin/admin_hooks_spec.rb4
-rw-r--r--spec/features/atom/dashboard_issues_spec.rb51
-rw-r--r--spec/features/builds_spec.rb36
-rw-r--r--spec/features/environments_spec.rb160
-rw-r--r--spec/features/groups/members/owner_manages_access_requests_spec.rb48
-rw-r--r--spec/features/groups/members/user_requests_access_spec.rb48
-rw-r--r--spec/features/issues/filter_by_labels_spec.rb20
-rw-r--r--spec/features/issues/filter_issues_spec.rb23
-rw-r--r--spec/features/issues/move_spec.rb16
-rw-r--r--spec/features/issues/todo_spec.rb33
-rw-r--r--spec/features/issues_spec.rb41
-rw-r--r--spec/features/projects/members/master_manages_access_requests_spec.rb47
-rw-r--r--spec/features/projects/members/user_requests_access_spec.rb54
-rw-r--r--spec/features/security/project/public_access_spec.rb43
-rw-r--r--spec/features/u2f_spec.rb57
15 files changed, 617 insertions, 64 deletions
diff --git a/spec/features/admin/admin_hooks_spec.rb b/spec/features/admin/admin_hooks_spec.rb
index 7265cdac7a7..31633817d53 100644
--- a/spec/features/admin/admin_hooks_spec.rb
+++ b/spec/features/admin/admin_hooks_spec.rb
@@ -12,9 +12,11 @@ describe "Admin::Hooks", feature: true do
describe "GET /admin/hooks" do
it "should be ok" do
visit admin_root_path
- page.within ".sidebar-wrapper" do
+
+ page.within ".layout-nav" do
click_on "Hooks"
end
+
expect(current_path).to eq(admin_hooks_path)
end
diff --git a/spec/features/atom/dashboard_issues_spec.rb b/spec/features/atom/dashboard_issues_spec.rb
index b710cb3c72f..4dd9548cfc5 100644
--- a/spec/features/atom/dashboard_issues_spec.rb
+++ b/spec/features/atom/dashboard_issues_spec.rb
@@ -5,8 +5,6 @@ describe "Dashboard Issues Feed", feature: true do
let!(:user) { create(:user) }
let!(:project1) { create(:project) }
let!(:project2) { create(:project) }
- let!(:issue1) { create(:issue, author: user, assignee: user, project: project1) }
- let!(:issue2) { create(:issue, author: user, assignee: user, project: project2) }
before do
project1.team << [user, :master]
@@ -14,16 +12,51 @@ describe "Dashboard Issues Feed", feature: true do
end
describe "atom feed" do
- it "should render atom feed via private token" do
+ it "renders atom feed via private token" do
visit issues_dashboard_path(:atom, private_token: user.private_token)
- expect(response_headers['Content-Type']).
- to have_content('application/atom+xml')
+ expect(response_headers['Content-Type']).to have_content('application/atom+xml')
expect(body).to have_selector('title', text: "#{user.name} issues")
- expect(body).to have_selector('author email', text: issue1.author_email)
- expect(body).to have_selector('entry summary', text: issue1.title)
- expect(body).to have_selector('author email', text: issue2.author_email)
- expect(body).to have_selector('entry summary', text: issue2.title)
+ end
+
+ context "issue with basic fields" do
+ let!(:issue2) { create(:issue, author: user, assignee: user, project: project2, description: 'test desc') }
+
+ it "renders issue fields" do
+ visit issues_dashboard_path(:atom, private_token: user.private_token)
+
+ entry = find(:xpath, "//feed/entry[contains(summary/text(),'#{issue2.title}')]")
+
+ expect(entry).to be_present
+ expect(entry).to have_selector('author email', text: issue2.author_email)
+ expect(entry).to have_selector('assignee email', text: issue2.author_email)
+ expect(entry).not_to have_selector('labels')
+ expect(entry).not_to have_selector('milestone')
+ expect(entry).to have_selector('description', text: issue2.description)
+ end
+ end
+
+ context "issue with label and milestone" do
+ let!(:milestone1) { create(:milestone, project: project1, title: 'v1') }
+ let!(:label1) { create(:label, project: project1, title: 'label1') }
+ let!(:issue1) { create(:issue, author: user, assignee: user, project: project1, milestone: milestone1) }
+
+ before do
+ issue1.labels << label1
+ end
+
+ it "renders issue label and milestone info" do
+ visit issues_dashboard_path(:atom, private_token: user.private_token)
+
+ entry = find(:xpath, "//feed/entry[contains(summary/text(),'#{issue1.title}')]")
+
+ expect(entry).to be_present
+ expect(entry).to have_selector('author email', text: issue1.author_email)
+ expect(entry).to have_selector('assignee email', text: issue1.author_email)
+ expect(entry).to have_selector('labels label', text: label1.title)
+ expect(entry).to have_selector('milestone', text: milestone1.title)
+ expect(entry).not_to have_selector('description')
+ end
end
end
end
diff --git a/spec/features/builds_spec.rb b/spec/features/builds_spec.rb
index b8ecc356b4d..16832c297ac 100644
--- a/spec/features/builds_spec.rb
+++ b/spec/features/builds_spec.rb
@@ -97,6 +97,42 @@ describe "Builds" do
end
end
+ context 'Artifacts expire date' do
+ before do
+ @build.update_attributes(artifacts_file: artifacts_file, artifacts_expire_at: expire_at)
+ visit namespace_project_build_path(@project.namespace, @project, @build)
+ end
+
+ context 'no expire date defined' do
+ let(:expire_at) { nil }
+
+ it 'does not have the Keep button' do
+ expect(page).not_to have_content 'Keep'
+ end
+ end
+
+ context 'when expire date is defined' do
+ let(:expire_at) { Time.now + 7.days }
+
+ it 'keeps artifacts when Keep button is clicked' do
+ expect(page).to have_content 'The artifacts will be removed'
+ click_link 'Keep'
+
+ expect(page).not_to have_link 'Keep'
+ expect(page).not_to have_content 'The artifacts will be removed'
+ end
+ end
+
+ context 'when artifacts expired' do
+ let(:expire_at) { Time.now - 7.days }
+
+ it 'does not have the Keep button' do
+ expect(page).to have_content 'The artifacts were removed'
+ expect(page).not_to have_link 'Keep'
+ end
+ end
+ end
+
context 'Build raw trace' do
before do
@build.run!
diff --git a/spec/features/environments_spec.rb b/spec/features/environments_spec.rb
new file mode 100644
index 00000000000..40fea5211e9
--- /dev/null
+++ b/spec/features/environments_spec.rb
@@ -0,0 +1,160 @@
+require 'spec_helper'
+
+feature 'Environments', feature: true do
+ given(:project) { create(:empty_project) }
+ given(:user) { create(:user) }
+ given(:role) { :developer }
+
+ background do
+ login_as(user)
+ project.team << [user, role]
+ end
+
+ describe 'when showing environments' do
+ given!(:environment) { }
+ given!(:deployment) { }
+
+ before do
+ visit namespace_project_environments_path(project.namespace, project)
+ end
+
+ context 'without environments' do
+ scenario 'does show no environments' do
+ expect(page).to have_content('No environments to show')
+ end
+ end
+
+ context 'with environments' do
+ given(:environment) { create(:environment, project: project) }
+
+ scenario 'does show environment name' do
+ expect(page).to have_link(environment.name)
+ end
+
+ context 'without deployments' do
+ scenario 'does show no deployments' do
+ expect(page).to have_content('No deployments yet')
+ end
+ end
+
+ context 'with deployments' do
+ given(:deployment) { create(:deployment, environment: environment) }
+
+ scenario 'does show deployment SHA' do
+ expect(page).to have_link(deployment.short_sha)
+ end
+ end
+ end
+
+ scenario 'does have a New environment button' do
+ expect(page).to have_link('New environment')
+ end
+ end
+
+ describe 'when showing the environment' do
+ given(:environment) { create(:environment, project: project) }
+ given!(:deployment) { }
+
+ before do
+ visit namespace_project_environment_path(project.namespace, project, environment)
+ end
+
+ context 'without deployments' do
+ scenario 'does show no deployments' do
+ expect(page).to have_content('No deployments for')
+ end
+ end
+
+ context 'with deployments' do
+ given(:deployment) { create(:deployment, environment: environment) }
+
+ scenario 'does show deployment SHA' do
+ expect(page).to have_link(deployment.short_sha)
+ end
+
+ scenario 'does not show a retry button for deployment without build' do
+ expect(page).not_to have_link('Retry')
+ end
+
+ context 'with build' do
+ given(:build) { create(:ci_build, project: project) }
+ given(:deployment) { create(:deployment, environment: environment, deployable: build) }
+
+ scenario 'does show build name' do
+ expect(page).to have_link("#{build.name} (##{build.id})")
+ end
+
+ scenario 'does show retry button' do
+ expect(page).to have_link('Retry')
+ end
+ end
+ end
+ end
+
+ describe 'when creating a new environment' do
+ before do
+ visit namespace_project_environments_path(project.namespace, project)
+ end
+
+ context 'when logged as developer' do
+ before do
+ click_link 'New environment'
+ end
+
+ context 'for valid name' do
+ before do
+ fill_in('Name', with: 'production')
+ click_on 'Create environment'
+ end
+
+ scenario 'does create a new pipeline' do
+ expect(page).to have_content('production')
+ end
+ end
+
+ context 'for invalid name' do
+ before do
+ fill_in('Name', with: 'name with spaces')
+ click_on 'Create environment'
+ end
+
+ scenario 'does show errors' do
+ expect(page).to have_content('Name can contain only letters')
+ end
+ end
+ end
+
+ context 'when logged as reporter' do
+ given(:role) { :reporter }
+
+ scenario 'does not have a New environment link' do
+ expect(page).not_to have_link('New environment')
+ end
+ end
+ end
+
+ describe 'when deleting existing environment' do
+ given(:environment) { create(:environment, project: project) }
+
+ before do
+ visit namespace_project_environment_path(project.namespace, project, environment)
+ end
+
+ context 'when logged as master' do
+ given(:role) { :master }
+
+ scenario 'does delete environment' do
+ click_link 'Destroy'
+ expect(page).not_to have_link(environment.name)
+ end
+ end
+
+ context 'when logged as developer' do
+ given(:role) { :developer }
+
+ scenario 'does not have a Destroy link' do
+ expect(page).not_to have_link('Destroy')
+ end
+ end
+ end
+end
diff --git a/spec/features/groups/members/owner_manages_access_requests_spec.rb b/spec/features/groups/members/owner_manages_access_requests_spec.rb
new file mode 100644
index 00000000000..22525ce530b
--- /dev/null
+++ b/spec/features/groups/members/owner_manages_access_requests_spec.rb
@@ -0,0 +1,48 @@
+require 'spec_helper'
+
+feature 'Groups > Members > Owner manages access requests', feature: true do
+ let(:user) { create(:user) }
+ let(:owner) { create(:user) }
+ let(:group) { create(:group, :public) }
+
+ background do
+ group.request_access(user)
+ group.add_owner(owner)
+ login_as(owner)
+ end
+
+ scenario 'owner can see access requests' do
+ visit group_group_members_path(group)
+
+ expect_visible_access_request(group, user)
+ end
+
+ scenario 'master can grant access' do
+ visit group_group_members_path(group)
+
+ expect_visible_access_request(group, user)
+
+ perform_enqueued_jobs { click_on 'Grant access' }
+
+ expect(ActionMailer::Base.deliveries.last.to).to eq [user.notification_email]
+ expect(ActionMailer::Base.deliveries.last.subject).to match "Access to the #{group.name} group was granted"
+ end
+
+ scenario 'master can deny access' do
+ visit group_group_members_path(group)
+
+ expect_visible_access_request(group, user)
+
+ perform_enqueued_jobs { click_on 'Deny access' }
+
+ expect(ActionMailer::Base.deliveries.last.to).to eq [user.notification_email]
+ expect(ActionMailer::Base.deliveries.last.subject).to match "Access to the #{group.name} group was denied"
+ end
+
+
+ def expect_visible_access_request(group, user)
+ expect(group.members.request.exists?(user_id: user)).to be_truthy
+ expect(page).to have_content "#{group.name} access requests (1)"
+ expect(page).to have_content user.name
+ end
+end
diff --git a/spec/features/groups/members/user_requests_access_spec.rb b/spec/features/groups/members/user_requests_access_spec.rb
new file mode 100644
index 00000000000..a878a96b6ee
--- /dev/null
+++ b/spec/features/groups/members/user_requests_access_spec.rb
@@ -0,0 +1,48 @@
+require 'spec_helper'
+
+feature 'Groups > Members > User requests access', feature: true do
+ let(:user) { create(:user) }
+ let(:owner) { create(:user) }
+ let(:group) { create(:group, :public) }
+
+ background do
+ group.add_owner(owner)
+ login_as(user)
+ visit group_path(group)
+ end
+
+ scenario 'user can request access to a group' do
+ perform_enqueued_jobs { click_link 'Request Access' }
+
+ expect(ActionMailer::Base.deliveries.last.to).to eq [owner.notification_email]
+ expect(ActionMailer::Base.deliveries.last.subject).to match "Request to join the #{group.name} group"
+
+ expect(group.members.request.exists?(user_id: user)).to be_truthy
+ expect(page).to have_content 'Your request for access has been queued for review.'
+
+ expect(page).to have_content 'Withdraw Access Request'
+ end
+
+ scenario 'user is not listed in the group members page' do
+ click_link 'Request Access'
+
+ expect(group.members.request.exists?(user_id: user)).to be_truthy
+
+ click_link 'Members'
+
+ page.within('.content') do
+ expect(page).not_to have_content(user.name)
+ end
+ end
+
+ scenario 'user can withdraw its request for access' do
+ click_link 'Request Access'
+
+ expect(group.members.request.exists?(user_id: user)).to be_truthy
+
+ click_link 'Withdraw Access Request'
+
+ expect(group.members.request.exists?(user_id: user)).to be_falsey
+ expect(page).to have_content 'Your access request to the group has been withdrawn.'
+ end
+end
diff --git a/spec/features/issues/filter_by_labels_spec.rb b/spec/features/issues/filter_by_labels_spec.rb
index 16c619c9288..5ea02b8d39c 100644
--- a/spec/features/issues/filter_by_labels_spec.rb
+++ b/spec/features/issues/filter_by_labels_spec.rb
@@ -56,8 +56,9 @@ feature 'Issue filtering by Labels', feature: true do
end
it 'should remove label "bug"' do
- first('.js-label-filter-remove').click
- expect(find('.filtered-labels')).to have_no_content "bug"
+ find('.js-label-filter-remove').click
+ wait_for_ajax
+ expect(find('.filtered-labels', visible: false)).to have_no_content "bug"
end
end
@@ -142,7 +143,8 @@ feature 'Issue filtering by Labels', feature: true do
end
it 'should remove label "enhancement"' do
- first('.js-label-filter-remove').click
+ find('.js-label-filter-remove', match: :first).click
+ wait_for_ajax
expect(find('.filtered-labels')).to have_no_content "enhancement"
end
end
@@ -179,6 +181,7 @@ feature 'Issue filtering by Labels', feature: true do
before do
page.within '.labels-filter' do
click_button 'Label'
+ wait_for_ajax
click_link 'bug'
find('.dropdown-menu-close').click
end
@@ -189,14 +192,11 @@ feature 'Issue filtering by Labels', feature: true do
end
it 'should allow user to remove filtered labels' do
- page.within '.filtered-labels' do
- first('.js-label-filter-remove').click
- expect(page).not_to have_content 'bug'
- end
+ first('.js-label-filter-remove').click
+ wait_for_ajax
- page.within '.labels-filter' do
- expect(page).not_to have_content 'bug'
- end
+ expect(find('.filtered-labels', visible: false)).not_to have_content 'bug'
+ expect(find('.labels-filter')).not_to have_content 'bug'
end
end
diff --git a/spec/features/issues/filter_issues_spec.rb b/spec/features/issues/filter_issues_spec.rb
index 1f0594e6b02..4bcb105b17d 100644
--- a/spec/features/issues/filter_issues_spec.rb
+++ b/spec/features/issues/filter_issues_spec.rb
@@ -1,6 +1,7 @@
require 'rails_helper'
describe 'Filter issues', feature: true do
+ include WaitForAjax
let!(:project) { create(:project) }
let!(:user) { create(:user)}
@@ -21,7 +22,7 @@ describe 'Filter issues', feature: true do
find('.dropdown-menu-user-link', text: user.username).click
- sleep 2
+ wait_for_ajax
end
context 'assignee', js: true do
@@ -53,7 +54,7 @@ describe 'Filter issues', feature: true do
find('.milestone-filter .dropdown-content a', text: milestone.title).click
- sleep 2
+ wait_for_ajax
end
context 'milestone', js: true do
@@ -80,23 +81,21 @@ describe 'Filter issues', feature: true do
before do
visit namespace_project_issues_path(project.namespace, project)
find('.js-label-select').click
+ wait_for_ajax
end
it 'should filter by any label' do
find('.dropdown-menu-labels a', text: 'Any Label').click
page.first('.labels-filter .dropdown-title .dropdown-menu-close-icon').click
- sleep 2
+ wait_for_ajax
- page.within '.labels-filter' do
- expect(page).to have_content 'Any Label'
- end
- expect(find('.js-label-select .dropdown-toggle-text')).to have_content('Any Label')
+ expect(find('.labels-filter')).to have_content 'Label'
end
it 'should filter by no label' do
find('.dropdown-menu-labels a', text: 'No Label').click
page.first('.labels-filter .dropdown-title .dropdown-menu-close-icon').click
- sleep 2
+ wait_for_ajax
page.within '.labels-filter' do
expect(page).to have_content 'No Label'
@@ -122,14 +121,14 @@ describe 'Filter issues', feature: true do
find('.dropdown-menu-user-link', text: user.username).click
- sleep 2
+ wait_for_ajax
find('.js-label-select').click
find('.dropdown-menu-labels .dropdown-content a', text: label.title).click
page.first('.labels-filter .dropdown-title .dropdown-menu-close-icon').click
- sleep 2
+ wait_for_ajax
end
context 'assignee and label', js: true do
@@ -276,9 +275,12 @@ describe 'Filter issues', feature: true do
it 'should be able to filter and sort issues' do
click_button 'Label'
+ wait_for_ajax
page.within '.labels-filter' do
click_link 'bug'
end
+ find('.dropdown-menu-close-icon').click
+ wait_for_ajax
page.within '.issues-list' do
expect(page).to have_selector('.issue', count: 2)
@@ -288,6 +290,7 @@ describe 'Filter issues', feature: true do
page.within '.dropdown-menu-sort' do
click_link 'Oldest created'
end
+ wait_for_ajax
page.within '.issues-list' do
expect(first('.issue')).to have_content('Frontend')
diff --git a/spec/features/issues/move_spec.rb b/spec/features/issues/move_spec.rb
index c7019c5aea1..7773c486b4e 100644
--- a/spec/features/issues/move_spec.rb
+++ b/spec/features/issues/move_spec.rb
@@ -26,6 +26,7 @@ feature 'issue move to another project' do
context 'user has permission to move issue' do
let!(:mr) { create(:merge_request, source_project: old_project) }
let(:new_project) { create(:project) }
+ let(:new_project_search) { create(:project) }
let(:text) { 'Text with !1' }
let(:cross_reference) { old_project.to_reference }
@@ -47,6 +48,21 @@ feature 'issue move to another project' do
expect(page).to have_content(issue.title)
end
+ scenario 'searching project dropdown', js: true do
+ new_project_search.team << [user, :reporter]
+
+ page.within '.js-move-dropdown' do
+ first('.select2-choice').click
+ end
+
+ fill_in('s2id_autogen2_search', with: new_project_search.name)
+
+ page.within '.select2-drop' do
+ expect(page).to have_content(new_project_search.name)
+ expect(page).not_to have_content(new_project.name)
+ end
+ end
+
context 'user does not have permission to move the issue to a project', js: true do
let!(:private_project) { create(:project, :private) }
let(:another_project) { create(:project) }
diff --git a/spec/features/issues/todo_spec.rb b/spec/features/issues/todo_spec.rb
new file mode 100644
index 00000000000..b69cce3e7d7
--- /dev/null
+++ b/spec/features/issues/todo_spec.rb
@@ -0,0 +1,33 @@
+require 'rails_helper'
+
+feature 'Manually create a todo item from issue', feature: true, js: true do
+ let!(:project) { create(:project) }
+ let!(:issue) { create(:issue, project: project) }
+ let!(:user) { create(:user)}
+
+ before do
+ project.team << [user, :master]
+ login_as(user)
+ visit namespace_project_issue_path(project.namespace, project, issue)
+ end
+
+ it 'should create todo when clicking button' do
+ page.within '.issuable-sidebar' do
+ click_button 'Add Todo'
+ expect(page).to have_content 'Mark Done'
+ end
+
+ page.within '.header-content .todos-pending-count' do
+ expect(page).to have_content '1'
+ end
+ end
+
+ it 'should mark a todo as done' do
+ page.within '.issuable-sidebar' do
+ click_button 'Add Todo'
+ click_button 'Mark Done'
+ end
+
+ expect(page).to have_selector('.todos-pending-count', visible: false)
+ end
+end
diff --git a/spec/features/issues_spec.rb b/spec/features/issues_spec.rb
index f6fb6a72d22..65fe918e2e8 100644
--- a/spec/features/issues_spec.rb
+++ b/spec/features/issues_spec.rb
@@ -396,6 +396,27 @@ describe 'Issues', feature: true do
expect(page).to have_content @user.name
end
end
+
+ it 'allows user to unselect themselves', js: true do
+ issue2 = create(:issue, project: project, author: @user)
+ visit namespace_project_issue_path(project.namespace, project, issue2)
+
+ page.within '.assignee' do
+ click_link 'Edit'
+ click_link @user.name
+
+ page.within '.value' do
+ expect(page).to have_content @user.name
+ end
+
+ click_link 'Edit'
+ click_link @user.name
+
+ page.within '.value' do
+ expect(page).to have_content "No assignee"
+ end
+ end
+ end
end
context 'by unauthorized user' do
@@ -440,6 +461,26 @@ describe 'Issues', feature: true do
expect(issue.reload.milestone).to be_nil
end
+
+ it 'allows user to de-select milestone', js: true do
+ visit namespace_project_issue_path(project.namespace, project, issue)
+
+ page.within('.milestone') do
+ click_link 'Edit'
+ click_link milestone.title
+
+ page.within '.value' do
+ expect(page).to have_content milestone.title
+ end
+
+ click_link 'Edit'
+ click_link milestone.title
+
+ page.within '.value' do
+ expect(page).to have_content 'None'
+ end
+ end
+ end
end
context 'by unauthorized user' do
diff --git a/spec/features/projects/members/master_manages_access_requests_spec.rb b/spec/features/projects/members/master_manages_access_requests_spec.rb
new file mode 100644
index 00000000000..5fe4caa12f0
--- /dev/null
+++ b/spec/features/projects/members/master_manages_access_requests_spec.rb
@@ -0,0 +1,47 @@
+require 'spec_helper'
+
+feature 'Projects > Members > Master manages access requests', feature: true do
+ let(:user) { create(:user) }
+ let(:master) { create(:user) }
+ let(:project) { create(:project, :public) }
+
+ background do
+ project.request_access(user)
+ project.team << [master, :master]
+ login_as(master)
+ end
+
+ scenario 'master can see access requests' do
+ visit namespace_project_project_members_path(project.namespace, project)
+
+ expect_visible_access_request(project, user)
+ end
+
+ scenario 'master can grant access' do
+ visit namespace_project_project_members_path(project.namespace, project)
+
+ expect_visible_access_request(project, user)
+
+ perform_enqueued_jobs { click_on 'Grant access' }
+
+ expect(ActionMailer::Base.deliveries.last.to).to eq [user.notification_email]
+ expect(ActionMailer::Base.deliveries.last.subject).to match "Access to the #{project.name_with_namespace} project was granted"
+ end
+
+ scenario 'master can deny access' do
+ visit namespace_project_project_members_path(project.namespace, project)
+
+ expect_visible_access_request(project, user)
+
+ perform_enqueued_jobs { click_on 'Deny access' }
+
+ expect(ActionMailer::Base.deliveries.last.to).to eq [user.notification_email]
+ expect(ActionMailer::Base.deliveries.last.subject).to match "Access to the #{project.name_with_namespace} project was denied"
+ end
+
+ def expect_visible_access_request(project, user)
+ expect(project.members.request.exists?(user_id: user)).to be_truthy
+ expect(page).to have_content "#{project.name} access requests (1)"
+ expect(page).to have_content user.name
+ end
+end
diff --git a/spec/features/projects/members/user_requests_access_spec.rb b/spec/features/projects/members/user_requests_access_spec.rb
new file mode 100644
index 00000000000..fd92a3a2f0c
--- /dev/null
+++ b/spec/features/projects/members/user_requests_access_spec.rb
@@ -0,0 +1,54 @@
+require 'spec_helper'
+
+feature 'Projects > Members > User requests access', feature: true do
+ let(:user) { create(:user) }
+ let(:master) { create(:user) }
+ let(:project) { create(:project, :public) }
+
+ background do
+ project.team << [master, :master]
+ login_as(user)
+ visit namespace_project_path(project.namespace, project)
+ end
+
+ scenario 'user can request access to a project' do
+ perform_enqueued_jobs { click_link 'Request Access' }
+
+ expect(ActionMailer::Base.deliveries.last.to).to eq [master.notification_email]
+ expect(ActionMailer::Base.deliveries.last.subject).to eq "Request to join the #{project.name_with_namespace} project"
+
+ expect(project.members.request.exists?(user_id: user)).to be_truthy
+ expect(page).to have_content 'Your request for access has been queued for review.'
+
+ expect(page).to have_content 'Withdraw Access Request'
+ end
+
+ scenario 'user is not listed in the project members page' do
+ click_link 'Request Access'
+
+ expect(project.members.request.exists?(user_id: user)).to be_truthy
+
+ open_project_settings_menu
+ click_link 'Members'
+
+ visit namespace_project_project_members_path(project.namespace, project)
+ page.within('.content') do
+ expect(page).not_to have_content(user.name)
+ end
+ end
+
+ scenario 'user can withdraw its request for access' do
+ click_link 'Request Access'
+
+ expect(project.members.request.exists?(user_id: user)).to be_truthy
+
+ click_link 'Withdraw Access Request'
+
+ expect(project.members.request.exists?(user_id: user)).to be_falsey
+ expect(page).to have_content 'Your access request to the project has been withdrawn.'
+ end
+
+ def open_project_settings_menu
+ find('#project-settings-button').click
+ end
+end
diff --git a/spec/features/security/project/public_access_spec.rb b/spec/features/security/project/public_access_spec.rb
index c5f741709ad..f6c6687e162 100644
--- a/spec/features/security/project/public_access_spec.rb
+++ b/spec/features/security/project/public_access_spec.rb
@@ -175,6 +175,49 @@ describe "Public Project Access", feature: true do
end
end
+ describe "GET /:project_path/environments" do
+ subject { namespace_project_environments_path(project.namespace, project) }
+
+ it { is_expected.to be_allowed_for :admin }
+ it { is_expected.to be_allowed_for owner }
+ it { is_expected.to be_allowed_for master }
+ it { is_expected.to be_allowed_for developer }
+ it { is_expected.to be_allowed_for reporter }
+ it { is_expected.to be_denied_for guest }
+ it { is_expected.to be_denied_for :user }
+ it { is_expected.to be_denied_for :external }
+ it { is_expected.to be_denied_for :visitor }
+ end
+
+ describe "GET /:project_path/environments/:id" do
+ let(:environment) { create(:environment, project: project) }
+ subject { namespace_project_environments_path(project.namespace, project, environment) }
+
+ it { is_expected.to be_allowed_for :admin }
+ it { is_expected.to be_allowed_for owner }
+ it { is_expected.to be_allowed_for master }
+ it { is_expected.to be_allowed_for developer }
+ it { is_expected.to be_allowed_for reporter }
+ it { is_expected.to be_denied_for guest }
+ it { is_expected.to be_denied_for :user }
+ it { is_expected.to be_denied_for :external }
+ it { is_expected.to be_denied_for :visitor }
+ end
+
+ describe "GET /:project_path/environments/new" do
+ subject { new_namespace_project_environment_path(project.namespace, project) }
+
+ it { is_expected.to be_allowed_for :admin }
+ it { is_expected.to be_allowed_for owner }
+ it { is_expected.to be_allowed_for master }
+ it { is_expected.to be_allowed_for developer }
+ it { is_expected.to be_denied_for reporter }
+ it { is_expected.to be_denied_for guest }
+ it { is_expected.to be_denied_for :user }
+ it { is_expected.to be_denied_for :external }
+ it { is_expected.to be_denied_for :visitor }
+ end
+
describe "GET /:project_path/blob" do
let(:commit) { project.repository.commit }
diff --git a/spec/features/u2f_spec.rb b/spec/features/u2f_spec.rb
index 366a90228b1..14613754f74 100644
--- a/spec/features/u2f_spec.rb
+++ b/spec/features/u2f_spec.rb
@@ -12,39 +12,24 @@ feature 'Using U2F (Universal 2nd Factor) Devices for Authentication', feature:
describe "registration" do
let(:user) { create(:user) }
- before { login_as(user) }
- describe 'when 2FA via OTP is disabled' do
- it 'allows registering a new device' do
- visit profile_account_path
- click_on 'Enable Two-Factor Authentication'
-
- register_u2f_device
+ before do
+ login_as(user)
+ user.update_attribute(:otp_required_for_login, true)
+ end
- expect(page.body).to match('Your U2F device was registered')
- end
+ describe 'when 2FA via OTP is disabled' do
+ before { user.update_attribute(:otp_required_for_login, false) }
- it 'allows registering more than one device' do
+ it 'does not allow registering a new device' do
visit profile_account_path
-
- # First device
click_on 'Enable Two-Factor Authentication'
- register_u2f_device
- expect(page.body).to match('Your U2F device was registered')
-
- # Second device
- click_on 'Manage Two-Factor Authentication'
- register_u2f_device
- expect(page.body).to match('Your U2F device was registered')
- click_on 'Manage Two-Factor Authentication'
- expect(page.body).to match('You have 2 U2F devices registered')
+ expect(page).to have_button('Setup New U2F Device', disabled: true)
end
end
describe 'when 2FA via OTP is enabled' do
- before { user.update_attributes(otp_required_for_login: true) }
-
it 'allows registering a new device' do
visit profile_account_path
click_on 'Manage Two-Factor Authentication'
@@ -67,7 +52,6 @@ feature 'Using U2F (Universal 2nd Factor) Devices for Authentication', feature:
click_on 'Manage Two-Factor Authentication'
register_u2f_device
expect(page.body).to match('Your U2F device was registered')
-
click_on 'Manage Two-Factor Authentication'
expect(page.body).to match('You have 2 U2F devices registered')
end
@@ -76,15 +60,16 @@ feature 'Using U2F (Universal 2nd Factor) Devices for Authentication', feature:
it 'allows the same device to be registered for multiple users' do
# First user
visit profile_account_path
- click_on 'Enable Two-Factor Authentication'
+ click_on 'Manage Two-Factor Authentication'
u2f_device = register_u2f_device
expect(page.body).to match('Your U2F device was registered')
logout
# Second user
- login_as(:user)
+ user = login_as(:user)
+ user.update_attribute(:otp_required_for_login, true)
visit profile_account_path
- click_on 'Enable Two-Factor Authentication'
+ click_on 'Manage Two-Factor Authentication'
register_u2f_device(u2f_device)
expect(page.body).to match('Your U2F device was registered')
@@ -94,7 +79,7 @@ feature 'Using U2F (Universal 2nd Factor) Devices for Authentication', feature:
context "when there are form errors" do
it "doesn't register the device if there are errors" do
visit profile_account_path
- click_on 'Enable Two-Factor Authentication'
+ click_on 'Manage Two-Factor Authentication'
# Have the "u2f device" respond with bad data
page.execute_script("u2f.register = function(_,_,_,callback) { callback('bad response'); };")
@@ -109,7 +94,7 @@ feature 'Using U2F (Universal 2nd Factor) Devices for Authentication', feature:
it "allows retrying registration" do
visit profile_account_path
- click_on 'Enable Two-Factor Authentication'
+ click_on 'Manage Two-Factor Authentication'
# Failed registration
page.execute_script("u2f.register = function(_,_,_,callback) { callback('bad response'); };")
@@ -133,8 +118,9 @@ feature 'Using U2F (Universal 2nd Factor) Devices for Authentication', feature:
before do
# Register and logout
login_as(user)
+ user.update_attribute(:otp_required_for_login, true)
visit profile_account_path
- click_on 'Enable Two-Factor Authentication'
+ click_on 'Manage Two-Factor Authentication'
@u2f_device = register_u2f_device
logout
end
@@ -154,7 +140,7 @@ feature 'Using U2F (Universal 2nd Factor) Devices for Authentication', feature:
describe "when 2FA via OTP is enabled" do
it "allows logging in with the U2F device" do
- user.update_attributes(otp_required_for_login: true)
+ user.update_attribute(:otp_required_for_login, true)
login_with(user)
@u2f_device.respond_to_u2f_authentication
@@ -171,8 +157,9 @@ feature 'Using U2F (Universal 2nd Factor) Devices for Authentication', feature:
it "does not allow logging in with that particular device" do
# Register current user with the different U2F device
current_user = login_as(:user)
+ current_user.update_attribute(:otp_required_for_login, true)
visit profile_account_path
- click_on 'Enable Two-Factor Authentication'
+ click_on 'Manage Two-Factor Authentication'
register_u2f_device
logout
@@ -191,8 +178,9 @@ feature 'Using U2F (Universal 2nd Factor) Devices for Authentication', feature:
it "allows logging in with that particular device" do
# Register current user with the same U2F device
current_user = login_as(:user)
+ current_user.update_attribute(:otp_required_for_login, true)
visit profile_account_path
- click_on 'Enable Two-Factor Authentication'
+ click_on 'Manage Two-Factor Authentication'
register_u2f_device(@u2f_device)
logout
@@ -227,8 +215,9 @@ feature 'Using U2F (Universal 2nd Factor) Devices for Authentication', feature:
before do
login_as(user)
+ user.update_attribute(:otp_required_for_login, true)
visit profile_account_path
- click_on 'Enable Two-Factor Authentication'
+ click_on 'Manage Two-Factor Authentication'
register_u2f_device
end